diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md deleted file mode 100644 index 8ca9413821..0000000000 --- a/.github/CONTRIBUTING.md +++ /dev/null @@ -1 +0,0 @@ -Please review the [guidelines for contributing](https://netty.io/wiki/developer-guide.html) for this repository. diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index a7a6188615..0000000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,13 +0,0 @@ -### Expected behavior - -### Actual behavior - -### Steps to reproduce - -### Minimal yet complete reproducer code (or URL to code) - -### Netty version - -### JVM version (e.g. `java -version`) - -### OS version (e.g. `uname -a`) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index 17b3725815..0000000000 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,14 +0,0 @@ -Motivation: - -Explain here the context, and why you're making that change. -What is the problem you're trying to solve. - -Modification: - -Describe the modifications you've done. - -Result: - -Fixes #. - -If there is no issue then describe the changes introduced by this PR. diff --git a/.github/scripts/build_affected_only.sh b/.github/scripts/build_affected_only.sh deleted file mode 100755 index 3b468b066b..0000000000 --- a/.github/scripts/build_affected_only.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash -# ---------------------------------------------------------------------------- -# Copyright 2021 The Netty Project -# -# The Netty Project licenses this file to you under the Apache License, -# version 2.0 (the "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at: -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- -set -e - -if [ "$#" -lt 2 ]; then - echo "Expected branch and maven arguments" - exit 1 -fi - -MODULES=$(git diff --name-only "$1" | cut -d '/' -f 1 | sort -u | sed -n -e 'H;${x;s/\n/,/g;s/^,//;p;}') -MAVEN_ARGUMENTS=${*:2} -if [ -z "$MODULES" ]; then - echo "No changes detected, skipping build" - exit 0 -fi -echo "Changes detected, start the build" -echo "./mvnw -pl $MODULES -amd $MAVEN_ARGUMENTS" -./mvnw -pl "$MODULES" -amd "${@:2}" - diff --git a/.github/scripts/check_build_result.sh b/.github/scripts/check_build_result.sh deleted file mode 100755 index 9ef6fcce44..0000000000 --- a/.github/scripts/check_build_result.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash -# ---------------------------------------------------------------------------- -# Copyright 2021 The Netty Project -# -# The Netty Project licenses this file to you under the Apache License, -# version 2.0 (the "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at: -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- -set -e - -if [ "$#" -ne 1 ]; then - echo "Expected build log as argument" - exit 1 -fi - -if grep -q 'BUILD FAILURE' $1 ; then - echo "Build failure detected, please inspect build log" - exit 1 -else - echo "Build successful" - exit 0 -fi diff --git a/.github/scripts/check_leak.sh b/.github/scripts/check_leak.sh deleted file mode 100755 index 7a1a39d363..0000000000 --- a/.github/scripts/check_leak.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash -# ---------------------------------------------------------------------------- -# Copyright 2021 The Netty Project -# -# The Netty Project licenses this file to you under the Apache License, -# version 2.0 (the "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at: -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- -set -e - -if [ "$#" -ne 1 ]; then - echo "Expected build log as argument" - exit 1 -fi - -if grep -q 'LEAK:' $1 ; then - echo "Leak detected, please inspect build log" - exit 1 -else - echo "No Leak detected" - exit 0 -fi - diff --git a/.github/scripts/merge_local_staging.sh b/.github/scripts/merge_local_staging.sh deleted file mode 100755 index 324f6f74da..0000000000 --- a/.github/scripts/merge_local_staging.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash -# ---------------------------------------------------------------------------- -# Copyright 2021 The Netty Project -# -# The Netty Project licenses this file to you under the Apache License, -# version 2.0 (the "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at: -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- -set -e -if [ "$#" -lt 2 ]; then - echo "Expected target directory and at least one local staging directory" - exit 1 -fi -TARGET=$1 - -for ((i=2; i<=$#; i++)) -do - DIR="${!i}" - SUB_DIR=$(ls -d "${DIR}"/* | awk -F / '{print $NF}') - - if [ ! -d "${TARGET}/${SUB_DIR}" ] - then - mkdir -p "${TARGET}/${SUB_DIR}" - fi - cat "${DIR}"/"${SUB_DIR}"/.index >> "${TARGET}/${SUB_DIR}"/.index - cp -r "${DIR}"/"${SUB_DIR}"/* "${TARGET}/${SUB_DIR}"/ -done diff --git a/.github/scripts/release_checkout_tag.sh b/.github/scripts/release_checkout_tag.sh deleted file mode 100755 index 73972b8e85..0000000000 --- a/.github/scripts/release_checkout_tag.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash -# ---------------------------------------------------------------------------- -# Copyright 2021 The Netty Project -# -# The Netty Project licenses this file to you under the Apache License, -# version 2.0 (the "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at: -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- -set -e - -if [ "$#" -ne 1 ]; then - echo "Expected release.properties file" - exit 1 -fi - -TAG=$(grep scm.tag= "$1" | cut -d'=' -f2) - -echo "Checkout tag $TAG" -git checkout "$TAG" -exit 0 diff --git a/.github/scripts/release_rollback.sh b/.github/scripts/release_rollback.sh deleted file mode 100755 index 52cd9a1dc6..0000000000 --- a/.github/scripts/release_rollback.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -# ---------------------------------------------------------------------------- -# Copyright 2021 The Netty Project -# -# The Netty Project licenses this file to you under the Apache License, -# version 2.0 (the "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at: -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- -set -e - -if [ "$#" -ne 3 ]; then - echo "Expected release.properties file, repository name and branch" - exit 1 -fi - -TAG=$(grep scm.tag= "$1" | cut -d'=' -f2) -git remote set-url origin git@github.com:"$2".git -git fetch -git checkout "$3" -./mvnw -B --file pom.xml release:rollback -git push origin :"$TAG" diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml deleted file mode 100644 index 22d02c73a4..0000000000 --- a/.github/workflows/ci-build.yml +++ /dev/null @@ -1,79 +0,0 @@ -# ---------------------------------------------------------------------------- -# Copyright 2021 The Netty Project -# -# The Netty Project licenses this file to you under the Apache License, -# version 2.0 (the "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at: -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- -name: Build project - -on: - push: - branches: [ "main"] - - schedule: - - cron: '30 1 * * 1' # At 01:30 on Monday, every Monday. - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -env: - MAVEN_OPTS: -Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false -Dmaven.wagon.http.retryhandler.count=5 -Dmaven.wagon.httpconnectionManager.ttlSeconds=240 - -jobs: - build: - runs-on: ubuntu-latest - strategy: - matrix: - include: - - setup: linux-x86_64-java11 - docker-compose-build: "-f docker/docker-compose.yaml -f docker/docker-compose.centos-6.111.yaml build" - docker-compose-run: "-f docker/docker-compose.yaml -f docker/docker-compose.centos-6.111.yaml run build" - - name: ${{ matrix.setup }} - steps: - - uses: actions/checkout@v2 - - # Cache .m2/repository - - name: Cache local Maven repository - uses: actions/cache@v2 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ matrix.setup }}-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven-${{ matrix.setup }}- - ${{ runner.os }}-maven- - - # Enable caching of Docker layers - - uses: satackey/action-docker-layer-caching@v0.0.11 - continue-on-error: true - with: - key: build-docker-cache-${{ matrix.setup }}-{hash} - restore-keys: | - build-docker-cache-${{ matrix.setup }}- - build-docker-cache- - - - name: Build docker image - run: docker-compose ${{ matrix.docker-compose-build }} - - - name: Build project without leak detection - run: docker-compose ${{ matrix.docker-compose-run }} | tee build.output - - - name: Checking for test failures - run: ./.github/scripts/check_build_result.sh build.output - - - uses: actions/upload-artifact@v2 - if: ${{ failure() }} - with: - name: target - path: | - **/target/surefire-reports/ - **/hs_err*.log diff --git a/.github/workflows/ci-deploy.yml b/.github/workflows/ci-deploy.yml deleted file mode 100644 index 93dbbf4521..0000000000 --- a/.github/workflows/ci-deploy.yml +++ /dev/null @@ -1,143 +0,0 @@ -# ---------------------------------------------------------------------------- -# Copyright 2021 The Netty Project -# -# The Netty Project licenses this file to you under the Apache License, -# version 2.0 (the "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at: -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- -name: Deploy project - -on: - push: - branches: [ "main" ] - - schedule: - - cron: '30 1 * * 1' # At 01:30 on Monday, every Monday. - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -env: - MAVEN_OPTS: -Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false -Dmaven.wagon.http.retryhandler.count=5 -Dmaven.wagon.httpconnectionManager.ttlSeconds=240 - -jobs: - stage-snapshot: - runs-on: ubuntu-latest - strategy: - matrix: - include: - - setup: linux-x86_64-java11 - docker-compose-build: "-f docker/docker-compose.yaml -f docker/docker-compose.centos-6.111.yaml build" - docker-compose-run: "-f docker/docker-compose.yaml -f docker/docker-compose.centos-6.111.yaml run stage-snapshot" - - setup: linux-aarch64 - docker-compose-build: "-f docker/docker-compose.centos-7.yaml build" - docker-compose-run: "-f docker/docker-compose.centos-7.yaml run cross-compile-aarch64-stage-snapshot" - - name: stage-snapshot-${{ matrix.setup }} - steps: - - uses: actions/checkout@v2 - - # Cache .m2/repository - - name: Cache local Maven repository - uses: actions/cache@v2 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ matrix.setup }}-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven-${{ matrix.setup }}- - ${{ runner.os }}-maven- - - # Enable caching of Docker layers - - uses: satackey/action-docker-layer-caching@v0.0.11 - env: - docker-cache-name: staging-${{ matrix.setup }}-cache-docker - continue-on-error: true - with: - key: ${{ runner.os }}-staging-${{ env.docker-cache-name }}-{hash} - restore-keys: | - ${{ runner.os }}-staging-${{ env.docker-cache-name }}- - - - name: Create local staging directory - run: mkdir -p ~/local-staging - - - name: Build docker image - run: docker-compose ${{ matrix.docker-compose-build }} - - - name: Stage snapshots to local staging directory - run: docker-compose ${{ matrix.docker-compose-run }} - - - name: Upload local staging directory - uses: actions/upload-artifact@v2 - with: - name: ${{ matrix.setup }}-local-staging - path: ~/local-staging - if-no-files-found: error - - deploy-staged-snapshots: - runs-on: ubuntu-18.04 - # Wait until we have staged everything - needs: stage-snapshot - steps: - - uses: actions/checkout@v2 - - - name: Set up JDK 11 - uses: actions/setup-java@v1 - with: - java-version: '11' - - # Cache .m2/repository - - name: Cache local Maven repository - uses: actions/cache@v2 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-deploy-staged-snapshots-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven-deploy-staged-snapshots- - ${{ runner.os }}-maven- - - # Setup some env to re-use later. - - name: Prepare enviroment variables - run: | - echo "LOCAL_STAGING_DIR=$HOME/local-staging" >> $GITHUB_ENV - - # Hardcode the staging artifacts that need to be downloaded. - # These must match the matrix setups. There is currently no way to pull this out of the config. - - name: Download linux-aarch64 staging directory - uses: actions/download-artifact@v2 - with: - name: linux-aarch64-local-staging - path: ~/linux-aarch64-local-staging - - - name: Download linux-x86_64-java11 staging directory - uses: actions/download-artifact@v2 - with: - name: linux-x86_64-java11-local-staging - path: ~/linux-x86_64-java11-local-staging - - - name: Merge staging repositories - run: | - mkdir -p ~/local-staging/deferred - cat ~/linux-aarch64-local-staging/deferred/.index >> ~/local-staging/deferred/.index - cp -r ~/linux-aarch64-local-staging/deferred/* ~/local-staging/deferred/ - cat ~/linux-x86_64-java11-local-staging/deferred/.index >> ~/local-staging/deferred/.index - cp -r ~/linux-x86_64-java11-local-staging/deferred/* ~/local-staging/deferred/ - - - uses: s4u/maven-settings-action@v2.2.0 - with: - servers: | - [{ - "id": "sonatype-nexus-snapshots", - "username": "${{ secrets.SONATYPE_USERNAME }}", - "password": "${{ secrets.SONATYPE_PASSWORD }}" - }] - - - name: Deploy local staged artifacts - run: ./mvnw -B --file pom.xml org.sonatype.plugins:nexus-staging-maven-plugin:deploy-staged -DaltStagingDirectory=$LOCAL_STAGING_DIR \ No newline at end of file diff --git a/.github/workflows/ci-pr-reports.yml b/.github/workflows/ci-pr-reports.yml deleted file mode 100644 index acbb719ca4..0000000000 --- a/.github/workflows/ci-pr-reports.yml +++ /dev/null @@ -1,54 +0,0 @@ -# ---------------------------------------------------------------------------- -# Copyright 2021 The Netty Project -# -# The Netty Project licenses this file to you under the Apache License, -# version 2.0 (the "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at: -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- -name: PR Reports -on: - workflow_run: - workflows: [ "Build PR" ] - types: - - completed -env: - MAVEN_OPTS: -Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false -Dmaven.wagon.http.retryhandler.count=5 -Dmaven.wagon.httpconnectionManager.ttlSeconds=240 - -jobs: - tests: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - ignore-if-missing: [false] - include: - - setup: linux-x86_64-java11 - - setup: linux-x86_64-java16 - - setup: linux-x86_64-java11-boringssl - - setup: windows-x86_64-java11-boringssl - continue-on-error: ${{ matrix.ignore-if-missing }} - steps: - - name: Download Artifacts - uses: dawidd6/action-download-artifact@v2.14.1 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - workflow: ${{ github.event.workflow_run.workflow_id }} - workflow_conclusion: completed - commit: ${{ github.event.workflow_run.head_commit.id }} - # File location set in ci-pr.yml and must be coordinated. - name: test-results-${{ matrix.setup }} - - name: Publish Test Report - uses: scacap/action-surefire-report@v1.0.13 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - report_paths: '**/target/surefire-reports/TEST-*.xml' - commit: ${{ github.event.workflow_run.head_commit.id }} - check_name: ${{ matrix.setup }} test reports \ No newline at end of file diff --git a/.github/workflows/ci-pr.yml b/.github/workflows/ci-pr.yml deleted file mode 100644 index 17c1104088..0000000000 --- a/.github/workflows/ci-pr.yml +++ /dev/null @@ -1,206 +0,0 @@ -# ---------------------------------------------------------------------------- -# Copyright 2021 The Netty Project -# -# The Netty Project licenses this file to you under the Apache License, -# version 2.0 (the "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at: -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- -name: Build PR - -on: - pull_request: - branches: [ "main"] - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -env: - MAVEN_OPTS: -Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false -Dmaven.wagon.http.retryhandler.count=5 -Dmaven.wagon.httpconnectionManager.ttlSeconds=240 - -jobs: - verify-pr: - runs-on: ubuntu-18.04 - steps: - - uses: actions/checkout@v2 - - name: Set up JDK 11 - uses: actions/setup-java@v1 - with: - java-version: '11' - # Cache .m2/repository - - name: Cache local Maven repository - uses: actions/cache@v2 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-verify-pr-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven-verify-pr- - ${{ runner.os }}-maven- - - name: Verify with Maven - run: ./mvnw -B -ntp --file pom.xml verify -DskipTests=true - - build-pr-windows: - runs-on: windows-2016 - name: windows-x86_64-java11-boringssl - needs: verify-pr - steps: - - uses: actions/checkout@v2 - - - name: Set up JDK 11 - uses: actions/setup-java@v1 - with: - java-version: 11 - - # Cache .m2/repository - # Caching of maven dependencies - - uses: actions/cache@v2 - with: - path: ~/.m2/repository - key: pr-windows-x86_64-maven-cache-${{ hashFiles('**/pom.xml') }} - restore-keys: | - pr-windows-x86_64-maven-cache- - - - name: Build project - run: ./mvnw.cmd -B -ntp --file pom.xml clean package -Pboringssl -DskipHttp2Testsuite=true -DskipAutobahnTestsuite=true - - - name: Upload Test Results - if: always() - uses: actions/upload-artifact@v2 - with: - name: test-results-windows-x86_64-java11-boringssl - path: '**/target/surefire-reports/TEST-*.xml' - - - uses: actions/upload-artifact@v2 - if: ${{ failure() }} - with: - name: build-pr-windows-target - path: | - **/target/surefire-reports/ - **/hs_err*.log - - build-pr-aarch64: - name: linux-aarch64-verify-native - # The host should always be Linux - runs-on: ubuntu-20.04 - needs: verify-pr - steps: - - uses: actions/checkout@v2 - - # Cache .m2/repository - - name: Cache local Maven repository - uses: actions/cache@v2 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-build-pr-aarch64-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven-build-pr-aarch64- - ${{ runner.os }}-maven- - - - uses: uraimo/run-on-arch-action@v2.0.9 - name: Run commands - id: runcmd - with: - arch: aarch64 - distro: ubuntu20.04 - - # Not required, but speeds up builds by storing container images in - # a GitHub package registry. - githubToken: ${{ github.token }} - - # Mount the .m2/repository - dockerRunArgs: | - --volume "/home/runner/.m2/repository/:/root/.m2/repository" - - # Install dependencies - install: | - apt-get update -q -y - apt-get install -q -y openjdk-11-jdk autoconf automake libtool make tar maven git - - # Compile native code and the modules it depend on and run NativeLoadingTest. This is enough to ensure - # we can load the native module on aarch64 - # - # Use tcnative.classifier that is empty as we don't support using the shared lib version on ubuntu. - run: | - JAVA_HOME=/usr/lib/jvm/java-11-openjdk-arm64 ./mvnw -B -ntp -pl testsuite-native -am clean package -DskipTests=true -Dcheckstyle.skip=true -DskipNativeTestsuite=false -Dtcnative.classifier= - - build-pr: - runs-on: ubuntu-latest - strategy: - matrix: - include: - - setup: linux-x86_64-java11 - docker-compose-build: "-f docker/docker-compose.yaml -f docker/docker-compose.centos-6.111.yaml build" - docker-compose-run: "-f docker/docker-compose.yaml -f docker/docker-compose.centos-6.111.yaml run build-leak" - - setup: linux-x86_64-java11-graal - docker-compose-build: "-f docker/docker-compose.yaml -f docker/docker-compose.centos-6.graalvm111.yaml build" - docker-compose-run: "-f docker/docker-compose.yaml -f docker/docker-compose.centos-6.graalvm111.yaml run build-leak" - - setup: linux-x86_64-java16 - docker-compose-build: "-f docker/docker-compose.yaml -f docker/docker-compose.centos-6.116.yaml build" - docker-compose-run: "-f docker/docker-compose.yaml -f docker/docker-compose.centos-6.116.yaml run build-leak" - - setup: linux-x86_64-java17 - docker-compose-build: "-f docker/docker-compose.yaml -f docker/docker-compose.centos-6.117.yaml build" - docker-compose-run: "-f docker/docker-compose.yaml -f docker/docker-compose.centos-6.117.yaml run build-leak" - - setup: linux-x86_64-java11-boringssl - docker-compose-build: "-f docker/docker-compose.yaml -f docker/docker-compose.centos-6.111.yaml build" - docker-compose-run: "-f docker/docker-compose.yaml -f docker/docker-compose.centos-6.111.yaml run build-leak-boringssl-static" - - setup: linux-x86_64-java11-unsafe-buffer - docker-compose-build: "-f docker/docker-compose.yaml -f docker/docker-compose.centos-6.111.yaml build" - docker-compose-run: "-f docker/docker-compose.yaml -f docker/docker-compose.centos-6.111.yaml run build-unsafe-buffer" - - name: ${{ matrix.setup }} build - needs: verify-pr - steps: - - uses: actions/checkout@v2 - - # Cache .m2/repository - - name: Cache local Maven repository - uses: actions/cache@v2 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ matrix.setup }}-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven-${{ matrix.setup }}- - ${{ runner.os }}-maven- - - # Enable caching of Docker layers - - uses: satackey/action-docker-layer-caching@v0.0.11 - continue-on-error: true - with: - key: build-docker-cache-${{ matrix.setup }}-{hash} - restore-keys: | - build-docker-cache-${{ matrix.setup }}- - build-docker-cache- - - - name: Build docker image - run: docker-compose ${{ matrix.docker-compose-build }} - - - name: Build project with leak detection - run: docker-compose ${{ matrix.docker-compose-run }} | tee build-leak.output - - - name: Checking for test failures - run: ./.github/scripts/check_build_result.sh build-leak.output - - - name: Checking for detected leak - run: ./.github/scripts/check_leak.sh build-leak.output - - - name: Upload Test Results - if: always() - uses: actions/upload-artifact@v2 - with: - name: test-results-${{ matrix.setup }} - path: '**/target/surefire-reports/TEST-*.xml' - - - uses: actions/upload-artifact@v2 - if: ${{ failure() }} - with: - name: build-${{ matrix.setup }}-target - path: | - **/target/surefire-reports/ - **/hs_err*.log diff --git a/.github/workflows/ci-release.yml b/.github/workflows/ci-release.yml deleted file mode 100644 index aa431372c7..0000000000 --- a/.github/workflows/ci-release.yml +++ /dev/null @@ -1,249 +0,0 @@ -# ---------------------------------------------------------------------------- -# Copyright 2021 The Netty Project -# -# The Netty Project licenses this file to you under the Apache License, -# version 2.0 (the "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at: -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- -name: Release - -on: - - # Releases can only be triggered via the action tab - workflow_dispatch: - -env: - MAVEN_OPTS: -Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false -Dmaven.wagon.http.retryhandler.count=5 -Dmaven.wagon.httpconnectionManager.ttlSeconds=240 - -jobs: - prepare-release: - runs-on: ubuntu-18.04 - steps: - - uses: actions/checkout@v2 - with: - ref: main - - - name: Set up JDK 11 - uses: actions/setup-java@v1 - with: - java-version: '11' - - - name: Setup git configuration - run: | - git config --global user.email "netty-project-bot@users.noreply.github.com" - git config --global user.name "Netty Project Bot" - - - name: Install SSH key - uses: shimataro/ssh-key-action@v2 - with: - key: ${{ secrets.SSH_PRIVATE_KEY_PEM }} - known_hosts: ${{ secrets.SSH_KNOWN_HOSTS }} - - # Cache .m2/repository - - name: Cache local Maven repository - uses: actions/cache@v2 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-prepare-release-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven-prepare-release- - ${{ runner.os }}-maven- - - - name: Prepare release with Maven - run: | - ./mvnw -B -ntp --file pom.xml release:prepare -DpreparationGoals=clean -DskipTests=true - ./mvnw -B -ntp clean - - - name: Checkout tag - run: ./.github/scripts/release_checkout_tag.sh release.properties - - - name: Upload workspace - uses: actions/upload-artifact@v2 - with: - name: prepare-release-workspace - path: ${{ github.workspace }}/** - - stage-release-linux: - runs-on: ubuntu-latest - needs: prepare-release - strategy: - matrix: - include: - - setup: linux-x86_64-java11 - docker-compose-build: "-f docker/docker-compose.yaml -f docker/docker-compose.centos-6.11.yaml build" - docker-compose-run: "-f docker/docker-compose.yaml -f docker/docker-compose.centos-6.11.yaml run stage-release" - - setup: linux-aarch64 - docker-compose-build: "-f docker/docker-compose.centos-7.yaml build" - docker-compose-run: "-f docker/docker-compose.centos-7.yaml run cross-compile-aarch64-stage-release" - - name: stage-release-${{ matrix.setup }} - - steps: - - name: Download release-workspace - uses: actions/download-artifact@v2 - with: - name: prepare-release-workspace - path: ./prepare-release-workspace/ - - - name: Adjust mvnw permissions - run: chmod 755 ./prepare-release-workspace/mvnw - - - name: Set up JDK 11 - uses: actions/setup-java@v1 - with: - java-version: '11' - - - name: Setup git configuration - run: | - git config --global user.email "netty-project-bot@users.noreply.github.com" - git config --global user.name "Netty Project Bot" - - - name: Install SSH key - uses: shimataro/ssh-key-action@v2 - with: - key: ${{ secrets.SSH_PRIVATE_KEY_PEM }} - known_hosts: ${{ secrets.SSH_KNOWN_HOSTS }} - - # Enable caching of Docker layers - - uses: satackey/action-docker-layer-caching@v0.0.11 - continue-on-error: true - with: - key: ${{ runner.os }}-staging-docker-cache-${{ matrix.setup }}-{hash} - restore-keys: | - ${{ runner.os }}-staging-docker-cache-${{ matrix.setup }}- - ${{ runner.os }}-staging-docker-cache- - - - uses: s4u/maven-settings-action@v2.2.0 - with: - servers: | - [{ - "id": "sonatype-nexus-staging", - "username": "${{ secrets.SONATYPE_USERNAME }}", - "password": "${{ secrets.SONATYPE_PASSWORD }}" - }] - - # Cache .m2/repository - - name: Cache local Maven repository - uses: actions/cache@v2 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ matrix.setup }}-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven-${{ matrix.setup }}- - ${{ runner.os }}-maven- - - - name: Create local staging directory - run: mkdir -p ~/local-staging - - - name: Build docker image - working-directory: ./prepare-release-workspace/ - run: docker-compose ${{ matrix.docker-compose-build }} - - - name: Stage release to local staging directory - working-directory: ./prepare-release-workspace/ - env: - GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} - GPG_KEYNAME: ${{ secrets.GPG_KEYNAME }} - GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - run: docker-compose ${{ matrix.docker-compose-run }} - - - name: Upload local staging directory - uses: actions/upload-artifact@v2 - with: - name: ${{ matrix.setup }}-local-staging - path: ~/local-staging - if-no-files-found: error - - - name: Rollback release on failure - working-directory: ./prepare-release-workspace/ - if: ${{ failure() }} - # Rollback the release in case of an failure - run: bash ./.github/scripts/release_rollback.sh release.properties netty/netty main - - deploy-staged-release: - runs-on: ubuntu-18.04 - # Wait until we have staged everything - needs: stage-release-linux - steps: - - name: Download release-workspace - uses: actions/download-artifact@v2 - with: - name: prepare-release-workspace - path: ./prepare-release-workspace/ - - - name: Adjust mvnw permissions - run: chmod 755 ./prepare-release-workspace/mvnw - - - name: Set up JDK 11 - uses: actions/setup-java@v1 - with: - java-version: '11' - - - name: Setup git configuration - run: | - git config --global user.email "netty-project-bot@users.noreply.github.com" - git config --global user.name "Netty Project Bot" - - - name: Install SSH key - uses: shimataro/ssh-key-action@v2 - with: - key: ${{ secrets.SSH_PRIVATE_KEY_PEM }} - known_hosts: ${{ secrets.SSH_KNOWN_HOSTS }} - - # Hardcode the staging artifacts that need to be downloaded. - # These must match the matrix setups. There is currently no way to pull this out of the config. - - name: Download linux-aarch64 staging directory - uses: actions/download-artifact@v2 - with: - name: linux-aarch64-local-staging - path: ~/linux-aarch64-local-staging - - - name: Download linux-x86_64-java11 staging directory - uses: actions/download-artifact@v2 - with: - name: linux-x86_64-java11-local-staging - path: ~/linux-x86_64-java11-local-staging - - # This step takes care of merging all the previous staged repositories in a way that will allow us to deploy - # all together with one maven command. - - name: Merge staging repositories - working-directory: ./prepare-release-workspace/ - run: bash ./.github/scripts/merge_local_staging.sh /home/runner/local-staging/staging ~/linux-aarch64-local-staging/staging ~/linux-x86_64-java11-local-staging/staging - - - uses: s4u/maven-settings-action@v2.2.0 - with: - servers: | - [{ - "id": "sonatype-nexus-staging", - "username": "${{ secrets.SONATYPE_USERNAME }}", - "password": "${{ secrets.SONATYPE_PASSWORD }}" - }] - - # Cache .m2/repository - - name: Cache local Maven repository - uses: actions/cache@v2 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-deploy-staged-release-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven-deploy-staged-release- - ${{ runner.os }}-maven- - - - name: Deploy local staged artifacts - working-directory: ./prepare-release-workspace/ - # If we don't want to close the repository we can add -DskipStagingRepositoryClose=true - run: ./mvnw -B -ntp --file pom.xml org.sonatype.plugins:nexus-staging-maven-plugin:deploy-staged -DnexusUrl=https://oss.sonatype.org -DserverId=sonatype-nexus-staging -DaltStagingDirectory=/home/runner/local-staging -DskipStagingRepositoryClose=true - - - name: Rollback release on failure - working-directory: ./prepare-release-workspace/ - if: ${{ failure() }} - # Rollback the release in case of an failure - run: bash ./.github/scripts/release_rollback.sh release.properties netty/netty main diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml deleted file mode 100644 index be5da1671c..0000000000 --- a/.github/workflows/codeql-analysis.yml +++ /dev/null @@ -1,102 +0,0 @@ -# ---------------------------------------------------------------------------- -# Copyright 2021 The Netty Project -# -# The Netty Project licenses this file to you under the Apache License, -# version 2.0 (the "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at: -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -name: "CodeQL" - -on: - push: - branches: ["4.1", main] - pull_request: - # The branches below must be a subset of the branches above - branches: ["4.1", main] - schedule: - - cron: '0 13 * * 3' - -env: - MAVEN_OPTS: -Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false -Dmaven.wagon.http.retryhandler.count=5 -Dmaven.wagon.httpconnectionManager.ttlSeconds=240 - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - # Override automatic language detection by changing the below list - # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] - language: ['java', 'cpp' ] - # Learn more... - # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection - - steps: - - name: Checkout repository - uses: actions/checkout@v2 - with: - # We must fetch at least the immediate parents so that if this is - # a pull request then we can checkout the head. - fetch-depth: 2 - - # Cache .m2/repository - - name: Cache local Maven repository - uses: actions/cache@v2 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ matrix.language }} ${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven-${{ matrix.language }} - ${{ runner.os }}-maven- - - # If this run was triggered by a pull request event, then checkout - # the head of the pull request instead of the merge commit. - - run: git checkout HEAD^2 - if: ${{ github.event_name == 'pull_request' }} - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v1 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main - - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - # - name: Autobuild - # uses: github/codeql-action/autobuild@v1 - - # ℹī¸ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl - - # ✏ī¸ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language - - uses: actions/setup-java@v1 - with: - java-version: '11' # The JDK version to make available on the path. - - - name: Compile project - run: ./mvnw -B -ntp clean package -DskipTests=true - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar deleted file mode 100644 index 41c70a7e0b..0000000000 Binary files a/.mvn/wrapper/maven-wrapper.jar and /dev/null differ diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties deleted file mode 100644 index 08e7e646f3..0000000000 --- a/.mvn/wrapper/maven-wrapper.properties +++ /dev/null @@ -1 +0,0 @@ -distributionUrl=https://downloads.apache.org/maven/maven-3/3.8.1/binaries/apache-maven-3.8.1-bin.zip diff --git a/README.md b/README.md index 9176ac5eaa..5d5d75ee24 100644 --- a/README.md +++ b/README.md @@ -33,32 +33,32 @@ Netty can be used in modular JDK9+ applications as a collection of automatic mod reverse-DNS style, and are derived from subproject names rather than root packages due to historical reasons. They are listed below: - * `io.netty.all` - * `io.netty.buffer` - * `io.netty.codec` - * `io.netty.codec.dns` - * `io.netty.codec.haproxy` - * `io.netty.codec.http` - * `io.netty.codec.http2` - * `io.netty.codec.memcache` - * `io.netty.codec.mqtt` - * `io.netty.codec.redis` - * `io.netty.codec.smtp` - * `io.netty.codec.socks` - * `io.netty.codec.stomp` - * `io.netty.codec.xml` - * `io.netty.common` - * `io.netty.handler` - * `io.netty.handler.proxy` - * `io.netty.resolver` - * `io.netty.resolver.dns` - * `io.netty.transport` - * `io.netty.transport.epoll` (`native` omitted - reserved keyword in Java) - * `io.netty.transport.kqueue` (`native` omitted - reserved keyword in Java) - * `io.netty.transport.unix.common` (`native` omitted - reserved keyword in Java) - * `io.netty.transport.rxtx` - * `io.netty.transport.sctp` - * `io.netty.transport.udt` + * `io.net5.all` + * `io.net5.buffer` + * `io.net5.codec` + * `io.net5.codec.dns` + * `io.net5.codec.haproxy` + * `io.net5.codec.http` + * `io.net5.codec.http2` + * `io.net5.codec.memcache` + * `io.net5.codec.mqtt` + * `io.net5.codec.redis` + * `io.net5.codec.smtp` + * `io.net5.codec.socks` + * `io.net5.codec.stomp` + * `io.net5.codec.xml` + * `io.net5.common` + * `io.net5.handler` + * `io.net5.handler.proxy` + * `io.net5.resolver` + * `io.net5.resolver.dns` + * `io.net5.transport` + * `io.net5.transport.epoll` (`native` omitted - reserved keyword in Java) + * `io.net5.transport.kqueue` (`native` omitted - reserved keyword in Java) + * `io.net5.transport.unix.common` (`native` omitted - reserved keyword in Java) + * `io.net5.transport.rxtx` + * `io.net5.transport.sctp` + * `io.net5.transport.udt` diff --git a/all/pom.xml b/all/pom.xml deleted file mode 100644 index 89e6082c39..0000000000 --- a/all/pom.xml +++ /dev/null @@ -1,659 +0,0 @@ - - - - - 4.0.0 - - io.netty - netty-parent - 5.0.0.Final-SNAPSHOT - - - netty-all - jar - - Netty/All-in-One - - - ${project.build.directory}/src - ${project.build.directory}/versions - true - - - - - - io.netty - netty-bom - ${project.version} - pom - import - - - - - - - - uber-staging - - - staged-releases - Staged Releases - https://oss.sonatype.org/service/local/repositories/${stagingRepositoryId}/content/ - - - - - - - - ${project.groupId} - netty-transport-native-epoll - linux-x86_64 - compile - true - - - ${project.groupId} - netty-transport-native-epoll - linux-aarch_64 - compile - true - - - ${project.groupId} - netty-transport-native-kqueue - osx-x86_64 - compile - true - - - ${project.groupId} - netty-transport-native-kqueue - osx-aarch_64 - compile - true - - - ${project.groupId} - netty-resolver-dns-native-macos - osx-x86_64 - compile - true - - - ${project.groupId} - netty-resolver-dns-native-macos - osx-aarch_64 - compile - true - - - - - uber-snapshot - - - - - - ${project.groupId} - netty-transport-native-epoll - linux-x86_64 - compile - true - - - ${project.groupId} - netty-transport-native-epoll - linux-aarch_64 - compile - true - - - ${project.groupId} - netty-transport-native-kqueue - osx-x86_64 - compile - true - - - ${project.groupId} - netty-transport-native-kqueue - osx-aarch_64 - compile - true - - - ${project.groupId} - netty-resolver-dns-native-macos - osx-x86_64 - compile - true - - - ${project.groupId} - netty-resolver-dns-native-macos - osx-aarch_64 - compile - true - - - - - - - linux - - - linux - - - - - - ${project.groupId} - netty-transport-native-epoll - ${project.version} - ${jni.classifier} - compile - true - - - - ${project.groupId} - netty-transport-native-kqueue - compile - true - - - ${project.groupId} - netty-resolver-dns-native-macos - compile - true - - - - - - mac - - - mac - - - - - - ${project.groupId} - netty-transport-native-kqueue - ${project.version} - ${jni.classifier} - compile - true - - - ${project.groupId} - netty-resolver-dns-native-macos - ${project.version} - ${jni.classifier} - compile - true - - - - ${project.groupId} - netty-transport-native-epoll - compile - true - - - - - freebsd - - - unix - freebsd - - - - - - ${project.groupId} - netty-transport-native-kqueue - ${project.version} - ${jni.classifier} - compile - true - - - ${project.groupId} - netty-resolver-dns-native-macos - compile - true - - - - ${project.groupId} - netty-transport-native-epoll - compile - true - - - - - openbsd - - - unix - openbsd - - - - - - ${project.groupId} - netty-transport-native-kqueue - ${project.version} - ${jni.classifier} - compile - true - - - ${project.groupId} - netty-resolver-dns-native-macos - compile - true - - - - ${project.groupId} - netty-transport-native-epoll - compile - true - - - - - - - - - ${project.groupId} - netty-buffer - compile - true - - - ${project.groupId} - netty-codec - compile - true - - - ${project.groupId} - netty-codec-dns - compile - true - - - ${project.groupId} - netty-codec-haproxy - compile - true - - - ${project.groupId} - netty-codec-http - compile - true - - - ${project.groupId} - netty-codec-http2 - compile - true - - - ${project.groupId} - netty-codec-memcache - compile - true - - - ${project.groupId} - netty-codec-mqtt - compile - true - - - ${project.groupId} - netty-codec-redis - compile - true - - - ${project.groupId} - netty-codec-smtp - compile - true - - - ${project.groupId} - netty-codec-socks - compile - true - - - ${project.groupId} - netty-codec-stomp - compile - true - - - ${project.groupId} - netty-codec-xml - compile - true - - - ${project.groupId} - netty-common - compile - true - - - ${project.groupId} - netty-handler - compile - true - - - ${project.groupId} - netty-handler-proxy - compile - true - - - ${project.groupId} - netty-resolver - compile - true - - - ${project.groupId} - netty-resolver-dns - compile - true - - - ${project.groupId} - netty-transport - compile - true - - - ${project.groupId} - netty-transport-sctp - compile - true - - - - - - - maven-clean-plugin - - - clean-first - generate-resources - - clean - - - - - - maven-dependency-plugin - - - - locate-dependencies - initialize - - properties - - - - - - unpack-sources - prepare-package - - unpack-dependencies - - - sources - io/netty/** - runtime - ${project.groupId} - ${generatedSourceDir} - - - - - - unpack-jars - prepare-package - - unpack-dependencies - - - io/netty/internal/tcnative/**,io/netty/example/**,META-INF/native/libnetty_tcnative*,META-INF/native/include/**,META-INF/native/**/*.a - io/netty/**,META-INF/native/**,META-INF/native-image/** - runtime - ${project.groupId} - ${project.build.outputDirectory} - - - - - - - maven-antrun-plugin - - - - write-version-properties - none - - - merge-version-properties - prepare-package - - run - - - - - - - - - - - - - - - - - - - - - - - - - clean-source-directory - package - - run - - - - - - - - - - - - - - - org.codehaus.mojo - build-helper-maven-plugin - - - add-source - prepare-package - - add-source - - - - ${generatedSourceDir} - - - - - - - - - org.apache.felix - maven-bundle-plugin - - - generate-manifest - none - - - - - - maven-jar-plugin - - - default-jar - none - - - all-in-one-jar - package - - jar - - - - - true - - - io.netty.all - - true - - - - - - - - - org.codehaus.mojo - animal-sniffer-maven-plugin - - - default - none - - - - - - - maven-checkstyle-plugin - - - check-style - none - - - - - - - maven-resources-plugin - - - default-resources - none - - - default-testResources - none - - - - - maven-compiler-plugin - - - default-compile - none - - - default-testCompile - none - - - - - maven-surefire-plugin - - - default-test - none - - - - - - - diff --git a/bom/pom.xml b/bom/pom.xml index ede1157fe7..23611af5a1 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -16,14 +16,15 @@ --> 4.0.0 - - org.sonatype.oss - oss-parent - 7 - - + + + mchv-snapshot + MCHV Apache Snapshot Maven Packages Distribution + https://mvn.mchv.eu/repository/mchv-snapshot + + - io.netty + io.net5 netty-bom 5.0.0.Final-SNAPSHOT pom @@ -65,265 +66,25 @@ - 2.0.43.Final - io.netty + io.net5 netty-buffer 5.0.0.Final-SNAPSHOT - io.netty - netty-codec - 5.0.0.Final-SNAPSHOT - - - io.netty - netty-codec-dns - 5.0.0.Final-SNAPSHOT - - - io.netty - netty-codec-haproxy - 5.0.0.Final-SNAPSHOT - - - io.netty - netty-codec-http - 5.0.0.Final-SNAPSHOT - - - io.netty - netty-codec-http2 - 5.0.0.Final-SNAPSHOT - - - io.netty - netty-codec-memcache - 5.0.0.Final-SNAPSHOT - - - io.netty - netty-codec-mqtt - 5.0.0.Final-SNAPSHOT - - - io.netty - netty-codec-redis - 5.0.0.Final-SNAPSHOT - - - io.netty - netty-codec-smtp - 5.0.0.Final-SNAPSHOT - - - io.netty - netty-codec-socks - 5.0.0.Final-SNAPSHOT - - - io.netty - netty-codec-stomp - 5.0.0.Final-SNAPSHOT - - - io.netty - netty-codec-xml - 5.0.0.Final-SNAPSHOT - - - io.netty + io.net5 netty-common 5.0.0.Final-SNAPSHOT - io.netty + io.net5 netty-dev-tools 5.0.0.Final-SNAPSHOT - - io.netty - netty-handler - 5.0.0.Final-SNAPSHOT - - - io.netty - netty-handler-proxy - 5.0.0.Final-SNAPSHOT - - - io.netty - netty-resolver - 5.0.0.Final-SNAPSHOT - - - io.netty - netty-resolver-dns - 5.0.0.Final-SNAPSHOT - - - io.netty - netty-transport - 5.0.0.Final-SNAPSHOT - - - io.netty - netty-transport-sctp - 5.0.0.Final-SNAPSHOT - - - io.netty - netty-example - 5.0.0.Final-SNAPSHOT - - - io.netty - netty-all - 5.0.0.Final-SNAPSHOT - - - io.netty - netty-resolver-dns-native-macos - 5.0.0.Final-SNAPSHOT - - - io.netty - netty-resolver-dns-native-macos - 5.0.0.Final-SNAPSHOT - osx-x86_64 - - - io.netty - netty-resolver-dns-native-macos - 5.0.0.Final-SNAPSHOT - osx-aarch_64 - - - io.netty - netty-transport-native-unix-common - 5.0.0.Final-SNAPSHOT - - - io.netty - netty-transport-native-unix-common - 5.0.0.Final-SNAPSHOT - linux-aarch_64 - - - io.netty - netty-transport-native-unix-common - 5.0.0.Final-SNAPSHOT - linux-x86_64 - - - io.netty - netty-transport-native-unix-common - 5.0.0.Final-SNAPSHOT - osx-x86_64 - - - io.netty - netty-transport-native-unix-common - 5.0.0.Final-SNAPSHOT - osx-aarch_64 - - - io.netty - netty-transport-native-epoll - 5.0.0.Final-SNAPSHOT - - - io.netty - netty-transport-native-epoll - 5.0.0.Final-SNAPSHOT - linux-aarch_64 - - - io.netty - netty-transport-native-epoll - 5.0.0.Final-SNAPSHOT - linux-x86_64 - - - io.netty - netty-transport-native-kqueue - 5.0.0.Final-SNAPSHOT - - - io.netty - netty-transport-native-kqueue - 5.0.0.Final-SNAPSHOT - osx-x86_64 - - - io.netty - netty-transport-native-kqueue - 5.0.0.Final-SNAPSHOT - osx-aarch_64 - - - - io.netty - netty-tcnative - ${tcnative.version} - - - io.netty - netty-tcnative - ${tcnative.version} - linux-x86_64 - - - io.netty - netty-tcnative - ${tcnative.version} - linux-aarch_64 - - - io.netty - netty-tcnative - ${tcnative.version} - osx-x86_64 - - - io.netty - netty-tcnative-boringssl-static - ${tcnative.version} - - - io.netty - netty-tcnative-boringssl-static - ${tcnative.version} - linux-x86_64 - - - io.netty - netty-tcnative-boringssl-static - ${tcnative.version} - linux-aarch_64 - - - io.netty - netty-tcnative-boringssl-static - ${tcnative.version} - osx-x86_64 - - - io.netty - netty-tcnative-boringssl-static - ${tcnative.version} - osx-aarch_64 - - - io.netty - netty-tcnative-boringssl-static - ${tcnative.version} - windows-x86_64 - \ No newline at end of file diff --git a/buffer/pom.xml b/buffer/pom.xml index 6c038d750c..7b45562b23 100644 --- a/buffer/pom.xml +++ b/buffer/pom.xml @@ -18,7 +18,7 @@ 4.0.0 - io.netty + io.net5 netty-parent 5.0.0.Final-SNAPSHOT @@ -29,7 +29,7 @@ Netty/Buffer - io.netty.buffer + io.net5.buffer diff --git a/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java b/buffer/src/main/java/io/net5/buffer/AbstractByteBuf.java similarity index 97% rename from buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java rename to buffer/src/main/java/io/net5/buffer/AbstractByteBuf.java index 34e7fd7cbe..8f114760b6 100644 --- a/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java +++ b/buffer/src/main/java/io/net5/buffer/AbstractByteBuf.java @@ -13,18 +13,18 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.AsciiString; -import io.netty.util.ByteProcessor; -import io.netty.util.CharsetUtil; -import io.netty.util.IllegalReferenceCountException; -import io.netty.util.ResourceLeakDetector; -import io.netty.util.ResourceLeakDetectorFactory; -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.SystemPropertyUtil; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; +import io.net5.util.AsciiString; +import io.net5.util.ByteProcessor; +import io.net5.util.CharsetUtil; +import io.net5.util.IllegalReferenceCountException; +import io.net5.util.ResourceLeakDetector; +import io.net5.util.ResourceLeakDetectorFactory; +import io.net5.util.internal.StringUtil; +import io.net5.util.internal.SystemPropertyUtil; +import io.net5.util.internal.logging.InternalLogger; +import io.net5.util.internal.logging.InternalLoggerFactory; import java.io.IOException; import java.io.InputStream; @@ -36,8 +36,8 @@ import java.nio.channels.GatheringByteChannel; import java.nio.channels.ScatteringByteChannel; import java.nio.charset.Charset; -import static io.netty.util.internal.MathUtil.isOutOfBounds; -import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; +import static io.net5.util.internal.MathUtil.isOutOfBounds; +import static io.net5.util.internal.ObjectUtil.checkPositiveOrZero; import static java.util.Objects.requireNonNull; /** @@ -45,9 +45,9 @@ import static java.util.Objects.requireNonNull; */ public abstract class AbstractByteBuf extends ByteBuf { private static final InternalLogger logger = InternalLoggerFactory.getInstance(AbstractByteBuf.class); - private static final String PROP_CHECK_ACCESSIBLE = "io.netty.buffer.checkAccessible"; + private static final String PROP_CHECK_ACCESSIBLE = "io.net5.buffer.checkAccessible"; static final boolean checkAccessible; // accessed from CompositeByteBuf - private static final String PROP_CHECK_BOUNDS = "io.netty.buffer.checkBounds"; + private static final String PROP_CHECK_BOUNDS = "io.net5.buffer.checkBounds"; private static final boolean checkBounds; static { diff --git a/buffer/src/main/java/io/netty/buffer/AbstractByteBufAllocator.java b/buffer/src/main/java/io/net5/buffer/AbstractByteBufAllocator.java similarity index 96% rename from buffer/src/main/java/io/netty/buffer/AbstractByteBufAllocator.java rename to buffer/src/main/java/io/net5/buffer/AbstractByteBufAllocator.java index 3aa05ae480..419f6dae7f 100644 --- a/buffer/src/main/java/io/netty/buffer/AbstractByteBufAllocator.java +++ b/buffer/src/main/java/io/net5/buffer/AbstractByteBufAllocator.java @@ -14,15 +14,15 @@ * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; +import static io.net5.util.internal.ObjectUtil.checkPositiveOrZero; -import io.netty.util.ResourceLeakDetector; -import io.netty.util.ResourceLeakTracker; -import io.netty.util.internal.MathUtil; -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.StringUtil; +import io.net5.util.ResourceLeakDetector; +import io.net5.util.ResourceLeakTracker; +import io.net5.util.internal.MathUtil; +import io.net5.util.internal.PlatformDependent; +import io.net5.util.internal.StringUtil; /** * Skeletal {@link ByteBufAllocator} implementation to extend. diff --git a/buffer/src/main/java/io/netty/buffer/AbstractDerivedByteBuf.java b/buffer/src/main/java/io/net5/buffer/AbstractDerivedByteBuf.java similarity index 99% rename from buffer/src/main/java/io/netty/buffer/AbstractDerivedByteBuf.java rename to buffer/src/main/java/io/net5/buffer/AbstractDerivedByteBuf.java index c3765c8014..530e898765 100644 --- a/buffer/src/main/java/io/netty/buffer/AbstractDerivedByteBuf.java +++ b/buffer/src/main/java/io/net5/buffer/AbstractDerivedByteBuf.java @@ -14,7 +14,7 @@ * under the License. */ -package io.netty.buffer; +package io.net5.buffer; import java.nio.ByteBuffer; diff --git a/buffer/src/main/java/io/netty/buffer/AbstractPooledDerivedByteBuf.java b/buffer/src/main/java/io/net5/buffer/AbstractPooledDerivedByteBuf.java similarity index 99% rename from buffer/src/main/java/io/netty/buffer/AbstractPooledDerivedByteBuf.java rename to buffer/src/main/java/io/net5/buffer/AbstractPooledDerivedByteBuf.java index c143dde072..622f2a7aed 100644 --- a/buffer/src/main/java/io/netty/buffer/AbstractPooledDerivedByteBuf.java +++ b/buffer/src/main/java/io/net5/buffer/AbstractPooledDerivedByteBuf.java @@ -14,9 +14,9 @@ * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.internal.ObjectPool.Handle; +import io.net5.util.internal.ObjectPool.Handle; import java.nio.ByteBuffer; import java.nio.ByteOrder; diff --git a/buffer/src/main/java/io/netty/buffer/AbstractReferenceCountedByteBuf.java b/buffer/src/main/java/io/net5/buffer/AbstractReferenceCountedByteBuf.java similarity index 97% rename from buffer/src/main/java/io/netty/buffer/AbstractReferenceCountedByteBuf.java rename to buffer/src/main/java/io/net5/buffer/AbstractReferenceCountedByteBuf.java index 05751cbef7..ed91c9ff44 100644 --- a/buffer/src/main/java/io/netty/buffer/AbstractReferenceCountedByteBuf.java +++ b/buffer/src/main/java/io/net5/buffer/AbstractReferenceCountedByteBuf.java @@ -14,11 +14,11 @@ * under the License. */ -package io.netty.buffer; +package io.net5.buffer; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; -import io.netty.util.internal.ReferenceCountUpdater; +import io.net5.util.internal.ReferenceCountUpdater; /** * Abstract base class for {@link ByteBuf} implementations that count references. diff --git a/buffer/src/main/java/io/netty/buffer/AbstractUnpooledSlicedByteBuf.java b/buffer/src/main/java/io/net5/buffer/AbstractUnpooledSlicedByteBuf.java similarity index 99% rename from buffer/src/main/java/io/netty/buffer/AbstractUnpooledSlicedByteBuf.java rename to buffer/src/main/java/io/net5/buffer/AbstractUnpooledSlicedByteBuf.java index f1863ff320..463a8a236d 100644 --- a/buffer/src/main/java/io/netty/buffer/AbstractUnpooledSlicedByteBuf.java +++ b/buffer/src/main/java/io/net5/buffer/AbstractUnpooledSlicedByteBuf.java @@ -13,9 +13,9 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.ByteProcessor; +import io.net5.util.ByteProcessor; import java.io.IOException; import java.io.InputStream; @@ -27,7 +27,7 @@ import java.nio.channels.GatheringByteChannel; import java.nio.channels.ScatteringByteChannel; import java.nio.charset.Charset; -import static io.netty.util.internal.MathUtil.isOutOfBounds; +import static io.net5.util.internal.MathUtil.isOutOfBounds; abstract class AbstractUnpooledSlicedByteBuf extends AbstractDerivedByteBuf { private final ByteBuf buffer; diff --git a/buffer/src/main/java/io/netty/buffer/AbstractUnsafeSwappedByteBuf.java b/buffer/src/main/java/io/net5/buffer/AbstractUnsafeSwappedByteBuf.java similarity index 97% rename from buffer/src/main/java/io/netty/buffer/AbstractUnsafeSwappedByteBuf.java rename to buffer/src/main/java/io/net5/buffer/AbstractUnsafeSwappedByteBuf.java index 3ebcef367a..cb814d1bad 100644 --- a/buffer/src/main/java/io/netty/buffer/AbstractUnsafeSwappedByteBuf.java +++ b/buffer/src/main/java/io/net5/buffer/AbstractUnsafeSwappedByteBuf.java @@ -13,13 +13,13 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.internal.PlatformDependent; +import io.net5.util.internal.PlatformDependent; import java.nio.ByteOrder; -import static io.netty.util.internal.PlatformDependent.BIG_ENDIAN_NATIVE_ORDER; +import static io.net5.util.internal.PlatformDependent.BIG_ENDIAN_NATIVE_ORDER; /** * Special {@link SwappedByteBuf} for {@link ByteBuf}s that is using unsafe. diff --git a/buffer/src/main/java/io/netty/buffer/AdvancedLeakAwareByteBuf.java b/buffer/src/main/java/io/net5/buffer/AdvancedLeakAwareByteBuf.java similarity index 98% rename from buffer/src/main/java/io/netty/buffer/AdvancedLeakAwareByteBuf.java rename to buffer/src/main/java/io/net5/buffer/AdvancedLeakAwareByteBuf.java index e8d96d0c91..c2f2d3944a 100644 --- a/buffer/src/main/java/io/netty/buffer/AdvancedLeakAwareByteBuf.java +++ b/buffer/src/main/java/io/net5/buffer/AdvancedLeakAwareByteBuf.java @@ -14,14 +14,14 @@ * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.ByteProcessor; -import io.netty.util.ResourceLeakDetector; -import io.netty.util.ResourceLeakTracker; -import io.netty.util.internal.SystemPropertyUtil; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; +import io.net5.util.ByteProcessor; +import io.net5.util.ResourceLeakDetector; +import io.net5.util.ResourceLeakTracker; +import io.net5.util.internal.SystemPropertyUtil; +import io.net5.util.internal.logging.InternalLogger; +import io.net5.util.internal.logging.InternalLoggerFactory; import java.io.IOException; import java.io.InputStream; @@ -36,7 +36,7 @@ import java.nio.charset.Charset; final class AdvancedLeakAwareByteBuf extends SimpleLeakAwareByteBuf { // If set to true we will only record stacktraces for touch(...), release(...) and retain(...) calls. - private static final String PROP_ACQUIRE_AND_RELEASE_ONLY = "io.netty.leakDetection.acquireAndReleaseOnly"; + private static final String PROP_ACQUIRE_AND_RELEASE_ONLY = "io.net5.leakDetection.acquireAndReleaseOnly"; private static final boolean ACQUIRE_AND_RELEASE_ONLY; private static final InternalLogger logger = InternalLoggerFactory.getInstance(AdvancedLeakAwareByteBuf.class); diff --git a/buffer/src/main/java/io/netty/buffer/AdvancedLeakAwareCompositeByteBuf.java b/buffer/src/main/java/io/net5/buffer/AdvancedLeakAwareCompositeByteBuf.java similarity index 99% rename from buffer/src/main/java/io/netty/buffer/AdvancedLeakAwareCompositeByteBuf.java rename to buffer/src/main/java/io/net5/buffer/AdvancedLeakAwareCompositeByteBuf.java index 3eb404e2fd..c978711d27 100644 --- a/buffer/src/main/java/io/netty/buffer/AdvancedLeakAwareCompositeByteBuf.java +++ b/buffer/src/main/java/io/net5/buffer/AdvancedLeakAwareCompositeByteBuf.java @@ -13,11 +13,11 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.ByteProcessor; -import io.netty.util.ResourceLeakTracker; +import io.net5.util.ByteProcessor; +import io.net5.util.ResourceLeakTracker; import java.io.IOException; import java.io.InputStream; @@ -31,7 +31,7 @@ import java.nio.charset.Charset; import java.util.Iterator; import java.util.List; -import static io.netty.buffer.AdvancedLeakAwareByteBuf.recordLeakNonRefCountingOperation; +import static io.net5.buffer.AdvancedLeakAwareByteBuf.recordLeakNonRefCountingOperation; final class AdvancedLeakAwareCompositeByteBuf extends SimpleLeakAwareCompositeByteBuf { diff --git a/buffer/src/main/java/io/netty/buffer/ByteBuf.java b/buffer/src/main/java/io/net5/buffer/ByteBuf.java similarity index 99% rename from buffer/src/main/java/io/netty/buffer/ByteBuf.java rename to buffer/src/main/java/io/net5/buffer/ByteBuf.java index 2adb7e6438..7e6c836b1b 100644 --- a/buffer/src/main/java/io/netty/buffer/ByteBuf.java +++ b/buffer/src/main/java/io/net5/buffer/ByteBuf.java @@ -13,10 +13,10 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.ByteProcessor; -import io.netty.util.ReferenceCounted; +import io.net5.util.ByteProcessor; +import io.net5.util.ReferenceCounted; import java.io.IOException; import java.io.InputStream; diff --git a/buffer/src/main/java/io/netty/buffer/ByteBufAllocator.java b/buffer/src/main/java/io/net5/buffer/ByteBufAllocator.java similarity index 99% rename from buffer/src/main/java/io/netty/buffer/ByteBufAllocator.java rename to buffer/src/main/java/io/net5/buffer/ByteBufAllocator.java index 802f5c85d3..fede7f702a 100644 --- a/buffer/src/main/java/io/netty/buffer/ByteBufAllocator.java +++ b/buffer/src/main/java/io/net5/buffer/ByteBufAllocator.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; /** * Implementations are responsible to allocate buffers. Implementations of this interface are expected to be diff --git a/buffer/src/main/java/io/netty/buffer/ByteBufAllocatorMetric.java b/buffer/src/main/java/io/net5/buffer/ByteBufAllocatorMetric.java similarity index 97% rename from buffer/src/main/java/io/netty/buffer/ByteBufAllocatorMetric.java rename to buffer/src/main/java/io/net5/buffer/ByteBufAllocatorMetric.java index 7f3ffbdcde..cef499d20d 100644 --- a/buffer/src/main/java/io/netty/buffer/ByteBufAllocatorMetric.java +++ b/buffer/src/main/java/io/net5/buffer/ByteBufAllocatorMetric.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; public interface ByteBufAllocatorMetric { /** diff --git a/buffer/src/main/java/io/netty/buffer/ByteBufAllocatorMetricProvider.java b/buffer/src/main/java/io/net5/buffer/ByteBufAllocatorMetricProvider.java similarity index 97% rename from buffer/src/main/java/io/netty/buffer/ByteBufAllocatorMetricProvider.java rename to buffer/src/main/java/io/net5/buffer/ByteBufAllocatorMetricProvider.java index 84b01848c5..013a1afc0d 100644 --- a/buffer/src/main/java/io/netty/buffer/ByteBufAllocatorMetricProvider.java +++ b/buffer/src/main/java/io/net5/buffer/ByteBufAllocatorMetricProvider.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; public interface ByteBufAllocatorMetricProvider { diff --git a/buffer/src/main/java/io/netty/buffer/ByteBufConvertible.java b/buffer/src/main/java/io/net5/buffer/ByteBufConvertible.java similarity index 98% rename from buffer/src/main/java/io/netty/buffer/ByteBufConvertible.java rename to buffer/src/main/java/io/net5/buffer/ByteBufConvertible.java index b2559f9c3b..5d0c7600d4 100644 --- a/buffer/src/main/java/io/netty/buffer/ByteBufConvertible.java +++ b/buffer/src/main/java/io/net5/buffer/ByteBufConvertible.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; /** * An interface that can be implemented by any object that know how to turn itself into a {@link ByteBuf}. diff --git a/buffer/src/main/java/io/netty/buffer/ByteBufHolder.java b/buffer/src/main/java/io/net5/buffer/ByteBufHolder.java similarity index 96% rename from buffer/src/main/java/io/netty/buffer/ByteBufHolder.java rename to buffer/src/main/java/io/net5/buffer/ByteBufHolder.java index c506dd95a3..11a01f6ad5 100644 --- a/buffer/src/main/java/io/netty/buffer/ByteBufHolder.java +++ b/buffer/src/main/java/io/net5/buffer/ByteBufHolder.java @@ -13,9 +13,9 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.ReferenceCounted; +import io.net5.util.ReferenceCounted; /** * A packet which is send or receive. diff --git a/buffer/src/main/java/io/netty/buffer/ByteBufInputStream.java b/buffer/src/main/java/io/net5/buffer/ByteBufInputStream.java similarity index 98% rename from buffer/src/main/java/io/netty/buffer/ByteBufInputStream.java rename to buffer/src/main/java/io/net5/buffer/ByteBufInputStream.java index c23fb9d91c..bb6549fb6f 100644 --- a/buffer/src/main/java/io/netty/buffer/ByteBufInputStream.java +++ b/buffer/src/main/java/io/net5/buffer/ByteBufInputStream.java @@ -13,13 +13,13 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; import static java.util.Objects.requireNonNull; -import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; +import static io.net5.util.internal.ObjectUtil.checkPositiveOrZero; -import io.netty.util.ReferenceCounted; -import io.netty.util.internal.StringUtil; +import io.net5.util.ReferenceCounted; +import io.net5.util.internal.StringUtil; import java.io.DataInput; import java.io.DataInputStream; diff --git a/buffer/src/main/java/io/netty/buffer/ByteBufOutputStream.java b/buffer/src/main/java/io/net5/buffer/ByteBufOutputStream.java similarity index 98% rename from buffer/src/main/java/io/netty/buffer/ByteBufOutputStream.java rename to buffer/src/main/java/io/net5/buffer/ByteBufOutputStream.java index ff184928cb..47c3da399e 100644 --- a/buffer/src/main/java/io/netty/buffer/ByteBufOutputStream.java +++ b/buffer/src/main/java/io/net5/buffer/ByteBufOutputStream.java @@ -13,11 +13,11 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; import static java.util.Objects.requireNonNull; -import io.netty.util.CharsetUtil; +import io.net5.util.CharsetUtil; import java.io.DataOutput; import java.io.DataOutputStream; diff --git a/buffer/src/main/java/io/netty/buffer/ByteBufProcessor.java b/buffer/src/main/java/io/net5/buffer/ByteBufProcessor.java similarity index 97% rename from buffer/src/main/java/io/netty/buffer/ByteBufProcessor.java rename to buffer/src/main/java/io/net5/buffer/ByteBufProcessor.java index acc917fc06..b1edd78878 100644 --- a/buffer/src/main/java/io/netty/buffer/ByteBufProcessor.java +++ b/buffer/src/main/java/io/net5/buffer/ByteBufProcessor.java @@ -14,9 +14,9 @@ * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.ByteProcessor; +import io.net5.util.ByteProcessor; /** * @deprecated Use {@link ByteProcessor}. diff --git a/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java b/buffer/src/main/java/io/net5/buffer/ByteBufUtil.java similarity index 98% rename from buffer/src/main/java/io/netty/buffer/ByteBufUtil.java rename to buffer/src/main/java/io/net5/buffer/ByteBufUtil.java index 68f1737b12..7585d3195a 100644 --- a/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java +++ b/buffer/src/main/java/io/net5/buffer/ByteBufUtil.java @@ -13,21 +13,21 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.AsciiString; -import io.netty.util.ByteProcessor; -import io.netty.util.CharsetUtil; -import io.netty.util.IllegalReferenceCountException; -import io.netty.util.concurrent.FastThreadLocal; -import io.netty.util.internal.MathUtil; -import io.netty.util.internal.ObjectPool; -import io.netty.util.internal.ObjectPool.Handle; -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.SystemPropertyUtil; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; +import io.net5.util.AsciiString; +import io.net5.util.ByteProcessor; +import io.net5.util.CharsetUtil; +import io.net5.util.IllegalReferenceCountException; +import io.net5.util.concurrent.FastThreadLocal; +import io.net5.util.internal.MathUtil; +import io.net5.util.internal.ObjectPool; +import io.net5.util.internal.ObjectPool.Handle; +import io.net5.util.internal.PlatformDependent; +import io.net5.util.internal.StringUtil; +import io.net5.util.internal.SystemPropertyUtil; +import io.net5.util.internal.logging.InternalLogger; +import io.net5.util.internal.logging.InternalLoggerFactory; import java.io.IOException; import java.io.OutputStream; @@ -43,10 +43,10 @@ import java.nio.charset.CodingErrorAction; import java.util.Arrays; import java.util.Locale; -import static io.netty.util.internal.MathUtil.isOutOfBounds; -import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; -import static io.netty.util.internal.StringUtil.NEWLINE; -import static io.netty.util.internal.StringUtil.isSurrogate; +import static io.net5.util.internal.MathUtil.isOutOfBounds; +import static io.net5.util.internal.ObjectUtil.checkPositiveOrZero; +import static io.net5.util.internal.StringUtil.NEWLINE; +import static io.net5.util.internal.StringUtil.isSurrogate; import static java.util.Objects.requireNonNull; /** @@ -74,28 +74,28 @@ public final class ByteBufUtil { static { String allocType = SystemPropertyUtil.get( - "io.netty.allocator.type", PlatformDependent.isAndroid() ? "unpooled" : "pooled"); + "io.net5.allocator.type", PlatformDependent.isAndroid() ? "unpooled" : "pooled"); allocType = allocType.toLowerCase(Locale.US).trim(); ByteBufAllocator alloc; if ("unpooled".equals(allocType)) { alloc = UnpooledByteBufAllocator.DEFAULT; - logger.debug("-Dio.netty.allocator.type: {}", allocType); + logger.debug("-Dio.net5.allocator.type: {}", allocType); } else if ("pooled".equals(allocType)) { alloc = PooledByteBufAllocator.DEFAULT; - logger.debug("-Dio.netty.allocator.type: {}", allocType); + logger.debug("-Dio.net5.allocator.type: {}", allocType); } else { alloc = PooledByteBufAllocator.DEFAULT; - logger.debug("-Dio.netty.allocator.type: pooled (unknown: {})", allocType); + logger.debug("-Dio.net5.allocator.type: pooled (unknown: {})", allocType); } DEFAULT_ALLOCATOR = alloc; - THREAD_LOCAL_BUFFER_SIZE = SystemPropertyUtil.getInt("io.netty.threadLocalDirectBufferSize", 0); - logger.debug("-Dio.netty.threadLocalDirectBufferSize: {}", THREAD_LOCAL_BUFFER_SIZE); + THREAD_LOCAL_BUFFER_SIZE = SystemPropertyUtil.getInt("io.net5.threadLocalDirectBufferSize", 0); + logger.debug("-Dio.net5.threadLocalDirectBufferSize: {}", THREAD_LOCAL_BUFFER_SIZE); - MAX_CHAR_BUFFER_SIZE = SystemPropertyUtil.getInt("io.netty.maxThreadLocalCharBufferSize", 16 * 1024); - logger.debug("-Dio.netty.maxThreadLocalCharBufferSize: {}", MAX_CHAR_BUFFER_SIZE); + MAX_CHAR_BUFFER_SIZE = SystemPropertyUtil.getInt("io.net5.maxThreadLocalCharBufferSize", 16 * 1024); + logger.debug("-Dio.net5.maxThreadLocalCharBufferSize: {}", MAX_CHAR_BUFFER_SIZE); } static final int MAX_TL_ARRAY_LEN = 1024; diff --git a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java b/buffer/src/main/java/io/net5/buffer/CompositeByteBuf.java similarity index 99% rename from buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java rename to buffer/src/main/java/io/net5/buffer/CompositeByteBuf.java index 8a32dd27ac..15e56adcf7 100644 --- a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java +++ b/buffer/src/main/java/io/net5/buffer/CompositeByteBuf.java @@ -13,15 +13,15 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; import static java.util.Objects.requireNonNull; -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 io.net5.util.ByteProcessor; +import io.net5.util.IllegalReferenceCountException; +import io.net5.util.ReferenceCountUtil; +import io.net5.util.internal.EmptyArrays; +import io.net5.util.internal.RecyclableArrayList; import java.io.IOException; import java.io.InputStream; diff --git a/buffer/src/main/java/io/netty/buffer/DefaultByteBufHolder.java b/buffer/src/main/java/io/net5/buffer/DefaultByteBufHolder.java similarity index 98% rename from buffer/src/main/java/io/netty/buffer/DefaultByteBufHolder.java rename to buffer/src/main/java/io/net5/buffer/DefaultByteBufHolder.java index da3fcf9283..7e55663371 100644 --- a/buffer/src/main/java/io/netty/buffer/DefaultByteBufHolder.java +++ b/buffer/src/main/java/io/net5/buffer/DefaultByteBufHolder.java @@ -13,11 +13,11 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; import static java.util.Objects.requireNonNull; -import io.netty.util.internal.StringUtil; +import io.net5.util.internal.StringUtil; /** * Default implementation of a {@link ByteBufHolder} that holds it's data in a {@link ByteBuf}. diff --git a/buffer/src/main/java/io/netty/buffer/DuplicatedByteBuf.java b/buffer/src/main/java/io/net5/buffer/DuplicatedByteBuf.java similarity index 99% rename from buffer/src/main/java/io/netty/buffer/DuplicatedByteBuf.java rename to buffer/src/main/java/io/net5/buffer/DuplicatedByteBuf.java index 50f8461150..40973ee070 100644 --- a/buffer/src/main/java/io/netty/buffer/DuplicatedByteBuf.java +++ b/buffer/src/main/java/io/net5/buffer/DuplicatedByteBuf.java @@ -13,9 +13,9 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.ByteProcessor; +import io.net5.util.ByteProcessor; import java.io.IOException; import java.io.InputStream; diff --git a/buffer/src/main/java/io/netty/buffer/EmptyByteBuf.java b/buffer/src/main/java/io/net5/buffer/EmptyByteBuf.java similarity index 98% rename from buffer/src/main/java/io/netty/buffer/EmptyByteBuf.java rename to buffer/src/main/java/io/net5/buffer/EmptyByteBuf.java index 93fdc58a9d..5d1d073969 100644 --- a/buffer/src/main/java/io/netty/buffer/EmptyByteBuf.java +++ b/buffer/src/main/java/io/net5/buffer/EmptyByteBuf.java @@ -14,15 +14,15 @@ * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; +import static io.net5.util.internal.ObjectUtil.checkPositiveOrZero; import static java.util.Objects.requireNonNull; -import io.netty.util.ByteProcessor; -import io.netty.util.internal.EmptyArrays; -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.StringUtil; +import io.net5.util.ByteProcessor; +import io.net5.util.internal.EmptyArrays; +import io.net5.util.internal.PlatformDependent; +import io.net5.util.internal.StringUtil; import java.io.InputStream; import java.io.OutputStream; diff --git a/buffer/src/main/java/io/netty/buffer/FixedCompositeByteBuf.java b/buffer/src/main/java/io/net5/buffer/FixedCompositeByteBuf.java similarity index 99% rename from buffer/src/main/java/io/netty/buffer/FixedCompositeByteBuf.java rename to buffer/src/main/java/io/net5/buffer/FixedCompositeByteBuf.java index a7b39907e2..3d0966fdd4 100644 --- a/buffer/src/main/java/io/netty/buffer/FixedCompositeByteBuf.java +++ b/buffer/src/main/java/io/net5/buffer/FixedCompositeByteBuf.java @@ -13,10 +13,10 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.internal.EmptyArrays; -import io.netty.util.internal.RecyclableArrayList; +import io.net5.util.internal.EmptyArrays; +import io.net5.util.internal.RecyclableArrayList; import java.io.IOException; import java.io.InputStream; diff --git a/buffer/src/main/java/io/netty/buffer/HeapByteBufUtil.java b/buffer/src/main/java/io/net5/buffer/HeapByteBufUtil.java similarity index 99% rename from buffer/src/main/java/io/netty/buffer/HeapByteBufUtil.java rename to buffer/src/main/java/io/net5/buffer/HeapByteBufUtil.java index 9f7972a273..8c68f299a4 100644 --- a/buffer/src/main/java/io/netty/buffer/HeapByteBufUtil.java +++ b/buffer/src/main/java/io/net5/buffer/HeapByteBufUtil.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; /** * Utility class for heap buffers. diff --git a/buffer/src/main/java/io/netty/buffer/PoolArena.java b/buffer/src/main/java/io/net5/buffer/PoolArena.java similarity index 99% rename from buffer/src/main/java/io/netty/buffer/PoolArena.java rename to buffer/src/main/java/io/net5/buffer/PoolArena.java index 1e336ce8e9..38b5609338 100644 --- a/buffer/src/main/java/io/netty/buffer/PoolArena.java +++ b/buffer/src/main/java/io/net5/buffer/PoolArena.java @@ -14,10 +14,10 @@ * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.StringUtil; +import io.net5.util.internal.PlatformDependent; +import io.net5.util.internal.StringUtil; import java.nio.ByteBuffer; import java.util.ArrayList; @@ -26,7 +26,7 @@ import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.LongAdder; -import static io.netty.buffer.PoolChunk.isSubpage; +import static io.net5.buffer.PoolChunk.isSubpage; import static java.lang.Math.max; abstract class PoolArena extends SizeClasses implements PoolArenaMetric { diff --git a/buffer/src/main/java/io/netty/buffer/PoolArenaMetric.java b/buffer/src/main/java/io/net5/buffer/PoolArenaMetric.java similarity index 99% rename from buffer/src/main/java/io/netty/buffer/PoolArenaMetric.java rename to buffer/src/main/java/io/net5/buffer/PoolArenaMetric.java index b11a3c4f1a..96069d82af 100644 --- a/buffer/src/main/java/io/netty/buffer/PoolArenaMetric.java +++ b/buffer/src/main/java/io/net5/buffer/PoolArenaMetric.java @@ -14,7 +14,7 @@ * under the License. */ -package io.netty.buffer; +package io.net5.buffer; import java.util.List; diff --git a/buffer/src/main/java/io/netty/buffer/PoolChunk.java b/buffer/src/main/java/io/net5/buffer/PoolChunk.java similarity index 99% rename from buffer/src/main/java/io/netty/buffer/PoolChunk.java rename to buffer/src/main/java/io/net5/buffer/PoolChunk.java index 9b84803794..71e56059de 100644 --- a/buffer/src/main/java/io/netty/buffer/PoolChunk.java +++ b/buffer/src/main/java/io/net5/buffer/PoolChunk.java @@ -13,10 +13,10 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.internal.LongLongHashMap; -import io.netty.util.internal.LongPriorityQueue; +import io.net5.util.internal.LongLongHashMap; +import io.net5.util.internal.LongPriorityQueue; import java.nio.ByteBuffer; import java.util.ArrayDeque; diff --git a/buffer/src/main/java/io/netty/buffer/PoolChunkList.java b/buffer/src/main/java/io/net5/buffer/PoolChunkList.java similarity index 99% rename from buffer/src/main/java/io/netty/buffer/PoolChunkList.java rename to buffer/src/main/java/io/net5/buffer/PoolChunkList.java index d0c38f6c1f..c82b98612f 100644 --- a/buffer/src/main/java/io/netty/buffer/PoolChunkList.java +++ b/buffer/src/main/java/io/net5/buffer/PoolChunkList.java @@ -14,9 +14,9 @@ * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.internal.StringUtil; +import io.net5.util.internal.StringUtil; import java.util.ArrayList; import java.util.Collections; diff --git a/buffer/src/main/java/io/netty/buffer/PoolChunkListMetric.java b/buffer/src/main/java/io/net5/buffer/PoolChunkListMetric.java similarity index 97% rename from buffer/src/main/java/io/netty/buffer/PoolChunkListMetric.java rename to buffer/src/main/java/io/net5/buffer/PoolChunkListMetric.java index ec45561e82..74435dd159 100644 --- a/buffer/src/main/java/io/netty/buffer/PoolChunkListMetric.java +++ b/buffer/src/main/java/io/net5/buffer/PoolChunkListMetric.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; /** * Metrics for a list of chunks. diff --git a/buffer/src/main/java/io/netty/buffer/PoolChunkMetric.java b/buffer/src/main/java/io/net5/buffer/PoolChunkMetric.java similarity index 97% rename from buffer/src/main/java/io/netty/buffer/PoolChunkMetric.java rename to buffer/src/main/java/io/net5/buffer/PoolChunkMetric.java index a006785d22..a33546e5ef 100644 --- a/buffer/src/main/java/io/netty/buffer/PoolChunkMetric.java +++ b/buffer/src/main/java/io/net5/buffer/PoolChunkMetric.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; /** * Metrics for a chunk. diff --git a/buffer/src/main/java/io/netty/buffer/PoolSubpage.java b/buffer/src/main/java/io/net5/buffer/PoolSubpage.java similarity index 96% rename from buffer/src/main/java/io/netty/buffer/PoolSubpage.java rename to buffer/src/main/java/io/net5/buffer/PoolSubpage.java index 62e905c4d0..3f20aeca70 100644 --- a/buffer/src/main/java/io/netty/buffer/PoolSubpage.java +++ b/buffer/src/main/java/io/net5/buffer/PoolSubpage.java @@ -14,13 +14,13 @@ * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import static io.netty.buffer.PoolChunk.RUN_OFFSET_SHIFT; -import static io.netty.buffer.PoolChunk.SIZE_SHIFT; -import static io.netty.buffer.PoolChunk.IS_USED_SHIFT; -import static io.netty.buffer.PoolChunk.IS_SUBPAGE_SHIFT; -import static io.netty.buffer.SizeClasses.LOG2_QUANTUM; +import static io.net5.buffer.PoolChunk.RUN_OFFSET_SHIFT; +import static io.net5.buffer.PoolChunk.SIZE_SHIFT; +import static io.net5.buffer.PoolChunk.IS_USED_SHIFT; +import static io.net5.buffer.PoolChunk.IS_SUBPAGE_SHIFT; +import static io.net5.buffer.SizeClasses.LOG2_QUANTUM; final class PoolSubpage implements PoolSubpageMetric { diff --git a/buffer/src/main/java/io/netty/buffer/PoolSubpageMetric.java b/buffer/src/main/java/io/net5/buffer/PoolSubpageMetric.java similarity index 97% rename from buffer/src/main/java/io/netty/buffer/PoolSubpageMetric.java rename to buffer/src/main/java/io/net5/buffer/PoolSubpageMetric.java index c0102737d6..f23fbe945d 100644 --- a/buffer/src/main/java/io/netty/buffer/PoolSubpageMetric.java +++ b/buffer/src/main/java/io/net5/buffer/PoolSubpageMetric.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; /** * Metrics for a sub-page. diff --git a/buffer/src/main/java/io/netty/buffer/PoolThreadCache.java b/buffer/src/main/java/io/net5/buffer/PoolThreadCache.java similarity index 97% rename from buffer/src/main/java/io/netty/buffer/PoolThreadCache.java rename to buffer/src/main/java/io/net5/buffer/PoolThreadCache.java index 9e7fde8a0d..88726a13b2 100644 --- a/buffer/src/main/java/io/netty/buffer/PoolThreadCache.java +++ b/buffer/src/main/java/io/net5/buffer/PoolThreadCache.java @@ -14,18 +14,18 @@ * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; +import static io.net5.util.internal.ObjectUtil.checkPositiveOrZero; -import io.netty.buffer.PoolArena.SizeClass; -import io.netty.util.internal.MathUtil; -import io.netty.util.internal.ObjectPool; -import io.netty.util.internal.ObjectPool.Handle; -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; +import io.net5.buffer.PoolArena.SizeClass; +import io.net5.util.internal.MathUtil; +import io.net5.util.internal.ObjectPool; +import io.net5.util.internal.ObjectPool.Handle; +import io.net5.util.internal.PlatformDependent; +import io.net5.util.internal.logging.InternalLogger; +import io.net5.util.internal.logging.InternalLoggerFactory; import java.nio.ByteBuffer; import java.util.ArrayList; diff --git a/buffer/src/main/java/io/netty/buffer/PooledByteBuf.java b/buffer/src/main/java/io/net5/buffer/PooledByteBuf.java similarity index 99% rename from buffer/src/main/java/io/netty/buffer/PooledByteBuf.java rename to buffer/src/main/java/io/net5/buffer/PooledByteBuf.java index 99d601f497..4a3f1b339f 100644 --- a/buffer/src/main/java/io/netty/buffer/PooledByteBuf.java +++ b/buffer/src/main/java/io/net5/buffer/PooledByteBuf.java @@ -14,9 +14,9 @@ * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.internal.ObjectPool.Handle; +import io.net5.util.internal.ObjectPool.Handle; import java.io.IOException; import java.nio.ByteBuffer; diff --git a/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java b/buffer/src/main/java/io/net5/buffer/PooledByteBufAllocator.java similarity index 88% rename from buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java rename to buffer/src/main/java/io/net5/buffer/PooledByteBufAllocator.java index a6caa66f20..992c9f2eb5 100644 --- a/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java +++ b/buffer/src/main/java/io/net5/buffer/PooledByteBufAllocator.java @@ -14,20 +14,20 @@ * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; +import static io.net5.util.internal.ObjectUtil.checkPositiveOrZero; -import io.netty.util.NettyRuntime; -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.FastThreadLocal; -import io.netty.util.concurrent.FastThreadLocalThread; -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.SystemPropertyUtil; -import io.netty.util.internal.ThreadExecutorMap; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; +import io.net5.util.NettyRuntime; +import io.net5.util.concurrent.EventExecutor; +import io.net5.util.concurrent.FastThreadLocal; +import io.net5.util.concurrent.FastThreadLocalThread; +import io.net5.util.internal.PlatformDependent; +import io.net5.util.internal.StringUtil; +import io.net5.util.internal.SystemPropertyUtil; +import io.net5.util.internal.ThreadExecutorMap; +import io.net5.util.internal.logging.InternalLogger; +import io.net5.util.internal.logging.InternalLoggerFactory; import java.nio.ByteBuffer; import java.util.ArrayList; @@ -59,8 +59,8 @@ public class PooledByteBufAllocator extends AbstractByteBufAllocator implements static { int defaultAlignment = SystemPropertyUtil.getInt( - "io.netty.allocator.directMemoryCacheAlignment", 0); - int defaultPageSize = SystemPropertyUtil.getInt("io.netty.allocator.pageSize", 8192); + "io.net5.allocator.directMemoryCacheAlignment", 0); + int defaultPageSize = SystemPropertyUtil.getInt("io.net5.allocator.pageSize", 8192); Throwable pageSizeFallbackCause = null; try { validateAndCalculatePageShifts(defaultPageSize, defaultAlignment); @@ -72,7 +72,7 @@ public class PooledByteBufAllocator extends AbstractByteBufAllocator implements DEFAULT_PAGE_SIZE = defaultPageSize; DEFAULT_DIRECT_MEMORY_CACHE_ALIGNMENT = defaultAlignment; - int defaultMaxOrder = SystemPropertyUtil.getInt("io.netty.allocator.maxOrder", 11); + int defaultMaxOrder = SystemPropertyUtil.getInt("io.net5.allocator.maxOrder", 11); Throwable maxOrderFallbackCause = null; try { validateAndCalculateChunkSize(DEFAULT_PAGE_SIZE, defaultMaxOrder); @@ -97,62 +97,62 @@ public class PooledByteBufAllocator extends AbstractByteBufAllocator implements final int defaultChunkSize = DEFAULT_PAGE_SIZE << DEFAULT_MAX_ORDER; DEFAULT_NUM_HEAP_ARENA = Math.max(0, SystemPropertyUtil.getInt( - "io.netty.allocator.numHeapArenas", + "io.net5.allocator.numHeapArenas", (int) Math.min( defaultMinNumArena, runtime.maxMemory() / defaultChunkSize / 2 / 3))); DEFAULT_NUM_DIRECT_ARENA = Math.max(0, SystemPropertyUtil.getInt( - "io.netty.allocator.numDirectArenas", + "io.net5.allocator.numDirectArenas", (int) Math.min( defaultMinNumArena, PlatformDependent.maxDirectMemory() / defaultChunkSize / 2 / 3))); // cache sizes - DEFAULT_SMALL_CACHE_SIZE = SystemPropertyUtil.getInt("io.netty.allocator.smallCacheSize", 256); - DEFAULT_NORMAL_CACHE_SIZE = SystemPropertyUtil.getInt("io.netty.allocator.normalCacheSize", 64); + DEFAULT_SMALL_CACHE_SIZE = SystemPropertyUtil.getInt("io.net5.allocator.smallCacheSize", 256); + DEFAULT_NORMAL_CACHE_SIZE = SystemPropertyUtil.getInt("io.net5.allocator.normalCacheSize", 64); // 32 kb is the default maximum capacity of the cached buffer. Similar to what is explained in // 'Scalable memory allocation using jemalloc' DEFAULT_MAX_CACHED_BUFFER_CAPACITY = SystemPropertyUtil.getInt( - "io.netty.allocator.maxCachedBufferCapacity", 32 * 1024); + "io.net5.allocator.maxCachedBufferCapacity", 32 * 1024); // the number of threshold of allocations when cached entries will be freed up if not frequently used DEFAULT_CACHE_TRIM_INTERVAL = SystemPropertyUtil.getInt( - "io.netty.allocator.cacheTrimInterval", 8192); + "io.net5.allocator.cacheTrimInterval", 8192); DEFAULT_CACHE_TRIM_INTERVAL_MILLIS = SystemPropertyUtil.getLong( - "io.netty.allocator.cacheTrimIntervalMillis", 0); + "io.net5.allocator.cacheTrimIntervalMillis", 0); DEFAULT_USE_CACHE_FOR_ALL_THREADS = SystemPropertyUtil.getBoolean( - "io.netty.allocator.useCacheForAllThreads", false); + "io.net5.allocator.useCacheForAllThreads", false); // 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); + "io.net5.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); + logger.debug("-Dio.net5.allocator.numHeapArenas: {}", DEFAULT_NUM_HEAP_ARENA); + logger.debug("-Dio.net5.allocator.numDirectArenas: {}", DEFAULT_NUM_DIRECT_ARENA); if (pageSizeFallbackCause == null) { - logger.debug("-Dio.netty.allocator.pageSize: {}", DEFAULT_PAGE_SIZE); + logger.debug("-Dio.net5.allocator.pageSize: {}", DEFAULT_PAGE_SIZE); } else { - logger.debug("-Dio.netty.allocator.pageSize: {}", DEFAULT_PAGE_SIZE, pageSizeFallbackCause); + logger.debug("-Dio.net5.allocator.pageSize: {}", DEFAULT_PAGE_SIZE, pageSizeFallbackCause); } if (maxOrderFallbackCause == null) { - logger.debug("-Dio.netty.allocator.maxOrder: {}", DEFAULT_MAX_ORDER); + logger.debug("-Dio.net5.allocator.maxOrder: {}", DEFAULT_MAX_ORDER); } else { - logger.debug("-Dio.netty.allocator.maxOrder: {}", DEFAULT_MAX_ORDER, maxOrderFallbackCause); + logger.debug("-Dio.net5.allocator.maxOrder: {}", DEFAULT_MAX_ORDER, maxOrderFallbackCause); } - logger.debug("-Dio.netty.allocator.chunkSize: {}", DEFAULT_PAGE_SIZE << DEFAULT_MAX_ORDER); - logger.debug("-Dio.netty.allocator.smallCacheSize: {}", DEFAULT_SMALL_CACHE_SIZE); - logger.debug("-Dio.netty.allocator.normalCacheSize: {}", DEFAULT_NORMAL_CACHE_SIZE); - 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.cacheTrimIntervalMillis: {}", DEFAULT_CACHE_TRIM_INTERVAL_MILLIS); - logger.debug("-Dio.netty.allocator.useCacheForAllThreads: {}", DEFAULT_USE_CACHE_FOR_ALL_THREADS); - logger.debug("-Dio.netty.allocator.maxCachedByteBuffersPerChunk: {}", + logger.debug("-Dio.net5.allocator.chunkSize: {}", DEFAULT_PAGE_SIZE << DEFAULT_MAX_ORDER); + logger.debug("-Dio.net5.allocator.smallCacheSize: {}", DEFAULT_SMALL_CACHE_SIZE); + logger.debug("-Dio.net5.allocator.normalCacheSize: {}", DEFAULT_NORMAL_CACHE_SIZE); + logger.debug("-Dio.net5.allocator.maxCachedBufferCapacity: {}", DEFAULT_MAX_CACHED_BUFFER_CAPACITY); + logger.debug("-Dio.net5.allocator.cacheTrimInterval: {}", DEFAULT_CACHE_TRIM_INTERVAL); + logger.debug("-Dio.net5.allocator.cacheTrimIntervalMillis: {}", DEFAULT_CACHE_TRIM_INTERVAL_MILLIS); + logger.debug("-Dio.net5.allocator.useCacheForAllThreads: {}", DEFAULT_USE_CACHE_FOR_ALL_THREADS); + logger.debug("-Dio.net5.allocator.maxCachedByteBuffersPerChunk: {}", DEFAULT_MAX_CACHED_BYTEBUFFERS_PER_CHUNK); } } @@ -384,42 +384,42 @@ public class PooledByteBufAllocator extends AbstractByteBufAllocator implements } /** - * Default number of heap arenas - System Property: io.netty.allocator.numHeapArenas - default 2 * cores + * Default number of heap arenas - System Property: io.net5.allocator.numHeapArenas - default 2 * cores */ public static int defaultNumHeapArena() { return DEFAULT_NUM_HEAP_ARENA; } /** - * Default number of direct arenas - System Property: io.netty.allocator.numDirectArenas - default 2 * cores + * Default number of direct arenas - System Property: io.net5.allocator.numDirectArenas - default 2 * cores */ public static int defaultNumDirectArena() { return DEFAULT_NUM_DIRECT_ARENA; } /** - * Default buffer page size - System Property: io.netty.allocator.pageSize - default 8192 + * Default buffer page size - System Property: io.net5.allocator.pageSize - default 8192 */ public static int defaultPageSize() { return DEFAULT_PAGE_SIZE; } /** - * Default maximum order - System Property: io.netty.allocator.maxOrder - default 11 + * Default maximum order - System Property: io.net5.allocator.maxOrder - default 11 */ public static int defaultMaxOrder() { return DEFAULT_MAX_ORDER; } /** - * Default thread caching behavior - System Property: io.netty.allocator.useCacheForAllThreads - default true + * Default thread caching behavior - System Property: io.net5.allocator.useCacheForAllThreads - default true */ public static boolean defaultUseCacheForAllThreads() { return DEFAULT_USE_CACHE_FOR_ALL_THREADS; } /** - * Default prefer direct - System Property: io.netty.noPreferDirect - default false + * Default prefer direct - System Property: io.net5.noPreferDirect - default false */ public static boolean defaultPreferDirect() { return PlatformDependent.directBufferPreferred(); @@ -436,14 +436,14 @@ public class PooledByteBufAllocator extends AbstractByteBufAllocator implements } /** - * Default small cache size - System Property: io.netty.allocator.smallCacheSize - default 256 + * Default small cache size - System Property: io.net5.allocator.smallCacheSize - default 256 */ public static int defaultSmallCacheSize() { return DEFAULT_SMALL_CACHE_SIZE; } /** - * Default normal cache size - System Property: io.netty.allocator.normalCacheSize - default 64 + * Default normal cache size - System Property: io.net5.allocator.normalCacheSize - default 64 */ public static int defaultNormalCacheSize() { return DEFAULT_NORMAL_CACHE_SIZE; diff --git a/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocatorMetric.java b/buffer/src/main/java/io/net5/buffer/PooledByteBufAllocatorMetric.java similarity index 98% rename from buffer/src/main/java/io/netty/buffer/PooledByteBufAllocatorMetric.java rename to buffer/src/main/java/io/net5/buffer/PooledByteBufAllocatorMetric.java index f20dfff5bc..52508325ab 100644 --- a/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocatorMetric.java +++ b/buffer/src/main/java/io/net5/buffer/PooledByteBufAllocatorMetric.java @@ -13,9 +13,9 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.internal.StringUtil; +import io.net5.util.internal.StringUtil; import java.util.List; diff --git a/buffer/src/main/java/io/netty/buffer/PooledDirectByteBuf.java b/buffer/src/main/java/io/net5/buffer/PooledDirectByteBuf.java similarity index 98% rename from buffer/src/main/java/io/netty/buffer/PooledDirectByteBuf.java rename to buffer/src/main/java/io/net5/buffer/PooledDirectByteBuf.java index 5509c5dbc7..5b122ea5da 100644 --- a/buffer/src/main/java/io/netty/buffer/PooledDirectByteBuf.java +++ b/buffer/src/main/java/io/net5/buffer/PooledDirectByteBuf.java @@ -14,10 +14,10 @@ * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.internal.ObjectPool; -import io.netty.util.internal.ObjectPool.Handle; +import io.net5.util.internal.ObjectPool; +import io.net5.util.internal.ObjectPool.Handle; import java.io.IOException; import java.io.InputStream; diff --git a/buffer/src/main/java/io/netty/buffer/PooledDuplicatedByteBuf.java b/buffer/src/main/java/io/net5/buffer/PooledDuplicatedByteBuf.java similarity index 98% rename from buffer/src/main/java/io/netty/buffer/PooledDuplicatedByteBuf.java rename to buffer/src/main/java/io/net5/buffer/PooledDuplicatedByteBuf.java index 7769a8d3a0..fb7eb591d5 100644 --- a/buffer/src/main/java/io/netty/buffer/PooledDuplicatedByteBuf.java +++ b/buffer/src/main/java/io/net5/buffer/PooledDuplicatedByteBuf.java @@ -14,11 +14,11 @@ * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.ByteProcessor; -import io.netty.util.internal.ObjectPool; -import io.netty.util.internal.ObjectPool.Handle; +import io.net5.util.ByteProcessor; +import io.net5.util.internal.ObjectPool; +import io.net5.util.internal.ObjectPool.Handle; import java.io.IOException; import java.io.InputStream; diff --git a/buffer/src/main/java/io/netty/buffer/PooledHeapByteBuf.java b/buffer/src/main/java/io/net5/buffer/PooledHeapByteBuf.java similarity index 97% rename from buffer/src/main/java/io/netty/buffer/PooledHeapByteBuf.java rename to buffer/src/main/java/io/net5/buffer/PooledHeapByteBuf.java index 5cd768a688..9abb588607 100644 --- a/buffer/src/main/java/io/netty/buffer/PooledHeapByteBuf.java +++ b/buffer/src/main/java/io/net5/buffer/PooledHeapByteBuf.java @@ -12,11 +12,11 @@ * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.internal.ObjectPool; -import io.netty.util.internal.ObjectPool.Handle; -import io.netty.util.internal.PlatformDependent; +import io.net5.util.internal.ObjectPool; +import io.net5.util.internal.ObjectPool.Handle; +import io.net5.util.internal.PlatformDependent; import java.io.IOException; import java.io.InputStream; diff --git a/buffer/src/main/java/io/netty/buffer/PooledSlicedByteBuf.java b/buffer/src/main/java/io/net5/buffer/PooledSlicedByteBuf.java similarity index 98% rename from buffer/src/main/java/io/netty/buffer/PooledSlicedByteBuf.java rename to buffer/src/main/java/io/net5/buffer/PooledSlicedByteBuf.java index a35ab9b03c..71adc8f42d 100644 --- a/buffer/src/main/java/io/netty/buffer/PooledSlicedByteBuf.java +++ b/buffer/src/main/java/io/net5/buffer/PooledSlicedByteBuf.java @@ -14,11 +14,11 @@ * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.ByteProcessor; -import io.netty.util.internal.ObjectPool; -import io.netty.util.internal.ObjectPool.Handle; +import io.net5.util.ByteProcessor; +import io.net5.util.internal.ObjectPool; +import io.net5.util.internal.ObjectPool.Handle; import java.io.IOException; import java.io.InputStream; @@ -28,7 +28,7 @@ import java.nio.channels.FileChannel; import java.nio.channels.GatheringByteChannel; import java.nio.channels.ScatteringByteChannel; -import static io.netty.buffer.AbstractUnpooledSlicedByteBuf.checkSliceOutOfBounds; +import static io.net5.buffer.AbstractUnpooledSlicedByteBuf.checkSliceOutOfBounds; final class PooledSlicedByteBuf extends AbstractPooledDerivedByteBuf { diff --git a/buffer/src/main/java/io/netty/buffer/PooledUnsafeDirectByteBuf.java b/buffer/src/main/java/io/net5/buffer/PooledUnsafeDirectByteBuf.java similarity index 97% rename from buffer/src/main/java/io/netty/buffer/PooledUnsafeDirectByteBuf.java rename to buffer/src/main/java/io/net5/buffer/PooledUnsafeDirectByteBuf.java index 8ebb772c87..9db2073bf1 100644 --- a/buffer/src/main/java/io/netty/buffer/PooledUnsafeDirectByteBuf.java +++ b/buffer/src/main/java/io/net5/buffer/PooledUnsafeDirectByteBuf.java @@ -14,11 +14,11 @@ * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.internal.ObjectPool; -import io.netty.util.internal.ObjectPool.Handle; -import io.netty.util.internal.PlatformDependent; +import io.net5.util.internal.ObjectPool; +import io.net5.util.internal.ObjectPool.Handle; +import io.net5.util.internal.PlatformDependent; import java.io.IOException; import java.io.InputStream; diff --git a/buffer/src/main/java/io/netty/buffer/PooledUnsafeHeapByteBuf.java b/buffer/src/main/java/io/net5/buffer/PooledUnsafeHeapByteBuf.java similarity index 96% rename from buffer/src/main/java/io/netty/buffer/PooledUnsafeHeapByteBuf.java rename to buffer/src/main/java/io/net5/buffer/PooledUnsafeHeapByteBuf.java index 4c04eac20d..bd62421443 100644 --- a/buffer/src/main/java/io/netty/buffer/PooledUnsafeHeapByteBuf.java +++ b/buffer/src/main/java/io/net5/buffer/PooledUnsafeHeapByteBuf.java @@ -13,11 +13,11 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.internal.ObjectPool; -import io.netty.util.internal.ObjectPool.Handle; -import io.netty.util.internal.PlatformDependent; +import io.net5.util.internal.ObjectPool; +import io.net5.util.internal.ObjectPool.Handle; +import io.net5.util.internal.PlatformDependent; final class PooledUnsafeHeapByteBuf extends PooledHeapByteBuf { diff --git a/buffer/src/main/java/io/netty/buffer/ReadOnlyByteBuf.java b/buffer/src/main/java/io/net5/buffer/ReadOnlyByteBuf.java similarity index 99% rename from buffer/src/main/java/io/netty/buffer/ReadOnlyByteBuf.java rename to buffer/src/main/java/io/net5/buffer/ReadOnlyByteBuf.java index 979916c985..64296b6a5e 100644 --- a/buffer/src/main/java/io/netty/buffer/ReadOnlyByteBuf.java +++ b/buffer/src/main/java/io/net5/buffer/ReadOnlyByteBuf.java @@ -13,9 +13,9 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.ByteProcessor; +import io.net5.util.ByteProcessor; import java.io.IOException; import java.io.InputStream; diff --git a/buffer/src/main/java/io/netty/buffer/ReadOnlyByteBufferBuf.java b/buffer/src/main/java/io/net5/buffer/ReadOnlyByteBufferBuf.java similarity index 99% rename from buffer/src/main/java/io/netty/buffer/ReadOnlyByteBufferBuf.java rename to buffer/src/main/java/io/net5/buffer/ReadOnlyByteBufferBuf.java index 1e0bf73958..5ba61d6ee1 100644 --- a/buffer/src/main/java/io/netty/buffer/ReadOnlyByteBufferBuf.java +++ b/buffer/src/main/java/io/net5/buffer/ReadOnlyByteBufferBuf.java @@ -13,9 +13,9 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.internal.StringUtil; +import io.net5.util.internal.StringUtil; import java.io.IOException; import java.io.InputStream; diff --git a/buffer/src/main/java/io/netty/buffer/ReadOnlyUnsafeDirectByteBuf.java b/buffer/src/main/java/io/net5/buffer/ReadOnlyUnsafeDirectByteBuf.java similarity index 98% rename from buffer/src/main/java/io/netty/buffer/ReadOnlyUnsafeDirectByteBuf.java rename to buffer/src/main/java/io/net5/buffer/ReadOnlyUnsafeDirectByteBuf.java index 154ebe8bfd..c8a7cd0fba 100644 --- a/buffer/src/main/java/io/netty/buffer/ReadOnlyUnsafeDirectByteBuf.java +++ b/buffer/src/main/java/io/net5/buffer/ReadOnlyUnsafeDirectByteBuf.java @@ -13,14 +13,14 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; import static java.util.Objects.requireNonNull; import java.nio.ByteBuffer; -import io.netty.util.internal.PlatformDependent; +import io.net5.util.internal.PlatformDependent; /** diff --git a/buffer/src/main/java/io/netty/buffer/SimpleLeakAwareByteBuf.java b/buffer/src/main/java/io/net5/buffer/SimpleLeakAwareByteBuf.java similarity index 98% rename from buffer/src/main/java/io/netty/buffer/SimpleLeakAwareByteBuf.java rename to buffer/src/main/java/io/net5/buffer/SimpleLeakAwareByteBuf.java index 5d6523b4fa..08ff52b5c4 100644 --- a/buffer/src/main/java/io/netty/buffer/SimpleLeakAwareByteBuf.java +++ b/buffer/src/main/java/io/net5/buffer/SimpleLeakAwareByteBuf.java @@ -14,12 +14,12 @@ * under the License. */ -package io.netty.buffer; +package io.net5.buffer; import static java.util.Objects.requireNonNull; -import io.netty.util.ResourceLeakDetector; -import io.netty.util.ResourceLeakTracker; +import io.net5.util.ResourceLeakDetector; +import io.net5.util.ResourceLeakTracker; import java.nio.ByteOrder; diff --git a/buffer/src/main/java/io/netty/buffer/SimpleLeakAwareCompositeByteBuf.java b/buffer/src/main/java/io/net5/buffer/SimpleLeakAwareCompositeByteBuf.java similarity index 98% rename from buffer/src/main/java/io/netty/buffer/SimpleLeakAwareCompositeByteBuf.java rename to buffer/src/main/java/io/net5/buffer/SimpleLeakAwareCompositeByteBuf.java index 686f122fbf..b4f73fbec6 100644 --- a/buffer/src/main/java/io/netty/buffer/SimpleLeakAwareCompositeByteBuf.java +++ b/buffer/src/main/java/io/net5/buffer/SimpleLeakAwareCompositeByteBuf.java @@ -13,11 +13,11 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; import static java.util.Objects.requireNonNull; -import io.netty.util.ResourceLeakTracker; +import io.net5.util.ResourceLeakTracker; import java.nio.ByteOrder; diff --git a/buffer/src/main/java/io/netty/buffer/SizeClasses.java b/buffer/src/main/java/io/net5/buffer/SizeClasses.java similarity index 99% rename from buffer/src/main/java/io/netty/buffer/SizeClasses.java rename to buffer/src/main/java/io/net5/buffer/SizeClasses.java index 04fe5c2cbf..b0f17ebe36 100644 --- a/buffer/src/main/java/io/netty/buffer/SizeClasses.java +++ b/buffer/src/main/java/io/net5/buffer/SizeClasses.java @@ -13,9 +13,9 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import static io.netty.buffer.PoolThreadCache.*; +import static io.net5.buffer.PoolThreadCache.*; /** * SizeClasses requires {@code pageShifts} to be defined prior to inclusion, diff --git a/buffer/src/main/java/io/netty/buffer/SizeClassesMetric.java b/buffer/src/main/java/io/net5/buffer/SizeClassesMetric.java similarity index 98% rename from buffer/src/main/java/io/netty/buffer/SizeClassesMetric.java rename to buffer/src/main/java/io/net5/buffer/SizeClassesMetric.java index 17ade94ac8..ba00fe5737 100644 --- a/buffer/src/main/java/io/netty/buffer/SizeClassesMetric.java +++ b/buffer/src/main/java/io/net5/buffer/SizeClassesMetric.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; /** * Expose metrics for an SizeClasses. diff --git a/buffer/src/main/java/io/netty/buffer/SlicedByteBuf.java b/buffer/src/main/java/io/net5/buffer/SlicedByteBuf.java similarity index 98% rename from buffer/src/main/java/io/netty/buffer/SlicedByteBuf.java rename to buffer/src/main/java/io/net5/buffer/SlicedByteBuf.java index be0b71101e..7171ad8384 100644 --- a/buffer/src/main/java/io/netty/buffer/SlicedByteBuf.java +++ b/buffer/src/main/java/io/net5/buffer/SlicedByteBuf.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; /** * A derived buffer which exposes its parent's sub-region only. It is diff --git a/buffer/src/main/java/io/netty/buffer/SwappedByteBuf.java b/buffer/src/main/java/io/net5/buffer/SwappedByteBuf.java similarity index 99% rename from buffer/src/main/java/io/netty/buffer/SwappedByteBuf.java rename to buffer/src/main/java/io/net5/buffer/SwappedByteBuf.java index 995de9ea88..db8422d48c 100644 --- a/buffer/src/main/java/io/netty/buffer/SwappedByteBuf.java +++ b/buffer/src/main/java/io/net5/buffer/SwappedByteBuf.java @@ -13,11 +13,11 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; import static java.util.Objects.requireNonNull; -import io.netty.util.ByteProcessor; +import io.net5.util.ByteProcessor; import java.io.IOException; import java.io.InputStream; diff --git a/buffer/src/main/java/io/netty/buffer/Unpooled.java b/buffer/src/main/java/io/net5/buffer/Unpooled.java similarity index 99% rename from buffer/src/main/java/io/netty/buffer/Unpooled.java rename to buffer/src/main/java/io/net5/buffer/Unpooled.java index 66b94e3c18..b863186ce6 100644 --- a/buffer/src/main/java/io/netty/buffer/Unpooled.java +++ b/buffer/src/main/java/io/net5/buffer/Unpooled.java @@ -13,13 +13,13 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; import static java.util.Objects.requireNonNull; -import io.netty.buffer.CompositeByteBuf.ByteWrapper; -import io.netty.util.CharsetUtil; -import io.netty.util.internal.PlatformDependent; +import io.net5.buffer.CompositeByteBuf.ByteWrapper; +import io.net5.util.CharsetUtil; +import io.net5.util.internal.PlatformDependent; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -36,7 +36,7 @@ import java.util.Arrays; * This classes is intended to be used with Java 5 static import statement: * *
- * import static io.netty.buffer.{@link Unpooled}.*;
+ * import static io.net5.buffer.{@link Unpooled}.*;
  *
  * {@link ByteBuf} heapBuffer    = buffer(128);
  * {@link ByteBuf} directBuffer  = directBuffer(256);
diff --git a/buffer/src/main/java/io/netty/buffer/UnpooledByteBufAllocator.java b/buffer/src/main/java/io/net5/buffer/UnpooledByteBufAllocator.java
similarity index 98%
rename from buffer/src/main/java/io/netty/buffer/UnpooledByteBufAllocator.java
rename to buffer/src/main/java/io/net5/buffer/UnpooledByteBufAllocator.java
index 2f7a015e93..6f836f1b4f 100644
--- a/buffer/src/main/java/io/netty/buffer/UnpooledByteBufAllocator.java
+++ b/buffer/src/main/java/io/net5/buffer/UnpooledByteBufAllocator.java
@@ -13,10 +13,10 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer;
+package io.net5.buffer;
 
-import io.netty.util.internal.PlatformDependent;
-import io.netty.util.internal.StringUtil;
+import io.net5.util.internal.PlatformDependent;
+import io.net5.util.internal.StringUtil;
 
 import java.nio.ByteBuffer;
 import java.util.concurrent.atomic.LongAdder;
diff --git a/buffer/src/main/java/io/netty/buffer/UnpooledDirectByteBuf.java b/buffer/src/main/java/io/net5/buffer/UnpooledDirectByteBuf.java
similarity index 99%
rename from buffer/src/main/java/io/netty/buffer/UnpooledDirectByteBuf.java
rename to buffer/src/main/java/io/net5/buffer/UnpooledDirectByteBuf.java
index cef96b8050..8151ae0d29 100644
--- a/buffer/src/main/java/io/netty/buffer/UnpooledDirectByteBuf.java
+++ b/buffer/src/main/java/io/net5/buffer/UnpooledDirectByteBuf.java
@@ -13,12 +13,12 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer;
+package io.net5.buffer;
 
-import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
+import static io.net5.util.internal.ObjectUtil.checkPositiveOrZero;
 import static java.util.Objects.requireNonNull;
 
-import io.netty.util.internal.PlatformDependent;
+import io.net5.util.internal.PlatformDependent;
 
 import java.io.IOException;
 import java.io.InputStream;
diff --git a/buffer/src/main/java/io/netty/buffer/UnpooledDuplicatedByteBuf.java b/buffer/src/main/java/io/net5/buffer/UnpooledDuplicatedByteBuf.java
similarity index 99%
rename from buffer/src/main/java/io/netty/buffer/UnpooledDuplicatedByteBuf.java
rename to buffer/src/main/java/io/net5/buffer/UnpooledDuplicatedByteBuf.java
index 5b9ea1dc52..057870ebf2 100644
--- a/buffer/src/main/java/io/netty/buffer/UnpooledDuplicatedByteBuf.java
+++ b/buffer/src/main/java/io/net5/buffer/UnpooledDuplicatedByteBuf.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer;
+package io.net5.buffer;
 
 /**
  * {@link DuplicatedByteBuf} implementation that can do optimizations because it knows the duplicated buffer
diff --git a/buffer/src/main/java/io/netty/buffer/UnpooledHeapByteBuf.java b/buffer/src/main/java/io/net5/buffer/UnpooledHeapByteBuf.java
similarity index 99%
rename from buffer/src/main/java/io/netty/buffer/UnpooledHeapByteBuf.java
rename to buffer/src/main/java/io/net5/buffer/UnpooledHeapByteBuf.java
index 4aefaff9e8..92531f1310 100644
--- a/buffer/src/main/java/io/netty/buffer/UnpooledHeapByteBuf.java
+++ b/buffer/src/main/java/io/net5/buffer/UnpooledHeapByteBuf.java
@@ -13,12 +13,12 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer;
+package io.net5.buffer;
 
 import static java.util.Objects.requireNonNull;
 
-import io.netty.util.internal.EmptyArrays;
-import io.netty.util.internal.PlatformDependent;
+import io.net5.util.internal.EmptyArrays;
+import io.net5.util.internal.PlatformDependent;
 
 import java.io.IOException;
 import java.io.InputStream;
diff --git a/buffer/src/main/java/io/netty/buffer/UnpooledSlicedByteBuf.java b/buffer/src/main/java/io/net5/buffer/UnpooledSlicedByteBuf.java
similarity index 99%
rename from buffer/src/main/java/io/netty/buffer/UnpooledSlicedByteBuf.java
rename to buffer/src/main/java/io/net5/buffer/UnpooledSlicedByteBuf.java
index 3c5a7655ea..f79240902a 100644
--- a/buffer/src/main/java/io/netty/buffer/UnpooledSlicedByteBuf.java
+++ b/buffer/src/main/java/io/net5/buffer/UnpooledSlicedByteBuf.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer;
+package io.net5.buffer;
 
 /**
  * A special {@link AbstractUnpooledSlicedByteBuf} that can make optimizations because it knows the sliced buffer is of
diff --git a/buffer/src/main/java/io/netty/buffer/UnpooledUnsafeDirectByteBuf.java b/buffer/src/main/java/io/net5/buffer/UnpooledUnsafeDirectByteBuf.java
similarity index 99%
rename from buffer/src/main/java/io/netty/buffer/UnpooledUnsafeDirectByteBuf.java
rename to buffer/src/main/java/io/net5/buffer/UnpooledUnsafeDirectByteBuf.java
index 6d1e326dc0..a3af434e4d 100644
--- a/buffer/src/main/java/io/netty/buffer/UnpooledUnsafeDirectByteBuf.java
+++ b/buffer/src/main/java/io/net5/buffer/UnpooledUnsafeDirectByteBuf.java
@@ -13,9 +13,9 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer;
+package io.net5.buffer;
 
-import io.netty.util.internal.PlatformDependent;
+import io.net5.util.internal.PlatformDependent;
 
 import java.io.IOException;
 import java.io.InputStream;
diff --git a/buffer/src/main/java/io/netty/buffer/UnpooledUnsafeHeapByteBuf.java b/buffer/src/main/java/io/net5/buffer/UnpooledUnsafeHeapByteBuf.java
similarity index 98%
rename from buffer/src/main/java/io/netty/buffer/UnpooledUnsafeHeapByteBuf.java
rename to buffer/src/main/java/io/net5/buffer/UnpooledUnsafeHeapByteBuf.java
index 283e76fe6c..154f02b939 100644
--- a/buffer/src/main/java/io/netty/buffer/UnpooledUnsafeHeapByteBuf.java
+++ b/buffer/src/main/java/io/net5/buffer/UnpooledUnsafeHeapByteBuf.java
@@ -13,9 +13,9 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer;
+package io.net5.buffer;
 
-import io.netty.util.internal.PlatformDependent;
+import io.net5.util.internal.PlatformDependent;
 
 /**
  * Big endian Java heap buffer implementation. It is recommended to use
diff --git a/buffer/src/main/java/io/netty/buffer/UnpooledUnsafeNoCleanerDirectByteBuf.java b/buffer/src/main/java/io/net5/buffer/UnpooledUnsafeNoCleanerDirectByteBuf.java
similarity index 95%
rename from buffer/src/main/java/io/netty/buffer/UnpooledUnsafeNoCleanerDirectByteBuf.java
rename to buffer/src/main/java/io/net5/buffer/UnpooledUnsafeNoCleanerDirectByteBuf.java
index e30f9293f5..5138bf4e87 100644
--- a/buffer/src/main/java/io/netty/buffer/UnpooledUnsafeNoCleanerDirectByteBuf.java
+++ b/buffer/src/main/java/io/net5/buffer/UnpooledUnsafeNoCleanerDirectByteBuf.java
@@ -13,9 +13,9 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer;
+package io.net5.buffer;
 
-import io.netty.util.internal.PlatformDependent;
+import io.net5.util.internal.PlatformDependent;
 
 import java.nio.ByteBuffer;
 
diff --git a/buffer/src/main/java/io/netty/buffer/UnreleasableByteBuf.java b/buffer/src/main/java/io/net5/buffer/UnreleasableByteBuf.java
similarity index 99%
rename from buffer/src/main/java/io/netty/buffer/UnreleasableByteBuf.java
rename to buffer/src/main/java/io/net5/buffer/UnreleasableByteBuf.java
index 4773ac57e2..a498f0bd09 100644
--- a/buffer/src/main/java/io/netty/buffer/UnreleasableByteBuf.java
+++ b/buffer/src/main/java/io/net5/buffer/UnreleasableByteBuf.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer;
+package io.net5.buffer;
 
 import static java.util.Objects.requireNonNull;
 
diff --git a/buffer/src/main/java/io/netty/buffer/UnsafeByteBufUtil.java b/buffer/src/main/java/io/net5/buffer/UnsafeByteBufUtil.java
similarity index 99%
rename from buffer/src/main/java/io/netty/buffer/UnsafeByteBufUtil.java
rename to buffer/src/main/java/io/net5/buffer/UnsafeByteBufUtil.java
index 741b2ddf9f..22de6f3217 100644
--- a/buffer/src/main/java/io/netty/buffer/UnsafeByteBufUtil.java
+++ b/buffer/src/main/java/io/net5/buffer/UnsafeByteBufUtil.java
@@ -13,9 +13,9 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer;
+package io.net5.buffer;
 
-import io.netty.util.internal.PlatformDependent;
+import io.net5.util.internal.PlatformDependent;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -24,8 +24,8 @@ import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.nio.ReadOnlyBufferException;
 
-import static io.netty.util.internal.MathUtil.isOutOfBounds;
-import static io.netty.util.internal.PlatformDependent.BIG_ENDIAN_NATIVE_ORDER;
+import static io.net5.util.internal.MathUtil.isOutOfBounds;
+import static io.net5.util.internal.PlatformDependent.BIG_ENDIAN_NATIVE_ORDER;
 import static java.util.Objects.requireNonNull;
 
 /**
diff --git a/buffer/src/main/java/io/netty/buffer/UnsafeDirectSwappedByteBuf.java b/buffer/src/main/java/io/net5/buffer/UnsafeDirectSwappedByteBuf.java
similarity index 96%
rename from buffer/src/main/java/io/netty/buffer/UnsafeDirectSwappedByteBuf.java
rename to buffer/src/main/java/io/net5/buffer/UnsafeDirectSwappedByteBuf.java
index dca920ed3d..8d40e03076 100644
--- a/buffer/src/main/java/io/netty/buffer/UnsafeDirectSwappedByteBuf.java
+++ b/buffer/src/main/java/io/net5/buffer/UnsafeDirectSwappedByteBuf.java
@@ -14,9 +14,9 @@
 * under the License.
 */
 
-package io.netty.buffer;
+package io.net5.buffer;
 
-import io.netty.util.internal.PlatformDependent;
+import io.net5.util.internal.PlatformDependent;
 
 /**
  * Special {@link SwappedByteBuf} for {@link ByteBuf}s that are backed by a {@code memoryAddress}.
diff --git a/buffer/src/main/java/io/netty/buffer/UnsafeHeapSwappedByteBuf.java b/buffer/src/main/java/io/net5/buffer/UnsafeHeapSwappedByteBuf.java
similarity index 96%
rename from buffer/src/main/java/io/netty/buffer/UnsafeHeapSwappedByteBuf.java
rename to buffer/src/main/java/io/net5/buffer/UnsafeHeapSwappedByteBuf.java
index 8de2870f96..b823052bd4 100644
--- a/buffer/src/main/java/io/netty/buffer/UnsafeHeapSwappedByteBuf.java
+++ b/buffer/src/main/java/io/net5/buffer/UnsafeHeapSwappedByteBuf.java
@@ -14,9 +14,9 @@
 * under the License.
 */
 
-package io.netty.buffer;
+package io.net5.buffer;
 
-import io.netty.util.internal.PlatformDependent;
+import io.net5.util.internal.PlatformDependent;
 
 /**
  * Special {@link SwappedByteBuf} for {@link ByteBuf}s that use unsafe to access the byte array.
diff --git a/buffer/src/main/java/io/netty/buffer/WrappedByteBuf.java b/buffer/src/main/java/io/net5/buffer/WrappedByteBuf.java
similarity index 99%
rename from buffer/src/main/java/io/netty/buffer/WrappedByteBuf.java
rename to buffer/src/main/java/io/net5/buffer/WrappedByteBuf.java
index ecf51bfc84..5bb7082f48 100644
--- a/buffer/src/main/java/io/netty/buffer/WrappedByteBuf.java
+++ b/buffer/src/main/java/io/net5/buffer/WrappedByteBuf.java
@@ -14,12 +14,12 @@
  * under the License.
  */
 
-package io.netty.buffer;
+package io.net5.buffer;
 
 import static java.util.Objects.requireNonNull;
 
-import io.netty.util.ByteProcessor;
-import io.netty.util.internal.StringUtil;
+import io.net5.util.ByteProcessor;
+import io.net5.util.internal.StringUtil;
 
 import java.io.IOException;
 import java.io.InputStream;
diff --git a/buffer/src/main/java/io/netty/buffer/WrappedCompositeByteBuf.java b/buffer/src/main/java/io/net5/buffer/WrappedCompositeByteBuf.java
similarity index 99%
rename from buffer/src/main/java/io/netty/buffer/WrappedCompositeByteBuf.java
rename to buffer/src/main/java/io/net5/buffer/WrappedCompositeByteBuf.java
index 44f9b50477..7cf46c7959 100644
--- a/buffer/src/main/java/io/netty/buffer/WrappedCompositeByteBuf.java
+++ b/buffer/src/main/java/io/net5/buffer/WrappedCompositeByteBuf.java
@@ -13,9 +13,9 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer;
+package io.net5.buffer;
 
-import io.netty.util.ByteProcessor;
+import io.net5.util.ByteProcessor;
 
 import java.io.IOException;
 import java.io.InputStream;
diff --git a/buffer/src/main/java/io/netty/buffer/WrappedUnpooledUnsafeDirectByteBuf.java b/buffer/src/main/java/io/net5/buffer/WrappedUnpooledUnsafeDirectByteBuf.java
similarity index 93%
rename from buffer/src/main/java/io/netty/buffer/WrappedUnpooledUnsafeDirectByteBuf.java
rename to buffer/src/main/java/io/net5/buffer/WrappedUnpooledUnsafeDirectByteBuf.java
index dd8493c058..5862bd5500 100644
--- a/buffer/src/main/java/io/netty/buffer/WrappedUnpooledUnsafeDirectByteBuf.java
+++ b/buffer/src/main/java/io/net5/buffer/WrappedUnpooledUnsafeDirectByteBuf.java
@@ -13,9 +13,9 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer;
+package io.net5.buffer;
 
-import io.netty.util.internal.PlatformDependent;
+import io.net5.util.internal.PlatformDependent;
 
 import java.nio.ByteBuffer;
 
diff --git a/buffer/src/main/java/io/netty/buffer/api/AllocationType.java b/buffer/src/main/java/io/net5/buffer/api/AllocationType.java
similarity index 97%
rename from buffer/src/main/java/io/netty/buffer/api/AllocationType.java
rename to buffer/src/main/java/io/net5/buffer/api/AllocationType.java
index 669db3e2e5..e491156c0a 100644
--- a/buffer/src/main/java/io/netty/buffer/api/AllocationType.java
+++ b/buffer/src/main/java/io/net5/buffer/api/AllocationType.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api;
+package io.net5.buffer.api;
 
 /**
  * An object used by {@linkplain BufferAllocator buffer allocators} to communicate desirable properties of an
diff --git a/buffer/src/main/java/io/netty/buffer/api/AllocatorControl.java b/buffer/src/main/java/io/net5/buffer/api/AllocatorControl.java
similarity index 97%
rename from buffer/src/main/java/io/netty/buffer/api/AllocatorControl.java
rename to buffer/src/main/java/io/net5/buffer/api/AllocatorControl.java
index 43db39963c..7bccacd6b0 100644
--- a/buffer/src/main/java/io/netty/buffer/api/AllocatorControl.java
+++ b/buffer/src/main/java/io/net5/buffer/api/AllocatorControl.java
@@ -13,9 +13,9 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api;
+package io.net5.buffer.api;
 
-import io.netty.util.internal.UnstableApi;
+import io.net5.util.internal.UnstableApi;
 
 /**
  * Methods for accessing and controlling the internals of an allocator.
diff --git a/buffer/src/main/java/io/netty/buffer/api/Buffer.java b/buffer/src/main/java/io/net5/buffer/api/Buffer.java
similarity index 99%
rename from buffer/src/main/java/io/netty/buffer/api/Buffer.java
rename to buffer/src/main/java/io/net5/buffer/api/Buffer.java
index e0090ad568..1ab1f73957 100644
--- a/buffer/src/main/java/io/netty/buffer/api/Buffer.java
+++ b/buffer/src/main/java/io/net5/buffer/api/Buffer.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api;
+package io.net5.buffer.api;
 
 import java.nio.ByteBuffer;
 
diff --git a/buffer/src/main/java/io/netty/buffer/api/BufferAccessor.java b/buffer/src/main/java/io/net5/buffer/api/BufferAccessor.java
similarity index 99%
rename from buffer/src/main/java/io/netty/buffer/api/BufferAccessor.java
rename to buffer/src/main/java/io/net5/buffer/api/BufferAccessor.java
index afcfd5d6f8..4ae1c71402 100644
--- a/buffer/src/main/java/io/netty/buffer/api/BufferAccessor.java
+++ b/buffer/src/main/java/io/net5/buffer/api/BufferAccessor.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api;
+package io.net5.buffer.api;
 
 /**
  * This interface is just the primitive data accessor methods that {@link Buffer} exposes.
diff --git a/buffer/src/main/java/io/netty/buffer/api/BufferAllocator.java b/buffer/src/main/java/io/net5/buffer/api/BufferAllocator.java
similarity index 98%
rename from buffer/src/main/java/io/netty/buffer/api/BufferAllocator.java
rename to buffer/src/main/java/io/net5/buffer/api/BufferAllocator.java
index 1d0f4dfa21..5f93a241e3 100644
--- a/buffer/src/main/java/io/netty/buffer/api/BufferAllocator.java
+++ b/buffer/src/main/java/io/net5/buffer/api/BufferAllocator.java
@@ -13,9 +13,9 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api;
+package io.net5.buffer.api;
 
-import io.netty.buffer.api.pool.PooledBufferAllocator;
+import io.net5.buffer.api.pool.PooledBufferAllocator;
 
 import java.util.function.Supplier;
 
diff --git a/buffer/src/main/java/io/netty/buffer/api/BufferClosedException.java b/buffer/src/main/java/io/net5/buffer/api/BufferClosedException.java
similarity index 97%
rename from buffer/src/main/java/io/netty/buffer/api/BufferClosedException.java
rename to buffer/src/main/java/io/net5/buffer/api/BufferClosedException.java
index ea22dbbdda..d18911417a 100644
--- a/buffer/src/main/java/io/netty/buffer/api/BufferClosedException.java
+++ b/buffer/src/main/java/io/net5/buffer/api/BufferClosedException.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api;
+package io.net5.buffer.api;
 
 /**
  * An exception thrown when an operation is attempted on a {@link Buffer} when it has been closed.
diff --git a/buffer/src/main/java/io/netty/buffer/api/BufferHolder.java b/buffer/src/main/java/io/net5/buffer/api/BufferHolder.java
similarity index 97%
rename from buffer/src/main/java/io/netty/buffer/api/BufferHolder.java
rename to buffer/src/main/java/io/net5/buffer/api/BufferHolder.java
index e811790d20..586deb9773 100644
--- a/buffer/src/main/java/io/netty/buffer/api/BufferHolder.java
+++ b/buffer/src/main/java/io/net5/buffer/api/BufferHolder.java
@@ -13,10 +13,10 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api;
+package io.net5.buffer.api;
 
-import io.netty.buffer.api.internal.ResourceSupport;
-import io.netty.buffer.api.internal.Statics;
+import io.net5.buffer.api.internal.ResourceSupport;
+import io.net5.buffer.api.internal.Statics;
 
 import java.lang.invoke.VarHandle;
 import java.util.Objects;
diff --git a/buffer/src/main/java/io/netty/buffer/api/BufferReadOnlyException.java b/buffer/src/main/java/io/net5/buffer/api/BufferReadOnlyException.java
similarity index 97%
rename from buffer/src/main/java/io/netty/buffer/api/BufferReadOnlyException.java
rename to buffer/src/main/java/io/net5/buffer/api/BufferReadOnlyException.java
index 29e98b7fc6..2a2a6b09be 100644
--- a/buffer/src/main/java/io/netty/buffer/api/BufferReadOnlyException.java
+++ b/buffer/src/main/java/io/net5/buffer/api/BufferReadOnlyException.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api;
+package io.net5.buffer.api;
 
 /**
  * An exception thrown when an operation is attempted on a {@linkplain Buffer#readOnly() read-only} {@link Buffer}.
diff --git a/buffer/src/main/java/io/netty/buffer/api/BufferRef.java b/buffer/src/main/java/io/net5/buffer/api/BufferRef.java
similarity index 98%
rename from buffer/src/main/java/io/netty/buffer/api/BufferRef.java
rename to buffer/src/main/java/io/net5/buffer/api/BufferRef.java
index 8cfa4a799c..1097dccc1c 100644
--- a/buffer/src/main/java/io/netty/buffer/api/BufferRef.java
+++ b/buffer/src/main/java/io/net5/buffer/api/BufferRef.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api;
+package io.net5.buffer.api;
 
 import java.lang.invoke.VarHandle;
 
diff --git a/buffer/src/main/java/io/netty/buffer/api/BufferStub.java b/buffer/src/main/java/io/net5/buffer/api/BufferStub.java
similarity index 99%
rename from buffer/src/main/java/io/netty/buffer/api/BufferStub.java
rename to buffer/src/main/java/io/net5/buffer/api/BufferStub.java
index d962ba9fb9..42996c52cf 100644
--- a/buffer/src/main/java/io/netty/buffer/api/BufferStub.java
+++ b/buffer/src/main/java/io/net5/buffer/api/BufferStub.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api;
+package io.net5.buffer.api;
 
 import java.nio.ByteBuffer;
 
diff --git a/buffer/src/main/java/io/netty/buffer/api/ByteCursor.java b/buffer/src/main/java/io/net5/buffer/api/ByteCursor.java
similarity index 97%
rename from buffer/src/main/java/io/netty/buffer/api/ByteCursor.java
rename to buffer/src/main/java/io/net5/buffer/api/ByteCursor.java
index 271e1829d9..6c2ca346c1 100644
--- a/buffer/src/main/java/io/netty/buffer/api/ByteCursor.java
+++ b/buffer/src/main/java/io/net5/buffer/api/ByteCursor.java
@@ -12,9 +12,9 @@
  * or implied. See the License for the specific language governing permissions and limitations under
  * the License.
  */
-package io.netty.buffer.api;
+package io.net5.buffer.api;
 
-import io.netty.util.ByteProcessor;
+import io.net5.util.ByteProcessor;
 
 /**
  * The ByteCursor scans through a sequence of bytes.
diff --git a/buffer/src/main/java/io/netty/buffer/api/CompositeBuffer.java b/buffer/src/main/java/io/net5/buffer/api/CompositeBuffer.java
similarity index 99%
rename from buffer/src/main/java/io/netty/buffer/api/CompositeBuffer.java
rename to buffer/src/main/java/io/net5/buffer/api/CompositeBuffer.java
index b42592614d..8be333aeff 100644
--- a/buffer/src/main/java/io/netty/buffer/api/CompositeBuffer.java
+++ b/buffer/src/main/java/io/net5/buffer/api/CompositeBuffer.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api;
+package io.net5.buffer.api;
 
 /**
  * The {@code CompositeBuffer} is a concrete {@link Buffer} implementation that make a number of other buffers appear
diff --git a/buffer/src/main/java/io/netty/buffer/api/DefaultCompositeBuffer.java b/buffer/src/main/java/io/net5/buffer/api/DefaultCompositeBuffer.java
similarity index 99%
rename from buffer/src/main/java/io/netty/buffer/api/DefaultCompositeBuffer.java
rename to buffer/src/main/java/io/net5/buffer/api/DefaultCompositeBuffer.java
index e3e0502f03..62c3a0e1e4 100644
--- a/buffer/src/main/java/io/netty/buffer/api/DefaultCompositeBuffer.java
+++ b/buffer/src/main/java/io/net5/buffer/api/DefaultCompositeBuffer.java
@@ -13,10 +13,10 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api;
+package io.net5.buffer.api;
 
-import io.netty.buffer.api.internal.ResourceSupport;
-import io.netty.buffer.api.internal.Statics;
+import io.net5.buffer.api.internal.ResourceSupport;
+import io.net5.buffer.api.internal.Statics;
 
 import java.nio.ByteBuffer;
 import java.nio.ReadOnlyBufferException;
@@ -27,8 +27,8 @@ import java.util.Objects;
 import java.util.Set;
 import java.util.stream.Stream;
 
-import static io.netty.buffer.api.internal.Statics.bufferIsClosed;
-import static io.netty.buffer.api.internal.Statics.bufferIsReadOnly;
+import static io.net5.buffer.api.internal.Statics.bufferIsClosed;
+import static io.net5.buffer.api.internal.Statics.bufferIsReadOnly;
 import static java.lang.Math.addExact;
 
 /**
diff --git a/buffer/src/main/java/io/netty/buffer/api/DefaultGlobalBufferAllocator.java b/buffer/src/main/java/io/net5/buffer/api/DefaultGlobalBufferAllocator.java
similarity index 71%
rename from buffer/src/main/java/io/netty/buffer/api/DefaultGlobalBufferAllocator.java
rename to buffer/src/main/java/io/net5/buffer/api/DefaultGlobalBufferAllocator.java
index 303522223c..78ad89157d 100644
--- a/buffer/src/main/java/io/netty/buffer/api/DefaultGlobalBufferAllocator.java
+++ b/buffer/src/main/java/io/net5/buffer/api/DefaultGlobalBufferAllocator.java
@@ -12,22 +12,22 @@
  * or implied. See the License for the specific language governing permissions and limitations under
  * the License.
  */
-package io.netty.buffer.api;
+package io.net5.buffer.api;
 
-import io.netty.util.internal.PlatformDependent;
-import io.netty.util.internal.SystemPropertyUtil;
-import io.netty.util.internal.logging.InternalLogger;
-import io.netty.util.internal.logging.InternalLoggerFactory;
+import io.net5.util.internal.PlatformDependent;
+import io.net5.util.internal.SystemPropertyUtil;
+import io.net5.util.internal.logging.InternalLogger;
+import io.net5.util.internal.logging.InternalLoggerFactory;
 
 import java.util.Locale;
 import java.util.function.Supplier;
 
-import static io.netty.buffer.api.BufferAllocator.offHeapPooled;
-import static io.netty.buffer.api.BufferAllocator.offHeapUnpooled;
-import static io.netty.buffer.api.BufferAllocator.onHeapPooled;
-import static io.netty.buffer.api.BufferAllocator.onHeapUnpooled;
-import static io.netty.util.internal.ObjectUtil.checkNotNullWithIAE;
-import static io.netty.util.internal.PlatformDependent.directBufferPreferred;
+import static io.net5.buffer.api.BufferAllocator.offHeapPooled;
+import static io.net5.buffer.api.BufferAllocator.offHeapUnpooled;
+import static io.net5.buffer.api.BufferAllocator.onHeapPooled;
+import static io.net5.buffer.api.BufferAllocator.onHeapUnpooled;
+import static io.net5.util.internal.ObjectUtil.checkNotNullWithIAE;
+import static io.net5.util.internal.PlatformDependent.directBufferPreferred;
 import static java.lang.Runtime.getRuntime;
 
 /**
@@ -39,19 +39,19 @@ public final class DefaultGlobalBufferAllocator implements BufferAllocator {
 
     static {
         String allocType = SystemPropertyUtil.get(
-                "io.netty.allocator.type", PlatformDependent.isAndroid() ? "unpooled" : "pooled");
+                "io.net5.allocator.type", PlatformDependent.isAndroid() ? "unpooled" : "pooled");
         allocType = allocType.toLowerCase(Locale.US).trim();
 
         BufferAllocator alloc;
         if ("unpooled".equals(allocType)) {
             alloc = directBufferPreferred() ? offHeapUnpooled() : onHeapUnpooled();
-            logger.debug("-Dio.netty.allocator.type: {}", allocType);
+            logger.debug("-Dio.net5.allocator.type: {}", allocType);
         } else if ("pooled".equals(allocType)) {
             alloc = directBufferPreferred() ? offHeapPooled() : onHeapPooled();
-            logger.debug("-Dio.netty.allocator.type: {}", allocType);
+            logger.debug("-Dio.net5.allocator.type: {}", allocType);
         } else {
             alloc = directBufferPreferred() ? offHeapPooled() : onHeapPooled();
-            logger.debug("-Dio.netty.allocator.type: pooled (unknown: {})", allocType);
+            logger.debug("-Dio.net5.allocator.type: pooled (unknown: {})", allocType);
         }
         DEFAUL_GLOBAL_BUFFER_ALLOCATOR = new DefaultGlobalBufferAllocator(alloc);
     }
diff --git a/buffer/src/main/java/io/netty/buffer/api/Drop.java b/buffer/src/main/java/io/net5/buffer/api/Drop.java
similarity index 97%
rename from buffer/src/main/java/io/netty/buffer/api/Drop.java
rename to buffer/src/main/java/io/net5/buffer/api/Drop.java
index 95f61a3c73..f0e9d3aaab 100644
--- a/buffer/src/main/java/io/netty/buffer/api/Drop.java
+++ b/buffer/src/main/java/io/net5/buffer/api/Drop.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api;
+package io.net5.buffer.api;
 
 /**
  * An interface used by {@link Resource} instances to implement their resource disposal mechanics.
diff --git a/buffer/src/main/java/io/netty/buffer/api/ManagedBufferAllocator.java b/buffer/src/main/java/io/net5/buffer/api/ManagedBufferAllocator.java
similarity index 92%
rename from buffer/src/main/java/io/netty/buffer/api/ManagedBufferAllocator.java
rename to buffer/src/main/java/io/net5/buffer/api/ManagedBufferAllocator.java
index d47e157493..ee3dc8cfce 100644
--- a/buffer/src/main/java/io/netty/buffer/api/ManagedBufferAllocator.java
+++ b/buffer/src/main/java/io/net5/buffer/api/ManagedBufferAllocator.java
@@ -13,14 +13,14 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api;
+package io.net5.buffer.api;
 
-import io.netty.buffer.api.internal.Statics;
+import io.net5.buffer.api.internal.Statics;
 
 import java.util.function.Supplier;
 
-import static io.netty.buffer.api.internal.Statics.NO_OP_DROP;
-import static io.netty.buffer.api.internal.Statics.allocatorClosedException;
+import static io.net5.buffer.api.internal.Statics.NO_OP_DROP;
+import static io.net5.buffer.api.internal.Statics.allocatorClosedException;
 
 class ManagedBufferAllocator implements BufferAllocator, AllocatorControl {
     private final MemoryManager manager;
diff --git a/buffer/src/main/java/io/netty/buffer/api/MemoryManager.java b/buffer/src/main/java/io/net5/buffer/api/MemoryManager.java
similarity index 96%
rename from buffer/src/main/java/io/netty/buffer/api/MemoryManager.java
rename to buffer/src/main/java/io/net5/buffer/api/MemoryManager.java
index 060b0a3859..b6489aebf0 100644
--- a/buffer/src/main/java/io/netty/buffer/api/MemoryManager.java
+++ b/buffer/src/main/java/io/net5/buffer/api/MemoryManager.java
@@ -13,13 +13,13 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api;
+package io.net5.buffer.api;
 
-import io.netty.buffer.api.internal.MemoryManagerLoader;
-import io.netty.buffer.api.internal.MemoryManagerOverride;
-import io.netty.util.internal.UnstableApi;
-import io.netty.util.internal.logging.InternalLogger;
-import io.netty.util.internal.logging.InternalLoggerFactory;
+import io.net5.buffer.api.internal.MemoryManagerLoader;
+import io.net5.buffer.api.internal.MemoryManagerOverride;
+import io.net5.util.internal.UnstableApi;
+import io.net5.util.internal.logging.InternalLogger;
+import io.net5.util.internal.logging.InternalLoggerFactory;
 
 import java.lang.ref.Cleaner;
 import java.util.Optional;
diff --git a/buffer/src/main/java/io/netty/buffer/api/Owned.java b/buffer/src/main/java/io/net5/buffer/api/Owned.java
similarity index 98%
rename from buffer/src/main/java/io/netty/buffer/api/Owned.java
rename to buffer/src/main/java/io/net5/buffer/api/Owned.java
index c7d8548c8a..e291b5987f 100644
--- a/buffer/src/main/java/io/netty/buffer/api/Owned.java
+++ b/buffer/src/main/java/io/net5/buffer/api/Owned.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api;
+package io.net5.buffer.api;
 
 /**
  * This interface encapsulates the ownership of a {@link Resource}, and exposes a method that may be used to transfer
diff --git a/buffer/src/main/java/io/netty/buffer/api/ReadableComponent.java b/buffer/src/main/java/io/net5/buffer/api/ReadableComponent.java
similarity index 99%
rename from buffer/src/main/java/io/netty/buffer/api/ReadableComponent.java
rename to buffer/src/main/java/io/net5/buffer/api/ReadableComponent.java
index 67d5b6d1f9..a88a8f36c0 100644
--- a/buffer/src/main/java/io/netty/buffer/api/ReadableComponent.java
+++ b/buffer/src/main/java/io/net5/buffer/api/ReadableComponent.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api;
+package io.net5.buffer.api;
 
 import java.nio.ByteBuffer;
 
diff --git a/buffer/src/main/java/io/netty/buffer/api/ReadableComponentProcessor.java b/buffer/src/main/java/io/net5/buffer/api/ReadableComponentProcessor.java
similarity index 98%
rename from buffer/src/main/java/io/netty/buffer/api/ReadableComponentProcessor.java
rename to buffer/src/main/java/io/net5/buffer/api/ReadableComponentProcessor.java
index 50b6e756b9..7d6f771412 100644
--- a/buffer/src/main/java/io/netty/buffer/api/ReadableComponentProcessor.java
+++ b/buffer/src/main/java/io/net5/buffer/api/ReadableComponentProcessor.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api;
+package io.net5.buffer.api;
 
 import java.nio.ByteBuffer;
 
diff --git a/buffer/src/main/java/io/netty/buffer/api/Resource.java b/buffer/src/main/java/io/net5/buffer/api/Resource.java
similarity index 98%
rename from buffer/src/main/java/io/netty/buffer/api/Resource.java
rename to buffer/src/main/java/io/net5/buffer/api/Resource.java
index 49b4931ef6..545610015b 100644
--- a/buffer/src/main/java/io/netty/buffer/api/Resource.java
+++ b/buffer/src/main/java/io/net5/buffer/api/Resource.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api;
+package io.net5.buffer.api;
 
 /**
  * A resource that has a life-time, and can be {@linkplain #close() closed}.
diff --git a/buffer/src/main/java/io/netty/buffer/api/Send.java b/buffer/src/main/java/io/net5/buffer/api/Send.java
similarity index 98%
rename from buffer/src/main/java/io/netty/buffer/api/Send.java
rename to buffer/src/main/java/io/net5/buffer/api/Send.java
index 1ef6977e19..8ffd7aa910 100644
--- a/buffer/src/main/java/io/netty/buffer/api/Send.java
+++ b/buffer/src/main/java/io/net5/buffer/api/Send.java
@@ -13,9 +13,9 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api;
+package io.net5.buffer.api;
 
-import io.netty.buffer.api.internal.SendFromSupplier;
+import io.net5.buffer.api.internal.SendFromSupplier;
 
 import java.util.function.Function;
 import java.util.function.Supplier;
diff --git a/buffer/src/main/java/io/netty/buffer/api/StandardAllocationTypes.java b/buffer/src/main/java/io/net5/buffer/api/StandardAllocationTypes.java
similarity index 97%
rename from buffer/src/main/java/io/netty/buffer/api/StandardAllocationTypes.java
rename to buffer/src/main/java/io/net5/buffer/api/StandardAllocationTypes.java
index e3ce0103fc..f313d05913 100644
--- a/buffer/src/main/java/io/netty/buffer/api/StandardAllocationTypes.java
+++ b/buffer/src/main/java/io/net5/buffer/api/StandardAllocationTypes.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api;
+package io.net5.buffer.api;
 
 /**
  * Standard implementations of {@link AllocationType} that all {@linkplain MemoryManager memory managers} should
diff --git a/buffer/src/main/java/io/netty/buffer/api/WritableComponent.java b/buffer/src/main/java/io/net5/buffer/api/WritableComponent.java
similarity index 98%
rename from buffer/src/main/java/io/netty/buffer/api/WritableComponent.java
rename to buffer/src/main/java/io/net5/buffer/api/WritableComponent.java
index d4a1fa17f2..a44df3c083 100644
--- a/buffer/src/main/java/io/netty/buffer/api/WritableComponent.java
+++ b/buffer/src/main/java/io/net5/buffer/api/WritableComponent.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api;
+package io.net5.buffer.api;
 
 import java.nio.ByteBuffer;
 
diff --git a/buffer/src/main/java/io/netty/buffer/api/WritableComponentProcessor.java b/buffer/src/main/java/io/net5/buffer/api/WritableComponentProcessor.java
similarity index 98%
rename from buffer/src/main/java/io/netty/buffer/api/WritableComponentProcessor.java
rename to buffer/src/main/java/io/net5/buffer/api/WritableComponentProcessor.java
index a10c67f61b..f0f88fe89a 100644
--- a/buffer/src/main/java/io/netty/buffer/api/WritableComponentProcessor.java
+++ b/buffer/src/main/java/io/net5/buffer/api/WritableComponentProcessor.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api;
+package io.net5.buffer.api;
 
 import java.nio.ByteBuffer;
 
diff --git a/buffer/src/main/java/io/netty/buffer/api/adaptor/BufferIntegratable.java b/buffer/src/main/java/io/net5/buffer/api/adaptor/BufferIntegratable.java
similarity index 83%
rename from buffer/src/main/java/io/netty/buffer/api/adaptor/BufferIntegratable.java
rename to buffer/src/main/java/io/net5/buffer/api/adaptor/BufferIntegratable.java
index fe9a73d675..c4da99b567 100644
--- a/buffer/src/main/java/io/netty/buffer/api/adaptor/BufferIntegratable.java
+++ b/buffer/src/main/java/io/net5/buffer/api/adaptor/BufferIntegratable.java
@@ -13,13 +13,13 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api.adaptor;
+package io.net5.buffer.api.adaptor;
 
-import io.netty.buffer.ByteBufConvertible;
-import io.netty.util.ReferenceCounted;
+import io.net5.buffer.ByteBufConvertible;
+import io.net5.util.ReferenceCounted;
 
 /**
- * Interfaces that are required for an object to stand-in for a {@link io.netty.buffer.ByteBuf} in Netty.
+ * Interfaces that are required for an object to stand-in for a {@link io.net5.buffer.ByteBuf} in Netty.
  */
 public interface BufferIntegratable extends ByteBufConvertible, ReferenceCounted {
 }
diff --git a/buffer/src/main/java/io/netty/buffer/api/adaptor/ByteBufAdaptor.java b/buffer/src/main/java/io/net5/buffer/api/adaptor/ByteBufAdaptor.java
similarity index 98%
rename from buffer/src/main/java/io/netty/buffer/api/adaptor/ByteBufAdaptor.java
rename to buffer/src/main/java/io/net5/buffer/api/adaptor/ByteBufAdaptor.java
index 6cf28b1d9e..281b07b647 100644
--- a/buffer/src/main/java/io/netty/buffer/api/adaptor/ByteBufAdaptor.java
+++ b/buffer/src/main/java/io/net5/buffer/api/adaptor/ByteBufAdaptor.java
@@ -13,22 +13,22 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api.adaptor;
+package io.net5.buffer.api.adaptor;
 
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.ByteBufAllocator;
-import io.netty.buffer.ByteBufConvertible;
-import io.netty.buffer.ByteBufUtil;
-import io.netty.buffer.DuplicatedByteBuf;
-import io.netty.buffer.SlicedByteBuf;
-import io.netty.buffer.SwappedByteBuf;
-import io.netty.buffer.Unpooled;
-import io.netty.buffer.api.Buffer;
-import io.netty.buffer.api.BufferAllocator;
-import io.netty.buffer.api.internal.ResourceSupport;
-import io.netty.buffer.api.internal.Statics;
-import io.netty.util.ByteProcessor;
-import io.netty.util.IllegalReferenceCountException;
+import io.net5.buffer.ByteBuf;
+import io.net5.buffer.ByteBufAllocator;
+import io.net5.buffer.ByteBufConvertible;
+import io.net5.buffer.ByteBufUtil;
+import io.net5.buffer.DuplicatedByteBuf;
+import io.net5.buffer.SlicedByteBuf;
+import io.net5.buffer.SwappedByteBuf;
+import io.net5.buffer.Unpooled;
+import io.net5.buffer.api.Buffer;
+import io.net5.buffer.api.BufferAllocator;
+import io.net5.buffer.api.internal.ResourceSupport;
+import io.net5.buffer.api.internal.Statics;
+import io.net5.util.ByteProcessor;
+import io.net5.util.IllegalReferenceCountException;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -42,8 +42,8 @@ import java.nio.charset.Charset;
 import java.util.Objects;
 import java.util.concurrent.atomic.AtomicReference;
 
-import static io.netty.buffer.api.internal.Statics.acquire;
-import static io.netty.buffer.api.internal.Statics.isOwned;
+import static io.net5.buffer.api.internal.Statics.acquire;
+import static io.net5.buffer.api.internal.Statics.isOwned;
 
 public final class ByteBufAdaptor extends ByteBuf {
     private final ByteBufAllocatorAdaptor alloc;
diff --git a/buffer/src/main/java/io/netty/buffer/api/adaptor/ByteBufAllocatorAdaptor.java b/buffer/src/main/java/io/net5/buffer/api/adaptor/ByteBufAllocatorAdaptor.java
similarity index 92%
rename from buffer/src/main/java/io/netty/buffer/api/adaptor/ByteBufAllocatorAdaptor.java
rename to buffer/src/main/java/io/net5/buffer/api/adaptor/ByteBufAllocatorAdaptor.java
index 26bc4c0191..42ddd5ce90 100644
--- a/buffer/src/main/java/io/netty/buffer/api/adaptor/ByteBufAllocatorAdaptor.java
+++ b/buffer/src/main/java/io/net5/buffer/api/adaptor/ByteBufAllocatorAdaptor.java
@@ -13,17 +13,17 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api.adaptor;
+package io.net5.buffer.api.adaptor;
 
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.ByteBufAllocator;
-import io.netty.buffer.CompositeByteBuf;
-import io.netty.buffer.api.Buffer;
-import io.netty.buffer.api.BufferAllocator;
-import io.netty.buffer.api.internal.AdaptableBuffer;
-import io.netty.util.internal.PlatformDependent;
+import io.net5.buffer.ByteBuf;
+import io.net5.buffer.ByteBufAllocator;
+import io.net5.buffer.CompositeByteBuf;
+import io.net5.buffer.api.Buffer;
+import io.net5.buffer.api.BufferAllocator;
+import io.net5.buffer.api.internal.AdaptableBuffer;
+import io.net5.util.internal.PlatformDependent;
 
-import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
+import static io.net5.util.internal.ObjectUtil.checkPositiveOrZero;
 import java.util.Objects;
 
 public class ByteBufAllocatorAdaptor implements ByteBufAllocator, AutoCloseable {
diff --git a/buffer/src/main/java/io/netty/buffer/api/adaptor/package-info.java b/buffer/src/main/java/io/net5/buffer/api/adaptor/package-info.java
similarity index 84%
rename from buffer/src/main/java/io/netty/buffer/api/adaptor/package-info.java
rename to buffer/src/main/java/io/net5/buffer/api/adaptor/package-info.java
index 12964152cb..a2cd8cdbe0 100644
--- a/buffer/src/main/java/io/netty/buffer/api/adaptor/package-info.java
+++ b/buffer/src/main/java/io/net5/buffer/api/adaptor/package-info.java
@@ -15,6 +15,6 @@
  */
 
 /**
- * Helpers for integrating with the existing {@link io.netty.buffer.ByteBuf} API.
+ * Helpers for integrating with the existing {@link io.net5.buffer.ByteBuf} API.
  */
-package io.netty.buffer.api.adaptor;
+package io.net5.buffer.api.adaptor;
diff --git a/buffer/src/main/java/io/netty/buffer/api/bytebuffer/ByteBufferMemoryManager.java b/buffer/src/main/java/io/net5/buffer/api/bytebuffer/ByteBufferMemoryManager.java
similarity index 84%
rename from buffer/src/main/java/io/netty/buffer/api/bytebuffer/ByteBufferMemoryManager.java
rename to buffer/src/main/java/io/net5/buffer/api/bytebuffer/ByteBufferMemoryManager.java
index 20a061a5b4..5aacf39e18 100644
--- a/buffer/src/main/java/io/netty/buffer/api/bytebuffer/ByteBufferMemoryManager.java
+++ b/buffer/src/main/java/io/net5/buffer/api/bytebuffer/ByteBufferMemoryManager.java
@@ -13,21 +13,21 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api.bytebuffer;
+package io.net5.buffer.api.bytebuffer;
 
-import io.netty.buffer.api.AllocationType;
-import io.netty.buffer.api.AllocatorControl;
-import io.netty.buffer.api.Buffer;
-import io.netty.buffer.api.Drop;
-import io.netty.buffer.api.MemoryManager;
-import io.netty.buffer.api.StandardAllocationTypes;
-import io.netty.buffer.api.internal.Statics;
+import io.net5.buffer.api.AllocationType;
+import io.net5.buffer.api.AllocatorControl;
+import io.net5.buffer.api.Buffer;
+import io.net5.buffer.api.Drop;
+import io.net5.buffer.api.MemoryManager;
+import io.net5.buffer.api.StandardAllocationTypes;
+import io.net5.buffer.api.internal.Statics;
 
 import java.lang.ref.Cleaner;
 import java.nio.ByteBuffer;
 
-import static io.netty.buffer.api.internal.Statics.bbslice;
-import static io.netty.buffer.api.internal.Statics.convert;
+import static io.net5.buffer.api.internal.Statics.bbslice;
+import static io.net5.buffer.api.internal.Statics.convert;
 
 public class ByteBufferMemoryManager implements MemoryManager {
     @Override
diff --git a/buffer/src/main/java/io/netty/buffer/api/bytebuffer/NioBuffer.java b/buffer/src/main/java/io/net5/buffer/api/bytebuffer/NioBuffer.java
similarity index 97%
rename from buffer/src/main/java/io/netty/buffer/api/bytebuffer/NioBuffer.java
rename to buffer/src/main/java/io/net5/buffer/api/bytebuffer/NioBuffer.java
index 2b25173da5..47f27d77b1 100644
--- a/buffer/src/main/java/io/netty/buffer/api/bytebuffer/NioBuffer.java
+++ b/buffer/src/main/java/io/net5/buffer/api/bytebuffer/NioBuffer.java
@@ -13,32 +13,32 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api.bytebuffer;
+package io.net5.buffer.api.bytebuffer;
 
-import io.netty.buffer.api.AllocatorControl;
-import io.netty.buffer.api.Buffer;
-import io.netty.buffer.api.BufferAllocator;
-import io.netty.buffer.api.BufferReadOnlyException;
-import io.netty.buffer.api.ByteCursor;
-import io.netty.buffer.api.Drop;
-import io.netty.buffer.api.Owned;
-import io.netty.buffer.api.ReadableComponent;
-import io.netty.buffer.api.ReadableComponentProcessor;
-import io.netty.buffer.api.WritableComponent;
-import io.netty.buffer.api.WritableComponentProcessor;
-import io.netty.buffer.api.internal.AdaptableBuffer;
-import io.netty.buffer.api.internal.ArcDrop;
-import io.netty.buffer.api.internal.Statics;
-import io.netty.util.internal.PlatformDependent;
+import io.net5.buffer.api.AllocatorControl;
+import io.net5.buffer.api.Buffer;
+import io.net5.buffer.api.BufferAllocator;
+import io.net5.buffer.api.BufferReadOnlyException;
+import io.net5.buffer.api.ByteCursor;
+import io.net5.buffer.api.Drop;
+import io.net5.buffer.api.Owned;
+import io.net5.buffer.api.ReadableComponent;
+import io.net5.buffer.api.ReadableComponentProcessor;
+import io.net5.buffer.api.WritableComponent;
+import io.net5.buffer.api.WritableComponentProcessor;
+import io.net5.buffer.api.internal.AdaptableBuffer;
+import io.net5.buffer.api.internal.ArcDrop;
+import io.net5.buffer.api.internal.Statics;
+import io.net5.util.internal.PlatformDependent;
 
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.nio.ReadOnlyBufferException;
 
-import static io.netty.buffer.api.internal.Statics.bbput;
-import static io.netty.buffer.api.internal.Statics.bbslice;
-import static io.netty.buffer.api.internal.Statics.bufferIsClosed;
-import static io.netty.buffer.api.internal.Statics.bufferIsReadOnly;
+import static io.net5.buffer.api.internal.Statics.bbput;
+import static io.net5.buffer.api.internal.Statics.bbslice;
+import static io.net5.buffer.api.internal.Statics.bufferIsClosed;
+import static io.net5.buffer.api.internal.Statics.bufferIsReadOnly;
 
 class NioBuffer extends AdaptableBuffer implements ReadableComponent, WritableComponent {
     private static final ByteBuffer CLOSED_BUFFER = ByteBuffer.allocate(0);
diff --git a/buffer/src/main/java/io/netty/buffer/api/bytebuffer/package-info.java b/buffer/src/main/java/io/net5/buffer/api/bytebuffer/package-info.java
similarity index 94%
rename from buffer/src/main/java/io/netty/buffer/api/bytebuffer/package-info.java
rename to buffer/src/main/java/io/net5/buffer/api/bytebuffer/package-info.java
index b49f8186e8..31c5576ef7 100644
--- a/buffer/src/main/java/io/netty/buffer/api/bytebuffer/package-info.java
+++ b/buffer/src/main/java/io/net5/buffer/api/bytebuffer/package-info.java
@@ -17,4 +17,4 @@
 /**
  * Safe ByteBuffer based implementation.
  */
-package io.netty.buffer.api.bytebuffer;
+package io.net5.buffer.api.bytebuffer;
diff --git a/buffer/src/main/java/io/netty/buffer/api/internal/AdaptableBuffer.java b/buffer/src/main/java/io/net5/buffer/api/internal/AdaptableBuffer.java
similarity index 85%
rename from buffer/src/main/java/io/netty/buffer/api/internal/AdaptableBuffer.java
rename to buffer/src/main/java/io/net5/buffer/api/internal/AdaptableBuffer.java
index 1317ac4010..82c3486cc7 100644
--- a/buffer/src/main/java/io/netty/buffer/api/internal/AdaptableBuffer.java
+++ b/buffer/src/main/java/io/net5/buffer/api/internal/AdaptableBuffer.java
@@ -13,17 +13,17 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api.internal;
+package io.net5.buffer.api.internal;
 
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.ByteBufAllocator;
-import io.netty.buffer.api.Buffer;
-import io.netty.buffer.api.Drop;
-import io.netty.buffer.api.adaptor.BufferIntegratable;
-import io.netty.buffer.api.adaptor.ByteBufAdaptor;
-import io.netty.buffer.api.adaptor.ByteBufAllocatorAdaptor;
-import io.netty.util.IllegalReferenceCountException;
-import io.netty.util.ReferenceCounted;
+import io.net5.buffer.ByteBuf;
+import io.net5.buffer.ByteBufAllocator;
+import io.net5.buffer.api.Buffer;
+import io.net5.buffer.api.Drop;
+import io.net5.buffer.api.adaptor.BufferIntegratable;
+import io.net5.buffer.api.adaptor.ByteBufAdaptor;
+import io.net5.buffer.api.adaptor.ByteBufAllocatorAdaptor;
+import io.net5.util.IllegalReferenceCountException;
+import io.net5.util.ReferenceCounted;
 
 public abstract class AdaptableBuffer>
         extends ResourceSupport implements BufferIntegratable, Buffer {
diff --git a/buffer/src/main/java/io/netty/buffer/api/internal/ArcDrop.java b/buffer/src/main/java/io/net5/buffer/api/internal/ArcDrop.java
similarity index 97%
rename from buffer/src/main/java/io/netty/buffer/api/internal/ArcDrop.java
rename to buffer/src/main/java/io/net5/buffer/api/internal/ArcDrop.java
index 6180b71202..b748b0edef 100644
--- a/buffer/src/main/java/io/netty/buffer/api/internal/ArcDrop.java
+++ b/buffer/src/main/java/io/net5/buffer/api/internal/ArcDrop.java
@@ -13,9 +13,9 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api.internal;
+package io.net5.buffer.api.internal;
 
-import io.netty.buffer.api.Drop;
+import io.net5.buffer.api.Drop;
 
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.VarHandle;
diff --git a/buffer/src/main/java/io/netty/buffer/api/internal/CleanerDrop.java b/buffer/src/main/java/io/net5/buffer/api/internal/CleanerDrop.java
similarity index 97%
rename from buffer/src/main/java/io/netty/buffer/api/internal/CleanerDrop.java
rename to buffer/src/main/java/io/net5/buffer/api/internal/CleanerDrop.java
index 75eae0c70a..72a22bcd6c 100644
--- a/buffer/src/main/java/io/netty/buffer/api/internal/CleanerDrop.java
+++ b/buffer/src/main/java/io/net5/buffer/api/internal/CleanerDrop.java
@@ -13,9 +13,9 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api.internal;
+package io.net5.buffer.api.internal;
 
-import io.netty.buffer.api.Drop;
+import io.net5.buffer.api.Drop;
 
 import java.lang.ref.Cleaner;
 import java.util.concurrent.atomic.AtomicReference;
diff --git a/buffer/src/main/java/io/netty/buffer/api/internal/LifecycleTracer.java b/buffer/src/main/java/io/net5/buffer/api/internal/LifecycleTracer.java
similarity index 95%
rename from buffer/src/main/java/io/netty/buffer/api/internal/LifecycleTracer.java
rename to buffer/src/main/java/io/net5/buffer/api/internal/LifecycleTracer.java
index 5c73d7563f..b23332919e 100644
--- a/buffer/src/main/java/io/netty/buffer/api/internal/LifecycleTracer.java
+++ b/buffer/src/main/java/io/net5/buffer/api/internal/LifecycleTracer.java
@@ -13,11 +13,11 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api.internal;
+package io.net5.buffer.api.internal;
 
-import io.netty.buffer.api.Drop;
-import io.netty.buffer.api.Owned;
-import io.netty.buffer.api.Resource;
+import io.net5.buffer.api.Drop;
+import io.net5.buffer.api.Owned;
+import io.net5.buffer.api.Resource;
 
 import java.util.ArrayDeque;
 import java.util.Set;
@@ -113,7 +113,7 @@ public abstract class LifecycleTracer {
 
     private static final class StackTracer extends LifecycleTracer {
         private static final int MAX_TRACE_POINTS = Math.min(Integer.getInteger(
-                "io.netty.buffer.api.internal.LifecycleTracer.MAX_TRACE_POINTS", 50), 1000);
+                "io.net5.buffer.api.internal.LifecycleTracer.MAX_TRACE_POINTS", 50), 1000);
         private static final StackWalker WALKER;
         static {
             int depth = Trace.TRACE_LIFECYCLE_DEPTH;
@@ -182,7 +182,7 @@ public abstract class LifecycleTracer {
         static {
             int traceDefault = 0;
             TRACE_LIFECYCLE_DEPTH = Math.max(Integer.getInteger(
-                    "io.netty.buffer.api.internal.LifecycleTracer.TRACE_LIFECYCLE_DEPTH", traceDefault), 0);
+                    "io.net5.buffer.api.internal.LifecycleTracer.TRACE_LIFECYCLE_DEPTH", traceDefault), 0);
         }
 
         final String name;
diff --git a/buffer/src/main/java/io/netty/buffer/api/internal/MemoryManagerLoader.java b/buffer/src/main/java/io/net5/buffer/api/internal/MemoryManagerLoader.java
similarity index 94%
rename from buffer/src/main/java/io/netty/buffer/api/internal/MemoryManagerLoader.java
rename to buffer/src/main/java/io/net5/buffer/api/internal/MemoryManagerLoader.java
index 57099fa887..a34dfeef9f 100644
--- a/buffer/src/main/java/io/netty/buffer/api/internal/MemoryManagerLoader.java
+++ b/buffer/src/main/java/io/net5/buffer/api/internal/MemoryManagerLoader.java
@@ -13,9 +13,9 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api.internal;
+package io.net5.buffer.api.internal;
 
-import io.netty.buffer.api.MemoryManager;
+import io.net5.buffer.api.MemoryManager;
 
 import java.util.ServiceLoader;
 import java.util.ServiceLoader.Provider;
diff --git a/buffer/src/main/java/io/netty/buffer/api/internal/MemoryManagerOverride.java b/buffer/src/main/java/io/net5/buffer/api/internal/MemoryManagerOverride.java
similarity index 89%
rename from buffer/src/main/java/io/netty/buffer/api/internal/MemoryManagerOverride.java
rename to buffer/src/main/java/io/net5/buffer/api/internal/MemoryManagerOverride.java
index dff0050cf8..762a0fb404 100644
--- a/buffer/src/main/java/io/netty/buffer/api/internal/MemoryManagerOverride.java
+++ b/buffer/src/main/java/io/net5/buffer/api/internal/MemoryManagerOverride.java
@@ -13,12 +13,12 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api.internal;
+package io.net5.buffer.api.internal;
 
-import io.netty.buffer.api.MemoryManager;
-import io.netty.buffer.api.bytebuffer.ByteBufferMemoryManager;
-import io.netty.util.internal.logging.InternalLogger;
-import io.netty.util.internal.logging.InternalLoggerFactory;
+import io.net5.buffer.api.MemoryManager;
+import io.net5.buffer.api.bytebuffer.ByteBufferMemoryManager;
+import io.net5.util.internal.logging.InternalLogger;
+import io.net5.util.internal.logging.InternalLoggerFactory;
 
 import java.util.Collections;
 import java.util.IdentityHashMap;
@@ -36,7 +36,7 @@ public final class MemoryManagerOverride {
     }
 
     private static MemoryManager createDefaultMemoryManagerInstance() {
-        String systemProperty = "io.netty.buffer.api.MemoryManager";
+        String systemProperty = "io.net5.buffer.api.MemoryManager";
         String configured = System.getProperty(systemProperty);
         if (configured != null) {
             Optional candidateManager = MemoryManager.lookupImplementation(configured);
diff --git a/buffer/src/main/java/io/netty/buffer/api/internal/ResourceSupport.java b/buffer/src/main/java/io/net5/buffer/api/internal/ResourceSupport.java
similarity index 97%
rename from buffer/src/main/java/io/netty/buffer/api/internal/ResourceSupport.java
rename to buffer/src/main/java/io/net5/buffer/api/internal/ResourceSupport.java
index 17eb7f09c9..a5671e3996 100644
--- a/buffer/src/main/java/io/netty/buffer/api/internal/ResourceSupport.java
+++ b/buffer/src/main/java/io/net5/buffer/api/internal/ResourceSupport.java
@@ -13,12 +13,12 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api.internal;
+package io.net5.buffer.api.internal;
 
-import io.netty.buffer.api.Drop;
-import io.netty.buffer.api.Owned;
-import io.netty.buffer.api.Resource;
-import io.netty.buffer.api.Send;
+import io.net5.buffer.api.Drop;
+import io.net5.buffer.api.Owned;
+import io.net5.buffer.api.Resource;
+import io.net5.buffer.api.Send;
 
 import java.util.Objects;
 
diff --git a/buffer/src/main/java/io/netty/buffer/api/internal/SendFromOwned.java b/buffer/src/main/java/io/net5/buffer/api/internal/SendFromOwned.java
similarity index 89%
rename from buffer/src/main/java/io/netty/buffer/api/internal/SendFromOwned.java
rename to buffer/src/main/java/io/net5/buffer/api/internal/SendFromOwned.java
index f86b78eb6e..c360f0ea6b 100644
--- a/buffer/src/main/java/io/netty/buffer/api/internal/SendFromOwned.java
+++ b/buffer/src/main/java/io/net5/buffer/api/internal/SendFromOwned.java
@@ -13,16 +13,16 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api.internal;
+package io.net5.buffer.api.internal;
 
-import io.netty.buffer.api.Drop;
-import io.netty.buffer.api.Owned;
-import io.netty.buffer.api.Resource;
-import io.netty.buffer.api.Send;
+import io.net5.buffer.api.Drop;
+import io.net5.buffer.api.Owned;
+import io.net5.buffer.api.Resource;
+import io.net5.buffer.api.Send;
 
 import java.lang.invoke.VarHandle;
 
-import static io.netty.buffer.api.internal.Statics.findVarHandle;
+import static io.net5.buffer.api.internal.Statics.findVarHandle;
 import static java.lang.invoke.MethodHandles.lookup;
 
 public class SendFromOwned, T extends ResourceSupport> implements Send {
diff --git a/buffer/src/main/java/io/netty/buffer/api/internal/SendFromSupplier.java b/buffer/src/main/java/io/net5/buffer/api/internal/SendFromSupplier.java
similarity index 92%
rename from buffer/src/main/java/io/netty/buffer/api/internal/SendFromSupplier.java
rename to buffer/src/main/java/io/net5/buffer/api/internal/SendFromSupplier.java
index 0b102f8f3c..105d2965d7 100644
--- a/buffer/src/main/java/io/netty/buffer/api/internal/SendFromSupplier.java
+++ b/buffer/src/main/java/io/net5/buffer/api/internal/SendFromSupplier.java
@@ -13,16 +13,16 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api.internal;
+package io.net5.buffer.api.internal;
 
-import io.netty.buffer.api.Resource;
-import io.netty.buffer.api.Send;
+import io.net5.buffer.api.Resource;
+import io.net5.buffer.api.Send;
 
 import java.lang.invoke.VarHandle;
 import java.util.Objects;
 import java.util.function.Supplier;
 
-import static io.netty.buffer.api.internal.Statics.findVarHandle;
+import static io.net5.buffer.api.internal.Statics.findVarHandle;
 import static java.lang.invoke.MethodHandles.lookup;
 
 public class SendFromSupplier> implements Send {
diff --git a/buffer/src/main/java/io/netty/buffer/api/internal/Statics.java b/buffer/src/main/java/io/net5/buffer/api/internal/Statics.java
similarity index 97%
rename from buffer/src/main/java/io/netty/buffer/api/internal/Statics.java
rename to buffer/src/main/java/io/net5/buffer/api/internal/Statics.java
index d613a3af5c..e421f607f8 100644
--- a/buffer/src/main/java/io/netty/buffer/api/internal/Statics.java
+++ b/buffer/src/main/java/io/net5/buffer/api/internal/Statics.java
@@ -13,12 +13,12 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api.internal;
+package io.net5.buffer.api.internal;
 
-import io.netty.buffer.api.Buffer;
-import io.netty.buffer.api.BufferClosedException;
-import io.netty.buffer.api.BufferReadOnlyException;
-import io.netty.buffer.api.Drop;
+import io.net5.buffer.api.Buffer;
+import io.net5.buffer.api.BufferClosedException;
+import io.net5.buffer.api.BufferReadOnlyException;
+import io.net5.buffer.api.Drop;
 
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
diff --git a/buffer/src/main/java/io/netty/buffer/api/internal/package-info.java b/buffer/src/main/java/io/net5/buffer/api/internal/package-info.java
similarity index 95%
rename from buffer/src/main/java/io/netty/buffer/api/internal/package-info.java
rename to buffer/src/main/java/io/net5/buffer/api/internal/package-info.java
index 5087850e82..b921e7c377 100644
--- a/buffer/src/main/java/io/netty/buffer/api/internal/package-info.java
+++ b/buffer/src/main/java/io/net5/buffer/api/internal/package-info.java
@@ -21,4 +21,4 @@
  *     Note: everything in this package is internal, and is not subject to backwards compatibility constraints.
  * 
  */
-package io.netty.buffer.api.internal;
+package io.net5.buffer.api.internal;
diff --git a/buffer/src/main/java/io/netty/buffer/api/package-info.java b/buffer/src/main/java/io/net5/buffer/api/package-info.java
similarity index 96%
rename from buffer/src/main/java/io/netty/buffer/api/package-info.java
rename to buffer/src/main/java/io/net5/buffer/api/package-info.java
index 920b173c8b..02f2b146e2 100644
--- a/buffer/src/main/java/io/netty/buffer/api/package-info.java
+++ b/buffer/src/main/java/io/net5/buffer/api/package-info.java
@@ -17,4 +17,4 @@
 /**
  * Incubating {@code Buffer} API, as a proposed alternative to {@code ByteBuf}.
  */
-package io.netty.buffer.api;
+package io.net5.buffer.api;
diff --git a/buffer/src/main/java/io/netty/buffer/api/pool/BufferAllocatorMetric.java b/buffer/src/main/java/io/net5/buffer/api/pool/BufferAllocatorMetric.java
similarity index 91%
rename from buffer/src/main/java/io/netty/buffer/api/pool/BufferAllocatorMetric.java
rename to buffer/src/main/java/io/net5/buffer/api/pool/BufferAllocatorMetric.java
index eeaa67627a..2d60fad80f 100644
--- a/buffer/src/main/java/io/netty/buffer/api/pool/BufferAllocatorMetric.java
+++ b/buffer/src/main/java/io/net5/buffer/api/pool/BufferAllocatorMetric.java
@@ -13,9 +13,9 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api.pool;
+package io.net5.buffer.api.pool;
 
-import io.netty.buffer.api.BufferAllocator;
+import io.net5.buffer.api.BufferAllocator;
 
 public interface BufferAllocatorMetric {
     /**
diff --git a/buffer/src/main/java/io/netty/buffer/api/pool/BufferAllocatorMetricProvider.java b/buffer/src/main/java/io/net5/buffer/api/pool/BufferAllocatorMetricProvider.java
similarity index 91%
rename from buffer/src/main/java/io/netty/buffer/api/pool/BufferAllocatorMetricProvider.java
rename to buffer/src/main/java/io/net5/buffer/api/pool/BufferAllocatorMetricProvider.java
index 1b19e732a0..1e963df586 100644
--- a/buffer/src/main/java/io/netty/buffer/api/pool/BufferAllocatorMetricProvider.java
+++ b/buffer/src/main/java/io/net5/buffer/api/pool/BufferAllocatorMetricProvider.java
@@ -13,9 +13,9 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api.pool;
+package io.net5.buffer.api.pool;
 
-import io.netty.buffer.api.BufferAllocator;
+import io.net5.buffer.api.BufferAllocator;
 
 public interface BufferAllocatorMetricProvider {
 
diff --git a/buffer/src/main/java/io/netty/buffer/api/pool/PoolArena.java b/buffer/src/main/java/io/net5/buffer/api/pool/PoolArena.java
similarity index 98%
rename from buffer/src/main/java/io/netty/buffer/api/pool/PoolArena.java
rename to buffer/src/main/java/io/net5/buffer/api/pool/PoolArena.java
index 983d6b5d2a..695b427665 100644
--- a/buffer/src/main/java/io/netty/buffer/api/pool/PoolArena.java
+++ b/buffer/src/main/java/io/net5/buffer/api/pool/PoolArena.java
@@ -13,13 +13,13 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api.pool;
+package io.net5.buffer.api.pool;
 
-import io.netty.buffer.api.AllocationType;
-import io.netty.buffer.api.AllocatorControl;
-import io.netty.buffer.api.Buffer;
-import io.netty.buffer.api.MemoryManager;
-import io.netty.util.internal.StringUtil;
+import io.net5.buffer.api.AllocationType;
+import io.net5.buffer.api.AllocatorControl;
+import io.net5.buffer.api.Buffer;
+import io.net5.buffer.api.MemoryManager;
+import io.net5.util.internal.StringUtil;
 
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.VarHandle;
@@ -28,7 +28,7 @@ import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.LongAdder;
 
-import static io.netty.buffer.api.pool.PoolChunk.isSubpage;
+import static io.net5.buffer.api.pool.PoolChunk.isSubpage;
 import static java.lang.Math.max;
 
 class PoolArena extends SizeClasses implements PoolArenaMetric, AllocatorControl {
diff --git a/buffer/src/main/java/io/netty/buffer/api/pool/PoolArenaMetric.java b/buffer/src/main/java/io/net5/buffer/api/pool/PoolArenaMetric.java
similarity index 98%
rename from buffer/src/main/java/io/netty/buffer/api/pool/PoolArenaMetric.java
rename to buffer/src/main/java/io/net5/buffer/api/pool/PoolArenaMetric.java
index 754dd7d2b2..bf00aed7b3 100644
--- a/buffer/src/main/java/io/netty/buffer/api/pool/PoolArenaMetric.java
+++ b/buffer/src/main/java/io/net5/buffer/api/pool/PoolArenaMetric.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api.pool;
+package io.net5.buffer.api.pool;
 
 import java.util.List;
 
diff --git a/buffer/src/main/java/io/netty/buffer/api/pool/PoolChunk.java b/buffer/src/main/java/io/net5/buffer/api/pool/PoolChunk.java
similarity index 98%
rename from buffer/src/main/java/io/netty/buffer/api/pool/PoolChunk.java
rename to buffer/src/main/java/io/net5/buffer/api/pool/PoolChunk.java
index 6285c69e11..cc751e6767 100644
--- a/buffer/src/main/java/io/netty/buffer/api/pool/PoolChunk.java
+++ b/buffer/src/main/java/io/net5/buffer/api/pool/PoolChunk.java
@@ -13,17 +13,17 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api.pool;
+package io.net5.buffer.api.pool;
 
-import io.netty.buffer.api.internal.CleanerDrop;
-import io.netty.buffer.api.AllocatorControl.UntetheredMemory;
-import io.netty.buffer.api.Buffer;
-import io.netty.buffer.api.Drop;
-import io.netty.buffer.api.MemoryManager;
-import io.netty.buffer.api.internal.ArcDrop;
-import io.netty.buffer.api.internal.Statics;
-import io.netty.util.internal.LongLongHashMap;
-import io.netty.util.internal.LongPriorityQueue;
+import io.net5.buffer.api.internal.CleanerDrop;
+import io.net5.buffer.api.AllocatorControl.UntetheredMemory;
+import io.net5.buffer.api.Buffer;
+import io.net5.buffer.api.Drop;
+import io.net5.buffer.api.MemoryManager;
+import io.net5.buffer.api.internal.ArcDrop;
+import io.net5.buffer.api.internal.Statics;
+import io.net5.util.internal.LongLongHashMap;
+import io.net5.util.internal.LongPriorityQueue;
 
 import java.util.PriorityQueue;
 
diff --git a/buffer/src/main/java/io/netty/buffer/api/pool/PoolChunkList.java b/buffer/src/main/java/io/net5/buffer/api/pool/PoolChunkList.java
similarity index 98%
rename from buffer/src/main/java/io/netty/buffer/api/pool/PoolChunkList.java
rename to buffer/src/main/java/io/net5/buffer/api/pool/PoolChunkList.java
index 03347f6481..8aa758dd31 100644
--- a/buffer/src/main/java/io/netty/buffer/api/pool/PoolChunkList.java
+++ b/buffer/src/main/java/io/net5/buffer/api/pool/PoolChunkList.java
@@ -13,10 +13,10 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api.pool;
+package io.net5.buffer.api.pool;
 
-import io.netty.buffer.api.AllocatorControl.UntetheredMemory;
-import io.netty.util.internal.StringUtil;
+import io.net5.buffer.api.AllocatorControl.UntetheredMemory;
+import io.net5.util.internal.StringUtil;
 
 import java.util.ArrayList;
 import java.util.Collections;
diff --git a/buffer/src/main/java/io/netty/buffer/api/pool/PoolChunkListMetric.java b/buffer/src/main/java/io/net5/buffer/api/pool/PoolChunkListMetric.java
similarity index 96%
rename from buffer/src/main/java/io/netty/buffer/api/pool/PoolChunkListMetric.java
rename to buffer/src/main/java/io/net5/buffer/api/pool/PoolChunkListMetric.java
index 9a60e1da52..9708b542f6 100644
--- a/buffer/src/main/java/io/netty/buffer/api/pool/PoolChunkListMetric.java
+++ b/buffer/src/main/java/io/net5/buffer/api/pool/PoolChunkListMetric.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api.pool;
+package io.net5.buffer.api.pool;
 
 /**
  * Metrics for a list of chunks.
diff --git a/buffer/src/main/java/io/netty/buffer/api/pool/PoolChunkMetric.java b/buffer/src/main/java/io/net5/buffer/api/pool/PoolChunkMetric.java
similarity index 96%
rename from buffer/src/main/java/io/netty/buffer/api/pool/PoolChunkMetric.java
rename to buffer/src/main/java/io/net5/buffer/api/pool/PoolChunkMetric.java
index 8a90384be3..6db4493e7a 100644
--- a/buffer/src/main/java/io/netty/buffer/api/pool/PoolChunkMetric.java
+++ b/buffer/src/main/java/io/net5/buffer/api/pool/PoolChunkMetric.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api.pool;
+package io.net5.buffer.api.pool;
 
 /**
  * Metrics for a chunk.
diff --git a/buffer/src/main/java/io/netty/buffer/api/pool/PoolSubpage.java b/buffer/src/main/java/io/net5/buffer/api/pool/PoolSubpage.java
similarity index 95%
rename from buffer/src/main/java/io/netty/buffer/api/pool/PoolSubpage.java
rename to buffer/src/main/java/io/net5/buffer/api/pool/PoolSubpage.java
index c0b558d996..64fb153d9f 100644
--- a/buffer/src/main/java/io/netty/buffer/api/pool/PoolSubpage.java
+++ b/buffer/src/main/java/io/net5/buffer/api/pool/PoolSubpage.java
@@ -13,13 +13,13 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api.pool;
+package io.net5.buffer.api.pool;
 
-import static io.netty.buffer.api.pool.PoolChunk.RUN_OFFSET_SHIFT;
-import static io.netty.buffer.api.pool.PoolChunk.SIZE_SHIFT;
-import static io.netty.buffer.api.pool.PoolChunk.IS_USED_SHIFT;
-import static io.netty.buffer.api.pool.PoolChunk.IS_SUBPAGE_SHIFT;
-import static io.netty.buffer.api.pool.SizeClasses.LOG2_QUANTUM;
+import static io.net5.buffer.api.pool.PoolChunk.RUN_OFFSET_SHIFT;
+import static io.net5.buffer.api.pool.PoolChunk.SIZE_SHIFT;
+import static io.net5.buffer.api.pool.PoolChunk.IS_USED_SHIFT;
+import static io.net5.buffer.api.pool.PoolChunk.IS_SUBPAGE_SHIFT;
+import static io.net5.buffer.api.pool.SizeClasses.LOG2_QUANTUM;
 
 final class PoolSubpage implements PoolSubpageMetric {
     final PoolChunk chunk;
diff --git a/buffer/src/main/java/io/netty/buffer/api/pool/PoolSubpageMetric.java b/buffer/src/main/java/io/net5/buffer/api/pool/PoolSubpageMetric.java
similarity index 97%
rename from buffer/src/main/java/io/netty/buffer/api/pool/PoolSubpageMetric.java
rename to buffer/src/main/java/io/net5/buffer/api/pool/PoolSubpageMetric.java
index 5793a0aadd..555a38d5f8 100644
--- a/buffer/src/main/java/io/netty/buffer/api/pool/PoolSubpageMetric.java
+++ b/buffer/src/main/java/io/net5/buffer/api/pool/PoolSubpageMetric.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api.pool;
+package io.net5.buffer.api.pool;
 
 /**
  * Metrics for a sub-page.
diff --git a/buffer/src/main/java/io/netty/buffer/api/pool/PoolThreadCache.java b/buffer/src/main/java/io/net5/buffer/api/pool/PoolThreadCache.java
similarity index 95%
rename from buffer/src/main/java/io/netty/buffer/api/pool/PoolThreadCache.java
rename to buffer/src/main/java/io/net5/buffer/api/pool/PoolThreadCache.java
index 2771b7f5e8..e02d69b298 100644
--- a/buffer/src/main/java/io/netty/buffer/api/pool/PoolThreadCache.java
+++ b/buffer/src/main/java/io/net5/buffer/api/pool/PoolThreadCache.java
@@ -13,24 +13,24 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api.pool;
+package io.net5.buffer.api.pool;
 
-import io.netty.buffer.api.AllocatorControl.UntetheredMemory;
-import io.netty.buffer.api.pool.PoolArena.SizeClass;
-import io.netty.util.internal.MathUtil;
-import io.netty.util.internal.ObjectPool;
-import io.netty.util.internal.ObjectPool.Handle;
-import io.netty.util.internal.PlatformDependent;
-import io.netty.util.internal.logging.InternalLogger;
-import io.netty.util.internal.logging.InternalLoggerFactory;
+import io.net5.buffer.api.AllocatorControl.UntetheredMemory;
+import io.net5.buffer.api.pool.PoolArena.SizeClass;
+import io.net5.util.internal.MathUtil;
+import io.net5.util.internal.ObjectPool;
+import io.net5.util.internal.ObjectPool.Handle;
+import io.net5.util.internal.PlatformDependent;
+import io.net5.util.internal.logging.InternalLogger;
+import io.net5.util.internal.logging.InternalLoggerFactory;
 
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Queue;
 
-import static io.netty.buffer.api.pool.PoolArena.SizeClass.Normal;
-import static io.netty.buffer.api.pool.PoolArena.SizeClass.Small;
-import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
+import static io.net5.buffer.api.pool.PoolArena.SizeClass.Normal;
+import static io.net5.buffer.api.pool.PoolArena.SizeClass.Small;
+import static io.net5.util.internal.ObjectUtil.checkPositiveOrZero;
 
 /**
  * Acts a Thread cache for allocations. This implementation is modelled after
diff --git a/buffer/src/main/java/io/netty/buffer/api/pool/PooledAllocatorControl.java b/buffer/src/main/java/io/net5/buffer/api/pool/PooledAllocatorControl.java
similarity index 89%
rename from buffer/src/main/java/io/netty/buffer/api/pool/PooledAllocatorControl.java
rename to buffer/src/main/java/io/net5/buffer/api/pool/PooledAllocatorControl.java
index 394c592306..90f09dbbaa 100644
--- a/buffer/src/main/java/io/netty/buffer/api/pool/PooledAllocatorControl.java
+++ b/buffer/src/main/java/io/net5/buffer/api/pool/PooledAllocatorControl.java
@@ -13,10 +13,10 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api.pool;
+package io.net5.buffer.api.pool;
 
-import io.netty.buffer.api.AllocatorControl;
-import io.netty.buffer.api.Buffer;
+import io.net5.buffer.api.AllocatorControl;
+import io.net5.buffer.api.Buffer;
 
 class PooledAllocatorControl implements AllocatorControl {
     PooledBufferAllocator parent;
diff --git a/buffer/src/main/java/io/netty/buffer/api/pool/PooledBufferAllocator.java b/buffer/src/main/java/io/net5/buffer/api/pool/PooledBufferAllocator.java
similarity index 83%
rename from buffer/src/main/java/io/netty/buffer/api/pool/PooledBufferAllocator.java
rename to buffer/src/main/java/io/net5/buffer/api/pool/PooledBufferAllocator.java
index 034821cfe1..ce7a004208 100644
--- a/buffer/src/main/java/io/netty/buffer/api/pool/PooledBufferAllocator.java
+++ b/buffer/src/main/java/io/net5/buffer/api/pool/PooledBufferAllocator.java
@@ -13,25 +13,25 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api.pool;
+package io.net5.buffer.api.pool;
 
-import io.netty.buffer.api.AllocationType;
-import io.netty.buffer.api.AllocatorControl.UntetheredMemory;
-import io.netty.buffer.api.Buffer;
-import io.netty.buffer.api.BufferAllocator;
-import io.netty.buffer.api.MemoryManager;
-import io.netty.buffer.api.StandardAllocationTypes;
-import io.netty.buffer.api.internal.Statics;
-import io.netty.util.NettyRuntime;
-import io.netty.util.concurrent.EventExecutor;
-import io.netty.util.concurrent.FastThreadLocal;
-import io.netty.util.concurrent.FastThreadLocalThread;
-import io.netty.util.internal.PlatformDependent;
-import io.netty.util.internal.StringUtil;
-import io.netty.util.internal.SystemPropertyUtil;
-import io.netty.util.internal.ThreadExecutorMap;
-import io.netty.util.internal.logging.InternalLogger;
-import io.netty.util.internal.logging.InternalLoggerFactory;
+import io.net5.buffer.api.AllocationType;
+import io.net5.buffer.api.AllocatorControl.UntetheredMemory;
+import io.net5.buffer.api.Buffer;
+import io.net5.buffer.api.BufferAllocator;
+import io.net5.buffer.api.MemoryManager;
+import io.net5.buffer.api.StandardAllocationTypes;
+import io.net5.buffer.api.internal.Statics;
+import io.net5.util.NettyRuntime;
+import io.net5.util.concurrent.EventExecutor;
+import io.net5.util.concurrent.FastThreadLocal;
+import io.net5.util.concurrent.FastThreadLocalThread;
+import io.net5.util.internal.PlatformDependent;
+import io.net5.util.internal.StringUtil;
+import io.net5.util.internal.SystemPropertyUtil;
+import io.net5.util.internal.ThreadExecutorMap;
+import io.net5.util.internal.logging.InternalLogger;
+import io.net5.util.internal.logging.InternalLoggerFactory;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -39,8 +39,8 @@ import java.util.List;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Supplier;
 
-import static io.netty.buffer.api.internal.Statics.allocatorClosedException;
-import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
+import static io.net5.buffer.api.internal.Statics.allocatorClosedException;
+import static io.net5.util.internal.ObjectUtil.checkPositiveOrZero;
 import static java.util.Objects.requireNonNull;
 
 public class PooledBufferAllocator implements BufferAllocator, BufferAllocatorMetricProvider {
@@ -67,8 +67,8 @@ public class PooledBufferAllocator implements BufferAllocator, BufferAllocatorMe
 
     static {
         int defaultAlignment = SystemPropertyUtil.getInt(
-                "io.netty.allocator.directMemoryCacheAlignment", 0);
-        int defaultPageSize = SystemPropertyUtil.getInt("io.netty.allocator.pageSize", 8192);
+                "io.net5.allocator.directMemoryCacheAlignment", 0);
+        int defaultPageSize = SystemPropertyUtil.getInt("io.net5.allocator.pageSize", 8192);
         Throwable pageSizeFallbackCause = null;
         try {
             validateAndCalculatePageShifts(defaultPageSize, defaultAlignment);
@@ -80,7 +80,7 @@ public class PooledBufferAllocator implements BufferAllocator, BufferAllocatorMe
         DEFAULT_PAGE_SIZE = defaultPageSize;
         DEFAULT_DIRECT_MEMORY_CACHE_ALIGNMENT = defaultAlignment;
 
-        int defaultMaxOrder = SystemPropertyUtil.getInt("io.netty.allocator.maxOrder", 9);
+        int defaultMaxOrder = SystemPropertyUtil.getInt("io.net5.allocator.maxOrder", 9);
         Throwable maxOrderFallbackCause = null;
         try {
             validateAndCalculateChunkSize(DEFAULT_PAGE_SIZE, defaultMaxOrder);
@@ -105,62 +105,62 @@ public class PooledBufferAllocator implements BufferAllocator, BufferAllocatorMe
         final int defaultChunkSize = DEFAULT_PAGE_SIZE << DEFAULT_MAX_ORDER;
         DEFAULT_NUM_HEAP_ARENA = Math.max(0,
                 SystemPropertyUtil.getInt(
-                        "io.netty.allocator.numArenas",
+                        "io.net5.allocator.numArenas",
                         (int) Math.min(
                                 defaultMinNumArena,
                                 runtime.maxMemory() / defaultChunkSize / 2 / 3)));
         DEFAULT_NUM_DIRECT_ARENA = Math.max(0,
                 SystemPropertyUtil.getInt(
-                        "io.netty.allocator.numDirectArenas",
+                        "io.net5.allocator.numDirectArenas",
                         (int) Math.min(
                                 defaultMinNumArena,
                                 PlatformDependent.maxDirectMemory() / defaultChunkSize / 2 / 3)));
 
         // cache sizes
-        DEFAULT_SMALL_CACHE_SIZE = SystemPropertyUtil.getInt("io.netty.allocator.smallCacheSize", 256);
-        DEFAULT_NORMAL_CACHE_SIZE = SystemPropertyUtil.getInt("io.netty.allocator.normalCacheSize", 64);
+        DEFAULT_SMALL_CACHE_SIZE = SystemPropertyUtil.getInt("io.net5.allocator.smallCacheSize", 256);
+        DEFAULT_NORMAL_CACHE_SIZE = SystemPropertyUtil.getInt("io.net5.allocator.normalCacheSize", 64);
 
         // 32 kb is the default maximum capacity of the cached buffer. Similar to what is explained in
         // 'Scalable memory allocation using jemalloc'
         DEFAULT_MAX_CACHED_BUFFER_CAPACITY = SystemPropertyUtil.getInt(
-                "io.netty.allocator.maxCachedBufferCapacity", 32 * 1024);
+                "io.net5.allocator.maxCachedBufferCapacity", 32 * 1024);
 
         // the number of threshold of allocations when cached entries will be freed up if not frequently used
         DEFAULT_CACHE_TRIM_INTERVAL = SystemPropertyUtil.getInt(
-                "io.netty.allocator.cacheTrimInterval", 8192);
+                "io.net5.allocator.cacheTrimInterval", 8192);
 
         DEFAULT_CACHE_TRIM_INTERVAL_MILLIS = SystemPropertyUtil.getLong(
-                "io.netty.allocator.cacheTrimIntervalMillis", 0);
+                "io.net5.allocator.cacheTrimIntervalMillis", 0);
 
         DEFAULT_USE_CACHE_FOR_ALL_THREADS = SystemPropertyUtil.getBoolean(
-                "io.netty.allocator.useCacheForAllThreads", false);
+                "io.net5.allocator.useCacheForAllThreads", false);
 
         // 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);
+                "io.net5.allocator.maxCachedByteBuffersPerChunk", 1023);
 
         if (logger.isDebugEnabled()) {
-            logger.debug("-Dio.netty.allocator.numArenas: {}", DEFAULT_NUM_HEAP_ARENA);
-            logger.debug("-Dio.netty.allocator.numDirectArenas: {}", DEFAULT_NUM_DIRECT_ARENA);
+            logger.debug("-Dio.net5.allocator.numArenas: {}", DEFAULT_NUM_HEAP_ARENA);
+            logger.debug("-Dio.net5.allocator.numDirectArenas: {}", DEFAULT_NUM_DIRECT_ARENA);
             if (pageSizeFallbackCause == null) {
-                logger.debug("-Dio.netty.allocator.pageSize: {}", DEFAULT_PAGE_SIZE);
+                logger.debug("-Dio.net5.allocator.pageSize: {}", DEFAULT_PAGE_SIZE);
             } else {
-                logger.debug("-Dio.netty.allocator.pageSize: {}", DEFAULT_PAGE_SIZE, pageSizeFallbackCause);
+                logger.debug("-Dio.net5.allocator.pageSize: {}", DEFAULT_PAGE_SIZE, pageSizeFallbackCause);
             }
             if (maxOrderFallbackCause == null) {
-                logger.debug("-Dio.netty.allocator.maxOrder: {}", DEFAULT_MAX_ORDER);
+                logger.debug("-Dio.net5.allocator.maxOrder: {}", DEFAULT_MAX_ORDER);
             } else {
-                logger.debug("-Dio.netty.allocator.maxOrder: {}", DEFAULT_MAX_ORDER, maxOrderFallbackCause);
+                logger.debug("-Dio.net5.allocator.maxOrder: {}", DEFAULT_MAX_ORDER, maxOrderFallbackCause);
             }
-            logger.debug("-Dio.netty.allocator.chunkSize: {}", DEFAULT_PAGE_SIZE << DEFAULT_MAX_ORDER);
-            logger.debug("-Dio.netty.allocator.smallCacheSize: {}", DEFAULT_SMALL_CACHE_SIZE);
-            logger.debug("-Dio.netty.allocator.normalCacheSize: {}", DEFAULT_NORMAL_CACHE_SIZE);
-            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.cacheTrimIntervalMillis: {}", DEFAULT_CACHE_TRIM_INTERVAL_MILLIS);
-            logger.debug("-Dio.netty.allocator.useCacheForAllThreads: {}", DEFAULT_USE_CACHE_FOR_ALL_THREADS);
-            logger.debug("-Dio.netty.allocator.maxCachedByteBuffersPerChunk: {}",
+            logger.debug("-Dio.net5.allocator.chunkSize: {}", DEFAULT_PAGE_SIZE << DEFAULT_MAX_ORDER);
+            logger.debug("-Dio.net5.allocator.smallCacheSize: {}", DEFAULT_SMALL_CACHE_SIZE);
+            logger.debug("-Dio.net5.allocator.normalCacheSize: {}", DEFAULT_NORMAL_CACHE_SIZE);
+            logger.debug("-Dio.net5.allocator.maxCachedBufferCapacity: {}", DEFAULT_MAX_CACHED_BUFFER_CAPACITY);
+            logger.debug("-Dio.net5.allocator.cacheTrimInterval: {}", DEFAULT_CACHE_TRIM_INTERVAL);
+            logger.debug("-Dio.net5.allocator.cacheTrimIntervalMillis: {}", DEFAULT_CACHE_TRIM_INTERVAL_MILLIS);
+            logger.debug("-Dio.net5.allocator.useCacheForAllThreads: {}", DEFAULT_USE_CACHE_FOR_ALL_THREADS);
+            logger.debug("-Dio.net5.allocator.maxCachedByteBuffersPerChunk: {}",
                     DEFAULT_MAX_CACHED_BYTEBUFFERS_PER_CHUNK);
         }
     }
@@ -349,56 +349,56 @@ public class PooledBufferAllocator implements BufferAllocator, BufferAllocatorMe
     }
 
     /**
-     * Default number of heap arenas - System Property: io.netty.allocator.numHeapArenas - default 2 * cores
+     * Default number of heap arenas - System Property: io.net5.allocator.numHeapArenas - default 2 * cores
      */
     public static int defaultNumHeapArena() {
         return DEFAULT_NUM_HEAP_ARENA;
     }
 
     /**
-     * Default number of direct arenas - System Property: io.netty.allocator.numDirectArenas - default 2 * cores
+     * Default number of direct arenas - System Property: io.net5.allocator.numDirectArenas - default 2 * cores
      */
     public static int defaultNumDirectArena() {
         return DEFAULT_NUM_DIRECT_ARENA;
     }
 
     /**
-     * Default buffer page size - System Property: io.netty.allocator.pageSize - default 8192
+     * Default buffer page size - System Property: io.net5.allocator.pageSize - default 8192
      */
     public static int defaultPageSize() {
         return DEFAULT_PAGE_SIZE;
     }
 
     /**
-     * Default maximum order - System Property: io.netty.allocator.maxOrder - default 11
+     * Default maximum order - System Property: io.net5.allocator.maxOrder - default 11
      */
     public static int defaultMaxOrder() {
         return DEFAULT_MAX_ORDER;
     }
 
     /**
-     * Default thread caching behavior - System Property: io.netty.allocator.useCacheForAllThreads - default true
+     * Default thread caching behavior - System Property: io.net5.allocator.useCacheForAllThreads - default true
      */
     public static boolean defaultUseCacheForAllThreads() {
         return DEFAULT_USE_CACHE_FOR_ALL_THREADS;
     }
 
     /**
-     * Default prefer direct - System Property: io.netty.noPreferDirect - default false
+     * Default prefer direct - System Property: io.net5.noPreferDirect - default false
      */
     public static boolean defaultPreferDirect() {
         return PlatformDependent.directBufferPreferred();
     }
 
     /**
-     * Default small cache size - System Property: io.netty.allocator.smallCacheSize - default 256
+     * Default small cache size - System Property: io.net5.allocator.smallCacheSize - default 256
      */
     public static int defaultSmallCacheSize() {
         return DEFAULT_SMALL_CACHE_SIZE;
     }
 
     /**
-     * Default normal cache size - System Property: io.netty.allocator.normalCacheSize - default 64
+     * Default normal cache size - System Property: io.net5.allocator.normalCacheSize - default 64
      */
     public static int defaultNormalCacheSize() {
         return DEFAULT_NORMAL_CACHE_SIZE;
diff --git a/buffer/src/main/java/io/netty/buffer/api/pool/PooledBufferAllocatorMetric.java b/buffer/src/main/java/io/net5/buffer/api/pool/PooledBufferAllocatorMetric.java
similarity index 97%
rename from buffer/src/main/java/io/netty/buffer/api/pool/PooledBufferAllocatorMetric.java
rename to buffer/src/main/java/io/net5/buffer/api/pool/PooledBufferAllocatorMetric.java
index e4d4f0465e..d5b52e3284 100644
--- a/buffer/src/main/java/io/netty/buffer/api/pool/PooledBufferAllocatorMetric.java
+++ b/buffer/src/main/java/io/net5/buffer/api/pool/PooledBufferAllocatorMetric.java
@@ -13,9 +13,9 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api.pool;
+package io.net5.buffer.api.pool;
 
-import io.netty.util.internal.StringUtil;
+import io.net5.util.internal.StringUtil;
 
 import java.util.List;
 
diff --git a/buffer/src/main/java/io/netty/buffer/api/pool/PooledDrop.java b/buffer/src/main/java/io/net5/buffer/api/pool/PooledDrop.java
similarity index 92%
rename from buffer/src/main/java/io/netty/buffer/api/pool/PooledDrop.java
rename to buffer/src/main/java/io/net5/buffer/api/pool/PooledDrop.java
index a3191a26d9..9d1887573c 100644
--- a/buffer/src/main/java/io/netty/buffer/api/pool/PooledDrop.java
+++ b/buffer/src/main/java/io/net5/buffer/api/pool/PooledDrop.java
@@ -13,10 +13,10 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api.pool;
+package io.net5.buffer.api.pool;
 
-import io.netty.buffer.api.Buffer;
-import io.netty.buffer.api.Drop;
+import io.net5.buffer.api.Buffer;
+import io.net5.buffer.api.Drop;
 
 class PooledDrop implements Drop {
     private final PoolArena arena;
diff --git a/buffer/src/main/java/io/netty/buffer/api/pool/SizeClasses.java b/buffer/src/main/java/io/net5/buffer/api/pool/SizeClasses.java
similarity index 99%
rename from buffer/src/main/java/io/netty/buffer/api/pool/SizeClasses.java
rename to buffer/src/main/java/io/net5/buffer/api/pool/SizeClasses.java
index 534cdf1a22..82f4fcb345 100644
--- a/buffer/src/main/java/io/netty/buffer/api/pool/SizeClasses.java
+++ b/buffer/src/main/java/io/net5/buffer/api/pool/SizeClasses.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api.pool;
+package io.net5.buffer.api.pool;
 
 import java.util.concurrent.ConcurrentHashMap;
 
diff --git a/buffer/src/main/java/io/netty/buffer/api/pool/SizeClassesMetric.java b/buffer/src/main/java/io/net5/buffer/api/pool/SizeClassesMetric.java
similarity index 98%
rename from buffer/src/main/java/io/netty/buffer/api/pool/SizeClassesMetric.java
rename to buffer/src/main/java/io/net5/buffer/api/pool/SizeClassesMetric.java
index 3f3ac3e383..afcb372a34 100644
--- a/buffer/src/main/java/io/netty/buffer/api/pool/SizeClassesMetric.java
+++ b/buffer/src/main/java/io/net5/buffer/api/pool/SizeClassesMetric.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api.pool;
+package io.net5.buffer.api.pool;
 
 /**
  * Expose metrics for an SizeClasses.
diff --git a/buffer/src/main/java/io/netty/buffer/api/pool/UnpooledUnthetheredMemory.java b/buffer/src/main/java/io/net5/buffer/api/pool/UnpooledUnthetheredMemory.java
similarity index 84%
rename from buffer/src/main/java/io/netty/buffer/api/pool/UnpooledUnthetheredMemory.java
rename to buffer/src/main/java/io/net5/buffer/api/pool/UnpooledUnthetheredMemory.java
index f30e253ea3..46c16836f4 100644
--- a/buffer/src/main/java/io/netty/buffer/api/pool/UnpooledUnthetheredMemory.java
+++ b/buffer/src/main/java/io/net5/buffer/api/pool/UnpooledUnthetheredMemory.java
@@ -13,14 +13,14 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api.pool;
+package io.net5.buffer.api.pool;
 
-import io.netty.buffer.api.AllocationType;
-import io.netty.buffer.api.AllocatorControl;
-import io.netty.buffer.api.Buffer;
-import io.netty.buffer.api.Drop;
-import io.netty.buffer.api.MemoryManager;
-import io.netty.buffer.api.internal.Statics;
+import io.net5.buffer.api.AllocationType;
+import io.net5.buffer.api.AllocatorControl;
+import io.net5.buffer.api.Buffer;
+import io.net5.buffer.api.Drop;
+import io.net5.buffer.api.MemoryManager;
+import io.net5.buffer.api.internal.Statics;
 
 @SuppressWarnings("unchecked")
 class UnpooledUnthetheredMemory implements AllocatorControl.UntetheredMemory {
diff --git a/buffer/src/main/java/io/netty/buffer/api/pool/package-info.java b/buffer/src/main/java/io/net5/buffer/api/pool/package-info.java
similarity index 83%
rename from buffer/src/main/java/io/netty/buffer/api/pool/package-info.java
rename to buffer/src/main/java/io/net5/buffer/api/pool/package-info.java
index ce60c3b685..f5183f8df3 100644
--- a/buffer/src/main/java/io/netty/buffer/api/pool/package-info.java
+++ b/buffer/src/main/java/io/net5/buffer/api/pool/package-info.java
@@ -14,6 +14,6 @@
  * under the License.
  */
 /**
- * A pooling {@link io.netty.buffer.api.BufferAllocator} implementation based on jemalloc.
+ * A pooling {@link io.net5.buffer.api.BufferAllocator} implementation based on jemalloc.
  */
-package io.netty.buffer.api.pool;
+package io.net5.buffer.api.pool;
diff --git a/buffer/src/main/java/io/netty/buffer/api/unsafe/UnsafeBuffer.java b/buffer/src/main/java/io/net5/buffer/api/unsafe/UnsafeBuffer.java
similarity index 98%
rename from buffer/src/main/java/io/netty/buffer/api/unsafe/UnsafeBuffer.java
rename to buffer/src/main/java/io/net5/buffer/api/unsafe/UnsafeBuffer.java
index ae814871ef..a48b422236 100644
--- a/buffer/src/main/java/io/netty/buffer/api/unsafe/UnsafeBuffer.java
+++ b/buffer/src/main/java/io/net5/buffer/api/unsafe/UnsafeBuffer.java
@@ -13,31 +13,31 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api.unsafe;
+package io.net5.buffer.api.unsafe;
 
-import io.netty.buffer.api.AllocatorControl;
-import io.netty.buffer.api.Buffer;
-import io.netty.buffer.api.BufferReadOnlyException;
-import io.netty.buffer.api.ByteCursor;
-import io.netty.buffer.api.Drop;
-import io.netty.buffer.api.Owned;
-import io.netty.buffer.api.ReadableComponent;
-import io.netty.buffer.api.ReadableComponentProcessor;
-import io.netty.buffer.api.WritableComponent;
-import io.netty.buffer.api.WritableComponentProcessor;
-import io.netty.buffer.api.internal.AdaptableBuffer;
-import io.netty.buffer.api.internal.ArcDrop;
-import io.netty.buffer.api.internal.Statics;
-import io.netty.util.internal.PlatformDependent;
+import io.net5.buffer.api.AllocatorControl;
+import io.net5.buffer.api.Buffer;
+import io.net5.buffer.api.BufferReadOnlyException;
+import io.net5.buffer.api.ByteCursor;
+import io.net5.buffer.api.Drop;
+import io.net5.buffer.api.Owned;
+import io.net5.buffer.api.ReadableComponent;
+import io.net5.buffer.api.ReadableComponentProcessor;
+import io.net5.buffer.api.WritableComponent;
+import io.net5.buffer.api.WritableComponentProcessor;
+import io.net5.buffer.api.internal.AdaptableBuffer;
+import io.net5.buffer.api.internal.ArcDrop;
+import io.net5.buffer.api.internal.Statics;
+import io.net5.util.internal.PlatformDependent;
 
 import java.lang.ref.Reference;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.nio.ReadOnlyBufferException;
 
-import static io.netty.buffer.api.internal.Statics.bbslice;
-import static io.netty.buffer.api.internal.Statics.bufferIsClosed;
-import static io.netty.buffer.api.internal.Statics.bufferIsReadOnly;
+import static io.net5.buffer.api.internal.Statics.bbslice;
+import static io.net5.buffer.api.internal.Statics.bufferIsClosed;
+import static io.net5.buffer.api.internal.Statics.bufferIsReadOnly;
 
 class UnsafeBuffer extends AdaptableBuffer implements ReadableComponent, WritableComponent {
     private static final int CLOSED_SIZE = -1;
diff --git a/buffer/src/main/java/io/netty/buffer/api/unsafe/UnsafeCleanerDrop.java b/buffer/src/main/java/io/net5/buffer/api/unsafe/UnsafeCleanerDrop.java
similarity index 88%
rename from buffer/src/main/java/io/netty/buffer/api/unsafe/UnsafeCleanerDrop.java
rename to buffer/src/main/java/io/net5/buffer/api/unsafe/UnsafeCleanerDrop.java
index a19d4b4572..693a79557c 100644
--- a/buffer/src/main/java/io/netty/buffer/api/unsafe/UnsafeCleanerDrop.java
+++ b/buffer/src/main/java/io/net5/buffer/api/unsafe/UnsafeCleanerDrop.java
@@ -13,12 +13,12 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api.unsafe;
+package io.net5.buffer.api.unsafe;
 
-import io.netty.buffer.api.Buffer;
-import io.netty.buffer.api.Drop;
-import io.netty.buffer.api.internal.Statics;
-import io.netty.util.internal.PlatformDependent;
+import io.net5.buffer.api.Buffer;
+import io.net5.buffer.api.Drop;
+import io.net5.buffer.api.internal.Statics;
+import io.net5.util.internal.PlatformDependent;
 
 import java.lang.ref.Cleaner;
 
diff --git a/buffer/src/main/java/io/netty/buffer/api/unsafe/UnsafeMemory.java b/buffer/src/main/java/io/net5/buffer/api/unsafe/UnsafeMemory.java
similarity index 96%
rename from buffer/src/main/java/io/netty/buffer/api/unsafe/UnsafeMemory.java
rename to buffer/src/main/java/io/net5/buffer/api/unsafe/UnsafeMemory.java
index 27de302d38..5344f35954 100644
--- a/buffer/src/main/java/io/netty/buffer/api/unsafe/UnsafeMemory.java
+++ b/buffer/src/main/java/io/net5/buffer/api/unsafe/UnsafeMemory.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api.unsafe;
+package io.net5.buffer.api.unsafe;
 
 class UnsafeMemory {
     final Object base;
diff --git a/buffer/src/main/java/io/netty/buffer/api/unsafe/UnsafeMemoryManager.java b/buffer/src/main/java/io/net5/buffer/api/unsafe/UnsafeMemoryManager.java
similarity index 88%
rename from buffer/src/main/java/io/netty/buffer/api/unsafe/UnsafeMemoryManager.java
rename to buffer/src/main/java/io/net5/buffer/api/unsafe/UnsafeMemoryManager.java
index 861c65534b..ea9ad7a546 100644
--- a/buffer/src/main/java/io/netty/buffer/api/unsafe/UnsafeMemoryManager.java
+++ b/buffer/src/main/java/io/net5/buffer/api/unsafe/UnsafeMemoryManager.java
@@ -13,20 +13,20 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.buffer.api.unsafe;
+package io.net5.buffer.api.unsafe;
 
-import io.netty.buffer.api.AllocationType;
-import io.netty.buffer.api.AllocatorControl;
-import io.netty.buffer.api.Buffer;
-import io.netty.buffer.api.Drop;
-import io.netty.buffer.api.MemoryManager;
-import io.netty.buffer.api.StandardAllocationTypes;
-import io.netty.buffer.api.internal.Statics;
-import io.netty.util.internal.PlatformDependent;
+import io.net5.buffer.api.AllocationType;
+import io.net5.buffer.api.AllocatorControl;
+import io.net5.buffer.api.Buffer;
+import io.net5.buffer.api.Drop;
+import io.net5.buffer.api.MemoryManager;
+import io.net5.buffer.api.StandardAllocationTypes;
+import io.net5.buffer.api.internal.Statics;
+import io.net5.util.internal.PlatformDependent;
 
 import java.lang.ref.Cleaner;
 
-import static io.netty.buffer.api.internal.Statics.convert;
+import static io.net5.buffer.api.internal.Statics.convert;
 
 public class UnsafeMemoryManager implements MemoryManager {
     public UnsafeMemoryManager() {
diff --git a/buffer/src/main/java/io/net5/buffer/api/unsafe/package-info.java b/buffer/src/main/java/io/net5/buffer/api/unsafe/package-info.java
new file mode 100644
index 0000000000..74e02f6ed0
--- /dev/null
+++ b/buffer/src/main/java/io/net5/buffer/api/unsafe/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2021 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ *   https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * A {@link io.net5.buffer.api.Buffer} implementation that is based on {@code sun.misc.Unsafe}.
+ */
+package io.net5.buffer.api.unsafe;
diff --git a/buffer/src/main/java/io/netty/buffer/package-info.java b/buffer/src/main/java/io/net5/buffer/package-info.java
similarity index 86%
rename from buffer/src/main/java/io/netty/buffer/package-info.java
rename to buffer/src/main/java/io/net5/buffer/package-info.java
index 4ed7939e50..b4dd8b98b9 100644
--- a/buffer/src/main/java/io/netty/buffer/package-info.java
+++ b/buffer/src/main/java/io/net5/buffer/package-info.java
@@ -21,7 +21,7 @@
  * Netty uses its own buffer API instead of NIO {@link java.nio.ByteBuffer} to
  * represent a sequence of bytes. This approach has significant advantage over
  * using {@link java.nio.ByteBuffer}.  Netty's new buffer type,
- * {@link io.netty.buffer.ByteBuf}, has been designed from ground
+ * {@link io.net5.buffer.ByteBuf}, has been designed from ground
  * up to address the problems of {@link java.nio.ByteBuffer} and to meet the
  * daily needs of network application developers.  To list a few cool features:
  * 
    @@ -35,13 +35,13 @@ * *

    Extensibility

    * - * {@link io.netty.buffer.ByteBuf} has rich set of operations + * {@link io.net5.buffer.ByteBuf} has rich set of operations * optimized for rapid protocol implementation. For example, - * {@link io.netty.buffer.ByteBuf} provides various operations + * {@link io.net5.buffer.ByteBuf} provides various operations * for accessing unsigned values and strings and searching for certain byte * sequence in a buffer. You can also extend or wrap existing buffer type * to add convenient accessors. The custom buffer type still implements - * {@link io.netty.buffer.ByteBuf} interface rather than + * {@link io.net5.buffer.ByteBuf} interface rather than * introducing an incompatible type. * *

    Transparent Zero Copy

    @@ -70,18 +70,18 @@ * // The composite type is incompatible with the component type. * ByteBuffer[] message = new ByteBuffer[] { header, body }; *
- * By contrast, {@link io.netty.buffer.ByteBuf} does not have such + * By contrast, {@link io.net5.buffer.ByteBuf} does not have such * caveats because it is fully extensible and has a built-in composite buffer * type. *
  * // The composite type is compatible with the component type.
- * {@link io.netty.buffer.ByteBuf} message = {@link io.netty.buffer.Unpooled}.wrappedBuffer(header, body);
+ * {@link io.net5.buffer.ByteBuf} message = {@link io.net5.buffer.Unpooled}.wrappedBuffer(header, body);
  *
  * // Therefore, you can even create a composite by mixing a composite and an
  * // ordinary buffer.
- * {@link io.netty.buffer.ByteBuf} messageWithFooter = {@link io.netty.buffer.Unpooled}.wrappedBuffer(message, footer);
+ * {@link io.net5.buffer.ByteBuf} messageWithFooter = {@link io.net5.buffer.Unpooled}.wrappedBuffer(message, footer);
  *
- * // Because the composite is still a {@link io.netty.buffer.ByteBuf}, you can access its content
+ * // Because the composite is still a {@link io.net5.buffer.ByteBuf}, you can access its content
  * // easily, and the accessor method will behave just like it's a single buffer
  * // even if the region you want to access spans over multiple components.  The
  * // unsigned integer being read here is located across body and footer.
@@ -100,7 +100,7 @@
  * 
  * // A new dynamic buffer is created.  Internally, the actual buffer is created
  * // lazily to avoid potentially wasted memory space.
- * {@link io.netty.buffer.ByteBuf} b = {@link io.netty.buffer.Unpooled}.buffer(4);
+ * {@link io.net5.buffer.ByteBuf} b = {@link io.net5.buffer.Unpooled}.buffer(4);
  *
  * // When the first write attempt is made, the internal buffer is created with
  * // the specified initial capacity (4).
@@ -118,11 +118,11 @@
  * 

Better Performance

* * Most frequently used buffer implementation of - * {@link io.netty.buffer.ByteBuf} is a very thin wrapper of a + * {@link io.net5.buffer.ByteBuf} is a very thin wrapper of a * byte array (i.e. {@code byte[]}). Unlike {@link java.nio.ByteBuffer}, it has * no complicated boundary check and index compensation, and therefore it is * easier for a JVM to optimize the buffer access. More complicated buffer * implementation is used only for sliced or composite buffers, and it performs * as well as {@link java.nio.ByteBuffer}. */ -package io.netty.buffer; +package io.net5.buffer; diff --git a/buffer/src/main/java/io/netty/buffer/search/AbstractMultiSearchProcessorFactory.java b/buffer/src/main/java/io/net5/buffer/search/AbstractMultiSearchProcessorFactory.java similarity index 94% rename from buffer/src/main/java/io/netty/buffer/search/AbstractMultiSearchProcessorFactory.java rename to buffer/src/main/java/io/net5/buffer/search/AbstractMultiSearchProcessorFactory.java index e05a497d5c..ca3d2bf252 100644 --- a/buffer/src/main/java/io/netty/buffer/search/AbstractMultiSearchProcessorFactory.java +++ b/buffer/src/main/java/io/net5/buffer/search/AbstractMultiSearchProcessorFactory.java @@ -12,7 +12,7 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package io.netty.buffer.search; +package io.net5.buffer.search; /** * Base class for precomputed factories that create {@link MultiSearchProcessor}s. @@ -32,7 +32,7 @@ package io.netty.buffer.search; * In such case {@link MultiSearchProcessor#getFoundNeedleId()} returns the index of the longest matching {@code needle} * in the array of {@code needles}. *
- * Usage example (given that the {@code haystack} is a {@link io.netty.buffer.ByteBuf} containing "ABCD" and the + * Usage example (given that the {@code haystack} is a {@link io.net5.buffer.ByteBuf} containing "ABCD" and the * {@code needles} are "AB", "BC" and "CD"): *
  *      MultiSearchProcessorFactory factory = MultiSearchProcessorFactory.newAhoCorasicSearchProcessorFactory(
@@ -80,8 +80,8 @@ public abstract class AbstractMultiSearchProcessorFactory implements MultiSearch
      * prefixes of the {@code needles}.
      * 
* Search (the actual application of {@link MultiSearchProcessor}) time is linear in the size of - * {@link io.netty.buffer.ByteBuf} on which the search is peformed ({@code O(|haystack|)}). - * Every byte of {@link io.netty.buffer.ByteBuf} is processed only once, sequentually, regardles of + * {@link io.net5.buffer.ByteBuf} on which the search is peformed ({@code O(|haystack|)}). + * Every byte of {@link io.net5.buffer.ByteBuf} is processed only once, sequentually, regardles of * the number of {@code needles} being searched for. * * @param needles a varargs array of arrays of bytes to search for diff --git a/buffer/src/main/java/io/netty/buffer/search/AbstractSearchProcessorFactory.java b/buffer/src/main/java/io/net5/buffer/search/AbstractSearchProcessorFactory.java similarity index 84% rename from buffer/src/main/java/io/netty/buffer/search/AbstractSearchProcessorFactory.java rename to buffer/src/main/java/io/net5/buffer/search/AbstractSearchProcessorFactory.java index f76b2d3e53..51e35e0366 100644 --- a/buffer/src/main/java/io/netty/buffer/search/AbstractSearchProcessorFactory.java +++ b/buffer/src/main/java/io/net5/buffer/search/AbstractSearchProcessorFactory.java @@ -12,7 +12,7 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package io.netty.buffer.search; +package io.net5.buffer.search; /** * Base class for precomputed factories that create {@link SearchProcessor}s. @@ -25,25 +25,25 @@ package io.netty.buffer.search; * (the {@code needle}), it contains precomputed data needed to perform the search, and is meant to be reused * whenever searching for the same {@code needle}. *
- * Note: implementations of {@link SearchProcessor} scan the {@link io.netty.buffer.ByteBuf} sequentially, + * Note: implementations of {@link SearchProcessor} scan the {@link io.net5.buffer.ByteBuf} sequentially, * one byte after another, without doing any random access. As a result, when using {@link SearchProcessor} - * with such methods as {@link io.netty.buffer.ByteBuf#forEachByte}, these methods return the index of the last byte - * of the found byte sequence within the {@link io.netty.buffer.ByteBuf} (which might feel counterintuitive, - * and different from {@link io.netty.buffer.ByteBufUtil#indexOf} which returns the index of the first byte + * with such methods as {@link io.net5.buffer.ByteBuf#forEachByte}, these methods return the index of the last byte + * of the found byte sequence within the {@link io.net5.buffer.ByteBuf} (which might feel counterintuitive, + * and different from {@link io.net5.buffer.ByteBufUtil#indexOf} which returns the index of the first byte * of found sequence). *
* A {@link SearchProcessor} is implemented as a * Finite State Automaton that contains a * small internal state which is updated with every byte processed. As a result, an instance of {@link SearchProcessor} * should not be reused across independent search sessions (eg. for searching in different - * {@link io.netty.buffer.ByteBuf}s). A new instance should be created with {@link AbstractSearchProcessorFactory} for + * {@link io.net5.buffer.ByteBuf}s). A new instance should be created with {@link AbstractSearchProcessorFactory} for * every search session. However, a {@link SearchProcessor} can (and should) be reused within the search session, * eg. when searching for all occurrences of the {@code needle} within the same {@code haystack}. That way, it can * also detect overlapping occurrences of the {@code needle} (eg. a string "ABABAB" contains two occurrences of "BAB" * that overlap by one character "B"). For this to work correctly, after an occurrence of the {@code needle} is * found ending at index {@code idx}, the search should continue starting from the index {@code idx + 1}. *
- * Example (given that the {@code haystack} is a {@link io.netty.buffer.ByteBuf} containing "ABABAB" and + * Example (given that the {@code haystack} is a {@link io.net5.buffer.ByteBuf} containing "ABABAB" and * the {@code needle} is "BAB"): *
  *     SearchProcessorFactory factory =
@@ -82,8 +82,8 @@ public abstract class AbstractSearchProcessorFactory implements SearchProcessorF
      * to the {@code needle} itself.
      * 
* Search (the actual application of {@link SearchProcessor}) time is linear in the size of - * {@link io.netty.buffer.ByteBuf} on which the search is peformed ({@code O(|haystack|)}). - * Every byte of {@link io.netty.buffer.ByteBuf} is processed only once, sequentually. + * {@link io.net5.buffer.ByteBuf} on which the search is peformed ({@code O(|haystack|)}). + * Every byte of {@link io.net5.buffer.ByteBuf} is processed only once, sequentually. * * @param needle an array of bytes to search for * @return a new instance of {@link KmpSearchProcessorFactory} precomputed for the given {@code needle} @@ -102,8 +102,8 @@ public abstract class AbstractSearchProcessorFactory implements SearchProcessorF * The factory allocates and retains a long[256] array. *
* Search (the actual application of {@link SearchProcessor}) time is linear in the size of - * {@link io.netty.buffer.ByteBuf} on which the search is peformed ({@code O(|haystack|)}). - * Every byte of {@link io.netty.buffer.ByteBuf} is processed only once, sequentually. + * {@link io.net5.buffer.ByteBuf} on which the search is peformed ({@code O(|haystack|)}). + * Every byte of {@link io.net5.buffer.ByteBuf} is processed only once, sequentually. * * @param needle an array of no more than 64 bytes to search for * @return a new instance of {@link BitapSearchProcessorFactory} precomputed for the given {@code needle} diff --git a/buffer/src/main/java/io/netty/buffer/search/AhoCorasicSearchProcessorFactory.java b/buffer/src/main/java/io/net5/buffer/search/AhoCorasicSearchProcessorFactory.java similarity index 97% rename from buffer/src/main/java/io/netty/buffer/search/AhoCorasicSearchProcessorFactory.java rename to buffer/src/main/java/io/net5/buffer/search/AhoCorasicSearchProcessorFactory.java index 5ee27c140c..3281f38c7e 100644 --- a/buffer/src/main/java/io/netty/buffer/search/AhoCorasicSearchProcessorFactory.java +++ b/buffer/src/main/java/io/net5/buffer/search/AhoCorasicSearchProcessorFactory.java @@ -12,9 +12,9 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package io.netty.buffer.search; +package io.net5.buffer.search; -import io.netty.util.internal.PlatformDependent; +import io.net5.util.internal.PlatformDependent; import java.util.ArrayDeque; import java.util.ArrayList; @@ -27,7 +27,7 @@ import java.util.Queue; * Use static {@link AbstractMultiSearchProcessorFactory#newAhoCorasicSearchProcessorFactory} * to create an instance of this factory. * Use {@link AhoCorasicSearchProcessorFactory#newSearchProcessor} to get an instance of - * {@link io.netty.util.ByteProcessor} implementation for performing the actual search. + * {@link io.net5.util.ByteProcessor} implementation for performing the actual search. * @see AbstractMultiSearchProcessorFactory */ public class AhoCorasicSearchProcessorFactory extends AbstractMultiSearchProcessorFactory { diff --git a/buffer/src/main/java/io/netty/buffer/search/BitapSearchProcessorFactory.java b/buffer/src/main/java/io/net5/buffer/search/BitapSearchProcessorFactory.java similarity index 94% rename from buffer/src/main/java/io/netty/buffer/search/BitapSearchProcessorFactory.java rename to buffer/src/main/java/io/net5/buffer/search/BitapSearchProcessorFactory.java index bb4a7c531e..4a9edf8254 100644 --- a/buffer/src/main/java/io/netty/buffer/search/BitapSearchProcessorFactory.java +++ b/buffer/src/main/java/io/net5/buffer/search/BitapSearchProcessorFactory.java @@ -12,15 +12,15 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package io.netty.buffer.search; +package io.net5.buffer.search; -import io.netty.util.internal.PlatformDependent; +import io.net5.util.internal.PlatformDependent; /** * Implements Bitap string search algorithm. * Use static {@link AbstractSearchProcessorFactory#newBitapSearchProcessorFactory} * to create an instance of this factory. - * Use {@link BitapSearchProcessorFactory#newSearchProcessor} to get an instance of {@link io.netty.util.ByteProcessor} + * Use {@link BitapSearchProcessorFactory#newSearchProcessor} to get an instance of {@link io.net5.util.ByteProcessor} * implementation for performing the actual search. * @see AbstractSearchProcessorFactory */ diff --git a/buffer/src/main/java/io/netty/buffer/search/KmpSearchProcessorFactory.java b/buffer/src/main/java/io/net5/buffer/search/KmpSearchProcessorFactory.java similarity index 95% rename from buffer/src/main/java/io/netty/buffer/search/KmpSearchProcessorFactory.java rename to buffer/src/main/java/io/net5/buffer/search/KmpSearchProcessorFactory.java index 5b16b7f842..3a49fa2277 100644 --- a/buffer/src/main/java/io/netty/buffer/search/KmpSearchProcessorFactory.java +++ b/buffer/src/main/java/io/net5/buffer/search/KmpSearchProcessorFactory.java @@ -12,9 +12,9 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package io.netty.buffer.search; +package io.net5.buffer.search; -import io.netty.util.internal.PlatformDependent; +import io.net5.util.internal.PlatformDependent; /** * Implements @@ -22,7 +22,7 @@ import io.netty.util.internal.PlatformDependent; * string search algorithm. * Use static {@link AbstractSearchProcessorFactory#newKmpSearchProcessorFactory} * to create an instance of this factory. - * Use {@link KmpSearchProcessorFactory#newSearchProcessor} to get an instance of {@link io.netty.util.ByteProcessor} + * Use {@link KmpSearchProcessorFactory#newSearchProcessor} to get an instance of {@link io.net5.util.ByteProcessor} * implementation for performing the actual search. * @see AbstractSearchProcessorFactory */ diff --git a/buffer/src/main/java/io/netty/buffer/search/MultiSearchProcessor.java b/buffer/src/main/java/io/net5/buffer/search/MultiSearchProcessor.java similarity index 96% rename from buffer/src/main/java/io/netty/buffer/search/MultiSearchProcessor.java rename to buffer/src/main/java/io/net5/buffer/search/MultiSearchProcessor.java index f7e99876f2..d5b2c0e9da 100644 --- a/buffer/src/main/java/io/netty/buffer/search/MultiSearchProcessor.java +++ b/buffer/src/main/java/io/net5/buffer/search/MultiSearchProcessor.java @@ -12,7 +12,7 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package io.netty.buffer.search; +package io.net5.buffer.search; /** * Interface for {@link SearchProcessor} that implements simultaneous search for multiple strings. diff --git a/buffer/src/main/java/io/netty/buffer/search/MultiSearchProcessorFactory.java b/buffer/src/main/java/io/net5/buffer/search/MultiSearchProcessorFactory.java similarity index 96% rename from buffer/src/main/java/io/netty/buffer/search/MultiSearchProcessorFactory.java rename to buffer/src/main/java/io/net5/buffer/search/MultiSearchProcessorFactory.java index 176ea8a12e..c222c0b75c 100644 --- a/buffer/src/main/java/io/netty/buffer/search/MultiSearchProcessorFactory.java +++ b/buffer/src/main/java/io/net5/buffer/search/MultiSearchProcessorFactory.java @@ -12,7 +12,7 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package io.netty.buffer.search; +package io.net5.buffer.search; public interface MultiSearchProcessorFactory extends SearchProcessorFactory { diff --git a/buffer/src/main/java/io/netty/buffer/search/SearchProcessor.java b/buffer/src/main/java/io/net5/buffer/search/SearchProcessor.java similarity index 92% rename from buffer/src/main/java/io/netty/buffer/search/SearchProcessor.java rename to buffer/src/main/java/io/net5/buffer/search/SearchProcessor.java index baefd25150..099304f970 100644 --- a/buffer/src/main/java/io/netty/buffer/search/SearchProcessor.java +++ b/buffer/src/main/java/io/net5/buffer/search/SearchProcessor.java @@ -12,9 +12,9 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package io.netty.buffer.search; +package io.net5.buffer.search; -import io.netty.util.ByteProcessor; +import io.net5.util.ByteProcessor; /** * Interface for {@link ByteProcessor} that implements string search. diff --git a/buffer/src/main/java/io/netty/buffer/search/SearchProcessorFactory.java b/buffer/src/main/java/io/net5/buffer/search/SearchProcessorFactory.java similarity index 96% rename from buffer/src/main/java/io/netty/buffer/search/SearchProcessorFactory.java rename to buffer/src/main/java/io/net5/buffer/search/SearchProcessorFactory.java index 17679d74b7..0b68aeb4d3 100644 --- a/buffer/src/main/java/io/netty/buffer/search/SearchProcessorFactory.java +++ b/buffer/src/main/java/io/net5/buffer/search/SearchProcessorFactory.java @@ -12,7 +12,7 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package io.netty.buffer.search; +package io.net5.buffer.search; public interface SearchProcessorFactory { diff --git a/buffer/src/main/java/io/netty/buffer/search/package-info.java b/buffer/src/main/java/io/net5/buffer/search/package-info.java similarity index 91% rename from buffer/src/main/java/io/netty/buffer/search/package-info.java rename to buffer/src/main/java/io/net5/buffer/search/package-info.java index 630e341b2d..94cff449bb 100644 --- a/buffer/src/main/java/io/netty/buffer/search/package-info.java +++ b/buffer/src/main/java/io/net5/buffer/search/package-info.java @@ -15,6 +15,6 @@ */ /** - * Utility classes for performing efficient substring search within {@link io.netty.buffer.ByteBuf}. + * Utility classes for performing efficient substring search within {@link io.net5.buffer.ByteBuf}. */ -package io.netty.buffer.search; +package io.net5.buffer.search; diff --git a/buffer/src/main/java/io/netty/buffer/api/unsafe/package-info.java b/buffer/src/main/java/io/netty/buffer/api/unsafe/package-info.java deleted file mode 100644 index 3fd081ad15..0000000000 --- a/buffer/src/main/java/io/netty/buffer/api/unsafe/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * A {@link io.netty.buffer.api.Buffer} implementation that is based on {@code sun.misc.Unsafe}. - */ -package io.netty.buffer.api.unsafe; diff --git a/buffer/src/main/resources/META-INF/native-image/io.netty/buffer/native-image.properties b/buffer/src/main/resources/META-INF/native-image/io.netty/buffer/native-image.properties index 4a422fde1a..c9bed1e656 100644 --- a/buffer/src/main/resources/META-INF/native-image/io.netty/buffer/native-image.properties +++ b/buffer/src/main/resources/META-INF/native-image/io.netty/buffer/native-image.properties @@ -12,4 +12,4 @@ # License for the specific language governing permissions and limitations # under the License. -Args = --initialize-at-run-time=io.netty.buffer.PooledByteBufAllocator,io.netty.buffer.ByteBufAllocator,io.netty.buffer.ByteBufUtil,io.netty.buffer.AbstractReferenceCountedByteBuf +Args = --initialize-at-run-time=io.net5.buffer.PooledByteBufAllocator,io.net5.buffer.ByteBufAllocator,io.net5.buffer.ByteBufUtil,io.net5.buffer.AbstractReferenceCountedByteBuf diff --git a/buffer/src/main/resources/META-INF/services/io.net5.buffer.api.MemoryManager b/buffer/src/main/resources/META-INF/services/io.net5.buffer.api.MemoryManager new file mode 100644 index 0000000000..0968d1980b --- /dev/null +++ b/buffer/src/main/resources/META-INF/services/io.net5.buffer.api.MemoryManager @@ -0,0 +1,2 @@ +io.net5.buffer.api.bytebuffer.ByteBufferMemoryManager +io.net5.buffer.api.unsafe.UnsafeMemoryManager diff --git a/buffer/src/main/resources/META-INF/services/io.netty.buffer.api.MemoryManager b/buffer/src/main/resources/META-INF/services/io.netty.buffer.api.MemoryManager deleted file mode 100644 index b885a62c29..0000000000 --- a/buffer/src/main/resources/META-INF/services/io.netty.buffer.api.MemoryManager +++ /dev/null @@ -1,2 +0,0 @@ -io.netty.buffer.api.bytebuffer.ByteBufferMemoryManager -io.netty.buffer.api.unsafe.UnsafeMemoryManager diff --git a/buffer/src/test/java/io/netty/buffer/AbstractByteBufAllocatorTest.java b/buffer/src/test/java/io/net5/buffer/AbstractByteBufAllocatorTest.java similarity index 98% rename from buffer/src/test/java/io/netty/buffer/AbstractByteBufAllocatorTest.java rename to buffer/src/test/java/io/net5/buffer/AbstractByteBufAllocatorTest.java index e6e064b40c..40aac70af3 100644 --- a/buffer/src/test/java/io/netty/buffer/AbstractByteBufAllocatorTest.java +++ b/buffer/src/test/java/io/net5/buffer/AbstractByteBufAllocatorTest.java @@ -13,9 +13,9 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.internal.PlatformDependent; +import io.net5.util.internal.PlatformDependent; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/buffer/src/test/java/io/netty/buffer/AbstractByteBufTest.java b/buffer/src/test/java/io/net5/buffer/AbstractByteBufTest.java similarity index 99% rename from buffer/src/test/java/io/netty/buffer/AbstractByteBufTest.java rename to buffer/src/test/java/io/net5/buffer/AbstractByteBufTest.java index dcac9b3c4e..ee83046036 100644 --- a/buffer/src/test/java/io/netty/buffer/AbstractByteBufTest.java +++ b/buffer/src/test/java/io/net5/buffer/AbstractByteBufTest.java @@ -13,12 +13,12 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.ByteProcessor; -import io.netty.util.CharsetUtil; -import io.netty.util.IllegalReferenceCountException; -import io.netty.util.internal.PlatformDependent; +import io.net5.util.ByteProcessor; +import io.net5.util.CharsetUtil; +import io.net5.util.IllegalReferenceCountException; +import io.net5.util.internal.PlatformDependent; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -52,13 +52,13 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; -import static io.netty.buffer.Unpooled.LITTLE_ENDIAN; -import static io.netty.buffer.Unpooled.buffer; -import static io.netty.buffer.Unpooled.copiedBuffer; -import static io.netty.buffer.Unpooled.directBuffer; -import static io.netty.buffer.Unpooled.unreleasableBuffer; -import static io.netty.buffer.Unpooled.wrappedBuffer; -import static io.netty.util.internal.EmptyArrays.EMPTY_BYTES; +import static io.net5.buffer.Unpooled.LITTLE_ENDIAN; +import static io.net5.buffer.Unpooled.buffer; +import static io.net5.buffer.Unpooled.copiedBuffer; +import static io.net5.buffer.Unpooled.directBuffer; +import static io.net5.buffer.Unpooled.unreleasableBuffer; +import static io.net5.buffer.Unpooled.wrappedBuffer; +import static io.net5.util.internal.EmptyArrays.EMPTY_BYTES; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertArrayEquals; diff --git a/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java b/buffer/src/test/java/io/net5/buffer/AbstractCompositeByteBufTest.java similarity index 99% rename from buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java rename to buffer/src/test/java/io/net5/buffer/AbstractCompositeByteBufTest.java index 8d84d12b77..6875e57b3e 100644 --- a/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java +++ b/buffer/src/test/java/io/net5/buffer/AbstractCompositeByteBufTest.java @@ -13,9 +13,9 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.ReferenceCountUtil; +import io.net5.util.ReferenceCountUtil; import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; @@ -30,12 +30,12 @@ import java.util.NoSuchElementException; import java.util.Objects; import java.util.concurrent.ThreadLocalRandom; -import static io.netty.buffer.Unpooled.EMPTY_BUFFER; -import static io.netty.buffer.Unpooled.buffer; -import static io.netty.buffer.Unpooled.compositeBuffer; -import static io.netty.buffer.Unpooled.directBuffer; -import static io.netty.buffer.Unpooled.wrappedBuffer; -import static io.netty.util.internal.EmptyArrays.EMPTY_BYTES; +import static io.net5.buffer.Unpooled.EMPTY_BUFFER; +import static io.net5.buffer.Unpooled.buffer; +import static io.net5.buffer.Unpooled.compositeBuffer; +import static io.net5.buffer.Unpooled.directBuffer; +import static io.net5.buffer.Unpooled.wrappedBuffer; +import static io.net5.util.internal.EmptyArrays.EMPTY_BYTES; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; diff --git a/buffer/src/test/java/io/netty/buffer/AbstractPooledByteBufTest.java b/buffer/src/test/java/io/net5/buffer/AbstractPooledByteBufTest.java similarity index 99% rename from buffer/src/test/java/io/netty/buffer/AbstractPooledByteBufTest.java rename to buffer/src/test/java/io/net5/buffer/AbstractPooledByteBufTest.java index 6b75a1e42f..e74b2a076f 100644 --- a/buffer/src/test/java/io/netty/buffer/AbstractPooledByteBufTest.java +++ b/buffer/src/test/java/io/net5/buffer/AbstractPooledByteBufTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; import org.junit.jupiter.api.Test; diff --git a/buffer/src/test/java/io/netty/buffer/AbstractReferenceCountedByteBufTest.java b/buffer/src/test/java/io/net5/buffer/AbstractReferenceCountedByteBufTest.java similarity index 99% rename from buffer/src/test/java/io/netty/buffer/AbstractReferenceCountedByteBufTest.java rename to buffer/src/test/java/io/net5/buffer/AbstractReferenceCountedByteBufTest.java index 9207462cfb..4e8227871d 100644 --- a/buffer/src/test/java/io/netty/buffer/AbstractReferenceCountedByteBufTest.java +++ b/buffer/src/test/java/io/net5/buffer/AbstractReferenceCountedByteBufTest.java @@ -13,9 +13,9 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.IllegalReferenceCountException; +import io.net5.util.IllegalReferenceCountException; import org.junit.jupiter.api.Test; import java.io.IOException; diff --git a/buffer/src/test/java/io/netty/buffer/AdvancedLeakAwareByteBufTest.java b/buffer/src/test/java/io/net5/buffer/AdvancedLeakAwareByteBufTest.java similarity index 92% rename from buffer/src/test/java/io/netty/buffer/AdvancedLeakAwareByteBufTest.java rename to buffer/src/test/java/io/net5/buffer/AdvancedLeakAwareByteBufTest.java index e79b35b277..efd0b27c86 100644 --- a/buffer/src/test/java/io/netty/buffer/AdvancedLeakAwareByteBufTest.java +++ b/buffer/src/test/java/io/net5/buffer/AdvancedLeakAwareByteBufTest.java @@ -13,15 +13,15 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import static io.netty.buffer.Unpooled.*; +import static io.net5.buffer.Unpooled.*; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import org.junit.jupiter.api.Test; -import io.netty.util.CharsetUtil; -import io.netty.util.ResourceLeakTracker; +import io.net5.util.CharsetUtil; +import io.net5.util.ResourceLeakTracker; public class AdvancedLeakAwareByteBufTest extends SimpleLeakAwareByteBufTest { diff --git a/buffer/src/test/java/io/netty/buffer/AdvancedLeakAwareCompositeByteBufTest.java b/buffer/src/test/java/io/net5/buffer/AdvancedLeakAwareCompositeByteBufTest.java similarity index 94% rename from buffer/src/test/java/io/netty/buffer/AdvancedLeakAwareCompositeByteBufTest.java rename to buffer/src/test/java/io/net5/buffer/AdvancedLeakAwareCompositeByteBufTest.java index e5742f4d9d..e28ef3d98e 100644 --- a/buffer/src/test/java/io/netty/buffer/AdvancedLeakAwareCompositeByteBufTest.java +++ b/buffer/src/test/java/io/net5/buffer/AdvancedLeakAwareCompositeByteBufTest.java @@ -13,9 +13,9 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.ResourceLeakTracker; +import io.net5.util.ResourceLeakTracker; public class AdvancedLeakAwareCompositeByteBufTest extends SimpleLeakAwareCompositeByteBufTest { diff --git a/buffer/src/test/java/io/netty/buffer/AlignedPooledByteBufAllocatorTest.java b/buffer/src/test/java/io/net5/buffer/AlignedPooledByteBufAllocatorTest.java similarity index 98% rename from buffer/src/test/java/io/netty/buffer/AlignedPooledByteBufAllocatorTest.java rename to buffer/src/test/java/io/net5/buffer/AlignedPooledByteBufAllocatorTest.java index 59fcf77c0e..b4915515bc 100644 --- a/buffer/src/test/java/io/netty/buffer/AlignedPooledByteBufAllocatorTest.java +++ b/buffer/src/test/java/io/net5/buffer/AlignedPooledByteBufAllocatorTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; public class AlignedPooledByteBufAllocatorTest extends PooledByteBufAllocatorTest { @Override diff --git a/buffer/src/test/java/io/netty/buffer/BigEndianCompositeByteBufTest.java b/buffer/src/test/java/io/net5/buffer/BigEndianCompositeByteBufTest.java similarity index 97% rename from buffer/src/test/java/io/netty/buffer/BigEndianCompositeByteBufTest.java rename to buffer/src/test/java/io/net5/buffer/BigEndianCompositeByteBufTest.java index 50d3e12d97..ec5704c7a5 100644 --- a/buffer/src/test/java/io/netty/buffer/BigEndianCompositeByteBufTest.java +++ b/buffer/src/test/java/io/net5/buffer/BigEndianCompositeByteBufTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; import org.junit.jupiter.api.Test; diff --git a/buffer/src/test/java/io/netty/buffer/BigEndianDirectByteBufTest.java b/buffer/src/test/java/io/net5/buffer/BigEndianDirectByteBufTest.java similarity index 98% rename from buffer/src/test/java/io/netty/buffer/BigEndianDirectByteBufTest.java rename to buffer/src/test/java/io/net5/buffer/BigEndianDirectByteBufTest.java index e7a1463074..cfd3cbd119 100644 --- a/buffer/src/test/java/io/netty/buffer/BigEndianDirectByteBufTest.java +++ b/buffer/src/test/java/io/net5/buffer/BigEndianDirectByteBufTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertSame; diff --git a/buffer/src/test/java/io/netty/buffer/BigEndianHeapByteBufTest.java b/buffer/src/test/java/io/net5/buffer/BigEndianHeapByteBufTest.java similarity index 98% rename from buffer/src/test/java/io/netty/buffer/BigEndianHeapByteBufTest.java rename to buffer/src/test/java/io/net5/buffer/BigEndianHeapByteBufTest.java index fd2ecb2c1d..e10b79558e 100644 --- a/buffer/src/test/java/io/netty/buffer/BigEndianHeapByteBufTest.java +++ b/buffer/src/test/java/io/net5/buffer/BigEndianHeapByteBufTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; import org.junit.jupiter.api.Test; diff --git a/buffer/src/test/java/io/netty/buffer/BigEndianUnsafeDirectByteBufTest.java b/buffer/src/test/java/io/net5/buffer/BigEndianUnsafeDirectByteBufTest.java similarity index 94% rename from buffer/src/test/java/io/netty/buffer/BigEndianUnsafeDirectByteBufTest.java rename to buffer/src/test/java/io/net5/buffer/BigEndianUnsafeDirectByteBufTest.java index a2de7f653b..e4841cce10 100644 --- a/buffer/src/test/java/io/netty/buffer/BigEndianUnsafeDirectByteBufTest.java +++ b/buffer/src/test/java/io/net5/buffer/BigEndianUnsafeDirectByteBufTest.java @@ -13,10 +13,10 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.internal.PlatformDependent; +import io.net5.util.internal.PlatformDependent; import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.BeforeEach; diff --git a/buffer/src/test/java/io/netty/buffer/BigEndianUnsafeNoCleanerDirectByteBufTest.java b/buffer/src/test/java/io/net5/buffer/BigEndianUnsafeNoCleanerDirectByteBufTest.java similarity index 94% rename from buffer/src/test/java/io/netty/buffer/BigEndianUnsafeNoCleanerDirectByteBufTest.java rename to buffer/src/test/java/io/net5/buffer/BigEndianUnsafeNoCleanerDirectByteBufTest.java index 19c053e459..ef419f99c7 100644 --- a/buffer/src/test/java/io/netty/buffer/BigEndianUnsafeNoCleanerDirectByteBufTest.java +++ b/buffer/src/test/java/io/net5/buffer/BigEndianUnsafeNoCleanerDirectByteBufTest.java @@ -13,9 +13,9 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.internal.PlatformDependent; +import io.net5.util.internal.PlatformDependent; import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.BeforeEach; diff --git a/buffer/src/test/java/io/netty/buffer/ByteBufAllocatorTest.java b/buffer/src/test/java/io/net5/buffer/ByteBufAllocatorTest.java similarity index 99% rename from buffer/src/test/java/io/netty/buffer/ByteBufAllocatorTest.java rename to buffer/src/test/java/io/net5/buffer/ByteBufAllocatorTest.java index b538953c76..0750b6b4d4 100644 --- a/buffer/src/test/java/io/netty/buffer/ByteBufAllocatorTest.java +++ b/buffer/src/test/java/io/net5/buffer/ByteBufAllocatorTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; import org.junit.jupiter.api.Test; diff --git a/buffer/src/test/java/io/netty/buffer/ByteBufDerivationTest.java b/buffer/src/test/java/io/net5/buffer/ByteBufDerivationTest.java similarity index 99% rename from buffer/src/test/java/io/netty/buffer/ByteBufDerivationTest.java rename to buffer/src/test/java/io/net5/buffer/ByteBufDerivationTest.java index ab4a59c3c7..0486cf1170 100644 --- a/buffer/src/test/java/io/netty/buffer/ByteBufDerivationTest.java +++ b/buffer/src/test/java/io/net5/buffer/ByteBufDerivationTest.java @@ -14,7 +14,7 @@ * under the License. */ -package io.netty.buffer; +package io.net5.buffer; import org.junit.jupiter.api.Test; diff --git a/buffer/src/test/java/io/netty/buffer/ByteBufStreamTest.java b/buffer/src/test/java/io/net5/buffer/ByteBufStreamTest.java similarity index 99% rename from buffer/src/test/java/io/netty/buffer/ByteBufStreamTest.java rename to buffer/src/test/java/io/net5/buffer/ByteBufStreamTest.java index cd9d42e262..68c2ddcbd8 100644 --- a/buffer/src/test/java/io/netty/buffer/ByteBufStreamTest.java +++ b/buffer/src/test/java/io/net5/buffer/ByteBufStreamTest.java @@ -13,14 +13,14 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; import org.junit.jupiter.api.Test; import java.io.EOFException; import java.nio.charset.Charset; -import static io.netty.util.internal.EmptyArrays.*; +import static io.net5.util.internal.EmptyArrays.*; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; diff --git a/buffer/src/test/java/io/netty/buffer/ByteBufUtilTest.java b/buffer/src/test/java/io/net5/buffer/ByteBufUtilTest.java similarity index 99% rename from buffer/src/test/java/io/netty/buffer/ByteBufUtilTest.java rename to buffer/src/test/java/io/net5/buffer/ByteBufUtilTest.java index 9cd7a4af4f..7b92450485 100644 --- a/buffer/src/test/java/io/netty/buffer/ByteBufUtilTest.java +++ b/buffer/src/test/java/io/net5/buffer/ByteBufUtilTest.java @@ -13,10 +13,10 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.AsciiString; -import io.netty.util.CharsetUtil; +import io.net5.util.AsciiString; +import io.net5.util.CharsetUtil; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -31,7 +31,7 @@ import java.util.Random; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; -import static io.netty.buffer.Unpooled.unreleasableBuffer; +import static io.net5.buffer.Unpooled.unreleasableBuffer; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.is; diff --git a/buffer/src/test/java/io/netty/buffer/ByteProcessorTest.java b/buffer/src/test/java/io/net5/buffer/ByteProcessorTest.java similarity index 97% rename from buffer/src/test/java/io/netty/buffer/ByteProcessorTest.java rename to buffer/src/test/java/io/net5/buffer/ByteProcessorTest.java index 01c7b7f82f..6bef68ca12 100644 --- a/buffer/src/test/java/io/netty/buffer/ByteProcessorTest.java +++ b/buffer/src/test/java/io/net5/buffer/ByteProcessorTest.java @@ -14,11 +14,11 @@ * under the License. */ -package io.netty.buffer; +package io.net5.buffer; import static org.junit.jupiter.api.Assertions.assertEquals; -import io.netty.util.ByteProcessor; -import io.netty.util.CharsetUtil; +import io.net5.util.ByteProcessor; +import io.net5.util.CharsetUtil; import org.junit.jupiter.api.Test; diff --git a/buffer/src/test/java/io/netty/buffer/ConsolidationTest.java b/buffer/src/test/java/io/net5/buffer/ConsolidationTest.java similarity index 97% rename from buffer/src/test/java/io/netty/buffer/ConsolidationTest.java rename to buffer/src/test/java/io/net5/buffer/ConsolidationTest.java index 6d304ccae7..319f92cc03 100644 --- a/buffer/src/test/java/io/netty/buffer/ConsolidationTest.java +++ b/buffer/src/test/java/io/net5/buffer/ConsolidationTest.java @@ -13,12 +13,12 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.CharsetUtil; +import io.net5.util.CharsetUtil; import org.junit.jupiter.api.Test; -import static io.netty.buffer.Unpooled.*; +import static io.net5.buffer.Unpooled.*; import static org.junit.jupiter.api.Assertions.assertEquals; /** diff --git a/buffer/src/test/java/io/netty/buffer/DefaultByteBufHolderTest.java b/buffer/src/test/java/io/net5/buffer/DefaultByteBufHolderTest.java similarity index 99% rename from buffer/src/test/java/io/netty/buffer/DefaultByteBufHolderTest.java rename to buffer/src/test/java/io/net5/buffer/DefaultByteBufHolderTest.java index 2a623aa0ee..3c6f09a421 100644 --- a/buffer/src/test/java/io/netty/buffer/DefaultByteBufHolderTest.java +++ b/buffer/src/test/java/io/net5/buffer/DefaultByteBufHolderTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; import org.junit.jupiter.api.Test; diff --git a/buffer/src/test/java/io/netty/buffer/DuplicatedByteBufTest.java b/buffer/src/test/java/io/net5/buffer/DuplicatedByteBufTest.java similarity index 98% rename from buffer/src/test/java/io/netty/buffer/DuplicatedByteBufTest.java rename to buffer/src/test/java/io/net5/buffer/DuplicatedByteBufTest.java index cc1b8ade21..81d5058b30 100644 --- a/buffer/src/test/java/io/netty/buffer/DuplicatedByteBufTest.java +++ b/buffer/src/test/java/io/net5/buffer/DuplicatedByteBufTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; import org.junit.jupiter.api.Test; diff --git a/buffer/src/test/java/io/netty/buffer/EmptyByteBufTest.java b/buffer/src/test/java/io/net5/buffer/EmptyByteBufTest.java similarity index 98% rename from buffer/src/test/java/io/netty/buffer/EmptyByteBufTest.java rename to buffer/src/test/java/io/net5/buffer/EmptyByteBufTest.java index 3ea80124bf..426b35045d 100644 --- a/buffer/src/test/java/io/netty/buffer/EmptyByteBufTest.java +++ b/buffer/src/test/java/io/net5/buffer/EmptyByteBufTest.java @@ -13,9 +13,9 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.CharsetUtil; +import io.net5.util.CharsetUtil; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; diff --git a/buffer/src/test/java/io/netty/buffer/FixedCompositeByteBufTest.java b/buffer/src/test/java/io/net5/buffer/FixedCompositeByteBufTest.java similarity index 99% rename from buffer/src/test/java/io/netty/buffer/FixedCompositeByteBufTest.java rename to buffer/src/test/java/io/net5/buffer/FixedCompositeByteBufTest.java index eab66930b9..a991c15dc5 100644 --- a/buffer/src/test/java/io/netty/buffer/FixedCompositeByteBufTest.java +++ b/buffer/src/test/java/io/net5/buffer/FixedCompositeByteBufTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; @@ -26,7 +26,7 @@ import java.nio.ReadOnlyBufferException; import java.nio.channels.ScatteringByteChannel; import java.nio.charset.Charset; -import static io.netty.buffer.Unpooled.*; +import static io.net5.buffer.Unpooled.*; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; diff --git a/buffer/src/test/java/io/netty/buffer/LittleEndianCompositeByteBufTest.java b/buffer/src/test/java/io/net5/buffer/LittleEndianCompositeByteBufTest.java similarity index 97% rename from buffer/src/test/java/io/netty/buffer/LittleEndianCompositeByteBufTest.java rename to buffer/src/test/java/io/net5/buffer/LittleEndianCompositeByteBufTest.java index b6b27de336..1a65380075 100644 --- a/buffer/src/test/java/io/netty/buffer/LittleEndianCompositeByteBufTest.java +++ b/buffer/src/test/java/io/net5/buffer/LittleEndianCompositeByteBufTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; /** diff --git a/buffer/src/test/java/io/netty/buffer/LittleEndianDirectByteBufTest.java b/buffer/src/test/java/io/net5/buffer/LittleEndianDirectByteBufTest.java similarity index 98% rename from buffer/src/test/java/io/netty/buffer/LittleEndianDirectByteBufTest.java rename to buffer/src/test/java/io/net5/buffer/LittleEndianDirectByteBufTest.java index 6fa0ed0161..a6299d6ce1 100644 --- a/buffer/src/test/java/io/netty/buffer/LittleEndianDirectByteBufTest.java +++ b/buffer/src/test/java/io/net5/buffer/LittleEndianDirectByteBufTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertSame; diff --git a/buffer/src/test/java/io/netty/buffer/LittleEndianHeapByteBufTest.java b/buffer/src/test/java/io/net5/buffer/LittleEndianHeapByteBufTest.java similarity index 97% rename from buffer/src/test/java/io/netty/buffer/LittleEndianHeapByteBufTest.java rename to buffer/src/test/java/io/net5/buffer/LittleEndianHeapByteBufTest.java index 077873425c..6abf99c6c8 100644 --- a/buffer/src/test/java/io/netty/buffer/LittleEndianHeapByteBufTest.java +++ b/buffer/src/test/java/io/net5/buffer/LittleEndianHeapByteBufTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/buffer/src/test/java/io/netty/buffer/LittleEndianUnsafeDirectByteBufTest.java b/buffer/src/test/java/io/net5/buffer/LittleEndianUnsafeDirectByteBufTest.java similarity index 94% rename from buffer/src/test/java/io/netty/buffer/LittleEndianUnsafeDirectByteBufTest.java rename to buffer/src/test/java/io/net5/buffer/LittleEndianUnsafeDirectByteBufTest.java index 57eff37ba4..e7f6ed3c0c 100644 --- a/buffer/src/test/java/io/netty/buffer/LittleEndianUnsafeDirectByteBufTest.java +++ b/buffer/src/test/java/io/net5/buffer/LittleEndianUnsafeDirectByteBufTest.java @@ -13,9 +13,9 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.internal.PlatformDependent; +import io.net5.util.internal.PlatformDependent; import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.BeforeEach; diff --git a/buffer/src/test/java/io/netty/buffer/LittleEndianUnsafeNoCleanerDirectByteBufTest.java b/buffer/src/test/java/io/net5/buffer/LittleEndianUnsafeNoCleanerDirectByteBufTest.java similarity index 94% rename from buffer/src/test/java/io/netty/buffer/LittleEndianUnsafeNoCleanerDirectByteBufTest.java rename to buffer/src/test/java/io/net5/buffer/LittleEndianUnsafeNoCleanerDirectByteBufTest.java index 6396148e07..e466d6fb52 100644 --- a/buffer/src/test/java/io/netty/buffer/LittleEndianUnsafeNoCleanerDirectByteBufTest.java +++ b/buffer/src/test/java/io/net5/buffer/LittleEndianUnsafeNoCleanerDirectByteBufTest.java @@ -13,9 +13,9 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.internal.PlatformDependent; +import io.net5.util.internal.PlatformDependent; import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.BeforeEach; diff --git a/buffer/src/test/java/io/netty/buffer/NoopResourceLeakTracker.java b/buffer/src/test/java/io/net5/buffer/NoopResourceLeakTracker.java similarity index 94% rename from buffer/src/test/java/io/netty/buffer/NoopResourceLeakTracker.java rename to buffer/src/test/java/io/net5/buffer/NoopResourceLeakTracker.java index 8f31f80f93..77b5ce7d4c 100644 --- a/buffer/src/test/java/io/netty/buffer/NoopResourceLeakTracker.java +++ b/buffer/src/test/java/io/net5/buffer/NoopResourceLeakTracker.java @@ -13,9 +13,9 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.ResourceLeakTracker; +import io.net5.util.ResourceLeakTracker; import java.util.concurrent.atomic.AtomicBoolean; diff --git a/buffer/src/test/java/io/netty/buffer/PoolArenaTest.java b/buffer/src/test/java/io/net5/buffer/PoolArenaTest.java similarity index 99% rename from buffer/src/test/java/io/netty/buffer/PoolArenaTest.java rename to buffer/src/test/java/io/net5/buffer/PoolArenaTest.java index 0eb7a5e7e4..94f8d8ab67 100644 --- a/buffer/src/test/java/io/netty/buffer/PoolArenaTest.java +++ b/buffer/src/test/java/io/net5/buffer/PoolArenaTest.java @@ -14,7 +14,7 @@ * under the License. */ -package io.netty.buffer; +package io.net5.buffer; import org.junit.jupiter.api.Test; diff --git a/buffer/src/test/java/io/netty/buffer/PooledAlignedBigEndianDirectByteBufTest.java b/buffer/src/test/java/io/net5/buffer/PooledAlignedBigEndianDirectByteBufTest.java similarity index 98% rename from buffer/src/test/java/io/netty/buffer/PooledAlignedBigEndianDirectByteBufTest.java rename to buffer/src/test/java/io/net5/buffer/PooledAlignedBigEndianDirectByteBufTest.java index 5ee3de6f12..f2d22ae820 100644 --- a/buffer/src/test/java/io/netty/buffer/PooledAlignedBigEndianDirectByteBufTest.java +++ b/buffer/src/test/java/io/net5/buffer/PooledAlignedBigEndianDirectByteBufTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; diff --git a/buffer/src/test/java/io/netty/buffer/PooledBigEndianDirectByteBufTest.java b/buffer/src/test/java/io/net5/buffer/PooledBigEndianDirectByteBufTest.java similarity index 97% rename from buffer/src/test/java/io/netty/buffer/PooledBigEndianDirectByteBufTest.java rename to buffer/src/test/java/io/net5/buffer/PooledBigEndianDirectByteBufTest.java index 0494f77459..f4133865ad 100644 --- a/buffer/src/test/java/io/netty/buffer/PooledBigEndianDirectByteBufTest.java +++ b/buffer/src/test/java/io/net5/buffer/PooledBigEndianDirectByteBufTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; import java.nio.ByteOrder; diff --git a/buffer/src/test/java/io/netty/buffer/PooledBigEndianHeapByteBufTest.java b/buffer/src/test/java/io/net5/buffer/PooledBigEndianHeapByteBufTest.java similarity index 97% rename from buffer/src/test/java/io/netty/buffer/PooledBigEndianHeapByteBufTest.java rename to buffer/src/test/java/io/net5/buffer/PooledBigEndianHeapByteBufTest.java index 12b57a858e..742d115614 100644 --- a/buffer/src/test/java/io/netty/buffer/PooledBigEndianHeapByteBufTest.java +++ b/buffer/src/test/java/io/net5/buffer/PooledBigEndianHeapByteBufTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; /** * Tests big-endian heap channel buffers diff --git a/buffer/src/test/java/io/netty/buffer/PooledByteBufAllocatorTest.java b/buffer/src/test/java/io/net5/buffer/PooledByteBufAllocatorTest.java similarity index 98% rename from buffer/src/test/java/io/netty/buffer/PooledByteBufAllocatorTest.java rename to buffer/src/test/java/io/net5/buffer/PooledByteBufAllocatorTest.java index bb3f5659c2..0f842ddcaf 100644 --- a/buffer/src/test/java/io/netty/buffer/PooledByteBufAllocatorTest.java +++ b/buffer/src/test/java/io/net5/buffer/PooledByteBufAllocatorTest.java @@ -14,12 +14,12 @@ * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.concurrent.FastThreadLocal; -import io.netty.util.concurrent.FastThreadLocalThread; -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.SystemPropertyUtil; +import io.net5.util.concurrent.FastThreadLocal; +import io.net5.util.concurrent.FastThreadLocalThread; +import io.net5.util.internal.PlatformDependent; +import io.net5.util.internal.SystemPropertyUtil; import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; @@ -35,8 +35,8 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.LockSupport; import org.junit.jupiter.api.Timeout; -import static io.netty.buffer.PoolChunk.runOffset; -import static io.netty.buffer.PoolChunk.runPages; +import static io.net5.buffer.PoolChunk.runOffset; +import static io.net5.buffer.PoolChunk.runPages; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -511,7 +511,7 @@ public class PooledByteBufAllocatorTest extends AbstractByteBufAllocatorTest { diff --git a/buffer/src/test/java/io/netty/buffer/UnpooledTest.java b/buffer/src/test/java/io/net5/buffer/UnpooledTest.java similarity index 99% rename from buffer/src/test/java/io/netty/buffer/UnpooledTest.java rename to buffer/src/test/java/io/net5/buffer/UnpooledTest.java index 75734cadf2..42f2573f48 100644 --- a/buffer/src/test/java/io/netty/buffer/UnpooledTest.java +++ b/buffer/src/test/java/io/net5/buffer/UnpooledTest.java @@ -13,9 +13,9 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.CharsetUtil; +import io.net5.util.CharsetUtil; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -29,8 +29,8 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; -import static io.netty.buffer.Unpooled.*; -import static io.netty.util.internal.EmptyArrays.*; +import static io.net5.buffer.Unpooled.*; +import static io.net5.util.internal.EmptyArrays.*; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertSame; diff --git a/buffer/src/test/java/io/netty/buffer/UnreleaseableByteBufTest.java b/buffer/src/test/java/io/net5/buffer/UnreleaseableByteBufTest.java similarity index 95% rename from buffer/src/test/java/io/netty/buffer/UnreleaseableByteBufTest.java rename to buffer/src/test/java/io/net5/buffer/UnreleaseableByteBufTest.java index 9ee22db4e7..5b8b98703a 100644 --- a/buffer/src/test/java/io/netty/buffer/UnreleaseableByteBufTest.java +++ b/buffer/src/test/java/io/net5/buffer/UnreleaseableByteBufTest.java @@ -13,11 +13,11 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; import org.junit.jupiter.api.Test; -import static io.netty.buffer.Unpooled.buffer; +import static io.net5.buffer.Unpooled.buffer; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertSame; diff --git a/buffer/src/test/java/io/netty/buffer/UnsafeByteBufUtilTest.java b/buffer/src/test/java/io/net5/buffer/UnsafeByteBufUtilTest.java similarity index 98% rename from buffer/src/test/java/io/netty/buffer/UnsafeByteBufUtilTest.java rename to buffer/src/test/java/io/net5/buffer/UnsafeByteBufUtilTest.java index 0558d69674..d059e489d0 100644 --- a/buffer/src/test/java/io/netty/buffer/UnsafeByteBufUtilTest.java +++ b/buffer/src/test/java/io/net5/buffer/UnsafeByteBufUtilTest.java @@ -13,16 +13,16 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.internal.PlatformDependent; +import io.net5.util.internal.PlatformDependent; import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.nio.ByteBuffer; -import static io.netty.util.internal.PlatformDependent.directBufferAddress; +import static io.net5.util.internal.PlatformDependent.directBufferAddress; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; diff --git a/buffer/src/test/java/io/netty/buffer/WrappedCompositeByteBufTest.java b/buffer/src/test/java/io/net5/buffer/WrappedCompositeByteBufTest.java similarity index 97% rename from buffer/src/test/java/io/netty/buffer/WrappedCompositeByteBufTest.java rename to buffer/src/test/java/io/net5/buffer/WrappedCompositeByteBufTest.java index c4993c809f..754df1c319 100644 --- a/buffer/src/test/java/io/netty/buffer/WrappedCompositeByteBufTest.java +++ b/buffer/src/test/java/io/net5/buffer/WrappedCompositeByteBufTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; public class WrappedCompositeByteBufTest extends BigEndianCompositeByteBufTest { diff --git a/buffer/src/test/java/io/netty/buffer/WrappedUnpooledUnsafeByteBufTest.java b/buffer/src/test/java/io/net5/buffer/WrappedUnpooledUnsafeByteBufTest.java similarity index 98% rename from buffer/src/test/java/io/netty/buffer/WrappedUnpooledUnsafeByteBufTest.java rename to buffer/src/test/java/io/net5/buffer/WrappedUnpooledUnsafeByteBufTest.java index 810522ec35..1ce082eb3b 100644 --- a/buffer/src/test/java/io/netty/buffer/WrappedUnpooledUnsafeByteBufTest.java +++ b/buffer/src/test/java/io/net5/buffer/WrappedUnpooledUnsafeByteBufTest.java @@ -13,9 +13,9 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; +package io.net5.buffer; -import io.netty.util.internal.PlatformDependent; +import io.net5.util.internal.PlatformDependent; import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/buffer/src/test/java/io/netty/buffer/api/tests/BufferBulkAccessTest.java b/buffer/src/test/java/io/net5/buffer/api/tests/BufferBulkAccessTest.java similarity index 98% rename from buffer/src/test/java/io/netty/buffer/api/tests/BufferBulkAccessTest.java rename to buffer/src/test/java/io/net5/buffer/api/tests/BufferBulkAccessTest.java index 13da19b308..e1752476d4 100644 --- a/buffer/src/test/java/io/netty/buffer/api/tests/BufferBulkAccessTest.java +++ b/buffer/src/test/java/io/net5/buffer/api/tests/BufferBulkAccessTest.java @@ -13,16 +13,16 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer.api.tests; +package io.net5.buffer.api.tests; -import io.netty.buffer.api.Buffer; -import io.netty.buffer.api.BufferAllocator; +import io.net5.buffer.api.Buffer; +import io.net5.buffer.api.BufferAllocator; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import java.nio.ByteBuffer; -import static io.netty.buffer.api.CompositeBuffer.compose; +import static io.net5.buffer.api.CompositeBuffer.compose; import static org.assertj.core.api.Assertions.assertThat; public class BufferBulkAccessTest extends BufferTestSupport { diff --git a/buffer/src/test/java/io/netty/buffer/api/tests/BufferByteOffsettedAccessorsTest.java b/buffer/src/test/java/io/net5/buffer/api/tests/BufferByteOffsettedAccessorsTest.java similarity index 99% rename from buffer/src/test/java/io/netty/buffer/api/tests/BufferByteOffsettedAccessorsTest.java rename to buffer/src/test/java/io/net5/buffer/api/tests/BufferByteOffsettedAccessorsTest.java index d8c000cd80..40be95b7f6 100644 --- a/buffer/src/test/java/io/netty/buffer/api/tests/BufferByteOffsettedAccessorsTest.java +++ b/buffer/src/test/java/io/net5/buffer/api/tests/BufferByteOffsettedAccessorsTest.java @@ -13,10 +13,10 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer.api.tests; +package io.net5.buffer.api.tests; -import io.netty.buffer.api.Buffer; -import io.netty.buffer.api.BufferAllocator; +import io.net5.buffer.api.Buffer; +import io.net5.buffer.api.BufferAllocator; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; diff --git a/buffer/src/test/java/io/netty/buffer/api/tests/BufferCharOffsettedAccessorsTest.java b/buffer/src/test/java/io/net5/buffer/api/tests/BufferCharOffsettedAccessorsTest.java similarity index 98% rename from buffer/src/test/java/io/netty/buffer/api/tests/BufferCharOffsettedAccessorsTest.java rename to buffer/src/test/java/io/net5/buffer/api/tests/BufferCharOffsettedAccessorsTest.java index 216ab6ed0b..77f4c1b6de 100644 --- a/buffer/src/test/java/io/netty/buffer/api/tests/BufferCharOffsettedAccessorsTest.java +++ b/buffer/src/test/java/io/net5/buffer/api/tests/BufferCharOffsettedAccessorsTest.java @@ -13,10 +13,10 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer.api.tests; +package io.net5.buffer.api.tests; -import io.netty.buffer.api.Buffer; -import io.netty.buffer.api.BufferAllocator; +import io.net5.buffer.api.Buffer; +import io.net5.buffer.api.BufferAllocator; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; diff --git a/buffer/src/test/java/io/netty/buffer/api/tests/BufferCleanerTest.java b/buffer/src/test/java/io/net5/buffer/api/tests/BufferCleanerTest.java similarity index 93% rename from buffer/src/test/java/io/netty/buffer/api/tests/BufferCleanerTest.java rename to buffer/src/test/java/io/net5/buffer/api/tests/BufferCleanerTest.java index c40153d17a..ba69d09819 100644 --- a/buffer/src/test/java/io/netty/buffer/api/tests/BufferCleanerTest.java +++ b/buffer/src/test/java/io/net5/buffer/api/tests/BufferCleanerTest.java @@ -13,10 +13,10 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer.api.tests; +package io.net5.buffer.api.tests; -import io.netty.buffer.api.MemoryManager; -import io.netty.buffer.api.internal.Statics; +import io.net5.buffer.api.MemoryManager; +import io.net5.buffer.api.internal.Statics; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -25,7 +25,7 @@ import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; -import static io.netty.buffer.api.MemoryManager.using; +import static io.net5.buffer.api.MemoryManager.using; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assumptions.assumeTrue; diff --git a/buffer/src/test/java/io/netty/buffer/api/tests/BufferCompactTest.java b/buffer/src/test/java/io/net5/buffer/api/tests/BufferCompactTest.java similarity index 91% rename from buffer/src/test/java/io/netty/buffer/api/tests/BufferCompactTest.java rename to buffer/src/test/java/io/net5/buffer/api/tests/BufferCompactTest.java index f0c4840b42..53f2162245 100644 --- a/buffer/src/test/java/io/netty/buffer/api/tests/BufferCompactTest.java +++ b/buffer/src/test/java/io/net5/buffer/api/tests/BufferCompactTest.java @@ -13,15 +13,15 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer.api.tests; +package io.net5.buffer.api.tests; -import io.netty.buffer.api.Buffer; -import io.netty.buffer.api.BufferAllocator; -import io.netty.buffer.api.internal.ResourceSupport; +import io.net5.buffer.api.Buffer; +import io.net5.buffer.api.BufferAllocator; +import io.net5.buffer.api.internal.ResourceSupport; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; -import static io.netty.buffer.api.internal.Statics.acquire; +import static io.net5.buffer.api.internal.Statics.acquire; import static org.junit.jupiter.api.Assertions.assertThrows; public class BufferCompactTest extends BufferTestSupport { diff --git a/buffer/src/test/java/io/netty/buffer/api/tests/BufferComponentIterationTest.java b/buffer/src/test/java/io/net5/buffer/api/tests/BufferComponentIterationTest.java similarity index 98% rename from buffer/src/test/java/io/netty/buffer/api/tests/BufferComponentIterationTest.java rename to buffer/src/test/java/io/net5/buffer/api/tests/BufferComponentIterationTest.java index 509c943721..10b7776be0 100644 --- a/buffer/src/test/java/io/netty/buffer/api/tests/BufferComponentIterationTest.java +++ b/buffer/src/test/java/io/net5/buffer/api/tests/BufferComponentIterationTest.java @@ -13,14 +13,14 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer.api.tests; +package io.net5.buffer.api.tests; -import io.netty.buffer.api.Buffer; -import io.netty.buffer.api.BufferAllocator; -import io.netty.buffer.api.BufferClosedException; -import io.netty.buffer.api.BufferReadOnlyException; -import io.netty.buffer.api.ByteCursor; -import io.netty.buffer.api.CompositeBuffer; +import io.net5.buffer.api.Buffer; +import io.net5.buffer.api.BufferAllocator; +import io.net5.buffer.api.BufferClosedException; +import io.net5.buffer.api.BufferReadOnlyException; +import io.net5.buffer.api.ByteCursor; +import io.net5.buffer.api.CompositeBuffer; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; diff --git a/buffer/src/test/java/io/netty/buffer/api/tests/BufferCompositionTest.java b/buffer/src/test/java/io/net5/buffer/api/tests/BufferCompositionTest.java similarity index 98% rename from buffer/src/test/java/io/netty/buffer/api/tests/BufferCompositionTest.java rename to buffer/src/test/java/io/net5/buffer/api/tests/BufferCompositionTest.java index 908c002cd9..3476e64e41 100644 --- a/buffer/src/test/java/io/netty/buffer/api/tests/BufferCompositionTest.java +++ b/buffer/src/test/java/io/net5/buffer/api/tests/BufferCompositionTest.java @@ -13,23 +13,23 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer.api.tests; +package io.net5.buffer.api.tests; -import io.netty.buffer.api.Buffer; -import io.netty.buffer.api.BufferAllocator; -import io.netty.buffer.api.BufferClosedException; -import io.netty.buffer.api.BufferReadOnlyException; -import io.netty.buffer.api.CompositeBuffer; -import io.netty.buffer.api.Drop; -import io.netty.buffer.api.Send; -import io.netty.buffer.api.internal.ResourceSupport; -import io.netty.buffer.api.internal.Statics; +import io.net5.buffer.api.Buffer; +import io.net5.buffer.api.BufferAllocator; +import io.net5.buffer.api.BufferClosedException; +import io.net5.buffer.api.BufferReadOnlyException; +import io.net5.buffer.api.CompositeBuffer; +import io.net5.buffer.api.Drop; +import io.net5.buffer.api.Send; +import io.net5.buffer.api.internal.ResourceSupport; +import io.net5.buffer.api.internal.Statics; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; -import static io.netty.buffer.api.internal.Statics.acquire; -import static io.netty.buffer.api.internal.Statics.isOwned; +import static io.net5.buffer.api.internal.Statics.acquire; +import static io.net5.buffer.api.internal.Statics.isOwned; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; diff --git a/buffer/src/test/java/io/netty/buffer/api/tests/BufferDoubleOffsettedAccessorsTest.java b/buffer/src/test/java/io/net5/buffer/api/tests/BufferDoubleOffsettedAccessorsTest.java similarity index 98% rename from buffer/src/test/java/io/netty/buffer/api/tests/BufferDoubleOffsettedAccessorsTest.java rename to buffer/src/test/java/io/net5/buffer/api/tests/BufferDoubleOffsettedAccessorsTest.java index 151e13d8b5..955ef9be5a 100644 --- a/buffer/src/test/java/io/netty/buffer/api/tests/BufferDoubleOffsettedAccessorsTest.java +++ b/buffer/src/test/java/io/net5/buffer/api/tests/BufferDoubleOffsettedAccessorsTest.java @@ -13,10 +13,10 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer.api.tests; +package io.net5.buffer.api.tests; -import io.netty.buffer.api.Buffer; -import io.netty.buffer.api.BufferAllocator; +import io.net5.buffer.api.Buffer; +import io.net5.buffer.api.BufferAllocator; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; diff --git a/buffer/src/test/java/io/netty/buffer/api/tests/BufferEnsureWritableTest.java b/buffer/src/test/java/io/net5/buffer/api/tests/BufferEnsureWritableTest.java similarity index 97% rename from buffer/src/test/java/io/netty/buffer/api/tests/BufferEnsureWritableTest.java rename to buffer/src/test/java/io/net5/buffer/api/tests/BufferEnsureWritableTest.java index ccf4556963..aab0904817 100644 --- a/buffer/src/test/java/io/netty/buffer/api/tests/BufferEnsureWritableTest.java +++ b/buffer/src/test/java/io/net5/buffer/api/tests/BufferEnsureWritableTest.java @@ -13,11 +13,11 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer.api.tests; +package io.net5.buffer.api.tests; -import io.netty.buffer.api.Buffer; -import io.netty.buffer.api.BufferAllocator; -import io.netty.buffer.api.CompositeBuffer; +import io.net5.buffer.api.Buffer; +import io.net5.buffer.api.BufferAllocator; +import io.net5.buffer.api.CompositeBuffer; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; diff --git a/buffer/src/test/java/io/netty/buffer/api/tests/BufferFloatOffsettedAccessorsTest.java b/buffer/src/test/java/io/net5/buffer/api/tests/BufferFloatOffsettedAccessorsTest.java similarity index 98% rename from buffer/src/test/java/io/netty/buffer/api/tests/BufferFloatOffsettedAccessorsTest.java rename to buffer/src/test/java/io/net5/buffer/api/tests/BufferFloatOffsettedAccessorsTest.java index 0c6369225e..96b8e3b3ae 100644 --- a/buffer/src/test/java/io/netty/buffer/api/tests/BufferFloatOffsettedAccessorsTest.java +++ b/buffer/src/test/java/io/net5/buffer/api/tests/BufferFloatOffsettedAccessorsTest.java @@ -13,10 +13,10 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer.api.tests; +package io.net5.buffer.api.tests; -import io.netty.buffer.api.Buffer; -import io.netty.buffer.api.BufferAllocator; +import io.net5.buffer.api.Buffer; +import io.net5.buffer.api.BufferAllocator; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; diff --git a/buffer/src/test/java/io/netty/buffer/api/tests/BufferIntOffsettedAccessorsTest.java b/buffer/src/test/java/io/net5/buffer/api/tests/BufferIntOffsettedAccessorsTest.java similarity index 99% rename from buffer/src/test/java/io/netty/buffer/api/tests/BufferIntOffsettedAccessorsTest.java rename to buffer/src/test/java/io/net5/buffer/api/tests/BufferIntOffsettedAccessorsTest.java index 95b6246696..5a66f5ca17 100644 --- a/buffer/src/test/java/io/netty/buffer/api/tests/BufferIntOffsettedAccessorsTest.java +++ b/buffer/src/test/java/io/net5/buffer/api/tests/BufferIntOffsettedAccessorsTest.java @@ -13,10 +13,10 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer.api.tests; +package io.net5.buffer.api.tests; -import io.netty.buffer.api.Buffer; -import io.netty.buffer.api.BufferAllocator; +import io.net5.buffer.api.Buffer; +import io.net5.buffer.api.BufferAllocator; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; diff --git a/buffer/src/test/java/io/netty/buffer/api/tests/BufferLifeCycleTest.java b/buffer/src/test/java/io/net5/buffer/api/tests/BufferLifeCycleTest.java similarity index 98% rename from buffer/src/test/java/io/netty/buffer/api/tests/BufferLifeCycleTest.java rename to buffer/src/test/java/io/net5/buffer/api/tests/BufferLifeCycleTest.java index 3e3e990aa9..fb06ea8d50 100644 --- a/buffer/src/test/java/io/netty/buffer/api/tests/BufferLifeCycleTest.java +++ b/buffer/src/test/java/io/net5/buffer/api/tests/BufferLifeCycleTest.java @@ -13,14 +13,14 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer.api.tests; +package io.net5.buffer.api.tests; -import io.netty.buffer.api.Buffer; -import io.netty.buffer.api.BufferAllocator; -import io.netty.buffer.api.BufferClosedException; -import io.netty.buffer.api.CompositeBuffer; -import io.netty.buffer.api.internal.ResourceSupport; -import io.netty.util.internal.EmptyArrays; +import io.net5.buffer.api.Buffer; +import io.net5.buffer.api.BufferAllocator; +import io.net5.buffer.api.BufferClosedException; +import io.net5.buffer.api.CompositeBuffer; +import io.net5.buffer.api.internal.ResourceSupport; +import io.net5.util.internal.EmptyArrays; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -29,8 +29,8 @@ import java.util.concurrent.Future; import java.util.concurrent.ThreadLocalRandom; import java.util.function.Supplier; -import static io.netty.buffer.api.internal.Statics.acquire; -import static io.netty.buffer.api.internal.Statics.isOwned; +import static io.net5.buffer.api.internal.Statics.acquire; +import static io.net5.buffer.api.internal.Statics.isOwned; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; diff --git a/buffer/src/test/java/io/netty/buffer/api/tests/BufferLongOffsettedAccessorsTest.java b/buffer/src/test/java/io/net5/buffer/api/tests/BufferLongOffsettedAccessorsTest.java similarity index 98% rename from buffer/src/test/java/io/netty/buffer/api/tests/BufferLongOffsettedAccessorsTest.java rename to buffer/src/test/java/io/net5/buffer/api/tests/BufferLongOffsettedAccessorsTest.java index 1df9eafd40..3d286418da 100644 --- a/buffer/src/test/java/io/netty/buffer/api/tests/BufferLongOffsettedAccessorsTest.java +++ b/buffer/src/test/java/io/net5/buffer/api/tests/BufferLongOffsettedAccessorsTest.java @@ -13,10 +13,10 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer.api.tests; +package io.net5.buffer.api.tests; -import io.netty.buffer.api.Buffer; -import io.netty.buffer.api.BufferAllocator; +import io.net5.buffer.api.Buffer; +import io.net5.buffer.api.BufferAllocator; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; diff --git a/buffer/src/test/java/io/netty/buffer/api/tests/BufferMediumOffsettedAccessorsTest.java b/buffer/src/test/java/io/net5/buffer/api/tests/BufferMediumOffsettedAccessorsTest.java similarity index 99% rename from buffer/src/test/java/io/netty/buffer/api/tests/BufferMediumOffsettedAccessorsTest.java rename to buffer/src/test/java/io/net5/buffer/api/tests/BufferMediumOffsettedAccessorsTest.java index 30edd9e949..3adb831fe4 100644 --- a/buffer/src/test/java/io/netty/buffer/api/tests/BufferMediumOffsettedAccessorsTest.java +++ b/buffer/src/test/java/io/net5/buffer/api/tests/BufferMediumOffsettedAccessorsTest.java @@ -13,10 +13,10 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer.api.tests; +package io.net5.buffer.api.tests; -import io.netty.buffer.api.Buffer; -import io.netty.buffer.api.BufferAllocator; +import io.net5.buffer.api.Buffer; +import io.net5.buffer.api.BufferAllocator; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; diff --git a/buffer/src/test/java/io/netty/buffer/api/tests/BufferOffsetsTest.java b/buffer/src/test/java/io/net5/buffer/api/tests/BufferOffsetsTest.java similarity index 98% rename from buffer/src/test/java/io/netty/buffer/api/tests/BufferOffsetsTest.java rename to buffer/src/test/java/io/net5/buffer/api/tests/BufferOffsetsTest.java index c700094942..138c62cd47 100644 --- a/buffer/src/test/java/io/netty/buffer/api/tests/BufferOffsetsTest.java +++ b/buffer/src/test/java/io/net5/buffer/api/tests/BufferOffsetsTest.java @@ -13,10 +13,10 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer.api.tests; +package io.net5.buffer.api.tests; -import io.netty.buffer.api.Buffer; -import io.netty.buffer.api.BufferAllocator; +import io.net5.buffer.api.Buffer; +import io.net5.buffer.api.BufferAllocator; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; diff --git a/buffer/src/test/java/io/netty/buffer/api/tests/BufferPrimitiveRelativeAccessorsTest.java b/buffer/src/test/java/io/net5/buffer/api/tests/BufferPrimitiveRelativeAccessorsTest.java similarity index 99% rename from buffer/src/test/java/io/netty/buffer/api/tests/BufferPrimitiveRelativeAccessorsTest.java rename to buffer/src/test/java/io/net5/buffer/api/tests/BufferPrimitiveRelativeAccessorsTest.java index 840eeba8a1..73546fe319 100644 --- a/buffer/src/test/java/io/netty/buffer/api/tests/BufferPrimitiveRelativeAccessorsTest.java +++ b/buffer/src/test/java/io/net5/buffer/api/tests/BufferPrimitiveRelativeAccessorsTest.java @@ -13,10 +13,10 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer.api.tests; +package io.net5.buffer.api.tests; -import io.netty.buffer.api.Buffer; -import io.netty.buffer.api.BufferAllocator; +import io.net5.buffer.api.Buffer; +import io.net5.buffer.api.BufferAllocator; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; diff --git a/buffer/src/test/java/io/netty/buffer/api/tests/BufferReadOnlyTest.java b/buffer/src/test/java/io/net5/buffer/api/tests/BufferReadOnlyTest.java similarity index 96% rename from buffer/src/test/java/io/netty/buffer/api/tests/BufferReadOnlyTest.java rename to buffer/src/test/java/io/net5/buffer/api/tests/BufferReadOnlyTest.java index e0731c6588..b66d099b7a 100644 --- a/buffer/src/test/java/io/netty/buffer/api/tests/BufferReadOnlyTest.java +++ b/buffer/src/test/java/io/net5/buffer/api/tests/BufferReadOnlyTest.java @@ -13,21 +13,21 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer.api.tests; +package io.net5.buffer.api.tests; -import io.netty.buffer.api.Buffer; -import io.netty.buffer.api.BufferAllocator; -import io.netty.buffer.api.BufferReadOnlyException; -import io.netty.buffer.api.CompositeBuffer; -import io.netty.buffer.api.Send; -import io.netty.buffer.api.internal.ResourceSupport; +import io.net5.buffer.api.Buffer; +import io.net5.buffer.api.BufferAllocator; +import io.net5.buffer.api.BufferReadOnlyException; +import io.net5.buffer.api.CompositeBuffer; +import io.net5.buffer.api.Send; +import io.net5.buffer.api.internal.ResourceSupport; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import java.util.function.Supplier; -import static io.netty.buffer.api.internal.Statics.isOwned; +import static io.net5.buffer.api.internal.Statics.isOwned; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; diff --git a/buffer/src/test/java/io/netty/buffer/api/tests/BufferRefTest.java b/buffer/src/test/java/io/net5/buffer/api/tests/BufferRefTest.java similarity index 93% rename from buffer/src/test/java/io/netty/buffer/api/tests/BufferRefTest.java rename to buffer/src/test/java/io/net5/buffer/api/tests/BufferRefTest.java index d4521adce7..c6e4923665 100644 --- a/buffer/src/test/java/io/netty/buffer/api/tests/BufferRefTest.java +++ b/buffer/src/test/java/io/net5/buffer/api/tests/BufferRefTest.java @@ -13,13 +13,13 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer.api.tests; +package io.net5.buffer.api.tests; -import io.netty.buffer.api.Buffer; -import io.netty.buffer.api.BufferAllocator; -import io.netty.buffer.api.BufferClosedException; -import io.netty.buffer.api.BufferRef; -import io.netty.buffer.api.Send; +import io.net5.buffer.api.Buffer; +import io.net5.buffer.api.BufferAllocator; +import io.net5.buffer.api.BufferClosedException; +import io.net5.buffer.api.BufferRef; +import io.net5.buffer.api.Send; import org.junit.jupiter.api.Test; import java.util.concurrent.atomic.AtomicReference; diff --git a/buffer/src/test/java/io/netty/buffer/api/tests/BufferSendTest.java b/buffer/src/test/java/io/net5/buffer/api/tests/BufferSendTest.java similarity index 93% rename from buffer/src/test/java/io/netty/buffer/api/tests/BufferSendTest.java rename to buffer/src/test/java/io/net5/buffer/api/tests/BufferSendTest.java index e7c6015de3..e8f51985d7 100644 --- a/buffer/src/test/java/io/netty/buffer/api/tests/BufferSendTest.java +++ b/buffer/src/test/java/io/net5/buffer/api/tests/BufferSendTest.java @@ -13,14 +13,14 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer.api.tests; +package io.net5.buffer.api.tests; -import io.netty.buffer.api.Buffer; -import io.netty.buffer.api.BufferAllocator; -import io.netty.buffer.api.BufferClosedException; -import io.netty.buffer.api.BufferRef; -import io.netty.buffer.api.Send; -import io.netty.buffer.api.internal.ResourceSupport; +import io.net5.buffer.api.Buffer; +import io.net5.buffer.api.BufferAllocator; +import io.net5.buffer.api.BufferClosedException; +import io.net5.buffer.api.BufferRef; +import io.net5.buffer.api.Send; +import io.net5.buffer.api.internal.ResourceSupport; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -29,8 +29,8 @@ import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.Future; import java.util.concurrent.SynchronousQueue; -import static io.netty.buffer.api.internal.Statics.acquire; -import static io.netty.buffer.api.internal.Statics.isOwned; +import static io.net5.buffer.api.internal.Statics.acquire; +import static io.net5.buffer.api.internal.Statics.isOwned; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; diff --git a/buffer/src/test/java/io/netty/buffer/api/tests/BufferShortOffsettedAccessorsTest.java b/buffer/src/test/java/io/net5/buffer/api/tests/BufferShortOffsettedAccessorsTest.java similarity index 99% rename from buffer/src/test/java/io/netty/buffer/api/tests/BufferShortOffsettedAccessorsTest.java rename to buffer/src/test/java/io/net5/buffer/api/tests/BufferShortOffsettedAccessorsTest.java index d9eb8c89e3..f0857f3481 100644 --- a/buffer/src/test/java/io/netty/buffer/api/tests/BufferShortOffsettedAccessorsTest.java +++ b/buffer/src/test/java/io/net5/buffer/api/tests/BufferShortOffsettedAccessorsTest.java @@ -13,10 +13,10 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer.api.tests; +package io.net5.buffer.api.tests; -import io.netty.buffer.api.Buffer; -import io.netty.buffer.api.BufferAllocator; +import io.net5.buffer.api.Buffer; +import io.net5.buffer.api.BufferAllocator; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; diff --git a/buffer/src/test/java/io/netty/buffer/api/tests/BufferSplitTest.java b/buffer/src/test/java/io/net5/buffer/api/tests/BufferSplitTest.java similarity index 95% rename from buffer/src/test/java/io/netty/buffer/api/tests/BufferSplitTest.java rename to buffer/src/test/java/io/net5/buffer/api/tests/BufferSplitTest.java index 26dcd4ed1d..f895069cf2 100644 --- a/buffer/src/test/java/io/netty/buffer/api/tests/BufferSplitTest.java +++ b/buffer/src/test/java/io/net5/buffer/api/tests/BufferSplitTest.java @@ -12,10 +12,10 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package io.netty.buffer.api.tests; +package io.net5.buffer.api.tests; -import io.netty.buffer.api.Buffer; -import io.netty.buffer.api.BufferAllocator; +import io.net5.buffer.api.Buffer; +import io.net5.buffer.api.BufferAllocator; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; diff --git a/buffer/src/test/java/io/netty/buffer/api/tests/BufferTestSupport.java b/buffer/src/test/java/io/net5/buffer/api/tests/BufferTestSupport.java similarity index 98% rename from buffer/src/test/java/io/netty/buffer/api/tests/BufferTestSupport.java rename to buffer/src/test/java/io/net5/buffer/api/tests/BufferTestSupport.java index ca24c9005e..74c3072bc1 100644 --- a/buffer/src/test/java/io/netty/buffer/api/tests/BufferTestSupport.java +++ b/buffer/src/test/java/io/net5/buffer/api/tests/BufferTestSupport.java @@ -13,16 +13,16 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer.api.tests; +package io.net5.buffer.api.tests; -import io.netty.buffer.api.Buffer; -import io.netty.buffer.api.BufferAllocator; -import io.netty.buffer.api.BufferClosedException; -import io.netty.buffer.api.CompositeBuffer; -import io.netty.buffer.api.MemoryManager; -import io.netty.buffer.api.internal.ResourceSupport; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; +import io.net5.buffer.api.Buffer; +import io.net5.buffer.api.BufferAllocator; +import io.net5.buffer.api.BufferClosedException; +import io.net5.buffer.api.CompositeBuffer; +import io.net5.buffer.api.MemoryManager; +import io.net5.buffer.api.internal.ResourceSupport; +import io.net5.util.internal.logging.InternalLogger; +import io.net5.util.internal.logging.InternalLoggerFactory; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -48,10 +48,10 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.stream.Stream.Builder; -import static io.netty.buffer.api.internal.Statics.acquire; -import static io.netty.buffer.api.tests.Fixture.Properties.DIRECT; -import static io.netty.buffer.api.tests.Fixture.Properties.HEAP; -import static io.netty.buffer.api.tests.Fixture.Properties.POOLED; +import static io.net5.buffer.api.internal.Statics.acquire; +import static io.net5.buffer.api.tests.Fixture.Properties.DIRECT; +import static io.net5.buffer.api.tests.Fixture.Properties.HEAP; +import static io.net5.buffer.api.tests.Fixture.Properties.POOLED; import static java.nio.ByteOrder.BIG_ENDIAN; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertFalse; diff --git a/buffer/src/test/java/io/netty/buffer/api/tests/BufferWriteBytesCombinationsTest.java b/buffer/src/test/java/io/net5/buffer/api/tests/BufferWriteBytesCombinationsTest.java similarity index 95% rename from buffer/src/test/java/io/netty/buffer/api/tests/BufferWriteBytesCombinationsTest.java rename to buffer/src/test/java/io/net5/buffer/api/tests/BufferWriteBytesCombinationsTest.java index 9eb63ced4c..c80e9a71af 100644 --- a/buffer/src/test/java/io/netty/buffer/api/tests/BufferWriteBytesCombinationsTest.java +++ b/buffer/src/test/java/io/net5/buffer/api/tests/BufferWriteBytesCombinationsTest.java @@ -13,10 +13,10 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer.api.tests; +package io.net5.buffer.api.tests; -import io.netty.buffer.api.Buffer; -import io.netty.buffer.api.BufferAllocator; +import io.net5.buffer.api.Buffer; +import io.net5.buffer.api.BufferAllocator; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; diff --git a/buffer/src/test/java/io/netty/buffer/api/tests/Fixture.java b/buffer/src/test/java/io/net5/buffer/api/tests/Fixture.java similarity index 96% rename from buffer/src/test/java/io/netty/buffer/api/tests/Fixture.java rename to buffer/src/test/java/io/net5/buffer/api/tests/Fixture.java index ebbf6095e4..fb4e9f343b 100644 --- a/buffer/src/test/java/io/netty/buffer/api/tests/Fixture.java +++ b/buffer/src/test/java/io/net5/buffer/api/tests/Fixture.java @@ -13,9 +13,9 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer.api.tests; +package io.net5.buffer.api.tests; -import io.netty.buffer.api.BufferAllocator; +import io.net5.buffer.api.BufferAllocator; import java.util.Arrays; import java.util.EnumSet; diff --git a/buffer/src/test/java/io/netty/buffer/api/tests/Memoize.java b/buffer/src/test/java/io/net5/buffer/api/tests/Memoize.java similarity index 96% rename from buffer/src/test/java/io/netty/buffer/api/tests/Memoize.java rename to buffer/src/test/java/io/net5/buffer/api/tests/Memoize.java index fbfda6b6ec..fe7bf2f928 100644 --- a/buffer/src/test/java/io/netty/buffer/api/tests/Memoize.java +++ b/buffer/src/test/java/io/net5/buffer/api/tests/Memoize.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer.api.tests; +package io.net5.buffer.api.tests; import java.util.function.Supplier; diff --git a/buffer/src/test/java/io/netty/buffer/api/tests/adaptor/AbstractByteBufTest.java b/buffer/src/test/java/io/net5/buffer/api/tests/adaptor/AbstractByteBufTest.java similarity index 99% rename from buffer/src/test/java/io/netty/buffer/api/tests/adaptor/AbstractByteBufTest.java rename to buffer/src/test/java/io/net5/buffer/api/tests/adaptor/AbstractByteBufTest.java index f45da25b65..682c32c689 100644 --- a/buffer/src/test/java/io/netty/buffer/api/tests/adaptor/AbstractByteBufTest.java +++ b/buffer/src/test/java/io/net5/buffer/api/tests/adaptor/AbstractByteBufTest.java @@ -13,14 +13,14 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer.api.tests.adaptor; +package io.net5.buffer.api.tests.adaptor; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.util.ByteProcessor; -import io.netty.util.CharsetUtil; -import io.netty.util.IllegalReferenceCountException; -import io.netty.util.internal.PlatformDependent; +import io.net5.buffer.ByteBuf; +import io.net5.buffer.ByteBufUtil; +import io.net5.util.ByteProcessor; +import io.net5.util.CharsetUtil; +import io.net5.util.IllegalReferenceCountException; +import io.net5.util.internal.PlatformDependent; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -54,13 +54,13 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; -import static io.netty.buffer.Unpooled.LITTLE_ENDIAN; -import static io.netty.buffer.Unpooled.buffer; -import static io.netty.buffer.Unpooled.copiedBuffer; -import static io.netty.buffer.Unpooled.directBuffer; -import static io.netty.buffer.Unpooled.unreleasableBuffer; -import static io.netty.buffer.Unpooled.wrappedBuffer; -import static io.netty.util.internal.EmptyArrays.EMPTY_BYTES; +import static io.net5.buffer.Unpooled.LITTLE_ENDIAN; +import static io.net5.buffer.Unpooled.buffer; +import static io.net5.buffer.Unpooled.copiedBuffer; +import static io.net5.buffer.Unpooled.directBuffer; +import static io.net5.buffer.Unpooled.unreleasableBuffer; +import static io.net5.buffer.Unpooled.wrappedBuffer; +import static io.net5.util.internal.EmptyArrays.EMPTY_BYTES; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/buffer/src/test/java/io/netty/buffer/api/tests/adaptor/ByteBufAdaptorTest.java b/buffer/src/test/java/io/net5/buffer/api/tests/adaptor/ByteBufAdaptorTest.java similarity index 93% rename from buffer/src/test/java/io/netty/buffer/api/tests/adaptor/ByteBufAdaptorTest.java rename to buffer/src/test/java/io/net5/buffer/api/tests/adaptor/ByteBufAdaptorTest.java index 8fd4ee91ec..57d8f5931a 100644 --- a/buffer/src/test/java/io/netty/buffer/api/tests/adaptor/ByteBufAdaptorTest.java +++ b/buffer/src/test/java/io/net5/buffer/api/tests/adaptor/ByteBufAdaptorTest.java @@ -13,12 +13,12 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer.api.tests.adaptor; +package io.net5.buffer.api.tests.adaptor; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.api.BufferAllocator; -import io.netty.buffer.api.MemoryManager; -import io.netty.buffer.api.adaptor.ByteBufAllocatorAdaptor; +import io.net5.buffer.ByteBuf; +import io.net5.buffer.api.BufferAllocator; +import io.net5.buffer.api.MemoryManager; +import io.net5.buffer.api.adaptor.ByteBufAllocatorAdaptor; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Disabled; diff --git a/buffer/src/test/java/io/netty/buffer/api/tests/adaptor/NioByteBufAdaptorTest.java b/buffer/src/test/java/io/net5/buffer/api/tests/adaptor/NioByteBufAdaptorTest.java similarity index 95% rename from buffer/src/test/java/io/netty/buffer/api/tests/adaptor/NioByteBufAdaptorTest.java rename to buffer/src/test/java/io/net5/buffer/api/tests/adaptor/NioByteBufAdaptorTest.java index de0f4e14a7..372e80f987 100644 --- a/buffer/src/test/java/io/netty/buffer/api/tests/adaptor/NioByteBufAdaptorTest.java +++ b/buffer/src/test/java/io/net5/buffer/api/tests/adaptor/NioByteBufAdaptorTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer.api.tests.adaptor; +package io.net5.buffer.api.tests.adaptor; import org.junit.jupiter.api.BeforeAll; diff --git a/buffer/src/test/java/io/netty/buffer/api/tests/adaptor/UnsafeByteBufAdaptorTest.java b/buffer/src/test/java/io/net5/buffer/api/tests/adaptor/UnsafeByteBufAdaptorTest.java similarity index 95% rename from buffer/src/test/java/io/netty/buffer/api/tests/adaptor/UnsafeByteBufAdaptorTest.java rename to buffer/src/test/java/io/net5/buffer/api/tests/adaptor/UnsafeByteBufAdaptorTest.java index ae5601354b..ccdf68ed6d 100644 --- a/buffer/src/test/java/io/netty/buffer/api/tests/adaptor/UnsafeByteBufAdaptorTest.java +++ b/buffer/src/test/java/io/net5/buffer/api/tests/adaptor/UnsafeByteBufAdaptorTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer.api.tests.adaptor; +package io.net5.buffer.api.tests.adaptor; import org.junit.jupiter.api.BeforeAll; diff --git a/buffer/src/test/java/io/netty/buffer/search/BitapSearchProcessorFactoryTest.java b/buffer/src/test/java/io/net5/buffer/search/BitapSearchProcessorFactoryTest.java similarity index 97% rename from buffer/src/test/java/io/netty/buffer/search/BitapSearchProcessorFactoryTest.java rename to buffer/src/test/java/io/net5/buffer/search/BitapSearchProcessorFactoryTest.java index c087604e06..fac11c04a8 100644 --- a/buffer/src/test/java/io/netty/buffer/search/BitapSearchProcessorFactoryTest.java +++ b/buffer/src/test/java/io/net5/buffer/search/BitapSearchProcessorFactoryTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer.search; +package io.net5.buffer.search; import org.junit.jupiter.api.Test; diff --git a/buffer/src/test/java/io/netty/buffer/search/MultiSearchProcessorTest.java b/buffer/src/test/java/io/net5/buffer/search/MultiSearchProcessorTest.java similarity index 96% rename from buffer/src/test/java/io/netty/buffer/search/MultiSearchProcessorTest.java rename to buffer/src/test/java/io/net5/buffer/search/MultiSearchProcessorTest.java index 1a489822ba..d7bb359914 100644 --- a/buffer/src/test/java/io/netty/buffer/search/MultiSearchProcessorTest.java +++ b/buffer/src/test/java/io/net5/buffer/search/MultiSearchProcessorTest.java @@ -13,11 +13,11 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer.search; +package io.net5.buffer.search; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.CharsetUtil; +import io.net5.buffer.ByteBuf; +import io.net5.buffer.Unpooled; +import io.net5.util.CharsetUtil; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/buffer/src/test/java/io/netty/buffer/search/SearchProcessorTest.java b/buffer/src/test/java/io/net5/buffer/search/SearchProcessorTest.java similarity index 98% rename from buffer/src/test/java/io/netty/buffer/search/SearchProcessorTest.java rename to buffer/src/test/java/io/net5/buffer/search/SearchProcessorTest.java index b24caf0d8a..8705c6027b 100644 --- a/buffer/src/test/java/io/netty/buffer/search/SearchProcessorTest.java +++ b/buffer/src/test/java/io/net5/buffer/search/SearchProcessorTest.java @@ -13,11 +13,11 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer.search; +package io.net5.buffer.search; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.CharsetUtil; +import io.net5.buffer.ByteBuf; +import io.net5.buffer.Unpooled; +import io.net5.util.CharsetUtil; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; diff --git a/codec-dns/pom.xml b/codec-dns/pom.xml deleted file mode 100644 index 3c643f9689..0000000000 --- a/codec-dns/pom.xml +++ /dev/null @@ -1,64 +0,0 @@ - - - - - 4.0.0 - - io.netty - netty-parent - 5.0.0.Final-SNAPSHOT - - - netty-codec-dns - jar - - Netty/Codec/DNS - - - io.netty.codec.dns - - - - - ${project.groupId} - netty-common - ${project.version} - - - ${project.groupId} - netty-buffer - ${project.version} - - - ${project.groupId} - netty-transport - ${project.version} - - - ${project.groupId} - netty-codec - ${project.version} - - - - org.apache.directory.server - apacheds-protocol-dns - test - - - - diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/AbstractDnsMessage.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/AbstractDnsMessage.java deleted file mode 100644 index 52d98d56c3..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/AbstractDnsMessage.java +++ /dev/null @@ -1,478 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.util.AbstractReferenceCounted; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.ReferenceCounted; -import io.netty.util.ResourceLeakDetector; -import io.netty.util.ResourceLeakDetectorFactory; -import io.netty.util.ResourceLeakTracker; -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.UnstableApi; - -import java.util.ArrayList; -import java.util.List; - -import static java.util.Objects.requireNonNull; - -/** - * A skeletal implementation of {@link DnsMessage}. - */ -@UnstableApi -public abstract class AbstractDnsMessage extends AbstractReferenceCounted implements DnsMessage { - - private static final ResourceLeakDetector leakDetector = - ResourceLeakDetectorFactory.instance().newResourceLeakDetector(DnsMessage.class); - - private static final int SECTION_QUESTION = DnsSection.QUESTION.ordinal(); - private static final int SECTION_COUNT = 4; - - private final ResourceLeakTracker leak = leakDetector.track(this); - private short id; - private DnsOpCode opCode; - private boolean recursionDesired; - private byte z; - - // To reduce the memory footprint of a message, - // each of the following fields is a single record or a list of records. - private Object questions; - private Object answers; - private Object authorities; - private Object additionals; - - /** - * Creates a new instance with the specified {@code id} and {@link DnsOpCode#QUERY} opCode. - */ - protected AbstractDnsMessage(int id) { - this(id, DnsOpCode.QUERY); - } - - /** - * Creates a new instance with the specified {@code id} and {@code opCode}. - */ - protected AbstractDnsMessage(int id, DnsOpCode opCode) { - setId(id); - setOpCode(opCode); - } - - @Override - public int id() { - return id & 0xFFFF; - } - - @Override - public DnsMessage setId(int id) { - this.id = (short) id; - return this; - } - - @Override - public DnsOpCode opCode() { - return opCode; - } - - @Override - public DnsMessage setOpCode(DnsOpCode opCode) { - this.opCode = requireNonNull(opCode, "opCode"); - return this; - } - - @Override - public boolean isRecursionDesired() { - return recursionDesired; - } - - @Override - public DnsMessage setRecursionDesired(boolean recursionDesired) { - this.recursionDesired = recursionDesired; - return this; - } - - @Override - public int z() { - return z; - } - - @Override - public DnsMessage setZ(int z) { - this.z = (byte) (z & 7); - return this; - } - - @Override - public int count(DnsSection section) { - return count(sectionOrdinal(section)); - } - - private int count(int section) { - final Object records = sectionAt(section); - if (records == null) { - return 0; - } - if (records instanceof DnsRecord) { - return 1; - } - - @SuppressWarnings("unchecked") - final List recordList = (List) records; - return recordList.size(); - } - - @Override - public int count() { - int count = 0; - for (int i = 0; i < SECTION_COUNT; i ++) { - count += count(i); - } - return count; - } - - @Override - public T recordAt(DnsSection section) { - return recordAt(sectionOrdinal(section)); - } - - private T recordAt(int section) { - final Object records = sectionAt(section); - if (records == null) { - return null; - } - - if (records instanceof DnsRecord) { - return castRecord(records); - } - - @SuppressWarnings("unchecked") - final List recordList = (List) records; - if (recordList.isEmpty()) { - return null; - } - - return castRecord(recordList.get(0)); - } - - @Override - public T recordAt(DnsSection section, int index) { - return recordAt(sectionOrdinal(section), index); - } - - private T recordAt(int section, int index) { - final Object records = sectionAt(section); - if (records == null) { - throw new IndexOutOfBoundsException("index: " + index + " (expected: none)"); - } - - if (records instanceof DnsRecord) { - if (index == 0) { - return castRecord(records); - } else { - throw new IndexOutOfBoundsException("index: " + index + "' (expected: 0)"); - } - } - - @SuppressWarnings("unchecked") - final List recordList = (List) records; - return castRecord(recordList.get(index)); - } - - @Override - public DnsMessage setRecord(DnsSection section, DnsRecord record) { - setRecord(sectionOrdinal(section), record); - return this; - } - - private void setRecord(int section, DnsRecord record) { - clear(section); - setSection(section, checkQuestion(section, record)); - } - - @Override - public T setRecord(DnsSection section, int index, DnsRecord record) { - return setRecord(sectionOrdinal(section), index, record); - } - - private T setRecord(int section, int index, DnsRecord record) { - checkQuestion(section, record); - - final Object records = sectionAt(section); - if (records == null) { - throw new IndexOutOfBoundsException("index: " + index + " (expected: none)"); - } - - if (records instanceof DnsRecord) { - if (index == 0) { - setSection(section, record); - return castRecord(records); - } else { - throw new IndexOutOfBoundsException("index: " + index + " (expected: 0)"); - } - } - - @SuppressWarnings("unchecked") - final List recordList = (List) records; - return castRecord(recordList.set(index, record)); - } - - @Override - public DnsMessage addRecord(DnsSection section, DnsRecord record) { - addRecord(sectionOrdinal(section), record); - return this; - } - - private void addRecord(int section, DnsRecord record) { - checkQuestion(section, record); - - final Object records = sectionAt(section); - if (records == null) { - setSection(section, record); - return; - } - - if (records instanceof DnsRecord) { - final List recordList = newRecordList(); - recordList.add(castRecord(records)); - recordList.add(record); - setSection(section, recordList); - return; - } - - @SuppressWarnings("unchecked") - final List recordList = (List) records; - recordList.add(record); - } - - @Override - public DnsMessage addRecord(DnsSection section, int index, DnsRecord record) { - addRecord(sectionOrdinal(section), index, record); - return this; - } - - private void addRecord(int section, int index, DnsRecord record) { - checkQuestion(section, record); - - final Object records = sectionAt(section); - if (records == null) { - if (index != 0) { - throw new IndexOutOfBoundsException("index: " + index + " (expected: 0)"); - } - - setSection(section, record); - return; - } - - if (records instanceof DnsRecord) { - final List recordList; - if (index == 0) { - recordList = newRecordList(); - recordList.add(record); - recordList.add(castRecord(records)); - } else if (index == 1) { - recordList = newRecordList(); - recordList.add(castRecord(records)); - recordList.add(record); - } else { - throw new IndexOutOfBoundsException("index: " + index + " (expected: 0 or 1)"); - } - setSection(section, recordList); - return; - } - - @SuppressWarnings("unchecked") - final List recordList = (List) records; - recordList.add(index, record); - } - - @Override - public T removeRecord(DnsSection section, int index) { - return removeRecord(sectionOrdinal(section), index); - } - - private T removeRecord(int section, int index) { - final Object records = sectionAt(section); - if (records == null) { - throw new IndexOutOfBoundsException("index: " + index + " (expected: none)"); - } - - if (records instanceof DnsRecord) { - if (index != 0) { - throw new IndexOutOfBoundsException("index: " + index + " (expected: 0)"); - } - - T record = castRecord(records); - setSection(section, null); - return record; - } - - @SuppressWarnings("unchecked") - final List recordList = (List) records; - return castRecord(recordList.remove(index)); - } - - @Override - public DnsMessage clear(DnsSection section) { - clear(sectionOrdinal(section)); - return this; - } - - @Override - public DnsMessage clear() { - for (int i = 0; i < SECTION_COUNT; i ++) { - clear(i); - } - return this; - } - - private void clear(int section) { - final Object recordOrList = sectionAt(section); - setSection(section, null); - if (recordOrList instanceof ReferenceCounted) { - ((ReferenceCounted) recordOrList).release(); - } else if (recordOrList instanceof List) { - @SuppressWarnings("unchecked") - List list = (List) recordOrList; - if (!list.isEmpty()) { - for (Object r : list) { - ReferenceCountUtil.release(r); - } - } - } - } - - @Override - public DnsMessage touch() { - return (DnsMessage) super.touch(); - } - - @Override - public DnsMessage touch(Object hint) { - if (leak != null) { - leak.record(hint); - } - return this; - } - - @Override - public DnsMessage retain() { - return (DnsMessage) super.retain(); - } - - @Override - public DnsMessage retain(int increment) { - return (DnsMessage) super.retain(increment); - } - - @Override - protected void deallocate() { - clear(); - - final ResourceLeakTracker leak = this.leak; - if (leak != null) { - boolean closed = leak.close(this); - assert closed; - } - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - - if (!(obj instanceof DnsMessage)) { - return false; - } - - final DnsMessage that = (DnsMessage) obj; - if (id() != that.id()) { - return false; - } - - if (this instanceof DnsQuery) { - if (!(that instanceof DnsQuery)) { - return false; - } - } else if (that instanceof DnsQuery) { - return false; - } - - return true; - } - - @Override - public int hashCode() { - return id() * 31 + (this instanceof DnsQuery? 0 : 1); - } - - private Object sectionAt(int section) { - switch (section) { - case 0: - return questions; - case 1: - return answers; - case 2: - return authorities; - case 3: - return additionals; - default: - break; - } - - throw new Error(); // Should never reach here. - } - - private void setSection(int section, Object value) { - switch (section) { - case 0: - questions = value; - return; - case 1: - answers = value; - return; - case 2: - authorities = value; - return; - case 3: - additionals = value; - return; - default: - break; - } - - throw new Error(); // Should never reach here. - } - - private static int sectionOrdinal(DnsSection section) { - return requireNonNull(section, "section").ordinal(); - } - - private static DnsRecord checkQuestion(int section, DnsRecord record) { - if (section == SECTION_QUESTION && !(requireNonNull(record, "record") instanceof DnsQuestion)) { - throw new IllegalArgumentException( - "record: " + record + " (expected: " + StringUtil.simpleClassName(DnsQuestion.class) + ')'); - } - return record; - } - - @SuppressWarnings("unchecked") - private static T castRecord(Object record) { - return (T) record; - } - - private static ArrayList newRecordList() { - return new ArrayList<>(2); - } -} diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/AbstractDnsOptPseudoRrRecord.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/AbstractDnsOptPseudoRrRecord.java deleted file mode 100644 index 13439c1686..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/AbstractDnsOptPseudoRrRecord.java +++ /dev/null @@ -1,78 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.UnstableApi; - -/** - * An OPT RR record. - * - * This is used for - * Extension Mechanisms for DNS (EDNS(0)). - */ -@UnstableApi -public abstract class AbstractDnsOptPseudoRrRecord extends AbstractDnsRecord implements DnsOptPseudoRecord { - - protected AbstractDnsOptPseudoRrRecord(int maxPayloadSize, int extendedRcode, int version) { - super(StringUtil.EMPTY_STRING, DnsRecordType.OPT, maxPayloadSize, packIntoLong(extendedRcode, version)); - } - - protected AbstractDnsOptPseudoRrRecord(int maxPayloadSize) { - super(StringUtil.EMPTY_STRING, DnsRecordType.OPT, maxPayloadSize, 0); - } - - // See https://tools.ietf.org/html/rfc6891#section-6.1.3 - private static long packIntoLong(int val, int val2) { - // We are currently not support DO and Z fields, just use 0. - return ((val & 0xffL) << 24 | (val2 & 0xff) << 16) & 0xFFFFFFFFL; - } - - @Override - public int extendedRcode() { - return (short) (((int) timeToLive() >> 24) & 0xff); - } - - @Override - public int version() { - return (short) (((int) timeToLive() >> 16) & 0xff); - } - - @Override - public int flags() { - return (short) ((short) timeToLive() & 0xff); - } - - @Override - public String toString() { - return toStringBuilder().toString(); - } - - final StringBuilder toStringBuilder() { - return new StringBuilder(64) - .append(StringUtil.simpleClassName(this)) - .append('(') - .append("OPT flags:") - .append(flags()) - .append(" version:") - .append(version()) - .append(" extendedRecode:") - .append(extendedRcode()) - .append(" udp:") - .append(dnsClass()) - .append(')'); - } -} 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 deleted file mode 100644 index 290ee1fa02..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/AbstractDnsRecord.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.UnstableApi; - -import java.net.IDN; - -import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; -import static java.util.Objects.requireNonNull; - -/** - * A skeletal implementation of {@link DnsRecord}. - */ -@UnstableApi -public abstract class AbstractDnsRecord implements DnsRecord { - - private final String name; - private final DnsRecordType type; - private final short dnsClass; - private final long timeToLive; - private int hashCode; - - /** - * Creates a new {@link #CLASS_IN IN-class} record. - * - * @param name the domain name - * @param type the type of the record - * @param timeToLive the TTL value of the record - */ - protected AbstractDnsRecord(String name, DnsRecordType type, long timeToLive) { - this(name, type, CLASS_IN, timeToLive); - } - - /** - * Creates a new record. - * - * @param name the domain name - * @param type the type of the record - * @param dnsClass the class of the record, usually one of the following: - *
    - *
  • {@link #CLASS_IN}
  • - *
  • {@link #CLASS_CSNET}
  • - *
  • {@link #CLASS_CHAOS}
  • - *
  • {@link #CLASS_HESIOD}
  • - *
  • {@link #CLASS_NONE}
  • - *
  • {@link #CLASS_ANY}
  • - *
- * @param timeToLive the TTL value of the record - */ - protected AbstractDnsRecord(String name, DnsRecordType type, int dnsClass, long timeToLive) { - 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 - // - https://github.com/netty/netty/issues/4935 - this.name = appendTrailingDot(IDNtoASCII(name)); - this.type = requireNonNull(type, "type"); - this.dnsClass = (short) dnsClass; - this.timeToLive = timeToLive; - } - - private static String IDNtoASCII(String name) { - requireNonNull(name, "name"); - if (PlatformDependent.isAndroid() && DefaultDnsRecordDecoder.ROOT.equals(name)) { - // Prior Android 10 there was a bug that did not correctly parse ".". - // - // See https://github.com/netty/netty/issues/10034 - return name; - } - return IDN.toASCII(name); - } - - private static String appendTrailingDot(String name) { - if (name.length() > 0 && name.charAt(name.length() - 1) != '.') { - return name + '.'; - } - return name; - } - - @Override - public String name() { - return name; - } - - @Override - public DnsRecordType type() { - return type; - } - - @Override - public int dnsClass() { - return dnsClass & 0xFFFF; - } - - @Override - public long timeToLive() { - return timeToLive; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - - if (!(obj instanceof DnsRecord)) { - return false; - } - - final DnsRecord that = (DnsRecord) obj; - final int hashCode = this.hashCode; - if (hashCode != 0 && hashCode != that.hashCode()) { - return false; - } - - return type().intValue() == that.type().intValue() && - dnsClass() == that.dnsClass() && - name().equals(that.name()); - } - - @Override - public int hashCode() { - final int hashCode = this.hashCode; - if (hashCode != 0) { - return hashCode; - } - - return this.hashCode = name.hashCode() * 31 + type().intValue() * 31 + dnsClass(); - } - - @Override - public String toString() { - StringBuilder buf = new StringBuilder(64); - - buf.append(StringUtil.simpleClassName(this)) - .append('(') - .append(name()) - .append(' ') - .append(timeToLive()) - .append(' '); - - DnsMessageUtil.appendRecordClass(buf, dnsClass()) - .append(' ') - .append(type().name()) - .append(')'); - - return buf.toString(); - } -} diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/DatagramDnsQuery.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/DatagramDnsQuery.java deleted file mode 100644 index 17208884e8..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/DatagramDnsQuery.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.channel.AddressedEnvelope; -import io.netty.util.internal.UnstableApi; - -import java.net.InetSocketAddress; -import java.net.SocketAddress; - -/** - * A {@link DnsQuery} implementation for UDP/IP. - */ -@UnstableApi -public class DatagramDnsQuery extends DefaultDnsQuery - implements AddressedEnvelope { - - private final InetSocketAddress sender; - private final InetSocketAddress recipient; - - /** - * Creates a new instance with the {@link DnsOpCode#QUERY} {@code opCode}. - * - * @param sender the address of the sender - * @param recipient the address of the recipient - * @param id the {@code ID} of the DNS query - */ - public DatagramDnsQuery( - InetSocketAddress sender, InetSocketAddress recipient, int id) { - this(sender, recipient, id, DnsOpCode.QUERY); - } - - /** - * Creates a new instance. - * - * @param sender the address of the sender - * @param recipient the address of the recipient - * @param id the {@code ID} of the DNS query - * @param opCode the {@code opCode} of the DNS query - */ - public DatagramDnsQuery( - InetSocketAddress sender, InetSocketAddress recipient, int id, DnsOpCode opCode) { - super(id, opCode); - - if (recipient == null && sender == null) { - throw new NullPointerException("recipient and sender"); - } - - this.sender = sender; - this.recipient = recipient; - } - - @Override - public DatagramDnsQuery content() { - return this; - } - - @Override - public InetSocketAddress sender() { - return sender; - } - - @Override - public InetSocketAddress recipient() { - return recipient; - } - - @Override - public DatagramDnsQuery setId(int id) { - return (DatagramDnsQuery) super.setId(id); - } - - @Override - public DatagramDnsQuery setOpCode(DnsOpCode opCode) { - return (DatagramDnsQuery) super.setOpCode(opCode); - } - - @Override - public DatagramDnsQuery setRecursionDesired(boolean recursionDesired) { - return (DatagramDnsQuery) super.setRecursionDesired(recursionDesired); - } - - @Override - public DatagramDnsQuery setZ(int z) { - return (DatagramDnsQuery) super.setZ(z); - } - - @Override - public DatagramDnsQuery setRecord(DnsSection section, DnsRecord record) { - return (DatagramDnsQuery) super.setRecord(section, record); - } - - @Override - public DatagramDnsQuery addRecord(DnsSection section, DnsRecord record) { - return (DatagramDnsQuery) super.addRecord(section, record); - } - - @Override - public DatagramDnsQuery addRecord(DnsSection section, int index, DnsRecord record) { - return (DatagramDnsQuery) super.addRecord(section, index, record); - } - - @Override - public DatagramDnsQuery clear(DnsSection section) { - return (DatagramDnsQuery) super.clear(section); - } - - @Override - public DatagramDnsQuery clear() { - return (DatagramDnsQuery) super.clear(); - } - - @Override - public DatagramDnsQuery touch() { - return (DatagramDnsQuery) super.touch(); - } - - @Override - public DatagramDnsQuery touch(Object hint) { - return (DatagramDnsQuery) super.touch(hint); - } - - @Override - public DatagramDnsQuery retain() { - return (DatagramDnsQuery) super.retain(); - } - - @Override - public DatagramDnsQuery retain(int increment) { - return (DatagramDnsQuery) super.retain(increment); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - - if (!super.equals(obj)) { - return false; - } - - if (!(obj instanceof AddressedEnvelope)) { - return false; - } - - @SuppressWarnings("unchecked") - final AddressedEnvelope that = (AddressedEnvelope) obj; - if (sender() == null) { - if (that.sender() != null) { - return false; - } - } else if (!sender().equals(that.sender())) { - return false; - } - - if (recipient() == null) { - if (that.recipient() != null) { - return false; - } - } else if (!recipient().equals(that.recipient())) { - return false; - } - - return true; - } - - @Override - public int hashCode() { - int hashCode = super.hashCode(); - if (sender() != null) { - hashCode = hashCode * 31 + sender().hashCode(); - } - if (recipient() != null) { - hashCode = hashCode * 31 + recipient().hashCode(); - } - return hashCode; - } -} diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/DatagramDnsQueryDecoder.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/DatagramDnsQueryDecoder.java deleted file mode 100644 index 0c9a02ae5f..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/DatagramDnsQueryDecoder.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.socket.DatagramPacket; -import io.netty.handler.codec.MessageToMessageDecoder; -import io.netty.util.internal.UnstableApi; - - -import static java.util.Objects.requireNonNull; - -/** - * Decodes a {@link DatagramPacket} into a {@link DatagramDnsQuery}. - */ -@UnstableApi -@ChannelHandler.Sharable -public class DatagramDnsQueryDecoder extends MessageToMessageDecoder { - - private final DnsRecordDecoder recordDecoder; - - /** - * Creates a new decoder with {@linkplain DnsRecordDecoder#DEFAULT the default record decoder}. - */ - public DatagramDnsQueryDecoder() { - this(DnsRecordDecoder.DEFAULT); - } - - /** - * Creates a new decoder with the specified {@code recordDecoder}. - */ - public DatagramDnsQueryDecoder(DnsRecordDecoder recordDecoder) { - this.recordDecoder = requireNonNull(recordDecoder, "recordDecoder"); - } - - @Override - protected void decode(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception { - DnsQuery query = DnsMessageUtil.decodeDnsQuery(recordDecoder, packet.content(), - new DnsMessageUtil.DnsQueryFactory() { - @Override - public DnsQuery newQuery(int id, DnsOpCode dnsOpCode) { - return new DatagramDnsQuery(packet.sender(), packet.recipient(), id, dnsOpCode); - } - }); - ctx.fireChannelRead(query); - } -} diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/DatagramDnsQueryEncoder.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/DatagramDnsQueryEncoder.java deleted file mode 100644 index 528bf8de85..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/DatagramDnsQueryEncoder.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.AddressedEnvelope; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.socket.DatagramPacket; -import io.netty.handler.codec.MessageToMessageEncoder; -import io.netty.util.internal.UnstableApi; - -import java.net.InetSocketAddress; -import java.util.List; - -/** - * Encodes a {@link DatagramDnsQuery} (or an {@link AddressedEnvelope} of {@link DnsQuery}} into a - * {@link DatagramPacket}. - */ -@UnstableApi -@ChannelHandler.Sharable -public class DatagramDnsQueryEncoder extends MessageToMessageEncoder> { - - private final DnsQueryEncoder encoder; - - /** - * Creates a new encoder with {@linkplain DnsRecordEncoder#DEFAULT the default record encoder}. - */ - public DatagramDnsQueryEncoder() { - this(DnsRecordEncoder.DEFAULT); - } - - /** - * Creates a new encoder with the specified {@code recordEncoder}. - */ - public DatagramDnsQueryEncoder(DnsRecordEncoder recordEncoder) { - this.encoder = new DnsQueryEncoder(recordEncoder); - } - - @Override - protected void encode( - ChannelHandlerContext ctx, - AddressedEnvelope in, List out) throws Exception { - - final InetSocketAddress recipient = in.recipient(); - final DnsQuery query = in.content(); - final ByteBuf buf = allocateBuffer(ctx, in); - - boolean success = false; - try { - encoder.encode(query, buf); - success = true; - } finally { - if (!success) { - buf.release(); - } - } - - out.add(new DatagramPacket(buf, recipient, null)); - } - - /** - * Allocate a {@link ByteBuf} which will be used for constructing a datagram packet. - * Sub-classes may override this method to return a {@link ByteBuf} with a perfect matching initial capacity. - */ - protected ByteBuf allocateBuffer( - ChannelHandlerContext ctx, - @SuppressWarnings("unused") AddressedEnvelope msg) throws Exception { - return ctx.alloc().ioBuffer(1024); - } -} diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/DatagramDnsResponse.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/DatagramDnsResponse.java deleted file mode 100644 index c25b4dfb15..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/DatagramDnsResponse.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.channel.AddressedEnvelope; -import io.netty.util.internal.UnstableApi; - -import java.net.InetSocketAddress; -import java.net.SocketAddress; - -/** - * A {@link DnsResponse} implementation for UDP/IP. - */ -@UnstableApi -public class DatagramDnsResponse extends DefaultDnsResponse - implements AddressedEnvelope { - - private final InetSocketAddress sender; - private final InetSocketAddress recipient; - - /** - * Creates a new instance with the {@link DnsOpCode#QUERY} {@code opCode} and - * the {@link DnsResponseCode#NOERROR} {@code RCODE}. - * - * @param sender the address of the sender - * @param recipient the address of the recipient - * @param id the {@code ID} of the DNS response - */ - public DatagramDnsResponse(InetSocketAddress sender, InetSocketAddress recipient, int id) { - this(sender, recipient, id, DnsOpCode.QUERY, DnsResponseCode.NOERROR); - } - - /** - * Creates a new instance with the {@link DnsResponseCode#NOERROR} responseCode. - * - * @param sender the address of the sender - * @param recipient the address of the recipient - * @param id the {@code ID} of the DNS response - * @param opCode the {@code opCode} of the DNS response - */ - public DatagramDnsResponse(InetSocketAddress sender, InetSocketAddress recipient, int id, DnsOpCode opCode) { - this(sender, recipient, id, opCode, DnsResponseCode.NOERROR); - } - - /** - * Creates a new instance. - * - * @param sender the address of the sender - * @param recipient the address of the recipient - * @param id the {@code ID} of the DNS response - * @param opCode the {@code opCode} of the DNS response - * @param responseCode the {@code RCODE} of the DNS response - */ - public DatagramDnsResponse( - InetSocketAddress sender, InetSocketAddress recipient, - int id, DnsOpCode opCode, DnsResponseCode responseCode) { - super(id, opCode, responseCode); - - if (recipient == null && sender == null) { - throw new NullPointerException("recipient and sender"); - } - - this.sender = sender; - this.recipient = recipient; - } - - @Override - public DatagramDnsResponse content() { - return this; - } - - @Override - public InetSocketAddress sender() { - return sender; - } - - @Override - public InetSocketAddress recipient() { - return recipient; - } - - @Override - public DatagramDnsResponse setAuthoritativeAnswer(boolean authoritativeAnswer) { - return (DatagramDnsResponse) super.setAuthoritativeAnswer(authoritativeAnswer); - } - - @Override - public DatagramDnsResponse setTruncated(boolean truncated) { - return (DatagramDnsResponse) super.setTruncated(truncated); - } - - @Override - public DatagramDnsResponse setRecursionAvailable(boolean recursionAvailable) { - return (DatagramDnsResponse) super.setRecursionAvailable(recursionAvailable); - } - - @Override - public DatagramDnsResponse setCode(DnsResponseCode code) { - return (DatagramDnsResponse) super.setCode(code); - } - - @Override - public DatagramDnsResponse setId(int id) { - return (DatagramDnsResponse) super.setId(id); - } - - @Override - public DatagramDnsResponse setOpCode(DnsOpCode opCode) { - return (DatagramDnsResponse) super.setOpCode(opCode); - } - - @Override - public DatagramDnsResponse setRecursionDesired(boolean recursionDesired) { - return (DatagramDnsResponse) super.setRecursionDesired(recursionDesired); - } - - @Override - public DatagramDnsResponse setZ(int z) { - return (DatagramDnsResponse) super.setZ(z); - } - - @Override - public DatagramDnsResponse setRecord(DnsSection section, DnsRecord record) { - return (DatagramDnsResponse) super.setRecord(section, record); - } - - @Override - public DatagramDnsResponse addRecord(DnsSection section, DnsRecord record) { - return (DatagramDnsResponse) super.addRecord(section, record); - } - - @Override - public DatagramDnsResponse addRecord(DnsSection section, int index, DnsRecord record) { - return (DatagramDnsResponse) super.addRecord(section, index, record); - } - - @Override - public DatagramDnsResponse clear(DnsSection section) { - return (DatagramDnsResponse) super.clear(section); - } - - @Override - public DatagramDnsResponse clear() { - return (DatagramDnsResponse) super.clear(); - } - - @Override - public DatagramDnsResponse touch() { - return (DatagramDnsResponse) super.touch(); - } - - @Override - public DatagramDnsResponse touch(Object hint) { - return (DatagramDnsResponse) super.touch(hint); - } - - @Override - public DatagramDnsResponse retain() { - return (DatagramDnsResponse) super.retain(); - } - - @Override - public DatagramDnsResponse retain(int increment) { - return (DatagramDnsResponse) super.retain(increment); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - - if (!super.equals(obj)) { - return false; - } - - if (!(obj instanceof AddressedEnvelope)) { - return false; - } - - @SuppressWarnings("unchecked") - final AddressedEnvelope that = (AddressedEnvelope) obj; - if (sender() == null) { - if (that.sender() != null) { - return false; - } - } else if (!sender().equals(that.sender())) { - return false; - } - - if (recipient() == null) { - if (that.recipient() != null) { - return false; - } - } else if (!recipient().equals(that.recipient())) { - return false; - } - - return true; - } - - @Override - public int hashCode() { - int hashCode = super.hashCode(); - if (sender() != null) { - hashCode = hashCode * 31 + sender().hashCode(); - } - if (recipient() != null) { - hashCode = hashCode * 31 + recipient().hashCode(); - } - return hashCode; - } -} diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/DatagramDnsResponseDecoder.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/DatagramDnsResponseDecoder.java deleted file mode 100644 index e3163e53b3..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/DatagramDnsResponseDecoder.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.socket.DatagramPacket; -import io.netty.handler.codec.CorruptedFrameException; -import io.netty.handler.codec.MessageToMessageDecoder; -import io.netty.util.internal.UnstableApi; - -import java.net.InetSocketAddress; - -/** - * Decodes a {@link DatagramPacket} into a {@link DatagramDnsResponse}. - */ -@UnstableApi -@ChannelHandler.Sharable -public class DatagramDnsResponseDecoder extends MessageToMessageDecoder { - - private final DnsResponseDecoder responseDecoder; - - /** - * Creates a new decoder with {@linkplain DnsRecordDecoder#DEFAULT the default record decoder}. - */ - public DatagramDnsResponseDecoder() { - this(DnsRecordDecoder.DEFAULT); - } - - /** - * Creates a new decoder with the specified {@code recordDecoder}. - */ - public DatagramDnsResponseDecoder(DnsRecordDecoder recordDecoder) { - this.responseDecoder = new DnsResponseDecoder(recordDecoder) { - @Override - protected DnsResponse newResponse(InetSocketAddress sender, InetSocketAddress recipient, - int id, DnsOpCode opCode, DnsResponseCode responseCode) { - return new DatagramDnsResponse(sender, recipient, id, opCode, responseCode); - } - }; - } - - @Override - protected void decode(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception { - final DnsResponse response; - try { - response = decodeResponse(ctx, packet); - } catch (IndexOutOfBoundsException e) { - throw new CorruptedFrameException("Unable to decode response", e); - } - ctx.fireChannelRead(response); - } - - protected DnsResponse decodeResponse(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception { - return responseDecoder.decode(packet.sender(), packet.recipient(), packet.content()); - } -} diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/DatagramDnsResponseEncoder.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/DatagramDnsResponseEncoder.java deleted file mode 100644 index aa27f94a55..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/DatagramDnsResponseEncoder.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.AddressedEnvelope; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.socket.DatagramPacket; -import io.netty.handler.codec.MessageToMessageEncoder; -import io.netty.util.internal.UnstableApi; - -import java.net.InetSocketAddress; -import java.util.List; - -import static java.util.Objects.requireNonNull; - -/** - * Encodes a {@link DatagramDnsResponse} (or an {@link AddressedEnvelope} of {@link DnsResponse}} into a - * {@link DatagramPacket}. - */ -@UnstableApi -@ChannelHandler.Sharable -public class DatagramDnsResponseEncoder - extends MessageToMessageEncoder> { - - private final DnsRecordEncoder recordEncoder; - - /** - * Creates a new encoder with {@linkplain DnsRecordEncoder#DEFAULT the default record encoder}. - */ - public DatagramDnsResponseEncoder() { - this(DnsRecordEncoder.DEFAULT); - } - - /** - * Creates a new encoder with the specified {@code recordEncoder}. - */ - public DatagramDnsResponseEncoder(DnsRecordEncoder recordEncoder) { - this.recordEncoder = requireNonNull(recordEncoder, "recordEncoder"); - } - - @Override - protected void encode(ChannelHandlerContext ctx, - AddressedEnvelope in, List out) throws Exception { - - final InetSocketAddress recipient = in.recipient(); - final DnsResponse response = in.content(); - final ByteBuf buf = allocateBuffer(ctx, in); - - DnsMessageUtil.encodeDnsResponse(recordEncoder, response, buf); - - out.add(new DatagramPacket(buf, recipient, null)); - } - - /** - * Allocate a {@link ByteBuf} which will be used for constructing a datagram packet. - * Sub-classes may override this method to return a {@link ByteBuf} with a perfect matching initial capacity. - */ - protected ByteBuf allocateBuffer( - ChannelHandlerContext ctx, - @SuppressWarnings("unused") AddressedEnvelope msg) throws Exception { - return ctx.alloc().ioBuffer(1024); - } -} diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/DefaultDnsOptEcsRecord.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/DefaultDnsOptEcsRecord.java deleted file mode 100644 index e10e396618..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/DefaultDnsOptEcsRecord.java +++ /dev/null @@ -1,104 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.channel.socket.InternetProtocolFamily; -import io.netty.util.internal.UnstableApi; - -import java.net.InetAddress; -import java.util.Arrays; - -/** - * Default {@link DnsOptEcsRecord} implementation. - */ -@UnstableApi -public final class DefaultDnsOptEcsRecord extends AbstractDnsOptPseudoRrRecord implements DnsOptEcsRecord { - private final int srcPrefixLength; - private final byte[] address; - - /** - * Creates a new instance. - * - * @param maxPayloadSize the suggested max payload size in bytes - * @param extendedRcode the extended rcode - * @param version the version - * @param srcPrefixLength the prefix length - * @param address the bytes of the {@link InetAddress} to use - */ - public DefaultDnsOptEcsRecord(int maxPayloadSize, int extendedRcode, int version, - int srcPrefixLength, byte[] address) { - super(maxPayloadSize, extendedRcode, version); - this.srcPrefixLength = srcPrefixLength; - this.address = verifyAddress(address).clone(); - } - - /** - * Creates a new instance. - * - * @param maxPayloadSize the suggested max payload size in bytes - * @param srcPrefixLength the prefix length - * @param address the bytes of the {@link InetAddress} to use - */ - public DefaultDnsOptEcsRecord(int maxPayloadSize, int srcPrefixLength, byte[] address) { - this(maxPayloadSize, 0, 0, srcPrefixLength, address); - } - - /** - * Creates a new instance. - * - * @param maxPayloadSize the suggested max payload size in bytes - * @param protocolFamily the {@link InternetProtocolFamily} to use. This should be the same as the one used to - * send the query. - */ - public DefaultDnsOptEcsRecord(int maxPayloadSize, InternetProtocolFamily protocolFamily) { - this(maxPayloadSize, 0, 0, 0, protocolFamily.localhost().getAddress()); - } - - private static byte[] verifyAddress(byte[] bytes) { - if (bytes.length == 4 || bytes.length == 16) { - return bytes; - } - throw new IllegalArgumentException("bytes.length must either 4 or 16"); - } - - @Override - public int sourcePrefixLength() { - return srcPrefixLength; - } - - @Override - public int scopePrefixLength() { - return 0; - } - - @Override - public byte[] address() { - return address.clone(); - } - - @Override - public String toString() { - StringBuilder sb = toStringBuilder(); - sb.setLength(sb.length() - 1); - return sb.append(" address:") - .append(Arrays.toString(address)) - .append(" sourcePrefixLength:") - .append(sourcePrefixLength()) - .append(" scopePrefixLength:") - .append(scopePrefixLength()) - .append(')').toString(); - } -} diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/DefaultDnsPtrRecord.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/DefaultDnsPtrRecord.java deleted file mode 100644 index 99feea0e97..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/DefaultDnsPtrRecord.java +++ /dev/null @@ -1,73 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import static java.util.Objects.requireNonNull; - -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.UnstableApi; - -@UnstableApi -public class DefaultDnsPtrRecord extends AbstractDnsRecord implements DnsPtrRecord { - - private final String hostname; - - /** - * Creates a new PTR record. - * - * @param name the domain name - * @param dnsClass the class of the record, usually one of the following: - *
    - *
  • {@link #CLASS_IN}
  • - *
  • {@link #CLASS_CSNET}
  • - *
  • {@link #CLASS_CHAOS}
  • - *
  • {@link #CLASS_HESIOD}
  • - *
  • {@link #CLASS_NONE}
  • - *
  • {@link #CLASS_ANY}
  • - *
- * @param timeToLive the TTL value of the record - * @param hostname the hostname this PTR record resolves to. - */ - public DefaultDnsPtrRecord( - String name, int dnsClass, long timeToLive, String hostname) { - super(name, DnsRecordType.PTR, dnsClass, timeToLive); - this.hostname = requireNonNull(hostname, "hostname"); - } - - @Override - public String hostname() { - return hostname; - } - - @Override - public String toString() { - final StringBuilder buf = new StringBuilder(64).append(StringUtil.simpleClassName(this)).append('('); - final DnsRecordType type = type(); - buf.append(name().isEmpty()? "" : name()) - .append(' ') - .append(timeToLive()) - .append(' '); - - DnsMessageUtil.appendRecordClass(buf, dnsClass()) - .append(' ') - .append(type.name()); - - buf.append(' ') - .append(hostname); - - return buf.toString(); - } -} diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/DefaultDnsQuery.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/DefaultDnsQuery.java deleted file mode 100644 index 6fcebcd189..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/DefaultDnsQuery.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.util.internal.UnstableApi; - -/** - * The default {@link DnsQuery} implementation. - */ -@UnstableApi -public class DefaultDnsQuery extends AbstractDnsMessage implements DnsQuery { - - /** - * Creates a new instance with the {@link DnsOpCode#QUERY} {@code opCode}. - * - * @param id the {@code ID} of the DNS query - */ - public DefaultDnsQuery(int id) { - super(id); - } - - /** - * Creates a new instance. - * - * @param id the {@code ID} of the DNS query - * @param opCode the {@code opCode} of the DNS query - */ - public DefaultDnsQuery(int id, DnsOpCode opCode) { - super(id, opCode); - } - - @Override - public DnsQuery setId(int id) { - return (DnsQuery) super.setId(id); - } - - @Override - public DnsQuery setOpCode(DnsOpCode opCode) { - return (DnsQuery) super.setOpCode(opCode); - } - - @Override - public DnsQuery setRecursionDesired(boolean recursionDesired) { - return (DnsQuery) super.setRecursionDesired(recursionDesired); - } - - @Override - public DnsQuery setZ(int z) { - return (DnsQuery) super.setZ(z); - } - - @Override - public DnsQuery setRecord(DnsSection section, DnsRecord record) { - return (DnsQuery) super.setRecord(section, record); - } - - @Override - public DnsQuery addRecord(DnsSection section, DnsRecord record) { - return (DnsQuery) super.addRecord(section, record); - } - - @Override - public DnsQuery addRecord(DnsSection section, int index, DnsRecord record) { - return (DnsQuery) super.addRecord(section, index, record); - } - - @Override - public DnsQuery clear(DnsSection section) { - return (DnsQuery) super.clear(section); - } - - @Override - public DnsQuery clear() { - return (DnsQuery) super.clear(); - } - - @Override - public DnsQuery touch() { - return (DnsQuery) super.touch(); - } - - @Override - public DnsQuery touch(Object hint) { - return (DnsQuery) super.touch(hint); - } - - @Override - public DnsQuery retain() { - return (DnsQuery) super.retain(); - } - - @Override - public DnsQuery retain(int increment) { - return (DnsQuery) super.retain(increment); - } - - @Override - public String toString() { - return DnsMessageUtil.appendQuery(new StringBuilder(128), this).toString(); - } -} diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/DefaultDnsQuestion.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/DefaultDnsQuestion.java deleted file mode 100644 index f32320e055..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/DefaultDnsQuestion.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.UnstableApi; - -/** - * The default {@link DnsQuestion} implementation. - */ -@UnstableApi -public class DefaultDnsQuestion extends AbstractDnsRecord implements DnsQuestion { - - /** - * Creates a new {@link #CLASS_IN IN-class} question. - * - * @param name the domain name of the DNS question - * @param type the type of the DNS question - */ - public DefaultDnsQuestion(String name, DnsRecordType type) { - super(name, type, 0); - } - - /** - * Creates a new question. - * - * @param name the domain name of the DNS question - * @param type the type of the DNS question - * @param dnsClass the class of the record, usually one of the following: - *
    - *
  • {@link #CLASS_IN}
  • - *
  • {@link #CLASS_CSNET}
  • - *
  • {@link #CLASS_CHAOS}
  • - *
  • {@link #CLASS_HESIOD}
  • - *
  • {@link #CLASS_NONE}
  • - *
  • {@link #CLASS_ANY}
  • - *
- */ - public DefaultDnsQuestion(String name, DnsRecordType type, int dnsClass) { - super(name, type, dnsClass, 0); - } - - @Override - public String toString() { - StringBuilder buf = new StringBuilder(64); - - buf.append(StringUtil.simpleClassName(this)) - .append('(') - .append(name()) - .append(' '); - - DnsMessageUtil.appendRecordClass(buf, dnsClass()) - .append(' ') - .append(type().name()) - .append(')'); - - return buf.toString(); - } -} diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/DefaultDnsRawRecord.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/DefaultDnsRawRecord.java deleted file mode 100644 index a56b319f1d..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/DefaultDnsRawRecord.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.buffer.ByteBuf; -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.UnstableApi; - -import static java.util.Objects.requireNonNull; - -/** - * The default {@code DnsRawRecord} implementation. - */ -@UnstableApi -public class DefaultDnsRawRecord extends AbstractDnsRecord implements DnsRawRecord { - - private final ByteBuf content; - - /** - * Creates a new {@link #CLASS_IN IN-class} record. - * - * @param name the domain name - * @param type the type of the record - * @param timeToLive the TTL value of the record - */ - public DefaultDnsRawRecord(String name, DnsRecordType type, long timeToLive, ByteBuf content) { - this(name, type, DnsRecord.CLASS_IN, timeToLive, content); - } - - /** - * Creates a new record. - * - * @param name the domain name - * @param type the type of the record - * @param dnsClass the class of the record, usually one of the following: - *
    - *
  • {@link #CLASS_IN}
  • - *
  • {@link #CLASS_CSNET}
  • - *
  • {@link #CLASS_CHAOS}
  • - *
  • {@link #CLASS_HESIOD}
  • - *
  • {@link #CLASS_NONE}
  • - *
  • {@link #CLASS_ANY}
  • - *
- * @param timeToLive the TTL value of the record - */ - public DefaultDnsRawRecord( - String name, DnsRecordType type, int dnsClass, long timeToLive, ByteBuf content) { - super(name, type, dnsClass, timeToLive); - this.content = requireNonNull(content, "content"); - } - - @Override - public ByteBuf content() { - return content; - } - - @Override - public DnsRawRecord copy() { - return replace(content().copy()); - } - - @Override - public DnsRawRecord duplicate() { - return replace(content().duplicate()); - } - - @Override - public DnsRawRecord retainedDuplicate() { - return replace(content().retainedDuplicate()); - } - - @Override - public DnsRawRecord replace(ByteBuf content) { - return new DefaultDnsRawRecord(name(), type(), dnsClass(), timeToLive(), content); - } - - @Override - public int refCnt() { - return content().refCnt(); - } - - @Override - public DnsRawRecord retain() { - content().retain(); - return this; - } - - @Override - public DnsRawRecord retain(int increment) { - content().retain(increment); - return this; - } - - @Override - public boolean release() { - return content().release(); - } - - @Override - public boolean release(int decrement) { - return content().release(decrement); - } - - @Override - public DnsRawRecord touch() { - content().touch(); - return this; - } - - @Override - public DnsRawRecord touch(Object hint) { - content().touch(hint); - return this; - } - - @Override - public String toString() { - final StringBuilder buf = new StringBuilder(64).append(StringUtil.simpleClassName(this)).append('('); - final DnsRecordType type = type(); - if (type != DnsRecordType.OPT) { - buf.append(name().isEmpty()? "" : name()) - .append(' ') - .append(timeToLive()) - .append(' '); - - DnsMessageUtil.appendRecordClass(buf, dnsClass()) - .append(' ') - .append(type.name()); - } else { - buf.append("OPT flags:") - .append(timeToLive()) - .append(" udp:") - .append(dnsClass()); - } - - buf.append(' ') - .append(content().readableBytes()) - .append("B)"); - - return buf.toString(); - } -} diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/DefaultDnsRecordDecoder.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/DefaultDnsRecordDecoder.java deleted file mode 100644 index 33a1e02fee..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/DefaultDnsRecordDecoder.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.buffer.ByteBuf; -import io.netty.util.internal.UnstableApi; - -/** - * The default {@link DnsRecordDecoder} implementation. - * - * @see DefaultDnsRecordEncoder - */ -@UnstableApi -public class DefaultDnsRecordDecoder implements DnsRecordDecoder { - - static final String ROOT = "."; - - /** - * Creates a new instance. - */ - protected DefaultDnsRecordDecoder() { } - - @Override - public final DnsQuestion decodeQuestion(ByteBuf in) throws Exception { - String name = decodeName(in); - DnsRecordType type = DnsRecordType.valueOf(in.readUnsignedShort()); - int qClass = in.readUnsignedShort(); - return new DefaultDnsQuestion(name, type, qClass); - } - - @Override - public final T decodeRecord(ByteBuf in) throws Exception { - final int startOffset = in.readerIndex(); - final String name = decodeName(in); - - final int endOffset = in.writerIndex(); - if (endOffset - in.readerIndex() < 10) { - // Not enough data - in.readerIndex(startOffset); - return null; - } - - final DnsRecordType type = DnsRecordType.valueOf(in.readUnsignedShort()); - final int aClass = in.readUnsignedShort(); - final long ttl = in.readUnsignedInt(); - final int length = in.readUnsignedShort(); - final int offset = in.readerIndex(); - - if (endOffset - offset < length) { - // Not enough data - in.readerIndex(startOffset); - return null; - } - - @SuppressWarnings("unchecked") - T record = (T) decodeRecord(name, type, aClass, ttl, in, offset, length); - in.readerIndex(offset + length); - return record; - } - - /** - * Decodes a record from the information decoded so far by {@link #decodeRecord(ByteBuf)}. - * - * @param name the domain name of the record - * @param type the type of the record - * @param dnsClass the class of the record - * @param timeToLive the TTL of the record - * @param in the {@link ByteBuf} that contains the RDATA - * @param offset the start offset of the RDATA in {@code in} - * @param length the length of the RDATA - * - * @return a {@link DnsRawRecord}. Override this method to decode RDATA and return other record implementation. - */ - protected DnsRecord decodeRecord( - String name, DnsRecordType type, int dnsClass, long timeToLive, - ByteBuf in, int offset, int length) throws Exception { - - // DNS message compression means that domain names may contain "pointers" to other positions in the packet - // to build a full message. This means the indexes are meaningful and we need the ability to reference the - // indexes un-obstructed, and thus we cannot use a slice here. - // See https://www.ietf.org/rfc/rfc1035 [4.1.4. Message compression] - if (type == DnsRecordType.PTR) { - return new DefaultDnsPtrRecord( - name, dnsClass, timeToLive, decodeName0(in.duplicate().setIndex(offset, offset + length))); - } - if (type == DnsRecordType.CNAME || type == DnsRecordType.NS) { - return new DefaultDnsRawRecord(name, type, dnsClass, timeToLive, - DnsCodecUtil.decompressDomainName( - in.duplicate().setIndex(offset, offset + length))); - } - return new DefaultDnsRawRecord( - name, type, dnsClass, timeToLive, in.retainedDuplicate().setIndex(offset, offset + length)); - } - - /** - * Retrieves a domain name given a buffer containing a DNS packet. If the - * name contains a pointer, the position of the buffer will be set to - * directly after the pointer's index after the name has been read. - * - * @param in the byte buffer containing the DNS packet - * @return the domain name for an entry - */ - protected String decodeName0(ByteBuf in) { - return decodeName(in); - } - - /** - * Retrieves a domain name given a buffer containing a DNS packet. If the - * name contains a pointer, the position of the buffer will be set to - * directly after the pointer's index after the name has been read. - * - * @param in the byte buffer containing the DNS packet - * @return the domain name for an entry - */ - public static String decodeName(ByteBuf in) { - return DnsCodecUtil.decodeDomainName(in); - } -} diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/DefaultDnsRecordEncoder.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/DefaultDnsRecordEncoder.java deleted file mode 100644 index a5134ec2c7..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/DefaultDnsRecordEncoder.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.socket.InternetProtocolFamily; -import io.netty.handler.codec.UnsupportedMessageTypeException; -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.UnstableApi; - -/** - * The default {@link DnsRecordEncoder} implementation. - * - * @see DefaultDnsRecordDecoder - */ -@UnstableApi -public class DefaultDnsRecordEncoder implements DnsRecordEncoder { - private static final int PREFIX_MASK = Byte.SIZE - 1; - - /** - * Creates a new instance. - */ - protected DefaultDnsRecordEncoder() { } - - @Override - public final void encodeQuestion(DnsQuestion question, ByteBuf out) throws Exception { - encodeName(question.name(), out); - out.writeShort(question.type().intValue()); - out.writeShort(question.dnsClass()); - } - - @Override - public void encodeRecord(DnsRecord record, ByteBuf out) throws Exception { - if (record instanceof DnsQuestion) { - encodeQuestion((DnsQuestion) record, out); - } else if (record instanceof DnsPtrRecord) { - encodePtrRecord((DnsPtrRecord) record, out); - } else if (record instanceof DnsOptEcsRecord) { - encodeOptEcsRecord((DnsOptEcsRecord) record, out); - } else if (record instanceof DnsOptPseudoRecord) { - encodeOptPseudoRecord((DnsOptPseudoRecord) record, out); - } else if (record instanceof DnsRawRecord) { - encodeRawRecord((DnsRawRecord) record, out); - } else { - throw new UnsupportedMessageTypeException(StringUtil.simpleClassName(record)); - } - } - - private void encodeRecord0(DnsRecord record, ByteBuf out) throws Exception { - encodeName(record.name(), out); - out.writeShort(record.type().intValue()); - out.writeShort(record.dnsClass()); - out.writeInt((int) record.timeToLive()); - } - - private void encodePtrRecord(DnsPtrRecord record, ByteBuf out) throws Exception { - encodeRecord0(record, out); - encodeName(record.hostname(), out); - } - - private void encodeOptPseudoRecord(DnsOptPseudoRecord record, ByteBuf out) throws Exception { - encodeRecord0(record, out); - out.writeShort(0); - } - - private void encodeOptEcsRecord(DnsOptEcsRecord record, ByteBuf out) throws Exception { - encodeRecord0(record, out); - - int sourcePrefixLength = record.sourcePrefixLength(); - int scopePrefixLength = record.scopePrefixLength(); - int lowOrderBitsToPreserve = sourcePrefixLength & PREFIX_MASK; - - byte[] bytes = record.address(); - int addressBits = bytes.length << 3; - if (addressBits < sourcePrefixLength || sourcePrefixLength < 0) { - throw new IllegalArgumentException(sourcePrefixLength + ": " + - sourcePrefixLength + " (expected: 0 >= " + addressBits + ')'); - } - - // See https://www.iana.org/assignments/address-family-numbers/address-family-numbers.xhtml - final short addressNumber = (short) (bytes.length == 4 ? - InternetProtocolFamily.IPv4.addressNumber() : InternetProtocolFamily.IPv6.addressNumber()); - int payloadLength = calculateEcsAddressLength(sourcePrefixLength, lowOrderBitsToPreserve); - - int fullPayloadLength = 2 + // OPTION-CODE - 2 + // OPTION-LENGTH - 2 + // FAMILY - 1 + // SOURCE PREFIX-LENGTH - 1 + // SCOPE PREFIX-LENGTH - payloadLength; // ADDRESS... - - out.writeShort(fullPayloadLength); - out.writeShort(8); // This is the defined type for ECS. - - out.writeShort(fullPayloadLength - 4); // Not include OPTION-CODE and OPTION-LENGTH - out.writeShort(addressNumber); - out.writeByte(sourcePrefixLength); - out.writeByte(scopePrefixLength); // Must be 0 in queries. - - if (lowOrderBitsToPreserve > 0) { - int bytesLength = payloadLength - 1; - out.writeBytes(bytes, 0, bytesLength); - - // Pad the leftover of the last byte with zeros. - out.writeByte(padWithZeros(bytes[bytesLength], lowOrderBitsToPreserve)); - } else { - // The sourcePrefixLength align with Byte so just copy in the bytes directly. - out.writeBytes(bytes, 0, payloadLength); - } - } - - // Package-Private for testing - static int calculateEcsAddressLength(int sourcePrefixLength, int lowOrderBitsToPreserve) { - return (sourcePrefixLength >>> 3) + (lowOrderBitsToPreserve != 0 ? 1 : 0); - } - - private void encodeRawRecord(DnsRawRecord record, ByteBuf out) throws Exception { - encodeRecord0(record, out); - - ByteBuf content = record.content(); - int contentLen = content.readableBytes(); - - out.writeShort(contentLen); - out.writeBytes(content, content.readerIndex(), contentLen); - } - - protected void encodeName(String name, ByteBuf buf) throws Exception { - DnsCodecUtil.encodeDomainName(name, buf); - } - - private static byte padWithZeros(byte b, int lowOrderBitsToPreserve) { - switch (lowOrderBitsToPreserve) { - case 0: - return 0; - case 1: - return (byte) (0x80 & b); - case 2: - return (byte) (0xC0 & b); - case 3: - return (byte) (0xE0 & b); - case 4: - return (byte) (0xF0 & b); - case 5: - return (byte) (0xF8 & b); - case 6: - return (byte) (0xFC & b); - case 7: - return (byte) (0xFE & b); - case 8: - return b; - default: - throw new IllegalArgumentException("lowOrderBitsToPreserve: " + lowOrderBitsToPreserve); - } - } -} diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/DefaultDnsResponse.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/DefaultDnsResponse.java deleted file mode 100644 index 98512b2e49..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/DefaultDnsResponse.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.util.internal.UnstableApi; - -import static java.util.Objects.requireNonNull; - -/** - * The default {@link DnsResponse} implementation. - */ -@UnstableApi -public class DefaultDnsResponse extends AbstractDnsMessage implements DnsResponse { - - private boolean authoritativeAnswer; - private boolean truncated; - private boolean recursionAvailable; - private DnsResponseCode code; - - /** - * Creates a new instance with the {@link DnsOpCode#QUERY} {@code opCode} and - * the {@link DnsResponseCode#NOERROR} {@code RCODE}. - * - * @param id the {@code ID} of the DNS response - */ - public DefaultDnsResponse(int id) { - this(id, DnsOpCode.QUERY, DnsResponseCode.NOERROR); - } - - /** - * Creates a new instance with the {@link DnsResponseCode#NOERROR} {@code RCODE}. - * - * @param id the {@code ID} of the DNS response - * @param opCode the {@code opCode} of the DNS response - */ - public DefaultDnsResponse(int id, DnsOpCode opCode) { - this(id, opCode, DnsResponseCode.NOERROR); - } - - /** - * Creates a new instance. - * - * @param id the {@code ID} of the DNS response - * @param opCode the {@code opCode} of the DNS response - * @param code the {@code RCODE} of the DNS response - */ - public DefaultDnsResponse(int id, DnsOpCode opCode, DnsResponseCode code) { - super(id, opCode); - setCode(code); - } - - @Override - public boolean isAuthoritativeAnswer() { - return authoritativeAnswer; - } - - @Override - public DnsResponse setAuthoritativeAnswer(boolean authoritativeAnswer) { - this.authoritativeAnswer = authoritativeAnswer; - return this; - } - - @Override - public boolean isTruncated() { - return truncated; - } - - @Override - public DnsResponse setTruncated(boolean truncated) { - this.truncated = truncated; - return this; - } - - @Override - public boolean isRecursionAvailable() { - return recursionAvailable; - } - - @Override - public DnsResponse setRecursionAvailable(boolean recursionAvailable) { - this.recursionAvailable = recursionAvailable; - return this; - } - - @Override - public DnsResponseCode code() { - return code; - } - - @Override - public DnsResponse setCode(DnsResponseCode code) { - this.code = requireNonNull(code, "code"); - return this; - } - - @Override - public DnsResponse setId(int id) { - return (DnsResponse) super.setId(id); - } - - @Override - public DnsResponse setOpCode(DnsOpCode opCode) { - return (DnsResponse) super.setOpCode(opCode); - } - - @Override - public DnsResponse setRecursionDesired(boolean recursionDesired) { - return (DnsResponse) super.setRecursionDesired(recursionDesired); - } - - @Override - public DnsResponse setZ(int z) { - return (DnsResponse) super.setZ(z); - } - - @Override - public DnsResponse setRecord(DnsSection section, DnsRecord record) { - return (DnsResponse) super.setRecord(section, record); - } - - @Override - public DnsResponse addRecord(DnsSection section, DnsRecord record) { - return (DnsResponse) super.addRecord(section, record); - } - - @Override - public DnsResponse addRecord(DnsSection section, int index, DnsRecord record) { - return (DnsResponse) super.addRecord(section, index, record); - } - - @Override - public DnsResponse clear(DnsSection section) { - return (DnsResponse) super.clear(section); - } - - @Override - public DnsResponse clear() { - return (DnsResponse) super.clear(); - } - - @Override - public DnsResponse touch() { - return (DnsResponse) super.touch(); - } - - @Override - public DnsResponse touch(Object hint) { - return (DnsResponse) super.touch(hint); - } - - @Override - public DnsResponse retain() { - return (DnsResponse) super.retain(); - } - - @Override - public DnsResponse retain(int increment) { - return (DnsResponse) super.retain(increment); - } - - @Override - public String toString() { - return DnsMessageUtil.appendResponse(new StringBuilder(128), this).toString(); - } -} diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsCodecUtil.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsCodecUtil.java deleted file mode 100644 index a702771df8..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsCodecUtil.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.dns; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.handler.codec.CorruptedFrameException; -import io.netty.util.CharsetUtil; - -import static io.netty.handler.codec.dns.DefaultDnsRecordDecoder.*; - -final class DnsCodecUtil { - private DnsCodecUtil() { - // Util class - } - - static void encodeDomainName(String name, ByteBuf buf) { - if (ROOT.equals(name)) { - // Root domain - buf.writeByte(0); - return; - } - - final String[] labels = name.split("\\."); - for (String label : labels) { - final int labelLen = label.length(); - if (labelLen == 0) { - // zero-length label means the end of the name. - break; - } - - buf.writeByte(labelLen); - ByteBufUtil.writeAscii(buf, label); - } - - buf.writeByte(0); // marks end of name field - } - - static String decodeDomainName(ByteBuf in) { - int position = -1; - int checked = 0; - final int end = in.writerIndex(); - final int readable = in.readableBytes(); - - // Looking at the spec we should always have at least enough readable bytes to read a byte here but it seems - // some servers do not respect this for empty names. So just workaround this and return an empty name in this - // case. - // - // See: - // - https://github.com/netty/netty/issues/5014 - // - https://www.ietf.org/rfc/rfc1035.txt , Section 3.1 - if (readable == 0) { - return ROOT; - } - - final StringBuilder name = new StringBuilder(readable << 1); - while (in.isReadable()) { - final int len = in.readUnsignedByte(); - final boolean pointer = (len & 0xc0) == 0xc0; - if (pointer) { - if (position == -1) { - position = in.readerIndex() + 1; - } - - if (!in.isReadable()) { - throw new CorruptedFrameException("truncated pointer in a name"); - } - - final int next = (len & 0x3f) << 8 | in.readUnsignedByte(); - if (next >= end) { - throw new CorruptedFrameException("name has an out-of-range pointer"); - } - in.readerIndex(next); - - // check for loops - checked += 2; - if (checked >= end) { - throw new CorruptedFrameException("name contains a loop."); - } - } else if (len != 0) { - if (!in.isReadable(len)) { - throw new CorruptedFrameException("truncated label in a name"); - } - name.append(in.toString(in.readerIndex(), len, CharsetUtil.UTF_8)).append('.'); - in.skipBytes(len); - } else { // len == 0 - break; - } - } - - if (position != -1) { - in.readerIndex(position); - } - - if (name.length() == 0) { - return ROOT; - } - - if (name.charAt(name.length() - 1) != '.') { - name.append('.'); - } - - return name.toString(); - } - - /** - * Decompress pointer data. - * @param compression compressed data - * @return decompressed data - */ - static ByteBuf decompressDomainName(ByteBuf compression) { - String domainName = decodeDomainName(compression); - ByteBuf result = compression.alloc().buffer(domainName.length() << 1); - encodeDomainName(domainName, result); - return result; - } -} diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsMessage.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsMessage.java deleted file mode 100644 index 46be48ae80..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsMessage.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.util.ReferenceCounted; -import io.netty.util.internal.UnstableApi; - -/** - * The superclass which contains core information concerning a {@link DnsQuery} and a {@link DnsResponse}. - */ -@UnstableApi -public interface DnsMessage extends ReferenceCounted { - - /** - * Returns the {@code ID} of this DNS message. - */ - int id(); - - /** - * Sets the {@code ID} of this DNS message. - */ - DnsMessage setId(int id); - - /** - * Returns the {@code opCode} of this DNS message. - */ - DnsOpCode opCode(); - - /** - * Sets the {@code opCode} of this DNS message. - */ - DnsMessage setOpCode(DnsOpCode opCode); - - /** - * Returns the {@code RD} (recursion desired} field of this DNS message. - */ - boolean isRecursionDesired(); - - /** - * Sets the {@code RD} (recursion desired} field of this DNS message. - */ - DnsMessage setRecursionDesired(boolean recursionDesired); - - /** - * Returns the {@code Z} (reserved for future use) field of this DNS message. - */ - int z(); - - /** - * Sets the {@code Z} (reserved for future use) field of this DNS message. - */ - DnsMessage setZ(int z); - - /** - * Returns the number of records in the specified {@code section} of this DNS message. - */ - int count(DnsSection section); - - /** - * Returns the number of records in this DNS message. - */ - int count(); - - /** - * Returns the first record in the specified {@code section} of this DNS message. - * When the specified {@code section} is {@link DnsSection#QUESTION}, the type of the returned record is - * always {@link DnsQuestion}. - * - * @return {@code null} if this message doesn't have any records in the specified {@code section} - */ - T recordAt(DnsSection section); - - /** - * Returns the record at the specified {@code index} of the specified {@code section} of this DNS message. - * When the specified {@code section} is {@link DnsSection#QUESTION}, the type of the returned record is - * always {@link DnsQuestion}. - * - * @throws IndexOutOfBoundsException if the specified {@code index} is out of bounds - */ - T recordAt(DnsSection section, int index); - - /** - * Sets the specified {@code section} of this DNS message to the specified {@code record}, - * making it a single-record section. When the specified {@code section} is {@link DnsSection#QUESTION}, - * the specified {@code record} must be a {@link DnsQuestion}. - */ - DnsMessage setRecord(DnsSection section, DnsRecord record); - - /** - * Sets the specified {@code record} at the specified {@code index} of the specified {@code section} - * of this DNS message. When the specified {@code section} is {@link DnsSection#QUESTION}, - * the specified {@code record} must be a {@link DnsQuestion}. - * - * @return the old record - * @throws IndexOutOfBoundsException if the specified {@code index} is out of bounds - */ - T setRecord(DnsSection section, int index, DnsRecord record); - - /** - * Adds the specified {@code record} at the end of the specified {@code section} of this DNS message. - * When the specified {@code section} is {@link DnsSection#QUESTION}, the specified {@code record} - * must be a {@link DnsQuestion}. - */ - DnsMessage addRecord(DnsSection section, DnsRecord record); - - /** - * Adds the specified {@code record} at the specified {@code index} of the specified {@code section} - * of this DNS message. When the specified {@code section} is {@link DnsSection#QUESTION}, the specified - * {@code record} must be a {@link DnsQuestion}. - * - * @throws IndexOutOfBoundsException if the specified {@code index} is out of bounds - */ - DnsMessage addRecord(DnsSection section, int index, DnsRecord record); - - /** - * Removes the record at the specified {@code index} of the specified {@code section} from this DNS message. - * When the specified {@code section} is {@link DnsSection#QUESTION}, the type of the returned record is - * always {@link DnsQuestion}. - * - * @return the removed record - */ - T removeRecord(DnsSection section, int index); - - /** - * Removes all the records in the specified {@code section} of this DNS message. - */ - DnsMessage clear(DnsSection section); - - /** - * Removes all the records in this DNS message. - */ - DnsMessage clear(); - - @Override - DnsMessage touch(); - - @Override - DnsMessage touch(Object hint); - - @Override - DnsMessage retain(); - - @Override - DnsMessage retain(int increment); -} diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsMessageUtil.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsMessageUtil.java deleted file mode 100644 index 1e9475c92c..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsMessageUtil.java +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.AddressedEnvelope; -import io.netty.handler.codec.CorruptedFrameException; -import io.netty.util.internal.StringUtil; - -import java.net.SocketAddress; - -/** - * Provides some utility methods for DNS message implementations. - */ -final class DnsMessageUtil { - - static StringBuilder appendQuery(StringBuilder buf, DnsQuery query) { - appendQueryHeader(buf, query); - appendAllRecords(buf, query); - return buf; - } - - static StringBuilder appendResponse(StringBuilder buf, DnsResponse response) { - appendResponseHeader(buf, response); - appendAllRecords(buf, response); - return buf; - } - - static StringBuilder appendRecordClass(StringBuilder buf, int dnsClass) { - final String name; - switch (dnsClass &= 0xFFFF) { - case DnsRecord.CLASS_IN: - name = "IN"; - break; - case DnsRecord.CLASS_CSNET: - name = "CSNET"; - break; - case DnsRecord.CLASS_CHAOS: - name = "CHAOS"; - break; - case DnsRecord.CLASS_HESIOD: - name = "HESIOD"; - break; - case DnsRecord.CLASS_NONE: - name = "NONE"; - break; - case DnsRecord.CLASS_ANY: - name = "ANY"; - break; - default: - name = null; - break; - } - - if (name != null) { - buf.append(name); - } else { - buf.append("UNKNOWN(").append(dnsClass).append(')'); - } - - return buf; - } - - private static void appendQueryHeader(StringBuilder buf, DnsQuery msg) { - buf.append(StringUtil.simpleClassName(msg)) - .append('('); - - appendAddresses(buf, msg) - .append(msg.id()) - .append(", ") - .append(msg.opCode()); - - if (msg.isRecursionDesired()) { - buf.append(", RD"); - } - if (msg.z() != 0) { - buf.append(", Z: ") - .append(msg.z()); - } - buf.append(')'); - } - - private static void appendResponseHeader(StringBuilder buf, DnsResponse msg) { - buf.append(StringUtil.simpleClassName(msg)) - .append('('); - - appendAddresses(buf, msg) - .append(msg.id()) - .append(", ") - .append(msg.opCode()) - .append(", ") - .append(msg.code()) - .append(','); - - boolean hasComma = true; - if (msg.isRecursionDesired()) { - hasComma = false; - buf.append(" RD"); - } - if (msg.isAuthoritativeAnswer()) { - hasComma = false; - buf.append(" AA"); - } - if (msg.isTruncated()) { - hasComma = false; - buf.append(" TC"); - } - if (msg.isRecursionAvailable()) { - hasComma = false; - buf.append(" RA"); - } - if (msg.z() != 0) { - if (!hasComma) { - buf.append(','); - } - buf.append(" Z: ") - .append(msg.z()); - } - - if (hasComma) { - buf.setCharAt(buf.length() - 1, ')'); - } else { - buf.append(')'); - } - } - - private static StringBuilder appendAddresses(StringBuilder buf, DnsMessage msg) { - - if (!(msg instanceof AddressedEnvelope)) { - return buf; - } - - @SuppressWarnings("unchecked") - AddressedEnvelope envelope = (AddressedEnvelope) msg; - - SocketAddress addr = envelope.sender(); - if (addr != null) { - buf.append("from: ") - .append(addr) - .append(", "); - } - - addr = envelope.recipient(); - if (addr != null) { - buf.append("to: ") - .append(addr) - .append(", "); - } - - return buf; - } - - private static void appendAllRecords(StringBuilder buf, DnsMessage msg) { - appendRecords(buf, msg, DnsSection.QUESTION); - appendRecords(buf, msg, DnsSection.ANSWER); - appendRecords(buf, msg, DnsSection.AUTHORITY); - appendRecords(buf, msg, DnsSection.ADDITIONAL); - } - - private static void appendRecords(StringBuilder buf, DnsMessage message, DnsSection section) { - final int count = message.count(section); - for (int i = 0; i < count; i ++) { - buf.append(StringUtil.NEWLINE) - .append(StringUtil.TAB) - .append(message.recordAt(section, i)); - } - } - - static DnsQuery decodeDnsQuery(DnsRecordDecoder decoder, ByteBuf buf, DnsQueryFactory supplier) throws Exception { - DnsQuery query = newQuery(buf, supplier); - boolean success = false; - try { - int questionCount = buf.readUnsignedShort(); - int answerCount = buf.readUnsignedShort(); - int authorityRecordCount = buf.readUnsignedShort(); - int additionalRecordCount = buf.readUnsignedShort(); - decodeQuestions(decoder, query, buf, questionCount); - decodeRecords(decoder, query, DnsSection.ANSWER, buf, answerCount); - decodeRecords(decoder, query, DnsSection.AUTHORITY, buf, authorityRecordCount); - decodeRecords(decoder, query, DnsSection.ADDITIONAL, buf, additionalRecordCount); - success = true; - return query; - } finally { - if (!success) { - query.release(); - } - } - } - - private static DnsQuery newQuery(ByteBuf buf, DnsQueryFactory supplier) { - int id = buf.readUnsignedShort(); - int flags = buf.readUnsignedShort(); - if (flags >> 15 == 1) { - throw new CorruptedFrameException("not a query"); - } - - DnsQuery query = supplier.newQuery(id, DnsOpCode.valueOf((byte) (flags >> 11 & 0xf))); - query.setRecursionDesired((flags >> 8 & 1) == 1); - query.setZ(flags >> 4 & 0x7); - return query; - } - - private static void decodeQuestions(DnsRecordDecoder decoder, - DnsQuery query, ByteBuf buf, int questionCount) throws Exception { - for (int i = questionCount; i > 0; --i) { - query.addRecord(DnsSection.QUESTION, decoder.decodeQuestion(buf)); - } - } - - private static void decodeRecords(DnsRecordDecoder decoder, - DnsQuery query, DnsSection section, ByteBuf buf, int count) throws Exception { - for (int i = count; i > 0; --i) { - DnsRecord r = decoder.decodeRecord(buf); - if (r == null) { - break; - } - query.addRecord(section, r); - } - } - - static void encodeDnsResponse(DnsRecordEncoder encoder, DnsResponse response, ByteBuf buf) throws Exception { - boolean success = false; - try { - encodeHeader(response, buf); - encodeQuestions(encoder, response, buf); - encodeRecords(encoder, response, DnsSection.ANSWER, buf); - encodeRecords(encoder, response, DnsSection.AUTHORITY, buf); - encodeRecords(encoder, response, DnsSection.ADDITIONAL, buf); - success = true; - } finally { - if (!success) { - buf.release(); - } - } - } - - /** - * Encodes the header that is always 12 bytes long. - * - * @param response the response header being encoded - * @param buf the buffer the encoded data should be written to - */ - private static void encodeHeader(DnsResponse response, ByteBuf buf) { - buf.writeShort(response.id()); - int flags = 32768; - flags |= (response.opCode().byteValue() & 0xFF) << 11; - if (response.isAuthoritativeAnswer()) { - flags |= 1 << 10; - } - if (response.isTruncated()) { - flags |= 1 << 9; - } - if (response.isRecursionDesired()) { - flags |= 1 << 8; - } - if (response.isRecursionAvailable()) { - flags |= 1 << 7; - } - flags |= response.z() << 4; - flags |= response.code().intValue(); - buf.writeShort(flags); - buf.writeShort(response.count(DnsSection.QUESTION)); - buf.writeShort(response.count(DnsSection.ANSWER)); - buf.writeShort(response.count(DnsSection.AUTHORITY)); - buf.writeShort(response.count(DnsSection.ADDITIONAL)); - } - - private static void encodeQuestions(DnsRecordEncoder encoder, DnsResponse response, ByteBuf buf) throws Exception { - int count = response.count(DnsSection.QUESTION); - for (int i = 0; i < count; ++i) { - encoder.encodeQuestion(response.recordAt(DnsSection.QUESTION, i), buf); - } - } - - private static void encodeRecords(DnsRecordEncoder encoder, - DnsResponse response, DnsSection section, ByteBuf buf) throws Exception { - int count = response.count(section); - for (int i = 0; i < count; ++i) { - encoder.encodeRecord(response.recordAt(section, i), buf); - } - } - - interface DnsQueryFactory { - DnsQuery newQuery(int id, DnsOpCode dnsOpCode); - } - - private DnsMessageUtil() { - } -} diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsOpCode.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsOpCode.java deleted file mode 100644 index b58896426d..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsOpCode.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.util.internal.UnstableApi; - -import static java.util.Objects.requireNonNull; - -/** - * The DNS {@code OpCode} as defined in RFC2929. - */ -@UnstableApi -public class DnsOpCode implements Comparable { - - /** - * The 'Query' DNS OpCode, as defined in RFC1035. - */ - public static final DnsOpCode QUERY = new DnsOpCode(0x00, "QUERY"); - - /** - * The 'IQuery' DNS OpCode, as defined in RFC1035. - */ - public static final DnsOpCode IQUERY = new DnsOpCode(0x01, "IQUERY"); - - /** - * The 'Status' DNS OpCode, as defined in RFC1035. - */ - public static final DnsOpCode STATUS = new DnsOpCode(0x02, "STATUS"); - - /** - * The 'Notify' DNS OpCode, as defined in RFC1996. - */ - public static final DnsOpCode NOTIFY = new DnsOpCode(0x04, "NOTIFY"); - - /** - * The 'Update' DNS OpCode, as defined in RFC2136. - */ - public static final DnsOpCode UPDATE = new DnsOpCode(0x05, "UPDATE"); - - /** - * Returns the {@link DnsOpCode} instance of the specified byte value. - */ - public static DnsOpCode valueOf(int b) { - switch (b) { - case 0x00: - return QUERY; - case 0x01: - return IQUERY; - case 0x02: - return STATUS; - case 0x04: - return NOTIFY; - case 0x05: - return UPDATE; - default: - break; - } - - return new DnsOpCode(b); - } - - private final byte byteValue; - private final String name; - private String text; - - private DnsOpCode(int byteValue) { - this(byteValue, "UNKNOWN"); - } - - public DnsOpCode(int byteValue, String name) { - this.byteValue = (byte) byteValue; - this.name = requireNonNull(name, "name"); - } - - public byte byteValue() { - return byteValue; - } - - @Override - public int hashCode() { - return byteValue; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - - if (!(obj instanceof DnsOpCode)) { - return false; - } - - return byteValue == ((DnsOpCode) obj).byteValue; - } - - @Override - public int compareTo(DnsOpCode o) { - return byteValue - o.byteValue; - } - - @Override - public String toString() { - String text = this.text; - if (text == null) { - this.text = text = name + '(' + (byteValue & 0xFF) + ')'; - } - return text; - } -} diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsOptEcsRecord.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsOptEcsRecord.java deleted file mode 100644 index d602dd626f..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsOptEcsRecord.java +++ /dev/null @@ -1,43 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.util.internal.UnstableApi; - -import java.net.InetAddress; - -/** - * An ECS record as defined in Client Subnet in DNS Queries. - */ -@UnstableApi -public interface DnsOptEcsRecord extends DnsOptPseudoRecord { - - /** - * Returns the leftmost number of significant bits of ADDRESS to be used for the lookup. - */ - int sourcePrefixLength(); - - /** - * Returns the leftmost number of significant bits of ADDRESS that the response covers. - * In queries, it MUST be 0. - */ - int scopePrefixLength(); - - /** - * Returns the bytes of the {@link InetAddress} to use. - */ - byte[] address(); -} diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsOptPseudoRecord.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsOptPseudoRecord.java deleted file mode 100644 index 4269d806cb..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsOptPseudoRecord.java +++ /dev/null @@ -1,44 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.util.internal.UnstableApi; - -/** - * An OPT RR record. - *

- * This is used for Extension - * Mechanisms for DNS (EDNS(0)). - */ -@UnstableApi -public interface DnsOptPseudoRecord extends DnsRecord { - - /** - * Returns the {@code EXTENDED-RCODE} which is encoded into {@link DnsOptPseudoRecord#timeToLive()}. - */ - int extendedRcode(); - - /** - * Returns the {@code VERSION} which is encoded into {@link DnsOptPseudoRecord#timeToLive()}. - */ - int version(); - - /** - * Returns the {@code flags} which includes {@code DO} and {@code Z} which is encoded - * into {@link DnsOptPseudoRecord#timeToLive()}. - */ - int flags(); -} diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsPtrRecord.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsPtrRecord.java deleted file mode 100644 index 3ba5622d12..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsPtrRecord.java +++ /dev/null @@ -1,28 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.util.internal.UnstableApi; - -@UnstableApi -public interface DnsPtrRecord extends DnsRecord { - - /** - * Returns the hostname this PTR record resolves to. - */ - String hostname(); - -} diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsQuery.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsQuery.java deleted file mode 100644 index 18b100dcea..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsQuery.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.util.internal.UnstableApi; - -/** - * A DNS query message. - */ -@UnstableApi -public interface DnsQuery extends DnsMessage { - @Override - DnsQuery setId(int id); - - @Override - DnsQuery setOpCode(DnsOpCode opCode); - - @Override - DnsQuery setRecursionDesired(boolean recursionDesired); - - @Override - DnsQuery setZ(int z); - - @Override - DnsQuery setRecord(DnsSection section, DnsRecord record); - - @Override - DnsQuery addRecord(DnsSection section, DnsRecord record); - - @Override - DnsQuery addRecord(DnsSection section, int index, DnsRecord record); - - @Override - DnsQuery clear(DnsSection section); - - @Override - DnsQuery clear(); - - @Override - DnsQuery touch(); - - @Override - DnsQuery touch(Object hint); - - @Override - DnsQuery retain(); - - @Override - DnsQuery retain(int increment); -} diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsQueryEncoder.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsQueryEncoder.java deleted file mode 100644 index a2f2dab62b..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsQueryEncoder.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.buffer.ByteBuf; - -import java.util.Objects; - -final class DnsQueryEncoder { - - private final DnsRecordEncoder recordEncoder; - - /** - * Creates a new encoder with the specified {@code recordEncoder}. - */ - DnsQueryEncoder(DnsRecordEncoder recordEncoder) { - this.recordEncoder = Objects.requireNonNull(recordEncoder, "recordEncoder"); - } - - /** - * Encodes the given {@link DnsQuery} into a {@link ByteBuf}. - */ - void encode(DnsQuery query, ByteBuf out) throws Exception { - encodeHeader(query, out); - encodeQuestions(query, out); - encodeRecords(query, DnsSection.ADDITIONAL, out); - } - - /** - * Encodes the header that is always 12 bytes long. - * - * @param query the query header being encoded - * @param buf the buffer the encoded data should be written to - */ - private static void encodeHeader(DnsQuery query, ByteBuf buf) { - buf.writeShort(query.id()); - int flags = 0; - flags |= (query.opCode().byteValue() & 0xFF) << 14; - if (query.isRecursionDesired()) { - flags |= 1 << 8; - } - buf.writeShort(flags); - buf.writeShort(query.count(DnsSection.QUESTION)); - buf.writeShort(0); // answerCount - buf.writeShort(0); // authorityResourceCount - buf.writeShort(query.count(DnsSection.ADDITIONAL)); - } - - private void encodeQuestions(DnsQuery query, ByteBuf buf) throws Exception { - final int count = query.count(DnsSection.QUESTION); - for (int i = 0; i < count; i++) { - recordEncoder.encodeQuestion((DnsQuestion) query.recordAt(DnsSection.QUESTION, i), buf); - } - } - - private void encodeRecords(DnsQuery query, DnsSection section, ByteBuf buf) throws Exception { - final int count = query.count(section); - for (int i = 0; i < count; i++) { - recordEncoder.encodeRecord(query.recordAt(section, i), buf); - } - } -} diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsQuestion.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsQuestion.java deleted file mode 100644 index 54697184fb..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsQuestion.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.util.internal.UnstableApi; - -/** - * A DNS question. - */ -@UnstableApi -public interface DnsQuestion extends DnsRecord { - /** - * An unused property. This method will always return {@code 0}. - */ - @Override - long timeToLive(); -} diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsRawRecord.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsRawRecord.java deleted file mode 100644 index 07f39a75b2..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsRawRecord.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufHolder; -import io.netty.util.internal.UnstableApi; - -/** - * A generic {@link DnsRecord} that contains an undecoded {@code RDATA}. - */ -@UnstableApi -public interface DnsRawRecord extends DnsRecord, ByteBufHolder { - @Override - DnsRawRecord copy(); - - @Override - DnsRawRecord duplicate(); - - @Override - DnsRawRecord retainedDuplicate(); - - @Override - DnsRawRecord replace(ByteBuf content); - - @Override - DnsRawRecord retain(); - - @Override - DnsRawRecord retain(int increment); - - @Override - DnsRawRecord touch(); - - @Override - DnsRawRecord touch(Object hint); -} diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsRecord.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsRecord.java deleted file mode 100644 index 85630df8b0..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsRecord.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.util.internal.UnstableApi; - -/** - * A DNS resource record. - */ -@UnstableApi -public interface DnsRecord { - - /** - * DNS resource record class: {@code IN} - */ - int CLASS_IN = 0x0001; - - /** - * DNS resource record class: {@code CSNET} - */ - int CLASS_CSNET = 0x0002; - - /** - * DNS resource record class: {@code CHAOS} - */ - int CLASS_CHAOS = 0x0003; - - /** - * DNS resource record class: {@code HESIOD} - */ - int CLASS_HESIOD = 0x0004; - - /** - * DNS resource record class: {@code NONE} - */ - int CLASS_NONE = 0x00fe; - - /** - * DNS resource record class: {@code ANY} - */ - int CLASS_ANY = 0x00ff; - - /** - * Returns the name of this resource record. - */ - String name(); - - /** - * Returns the type of this resource record. - */ - DnsRecordType type(); - - /** - * Returns the class of this resource record. - * - * @return the class value, usually one of the following: - *

    - *
  • {@link #CLASS_IN}
  • - *
  • {@link #CLASS_CSNET}
  • - *
  • {@link #CLASS_CHAOS}
  • - *
  • {@link #CLASS_HESIOD}
  • - *
  • {@link #CLASS_NONE}
  • - *
  • {@link #CLASS_ANY}
  • - *
- */ - int dnsClass(); - - /** - * Returns the time to live after reading for this resource record. - */ - long timeToLive(); -} diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsRecordDecoder.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsRecordDecoder.java deleted file mode 100644 index 13c510e502..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsRecordDecoder.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.buffer.ByteBuf; -import io.netty.util.internal.UnstableApi; - -/** - * Decodes a DNS record into its object representation. - * - * @see DatagramDnsResponseDecoder - */ -@UnstableApi -public interface DnsRecordDecoder { - - DnsRecordDecoder DEFAULT = new DefaultDnsRecordDecoder(); - - /** - * Decodes a DNS question into its object representation. - * - * @param in the input buffer which contains a DNS question at its reader index - */ - DnsQuestion decodeQuestion(ByteBuf in) throws Exception; - - /** - * Decodes a DNS record into its object representation. - * - * @param in the input buffer which contains a DNS record at its reader index - * - * @return the decoded record, or {@code null} if there are not enough data in the input buffer - */ - T decodeRecord(ByteBuf in) throws Exception; -} diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsRecordEncoder.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsRecordEncoder.java deleted file mode 100644 index a5ce418639..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsRecordEncoder.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.buffer.ByteBuf; -import io.netty.util.internal.UnstableApi; - -/** - * Encodes a {@link DnsRecord} into binary representation. - * - * @see DatagramDnsQueryEncoder - */ -@UnstableApi -public interface DnsRecordEncoder { - - DnsRecordEncoder DEFAULT = new DefaultDnsRecordEncoder(); - - /** - * Encodes a {@link DnsQuestion}. - * - * @param out the output buffer where the encoded question will be written to - */ - void encodeQuestion(DnsQuestion question, ByteBuf out) throws Exception; - - /** - * Encodes a {@link DnsRecord}. - * - * @param out the output buffer where the encoded record will be written to - */ - void encodeRecord(DnsRecord record, ByteBuf out) throws Exception; -} diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsRecordType.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsRecordType.java deleted file mode 100644 index f4455beaf3..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsRecordType.java +++ /dev/null @@ -1,404 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.util.collection.IntObjectHashMap; -import io.netty.util.internal.UnstableApi; - -import java.util.HashMap; -import java.util.Map; - -/** - * Represents a DNS record type. - */ -@UnstableApi -public class DnsRecordType implements Comparable { - - /** - * Address record RFC 1035 Returns a 32-bit IPv4 address, most commonly used - * to map hostnames to an IP address of the host, but also used for DNSBLs, - * storing subnet masks in RFC 1101, etc. - */ - public static final DnsRecordType A = new DnsRecordType(0x0001, "A"); - - /** - * Name server record RFC 1035 Delegates a DNS zone to use the given - * authoritative name servers - */ - public static final DnsRecordType NS = new DnsRecordType(0x0002, "NS"); - - /** - * Canonical name record RFC 1035 Alias of one name to another: the DNS - * lookup will continue by retrying the lookup with the new name. - */ - public static final DnsRecordType CNAME = new DnsRecordType(0x0005, "CNAME"); - - /** - * Start of [a zone of] authority record RFC 1035 and RFC 2308 Specifies - * authoritative information about a DNS zone, including the primary name - * server, the email of the domain administrator, the domain serial number, - * and several timers relating to refreshing the zone. - */ - public static final DnsRecordType SOA = new DnsRecordType(0x0006, "SOA"); - - /** - * Pointer record RFC 1035 Pointer to a canonical name. Unlike a CNAME, DNS - * processing does NOT proceed, just the name is returned. The most common - * use is for implementing reverse DNS lookups, but other uses include such - * things as DNS-SD. - */ - public static final DnsRecordType PTR = new DnsRecordType(0x000c, "PTR"); - - /** - * Mail exchange record RFC 1035 Maps a domain name to a list of message - * transfer agents for that domain. - */ - public static final DnsRecordType MX = new DnsRecordType(0x000f, "MX"); - - /** - * Text record RFC 1035 Originally for arbitrary human-readable text in a - * DNS record. Since the early 1990s, however, this record more often - * carries machine-readable data, such as specified by RFC 1464, - * opportunistic encryption, Sender Policy Framework, DKIM, DMARC DNS-SD, - * etc. - */ - public static final DnsRecordType TXT = new DnsRecordType(0x0010, "TXT"); - - /** - * Responsible person record RFC 1183 Information about the responsible - * person(s) for the domain. Usually an email address with the @ replaced by - * a . - */ - public static final DnsRecordType RP = new DnsRecordType(0x0011, "RP"); - - /** - * AFS database record RFC 1183 Location of database servers of an AFS cell. - * This record is commonly used by AFS clients to contact AFS cells outside - * their local domain. A subtype of this record is used by the obsolete - * DCE/DFS file system. - */ - public static final DnsRecordType AFSDB = new DnsRecordType(0x0012, "AFSDB"); - - /** - * Signature record RFC 2535 Signature record used in SIG(0) (RFC 2931) and - * TKEY (RFC 2930). RFC 3755 designated RRSIG as the replacement for SIG for - * use within DNSSEC. - */ - public static final DnsRecordType SIG = new DnsRecordType(0x0018, "SIG"); - - /** - * key record RFC 2535 and RFC 2930 Used only for SIG(0) (RFC 2931) and TKEY - * (RFC 2930). RFC 3445 eliminated their use for application keys and - * limited their use to DNSSEC. RFC 3755 designates DNSKEY as the - * replacement within DNSSEC. RFC 4025 designates IPSECKEY as the - * replacement for use with IPsec. - */ - public static final DnsRecordType KEY = new DnsRecordType(0x0019, "KEY"); - - /** - * IPv6 address record RFC 3596 Returns a 128-bit IPv6 address, most - * commonly used to map hostnames to an IP address of the host. - */ - public static final DnsRecordType AAAA = new DnsRecordType(0x001c, "AAAA"); - - /** - * Location record RFC 1876 Specifies a geographical location associated - * with a domain name. - */ - public static final DnsRecordType LOC = new DnsRecordType(0x001d, "LOC"); - - /** - * Service locator RFC 2782 Generalized service location record, used for - * newer protocols instead of creating protocol-specific records such as MX. - */ - public static final DnsRecordType SRV = new DnsRecordType(0x0021, "SRV"); - - /** - * Naming Authority Pointer record RFC 3403 Allows regular expression based - * rewriting of domain names which can then be used as URIs, further domain - * names to lookups, etc. - */ - public static final DnsRecordType NAPTR = new DnsRecordType(0x0023, "NAPTR"); - - /** - * Key eXchanger record RFC 2230 Used with some cryptographic systems (not - * including DNSSEC) to identify a key management agent for the associated - * domain-name. Note that this has nothing to do with DNS Security. It is - * Informational status, rather than being on the IETF standards-track. It - * has always had limited deployment, but is still in use. - */ - public static final DnsRecordType KX = new DnsRecordType(0x0024, "KX"); - - /** - * Certificate record RFC 4398 Stores PKIX, SPKI, PGP, etc. - */ - public static final DnsRecordType CERT = new DnsRecordType(0x0025, "CERT"); - - /** - * Delegation name record RFC 2672 DNAME creates an alias for a name and all - * its subnames, unlike CNAME, which aliases only the exact name in its - * label. Like the CNAME record, the DNS lookup will continue by retrying - * the lookup with the new name. - */ - public static final DnsRecordType DNAME = new DnsRecordType(0x0027, "DNAME"); - - /** - * Option record RFC 2671 This is a pseudo DNS record type needed to support - * EDNS. - */ - public static final DnsRecordType OPT = new DnsRecordType(0x0029, "OPT"); - - /** - * Address Prefix List record RFC 3123 Specify lists of address ranges, e.g. - * in CIDR format, for various address families. Experimental. - */ - public static final DnsRecordType APL = new DnsRecordType(0x002a, "APL"); - - /** - * Delegation signer record RFC 4034 The record used to identify the DNSSEC - * signing key of a delegated zone. - */ - public static final DnsRecordType DS = new DnsRecordType(0x002b, "DS"); - - /** - * SSH Public Key Fingerprint record RFC 4255 Resource record for publishing - * SSH public host key fingerprints in the DNS System, in order to aid in - * verifying the authenticity of the host. RFC 6594 defines ECC SSH keys and - * SHA-256 hashes. See the IANA SSHFP RR parameters registry for details. - */ - public static final DnsRecordType SSHFP = new DnsRecordType(0x002c, "SSHFP"); - - /** - * IPsec Key record RFC 4025 Key record that can be used with IPsec. - */ - public static final DnsRecordType IPSECKEY = new DnsRecordType(0x002d, "IPSECKEY"); - - /** - * DNSSEC signature record RFC 4034 Signature for a DNSSEC-secured record - * set. Uses the same format as the SIG record. - */ - public static final DnsRecordType RRSIG = new DnsRecordType(0x002e, "RRSIG"); - - /** - * Next-Secure record RFC 4034 Part of DNSSEC, used to prove a name does not - * exist. Uses the same format as the (obsolete) NXT record. - */ - public static final DnsRecordType NSEC = new DnsRecordType(0x002f, "NSEC"); - - /** - * DNS Key record RFC 4034 The key record used in DNSSEC. Uses the same - * format as the KEY record. - */ - public static final DnsRecordType DNSKEY = new DnsRecordType(0x0030, "DNSKEY"); - - /** - * DHCP identifier record RFC 4701 Used in conjunction with the FQDN option - * to DHCP. - */ - public static final DnsRecordType DHCID = new DnsRecordType(0x0031, "DHCID"); - - /** - * NSEC record version 3 RFC 5155 An extension to DNSSEC that allows proof - * of nonexistence for a name without permitting zonewalking. - */ - public static final DnsRecordType NSEC3 = new DnsRecordType(0x0032, "NSEC3"); - - /** - * NSEC3 parameters record RFC 5155 Parameter record for use with NSEC3. - */ - public static final DnsRecordType NSEC3PARAM = new DnsRecordType(0x0033, "NSEC3PARAM"); - - /** - * TLSA certificate association record RFC 6698 A record for DNS-based - * Authentication of Named Entities (DANE). RFC 6698 defines The TLSA DNS - * resource record is used to associate a TLS server certificate or public - * key with the domain name where the record is found, thus forming a 'TLSA - * certificate association'. - */ - public static final DnsRecordType TLSA = new DnsRecordType(0x0034, "TLSA"); - - /** - * Host Identity Protocol record RFC 5205 Method of separating the end-point - * identifier and locator roles of IP addresses. - */ - public static final DnsRecordType HIP = new DnsRecordType(0x0037, "HIP"); - - /** - * Sender Policy Framework record RFC 4408 Specified as part of the SPF - * protocol as an alternative to of storing SPF data in TXT records. Uses - * the same format as the earlier TXT record. - */ - public static final DnsRecordType SPF = new DnsRecordType(0x0063, "SPF"); - - /** - * Secret key record RFC 2930 A method of providing keying material to be - * used with TSIG that is encrypted under the public key in an accompanying - * KEY RR.. - */ - public static final DnsRecordType TKEY = new DnsRecordType(0x00f9, "TKEY"); - - /** - * Transaction Signature record RFC 2845 Can be used to authenticate dynamic - * updates as coming from an approved client, or to authenticate responses - * as coming from an approved recursive name server similar to DNSSEC. - */ - public static final DnsRecordType TSIG = new DnsRecordType(0x00fa, "TSIG"); - - /** - * Incremental Zone Transfer record RFC 1996 Requests a zone transfer of the - * given zone but only differences from a previous serial number. This - * request may be ignored and a full (AXFR) sent in response if the - * authoritative server is unable to fulfill the request due to - * configuration or lack of required deltas. - */ - public static final DnsRecordType IXFR = new DnsRecordType(0x00fb, "IXFR"); - - /** - * Authoritative Zone Transfer record RFC 1035 Transfer entire zone file - * from the master name server to secondary name servers. - */ - public static final DnsRecordType AXFR = new DnsRecordType(0x00fc, "AXFR"); - - /** - * All cached records RFC 1035 Returns all records of all types known to the - * name server. If the name server does not have any information on the - * name, the request will be forwarded on. The records returned may not be - * complete. For example, if there is both an A and an MX for a name, but - * the name server has only the A record cached, only the A record will be - * returned. Sometimes referred to as ANY, for example in Windows nslookup - * and Wireshark. - */ - public static final DnsRecordType ANY = new DnsRecordType(0x00ff, "ANY"); - - /** - * Certification Authority Authorization record RFC 6844 CA pinning, - * constraining acceptable CAs for a host/domain. - */ - public static final DnsRecordType CAA = new DnsRecordType(0x0101, "CAA"); - - /** - * DNSSEC Trust Authorities record N/A Part of a deployment proposal for - * DNSSEC without a signed DNS root. See the IANA database and Weiler Spec - * for details. Uses the same format as the DS record. - */ - public static final DnsRecordType TA = new DnsRecordType(0x8000, "TA"); - - /** - * DNSSEC Lookaside Validation record RFC 4431 For publishing DNSSEC trust - * anchors outside of the DNS delegation chain. Uses the same format as the - * DS record. RFC 5074 describes a way of using these records. - */ - public static final DnsRecordType DLV = new DnsRecordType(0x8001, "DLV"); - - private static final Map BY_NAME = new HashMap<>(); - private static final IntObjectHashMap BY_TYPE = new IntObjectHashMap(); - private static final String EXPECTED; - - static { - DnsRecordType[] all = { - A, NS, CNAME, SOA, PTR, MX, TXT, RP, AFSDB, SIG, KEY, AAAA, LOC, SRV, NAPTR, KX, CERT, DNAME, OPT, APL, - DS, SSHFP, IPSECKEY, RRSIG, NSEC, DNSKEY, DHCID, NSEC3, NSEC3PARAM, TLSA, HIP, SPF, TKEY, TSIG, IXFR, - AXFR, ANY, CAA, TA, DLV - }; - - final StringBuilder expected = new StringBuilder(512); - - expected.append(" (expected: "); - for (DnsRecordType type: all) { - BY_NAME.put(type.name(), type); - BY_TYPE.put(type.intValue(), type); - - expected.append(type.name()) - .append('(') - .append(type.intValue()) - .append("), "); - } - - expected.setLength(expected.length() - 2); - expected.append(')'); - EXPECTED = expected.toString(); - } - - public static DnsRecordType valueOf(int intValue) { - DnsRecordType result = BY_TYPE.get(intValue); - if (result == null) { - return new DnsRecordType(intValue); - } - return result; - } - - public static DnsRecordType valueOf(String name) { - DnsRecordType result = BY_NAME.get(name); - if (result == null) { - throw new IllegalArgumentException("name: " + name + EXPECTED); - } - return result; - } - - private final int intValue; - private final String name; - private String text; - - private DnsRecordType(int intValue) { - this(intValue, "UNKNOWN"); - } - - public DnsRecordType(int intValue, String name) { - if ((intValue & 0xffff) != intValue) { - throw new IllegalArgumentException("intValue: " + intValue + " (expected: 0 ~ 65535)"); - } - this.intValue = intValue; - this.name = name; - } - - /** - * Returns the name of this type, as seen in bind config files - */ - public String name() { - return name; - } - - /** - * Returns the value of this DnsType as it appears in DNS protocol - */ - public int intValue() { - return intValue; - } - - @Override - public int hashCode() { - return intValue; - } - - @Override - public boolean equals(Object o) { - return o instanceof DnsRecordType && ((DnsRecordType) o).intValue == intValue; - } - - @Override - public int compareTo(DnsRecordType o) { - return intValue() - o.intValue(); - } - - @Override - public String toString() { - String text = this.text; - if (text == null) { - this.text = text = name + '(' + intValue() + ')'; - } - return text; - } -} diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsResponse.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsResponse.java deleted file mode 100644 index 537e216ed4..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsResponse.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.util.internal.UnstableApi; - -/** - * A DNS response message. - */ -@UnstableApi -public interface DnsResponse extends DnsMessage { - - /** - * Returns {@code true} if responding server is authoritative for the domain - * name in the query message. - */ - boolean isAuthoritativeAnswer(); - - /** - * Set to {@code true} if responding server is authoritative for the domain - * name in the query message. - * - * @param authoritativeAnswer flag for authoritative answer - */ - DnsResponse setAuthoritativeAnswer(boolean authoritativeAnswer); - - /** - * Returns {@code true} if response has been truncated, usually if it is - * over 512 bytes. - */ - boolean isTruncated(); - - /** - * Set to {@code true} if response has been truncated (usually happens for - * responses over 512 bytes). - * - * @param truncated flag for truncation - */ - DnsResponse setTruncated(boolean truncated); - - /** - * Returns {@code true} if DNS server can handle recursive queries. - */ - boolean isRecursionAvailable(); - - /** - * Set to {@code true} if DNS server can handle recursive queries. - * - * @param recursionAvailable flag for recursion availability - */ - DnsResponse setRecursionAvailable(boolean recursionAvailable); - - /** - * Returns the 4 bit return code. - */ - DnsResponseCode code(); - - /** - * Sets the response code for this message. - * - * @param code the response code - */ - DnsResponse setCode(DnsResponseCode code); - - @Override - DnsResponse setId(int id); - - @Override - DnsResponse setOpCode(DnsOpCode opCode); - - @Override - DnsResponse setRecursionDesired(boolean recursionDesired); - - @Override - DnsResponse setZ(int z); - - @Override - DnsResponse setRecord(DnsSection section, DnsRecord record); - - @Override - DnsResponse addRecord(DnsSection section, DnsRecord record); - - @Override - DnsResponse addRecord(DnsSection section, int index, DnsRecord record); - - @Override - DnsResponse clear(DnsSection section); - - @Override - DnsResponse clear(); - - @Override - DnsResponse touch(); - - @Override - DnsResponse touch(Object hint); - - @Override - DnsResponse retain(); - - @Override - DnsResponse retain(int increment); -} diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsResponseCode.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsResponseCode.java deleted file mode 100644 index 7c370f90bf..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsResponseCode.java +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.util.internal.UnstableApi; - -import static java.util.Objects.requireNonNull; - -/** - * The DNS {@code RCODE}, as defined in RFC2929. - */ -@UnstableApi -public class DnsResponseCode implements Comparable { - - /** - * The 'NoError' DNS RCODE (0), as defined in RFC1035. - */ - public static final DnsResponseCode NOERROR = new DnsResponseCode(0, "NoError"); - - /** - * The 'FormErr' DNS RCODE (1), as defined in RFC1035. - */ - public static final DnsResponseCode FORMERR = new DnsResponseCode(1, "FormErr"); - - /** - * The 'ServFail' DNS RCODE (2), as defined in RFC1035. - */ - public static final DnsResponseCode SERVFAIL = new DnsResponseCode(2, "ServFail"); - - /** - * The 'NXDomain' DNS RCODE (3), as defined in RFC1035. - */ - public static final DnsResponseCode NXDOMAIN = new DnsResponseCode(3, "NXDomain"); - - /** - * The 'NotImp' DNS RCODE (4), as defined in RFC1035. - */ - public static final DnsResponseCode NOTIMP = new DnsResponseCode(4, "NotImp"); - - /** - * The 'Refused' DNS RCODE (5), as defined in RFC1035. - */ - public static final DnsResponseCode REFUSED = new DnsResponseCode(5, "Refused"); - - /** - * The 'YXDomain' DNS RCODE (6), as defined in RFC2136. - */ - public static final DnsResponseCode YXDOMAIN = new DnsResponseCode(6, "YXDomain"); - - /** - * The 'YXRRSet' DNS RCODE (7), as defined in RFC2136. - */ - public static final DnsResponseCode YXRRSET = new DnsResponseCode(7, "YXRRSet"); - - /** - * The 'NXRRSet' DNS RCODE (8), as defined in RFC2136. - */ - public static final DnsResponseCode NXRRSET = new DnsResponseCode(8, "NXRRSet"); - - /** - * The 'NotAuth' DNS RCODE (9), as defined in RFC2136. - */ - public static final DnsResponseCode NOTAUTH = new DnsResponseCode(9, "NotAuth"); - - /** - * The 'NotZone' DNS RCODE (10), as defined in RFC2136. - */ - public static final DnsResponseCode NOTZONE = new DnsResponseCode(10, "NotZone"); - - /** - * The 'BADVERS' or 'BADSIG' DNS RCODE (16), as defined in RFC2671 - * and RFC2845. - */ - public static final DnsResponseCode BADVERS_OR_BADSIG = new DnsResponseCode(16, "BADVERS_OR_BADSIG"); - - /** - * The 'BADKEY' DNS RCODE (17), as defined in RFC2845. - */ - public static final DnsResponseCode BADKEY = new DnsResponseCode(17, "BADKEY"); - - /** - * The 'BADTIME' DNS RCODE (18), as defined in RFC2845. - */ - public static final DnsResponseCode BADTIME = new DnsResponseCode(18, "BADTIME"); - - /** - * The 'BADMODE' DNS RCODE (19), as defined in RFC2930. - */ - public static final DnsResponseCode BADMODE = new DnsResponseCode(19, "BADMODE"); - - /** - * The 'BADNAME' DNS RCODE (20), as defined in RFC2930. - */ - public static final DnsResponseCode BADNAME = new DnsResponseCode(20, "BADNAME"); - - /** - * The 'BADALG' DNS RCODE (21), as defined in RFC2930. - */ - public static final DnsResponseCode BADALG = new DnsResponseCode(21, "BADALG"); - - /** - * Returns the {@link DnsResponseCode} that corresponds with the given {@code responseCode}. - * - * @param responseCode the DNS RCODE - * - * @return the corresponding {@link DnsResponseCode} - */ - public static DnsResponseCode valueOf(int responseCode) { - switch (responseCode) { - case 0: - return NOERROR; - case 1: - return FORMERR; - case 2: - return SERVFAIL; - case 3: - return NXDOMAIN; - case 4: - return NOTIMP; - case 5: - return REFUSED; - case 6: - return YXDOMAIN; - case 7: - return YXRRSET; - case 8: - return NXRRSET; - case 9: - return NOTAUTH; - case 10: - return NOTZONE; - case 16: - return BADVERS_OR_BADSIG; - case 17: - return BADKEY; - case 18: - return BADTIME; - case 19: - return BADMODE; - case 20: - return BADNAME; - case 21: - return BADALG; - default: - return new DnsResponseCode(responseCode); - } - } - - private final int code; - private final String name; - private String text; - - private DnsResponseCode(int code) { - this(code, "UNKNOWN"); - } - - public DnsResponseCode(int code, String name) { - if (code < 0 || code > 65535) { - throw new IllegalArgumentException("code: " + code + " (expected: 0 ~ 65535)"); - } - - this.code = code; - this.name = requireNonNull(name, "name"); - } - - /** - * Returns the error code for this {@link DnsResponseCode}. - */ - public int intValue() { - return code; - } - - @Override - public int compareTo(DnsResponseCode o) { - return intValue() - o.intValue(); - } - - @Override - public int hashCode() { - return intValue(); - } - - /** - * Equality of {@link DnsResponseCode} only depends on {@link #intValue()}. - */ - @Override - public boolean equals(Object o) { - if (!(o instanceof DnsResponseCode)) { - return false; - } - - return intValue() == ((DnsResponseCode) o).intValue(); - } - - /** - * Returns a formatted error message for this {@link DnsResponseCode}. - */ - @Override - public String toString() { - String text = this.text; - if (text == null) { - this.text = text = name + '(' + intValue() + ')'; - } - return text; - } -} diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsResponseDecoder.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsResponseDecoder.java deleted file mode 100644 index 017f259a30..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsResponseDecoder.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.CorruptedFrameException; - -import java.net.SocketAddress; -import java.util.Objects; - -abstract class DnsResponseDecoder { - - private final DnsRecordDecoder recordDecoder; - - /** - * Creates a new decoder with the specified {@code recordDecoder}. - */ - DnsResponseDecoder(DnsRecordDecoder recordDecoder) { - this.recordDecoder = Objects.requireNonNull(recordDecoder, "recordDecoder"); - } - - final DnsResponse decode(A sender, A recipient, ByteBuf buffer) throws Exception { - final int id = buffer.readUnsignedShort(); - - final int flags = buffer.readUnsignedShort(); - if (flags >> 15 == 0) { - throw new CorruptedFrameException("not a response"); - } - - final DnsResponse response = newResponse( - sender, - recipient, - id, - DnsOpCode.valueOf((byte) (flags >> 11 & 0xf)), DnsResponseCode.valueOf((byte) (flags & 0xf))); - - response.setRecursionDesired((flags >> 8 & 1) == 1); - response.setAuthoritativeAnswer((flags >> 10 & 1) == 1); - response.setTruncated((flags >> 9 & 1) == 1); - response.setRecursionAvailable((flags >> 7 & 1) == 1); - response.setZ(flags >> 4 & 0x7); - - boolean success = false; - try { - final int questionCount = buffer.readUnsignedShort(); - final int answerCount = buffer.readUnsignedShort(); - final int authorityRecordCount = buffer.readUnsignedShort(); - final int additionalRecordCount = buffer.readUnsignedShort(); - - decodeQuestions(response, buffer, questionCount); - if (!decodeRecords(response, DnsSection.ANSWER, buffer, answerCount)) { - success = true; - return response; - } - if (!decodeRecords(response, DnsSection.AUTHORITY, buffer, authorityRecordCount)) { - success = true; - return response; - } - - decodeRecords(response, DnsSection.ADDITIONAL, buffer, additionalRecordCount); - success = true; - return response; - } finally { - if (!success) { - response.release(); - } - } - } - - protected abstract DnsResponse newResponse(A sender, A recipient, int id, - DnsOpCode opCode, DnsResponseCode responseCode) throws Exception; - - private void decodeQuestions(DnsResponse response, ByteBuf buf, int questionCount) throws Exception { - for (int i = questionCount; i > 0; i --) { - response.addRecord(DnsSection.QUESTION, recordDecoder.decodeQuestion(buf)); - } - } - - private boolean decodeRecords( - DnsResponse response, DnsSection section, ByteBuf buf, int count) throws Exception { - for (int i = count; i > 0; i --) { - final DnsRecord r = recordDecoder.decodeRecord(buf); - if (r == null) { - // Truncated response - return false; - } - - response.addRecord(section, r); - } - return true; - } -} diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsSection.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsSection.java deleted file mode 100644 index fcf9e766ca..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/DnsSection.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.util.internal.UnstableApi; - -/** - * Represents a section of a {@link DnsMessage}. - */ -@UnstableApi -public enum DnsSection { - /** - * The section that contains {@link DnsQuestion}s. - */ - QUESTION, - /** - * The section that contains the answer {@link DnsRecord}s. - */ - ANSWER, - /** - * The section that contains the authority {@link DnsRecord}s. - */ - AUTHORITY, - /** - * The section that contains the additional {@link DnsRecord}s. - */ - ADDITIONAL -} diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/TcpDnsQueryDecoder.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/TcpDnsQueryDecoder.java deleted file mode 100644 index 7af77a8eb4..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/TcpDnsQueryDecoder.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.LengthFieldBasedFrameDecoder; -import io.netty.util.internal.ObjectUtil; -import io.netty.util.internal.UnstableApi; - -@UnstableApi -public final class TcpDnsQueryDecoder extends LengthFieldBasedFrameDecoder { - private final DnsRecordDecoder decoder; - - /** - * Creates a new decoder with {@linkplain DnsRecordDecoder#DEFAULT the default record decoder}. - */ - public TcpDnsQueryDecoder() { - this(DnsRecordDecoder.DEFAULT, 65535); - } - - /** - * Creates a new decoder with the specified {@code decoder}. - */ - public TcpDnsQueryDecoder(DnsRecordDecoder decoder, int maxFrameLength) { - super(maxFrameLength, 0, 2, 0, 2); - this.decoder = ObjectUtil.checkNotNull(decoder, "decoder"); - } - - @Override - protected Object decode0(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - ByteBuf frame = (ByteBuf) super.decode0(ctx, in); - if (frame == null) { - return null; - } - - return DnsMessageUtil.decodeDnsQuery(decoder, frame.slice(), new DnsMessageUtil.DnsQueryFactory() { - @Override - public DnsQuery newQuery(int id, DnsOpCode dnsOpCode) { - return new DefaultDnsQuery(id, dnsOpCode); - } - }); - } -} diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/TcpDnsQueryEncoder.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/TcpDnsQueryEncoder.java deleted file mode 100644 index 401d3b46b8..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/TcpDnsQueryEncoder.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToByteEncoder; -import io.netty.util.internal.UnstableApi; - -@ChannelHandler.Sharable -@UnstableApi -public final class TcpDnsQueryEncoder extends MessageToByteEncoder { - - private final DnsQueryEncoder encoder; - - /** - * Creates a new encoder with {@linkplain DnsRecordEncoder#DEFAULT the default record encoder}. - */ - public TcpDnsQueryEncoder() { - this(DnsRecordEncoder.DEFAULT); - } - - /** - * Creates a new encoder with the specified {@code recordEncoder}. - */ - public TcpDnsQueryEncoder(DnsRecordEncoder recordEncoder) { - this.encoder = new DnsQueryEncoder(recordEncoder); - } - - @Override - protected void encode(ChannelHandlerContext ctx, DnsQuery msg, ByteBuf out) throws Exception { - // Length is two octets as defined by RFC-7766 - // See https://tools.ietf.org/html/rfc7766#section-8 - out.writerIndex(out.writerIndex() + 2); - encoder.encode(msg, out); - - // Now fill in the correct length based on the amount of data that we wrote the ByteBuf. - out.setShort(0, out.readableBytes() - 2); - } - - @Override - protected ByteBuf allocateBuffer(ChannelHandlerContext ctx, @SuppressWarnings("unused") DnsQuery msg, - boolean preferDirect) { - if (preferDirect) { - return ctx.alloc().ioBuffer(1024); - } else { - return ctx.alloc().heapBuffer(1024); - } - } -} diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/TcpDnsResponseDecoder.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/TcpDnsResponseDecoder.java deleted file mode 100644 index 986d4827c1..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/TcpDnsResponseDecoder.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.LengthFieldBasedFrameDecoder; -import io.netty.util.internal.UnstableApi; - -import java.net.SocketAddress; - -@UnstableApi -public final class TcpDnsResponseDecoder extends LengthFieldBasedFrameDecoder { - - private final DnsResponseDecoder responseDecoder; - - /** - * Creates a new decoder with {@linkplain DnsRecordDecoder#DEFAULT the default record decoder}. - */ - public TcpDnsResponseDecoder() { - this(DnsRecordDecoder.DEFAULT, 64 * 1024); - } - - /** - * Creates a new decoder with the specified {@code recordDecoder} and {@code maxFrameLength} - */ - public TcpDnsResponseDecoder(DnsRecordDecoder recordDecoder, int maxFrameLength) { - // Length is two octets as defined by RFC-7766 - // See https://tools.ietf.org/html/rfc7766#section-8 - super(maxFrameLength, 0, 2, 0, 2); - - this.responseDecoder = new DnsResponseDecoder(recordDecoder) { - @Override - protected DnsResponse newResponse(SocketAddress sender, SocketAddress recipient, - int id, DnsOpCode opCode, DnsResponseCode responseCode) { - return new DefaultDnsResponse(id, opCode, responseCode); - } - }; - } - - @Override - protected Object decode0(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - ByteBuf frame = (ByteBuf) super.decode0(ctx, in); - if (frame == null) { - return null; - } - - try { - return responseDecoder.decode(ctx.channel().remoteAddress(), ctx.channel().localAddress(), frame.slice()); - } finally { - frame.release(); - } - } - - @Override - protected ByteBuf extractFrame(ChannelHandlerContext ctx, ByteBuf buffer, int index, int length) { - return buffer.copy(index, length); - } -} diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/TcpDnsResponseEncoder.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/TcpDnsResponseEncoder.java deleted file mode 100644 index 2a9d5d01c8..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/TcpDnsResponseEncoder.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToMessageEncoder; -import io.netty.util.internal.ObjectUtil; -import io.netty.util.internal.UnstableApi; - -import java.util.List; - -@UnstableApi -@ChannelHandler.Sharable -public final class TcpDnsResponseEncoder extends MessageToMessageEncoder { - private final DnsRecordEncoder encoder; - - /** - * Creates a new encoder with {@linkplain DnsRecordEncoder#DEFAULT the default record encoder}. - */ - public TcpDnsResponseEncoder() { - this(DnsRecordEncoder.DEFAULT); - } - - /** - * Creates a new encoder with the specified {@code encoder}. - */ - public TcpDnsResponseEncoder(DnsRecordEncoder encoder) { - this.encoder = ObjectUtil.checkNotNull(encoder, "encoder"); - } - - @Override - protected void encode(ChannelHandlerContext ctx, DnsResponse response, List out) throws Exception { - ByteBuf buf = ctx.alloc().ioBuffer(1024); - - buf.writerIndex(buf.writerIndex() + 2); - DnsMessageUtil.encodeDnsResponse(encoder, response, buf); - buf.setShort(0, buf.readableBytes() - 2); - - out.add(buf); - } -} diff --git a/codec-dns/src/main/java/io/netty/handler/codec/dns/package-info.java b/codec-dns/src/main/java/io/netty/handler/codec/dns/package-info.java deleted file mode 100644 index fd3d6bcd9a..0000000000 --- a/codec-dns/src/main/java/io/netty/handler/codec/dns/package-info.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * DNS codec. - */ -@UnstableApi -package io.netty.handler.codec.dns; - -import io.netty.util.internal.UnstableApi; diff --git a/codec-dns/src/test/java/io/netty/handler/codec/dns/AbstractDnsRecordTest.java b/codec-dns/src/test/java/io/netty/handler/codec/dns/AbstractDnsRecordTest.java deleted file mode 100644 index c5c6d8b535..0000000000 --- a/codec-dns/src/test/java/io/netty/handler/codec/dns/AbstractDnsRecordTest.java +++ /dev/null @@ -1,64 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - -public class AbstractDnsRecordTest { - - @Test - public void testValidDomainName() { - String name = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; - AbstractDnsRecord record = new AbstractDnsRecord(name, DnsRecordType.A, 0) { }; - assertEquals(name + '.', record.name()); - } - - @Test - public void testValidDomainNameUmlaut() { - String name = "ä"; - AbstractDnsRecord record = new AbstractDnsRecord(name, DnsRecordType.A, 0) { }; - assertEquals("xn--4ca.", record.name()); - } - - @Test - public void testValidDomainNameTrailingDot() { - String name = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."; - AbstractDnsRecord record = new AbstractDnsRecord(name, DnsRecordType.A, 0) { }; - assertEquals(name, record.name()); - } - - @Test - public void testValidDomainNameUmlautTrailingDot() { - String name = "ä."; - AbstractDnsRecord record = new AbstractDnsRecord(name, DnsRecordType.A, 0) { }; - assertEquals("xn--4ca.", record.name()); - } - - @Test - public void testValidDomainNameLength() { - String name = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; - assertThrows(IllegalArgumentException.class, () -> new AbstractDnsRecord(name, DnsRecordType.A, 0) { }); - } - - @Test - public void testValidDomainNameUmlautLength() { - String name = "äaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; - assertThrows(IllegalArgumentException.class, () -> new AbstractDnsRecord(name, DnsRecordType.A, 0) { }); - } -} diff --git a/codec-dns/src/test/java/io/netty/handler/codec/dns/DefaultDnsRecordDecoderTest.java b/codec-dns/src/test/java/io/netty/handler/codec/dns/DefaultDnsRecordDecoderTest.java deleted file mode 100644 index a8379f6d8d..0000000000 --- a/codec-dns/src/test/java/io/netty/handler/codec/dns/DefaultDnsRecordDecoderTest.java +++ /dev/null @@ -1,256 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.buffer.Unpooled; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; - -public class DefaultDnsRecordDecoderTest { - - @Test - public void testDecodeName() { - testDecodeName("netty.io.", Unpooled.wrappedBuffer(new byte[] { - 5, 'n', 'e', 't', 't', 'y', 2, 'i', 'o', 0 - })); - } - - @Test - public void testDecodeNameWithoutTerminator() { - testDecodeName("netty.io.", Unpooled.wrappedBuffer(new byte[] { - 5, 'n', 'e', 't', 't', 'y', 2, 'i', 'o' - })); - } - - @Test - public void testDecodeNameWithExtraTerminator() { - // Should not be decoded as 'netty.io..' - testDecodeName("netty.io.", Unpooled.wrappedBuffer(new byte[] { - 5, 'n', 'e', 't', 't', 'y', 2, 'i', 'o', 0, 0 - })); - } - - @Test - public void testDecodeEmptyName() { - testDecodeName(".", Unpooled.buffer().writeByte(0)); - } - - @Test - public void testDecodeEmptyNameFromEmptyBuffer() { - testDecodeName(".", Unpooled.EMPTY_BUFFER); - } - - @Test - public void testDecodeEmptyNameFromExtraZeroes() { - testDecodeName(".", Unpooled.wrappedBuffer(new byte[] { 0, 0 })); - } - - private static void testDecodeName(String expected, ByteBuf buffer) { - try { - DefaultDnsRecordDecoder decoder = new DefaultDnsRecordDecoder(); - assertEquals(expected, decoder.decodeName0(buffer)); - } finally { - buffer.release(); - } - } - - @Test - public void testDecodePtrRecord() throws Exception { - DefaultDnsRecordDecoder decoder = new DefaultDnsRecordDecoder(); - ByteBuf buffer = Unpooled.buffer().writeByte(0); - int readerIndex = buffer.readerIndex(); - int writerIndex = buffer.writerIndex(); - try { - DnsPtrRecord record = (DnsPtrRecord) decoder.decodeRecord( - "netty.io", DnsRecordType.PTR, DnsRecord.CLASS_IN, 60, buffer, 0, 1); - assertEquals("netty.io.", record.name()); - assertEquals(DnsRecord.CLASS_IN, record.dnsClass()); - assertEquals(60, record.timeToLive()); - assertEquals(DnsRecordType.PTR, record.type()); - assertEquals(readerIndex, buffer.readerIndex()); - assertEquals(writerIndex, buffer.writerIndex()); - } finally { - buffer.release(); - } - } - - @Test - public void testdecompressCompressPointer() { - byte[] compressionPointer = { - 5, 'n', 'e', 't', 't', 'y', 2, 'i', 'o', 0, - (byte) 0xC0, 0 - }; - ByteBuf buffer = Unpooled.wrappedBuffer(compressionPointer); - ByteBuf uncompressed = null; - try { - uncompressed = DnsCodecUtil.decompressDomainName(buffer.duplicate().setIndex(10, 12)); - assertEquals(0, ByteBufUtil.compare(buffer.duplicate().setIndex(0, 10), uncompressed)); - } finally { - buffer.release(); - if (uncompressed != null) { - uncompressed.release(); - } - } - } - - @Test - public void testdecompressNestedCompressionPointer() { - byte[] nestedCompressionPointer = { - 6, 'g', 'i', 't', 'h', 'u', 'b', 2, 'i', 'o', 0, // github.io - 5, 'n', 'e', 't', 't', 'y', (byte) 0xC0, 0, // netty.github.io - (byte) 0xC0, 11, // netty.github.io - }; - ByteBuf buffer = Unpooled.wrappedBuffer(nestedCompressionPointer); - ByteBuf uncompressed = null; - try { - uncompressed = DnsCodecUtil.decompressDomainName(buffer.duplicate().setIndex(19, 21)); - assertEquals(0, ByteBufUtil.compare( - Unpooled.wrappedBuffer(new byte[] { - 5, 'n', 'e', 't', 't', 'y', 6, 'g', 'i', 't', 'h', 'u', 'b', 2, 'i', 'o', 0 - }), uncompressed)); - } finally { - buffer.release(); - if (uncompressed != null) { - uncompressed.release(); - } - } - } - - @Test - public void testDecodeCompressionRDataPointer() throws Exception { - DefaultDnsRecordDecoder decoder = new DefaultDnsRecordDecoder(); - byte[] compressionPointer = { - 5, 'n', 'e', 't', 't', 'y', 2, 'i', 'o', 0, - (byte) 0xC0, 0 - }; - ByteBuf buffer = Unpooled.wrappedBuffer(compressionPointer); - DefaultDnsRawRecord cnameRecord = null; - DefaultDnsRawRecord nsRecord = null; - try { - cnameRecord = (DefaultDnsRawRecord) decoder.decodeRecord( - "netty.github.io", DnsRecordType.CNAME, DnsRecord.CLASS_IN, 60, buffer, 10, 2); - assertEquals(0, ByteBufUtil.compare(buffer.duplicate().setIndex(0, 10), cnameRecord.content()), - "The rdata of CNAME-type record should be decompressed in advance"); - assertEquals("netty.io.", DnsCodecUtil.decodeDomainName(cnameRecord.content())); - nsRecord = (DefaultDnsRawRecord) decoder.decodeRecord( - "netty.github.io", DnsRecordType.NS, DnsRecord.CLASS_IN, 60, buffer, 10, 2); - assertEquals(0, ByteBufUtil.compare(buffer.duplicate().setIndex(0, 10), nsRecord.content()), - "The rdata of NS-type record should be decompressed in advance"); - assertEquals("netty.io.", DnsCodecUtil.decodeDomainName(nsRecord.content())); - } finally { - buffer.release(); - if (cnameRecord != null) { - cnameRecord.release(); - } - - if (nsRecord != null) { - nsRecord.release(); - } - } - } - - @Test - public void testDecodeMessageCompression() throws Exception { - // See https://www.ietf.org/rfc/rfc1035 [4.1.4. Message compression] - DefaultDnsRecordDecoder decoder = new DefaultDnsRecordDecoder(); - byte[] rfcExample = { 1, 'F', 3, 'I', 'S', 'I', 4, 'A', 'R', 'P', 'A', - 0, 3, 'F', 'O', 'O', - (byte) 0xC0, 0, // this is 20 in the example - (byte) 0xC0, 6, // this is 26 in the example - }; - DefaultDnsRawRecord rawPlainRecord = null; - DefaultDnsRawRecord rawUncompressedRecord = null; - DefaultDnsRawRecord rawUncompressedIndexedRecord = null; - ByteBuf buffer = Unpooled.wrappedBuffer(rfcExample); - try { - // First lets test that our utility function can correctly handle index references and decompression. - String plainName = DefaultDnsRecordDecoder.decodeName(buffer.duplicate()); - assertEquals("F.ISI.ARPA.", plainName); - String uncompressedPlainName = DefaultDnsRecordDecoder.decodeName(buffer.duplicate().setIndex(16, 20)); - assertEquals(plainName, uncompressedPlainName); - String uncompressedIndexedName = DefaultDnsRecordDecoder.decodeName(buffer.duplicate().setIndex(12, 20)); - assertEquals("FOO." + plainName, uncompressedIndexedName); - - // Now lets make sure out object parsing produces the same results for non PTR type (just use CNAME). - rawPlainRecord = (DefaultDnsRawRecord) decoder.decodeRecord( - plainName, DnsRecordType.CNAME, DnsRecord.CLASS_IN, 60, buffer, 0, 11); - assertEquals(plainName, rawPlainRecord.name()); - assertEquals(plainName, DefaultDnsRecordDecoder.decodeName(rawPlainRecord.content())); - - rawUncompressedRecord = (DefaultDnsRawRecord) decoder.decodeRecord( - uncompressedPlainName, DnsRecordType.CNAME, DnsRecord.CLASS_IN, 60, buffer, 16, 4); - assertEquals(uncompressedPlainName, rawUncompressedRecord.name()); - assertEquals(uncompressedPlainName, DefaultDnsRecordDecoder.decodeName(rawUncompressedRecord.content())); - - rawUncompressedIndexedRecord = (DefaultDnsRawRecord) decoder.decodeRecord( - uncompressedIndexedName, DnsRecordType.CNAME, DnsRecord.CLASS_IN, 60, buffer, 12, 8); - assertEquals(uncompressedIndexedName, rawUncompressedIndexedRecord.name()); - assertEquals(uncompressedIndexedName, - DefaultDnsRecordDecoder.decodeName(rawUncompressedIndexedRecord.content())); - - // Now lets make sure out object parsing produces the same results for PTR type. - DnsPtrRecord ptrRecord = (DnsPtrRecord) decoder.decodeRecord( - plainName, DnsRecordType.PTR, DnsRecord.CLASS_IN, 60, buffer, 0, 11); - assertEquals(plainName, ptrRecord.name()); - assertEquals(plainName, ptrRecord.hostname()); - - ptrRecord = (DnsPtrRecord) decoder.decodeRecord( - uncompressedPlainName, DnsRecordType.PTR, DnsRecord.CLASS_IN, 60, buffer, 16, 4); - assertEquals(uncompressedPlainName, ptrRecord.name()); - assertEquals(uncompressedPlainName, ptrRecord.hostname()); - - ptrRecord = (DnsPtrRecord) decoder.decodeRecord( - uncompressedIndexedName, DnsRecordType.PTR, DnsRecord.CLASS_IN, 60, buffer, 12, 8); - assertEquals(uncompressedIndexedName, ptrRecord.name()); - assertEquals(uncompressedIndexedName, ptrRecord.hostname()); - } finally { - if (rawPlainRecord != null) { - rawPlainRecord.release(); - } - if (rawUncompressedRecord != null) { - rawUncompressedRecord.release(); - } - if (rawUncompressedIndexedRecord != null) { - rawUncompressedIndexedRecord.release(); - } - buffer.release(); - } - } - - @Test - public void testTruncatedPacket() throws Exception { - ByteBuf buffer = Unpooled.buffer(); - buffer.writeByte(0); - buffer.writeShort(DnsRecordType.A.intValue()); - buffer.writeShort(1); - buffer.writeInt(32); - - // Write a truncated last value. - buffer.writeByte(0); - DefaultDnsRecordDecoder decoder = new DefaultDnsRecordDecoder(); - try { - int readerIndex = buffer.readerIndex(); - assertNull(decoder.decodeRecord(buffer)); - assertEquals(readerIndex, buffer.readerIndex()); - } finally { - buffer.release(); - } - } -} diff --git a/codec-dns/src/test/java/io/netty/handler/codec/dns/DefaultDnsRecordEncoderTest.java b/codec-dns/src/test/java/io/netty/handler/codec/dns/DefaultDnsRecordEncoderTest.java deleted file mode 100644 index 7d3d333757..0000000000 --- a/codec-dns/src/test/java/io/netty/handler/codec/dns/DefaultDnsRecordEncoderTest.java +++ /dev/null @@ -1,151 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.socket.InternetProtocolFamily; -import io.netty.util.internal.SocketUtils; -import io.netty.util.internal.StringUtil; -import org.junit.jupiter.api.Test; - -import java.net.InetAddress; -import java.util.concurrent.ThreadLocalRandom; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class DefaultDnsRecordEncoderTest { - - @Test - public void testEncodeName() throws Exception { - testEncodeName(new byte[] { 5, 'n', 'e', 't', 't', 'y', 2, 'i', 'o', 0 }, "netty.io."); - } - - @Test - public void testEncodeNameWithoutTerminator() throws Exception { - testEncodeName(new byte[] { 5, 'n', 'e', 't', 't', 'y', 2, 'i', 'o', 0 }, "netty.io"); - } - - @Test - public void testEncodeNameWithExtraTerminator() throws Exception { - testEncodeName(new byte[] { 5, 'n', 'e', 't', 't', 'y', 2, 'i', 'o', 0 }, "netty.io.."); - } - - // Test for https://github.com/netty/netty/issues/5014 - @Test - public void testEncodeEmptyName() throws Exception { - testEncodeName(new byte[] { 0 }, StringUtil.EMPTY_STRING); - } - - @Test - public void testEncodeRootName() throws Exception { - testEncodeName(new byte[] { 0 }, "."); - } - - private static void testEncodeName(byte[] expected, String name) throws Exception { - DefaultDnsRecordEncoder encoder = new DefaultDnsRecordEncoder(); - ByteBuf out = Unpooled.buffer(); - ByteBuf expectedBuf = Unpooled.wrappedBuffer(expected); - try { - encoder.encodeName(name, out); - assertEquals(expectedBuf, out); - } finally { - out.release(); - expectedBuf.release(); - } - } - - @Test - public void testOptEcsRecordIpv4() throws Exception { - testOptEcsRecordIp(SocketUtils.addressByName("1.2.3.4")); - testOptEcsRecordIp(SocketUtils.addressByName("1.2.3.255")); - } - - @Test - public void testOptEcsRecordIpv6() throws Exception { - testOptEcsRecordIp(SocketUtils.addressByName("::0")); - testOptEcsRecordIp(SocketUtils.addressByName("::FF")); - } - - private static void testOptEcsRecordIp(InetAddress address) throws Exception { - int addressBits = address.getAddress().length * Byte.SIZE; - for (int i = 0; i <= addressBits; ++i) { - testIp(address, i); - } - } - - private static void testIp(InetAddress address, int prefix) throws Exception { - int lowOrderBitsToPreserve = prefix % Byte.SIZE; - - ByteBuf addressPart = Unpooled.wrappedBuffer(address.getAddress(), 0, - DefaultDnsRecordEncoder.calculateEcsAddressLength(prefix, lowOrderBitsToPreserve)); - - if (lowOrderBitsToPreserve > 0) { - // Pad the leftover of the last byte with zeros. - int idx = addressPart.writerIndex() - 1; - byte lastByte = addressPart.getByte(idx); - int paddingMask = -1 << 8 - lowOrderBitsToPreserve; - addressPart.setByte(idx, lastByte & paddingMask); - } - - int payloadSize = nextInt(Short.MAX_VALUE); - int extendedRcode = nextInt(Byte.MAX_VALUE * 2); // Unsigned - int version = nextInt(Byte.MAX_VALUE * 2); // Unsigned - - DefaultDnsRecordEncoder encoder = new DefaultDnsRecordEncoder(); - ByteBuf out = Unpooled.buffer(); - try { - DnsOptEcsRecord record = new DefaultDnsOptEcsRecord( - payloadSize, extendedRcode, version, prefix, address.getAddress()); - encoder.encodeRecord(record, out); - - assertEquals(0, out.readByte()); // Name - assertEquals(DnsRecordType.OPT.intValue(), out.readUnsignedShort()); // Opt - assertEquals(payloadSize, out.readUnsignedShort()); // payload - assertEquals(record.timeToLive(), out.getUnsignedInt(out.readerIndex())); - - // Read unpacked TTL. - assertEquals(extendedRcode, out.readUnsignedByte()); - assertEquals(version, out.readUnsignedByte()); - assertEquals(extendedRcode, record.extendedRcode()); - assertEquals(version, record.version()); - assertEquals(0, record.flags()); - - assertEquals(0, out.readShort()); - - int payloadLength = out.readUnsignedShort(); - assertEquals(payloadLength, out.readableBytes()); - - assertEquals(8, out.readShort()); // As defined by RFC. - - int rdataLength = out.readUnsignedShort(); - assertEquals(rdataLength, out.readableBytes()); - - assertEquals((short) InternetProtocolFamily.of(address).addressNumber(), out.readShort()); - - assertEquals(prefix, out.readUnsignedByte()); - assertEquals(0, out.readUnsignedByte()); // This must be 0 for requests. - assertEquals(addressPart, out); - } finally { - addressPart.release(); - out.release(); - } - } - - private static int nextInt(int max) { - return ThreadLocalRandom.current().nextInt(max); - } -} diff --git a/codec-dns/src/test/java/io/netty/handler/codec/dns/DnsQueryTest.java b/codec-dns/src/test/java/io/netty/handler/codec/dns/DnsQueryTest.java deleted file mode 100644 index 272a62be3e..0000000000 --- a/codec-dns/src/test/java/io/netty/handler/codec/dns/DnsQueryTest.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.channel.embedded.EmbeddedChannel; - -import io.netty.channel.socket.DatagramPacket; -import io.netty.util.internal.SocketUtils; -import org.junit.jupiter.api.Test; - -import java.net.InetSocketAddress; -import java.util.ArrayList; -import java.util.List; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class DnsQueryTest { - - @Test - public void testEncodeAndDecodeQuery() { - InetSocketAddress addr = SocketUtils.socketAddress("8.8.8.8", 53); - EmbeddedChannel writeChannel = new EmbeddedChannel(new DatagramDnsQueryEncoder()); - EmbeddedChannel readChannel = new EmbeddedChannel(new DatagramDnsQueryDecoder()); - - List queries = new ArrayList<>(5); - queries.add(new DatagramDnsQuery(null, addr, 1).setRecord( - DnsSection.QUESTION, - new DefaultDnsQuestion("1.0.0.127.in-addr.arpa", DnsRecordType.PTR))); - queries.add(new DatagramDnsQuery(null, addr, 1).setRecord( - DnsSection.QUESTION, - new DefaultDnsQuestion("www.example.com", DnsRecordType.A))); - queries.add(new DatagramDnsQuery(null, addr, 1).setRecord( - DnsSection.QUESTION, - new DefaultDnsQuestion("example.com", DnsRecordType.AAAA))); - queries.add(new DatagramDnsQuery(null, addr, 1).setRecord( - DnsSection.QUESTION, - new DefaultDnsQuestion("example.com", DnsRecordType.MX))); - queries.add(new DatagramDnsQuery(null, addr, 1).setRecord( - DnsSection.QUESTION, - new DefaultDnsQuestion("example.com", DnsRecordType.CNAME))); - - for (DnsQuery query: queries) { - assertThat(query.count(DnsSection.QUESTION), is(1)); - assertThat(query.count(DnsSection.ANSWER), is(0)); - assertThat(query.count(DnsSection.AUTHORITY), is(0)); - assertThat(query.count(DnsSection.ADDITIONAL), is(0)); - - assertTrue(writeChannel.writeOutbound(query)); - - DatagramPacket packet = writeChannel.readOutbound(); - assertTrue(packet.content().isReadable()); - assertTrue(readChannel.writeInbound(packet)); - assertEquals(query, readChannel.readInbound()); - assertNull(writeChannel.readOutbound()); - assertNull(readChannel.readInbound()); - } - - assertFalse(writeChannel.finish()); - assertFalse(readChannel.finish()); - } -} diff --git a/codec-dns/src/test/java/io/netty/handler/codec/dns/DnsRecordTypeTest.java b/codec-dns/src/test/java/io/netty/handler/codec/dns/DnsRecordTypeTest.java deleted file mode 100644 index 2a74834ad7..0000000000 --- a/codec-dns/src/test/java/io/netty/handler/codec/dns/DnsRecordTypeTest.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import org.junit.jupiter.api.Test; - -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertSame; - -public class DnsRecordTypeTest { - - private static List allTypes() throws Exception { - List result = new ArrayList<>(); - for (Field field : DnsRecordType.class.getFields()) { - if ((field.getModifiers() & Modifier.STATIC) != 0 && field.getType() == DnsRecordType.class) { - result.add((DnsRecordType) field.get(null)); - } - } - assertFalse(result.isEmpty()); - return result; - } - - @Test - public void testSanity() throws Exception { - assertEquals(allTypes().size(), new HashSet<>(allTypes()).size(), - "More than one type has the same int value"); - } - - /** - * Test of hashCode method, of class DnsRecordType. - */ - @Test - public void testHashCode() throws Exception { - for (DnsRecordType t : allTypes()) { - assertEquals(t.intValue(), t.hashCode()); - } - } - - /** - * Test of equals method, of class DnsRecordType. - */ - @Test - public void testEquals() throws Exception { - for (DnsRecordType t1 : allTypes()) { - for (DnsRecordType t2 : allTypes()) { - if (t1 != t2) { - assertNotEquals(t1, t2); - } - } - } - } - - /** - * Test of find method, of class DnsRecordType. - */ - @Test - public void testFind() throws Exception { - for (DnsRecordType t : allTypes()) { - DnsRecordType found = DnsRecordType.valueOf(t.intValue()); - assertSame(t, found); - found = DnsRecordType.valueOf(t.name()); - assertSame(t, found, t.name()); - } - } -} diff --git a/codec-dns/src/test/java/io/netty/handler/codec/dns/DnsResponseTest.java b/codec-dns/src/test/java/io/netty/handler/codec/dns/DnsResponseTest.java deleted file mode 100644 index 79f405f2ce..0000000000 --- a/codec-dns/src/test/java/io/netty/handler/codec/dns/DnsResponseTest.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.AddressedEnvelope; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.channel.socket.DatagramPacket; -import io.netty.handler.codec.CorruptedFrameException; -import org.junit.jupiter.api.Test; - -import java.net.InetSocketAddress; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; - -public class DnsResponseTest { - - private static final byte[][] packets = { - { - 0, 1, -127, -128, 0, 1, 0, 1, 0, 0, 0, 0, 3, 119, 119, 119, 7, 101, 120, 97, 109, 112, 108, 101, 3, - 99, 111, 109, 0, 0, 1, 0, 1, -64, 12, 0, 1, 0, 1, 0, 0, 16, -113, 0, 4, -64, 0, 43, 10 - }, - { - 0, 1, -127, -128, 0, 1, 0, 1, 0, 0, 0, 0, 3, 119, 119, 119, 7, 101, 120, 97, 109, 112, 108, 101, 3, - 99, 111, 109, 0, 0, 28, 0, 1, -64, 12, 0, 28, 0, 1, 0, 0, 69, -8, 0, 16, 32, 1, 5, 0, 0, -120, 2, - 0, 0, 0, 0, 0, 0, 0, 0, 16 - }, - { - 0, 2, -127, -128, 0, 1, 0, 0, 0, 1, 0, 0, 3, 119, 119, 119, 7, 101, 120, 97, 109, 112, 108, 101, 3, - 99, 111, 109, 0, 0, 15, 0, 1, -64, 16, 0, 6, 0, 1, 0, 0, 3, -43, 0, 45, 3, 115, 110, 115, 3, 100, - 110, 115, 5, 105, 99, 97, 110, 110, 3, 111, 114, 103, 0, 3, 110, 111, 99, -64, 49, 119, -4, 39, - 112, 0, 0, 28, 32, 0, 0, 14, 16, 0, 18, 117, 0, 0, 0, 14, 16 - }, - { - 0, 3, -127, -128, 0, 1, 0, 1, 0, 0, 0, 0, 3, 119, 119, 119, 7, 101, 120, 97, 109, 112, 108, 101, 3, - 99, 111, 109, 0, 0, 16, 0, 1, -64, 12, 0, 16, 0, 1, 0, 0, 84, 75, 0, 12, 11, 118, 61, 115, 112, - 102, 49, 32, 45, 97, 108, 108 - }, - { - -105, 19, -127, 0, 0, 1, 0, 0, 0, 13, 0, 0, 2, 104, 112, 11, 116, 105, 109, 98, 111, 117, 100, 114, - 101, 97, 117, 3, 111, 114, 103, 0, 0, 1, 0, 1, 0, 0, 2, 0, 1, 0, 7, -23, 0, 0, 20, 1, 68, 12, 82, - 79, 79, 84, 45, 83, 69, 82, 86, 69, 82, 83, 3, 78, 69, 84, 0, 0, 0, 2, 0, 1, 0, 7, -23, 0, 0, 4, 1, - 70, -64, 49, 0, 0, 2, 0, 1, 0, 7, -23, 0, 0, 4, 1, 69, -64, 49, 0, 0, 2, 0, 1, 0, 7, -23, 0, 0, 4, - 1, 75, -64, 49, 0, 0, 2, 0, 1, 0, 7, -23, 0, 0, 4, 1, 67, -64, 49, 0, 0, 2, 0, 1, 0, 7, -23, 0, 0, - 4, 1, 76, -64, 49, 0, 0, 2, 0, 1, 0, 7, -23, 0, 0, 4, 1, 71, -64, 49, 0, 0, 2, 0, 1, 0, 7, -23, 0, - 0, 4, 1, 73, -64, 49, 0, 0, 2, 0, 1, 0, 7, -23, 0, 0, 4, 1, 66, -64, 49, 0, 0, 2, 0, 1, 0, 7, -23, - 0, 0, 4, 1, 77, -64, 49, 0, 0, 2, 0, 1, 0, 7, -23, 0, 0, 4, 1, 65, -64, 49, 0, 0, 2, 0, 1, 0, 7, - -23, 0, 0, 4, 1, 72, -64, 49, 0, 0, 2, 0, 1, 0, 7, -23, 0, 0, 4, 1, 74, -64, 49 - } - }; - - private static final byte[] malformedLoopPacket = { - 0, 4, -127, -128, 0, 1, 0, 0, 0, 0, 0, 0, -64, 12, 0, 1, 0, 1 - }; - - @Test - public void readResponseTest() throws Exception { - EmbeddedChannel embedder = new EmbeddedChannel(new DatagramDnsResponseDecoder()); - for (byte[] p: packets) { - ByteBuf packet = embedder.alloc().buffer(512).writeBytes(p); - embedder.writeInbound(new DatagramPacket(packet, null, new InetSocketAddress(0))); - AddressedEnvelope envelope = embedder.readInbound(); - assertThat(envelope, is(instanceOf(DatagramDnsResponse.class))); - DnsResponse response = envelope.content(); - assertThat(response, is(sameInstance((Object) envelope))); - - ByteBuf raw = Unpooled.wrappedBuffer(p); - assertThat(response.id(), is(raw.getUnsignedShort(0))); - assertThat(response.count(DnsSection.QUESTION), is(raw.getUnsignedShort(4))); - assertThat(response.count(DnsSection.ANSWER), is(raw.getUnsignedShort(6))); - assertThat(response.count(DnsSection.AUTHORITY), is(raw.getUnsignedShort(8))); - assertThat(response.count(DnsSection.ADDITIONAL), is(raw.getUnsignedShort(10))); - - envelope.release(); - } - assertFalse(embedder.finish()); - } - - @Test - public void readMalformedResponseTest() throws Exception { - EmbeddedChannel embedder = new EmbeddedChannel(new DatagramDnsResponseDecoder()); - ByteBuf packet = embedder.alloc().buffer(512).writeBytes(malformedLoopPacket); - try { - assertThrows(CorruptedFrameException.class, - () -> embedder.writeInbound(new DatagramPacket(packet, null, new InetSocketAddress(0)))); - } finally { - assertFalse(embedder.finish()); - } - } - - @Test - public void readIncompleteResponseTest() { - EmbeddedChannel embedder = new EmbeddedChannel(new DatagramDnsResponseDecoder()); - ByteBuf packet = embedder.alloc().buffer(512); - try { - assertThrows(CorruptedFrameException.class, - () -> embedder.writeInbound(new DatagramPacket(packet, null, new InetSocketAddress(0)))); - } finally { - assertFalse(embedder.finish()); - } - } -} diff --git a/codec-dns/src/test/java/io/netty/handler/codec/dns/TcpDnsTest.java b/codec-dns/src/test/java/io/netty/handler/codec/dns/TcpDnsTest.java deleted file mode 100644 index 318521ebc4..0000000000 --- a/codec-dns/src/test/java/io/netty/handler/codec/dns/TcpDnsTest.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.dns; - -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.ReferenceCountUtil; -import org.hamcrest.Matchers; -import org.junit.jupiter.api.Test; - -import java.util.Random; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class TcpDnsTest { - private static final String QUERY_DOMAIN = "www.example.com"; - private static final long TTL = 600; - private static final byte[] QUERY_RESULT = new byte[]{(byte) 192, (byte) 168, 1, 1}; - - @Test - public void testQueryDecode() { - EmbeddedChannel channel = new EmbeddedChannel(new TcpDnsQueryDecoder()); - - int randomID = new Random().nextInt(60000 - 1000) + 1000; - DnsQuery query = new DefaultDnsQuery(randomID, DnsOpCode.QUERY) - .setRecord(DnsSection.QUESTION, new DefaultDnsQuestion(QUERY_DOMAIN, DnsRecordType.A)); - assertTrue(channel.writeInbound(query)); - - DnsQuery readQuery = channel.readInbound(); - assertThat(readQuery, is(query)); - assertThat(readQuery.recordAt(DnsSection.QUESTION).name(), is(query.recordAt(DnsSection.QUESTION).name())); - readQuery.release(); - assertFalse(channel.finish()); - } - - @Test - public void testResponseEncode() { - EmbeddedChannel channel = new EmbeddedChannel(new TcpDnsResponseEncoder()); - - int randomID = new Random().nextInt(60000 - 1000) + 1000; - DnsQuery query = new DefaultDnsQuery(randomID, DnsOpCode.QUERY) - .setRecord(DnsSection.QUESTION, new DefaultDnsQuestion(QUERY_DOMAIN, DnsRecordType.A)); - - DnsQuestion question = query.recordAt(DnsSection.QUESTION); - channel.writeInbound(newResponse(query, question, QUERY_RESULT)); - - DnsResponse readResponse = channel.readInbound(); - assertThat(readResponse.recordAt(DnsSection.QUESTION), is((DnsRecord) question)); - DnsRawRecord record = new DefaultDnsRawRecord(question.name(), - DnsRecordType.A, TTL, Unpooled.wrappedBuffer(QUERY_RESULT)); - assertThat(readResponse.recordAt(DnsSection.ANSWER), is((DnsRecord) record)); - assertThat(readResponse.recordAt(DnsSection.ANSWER).content(), is(record.content())); - ReferenceCountUtil.release(readResponse); - ReferenceCountUtil.release(record); - query.release(); - assertFalse(channel.finish()); - } - - private static DefaultDnsResponse newResponse(DnsQuery query, DnsQuestion question, byte[]... addresses) { - DefaultDnsResponse response = new DefaultDnsResponse(query.id()); - response.addRecord(DnsSection.QUESTION, question); - - for (byte[] address : addresses) { - DefaultDnsRawRecord queryAnswer = new DefaultDnsRawRecord(question.name(), - DnsRecordType.A, TTL, Unpooled.wrappedBuffer(address)); - response.addRecord(DnsSection.ANSWER, queryAnswer); - } - return response; - } -} diff --git a/codec-haproxy/pom.xml b/codec-haproxy/pom.xml deleted file mode 100644 index d641927639..0000000000 --- a/codec-haproxy/pom.xml +++ /dev/null @@ -1,53 +0,0 @@ - - - - - 4.0.0 - - io.netty - netty-parent - 5.0.0.Final-SNAPSHOT - - - netty-codec-haproxy - jar - - Netty/Codec/HAProxy - - - io.netty.codec.haproxy - - - - - ${project.groupId} - netty-buffer - ${project.version} - - - ${project.groupId} - netty-transport - ${project.version} - - - ${project.groupId} - netty-codec - ${project.version} - - - - diff --git a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyCommand.java b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyCommand.java deleted file mode 100644 index 4fc7566e95..0000000000 --- a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyCommand.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.haproxy; - -/** - * The command of an HAProxy proxy protocol header - */ -public enum HAProxyCommand { - /** - * The LOCAL command represents a connection that was established on purpose by the proxy - * without being relayed. - */ - LOCAL(HAProxyConstants.COMMAND_LOCAL_BYTE), - /** - * The PROXY command represents a connection that was established on behalf of another node, - * and reflects the original connection endpoints. - */ - PROXY(HAProxyConstants.COMMAND_PROXY_BYTE); - - /** - * The command is specified in the lowest 4 bits of the protocol version and command byte - */ - private static final byte COMMAND_MASK = 0x0f; - - private final byte byteValue; - - /** - * Creates a new instance - */ - HAProxyCommand(byte byteValue) { - this.byteValue = byteValue; - } - - /** - * Returns the {@link HAProxyCommand} represented by the lowest 4 bits of the specified byte. - * - * @param verCmdByte protocol version and command byte - */ - public static HAProxyCommand valueOf(byte verCmdByte) { - int cmd = verCmdByte & COMMAND_MASK; - switch ((byte) cmd) { - case HAProxyConstants.COMMAND_PROXY_BYTE: - return PROXY; - case HAProxyConstants.COMMAND_LOCAL_BYTE: - return LOCAL; - default: - throw new IllegalArgumentException("unknown command: " + cmd); - } - } - - /** - * Returns the byte value of this command. - */ - public byte byteValue() { - return byteValue; - } -} diff --git a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyConstants.java b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyConstants.java deleted file mode 100644 index 436adec57e..0000000000 --- a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyConstants.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.haproxy; - -final class HAProxyConstants { - - /** - * Command byte constants - */ - static final byte COMMAND_LOCAL_BYTE = 0x00; - static final byte COMMAND_PROXY_BYTE = 0x01; - - /** - * Version byte constants - */ - static final byte VERSION_ONE_BYTE = 0x10; - static final byte VERSION_TWO_BYTE = 0x20; - - /** - * Transport protocol byte constants - */ - static final byte TRANSPORT_UNSPEC_BYTE = 0x00; - static final byte TRANSPORT_STREAM_BYTE = 0x01; - static final byte TRANSPORT_DGRAM_BYTE = 0x02; - - /** - * Address family byte constants - */ - static final byte AF_UNSPEC_BYTE = 0x00; - static final byte AF_IPV4_BYTE = 0x10; - static final byte AF_IPV6_BYTE = 0x20; - static final byte AF_UNIX_BYTE = 0x30; - - /** - * Transport protocol and address family byte constants - */ - static final byte TPAF_UNKNOWN_BYTE = 0x00; - static final byte TPAF_TCP4_BYTE = 0x11; - static final byte TPAF_TCP6_BYTE = 0x21; - static final byte TPAF_UDP4_BYTE = 0x12; - static final byte TPAF_UDP6_BYTE = 0x22; - static final byte TPAF_UNIX_STREAM_BYTE = 0x31; - static final byte TPAF_UNIX_DGRAM_BYTE = 0x32; - - /** - * V2 protocol binary header prefix - */ - static final byte[] BINARY_PREFIX = { - (byte) 0x0D, - (byte) 0x0A, - (byte) 0x0D, - (byte) 0x0A, - (byte) 0x00, - (byte) 0x0D, - (byte) 0x0A, - (byte) 0x51, - (byte) 0x55, - (byte) 0x49, - (byte) 0x54, - (byte) 0x0A - }; - - static final byte[] TEXT_PREFIX = { - (byte) 'P', - (byte) 'R', - (byte) 'O', - (byte) 'X', - (byte) 'Y', - }; - - private HAProxyConstants() { } -} diff --git a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyMessage.java b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyMessage.java deleted file mode 100644 index eefdbe607a..0000000000 --- a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyMessage.java +++ /dev/null @@ -1,629 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.haproxy; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.haproxy.HAProxyProxiedProtocol.AddressFamily; -import io.netty.util.AbstractReferenceCounted; -import io.netty.util.ByteProcessor; -import io.netty.util.CharsetUtil; -import io.netty.util.NetUtil; -import io.netty.util.ResourceLeakDetector; -import io.netty.util.ResourceLeakDetectorFactory; -import io.netty.util.ResourceLeakTracker; -import io.netty.util.internal.StringUtil; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * Message container for decoded HAProxy proxy protocol parameters - */ -public final class HAProxyMessage extends AbstractReferenceCounted { - private static final ResourceLeakDetector leakDetector = - ResourceLeakDetectorFactory.instance().newResourceLeakDetector(HAProxyMessage.class); - - private final ResourceLeakTracker leak; - private final HAProxyProtocolVersion protocolVersion; - private final HAProxyCommand command; - private final HAProxyProxiedProtocol proxiedProtocol; - private final String sourceAddress; - private final String destinationAddress; - private final int sourcePort; - private final int destinationPort; - private final List tlvs; - - /** - * Creates a new instance - */ - private HAProxyMessage( - HAProxyProtocolVersion protocolVersion, HAProxyCommand command, HAProxyProxiedProtocol proxiedProtocol, - String sourceAddress, String destinationAddress, String sourcePort, String destinationPort) { - this( - protocolVersion, command, proxiedProtocol, - sourceAddress, destinationAddress, portStringToInt(sourcePort), portStringToInt(destinationPort)); - } - - /** - * Creates a new instance of HAProxyMessage. - * @param protocolVersion the protocol version. - * @param command the command. - * @param proxiedProtocol the protocol containing the address family and transport protocol. - * @param sourceAddress the source address. - * @param destinationAddress the destination address. - * @param sourcePort the source port. This value must be 0 for unix, unspec addresses. - * @param destinationPort the destination port. This value must be 0 for unix, unspec addresses. - */ - public HAProxyMessage( - HAProxyProtocolVersion protocolVersion, HAProxyCommand command, HAProxyProxiedProtocol proxiedProtocol, - String sourceAddress, String destinationAddress, int sourcePort, int destinationPort) { - - this(protocolVersion, command, proxiedProtocol, - sourceAddress, destinationAddress, sourcePort, destinationPort, Collections.emptyList()); - } - - /** - * Creates a new instance of HAProxyMessage. - * @param protocolVersion the protocol version. - * @param command the command. - * @param proxiedProtocol the protocol containing the address family and transport protocol. - * @param sourceAddress the source address. - * @param destinationAddress the destination address. - * @param sourcePort the source port. This value must be 0 for unix, unspec addresses. - * @param destinationPort the destination port. This value must be 0 for unix, unspec addresses. - * @param tlvs the list of tlvs. - */ - public HAProxyMessage( - HAProxyProtocolVersion protocolVersion, HAProxyCommand command, HAProxyProxiedProtocol proxiedProtocol, - String sourceAddress, String destinationAddress, int sourcePort, int destinationPort, - List tlvs) { - requireNonNull(protocolVersion, "protocolVersion"); - requireNonNull(proxiedProtocol, "proxiedProtocol"); - requireNonNull(tlvs, "tlvs"); - AddressFamily addrFamily = proxiedProtocol.addressFamily(); - - checkAddress(sourceAddress, addrFamily); - checkAddress(destinationAddress, addrFamily); - checkPort(sourcePort, addrFamily); - checkPort(destinationPort, addrFamily); - - this.protocolVersion = protocolVersion; - this.command = command; - this.proxiedProtocol = proxiedProtocol; - this.sourceAddress = sourceAddress; - this.destinationAddress = destinationAddress; - this.sourcePort = sourcePort; - this.destinationPort = destinationPort; - this.tlvs = Collections.unmodifiableList(tlvs); - - leak = leakDetector.track(this); - } - - /** - * Decodes a version 2, binary proxy protocol header. - * - * @param header a version 2 proxy protocol header - * @return {@link HAProxyMessage} instance - * @throws HAProxyProtocolException if any portion of the header is invalid - */ - static HAProxyMessage decodeHeader(ByteBuf header) { - requireNonNull(header, "header"); - - if (header.readableBytes() < 16) { - throw new HAProxyProtocolException( - "incomplete header: " + header.readableBytes() + " bytes (expected: 16+ bytes)"); - } - - // Per spec, the 13th byte is the protocol version and command byte - header.skipBytes(12); - final byte verCmdByte = header.readByte(); - - HAProxyProtocolVersion ver; - try { - ver = HAProxyProtocolVersion.valueOf(verCmdByte); - } catch (IllegalArgumentException e) { - throw new HAProxyProtocolException(e); - } - - if (ver != HAProxyProtocolVersion.V2) { - throw new HAProxyProtocolException("version 1 unsupported: 0x" + Integer.toHexString(verCmdByte)); - } - - HAProxyCommand cmd; - try { - cmd = HAProxyCommand.valueOf(verCmdByte); - } catch (IllegalArgumentException e) { - throw new HAProxyProtocolException(e); - } - - if (cmd == HAProxyCommand.LOCAL) { - return unknownMsg(HAProxyProtocolVersion.V2, HAProxyCommand.LOCAL); - } - - // Per spec, the 14th byte is the protocol and address family byte - HAProxyProxiedProtocol protAndFam; - try { - protAndFam = HAProxyProxiedProtocol.valueOf(header.readByte()); - } catch (IllegalArgumentException e) { - throw new HAProxyProtocolException(e); - } - - if (protAndFam == HAProxyProxiedProtocol.UNKNOWN) { - return unknownMsg(HAProxyProtocolVersion.V2, HAProxyCommand.PROXY); - } - - int addressInfoLen = header.readUnsignedShort(); - - String srcAddress; - String dstAddress; - int addressLen; - int srcPort = 0; - int dstPort = 0; - - AddressFamily addressFamily = protAndFam.addressFamily(); - - if (addressFamily == AddressFamily.AF_UNIX) { - // unix sockets require 216 bytes for address information - if (addressInfoLen < 216 || header.readableBytes() < 216) { - throw new HAProxyProtocolException( - "incomplete UNIX socket address information: " + - Math.min(addressInfoLen, header.readableBytes()) + " bytes (expected: 216+ bytes)"); - } - int startIdx = header.readerIndex(); - int addressEnd = header.forEachByte(startIdx, 108, ByteProcessor.FIND_NUL); - if (addressEnd == -1) { - addressLen = 108; - } else { - addressLen = addressEnd - startIdx; - } - srcAddress = header.toString(startIdx, addressLen, CharsetUtil.US_ASCII); - - startIdx += 108; - - addressEnd = header.forEachByte(startIdx, 108, ByteProcessor.FIND_NUL); - if (addressEnd == -1) { - addressLen = 108; - } else { - addressLen = addressEnd - startIdx; - } - dstAddress = header.toString(startIdx, addressLen, CharsetUtil.US_ASCII); - // AF_UNIX defines that exactly 108 bytes are reserved for the address. The previous methods - // did not increase the reader index although we already consumed the information. - header.readerIndex(startIdx + 108); - } else { - if (addressFamily == AddressFamily.AF_IPv4) { - // IPv4 requires 12 bytes for address information - if (addressInfoLen < 12 || header.readableBytes() < 12) { - throw new HAProxyProtocolException( - "incomplete IPv4 address information: " + - Math.min(addressInfoLen, header.readableBytes()) + " bytes (expected: 12+ bytes)"); - } - addressLen = 4; - } else if (addressFamily == AddressFamily.AF_IPv6) { - // IPv6 requires 36 bytes for address information - if (addressInfoLen < 36 || header.readableBytes() < 36) { - throw new HAProxyProtocolException( - "incomplete IPv6 address information: " + - Math.min(addressInfoLen, header.readableBytes()) + " bytes (expected: 36+ bytes)"); - } - addressLen = 16; - } else { - throw new HAProxyProtocolException( - "unable to parse address information (unknown address family: " + addressFamily + ')'); - } - - // Per spec, the src address begins at the 17th byte - srcAddress = ipBytesToString(header, addressLen); - dstAddress = ipBytesToString(header, addressLen); - srcPort = header.readUnsignedShort(); - dstPort = header.readUnsignedShort(); - } - - final List tlvs = readTlvs(header); - - return new HAProxyMessage(ver, cmd, protAndFam, srcAddress, dstAddress, srcPort, dstPort, tlvs); - } - - private static List readTlvs(final ByteBuf header) { - HAProxyTLV haProxyTLV = readNextTLV(header); - if (haProxyTLV == null) { - return Collections.emptyList(); - } - // In most cases there are less than 4 TLVs available - List haProxyTLVs = new ArrayList<>(4); - - do { - haProxyTLVs.add(haProxyTLV); - if (haProxyTLV instanceof HAProxySSLTLV) { - haProxyTLVs.addAll(((HAProxySSLTLV) haProxyTLV).encapsulatedTLVs()); - } - } while ((haProxyTLV = readNextTLV(header)) != null); - return haProxyTLVs; - } - - private static HAProxyTLV readNextTLV(final ByteBuf header) { - - // We need at least 4 bytes for a TLV - if (header.readableBytes() < 4) { - return null; - } - - final byte typeAsByte = header.readByte(); - final HAProxyTLV.Type type = HAProxyTLV.Type.typeForByteValue(typeAsByte); - - final int length = header.readUnsignedShort(); - switch (type) { - case PP2_TYPE_SSL: - final ByteBuf rawContent = header.retainedSlice(header.readerIndex(), length); - final ByteBuf byteBuf = header.readSlice(length); - final byte client = byteBuf.readByte(); - final int verify = byteBuf.readInt(); - - if (byteBuf.readableBytes() >= 4) { - - final List encapsulatedTlvs = new ArrayList<>(4); - do { - final HAProxyTLV haProxyTLV = readNextTLV(byteBuf); - if (haProxyTLV == null) { - break; - } - encapsulatedTlvs.add(haProxyTLV); - } while (byteBuf.readableBytes() >= 4); - - return new HAProxySSLTLV(verify, client, encapsulatedTlvs, rawContent); - } - return new HAProxySSLTLV(verify, client, Collections.emptyList(), rawContent); - // If we're not dealing with an SSL Type, we can use the same mechanism - case PP2_TYPE_ALPN: - case PP2_TYPE_AUTHORITY: - case PP2_TYPE_SSL_VERSION: - case PP2_TYPE_SSL_CN: - case PP2_TYPE_NETNS: - case OTHER: - return new HAProxyTLV(type, typeAsByte, header.readRetainedSlice(length)); - default: - return null; - } - } - - /** - * Decodes a version 1, human-readable proxy protocol header. - * - * @param header a version 1 proxy protocol header - * @return {@link HAProxyMessage} instance - * @throws HAProxyProtocolException if any portion of the header is invalid - */ - static HAProxyMessage decodeHeader(String header) { - if (header == null) { - throw new HAProxyProtocolException("header"); - } - - String[] parts = header.split(" "); - int numParts = parts.length; - - if (numParts < 2) { - throw new HAProxyProtocolException( - "invalid header: " + header + " (expected: 'PROXY' and proxied protocol values)"); - } - - if (!"PROXY".equals(parts[0])) { - throw new HAProxyProtocolException("unknown identifier: " + parts[0]); - } - - HAProxyProxiedProtocol protAndFam; - try { - protAndFam = HAProxyProxiedProtocol.valueOf(parts[1]); - } catch (IllegalArgumentException e) { - throw new HAProxyProtocolException(e); - } - - if (protAndFam != HAProxyProxiedProtocol.TCP4 && - protAndFam != HAProxyProxiedProtocol.TCP6 && - protAndFam != HAProxyProxiedProtocol.UNKNOWN) { - throw new HAProxyProtocolException("unsupported v1 proxied protocol: " + parts[1]); - } - - if (protAndFam == HAProxyProxiedProtocol.UNKNOWN) { - return unknownMsg(HAProxyProtocolVersion.V1, HAProxyCommand.PROXY); - } - - if (numParts != 6) { - throw new HAProxyProtocolException("invalid TCP4/6 header: " + header + " (expected: 6 parts)"); - } - - try { - return new HAProxyMessage( - HAProxyProtocolVersion.V1, HAProxyCommand.PROXY, - protAndFam, parts[2], parts[3], parts[4], parts[5]); - } catch (RuntimeException e) { - throw new HAProxyProtocolException("invalid HAProxy message", e); - } - } - - /** - * Proxy protocol message for 'UNKNOWN' proxied protocols. Per spec, when the proxied protocol is - * 'UNKNOWN' we must discard all other header values. - */ - private static HAProxyMessage unknownMsg(HAProxyProtocolVersion version, HAProxyCommand command) { - return new HAProxyMessage(version, command, HAProxyProxiedProtocol.UNKNOWN, null, null, 0, 0); - } - - /** - * Convert ip address bytes to string representation - * - * @param header buffer containing ip address bytes - * @param addressLen number of bytes to read (4 bytes for IPv4, 16 bytes for IPv6) - * @return string representation of the ip address - */ - private static String ipBytesToString(ByteBuf header, int addressLen) { - StringBuilder sb = new StringBuilder(); - final int ipv4Len = 4; - final int ipv6Len = 8; - if (addressLen == ipv4Len) { - for (int i = 0; i < ipv4Len; i++) { - sb.append(header.readByte() & 0xff); - sb.append('.'); - } - } else { - for (int i = 0; i < ipv6Len; i++) { - sb.append(Integer.toHexString(header.readUnsignedShort())); - sb.append(':'); - } - } - sb.setLength(sb.length() - 1); - return sb.toString(); - } - - /** - * Convert port to integer - * - * @param value the port - * @return port as an integer - * @throws IllegalArgumentException if port is not a valid integer - */ - private static int portStringToInt(String value) { - int port; - try { - port = Integer.parseInt(value); - } catch (NumberFormatException e) { - throw new IllegalArgumentException("invalid port: " + value, e); - } - - if (port <= 0 || port > 65535) { - throw new IllegalArgumentException("invalid port: " + value + " (expected: 1 ~ 65535)"); - } - - return port; - } - - /** - * Validate an address (IPv4, IPv6, Unix Socket) - * - * @param address human-readable address - * @param addrFamily the {@link AddressFamily} to check the address against - * @throws IllegalArgumentException if the address is invalid - */ - private static void checkAddress(String address, AddressFamily addrFamily) { - requireNonNull(addrFamily, "addrFamily"); - - switch (addrFamily) { - case AF_UNSPEC: - if (address != null) { - throw new IllegalArgumentException("unable to validate an AF_UNSPEC address: " + address); - } - return; - case AF_UNIX: - requireNonNull(address, "address"); - if (address.getBytes(CharsetUtil.US_ASCII).length > 108) { - throw new IllegalArgumentException("invalid AF_UNIX address: " + address); - } - return; - } - - requireNonNull(address, "address"); - - switch (addrFamily) { - case AF_IPv4: - if (!NetUtil.isValidIpV4Address(address)) { - throw new IllegalArgumentException("invalid IPv4 address: " + address); - } - break; - case AF_IPv6: - if (!NetUtil.isValidIpV6Address(address)) { - throw new IllegalArgumentException("invalid IPv6 address: " + address); - } - break; - default: - throw new IllegalArgumentException("unexpected addrFamily: " + addrFamily); - } - } - - /** - * Validate the port depending on the addrFamily. - * - * @param port the UDP/TCP port - * @throws IllegalArgumentException if the port is out of range (0-65535 inclusive) - */ - private static void checkPort(int port, AddressFamily addrFamily) { - switch (addrFamily) { - case AF_IPv6: - case AF_IPv4: - if (port < 0 || port > 65535) { - throw new IllegalArgumentException("invalid port: " + port + " (expected: 0 ~ 65535)"); - } - break; - case AF_UNIX: - case AF_UNSPEC: - if (port != 0) { - throw new IllegalArgumentException("port cannot be specified with addrFamily: " + addrFamily); - } - break; - default: - throw new IllegalArgumentException("unexpected addrFamily: " + addrFamily); - } - } - - /** - * Returns the {@link HAProxyProtocolVersion} of this {@link HAProxyMessage}. - */ - public HAProxyProtocolVersion protocolVersion() { - return protocolVersion; - } - - /** - * Returns the {@link HAProxyCommand} of this {@link HAProxyMessage}. - */ - public HAProxyCommand command() { - return command; - } - - /** - * Returns the {@link HAProxyProxiedProtocol} of this {@link HAProxyMessage}. - */ - public HAProxyProxiedProtocol proxiedProtocol() { - return proxiedProtocol; - } - - /** - * Returns the human-readable source address of this {@link HAProxyMessage} or {@code null} - * if HAProxy performs health check with {@code send-proxy-v2}. - */ - public String sourceAddress() { - return sourceAddress; - } - - /** - * Returns the human-readable destination address of this {@link HAProxyMessage}. - */ - public String destinationAddress() { - return destinationAddress; - } - - /** - * Returns the UDP/TCP source port of this {@link HAProxyMessage}. - */ - public int sourcePort() { - return sourcePort; - } - - /** - * Returns the UDP/TCP destination port of this {@link HAProxyMessage}. - */ - public int destinationPort() { - return destinationPort; - } - - /** - * Returns a list of {@link HAProxyTLV} or an empty list if no TLVs are present. - *

- * TLVs are only available for the Proxy Protocol V2 - */ - public List tlvs() { - return tlvs; - } - - int tlvNumBytes() { - int tlvNumBytes = 0; - for (int i = 0; i < tlvs.size(); i++) { - tlvNumBytes += tlvs.get(i).totalNumBytes(); - } - return tlvNumBytes; - } - - @Override - public HAProxyMessage touch() { - tryRecord(); - return (HAProxyMessage) super.touch(); - } - - @Override - public HAProxyMessage touch(Object hint) { - if (leak != null) { - leak.record(hint); - } - return this; - } - - @Override - public HAProxyMessage retain() { - tryRecord(); - return (HAProxyMessage) super.retain(); - } - - @Override - public HAProxyMessage retain(int increment) { - tryRecord(); - return (HAProxyMessage) super.retain(increment); - } - - @Override - public boolean release() { - tryRecord(); - return super.release(); - } - - @Override - public boolean release(int decrement) { - tryRecord(); - return super.release(decrement); - } - - private void tryRecord() { - if (leak != null) { - leak.record(); - } - } - - @Override - protected void deallocate() { - try { - for (HAProxyTLV tlv : tlvs) { - tlv.release(); - } - } finally { - final ResourceLeakTracker leak = this.leak; - if (leak != null) { - boolean closed = leak.close(this); - assert closed; - } - } - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(256) - .append(StringUtil.simpleClassName(this)) - .append("(protocolVersion: ").append(protocolVersion) - .append(", command: ").append(command) - .append(", proxiedProtocol: ").append(proxiedProtocol) - .append(", sourceAddress: ").append(sourceAddress) - .append(", destinationAddress: ").append(destinationAddress) - .append(", sourcePort: ").append(sourcePort) - .append(", destinationPort: ").append(destinationPort) - .append(", tlvs: ["); - if (!tlvs.isEmpty()) { - for (HAProxyTLV tlv: tlvs) { - sb.append(tlv).append(", "); - } - sb.setLength(sb.length() - 2); - } - sb.append("])"); - return sb.toString(); - } -} diff --git a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyMessageDecoder.java b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyMessageDecoder.java deleted file mode 100644 index 0b49143269..0000000000 --- a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyMessageDecoder.java +++ /dev/null @@ -1,469 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.haproxy; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.ByteToMessageDecoder; -import io.netty.handler.codec.ProtocolDetectionResult; -import io.netty.util.CharsetUtil; - - -import static io.netty.handler.codec.haproxy.HAProxyConstants.*; - -/** - * Decodes an HAProxy proxy protocol header - * - * @see Proxy Protocol Specification - */ -public class HAProxyMessageDecoder extends ByteToMessageDecoder { - /** - * Maximum possible length of a v1 proxy protocol header per spec - */ - private static final int V1_MAX_LENGTH = 108; - - /** - * Maximum possible length of a v2 proxy protocol header (fixed 16 bytes + max unsigned short) - */ - private static final int V2_MAX_LENGTH = 16 + 65535; - - /** - * Minimum possible length of a fully functioning v2 proxy protocol header (fixed 16 bytes + v2 address info space) - */ - private static final int V2_MIN_LENGTH = 16 + 216; - - /** - * Maximum possible length for v2 additional TLV data (max unsigned short - max v2 address info space) - */ - private static final int V2_MAX_TLV = 65535 - 216; - - /** - * Binary header prefix length - */ - private static final int BINARY_PREFIX_LENGTH = BINARY_PREFIX.length; - - /** - * {@link ProtocolDetectionResult} for {@link HAProxyProtocolVersion#V1}. - */ - private static final ProtocolDetectionResult DETECTION_RESULT_V1 = - ProtocolDetectionResult.detected(HAProxyProtocolVersion.V1); - - /** - * {@link ProtocolDetectionResult} for {@link HAProxyProtocolVersion#V2}. - */ - private static final ProtocolDetectionResult DETECTION_RESULT_V2 = - ProtocolDetectionResult.detected(HAProxyProtocolVersion.V2); - - /** - * Used to extract a header frame out of the {@link ByteBuf} and return it. - */ - private HeaderExtractor headerExtractor; - - /** - * {@code true} if we're discarding input because we're already over maxLength - */ - private boolean discarding; - - /** - * Number of discarded bytes - */ - private int discardedBytes; - - /** - * Whether or not to throw an exception as soon as we exceed maxLength. - */ - private final boolean failFast; - - /** - * {@code true} if we're finished decoding the proxy protocol header - */ - private boolean finished; - - /** - * Protocol specification version - */ - private int version = -1; - - /** - * The latest v2 spec (2014/05/18) allows for additional data to be sent in the proxy protocol header beyond the - * address information block so now we need a configurable max header size - */ - private final int v2MaxHeaderSize; - - /** - * Creates a new decoder with no additional data (TLV) restrictions, and should throw an exception as soon as - * we exceed maxLength. - */ - public HAProxyMessageDecoder() { - this(true); - } - - /** - * Creates a new decoder with no additional data (TLV) restrictions, whether or not to throw an exception as soon - * as we exceed maxLength. - * - * @param failFast Whether or not to throw an exception as soon as we exceed maxLength - */ - public HAProxyMessageDecoder(boolean failFast) { - v2MaxHeaderSize = V2_MAX_LENGTH; - this.failFast = failFast; - } - - /** - * Creates a new decoder with restricted additional data (TLV) size, and should throw an exception as soon as - * we exceed maxLength. - *

- * Note: limiting TLV size only affects processing of v2, binary headers. Also, as allowed by the 1.5 spec - * TLV data is currently ignored. For maximum performance it would be best to configure your upstream proxy host to - * NOT send TLV data and instantiate with a max TLV size of {@code 0}. - *

- * - * @param maxTlvSize maximum number of bytes allowed for additional data (Type-Length-Value vectors) in a v2 header - */ - public HAProxyMessageDecoder(int maxTlvSize) { - this(maxTlvSize, true); - } - - /** - * Creates a new decoder with restricted additional data (TLV) size, whether or not to throw an exception as soon - * as we exceed maxLength. - * - * @param maxTlvSize maximum number of bytes allowed for additional data (Type-Length-Value vectors) in a v2 header - * @param failFast Whether or not to throw an exception as soon as we exceed maxLength - */ - public HAProxyMessageDecoder(int maxTlvSize, boolean failFast) { - if (maxTlvSize < 1) { - v2MaxHeaderSize = V2_MIN_LENGTH; - } else if (maxTlvSize > V2_MAX_TLV) { - v2MaxHeaderSize = V2_MAX_LENGTH; - } else { - int calcMax = maxTlvSize + V2_MIN_LENGTH; - if (calcMax > V2_MAX_LENGTH) { // lgtm[java/constant-comparison] - v2MaxHeaderSize = V2_MAX_LENGTH; - } else { - v2MaxHeaderSize = calcMax; - } - } - this.failFast = failFast; - } - - /** - * Returns the proxy protocol specification version in the buffer if the version is found. - * Returns -1 if no version was found in the buffer. - */ - private static int findVersion(final ByteBuf buffer) { - final int n = buffer.readableBytes(); - // per spec, the version number is found in the 13th byte - if (n < 13) { - return -1; - } - - int idx = buffer.readerIndex(); - return match(BINARY_PREFIX, buffer, idx) ? buffer.getByte(idx + BINARY_PREFIX_LENGTH) : 1; - } - - /** - * Returns the index in the buffer of the end of header if found. - * Returns -1 if no end of header was found in the buffer. - */ - private static int findEndOfHeader(final ByteBuf buffer) { - final int n = buffer.readableBytes(); - - // per spec, the 15th and 16th bytes contain the address length in bytes - if (n < 16) { - return -1; - } - - int offset = buffer.readerIndex() + 14; - - // the total header length will be a fixed 16 byte sequence + the dynamic address information block - int totalHeaderBytes = 16 + buffer.getUnsignedShort(offset); - - // ensure we actually have the full header available - if (n >= totalHeaderBytes) { - return totalHeaderBytes; - } else { - return -1; - } - } - - /** - * Returns the index in the buffer of the end of line found. - * Returns -1 if no end of line was found in the buffer. - */ - private static int findEndOfLine(final ByteBuf buffer) { - final int n = buffer.writerIndex(); - for (int i = buffer.readerIndex(); i < n; i++) { - final byte b = buffer.getByte(i); - if (b == '\r' && i < n - 1 && buffer.getByte(i + 1) == '\n') { - return i; // \r\n - } - } - return -1; // Not found. - } - - @Override - public boolean isSingleDecode() { - // ByteToMessageDecoder uses this method to optionally break out of the decoding loop after each unit of work. - // Since we only ever want to decode a single header we always return true to save a bit of work here. - return true; - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - super.channelRead(ctx, msg); - if (finished) { - ctx.pipeline().remove(this); - } - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - ctx.fireExceptionCaught(cause); - if (cause instanceof HAProxyProtocolException) { - ctx.close(); // drop connection immediately per spec - } - } - - @Override - protected final void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - // determine the specification version - if (version == -1) { - if ((version = findVersion(in)) == -1) { - return; - } - } - - ByteBuf decoded; - - if (version == 1) { - decoded = decodeLine(ctx, in); - } else { - decoded = decodeStruct(ctx, in); - } - - if (decoded != null) { - finished = true; - try { - if (version == 1) { - ctx.fireChannelRead(HAProxyMessage.decodeHeader(decoded.toString(CharsetUtil.US_ASCII))); - } else { - ctx.fireChannelRead(HAProxyMessage.decodeHeader(decoded)); - } - } catch (HAProxyProtocolException e) { - fail(ctx, null, e); - } - } - } - - /** - * Create a frame out of the {@link ByteBuf} and return it. - * - * @param ctx the {@link ChannelHandlerContext} which this {@link HAProxyMessageDecoder} belongs to - * @param buffer the {@link ByteBuf} from which to read data - * @return frame the {@link ByteBuf} which represent the frame or {@code null} if no frame could - * be created - */ - private ByteBuf decodeStruct(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception { - if (headerExtractor == null) { - headerExtractor = new StructHeaderExtractor(v2MaxHeaderSize); - } - return headerExtractor.extract(ctx, buffer); - } - - /** - * Create a frame out of the {@link ByteBuf} and return it. - * - * @param ctx the {@link ChannelHandlerContext} which this {@link HAProxyMessageDecoder} belongs to - * @param buffer the {@link ByteBuf} from which to read data - * @return frame the {@link ByteBuf} which represent the frame or {@code null} if no frame could - * be created - */ - private ByteBuf decodeLine(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception { - if (headerExtractor == null) { - headerExtractor = new LineHeaderExtractor(V1_MAX_LENGTH); - } - return headerExtractor.extract(ctx, buffer); - } - - private void failOverLimit(final ChannelHandlerContext ctx, int length) { - failOverLimit(ctx, String.valueOf(length)); - } - - private void failOverLimit(final ChannelHandlerContext ctx, String length) { - int maxLength = version == 1 ? V1_MAX_LENGTH : v2MaxHeaderSize; - fail(ctx, "header length (" + length + ") exceeds the allowed maximum (" + maxLength + ')', null); - } - - private void fail(final ChannelHandlerContext ctx, String errMsg, Exception e) { - finished = true; - HAProxyProtocolException ppex; - if (errMsg != null && e != null) { - ppex = new HAProxyProtocolException(errMsg, e); - } else if (errMsg != null) { - ppex = new HAProxyProtocolException(errMsg); - } else if (e != null) { - ppex = new HAProxyProtocolException(e); - } else { - ppex = new HAProxyProtocolException(); - } - throw ppex; - } - - /** - * Returns the {@link ProtocolDetectionResult} for the given {@link ByteBuf}. - */ - public static ProtocolDetectionResult detectProtocol(ByteBuf buffer) { - if (buffer.readableBytes() < 12) { - return ProtocolDetectionResult.needsMoreData(); - } - - int idx = buffer.readerIndex(); - - if (match(BINARY_PREFIX, buffer, idx)) { - return DETECTION_RESULT_V2; - } - if (match(TEXT_PREFIX, buffer, idx)) { - return DETECTION_RESULT_V1; - } - return ProtocolDetectionResult.invalid(); - } - - private static boolean match(byte[] prefix, ByteBuf buffer, int idx) { - for (int i = 0; i < prefix.length; i++) { - final byte b = buffer.getByte(idx + i); - if (b != prefix[i]) { - return false; - } - } - return true; - } - - /** - * HeaderExtractor create a header frame out of the {@link ByteBuf}. - */ - private abstract class HeaderExtractor { - /** Header max size */ - private final int maxHeaderSize; - - protected HeaderExtractor(int maxHeaderSize) { - this.maxHeaderSize = maxHeaderSize; - } - - /** - * Create a frame out of the {@link ByteBuf} and return it. - * - * @param ctx the {@link ChannelHandlerContext} which this {@link HAProxyMessageDecoder} belongs to - * @param buffer the {@link ByteBuf} from which to read data - * @return frame the {@link ByteBuf} which represent the frame or {@code null} if no frame could - * be created - * @throws Exception if exceed maxLength - */ - public ByteBuf extract(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception { - final int eoh = findEndOfHeader(buffer); - if (!discarding) { - if (eoh >= 0) { - final int length = eoh - buffer.readerIndex(); - if (length > maxHeaderSize) { - buffer.readerIndex(eoh + delimiterLength(buffer, eoh)); - failOverLimit(ctx, length); - return null; - } - ByteBuf frame = buffer.readSlice(length); - buffer.skipBytes(delimiterLength(buffer, eoh)); - return frame; - } else { - final int length = buffer.readableBytes(); - if (length > maxHeaderSize) { - discardedBytes = length; - buffer.skipBytes(length); - discarding = true; - if (failFast) { - failOverLimit(ctx, "over " + discardedBytes); - } - } - return null; - } - } else { - if (eoh >= 0) { - final int length = discardedBytes + eoh - buffer.readerIndex(); - buffer.readerIndex(eoh + delimiterLength(buffer, eoh)); - discardedBytes = 0; - discarding = false; - if (!failFast) { - failOverLimit(ctx, "over " + length); - } - } else { - discardedBytes += buffer.readableBytes(); - buffer.skipBytes(buffer.readableBytes()); - } - return null; - } - } - - /** - * Find the end of the header from the given {@link ByteBuf}īŧŒthe end may be a CRLF, or the length given by the - * header. - * - * @param buffer the buffer to be searched - * @return {@code -1} if can not find the end, otherwise return the buffer index of end - */ - protected abstract int findEndOfHeader(ByteBuf buffer); - - /** - * Get the length of the header delimiter. - * - * @param buffer the buffer where delimiter is located - * @param eoh index of delimiter - * @return length of the delimiter - */ - protected abstract int delimiterLength(ByteBuf buffer, int eoh); - } - - private final class LineHeaderExtractor extends HeaderExtractor { - - LineHeaderExtractor(int maxHeaderSize) { - super(maxHeaderSize); - } - - @Override - protected int findEndOfHeader(ByteBuf buffer) { - return findEndOfLine(buffer); - } - - @Override - protected int delimiterLength(ByteBuf buffer, int eoh) { - return buffer.getByte(eoh) == '\r' ? 2 : 1; - } - } - - private final class StructHeaderExtractor extends HeaderExtractor { - - StructHeaderExtractor(int maxHeaderSize) { - super(maxHeaderSize); - } - - @Override - protected int findEndOfHeader(ByteBuf buffer) { - return HAProxyMessageDecoder.findEndOfHeader(buffer); - } - - @Override - protected int delimiterLength(ByteBuf buffer, int eoh) { - return 0; - } - } -} diff --git a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyMessageEncoder.java b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyMessageEncoder.java deleted file mode 100644 index a745bf6e96..0000000000 --- a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyMessageEncoder.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.haproxy; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToByteEncoder; -import io.netty.util.CharsetUtil; -import io.netty.util.NetUtil; - -import java.util.List; - -import static io.netty.handler.codec.haproxy.HAProxyConstants.*; - -/** - * Encodes an HAProxy proxy protocol message - * - * @see Proxy Protocol Specification - */ -@Sharable -public final class HAProxyMessageEncoder extends MessageToByteEncoder { - - private static final int V2_VERSION_BITMASK = 0x02 << 4; - - // Length for source/destination addresses for the UNIX family must be 108 bytes each. - static final int UNIX_ADDRESS_BYTES_LENGTH = 108; - static final int TOTAL_UNIX_ADDRESS_BYTES_LENGTH = UNIX_ADDRESS_BYTES_LENGTH * 2; - - public static final HAProxyMessageEncoder INSTANCE = new HAProxyMessageEncoder(); - - private HAProxyMessageEncoder() { - } - - @Override - protected void encode(ChannelHandlerContext ctx, HAProxyMessage msg, ByteBuf out) throws Exception { - switch (msg.protocolVersion()) { - case V1: - encodeV1(msg, out); - break; - case V2: - encodeV2(msg, out); - break; - default: - throw new HAProxyProtocolException("Unsupported version: " + msg.protocolVersion()); - } - } - - private static void encodeV1(HAProxyMessage msg, ByteBuf out) { - out.writeBytes(TEXT_PREFIX); - out.writeByte((byte) ' '); - out.writeCharSequence(msg.proxiedProtocol().name(), CharsetUtil.US_ASCII); - out.writeByte((byte) ' '); - out.writeCharSequence(msg.sourceAddress(), CharsetUtil.US_ASCII); - out.writeByte((byte) ' '); - out.writeCharSequence(msg.destinationAddress(), CharsetUtil.US_ASCII); - out.writeByte((byte) ' '); - out.writeCharSequence(String.valueOf(msg.sourcePort()), CharsetUtil.US_ASCII); - out.writeByte((byte) ' '); - out.writeCharSequence(String.valueOf(msg.destinationPort()), CharsetUtil.US_ASCII); - out.writeByte((byte) '\r'); - out.writeByte((byte) '\n'); - } - - private static void encodeV2(HAProxyMessage msg, ByteBuf out) { - out.writeBytes(BINARY_PREFIX); - out.writeByte(V2_VERSION_BITMASK | msg.command().byteValue()); - out.writeByte(msg.proxiedProtocol().byteValue()); - - switch (msg.proxiedProtocol().addressFamily()) { - case AF_IPv4: - case AF_IPv6: - byte[] srcAddrBytes = NetUtil.createByteArrayFromIpAddressString(msg.sourceAddress()); - byte[] dstAddrBytes = NetUtil.createByteArrayFromIpAddressString(msg.destinationAddress()); - // srcAddrLen + dstAddrLen + 4 (srcPort + dstPort) + numTlvBytes - out.writeShort(srcAddrBytes.length + dstAddrBytes.length + 4 + msg.tlvNumBytes()); - out.writeBytes(srcAddrBytes); - out.writeBytes(dstAddrBytes); - out.writeShort(msg.sourcePort()); - out.writeShort(msg.destinationPort()); - encodeTlvs(msg.tlvs(), out); - break; - case AF_UNIX: - out.writeShort(TOTAL_UNIX_ADDRESS_BYTES_LENGTH + msg.tlvNumBytes()); - int srcAddrBytesWritten = out.writeCharSequence(msg.sourceAddress(), CharsetUtil.US_ASCII); - out.writeZero(UNIX_ADDRESS_BYTES_LENGTH - srcAddrBytesWritten); - int dstAddrBytesWritten = out.writeCharSequence(msg.destinationAddress(), CharsetUtil.US_ASCII); - out.writeZero(UNIX_ADDRESS_BYTES_LENGTH - dstAddrBytesWritten); - encodeTlvs(msg.tlvs(), out); - break; - case AF_UNSPEC: - out.writeShort(0); - break; - default: - throw new HAProxyProtocolException("unexpected addrFamily"); - } - } - - private static void encodeTlv(HAProxyTLV haProxyTLV, ByteBuf out) { - if (haProxyTLV instanceof HAProxySSLTLV) { - HAProxySSLTLV ssltlv = (HAProxySSLTLV) haProxyTLV; - out.writeByte(haProxyTLV.typeByteValue()); - out.writeShort(ssltlv.contentNumBytes()); - out.writeByte(ssltlv.client()); - out.writeInt(ssltlv.verify()); - encodeTlvs(ssltlv.encapsulatedTLVs(), out); - } else { - out.writeByte(haProxyTLV.typeByteValue()); - ByteBuf value = haProxyTLV.content(); - int readableBytes = value.readableBytes(); - out.writeShort(readableBytes); - out.writeBytes(value.readSlice(readableBytes)); - } - } - - private static void encodeTlvs(List haProxyTLVs, ByteBuf out) { - for (int i = 0; i < haProxyTLVs.size(); i++) { - encodeTlv(haProxyTLVs.get(i), out); - } - } -} diff --git a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyProtocolException.java b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyProtocolException.java deleted file mode 100644 index cedae73600..0000000000 --- a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyProtocolException.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.haproxy; - -import io.netty.handler.codec.DecoderException; - -/** - * A {@link DecoderException} which is thrown when an invalid HAProxy proxy protocol header is encountered - */ -public class HAProxyProtocolException extends DecoderException { - - private static final long serialVersionUID = 713710864325167351L; - - /** - * Creates a new instance - */ - public HAProxyProtocolException() { } - - /** - * Creates a new instance - */ - public HAProxyProtocolException(String message, Throwable cause) { - super(message, cause); - } - - /** - * Creates a new instance - */ - public HAProxyProtocolException(String message) { - super(message); - } - - /** - * Creates a new instance - */ - public HAProxyProtocolException(Throwable cause) { - super(cause); - } -} diff --git a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyProtocolVersion.java b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyProtocolVersion.java deleted file mode 100644 index 3fa23137f4..0000000000 --- a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyProtocolVersion.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.haproxy; - -import static io.netty.handler.codec.haproxy.HAProxyConstants.*; - -/** - * The HAProxy proxy protocol specification version. - */ -public enum HAProxyProtocolVersion { - /** - * The ONE proxy protocol version represents a version 1 (human-readable) header. - */ - V1(VERSION_ONE_BYTE), - /** - * The TWO proxy protocol version represents a version 2 (binary) header. - */ - V2(VERSION_TWO_BYTE); - - /** - * The highest 4 bits of the protocol version and command byte contain the version - */ - private static final byte VERSION_MASK = (byte) 0xf0; - - private final byte byteValue; - - /** - * Creates a new instance - */ - HAProxyProtocolVersion(byte byteValue) { - this.byteValue = byteValue; - } - - /** - * Returns the {@link HAProxyProtocolVersion} represented by the highest 4 bits of the specified byte. - * - * @param verCmdByte protocol version and command byte - */ - public static HAProxyProtocolVersion valueOf(byte verCmdByte) { - int version = verCmdByte & VERSION_MASK; - switch ((byte) version) { - case VERSION_TWO_BYTE: - return V2; - case VERSION_ONE_BYTE: - return V1; - default: - throw new IllegalArgumentException("unknown version: " + version); - } - } - - /** - * Returns the byte value of this version. - */ - public byte byteValue() { - return byteValue; - } -} diff --git a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyProxiedProtocol.java b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyProxiedProtocol.java deleted file mode 100644 index 41e5eb6678..0000000000 --- a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyProxiedProtocol.java +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.haproxy; - -import static io.netty.handler.codec.haproxy.HAProxyConstants.*; - -/** - * A protocol proxied by HAProxy which is represented by its transport protocol and address family. - */ -public enum HAProxyProxiedProtocol { - /** - * The UNKNOWN represents a connection which was forwarded for an unknown protocol and an unknown address family. - */ - UNKNOWN(TPAF_UNKNOWN_BYTE, AddressFamily.AF_UNSPEC, TransportProtocol.UNSPEC), - /** - * The TCP4 represents a connection which was forwarded for an IPv4 client over TCP. - */ - TCP4(TPAF_TCP4_BYTE, AddressFamily.AF_IPv4, TransportProtocol.STREAM), - /** - * The TCP6 represents a connection which was forwarded for an IPv6 client over TCP. - */ - TCP6(TPAF_TCP6_BYTE, AddressFamily.AF_IPv6, TransportProtocol.STREAM), - /** - * The UDP4 represents a connection which was forwarded for an IPv4 client over UDP. - */ - UDP4(TPAF_UDP4_BYTE, AddressFamily.AF_IPv4, TransportProtocol.DGRAM), - /** - * The UDP6 represents a connection which was forwarded for an IPv6 client over UDP. - */ - UDP6(TPAF_UDP6_BYTE, AddressFamily.AF_IPv6, TransportProtocol.DGRAM), - /** - * The UNIX_STREAM represents a connection which was forwarded for a UNIX stream socket. - */ - UNIX_STREAM(TPAF_UNIX_STREAM_BYTE, AddressFamily.AF_UNIX, TransportProtocol.STREAM), - /** - * The UNIX_DGRAM represents a connection which was forwarded for a UNIX datagram socket. - */ - UNIX_DGRAM(TPAF_UNIX_DGRAM_BYTE, AddressFamily.AF_UNIX, TransportProtocol.DGRAM); - - private final byte byteValue; - private final AddressFamily addressFamily; - private final TransportProtocol transportProtocol; - - /** - * Creates a new instance. - */ - HAProxyProxiedProtocol( - byte byteValue, - AddressFamily addressFamily, - TransportProtocol transportProtocol) { - - this.byteValue = byteValue; - this.addressFamily = addressFamily; - this.transportProtocol = transportProtocol; - } - - /** - * Returns the {@link HAProxyProxiedProtocol} represented by the specified byte. - * - * @param tpafByte transport protocol and address family byte - */ - public static HAProxyProxiedProtocol valueOf(byte tpafByte) { - switch (tpafByte) { - case TPAF_TCP4_BYTE: - return TCP4; - case TPAF_TCP6_BYTE: - return TCP6; - case TPAF_UNKNOWN_BYTE: - return UNKNOWN; - case TPAF_UDP4_BYTE: - return UDP4; - case TPAF_UDP6_BYTE: - return UDP6; - case TPAF_UNIX_STREAM_BYTE: - return UNIX_STREAM; - case TPAF_UNIX_DGRAM_BYTE: - return UNIX_DGRAM; - default: - throw new IllegalArgumentException( - "unknown transport protocol + address family: " + (tpafByte & 0xFF)); - } - } - - /** - * Returns the byte value of this protocol and address family. - */ - public byte byteValue() { - return byteValue; - } - - /** - * Returns the {@link AddressFamily} of this protocol and address family. - */ - public AddressFamily addressFamily() { - return addressFamily; - } - - /** - * Returns the {@link TransportProtocol} of this protocol and address family. - */ - public TransportProtocol transportProtocol() { - return transportProtocol; - } - - /** - * The address family of an HAProxy proxy protocol header. - */ - public enum AddressFamily { - /** - * The UNSPECIFIED address family represents a connection which was forwarded for an unknown protocol. - */ - AF_UNSPEC(AF_UNSPEC_BYTE), - /** - * The IPV4 address family represents a connection which was forwarded for an IPV4 client. - */ - AF_IPv4(AF_IPV4_BYTE), - /** - * The IPV6 address family represents a connection which was forwarded for an IPV6 client. - */ - AF_IPv6(AF_IPV6_BYTE), - /** - * The UNIX address family represents a connection which was forwarded for a unix socket. - */ - AF_UNIX(AF_UNIX_BYTE); - - /** - * The highest 4 bits of the transport protocol and address family byte contain the address family - */ - private static final byte FAMILY_MASK = (byte) 0xf0; - - private final byte byteValue; - - /** - * Creates a new instance - */ - AddressFamily(byte byteValue) { - this.byteValue = byteValue; - } - - /** - * Returns the {@link AddressFamily} represented by the highest 4 bits of the specified byte. - * - * @param tpafByte transport protocol and address family byte - */ - public static AddressFamily valueOf(byte tpafByte) { - int addressFamily = tpafByte & FAMILY_MASK; - switch((byte) addressFamily) { - case AF_IPV4_BYTE: - return AF_IPv4; - case AF_IPV6_BYTE: - return AF_IPv6; - case AF_UNSPEC_BYTE: - return AF_UNSPEC; - case AF_UNIX_BYTE: - return AF_UNIX; - default: - throw new IllegalArgumentException("unknown address family: " + addressFamily); - } - } - - /** - * Returns the byte value of this address family. - */ - public byte byteValue() { - return byteValue; - } - } - - /** - * The transport protocol of an HAProxy proxy protocol header - */ - public enum TransportProtocol { - /** - * The UNSPEC transport protocol represents a connection which was forwarded for an unknown protocol. - */ - UNSPEC(TRANSPORT_UNSPEC_BYTE), - /** - * The STREAM transport protocol represents a connection which was forwarded for a TCP connection. - */ - STREAM(TRANSPORT_STREAM_BYTE), - /** - * The DGRAM transport protocol represents a connection which was forwarded for a UDP connection. - */ - DGRAM(TRANSPORT_DGRAM_BYTE); - - /** - * The transport protocol is specified in the lowest 4 bits of the transport protocol and address family byte - */ - private static final byte TRANSPORT_MASK = 0x0f; - - private final byte transportByte; - - /** - * Creates a new instance. - */ - TransportProtocol(byte transportByte) { - this.transportByte = transportByte; - } - - /** - * Returns the {@link TransportProtocol} represented by the lowest 4 bits of the specified byte. - * - * @param tpafByte transport protocol and address family byte - */ - public static TransportProtocol valueOf(byte tpafByte) { - int transportProtocol = tpafByte & TRANSPORT_MASK; - switch ((byte) transportProtocol) { - case TRANSPORT_STREAM_BYTE: - return STREAM; - case TRANSPORT_UNSPEC_BYTE: - return UNSPEC; - case TRANSPORT_DGRAM_BYTE: - return DGRAM; - default: - throw new IllegalArgumentException("unknown transport protocol: " + transportProtocol); - } - } - - /** - * Returns the byte value of this transport protocol. - */ - public byte byteValue() { - return transportByte; - } - } -} diff --git a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxySSLTLV.java b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxySSLTLV.java deleted file mode 100644 index e880769d9d..0000000000 --- a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxySSLTLV.java +++ /dev/null @@ -1,125 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.haproxy; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.internal.StringUtil; - -import java.util.Collections; -import java.util.List; - -/** - * Represents a {@link HAProxyTLV} of the type {@link HAProxyTLV.Type#PP2_TYPE_SSL}. - * This TLV encapsulates other TLVs and has additional information like verification information and a client bitfield. - */ -public final class HAProxySSLTLV extends HAProxyTLV { - - private final int verify; - private final List tlvs; - private final byte clientBitField; - - /** - * Creates a new HAProxySSLTLV - * - * @param verify the verification result as defined in the specification for the pp2_tlv_ssl struct (see - * https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) - * @param clientBitField the bitfield with client information - * @param tlvs the encapsulated {@link HAProxyTLV}s - */ - public HAProxySSLTLV(final int verify, final byte clientBitField, final List tlvs) { - this(verify, clientBitField, tlvs, Unpooled.EMPTY_BUFFER); - } - - /** - * Creates a new HAProxySSLTLV - * - * @param verify the verification result as defined in the specification for the pp2_tlv_ssl struct (see - * https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) - * @param clientBitField the bitfield with client information - * @param tlvs the encapsulated {@link HAProxyTLV}s - * @param rawContent the raw TLV content - */ - HAProxySSLTLV(final int verify, final byte clientBitField, final List tlvs, final ByteBuf rawContent) { - super(Type.PP2_TYPE_SSL, (byte) 0x20, rawContent); - - this.verify = verify; - this.tlvs = Collections.unmodifiableList(tlvs); - this.clientBitField = clientBitField; - } - - /** - * Returns {@code true} if the bit field for PP2_CLIENT_CERT_CONN was set - */ - public boolean isPP2ClientCertConn() { - return (clientBitField & 0x2) != 0; - } - - /** - * Returns {@code true} if the bit field for PP2_CLIENT_SSL was set - */ - public boolean isPP2ClientSSL() { - return (clientBitField & 0x1) != 0; - } - - /** - * Returns {@code true} if the bit field for PP2_CLIENT_CERT_SESS was set - */ - public boolean isPP2ClientCertSess() { - return (clientBitField & 0x4) != 0; - } - - /** - * Returns the client bit field - */ - public byte client() { - return clientBitField; - } - - /** - * Returns the verification result - */ - public int verify() { - return verify; - } - - /** - * Returns an unmodifiable Set of encapsulated {@link HAProxyTLV}s. - */ - public List encapsulatedTLVs() { - return tlvs; - } - - @Override - int contentNumBytes() { - int tlvNumBytes = 0; - for (int i = 0; i < tlvs.size(); i++) { - tlvNumBytes += tlvs.get(i).totalNumBytes(); - } - return 5 + tlvNumBytes; // clientBit(1) + verify(4) + tlvs - } - - @Override - public String toString() { - return StringUtil.simpleClassName(this) + - "(type: " + type() + - ", typeByteValue: " + typeByteValue() + - ", client: " + client() + - ", verify: " + verify() + - ", numEncapsulatedTlvs: " + tlvs.size() + ')'; - } -} diff --git a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyTLV.java b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyTLV.java deleted file mode 100644 index 31bf1fabca..0000000000 --- a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyTLV.java +++ /dev/null @@ -1,218 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.haproxy; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.DefaultByteBufHolder; -import io.netty.util.internal.StringUtil; - -import static java.util.Objects.requireNonNull; - -/** - * A Type-Length Value (TLV vector) that can be added to the PROXY protocol - * to include additional information like SSL information. - * - * @see HAProxySSLTLV - */ -public class HAProxyTLV extends DefaultByteBufHolder { - - private final Type type; - private final byte typeByteValue; - - /** - * The size of this tlv in bytes. - * @return the number of bytes. - */ - int totalNumBytes() { - return 3 + contentNumBytes(); // type(1) + length(2) + content - } - - int contentNumBytes() { - return content().readableBytes(); - } - - /** - * The registered types a TLV can have regarding the PROXY protocol 1.5 spec - */ - public enum Type { - PP2_TYPE_ALPN, - PP2_TYPE_AUTHORITY, - PP2_TYPE_SSL, - PP2_TYPE_SSL_VERSION, - PP2_TYPE_SSL_CN, - PP2_TYPE_NETNS, - /** - * A TLV type that is not officially defined in the spec. May be used for nonstandard TLVs - */ - OTHER; - - /** - * Returns the {@link Type} for a specific byte value as defined in the PROXY protocol 1.5 spec - *

- * If the byte value is not an official one, it will return {@link Type#OTHER}. - * - * @param byteValue the byte for a type - * - * @return the {@link Type} of a TLV - */ - public static Type typeForByteValue(byte byteValue) { - switch (byteValue) { - case 0x01: - return PP2_TYPE_ALPN; - case 0x02: - return PP2_TYPE_AUTHORITY; - case 0x20: - return PP2_TYPE_SSL; - case 0x21: - return PP2_TYPE_SSL_VERSION; - case 0x22: - return PP2_TYPE_SSL_CN; - case 0x30: - return PP2_TYPE_NETNS; - default: - return OTHER; - } - } - - /** - * Returns the byte value for the {@link Type} as defined in the PROXY protocol 1.5 spec. - * - * @param type the {@link Type} - * - * @return the byte value of the {@link Type}. - */ - public static byte byteValueForType(Type type) { - switch (type) { - case PP2_TYPE_ALPN: - return 0x01; - case PP2_TYPE_AUTHORITY: - return 0x02; - case PP2_TYPE_SSL: - return 0x20; - case PP2_TYPE_SSL_VERSION: - return 0x21; - case PP2_TYPE_SSL_CN: - return 0x22; - case PP2_TYPE_NETNS: - return 0x30; - default: - throw new IllegalArgumentException("unknown type: " + type); - } - } - } - - /** - * Creates a new HAProxyTLV - * - * @param typeByteValue the byteValue of the TLV. This is especially important if non-standard TLVs are used - * @param content the raw content of the TLV - */ - public HAProxyTLV(byte typeByteValue, ByteBuf content) { - this(Type.typeForByteValue(typeByteValue), typeByteValue, content); - } - - /** - * Creates a new HAProxyTLV - * - * @param type the {@link Type} of the TLV - * @param content the raw content of the TLV - */ - public HAProxyTLV(Type type, ByteBuf content) { - this(type, Type.byteValueForType(type), content); - } - - /** - * Creates a new HAProxyTLV - * - * @param type the {@link Type} of the TLV - * @param typeByteValue the byteValue of the TLV. This is especially important if non-standard TLVs are used - * @param content the raw content of the TLV - */ - HAProxyTLV(final Type type, final byte typeByteValue, final ByteBuf content) { - super(content); - requireNonNull(type, "type"); - - this.type = type; - this.typeByteValue = typeByteValue; - } - - /** - * Returns the {@link Type} of this TLV - */ - public Type type() { - return type; - } - - /** - * Returns the type of the TLV as byte - */ - public byte typeByteValue() { - return typeByteValue; - } - - @Override - public HAProxyTLV copy() { - return replace(content().copy()); - } - - @Override - public HAProxyTLV duplicate() { - return replace(content().duplicate()); - } - - @Override - public HAProxyTLV retainedDuplicate() { - return replace(content().retainedDuplicate()); - } - - @Override - public HAProxyTLV replace(ByteBuf content) { - return new HAProxyTLV(type, typeByteValue, content); - } - - @Override - public HAProxyTLV retain() { - super.retain(); - return this; - } - - @Override - public HAProxyTLV retain(int increment) { - super.retain(increment); - return this; - } - - @Override - public HAProxyTLV touch() { - super.touch(); - return this; - } - - @Override - public HAProxyTLV touch(Object hint) { - super.touch(hint); - return this; - } - - @Override - public String toString() { - return StringUtil.simpleClassName(this) + - "(type: " + type() + - ", typeByteValue: " + typeByteValue() + - ", content: " + contentToString() + ')'; - } -} diff --git a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/package-info.java b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/package-info.java deleted file mode 100644 index 64f2c578e8..0000000000 --- a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/package-info.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * Decodes an HAProxy proxy protocol header - * - * @see Proxy Protocol Specification - */ -package io.netty.handler.codec.haproxy; diff --git a/codec-haproxy/src/test/java/io/netty/handler/codec/haproxy/HAProxyIntegrationTest.java b/codec-haproxy/src/test/java/io/netty/handler/codec/haproxy/HAProxyIntegrationTest.java deleted file mode 100644 index 92437f27f3..0000000000 --- a/codec-haproxy/src/test/java/io/netty/handler/codec/haproxy/HAProxyIntegrationTest.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.haproxy; - -import io.netty.bootstrap.Bootstrap; -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.channel.local.LocalAddress; -import io.netty.channel.local.LocalChannel; -import io.netty.channel.local.LocalHandler; -import io.netty.channel.local.LocalServerChannel; -import org.junit.jupiter.api.Test; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class HAProxyIntegrationTest { - - @Test - public void testBasicCase() throws Exception { - final CountDownLatch latch = new CountDownLatch(1); - final AtomicReference msgHolder = new AtomicReference<>(); - LocalAddress localAddress = new LocalAddress("HAProxyIntegrationTest"); - - EventLoopGroup group = new MultithreadEventLoopGroup(LocalHandler.newFactory()); - ServerBootstrap sb = new ServerBootstrap(); - sb.channel(LocalServerChannel.class) - .group(group) - .childHandler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) { - ch.pipeline().addLast(new HAProxyMessageDecoder()); - ch.pipeline().addLast(new SimpleChannelInboundHandler() { - @Override - protected void messageReceived(ChannelHandlerContext ctx, HAProxyMessage msg) { - msgHolder.set(msg.retain()); - latch.countDown(); - } - }); - } - }); - Channel serverChannel = sb.bind(localAddress).get(); - - Bootstrap b = new Bootstrap(); - Channel clientChannel = b.channel(LocalChannel.class) - .handler(HAProxyMessageEncoder.INSTANCE) - .group(group) - .connect(localAddress).get(); - - try { - HAProxyMessage message = new HAProxyMessage( - HAProxyProtocolVersion.V1, HAProxyCommand.PROXY, HAProxyProxiedProtocol.TCP4, - "192.168.0.1", "192.168.0.11", 56324, 443); - clientChannel.writeAndFlush(message).sync(); - - assertTrue(latch.await(5, TimeUnit.SECONDS)); - HAProxyMessage readMessage = msgHolder.get(); - - assertEquals(message.protocolVersion(), readMessage.protocolVersion()); - assertEquals(message.command(), readMessage.command()); - assertEquals(message.proxiedProtocol(), readMessage.proxiedProtocol()); - assertEquals(message.sourceAddress(), readMessage.sourceAddress()); - assertEquals(message.destinationAddress(), readMessage.destinationAddress()); - assertEquals(message.sourcePort(), readMessage.sourcePort()); - assertEquals(message.destinationPort(), readMessage.destinationPort()); - - readMessage.release(); - } finally { - clientChannel.close().sync(); - serverChannel.close().sync(); - group.shutdownGracefully().sync(); - } - } -} diff --git a/codec-haproxy/src/test/java/io/netty/handler/codec/haproxy/HAProxyMessageDecoderTest.java b/codec-haproxy/src/test/java/io/netty/handler/codec/haproxy/HAProxyMessageDecoderTest.java deleted file mode 100644 index da7aa8c3af..0000000000 --- a/codec-haproxy/src/test/java/io/netty/handler/codec/haproxy/HAProxyMessageDecoderTest.java +++ /dev/null @@ -1,1120 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.haproxy; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.ProtocolDetectionResult; -import io.netty.handler.codec.ProtocolDetectionState; -import io.netty.handler.codec.haproxy.HAProxyProxiedProtocol.AddressFamily; -import io.netty.handler.codec.haproxy.HAProxyProxiedProtocol.TransportProtocol; -import io.netty.util.CharsetUtil; -import io.netty.util.concurrent.Future; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.List; - -import static io.netty.buffer.Unpooled.buffer; -import static io.netty.buffer.Unpooled.copiedBuffer; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - -public class HAProxyMessageDecoderTest { - private EmbeddedChannel ch; - - @BeforeEach - public void setUp() { - ch = new EmbeddedChannel(new HAProxyMessageDecoder()); - } - - @Test - public void testIPV4Decode() { - int startChannels = ch.pipeline().names().size(); - String header = "PROXY TCP4 192.168.0.1 192.168.0.11 56324 443\r\n"; - ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII)); - Object msgObj = ch.readInbound(); - assertEquals(startChannels - 1, ch.pipeline().names().size()); - assertTrue(msgObj instanceof HAProxyMessage); - HAProxyMessage msg = (HAProxyMessage) msgObj; - assertEquals(HAProxyProtocolVersion.V1, msg.protocolVersion()); - assertEquals(HAProxyCommand.PROXY, msg.command()); - assertEquals(HAProxyProxiedProtocol.TCP4, msg.proxiedProtocol()); - assertEquals("192.168.0.1", msg.sourceAddress()); - assertEquals("192.168.0.11", msg.destinationAddress()); - assertEquals(56324, msg.sourcePort()); - assertEquals(443, msg.destinationPort()); - assertNull(ch.readInbound()); - assertFalse(ch.finish()); - assertTrue(msg.release()); - } - - @Test - public void testIPV6Decode() { - int startChannels = ch.pipeline().names().size(); - String header = "PROXY TCP6 2001:0db8:85a3:0000:0000:8a2e:0370:7334 1050:0:0:0:5:600:300c:326b 56324 443\r\n"; - ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII)); - Object msgObj = ch.readInbound(); - assertEquals(startChannels - 1, ch.pipeline().names().size()); - assertTrue(msgObj instanceof HAProxyMessage); - HAProxyMessage msg = (HAProxyMessage) msgObj; - assertEquals(HAProxyProtocolVersion.V1, msg.protocolVersion()); - assertEquals(HAProxyCommand.PROXY, msg.command()); - assertEquals(HAProxyProxiedProtocol.TCP6, msg.proxiedProtocol()); - assertEquals("2001:0db8:85a3:0000:0000:8a2e:0370:7334", msg.sourceAddress()); - assertEquals("1050:0:0:0:5:600:300c:326b", msg.destinationAddress()); - assertEquals(56324, msg.sourcePort()); - assertEquals(443, msg.destinationPort()); - assertNull(ch.readInbound()); - assertFalse(ch.finish()); - assertTrue(msg.release()); - } - - @Test - public void testUnknownProtocolDecode() { - int startChannels = ch.pipeline().names().size(); - String header = "PROXY UNKNOWN 192.168.0.1 192.168.0.11 56324 443\r\n"; - ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII)); - Object msgObj = ch.readInbound(); - assertEquals(startChannels - 1, ch.pipeline().names().size()); - assertTrue(msgObj instanceof HAProxyMessage); - HAProxyMessage msg = (HAProxyMessage) msgObj; - assertEquals(HAProxyProtocolVersion.V1, msg.protocolVersion()); - assertEquals(HAProxyCommand.PROXY, msg.command()); - assertEquals(HAProxyProxiedProtocol.UNKNOWN, msg.proxiedProtocol()); - assertNull(msg.sourceAddress()); - assertNull(msg.destinationAddress()); - assertEquals(0, msg.sourcePort()); - assertEquals(0, msg.destinationPort()); - assertNull(ch.readInbound()); - assertFalse(ch.finish()); - assertTrue(msg.release()); - } - - @Test - public void testV1NoUDP() { - String header = "PROXY UDP4 192.168.0.1 192.168.0.11 56324 443\r\n"; - assertThrows(HAProxyProtocolException.class, - () -> ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII))); - } - - @Test - public void testInvalidPort() { - String header = "PROXY TCP4 192.168.0.1 192.168.0.11 80000 443\r\n"; - assertThrows(HAProxyProtocolException.class, - () -> ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII))); - } - - @Test - public void testInvalidIPV4Address() { - String header = "PROXY TCP4 299.168.0.1 192.168.0.11 56324 443\r\n"; - assertThrows(HAProxyProtocolException.class, - () -> ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII))); - } - - @Test - public void testInvalidIPV6Address() { - String header = "PROXY TCP6 r001:0db8:85a3:0000:0000:8a2e:0370:7334 1050:0:0:0:5:600:300c:326b 56324 443\r\n"; - assertThrows(HAProxyProtocolException.class, - () -> ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII))); - } - - @Test - public void testInvalidProtocol() { - String header = "PROXY TCP7 192.168.0.1 192.168.0.11 56324 443\r\n"; - assertThrows(HAProxyProtocolException.class, - () -> ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII))); - } - - @Test - public void testMissingParams() { - String header = "PROXY TCP4 192.168.0.1 192.168.0.11 56324\r\n"; - assertThrows(HAProxyProtocolException.class, - () -> ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII))); - } - - @Test - public void testTooManyParams() { - String header = "PROXY TCP4 192.168.0.1 192.168.0.11 56324 443 123\r\n"; - assertThrows(HAProxyProtocolException.class, - () -> ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII))); - } - - @Test - public void testInvalidCommand() { - String header = "PING TCP4 192.168.0.1 192.168.0.11 56324 443\r\n"; - assertThrows(HAProxyProtocolException.class, - () -> ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII))); - } - - @Test - public void testInvalidEOL() { - String header = "PROXY TCP4 192.168.0.1 192.168.0.11 56324 443\nGET / HTTP/1.1\r\n"; - assertThrows(HAProxyProtocolException.class, - () -> ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII))); - } - - @Test - public void testHeaderTooLong() { - String header = "PROXY TCP4 192.168.0.1 192.168.0.11 56324 " + - "00000000000000000000000000000000000000000000000000000000000000000443\r\n"; - assertThrows(HAProxyProtocolException.class, - () -> ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII))); - } - - @Test - public void testFailSlowHeaderTooLong() { - EmbeddedChannel slowFailCh = new EmbeddedChannel(new HAProxyMessageDecoder(false)); - try { - String headerPart1 = "PROXY TCP4 192.168.0.1 192.168.0.11 56324 " + - "000000000000000000000000000000000000000000000000000000000000000000000443"; - // Should not throw exception - assertFalse(slowFailCh.writeInbound(copiedBuffer(headerPart1, CharsetUtil.US_ASCII))); - String headerPart2 = "more header data"; - // Should not throw exception - assertFalse(slowFailCh.writeInbound(copiedBuffer(headerPart2, CharsetUtil.US_ASCII))); - String headerPart3 = "end of header\r\n"; - - int discarded = headerPart1.length() + headerPart2.length() + headerPart3.length() - 2; - assertThrows(HAProxyProtocolException.class, - () -> slowFailCh.writeInbound(copiedBuffer(headerPart3, CharsetUtil.US_ASCII)), "over " + discarded); - } finally { - assertFalse(slowFailCh.finishAndReleaseAll()); - } - } - - @Test - public void testFailFastHeaderTooLong() { - EmbeddedChannel fastFailCh = new EmbeddedChannel(new HAProxyMessageDecoder(true)); - try { - String headerPart1 = "PROXY TCP4 192.168.0.1 192.168.0.11 56324 " + - "000000000000000000000000000000000000000000000000000000000000000000000443"; - assertThrows(HAProxyProtocolException.class, - () -> fastFailCh.writeInbound(copiedBuffer(headerPart1, CharsetUtil.US_ASCII)), - "over " + headerPart1.length()); - } finally { - assertFalse(fastFailCh.finishAndReleaseAll()); - } - } - - @Test - public void testIncompleteHeader() { - String header = "PROXY TCP4 192.168.0.1 192.168.0.11 56324"; - ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII)); - assertNull(ch.readInbound()); - assertFalse(ch.finish()); - } - - @Test - public void testCloseOnInvalid() { - Future closeFuture = ch.closeFuture(); - String header = "GET / HTTP/1.1\r\n"; - try { - ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII)); - } catch (HAProxyProtocolException ppex) { - // swallow this exception since we're just testing to be sure the channel was closed - } - boolean isComplete = closeFuture.awaitUninterruptibly(5000); - if (!isComplete || !closeFuture.isDone() || closeFuture.isFailed()) { - fail("Expected channel close"); - } - } - - @Test - public void testTransportProtocolAndAddressFamily() { - final byte unknown = HAProxyProxiedProtocol.UNKNOWN.byteValue(); - final byte tcp4 = HAProxyProxiedProtocol.TCP4.byteValue(); - final byte tcp6 = HAProxyProxiedProtocol.TCP6.byteValue(); - final byte udp4 = HAProxyProxiedProtocol.UDP4.byteValue(); - final byte udp6 = HAProxyProxiedProtocol.UDP6.byteValue(); - final byte unix_stream = HAProxyProxiedProtocol.UNIX_STREAM.byteValue(); - final byte unix_dgram = HAProxyProxiedProtocol.UNIX_DGRAM.byteValue(); - - assertEquals(TransportProtocol.UNSPEC, TransportProtocol.valueOf(unknown)); - assertEquals(TransportProtocol.STREAM, TransportProtocol.valueOf(tcp4)); - assertEquals(TransportProtocol.STREAM, TransportProtocol.valueOf(tcp6)); - assertEquals(TransportProtocol.STREAM, TransportProtocol.valueOf(unix_stream)); - assertEquals(TransportProtocol.DGRAM, TransportProtocol.valueOf(udp4)); - assertEquals(TransportProtocol.DGRAM, TransportProtocol.valueOf(udp6)); - assertEquals(TransportProtocol.DGRAM, TransportProtocol.valueOf(unix_dgram)); - - assertEquals(AddressFamily.AF_UNSPEC, AddressFamily.valueOf(unknown)); - assertEquals(AddressFamily.AF_IPv4, AddressFamily.valueOf(tcp4)); - assertEquals(AddressFamily.AF_IPv4, AddressFamily.valueOf(udp4)); - assertEquals(AddressFamily.AF_IPv6, AddressFamily.valueOf(tcp6)); - assertEquals(AddressFamily.AF_IPv6, AddressFamily.valueOf(udp6)); - assertEquals(AddressFamily.AF_UNIX, AddressFamily.valueOf(unix_stream)); - assertEquals(AddressFamily.AF_UNIX, AddressFamily.valueOf(unix_dgram)); - } - - @Test - public void testV2IPV4Decode() { - byte[] header = new byte[28]; - header[0] = 0x0D; // Binary Prefix - header[1] = 0x0A; // ----- - header[2] = 0x0D; // ----- - header[3] = 0x0A; // ----- - header[4] = 0x00; // ----- - header[5] = 0x0D; // ----- - header[6] = 0x0A; // ----- - header[7] = 0x51; // ----- - header[8] = 0x55; // ----- - header[9] = 0x49; // ----- - header[10] = 0x54; // ----- - header[11] = 0x0A; // ----- - - header[12] = 0x21; // v2, cmd=PROXY - header[13] = 0x11; // TCP over IPv4 - - header[14] = 0x00; // Remaining Bytes - header[15] = 0x0c; // ----- - - header[16] = (byte) 0xc0; // Source Address - header[17] = (byte) 0xa8; // ----- - header[18] = 0x00; // ----- - header[19] = 0x01; // ----- - - header[20] = (byte) 0xc0; // Destination Address - header[21] = (byte) 0xa8; // ----- - header[22] = 0x00; // ----- - header[23] = 0x0b; // ----- - - header[24] = (byte) 0xdc; // Source Port - header[25] = 0x04; // ----- - - header[26] = 0x01; // Destination Port - header[27] = (byte) 0xbb; // ----- - - int startChannels = ch.pipeline().names().size(); - ch.writeInbound(copiedBuffer(header)); - Object msgObj = ch.readInbound(); - assertEquals(startChannels - 1, ch.pipeline().names().size()); - assertTrue(msgObj instanceof HAProxyMessage); - HAProxyMessage msg = (HAProxyMessage) msgObj; - assertEquals(HAProxyProtocolVersion.V2, msg.protocolVersion()); - assertEquals(HAProxyCommand.PROXY, msg.command()); - assertEquals(HAProxyProxiedProtocol.TCP4, msg.proxiedProtocol()); - assertEquals("192.168.0.1", msg.sourceAddress()); - assertEquals("192.168.0.11", msg.destinationAddress()); - assertEquals(56324, msg.sourcePort()); - assertEquals(443, msg.destinationPort()); - assertNull(ch.readInbound()); - assertFalse(ch.finish()); - assertTrue(msg.release()); - } - - @Test - public void testV2UDPDecode() { - byte[] header = new byte[28]; - header[0] = 0x0D; // Binary Prefix - header[1] = 0x0A; // ----- - header[2] = 0x0D; // ----- - header[3] = 0x0A; // ----- - header[4] = 0x00; // ----- - header[5] = 0x0D; // ----- - header[6] = 0x0A; // ----- - header[7] = 0x51; // ----- - header[8] = 0x55; // ----- - header[9] = 0x49; // ----- - header[10] = 0x54; // ----- - header[11] = 0x0A; // ----- - - header[12] = 0x21; // v2, cmd=PROXY - header[13] = 0x12; // UDP over IPv4 - - header[14] = 0x00; // Remaining Bytes - header[15] = 0x0c; // ----- - - header[16] = (byte) 0xc0; // Source Address - header[17] = (byte) 0xa8; // ----- - header[18] = 0x00; // ----- - header[19] = 0x01; // ----- - - header[20] = (byte) 0xc0; // Destination Address - header[21] = (byte) 0xa8; // ----- - header[22] = 0x00; // ----- - header[23] = 0x0b; // ----- - - header[24] = (byte) 0xdc; // Source Port - header[25] = 0x04; // ----- - - header[26] = 0x01; // Destination Port - header[27] = (byte) 0xbb; // ----- - - int startChannels = ch.pipeline().names().size(); - ch.writeInbound(copiedBuffer(header)); - Object msgObj = ch.readInbound(); - assertEquals(startChannels - 1, ch.pipeline().names().size()); - assertTrue(msgObj instanceof HAProxyMessage); - HAProxyMessage msg = (HAProxyMessage) msgObj; - assertEquals(HAProxyProtocolVersion.V2, msg.protocolVersion()); - assertEquals(HAProxyCommand.PROXY, msg.command()); - assertEquals(HAProxyProxiedProtocol.UDP4, msg.proxiedProtocol()); - assertEquals("192.168.0.1", msg.sourceAddress()); - assertEquals("192.168.0.11", msg.destinationAddress()); - assertEquals(56324, msg.sourcePort()); - assertEquals(443, msg.destinationPort()); - assertNull(ch.readInbound()); - assertFalse(ch.finish()); - assertTrue(msg.release()); - } - - @Test - public void testv2IPV6Decode() { - byte[] header = new byte[52]; - header[0] = 0x0D; // Binary Prefix - header[1] = 0x0A; // ----- - header[2] = 0x0D; // ----- - header[3] = 0x0A; // ----- - header[4] = 0x00; // ----- - header[5] = 0x0D; // ----- - header[6] = 0x0A; // ----- - header[7] = 0x51; // ----- - header[8] = 0x55; // ----- - header[9] = 0x49; // ----- - header[10] = 0x54; // ----- - header[11] = 0x0A; // ----- - - header[12] = 0x21; // v2, cmd=PROXY - header[13] = 0x21; // TCP over IPv6 - - header[14] = 0x00; // Remaining Bytes - header[15] = 0x24; // ----- - - header[16] = 0x20; // Source Address - header[17] = 0x01; // ----- - header[18] = 0x0d; // ----- - header[19] = (byte) 0xb8; // ----- - header[20] = (byte) 0x85; // ----- - header[21] = (byte) 0xa3; // ----- - header[22] = 0x00; // ----- - header[23] = 0x00; // ----- - header[24] = 0x00; // ----- - header[25] = 0x00; // ----- - header[26] = (byte) 0x8a; // ----- - header[27] = 0x2e; // ----- - header[28] = 0x03; // ----- - header[29] = 0x70; // ----- - header[30] = 0x73; // ----- - header[31] = 0x34; // ----- - - header[32] = 0x10; // Destination Address - header[33] = 0x50; // ----- - header[34] = 0x00; // ----- - header[35] = 0x00; // ----- - header[36] = 0x00; // ----- - header[37] = 0x00; // ----- - header[38] = 0x00; // ----- - header[39] = 0x00; // ----- - header[40] = 0x00; // ----- - header[41] = 0x05; // ----- - header[42] = 0x06; // ----- - header[43] = 0x00; // ----- - header[44] = 0x30; // ----- - header[45] = 0x0c; // ----- - header[46] = 0x32; // ----- - header[47] = 0x6b; // ----- - - header[48] = (byte) 0xdc; // Source Port - header[49] = 0x04; // ----- - - header[50] = 0x01; // Destination Port - header[51] = (byte) 0xbb; // ----- - - int startChannels = ch.pipeline().names().size(); - ch.writeInbound(copiedBuffer(header)); - Object msgObj = ch.readInbound(); - assertEquals(startChannels - 1, ch.pipeline().names().size()); - assertTrue(msgObj instanceof HAProxyMessage); - HAProxyMessage msg = (HAProxyMessage) msgObj; - assertEquals(HAProxyProtocolVersion.V2, msg.protocolVersion()); - assertEquals(HAProxyCommand.PROXY, msg.command()); - assertEquals(HAProxyProxiedProtocol.TCP6, msg.proxiedProtocol()); - assertEquals("2001:db8:85a3:0:0:8a2e:370:7334", msg.sourceAddress()); - assertEquals("1050:0:0:0:5:600:300c:326b", msg.destinationAddress()); - assertEquals(56324, msg.sourcePort()); - assertEquals(443, msg.destinationPort()); - assertNull(ch.readInbound()); - assertFalse(ch.finish()); - assertTrue(msg.release()); - } - - @Test - public void testv2UnixDecode() { - byte[] header = new byte[232]; - header[0] = 0x0D; // Binary Prefix - header[1] = 0x0A; // ----- - header[2] = 0x0D; // ----- - header[3] = 0x0A; // ----- - header[4] = 0x00; // ----- - header[5] = 0x0D; // ----- - header[6] = 0x0A; // ----- - header[7] = 0x51; // ----- - header[8] = 0x55; // ----- - header[9] = 0x49; // ----- - header[10] = 0x54; // ----- - header[11] = 0x0A; // ----- - - header[12] = 0x21; // v2, cmd=PROXY - header[13] = 0x31; // UNIX_STREAM - - header[14] = 0x00; // Remaining Bytes - header[15] = (byte) 0xd8; // ----- - - header[16] = 0x2f; // Source Address - header[17] = 0x76; // ----- - header[18] = 0x61; // ----- - header[19] = 0x72; // ----- - header[20] = 0x2f; // ----- - header[21] = 0x72; // ----- - header[22] = 0x75; // ----- - header[23] = 0x6e; // ----- - header[24] = 0x2f; // ----- - header[25] = 0x73; // ----- - header[26] = 0x72; // ----- - header[27] = 0x63; // ----- - header[28] = 0x2e; // ----- - header[29] = 0x73; // ----- - header[30] = 0x6f; // ----- - header[31] = 0x63; // ----- - header[32] = 0x6b; // ----- - header[33] = 0x00; // ----- - - header[124] = 0x2f; // Destination Address - header[125] = 0x76; // ----- - header[126] = 0x61; // ----- - header[127] = 0x72; // ----- - header[128] = 0x2f; // ----- - header[129] = 0x72; // ----- - header[130] = 0x75; // ----- - header[131] = 0x6e; // ----- - header[132] = 0x2f; // ----- - header[133] = 0x64; // ----- - header[134] = 0x65; // ----- - header[135] = 0x73; // ----- - header[136] = 0x74; // ----- - header[137] = 0x2e; // ----- - header[138] = 0x73; // ----- - header[139] = 0x6f; // ----- - header[140] = 0x63; // ----- - header[141] = 0x6b; // ----- - header[142] = 0x00; // ----- - - int startChannels = ch.pipeline().names().size(); - ch.writeInbound(copiedBuffer(header)); - Object msgObj = ch.readInbound(); - assertEquals(startChannels - 1, ch.pipeline().names().size()); - assertTrue(msgObj instanceof HAProxyMessage); - HAProxyMessage msg = (HAProxyMessage) msgObj; - assertEquals(HAProxyProtocolVersion.V2, msg.protocolVersion()); - assertEquals(HAProxyCommand.PROXY, msg.command()); - assertEquals(HAProxyProxiedProtocol.UNIX_STREAM, msg.proxiedProtocol()); - assertEquals("/var/run/src.sock", msg.sourceAddress()); - assertEquals("/var/run/dest.sock", msg.destinationAddress()); - assertEquals(0, msg.sourcePort()); - assertEquals(0, msg.destinationPort()); - assertNull(ch.readInbound()); - assertFalse(ch.finish()); - assertTrue(msg.release()); - } - - @Test - public void testV2LocalProtocolDecode() { - byte[] header = new byte[28]; - header[0] = 0x0D; // Binary Prefix - header[1] = 0x0A; // ----- - header[2] = 0x0D; // ----- - header[3] = 0x0A; // ----- - header[4] = 0x00; // ----- - header[5] = 0x0D; // ----- - header[6] = 0x0A; // ----- - header[7] = 0x51; // ----- - header[8] = 0x55; // ----- - header[9] = 0x49; // ----- - header[10] = 0x54; // ----- - header[11] = 0x0A; // ----- - - header[12] = 0x20; // v2, cmd=LOCAL - header[13] = 0x00; // Unspecified transport protocol and address family - - header[14] = 0x00; // Remaining Bytes - header[15] = 0x0c; // ----- - - header[16] = (byte) 0xc0; // Source Address - header[17] = (byte) 0xa8; // ----- - header[18] = 0x00; // ----- - header[19] = 0x01; // ----- - - header[20] = (byte) 0xc0; // Destination Address - header[21] = (byte) 0xa8; // ----- - header[22] = 0x00; // ----- - header[23] = 0x0b; // ----- - - header[24] = (byte) 0xdc; // Source Port - header[25] = 0x04; // ----- - - header[26] = 0x01; // Destination Port - header[27] = (byte) 0xbb; // ----- - - int startChannels = ch.pipeline().names().size(); - ch.writeInbound(copiedBuffer(header)); - Object msgObj = ch.readInbound(); - assertEquals(startChannels - 1, ch.pipeline().names().size()); - assertTrue(msgObj instanceof HAProxyMessage); - HAProxyMessage msg = (HAProxyMessage) msgObj; - assertEquals(HAProxyProtocolVersion.V2, msg.protocolVersion()); - assertEquals(HAProxyCommand.LOCAL, msg.command()); - assertEquals(HAProxyProxiedProtocol.UNKNOWN, msg.proxiedProtocol()); - assertNull(msg.sourceAddress()); - assertNull(msg.destinationAddress()); - assertEquals(0, msg.sourcePort()); - assertEquals(0, msg.destinationPort()); - assertNull(ch.readInbound()); - assertFalse(ch.finish()); - assertTrue(msg.release()); - } - - @Test - public void testV2UnknownProtocolDecode() { - byte[] header = new byte[28]; - header[0] = 0x0D; // Binary Prefix - header[1] = 0x0A; // ----- - header[2] = 0x0D; // ----- - header[3] = 0x0A; // ----- - header[4] = 0x00; // ----- - header[5] = 0x0D; // ----- - header[6] = 0x0A; // ----- - header[7] = 0x51; // ----- - header[8] = 0x55; // ----- - header[9] = 0x49; // ----- - header[10] = 0x54; // ----- - header[11] = 0x0A; // ----- - - header[12] = 0x21; // v2, cmd=PROXY - header[13] = 0x00; // Unspecified transport protocol and address family - - header[14] = 0x00; // Remaining Bytes - header[15] = 0x0c; // ----- - - header[16] = (byte) 0xc0; // Source Address - header[17] = (byte) 0xa8; // ----- - header[18] = 0x00; // ----- - header[19] = 0x01; // ----- - - header[20] = (byte) 0xc0; // Destination Address - header[21] = (byte) 0xa8; // ----- - header[22] = 0x00; // ----- - header[23] = 0x0b; // ----- - - header[24] = (byte) 0xdc; // Source Port - header[25] = 0x04; // ----- - - header[26] = 0x01; // Destination Port - header[27] = (byte) 0xbb; // ----- - - int startChannels = ch.pipeline().names().size(); - ch.writeInbound(copiedBuffer(header)); - Object msgObj = ch.readInbound(); - assertEquals(startChannels - 1, ch.pipeline().names().size()); - assertTrue(msgObj instanceof HAProxyMessage); - HAProxyMessage msg = (HAProxyMessage) msgObj; - assertEquals(HAProxyProtocolVersion.V2, msg.protocolVersion()); - assertEquals(HAProxyCommand.PROXY, msg.command()); - assertEquals(HAProxyProxiedProtocol.UNKNOWN, msg.proxiedProtocol()); - assertNull(msg.sourceAddress()); - assertNull(msg.destinationAddress()); - assertEquals(0, msg.sourcePort()); - assertEquals(0, msg.destinationPort()); - assertNull(ch.readInbound()); - assertFalse(ch.finish()); - assertTrue(msg.release()); - } - - @Test - public void testV2WithSslTLVs() throws Exception { - ch = new EmbeddedChannel(new HAProxyMessageDecoder()); - - final byte[] bytes = { - 13, 10, 13, 10, 0, 13, 10, 81, 85, 73, 84, 10, 33, 17, 0, 35, 127, 0, 0, 1, 127, 0, 0, 1, - -55, -90, 7, 89, 32, 0, 20, 5, 0, 0, 0, 0, 33, 0, 5, 84, 76, 83, 118, 49, 34, 0, 4, 76, 69, 65, 70 - }; - - int startChannels = ch.pipeline().names().size(); - assertTrue(ch.writeInbound(copiedBuffer(bytes))); - Object msgObj = ch.readInbound(); - assertEquals(startChannels - 1, ch.pipeline().names().size()); - HAProxyMessage msg = (HAProxyMessage) msgObj; - - assertEquals(HAProxyProtocolVersion.V2, msg.protocolVersion()); - assertEquals(HAProxyCommand.PROXY, msg.command()); - assertEquals(HAProxyProxiedProtocol.TCP4, msg.proxiedProtocol()); - assertEquals("127.0.0.1", msg.sourceAddress()); - assertEquals("127.0.0.1", msg.destinationAddress()); - assertEquals(51622, msg.sourcePort()); - assertEquals(1881, msg.destinationPort()); - final List tlvs = msg.tlvs(); - - assertEquals(3, tlvs.size()); - final HAProxyTLV firstTlv = tlvs.get(0); - assertEquals(HAProxyTLV.Type.PP2_TYPE_SSL, firstTlv.type()); - final HAProxySSLTLV sslTlv = (HAProxySSLTLV) firstTlv; - assertEquals(0, sslTlv.verify()); - assertTrue(sslTlv.isPP2ClientSSL()); - assertTrue(sslTlv.isPP2ClientCertSess()); - assertFalse(sslTlv.isPP2ClientCertConn()); - - final HAProxyTLV secondTlv = tlvs.get(1); - - assertEquals(HAProxyTLV.Type.PP2_TYPE_SSL_VERSION, secondTlv.type()); - ByteBuf secondContentBuf = secondTlv.content(); - byte[] secondContent = new byte[secondContentBuf.readableBytes()]; - secondContentBuf.readBytes(secondContent); - assertArrayEquals("TLSv1".getBytes(CharsetUtil.US_ASCII), secondContent); - - final HAProxyTLV thirdTLV = tlvs.get(2); - assertEquals(HAProxyTLV.Type.PP2_TYPE_SSL_CN, thirdTLV.type()); - ByteBuf thirdContentBuf = thirdTLV.content(); - byte[] thirdContent = new byte[thirdContentBuf.readableBytes()]; - thirdContentBuf.readBytes(thirdContent); - assertArrayEquals("LEAF".getBytes(CharsetUtil.US_ASCII), thirdContent); - - assertTrue(sslTlv.encapsulatedTLVs().contains(secondTlv)); - assertTrue(sslTlv.encapsulatedTLVs().contains(thirdTLV)); - - assertTrue(0 < firstTlv.refCnt()); - assertTrue(0 < secondTlv.refCnt()); - assertTrue(0 < thirdTLV.refCnt()); - assertTrue(msg.release()); - assertEquals(0, firstTlv.refCnt()); - assertEquals(0, secondTlv.refCnt()); - assertEquals(0, thirdTLV.refCnt()); - - assertNull(ch.readInbound()); - assertFalse(ch.finish()); - } - - @Test - public void testReleaseHAProxyMessage() { - ch = new EmbeddedChannel(new HAProxyMessageDecoder()); - - final byte[] bytes = { - 13, 10, 13, 10, 0, 13, 10, 81, 85, 73, 84, 10, 33, 17, 0, 35, 127, 0, 0, 1, 127, 0, 0, 1, - -55, -90, 7, 89, 32, 0, 20, 5, 0, 0, 0, 0, 33, 0, 5, 84, 76, 83, 118, 49, 34, 0, 4, 76, 69, 65, 70 - }; - - int startChannels = ch.pipeline().names().size(); - assertTrue(ch.writeInbound(copiedBuffer(bytes))); - Object msgObj = ch.readInbound(); - assertEquals(startChannels - 1, ch.pipeline().names().size()); - HAProxyMessage msg = (HAProxyMessage) msgObj; - - final List tlvs = msg.tlvs(); - assertEquals(3, tlvs.size()); - - assertEquals(1, msg.refCnt()); - for (HAProxyTLV tlv : tlvs) { - assertEquals(3, tlv.refCnt()); - } - - // Retain the haproxy message - msg.retain(); - assertEquals(2, msg.refCnt()); - for (HAProxyTLV tlv : tlvs) { - assertEquals(3, tlv.refCnt()); - } - - // Decrease the haproxy message refCnt - msg.release(); - assertEquals(1, msg.refCnt()); - for (HAProxyTLV tlv : tlvs) { - assertEquals(3, tlv.refCnt()); - } - - // Release haproxy message, TLVs will be released with it - msg.release(); - assertEquals(0, msg.refCnt()); - for (HAProxyTLV tlv : tlvs) { - assertEquals(0, tlv.refCnt()); - } - } - - @Test - public void testV2WithTLV() { - ch = new EmbeddedChannel(new HAProxyMessageDecoder(4)); - - byte[] header = new byte[236]; - header[0] = 0x0D; // Binary Prefix - header[1] = 0x0A; // ----- - header[2] = 0x0D; // ----- - header[3] = 0x0A; // ----- - header[4] = 0x00; // ----- - header[5] = 0x0D; // ----- - header[6] = 0x0A; // ----- - header[7] = 0x51; // ----- - header[8] = 0x55; // ----- - header[9] = 0x49; // ----- - header[10] = 0x54; // ----- - header[11] = 0x0A; // ----- - - header[12] = 0x21; // v2, cmd=PROXY - header[13] = 0x31; // UNIX_STREAM - - header[14] = 0x00; // Remaining Bytes - header[15] = (byte) 0xdc; // ----- - - header[16] = 0x2f; // Source Address - header[17] = 0x76; // ----- - header[18] = 0x61; // ----- - header[19] = 0x72; // ----- - header[20] = 0x2f; // ----- - header[21] = 0x72; // ----- - header[22] = 0x75; // ----- - header[23] = 0x6e; // ----- - header[24] = 0x2f; // ----- - header[25] = 0x73; // ----- - header[26] = 0x72; // ----- - header[27] = 0x63; // ----- - header[28] = 0x2e; // ----- - header[29] = 0x73; // ----- - header[30] = 0x6f; // ----- - header[31] = 0x63; // ----- - header[32] = 0x6b; // ----- - header[33] = 0x00; // ----- - - header[124] = 0x2f; // Destination Address - header[125] = 0x76; // ----- - header[126] = 0x61; // ----- - header[127] = 0x72; // ----- - header[128] = 0x2f; // ----- - header[129] = 0x72; // ----- - header[130] = 0x75; // ----- - header[131] = 0x6e; // ----- - header[132] = 0x2f; // ----- - header[133] = 0x64; // ----- - header[134] = 0x65; // ----- - header[135] = 0x73; // ----- - header[136] = 0x74; // ----- - header[137] = 0x2e; // ----- - header[138] = 0x73; // ----- - header[139] = 0x6f; // ----- - header[140] = 0x63; // ----- - header[141] = 0x6b; // ----- - header[142] = 0x00; // ----- - - // ---- Additional data (TLV) ---- \\ - - header[232] = 0x01; // Type - header[233] = 0x00; // Remaining bytes - header[234] = 0x01; // ----- - header[235] = 0x01; // Payload - - int startChannels = ch.pipeline().names().size(); - ch.writeInbound(copiedBuffer(header)); - Object msgObj = ch.readInbound(); - assertEquals(startChannels - 1, ch.pipeline().names().size()); - assertTrue(msgObj instanceof HAProxyMessage); - HAProxyMessage msg = (HAProxyMessage) msgObj; - assertEquals(HAProxyProtocolVersion.V2, msg.protocolVersion()); - assertEquals(HAProxyCommand.PROXY, msg.command()); - assertEquals(HAProxyProxiedProtocol.UNIX_STREAM, msg.proxiedProtocol()); - assertEquals("/var/run/src.sock", msg.sourceAddress()); - assertEquals("/var/run/dest.sock", msg.destinationAddress()); - assertEquals(0, msg.sourcePort()); - assertEquals(0, msg.destinationPort()); - assertNull(ch.readInbound()); - assertFalse(ch.finish()); - assertTrue(msg.release()); - } - - @Test - public void testV2InvalidProtocol() { - byte[] header = new byte[28]; - header[0] = 0x0D; // Binary Prefix - header[1] = 0x0A; // ----- - header[2] = 0x0D; // ----- - header[3] = 0x0A; // ----- - header[4] = 0x00; // ----- - header[5] = 0x0D; // ----- - header[6] = 0x0A; // ----- - header[7] = 0x51; // ----- - header[8] = 0x55; // ----- - header[9] = 0x49; // ----- - header[10] = 0x54; // ----- - header[11] = 0x0A; // ----- - - header[12] = 0x21; // v2, cmd=PROXY - header[13] = 0x41; // Bogus transport protocol - - header[14] = 0x00; // Remaining Bytes - header[15] = 0x0c; // ----- - - header[16] = (byte) 0xc0; // Source Address - header[17] = (byte) 0xa8; // ----- - header[18] = 0x00; // ----- - header[19] = 0x01; // ----- - - header[20] = (byte) 0xc0; // Destination Address - header[21] = (byte) 0xa8; // ----- - header[22] = 0x00; // ----- - header[23] = 0x0b; // ----- - - header[24] = (byte) 0xdc; // Source Port - header[25] = 0x04; // ----- - - header[26] = 0x01; // Destination Port - header[27] = (byte) 0xbb; // ----- - - assertThrows(HAProxyProtocolException.class, () -> ch.writeInbound(copiedBuffer(header))); - } - - @Test - public void testV2MissingParams() { - byte[] header = new byte[26]; - header[0] = 0x0D; // Binary Prefix - header[1] = 0x0A; // ----- - header[2] = 0x0D; // ----- - header[3] = 0x0A; // ----- - header[4] = 0x00; // ----- - header[5] = 0x0D; // ----- - header[6] = 0x0A; // ----- - header[7] = 0x51; // ----- - header[8] = 0x55; // ----- - header[9] = 0x49; // ----- - header[10] = 0x54; // ----- - header[11] = 0x0A; // ----- - - header[12] = 0x21; // v2, cmd=PROXY - header[13] = 0x11; // TCP over IPv4 - - header[14] = 0x00; // Remaining Bytes - header[15] = 0x0a; // ----- - - header[16] = (byte) 0xc0; // Source Address - header[17] = (byte) 0xa8; // ----- - header[18] = 0x00; // ----- - header[19] = 0x01; // ----- - - header[20] = (byte) 0xc0; // Destination Address - header[21] = (byte) 0xa8; // ----- - header[22] = 0x00; // ----- - header[23] = 0x0b; // ----- - - header[24] = (byte) 0xdc; // Source Port - header[25] = 0x04; // ----- - - assertThrows(HAProxyProtocolException.class, () -> ch.writeInbound(copiedBuffer(header))); - } - - @Test - public void testV2InvalidCommand() { - byte[] header = new byte[28]; - header[0] = 0x0D; // Binary Prefix - header[1] = 0x0A; // ----- - header[2] = 0x0D; // ----- - header[3] = 0x0A; // ----- - header[4] = 0x00; // ----- - header[5] = 0x0D; // ----- - header[6] = 0x0A; // ----- - header[7] = 0x51; // ----- - header[8] = 0x55; // ----- - header[9] = 0x49; // ----- - header[10] = 0x54; // ----- - header[11] = 0x0A; // ----- - - header[12] = 0x22; // v2, Bogus command - header[13] = 0x11; // TCP over IPv4 - - header[14] = 0x00; // Remaining Bytes - header[15] = 0x0c; // ----- - - header[16] = (byte) 0xc0; // Source Address - header[17] = (byte) 0xa8; // ----- - header[18] = 0x00; // ----- - header[19] = 0x01; // ----- - - header[20] = (byte) 0xc0; // Destination Address - header[21] = (byte) 0xa8; // ----- - header[22] = 0x00; // ----- - header[23] = 0x0b; // ----- - - header[24] = (byte) 0xdc; // Source Port - header[25] = 0x04; // ----- - - header[26] = 0x01; // Destination Port - header[27] = (byte) 0xbb; // ----- - - assertThrows(HAProxyProtocolException.class, () -> ch.writeInbound(copiedBuffer(header))); - } - - @Test - public void testV2InvalidVersion() { - byte[] header = new byte[28]; - header[0] = 0x0D; // Binary Prefix - header[1] = 0x0A; // ----- - header[2] = 0x0D; // ----- - header[3] = 0x0A; // ----- - header[4] = 0x00; // ----- - header[5] = 0x0D; // ----- - header[6] = 0x0A; // ----- - header[7] = 0x51; // ----- - header[8] = 0x55; // ----- - header[9] = 0x49; // ----- - header[10] = 0x54; // ----- - header[11] = 0x0A; // ----- - - header[12] = 0x31; // Bogus version, cmd=PROXY - header[13] = 0x11; // TCP over IPv4 - - header[14] = 0x00; // Remaining Bytes - header[15] = 0x0c; // ----- - - header[16] = (byte) 0xc0; // Source Address - header[17] = (byte) 0xa8; // ----- - header[18] = 0x00; // ----- - header[19] = 0x01; // ----- - - header[20] = (byte) 0xc0; // Destination Address - header[21] = (byte) 0xa8; // ----- - header[22] = 0x00; // ----- - header[23] = 0x0b; // ----- - - header[24] = (byte) 0xdc; // Source Port - header[25] = 0x04; // ----- - - header[26] = 0x01; // Destination Port - header[27] = (byte) 0xbb; // ----- - - assertThrows(HAProxyProtocolException.class, () -> ch.writeInbound(copiedBuffer(header))); - } - - @Test - public void testV2HeaderTooLong() { - ch = new EmbeddedChannel(new HAProxyMessageDecoder(0)); - - byte[] header = new byte[248]; - header[0] = 0x0D; // Binary Prefix - header[1] = 0x0A; // ----- - header[2] = 0x0D; // ----- - header[3] = 0x0A; // ----- - header[4] = 0x00; // ----- - header[5] = 0x0D; // ----- - header[6] = 0x0A; // ----- - header[7] = 0x51; // ----- - header[8] = 0x55; // ----- - header[9] = 0x49; // ----- - header[10] = 0x54; // ----- - header[11] = 0x0A; // ----- - - header[12] = 0x21; // v2, cmd=PROXY - header[13] = 0x11; // TCP over IPv4 - - header[14] = 0x00; // Remaining Bytes - header[15] = (byte) 0xe8; // ----- - - header[16] = (byte) 0xc0; // Source Address - header[17] = (byte) 0xa8; // ----- - header[18] = 0x00; // ----- - header[19] = 0x01; // ----- - - header[20] = (byte) 0xc0; // Destination Address - header[21] = (byte) 0xa8; // ----- - header[22] = 0x00; // ----- - header[23] = 0x0b; // ----- - - header[24] = (byte) 0xdc; // Source Port - header[25] = 0x04; // ----- - - header[26] = 0x01; // Destination Port - header[27] = (byte) 0xbb; // ----- - - assertThrows(HAProxyProtocolException.class, () -> ch.writeInbound(copiedBuffer(header))); - } - - @Test - public void testV2IncompleteHeader() { - byte[] header = new byte[13]; - header[0] = 0x0D; // Binary Prefix - header[1] = 0x0A; // ----- - header[2] = 0x0D; // ----- - header[3] = 0x0A; // ----- - header[4] = 0x00; // ----- - header[5] = 0x0D; // ----- - header[6] = 0x0A; // ----- - header[7] = 0x51; // ----- - header[8] = 0x55; // ----- - header[9] = 0x49; // ----- - header[10] = 0x54; // ----- - header[11] = 0x0A; // ----- - - header[12] = 0x21; // v2, cmd=PROXY - - ch.writeInbound(copiedBuffer(header)); - assertNull(ch.readInbound()); - assertFalse(ch.finish()); - } - - @Test - public void testDetectProtocol() { - final ByteBuf validHeaderV1 = copiedBuffer("PROXY TCP4 192.168.0.1 192.168.0.11 56324 443\r\n", - CharsetUtil.US_ASCII); - ProtocolDetectionResult result = HAProxyMessageDecoder.detectProtocol(validHeaderV1); - assertEquals(ProtocolDetectionState.DETECTED, result.state()); - assertEquals(HAProxyProtocolVersion.V1, result.detectedProtocol()); - validHeaderV1.release(); - - final ByteBuf invalidHeader = copiedBuffer("Invalid header", CharsetUtil.US_ASCII); - result = HAProxyMessageDecoder.detectProtocol(invalidHeader); - assertEquals(ProtocolDetectionState.INVALID, result.state()); - assertNull(result.detectedProtocol()); - invalidHeader.release(); - - final ByteBuf validHeaderV2 = buffer(); - validHeaderV2.writeByte(0x0D); - validHeaderV2.writeByte(0x0A); - validHeaderV2.writeByte(0x0D); - validHeaderV2.writeByte(0x0A); - validHeaderV2.writeByte(0x00); - validHeaderV2.writeByte(0x0D); - validHeaderV2.writeByte(0x0A); - validHeaderV2.writeByte(0x51); - validHeaderV2.writeByte(0x55); - validHeaderV2.writeByte(0x49); - validHeaderV2.writeByte(0x54); - validHeaderV2.writeByte(0x0A); - result = HAProxyMessageDecoder.detectProtocol(validHeaderV2); - assertEquals(ProtocolDetectionState.DETECTED, result.state()); - assertEquals(HAProxyProtocolVersion.V2, result.detectedProtocol()); - validHeaderV2.release(); - - final ByteBuf incompleteHeader = buffer(); - incompleteHeader.writeByte(0x0D); - incompleteHeader.writeByte(0x0A); - incompleteHeader.writeByte(0x0D); - incompleteHeader.writeByte(0x0A); - incompleteHeader.writeByte(0x00); - incompleteHeader.writeByte(0x0D); - incompleteHeader.writeByte(0x0A); - result = HAProxyMessageDecoder.detectProtocol(incompleteHeader); - assertEquals(ProtocolDetectionState.NEEDS_MORE_DATA, result.state()); - assertNull(result.detectedProtocol()); - incompleteHeader.release(); - } -} diff --git a/codec-haproxy/src/test/java/io/netty/handler/codec/haproxy/HAProxySSLTLVTest.java b/codec-haproxy/src/test/java/io/netty/handler/codec/haproxy/HAProxySSLTLVTest.java deleted file mode 100644 index a2065b41d5..0000000000 --- a/codec-haproxy/src/test/java/io/netty/handler/codec/haproxy/HAProxySSLTLVTest.java +++ /dev/null @@ -1,68 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.haproxy; - -import io.netty.buffer.Unpooled; -import org.junit.jupiter.api.Test; - -import java.util.Collections; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class HAProxySSLTLVTest { - - @Test - public void testClientBitmask() throws Exception { - - // 0b0000_0111 - final byte allClientsEnabled = 0x7; - final HAProxySSLTLV allClientsEnabledTLV = - new HAProxySSLTLV(0, allClientsEnabled, Collections.emptyList(), Unpooled.buffer()); - - assertTrue(allClientsEnabledTLV.isPP2ClientCertConn()); - assertTrue(allClientsEnabledTLV.isPP2ClientSSL()); - assertTrue(allClientsEnabledTLV.isPP2ClientCertSess()); - - assertTrue(allClientsEnabledTLV.release()); - - // 0b0000_0101 - final byte clientSSLandClientCertSessEnabled = 0x5; - - final HAProxySSLTLV clientSSLandClientCertSessTLV = - new HAProxySSLTLV(0, clientSSLandClientCertSessEnabled, Collections.emptyList(), - Unpooled.buffer()); - - assertFalse(clientSSLandClientCertSessTLV.isPP2ClientCertConn()); - assertTrue(clientSSLandClientCertSessTLV.isPP2ClientSSL()); - assertTrue(clientSSLandClientCertSessTLV.isPP2ClientCertSess()); - - assertTrue(clientSSLandClientCertSessTLV.release()); - // 0b0000_0000 - final byte noClientEnabled = 0x0; - - final HAProxySSLTLV noClientTlv = - new HAProxySSLTLV(0, noClientEnabled, Collections.emptyList(), - Unpooled.buffer()); - - assertFalse(noClientTlv.isPP2ClientCertConn()); - assertFalse(noClientTlv.isPP2ClientSSL()); - assertFalse(noClientTlv.isPP2ClientCertSess()); - - assertTrue(noClientTlv.release()); - } -} diff --git a/codec-haproxy/src/test/java/io/netty/handler/codec/haproxy/HaProxyMessageEncoderTest.java b/codec-haproxy/src/test/java/io/netty/handler/codec/haproxy/HaProxyMessageEncoderTest.java deleted file mode 100644 index efc201e7dd..0000000000 --- a/codec-haproxy/src/test/java/io/netty/handler/codec/haproxy/HaProxyMessageEncoderTest.java +++ /dev/null @@ -1,408 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.haproxy; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.haproxy.HAProxyTLV.Type; -import io.netty.util.ByteProcessor; -import io.netty.util.CharsetUtil; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import static io.netty.handler.codec.haproxy.HAProxyConstants.*; -import static io.netty.handler.codec.haproxy.HAProxyMessageEncoder.*; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class HaProxyMessageEncoderTest { - - private static final int V2_HEADER_BYTES_LENGTH = 16; - private static final int IPv4_ADDRESS_BYTES_LENGTH = 12; - private static final int IPv6_ADDRESS_BYTES_LENGTH = 36; - - @Test - public void testIPV4EncodeProxyV1() { - EmbeddedChannel ch = new EmbeddedChannel(INSTANCE); - - HAProxyMessage message = new HAProxyMessage( - HAProxyProtocolVersion.V1, HAProxyCommand.PROXY, HAProxyProxiedProtocol.TCP4, - "192.168.0.1", "192.168.0.11", 56324, 443); - assertTrue(ch.writeOutbound(message)); - - ByteBuf byteBuf = ch.readOutbound(); - - assertEquals("PROXY TCP4 192.168.0.1 192.168.0.11 56324 443\r\n", - byteBuf.toString(CharsetUtil.US_ASCII)); - - byteBuf.release(); - assertFalse(ch.finish()); - } - - @Test - public void testIPV6EncodeProxyV1() { - EmbeddedChannel ch = new EmbeddedChannel(INSTANCE); - - HAProxyMessage message = new HAProxyMessage( - HAProxyProtocolVersion.V1, HAProxyCommand.PROXY, HAProxyProxiedProtocol.TCP6, - "2001:0db8:85a3:0000:0000:8a2e:0370:7334", "1050:0:0:0:5:600:300c:326b", 56324, 443); - assertTrue(ch.writeOutbound(message)); - - ByteBuf byteBuf = ch.readOutbound(); - - assertEquals("PROXY TCP6 2001:0db8:85a3:0000:0000:8a2e:0370:7334 1050:0:0:0:5:600:300c:326b 56324 443\r\n", - byteBuf.toString(CharsetUtil.US_ASCII)); - - byteBuf.release(); - assertFalse(ch.finish()); - } - - @Test - public void testIPv4EncodeProxyV2() { - EmbeddedChannel ch = new EmbeddedChannel(INSTANCE); - - HAProxyMessage message = new HAProxyMessage( - HAProxyProtocolVersion.V2, HAProxyCommand.PROXY, HAProxyProxiedProtocol.TCP4, - "192.168.0.1", "192.168.0.11", 56324, 443); - assertTrue(ch.writeOutbound(message)); - - ByteBuf byteBuf = ch.readOutbound(); - - // header - byte[] headerBytes = ByteBufUtil.getBytes(byteBuf, 0, 12); - assertArrayEquals(BINARY_PREFIX, headerBytes); - - // command - byte commandByte = byteBuf.getByte(12); - assertEquals(0x02, (commandByte & 0xf0) >> 4); - assertEquals(0x01, commandByte & 0x0f); - - // transport protocol, address family - byte transportByte = byteBuf.getByte(13); - assertEquals(0x01, (transportByte & 0xf0) >> 4); - assertEquals(0x01, transportByte & 0x0f); - - // source address length - int sourceAddrLength = byteBuf.getUnsignedShort(14); - assertEquals(12, sourceAddrLength); - - // source address - byte[] sourceAddr = ByteBufUtil.getBytes(byteBuf, 16, 4); - assertArrayEquals(new byte[] { (byte) 0xc0, (byte) 0xa8, 0x00, 0x01 }, sourceAddr); - - // destination address - byte[] destAddr = ByteBufUtil.getBytes(byteBuf, 20, 4); - assertArrayEquals(new byte[] { (byte) 0xc0, (byte) 0xa8, 0x00, 0x0b }, destAddr); - - // source port - int sourcePort = byteBuf.getUnsignedShort(24); - assertEquals(56324, sourcePort); - - // destination port - int destPort = byteBuf.getUnsignedShort(26); - assertEquals(443, destPort); - - byteBuf.release(); - assertFalse(ch.finish()); - } - - @Test - public void testIPv6EncodeProxyV2() { - EmbeddedChannel ch = new EmbeddedChannel(INSTANCE); - - HAProxyMessage message = new HAProxyMessage( - HAProxyProtocolVersion.V2, HAProxyCommand.PROXY, HAProxyProxiedProtocol.TCP6, - "2001:0db8:85a3:0000:0000:8a2e:0370:7334", "1050:0:0:0:5:600:300c:326b", 56324, 443); - assertTrue(ch.writeOutbound(message)); - - ByteBuf byteBuf = ch.readOutbound(); - - // header - byte[] headerBytes = ByteBufUtil.getBytes(byteBuf, 0, 12); - assertArrayEquals(BINARY_PREFIX, headerBytes); - - // command - byte commandByte = byteBuf.getByte(12); - assertEquals(0x02, (commandByte & 0xf0) >> 4); - assertEquals(0x01, commandByte & 0x0f); - - // transport protocol, address family - byte transportByte = byteBuf.getByte(13); - assertEquals(0x02, (transportByte & 0xf0) >> 4); - assertEquals(0x01, transportByte & 0x0f); - - // source address length - int sourceAddrLength = byteBuf.getUnsignedShort(14); - assertEquals(IPv6_ADDRESS_BYTES_LENGTH, sourceAddrLength); - - // source address - byte[] sourceAddr = ByteBufUtil.getBytes(byteBuf, 16, 16); - assertArrayEquals(new byte[] { - (byte) 0x20, (byte) 0x01, 0x0d, (byte) 0xb8, - (byte) 0x85, (byte) 0xa3, 0x00, 0x00, 0x00, 0x00, (byte) 0x8a, 0x2e, - 0x03, 0x70, 0x73, 0x34 - }, sourceAddr); - - // destination address - byte[] destAddr = ByteBufUtil.getBytes(byteBuf, 32, 16); - assertArrayEquals(new byte[] { - (byte) 0x10, (byte) 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x05, 0x06, 0x00, 0x30, 0x0c, 0x32, 0x6b - }, destAddr); - - // source port - int sourcePort = byteBuf.getUnsignedShort(48); - assertEquals(56324, sourcePort); - - // destination port - int destPort = byteBuf.getUnsignedShort(50); - assertEquals(443, destPort); - - byteBuf.release(); - assertFalse(ch.finish()); - } - - @Test - public void testUnixEncodeProxyV2() { - EmbeddedChannel ch = new EmbeddedChannel(INSTANCE); - - HAProxyMessage message = new HAProxyMessage( - HAProxyProtocolVersion.V2, HAProxyCommand.PROXY, HAProxyProxiedProtocol.UNIX_STREAM, - "/var/run/src.sock", "/var/run/dst.sock", 0, 0); - assertTrue(ch.writeOutbound(message)); - - ByteBuf byteBuf = ch.readOutbound(); - - // header - byte[] headerBytes = ByteBufUtil.getBytes(byteBuf, 0, 12); - assertArrayEquals(BINARY_PREFIX, headerBytes); - - // command - byte commandByte = byteBuf.getByte(12); - assertEquals(0x02, (commandByte & 0xf0) >> 4); - assertEquals(0x01, commandByte & 0x0f); - - // transport protocol, address family - byte transportByte = byteBuf.getByte(13); - assertEquals(0x03, (transportByte & 0xf0) >> 4); - assertEquals(0x01, transportByte & 0x0f); - - // address length - int addrLength = byteBuf.getUnsignedShort(14); - assertEquals(TOTAL_UNIX_ADDRESS_BYTES_LENGTH, addrLength); - - // source address - int srcAddrEnd = byteBuf.forEachByte(16, 108, ByteProcessor.FIND_NUL); - assertEquals("/var/run/src.sock", - byteBuf.slice(16, srcAddrEnd - 16).toString(CharsetUtil.US_ASCII)); - - // destination address - int dstAddrEnd = byteBuf.forEachByte(124, 108, ByteProcessor.FIND_NUL); - assertEquals("/var/run/dst.sock", - byteBuf.slice(124, dstAddrEnd - 124).toString(CharsetUtil.US_ASCII)); - - byteBuf.release(); - assertFalse(ch.finish()); - } - - @Test - public void testTLVEncodeProxy() { - EmbeddedChannel ch = new EmbeddedChannel(INSTANCE); - - List tlvs = new ArrayList(); - - ByteBuf helloWorld = Unpooled.copiedBuffer("hello world", CharsetUtil.US_ASCII); - HAProxyTLV alpnTlv = new HAProxyTLV(Type.PP2_TYPE_ALPN, (byte) 0x01, helloWorld.copy()); - tlvs.add(alpnTlv); - - ByteBuf arbitrary = Unpooled.copiedBuffer("an arbitrary string", CharsetUtil.US_ASCII); - HAProxyTLV authorityTlv = new HAProxyTLV(Type.PP2_TYPE_AUTHORITY, (byte) 0x01, arbitrary.copy()); - tlvs.add(authorityTlv); - - HAProxyMessage message = new HAProxyMessage( - HAProxyProtocolVersion.V2, HAProxyCommand.PROXY, HAProxyProxiedProtocol.TCP4, - "192.168.0.1", "192.168.0.11", 56324, 443, tlvs); - assertTrue(ch.writeOutbound(message)); - - ByteBuf byteBuf = ch.readOutbound(); - - // length - assertEquals(byteBuf.getUnsignedShort(14), byteBuf.readableBytes() - V2_HEADER_BYTES_LENGTH); - - // skip to tlv section - ByteBuf tlv = byteBuf.skipBytes(V2_HEADER_BYTES_LENGTH + IPv4_ADDRESS_BYTES_LENGTH); - - // alpn tlv - assertEquals(alpnTlv.typeByteValue(), tlv.readByte()); - short bufLength = tlv.readShort(); - assertEquals(helloWorld.array().length, bufLength); - assertEquals(helloWorld, tlv.readSlice(bufLength)); - - // authority tlv - assertEquals(authorityTlv.typeByteValue(), tlv.readByte()); - bufLength = tlv.readShort(); - assertEquals(arbitrary.array().length, bufLength); - assertEquals(arbitrary, tlv.readSlice(bufLength)); - - byteBuf.release(); - assertFalse(ch.finish()); - } - - @Test - public void testSslTLVEncodeProxy() { - EmbeddedChannel ch = new EmbeddedChannel(INSTANCE); - - List tlvs = new ArrayList(); - - ByteBuf helloWorld = Unpooled.copiedBuffer("hello world", CharsetUtil.US_ASCII); - HAProxyTLV alpnTlv = new HAProxyTLV(Type.PP2_TYPE_ALPN, (byte) 0x01, helloWorld.copy()); - tlvs.add(alpnTlv); - - ByteBuf arbitrary = Unpooled.copiedBuffer("an arbitrary string", CharsetUtil.US_ASCII); - HAProxyTLV authorityTlv = new HAProxyTLV(Type.PP2_TYPE_AUTHORITY, (byte) 0x01, arbitrary.copy()); - tlvs.add(authorityTlv); - - ByteBuf sslContent = Unpooled.copiedBuffer("some ssl content", CharsetUtil.US_ASCII); - HAProxySSLTLV haProxySSLTLV = new HAProxySSLTLV(1, (byte) 0x01, tlvs, sslContent.copy()); - - HAProxyMessage message = new HAProxyMessage( - HAProxyProtocolVersion.V2, HAProxyCommand.PROXY, HAProxyProxiedProtocol.TCP4, - "192.168.0.1", "192.168.0.11", 56324, 443, - Collections.singletonList(haProxySSLTLV)); - assertTrue(ch.writeOutbound(message)); - - ByteBuf byteBuf = ch.readOutbound(); - - assertEquals(byteBuf.getUnsignedShort(14), byteBuf.readableBytes() - V2_HEADER_BYTES_LENGTH); - ByteBuf tlv = byteBuf.skipBytes(V2_HEADER_BYTES_LENGTH + IPv4_ADDRESS_BYTES_LENGTH); - - // ssl tlv type - assertEquals(haProxySSLTLV.typeByteValue(), tlv.readByte()); - - // length - int bufLength = tlv.readUnsignedShort(); - assertEquals(bufLength, tlv.readableBytes()); - - // client, verify - assertEquals(0x01, byteBuf.readByte()); - assertEquals(1, byteBuf.readInt()); - - // alpn tlv - assertEquals(alpnTlv.typeByteValue(), tlv.readByte()); - bufLength = tlv.readShort(); - assertEquals(helloWorld.array().length, bufLength); - assertEquals(helloWorld, tlv.readSlice(bufLength)); - - // authority tlv - assertEquals(authorityTlv.typeByteValue(), tlv.readByte()); - bufLength = tlv.readShort(); - assertEquals(arbitrary.array().length, bufLength); - assertEquals(arbitrary, tlv.readSlice(bufLength)); - - byteBuf.release(); - assertFalse(ch.finish()); - } - - @Test - public void testEncodeLocalProxyV2() { - EmbeddedChannel ch = new EmbeddedChannel(INSTANCE); - - HAProxyMessage message = new HAProxyMessage( - HAProxyProtocolVersion.V2, HAProxyCommand.LOCAL, HAProxyProxiedProtocol.UNKNOWN, - null, null, 0, 0); - assertTrue(ch.writeOutbound(message)); - - ByteBuf byteBuf = ch.readOutbound(); - - // header - byte[] headerBytes = new byte[12]; - byteBuf.readBytes(headerBytes); - assertArrayEquals(BINARY_PREFIX, headerBytes); - - // command - byte commandByte = byteBuf.readByte(); - assertEquals(0x02, (commandByte & 0xf0) >> 4); - assertEquals(0x00, commandByte & 0x0f); - - // transport protocol, address family - byte transportByte = byteBuf.readByte(); - assertEquals(0x00, transportByte); - - // source address length - int sourceAddrLength = byteBuf.readUnsignedShort(); - assertEquals(0, sourceAddrLength); - - assertFalse(byteBuf.isReadable()); - - byteBuf.release(); - assertFalse(ch.finish()); - } - - @Test - public void testInvalidIpV4Address() { - String invalidIpv4Address = "192.168.0.1234"; - assertThrows(IllegalArgumentException.class, () -> new HAProxyMessage( - HAProxyProtocolVersion.V1, HAProxyCommand.PROXY, HAProxyProxiedProtocol.TCP4, - invalidIpv4Address, "192.168.0.11", 56324, 443)); - } - - @Test - public void testInvalidIpV6Address() { - String invalidIpv6Address = "2001:0db8:85a3:0000:0000:8a2e:0370:73345"; - assertThrows(IllegalArgumentException.class, () -> new HAProxyMessage( - HAProxyProtocolVersion.V1, HAProxyCommand.PROXY, HAProxyProxiedProtocol.TCP6, - invalidIpv6Address, "1050:0:0:0:5:600:300c:326b", 56324, 443)); - } - - @Test - public void testInvalidUnixAddress() { - String invalidUnixAddress = new String(new byte[UNIX_ADDRESS_BYTES_LENGTH + 1]); - assertThrows(IllegalArgumentException.class, () -> new HAProxyMessage( - HAProxyProtocolVersion.V2, HAProxyCommand.PROXY, HAProxyProxiedProtocol.UNIX_STREAM, - invalidUnixAddress, "/var/run/dst.sock", 0, 0)); - } - - @Test - public void testNullUnixAddress() { - assertThrows(NullPointerException.class, () -> new HAProxyMessage( - HAProxyProtocolVersion.V2, HAProxyCommand.PROXY, HAProxyProxiedProtocol.UNIX_STREAM, - null, null, 0, 0)); - } - - @Test - public void testLongUnixAddress() { - String longUnixAddress = new String(new char[109]).replace("\0", "a"); - assertThrows(IllegalArgumentException.class, () -> new HAProxyMessage( - HAProxyProtocolVersion.V2, HAProxyCommand.PROXY, HAProxyProxiedProtocol.UNIX_STREAM, - "source", longUnixAddress, 0, 0)); - } - - @Test - public void testInvalidUnixPort() { - assertThrows(IllegalArgumentException.class, () -> new HAProxyMessage( - HAProxyProtocolVersion.V2, HAProxyCommand.PROXY, HAProxyProxiedProtocol.UNIX_STREAM, - "/var/run/src.sock", "/var/run/dst.sock", 80, 443)); - } -} diff --git a/codec-http/pom.xml b/codec-http/pom.xml deleted file mode 100644 index f2f93ea21c..0000000000 --- a/codec-http/pom.xml +++ /dev/null @@ -1,101 +0,0 @@ - - - - - 4.0.0 - - io.netty - netty-parent - 5.0.0.Final-SNAPSHOT - - - netty-codec-http - jar - - Netty/Codec/HTTP - - - io.netty.codec.http - - - - - ${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} - - - com.jcraft - jzlib - true - - - org.mockito - mockito-core - - - commons-io - commons-io - - - com.aayushatharva.brotli4j - brotli4j - true - - - com.aayushatharva.brotli4j - native-linux-x86_64 - test - - - com.aayushatharva.brotli4j - native-osx-x86_64 - test - - - com.aayushatharva.brotli4j - native-windows-x86_64 - test - - - com.github.luben - zstd-jni - true - - - - 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 deleted file mode 100644 index 34bca41a29..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/CombinedHttpHeaders.java +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.handler.codec.DefaultHeaders; -import io.netty.handler.codec.Headers; -import io.netty.handler.codec.ValueConverter; -import io.netty.util.HashingStrategy; -import io.netty.util.internal.StringUtil; - -import java.util.Collection; -import java.util.Iterator; -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; - -/** - * Will add multiple values for the same header as single header with a comma separated list of values. - *

- * Please refer to section RFC 7230, 3.2.2. - */ -public class CombinedHttpHeaders extends DefaultHttpHeaders { - public CombinedHttpHeaders(boolean validate) { - super(new CombinedHttpHeadersImpl(CASE_INSENSITIVE_HASHER, valueConverter(validate), nameValidator(validate))); - } - - @Override - public boolean containsValue(CharSequence name, CharSequence value, boolean ignoreCase) { - return super.containsValue(name, StringUtil.trimOws(value), ignoreCase); - } - - private static final class CombinedHttpHeadersImpl - extends DefaultHeaders { - /** - * An estimate of the size of a header value. - */ - private static final int VALUE_LENGTH_ESTIMATE = 10; - private CsvValueEscaper objectEscaper; - private CsvValueEscaper charSequenceEscaper; - - private CsvValueEscaper objectEscaper() { - if (objectEscaper == null) { - objectEscaper = value -> StringUtil.escapeCsv(valueConverter().convertObject(value), true); - } - return objectEscaper; - } - - private CsvValueEscaper charSequenceEscaper() { - if (charSequenceEscaper == null) { - charSequenceEscaper = value -> StringUtil.escapeCsv(value, true); - } - return charSequenceEscaper; - } - - CombinedHttpHeadersImpl(HashingStrategy nameHashingStrategy, - ValueConverter valueConverter, - io.netty.handler.codec.DefaultHeaders.NameValidator nameValidator) { - super(nameHashingStrategy, valueConverter, nameValidator); - } - - @Override - public Iterator valueIterator(CharSequence name) { - Iterator itr = super.valueIterator(name); - if (!itr.hasNext() || cannotBeCombined(name)) { - return itr; - } - Iterator unescapedItr = unescapeCsvFields(itr.next()).iterator(); - if (itr.hasNext()) { - throw new IllegalStateException("CombinedHttpHeaders should only have one value"); - } - return unescapedItr; - } - - @Override - public List getAll(CharSequence name) { - List values = super.getAll(name); - if (values.isEmpty() || cannotBeCombined(name)) { - return values; - } - if (values.size() != 1) { - throw new IllegalStateException("CombinedHttpHeaders should only have one value"); - } - return unescapeCsvFields(values.get(0)); - } - - @Override - public CombinedHttpHeadersImpl add(Headers headers) { - // Override the fast-copy mechanism used by DefaultHeaders - if (headers == this) { - throw new IllegalArgumentException("can't add to itself."); - } - if (headers instanceof CombinedHttpHeadersImpl) { - if (isEmpty()) { - // Can use the fast underlying copy - addImpl(headers); - } else { - // Values are already escaped so don't escape again - for (Map.Entry header : headers) { - addEscapedValue(header.getKey(), header.getValue()); - } - } - } else { - for (Map.Entry header : headers) { - add(header.getKey(), header.getValue()); - } - } - return this; - } - - @Override - public CombinedHttpHeadersImpl set(Headers headers) { - if (headers == this) { - return this; - } - clear(); - return add(headers); - } - - @Override - public CombinedHttpHeadersImpl setAll(Headers headers) { - if (headers == this) { - return this; - } - for (CharSequence key : headers.names()) { - remove(key); - } - return add(headers); - } - - @Override - public CombinedHttpHeadersImpl add(CharSequence name, CharSequence value) { - return addEscapedValue(name, charSequenceEscaper().escape(value)); - } - - @Override - public CombinedHttpHeadersImpl add(CharSequence name, CharSequence... values) { - return addEscapedValue(name, commaSeparate(charSequenceEscaper(), values)); - } - - @Override - public CombinedHttpHeadersImpl add(CharSequence name, Iterable values) { - return addEscapedValue(name, commaSeparate(charSequenceEscaper(), values)); - } - - @Override - public CombinedHttpHeadersImpl addObject(CharSequence name, Object value) { - return addEscapedValue(name, commaSeparate(objectEscaper(), value)); - } - - @Override - public CombinedHttpHeadersImpl addObject(CharSequence name, Iterable values) { - return addEscapedValue(name, commaSeparate(objectEscaper(), values)); - } - - @Override - public CombinedHttpHeadersImpl addObject(CharSequence name, Object... values) { - return addEscapedValue(name, commaSeparate(objectEscaper(), values)); - } - - @Override - public CombinedHttpHeadersImpl set(CharSequence name, CharSequence... values) { - super.set(name, commaSeparate(charSequenceEscaper(), values)); - return this; - } - - @Override - public CombinedHttpHeadersImpl set(CharSequence name, Iterable values) { - super.set(name, commaSeparate(charSequenceEscaper(), values)); - return this; - } - - @Override - public CombinedHttpHeadersImpl setObject(CharSequence name, Object value) { - super.set(name, commaSeparate(objectEscaper(), value)); - return this; - } - - @Override - public CombinedHttpHeadersImpl setObject(CharSequence name, Object... values) { - super.set(name, commaSeparate(objectEscaper(), values)); - return this; - } - - @Override - public CombinedHttpHeadersImpl setObject(CharSequence name, Iterable values) { - super.set(name, commaSeparate(objectEscaper(), 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 || cannotBeCombined(name)) { - super.add(name, escapedValue); - } else { - super.set(name, commaSeparateEscapedValues(currentValue, escapedValue)); - } - return this; - } - - private static CharSequence commaSeparate(CsvValueEscaper escaper, T... values) { - StringBuilder sb = new StringBuilder(values.length * VALUE_LENGTH_ESTIMATE); - if (values.length > 0) { - int end = values.length - 1; - for (int i = 0; i < end; i++) { - sb.append(escaper.escape(values[i])).append(COMMA); - } - sb.append(escaper.escape(values[end])); - } - return sb; - } - - private static CharSequence commaSeparate(CsvValueEscaper escaper, Iterable values) { - @SuppressWarnings("rawtypes") - final StringBuilder sb = values instanceof Collection - ? new StringBuilder(((Collection) values).size() * VALUE_LENGTH_ESTIMATE) : new StringBuilder(); - Iterator iterator = values.iterator(); - if (iterator.hasNext()) { - T next = iterator.next(); - while (iterator.hasNext()) { - sb.append(escaper.escape(next)).append(COMMA); - next = iterator.next(); - } - sb.append(escaper.escape(next)); - } - return sb; - } - - private static CharSequence commaSeparateEscapedValues(CharSequence currentValue, CharSequence value) { - return new StringBuilder(currentValue.length() + 1 + value.length()) - .append(currentValue) - .append(COMMA) - .append(value); - } - - /** - * Escapes comma separated values (CSV). - * - * @param The type that a concrete implementation handles - */ - private interface CsvValueEscaper { - /** - * Appends the value to the specified {@link StringBuilder}, escaping if necessary. - * - * @param value the value to be appended, escaped if necessary - */ - CharSequence escape(T value); - } - } -} 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 deleted file mode 100644 index 8ceaf4b19d..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/ComposedLastHttpContent.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.handler.codec.DecoderResult; - - -final class ComposedLastHttpContent implements LastHttpContent { - private final HttpHeaders trailingHeaders; - private DecoderResult result; - - ComposedLastHttpContent(HttpHeaders trailingHeaders) { - this.trailingHeaders = trailingHeaders; - } - - ComposedLastHttpContent(HttpHeaders trailingHeaders, DecoderResult result) { - this(trailingHeaders); - this.result = result; - } - - @Override - public HttpHeaders trailingHeaders() { - return trailingHeaders; - } - - @Override - public LastHttpContent copy() { - LastHttpContent content = new DefaultLastHttpContent(Unpooled.EMPTY_BUFFER); - content.trailingHeaders().set(trailingHeaders()); - return content; - } - - @Override - public LastHttpContent duplicate() { - return copy(); - } - - @Override - public LastHttpContent retainedDuplicate() { - return copy(); - } - - @Override - public LastHttpContent replace(ByteBuf content) { - final LastHttpContent dup = new DefaultLastHttpContent(content); - dup.trailingHeaders().setAll(trailingHeaders()); - return dup; - } - - @Override - public LastHttpContent retain(int increment) { - return this; - } - - @Override - public LastHttpContent retain() { - return this; - } - - @Override - public LastHttpContent touch() { - return this; - } - - @Override - public LastHttpContent touch(Object hint) { - return this; - } - - @Override - public ByteBuf content() { - return Unpooled.EMPTY_BUFFER; - } - - @Override - public DecoderResult decoderResult() { - return result; - } - - @Override - public DecoderResult getDecoderResult() { - return decoderResult(); - } - - @Override - public void setDecoderResult(DecoderResult result) { - this.result = result; - } - - @Override - public int refCnt() { - return 1; - } - - @Override - public boolean release() { - return false; - } - - @Override - public boolean release(int decrement) { - return false; - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/CompressionEncoderFactory.java b/codec-http/src/main/java/io/netty/handler/codec/http/CompressionEncoderFactory.java deleted file mode 100644 index f3d361d9e7..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/CompressionEncoderFactory.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.MessageToByteEncoder; - -/** - * Compression Encoder Factory for create {@link MessageToByteEncoder} - * used to compress http content - */ -interface CompressionEncoderFactory { - MessageToByteEncoder createEncoder(); -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/DefaultFullHttpRequest.java b/codec-http/src/main/java/io/netty/handler/codec/http/DefaultFullHttpRequest.java deleted file mode 100644 index 841f4efad6..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/DefaultFullHttpRequest.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.buffer.Unpooled; -import io.netty.util.IllegalReferenceCountException; -import static java.util.Objects.requireNonNull; - -/** - * Default implementation of {@link FullHttpRequest}. - */ -public class DefaultFullHttpRequest extends DefaultHttpRequest implements FullHttpRequest { - private final ByteBuf content; - private final HttpHeaders trailingHeader; - - /** - * Used to cache the value of the hash code and avoid {@link IllegalReferenceCountException}. - */ - private int hash; - - public DefaultFullHttpRequest(HttpVersion httpVersion, HttpMethod method, String uri) { - this(httpVersion, method, uri, Unpooled.buffer(0)); - } - - public DefaultFullHttpRequest(HttpVersion httpVersion, HttpMethod method, String uri, ByteBuf content) { - this(httpVersion, method, uri, content, true); - } - - public DefaultFullHttpRequest(HttpVersion httpVersion, HttpMethod method, String uri, boolean validateHeaders) { - this(httpVersion, method, uri, Unpooled.buffer(0), validateHeaders); - } - - public DefaultFullHttpRequest(HttpVersion httpVersion, HttpMethod method, String uri, - ByteBuf content, boolean validateHeaders) { - super(httpVersion, method, uri, validateHeaders); - this.content = requireNonNull(content, "content"); - trailingHeader = new DefaultHttpHeaders(validateHeaders); - } - - public DefaultFullHttpRequest(HttpVersion httpVersion, HttpMethod method, String uri, - ByteBuf content, HttpHeaders headers, HttpHeaders trailingHeader) { - super(httpVersion, method, uri, headers); - this.content = requireNonNull(content, "content"); - this.trailingHeader = requireNonNull(trailingHeader, "trailingHeader"); - } - - @Override - public HttpHeaders trailingHeaders() { - return trailingHeader; - } - - @Override - public ByteBuf content() { - return content; - } - - @Override - public int refCnt() { - return content.refCnt(); - } - - @Override - public FullHttpRequest retain() { - content.retain(); - return this; - } - - @Override - public FullHttpRequest retain(int increment) { - content.retain(increment); - return this; - } - - @Override - public FullHttpRequest touch() { - content.touch(); - return this; - } - - @Override - public FullHttpRequest touch(Object hint) { - content.touch(hint); - return this; - } - - @Override - public boolean release() { - return content.release(); - } - - @Override - public boolean release(int decrement) { - return content.release(decrement); - } - - @Override - public FullHttpRequest setProtocolVersion(HttpVersion version) { - super.setProtocolVersion(version); - return this; - } - - @Override - public FullHttpRequest setMethod(HttpMethod method) { - super.setMethod(method); - return this; - } - - @Override - public FullHttpRequest setUri(String uri) { - super.setUri(uri); - return this; - } - - @Override - public FullHttpRequest copy() { - return replace(content().copy()); - } - - @Override - public FullHttpRequest duplicate() { - return replace(content().duplicate()); - } - - @Override - public FullHttpRequest retainedDuplicate() { - return replace(content().retainedDuplicate()); - } - - @Override - public FullHttpRequest replace(ByteBuf content) { - FullHttpRequest request = new DefaultFullHttpRequest(protocolVersion(), method(), uri(), content, - headers().copy(), trailingHeaders().copy()); - request.setDecoderResult(decoderResult()); - return request; - } - - @Override - public int hashCode() { - int hash = this.hash; - if (hash == 0) { - if (ByteBufUtil.isAccessible(content())) { - try { - hash = 31 + content().hashCode(); - } catch (IllegalReferenceCountException ignored) { - // Handle race condition between checking refCnt() == 0 and using the object. - hash = 31; - } - } else { - hash = 31; - } - hash = 31 * hash + trailingHeaders().hashCode(); - hash = 31 * hash + super.hashCode(); - this.hash = hash; - } - return hash; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof DefaultFullHttpRequest)) { - return false; - } - - DefaultFullHttpRequest other = (DefaultFullHttpRequest) o; - - return super.equals(other) && - content().equals(other.content()) && - trailingHeaders().equals(other.trailingHeaders()); - } - - @Override - public String toString() { - return HttpMessageUtil.appendFullRequest(new StringBuilder(256), this).toString(); - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/DefaultFullHttpResponse.java b/codec-http/src/main/java/io/netty/handler/codec/http/DefaultFullHttpResponse.java deleted file mode 100644 index 180af3137e..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/DefaultFullHttpResponse.java +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.buffer.Unpooled; -import io.netty.util.IllegalReferenceCountException; - -import static java.util.Objects.requireNonNull; - -/** - * Default implementation of a {@link FullHttpResponse}. - */ -public class DefaultFullHttpResponse extends DefaultHttpResponse implements FullHttpResponse { - - private final ByteBuf content; - private final HttpHeaders trailingHeaders; - - /** - * Used to cache the value of the hash code and avoid {@link IllegalReferenceCountException}. - */ - private int hash; - - public DefaultFullHttpResponse(HttpVersion version, HttpResponseStatus status) { - this(version, status, Unpooled.buffer(0)); - } - - public DefaultFullHttpResponse(HttpVersion version, HttpResponseStatus status, ByteBuf content) { - this(version, status, content, true); - } - - public DefaultFullHttpResponse(HttpVersion version, HttpResponseStatus status, boolean validateHeaders) { - this(version, status, Unpooled.buffer(0), validateHeaders, false); - } - - public DefaultFullHttpResponse(HttpVersion version, HttpResponseStatus status, boolean validateHeaders, - boolean singleFieldHeaders) { - this(version, status, Unpooled.buffer(0), validateHeaders, singleFieldHeaders); - } - - public DefaultFullHttpResponse(HttpVersion version, HttpResponseStatus status, - ByteBuf content, boolean validateHeaders) { - this(version, status, content, validateHeaders, false); - } - - public DefaultFullHttpResponse(HttpVersion version, HttpResponseStatus status, - ByteBuf content, boolean validateHeaders, boolean singleFieldHeaders) { - super(version, status, validateHeaders, singleFieldHeaders); - this.content = requireNonNull(content, "content"); - this.trailingHeaders = singleFieldHeaders ? new CombinedHttpHeaders(validateHeaders) - : new DefaultHttpHeaders(validateHeaders); - } - - public DefaultFullHttpResponse(HttpVersion version, HttpResponseStatus status, - ByteBuf content, HttpHeaders headers, HttpHeaders trailingHeaders) { - super(version, status, headers); - this.content = requireNonNull(content, "content"); - this.trailingHeaders = requireNonNull(trailingHeaders, "trailingHeaders"); - } - - @Override - public HttpHeaders trailingHeaders() { - return trailingHeaders; - } - - @Override - public ByteBuf content() { - return content; - } - - @Override - public int refCnt() { - return content.refCnt(); - } - - @Override - public FullHttpResponse retain() { - content.retain(); - return this; - } - - @Override - public FullHttpResponse retain(int increment) { - content.retain(increment); - return this; - } - - @Override - public FullHttpResponse touch() { - content.touch(); - return this; - } - - @Override - public FullHttpResponse touch(Object hint) { - content.touch(hint); - return this; - } - - @Override - public boolean release() { - return content.release(); - } - - @Override - public boolean release(int decrement) { - return content.release(decrement); - } - - @Override - public FullHttpResponse setProtocolVersion(HttpVersion version) { - super.setProtocolVersion(version); - return this; - } - - @Override - public FullHttpResponse setStatus(HttpResponseStatus status) { - super.setStatus(status); - return this; - } - - @Override - public FullHttpResponse copy() { - return replace(content().copy()); - } - - @Override - public FullHttpResponse duplicate() { - return replace(content().duplicate()); - } - - @Override - public FullHttpResponse retainedDuplicate() { - return replace(content().retainedDuplicate()); - } - - @Override - public FullHttpResponse replace(ByteBuf content) { - FullHttpResponse response = new DefaultFullHttpResponse(protocolVersion(), status(), content, - headers().copy(), trailingHeaders().copy()); - response.setDecoderResult(decoderResult()); - return response; - } - - @Override - public int hashCode() { - int hash = this.hash; - if (hash == 0) { - if (ByteBufUtil.isAccessible(content())) { - try { - hash = 31 + content().hashCode(); - } catch (IllegalReferenceCountException ignored) { - // Handle race condition between checking refCnt() == 0 and using the object. - hash = 31; - } - } else { - hash = 31; - } - hash = 31 * hash + trailingHeaders().hashCode(); - hash = 31 * hash + super.hashCode(); - this.hash = hash; - } - return hash; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof DefaultFullHttpResponse)) { - return false; - } - - DefaultFullHttpResponse other = (DefaultFullHttpResponse) o; - - return super.equals(other) && - content().equals(other.content()) && - trailingHeaders().equals(other.trailingHeaders()); - } - - @Override - public String toString() { - return HttpMessageUtil.appendFullResponse(new StringBuilder(256), this).toString(); - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpContent.java b/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpContent.java deleted file mode 100644 index 5680b4fc83..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpContent.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.util.internal.StringUtil; - -/** - * The default {@link HttpContent} implementation. - */ -public class DefaultHttpContent extends DefaultHttpObject implements HttpContent { - - private final ByteBuf content; - - /** - * Creates a new instance with the specified chunk content. - */ - public DefaultHttpContent(ByteBuf content) { - requireNonNull(content, "content"); - this.content = content; - } - - @Override - public ByteBuf content() { - return content; - } - - @Override - public HttpContent copy() { - return replace(content.copy()); - } - - @Override - public HttpContent duplicate() { - return replace(content.duplicate()); - } - - @Override - public HttpContent retainedDuplicate() { - return replace(content.retainedDuplicate()); - } - - @Override - public HttpContent replace(ByteBuf content) { - return new DefaultHttpContent(content); - } - - @Override - public int refCnt() { - return content.refCnt(); - } - - @Override - public HttpContent retain() { - content.retain(); - return this; - } - - @Override - public HttpContent retain(int increment) { - content.retain(increment); - return this; - } - - @Override - public HttpContent touch() { - content.touch(); - return this; - } - - @Override - public HttpContent touch(Object hint) { - content.touch(hint); - return this; - } - - @Override - public boolean release() { - return content.release(); - } - - @Override - public boolean release(int decrement) { - return content.release(decrement); - } - - @Override - public String toString() { - return StringUtil.simpleClassName(this) + - "(data: " + content() + ", decoderResult: " + decoderResult() + ')'; - } -} 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 deleted file mode 100644 index 71bbefaf20..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpHeaders.java +++ /dev/null @@ -1,503 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.handler.codec.CharSequenceValueConverter; -import io.netty.handler.codec.DateFormatter; -import io.netty.handler.codec.DefaultHeaders; -import io.netty.handler.codec.DefaultHeaders.NameValidator; -import io.netty.handler.codec.DefaultHeadersImpl; -import io.netty.handler.codec.HeadersUtils; -import io.netty.handler.codec.ValueConverter; -import io.netty.util.AsciiString; -import io.netty.util.ByteProcessor; - -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Collections; -import java.util.Date; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import static io.netty.util.AsciiString.CASE_INSENSITIVE_HASHER; -import static io.netty.util.AsciiString.CASE_SENSITIVE_HASHER; - -/** - * Default implementation of {@link HttpHeaders}. - */ -public class DefaultHttpHeaders extends HttpHeaders { - private static final int HIGHEST_INVALID_VALUE_CHAR_MASK = ~15; - private static final ByteProcessor HEADER_NAME_VALIDATOR = value -> { - validateHeaderNameElement(value); - return true; - }; - static final NameValidator HttpNameValidator = name -> { - if (name == null || name.length() == 0) { - throw new IllegalArgumentException("empty headers are not allowed [" + name + ']'); - } - if (name instanceof AsciiString) { - ((AsciiString) name).forEachByte(HEADER_NAME_VALIDATOR); - } else { - // Go through each character in the name - for (int index = 0; index < name.length(); ++index) { - validateHeaderNameElement(name.charAt(index)); - } - } - }; - - private final DefaultHeaders headers; - - public DefaultHttpHeaders() { - this(true); - } - - /** - * Warning! Setting {@code validate} to {@code false} will mean that Netty won't - * validate & protect against user-supplied header values that are malicious. - * This can leave your server implementation vulnerable to - * - * CWE-113: Improper Neutralization of CRLF Sequences in HTTP Headers ('HTTP Response Splitting') - * . - * When disabling this validation, it is the responsibility of the caller to ensure that the values supplied - * do not contain a non-url-escaped carriage return (CR) and/or line feed (LF) characters. - * - * @param validate Should Netty validate Header values to ensure they aren't malicious. - */ - public DefaultHttpHeaders(boolean validate) { - this(validate, nameValidator(validate)); - } - - protected DefaultHttpHeaders(boolean validate, NameValidator nameValidator) { - this(new DefaultHeadersImpl<>(CASE_INSENSITIVE_HASHER, - valueConverter(validate), - nameValidator)); - } - - protected DefaultHttpHeaders(DefaultHeaders headers) { - this.headers = headers; - } - - @Override - public HttpHeaders add(HttpHeaders headers) { - if (headers instanceof DefaultHttpHeaders) { - this.headers.add(((DefaultHttpHeaders) headers).headers); - return this; - } else { - return super.add(headers); - } - } - - @Override - public HttpHeaders set(HttpHeaders headers) { - if (headers instanceof DefaultHttpHeaders) { - this.headers.set(((DefaultHttpHeaders) headers).headers); - return this; - } else { - return super.set(headers); - } - } - - @Override - public HttpHeaders add(String name, Object value) { - headers.addObject(name, value); - return this; - } - - @Override - public HttpHeaders add(CharSequence name, Object value) { - headers.addObject(name, value); - return this; - } - - @Override - public HttpHeaders add(String name, Iterable values) { - headers.addObject(name, values); - return this; - } - - @Override - public HttpHeaders add(CharSequence name, Iterable values) { - headers.addObject(name, values); - return this; - } - - @Override - public HttpHeaders addInt(CharSequence name, int value) { - headers.addInt(name, value); - return this; - } - - @Override - public HttpHeaders addShort(CharSequence name, short value) { - headers.addShort(name, value); - return this; - } - - @Override - public HttpHeaders remove(String name) { - headers.remove(name); - return this; - } - - @Override - public HttpHeaders remove(CharSequence name) { - headers.remove(name); - return this; - } - - @Override - public HttpHeaders set(String name, Object value) { - headers.setObject(name, value); - return this; - } - - @Override - public HttpHeaders set(CharSequence name, Object value) { - headers.setObject(name, value); - return this; - } - - @Override - public HttpHeaders set(String name, Iterable values) { - headers.setObject(name, values); - return this; - } - - @Override - public HttpHeaders set(CharSequence name, Iterable values) { - headers.setObject(name, values); - return this; - } - - @Override - public HttpHeaders setInt(CharSequence name, int value) { - headers.setInt(name, value); - return this; - } - - @Override - public HttpHeaders setShort(CharSequence name, short value) { - headers.setShort(name, value); - return this; - } - - @Override - public HttpHeaders clear() { - headers.clear(); - return this; - } - - @Override - public String get(String name) { - return get((CharSequence) name); - } - - @Override - public String get(CharSequence name) { - return HeadersUtils.getAsString(headers, name); - } - - @Override - public Integer getInt(CharSequence name) { - return headers.getInt(name); - } - - @Override - public int getInt(CharSequence name, int defaultValue) { - return headers.getInt(name, defaultValue); - } - - @Override - public Short getShort(CharSequence name) { - return headers.getShort(name); - } - - @Override - public short getShort(CharSequence name, short defaultValue) { - return headers.getShort(name, defaultValue); - } - - @Override - public Long getTimeMillis(CharSequence name) { - return headers.getTimeMillis(name); - } - - @Override - public long getTimeMillis(CharSequence name, long defaultValue) { - return headers.getTimeMillis(name, defaultValue); - } - - @Override - public List getAll(String name) { - return getAll((CharSequence) name); - } - - @Override - public List getAll(CharSequence name) { - return HeadersUtils.getAllAsString(headers, name); - } - - @Override - public List> entries() { - if (isEmpty()) { - return Collections.emptyList(); - } - List> entriesConverted = new ArrayList<>( - headers.size()); - for (Entry entry : this) { - entriesConverted.add(entry); - } - return entriesConverted; - } - - @Deprecated - @Override - public Iterator> iterator() { - return HeadersUtils.iteratorAsString(headers); - } - - @Override - public Iterator> iteratorCharSequence() { - return headers.iterator(); - } - - @Override - public Iterator valueStringIterator(CharSequence name) { - final Iterator itr = valueCharSequenceIterator(name); - return new Iterator() { - @Override - public boolean hasNext() { - return itr.hasNext(); - } - - @Override - public String next() { - return itr.next().toString(); - } - - @Override - public void remove() { - itr.remove(); - } - }; - } - - @Override - public Iterator valueCharSequenceIterator(CharSequence name) { - return headers.valueIterator(name); - } - - @Override - public boolean contains(String name) { - return contains((CharSequence) name); - } - - @Override - public boolean contains(CharSequence name) { - return headers.contains(name); - } - - @Override - public boolean isEmpty() { - return headers.isEmpty(); - } - - @Override - public int size() { - return headers.size(); - } - - @Override - public boolean contains(String name, String value, boolean ignoreCase) { - return contains((CharSequence) name, (CharSequence) value, ignoreCase); - } - - @Override - public boolean contains(CharSequence name, CharSequence value, boolean ignoreCase) { - return headers.contains(name, value, ignoreCase ? CASE_INSENSITIVE_HASHER : CASE_SENSITIVE_HASHER); - } - - @Override - public Set names() { - return HeadersUtils.namesAsString(headers); - } - - @Override - public boolean equals(Object o) { - return o instanceof DefaultHttpHeaders - && headers.equals(((DefaultHttpHeaders) o).headers, CASE_SENSITIVE_HASHER); - } - - @Override - public int hashCode() { - return headers.hashCode(CASE_SENSITIVE_HASHER); - } - - @Override - public HttpHeaders copy() { - return new DefaultHttpHeaders(headers.copy()); - } - - private static void validateHeaderNameElement(byte value) { - switch (value) { - case 0x00: - case '\t': - case '\n': - case 0x0b: - case '\f': - case '\r': - case ' ': - case ',': - case ':': - case ';': - case '=': - throw new IllegalArgumentException( - "a header name cannot contain the following prohibited characters: =,;: \\t\\r\\n\\v\\f: " + - 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); - } - } - } - - private static void validateHeaderNameElement(char value) { - switch (value) { - case 0x00: - case '\t': - case '\n': - case 0x0b: - case '\f': - case '\r': - case ' ': - case ',': - case ':': - case ';': - case '=': - throw new IllegalArgumentException( - "a header name cannot contain the following prohibited characters: =,;: \\t\\r\\n\\v\\f: " + - value); - default: - // Check to see if the character is not an ASCII character, or invalid - if (value > 127) { - throw new IllegalArgumentException("a header name cannot contain non-ASCII character: " + - value); - } - } - } - - static ValueConverter valueConverter(boolean validate) { - return validate ? HeaderValueConverterAndValidator.INSTANCE : HeaderValueConverter.INSTANCE; - } - - @SuppressWarnings("unchecked") - static NameValidator nameValidator(boolean validate) { - return validate ? HttpNameValidator : NameValidator.NOT_NULL; - } - - private static class HeaderValueConverter extends CharSequenceValueConverter { - static final HeaderValueConverter INSTANCE = new HeaderValueConverter(); - - @Override - public CharSequence convertObject(Object value) { - if (value instanceof CharSequence) { - return (CharSequence) value; - } - if (value instanceof Date) { - return DateFormatter.format((Date) value); - } - if (value instanceof Calendar) { - return DateFormatter.format(((Calendar) value).getTime()); - } - return value.toString(); - } - } - - private static final class HeaderValueConverterAndValidator extends HeaderValueConverter { - static final HeaderValueConverterAndValidator INSTANCE = new HeaderValueConverterAndValidator(); - - @Override - public CharSequence convertObject(Object value) { - CharSequence seq = super.convertObject(value); - int state = 0; - // Start looping through each of the character - for (int index = 0; index < seq.length(); index++) { - state = validateValueChar(seq, state, seq.charAt(index)); - } - - if (state != 0) { - throw new IllegalArgumentException("a header value must not end with '\\r' or '\\n':" + seq); - } - return seq; - } - - private static int validateValueChar(CharSequence seq, int state, char character) { - /* - * State: - * 0: Previous character was neither CR nor LF - * 1: The previous character was CR - * 2: The previous character was LF - */ - if ((character & HIGHEST_INVALID_VALUE_CHAR_MASK) == 0) { - // Check the absolutely prohibited characters. - switch (character) { - case 0x0: // NULL - throw new IllegalArgumentException("a header value contains a prohibited character '\0': " + seq); - case 0x0b: // Vertical tab - throw new IllegalArgumentException("a header value contains a prohibited character '\\v': " + seq); - case '\f': - throw new IllegalArgumentException("a header value contains a prohibited character '\\f': " + seq); - default: - break; - } - } - - // Check the CRLF (HT | SP) pattern - switch (state) { - case 0: - switch (character) { - case '\r': - return 1; - case '\n': - return 2; - default: - break; - } - break; - case 1: - if (character == '\n') { - return 2; - } - throw new IllegalArgumentException("only '\\n' is allowed after '\\r': " + seq); - case 2: - switch (character) { - case '\t': - case ' ': - return 0; - default: - throw new IllegalArgumentException("only ' ' and '\\t' are allowed after '\\n': " + seq); - } - default: - break; - } - return state; - } - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpMessage.java b/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpMessage.java deleted file mode 100644 index e2d083c582..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpMessage.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import static java.util.Objects.requireNonNull; - -/** - * The default {@link HttpMessage} implementation. - */ -public abstract class DefaultHttpMessage extends DefaultHttpObject implements HttpMessage { - private static final int HASH_CODE_PRIME = 31; - private HttpVersion version; - private final HttpHeaders headers; - - /** - * Creates a new instance. - */ - protected DefaultHttpMessage(final HttpVersion version) { - this(version, true, false); - } - - /** - * Creates a new instance. - */ - protected DefaultHttpMessage(final HttpVersion version, boolean validateHeaders, boolean singleFieldHeaders) { - this(version, - singleFieldHeaders ? new CombinedHttpHeaders(validateHeaders) - : new DefaultHttpHeaders(validateHeaders)); - } - - /** - * Creates a new instance. - */ - protected DefaultHttpMessage(final HttpVersion version, HttpHeaders headers) { - this.version = requireNonNull(version, "version"); - this.headers = requireNonNull(headers, "headers"); - } - - @Override - public HttpHeaders headers() { - return headers; - } - - @Override - @Deprecated - public HttpVersion getProtocolVersion() { - return protocolVersion(); - } - - @Override - public HttpVersion protocolVersion() { - return version; - } - - @Override - public int hashCode() { - int result = 1; - result = HASH_CODE_PRIME * result + headers.hashCode(); - result = HASH_CODE_PRIME * result + version.hashCode(); - result = HASH_CODE_PRIME * result + super.hashCode(); - return result; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof DefaultHttpMessage)) { - return false; - } - - DefaultHttpMessage other = (DefaultHttpMessage) o; - - return headers().equals(other.headers()) && - protocolVersion().equals(other.protocolVersion()) && - super.equals(o); - } - - @Override - public HttpMessage setProtocolVersion(HttpVersion version) { - requireNonNull(version, "version"); - this.version = version; - return this; - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpObject.java b/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpObject.java deleted file mode 100644 index 9011d1ceaa..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpObject.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import static java.util.Objects.requireNonNull; - -import io.netty.handler.codec.DecoderResult; - -public class DefaultHttpObject implements HttpObject { - - private static final int HASH_CODE_PRIME = 31; - private DecoderResult decoderResult = DecoderResult.SUCCESS; - - protected DefaultHttpObject() { - // Disallow direct instantiation - } - - @Override - public DecoderResult decoderResult() { - return decoderResult; - } - - @Override - @Deprecated - public DecoderResult getDecoderResult() { - return decoderResult(); - } - - @Override - public void setDecoderResult(DecoderResult decoderResult) { - requireNonNull(decoderResult, "decoderResult"); - this.decoderResult = decoderResult; - } - - @Override - public int hashCode() { - int result = 1; - result = HASH_CODE_PRIME * result + decoderResult.hashCode(); - return result; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof DefaultHttpObject)) { - return false; - } - - DefaultHttpObject other = (DefaultHttpObject) o; - - return decoderResult().equals(other.decoderResult()); - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpRequest.java b/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpRequest.java deleted file mode 100644 index 88aff4f7f4..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpRequest.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import static java.util.Objects.requireNonNull; - -/** - * The default {@link HttpRequest} implementation. - */ -public class DefaultHttpRequest extends DefaultHttpMessage implements HttpRequest { - private static final int HASH_CODE_PRIME = 31; - private HttpMethod method; - private String uri; - - /** - * Creates a new instance. - * - * @param httpVersion the HTTP version of the request - * @param method the HTTP method of the request - * @param uri the URI or path of the request - */ - public DefaultHttpRequest(HttpVersion httpVersion, HttpMethod method, String uri) { - this(httpVersion, method, uri, true); - } - - /** - * Creates a new instance. - * - * @param httpVersion the HTTP version of the request - * @param method the HTTP method of the request - * @param uri the URI or path of the request - * @param validateHeaders validate the header names and values when adding them to the {@link HttpHeaders} - */ - public DefaultHttpRequest(HttpVersion httpVersion, HttpMethod method, String uri, boolean validateHeaders) { - super(httpVersion, validateHeaders, false); - this.method = requireNonNull(method, "method"); - this.uri = requireNonNull(uri, "uri"); - } - - /** - * Creates a new instance. - * - * @param httpVersion the HTTP version of the request - * @param method the HTTP method of the request - * @param uri the URI or path of the request - * @param headers the Headers for this Request - */ - public DefaultHttpRequest(HttpVersion httpVersion, HttpMethod method, String uri, HttpHeaders headers) { - super(httpVersion, headers); - this.method = requireNonNull(method, "method"); - this.uri = requireNonNull(uri, "uri"); - } - - @Override - @Deprecated - public HttpMethod getMethod() { - return method(); - } - - @Override - public HttpMethod method() { - return method; - } - - @Override - @Deprecated - public String getUri() { - return uri(); - } - - @Override - public String uri() { - return uri; - } - - @Override - public HttpRequest setMethod(HttpMethod method) { - requireNonNull(method, "method"); - this.method = method; - return this; - } - - @Override - public HttpRequest setUri(String uri) { - requireNonNull(uri, "uri"); - this.uri = uri; - return this; - } - - @Override - public HttpRequest setProtocolVersion(HttpVersion version) { - super.setProtocolVersion(version); - return this; - } - - @Override - public int hashCode() { - int result = 1; - result = HASH_CODE_PRIME * result + method.hashCode(); - result = HASH_CODE_PRIME * result + uri.hashCode(); - result = HASH_CODE_PRIME * result + super.hashCode(); - return result; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof DefaultHttpRequest)) { - return false; - } - - DefaultHttpRequest other = (DefaultHttpRequest) o; - - return method().equals(other.method()) && - uri().equalsIgnoreCase(other.uri()) && - super.equals(o); - } - - @Override - public String toString() { - return HttpMessageUtil.appendRequest(new StringBuilder(256), this).toString(); - } -} 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 deleted file mode 100644 index a77ecb0f18..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpResponse.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import static java.util.Objects.requireNonNull; - -/** - * The default {@link HttpResponse} implementation. - */ -public class DefaultHttpResponse extends DefaultHttpMessage implements HttpResponse { - - private HttpResponseStatus status; - - /** - * Creates a new instance. - * - * @param version the HTTP version of this response - * @param status the status of this response - */ - public DefaultHttpResponse(HttpVersion version, HttpResponseStatus status) { - this(version, status, true, false); - } - - /** - * Creates a new instance. - * - * @param version the HTTP version of this response - * @param status the status of this response - * @param validateHeaders validate the header names and values when adding them to the {@link HttpHeaders} - */ - public DefaultHttpResponse(HttpVersion version, HttpResponseStatus status, boolean validateHeaders) { - this(version, status, validateHeaders, false); - } - - /** - * Creates a new instance. - * - * @param version the HTTP version of this response - * @param status the status of this response - * @param validateHeaders validate the header names and values when adding them to the {@link HttpHeaders} - * @param singleFieldHeaders {@code true} to check and enforce that headers with the same name are appended - * to the same entry and comma separated. - * See RFC 7230, 3.2.2. - * {@code false} to allow multiple header entries with the same name to - * coexist. - */ - public DefaultHttpResponse(HttpVersion version, HttpResponseStatus status, boolean validateHeaders, - boolean singleFieldHeaders) { - super(version, validateHeaders, singleFieldHeaders); - this.status = requireNonNull(status, "status"); - } - - /** - * Creates a new instance. - * - * @param version the HTTP version of this response - * @param status the status of this response - * @param headers the headers for this HTTP Response - */ - public DefaultHttpResponse(HttpVersion version, HttpResponseStatus status, HttpHeaders headers) { - super(version, headers); - this.status = requireNonNull(status, "status"); - } - - @Override - @Deprecated - public HttpResponseStatus getStatus() { - return status(); - } - - @Override - public HttpResponseStatus status() { - return status; - } - - @Override - public HttpResponse setStatus(HttpResponseStatus status) { - requireNonNull(status, "status"); - this.status = status; - return this; - } - - @Override - public HttpResponse setProtocolVersion(HttpVersion version) { - super.setProtocolVersion(version); - return this; - } - - @Override - 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/main/java/io/netty/handler/codec/http/DefaultLastHttpContent.java b/codec-http/src/main/java/io/netty/handler/codec/http/DefaultLastHttpContent.java deleted file mode 100644 index 1cc1c33e26..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/DefaultLastHttpContent.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.handler.codec.DefaultHeaders.NameValidator; -import io.netty.util.internal.StringUtil; - -import java.util.Map.Entry; - -/** - * The default {@link LastHttpContent} implementation. - */ -public class DefaultLastHttpContent extends DefaultHttpContent implements LastHttpContent { - private final HttpHeaders trailingHeaders; - private final boolean validateHeaders; - - public DefaultLastHttpContent() { - this(Unpooled.buffer(0)); - } - - public DefaultLastHttpContent(ByteBuf content) { - this(content, true); - } - - public DefaultLastHttpContent(ByteBuf content, boolean validateHeaders) { - super(content); - trailingHeaders = new TrailingHttpHeaders(validateHeaders); - this.validateHeaders = validateHeaders; - } - - @Override - public LastHttpContent copy() { - return replace(content().copy()); - } - - @Override - public LastHttpContent duplicate() { - return replace(content().duplicate()); - } - - @Override - public LastHttpContent retainedDuplicate() { - return replace(content().retainedDuplicate()); - } - - @Override - public LastHttpContent replace(ByteBuf content) { - final DefaultLastHttpContent dup = new DefaultLastHttpContent(content, validateHeaders); - dup.trailingHeaders().set(trailingHeaders()); - return dup; - } - - @Override - public LastHttpContent retain(int increment) { - super.retain(increment); - return this; - } - - @Override - public LastHttpContent retain() { - super.retain(); - return this; - } - - @Override - public LastHttpContent touch() { - super.touch(); - return this; - } - - @Override - public LastHttpContent touch(Object hint) { - super.touch(hint); - return this; - } - - @Override - public HttpHeaders trailingHeaders() { - return trailingHeaders; - } - - @Override - public String toString() { - StringBuilder buf = new StringBuilder(super.toString()); - buf.append(StringUtil.NEWLINE); - appendHeaders(buf); - - // Remove the last newline. - buf.setLength(buf.length() - StringUtil.NEWLINE.length()); - return buf.toString(); - } - - private void appendHeaders(StringBuilder buf) { - for (Entry e : trailingHeaders()) { - buf.append(e.getKey()); - buf.append(": "); - buf.append(e.getValue()); - buf.append(StringUtil.NEWLINE); - } - } - - private static final class TrailingHttpHeaders extends DefaultHttpHeaders { - private static final NameValidator TrailerNameValidator = name -> { - DefaultHttpHeaders.HttpNameValidator.validateName(name); - if (HttpHeaderNames.CONTENT_LENGTH.contentEqualsIgnoreCase(name) - || HttpHeaderNames.TRANSFER_ENCODING.contentEqualsIgnoreCase(name) - || HttpHeaderNames.TRAILER.contentEqualsIgnoreCase(name)) { - throw new IllegalArgumentException("prohibited trailing header: " + name); - } - }; - - @SuppressWarnings({ "unchecked" }) - TrailingHttpHeaders(boolean validate) { - super(validate, validate ? TrailerNameValidator : NameValidator.NOT_NULL); - } - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/DelegatingChannelHandlerContext.java b/codec-http/src/main/java/io/netty/handler/codec/http/DelegatingChannelHandlerContext.java deleted file mode 100644 index bd5d85c2ff..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/DelegatingChannelHandlerContext.java +++ /dev/null @@ -1,217 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.api.BufferAllocator; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.util.Attribute; -import io.netty.util.AttributeKey; -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; - -import java.net.SocketAddress; -import java.util.Objects; - -abstract class DelegatingChannelHandlerContext implements ChannelHandlerContext { - - private final ChannelHandlerContext ctx; - - DelegatingChannelHandlerContext(ChannelHandlerContext ctx) { - this.ctx = Objects.requireNonNull(ctx, "ctx"); - } - - @Override - public Channel channel() { - return ctx.channel(); - } - - @Override - public EventExecutor executor() { - return ctx.executor(); - } - - @Override - public String name() { - return ctx.name(); - } - - @Override - public ChannelHandler handler() { - return ctx.handler(); - } - - @Override - public boolean isRemoved() { - return ctx.isRemoved(); - } - - @Override - public ChannelHandlerContext fireChannelRegistered() { - ctx.fireChannelRegistered(); - return this; - } - - @Override - public ChannelHandlerContext fireChannelUnregistered() { - ctx.fireChannelUnregistered(); - return this; - } - - @Override - public ChannelHandlerContext fireChannelActive() { - ctx.fireChannelActive(); - return this; - } - - @Override - public ChannelHandlerContext fireChannelInactive() { - ctx.fireChannelInactive(); - return this; - } - - @Override - public ChannelHandlerContext fireExceptionCaught(Throwable cause) { - ctx.fireExceptionCaught(cause); - return this; - } - - @Override - public ChannelHandlerContext fireUserEventTriggered(Object evt) { - ctx.fireUserEventTriggered(evt); - return this; - } - - @Override - public ChannelHandlerContext fireChannelRead(Object msg) { - - ctx.fireChannelRead(msg); - return this; - } - - @Override - public ChannelHandlerContext fireChannelReadComplete() { - ctx.fireChannelReadComplete(); - return this; - } - - @Override - public ChannelHandlerContext fireChannelWritabilityChanged() { - ctx.fireChannelWritabilityChanged(); - return this; - } - - @Override - public ChannelHandlerContext read() { - ctx.read(); - return this; - } - - @Override - public ChannelHandlerContext flush() { - ctx.flush(); - return this; - } - - @Override - public ChannelPipeline pipeline() { - return ctx.pipeline(); - } - - @Override - public ByteBufAllocator alloc() { - return ctx.alloc(); - } - - @Override - public BufferAllocator bufferAllocator() { - return ctx.bufferAllocator(); - } - - @Deprecated - public Attribute attr(AttributeKey key) { - return ctx.attr(key); - } - - @Deprecated - public boolean hasAttr(AttributeKey key) { - return ctx.hasAttr(key); - } - - @Override - public Future bind(SocketAddress localAddress) { - return ctx.bind(localAddress); - } - - @Override - public Future connect(SocketAddress remoteAddress) { - return ctx.connect(remoteAddress); - } - - @Override - public Future connect(SocketAddress remoteAddress, SocketAddress localAddress) { - return ctx.connect(remoteAddress, localAddress); - } - - @Override - public Future disconnect() { - return ctx.disconnect(); - } - - @Override - public Future close() { - return ctx.close(); - } - - @Override - public Future deregister() { - return ctx.deregister(); - } - - @Override - public Future register() { - return ctx.register(); - } - - @Override - public Future write(Object msg) { - return ctx.write(msg); - } - - @Override - public Future writeAndFlush(Object msg) { - return ctx.writeAndFlush(msg); - } - - @Override - public Promise newPromise() { - return ctx.newPromise(); - } - - @Override - public Future newSucceededFuture() { - return ctx.newSucceededFuture(); - } - - @Override - public Future newFailedFuture(Throwable cause) { - return ctx.newFailedFuture(cause); - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/EmptyHttpHeaders.java b/codec-http/src/main/java/io/netty/handler/codec/http/EmptyHttpHeaders.java deleted file mode 100644 index f223909de7..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/EmptyHttpHeaders.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.http; - -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Map.Entry; -import java.util.Set; - -public class EmptyHttpHeaders extends HttpHeaders { - private static final Iterator> EMPTY_CHARS_ITERATOR = - Collections.>emptyList().iterator(); - - public static final EmptyHttpHeaders INSTANCE = new EmptyHttpHeaders(); - - protected EmptyHttpHeaders() { - } - - @Override - public String get(String name) { - return null; - } - - @Override - public Integer getInt(CharSequence name) { - return null; - } - - @Override - public int getInt(CharSequence name, int defaultValue) { - return defaultValue; - } - - @Override - public Short getShort(CharSequence name) { - return null; - } - - @Override - public short getShort(CharSequence name, short defaultValue) { - return defaultValue; - } - - @Override - public Long getTimeMillis(CharSequence name) { - return null; - } - - @Override - public long getTimeMillis(CharSequence name, long defaultValue) { - return defaultValue; - } - - @Override - public List getAll(String name) { - return Collections.emptyList(); - } - - @Override - public List> entries() { - return Collections.emptyList(); - } - - @Override - public boolean contains(String name) { - return false; - } - - @Override - public boolean isEmpty() { - return true; - } - - @Override - public int size() { - return 0; - } - - @Override - public Set names() { - return Collections.emptySet(); - } - - @Override - public HttpHeaders add(String name, Object value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public HttpHeaders add(String name, Iterable values) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public HttpHeaders addInt(CharSequence name, int value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public HttpHeaders addShort(CharSequence name, short value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public HttpHeaders set(String name, Object value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public HttpHeaders set(String name, Iterable values) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public HttpHeaders setInt(CharSequence name, int value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public HttpHeaders setShort(CharSequence name, short value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public HttpHeaders remove(String name) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public HttpHeaders clear() { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Iterator> iterator() { - return entries().iterator(); - } - - @Override - public Iterator> iteratorCharSequence() { - return EMPTY_CHARS_ITERATOR; - } - -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/FullHttpMessage.java b/codec-http/src/main/java/io/netty/handler/codec/http/FullHttpMessage.java deleted file mode 100644 index 735f4b63f7..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/FullHttpMessage.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.buffer.ByteBuf; - -/** - * Combines {@link HttpMessage} and {@link LastHttpContent} into one - * message. So it represent a complete http message. - */ -public interface FullHttpMessage extends HttpMessage, LastHttpContent { - @Override - FullHttpMessage copy(); - - @Override - FullHttpMessage duplicate(); - - @Override - FullHttpMessage retainedDuplicate(); - - @Override - FullHttpMessage replace(ByteBuf content); - - @Override - FullHttpMessage retain(int increment); - - @Override - FullHttpMessage retain(); - - @Override - FullHttpMessage touch(); - - @Override - FullHttpMessage touch(Object hint); -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/FullHttpRequest.java b/codec-http/src/main/java/io/netty/handler/codec/http/FullHttpRequest.java deleted file mode 100644 index 1db1cec9c7..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/FullHttpRequest.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.buffer.ByteBuf; - -/** - * Combine the {@link HttpRequest} and {@link FullHttpMessage}, so the request is a complete HTTP - * request. - */ -public interface FullHttpRequest extends HttpRequest, FullHttpMessage { - @Override - FullHttpRequest copy(); - - @Override - FullHttpRequest duplicate(); - - @Override - FullHttpRequest retainedDuplicate(); - - @Override - FullHttpRequest replace(ByteBuf content); - - @Override - FullHttpRequest retain(int increment); - - @Override - FullHttpRequest retain(); - - @Override - FullHttpRequest touch(); - - @Override - FullHttpRequest touch(Object hint); - - @Override - FullHttpRequest setProtocolVersion(HttpVersion version); - - @Override - FullHttpRequest setMethod(HttpMethod method); - - @Override - FullHttpRequest setUri(String uri); -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/FullHttpResponse.java b/codec-http/src/main/java/io/netty/handler/codec/http/FullHttpResponse.java deleted file mode 100644 index 9f9be63045..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/FullHttpResponse.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.buffer.ByteBuf; - -/** - * Combination of a {@link HttpResponse} and {@link FullHttpMessage}. - * So it represent a complete http response. - */ -public interface FullHttpResponse extends HttpResponse, FullHttpMessage { - @Override - FullHttpResponse copy(); - - @Override - FullHttpResponse duplicate(); - - @Override - FullHttpResponse retainedDuplicate(); - - @Override - FullHttpResponse replace(ByteBuf content); - - @Override - FullHttpResponse retain(int increment); - - @Override - FullHttpResponse retain(); - - @Override - FullHttpResponse touch(); - - @Override - FullHttpResponse touch(Object hint); - - @Override - FullHttpResponse setProtocolVersion(HttpVersion version); - - @Override - FullHttpResponse setStatus(HttpResponseStatus status); -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpChunkedInput.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpChunkedInput.java deleted file mode 100644 index 7bf7b21111..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpChunkedInput.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.stream.ChunkedInput; - -/** - * A {@link ChunkedInput} that fetches data chunk by chunk for use with HTTP chunked transfers. - *

- * Each chunk from the input data will be wrapped within a {@link HttpContent}. At the end of the input data, - * {@link LastHttpContent} will be written. - *

- * Ensure that your HTTP response header contains {@code Transfer-Encoding: chunked}. - *

- *

- * public void messageReceived(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
- *     HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
- *     response.headers().set(TRANSFER_ENCODING, CHUNKED);
- *     ctx.write(response);
- *
- *     HttpContentChunkedInput httpChunkWriter = new HttpChunkedInput(
- *         new ChunkedFile("/tmp/myfile.txt"));
- *     Future<Void> sendFileFuture = ctx.write(httpChunkWriter);
- * }
- * 
- */ -public class HttpChunkedInput implements ChunkedInput { - - private final ChunkedInput input; - private final LastHttpContent lastHttpContent; - private boolean sentLastChunk; - - /** - * Creates a new instance using the specified input. - * @param input {@link ChunkedInput} containing data to write - */ - public HttpChunkedInput(ChunkedInput input) { - this.input = input; - lastHttpContent = LastHttpContent.EMPTY_LAST_CONTENT; - } - - /** - * Creates a new instance using the specified input. {@code lastHttpContent} will be written as the terminating - * chunk. - * @param input {@link ChunkedInput} containing data to write - * @param lastHttpContent {@link LastHttpContent} that will be written as the terminating chunk. Use this for - * training headers. - */ - public HttpChunkedInput(ChunkedInput input, LastHttpContent lastHttpContent) { - this.input = input; - this.lastHttpContent = lastHttpContent; - } - - @Override - public boolean isEndOfInput() throws Exception { - if (input.isEndOfInput()) { - // Only end of input after last HTTP chunk has been sent - return sentLastChunk; - } else { - return false; - } - } - - @Override - public void close() throws Exception { - input.close(); - } - - @Deprecated - @Override - public HttpContent readChunk(ChannelHandlerContext ctx) throws Exception { - return readChunk(ctx.alloc()); - } - - @Override - public HttpContent readChunk(ByteBufAllocator allocator) throws Exception { - if (input.isEndOfInput()) { - if (sentLastChunk) { - return null; - } else { - // Send last chunk for this input - sentLastChunk = true; - return lastHttpContent; - } - } else { - ByteBuf buf = input.readChunk(allocator); - if (buf == null) { - return null; - } - return new DefaultHttpContent(buf); - } - } - - @Override - public long length() { - return input.length(); - } - - @Override - public long progress() { - return input.progress(); - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpClientCodec.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpClientCodec.java deleted file mode 100644 index 0db29f825b..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpClientCodec.java +++ /dev/null @@ -1,335 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.CombinedChannelDuplexHandler; -import io.netty.handler.codec.PrematureChannelClosureException; -import io.netty.util.ReferenceCountUtil; - -import java.util.ArrayDeque; -import java.util.List; -import java.util.Queue; -import java.util.concurrent.atomic.AtomicLong; - -import static io.netty.handler.codec.http.HttpObjectDecoder.DEFAULT_ALLOW_DUPLICATE_CONTENT_LENGTHS; -import static io.netty.handler.codec.http.HttpObjectDecoder.DEFAULT_MAX_HEADER_SIZE; -import static io.netty.handler.codec.http.HttpObjectDecoder.DEFAULT_MAX_INITIAL_LINE_LENGTH; -import static io.netty.handler.codec.http.HttpObjectDecoder.DEFAULT_VALIDATE_HEADERS; - -/** - * A combination of {@link HttpRequestEncoder} and {@link HttpResponseDecoder} - * which enables easier client side HTTP implementation. {@link HttpClientCodec} - * provides additional state management for HEAD and CONNECT - * requests, which {@link HttpResponseDecoder} lacks. Please refer to - * {@link HttpResponseDecoder} to learn what additional state management needs - * to be done for HEAD and CONNECT and why - * {@link HttpResponseDecoder} can not handle it by itself. - * - * If the {@link Channel} is closed and there are missing responses, - * a {@link PrematureChannelClosureException} is thrown. - * - * @see HttpServerCodec - */ -public final class HttpClientCodec extends CombinedChannelDuplexHandler - implements HttpClientUpgradeHandler.SourceCodec { - public static final boolean DEFAULT_FAIL_ON_MISSING_RESPONSE = false; - public static final boolean DEFAULT_PARSE_HTTP_AFTER_CONNECT_REQUEST = false; - - /** A queue that is used for correlating a request and a response. */ - private final Queue queue = new ArrayDeque<>(); - private final boolean parseHttpAfterConnectRequest; - - /** If true, decoding stops (i.e. pass-through) */ - private boolean done; - - private final AtomicLong requestResponseCounter = new AtomicLong(); - private final boolean failOnMissingResponse; - - /** - * Creates a new instance with the default decoder options - * ({@code maxInitialLineLength (4096}}, {@code maxHeaderSize (8192)}, and - * {@code maxChunkSize (8192)}). - */ - public HttpClientCodec() { - this(DEFAULT_MAX_INITIAL_LINE_LENGTH, DEFAULT_MAX_HEADER_SIZE, DEFAULT_FAIL_ON_MISSING_RESPONSE); - } - - /** - * Creates a new instance with the specified decoder options. - */ - public HttpClientCodec(int maxInitialLineLength, int maxHeaderSize) { - this(maxInitialLineLength, maxHeaderSize, DEFAULT_FAIL_ON_MISSING_RESPONSE); - } - - /** - * Creates a new instance with the specified decoder options. - */ - public HttpClientCodec( - int maxInitialLineLength, int maxHeaderSize, boolean failOnMissingResponse) { - this(maxInitialLineLength, maxHeaderSize, failOnMissingResponse, DEFAULT_VALIDATE_HEADERS); - } - - /** - * Creates a new instance with the specified decoder options. - */ - public HttpClientCodec( - int maxInitialLineLength, int maxHeaderSize, boolean failOnMissingResponse, - boolean validateHeaders) { - this(maxInitialLineLength, maxHeaderSize, failOnMissingResponse, validateHeaders, - DEFAULT_PARSE_HTTP_AFTER_CONNECT_REQUEST); - } - - /** - * Creates a new instance with the specified decoder options. - */ - public HttpClientCodec( - int maxInitialLineLength, int maxHeaderSize, boolean failOnMissingResponse, - boolean validateHeaders, boolean parseHttpAfterConnectRequest) { - init(new Decoder(maxInitialLineLength, maxHeaderSize, validateHeaders), new Encoder()); - this.failOnMissingResponse = failOnMissingResponse; - this.parseHttpAfterConnectRequest = parseHttpAfterConnectRequest; - } - - /** - * Creates a new instance with the specified decoder options. - */ - public HttpClientCodec( - int maxInitialLineLength, int maxHeaderSize, boolean failOnMissingResponse, - boolean validateHeaders, int initialBufferSize) { - this(maxInitialLineLength, maxHeaderSize, failOnMissingResponse, validateHeaders, - initialBufferSize, DEFAULT_PARSE_HTTP_AFTER_CONNECT_REQUEST); - } - - /** - * Creates a new instance with the specified decoder options. - */ - public HttpClientCodec( - int maxInitialLineLength, int maxHeaderSize, boolean failOnMissingResponse, - boolean validateHeaders, int initialBufferSize, boolean parseHttpAfterConnectRequest) { - this(maxInitialLineLength, maxHeaderSize, failOnMissingResponse, validateHeaders, - initialBufferSize, parseHttpAfterConnectRequest, DEFAULT_ALLOW_DUPLICATE_CONTENT_LENGTHS); - } - - /** - * Creates a new instance with the specified decoder options. - */ - public HttpClientCodec( - int maxInitialLineLength, int maxHeaderSize, boolean failOnMissingResponse, - boolean validateHeaders, int initialBufferSize, boolean parseHttpAfterConnectRequest, - boolean allowDuplicateContentLengths) { - init(new Decoder(maxInitialLineLength, maxHeaderSize, validateHeaders, initialBufferSize, - allowDuplicateContentLengths), - new Encoder()); - this.parseHttpAfterConnectRequest = parseHttpAfterConnectRequest; - this.failOnMissingResponse = failOnMissingResponse; - } - - /** - * Prepares to upgrade to another protocol from HTTP. Disables the {@link Encoder}. - */ - @Override - public void prepareUpgradeFrom(ChannelHandlerContext ctx) { - ((Encoder) outboundHandler()).upgraded = true; - } - - /** - * Upgrades to another protocol from HTTP. Removes the {@link Decoder} and {@link Encoder} from - * the pipeline. - */ - @Override - public void upgradeFrom(ChannelHandlerContext ctx) { - final ChannelPipeline p = ctx.pipeline(); - p.remove(this); - } - - public void setSingleDecode(boolean singleDecode) { - inboundHandler().setSingleDecode(singleDecode); - } - - public boolean isSingleDecode() { - return inboundHandler().isSingleDecode(); - } - - private final class Encoder extends HttpRequestEncoder { - - boolean upgraded; - - @Override - protected void encode( - ChannelHandlerContext ctx, Object msg, List out) throws Exception { - - if (upgraded) { - out.add(ReferenceCountUtil.retain(msg)); - return; - } - - if (msg instanceof HttpRequest) { - queue.offer(((HttpRequest) msg).method()); - } - - super.encode(ctx, msg, out); - - if (failOnMissingResponse && !done) { - // check if the request is chunked if so do not increment - if (msg instanceof LastHttpContent) { - // increment as its the last chunk - requestResponseCounter.incrementAndGet(); - } - } - } - } - - private final class Decoder extends HttpResponseDecoder { - - private ChannelHandlerContext context; - - Decoder(int maxInitialLineLength, int maxHeaderSize, boolean validateHeaders) { - super(maxInitialLineLength, maxHeaderSize, validateHeaders); - } - - Decoder(int maxInitialLineLength, int maxHeaderSize, boolean validateHeaders, - int initialBufferSize, boolean allowDuplicateContentLengths) { - super(maxInitialLineLength, maxHeaderSize, validateHeaders, initialBufferSize, - allowDuplicateContentLengths); - } - - @Override - protected void handlerAdded0(ChannelHandlerContext ctx) { - if (failOnMissingResponse) { - context = new DelegatingChannelHandlerContext(ctx) { - @Override - public ChannelHandlerContext fireChannelRead(Object msg) { - decrement(msg); - - super.fireChannelRead(msg); - return this; - } - }; - } else { - context = ctx; - } - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception { - if (done) { - int readable = actualReadableBytes(); - if (readable == 0) { - // if non is readable just return null - // https://github.com/netty/netty/issues/1159 - return; - } - ctx.fireChannelRead(buffer.readBytes(readable)); - } else { - super.decode(context, buffer); - } - } - - private void decrement(Object msg) { - if (msg == null) { - return; - } - - // check if it's an Header and its transfer encoding is not chunked. - if (msg instanceof LastHttpContent) { - requestResponseCounter.decrementAndGet(); - } - } - - @Override - protected boolean isContentAlwaysEmpty(HttpMessage msg) { - // Get the method of the HTTP request that corresponds to the - // current response. - // - // Even if we do not use the method to compare we still need to poll it to ensure we keep - // request / response pairs in sync. - HttpMethod method = queue.poll(); - - final int statusCode = ((HttpResponse) msg).status().code(); - if (statusCode >= 100 && statusCode < 200) { - // An informational response should be excluded from paired comparison. - // Just delegate to super method which has all the needed handling. - return super.isContentAlwaysEmpty(msg); - } - - // If the remote peer did for example send multiple responses for one request (which is not allowed per - // spec but may still be possible) method will be null so guard against it. - if (method != null) { - char firstChar = method.name().charAt(0); - switch (firstChar) { - case 'H': - // According to 4.3, RFC2616: - // All responses to the HEAD request method MUST NOT include a - // message-body, even though the presence of entity-header fields - // might lead one to believe they do. - if (HttpMethod.HEAD.equals(method)) { - return true; - - // The following code was inserted to work around the servers - // that behave incorrectly. It has been commented out - // because it does not work with well behaving servers. - // Please note, even if the 'Transfer-Encoding: chunked' - // header exists in the HEAD response, the response should - // have absolutely no content. - // - //// Interesting edge case: - //// Some poorly implemented servers will send a zero-byte - //// chunk if Transfer-Encoding of the response is 'chunked'. - //// - //// return !msg.isChunked(); - } - break; - case 'C': - // Successful CONNECT request results in a response with empty body. - if (statusCode == 200) { - if (HttpMethod.CONNECT.equals(method)) { - // Proxy connection established - Parse HTTP only if configured by - // parseHttpAfterConnectRequest, else pass through. - if (!parseHttpAfterConnectRequest) { - done = true; - queue.clear(); - } - return true; - } - } - break; - default: - break; - } - } - return super.isContentAlwaysEmpty(msg); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) - throws Exception { - super.channelInactive(ctx); - - if (failOnMissingResponse) { - long missingResponses = requestResponseCounter.get(); - if (missingResponses > 0) { - ctx.fireExceptionCaught(new PrematureChannelClosureException( - "channel gone inactive with " + missingResponses + - " missing response(s)")); - } - } - } - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpClientUpgradeHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpClientUpgradeHandler.java deleted file mode 100644 index 9f0f1fa603..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpClientUpgradeHandler.java +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.AsciiString; -import io.netty.util.concurrent.Future; - -import java.util.Collection; -import java.util.LinkedHashSet; -import java.util.Set; - -import static io.netty.handler.codec.http.HttpResponseStatus.SWITCHING_PROTOCOLS; -import static io.netty.util.ReferenceCountUtil.release; -import static java.util.Objects.requireNonNull; - -/** - * Client-side handler for handling an HTTP upgrade handshake to another protocol. When the first - * HTTP request is sent, this handler will add all appropriate headers to perform an upgrade to the - * new protocol. If the upgrade fails (i.e. response is not 101 Switching Protocols), this handler - * simply removes itself from the pipeline. If the upgrade is successful, upgrades the pipeline to - * the new protocol. - */ -public class HttpClientUpgradeHandler extends HttpObjectAggregator { - - /** - * User events that are fired to notify about upgrade status. - */ - public enum UpgradeEvent { - /** - * The Upgrade request was sent to the server. - */ - UPGRADE_ISSUED, - - /** - * The Upgrade to the new protocol was successful. - */ - UPGRADE_SUCCESSFUL, - - /** - * The Upgrade was unsuccessful due to the server not issuing - * with a 101 Switching Protocols response. - */ - UPGRADE_REJECTED - } - - /** - * The source codec that is used in the pipeline initially. - */ - public interface SourceCodec { - - /** - * Removes or disables the encoder of this codec so that the {@link UpgradeCodec} can send an initial greeting - * (if any). - */ - void prepareUpgradeFrom(ChannelHandlerContext ctx); - - /** - * Removes this codec (i.e. all associated handlers) from the pipeline. - */ - void upgradeFrom(ChannelHandlerContext ctx); - } - - /** - * A codec that the source can be upgraded to. - */ - public interface UpgradeCodec { - /** - * Returns the name of the protocol supported by this codec, as indicated by the {@code 'UPGRADE'} header. - */ - CharSequence protocol(); - - /** - * Sets any protocol-specific headers required to the upgrade request. Returns the names of - * all headers that were added. These headers will be used to populate the CONNECTION header. - */ - Collection setUpgradeHeaders(ChannelHandlerContext ctx, HttpRequest upgradeRequest); - - /** - * Performs an HTTP protocol upgrade from the source codec. This method is responsible for - * adding all handlers required for the new protocol. - * - * @param ctx the context for the current handler. - * @param upgradeResponse the 101 Switching Protocols response that indicates that the server - * has switched to this protocol. - */ - void upgradeTo(ChannelHandlerContext ctx, FullHttpResponse upgradeResponse) throws Exception; - } - - private final SourceCodec sourceCodec; - private final UpgradeCodec upgradeCodec; - private boolean upgradeRequested; - - /** - * Constructs the client upgrade handler. - * - * @param sourceCodec the codec that is being used initially. - * @param upgradeCodec the codec that the client would like to upgrade to. - * @param maxContentLength the maximum length of the aggregated content. - */ - public HttpClientUpgradeHandler(SourceCodec sourceCodec, UpgradeCodec upgradeCodec, - int maxContentLength) { - super(maxContentLength); - requireNonNull(sourceCodec, "sourceCodec"); - requireNonNull(upgradeCodec, "upgradeCodec"); - this.sourceCodec = sourceCodec; - this.upgradeCodec = upgradeCodec; - } - - @Override - public Future write(ChannelHandlerContext ctx, Object msg) { - if (!(msg instanceof HttpRequest)) { - return ctx.write(msg); - } - - if (upgradeRequested) { - return ctx.newFailedFuture(new IllegalStateException( - "Attempting to write HTTP request with upgrade in progress")); - } - - upgradeRequested = true; - setUpgradeRequestHeaders(ctx, (HttpRequest) msg); - - // Continue writing the request. - Future f = ctx.write(msg); - - // Notify that the upgrade request was issued. - ctx.fireUserEventTriggered(UpgradeEvent.UPGRADE_ISSUED); - // Now we wait for the next HTTP response to see if we switch protocols. - return f; - } - - @Override - protected void decode(final ChannelHandlerContext ctx, HttpObject msg) - throws Exception { - FullHttpResponse response = null; - try { - if (!upgradeRequested) { - throw new IllegalStateException("Read HTTP response without requesting protocol switch"); - } - - if (msg instanceof HttpResponse) { - HttpResponse rep = (HttpResponse) msg; - if (!SWITCHING_PROTOCOLS.equals(rep.status())) { - // The server does not support the requested protocol, just remove this handler - // and continue processing HTTP. - // NOTE: not releasing the response since we're letting it propagate to the - // next handler. - ctx.fireUserEventTriggered(UpgradeEvent.UPGRADE_REJECTED); - ctx.fireChannelRead(msg); - removeThisHandler(ctx); - return; - } - } - - if (msg instanceof FullHttpResponse) { - response = (FullHttpResponse) msg; - - // Need to retain since the base class will release after returning from this method. - tryUpgrade(ctx, response.retain()); - } else { - // Call the base class to handle the aggregation of the full request. - super.decode(new DelegatingChannelHandlerContext(ctx) { - @Override - public ChannelHandlerContext fireChannelRead(Object msg) { - FullHttpResponse response = (FullHttpResponse) msg; - tryUpgrade(ctx, response); - return this; - } - }, msg); - } - - } catch (Throwable t) { - release(response); - ctx.fireExceptionCaught(t); - removeThisHandler(ctx); - } - } - - private void tryUpgrade(ChannelHandlerContext ctx, FullHttpResponse response) { - try { - CharSequence upgradeHeader = response.headers().get(HttpHeaderNames.UPGRADE); - if (upgradeHeader != null && !AsciiString.contentEqualsIgnoreCase(upgradeCodec.protocol(), upgradeHeader)) { - throw new IllegalStateException( - "Switching Protocols response with unexpected UPGRADE protocol: " + upgradeHeader); - } - - // Upgrade to the new protocol. - sourceCodec.prepareUpgradeFrom(ctx); - upgradeCodec.upgradeTo(ctx, response); - - // Notify that the upgrade to the new protocol completed successfully. - ctx.fireUserEventTriggered(UpgradeEvent.UPGRADE_SUCCESSFUL); - - // We guarantee UPGRADE_SUCCESSFUL event will be arrived at the next handler - // before http2 setting frame and http response. - sourceCodec.upgradeFrom(ctx); - - // We switched protocols, so we're done with the upgrade response. - // Release it and clear it from the output. - response.release(); - removeThisHandler(ctx); - } catch (Throwable t) { - release(response); - ctx.fireExceptionCaught(t); - removeThisHandler(ctx); - } - } - - private static void removeThisHandler(ChannelHandlerContext ctx) { - ctx.pipeline().remove(ctx.name()); - } - - /** - * Adds all upgrade request headers necessary for an upgrade to the supported protocols. - */ - private void setUpgradeRequestHeaders(ChannelHandlerContext ctx, HttpRequest request) { - // Set the UPGRADE header on the request. - request.headers().set(HttpHeaderNames.UPGRADE, upgradeCodec.protocol()); - - // Add all protocol-specific headers to the request. - Set connectionParts = new LinkedHashSet<>(2); - connectionParts.addAll(upgradeCodec.setUpgradeHeaders(ctx, request)); - - // Set the CONNECTION header from the set of all protocol-specific headers that were added. - StringBuilder builder = new StringBuilder(); - for (CharSequence part : connectionParts) { - builder.append(part); - builder.append(','); - } - builder.append(HttpHeaderValues.UPGRADE); - request.headers().add(HttpHeaderNames.CONNECTION, builder.toString()); - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpConstants.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpConstants.java deleted file mode 100644 index 9bb1f70554..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpConstants.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.util.CharsetUtil; - -import java.nio.charset.Charset; - -public final class HttpConstants { - - /** - * Horizontal space - */ - public static final byte SP = 32; - - /** - * Horizontal tab - */ - public static final byte HT = 9; - - /** - * Carriage return - */ - public static final byte CR = 13; - - /** - * Equals '=' - */ - public static final byte EQUALS = 61; - - /** - * Line feed character - */ - public static final byte LF = 10; - - /** - * Colon ':' - */ - public static final byte COLON = 58; - - /** - * Semicolon ';' - */ - public static final byte SEMICOLON = 59; - - /** - * Comma ',' - */ - public static final byte COMMA = 44; - - /** - * Double quote '"' - */ - public static final byte DOUBLE_QUOTE = '"'; - - /** - * Default character set (UTF-8) - */ - public static final Charset DEFAULT_CHARSET = CharsetUtil.UTF_8; - - /** - * Horizontal space - */ - public static final char SP_CHAR = (char) SP; - - private HttpConstants() { - // Unused - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpContent.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpContent.java deleted file mode 100644 index d6b9e2e335..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpContent.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufHolder; -import io.netty.channel.ChannelPipeline; - -/** - * An HTTP chunk which is used for HTTP chunked transfer-encoding. - * {@link HttpObjectDecoder} generates {@link HttpContent} after - * {@link HttpMessage} when the content is large or the encoding of the content - * is 'chunked. If you prefer not to receive {@link HttpContent} in your handler, - * place {@link HttpObjectAggregator} after {@link HttpObjectDecoder} in the - * {@link ChannelPipeline}. - */ -public interface HttpContent extends HttpObject, ByteBufHolder { - @Override - HttpContent copy(); - - @Override - HttpContent duplicate(); - - @Override - HttpContent retainedDuplicate(); - - @Override - HttpContent replace(ByteBuf content); - - @Override - HttpContent retain(); - - @Override - HttpContent retain(int increment); - - @Override - HttpContent touch(); - - @Override - HttpContent touch(Object hint); -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentCompressor.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentCompressor.java deleted file mode 100644 index 9fb30f768c..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentCompressor.java +++ /dev/null @@ -1,438 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import java.util.HashMap; -import java.util.Map; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.MessageToByteEncoder; -import io.netty.handler.codec.compression.Brotli; -import io.netty.handler.codec.compression.BrotliEncoder; -import io.netty.handler.codec.compression.BrotliOptions; -import io.netty.handler.codec.compression.CompressionOptions; -import io.netty.handler.codec.compression.DeflateOptions; -import io.netty.handler.codec.compression.GzipOptions; -import io.netty.handler.codec.compression.StandardCompressionOptions; -import io.netty.handler.codec.compression.ZlibCodecFactory; -import io.netty.handler.codec.compression.ZlibEncoder; -import io.netty.handler.codec.compression.ZlibWrapper; -import io.netty.handler.codec.compression.Zstd; -import io.netty.handler.codec.compression.ZstdEncoder; -import io.netty.handler.codec.compression.ZstdOptions; -import io.netty.util.internal.ObjectUtil; - -/** - * Compresses an {@link HttpMessage} and an {@link HttpContent} in {@code gzip} or - * {@code deflate} encoding while respecting the {@code "Accept-Encoding"} header. - * If there is no matching encoding, no compression is done. For more - * information on how this handler modifies the message, please refer to - * {@link HttpContentEncoder}. - */ -public class HttpContentCompressor extends HttpContentEncoder { - - private final boolean supportsCompressionOptions; - private final BrotliOptions brotliOptions; - private final GzipOptions gzipOptions; - private final DeflateOptions deflateOptions; - private final ZstdOptions zstdOptions; - - private final int compressionLevel; - private final int windowBits; - private final int memLevel; - private final int contentSizeThreshold; - private ChannelHandlerContext ctx; - private final Map factories; - - /** - * Creates a new handler with the default compression level (6), - * default window size (15) and default memory level (8). - */ - public HttpContentCompressor() { - this(6); - } - - /** - * Creates a new handler with the specified compression level, default - * window size (15) and default memory level (8). - * - * @param compressionLevel - * {@code 1} yields the fastest compression and {@code 9} yields the - * best compression. {@code 0} means no compression. The default - * compression level is {@code 6}. - */ - @Deprecated - public HttpContentCompressor(int compressionLevel) { - this(compressionLevel, 15, 8, 0); - } - - /** - * Creates a new handler with the specified compression level, window size, - * and memory level.. - * - * @param compressionLevel - * {@code 1} yields the fastest compression and {@code 9} yields the - * best compression. {@code 0} means no compression. The default - * compression level is {@code 6}. - * @param windowBits - * The base two logarithm of the size of the history buffer. The - * value should be in the range {@code 9} to {@code 15} inclusive. - * Larger values result in better compression at the expense of - * memory usage. The default value is {@code 15}. - * @param memLevel - * How much memory should be allocated for the internal compression - * state. {@code 1} uses minimum memory and {@code 9} uses maximum - * memory. Larger values result in better and faster compression - * at the expense of memory usage. The default value is {@code 8} - */ - @Deprecated - public HttpContentCompressor(int compressionLevel, int windowBits, int memLevel) { - this(compressionLevel, windowBits, memLevel, 0); - } - - /** - * Creates a new handler with the specified compression level, window size, - * and memory level.. - * - * @param compressionLevel - * {@code 1} yields the fastest compression and {@code 9} yields the - * best compression. {@code 0} means no compression. The default - * compression level is {@code 6}. - * @param windowBits - * The base two logarithm of the size of the history buffer. The - * value should be in the range {@code 9} to {@code 15} inclusive. - * Larger values result in better compression at the expense of - * memory usage. The default value is {@code 15}. - * @param memLevel - * How much memory should be allocated for the internal compression - * state. {@code 1} uses minimum memory and {@code 9} uses maximum - * memory. Larger values result in better and faster compression - * at the expense of memory usage. The default value is {@code 8} - * @param contentSizeThreshold - * The response body is compressed when the size of the response - * body exceeds the threshold. The value should be a non negative - * number. {@code 0} will enable compression for all responses. - */ - @Deprecated - public HttpContentCompressor(int compressionLevel, int windowBits, int memLevel, int contentSizeThreshold) { - this.compressionLevel = ObjectUtil.checkInRange(compressionLevel, 0, 9, "compressionLevel"); - this.windowBits = ObjectUtil.checkInRange(windowBits, 9, 15, "windowBits"); - this.memLevel = ObjectUtil.checkInRange(memLevel, 1, 9, "memLevel"); - this.contentSizeThreshold = ObjectUtil.checkPositiveOrZero(contentSizeThreshold, "contentSizeThreshold"); - this.brotliOptions = null; - this.gzipOptions = null; - this.deflateOptions = null; - this.zstdOptions = null; - this.factories = null; - this.supportsCompressionOptions = false; - } - - /** - * Create a new {@link HttpContentCompressor} Instance with specified - * {@link CompressionOptions}s and contentSizeThreshold set to {@code 0} - * - * @param compressionOptions {@link CompressionOptions} or {@code null} if the default - * should be used. - */ - public HttpContentCompressor(CompressionOptions... compressionOptions) { - this(0, compressionOptions); - } - - /** - * Create a new {@link HttpContentCompressor} instance with specified - * {@link CompressionOptions}s - * - * @param contentSizeThreshold - * The response body is compressed when the size of the response - * body exceeds the threshold. The value should be a non negative - * number. {@code 0} will enable compression for all responses. - * @param compressionOptions {@link CompressionOptions} or {@code null} - * if the default should be used. - */ - public HttpContentCompressor(int contentSizeThreshold, CompressionOptions... compressionOptions) { - this.contentSizeThreshold = ObjectUtil.checkPositiveOrZero(contentSizeThreshold, "contentSizeThreshold"); - BrotliOptions brotliOptions = null; - GzipOptions gzipOptions = null; - DeflateOptions deflateOptions = null; - ZstdOptions zstdOptions = null; - if (compressionOptions == null || compressionOptions.length == 0) { - brotliOptions = Brotli.isAvailable() ? StandardCompressionOptions.brotli() : null; - gzipOptions = StandardCompressionOptions.gzip(); - deflateOptions = StandardCompressionOptions.deflate(); - zstdOptions = Zstd.isAvailable() ? StandardCompressionOptions.zstd() : null; - } else { - ObjectUtil.deepCheckNotNull("compressionOptions", compressionOptions); - for (CompressionOptions compressionOption : compressionOptions) { - if (compressionOption instanceof BrotliOptions) { - // if we have BrotliOptions, it means Brotli is available - brotliOptions = (BrotliOptions) compressionOption; - } else if (compressionOption instanceof GzipOptions) { - gzipOptions = (GzipOptions) compressionOption; - } else if (compressionOption instanceof DeflateOptions) { - deflateOptions = (DeflateOptions) compressionOption; - } else if (compressionOption instanceof ZstdOptions) { - zstdOptions = (ZstdOptions) compressionOption; - } else { - throw new IllegalArgumentException("Unsupported " + CompressionOptions.class.getSimpleName() + - ": " + compressionOption); - } - } - } - - this.gzipOptions = gzipOptions; - this.deflateOptions = deflateOptions; - this.brotliOptions = brotliOptions; - this.zstdOptions = zstdOptions; - - this.factories = new HashMap(); - - if (this.gzipOptions != null) { - this.factories.put("gzip", new GzipEncoderFactory()); - } - if (this.deflateOptions != null) { - this.factories.put("deflate", new DeflateEncoderFactory()); - } - if (this.brotliOptions != null) { - this.factories.put("br", new BrEncoderFactory()); - } - if (this.zstdOptions != null) { - this.factories.put("zstd", new ZstdEncoderFactory()); - } - - this.compressionLevel = -1; - this.windowBits = -1; - this.memLevel = -1; - supportsCompressionOptions = true; - } - - @Override - public void handlerAdded(ChannelHandlerContext ctx) throws Exception { - this.ctx = ctx; - } - - @Override - protected Result beginEncode(HttpResponse httpResponse, String acceptEncoding) throws Exception { - if (this.contentSizeThreshold > 0) { - if (httpResponse instanceof HttpContent && - ((HttpContent) httpResponse).content().readableBytes() < contentSizeThreshold) { - return null; - } - } - - String contentEncoding = httpResponse.headers().get(HttpHeaderNames.CONTENT_ENCODING); - if (contentEncoding != null) { - // Content-Encoding was set, either as something specific or as the IDENTITY encoding - // Therefore, we should NOT encode here - return null; - } - - if (supportsCompressionOptions) { - String targetContentEncoding = determineEncoding(acceptEncoding); - if (targetContentEncoding == null) { - return null; - } - - CompressionEncoderFactory encoderFactory = factories.get(targetContentEncoding); - - if (encoderFactory == null) { - throw new Error(); - } - - return new Result(targetContentEncoding, - new EmbeddedChannel(ctx.channel().id(), ctx.channel().metadata().hasDisconnect(), - ctx.channel().config(), encoderFactory.createEncoder())); - } else { - ZlibWrapper wrapper = determineWrapper(acceptEncoding); - if (wrapper == null) { - return null; - } - - String targetContentEncoding; - switch (wrapper) { - case GZIP: - targetContentEncoding = "gzip"; - break; - case ZLIB: - targetContentEncoding = "deflate"; - break; - default: - throw new Error(); - } - - return new Result( - targetContentEncoding, - new EmbeddedChannel(ctx.channel().id(), ctx.channel().metadata().hasDisconnect(), - ctx.channel().config(), ZlibCodecFactory.newZlibEncoder( - wrapper, compressionLevel, windowBits, memLevel))); - } - } - - @SuppressWarnings("FloatingPointEquality") - protected String determineEncoding(String acceptEncoding) { - float starQ = -1.0f; - float brQ = -1.0f; - float zstdQ = -1.0f; - float gzipQ = -1.0f; - float deflateQ = -1.0f; - for (String encoding : acceptEncoding.split(",")) { - float q = 1.0f; - int equalsPos = encoding.indexOf('='); - if (equalsPos != -1) { - try { - q = Float.parseFloat(encoding.substring(equalsPos + 1)); - } catch (NumberFormatException e) { - // Ignore encoding - q = 0.0f; - } - } - if (encoding.contains("*")) { - starQ = q; - } else if (encoding.contains("br") && q > brQ) { - brQ = q; - } else if (encoding.contains("zstd") && q > zstdQ) { - zstdQ = q; - } else if (encoding.contains("gzip") && q > gzipQ) { - gzipQ = q; - } else if (encoding.contains("deflate") && q > deflateQ) { - deflateQ = q; - } - } - if (brQ > 0.0f || zstdQ > 0.0f || gzipQ > 0.0f || deflateQ > 0.0f) { - if (brQ != -1.0f && brQ >= zstdQ && this.brotliOptions != null) { - return "br"; - } else if (zstdQ != -1.0f && zstdQ >= gzipQ && this.zstdOptions != null) { - return "zstd"; - } else if (gzipQ != -1.0f && gzipQ >= deflateQ && this.gzipOptions != null) { - return "gzip"; - } else if (deflateQ != -1.0f && this.deflateOptions != null) { - return "deflate"; - } - } - if (starQ > 0.0f) { - if (brQ == -1.0f && this.brotliOptions != null) { - return "br"; - } - if (zstdQ == -1.0f && this.zstdOptions != null) { - return "zstd"; - } - if (gzipQ == -1.0f && this.gzipOptions != null) { - return "gzip"; - } - if (deflateQ == -1.0f && this.deflateOptions != null) { - return "deflate"; - } - } - return null; - } - - @Deprecated - @SuppressWarnings("FloatingPointEquality") - protected ZlibWrapper determineWrapper(String acceptEncoding) { - float starQ = -1.0f; - float gzipQ = -1.0f; - float deflateQ = -1.0f; - for (String encoding : acceptEncoding.split(",")) { - float q = 1.0f; - int equalsPos = encoding.indexOf('='); - if (equalsPos != -1) { - try { - q = Float.parseFloat(encoding.substring(equalsPos + 1)); - } catch (NumberFormatException e) { - // Ignore encoding - q = 0.0f; - } - } - if (encoding.contains("*")) { - starQ = q; - } else if (encoding.contains("gzip") && q > gzipQ) { - gzipQ = q; - } else if (encoding.contains("deflate") && q > deflateQ) { - deflateQ = q; - } - } - if (gzipQ > 0.0f || deflateQ > 0.0f) { - if (gzipQ >= deflateQ) { - return ZlibWrapper.GZIP; - } else { - return ZlibWrapper.ZLIB; - } - } - if (starQ > 0.0f) { - if (gzipQ == -1.0f) { - return ZlibWrapper.GZIP; - } - if (deflateQ == -1.0f) { - return ZlibWrapper.ZLIB; - } - } - return null; - } - - /** - * Compression Encoder Factory that creates {@link ZlibEncoder}s - * used to compress http content for gzip content encoding - */ - private final class GzipEncoderFactory implements CompressionEncoderFactory { - - @Override - public MessageToByteEncoder createEncoder() { - return ZlibCodecFactory.newZlibEncoder( - ZlibWrapper.GZIP, gzipOptions.compressionLevel(), - gzipOptions.windowBits(), gzipOptions.memLevel()); - } - } - - /** - * Compression Encoder Factory that creates {@link ZlibEncoder}s - * used to compress http content for deflate content encoding - */ - private final class DeflateEncoderFactory implements CompressionEncoderFactory { - - @Override - public MessageToByteEncoder createEncoder() { - return ZlibCodecFactory.newZlibEncoder( - ZlibWrapper.ZLIB, deflateOptions.compressionLevel(), - deflateOptions.windowBits(), deflateOptions.memLevel()); - } - } - - /** - * Compression Encoder Factory that creates {@link BrotliEncoder}s - * used to compress http content for br content encoding - */ - private final class BrEncoderFactory implements CompressionEncoderFactory { - - @Override - public MessageToByteEncoder createEncoder() { - return new BrotliEncoder(brotliOptions.parameters()); - } - } - - /** - * Compression Encoder Factory for create {@link ZstdEncoder} - * used to compress http content for zstd content encoding - */ - private final class ZstdEncoderFactory implements CompressionEncoderFactory { - - @Override - public MessageToByteEncoder createEncoder() { - return new ZstdEncoder(zstdOptions.compressionLevel(), - zstdOptions.blockSize(), zstdOptions.maxEncodeSize()); - } - } -} 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 deleted file mode 100644 index 41869e5d46..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentDecoder.java +++ /dev/null @@ -1,287 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.buffer.ByteBuf; -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; - -/** - * Decodes the content of the received {@link HttpRequest} and {@link HttpContent}. - * The original content is replaced with the new content decoded by the - * {@link EmbeddedChannel}, which is created by {@link #newContentDecoder(String)}. - * Once decoding is finished, the value of the 'Content-Encoding' - * header is set to the target content encoding, as returned by {@link #getTargetContentEncoding(String)}. - * Also, the 'Content-Length' header is updated to the length of the - * decoded content. If the content encoding of the original is not supported - * by the decoder, {@link #newContentDecoder(String)} should return {@code null} - * so that no decoding occurs (i.e. pass-through). - *

- * Please note that this is an abstract class. You have to extend this class - * and implement {@link #newContentDecoder(String)} properly to make this class - * functional. For example, refer to the source code of {@link HttpContentDecompressor}. - *

- * This handler must be placed after {@link HttpObjectDecoder} in the pipeline - * so that this handler can intercept HTTP requests after {@link HttpObjectDecoder} - * converts {@link ByteBuf}s into HTTP requests. - */ -public abstract class HttpContentDecoder extends MessageToMessageDecoder { - - static final String IDENTITY = HttpHeaderValues.IDENTITY.toString(); - - protected ChannelHandlerContext ctx; - private EmbeddedChannel decoder; - private boolean continueResponse; - private boolean needRead = true; - - @Override - protected void decode(ChannelHandlerContext ctx, HttpObject msg) throws Exception { - if (msg instanceof HttpResponse && ((HttpResponse) msg).status().code() == 100) { - - if (!(msg instanceof LastHttpContent)) { - continueResponse = true; - } - // 100-continue response must be passed through. - fireChannelRead(ctx, ReferenceCountUtil.retain(msg)); - return; - } - - if (continueResponse) { - if (msg instanceof LastHttpContent) { - continueResponse = false; - } - // 100-continue response must be passed through. - fireChannelRead(ctx, ReferenceCountUtil.retain(msg)); - return; - } - - 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 { - String transferEncoding = headers.get(HttpHeaderNames.TRANSFER_ENCODING); - if (transferEncoding != null) { - int idx = transferEncoding.indexOf(","); - if (idx != -1) { - contentEncoding = transferEncoding.substring(0, idx).trim(); - } else { - contentEncoding = transferEncoding.trim(); - } - } else { - contentEncoding = IDENTITY; - } - } - decoder = newContentDecoder(contentEncoding); - - if (decoder == null) { - if (message instanceof HttpContent) { - ((HttpContent) message).retain(); - } - fireChannelRead(ctx, 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 - - // 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: https://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()); - } else { - throw new CodecException("Object of class " + message.getClass().getName() + - " is not an HttpRequest or HttpResponse"); - } - copy.headers().set(message.headers()); - copy.setDecoderResult(message.decoderResult()); - fireChannelRead(ctx, copy); - } else { - fireChannelRead(ctx, message); - } - } - - if (msg instanceof HttpContent) { - final HttpContent c = (HttpContent) msg; - if (decoder == null) { - fireChannelRead(ctx, c.retain()); - } else { - decodeContent(ctx, c); - } - } - } - - private void decodeContent(ChannelHandlerContext ctx, HttpContent c) { - ByteBuf content = c.content(); - - decode(ctx, content); - - if (c instanceof LastHttpContent) { - finishDecode(ctx); - - LastHttpContent last = (LastHttpContent) c; - // Generate an additional chunk if the decoder produced - // the last product on closure, - HttpHeaders headers = last.trailingHeaders(); - if (headers.isEmpty()) { - fireChannelRead(ctx, LastHttpContent.EMPTY_LAST_CONTENT); - } else { - fireChannelRead(ctx, new ComposedLastHttpContent(headers, DecoderResult.SUCCESS)); - } - } - } - - @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. - * - * @param contentEncoding the value of the {@code "Content-Encoding"} header - * @return a new {@link EmbeddedChannel} if the specified encoding is supported. - * {@code null} otherwise (alternatively, you can throw an exception - * to block unknown encoding). - */ - protected abstract EmbeddedChannel newContentDecoder(String contentEncoding) throws Exception; - - /** - * Returns the expected content encoding of the decoded content. - * This getMethod returns {@code "identity"} by default, which is the case for - * most decoders. - * - * @param contentEncoding the value of the {@code "Content-Encoding"} header - * @return the expected content encoding of the new content - */ - protected String getTargetContentEncoding( - @SuppressWarnings("UnusedParameters") String contentEncoding) throws Exception { - return IDENTITY; - } - - @Override - public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { - cleanupSafely(ctx); - super.handlerRemoved(ctx); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - cleanupSafely(ctx); - super.channelInactive(ctx); - } - - @Override - public void handlerAdded(ChannelHandlerContext ctx) throws Exception { - this.ctx = ctx; - super.handlerAdded(ctx); - } - - private void cleanup() { - if (decoder != null) { - // Clean-up the previous decoder if not cleaned up correctly. - decoder.finishAndReleaseAll(); - decoder = null; - } - } - - private void cleanupSafely(ChannelHandlerContext ctx) { - try { - cleanup(); - } catch (Throwable cause) { - // If cleanup throws any error we need to propagate it through the pipeline - // so we don't fail to propagate pipeline events. - ctx.fireExceptionCaught(cause); - } - } - - private void decode(ChannelHandlerContext ctx, ByteBuf in) { - // call retain here as it will call release after its written to the channel - decoder.writeInbound(in.retain()); - fetchDecoderOutput(ctx); - } - - private void finishDecode(ChannelHandlerContext ctx) { - if (decoder.finish()) { - fetchDecoderOutput(ctx); - } - decoder = null; - } - - private void fetchDecoderOutput(ChannelHandlerContext ctx) { - for (;;) { - ByteBuf buf = decoder.readInbound(); - if (buf == null) { - break; - } - if (!buf.isReadable()) { - buf.release(); - continue; - } - ctx.fireChannelRead(new DefaultHttpContent(buf)); - } - } - - private void fireChannelRead(ChannelHandlerContext ctx, Object msg) { - needRead = false; - ctx.fireChannelRead(msg); - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentDecompressor.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentDecompressor.java deleted file mode 100644 index d66151d463..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentDecompressor.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import static io.netty.handler.codec.http.HttpHeaderValues.BR; -import static io.netty.handler.codec.http.HttpHeaderValues.DEFLATE; -import static io.netty.handler.codec.http.HttpHeaderValues.GZIP; -import static io.netty.handler.codec.http.HttpHeaderValues.X_DEFLATE; -import static io.netty.handler.codec.http.HttpHeaderValues.X_GZIP; - -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.compression.Brotli; -import io.netty.handler.codec.compression.BrotliDecoder; -import io.netty.handler.codec.compression.ZlibCodecFactory; -import io.netty.handler.codec.compression.ZlibWrapper; - -/** - * Decompresses an {@link HttpMessage} and an {@link HttpContent} compressed in - * {@code gzip} or {@code deflate} encoding. For more information on how this - * handler modifies the message, please refer to {@link HttpContentDecoder}. - */ -public class HttpContentDecompressor extends HttpContentDecoder { - - private final boolean strict; - - /** - * Create a new {@link HttpContentDecompressor} in non-strict mode. - */ - public HttpContentDecompressor() { - this(false); - } - - /** - * Create a new {@link HttpContentDecompressor}. - * - * @param strict if {@code true} use strict handling of deflate if used, otherwise handle it in a - * more lenient fashion. - */ - public HttpContentDecompressor(boolean strict) { - this.strict = strict; - } - - @Override - protected EmbeddedChannel newContentDecoder(String contentEncoding) throws Exception { - if (GZIP.contentEqualsIgnoreCase(contentEncoding) || - X_GZIP.contentEqualsIgnoreCase(contentEncoding)) { - return new EmbeddedChannel(ctx.channel().id(), ctx.channel().metadata().hasDisconnect(), - ctx.channel().config(), ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP)); - } - if (DEFLATE.contentEqualsIgnoreCase(contentEncoding) || - X_DEFLATE.contentEqualsIgnoreCase(contentEncoding)) { - final ZlibWrapper wrapper = strict ? ZlibWrapper.ZLIB : ZlibWrapper.ZLIB_OR_NONE; - // To be strict, 'deflate' means ZLIB, but some servers were not implemented correctly. - return new EmbeddedChannel(ctx.channel().id(), ctx.channel().metadata().hasDisconnect(), - ctx.channel().config(), ZlibCodecFactory.newZlibDecoder(wrapper)); - } - if (Brotli.isAvailable() && BR.contentEqualsIgnoreCase(contentEncoding)) { - return new EmbeddedChannel(ctx.channel().id(), ctx.channel().metadata().hasDisconnect(), - ctx.channel().config(), new BrotliDecoder()); - } - - // 'identity' or unsupported - return null; - } -} 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 deleted file mode 100644 index 61bcd845d0..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentEncoder.java +++ /dev/null @@ -1,382 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -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; -import io.netty.util.internal.StringUtil; - -import java.util.ArrayDeque; -import java.util.List; -import java.util.Queue; - -import static io.netty.handler.codec.http.HttpHeaderNames.*; - -/** - * Encodes the content of the outbound {@link HttpResponse} and {@link HttpContent}. - * The original content is replaced with the new content encoded by the - * {@link EmbeddedChannel}, which is created by {@link #beginEncode(HttpResponse, String)}. - * Once encoding is finished, the value of the 'Content-Encoding' header - * is set to the target content encoding, as returned by - * {@link #beginEncode(HttpResponse, String)}. - * Also, the 'Content-Length' header is updated to the length of the - * encoded content. If there is no supported or allowed encoding in the - * corresponding {@link HttpRequest}'s {@code "Accept-Encoding"} header, - * {@link #beginEncode(HttpResponse, String)} should return {@code null} so that - * no encoding occurs (i.e. pass-through). - *

- * Please note that this is an abstract class. You have to extend this class - * and implement {@link #beginEncode(HttpResponse, String)} properly to make - * this class functional. For example, refer to the source code of - * {@link HttpContentCompressor}. - *

- * This handler must be placed after {@link HttpObjectEncoder} in the pipeline - * so that this handler can intercept HTTP responses before {@link HttpObjectEncoder} - * converts them into {@link ByteBuf}s. - */ -public abstract class HttpContentEncoder extends MessageToMessageCodec { - - private enum State { - PASS_THROUGH, - AWAIT_HEADERS, - AWAIT_CONTENT - } - - private static final CharSequence ZERO_LENGTH_HEAD = "HEAD"; - private static final CharSequence ZERO_LENGTH_CONNECT = "CONNECT"; - private static final int CONTINUE_CODE = HttpResponseStatus.CONTINUE.code(); - - private final Queue acceptEncodingQueue = new ArrayDeque<>(); - private EmbeddedChannel encoder; - private State state = State.AWAIT_HEADERS; - - @Override - public boolean acceptOutboundMessage(Object msg) throws Exception { - return msg instanceof HttpContent || msg instanceof HttpResponse; - } - - @Override - protected void decode(ChannelHandlerContext ctx, HttpRequest msg) throws Exception { - CharSequence acceptEncoding; - List acceptEncodingHeaders = msg.headers().getAll(ACCEPT_ENCODING); - switch (acceptEncodingHeaders.size()) { - case 0: - acceptEncoding = HttpContentDecoder.IDENTITY; - break; - case 1: - acceptEncoding = acceptEncodingHeaders.get(0); - break; - default: - // Multiple message-header fields https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 - acceptEncoding = StringUtil.join(",", acceptEncodingHeaders); - break; - } - - HttpMethod method = msg.method(); - if (HttpMethod.HEAD.equals(method)) { - acceptEncoding = ZERO_LENGTH_HEAD; - } else if (HttpMethod.CONNECT.equals(method)) { - acceptEncoding = ZERO_LENGTH_CONNECT; - } - - acceptEncodingQueue.add(acceptEncoding); - ctx.fireChannelRead(ReferenceCountUtil.retain(msg)); - } - - @Override - protected void encode(ChannelHandlerContext ctx, HttpObject msg, List out) throws Exception { - final boolean isFull = msg instanceof HttpResponse && msg instanceof LastHttpContent; - switch (state) { - case AWAIT_HEADERS: { - ensureHeaders(msg); - assert encoder == null; - - final HttpResponse res = (HttpResponse) msg; - final int code = res.status().code(); - final CharSequence acceptEncoding; - if (code == CONTINUE_CODE) { - // We need to not poll the encoding when response with CONTINUE as another response will follow - // for the issued request. See https://github.com/netty/netty/issues/4079 - acceptEncoding = null; - } else { - // Get the list of encodings accepted by the peer. - acceptEncoding = acceptEncodingQueue.poll(); - if (acceptEncoding == null) { - throw new IllegalStateException("cannot send more responses than requests"); - } - } - - /* - * per rfc2616 4.3 Message Body - * All 1xx (informational), 204 (no content), and 304 (not modified) responses MUST NOT include a - * message-body. All other responses do include a message-body, although it MAY be of zero length. - * - * 9.4 HEAD - * The HEAD method is identical to GET except that the server MUST NOT return a message-body - * in the response. - * - * Also we should pass through HTTP/1.0 as transfer-encoding: chunked is not supported. - * - * See https://github.com/netty/netty/issues/5382 - */ - if (isPassthru(res.protocolVersion(), code, acceptEncoding)) { - if (isFull) { - out.add(ReferenceCountUtil.retain(res)); - } else { - out.add(ReferenceCountUtil.retain(res)); - // Pass through all following contents. - state = State.PASS_THROUGH; - } - break; - } - - if (isFull) { - // Pass through the full response with empty content and continue waiting for the next resp. - if (!((ByteBufHolder) res).content().isReadable()) { - out.add(ReferenceCountUtil.retain(res)); - break; - } - } - - // Prepare to encode the content. - final Result result = beginEncode(res, acceptEncoding.toString()); - - // If unable to encode, pass through. - if (result == null) { - if (isFull) { - out.add(ReferenceCountUtil.retain(res)); - } else { - out.add(ReferenceCountUtil.retain(res)); - // Pass through all following contents. - state = State.PASS_THROUGH; - } - break; - } - - encoder = result.contentEncoder(); - - // Encode the content and remove or replace the existing headers - // so that the message looks like a decoded message. - res.headers().set(HttpHeaderNames.CONTENT_ENCODING, result.targetContentEncoding()); - - // Output the rewritten response. - if (isFull) { - // Convert full message into unfull one. - HttpResponse newRes = new DefaultHttpResponse(res.protocolVersion(), res.status()); - newRes.headers().set(res.headers()); - out.add(newRes); - - ensureContent(res); - encodeFullResponse(newRes, (HttpContent) res, out); - break; - } else { - // Make the response chunked to simplify content transformation. - res.headers().remove(HttpHeaderNames.CONTENT_LENGTH); - res.headers().set(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED); - - out.add(ReferenceCountUtil.retain(res)); - state = State.AWAIT_CONTENT; - if (!(msg instanceof HttpContent)) { - // only break out the switch statement if we have not content to process - // See https://github.com/netty/netty/issues/2006 - break; - } - // Fall through to encode the content - } - } - case AWAIT_CONTENT: { - ensureContent(msg); - if (encodeContent((HttpContent) msg, out)) { - state = State.AWAIT_HEADERS; - } - break; - } - case PASS_THROUGH: { - ensureContent(msg); - out.add(ReferenceCountUtil.retain(msg)); - // Passed through all following contents of the current response. - if (msg instanceof LastHttpContent) { - state = State.AWAIT_HEADERS; - } - break; - } - } - } - - private void encodeFullResponse(HttpResponse newRes, HttpContent content, List out) { - int existingMessages = out.size(); - encodeContent(content, out); - - if (HttpUtil.isContentLengthSet(newRes)) { - // adjust the content-length header - int messageSize = 0; - for (int i = existingMessages; i < out.size(); i++) { - Object item = out.get(i); - if (item instanceof HttpContent) { - messageSize += ((HttpContent) item).content().readableBytes(); - } - } - HttpUtil.setContentLength(newRes, messageSize); - } else { - newRes.headers().set(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED); - } - } - - private static boolean isPassthru(HttpVersion version, int code, CharSequence httpMethod) { - return code < 200 || code == 204 || code == 304 || - (httpMethod == ZERO_LENGTH_HEAD || (httpMethod == ZERO_LENGTH_CONNECT && code == 200)) || - version == HttpVersion.HTTP_1_0; - } - - private static void ensureHeaders(HttpObject msg) { - if (!(msg instanceof HttpResponse)) { - throw new IllegalStateException( - "unexpected message type: " + - msg.getClass().getName() + " (expected: " + HttpResponse.class.getSimpleName() + ')'); - } - } - - private static void ensureContent(HttpObject msg) { - if (!(msg instanceof HttpContent)) { - throw new IllegalStateException( - "unexpected message type: " + - msg.getClass().getName() + " (expected: " + HttpContent.class.getSimpleName() + ')'); - } - } - - private boolean encodeContent(HttpContent c, List out) { - ByteBuf content = c.content(); - - encode(content, out); - - if (c instanceof LastHttpContent) { - finishEncode(out); - LastHttpContent last = (LastHttpContent) c; - - // Generate an additional chunk if the decoder produced - // the last product on closure, - HttpHeaders headers = last.trailingHeaders(); - if (headers.isEmpty()) { - out.add(LastHttpContent.EMPTY_LAST_CONTENT); - } else { - out.add(new ComposedLastHttpContent(headers, DecoderResult.SUCCESS)); - } - return true; - } - return false; - } - - /** - * Prepare to encode the HTTP message content. - * - * @param httpResponse - * the http response - * @param acceptEncoding - * the value of the {@code "Accept-Encoding"} header - * - * @return the result of preparation, which is composed of the determined - * target content encoding and a new {@link EmbeddedChannel} that - * encodes the content into the target content encoding. - * {@code null} if {@code acceptEncoding} is unsupported or rejected - * and thus the content should be handled as-is (i.e. no encoding). - */ - protected abstract Result beginEncode(HttpResponse httpResponse, String acceptEncoding) throws Exception; - - @Override - public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { - cleanupSafely(ctx); - super.handlerRemoved(ctx); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - cleanupSafely(ctx); - super.channelInactive(ctx); - } - - private void cleanup() { - if (encoder != null) { - // Clean-up the previous encoder if not cleaned up correctly. - encoder.finishAndReleaseAll(); - encoder = null; - } - } - - private void cleanupSafely(ChannelHandlerContext ctx) { - try { - cleanup(); - } catch (Throwable cause) { - // If cleanup throws any error we need to propagate it through the pipeline - // so we don't fail to propagate pipeline events. - ctx.fireExceptionCaught(cause); - } - } - - private void encode(ByteBuf in, List out) { - // call retain here as it will call release after its written to the channel - encoder.writeOutbound(in.retain()); - fetchEncoderOutput(out); - } - - private void finishEncode(List out) { - if (encoder.finish()) { - fetchEncoderOutput(out); - } - encoder = null; - } - - private void fetchEncoderOutput(List out) { - for (;;) { - ByteBuf buf = encoder.readOutbound(); - if (buf == null) { - break; - } - if (!buf.isReadable()) { - buf.release(); - continue; - } - out.add(new DefaultHttpContent(buf)); - } - } - - public static final class Result { - private final String targetContentEncoding; - private final EmbeddedChannel contentEncoder; - - public Result(String targetContentEncoding, EmbeddedChannel contentEncoder) { - requireNonNull(targetContentEncoding, "targetContentEncoding"); - requireNonNull(contentEncoder, "contentEncoder"); - - this.targetContentEncoding = targetContentEncoding; - this.contentEncoder = contentEncoder; - } - - public String targetContentEncoding() { - return targetContentEncoding; - } - - public EmbeddedChannel contentEncoder() { - return contentEncoder; - } - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpExpectationFailedEvent.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpExpectationFailedEvent.java deleted file mode 100644 index c9a5b19dbe..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpExpectationFailedEvent.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -/** - * A user event designed to communicate that a expectation has failed and there should be no expectation that a - * body will follow. - */ -public final class HttpExpectationFailedEvent { - public static final HttpExpectationFailedEvent INSTANCE = new HttpExpectationFailedEvent(); - private HttpExpectationFailedEvent() { } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaderNames.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaderNames.java deleted file mode 100644 index 7ac98265dc..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaderNames.java +++ /dev/null @@ -1,371 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.http; - -import io.netty.util.AsciiString; - -/** - * Standard HTTP header names. - *

- * These are all defined as lowercase to support HTTP/2 requirements while also not - * violating HTTP/1.x requirements. New header names should always be lowercase. - */ -public final class HttpHeaderNames { - /** - * {@code "accept"} - */ - public static final AsciiString ACCEPT = AsciiString.cached("accept"); - /** - * {@code "accept-charset"} - */ - public static final AsciiString ACCEPT_CHARSET = AsciiString.cached("accept-charset"); - /** - * {@code "accept-encoding"} - */ - public static final AsciiString ACCEPT_ENCODING = AsciiString.cached("accept-encoding"); - /** - * {@code "accept-language"} - */ - public static final AsciiString ACCEPT_LANGUAGE = AsciiString.cached("accept-language"); - /** - * {@code "accept-ranges"} - */ - public static final AsciiString ACCEPT_RANGES = AsciiString.cached("accept-ranges"); - /** - * {@code "accept-patch"} - */ - public static final AsciiString ACCEPT_PATCH = AsciiString.cached("accept-patch"); - /** - * {@code "access-control-allow-credentials"} - */ - public static final AsciiString ACCESS_CONTROL_ALLOW_CREDENTIALS = - AsciiString.cached("access-control-allow-credentials"); - /** - * {@code "access-control-allow-headers"} - */ - public static final AsciiString ACCESS_CONTROL_ALLOW_HEADERS = - AsciiString.cached("access-control-allow-headers"); - /** - * {@code "access-control-allow-methods"} - */ - public static final AsciiString ACCESS_CONTROL_ALLOW_METHODS = - AsciiString.cached("access-control-allow-methods"); - /** - * {@code "access-control-allow-origin"} - */ - public static final AsciiString ACCESS_CONTROL_ALLOW_ORIGIN = - AsciiString.cached("access-control-allow-origin"); - /** - * {@code "access-control-expose-headers"} - */ - public static final AsciiString ACCESS_CONTROL_EXPOSE_HEADERS = - AsciiString.cached("access-control-expose-headers"); - /** - * {@code "access-control-max-age"} - */ - public static final AsciiString ACCESS_CONTROL_MAX_AGE = AsciiString.cached("access-control-max-age"); - /** - * {@code "access-control-request-headers"} - */ - public static final AsciiString ACCESS_CONTROL_REQUEST_HEADERS = - AsciiString.cached("access-control-request-headers"); - /** - * {@code "access-control-request-method"} - */ - public static final AsciiString ACCESS_CONTROL_REQUEST_METHOD = - AsciiString.cached("access-control-request-method"); - /** - * {@code "age"} - */ - public static final AsciiString AGE = AsciiString.cached("age"); - /** - * {@code "allow"} - */ - public static final AsciiString ALLOW = AsciiString.cached("allow"); - /** - * {@code "authorization"} - */ - public static final AsciiString AUTHORIZATION = AsciiString.cached("authorization"); - /** - * {@code "cache-control"} - */ - public static final AsciiString CACHE_CONTROL = AsciiString.cached("cache-control"); - /** - * {@code "connection"} - */ - public static final AsciiString CONNECTION = AsciiString.cached("connection"); - /** - * {@code "content-base"} - */ - public static final AsciiString CONTENT_BASE = AsciiString.cached("content-base"); - /** - * {@code "content-encoding"} - */ - public static final AsciiString CONTENT_ENCODING = AsciiString.cached("content-encoding"); - /** - * {@code "content-language"} - */ - public static final AsciiString CONTENT_LANGUAGE = AsciiString.cached("content-language"); - /** - * {@code "content-length"} - */ - public static final AsciiString CONTENT_LENGTH = AsciiString.cached("content-length"); - /** - * {@code "content-location"} - */ - public static final AsciiString CONTENT_LOCATION = AsciiString.cached("content-location"); - /** - * {@code "content-transfer-encoding"} - */ - public static final AsciiString CONTENT_TRANSFER_ENCODING = AsciiString.cached("content-transfer-encoding"); - /** - * {@code "content-disposition"} - */ - public static final AsciiString CONTENT_DISPOSITION = AsciiString.cached("content-disposition"); - /** - * {@code "content-md5"} - */ - public static final AsciiString CONTENT_MD5 = AsciiString.cached("content-md5"); - /** - * {@code "content-range"} - */ - public static final AsciiString CONTENT_RANGE = AsciiString.cached("content-range"); - /** - * {@code "content-security-policy"} - */ - public static final AsciiString CONTENT_SECURITY_POLICY = AsciiString.cached("content-security-policy"); - /** - * {@code "content-type"} - */ - public static final AsciiString CONTENT_TYPE = AsciiString.cached("content-type"); - /** - * {@code "cookie"} - */ - public static final AsciiString COOKIE = AsciiString.cached("cookie"); - /** - * {@code "date"} - */ - public static final AsciiString DATE = AsciiString.cached("date"); - /** - * {@code "dnt"} - */ - public static final AsciiString DNT = AsciiString.cached("dnt"); - /** - * {@code "etag"} - */ - public static final AsciiString ETAG = AsciiString.cached("etag"); - /** - * {@code "expect"} - */ - public static final AsciiString EXPECT = AsciiString.cached("expect"); - /** - * {@code "expires"} - */ - public static final AsciiString EXPIRES = AsciiString.cached("expires"); - /** - * {@code "from"} - */ - public static final AsciiString FROM = AsciiString.cached("from"); - /** - * {@code "host"} - */ - public static final AsciiString HOST = AsciiString.cached("host"); - /** - * {@code "if-match"} - */ - public static final AsciiString IF_MATCH = AsciiString.cached("if-match"); - /** - * {@code "if-modified-since"} - */ - public static final AsciiString IF_MODIFIED_SINCE = AsciiString.cached("if-modified-since"); - /** - * {@code "if-none-match"} - */ - public static final AsciiString IF_NONE_MATCH = AsciiString.cached("if-none-match"); - /** - * {@code "if-range"} - */ - public static final AsciiString IF_RANGE = AsciiString.cached("if-range"); - /** - * {@code "if-unmodified-since"} - */ - public static final AsciiString IF_UNMODIFIED_SINCE = AsciiString.cached("if-unmodified-since"); - /** - * @deprecated use {@link #CONNECTION} - * - * {@code "keep-alive"} - */ - @Deprecated - public static final AsciiString KEEP_ALIVE = AsciiString.cached("keep-alive"); - /** - * {@code "last-modified"} - */ - public static final AsciiString LAST_MODIFIED = AsciiString.cached("last-modified"); - /** - * {@code "location"} - */ - public static final AsciiString LOCATION = AsciiString.cached("location"); - /** - * {@code "max-forwards"} - */ - public static final AsciiString MAX_FORWARDS = AsciiString.cached("max-forwards"); - /** - * {@code "origin"} - */ - public static final AsciiString ORIGIN = AsciiString.cached("origin"); - /** - * {@code "pragma"} - */ - public static final AsciiString PRAGMA = AsciiString.cached("pragma"); - /** - * {@code "proxy-authenticate"} - */ - public static final AsciiString PROXY_AUTHENTICATE = AsciiString.cached("proxy-authenticate"); - /** - * {@code "proxy-authorization"} - */ - public static final AsciiString PROXY_AUTHORIZATION = AsciiString.cached("proxy-authorization"); - /** - * @deprecated use {@link #CONNECTION} - * - * {@code "proxy-connection"} - */ - @Deprecated - public static final AsciiString PROXY_CONNECTION = AsciiString.cached("proxy-connection"); - /** - * {@code "range"} - */ - public static final AsciiString RANGE = AsciiString.cached("range"); - /** - * {@code "referer"} - */ - public static final AsciiString REFERER = AsciiString.cached("referer"); - /** - * {@code "retry-after"} - */ - public static final AsciiString RETRY_AFTER = AsciiString.cached("retry-after"); - /** - * {@code "sec-websocket-key1"} - */ - public static final AsciiString SEC_WEBSOCKET_KEY1 = AsciiString.cached("sec-websocket-key1"); - /** - * {@code "sec-websocket-key2"} - */ - public static final AsciiString SEC_WEBSOCKET_KEY2 = AsciiString.cached("sec-websocket-key2"); - /** - * {@code "sec-websocket-location"} - */ - public static final AsciiString SEC_WEBSOCKET_LOCATION = AsciiString.cached("sec-websocket-location"); - /** - * {@code "sec-websocket-origin"} - */ - public static final AsciiString SEC_WEBSOCKET_ORIGIN = AsciiString.cached("sec-websocket-origin"); - /** - * {@code "sec-websocket-protocol"} - */ - public static final AsciiString SEC_WEBSOCKET_PROTOCOL = AsciiString.cached("sec-websocket-protocol"); - /** - * {@code "sec-websocket-version"} - */ - public static final AsciiString SEC_WEBSOCKET_VERSION = AsciiString.cached("sec-websocket-version"); - /** - * {@code "sec-websocket-key"} - */ - public static final AsciiString SEC_WEBSOCKET_KEY = AsciiString.cached("sec-websocket-key"); - /** - * {@code "sec-websocket-accept"} - */ - public static final AsciiString SEC_WEBSOCKET_ACCEPT = AsciiString.cached("sec-websocket-accept"); - /** - * {@code "sec-websocket-protocol"} - */ - public static final AsciiString SEC_WEBSOCKET_EXTENSIONS = AsciiString.cached("sec-websocket-extensions"); - /** - * {@code "server"} - */ - public static final AsciiString SERVER = AsciiString.cached("server"); - /** - * {@code "set-cookie"} - */ - public static final AsciiString SET_COOKIE = AsciiString.cached("set-cookie"); - /** - * {@code "set-cookie2"} - */ - public static final AsciiString SET_COOKIE2 = AsciiString.cached("set-cookie2"); - /** - * {@code "te"} - */ - public static final AsciiString TE = AsciiString.cached("te"); - /** - * {@code "trailer"} - */ - public static final AsciiString TRAILER = AsciiString.cached("trailer"); - /** - * {@code "transfer-encoding"} - */ - public static final AsciiString TRANSFER_ENCODING = AsciiString.cached("transfer-encoding"); - /** - * {@code "upgrade"} - */ - public static final AsciiString UPGRADE = AsciiString.cached("upgrade"); - /** - * {@code "upgrade-insecure-requests"} - */ - public static final AsciiString UPGRADE_INSECURE_REQUESTS = AsciiString.cached("upgrade-insecure-requests"); - /** - * {@code "user-agent"} - */ - public static final AsciiString USER_AGENT = AsciiString.cached("user-agent"); - /** - * {@code "vary"} - */ - public static final AsciiString VARY = AsciiString.cached("vary"); - /** - * {@code "via"} - */ - public static final AsciiString VIA = AsciiString.cached("via"); - /** - * {@code "warning"} - */ - public static final AsciiString WARNING = AsciiString.cached("warning"); - /** - * {@code "websocket-location"} - */ - public static final AsciiString WEBSOCKET_LOCATION = AsciiString.cached("websocket-location"); - /** - * {@code "websocket-origin"} - */ - public static final AsciiString WEBSOCKET_ORIGIN = AsciiString.cached("websocket-origin"); - /** - * {@code "websocket-protocol"} - */ - public static final AsciiString WEBSOCKET_PROTOCOL = AsciiString.cached("websocket-protocol"); - /** - * {@code "www-authenticate"} - */ - public static final AsciiString WWW_AUTHENTICATE = AsciiString.cached("www-authenticate"); - /** - * {@code "x-frame-options"} - */ - public static final AsciiString X_FRAME_OPTIONS = AsciiString.cached("x-frame-options"); - /** - * {@code "x-requested-with"} - */ - public static final AsciiString X_REQUESTED_WITH = AsciiString.cached("x-requested-with"); - - private HttpHeaderNames() { } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaderValues.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaderValues.java deleted file mode 100644 index 5fb1086129..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaderValues.java +++ /dev/null @@ -1,249 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.http; - -import io.netty.util.AsciiString; - -/** - * Standard HTTP header values. - */ -public final class HttpHeaderValues { - /** - * {@code "application/json"} - */ - public static final AsciiString APPLICATION_JSON = AsciiString.cached("application/json"); - /** - * {@code "application/x-www-form-urlencoded"} - */ - public static final AsciiString APPLICATION_X_WWW_FORM_URLENCODED = - AsciiString.cached("application/x-www-form-urlencoded"); - /** - * {@code "application/octet-stream"} - */ - public static final AsciiString APPLICATION_OCTET_STREAM = AsciiString.cached("application/octet-stream"); - /** - * {@code "application/xhtml+xml"} - */ - public static final AsciiString APPLICATION_XHTML = AsciiString.cached("application/xhtml+xml"); - /** - * {@code "application/xml"} - */ - public static final AsciiString APPLICATION_XML = AsciiString.cached("application/xml"); - /** - * {@code "application/zstd"} - */ - public static final AsciiString APPLICATION_ZSTD = AsciiString.cached("application/zstd"); - /** - * {@code "attachment"} - * See {@link HttpHeaderNames#CONTENT_DISPOSITION} - */ - public static final AsciiString ATTACHMENT = AsciiString.cached("attachment"); - /** - * {@code "base64"} - */ - public static final AsciiString BASE64 = AsciiString.cached("base64"); - /** - * {@code "binary"} - */ - public static final AsciiString BINARY = AsciiString.cached("binary"); - /** - * {@code "boundary"} - */ - public static final AsciiString BOUNDARY = AsciiString.cached("boundary"); - /** - * {@code "bytes"} - */ - public static final AsciiString BYTES = AsciiString.cached("bytes"); - /** - * {@code "charset"} - */ - public static final AsciiString CHARSET = AsciiString.cached("charset"); - /** - * {@code "chunked"} - */ - public static final AsciiString CHUNKED = AsciiString.cached("chunked"); - /** - * {@code "close"} - */ - public static final AsciiString CLOSE = AsciiString.cached("close"); - /** - * {@code "compress"} - */ - public static final AsciiString COMPRESS = AsciiString.cached("compress"); - /** - * {@code "100-continue"} - */ - public static final AsciiString CONTINUE = AsciiString.cached("100-continue"); - /** - * {@code "deflate"} - */ - public static final AsciiString DEFLATE = AsciiString.cached("deflate"); - /** - * {@code "x-deflate"} - */ - public static final AsciiString X_DEFLATE = AsciiString.cached("x-deflate"); - /** - * {@code "file"} - * See {@link HttpHeaderNames#CONTENT_DISPOSITION} - */ - public static final AsciiString FILE = AsciiString.cached("file"); - /** - * {@code "filename"} - * See {@link HttpHeaderNames#CONTENT_DISPOSITION} - */ - public static final AsciiString FILENAME = AsciiString.cached("filename"); - /** - * {@code "form-data"} - * See {@link HttpHeaderNames#CONTENT_DISPOSITION} - */ - public static final AsciiString FORM_DATA = AsciiString.cached("form-data"); - /** - * {@code "gzip"} - */ - public static final AsciiString GZIP = AsciiString.cached("gzip"); - /** - * {@code "br"} - */ - public static final AsciiString BR = AsciiString.cached("br"); - /** - * {@code "zstd"} - */ - public static final AsciiString ZSTD = AsciiString.cached("zstd"); - /** - * {@code "gzip,deflate"} - */ - public static final AsciiString GZIP_DEFLATE = AsciiString.cached("gzip,deflate"); - /** - * {@code "x-gzip"} - */ - public static final AsciiString X_GZIP = AsciiString.cached("x-gzip"); - /** - * {@code "identity"} - */ - public static final AsciiString IDENTITY = AsciiString.cached("identity"); - /** - * {@code "keep-alive"} - */ - public static final AsciiString KEEP_ALIVE = AsciiString.cached("keep-alive"); - /** - * {@code "max-age"} - */ - public static final AsciiString MAX_AGE = AsciiString.cached("max-age"); - /** - * {@code "max-stale"} - */ - public static final AsciiString MAX_STALE = AsciiString.cached("max-stale"); - /** - * {@code "min-fresh"} - */ - public static final AsciiString MIN_FRESH = AsciiString.cached("min-fresh"); - /** - * {@code "multipart/form-data"} - */ - public static final AsciiString MULTIPART_FORM_DATA = AsciiString.cached("multipart/form-data"); - /** - * {@code "multipart/mixed"} - */ - public static final AsciiString MULTIPART_MIXED = AsciiString.cached("multipart/mixed"); - /** - * {@code "must-revalidate"} - */ - public static final AsciiString MUST_REVALIDATE = AsciiString.cached("must-revalidate"); - /** - * {@code "name"} - * See {@link HttpHeaderNames#CONTENT_DISPOSITION} - */ - public static final AsciiString NAME = AsciiString.cached("name"); - /** - * {@code "no-cache"} - */ - public static final AsciiString NO_CACHE = AsciiString.cached("no-cache"); - /** - * {@code "no-store"} - */ - public static final AsciiString NO_STORE = AsciiString.cached("no-store"); - /** - * {@code "no-transform"} - */ - public static final AsciiString NO_TRANSFORM = AsciiString.cached("no-transform"); - /** - * {@code "none"} - */ - public static final AsciiString NONE = AsciiString.cached("none"); - /** - * {@code "0"} - */ - public static final AsciiString ZERO = AsciiString.cached("0"); - /** - * {@code "only-if-cached"} - */ - public static final AsciiString ONLY_IF_CACHED = AsciiString.cached("only-if-cached"); - /** - * {@code "private"} - */ - public static final AsciiString PRIVATE = AsciiString.cached("private"); - /** - * {@code "proxy-revalidate"} - */ - public static final AsciiString PROXY_REVALIDATE = AsciiString.cached("proxy-revalidate"); - /** - * {@code "public"} - */ - public static final AsciiString PUBLIC = AsciiString.cached("public"); - /** - * {@code "quoted-printable"} - */ - public static final AsciiString QUOTED_PRINTABLE = AsciiString.cached("quoted-printable"); - /** - * {@code "s-maxage"} - */ - public static final AsciiString S_MAXAGE = AsciiString.cached("s-maxage"); - /** - * {@code "text/css"} - */ - public static final AsciiString TEXT_CSS = AsciiString.cached("text/css"); - /** - * {@code "text/html"} - */ - public static final AsciiString TEXT_HTML = AsciiString.cached("text/html"); - /** - * {@code "text/event-stream"} - */ - public static final AsciiString TEXT_EVENT_STREAM = AsciiString.cached("text/event-stream"); - /** - * {@code "text/plain"} - */ - public static final AsciiString TEXT_PLAIN = AsciiString.cached("text/plain"); - /** - * {@code "trailers"} - */ - public static final AsciiString TRAILERS = AsciiString.cached("trailers"); - /** - * {@code "upgrade"} - */ - public static final AsciiString UPGRADE = AsciiString.cached("upgrade"); - /** - * {@code "websocket"} - */ - public static final AsciiString WEBSOCKET = AsciiString.cached("websocket"); - /** - * {@code "XmlHttpRequest"} - */ - public static final AsciiString XML_HTTP_REQUEST = AsciiString.cached("XMLHttpRequest"); - - private HttpHeaderValues() { } -} 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 deleted file mode 100644 index bf7a262c34..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaders.java +++ /dev/null @@ -1,1239 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.handler.codec.DateFormatter; -import io.netty.handler.codec.Headers; -import io.netty.handler.codec.HeadersUtils; -import io.netty.util.AsciiString; -import io.netty.util.CharsetUtil; - -import java.text.ParseException; -import java.util.Calendar; -import java.util.Date; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import static io.netty.util.AsciiString.contentEquals; -import static io.netty.util.AsciiString.contentEqualsIgnoreCase; -import static io.netty.util.AsciiString.trim; -import static java.util.Objects.requireNonNull; - -/** - * Provides the constants for the standard HTTP header names and values and - * commonly used utility methods that accesses an {@link HttpMessage}. - */ -public abstract class HttpHeaders implements Iterable> { - - /** - * @deprecated Use {@link HttpUtil#isKeepAlive(HttpMessage)} instead. - * - * 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()}. - */ - @Deprecated - public static boolean isKeepAlive(HttpMessage message) { - return HttpUtil.isKeepAlive(message); - } - - /** - * @deprecated Use {@link HttpUtil#setKeepAlive(HttpMessage, boolean)} instead. - * - * Sets the value of the {@code "Connection"} header depending on the - * protocol version of the specified message. This getMethod sets or removes - * the {@code "Connection"} header depending on what the default keep alive - * mode of the message's protocol version is, as specified by - * {@link HttpVersion#isKeepAliveDefault()}. - *

    - *
  • If the connection is kept alive by default: - *
      - *
    • set to {@code "close"} if {@code keepAlive} is {@code false}.
    • - *
    • remove otherwise.
    • - *
  • - *
  • If the connection is closed by default: - *
      - *
    • set to {@code "keep-alive"} if {@code keepAlive} is {@code true}.
    • - *
    • remove otherwise.
    • - *
  • - *
- */ - @Deprecated - public static void setKeepAlive(HttpMessage message, boolean keepAlive) { - HttpUtil.setKeepAlive(message, keepAlive); - } - - /** - * @deprecated Use {@link #get(CharSequence)} instead. - */ - @Deprecated - public static String getHeader(HttpMessage message, String name) { - return message.headers().get(name); - } - - /** - * @deprecated Use {@link #get(CharSequence)} instead. - * - * Returns the header value with the specified header name. If there are - * more than one header value for the specified header name, the first - * value is returned. - * - * @return the header value or {@code null} if there is no such header - */ - @Deprecated - public static String getHeader(HttpMessage message, CharSequence name) { - return message.headers().get(name); - } - - /** - * @deprecated Use {@link #get(CharSequence, String)} instead. - * - * @see #getHeader(HttpMessage, CharSequence, String) - */ - @Deprecated - public static String getHeader(HttpMessage message, String name, String defaultValue) { - return message.headers().get(name, defaultValue); - } - - /** - * @deprecated Use {@link #get(CharSequence, String)} instead. - * - * Returns the header value with the specified header name. If there are - * more than one header value for the specified header name, the first - * value is returned. - * - * @return the header value or the {@code defaultValue} if there is no such - * header - */ - @Deprecated - public static String getHeader(HttpMessage message, CharSequence name, String defaultValue) { - return message.headers().get(name, defaultValue); - } - - /** - * @deprecated Use {@link #set(CharSequence, Object)} instead. - * - * @see #setHeader(HttpMessage, CharSequence, Object) - */ - @Deprecated - public static void setHeader(HttpMessage message, String name, Object value) { - message.headers().set(name, value); - } - - /** - * @deprecated Use {@link #set(CharSequence, Object)} instead. - * - * Sets a new header with the specified name and value. If there is an - * existing header with the same name, the existing header is removed. - * If the specified value is not a {@link String}, it is converted into a - * {@link String} by {@link Object#toString()}, except for {@link Date} - * and {@link Calendar} which are formatted to the date format defined in - * RFC2616. - */ - @Deprecated - public static void setHeader(HttpMessage message, CharSequence name, Object value) { - message.headers().set(name, value); - } - - /** - * @deprecated Use {@link #set(CharSequence, Iterable)} instead. - * - * @see #setHeader(HttpMessage, CharSequence, Iterable) - */ - @Deprecated - public static void setHeader(HttpMessage message, String name, Iterable values) { - message.headers().set(name, values); - } - - /** - * @deprecated Use {@link #set(CharSequence, Iterable)} instead. - * - * Sets a new header with the specified name and values. If there is an - * existing header with the same name, the existing header is removed. - * This getMethod can be represented approximately as the following code: - *
-     * removeHeader(message, name);
-     * for (Object v: values) {
-     *     if (v == null) {
-     *         break;
-     *     }
-     *     addHeader(message, name, v);
-     * }
-     * 
- */ - @Deprecated - public static void setHeader(HttpMessage message, CharSequence name, Iterable values) { - message.headers().set(name, values); - } - - /** - * @deprecated Use {@link #add(CharSequence, Object)} instead. - * - * @see #addHeader(HttpMessage, CharSequence, Object) - */ - @Deprecated - public static void addHeader(HttpMessage message, String name, Object value) { - message.headers().add(name, value); - } - - /** - * @deprecated Use {@link #add(CharSequence, Object)} instead. - * - * Adds a new header with the specified name and value. - * If the specified value is not a {@link String}, it is converted into a - * {@link String} by {@link Object#toString()}, except for {@link Date} - * and {@link Calendar} which are formatted to the date format defined in - * RFC2616. - */ - @Deprecated - public static void addHeader(HttpMessage message, CharSequence name, Object value) { - message.headers().add(name, value); - } - - /** - * @deprecated Use {@link #remove(CharSequence)} instead. - * - * @see #removeHeader(HttpMessage, CharSequence) - */ - @Deprecated - public static void removeHeader(HttpMessage message, String name) { - message.headers().remove(name); - } - - /** - * @deprecated Use {@link #remove(CharSequence)} instead. - * - * Removes the header with the specified name. - */ - @Deprecated - public static void removeHeader(HttpMessage message, CharSequence name) { - message.headers().remove(name); - } - - /** - * @deprecated Use {@link #clear()} instead. - * - * Removes all headers from the specified message. - */ - @Deprecated - public static void clearHeaders(HttpMessage message) { - message.headers().clear(); - } - - /** - * @deprecated Use {@link #getInt(CharSequence)} instead. - * - * @see #getIntHeader(HttpMessage, CharSequence) - */ - @Deprecated - public static int getIntHeader(HttpMessage message, String name) { - return getIntHeader(message, (CharSequence) name); - } - - /** - * @deprecated Use {@link #getInt(CharSequence)} instead. - * - * Returns the integer header value with the specified header name. If - * there are more than one header value for the specified header name, the - * first value is returned. - * - * @return the header value - * @throws NumberFormatException - * if there is no such header or the header value is not a number - */ - @Deprecated - public static int getIntHeader(HttpMessage message, CharSequence name) { - String value = message.headers().get(name); - if (value == null) { - throw new NumberFormatException("header not found: " + name); - } - return Integer.parseInt(value); - } - - /** - * @deprecated Use {@link #getInt(CharSequence, int)} instead. - * - * @see #getIntHeader(HttpMessage, CharSequence, int) - */ - @Deprecated - public static int getIntHeader(HttpMessage message, String name, int defaultValue) { - return message.headers().getInt(name, defaultValue); - } - - /** - * @deprecated Use {@link #getInt(CharSequence, int)} instead. - * - * Returns the integer header value with the specified header name. If - * there are more than one header value for the specified header name, the - * first value is returned. - * - * @return the header value or the {@code defaultValue} if there is no such - * header or the header value is not a number - */ - @Deprecated - public static int getIntHeader(HttpMessage message, CharSequence name, int defaultValue) { - return message.headers().getInt(name, defaultValue); - } - - /** - * @deprecated Use {@link #setInt(CharSequence, int)} instead. - * - * @see #setIntHeader(HttpMessage, CharSequence, int) - */ - @Deprecated - public static void setIntHeader(HttpMessage message, String name, int value) { - message.headers().setInt(name, value); - } - - /** - * @deprecated Use {@link #setInt(CharSequence, int)} instead. - * - * Sets a new integer header with the specified name and value. If there - * is an existing header with the same name, the existing header is removed. - */ - @Deprecated - public static void setIntHeader(HttpMessage message, CharSequence name, int value) { - message.headers().setInt(name, value); - } - - /** - * @deprecated Use {@link #set(CharSequence, Iterable)} instead. - * - * @see #setIntHeader(HttpMessage, CharSequence, Iterable) - */ - @Deprecated - public static void setIntHeader(HttpMessage message, String name, Iterable values) { - message.headers().set(name, values); - } - - /** - * @deprecated Use {@link #set(CharSequence, Iterable)} instead. - * - * Sets a new integer header with the specified name and values. If there - * is an existing header with the same name, the existing header is removed. - */ - @Deprecated - public static void setIntHeader(HttpMessage message, CharSequence name, Iterable values) { - message.headers().set(name, values); - } - - /** - * @deprecated Use {@link #add(CharSequence, Iterable)} instead. - * - * @see #addIntHeader(HttpMessage, CharSequence, int) - */ - @Deprecated - public static void addIntHeader(HttpMessage message, String name, int value) { - message.headers().add(name, value); - } - - /** - * @deprecated Use {@link #addInt(CharSequence, int)} instead. - * - * Adds a new integer header with the specified name and value. - */ - @Deprecated - public static void addIntHeader(HttpMessage message, CharSequence name, int value) { - message.headers().addInt(name, value); - } - - /** - * @deprecated Use {@link #getTimeMillis(CharSequence)} instead. - * - * @see #getDateHeader(HttpMessage, CharSequence) - */ - @Deprecated - public static Date getDateHeader(HttpMessage message, String name) throws ParseException { - return getDateHeader(message, (CharSequence) name); - } - - /** - * @deprecated Use {@link #getTimeMillis(CharSequence)} instead. - * - * Returns the date header value with the specified header name. If - * there are more than one header value for the specified header name, the - * first value is returned. - * - * @return the header value - * @throws ParseException - * if there is no such header or the header value is not a formatted date - */ - @Deprecated - public static Date getDateHeader(HttpMessage message, CharSequence name) throws ParseException { - String value = message.headers().get(name); - if (value == null) { - throw new ParseException("header not found: " + name, 0); - } - Date date = DateFormatter.parseHttpDate(value); - if (date == null) { - throw new ParseException("header can't be parsed into a Date: " + value, 0); - } - return date; - } - - /** - * @deprecated Use {@link #getTimeMillis(CharSequence, long)} instead. - * - * @see #getDateHeader(HttpMessage, CharSequence, Date) - */ - @Deprecated - public static Date getDateHeader(HttpMessage message, String name, Date defaultValue) { - return getDateHeader(message, (CharSequence) name, defaultValue); - } - - /** - * @deprecated Use {@link #getTimeMillis(CharSequence, long)} instead. - * - * Returns the date header value with the specified header name. If - * there are more than one header value for the specified header name, the - * first value is returned. - * - * @return the header value or the {@code defaultValue} if there is no such - * header or the header value is not a formatted date - */ - @Deprecated - public static Date getDateHeader(HttpMessage message, CharSequence name, Date defaultValue) { - final String value = getHeader(message, name); - Date date = DateFormatter.parseHttpDate(value); - return date != null ? date : defaultValue; - } - - /** - * @deprecated Use {@link #set(CharSequence, Object)} instead. - * - * @see #setDateHeader(HttpMessage, CharSequence, Date) - */ - @Deprecated - public static void setDateHeader(HttpMessage message, String name, Date value) { - setDateHeader(message, (CharSequence) name, value); - } - - /** - * @deprecated Use {@link #set(CharSequence, Object)} instead. - * - * Sets a new date header with the specified name and value. If there - * is an existing header with the same name, the existing header is removed. - * The specified value is formatted as defined in - * RFC2616 - */ - @Deprecated - public static void setDateHeader(HttpMessage message, CharSequence name, Date value) { - if (value != null) { - message.headers().set(name, DateFormatter.format(value)); - } else { - message.headers().set(name, null); - } - } - - /** - * @deprecated Use {@link #set(CharSequence, Iterable)} instead. - * - * @see #setDateHeader(HttpMessage, CharSequence, Iterable) - */ - @Deprecated - public static void setDateHeader(HttpMessage message, String name, Iterable values) { - message.headers().set(name, values); - } - - /** - * @deprecated Use {@link #set(CharSequence, Iterable)} instead. - * - * Sets a new date header with the specified name and values. If there - * is an existing header with the same name, the existing header is removed. - * The specified values are formatted as defined in - * RFC2616 - */ - @Deprecated - public static void setDateHeader(HttpMessage message, CharSequence name, Iterable values) { - message.headers().set(name, values); - } - - /** - * @deprecated Use {@link #add(CharSequence, Object)} instead. - * - * @see #addDateHeader(HttpMessage, CharSequence, Date) - */ - @Deprecated - public static void addDateHeader(HttpMessage message, String name, Date value) { - message.headers().add(name, value); - } - - /** - * @deprecated Use {@link #add(CharSequence, Object)} instead. - * - * Adds a new date header with the specified name and value. The specified - * value is formatted as defined in - * RFC2616 - */ - @Deprecated - public static void addDateHeader(HttpMessage message, CharSequence name, Date value) { - message.headers().add(name, value); - } - - /** - * @deprecated Use {@link HttpUtil#getContentLength(HttpMessage)} instead. - * - * Returns the length of the content. Please note that this value is - * not retrieved from {@link HttpContent#content()} but from the - * {@code "Content-Length"} header, and thus they are independent from each - * other. - * - * @return the content length - * - * @throws NumberFormatException - * if the message does not have the {@code "Content-Length"} header - * or its value is not a number - */ - @Deprecated - public static long getContentLength(HttpMessage message) { - return HttpUtil.getContentLength(message); - } - - /** - * @deprecated Use {@link HttpUtil#getContentLength(HttpMessage, long)} instead. - * - * Returns the length of the content. Please note that this value is - * not retrieved from {@link HttpContent#content()} but from the - * {@code "Content-Length"} header, and thus they are independent from each - * other. - * - * @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 - */ - @Deprecated - public static long getContentLength(HttpMessage message, long defaultValue) { - return HttpUtil.getContentLength(message, defaultValue); - } - - /** - * @deprecated Use {@link HttpUtil#setContentLength(HttpMessage, long)} instead. - */ - @Deprecated - public static void setContentLength(HttpMessage message, long length) { - HttpUtil.setContentLength(message, length); - } - - /** - * @deprecated Use {@link #get(CharSequence)} instead. - * - * Returns the value of the {@code "Host"} header. - */ - @Deprecated - public static String getHost(HttpMessage message) { - return message.headers().get(HttpHeaderNames.HOST); - } - - /** - * @deprecated Use {@link #get(CharSequence, String)} instead. - * - * Returns the value of the {@code "Host"} header. If there is no such - * header, the {@code defaultValue} is returned. - */ - @Deprecated - public static String getHost(HttpMessage message, String defaultValue) { - return message.headers().get(HttpHeaderNames.HOST, defaultValue); - } - - /** - * @deprecated Use {@link #set(CharSequence, Object)} instead. - * - * @see #setHost(HttpMessage, CharSequence) - */ - @Deprecated - public static void setHost(HttpMessage message, String value) { - message.headers().set(HttpHeaderNames.HOST, value); - } - - /** - * @deprecated Use {@link #set(CharSequence, Object)} instead. - * - * Sets the {@code "Host"} header. - */ - @Deprecated - public static void setHost(HttpMessage message, CharSequence value) { - message.headers().set(HttpHeaderNames.HOST, value); - } - - /** - * @deprecated Use {@link #getTimeMillis(CharSequence)} instead. - * - * Returns the value of the {@code "Date"} header. - * - * @throws ParseException - * if there is no such header or the header value is not a formatted date - */ - @Deprecated - public static Date getDate(HttpMessage message) throws ParseException { - return getDateHeader(message, HttpHeaderNames.DATE); - } - - /** - * @deprecated Use {@link #getTimeMillis(CharSequence, long)} instead. - * - * Returns the value of the {@code "Date"} header. If there is no such - * header or the header is not a formatted date, the {@code defaultValue} - * is returned. - */ - @Deprecated - public static Date getDate(HttpMessage message, Date defaultValue) { - return getDateHeader(message, HttpHeaderNames.DATE, defaultValue); - } - - /** - * @deprecated Use {@link #set(CharSequence, Object)} instead. - * - * Sets the {@code "Date"} header. - */ - @Deprecated - public static void setDate(HttpMessage message, Date value) { - message.headers().set(HttpHeaderNames.DATE, value); - } - - /** - * @deprecated Use {@link HttpUtil#is100ContinueExpected(HttpMessage)} instead. - * - * Returns {@code true} if and only if the specified message contains the - * {@code "Expect: 100-continue"} header. - */ - @Deprecated - public static boolean is100ContinueExpected(HttpMessage message) { - return HttpUtil.is100ContinueExpected(message); - } - - /** - * @deprecated Use {@link HttpUtil#set100ContinueExpected(HttpMessage, boolean)} instead. - * - * Sets the {@code "Expect: 100-continue"} header to the specified message. - * If there is any existing {@code "Expect"} header, they are replaced with - * the new one. - */ - @Deprecated - public static void set100ContinueExpected(HttpMessage message) { - HttpUtil.set100ContinueExpected(message, true); - } - - /** - * @deprecated Use {@link HttpUtil#set100ContinueExpected(HttpMessage, boolean)} instead. - * - * Sets or removes the {@code "Expect: 100-continue"} header to / from the - * specified message. If {@code set} is {@code true}, - * the {@code "Expect: 100-continue"} header is set and all other previous - * {@code "Expect"} headers are removed. Otherwise, all {@code "Expect"} - * headers are removed completely. - */ - @Deprecated - public static void set100ContinueExpected(HttpMessage message, boolean set) { - HttpUtil.set100ContinueExpected(message, set); - } - - /** - * @deprecated Use {@link HttpUtil#isTransferEncodingChunked(HttpMessage)} instead. - * - * Checks to see if the transfer encoding in a specified {@link HttpMessage} is chunked - * - * @param message The message to check - * @return True if transfer encoding is chunked, otherwise false - */ - @Deprecated - public static boolean isTransferEncodingChunked(HttpMessage message) { - return HttpUtil.isTransferEncodingChunked(message); - } - - /** - * @deprecated Use {@link HttpUtil#setTransferEncodingChunked(HttpMessage, boolean)} instead. - */ - @Deprecated - public static void removeTransferEncodingChunked(HttpMessage m) { - HttpUtil.setTransferEncodingChunked(m, false); - } - - /** - * @deprecated Use {@link HttpUtil#setTransferEncodingChunked(HttpMessage, boolean)} instead. - */ - @Deprecated - public static void setTransferEncodingChunked(HttpMessage m) { - HttpUtil.setTransferEncodingChunked(m, true); - } - - /** - * @deprecated Use {@link HttpUtil#isContentLengthSet(HttpMessage)} instead. - */ - @Deprecated - public static boolean isContentLengthSet(HttpMessage m) { - return HttpUtil.isContentLengthSet(m); - } - - /** - * @deprecated Use {@link AsciiString#contentEqualsIgnoreCase(CharSequence, CharSequence)} instead. - */ - @Deprecated - public static boolean equalsIgnoreCase(CharSequence name1, CharSequence name2) { - return contentEqualsIgnoreCase(name1, name2); - } - - @Deprecated - public static void encodeAscii(CharSequence seq, ByteBuf buf) { - if (seq instanceof AsciiString) { - ByteBufUtil.copy((AsciiString) seq, 0, buf, seq.length()); - } else { - buf.writeCharSequence(seq, CharsetUtil.US_ASCII); - } - } - - /** - * @deprecated Use {@link AsciiString} instead. - *

- * Create a new {@link CharSequence} which is optimized for reuse as {@link HttpHeaders} name or value. - * So if you have a Header name or value that you want to reuse you should make use of this. - */ - @Deprecated - public static CharSequence newEntity(String name) { - return new AsciiString(name); - } - - protected HttpHeaders() { } - - /** - * @see #get(CharSequence) - */ - public abstract String get(String name); - - /** - * Returns the value of a header with the specified name. If there are - * more than one values for the specified name, the first value is returned. - * - * @param name The name of the header to search - * @return The first header value or {@code null} if there is no such header - * @see #getAsString(CharSequence) - */ - public String get(CharSequence name) { - return get(name.toString()); - } - - /** - * Returns the value of a header with the specified name. If there are - * more than one values for the specified name, the first value is returned. - * - * @param name The name of the header to search - * @return The first header value or {@code defaultValue} if there is no such header - */ - public String get(CharSequence name, String defaultValue) { - String value = get(name); - if (value == null) { - return defaultValue; - } - return value; - } - - /** - * Returns the integer value of a header with the specified name. If there are more than one values for the - * specified name, the first value is returned. - * - * @param name the name of the header to search - * @return the first header value if the header is found and its value is an integer. {@code null} if there's no - * such header or its value is not an integer. - */ - public abstract Integer getInt(CharSequence name); - - /** - * Returns the integer value of a header with the specified name. If there are more than one values for the - * specified name, the first value is returned. - * - * @param name the name of the header to search - * @param defaultValue the default value - * @return the first header value if the header is found and its value is an integer. {@code defaultValue} if - * there's no such header or its value is not an integer. - */ - public abstract int getInt(CharSequence name, int defaultValue); - - /** - * Returns the short value of a header with the specified name. If there are more than one values for the - * specified name, the first value is returned. - * - * @param name the name of the header to search - * @return the first header value if the header is found and its value is a short. {@code null} if there's no - * such header or its value is not a short. - */ - public abstract Short getShort(CharSequence name); - - /** - * Returns the short value of a header with the specified name. If there are more than one values for the - * specified name, the first value is returned. - * - * @param name the name of the header to search - * @param defaultValue the default value - * @return the first header value if the header is found and its value is a short. {@code defaultValue} if - * there's no such header or its value is not a short. - */ - public abstract short getShort(CharSequence name, short defaultValue); - - /** - * Returns the date value of a header with the specified name. If there are more than one values for the - * specified name, the first value is returned. - * - * @param name the name of the header to search - * @return the first header value if the header is found and its value is a date. {@code null} if there's no - * such header or its value is not a date. - */ - public abstract Long getTimeMillis(CharSequence name); - - /** - * Returns the date value of a header with the specified name. If there are more than one values for the - * specified name, the first value is returned. - * - * @param name the name of the header to search - * @param defaultValue the default value - * @return the first header value if the header is found and its value is a date. {@code defaultValue} if - * there's no such header or its value is not a date. - */ - public abstract long getTimeMillis(CharSequence name, long defaultValue); - - /** - * @see #getAll(CharSequence) - */ - public abstract List getAll(String name); - - /** - * Returns the values of headers with the specified name - * - * @param name The name of the headers to search - * @return A {@link List} of header values which will be empty if no values - * are found - * @see #getAllAsString(CharSequence) - */ - public List getAll(CharSequence name) { - return getAll(name.toString()); - } - - /** - * Returns a new {@link List} that contains all headers in this object. Note that modifying the - * returned {@link List} will not affect the state of this object. If you intend to enumerate over the header - * entries only, use {@link #iterator()} instead, which has much less overhead. - * @see #iteratorCharSequence() - */ - public abstract List> entries(); - - /** - * @see #contains(CharSequence) - */ - public abstract boolean contains(String name); - - /** - * @deprecated It is preferred to use {@link #iteratorCharSequence()} unless you need {@link String}. - * If {@link String} is required then use {@link #iteratorAsString()}. - */ - @Deprecated - @Override - public abstract Iterator> iterator(); - - /** - * @return Iterator over the name/value header pairs. - */ - public abstract Iterator> iteratorCharSequence(); - - /** - * Equivalent to {@link #getAll(String)} but it is possible that no intermediate list is generated. - * @param name the name of the header to retrieve - * @return an {@link Iterator} of header values corresponding to {@code name}. - */ - public Iterator valueStringIterator(CharSequence name) { - return getAll(name).iterator(); - } - - /** - * Equivalent to {@link #getAll(String)} but it is possible that no intermediate list is generated. - * @param name the name of the header to retrieve - * @return an {@link Iterator} of header values corresponding to {@code name}. - */ - public Iterator valueCharSequenceIterator(CharSequence name) { - return valueStringIterator(name); - } - - /** - * Checks to see if there is a header with the specified name - * - * @param name The name of the header to search for - * @return True if at least one header is found - */ - public boolean contains(CharSequence name) { - return contains(name.toString()); - } - - /** - * Checks if no header exists. - */ - public abstract boolean isEmpty(); - - /** - * Returns the number of headers in this object. - */ - public abstract int size(); - - /** - * Returns a new {@link Set} that contains the names of all headers in this object. Note that modifying the - * returned {@link Set} will not affect the state of this object. If you intend to enumerate over the header - * entries only, use {@link #iterator()} instead, which has much less overhead. - */ - public abstract Set names(); - - /** - * @see #add(CharSequence, Object) - */ - public abstract HttpHeaders add(String name, Object value); - - /** - * Adds a new header with the specified name and value. - * - * If the specified value is not a {@link String}, it is converted - * into a {@link String} by {@link Object#toString()}, except in the cases - * of {@link Date} and {@link Calendar}, which are formatted to the date - * format defined in RFC2616. - * - * @param name The name of the header being added - * @param value The value of the header being added - * - * @return {@code this} - */ - public HttpHeaders add(CharSequence name, Object value) { - return add(name.toString(), value); - } - - /** - * @see #add(CharSequence, Iterable) - */ - public abstract HttpHeaders add(String name, Iterable values); - - /** - * Adds a new header with the specified name and values. - * - * This getMethod can be represented approximately as the following code: - *

-     * for (Object v: values) {
-     *     if (v == null) {
-     *         break;
-     *     }
-     *     headers.add(name, v);
-     * }
-     * 
- * - * @param name The name of the headers being set - * @param values The values of the headers being set - * @return {@code this} - */ - public HttpHeaders add(CharSequence name, Iterable values) { - return add(name.toString(), values); - } - - /** - * Adds all header entries of the specified {@code headers}. - * - * @return {@code this} - */ - public HttpHeaders add(HttpHeaders headers) { - requireNonNull(headers, "headers"); - for (Map.Entry e: headers) { - add(e.getKey(), e.getValue()); - } - return this; - } - - /** - * Add the {@code name} to {@code value}. - * @param name The name to modify - * @param value The value - * @return {@code this} - */ - public abstract HttpHeaders addInt(CharSequence name, int value); - - /** - * Add the {@code name} to {@code value}. - * @param name The name to modify - * @param value The value - * @return {@code this} - */ - public abstract HttpHeaders addShort(CharSequence name, short value); - - /** - * @see #set(CharSequence, Object) - */ - public abstract HttpHeaders set(String name, Object value); - - /** - * Sets a header with the specified name and value. - * - * If there is an existing header with the same name, it is removed. - * If the specified value is not a {@link String}, it is converted into a - * {@link String} by {@link Object#toString()}, except for {@link Date} - * and {@link Calendar}, which are formatted to the date format defined in - * RFC2616. - * - * @param name The name of the header being set - * @param value The value of the header being set - * @return {@code this} - */ - public HttpHeaders set(CharSequence name, Object value) { - return set(name.toString(), value); - } - - /** - * @see #set(CharSequence, Iterable) - */ - public abstract HttpHeaders set(String name, Iterable values); - - /** - * Sets a header with the specified name and values. - * - * If there is an existing header with the same name, it is removed. - * This getMethod can be represented approximately as the following code: - *
-     * headers.remove(name);
-     * for (Object v: values) {
-     *     if (v == null) {
-     *         break;
-     *     }
-     *     headers.add(name, v);
-     * }
-     * 
- * - * @param name The name of the headers being set - * @param values The values of the headers being set - * @return {@code this} - */ - public HttpHeaders set(CharSequence name, Iterable values) { - return set(name.toString(), values); - } - - /** - * Cleans the current header entries and copies all header entries of the specified {@code headers}. - * - * @return {@code this} - */ - public HttpHeaders set(HttpHeaders headers) { - requireNonNull(headers, "headers"); - - clear(); - - if (headers.isEmpty()) { - return this; - } - - for (Entry entry : headers) { - add(entry.getKey(), entry.getValue()); - } - return this; - } - - /** - * Retains all current headers but calls {@link #set(String, Object)} for each entry in {@code headers} - * - * @param headers The headers used to {@link #set(String, Object)} values in this instance - * @return {@code this} - */ - public HttpHeaders setAll(HttpHeaders headers) { - requireNonNull(headers, "headers"); - - if (headers.isEmpty()) { - return this; - } - - for (Entry entry : headers) { - set(entry.getKey(), entry.getValue()); - } - return this; - } - - /** - * Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}. - * @param name The name to modify - * @param value The value - * @return {@code this} - */ - public abstract HttpHeaders setInt(CharSequence name, int value); - - /** - * Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}. - * @param name The name to modify - * @param value The value - * @return {@code this} - */ - public abstract HttpHeaders setShort(CharSequence name, short value); - - /** - * @see #remove(CharSequence) - */ - public abstract HttpHeaders remove(String name); - - /** - * Removes the header with the specified name. - * - * @param name The name of the header to remove - * @return {@code this} - */ - public HttpHeaders remove(CharSequence name) { - return remove(name.toString()); - } - - /** - * Removes all headers from this {@link HttpMessage}. - * - * @return {@code this} - */ - public abstract HttpHeaders clear(); - - /** - * @see #contains(CharSequence, CharSequence, boolean) - */ - public boolean contains(String name, String value, boolean ignoreCase) { - Iterator valueIterator = valueStringIterator(name); - if (ignoreCase) { - while (valueIterator.hasNext()) { - if (valueIterator.next().equalsIgnoreCase(value)) { - return true; - } - } - } else { - while (valueIterator.hasNext()) { - if (valueIterator.next().equals(value)) { - return true; - } - } - } - return false; - } - - /** - * Returns {@code true} if a header with the {@code name} and {@code value} exists, {@code false} otherwise. - * This also handles multiple values that are separated with a {@code ,}. - *

- * If {@code ignoreCase} is {@code true} then a case insensitive compare is done on the value. - * @param name the name of the header to find - * @param value the value of the header to find - * @param ignoreCase {@code true} then a case insensitive compare is run to compare values. - * otherwise a case sensitive compare is run to compare values. - */ - public boolean containsValue(CharSequence name, CharSequence value, boolean ignoreCase) { - Iterator itr = valueCharSequenceIterator(name); - while (itr.hasNext()) { - if (containsCommaSeparatedTrimmed(itr.next(), value, ignoreCase)) { - return true; - } - } - return false; - } - - private static boolean containsCommaSeparatedTrimmed(CharSequence rawNext, CharSequence expected, - boolean ignoreCase) { - int begin = 0; - int end; - if (ignoreCase) { - if ((end = AsciiString.indexOf(rawNext, ',', begin)) == -1) { - if (contentEqualsIgnoreCase(trim(rawNext), expected)) { - return true; - } - } else { - do { - if (contentEqualsIgnoreCase(trim(rawNext.subSequence(begin, end)), expected)) { - return true; - } - begin = end + 1; - } while ((end = AsciiString.indexOf(rawNext, ',', begin)) != -1); - - if (begin < rawNext.length()) { - if (contentEqualsIgnoreCase(trim(rawNext.subSequence(begin, rawNext.length())), expected)) { - return true; - } - } - } - } else { - if ((end = AsciiString.indexOf(rawNext, ',', begin)) == -1) { - if (contentEquals(trim(rawNext), expected)) { - return true; - } - } else { - do { - if (contentEquals(trim(rawNext.subSequence(begin, end)), expected)) { - return true; - } - begin = end + 1; - } while ((end = AsciiString.indexOf(rawNext, ',', begin)) != -1); - - if (begin < rawNext.length()) { - if (contentEquals(trim(rawNext.subSequence(begin, rawNext.length())), expected)) { - return true; - } - } - } - } - return false; - } - - /** - * {@link Headers#get(Object)} and convert the result to a {@link String}. - * @param name the name of the header to retrieve - * @return the first header value if the header is found. {@code null} if there's no such header. - */ - public final String getAsString(CharSequence name) { - return get(name); - } - - /** - * {@link Headers#getAll(Object)} and convert each element of {@link List} to a {@link String}. - * @param name the name of the header to retrieve - * @return a {@link List} of header values or an empty {@link List} if no values are found. - */ - public final List getAllAsString(CharSequence name) { - return getAll(name); - } - - /** - * {@link Iterator} that converts each {@link Entry}'s key and value to a {@link String}. - */ - public final Iterator> iteratorAsString() { - return iterator(); - } - - /** - * Returns {@code true} if a header with the {@code name} and {@code value} exists, {@code false} otherwise. - *

- * If {@code ignoreCase} is {@code true} then a case insensitive compare is done on the value. - * @param name the name of the header to find - * @param value the value of the header to find - * @param ignoreCase {@code true} then a case insensitive compare is run to compare values. - * otherwise a case sensitive compare is run to compare values. - */ - public boolean contains(CharSequence name, CharSequence value, boolean ignoreCase) { - return contains(name.toString(), value.toString(), ignoreCase); - } - - @Override - public String toString() { - return HeadersUtils.toString(getClass(), iteratorCharSequence(), size()); - } - - /** - * Returns a deep copy of the passed in {@link HttpHeaders}. - */ - public HttpHeaders copy() { - return new DefaultHttpHeaders().set(this); - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeadersEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeadersEncoder.java deleted file mode 100644 index 2ba766a4c6..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeadersEncoder.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.http; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.util.AsciiString; -import io.netty.util.CharsetUtil; - -import static io.netty.handler.codec.http.HttpConstants.*; -import static io.netty.handler.codec.http.HttpObjectEncoder.CRLF_SHORT; - -final class HttpHeadersEncoder { - private static final int COLON_AND_SPACE_SHORT = (COLON << 8) | SP; - - private HttpHeadersEncoder() { - } - - static void encoderHeader(CharSequence name, CharSequence value, ByteBuf buf) { - final int nameLen = name.length(); - final int valueLen = value.length(); - final int entryLen = nameLen + valueLen + 4; - buf.ensureWritable(entryLen); - int offset = buf.writerIndex(); - writeAscii(buf, offset, name); - offset += nameLen; - ByteBufUtil.setShortBE(buf, offset, COLON_AND_SPACE_SHORT); - offset += 2; - writeAscii(buf, offset, value); - offset += valueLen; - ByteBufUtil.setShortBE(buf, offset, CRLF_SHORT); - offset += 2; - buf.writerIndex(offset); - } - - private static void writeAscii(ByteBuf buf, int offset, CharSequence value) { - if (value instanceof AsciiString) { - ByteBufUtil.copy((AsciiString) value, 0, buf, offset, value.length()); - } else { - buf.setCharSequence(offset, value, CharsetUtil.US_ASCII); - } - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpMessage.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpMessage.java deleted file mode 100644 index d0307cef98..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpMessage.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - - -/** - * An interface that defines an HTTP message, providing common properties for - * {@link HttpRequest} and {@link HttpResponse}. - * - * @see HttpResponse - * @see HttpRequest - * @see HttpHeaders - */ -public interface HttpMessage extends HttpObject { - - /** - * @deprecated Use {@link #protocolVersion()} instead. - */ - @Deprecated - HttpVersion getProtocolVersion(); - - /** - * Returns the protocol version of this {@link HttpMessage} - */ - HttpVersion protocolVersion(); - - /** - * Set the protocol version of this {@link HttpMessage} - */ - HttpMessage setProtocolVersion(HttpVersion version); - - /** - * Returns the headers of this message. - */ - HttpHeaders headers(); -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpMessageDecoderResult.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpMessageDecoderResult.java deleted file mode 100644 index b89252b24e..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpMessageDecoderResult.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.handler.codec.DecoderResult; - -/** - * A {@link DecoderResult} for {@link HttpMessage}s as produced by an {@link HttpObjectDecoder}. - *

- * Please note that there is no guarantee that a {@link HttpObjectDecoder} will produce a {@link - * HttpMessageDecoderResult}. It may simply produce a regular {@link DecoderResult}. This result is intended for - * successful {@link HttpMessage} decoder results. - */ -public final class HttpMessageDecoderResult extends DecoderResult { - - private final int initialLineLength; - private final int headerSize; - - HttpMessageDecoderResult(int initialLineLength, int headerSize) { - super(SIGNAL_SUCCESS); - this.initialLineLength = initialLineLength; - this.headerSize = headerSize; - } - - /** - * The decoded initial line length (in bytes), as controlled by {@code maxInitialLineLength}. - */ - public int initialLineLength() { - return initialLineLength; - } - - /** - * The decoded header size (in bytes), as controlled by {@code maxHeaderSize}. - */ - public int headerSize() { - return headerSize; - } - - /** - * The decoded initial line length plus the decoded header size (in bytes). - */ - public int totalSize() { - return initialLineLength + headerSize; - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpMessageUtil.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpMessageUtil.java deleted file mode 100644 index 2e6115c62a..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpMessageUtil.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.http; - -import io.netty.util.internal.StringUtil; - -import java.util.Map; - -/** - * Provides some utility methods for HTTP message implementations. - */ -final class HttpMessageUtil { - - static StringBuilder appendRequest(StringBuilder buf, HttpRequest req) { - appendCommon(buf, req); - appendInitialLine(buf, req); - appendHeaders(buf, req.headers()); - removeLastNewLine(buf); - return buf; - } - - static StringBuilder appendResponse(StringBuilder buf, HttpResponse res) { - appendCommon(buf, res); - appendInitialLine(buf, res); - appendHeaders(buf, res.headers()); - removeLastNewLine(buf); - return buf; - } - - private static void appendCommon(StringBuilder buf, HttpMessage msg) { - buf.append(StringUtil.simpleClassName(msg)); - buf.append("(decodeResult: "); - buf.append(msg.decoderResult()); - buf.append(", version: "); - buf.append(msg.protocolVersion()); - buf.append(')'); - buf.append(StringUtil.NEWLINE); - } - - static StringBuilder appendFullRequest(StringBuilder buf, FullHttpRequest req) { - appendFullCommon(buf, req); - appendInitialLine(buf, req); - appendHeaders(buf, req.headers()); - appendHeaders(buf, req.trailingHeaders()); - removeLastNewLine(buf); - return buf; - } - - static StringBuilder appendFullResponse(StringBuilder buf, FullHttpResponse res) { - appendFullCommon(buf, res); - appendInitialLine(buf, res); - appendHeaders(buf, res.headers()); - appendHeaders(buf, res.trailingHeaders()); - removeLastNewLine(buf); - return buf; - } - - private static void appendFullCommon(StringBuilder buf, FullHttpMessage msg) { - buf.append(StringUtil.simpleClassName(msg)); - buf.append("(decodeResult: "); - buf.append(msg.decoderResult()); - buf.append(", version: "); - buf.append(msg.protocolVersion()); - buf.append(", content: "); - buf.append(msg.content()); - buf.append(')'); - buf.append(StringUtil.NEWLINE); - } - - private static void appendInitialLine(StringBuilder buf, HttpRequest req) { - buf.append(req.method()); - buf.append(' '); - buf.append(req.uri()); - buf.append(' '); - buf.append(req.protocolVersion()); - buf.append(StringUtil.NEWLINE); - } - - private static void appendInitialLine(StringBuilder buf, HttpResponse res) { - buf.append(res.protocolVersion()); - buf.append(' '); - buf.append(res.status()); - buf.append(StringUtil.NEWLINE); - } - - private static void appendHeaders(StringBuilder buf, HttpHeaders headers) { - for (Map.Entry e: headers) { - buf.append(e.getKey()); - buf.append(": "); - buf.append(e.getValue()); - buf.append(StringUtil.NEWLINE); - } - } - - private static void removeLastNewLine(StringBuilder buf) { - buf.setLength(buf.length() - StringUtil.NEWLINE.length()); - } - - private HttpMessageUtil() { } -} 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 deleted file mode 100644 index 137cab1caa..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpMethod.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.util.AsciiString; - -import static io.netty.util.internal.MathUtil.findNextPositivePowerOfTwo; -import static io.netty.util.internal.ObjectUtil.checkNonEmptyAfterTrim; - -/** - * The request method of HTTP or its derived protocols, such as - * RTSP and - * ICAP. - */ -public class HttpMethod implements Comparable { - /** - * The OPTIONS method represents a request for information about the communication options - * available on the request/response chain identified by the Request-URI. This method allows - * the client to determine the options and/or requirements associated with a resource, or the - * capabilities of a server, without implying a resource action or initiating a resource - * retrieval. - */ - public static final HttpMethod OPTIONS = new HttpMethod("OPTIONS"); - - /** - * The GET method means retrieve whatever information (in the form of an entity) is identified - * by the Request-URI. If the Request-URI refers to a data-producing process, it is the - * produced data which shall be returned as the entity in the response and not the source text - * of the process, unless that text happens to be the output of the process. - */ - public static final HttpMethod GET = new HttpMethod("GET"); - - /** - * The HEAD method is identical to GET except that the server MUST NOT return a message-body - * in the response. - */ - public static final HttpMethod HEAD = new HttpMethod("HEAD"); - - /** - * The POST method is used to request that the origin server accept the entity enclosed in the - * request as a new subordinate of the resource identified by the Request-URI in the - * Request-Line. - */ - public static final HttpMethod POST = new HttpMethod("POST"); - - /** - * The PUT method requests that the enclosed entity be stored under the supplied Request-URI. - */ - public static final HttpMethod PUT = new HttpMethod("PUT"); - - /** - * The PATCH method requests that a set of changes described in the - * request entity be applied to the resource identified by the Request-URI. - */ - public static final HttpMethod PATCH = new HttpMethod("PATCH"); - - /** - * The DELETE method requests that the origin server delete the resource identified by the - * Request-URI. - */ - public static final HttpMethod DELETE = new HttpMethod("DELETE"); - - /** - * The TRACE method is used to invoke a remote, application-layer loop- back of the request - * message. - */ - public static final HttpMethod TRACE = new HttpMethod("TRACE"); - - /** - * This specification reserves the method name CONNECT for use with a proxy that can dynamically - * switch to being a tunnel - */ - public static final HttpMethod CONNECT = new HttpMethod("CONNECT"); - - private static final EnumNameMap methodMap; - - static { - methodMap = new EnumNameMap<>( - new EnumNameMap.Node<>(OPTIONS.toString(), OPTIONS), - new EnumNameMap.Node<>(GET.toString(), GET), - new EnumNameMap.Node<>(HEAD.toString(), HEAD), - new EnumNameMap.Node<>(POST.toString(), POST), - new EnumNameMap.Node<>(PUT.toString(), PUT), - new EnumNameMap.Node<>(PATCH.toString(), PATCH), - new EnumNameMap.Node<>(DELETE.toString(), DELETE), - new EnumNameMap.Node<>(TRACE.toString(), TRACE), - new EnumNameMap.Node<>(CONNECT.toString(), CONNECT)); - } - - /** - * Returns the {@link HttpMethod} represented by the specified name. - * If the specified name is a standard HTTP method name, a cached instance - * will be returned. Otherwise, a new instance will be returned. - */ - public static HttpMethod valueOf(String name) { - HttpMethod result = methodMap.get(name); - return result != null ? result : new HttpMethod(name); - } - - private final AsciiString name; - - /** - * Creates a new HTTP method with the specified name. You will not need to - * create a new method unless you are implementing a protocol derived from - * HTTP, such as - * RTSP and - * ICAP - */ - public HttpMethod(String name) { - name = checkNonEmptyAfterTrim(name, "name"); - - for (int i = 0; i < name.length(); i ++) { - char c = name.charAt(i); - if (Character.isISOControl(c) || Character.isWhitespace(c)) { - throw new IllegalArgumentException("invalid character in name"); - } - } - - this.name = AsciiString.cached(name); - } - - /** - * Returns the name of this method. - */ - public String name() { - return name.toString(); - } - - /** - * Returns the name of this method. - */ - public AsciiString asciiName() { - return name; - } - - @Override - public int hashCode() { - return name().hashCode(); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof HttpMethod)) { - return false; - } - - HttpMethod that = (HttpMethod) o; - return name().equals(that.name()); - } - - @Override - public String toString() { - return name.toString(); - } - - @Override - public int compareTo(HttpMethod o) { - if (o == this) { - return 0; - } - return name().compareTo(o.name()); - } - - private static final class EnumNameMap { - private final EnumNameMap.Node[] values; - private final int valuesMask; - - EnumNameMap(EnumNameMap.Node... nodes) { - values = (EnumNameMap.Node[]) new EnumNameMap.Node[findNextPositivePowerOfTwo(nodes.length)]; - valuesMask = values.length - 1; - for (EnumNameMap.Node node : nodes) { - int i = hashCode(node.key) & valuesMask; - if (values[i] != null) { - throw new IllegalArgumentException("index " + i + " collision between values: [" + - values[i].key + ", " + node.key + ']'); - } - values[i] = node; - } - } - - T get(String name) { - EnumNameMap.Node node = values[hashCode(name) & valuesMask]; - return node == null || !node.key.equals(name) ? null : node.value; - } - - private static int hashCode(String name) { - // This hash code needs to produce a unique index in the "values" array for each HttpMethod. If new - // HttpMethods are added this algorithm will need to be adjusted. The constructor will "fail fast" if there - // are duplicates detected. - // For example with the current set of HttpMethods it just so happens that the String hash code value - // shifted right by 6 bits modulo 16 is unique relative to all other HttpMethod values. - return name.hashCode() >>> 6; - } - - private static final class Node { - final String key; - final T value; - - Node(String key, T value) { - this.key = key; - this.value = value; - } - } - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObject.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObject.java deleted file mode 100644 index 62c3841eb3..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObject.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.handler.codec.DecoderResult; -import io.netty.handler.codec.DecoderResultProvider; - -public interface HttpObject extends DecoderResultProvider { - /** - * @deprecated Use {@link #decoderResult()} instead. - */ - @Deprecated - DecoderResult getDecoderResult(); -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectAggregator.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectAggregator.java deleted file mode 100644 index 1ac0c55989..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectAggregator.java +++ /dev/null @@ -1,581 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.handler.codec.DecoderResult; -import io.netty.handler.codec.MessageAggregator; -import io.netty.handler.codec.TooLongFrameException; -import io.netty.util.concurrent.Future; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import static io.netty.handler.codec.http.HttpHeaderNames.CONNECTION; -import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH; -import static io.netty.handler.codec.http.HttpHeaderNames.EXPECT; -import static io.netty.handler.codec.http.HttpUtil.getContentLength; - -/** - * A {@link ChannelHandler} that aggregates an {@link HttpMessage} - * and its following {@link HttpContent}s into a single {@link FullHttpRequest} - * or {@link FullHttpResponse} (depending on if it used to handle requests or responses) - * with no following {@link HttpContent}s. It is useful when you don't want to take - * care of HTTP messages whose transfer encoding is 'chunked'. Insert this - * handler after {@link HttpResponseDecoder} in the {@link ChannelPipeline} if being used to handle - * responses, or after {@link HttpRequestDecoder} and {@link HttpResponseEncoder} in the - * {@link ChannelPipeline} if being used to handle requests. - *

- *
- *  {@link ChannelPipeline} p = ...;
- *  ...
- *  p.addLast("decoder", new {@link HttpRequestDecoder}());
- *  p.addLast("encoder", new {@link HttpResponseEncoder}());
- *  p.addLast("aggregator", new {@link HttpObjectAggregator}(1048576));
- *  ...
- *  p.addLast("handler", new HttpRequestHandler());
- *  
- *
- *

- * For convenience, consider putting a {@link HttpServerCodec} before the {@link HttpObjectAggregator} - * as it functions as both a {@link HttpRequestDecoder} and a {@link HttpResponseEncoder}. - *

- * Be aware that {@link HttpObjectAggregator} may end up sending a {@link HttpResponse}: - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
Response StatusCondition When Sent
100 ContinueA '100-continue' expectation is received and the 'content-length' doesn't exceed maxContentLength
417 Expectation FailedA '100-continue' expectation is received and the 'content-length' exceeds maxContentLength
413 Request Entity Too LargeEither the 'content-length' or the bytes received so far exceed maxContentLength
- * - * @see FullHttpRequest - * @see FullHttpResponse - * @see HttpResponseDecoder - * @see HttpServerCodec - */ -public class HttpObjectAggregator - extends MessageAggregator { - private static final InternalLogger logger = InternalLoggerFactory.getInstance(HttpObjectAggregator.class); - private static final FullHttpResponse CONTINUE = - new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE, Unpooled.EMPTY_BUFFER); - private static final FullHttpResponse EXPECTATION_FAILED = new DefaultFullHttpResponse( - HttpVersion.HTTP_1_1, HttpResponseStatus.EXPECTATION_FAILED, Unpooled.EMPTY_BUFFER); - private static final FullHttpResponse TOO_LARGE_CLOSE = new DefaultFullHttpResponse( - HttpVersion.HTTP_1_1, HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE, Unpooled.EMPTY_BUFFER); - private static final FullHttpResponse TOO_LARGE = new DefaultFullHttpResponse( - HttpVersion.HTTP_1_1, HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE, Unpooled.EMPTY_BUFFER); - - static { - EXPECTATION_FAILED.headers().set(CONTENT_LENGTH, 0); - TOO_LARGE.headers().set(CONTENT_LENGTH, 0); - - TOO_LARGE_CLOSE.headers().set(CONTENT_LENGTH, 0); - TOO_LARGE_CLOSE.headers().set(CONNECTION, HttpHeaderValues.CLOSE); - } - - private final boolean closeOnExpectationFailed; - - /** - * Creates a new instance. - * @param maxContentLength the maximum length of the aggregated content in bytes. - * If the length of the aggregated content exceeds this value, - * {@link #handleOversizedMessage(ChannelHandlerContext, HttpMessage)} will be called. - */ - public HttpObjectAggregator(int maxContentLength) { - this(maxContentLength, false); - } - - /** - * Creates a new instance. - * @param maxContentLength the maximum length of the aggregated content in bytes. - * If the length of the aggregated content exceeds this value, - * {@link #handleOversizedMessage(ChannelHandlerContext, HttpMessage)} will be called. - * @param closeOnExpectationFailed If a 100-continue response is detected but the content length is too large - * then {@code true} means close the connection. otherwise the connection will remain open and data will be - * consumed and discarded until the next request is received. - */ - public HttpObjectAggregator(int maxContentLength, boolean closeOnExpectationFailed) { - super(maxContentLength); - this.closeOnExpectationFailed = closeOnExpectationFailed; - } - - @Override - protected boolean isStartMessage(HttpObject msg) throws Exception { - return msg instanceof HttpMessage; - } - - @Override - protected boolean isContentMessage(HttpObject msg) throws Exception { - return msg instanceof HttpContent; - } - - @Override - protected boolean isLastContentMessage(HttpContent msg) throws Exception { - return msg instanceof LastHttpContent; - } - - @Override - protected boolean isAggregated(HttpObject msg) throws Exception { - return msg instanceof FullHttpMessage; - } - - @Override - protected boolean isContentLengthInvalid(HttpMessage start, int maxContentLength) { - try { - return getContentLength(start, -1L) > maxContentLength; - } catch (final NumberFormatException e) { - return false; - } - } - - private static Object continueResponse(HttpMessage start, int maxContentLength, ChannelPipeline pipeline) { - if (HttpUtil.isUnsupportedExpectation(start)) { - // if the request contains an unsupported expectation, we return 417 - pipeline.fireUserEventTriggered(HttpExpectationFailedEvent.INSTANCE); - return EXPECTATION_FAILED.retainedDuplicate(); - } else if (HttpUtil.is100ContinueExpected(start)) { - // if the request contains 100-continue but the content-length is too large, we return 413 - if (getContentLength(start, -1L) <= maxContentLength) { - return CONTINUE.retainedDuplicate(); - } - pipeline.fireUserEventTriggered(HttpExpectationFailedEvent.INSTANCE); - return TOO_LARGE.retainedDuplicate(); - } - - return null; - } - - @Override - protected Object newContinueResponse(HttpMessage start, int maxContentLength, ChannelPipeline pipeline) { - Object response = continueResponse(start, maxContentLength, pipeline); - // we're going to respond based on the request expectation so there's no - // need to propagate the expectation further. - if (response != null) { - start.headers().remove(EXPECT); - } - return response; - } - - @Override - protected boolean closeAfterContinueResponse(Object msg) { - return closeOnExpectationFailed && ignoreContentAfterContinueResponse(msg); - } - - @Override - protected boolean ignoreContentAfterContinueResponse(Object msg) { - if (msg instanceof HttpResponse) { - final HttpResponse httpResponse = (HttpResponse) msg; - return httpResponse.status().codeClass().equals(HttpStatusClass.CLIENT_ERROR); - } - return false; - } - - @Override - protected FullHttpMessage beginAggregation(HttpMessage start, ByteBuf content) throws Exception { - assert !(start instanceof FullHttpMessage); - - HttpUtil.setTransferEncodingChunked(start, false); - - AggregatedFullHttpMessage ret; - if (start instanceof HttpRequest) { - ret = new AggregatedFullHttpRequest((HttpRequest) start, content, null); - } else if (start instanceof HttpResponse) { - ret = new AggregatedFullHttpResponse((HttpResponse) start, content, null); - } else { - throw new Error(); - } - return ret; - } - - @Override - protected void aggregate(FullHttpMessage aggregated, HttpContent content) throws Exception { - if (content instanceof LastHttpContent) { - // Merge trailing headers into the message. - ((AggregatedFullHttpMessage) aggregated).setTrailingHeaders(((LastHttpContent) content).trailingHeaders()); - } - } - - @Override - protected void finishAggregation(FullHttpMessage aggregated) throws Exception { - // Set the 'Content-Length' header. If one isn't already set. - // This is important as HEAD responses will use a 'Content-Length' header which - // does not match the actual body, but the number of bytes that would be - // transmitted if a GET would have been used. - // - // See rfc2616 14.13 Content-Length - if (!HttpUtil.isContentLengthSet(aggregated)) { - aggregated.headers().set( - CONTENT_LENGTH, - String.valueOf(aggregated.content().readableBytes())); - } - } - - @Override - protected void handleOversizedMessage(final ChannelHandlerContext ctx, HttpMessage oversized) throws Exception { - if (oversized instanceof HttpRequest) { - // send back a 413 and close the connection - - // If the client started to send data already, close because it's impossible to recover. - // If keep-alive is off and 'Expect: 100-continue' is missing, no need to leave the connection open. - if (oversized instanceof FullHttpMessage || - !HttpUtil.is100ContinueExpected(oversized) && !HttpUtil.isKeepAlive(oversized)) { - Future future = ctx.writeAndFlush(TOO_LARGE_CLOSE.retainedDuplicate()); - future.addListener(future1 -> { - if (future1.isFailed()) { - logger.debug("Failed to send a 413 Request Entity Too Large.", future1.cause()); - } - ctx.close(); - }); - } else { - ctx.writeAndFlush(TOO_LARGE.retainedDuplicate()).addListener(future -> { - if (future.isFailed()) { - logger.debug("Failed to send a 413 Request Entity Too Large.", future.cause()); - ctx.close(); - } - }); - } - } else if (oversized instanceof HttpResponse) { - throw new ResponseTooLargeException("Response entity too large: " + oversized); - } else { - throw new IllegalStateException(); - } - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - super.exceptionCaught(ctx, cause); - if (cause instanceof ResponseTooLargeException) { - ctx.close(); - } - } - - private static final class ResponseTooLargeException extends TooLongFrameException { - ResponseTooLargeException(String message) { - super(message); - } - } - - private abstract static class AggregatedFullHttpMessage implements FullHttpMessage { - protected final HttpMessage message; - private final ByteBuf content; - private HttpHeaders trailingHeaders; - - AggregatedFullHttpMessage(HttpMessage message, ByteBuf content, HttpHeaders trailingHeaders) { - this.message = message; - this.content = content; - this.trailingHeaders = trailingHeaders; - } - - @Override - public HttpHeaders trailingHeaders() { - HttpHeaders trailingHeaders = this.trailingHeaders; - if (trailingHeaders == null) { - return EmptyHttpHeaders.INSTANCE; - } else { - return trailingHeaders; - } - } - - void setTrailingHeaders(HttpHeaders trailingHeaders) { - this.trailingHeaders = trailingHeaders; - } - - @Override - public HttpVersion getProtocolVersion() { - return message.protocolVersion(); - } - - @Override - public HttpVersion protocolVersion() { - return message.protocolVersion(); - } - - @Override - public FullHttpMessage setProtocolVersion(HttpVersion version) { - message.setProtocolVersion(version); - return this; - } - - @Override - public HttpHeaders headers() { - return message.headers(); - } - - @Override - public DecoderResult decoderResult() { - return message.decoderResult(); - } - - @Override - public DecoderResult getDecoderResult() { - return message.decoderResult(); - } - - @Override - public void setDecoderResult(DecoderResult result) { - message.setDecoderResult(result); - } - - @Override - public ByteBuf content() { - return content; - } - - @Override - public int refCnt() { - return content.refCnt(); - } - - @Override - public FullHttpMessage retain() { - content.retain(); - return this; - } - - @Override - public FullHttpMessage retain(int increment) { - content.retain(increment); - return this; - } - - @Override - public FullHttpMessage touch(Object hint) { - content.touch(hint); - return this; - } - - @Override - public FullHttpMessage touch() { - content.touch(); - return this; - } - - @Override - public boolean release() { - return content.release(); - } - - @Override - public boolean release(int decrement) { - return content.release(decrement); - } - - @Override - public abstract FullHttpMessage copy(); - - @Override - public abstract FullHttpMessage duplicate(); - - @Override - public abstract FullHttpMessage retainedDuplicate(); - } - - private static final class AggregatedFullHttpRequest extends AggregatedFullHttpMessage implements FullHttpRequest { - - AggregatedFullHttpRequest(HttpRequest request, ByteBuf content, HttpHeaders trailingHeaders) { - super(request, content, trailingHeaders); - } - - @Override - public FullHttpRequest copy() { - return replace(content().copy()); - } - - @Override - public FullHttpRequest duplicate() { - return replace(content().duplicate()); - } - - @Override - public FullHttpRequest retainedDuplicate() { - return replace(content().retainedDuplicate()); - } - - @Override - public FullHttpRequest replace(ByteBuf content) { - DefaultFullHttpRequest dup = new DefaultFullHttpRequest(protocolVersion(), method(), uri(), content, - headers().copy(), trailingHeaders().copy()); - dup.setDecoderResult(decoderResult()); - return dup; - } - - @Override - public FullHttpRequest retain(int increment) { - super.retain(increment); - return this; - } - - @Override - public FullHttpRequest retain() { - super.retain(); - return this; - } - - @Override - public FullHttpRequest touch() { - super.touch(); - return this; - } - - @Override - public FullHttpRequest touch(Object hint) { - super.touch(hint); - return this; - } - - @Override - public FullHttpRequest setMethod(HttpMethod method) { - ((HttpRequest) message).setMethod(method); - return this; - } - - @Override - public FullHttpRequest setUri(String uri) { - ((HttpRequest) message).setUri(uri); - return this; - } - - @Override - public HttpMethod getMethod() { - return ((HttpRequest) message).method(); - } - - @Override - public String getUri() { - return ((HttpRequest) message).uri(); - } - - @Override - public HttpMethod method() { - return getMethod(); - } - - @Override - public String uri() { - return getUri(); - } - - @Override - public FullHttpRequest setProtocolVersion(HttpVersion version) { - super.setProtocolVersion(version); - return this; - } - - @Override - public String toString() { - return HttpMessageUtil.appendFullRequest(new StringBuilder(256), this).toString(); - } - } - - private static final class AggregatedFullHttpResponse extends AggregatedFullHttpMessage - implements FullHttpResponse { - - AggregatedFullHttpResponse(HttpResponse message, ByteBuf content, HttpHeaders trailingHeaders) { - super(message, content, trailingHeaders); - } - - @Override - public FullHttpResponse copy() { - return replace(content().copy()); - } - - @Override - public FullHttpResponse duplicate() { - return replace(content().duplicate()); - } - - @Override - public FullHttpResponse retainedDuplicate() { - return replace(content().retainedDuplicate()); - } - - @Override - public FullHttpResponse replace(ByteBuf content) { - DefaultFullHttpResponse dup = new DefaultFullHttpResponse(getProtocolVersion(), getStatus(), content, - headers().copy(), trailingHeaders().copy()); - dup.setDecoderResult(decoderResult()); - return dup; - } - - @Override - public FullHttpResponse setStatus(HttpResponseStatus status) { - ((HttpResponse) message).setStatus(status); - return this; - } - - @Override - public HttpResponseStatus getStatus() { - return ((HttpResponse) message).status(); - } - - @Override - public HttpResponseStatus status() { - return getStatus(); - } - - @Override - public FullHttpResponse setProtocolVersion(HttpVersion version) { - super.setProtocolVersion(version); - return this; - } - - @Override - public FullHttpResponse retain(int increment) { - super.retain(increment); - return this; - } - - @Override - public FullHttpResponse retain() { - super.retain(); - return this; - } - - @Override - public FullHttpResponse touch(Object hint) { - super.touch(hint); - return this; - } - - @Override - public FullHttpResponse touch() { - super.touch(); - return this; - } - - @Override - public String toString() { - return HttpMessageUtil.appendFullResponse(new StringBuilder(256), this).toString(); - } - } -} 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 deleted file mode 100644 index a1b9401f93..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java +++ /dev/null @@ -1,975 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.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; -import io.netty.channel.ChannelPipeline; -import io.netty.handler.codec.ByteToMessageDecoder; -import io.netty.handler.codec.DecoderResult; -import io.netty.handler.codec.PrematureChannelClosureException; -import io.netty.handler.codec.TooLongFrameException; -import io.netty.util.ByteProcessor; -import io.netty.util.internal.AppendableCharSequence; - -import java.util.List; - -/** - * Decodes {@link ByteBuf}s into {@link HttpMessage}s and - * {@link HttpContent}s. - * - *

Parameters that prevents excessive memory consumption

- * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
NameDefault valueMeaning
{@code maxInitialLineLength}{@value #DEFAULT_MAX_INITIAL_LINE_LENGTH}The maximum length of the initial line - * (e.g. {@code "GET / HTTP/1.0"} or {@code "HTTP/1.0 200 OK"}) - * If the length of the initial line exceeds this value, a - * {@link TooLongFrameException} will be raised.
{@code maxHeaderSize}{@value #DEFAULT_MAX_HEADER_SIZE}The maximum length of all headers. If the sum of the length of each - * header exceeds this value, a {@link TooLongFrameException} will be raised.
- * - *

Parameters that control parsing behavior

- * - * - * - * - * - * - * - * - * - *
NameDefault valueMeaning
{@code allowDuplicateContentLengths}{@value #DEFAULT_ALLOW_DUPLICATE_CONTENT_LENGTHS}When set to {@code false}, will reject any messages that contain multiple Content-Length header fields. - * When set to {@code true}, will allow multiple Content-Length headers only if they are all the same decimal value. - * The duplicated field-values will be replaced with a single valid Content-Length field. - * See RFC 7230, Section 3.3.2.
- * - *

Chunked Content

- * - * If the content of an HTTP message is greater than {@code maxChunkSize} or - * the transfer encoding of the HTTP message is 'chunked', this decoder - * generates one {@link HttpMessage} instance and its following - * {@link HttpContent}s per single HTTP message to avoid excessive memory - * consumption. For example, the following HTTP message: - *
- * GET / HTTP/1.1
- * Transfer-Encoding: chunked
- *
- * 1a
- * abcdefghijklmnopqrstuvwxyz
- * 10
- * 1234567890abcdef
- * 0
- * Content-MD5: ...
- * [blank line]
- * 
- * triggers {@link HttpRequestDecoder} to generate 3 objects: - *
    - *
  1. An {@link HttpRequest},
  2. - *
  3. The first {@link HttpContent} whose content is {@code 'abcdefghijklmnopqrstuvwxyz'},
  4. - *
  5. The second {@link LastHttpContent} whose content is {@code '1234567890abcdef'}, which marks - * the end of the content.
  6. - *
- * - * If you prefer not to handle {@link HttpContent}s by yourself for your - * convenience, insert {@link HttpObjectAggregator} after this decoder in the - * {@link ChannelPipeline}. However, please note that your server might not - * be as memory efficient as without the aggregator. - * - *

Extensibility

- * - * Please note that this decoder is designed to be extended to implement - * a protocol derived from HTTP, such as - * RTSP and - * ICAP. - * To implement the decoder of such a derived protocol, extend this class and - * implement all abstract methods properly. - */ -public abstract class HttpObjectDecoder extends ByteToMessageDecoder { - public static final int DEFAULT_MAX_INITIAL_LINE_LENGTH = 4096; - public static final int DEFAULT_MAX_HEADER_SIZE = 8192; - public static final boolean DEFAULT_CHUNKED_SUPPORTED = true; - public static final boolean DEFAULT_VALIDATE_HEADERS = true; - public static final int DEFAULT_INITIAL_BUFFER_SIZE = 128; - public static final boolean DEFAULT_ALLOW_DUPLICATE_CONTENT_LENGTHS = false; - - private static final String EMPTY_VALUE = ""; - - private final boolean chunkedSupported; - protected final boolean validateHeaders; - private final boolean allowDuplicateContentLengths; - private final HeaderParser headerParser; - private final LineParser lineParser; - - private HttpMessage message; - private long chunkSize; - private long contentLength = Long.MIN_VALUE; - private volatile boolean resetRequested; - - // These will be updated by splitHeader(...) - private CharSequence name; - private CharSequence value; - - private LastHttpContent trailer; - - /** - * The internal state of {@link HttpObjectDecoder}. - * Internal use only. - */ - private enum State { - SKIP_CONTROL_CHARS, - READ_INITIAL, - READ_HEADER, - READ_VARIABLE_LENGTH_CONTENT, - READ_FIXED_LENGTH_CONTENT, - READ_CHUNK_SIZE, - READ_CHUNKED_CONTENT, - READ_CHUNK_DELIMITER, - READ_CHUNK_FOOTER, - BAD_MESSAGE, - UPGRADED - } - - private State currentState = State.SKIP_CONTROL_CHARS; - - /** - * Creates a new instance with the default - * {@code maxInitialLineLength (4096}}, {@code maxHeaderSize (8192)}, and - * {@code maxChunkSize (8192)}. - */ - protected HttpObjectDecoder() { - this(DEFAULT_MAX_INITIAL_LINE_LENGTH, DEFAULT_MAX_HEADER_SIZE, DEFAULT_CHUNKED_SUPPORTED); - } - - /** - * Creates a new instance with the specified parameters. - */ - protected HttpObjectDecoder( - int maxInitialLineLength, int maxHeaderSize, boolean chunkedSupported) { - this(maxInitialLineLength, maxHeaderSize, chunkedSupported, DEFAULT_VALIDATE_HEADERS); - } - - /** - * Creates a new instance with the specified parameters. - */ - protected HttpObjectDecoder( - int maxInitialLineLength, int maxHeaderSize, - boolean chunkedSupported, boolean validateHeaders) { - this(maxInitialLineLength, maxHeaderSize, chunkedSupported, validateHeaders, DEFAULT_INITIAL_BUFFER_SIZE); - } - - /** - * Creates a new instance with the specified parameters. - */ - protected HttpObjectDecoder( - int maxInitialLineLength, int maxHeaderSize, - boolean chunkedSupported, boolean validateHeaders, int initialBufferSize) { - this(maxInitialLineLength, maxHeaderSize, chunkedSupported, validateHeaders, initialBufferSize, - DEFAULT_ALLOW_DUPLICATE_CONTENT_LENGTHS); - } - - protected HttpObjectDecoder( - int maxInitialLineLength, int maxHeaderSize, - boolean chunkedSupported, boolean validateHeaders, int initialBufferSize, - boolean allowDuplicateContentLengths) { - checkPositive(maxInitialLineLength, "maxInitialLineLength"); - checkPositive(maxHeaderSize, "maxHeaderSize"); - AppendableCharSequence seq = new AppendableCharSequence(initialBufferSize); - lineParser = new LineParser(seq, maxInitialLineLength); - headerParser = new HeaderParser(seq, maxHeaderSize); - this.chunkedSupported = chunkedSupported; - this.validateHeaders = validateHeaders; - this.allowDuplicateContentLengths = allowDuplicateContentLengths; - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception { - if (resetRequested) { - resetNow(); - } - - switch (currentState) { - case SKIP_CONTROL_CHARS: - // Fall-through - case READ_INITIAL: try { - AppendableCharSequence line = lineParser.parse(buffer); - if (line == null) { - return; - } - String[] initialLine = splitInitialLine(line); - if (initialLine.length < 3) { - // Invalid initial line - ignore. - currentState = State.SKIP_CONTROL_CHARS; - return; - } - - message = createMessage(initialLine); - currentState = State.READ_HEADER; - // fall-through - } catch (Exception e) { - ctx.fireChannelRead(invalidMessage(buffer, e)); - return; - } - case READ_HEADER: try { - State nextState = readHeaders(buffer); - if (nextState == null) { - return; - } - currentState = nextState; - switch (nextState) { - case SKIP_CONTROL_CHARS: - // fast-path - // No content is expected. - ctx.fireChannelRead(message); - ctx.fireChannelRead(LastHttpContent.EMPTY_LAST_CONTENT); - resetNow(); - return; - case READ_CHUNK_SIZE: - if (!chunkedSupported) { - throw new IllegalArgumentException("Chunked messages not supported"); - } - // Chunked encoding - generate HttpMessage first. HttpChunks will follow. - ctx.fireChannelRead(message); - return; - default: - /* - RFC 7230, 3.3.3 (https://tools.ietf.org/html/rfc7230#section-3.3.3) states that if a - request does not have either a transfer-encoding or a content-length header then the message body - length is 0. However for a response the body length is the number of octets received prior to the - server closing the connection. So we treat this as variable length chunked encoding. - */ - long contentLength = contentLength(); - if (contentLength == 0 || contentLength == -1 && isDecodingRequest()) { - ctx.fireChannelRead(message); - ctx.fireChannelRead(LastHttpContent.EMPTY_LAST_CONTENT); - resetNow(); - return; - } - - assert nextState == State.READ_FIXED_LENGTH_CONTENT || - nextState == State.READ_VARIABLE_LENGTH_CONTENT; - - ctx.fireChannelRead(message); - - if (nextState == State.READ_FIXED_LENGTH_CONTENT) { - // chunkSize will be decreased as the READ_FIXED_LENGTH_CONTENT state reads data chunk by chunk. - chunkSize = contentLength; - } - - // We return here, this forces decode to be called again where we will decode the content - return; - } - } catch (Exception e) { - ctx.fireChannelRead(invalidMessage(buffer, e)); - return; - } - case READ_VARIABLE_LENGTH_CONTENT: { - // Keep reading data as a chunk until the end of connection is reached. - int toRead = buffer.readableBytes(); - if (toRead > 0) { - ByteBuf content = buffer.readRetainedSlice(toRead); - ctx.fireChannelRead(new DefaultHttpContent(content)); - } - return; - } - case READ_FIXED_LENGTH_CONTENT: { - int toRead = buffer.readableBytes(); - - // Check if the buffer is readable first as we use the readable byte count - // to create the HttpChunk. This is needed as otherwise we may end up with - // create an HttpChunk instance that contains an empty buffer and so is - // handled like it is the last HttpChunk. - // - // See https://github.com/netty/netty/issues/433 - if (toRead == 0) { - return; - } - - if (toRead > chunkSize) { - toRead = (int) chunkSize; - } - - ByteBuf content = buffer.readRetainedSlice(toRead); - chunkSize -= toRead; - - if (chunkSize == 0) { - // Read all content. - ctx.fireChannelRead(new DefaultLastHttpContent(content, validateHeaders)); - resetNow(); - } else { - ctx.fireChannelRead(new DefaultHttpContent(content)); - } - return; - } - /* - everything else after this point takes care of reading chunked content. basically, read chunk size, - read chunk, read and ignore the CRLF and repeat until 0 - */ - case READ_CHUNK_SIZE: try { - AppendableCharSequence line = lineParser.parse(buffer); - if (line == null) { - return; - } - int chunkSize = getChunkSize(line.toString()); - this.chunkSize = chunkSize; - if (chunkSize == 0) { - currentState = State.READ_CHUNK_FOOTER; - return; - } - currentState = State.READ_CHUNKED_CONTENT; - // fall-through - } catch (Exception e) { - ctx.fireChannelRead(invalidChunk(buffer, e)); - return; - } - case READ_CHUNKED_CONTENT: { - assert chunkSize <= Integer.MAX_VALUE; - int toRead = (int) chunkSize; - toRead = Math.min(toRead, buffer.readableBytes()); - if (toRead == 0) { - return; - } - HttpContent chunk = new DefaultHttpContent(buffer.readRetainedSlice(toRead)); - chunkSize -= toRead; - - ctx.fireChannelRead(chunk); - - if (chunkSize != 0) { - return; - } - currentState = State.READ_CHUNK_DELIMITER; - // fall-through - } - case READ_CHUNK_DELIMITER: { - final int wIdx = buffer.writerIndex(); - int rIdx = buffer.readerIndex(); - while (wIdx > rIdx) { - byte next = buffer.getByte(rIdx++); - if (next == HttpConstants.LF) { - currentState = State.READ_CHUNK_SIZE; - break; - } - } - buffer.readerIndex(rIdx); - return; - } - case READ_CHUNK_FOOTER: try { - LastHttpContent trailer = readTrailingHeaders(buffer); - if (trailer == null) { - return; - } - ctx.fireChannelRead(trailer); - resetNow(); - return; - } catch (Exception e) { - ctx.fireChannelRead(invalidChunk(buffer, e)); - return; - } - case BAD_MESSAGE: { - // Keep discarding until disconnection. - buffer.skipBytes(buffer.readableBytes()); - break; - } - case UPGRADED: { - int readableBytes = buffer.readableBytes(); - if (readableBytes > 0) { - // Keep on consuming as otherwise we may trigger an DecoderException, - // other handler will replace this codec with the upgraded protocol codec to - // take the traffic over at some point then. - // See https://github.com/netty/netty/issues/2173 - ctx.fireChannelRead(buffer.readBytes(readableBytes)); - } - break; - } - default: - break; - } - } - - @Override - protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - super.decodeLast(ctx, in); - - if (resetRequested) { - // If a reset was requested by decodeLast() we need to do it now otherwise we may produce a - // LastHttpContent while there was already one. - resetNow(); - } - // Handle the last unfinished message. - if (message != null) { - boolean chunked = HttpUtil.isTransferEncodingChunked(message); - if (currentState == State.READ_VARIABLE_LENGTH_CONTENT && !in.isReadable() && !chunked) { - // End of connection. - ctx.fireChannelRead(LastHttpContent.EMPTY_LAST_CONTENT); - resetNow(); - return; - } - - if (currentState == State.READ_HEADER) { - // If we are still in the state of reading headers we need to create a new invalid message that - // signals that the connection was closed before we received the headers. - ctx.fireChannelRead(invalidMessage(Unpooled.EMPTY_BUFFER, - new PrematureChannelClosureException("Connection closed before received headers"))); - resetNow(); - return; - } - - // Check if the closure of the connection signifies the end of the content. - boolean prematureClosure; - if (isDecodingRequest() || chunked) { - // The last request did not wait for a response. - prematureClosure = true; - } else { - // Compare the length of the received content and the 'Content-Length' header. - // If the 'Content-Length' header is absent, the length of the content is determined by the end of the - // connection, so it is perfectly fine. - prematureClosure = contentLength() > 0; - } - - if (!prematureClosure) { - ctx.fireChannelRead(LastHttpContent.EMPTY_LAST_CONTENT); - } - resetNow(); - } - } - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (evt instanceof HttpExpectationFailedEvent) { - switch (currentState) { - case READ_FIXED_LENGTH_CONTENT: - case READ_VARIABLE_LENGTH_CONTENT: - case READ_CHUNK_SIZE: - reset(); - break; - default: - break; - } - } - super.userEventTriggered(ctx, evt); - } - - protected boolean isContentAlwaysEmpty(HttpMessage msg) { - if (msg instanceof HttpResponse) { - HttpResponse res = (HttpResponse) msg; - int code = res.status().code(); - - // Correctly handle return codes of 1xx. - // - // See: - // - https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html Section 4.4 - // - https://github.com/netty/netty/issues/222 - if (code >= 100 && code < 200) { - // One exception: Hixie 76 websocket handshake response - return !(code == 101 && !res.headers().contains(HttpHeaderNames.SEC_WEBSOCKET_ACCEPT) - && res.headers().contains(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET, true)); - } - - switch (code) { - case 204: case 304: - return true; - default: - return false; - } - } - return false; - } - - /** - * Returns true if the server switched to a different protocol than HTTP/1.0 or HTTP/1.1, e.g. HTTP/2 or Websocket. - * Returns false if the upgrade happened in a different layer, e.g. upgrade from HTTP/1.1 to HTTP/1.1 over TLS. - */ - protected boolean isSwitchingToNonHttp1Protocol(HttpResponse msg) { - if (msg.status().code() != HttpResponseStatus.SWITCHING_PROTOCOLS.code()) { - return false; - } - String newProtocol = msg.headers().get(HttpHeaderNames.UPGRADE); - return newProtocol == null || - !newProtocol.contains(HttpVersion.HTTP_1_0.text()) && - !newProtocol.contains(HttpVersion.HTTP_1_1.text()); - } - - /** - * Resets the state of the decoder so that it is ready to decode a new message. - * This method is useful for handling a rejected request with {@code Expect: 100-continue} header. - */ - public void reset() { - resetRequested = true; - } - - private void resetNow() { - HttpMessage message = this.message; - this.message = null; - name = null; - value = null; - contentLength = Long.MIN_VALUE; - lineParser.reset(); - headerParser.reset(); - trailer = null; - if (!isDecodingRequest()) { - HttpResponse res = (HttpResponse) message; - if (res != null && isSwitchingToNonHttp1Protocol(res)) { - currentState = State.UPGRADED; - return; - } - } - - resetRequested = false; - currentState = State.SKIP_CONTROL_CHARS; - } - - private HttpMessage invalidMessage(ByteBuf in, Exception cause) { - currentState = State.BAD_MESSAGE; - - // Advance the readerIndex so that ByteToMessageDecoder does not complain - // when we produced an invalid message without consuming anything. - in.skipBytes(in.readableBytes()); - - if (message == null) { - message = createInvalidMessage(); - } - message.setDecoderResult(DecoderResult.failure(cause)); - - HttpMessage ret = message; - message = null; - return ret; - } - - private HttpContent invalidChunk(ByteBuf in, Exception cause) { - currentState = State.BAD_MESSAGE; - - // Advance the readerIndex so that ByteToMessageDecoder does not complain - // when we produced an invalid message without consuming anything. - in.skipBytes(in.readableBytes()); - - HttpContent chunk = new DefaultLastHttpContent(Unpooled.EMPTY_BUFFER); - chunk.setDecoderResult(DecoderResult.failure(cause)); - message = null; - trailer = null; - return chunk; - } - - private State readHeaders(ByteBuf buffer) { - final HttpMessage message = this.message; - final HttpHeaders headers = message.headers(); - - AppendableCharSequence line = headerParser.parse(buffer); - if (line == null) { - return null; - } - if (line.length() > 0) { - do { - 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 - String trimmedLine = line.toString().trim(); - String valueStr = String.valueOf(value); - value = valueStr + ' ' + trimmedLine; - } else { - if (name != null) { - headers.add(name, value); - } - splitHeader(line); - } - - line = headerParser.parse(buffer); - if (line == null) { - return null; - } - } while (line.length() > 0); - } - - // Add the last header. - if (name != null) { - headers.add(name, value); - } - - // reset name and value fields - name = null; - value = null; - - // Done parsing initial line and headers. Set decoder result. - HttpMessageDecoderResult decoderResult = new HttpMessageDecoderResult(lineParser.size, headerParser.size); - message.setDecoderResult(decoderResult); - - List contentLengthFields = headers.getAll(HttpHeaderNames.CONTENT_LENGTH); - if (!contentLengthFields.isEmpty()) { - HttpVersion version = message.protocolVersion(); - boolean isHttp10OrEarlier = version.majorVersion() < 1 || (version.majorVersion() == 1 - && version.minorVersion() == 0); - // Guard against multiple Content-Length headers as stated in - // https://tools.ietf.org/html/rfc7230#section-3.3.2: - contentLength = HttpUtil.normalizeAndGetContentLength(contentLengthFields, - isHttp10OrEarlier, allowDuplicateContentLengths); - if (contentLength != -1) { - headers.set(HttpHeaderNames.CONTENT_LENGTH, contentLength); - } - } - - if (isContentAlwaysEmpty(message)) { - HttpUtil.setTransferEncodingChunked(message, false); - return State.SKIP_CONTROL_CHARS; - } else if (HttpUtil.isTransferEncodingChunked(message)) { - if (!contentLengthFields.isEmpty() && message.protocolVersion() == HttpVersion.HTTP_1_1) { - handleTransferEncodingChunkedWithContentLength(message); - } - return State.READ_CHUNK_SIZE; - } else if (contentLength() >= 0) { - return State.READ_FIXED_LENGTH_CONTENT; - } else { - return State.READ_VARIABLE_LENGTH_CONTENT; - } - } - - /** - * Invoked when a message with both a "Transfer-Encoding: chunked" and a "Content-Length" header field is detected. - * The default behavior is to remove the Content-Length field, but this method could be overridden - * to change the behavior (to, e.g., throw an exception and produce an invalid message). - *

- * 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.
-     * 
- * Also see: - * https://github.com/apache/tomcat/blob/b693d7c1981fa7f51e58bc8c8e72e3fe80b7b773/ - * java/org/apache/coyote/http11/Http11Processor.java#L747-L755 - * https://github.com/nginx/nginx/blob/0ad4393e30c119d250415cb769e3d8bc8dce5186/ - * src/http/ngx_http_request.c#L1946-L1953 - */ - protected void handleTransferEncodingChunkedWithContentLength(HttpMessage message) { - message.headers().remove(HttpHeaderNames.CONTENT_LENGTH); - contentLength = Long.MIN_VALUE; - } - - private long contentLength() { - if (contentLength == Long.MIN_VALUE) { - contentLength = HttpUtil.getContentLength(message, -1L); - } - return contentLength; - } - - private LastHttpContent readTrailingHeaders(ByteBuf buffer) { - AppendableCharSequence line = headerParser.parse(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 (trailer == null) { - trailer = this.trailer = new DefaultLastHttpContent(Unpooled.EMPTY_BUFFER, validateHeaders); - } - while (line.length() > 0) { - char firstChar = line.charAtUnsafe(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; - } - } - - this.trailer = null; - return trailer; - } - - protected abstract boolean isDecodingRequest(); - protected abstract HttpMessage createMessage(String[] initialLine) throws Exception; - protected abstract HttpMessage createInvalidMessage(); - - private static int getChunkSize(String hex) { - hex = hex.trim(); - for (int i = 0; i < hex.length(); i ++) { - char c = hex.charAt(i); - if (c == ';' || Character.isWhitespace(c) || Character.isISOControl(c)) { - hex = hex.substring(0, i); - break; - } - } - - return Integer.parseInt(hex, 16); - } - - private static String[] splitInitialLine(AppendableCharSequence sb) { - int aStart; - int aEnd; - int bStart; - int bEnd; - int cStart; - int cEnd; - - aStart = findNonSPLenient(sb, 0); - aEnd = findSPLenient(sb, aStart); - - bStart = findNonSPLenient(sb, aEnd); - bEnd = findSPLenient(sb, bStart); - - cStart = findNonSPLenient(sb, bEnd); - cEnd = findEndOfString(sb); - - return new String[] { - sb.subStringUnsafe(aStart, aEnd), - sb.subStringUnsafe(bStart, bEnd), - cStart < cEnd? sb.subStringUnsafe(cStart, cEnd) : "" }; - } - - private void splitHeader(AppendableCharSequence sb) { - final int length = sb.length(); - int nameStart; - int nameEnd; - int colonEnd; - int valueStart; - int valueEnd; - - nameStart = findNonWhitespace(sb, 0, false); - for (nameEnd = nameStart; nameEnd < length; nameEnd ++) { - char ch = sb.charAtUnsafe(nameEnd); - // 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() && isOWS(ch))) { - break; - } - } - - 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 ++; - break; - } - } - - name = sb.subStringUnsafe(nameStart, nameEnd); - valueStart = findNonWhitespace(sb, colonEnd, true); - if (valueStart == length) { - value = EMPTY_VALUE; - } else { - valueEnd = findEndOfString(sb); - value = sb.subStringUnsafe(valueStart, valueEnd); - } - } - - private static int findNonSPLenient(AppendableCharSequence sb, int offset) { - for (int result = offset; result < sb.length(); ++result) { - char c = sb.charAtUnsafe(result); - // See https://tools.ietf.org/html/rfc7230#section-3.5 - if (isSPLenient(c)) { - continue; - } - if (Character.isWhitespace(c)) { - // Any other whitespace delimiter is invalid - throw new IllegalArgumentException("Invalid separator"); - } - return result; - } - return sb.length(); - } - - private static int findSPLenient(AppendableCharSequence sb, int offset) { - for (int result = offset; result < sb.length(); ++result) { - if (isSPLenient(sb.charAtUnsafe(result))) { - return result; - } - } - return sb.length(); - } - - private static boolean isSPLenient(char c) { - // See https://tools.ietf.org/html/rfc7230#section-3.5 - return c == ' ' || c == (char) 0x09 || c == (char) 0x0B || c == (char) 0x0C || c == (char) 0x0D; - } - - private static int findNonWhitespace(AppendableCharSequence sb, int offset, boolean validateOWS) { - for (int result = offset; result < sb.length(); ++result) { - char c = sb.charAtUnsafe(result); - if (!Character.isWhitespace(c)) { - return result; - } else if (validateOWS && !isOWS(c)) { - // Only OWS is supported for whitespace - throw new IllegalArgumentException("Invalid separator, only a single space or horizontal tab allowed," + - " but received a '" + c + "' (0x" + Integer.toHexString(c) + ")"); - } - } - return sb.length(); - } - - private static int findEndOfString(AppendableCharSequence sb) { - for (int result = sb.length() - 1; result > 0; --result) { - if (!Character.isWhitespace(sb.charAtUnsafe(result))) { - return result + 1; - } - } - return 0; - } - - private static boolean isOWS(char ch) { - return ch == ' ' || ch == (char) 0x09; - } - - private static class HeaderParser implements ByteProcessor { - private final AppendableCharSequence seq; - private final int maxLength; - int size; - - HeaderParser(AppendableCharSequence seq, int maxLength) { - this.seq = seq; - this.maxLength = maxLength; - } - - public AppendableCharSequence parse(ByteBuf buffer) { - final int oldSize = size; - seq.reset(); - int i = buffer.forEachByte(this); - if (i == -1) { - size = oldSize; - return null; - } - buffer.readerIndex(i + 1); - return seq; - } - - public void reset() { - size = 0; - } - - @Override - public boolean process(byte value) { - char nextByte = (char) (value & 0xFF); - if (nextByte == HttpConstants.LF) { - int len = seq.length(); - // Drop CR if we had a CRLF pair - if (len >= 1 && seq.charAtUnsafe(len - 1) == HttpConstants.CR) { - -- size; - seq.setLength(len - 1); - } - return false; - } - - increaseCount(); - - seq.append(nextByte); - return true; - } - - protected final void increaseCount() { - if (++ size > maxLength) { - // TODO: Respond with Bad Request and discard the traffic - // or close the connection. - // No need to notify the upstream handlers - just log. - // If decoding a response, just throw an exception. - throw newException(maxLength); - } - } - - protected TooLongFrameException newException(int maxLength) { - return new TooLongFrameException("HTTP header is larger than " + maxLength + " bytes."); - } - } - - private final class LineParser extends HeaderParser { - - LineParser(AppendableCharSequence seq, int maxLength) { - super(seq, maxLength); - } - - @Override - public AppendableCharSequence parse(ByteBuf buffer) { - // Suppress a warning because HeaderParser.reset() is supposed to be called - reset(); // lgtm[java/subtle-inherited-call] - return super.parse(buffer); - } - - @Override - public boolean process(byte value) { - if (currentState == State.SKIP_CONTROL_CHARS) { - char c = (char) (value & 0xFF); - if (Character.isISOControl(c) || Character.isWhitespace(c)) { - increaseCount(); - return true; - } - currentState = State.READ_INITIAL; - } - return super.process(value); - } - - @Override - protected TooLongFrameException newException(int maxLength) { - return new TooLongFrameException("An HTTP line is larger than " + maxLength + " bytes."); - } - } -} 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 deleted file mode 100755 index 5b7ba82fb3..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectEncoder.java +++ /dev/null @@ -1,293 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.buffer.ByteBufConvertible; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.FileRegion; -import io.netty.handler.codec.MessageToMessageEncoder; -import io.netty.util.CharsetUtil; -import io.netty.util.internal.StringUtil; - -import java.util.Iterator; -import java.util.List; -import java.util.Map.Entry; - -import static io.netty.buffer.Unpooled.directBuffer; -import static io.netty.buffer.Unpooled.unreleasableBuffer; -import static io.netty.handler.codec.http.HttpConstants.CR; -import static io.netty.handler.codec.http.HttpConstants.LF; - -/** - * Encodes an {@link HttpMessage} or an {@link HttpContent} into - * a {@link ByteBuf}. - * - *

Extensibility

- * - * Please note that this encoder is designed to be extended to implement - * a protocol derived from HTTP, such as - * RTSP and - * ICAP. - * To implement the encoder of such a derived protocol, extend this class and - * implement all abstract methods properly. - */ -public abstract class HttpObjectEncoder extends MessageToMessageEncoder { - static final int CRLF_SHORT = (CR << 8) | LF; - private static final int ZERO_CRLF_MEDIUM = ('0' << 16) | CRLF_SHORT; - private static final byte[] ZERO_CRLF_CRLF = { '0', CR, LF, CR, LF }; - private static final ByteBuf CRLF_BUF = unreleasableBuffer(directBuffer(2).writeByte(CR).writeByte(LF)); - private static final ByteBuf ZERO_CRLF_CRLF_BUF = unreleasableBuffer(directBuffer(ZERO_CRLF_CRLF.length) - .writeBytes(ZERO_CRLF_CRLF)); - private static final float HEADERS_WEIGHT_NEW = 1 / 5f; - private static final float HEADERS_WEIGHT_HISTORICAL = 1 - HEADERS_WEIGHT_NEW; - private static final float TRAILERS_WEIGHT_NEW = HEADERS_WEIGHT_NEW; - private static final float TRAILERS_WEIGHT_HISTORICAL = HEADERS_WEIGHT_HISTORICAL; - - private static final int ST_INIT = 0; - private static final int ST_CONTENT_NON_CHUNK = 1; - private static final int ST_CONTENT_CHUNK = 2; - private static final int ST_CONTENT_ALWAYS_EMPTY = 3; - - @SuppressWarnings("RedundantFieldInitialization") - private int state = ST_INIT; - - /** - * Used to calculate an exponential moving average of the encoded size of the initial line and the headers for - * a guess for future buffer allocations. - */ - private float headersEncodedSizeAccumulator = 256; - - /** - * Used to calculate an exponential moving average of the encoded size of the trailers for - * a guess for future buffer allocations. - */ - private float trailersEncodedSizeAccumulator = 256; - - @Override - protected void encode(ChannelHandlerContext ctx, Object msg, List out) throws Exception { - ByteBuf buf = null; - if (msg instanceof HttpMessage) { - if (state != ST_INIT) { - throw new IllegalStateException("unexpected message type: " + StringUtil.simpleClassName(msg) - + ", state: " + state); - } - - @SuppressWarnings({ "unchecked", "CastConflictsWithInstanceof" }) - H m = (H) msg; - - buf = ctx.alloc().buffer((int) headersEncodedSizeAccumulator); - // Encode the message. - encodeInitialLine(buf, m); - state = isContentAlwaysEmpty(m) ? ST_CONTENT_ALWAYS_EMPTY : - HttpUtil.isTransferEncodingChunked(m) ? ST_CONTENT_CHUNK : ST_CONTENT_NON_CHUNK; - - sanitizeHeadersBeforeEncode(m, state == ST_CONTENT_ALWAYS_EMPTY); - - encodeHeaders(m.headers(), buf); - ByteBufUtil.writeShortBE(buf, CRLF_SHORT); - - headersEncodedSizeAccumulator = HEADERS_WEIGHT_NEW * padSizeForAccumulation(buf.readableBytes()) + - HEADERS_WEIGHT_HISTORICAL * headersEncodedSizeAccumulator; - } - - // Bypass the encoder in case of an empty buffer, so that the following idiom works: - // - // ch.write(Unpooled.EMPTY_BUFFER).addListener(ch, ChannelFutureListeners.CLOSE); - // - // See https://github.com/netty/netty/issues/2983 for more information. - if (msg instanceof ByteBufConvertible) { - final ByteBuf potentialEmptyBuf = ((ByteBufConvertible) msg).asByteBuf(); - if (!potentialEmptyBuf.isReadable()) { - out.add(potentialEmptyBuf.retain()); - return; - } - } - - if (msg instanceof HttpContent || msg instanceof ByteBufConvertible || msg instanceof FileRegion) { - switch (state) { - case ST_INIT: - throw new IllegalStateException("unexpected message type: " + StringUtil.simpleClassName(msg) - + ", state: " + state); - case ST_CONTENT_NON_CHUNK: - final long contentLength = contentLength(msg); - if (contentLength > 0) { - if (buf != null && buf.writableBytes() >= contentLength && msg instanceof HttpContent) { - // merge into other buffer for performance reasons - buf.writeBytes(((HttpContent) msg).content()); - out.add(buf); - } else { - if (buf != null) { - out.add(buf); - } - out.add(encodeAndRetain(msg)); - } - - if (msg instanceof LastHttpContent) { - state = ST_INIT; - } - - break; - } - - // fall-through! - case ST_CONTENT_ALWAYS_EMPTY: - - if (buf != null) { - // We allocated a buffer so add it now. - out.add(buf); - } else { - // Need to produce some output otherwise an - // IllegalStateException will be thrown as we did not write anything - // Its ok to just write an EMPTY_BUFFER as if there are reference count issues these will be - // propagated as the caller of the encode(...) method will release the original - // buffer. - // Writing an empty buffer will not actually write anything on the wire, so if there is a user - // error with msg it will not be visible externally - out.add(Unpooled.EMPTY_BUFFER); - } - - break; - case ST_CONTENT_CHUNK: - if (buf != null) { - // We allocated a buffer so add it now. - out.add(buf); - } - encodeChunkedContent(ctx, msg, contentLength(msg), out); - - break; - default: - throw new Error(); - } - - if (msg instanceof LastHttpContent) { - state = ST_INIT; - } - } else if (buf != null) { - out.add(buf); - } - } - - /** - * Encode the {@link HttpHeaders} into a {@link ByteBuf}. - */ - protected void encodeHeaders(HttpHeaders headers, ByteBuf buf) { - Iterator> iter = headers.iteratorCharSequence(); - while (iter.hasNext()) { - Entry header = iter.next(); - HttpHeadersEncoder.encoderHeader(header.getKey(), header.getValue(), buf); - } - } - - private void encodeChunkedContent(ChannelHandlerContext ctx, Object msg, long contentLength, List out) { - if (contentLength > 0) { - String lengthHex = Long.toHexString(contentLength); - ByteBuf buf = ctx.alloc().buffer(lengthHex.length() + 2); - buf.writeCharSequence(lengthHex, CharsetUtil.US_ASCII); - ByteBufUtil.writeShortBE(buf, CRLF_SHORT); - out.add(buf); - out.add(encodeAndRetain(msg)); - out.add(CRLF_BUF.duplicate()); - } - - if (msg instanceof LastHttpContent) { - HttpHeaders headers = ((LastHttpContent) msg).trailingHeaders(); - if (headers.isEmpty()) { - out.add(ZERO_CRLF_CRLF_BUF.duplicate()); - } else { - ByteBuf buf = ctx.alloc().buffer((int) trailersEncodedSizeAccumulator); - ByteBufUtil.writeMediumBE(buf, ZERO_CRLF_MEDIUM); - encodeHeaders(headers, buf); - ByteBufUtil.writeShortBE(buf, CRLF_SHORT); - trailersEncodedSizeAccumulator = TRAILERS_WEIGHT_NEW * padSizeForAccumulation(buf.readableBytes()) + - TRAILERS_WEIGHT_HISTORICAL * trailersEncodedSizeAccumulator; - out.add(buf); - } - } else if (contentLength == 0) { - // Need to produce some output otherwise an - // IllegalStateException will be thrown - out.add(encodeAndRetain(msg)); - } - } - - /** - * Allows to sanitize headers of the message before encoding these. - */ - protected void sanitizeHeadersBeforeEncode(@SuppressWarnings("unused") H msg, boolean isAlwaysEmpty) { - // noop - } - - /** - * Determine whether a message has a content or not. Some message may have headers indicating - * a content without having an actual content, e.g the response to an HEAD or CONNECT request. - * - * @param msg the message to test - * @return {@code true} to signal the message has no content - */ - protected boolean isContentAlwaysEmpty(@SuppressWarnings("unused") H msg) { - return false; - } - - @Override - public boolean acceptOutboundMessage(Object msg) throws Exception { - return msg instanceof HttpObject || msg instanceof ByteBufConvertible || msg instanceof FileRegion; - } - - private static Object encodeAndRetain(Object msg) { - if (msg instanceof ByteBufConvertible) { - return ((ByteBufConvertible) msg).asByteBuf().retain(); - } - if (msg instanceof HttpContent) { - return ((HttpContent) msg).content().retain(); - } - if (msg instanceof FileRegion) { - return ((FileRegion) msg).retain(); - } - throw new IllegalStateException("unexpected message type: " + StringUtil.simpleClassName(msg)); - } - - private static long contentLength(Object msg) { - if (msg instanceof HttpContent) { - return ((HttpContent) msg).content().readableBytes(); - } - if (msg instanceof ByteBufConvertible) { - return ((ByteBufConvertible) msg).asByteBuf().readableBytes(); - } - if (msg instanceof FileRegion) { - return ((FileRegion) msg).count(); - } - throw new IllegalStateException("unexpected message type: " + StringUtil.simpleClassName(msg)); - } - - /** - * Add some additional overhead to the buffer. The rational is that it is better to slightly over allocate and waste - * some memory, rather than under allocate and require a resize/copy. - * @param readableBytes The readable bytes in the buffer. - * @return The {@code readableBytes} with some additional padding. - */ - private static int padSizeForAccumulation(int readableBytes) { - return (readableBytes << 2) / 3; - } - - @Deprecated - protected static void encodeAscii(String s, ByteBuf buf) { - buf.writeCharSequence(s, CharsetUtil.US_ASCII); - } - - protected abstract void encodeInitialLine(ByteBuf buf, H message) throws Exception; -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpRequest.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpRequest.java deleted file mode 100644 index 5484b4cd88..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpRequest.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -/** - * An HTTP request. - * - *

Accessing Query Parameters and Cookie

- *

- * Unlike the Servlet API, a query string is constructed and decomposed by - * {@link QueryStringEncoder} and {@link QueryStringDecoder}. - * - * {@link io.netty.handler.codec.http.cookie.Cookie} support is also provided - * separately via {@link io.netty.handler.codec.http.cookie.ServerCookieDecoder}, - * {@link io.netty.handler.codec.http.cookie.ClientCookieDecoder}, - * {@link io.netty.handler.codec.http.cookie.ServerCookieEncoder}, - * and {@link io.netty.handler.codec.http.cookie.ClientCookieEncoder}. - * - * @see HttpResponse - * @see io.netty.handler.codec.http.cookie.ServerCookieDecoder - * @see io.netty.handler.codec.http.cookie.ClientCookieDecoder - * @see io.netty.handler.codec.http.cookie.ServerCookieEncoder - * @see io.netty.handler.codec.http.cookie.ClientCookieEncoder - */ -public interface HttpRequest extends HttpMessage { - - /** - * @deprecated Use {@link #method()} instead. - */ - @Deprecated - HttpMethod getMethod(); - - /** - * Returns the {@link HttpMethod} of this {@link HttpRequest}. - * - * @return The {@link HttpMethod} of this {@link HttpRequest} - */ - HttpMethod method(); - - /** - * Set the {@link HttpMethod} of this {@link HttpRequest}. - */ - HttpRequest setMethod(HttpMethod method); - - /** - * @deprecated Use {@link #uri()} instead. - */ - @Deprecated - String getUri(); - - /** - * Returns the requested URI (or alternatively, path) - * - * @return The URI being requested - */ - String uri(); - - /** - * Set the requested URI (or alternatively, path) - */ - HttpRequest setUri(String uri); - - @Override - HttpRequest setProtocolVersion(HttpVersion version); -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpRequestDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpRequestDecoder.java deleted file mode 100644 index e2c9a9e1f7..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpRequestDecoder.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelPipeline; -import io.netty.handler.codec.TooLongFrameException; - - -/** - * Decodes {@link ByteBuf}s into {@link HttpRequest}s and {@link HttpContent}s. - * - *

Parameters that prevents excessive memory consumption

- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
NameMeaning
{@code maxInitialLineLength}The maximum length of the initial line (e.g. {@code "GET / HTTP/1.0"}) - * If the length of the initial line exceeds this value, a - * {@link TooLongFrameException} will be raised.
{@code maxHeaderSize}The maximum length of all headers. If the sum of the length of each - * header exceeds this value, a {@link TooLongFrameException} will be raised.
{@code maxChunkSize}The maximum length of the content or each chunk. If the content length - * exceeds this value, the transfer encoding of the decoded request will be - * converted to 'chunked' and the content will be split into multiple - * {@link HttpContent}s. If the transfer encoding of the HTTP request is - * 'chunked' already, each chunk will be split into smaller chunks if the - * length of the chunk exceeds this value. If you prefer not to handle - * {@link HttpContent}s in your handler, insert {@link HttpObjectAggregator} - * after this decoder in the {@link ChannelPipeline}.
- */ -public class HttpRequestDecoder extends HttpObjectDecoder { - - /** - * Creates a new instance with the default - * {@code maxInitialLineLength (4096)}, {@code maxHeaderSize (8192)}, and - * {@code maxChunkSize (8192)}. - */ - public HttpRequestDecoder() { - } - - /** - * Creates a new instance with the specified parameters. - */ - public HttpRequestDecoder( - int maxInitialLineLength, int maxHeaderSize) { - super(maxInitialLineLength, maxHeaderSize, DEFAULT_CHUNKED_SUPPORTED); - } - - public HttpRequestDecoder( - int maxInitialLineLength, int maxHeaderSize, boolean validateHeaders) { - super(maxInitialLineLength, maxHeaderSize, DEFAULT_CHUNKED_SUPPORTED, validateHeaders); - } - - public HttpRequestDecoder( - int maxInitialLineLength, int maxHeaderSize, boolean validateHeaders, - int initialBufferSize) { - super(maxInitialLineLength, maxHeaderSize, DEFAULT_CHUNKED_SUPPORTED, validateHeaders, initialBufferSize); - } - - public HttpRequestDecoder( - int maxInitialLineLength, int maxHeaderSize, boolean validateHeaders, - int initialBufferSize, boolean allowDuplicateContentLengths) { - super(maxInitialLineLength, maxHeaderSize, DEFAULT_CHUNKED_SUPPORTED, validateHeaders, - initialBufferSize, allowDuplicateContentLengths); - } - - @Override - protected HttpMessage createMessage(String[] initialLine) throws Exception { - return new DefaultHttpRequest( - HttpVersion.valueOf(initialLine[2]), - HttpMethod.valueOf(initialLine[0]), initialLine[1], validateHeaders); - } - - @Override - protected HttpMessage createInvalidMessage() { - return new DefaultFullHttpRequest(HttpVersion.HTTP_1_0, HttpMethod.GET, "/bad-request", validateHeaders); - } - - @Override - protected boolean isDecodingRequest() { - return true; - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpRequestEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpRequestEncoder.java deleted file mode 100644 index a741e0323c..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpRequestEncoder.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.util.CharsetUtil; - -import static io.netty.handler.codec.http.HttpConstants.SP; - -/** - * Encodes an {@link HttpRequest} or an {@link HttpContent} into - * a {@link ByteBuf}. - */ -public class HttpRequestEncoder extends HttpObjectEncoder { - private static final char SLASH = '/'; - private static final char QUESTION_MARK = '?'; - private static final int SLASH_AND_SPACE_SHORT = (SLASH << 8) | SP; - private static final int SPACE_SLASH_AND_SPACE_MEDIUM = (SP << 16) | SLASH_AND_SPACE_SHORT; - - @Override - public boolean acceptOutboundMessage(Object msg) throws Exception { - return super.acceptOutboundMessage(msg) && !(msg instanceof HttpResponse); - } - - @Override - protected void encodeInitialLine(ByteBuf buf, HttpRequest request) throws Exception { - ByteBufUtil.copy(request.method().asciiName(), buf); - - String uri = request.uri(); - - if (uri.isEmpty()) { - // Add " / " as absolute path if uri is not present. - // See https://tools.ietf.org/html/rfc2616#section-5.1.2 - ByteBufUtil.writeMediumBE(buf, SPACE_SLASH_AND_SPACE_MEDIUM); - } else { - CharSequence uriCharSequence = uri; - boolean needSlash = false; - int start = uri.indexOf("://"); - if (start != -1 && uri.charAt(0) != SLASH) { - start += 3; - // Correctly handle query params. - // See https://github.com/netty/netty/issues/2732 - int index = uri.indexOf(QUESTION_MARK, start); - if (index == -1) { - if (uri.lastIndexOf(SLASH) < start) { - needSlash = true; - } - } else { - if (uri.lastIndexOf(SLASH, index) < start) { - uriCharSequence = new StringBuilder(uri).insert(index, SLASH); - } - } - } - buf.writeByte(SP).writeCharSequence(uriCharSequence, CharsetUtil.UTF_8); - if (needSlash) { - // write "/ " after uri - ByteBufUtil.writeShortBE(buf, SLASH_AND_SPACE_SHORT); - } else { - buf.writeByte(SP); - } - } - - request.protocolVersion().encode(buf); - ByteBufUtil.writeShortBE(buf, CRLF_SHORT); - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponse.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponse.java deleted file mode 100644 index b0ba345293..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponse.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -/** - * An HTTP response. - * - *

Accessing Cookies

- *

- * Unlike the Servlet API, {@link io.netty.handler.codec.http.cookie.Cookie} support is provided - * separately via {@link io.netty.handler.codec.http.cookie.ServerCookieDecoder}, - * {@link io.netty.handler.codec.http.cookie.ClientCookieDecoder}, - * {@link io.netty.handler.codec.http.cookie.ServerCookieEncoder}, - * and {@link io.netty.handler.codec.http.cookie.ClientCookieEncoder}. - * - * @see HttpRequest - * @see io.netty.handler.codec.http.cookie.ServerCookieDecoder - * @see io.netty.handler.codec.http.cookie.ClientCookieDecoder - * @see io.netty.handler.codec.http.cookie.ServerCookieEncoder - * @see io.netty.handler.codec.http.cookie.ClientCookieEncoder - */ -public interface HttpResponse extends HttpMessage { - - /** - * @deprecated Use {@link #status()} instead. - */ - @Deprecated - HttpResponseStatus getStatus(); - - /** - * Returns the status of this {@link HttpResponse}. - * - * @return The {@link HttpResponseStatus} of this {@link HttpResponse} - */ - HttpResponseStatus status(); - - /** - * Set the status of this {@link HttpResponse}. - */ - HttpResponse setStatus(HttpResponseStatus status); - - @Override - HttpResponse setProtocolVersion(HttpVersion version); -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponseDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponseDecoder.java deleted file mode 100644 index 40d5f65785..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponseDecoder.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelPipeline; -import io.netty.handler.codec.TooLongFrameException; - - -/** - * Decodes {@link ByteBuf}s into {@link HttpResponse}s and - * {@link HttpContent}s. - * - *

Parameters that prevents excessive memory consumption

- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
NameMeaning
{@code maxInitialLineLength}The maximum length of the initial line (e.g. {@code "HTTP/1.0 200 OK"}) - * If the length of the initial line exceeds this value, a - * {@link TooLongFrameException} will be raised.
{@code maxHeaderSize}The maximum length of all headers. If the sum of the length of each - * header exceeds this value, a {@link TooLongFrameException} will be raised.
{@code maxChunkSize}The maximum length of the content or each chunk. If the content length - * exceeds this value, the transfer encoding of the decoded response will be - * converted to 'chunked' and the content will be split into multiple - * {@link HttpContent}s. If the transfer encoding of the HTTP response is - * 'chunked' already, each chunk will be split into smaller chunks if the - * length of the chunk exceeds this value. If you prefer not to handle - * {@link HttpContent}s in your handler, insert {@link HttpObjectAggregator} - * after this decoder in the {@link ChannelPipeline}.
- * - *

Decoding a response for a HEAD request

- *

- * Unlike other HTTP requests, the successful response of a HEAD - * request does not have any content even if there is Content-Length - * header. Because {@link HttpResponseDecoder} is not able to determine if the - * response currently being decoded is associated with a HEAD request, - * you must override {@link #isContentAlwaysEmpty(HttpMessage)} to return - * true for the response of the HEAD request. - *

- * If you are writing an HTTP client that issues a HEAD request, - * please use {@link HttpClientCodec} instead of this decoder. It will perform - * additional state management to handle the responses for HEAD - * requests correctly. - *

- * - *

Decoding a response for a CONNECT request

- *

- * You also need to do additional state management to handle the response of a - * CONNECT request properly, like you did for HEAD. One - * difference is that the decoder should stop decoding completely after decoding - * the successful 200 response since the connection is not an HTTP connection - * anymore. - *

- * {@link HttpClientCodec} also handles this edge case correctly, so you have to - * use {@link HttpClientCodec} if you are writing an HTTP client that issues a - * CONNECT request. - *

- */ -public class HttpResponseDecoder extends HttpObjectDecoder { - - private static final HttpResponseStatus UNKNOWN_STATUS = new HttpResponseStatus(999, "Unknown"); - - /** - * Creates a new instance with the default - * {@code maxInitialLineLength (4096)}, {@code maxHeaderSize (8192)}, and - * {@code maxChunkSize (8192)}. - */ - public HttpResponseDecoder() { - } - - /** - * Creates a new instance with the specified parameters. - */ - public HttpResponseDecoder( - int maxInitialLineLength, int maxHeaderSize) { - super(maxInitialLineLength, maxHeaderSize, DEFAULT_CHUNKED_SUPPORTED); - } - - public HttpResponseDecoder( - int maxInitialLineLength, int maxHeaderSize, boolean validateHeaders) { - super(maxInitialLineLength, maxHeaderSize, DEFAULT_CHUNKED_SUPPORTED, validateHeaders); - } - - public HttpResponseDecoder( - int maxInitialLineLength, int maxHeaderSize, boolean validateHeaders, - int initialBufferSize) { - super(maxInitialLineLength, maxHeaderSize, DEFAULT_CHUNKED_SUPPORTED, validateHeaders, initialBufferSize); - } - - public HttpResponseDecoder( - int maxInitialLineLength, int maxHeaderSize, boolean validateHeaders, - int initialBufferSize, boolean allowDuplicateContentLengths) { - super(maxInitialLineLength, maxHeaderSize, DEFAULT_CHUNKED_SUPPORTED, validateHeaders, - initialBufferSize, allowDuplicateContentLengths); - } - - @Override - protected HttpMessage createMessage(String[] initialLine) { - return new DefaultHttpResponse( - HttpVersion.valueOf(initialLine[0]), - HttpResponseStatus.valueOf(Integer.parseInt(initialLine[1]), initialLine[2]), validateHeaders); - } - - @Override - protected HttpMessage createInvalidMessage() { - return new DefaultFullHttpResponse(HttpVersion.HTTP_1_0, UNKNOWN_STATUS, validateHeaders); - } - - @Override - protected boolean isDecodingRequest() { - return false; - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponseEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponseEncoder.java deleted file mode 100644 index 1bbddfb91d..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponseEncoder.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; - -import static io.netty.handler.codec.http.HttpConstants.*; - -/** - * Encodes an {@link HttpResponse} or an {@link HttpContent} into - * a {@link ByteBuf}. - */ -public class HttpResponseEncoder extends HttpObjectEncoder { - - @Override - public boolean acceptOutboundMessage(Object msg) throws Exception { - return super.acceptOutboundMessage(msg) && !(msg instanceof HttpRequest); - } - - @Override - protected void encodeInitialLine(ByteBuf buf, HttpResponse response) throws Exception { - response.protocolVersion().encode(buf); - buf.writeByte(SP); - response.status().encode(buf); - ByteBufUtil.writeShortBE(buf, CRLF_SHORT); - } - - @Override - protected void sanitizeHeadersBeforeEncode(HttpResponse msg, boolean isAlwaysEmpty) { - if (isAlwaysEmpty) { - HttpResponseStatus status = msg.status(); - if (status.codeClass() == HttpStatusClass.INFORMATIONAL || - status.code() == HttpResponseStatus.NO_CONTENT.code()) { - - // Stripping Content-Length: - // See https://tools.ietf.org/html/rfc7230#section-3.3.2 - msg.headers().remove(HttpHeaderNames.CONTENT_LENGTH); - - // Stripping Transfer-Encoding: - // See https://tools.ietf.org/html/rfc7230#section-3.3.1 - msg.headers().remove(HttpHeaderNames.TRANSFER_ENCODING); - } else if (status.code() == HttpResponseStatus.RESET_CONTENT.code()) { - - // Stripping Transfer-Encoding: - msg.headers().remove(HttpHeaderNames.TRANSFER_ENCODING); - - // Set Content-Length: 0 - // https://httpstatuses.com/205 - msg.headers().setInt(HttpHeaderNames.CONTENT_LENGTH, 0); - } - } - } - - @Override - protected boolean isContentAlwaysEmpty(HttpResponse msg) { - // Correctly handle special cases as stated in: - // https://tools.ietf.org/html/rfc7230#section-3.3.3 - HttpResponseStatus status = msg.status(); - - if (status.codeClass() == HttpStatusClass.INFORMATIONAL) { - - if (status.code() == HttpResponseStatus.SWITCHING_PROTOCOLS.code()) { - // We need special handling for WebSockets version 00 as it will include an body. - // Fortunally this version should not really be used in the wild very often. - // See https://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00#section-1.2 - return msg.headers().contains(HttpHeaderNames.SEC_WEBSOCKET_VERSION); - } - return true; - } - return status.code() == HttpResponseStatus.NO_CONTENT.code() || - status.code() == HttpResponseStatus.NOT_MODIFIED.code() || - status.code() == HttpResponseStatus.RESET_CONTENT.code(); - } -} 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 deleted file mode 100644 index bb7641eac8..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponseStatus.java +++ /dev/null @@ -1,646 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.util.AsciiString; -import io.netty.util.CharsetUtil; - -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; -import static java.util.Objects.requireNonNull; - -/** - * The response code and its description of HTTP or its derived protocols, such as - * RTSP and - * ICAP. - */ -public class HttpResponseStatus implements Comparable { - - /** - * 100 Continue - */ - public static final HttpResponseStatus CONTINUE = newStatus(100, "Continue"); - - /** - * 101 Switching Protocols - */ - public static final HttpResponseStatus SWITCHING_PROTOCOLS = newStatus(101, "Switching Protocols"); - - /** - * 102 Processing (WebDAV, RFC2518) - */ - public static final HttpResponseStatus PROCESSING = newStatus(102, "Processing"); - - /** - * 200 OK - */ - public static final HttpResponseStatus OK = newStatus(200, "OK"); - - /** - * 201 Created - */ - public static final HttpResponseStatus CREATED = newStatus(201, "Created"); - - /** - * 202 Accepted - */ - public static final HttpResponseStatus ACCEPTED = newStatus(202, "Accepted"); - - /** - * 203 Non-Authoritative Information (since HTTP/1.1) - */ - public static final HttpResponseStatus NON_AUTHORITATIVE_INFORMATION = - newStatus(203, "Non-Authoritative Information"); - - /** - * 204 No Content - */ - public static final HttpResponseStatus NO_CONTENT = newStatus(204, "No Content"); - - /** - * 205 Reset Content - */ - public static final HttpResponseStatus RESET_CONTENT = newStatus(205, "Reset Content"); - - /** - * 206 Partial Content - */ - public static final HttpResponseStatus PARTIAL_CONTENT = newStatus(206, "Partial Content"); - - /** - * 207 Multi-Status (WebDAV, RFC2518) - */ - public static final HttpResponseStatus MULTI_STATUS = newStatus(207, "Multi-Status"); - - /** - * 300 Multiple Choices - */ - public static final HttpResponseStatus MULTIPLE_CHOICES = newStatus(300, "Multiple Choices"); - - /** - * 301 Moved Permanently - */ - public static final HttpResponseStatus MOVED_PERMANENTLY = newStatus(301, "Moved Permanently"); - - /** - * 302 Found - */ - public static final HttpResponseStatus FOUND = newStatus(302, "Found"); - - /** - * 303 See Other (since HTTP/1.1) - */ - public static final HttpResponseStatus SEE_OTHER = newStatus(303, "See Other"); - - /** - * 304 Not Modified - */ - public static final HttpResponseStatus NOT_MODIFIED = newStatus(304, "Not Modified"); - - /** - * 305 Use Proxy (since HTTP/1.1) - */ - public static final HttpResponseStatus USE_PROXY = newStatus(305, "Use Proxy"); - - /** - * 307 Temporary Redirect (since HTTP/1.1) - */ - public static final HttpResponseStatus TEMPORARY_REDIRECT = newStatus(307, "Temporary Redirect"); - - /** - * 308 Permanent Redirect (RFC7538) - */ - public static final HttpResponseStatus PERMANENT_REDIRECT = newStatus(308, "Permanent Redirect"); - - /** - * 400 Bad Request - */ - public static final HttpResponseStatus BAD_REQUEST = newStatus(400, "Bad Request"); - - /** - * 401 Unauthorized - */ - public static final HttpResponseStatus UNAUTHORIZED = newStatus(401, "Unauthorized"); - - /** - * 402 Payment Required - */ - public static final HttpResponseStatus PAYMENT_REQUIRED = newStatus(402, "Payment Required"); - - /** - * 403 Forbidden - */ - public static final HttpResponseStatus FORBIDDEN = newStatus(403, "Forbidden"); - - /** - * 404 Not Found - */ - public static final HttpResponseStatus NOT_FOUND = newStatus(404, "Not Found"); - - /** - * 405 Method Not Allowed - */ - public static final HttpResponseStatus METHOD_NOT_ALLOWED = newStatus(405, "Method Not Allowed"); - - /** - * 406 Not Acceptable - */ - public static final HttpResponseStatus NOT_ACCEPTABLE = newStatus(406, "Not Acceptable"); - - /** - * 407 Proxy Authentication Required - */ - public static final HttpResponseStatus PROXY_AUTHENTICATION_REQUIRED = - newStatus(407, "Proxy Authentication Required"); - - /** - * 408 Request Timeout - */ - public static final HttpResponseStatus REQUEST_TIMEOUT = newStatus(408, "Request Timeout"); - - /** - * 409 Conflict - */ - public static final HttpResponseStatus CONFLICT = newStatus(409, "Conflict"); - - /** - * 410 Gone - */ - public static final HttpResponseStatus GONE = newStatus(410, "Gone"); - - /** - * 411 Length Required - */ - public static final HttpResponseStatus LENGTH_REQUIRED = newStatus(411, "Length Required"); - - /** - * 412 Precondition Failed - */ - public static final HttpResponseStatus PRECONDITION_FAILED = newStatus(412, "Precondition Failed"); - - /** - * 413 Request Entity Too Large - */ - public static final HttpResponseStatus REQUEST_ENTITY_TOO_LARGE = - newStatus(413, "Request Entity Too Large"); - - /** - * 414 Request-URI Too Long - */ - public static final HttpResponseStatus REQUEST_URI_TOO_LONG = newStatus(414, "Request-URI Too Long"); - - /** - * 415 Unsupported Media Type - */ - public static final HttpResponseStatus UNSUPPORTED_MEDIA_TYPE = newStatus(415, "Unsupported Media Type"); - - /** - * 416 Requested Range Not Satisfiable - */ - public static final HttpResponseStatus REQUESTED_RANGE_NOT_SATISFIABLE = - newStatus(416, "Requested Range Not Satisfiable"); - - /** - * 417 Expectation Failed - */ - public static final HttpResponseStatus EXPECTATION_FAILED = newStatus(417, "Expectation Failed"); - - /** - * 421 Misdirected Request - * - * @see 421 (Misdirected Request) Status Code - */ - public static final HttpResponseStatus MISDIRECTED_REQUEST = newStatus(421, "Misdirected Request"); - - /** - * 422 Unprocessable Entity (WebDAV, RFC4918) - */ - public static final HttpResponseStatus UNPROCESSABLE_ENTITY = newStatus(422, "Unprocessable Entity"); - - /** - * 423 Locked (WebDAV, RFC4918) - */ - public static final HttpResponseStatus LOCKED = newStatus(423, "Locked"); - - /** - * 424 Failed Dependency (WebDAV, RFC4918) - */ - public static final HttpResponseStatus FAILED_DEPENDENCY = newStatus(424, "Failed Dependency"); - - /** - * 425 Unordered Collection (WebDAV, RFC3648) - */ - public static final HttpResponseStatus UNORDERED_COLLECTION = newStatus(425, "Unordered Collection"); - - /** - * 426 Upgrade Required (RFC2817) - */ - public static final HttpResponseStatus UPGRADE_REQUIRED = newStatus(426, "Upgrade Required"); - - /** - * 428 Precondition Required (RFC6585) - */ - public static final HttpResponseStatus PRECONDITION_REQUIRED = newStatus(428, "Precondition Required"); - - /** - * 429 Too Many Requests (RFC6585) - */ - public static final HttpResponseStatus TOO_MANY_REQUESTS = newStatus(429, "Too Many Requests"); - - /** - * 431 Request Header Fields Too Large (RFC6585) - */ - public static final HttpResponseStatus REQUEST_HEADER_FIELDS_TOO_LARGE = - newStatus(431, "Request Header Fields Too Large"); - - /** - * 500 Internal Server Error - */ - public static final HttpResponseStatus INTERNAL_SERVER_ERROR = newStatus(500, "Internal Server Error"); - - /** - * 501 Not Implemented - */ - public static final HttpResponseStatus NOT_IMPLEMENTED = newStatus(501, "Not Implemented"); - - /** - * 502 Bad Gateway - */ - public static final HttpResponseStatus BAD_GATEWAY = newStatus(502, "Bad Gateway"); - - /** - * 503 Service Unavailable - */ - public static final HttpResponseStatus SERVICE_UNAVAILABLE = newStatus(503, "Service Unavailable"); - - /** - * 504 Gateway Timeout - */ - public static final HttpResponseStatus GATEWAY_TIMEOUT = newStatus(504, "Gateway Timeout"); - - /** - * 505 HTTP Version Not Supported - */ - public static final HttpResponseStatus HTTP_VERSION_NOT_SUPPORTED = - newStatus(505, "HTTP Version Not Supported"); - - /** - * 506 Variant Also Negotiates (RFC2295) - */ - public static final HttpResponseStatus VARIANT_ALSO_NEGOTIATES = newStatus(506, "Variant Also Negotiates"); - - /** - * 507 Insufficient Storage (WebDAV, RFC4918) - */ - public static final HttpResponseStatus INSUFFICIENT_STORAGE = newStatus(507, "Insufficient Storage"); - - /** - * 510 Not Extended (RFC2774) - */ - public static final HttpResponseStatus NOT_EXTENDED = newStatus(510, "Not Extended"); - - /** - * 511 Network Authentication Required (RFC6585) - */ - public static final HttpResponseStatus NETWORK_AUTHENTICATION_REQUIRED = - newStatus(511, "Network Authentication Required"); - - private static HttpResponseStatus newStatus(int statusCode, String reasonPhrase) { - return new HttpResponseStatus(statusCode, reasonPhrase, true); - } - - /** - * Returns the {@link HttpResponseStatus} represented by the specified code. - * If the specified code is a standard HTTP status code, a cached instance - * will be returned. Otherwise, a new instance will be returned. - */ - public static HttpResponseStatus valueOf(int code) { - HttpResponseStatus status = valueOf0(code); - return status != null ? status : new HttpResponseStatus(code); - } - - private static HttpResponseStatus valueOf0(int code) { - switch (code) { - case 100: - return CONTINUE; - case 101: - return SWITCHING_PROTOCOLS; - case 102: - return PROCESSING; - case 200: - return OK; - case 201: - return CREATED; - case 202: - return ACCEPTED; - case 203: - return NON_AUTHORITATIVE_INFORMATION; - case 204: - return NO_CONTENT; - case 205: - return RESET_CONTENT; - case 206: - return PARTIAL_CONTENT; - case 207: - return MULTI_STATUS; - case 300: - return MULTIPLE_CHOICES; - case 301: - return MOVED_PERMANENTLY; - case 302: - return FOUND; - case 303: - return SEE_OTHER; - case 304: - return NOT_MODIFIED; - case 305: - return USE_PROXY; - case 307: - return TEMPORARY_REDIRECT; - case 308: - return PERMANENT_REDIRECT; - case 400: - return BAD_REQUEST; - case 401: - return UNAUTHORIZED; - case 402: - return PAYMENT_REQUIRED; - case 403: - return FORBIDDEN; - case 404: - return NOT_FOUND; - case 405: - return METHOD_NOT_ALLOWED; - case 406: - return NOT_ACCEPTABLE; - case 407: - return PROXY_AUTHENTICATION_REQUIRED; - case 408: - return REQUEST_TIMEOUT; - case 409: - return CONFLICT; - case 410: - return GONE; - case 411: - return LENGTH_REQUIRED; - case 412: - return PRECONDITION_FAILED; - case 413: - return REQUEST_ENTITY_TOO_LARGE; - case 414: - return REQUEST_URI_TOO_LONG; - case 415: - return UNSUPPORTED_MEDIA_TYPE; - case 416: - return REQUESTED_RANGE_NOT_SATISFIABLE; - case 417: - return EXPECTATION_FAILED; - case 421: - return MISDIRECTED_REQUEST; - case 422: - return UNPROCESSABLE_ENTITY; - case 423: - return LOCKED; - case 424: - return FAILED_DEPENDENCY; - case 425: - return UNORDERED_COLLECTION; - case 426: - return UPGRADE_REQUIRED; - case 428: - return PRECONDITION_REQUIRED; - case 429: - return TOO_MANY_REQUESTS; - case 431: - return REQUEST_HEADER_FIELDS_TOO_LARGE; - case 500: - return INTERNAL_SERVER_ERROR; - case 501: - return NOT_IMPLEMENTED; - case 502: - return BAD_GATEWAY; - case 503: - return SERVICE_UNAVAILABLE; - case 504: - return GATEWAY_TIMEOUT; - case 505: - return HTTP_VERSION_NOT_SUPPORTED; - case 506: - return VARIANT_ALSO_NEGOTIATES; - case 507: - return INSUFFICIENT_STORAGE; - case 510: - return NOT_EXTENDED; - case 511: - return NETWORK_AUTHENTICATION_REQUIRED; - } - return null; - } - - /** - * Returns the {@link HttpResponseStatus} represented by the specified {@code code} and {@code reasonPhrase}. - * If the specified code is a standard HTTP status {@code code} and {@code reasonPhrase}, a cached instance - * will be returned. Otherwise, a new instance will be returned. - * @param code The response code value. - * @param reasonPhrase The response code reason phrase. - * @return the {@link HttpResponseStatus} represented by the specified {@code code} and {@code reasonPhrase}. - */ - public static HttpResponseStatus valueOf(int code, String reasonPhrase) { - HttpResponseStatus responseStatus = valueOf0(code); - return responseStatus != null && responseStatus.reasonPhrase().contentEquals(reasonPhrase) ? responseStatus : - new HttpResponseStatus(code, reasonPhrase); - } - - /** - * Parses the specified HTTP status line into a {@link HttpResponseStatus}. The expected formats of the line are: - *
    - *
  • {@code statusCode} (e.g. 200)
  • - *
  • {@code statusCode} {@code reasonPhrase} (e.g. 404 Not Found)
  • - *
- * - * @throws IllegalArgumentException if the specified status line is malformed - */ - public static HttpResponseStatus parseLine(CharSequence line) { - return (line instanceof AsciiString) ? parseLine((AsciiString) line) : parseLine(line.toString()); - } - - /** - * Parses the specified HTTP status line into a {@link HttpResponseStatus}. The expected formats of the line are: - *
    - *
  • {@code statusCode} (e.g. 200)
  • - *
  • {@code statusCode} {@code reasonPhrase} (e.g. 404 Not Found)
  • - *
- * - * @throws IllegalArgumentException if the specified status line is malformed - */ - public static HttpResponseStatus parseLine(String line) { - try { - int space = line.indexOf(' '); - return space == -1 ? valueOf(parseInt(line)) : - valueOf(parseInt(line.substring(0, space)), line.substring(space + 1)); - } catch (Exception e) { - throw new IllegalArgumentException("malformed status line: " + line, e); - } - } - - /** - * Parses the specified HTTP status line into a {@link HttpResponseStatus}. The expected formats of the line are: - *
    - *
  • {@code statusCode} (e.g. 200)
  • - *
  • {@code statusCode} {@code reasonPhrase} (e.g. 404 Not Found)
  • - *
- * - * @throws IllegalArgumentException if the specified status line is malformed - */ - public static HttpResponseStatus parseLine(AsciiString line) { - try { - int space = line.forEachByte(FIND_ASCII_SPACE); - return space == -1 ? valueOf(line.parseInt()) : valueOf(line.parseInt(0, space), line.toString(space + 1)); - } catch (Exception e) { - throw new IllegalArgumentException("malformed status line: " + line, e); - } - } - - private final int code; - private final AsciiString codeAsText; - private HttpStatusClass codeClass; - - private final String reasonPhrase; - private final byte[] bytes; - - /** - * Creates a new instance with the specified {@code code} and the auto-generated default reason phrase. - */ - private HttpResponseStatus(int code) { - this(code, HttpStatusClass.valueOf(code).defaultReasonPhrase() + " (" + code + ')', false); - } - - /** - * Creates a new instance with the specified {@code code} and its {@code reasonPhrase}. - */ - public HttpResponseStatus(int code, String reasonPhrase) { - this(code, reasonPhrase, false); - } - - private HttpResponseStatus(int code, String reasonPhrase, boolean bytes) { - checkPositiveOrZero(code, "code"); - - requireNonNull(reasonPhrase, "reasonPhrase"); - - for (int i = 0; i < reasonPhrase.length(); i ++) { - char c = reasonPhrase.charAt(i); - // Check prohibited characters. - switch (c) { - case '\n': case '\r': - throw new IllegalArgumentException( - "reasonPhrase contains one of the following prohibited characters: " + - "\\r\\n: " + reasonPhrase); - } - } - - this.code = code; - String codeString = Integer.toString(code); - codeAsText = new AsciiString(codeString); - this.reasonPhrase = reasonPhrase; - if (bytes) { - this.bytes = (codeString + ' ' + reasonPhrase).getBytes(CharsetUtil.US_ASCII); - } else { - this.bytes = null; - } - } - - /** - * Returns the code of this {@link HttpResponseStatus}. - */ - public int code() { - return code; - } - - /** - * Returns the status code as {@link AsciiString}. - */ - public AsciiString codeAsText() { - return codeAsText; - } - - /** - * Returns the reason phrase of this {@link HttpResponseStatus}. - */ - public String reasonPhrase() { - return reasonPhrase; - } - - /** - * Returns the class of this {@link HttpResponseStatus} - */ - public HttpStatusClass codeClass() { - HttpStatusClass type = this.codeClass; - if (type == null) { - this.codeClass = type = HttpStatusClass.valueOf(code); - } - return type; - } - - @Override - public int hashCode() { - return code(); - } - - /** - * Equality of {@link HttpResponseStatus} only depends on {@link #code()}. The - * reason phrase is not considered for equality. - */ - @Override - public boolean equals(Object o) { - if (!(o instanceof HttpResponseStatus)) { - return false; - } - - return code() == ((HttpResponseStatus) o).code(); - } - - /** - * Equality of {@link HttpResponseStatus} only depends on {@link #code()}. The - * reason phrase is not considered for equality. - */ - @Override - public int compareTo(HttpResponseStatus o) { - return code() - o.code(); - } - - @Override - public String toString() { - return new StringBuilder(reasonPhrase.length() + 4) - .append(codeAsText) - .append(' ') - .append(reasonPhrase) - .toString(); - } - - void encode(ByteBuf buf) { - if (bytes == null) { - ByteBufUtil.copy(codeAsText, buf); - buf.writeByte(SP); - buf.writeCharSequence(reasonPhrase, CharsetUtil.US_ASCII); - } else { - buf.writeBytes(bytes); - } - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpScheme.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpScheme.java deleted file mode 100644 index 97a6c6df73..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpScheme.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.util.AsciiString; - -/** - * Defines the common schemes used for the HTTP protocol as defined by - * rfc7230. - */ -public final class HttpScheme { - - /** - * Scheme for non-secure HTTP connection. - */ - public static final HttpScheme HTTP = new HttpScheme(80, "http"); - - /** - * Scheme for secure HTTP connection. - */ - public static final HttpScheme HTTPS = new HttpScheme(443, "https"); - - private final int port; - private final AsciiString name; - - private HttpScheme(int port, String name) { - this.port = port; - this.name = AsciiString.cached(name); - } - - public AsciiString name() { - return name; - } - - public int port() { - return port; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof HttpScheme)) { - return false; - } - HttpScheme other = (HttpScheme) o; - return other.port() == port && other.name().equals(name); - } - - @Override - public int hashCode() { - return port * 31 + name.hashCode(); - } - - @Override - public String toString() { - return name.toString(); - } -} 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 deleted file mode 100644 index 63e40805ef..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerCodec.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.CombinedChannelDuplexHandler; - -import java.util.ArrayDeque; -import java.util.Queue; - -import static io.netty.handler.codec.http.HttpObjectDecoder.DEFAULT_MAX_HEADER_SIZE; -import static io.netty.handler.codec.http.HttpObjectDecoder.DEFAULT_MAX_INITIAL_LINE_LENGTH; - -/** - * A combination of {@link HttpRequestDecoder} and {@link HttpResponseEncoder} - * which enables easier server side HTTP implementation. - * - * @see HttpClientCodec - */ -public final class HttpServerCodec extends CombinedChannelDuplexHandler - implements HttpServerUpgradeHandler.SourceCodec { - - /** A queue that is used for correlating a request and a response. */ - private final Queue queue = new ArrayDeque<>(); - - /** - * Creates a new instance with the default decoder options - * ({@code maxInitialLineLength (4096}}, {@code maxHeaderSize (8192)}, and - * {@code maxChunkSize (8192)}). - */ - public HttpServerCodec() { - this(DEFAULT_MAX_INITIAL_LINE_LENGTH, DEFAULT_MAX_HEADER_SIZE); - } - - /** - * Creates a new instance with the specified decoder options. - */ - public HttpServerCodec(int maxInitialLineLength, int maxHeaderSize) { - init(new HttpServerRequestDecoder(maxInitialLineLength, maxHeaderSize), - new HttpServerResponseEncoder()); - } - - /** - * Creates a new instance with the specified decoder options. - */ - public HttpServerCodec(int maxInitialLineLength, int maxHeaderSize, boolean validateHeaders) { - init(new HttpServerRequestDecoder(maxInitialLineLength, maxHeaderSize, validateHeaders), - new HttpServerResponseEncoder()); - } - - /** - * Creates a new instance with the specified decoder options. - */ - public HttpServerCodec(int maxInitialLineLength, int maxHeaderSize, boolean validateHeaders, - int initialBufferSize) { - init( - new HttpServerRequestDecoder(maxInitialLineLength, maxHeaderSize, - validateHeaders, initialBufferSize), - new HttpServerResponseEncoder()); - } - - /** - * Creates a new instance with the specified decoder options. - */ - public HttpServerCodec(int maxInitialLineLength, int maxHeaderSize, boolean validateHeaders, - int initialBufferSize, boolean allowDuplicateContentLengths) { - init(new HttpServerRequestDecoder(maxInitialLineLength, maxHeaderSize, validateHeaders, - initialBufferSize, allowDuplicateContentLengths), - new HttpServerResponseEncoder()); - } - - /** - * Upgrades to another protocol from HTTP. Removes the {@link HttpRequestDecoder} and - * {@link HttpResponseEncoder} from the pipeline. - */ - @Override - public void upgradeFrom(ChannelHandlerContext ctx) { - ctx.pipeline().remove(this); - } - - private final class HttpServerRequestDecoder extends HttpRequestDecoder { - - private ChannelHandlerContext context; - - HttpServerRequestDecoder(int maxInitialLineLength, int maxHeaderSize) { - super(maxInitialLineLength, maxHeaderSize); - } - - HttpServerRequestDecoder(int maxInitialLineLength, int maxHeaderSize, - boolean validateHeaders) { - super(maxInitialLineLength, maxHeaderSize, validateHeaders); - } - - HttpServerRequestDecoder(int maxInitialLineLength, int maxHeaderSize, - boolean validateHeaders, int initialBufferSize) { - super(maxInitialLineLength, maxHeaderSize, validateHeaders, initialBufferSize); - } - - @Override - protected void decode(final ChannelHandlerContext ctx, ByteBuf buffer) throws Exception { - super.decode(context, buffer); - } - - HttpServerRequestDecoder(int maxInitialLineLength, int maxHeaderSize, - boolean validateHeaders, int initialBufferSize, boolean allowDuplicateContentLengths) { - super(maxInitialLineLength, maxHeaderSize, validateHeaders, initialBufferSize, - allowDuplicateContentLengths); - } - - @Override - protected void handlerAdded0(final ChannelHandlerContext ctx) { - context = new DelegatingChannelHandlerContext(ctx) { - - @Override - public ChannelHandlerContext fireChannelRead(Object msg) { - if (msg instanceof HttpRequest) { - queue.add(((HttpRequest) msg).method()); - } - super.fireChannelRead(msg); - return this; - } - }; - } - } - - private final class HttpServerResponseEncoder extends HttpResponseEncoder { - - private HttpMethod method; - - @Override - protected void sanitizeHeadersBeforeEncode(HttpResponse msg, boolean isAlwaysEmpty) { - 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); - return; - } - - super.sanitizeHeadersBeforeEncode(msg, isAlwaysEmpty); - } - - @Override - protected boolean isContentAlwaysEmpty(@SuppressWarnings("unused") HttpResponse msg) { - method = queue.poll(); - return HttpMethod.HEAD.equals(method) || super.isContentAlwaysEmpty(msg); - } - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerExpectContinueHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerExpectContinueHandler.java deleted file mode 100644 index 1055d7b475..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerExpectContinueHandler.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelFutureListeners; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.ReferenceCountUtil; - -import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH; -import static io.netty.handler.codec.http.HttpResponseStatus.CONTINUE; -import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; - -/** - * Sends a 100 CONTINUE - * {@link HttpResponse} to {@link HttpRequest}s which contain a 'expect: 100-continue' header. It - * should only be used for applications which do not install the {@link HttpObjectAggregator}. - *

- * By default it accepts all expectations. - *

- * Since {@link HttpServerExpectContinueHandler} expects {@link HttpRequest}s it should be added after {@link - * HttpServerCodec} but before any other handlers that might send a {@link HttpResponse}.

- *
- *  {@link io.netty.channel.ChannelPipeline} p = ...;
- *  ...
- *  p.addLast("serverCodec", new {@link HttpServerCodec}());
- *  p.addLast("respondExpectContinue", new {@link HttpServerExpectContinueHandler}());
- *  ...
- *  p.addLast("handler", new HttpRequestHandler());
- *  
- *
- */ -public class HttpServerExpectContinueHandler implements ChannelHandler { - - private static final FullHttpResponse EXPECTATION_FAILED = new DefaultFullHttpResponse( - HTTP_1_1, HttpResponseStatus.EXPECTATION_FAILED, Unpooled.EMPTY_BUFFER); - - private static final FullHttpResponse ACCEPT = new DefaultFullHttpResponse( - HTTP_1_1, CONTINUE, Unpooled.EMPTY_BUFFER); - - static { - EXPECTATION_FAILED.headers().set(CONTENT_LENGTH, 0); - ACCEPT.headers().set(CONTENT_LENGTH, 0); - } - - /** - * Produces a {@link HttpResponse} for {@link HttpRequest}s which define an expectation. Returns {@code null} if the - * request should be rejected. See {@link #rejectResponse(HttpRequest)}. - */ - protected HttpResponse acceptMessage(@SuppressWarnings("unused") HttpRequest request) { - return ACCEPT.retainedDuplicate(); - } - - /** - * Returns the appropriate 4XX {@link HttpResponse} for the given {@link HttpRequest}. - */ - protected HttpResponse rejectResponse(@SuppressWarnings("unused") HttpRequest request) { - return EXPECTATION_FAILED.retainedDuplicate(); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - if (msg instanceof HttpRequest) { - HttpRequest req = (HttpRequest) msg; - - if (HttpUtil.is100ContinueExpected(req)) { - HttpResponse accept = acceptMessage(req); - - if (accept == null) { - // the expectation failed so we refuse the request. - HttpResponse rejection = rejectResponse(req); - ReferenceCountUtil.release(msg); - ctx.writeAndFlush(rejection).addListener(ctx.channel(), ChannelFutureListeners.CLOSE_ON_FAILURE); - return; - } - - ctx.writeAndFlush(accept).addListener(ctx.channel(), ChannelFutureListeners.CLOSE_ON_FAILURE); - req.headers().remove(HttpHeaderNames.EXPECT); - } - } - ctx.fireChannelRead(msg); - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerKeepAliveHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerKeepAliveHandler.java deleted file mode 100644 index 64216fbd0b..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerKeepAliveHandler.java +++ /dev/null @@ -1,133 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.channel.ChannelFutureListeners; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.util.concurrent.Future; - -import static io.netty.handler.codec.http.HttpUtil.isContentLengthSet; -import static io.netty.handler.codec.http.HttpUtil.isKeepAlive; -import static io.netty.handler.codec.http.HttpUtil.isTransferEncodingChunked; -import static io.netty.handler.codec.http.HttpUtil.setKeepAlive; - -/** - * HttpServerKeepAliveHandler helps close persistent connections when appropriate. - *

- * The server channel is expected to set the proper 'Connection' header if it can handle persistent connections. {@link - * HttpServerKeepAliveHandler} will automatically close the channel for any LastHttpContent that corresponds to a client - * request for closing the connection, or if the HttpResponse associated with that LastHttpContent requested closing the - * connection or didn't have a self defined message length. - *

- * Since {@link HttpServerKeepAliveHandler} expects {@link HttpObject}s it should be added after {@link HttpServerCodec} - * but before any other handlers that might send a {@link HttpResponse}.

- *
- *  {@link ChannelPipeline} p = ...;
- *  ...
- *  p.addLast("serverCodec", new {@link HttpServerCodec}());
- *  p.addLast("httpKeepAlive", new {@link HttpServerKeepAliveHandler}());
- *  p.addLast("aggregator", new {@link HttpObjectAggregator}(1048576));
- *  ...
- *  p.addLast("handler", new HttpRequestHandler());
- *  
- *
- */ -public class HttpServerKeepAliveHandler implements ChannelHandler { - private static final String MULTIPART_PREFIX = "multipart"; - - private boolean persistentConnection = true; - // Track pending responses to support client pipelining: https://tools.ietf.org/html/rfc7230#section-6.3.2 - private int pendingResponses; - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - // read message and track if it was keepAlive - if (msg instanceof HttpRequest) { - final HttpRequest request = (HttpRequest) msg; - if (persistentConnection) { - pendingResponses += 1; - persistentConnection = isKeepAlive(request); - } - } - ctx.fireChannelRead(msg); - } - - @Override - public Future write(ChannelHandlerContext ctx, Object msg) { - // modify message on way out to add headers if needed - if (msg instanceof HttpResponse) { - final HttpResponse response = (HttpResponse) msg; - trackResponse(response); - // Assume the response writer knows if they can persist or not and sets isKeepAlive on the response - if (!isKeepAlive(response) || !isSelfDefinedMessageLength(response)) { - // No longer keep alive as the client can't tell when the message is done unless we close connection - pendingResponses = 0; - persistentConnection = false; - } - // Server might think it can keep connection alive, but we should fix response header if we know better - if (!shouldKeepAlive()) { - setKeepAlive(response, false); - } - } - boolean shouldClose = msg instanceof LastHttpContent && !shouldKeepAlive(); - Future future = ctx.write(msg); - if (shouldClose) { - future.addListener(ctx, ChannelFutureListeners.CLOSE); - } - return future; - } - - private void trackResponse(HttpResponse response) { - if (!isInformational(response)) { - pendingResponses -= 1; - } - } - - private boolean shouldKeepAlive() { - return pendingResponses != 0 || persistentConnection; - } - - /** - * Keep-alive only works if the client can detect when the message has ended without relying on the connection being - * closed. - *

- *

- * - * @param response The HttpResponse to check - * - * @return true if the response has a self defined message length. - */ - private static boolean isSelfDefinedMessageLength(HttpResponse response) { - return isContentLengthSet(response) || isTransferEncodingChunked(response) || isMultipart(response) || - isInformational(response) || response.status().code() == HttpResponseStatus.NO_CONTENT.code(); - } - - private static boolean isInformational(HttpResponse response) { - return response.status().codeClass() == HttpStatusClass.INFORMATIONAL; - } - - private static boolean isMultipart(HttpResponse response) { - String contentType = response.headers().get(HttpHeaderNames.CONTENT_TYPE); - return contentType != null && - contentType.regionMatches(true, 0, MULTIPART_PREFIX, 0, MULTIPART_PREFIX.length()); - } -} 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 deleted file mode 100644 index b27d707ba0..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerUpgradeHandler.java +++ /dev/null @@ -1,426 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http; - -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelFutureListeners; -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.ReferenceCounted; -import io.netty.util.concurrent.Future; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import static io.netty.handler.codec.http.HttpResponseStatus.SWITCHING_PROTOCOLS; -import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; -import static java.util.Objects.requireNonNull; -import static io.netty.util.AsciiString.containsAllContentEqualsIgnoreCase; -import static io.netty.util.AsciiString.containsContentEqualsIgnoreCase; -import static io.netty.util.internal.StringUtil.COMMA; - -/** - * A server-side handler that receives HTTP requests and optionally performs a protocol switch if - * the requested protocol is supported. Once an upgrade is performed, this handler removes itself - * from the pipeline. - */ -public class HttpServerUpgradeHandler extends HttpObjectAggregator { - - /** - * The source codec that is used in the pipeline initially. - */ - public interface SourceCodec { - /** - * Removes this codec (i.e. all associated handlers) from the pipeline. - */ - void upgradeFrom(ChannelHandlerContext ctx); - } - - /** - * A codec that the source can be upgraded to. - */ - public interface UpgradeCodec { - /** - * Gets all protocol-specific headers required by this protocol for a successful upgrade. - * Any supplied header will be required to appear in the {@link HttpHeaderNames#CONNECTION} header as well. - */ - Collection requiredUpgradeHeaders(); - - /** - * Prepares the {@code upgradeHeaders} for a protocol update based upon the contents of {@code upgradeRequest}. - * This method returns a boolean value to proceed or abort the upgrade in progress. If {@code false} is - * returned, the upgrade is aborted and the {@code upgradeRequest} will be passed through the inbound pipeline - * as if no upgrade was performed. If {@code true} is returned, the upgrade will proceed to the next - * step which invokes {@link #upgradeTo}. When returning {@code true}, you can add headers to - * the {@code upgradeHeaders} so that they are added to the 101 Switching protocols response. - */ - boolean prepareUpgradeResponse(ChannelHandlerContext ctx, FullHttpRequest upgradeRequest, - HttpHeaders upgradeHeaders); - - /** - * Performs an HTTP protocol upgrade from the source codec. This method is responsible for - * adding all handlers required for the new protocol. - * - * @param ctx the context for the current handler. - * @param upgradeRequest the request that triggered the upgrade to this protocol. - */ - void upgradeTo(ChannelHandlerContext ctx, FullHttpRequest upgradeRequest); - } - - /** - * Creates a new {@link UpgradeCodec} for the requested protocol name. - */ - public interface UpgradeCodecFactory { - /** - * Invoked by {@link HttpServerUpgradeHandler} for all the requested protocol names in the order of - * the client preference. The first non-{@code null} {@link UpgradeCodec} returned by this method - * will be selected. - * - * @return a new {@link UpgradeCodec}, or {@code null} if the specified protocol name is not supported - */ - UpgradeCodec newUpgradeCodec(CharSequence protocol); - } - - /** - * User event that is fired to notify about the completion of an HTTP upgrade - * to another protocol. Contains the original upgrade request so that the response - * (if required) can be sent using the new protocol. - */ - public static final class UpgradeEvent implements ReferenceCounted { - private final CharSequence protocol; - private final FullHttpRequest upgradeRequest; - - UpgradeEvent(CharSequence protocol, FullHttpRequest upgradeRequest) { - this.protocol = protocol; - this.upgradeRequest = upgradeRequest; - } - - /** - * The protocol that the channel has been upgraded to. - */ - public CharSequence protocol() { - return protocol; - } - - /** - * Gets the request that triggered the protocol upgrade. - */ - public FullHttpRequest upgradeRequest() { - return upgradeRequest; - } - - @Override - public int refCnt() { - return upgradeRequest.refCnt(); - } - - @Override - public UpgradeEvent retain() { - upgradeRequest.retain(); - return this; - } - - @Override - public UpgradeEvent retain(int increment) { - upgradeRequest.retain(increment); - return this; - } - - @Override - public UpgradeEvent touch() { - upgradeRequest.touch(); - return this; - } - - @Override - public UpgradeEvent touch(Object hint) { - upgradeRequest.touch(hint); - return this; - } - - @Override - public boolean release() { - return upgradeRequest.release(); - } - - @Override - public boolean release(int decrement) { - return upgradeRequest.release(decrement); - } - - @Override - public String toString() { - return "UpgradeEvent [protocol=" + protocol + ", upgradeRequest=" + upgradeRequest + ']'; - } - } - - private final SourceCodec sourceCodec; - private final UpgradeCodecFactory upgradeCodecFactory; - private final boolean validateHeaders; - private boolean handlingUpgrade; - - /** - * Constructs the upgrader with the supported codecs. - *

- * The handler instantiated by this constructor will reject an upgrade request with non-empty content. - * It should not be a concern because an upgrade request is most likely a GET request. - * If you have a client that sends a non-GET upgrade request, please consider using - * {@link #HttpServerUpgradeHandler(SourceCodec, UpgradeCodecFactory, int)} to specify the maximum - * length of the content of an upgrade request. - *

- * - * @param sourceCodec the codec that is being used initially - * @param upgradeCodecFactory the factory that creates a new upgrade codec - * for one of the requested upgrade protocols - */ - public HttpServerUpgradeHandler(SourceCodec sourceCodec, UpgradeCodecFactory upgradeCodecFactory) { - this(sourceCodec, upgradeCodecFactory, 0); - } - - /** - * Constructs the upgrader with the supported codecs. - * - * @param sourceCodec the codec that is being used initially - * @param upgradeCodecFactory the factory that creates a new upgrade codec - * for one of the requested upgrade protocols - * @param maxContentLength the maximum length of the content of an upgrade request - */ - public HttpServerUpgradeHandler( - SourceCodec sourceCodec, UpgradeCodecFactory upgradeCodecFactory, int maxContentLength) { - this(sourceCodec, upgradeCodecFactory, maxContentLength, true); - } - - /** - * Constructs the upgrader with the supported codecs. - * - * @param sourceCodec the codec that is being used initially - * @param upgradeCodecFactory the factory that creates a new upgrade codec - * for one of the requested upgrade protocols - * @param maxContentLength the maximum length of the content of an upgrade request - * @param validateHeaders validate the header names and values of the upgrade response. - */ - public HttpServerUpgradeHandler(SourceCodec sourceCodec, UpgradeCodecFactory upgradeCodecFactory, - int maxContentLength, boolean validateHeaders) { - super(maxContentLength); - - this.sourceCodec = requireNonNull(sourceCodec, "sourceCodec"); - this.upgradeCodecFactory = requireNonNull(upgradeCodecFactory, "upgradeCodecFactory"); - this.validateHeaders = validateHeaders; - } - - @Override - protected void decode(final ChannelHandlerContext ctx, HttpObject msg) - throws Exception { - - if (!handlingUpgrade) { - // Not handling an upgrade request yet. Check if we received a new upgrade request. - if (msg instanceof HttpRequest) { - HttpRequest req = (HttpRequest) msg; - if (req.headers().contains(HttpHeaderNames.UPGRADE) && - shouldHandleUpgradeRequest(req)) { - handlingUpgrade = true; - } else { - ReferenceCountUtil.retain(msg); - ctx.fireChannelRead(msg); - return; - } - } else { - ReferenceCountUtil.retain(msg); - ctx.fireChannelRead(msg); - return; - } - } - - FullHttpRequest fullRequest; - if (msg instanceof FullHttpRequest) { - fullRequest = (FullHttpRequest) msg; - tryUpgrade(ctx, fullRequest.retain()); - } else { - // Call the base class to handle the aggregation of the full request. - super.decode(new DelegatingChannelHandlerContext(ctx) { - @Override - public ChannelHandlerContext fireChannelRead(Object msg) { - // Finished aggregating the full request, get it from the output list. - handlingUpgrade = false; - tryUpgrade(ctx, (FullHttpRequest) msg); - return this; - } - }, msg); - } - } - - private void tryUpgrade(ChannelHandlerContext ctx, FullHttpRequest request) { - if (!upgrade(ctx, request)) { - - // The upgrade did not succeed, just allow the full request to propagate to the - // next handler. - ctx.fireChannelRead(request); - } - } - - /** - * Determines whether the specified upgrade {@link HttpRequest} should be handled by this handler or not. - * This method will be invoked only when the request contains an {@code Upgrade} header. - * It always returns {@code true} by default, which means any request with an {@code Upgrade} header - * will be handled. You can override this method to ignore certain {@code Upgrade} headers, for example: - *
{@code
-     * @Override
-     * protected boolean isUpgradeRequest(HttpRequest req) {
-     *   // Do not handle WebSocket upgrades.
-     *   return !req.headers().contains(HttpHeaderNames.UPGRADE, "websocket", false);
-     * }
-     * }
- */ - protected boolean shouldHandleUpgradeRequest(HttpRequest req) { - return true; - } - - /** - * Attempts to upgrade to the protocol(s) identified by the {@link HttpHeaderNames#UPGRADE} header (if provided - * in the request). - * - * @param ctx the context for this handler. - * @param request the HTTP request. - * @return {@code true} if the upgrade occurred, otherwise {@code false}. - */ - private boolean upgrade(final ChannelHandlerContext ctx, final FullHttpRequest request) { - // Select the best protocol based on those requested in the UPGRADE header. - final List requestedProtocols = splitHeader(request.headers().get(HttpHeaderNames.UPGRADE)); - final int numRequestedProtocols = requestedProtocols.size(); - UpgradeCodec upgradeCodec = null; - CharSequence upgradeProtocol = null; - for (int i = 0; i < numRequestedProtocols; i ++) { - final CharSequence p = requestedProtocols.get(i); - final UpgradeCodec c = upgradeCodecFactory.newUpgradeCodec(p); - if (c != null) { - upgradeProtocol = p; - upgradeCodec = c; - break; - } - } - - if (upgradeCodec == null) { - // None of the requested protocols are supported, don't upgrade. - return false; - } - - // Make sure the CONNECTION header is present. - List connectionHeaderValues = request.headers().getAll(HttpHeaderNames.CONNECTION); - - if (connectionHeaderValues == null || connectionHeaderValues.isEmpty()) { - 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(concatenatedConnectionValue); - if (!containsContentEqualsIgnoreCase(values, HttpHeaderNames.UPGRADE) || - !containsAllContentEqualsIgnoreCase(values, requiredHeaders)) { - return false; - } - - // Ensure that all required protocol-specific headers are found in the request. - for (CharSequence requiredHeader : requiredHeaders) { - if (!request.headers().contains(requiredHeader)) { - return false; - } - } - - // Prepare and send the upgrade response. Wait for this write to complete before upgrading, - // since we need the old codec in-place to properly encode the response. - final FullHttpResponse upgradeResponse = createUpgradeResponse(upgradeProtocol); - if (!upgradeCodec.prepareUpgradeResponse(ctx, request, upgradeResponse.headers())) { - return false; - } - - // Create the user event to be fired once the upgrade completes. - final UpgradeEvent event = new UpgradeEvent(upgradeProtocol, request); - - // After writing the upgrade response we immediately prepare the - // pipeline for the next protocol to avoid a race between completion - // of the write future and receiving data before the pipeline is - // restructured. - try { - Future writeComplete = ctx.writeAndFlush(upgradeResponse); - // Perform the upgrade to the new protocol. - sourceCodec.upgradeFrom(ctx); - upgradeCodec.upgradeTo(ctx, request); - - // Notify that the upgrade has occurred. Retain the event to offset - // the release() in the finally block. - ctx.fireUserEventTriggered(event.retain()); - - // Remove this handler from the pipeline. - ctx.pipeline().remove(HttpServerUpgradeHandler.this); - - // Add the listener last to avoid firing upgrade logic after - // the channel is already closed since the listener may fire - // immediately if the write failed eagerly. - writeComplete.addListener(ctx.channel(), ChannelFutureListeners.CLOSE_ON_FAILURE); - } finally { - // Release the event if the upgrade event wasn't fired. - event.release(); - } - return true; - } - - /** - * Creates the 101 Switching Protocols response message. - */ - private FullHttpResponse createUpgradeResponse(CharSequence upgradeProtocol) { - DefaultFullHttpResponse res = new DefaultFullHttpResponse( - HTTP_1_1, SWITCHING_PROTOCOLS, Unpooled.EMPTY_BUFFER, validateHeaders); - res.headers().add(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE); - res.headers().add(HttpHeaderNames.UPGRADE, upgradeProtocol); - return res; - } - - /** - * Splits a comma-separated header value. The returned set is case-insensitive and contains each - * part with whitespace removed. - */ - private static List splitHeader(CharSequence header) { - final StringBuilder builder = new StringBuilder(header.length()); - final List protocols = new ArrayList<>(4); - for (int i = 0; i < header.length(); ++i) { - char c = header.charAt(i); - if (Character.isWhitespace(c)) { - // Don't include any whitespace. - continue; - } - if (c == ',') { - // Add the string and reset the builder for the next protocol. - protocols.add(builder.toString()); - builder.setLength(0); - } else { - builder.append(c); - } - } - - // Add the last protocol - if (builder.length() > 0) { - protocols.add(builder.toString()); - } - - return protocols; - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpStatusClass.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpStatusClass.java deleted file mode 100644 index 4d5ac8c549..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpStatusClass.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.http; - -import io.netty.util.AsciiString; - -/** - * The class of HTTP status. - */ -public enum HttpStatusClass { - /** - * The informational class (1xx) - */ - INFORMATIONAL(100, 200, "Informational"), - /** - * The success class (2xx) - */ - SUCCESS(200, 300, "Success"), - /** - * The redirection class (3xx) - */ - REDIRECTION(300, 400, "Redirection"), - /** - * The client error class (4xx) - */ - CLIENT_ERROR(400, 500, "Client Error"), - /** - * The server error class (5xx) - */ - SERVER_ERROR(500, 600, "Server Error"), - /** - * The unknown class - */ - UNKNOWN(0, 0, "Unknown Status") { - @Override - public boolean contains(int code) { - return code < 100 || code >= 600; - } - }; - - /** - * Returns the class of the specified HTTP status code. - */ - public static HttpStatusClass valueOf(int code) { - if (INFORMATIONAL.contains(code)) { - return INFORMATIONAL; - } - if (SUCCESS.contains(code)) { - return SUCCESS; - } - if (REDIRECTION.contains(code)) { - return REDIRECTION; - } - if (CLIENT_ERROR.contains(code)) { - return CLIENT_ERROR; - } - if (SERVER_ERROR.contains(code)) { - return SERVER_ERROR; - } - return UNKNOWN; - } - - /** - * Returns the class of the specified HTTP status code. - * @param code Just the numeric portion of the http status code. - */ - public static HttpStatusClass valueOf(CharSequence code) { - if (code != null && code.length() == 3) { - char c0 = code.charAt(0); - return isDigit(c0) && isDigit(code.charAt(1)) && isDigit(code.charAt(2)) ? valueOf(digit(c0) * 100) - : UNKNOWN; - } - return UNKNOWN; - } - - private static int digit(char c) { - return c - '0'; - } - - private static boolean isDigit(char c) { - return c >= '0' && c <= '9'; - } - - private final int min; - private final int max; - private final AsciiString defaultReasonPhrase; - - HttpStatusClass(int min, int max, String defaultReasonPhrase) { - this.min = min; - this.max = max; - this.defaultReasonPhrase = AsciiString.cached(defaultReasonPhrase); - } - - /** - * Returns {@code true} if and only if the specified HTTP status code falls into this class. - */ - public boolean contains(int code) { - return code >= min && code < max; - } - - /** - * Returns the default reason phrase of this HTTP status class. - */ - AsciiString defaultReasonPhrase() { - return defaultReasonPhrase; - } -} 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 deleted file mode 100644 index 16ec11bbd8..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpUtil.java +++ /dev/null @@ -1,622 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import static java.util.Objects.requireNonNull; - -import java.net.InetSocketAddress; -import java.net.URI; -import java.nio.charset.Charset; -import java.nio.charset.IllegalCharsetNameException; -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; -import io.netty.util.internal.UnstableApi; - -import static io.netty.util.internal.StringUtil.COMMA; -import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; - -/** - * Utility methods useful in the HTTP context. - */ -public final class HttpUtil { - - private static final AsciiString CHARSET_EQUALS = AsciiString.of(HttpHeaderValues.CHARSET + "="); - private static final AsciiString SEMICOLON = AsciiString.cached(";"); - private static final String COMMA_STRING = String.valueOf(COMMA); - - private HttpUtil() { } - - /** - * Determine if a uri is in origin-form according to - *
rfc7230, 5.3. - */ - public static boolean isOriginForm(URI uri) { - return uri.getScheme() == null && uri.getSchemeSpecificPart() == null && - uri.getHost() == null && uri.getAuthority() == null; - } - - /** - * Determine if a uri is in asterisk-form according to - * rfc7230, 5.3. - */ - public static boolean isAsteriskForm(URI uri) { - return "*".equals(uri.getPath()) && - uri.getScheme() == null && uri.getSchemeSpecificPart() == null && - uri.getHost() == null && uri.getAuthority() == null && uri.getQuery() == null && - uri.getFragment() == null; - } - - /** - * 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) { - return !message.headers().containsValue(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE, true) && - (message.protocolVersion().isKeepAliveDefault() || - message.headers().containsValue(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE, true)); - } - - /** - * Sets the value of the {@code "Connection"} header depending on the - * protocol version of the specified message. This getMethod sets or removes - * the {@code "Connection"} header depending on what the default keep alive - * mode of the message's protocol version is, as specified by - * {@link HttpVersion#isKeepAliveDefault()}. - *
    - *
  • If the connection is kept alive by default: - *
      - *
    • set to {@code "close"} if {@code keepAlive} is {@code false}.
    • - *
    • remove otherwise.
    • - *
  • - *
  • If the connection is closed by default: - *
      - *
    • set to {@code "keep-alive"} if {@code keepAlive} is {@code true}.
    • - *
    • remove otherwise.
    • - *
  • - *
- * @see #setKeepAlive(HttpHeaders, HttpVersion, boolean) - */ - public static void setKeepAlive(HttpMessage message, boolean keepAlive) { - setKeepAlive(message.headers(), message.protocolVersion(), keepAlive); - } - - /** - * Sets the value of the {@code "Connection"} header depending on the - * protocol version of the specified message. This getMethod sets or removes - * the {@code "Connection"} header depending on what the default keep alive - * mode of the message's protocol version is, as specified by - * {@link HttpVersion#isKeepAliveDefault()}. - *
    - *
  • If the connection is kept alive by default: - *
      - *
    • set to {@code "close"} if {@code keepAlive} is {@code false}.
    • - *
    • remove otherwise.
    • - *
  • - *
  • If the connection is closed by default: - *
      - *
    • set to {@code "keep-alive"} if {@code keepAlive} is {@code true}.
    • - *
    • remove otherwise.
    • - *
  • - *
- */ - public static void setKeepAlive(HttpHeaders h, HttpVersion httpVersion, boolean keepAlive) { - if (httpVersion.isKeepAliveDefault()) { - if (keepAlive) { - h.remove(HttpHeaderNames.CONNECTION); - } else { - h.set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE); - } - } else { - if (keepAlive) { - h.set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE); - } else { - h.remove(HttpHeaderNames.CONNECTION); - } - } - } - - /** - * Returns the length of the content. Please note that this value is - * not retrieved from {@link HttpContent#content()} but from the - * {@code "Content-Length"} header, and thus they are independent from each - * other. - * - * @return the content length - * - * @throws NumberFormatException - * if the message does not have the {@code "Content-Length"} header - * or its value is not a number - */ - public static long getContentLength(HttpMessage message) { - String value = message.headers().get(HttpHeaderNames.CONTENT_LENGTH); - if (value != null) { - return Long.parseLong(value); - } - - // We know the content length if it's a Web Socket message even if - // Content-Length header is missing. - long webSocketContentLength = getWebSocketContentLength(message); - if (webSocketContentLength >= 0) { - return webSocketContentLength; - } - - // Otherwise we don't. - throw new NumberFormatException("header not found: " + HttpHeaderNames.CONTENT_LENGTH); - } - - /** - * Returns the length of the content or the specified default value if the message does not have the {@code - * "Content-Length" header}. Please note that this value is not retrieved from {@link HttpContent#content()} but - * from the {@code "Content-Length"} header, and thus they are independent from each other. - * - * @param message the message - * @param defaultValue the default value - * @return the content length or the specified default value - * @throws NumberFormatException if the {@code "Content-Length"} header does not parse as a long - */ - public static long getContentLength(HttpMessage message, long defaultValue) { - String value = message.headers().get(HttpHeaderNames.CONTENT_LENGTH); - if (value != null) { - return Long.parseLong(value); - } - - // We know the content length if it's a Web Socket message even if - // Content-Length header is missing. - long webSocketContentLength = getWebSocketContentLength(message); - if (webSocketContentLength >= 0) { - return webSocketContentLength; - } - - // Otherwise we don't. - return 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. - */ - public static int getContentLength(HttpMessage message, int defaultValue) { - return (int) Math.min(Integer.MAX_VALUE, getContentLength(message, (long) defaultValue)); - } - - /** - * Returns the content length of the specified web socket message. If the - * specified message is not a web socket message, {@code -1} is returned. - */ - private static int getWebSocketContentLength(HttpMessage message) { - // WebSocket messages have constant content-lengths. - HttpHeaders h = message.headers(); - if (message instanceof HttpRequest) { - HttpRequest req = (HttpRequest) message; - if (HttpMethod.GET.equals(req.method()) && - h.contains(HttpHeaderNames.SEC_WEBSOCKET_KEY1) && - h.contains(HttpHeaderNames.SEC_WEBSOCKET_KEY2)) { - return 8; - } - } else if (message instanceof HttpResponse) { - HttpResponse res = (HttpResponse) message; - if (res.status().code() == 101 && - h.contains(HttpHeaderNames.SEC_WEBSOCKET_ORIGIN) && - h.contains(HttpHeaderNames.SEC_WEBSOCKET_LOCATION)) { - return 16; - } - } - - // Not a web socket message - return -1; - } - - /** - * Sets the {@code "Content-Length"} header. - */ - public static void setContentLength(HttpMessage message, long length) { - message.headers().set(HttpHeaderNames.CONTENT_LENGTH, length); - } - - public static boolean isContentLengthSet(HttpMessage m) { - return m.headers().contains(HttpHeaderNames.CONTENT_LENGTH); - } - - /** - * Returns {@code true} if and only if the specified message contains an expect header and the only expectation - * present is the 100-continue expectation. Note that this method returns {@code false} if the expect header is - * not valid for the message (e.g., the message is a response, or the version on the message is HTTP/1.0). - * - * @param message the message - * @return {@code true} if and only if the expectation 100-continue is present and it is the only expectation - * present - */ - public static boolean is100ContinueExpected(HttpMessage message) { - 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); - } - - /** - * Returns {@code true} if the specified message contains an expect header specifying an expectation that is not - * supported. Note that this method returns {@code false} if the expect header is not valid for the message - * (e.g., the message is a response, or the version on the message is HTTP/1.0). - * - * @param message the message - * @return {@code true} if and only if an expectation is present that is not supported - */ - static boolean isUnsupportedExpectation(HttpMessage message) { - if (!isExpectHeaderValid(message)) { - return false; - } - - final String expectValue = message.headers().get(HttpHeaderNames.EXPECT); - return expectValue != null && !HttpHeaderValues.CONTINUE.toString().equalsIgnoreCase(expectValue); - } - - private static boolean isExpectHeaderValid(final HttpMessage message) { - /* - * Expect: 100-continue is for requests only and it works only on HTTP/1.1 or later. Note further that RFC 7231 - * section 5.1.1 says "A server that receives a 100-continue expectation in an HTTP/1.0 request MUST ignore - * that expectation." - */ - return message instanceof HttpRequest && - message.protocolVersion().compareTo(HttpVersion.HTTP_1_1) >= 0; - } - - /** - * Sets or removes the {@code "Expect: 100-continue"} header to / from the - * specified message. If {@code expected} is {@code true}, - * the {@code "Expect: 100-continue"} header is set and all other previous - * {@code "Expect"} headers are removed. Otherwise, all {@code "Expect"} - * headers are removed completely. - */ - public static void set100ContinueExpected(HttpMessage message, boolean expected) { - if (expected) { - message.headers().set(HttpHeaderNames.EXPECT, HttpHeaderValues.CONTINUE); - } else { - message.headers().remove(HttpHeaderNames.EXPECT); - } - } - - /** - * Checks to see if the transfer encoding in a specified {@link HttpMessage} is chunked - * - * @param message The message to check - * @return True if transfer encoding is chunked, otherwise false - */ - public static boolean isTransferEncodingChunked(HttpMessage message) { - return message.headers().containsValue(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED, true); - } - - /** - * 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. - */ - public static void setTransferEncodingChunked(HttpMessage m, boolean chunked) { - if (chunked) { - m.headers().set(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED); - m.headers().remove(HttpHeaderNames.CONTENT_LENGTH); - } else { - List encodings = m.headers().getAll(HttpHeaderNames.TRANSFER_ENCODING); - if (encodings.isEmpty()) { - return; - } - List values = new ArrayList<>(encodings); - Iterator valuesIt = values.iterator(); - while (valuesIt.hasNext()) { - CharSequence value = valuesIt.next(); - if (HttpHeaderValues.CHUNKED.contentEqualsIgnoreCase(value)) { - valuesIt.remove(); - } - } - if (values.isEmpty()) { - m.headers().remove(HttpHeaderNames.TRANSFER_ENCODING); - } else { - m.headers().set(HttpHeaderNames.TRANSFER_ENCODING, values); - } - } - } - - /** - * Fetch charset from message's Content-Type header. - * - * @param message entity to fetch Content-Type header from - * @return the charset from message's Content-Type header or {@link CharsetUtil#ISO_8859_1} - * if charset is not presented or unparsable - */ - public static Charset getCharset(HttpMessage message) { - return getCharset(message, CharsetUtil.ISO_8859_1); - } - - /** - * Fetch charset from Content-Type header value. - * - * @param contentTypeValue Content-Type header value to parse - * @return the charset from message's Content-Type header or {@link CharsetUtil#ISO_8859_1} - * if charset is not presented or unparsable - */ - public static Charset getCharset(CharSequence contentTypeValue) { - if (contentTypeValue != null) { - return getCharset(contentTypeValue, CharsetUtil.ISO_8859_1); - } else { - return CharsetUtil.ISO_8859_1; - } - } - - /** - * Fetch charset from message's Content-Type header. - * - * @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 - */ - public static Charset getCharset(HttpMessage message, Charset defaultCharset) { - CharSequence contentTypeValue = message.headers().get(HttpHeaderNames.CONTENT_TYPE); - if (contentTypeValue != null) { - return getCharset(contentTypeValue, defaultCharset); - } else { - return 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 - * @return the charset from message's Content-Type header or {@code defaultCharset} - * if charset is not presented or unparsable - */ - public static Charset getCharset(CharSequence contentTypeValue, Charset defaultCharset) { - if (contentTypeValue != null) { - CharSequence charsetRaw = getCharsetAsSequence(contentTypeValue); - if (charsetRaw != null) { - if (charsetRaw.length() > 2) { // at least contains 2 quotes(") - if (charsetRaw.charAt(0) == '"' && charsetRaw.charAt(charsetRaw.length() - 1) == '"') { - charsetRaw = charsetRaw.subSequence(1, charsetRaw.length() - 1); - } - } - try { - return Charset.forName(charsetRaw.toString()); - } catch (UnsupportedCharsetException | IllegalCharsetNameException ignored) { - // just return the default charset - return defaultCharset; - } - } else { - return defaultCharset; - } - } else { - return defaultCharset; - } - } - - /** - * Fetch charset from message's Content-Type header as a char sequence. - * - * A lot of sites/possibly clients have charset="CHARSET", for example charset="utf-8". Or "utf8" instead of "utf-8" - * This is not according to standard, but this method provide an ability to catch desired mistakes manually in code - * - * @param message entity to fetch Content-Type header from - * @return the {@code CharSequence} with charset from message's Content-Type header - * or {@code null} if charset is not presented - * @deprecated use {@link #getCharsetAsSequence(HttpMessage)} - */ - @Deprecated - public static CharSequence getCharsetAsString(HttpMessage message) { - return getCharsetAsSequence(message); - } - - /** - * Fetch charset from message's Content-Type header as a char sequence. - * - * A lot of sites/possibly clients have charset="CHARSET", for example charset="utf-8". Or "utf8" instead of "utf-8" - * This is not according to standard, but this method provide an ability to catch desired mistakes manually in code - * - * @return the {@code CharSequence} with charset from message's Content-Type header - * or {@code null} if charset is not presented - */ - public static CharSequence getCharsetAsSequence(HttpMessage message) { - CharSequence contentTypeValue = message.headers().get(HttpHeaderNames.CONTENT_TYPE); - if (contentTypeValue != null) { - return getCharsetAsSequence(contentTypeValue); - } else { - return null; - } - } - - /** - * Fetch charset from Content-Type header value as a char sequence. - * - * A lot of sites/possibly clients have charset="CHARSET", for example charset="utf-8". Or "utf8" instead of "utf-8" - * This is not according to standard, but this method provide an ability to catch desired mistakes manually in code - * - * @param contentTypeValue Content-Type header value to parse - * @return the {@code CharSequence} with charset from message's Content-Type header - * or {@code null} if charset is not presented - * @throws NullPointerException in case if {@code contentTypeValue == null} - */ - public static CharSequence getCharsetAsSequence(CharSequence contentTypeValue) { - requireNonNull(contentTypeValue, "contentTypeValue"); - - int indexOfCharset = AsciiString.indexOfIgnoreCaseAscii(contentTypeValue, CHARSET_EQUALS, 0); - 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; - } - - /** - * Fetch MIME type part from message's Content-Type header as a char sequence. - * - * @param message entity to fetch Content-Type header from - * @return the MIME type as a {@code CharSequence} from message's Content-Type header - * or {@code null} if content-type header or MIME type part of this header are not presented - *

- * "content-type: text/html; charset=utf-8" - "text/html" will be returned
- * "content-type: text/html" - "text/html" will be returned
- * "content-type: " or no header - {@code null} we be returned - */ - public static CharSequence getMimeType(HttpMessage message) { - CharSequence contentTypeValue = message.headers().get(HttpHeaderNames.CONTENT_TYPE); - if (contentTypeValue != null) { - return getMimeType(contentTypeValue); - } else { - return null; - } - } - - /** - * Fetch MIME type part from Content-Type header value as a char sequence. - * - * @param contentTypeValue Content-Type header value to parse - * @return the MIME type as a {@code CharSequence} from message's Content-Type header - * or {@code null} if content-type header or MIME type part of this header are not presented - *

- * "content-type: text/html; charset=utf-8" - "text/html" will be returned
- * "content-type: text/html" - "text/html" will be returned
- * "content-type: empty header - {@code null} we be returned - * @throws NullPointerException in case if {@code contentTypeValue == null} - */ - public static CharSequence getMimeType(CharSequence contentTypeValue) { - requireNonNull(contentTypeValue, "contentTypeValue"); - - int indexOfSemicolon = AsciiString.indexOfIgnoreCaseAscii(contentTypeValue, SEMICOLON, 0); - if (indexOfSemicolon != AsciiString.INDEX_NOT_FOUND) { - return contentTypeValue.subSequence(0, indexOfSemicolon); - } else { - return contentTypeValue.length() > 0 ? contentTypeValue : null; - } - } - - /** - * Formats the host string of an address so it can be used for computing an HTTP component - * such as a URL or a Host header - * - * @param addr the address - * @return the formatted String - */ - public static String formatHostnameForHttp(InetSocketAddress addr) { - String hostString = NetUtil.getHostname(addr); - if (NetUtil.isValidIpV6Address(hostString)) { - if (!addr.isUnresolved()) { - hostString = NetUtil.toAddressString(addr.getAddress()); - } - return '[' + hostString + ']'; - } - return hostString; - } - - /** - * Validates, and optionally extracts the content length from headers. This method is not intended for - * general use, but is here to be shared between HTTP/1 and HTTP/2 parsing. - * - * @param contentLengthFields the content-length header fields. - * @param isHttp10OrEarlier {@code true} if we are handling HTTP/1.0 or earlier - * @param allowDuplicateContentLengths {@code true} if multiple, identical-value content lengths should be allowed. - * @return the normalized content length from the headers or {@code -1} if the fields were empty. - * @throws IllegalArgumentException if the content-length fields are not valid - */ - @UnstableApi - public static long normalizeAndGetContentLength( - List contentLengthFields, boolean isHttp10OrEarlier, - boolean allowDuplicateContentLengths) { - if (contentLengthFields.isEmpty()) { - return -1; - } - - // 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. - String firstField = contentLengthFields.get(0).toString(); - boolean multipleContentLengths = - contentLengthFields.size() > 1 || firstField.indexOf(COMMA) >= 0; - - if (multipleContentLengths && !isHttp10OrEarlier) { - if (allowDuplicateContentLengths) { - // Find and enforce that all Content-Length values are the same - String firstValue = null; - for (CharSequence field : contentLengthFields) { - String[] tokens = field.toString().split(COMMA_STRING, -1); - for (String token : tokens) { - String trimmed = token.trim(); - if (firstValue == null) { - firstValue = trimmed; - } else if (!trimmed.equals(firstValue)) { - throw new IllegalArgumentException( - "Multiple Content-Length values found: " + contentLengthFields); - } - } - } - // Replace the duplicated field-values with a single valid Content-Length field - firstField = firstValue; - } else { - // Reject the message as invalid - throw new IllegalArgumentException( - "Multiple Content-Length values found: " + contentLengthFields); - } - } - // Ensure we not allow sign as part of the content-length: - // See https://github.com/squid-cache/squid/security/advisories/GHSA-qf3v-rc95-96j5 - if (firstField.isEmpty() || !Character.isDigit(firstField.charAt(0))) { - // Reject the message as invalid - throw new IllegalArgumentException( - "Content-Length value is not a number: " + firstField); - } - try { - final long value = Long.parseLong(firstField); - return checkPositiveOrZero(value, "Content-Length value"); - } catch (NumberFormatException e) { - // Reject the message as invalid - throw new IllegalArgumentException( - "Content-Length value is not a number: " + firstField, e); - } - } -} 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 deleted file mode 100644 index 3bf52b1250..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpVersion.java +++ /dev/null @@ -1,256 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; -import static io.netty.util.internal.ObjectUtil.checkNonEmptyAfterTrim; -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.util.CharsetUtil; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * The version of HTTP or its derived protocols, such as - * RTSP and - * ICAP. - */ -public class HttpVersion implements Comparable { - - private static final Pattern VERSION_PATTERN = - Pattern.compile("(\\S+)/(\\d+)\\.(\\d+)"); - - private static final String HTTP_1_0_STRING = "HTTP/1.0"; - private static final String HTTP_1_1_STRING = "HTTP/1.1"; - - /** - * HTTP/1.0 - */ - public static final HttpVersion HTTP_1_0 = new HttpVersion("HTTP", 1, 0, false, true); - - /** - * HTTP/1.1 - */ - public static final HttpVersion HTTP_1_1 = new HttpVersion("HTTP", 1, 1, true, true); - - /** - * Returns an existing or new {@link HttpVersion} instance which matches to - * the specified protocol version string. If the specified {@code text} is - * equal to {@code "HTTP/1.0"}, {@link #HTTP_1_0} will be returned. If the - * specified {@code text} is equal to {@code "HTTP/1.1"}, {@link #HTTP_1_1} - * will be returned. Otherwise, a new {@link HttpVersion} instance will be - * returned. - */ - public static HttpVersion valueOf(String text) { - requireNonNull(text, "text"); - - text = text.trim(); - - if (text.isEmpty()) { - throw new IllegalArgumentException("text is empty (possibly HTTP/0.9)"); - } - - // Try to match without convert to uppercase first as this is what 99% of all clients - // will send anyway. Also there is a change to the RFC to make it clear that it is - // expected to be case-sensitive - // - // See: - // * https://trac.tools.ietf.org/wg/httpbis/trac/ticket/1 - // * https://trac.tools.ietf.org/wg/httpbis/trac/wiki - // - HttpVersion version = version0(text); - if (version == null) { - version = new HttpVersion(text, true); - } - return version; - } - - private static HttpVersion version0(String text) { - if (HTTP_1_1_STRING.equals(text)) { - return HTTP_1_1; - } - if (HTTP_1_0_STRING.equals(text)) { - return HTTP_1_0; - } - return null; - } - - private final String protocolName; - private final int majorVersion; - private final int minorVersion; - private final String text; - private final boolean keepAliveDefault; - private final byte[] bytes; - - /** - * Creates a new HTTP version with the specified version string. You will - * not need to create a new instance unless you are implementing a protocol - * derived from HTTP, such as - * RTSP and - * ICAP. - * - * @param keepAliveDefault - * {@code true} if and only if the connection is kept alive unless - * the {@code "Connection"} header is set to {@code "close"} explicitly. - */ - public HttpVersion(String text, boolean keepAliveDefault) { - text = checkNonEmptyAfterTrim(text, "text").toUpperCase(); - - Matcher m = VERSION_PATTERN.matcher(text); - if (!m.matches()) { - throw new IllegalArgumentException("invalid version format: " + text); - } - - protocolName = m.group(1); - majorVersion = Integer.parseInt(m.group(2)); - minorVersion = Integer.parseInt(m.group(3)); - this.text = protocolName + '/' + majorVersion + '.' + minorVersion; - this.keepAliveDefault = keepAliveDefault; - bytes = null; - } - - /** - * Creates a new HTTP version with the specified protocol name and version - * numbers. You will not need to create a new instance unless you are - * implementing a protocol derived from HTTP, such as - * RTSP and - * ICAP - * - * @param keepAliveDefault - * {@code true} if and only if the connection is kept alive unless - * the {@code "Connection"} header is set to {@code "close"} explicitly. - */ - public HttpVersion( - String protocolName, int majorVersion, int minorVersion, - boolean keepAliveDefault) { - this(protocolName, majorVersion, minorVersion, keepAliveDefault, false); - } - - private HttpVersion( - String protocolName, int majorVersion, int minorVersion, - boolean keepAliveDefault, boolean bytes) { - protocolName = checkNonEmptyAfterTrim(protocolName, "protocolName").toUpperCase(); - - for (int i = 0; i < protocolName.length(); i ++) { - if (Character.isISOControl(protocolName.charAt(i)) || - Character.isWhitespace(protocolName.charAt(i))) { - throw new IllegalArgumentException("invalid character in protocolName"); - } - } - - checkPositiveOrZero(majorVersion, "majorVersion"); - checkPositiveOrZero(minorVersion, "minorVersion"); - - this.protocolName = protocolName; - this.majorVersion = majorVersion; - this.minorVersion = minorVersion; - text = protocolName + '/' + majorVersion + '.' + minorVersion; - this.keepAliveDefault = keepAliveDefault; - - if (bytes) { - this.bytes = text.getBytes(CharsetUtil.US_ASCII); - } else { - this.bytes = null; - } - } - - /** - * Returns the name of the protocol such as {@code "HTTP"} in {@code "HTTP/1.0"}. - */ - public String protocolName() { - return protocolName; - } - - /** - * Returns the name of the protocol such as {@code 1} in {@code "HTTP/1.0"}. - */ - public int majorVersion() { - return majorVersion; - } - - /** - * Returns the name of the protocol such as {@code 0} in {@code "HTTP/1.0"}. - */ - public int minorVersion() { - return minorVersion; - } - - /** - * Returns the full protocol version text such as {@code "HTTP/1.0"}. - */ - public String text() { - return text; - } - - /** - * Returns {@code true} if and only if the connection is kept alive unless - * the {@code "Connection"} header is set to {@code "close"} explicitly. - */ - public boolean isKeepAliveDefault() { - return keepAliveDefault; - } - - /** - * Returns the full protocol version text such as {@code "HTTP/1.0"}. - */ - @Override - public String toString() { - return text(); - } - - @Override - public int hashCode() { - return (protocolName().hashCode() * 31 + majorVersion()) * 31 + - minorVersion(); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof HttpVersion)) { - return false; - } - - HttpVersion that = (HttpVersion) o; - return minorVersion() == that.minorVersion() && - majorVersion() == that.majorVersion() && - protocolName().equals(that.protocolName()); - } - - @Override - public int compareTo(HttpVersion o) { - int v = protocolName().compareTo(o.protocolName()); - if (v != 0) { - return v; - } - - v = majorVersion() - o.majorVersion(); - if (v != 0) { - return v; - } - - return minorVersion() - o.minorVersion(); - } - - void encode(ByteBuf buf) { - if (bytes == null) { - buf.writeCharSequence(text, CharsetUtil.US_ASCII); - } else { - buf.writeBytes(bytes); - } - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/LastHttpContent.java b/codec-http/src/main/java/io/netty/handler/codec/http/LastHttpContent.java deleted file mode 100644 index a52a3830f9..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/LastHttpContent.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.handler.codec.DecoderResult; - -/** - * The last {@link HttpContent} which has trailing headers. - */ -public interface LastHttpContent extends HttpContent { - - /** - * The 'end of content' marker in chunked encoding. - */ - LastHttpContent EMPTY_LAST_CONTENT = new LastHttpContent() { - - @Override - public ByteBuf content() { - return Unpooled.EMPTY_BUFFER; - } - - @Override - public LastHttpContent copy() { - return EMPTY_LAST_CONTENT; - } - - @Override - public LastHttpContent duplicate() { - return this; - } - - @Override - public LastHttpContent replace(ByteBuf content) { - return new DefaultLastHttpContent(content); - } - - @Override - public LastHttpContent retainedDuplicate() { - return this; - } - - @Override - public HttpHeaders trailingHeaders() { - return EmptyHttpHeaders.INSTANCE; - } - - @Override - public DecoderResult decoderResult() { - return DecoderResult.SUCCESS; - } - - @Override - @Deprecated - public DecoderResult getDecoderResult() { - return decoderResult(); - } - - @Override - public void setDecoderResult(DecoderResult result) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public int refCnt() { - return 1; - } - - @Override - public LastHttpContent retain() { - return this; - } - - @Override - public LastHttpContent retain(int increment) { - return this; - } - - @Override - public LastHttpContent touch() { - return this; - } - - @Override - public LastHttpContent touch(Object hint) { - return this; - } - - @Override - public boolean release() { - return false; - } - - @Override - public boolean release(int decrement) { - return false; - } - - @Override - public String toString() { - return "EmptyLastHttpContent"; - } - }; - - HttpHeaders trailingHeaders(); - - @Override - LastHttpContent copy(); - - @Override - LastHttpContent duplicate(); - - @Override - LastHttpContent retainedDuplicate(); - - @Override - LastHttpContent replace(ByteBuf content); - - @Override - LastHttpContent retain(int increment); - - @Override - LastHttpContent retain(); - - @Override - LastHttpContent touch(); - - @Override - LastHttpContent touch(Object hint); -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/QueryStringDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/QueryStringDecoder.java deleted file mode 100644 index 8e319c7448..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/QueryStringDecoder.java +++ /dev/null @@ -1,393 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.util.CharsetUtil; -import io.netty.util.internal.PlatformDependent; - -import java.net.URI; -import java.net.URLDecoder; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import static io.netty.util.internal.ObjectUtil.checkPositive; -import static io.netty.util.internal.StringUtil.EMPTY_STRING; -import static io.netty.util.internal.StringUtil.SPACE; -import static io.netty.util.internal.StringUtil.decodeHexByte; -import static java.util.Objects.requireNonNull; - -/** - * Splits an HTTP query string into a path string and key-value parameter pairs. - * This decoder is for one time use only. Create a new instance for each URI: - *

- * {@link QueryStringDecoder} decoder = new {@link QueryStringDecoder}("/hello?recipient=world&x=1;y=2");
- * assert decoder.path().equals("/hello");
- * assert decoder.parameters().get("recipient").get(0).equals("world");
- * assert decoder.parameters().get("x").get(0).equals("1");
- * assert decoder.parameters().get("y").get(0).equals("2");
- * 
- * - * This decoder can also decode the content of an HTTP POST request whose - * content type is application/x-www-form-urlencoded: - *
- * {@link QueryStringDecoder} decoder = new {@link QueryStringDecoder}("recipient=world&x=1;y=2", false);
- * ...
- * 
- * - *

HashDOS vulnerability fix

- * - * As a workaround to the HashDOS vulnerability, the decoder - * limits the maximum number of decoded key-value parameter pairs, up to {@literal 1024} by - * default, and you can configure it when you construct the decoder by passing an additional - * integer parameter. - * - * @see QueryStringEncoder - */ -public class QueryStringDecoder { - - private static final int DEFAULT_MAX_PARAMS = 1024; - - private final Charset charset; - private final String uri; - private final int maxParams; - private final boolean semicolonIsNormalChar; - private int pathEndIdx; - private String path; - private Map> params; - - /** - * Creates a new decoder that decodes the specified URI. The decoder will - * assume that the query string is encoded in UTF-8. - */ - public QueryStringDecoder(String uri) { - this(uri, HttpConstants.DEFAULT_CHARSET); - } - - /** - * Creates a new decoder that decodes the specified URI encoded in the - * specified charset. - */ - public QueryStringDecoder(String uri, boolean hasPath) { - this(uri, HttpConstants.DEFAULT_CHARSET, hasPath); - } - - /** - * Creates a new decoder that decodes the specified URI encoded in the - * specified charset. - */ - public QueryStringDecoder(String uri, Charset charset) { - this(uri, charset, true); - } - - /** - * Creates a new decoder that decodes the specified URI encoded in the - * specified charset. - */ - public QueryStringDecoder(String uri, Charset charset, boolean hasPath) { - this(uri, charset, hasPath, DEFAULT_MAX_PARAMS); - } - - /** - * Creates a new decoder that decodes the specified URI encoded in the - * specified charset. - */ - public QueryStringDecoder(String uri, Charset charset, boolean hasPath, int maxParams) { - this(uri, charset, hasPath, maxParams, false); - } - - /** - * Creates a new decoder that decodes the specified URI encoded in the - * specified charset. - */ - public QueryStringDecoder(String uri, Charset charset, boolean hasPath, - int maxParams, boolean semicolonIsNormalChar) { - this.uri = requireNonNull(uri, "uri"); - this.charset = requireNonNull(charset, "charset"); - this.maxParams = checkPositive(maxParams, "maxParams"); - this.semicolonIsNormalChar = semicolonIsNormalChar; - - // `-1` means that path end index will be initialized lazily - pathEndIdx = hasPath ? -1 : 0; - } - - /** - * Creates a new decoder that decodes the specified URI. The decoder will - * assume that the query string is encoded in UTF-8. - */ - public QueryStringDecoder(URI uri) { - this(uri, HttpConstants.DEFAULT_CHARSET); - } - - /** - * Creates a new decoder that decodes the specified URI encoded in the - * specified charset. - */ - public QueryStringDecoder(URI uri, Charset charset) { - this(uri, charset, DEFAULT_MAX_PARAMS); - } - - /** - * Creates a new decoder that decodes the specified URI encoded in the - * specified charset. - */ - public QueryStringDecoder(URI uri, Charset charset, int maxParams) { - this(uri, charset, maxParams, false); - } - - /** - * Creates a new decoder that decodes the specified URI encoded in the - * specified charset. - */ - public QueryStringDecoder(URI uri, Charset charset, int maxParams, boolean semicolonIsNormalChar) { - String rawPath = uri.getRawPath(); - if (rawPath == null) { - rawPath = EMPTY_STRING; - } - String rawQuery = uri.getRawQuery(); - // Also take care of cut of things like "http://localhost" - this.uri = rawQuery == null? rawPath : rawPath + '?' + rawQuery; - this.charset = requireNonNull(charset, "charset"); - this.maxParams = checkPositive(maxParams, "maxParams"); - this.semicolonIsNormalChar = semicolonIsNormalChar; - pathEndIdx = rawPath.length(); - } - - @Override - public String toString() { - return uri(); - } - - /** - * Returns the uri used to initialize this {@link QueryStringDecoder}. - */ - public String uri() { - return uri; - } - - /** - * Returns the decoded path string of the URI. - */ - public String path() { - if (path == null) { - path = decodeComponent(uri, 0, pathEndIdx(), charset, true); - } - return path; - } - - /** - * Returns the decoded key-value parameter pairs of the URI. - */ - public Map> parameters() { - if (params == null) { - params = decodeParams(uri, pathEndIdx(), charset, maxParams, semicolonIsNormalChar); - } - return params; - } - - /** - * Returns the raw path string of the URI. - */ - public String rawPath() { - return uri.substring(0, pathEndIdx()); - } - - /** - * Returns raw query string of the URI. - */ - public String rawQuery() { - int start = pathEndIdx() + 1; - return start < uri.length() ? uri.substring(start) : EMPTY_STRING; - } - - private int pathEndIdx() { - if (pathEndIdx == -1) { - pathEndIdx = findPathEndIndex(uri); - } - return pathEndIdx; - } - - private static Map> decodeParams(String s, int from, Charset charset, int paramsLimit, - boolean semicolonIsNormalChar) { - int len = s.length(); - if (from >= len) { - return Collections.emptyMap(); - } - if (s.charAt(from) == '?') { - from++; - } - Map> params = new LinkedHashMap<>(); - int nameStart = from; - int valueStart = -1; - int i; - loop: - for (i = from; i < len; i++) { - switch (s.charAt(i)) { - case '=': - if (nameStart == i) { - nameStart = i + 1; - } else if (valueStart < nameStart) { - valueStart = i + 1; - } - break; - case ';': - if (semicolonIsNormalChar) { - continue; - } - // fall-through - case '&': - if (addParam(s, nameStart, valueStart, i, params, charset)) { - paramsLimit--; - if (paramsLimit == 0) { - return params; - } - } - nameStart = i + 1; - break; - case '#': - break loop; - default: - // continue - } - } - addParam(s, nameStart, valueStart, i, params, charset); - return params; - } - - private static boolean addParam(String s, int nameStart, int valueStart, int valueEnd, - Map> params, Charset charset) { - if (nameStart >= valueEnd) { - return false; - } - if (valueStart <= nameStart) { - valueStart = valueEnd + 1; - } - String name = decodeComponent(s, nameStart, valueStart - 1, charset, false); - String value = decodeComponent(s, valueStart, valueEnd, charset, false); - List values = params.get(name); - if (values == null) { - values = new ArrayList<>(1); // Often there's only 1 value. - params.put(name, values); - } - values.add(value); - return true; - } - - /** - * Decodes a bit of a URL encoded by a browser. - *

- * This is equivalent to calling {@link #decodeComponent(String, Charset)} - * with the UTF-8 charset (recommended to comply with RFC 3986, Section 2). - * @param s The string to decode (can be empty). - * @return The decoded string, or {@code s} if there's nothing to decode. - * If the string to decode is {@code null}, returns an empty string. - * @throws IllegalArgumentException if the string contains a malformed - * escape sequence. - */ - public static String decodeComponent(final String s) { - return decodeComponent(s, HttpConstants.DEFAULT_CHARSET); - } - - /** - * Decodes a bit of a URL encoded by a browser. - *

- * The string is expected to be encoded as per RFC 3986, Section 2. - * This is the encoding used by JavaScript functions {@code encodeURI} - * and {@code encodeURIComponent}, but not {@code escape}. For example - * in this encoding, é (in Unicode {@code U+00E9} or in UTF-8 - * {@code 0xC3 0xA9}) is encoded as {@code %C3%A9} or {@code %c3%a9}. - *

- * This is essentially equivalent to calling - * {@link URLDecoder#decode(String, String)} - * except that it's over 2x faster and generates less garbage for the GC. - * Actually this function doesn't allocate any memory if there's nothing - * to decode, the argument itself is returned. - * @param s The string to decode (can be empty). - * @param charset The charset to use to decode the string (should really - * be {@link CharsetUtil#UTF_8}. - * @return The decoded string, or {@code s} if there's nothing to decode. - * If the string to decode is {@code null}, returns an empty string. - * @throws IllegalArgumentException if the string contains a malformed - * escape sequence. - */ - public static String decodeComponent(final String s, final Charset charset) { - if (s == null) { - return EMPTY_STRING; - } - return decodeComponent(s, 0, s.length(), charset, false); - } - - private static String decodeComponent(String s, int from, int toExcluded, Charset charset, boolean isPath) { - int len = toExcluded - from; - if (len <= 0) { - return EMPTY_STRING; - } - int firstEscaped = -1; - for (int i = from; i < toExcluded; i++) { - char c = s.charAt(i); - if (c == '%' || c == '+' && !isPath) { - firstEscaped = i; - break; - } - } - if (firstEscaped == -1) { - return s.substring(from, toExcluded); - } - - // Each encoded byte takes 3 characters (e.g. "%20") - int decodedCapacity = (toExcluded - firstEscaped) / 3; - byte[] buf = PlatformDependent.allocateUninitializedArray(decodedCapacity); - int bufIdx; - - StringBuilder strBuf = new StringBuilder(len); - strBuf.append(s, from, firstEscaped); - - for (int i = firstEscaped; i < toExcluded; i++) { - char c = s.charAt(i); - if (c != '%') { - strBuf.append(c != '+' || isPath? c : SPACE); - continue; - } - - bufIdx = 0; - do { - if (i + 3 > toExcluded) { - throw new IllegalArgumentException("unterminated escape sequence at index " + i + " of: " + s); - } - buf[bufIdx++] = decodeHexByte(s, i + 1); - i += 3; - } while (i < toExcluded && s.charAt(i) == '%'); - i--; - - strBuf.append(new String(buf, 0, bufIdx, charset)); - } - return strBuf.toString(); - } - - private static int findPathEndIndex(String uri) { - int len = uri.length(); - for (int i = 0; i < len; i++) { - char c = uri.charAt(i); - if (c == '?' || c == '#') { - return i; - } - } - return len; - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/QueryStringEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/QueryStringEncoder.java deleted file mode 100644 index f7bb2de27e..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/QueryStringEncoder.java +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBufUtil; -import io.netty.util.CharsetUtil; -import io.netty.util.internal.StringUtil; - -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URLEncoder; -import java.nio.charset.Charset; -import java.util.Objects; - -/** - * Creates a URL-encoded URI from a path string and key-value parameter pairs. - * This encoder is for one time use only. Create a new instance for each URI. - * - *

- * {@link QueryStringEncoder} encoder = new {@link QueryStringEncoder}("/hello");
- * encoder.addParam("recipient", "world");
- * assert encoder.toString().equals("/hello?recipient=world");
- * 
- * - * @see QueryStringDecoder - */ -public class QueryStringEncoder { - - private final Charset charset; - private final StringBuilder uriBuilder; - private boolean hasParams; - private static final byte WRITE_UTF_UNKNOWN = (byte) '?'; - private static final char[] CHAR_MAP = "0123456789ABCDEF".toCharArray(); - - /** - * Creates a new encoder that encodes a URI that starts with the specified - * path string. The encoder will encode the URI in UTF-8. - */ - public QueryStringEncoder(String uri) { - this(uri, HttpConstants.DEFAULT_CHARSET); - } - - /** - * Creates a new encoder that encodes a URI that starts with the specified - * path string in the specified charset. - */ - public QueryStringEncoder(String uri, Charset charset) { - Objects.requireNonNull(charset, "charset"); - uriBuilder = new StringBuilder(uri); - this.charset = CharsetUtil.UTF_8.equals(charset) ? null : charset; - } - - /** - * Adds a parameter with the specified name and value to this encoder. - */ - public void addParam(String name, String value) { - requireNonNull(name, "name"); - if (hasParams) { - uriBuilder.append('&'); - } else { - uriBuilder.append('?'); - hasParams = true; - } - - encodeComponent(name); - if (value != null) { - uriBuilder.append('='); - encodeComponent(value); - } - } - - private void encodeComponent(CharSequence s) { - if (charset == null) { - encodeUtf8Component(s); - } else { - encodeNonUtf8Component(s); - } - } - - /** - * Returns the URL-encoded URI object which was created from the path string - * specified in the constructor and the parameters added by - * {@link #addParam(String, String)} method. - */ - public URI toUri() throws URISyntaxException { - return new URI(toString()); - } - - /** - * Returns the URL-encoded URI which was created from the path string - * specified in the constructor and the parameters added by - * {@link #addParam(String, String)} method. - */ - @Override - public String toString() { - return uriBuilder.toString(); - } - - /** - * Encode the String as per RFC 3986, Section 2. - *

- * There is a little different between the JDK's encode method : {@link URLEncoder#encode(String, String)}. - * The JDK's encoder encode the space to {@code +} and this method directly encode the blank to {@code %20} - * beyond that , this method reuse the {@link #uriBuilder} in this class rather then create a new one, - * thus generates less garbage for the GC. - * - * @param s The String to encode - */ - private void encodeNonUtf8Component(CharSequence s) { - //Don't allocate memory until needed - char[] buf = null; - - for (int i = 0, len = s.length(); i < len;) { - char c = s.charAt(i); - if (dontNeedEncoding(c)) { - uriBuilder.append(c); - i++; - } else { - int index = 0; - if (buf == null) { - buf = new char[s.length() - i]; - } - - do { - buf[index] = c; - index++; - i++; - } while (i < s.length() && !dontNeedEncoding(c = s.charAt(i))); - - byte[] bytes = new String(buf, 0, index).getBytes(charset); - - for (byte b : bytes) { - appendEncoded(b); - } - } - } - } - - /** - * @see ByteBufUtil#writeUtf8(io.netty.buffer.ByteBuf, CharSequence, int, int) - */ - private void encodeUtf8Component(CharSequence s) { - for (int i = 0, len = s.length(); i < len; i++) { - char c = s.charAt(i); - if (!dontNeedEncoding(c)) { - encodeUtf8Component(s, i, len); - return; - } - } - uriBuilder.append(s); - } - - private void encodeUtf8Component(CharSequence s, int encodingStart, int len) { - if (encodingStart > 0) { - // Append non-encoded characters directly first. - uriBuilder.append(s, 0, encodingStart); - } - encodeUtf8ComponentSlow(s, encodingStart, len); - } - - private void encodeUtf8ComponentSlow(CharSequence s, int start, int len) { - for (int i = start; i < len; i++) { - char c = s.charAt(i); - if (c < 0x80) { - if (dontNeedEncoding(c)) { - uriBuilder.append(c); - } else { - appendEncoded(c); - } - } else if (c < 0x800) { - appendEncoded(0xc0 | (c >> 6)); - appendEncoded(0x80 | (c & 0x3f)); - } else if (StringUtil.isSurrogate(c)) { - if (!Character.isHighSurrogate(c)) { - appendEncoded(WRITE_UTF_UNKNOWN); - continue; - } - // Surrogate Pair consumes 2 characters. - if (++i == s.length()) { - appendEncoded(WRITE_UTF_UNKNOWN); - break; - } - // Extra method to allow inlining the rest of writeUtf8 which is the most likely code path. - writeUtf8Surrogate(c, s.charAt(i)); - } else { - appendEncoded(0xe0 | (c >> 12)); - appendEncoded(0x80 | ((c >> 6) & 0x3f)); - appendEncoded(0x80 | (c & 0x3f)); - } - } - } - - private void writeUtf8Surrogate(char c, char c2) { - if (!Character.isLowSurrogate(c2)) { - appendEncoded(WRITE_UTF_UNKNOWN); - appendEncoded(Character.isHighSurrogate(c2) ? WRITE_UTF_UNKNOWN : c2); - return; - } - int codePoint = Character.toCodePoint(c, c2); - // See https://www.unicode.org/versions/Unicode7.0.0/ch03.pdf#G2630. - appendEncoded(0xf0 | (codePoint >> 18)); - appendEncoded(0x80 | ((codePoint >> 12) & 0x3f)); - appendEncoded(0x80 | ((codePoint >> 6) & 0x3f)); - appendEncoded(0x80 | (codePoint & 0x3f)); - } - - private void appendEncoded(int b) { - uriBuilder.append('%').append(forDigit(b >> 4)).append(forDigit(b)); - } - - /** - * Convert the given digit to a upper hexadecimal char. - * - * @param digit the number to convert to a character. - * @return the {@code char} representation of the specified digit - * in hexadecimal. - */ - private static char forDigit(int digit) { - return CHAR_MAP[digit & 0xF]; - } - - /** - * Determines whether the given character is a unreserved character. - *

- * unreserved characters do not need to be encoded, and include uppercase and lowercase - * letters, decimal digits, hyphen, period, underscore, and tilde. - *

- * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" / "*" - * - * @param ch the char to be judged whether it need to be encode - * @return true or false - */ - private static boolean dontNeedEncoding(char ch) { - return ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z' || ch >= '0' && ch <= '9' - || ch == '-' || ch == '_' || ch == '.' || ch == '*' || ch == '~'; - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/ReadOnlyHttpHeaders.java b/codec-http/src/main/java/io/netty/handler/codec/http/ReadOnlyHttpHeaders.java deleted file mode 100644 index 36e258875b..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/ReadOnlyHttpHeaders.java +++ /dev/null @@ -1,460 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.util.AsciiString; -import io.netty.util.internal.UnstableApi; - -import java.util.AbstractMap.SimpleImmutableEntry; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Set; - -import static io.netty.handler.codec.CharSequenceValueConverter.INSTANCE; -import static io.netty.handler.codec.http.DefaultHttpHeaders.HttpNameValidator; -import static io.netty.util.AsciiString.contentEquals; -import static io.netty.util.AsciiString.contentEqualsIgnoreCase; - -/** - * A variant of {@link HttpHeaders} which only supports read-only methods. - *

- * Any array passed to this class may be used directly in the underlying data structures of this class. If these - * arrays may be modified it is the caller's responsibility to supply this class with a copy of the array. - *

- * This may be a good alternative to {@link DefaultHttpHeaders} if your have a fixed set of headers which will not - * change. - */ -@UnstableApi -public final class ReadOnlyHttpHeaders extends HttpHeaders { - private final CharSequence[] nameValuePairs; - - /** - * Create a new instance. - * @param validateHeaders {@code true} to validate the contents of each header name. - * @param nameValuePairs An array of the structure {@code [,,...]}. - * A copy will NOT be made of this array. If the contents of this array - * may be modified externally you are responsible for passing in a copy. - */ - public ReadOnlyHttpHeaders(boolean validateHeaders, CharSequence... nameValuePairs) { - if ((nameValuePairs.length & 1) != 0) { - throw newInvalidArraySizeException(); - } - if (validateHeaders) { - validateHeaders(nameValuePairs); - } - this.nameValuePairs = nameValuePairs; - } - - private static IllegalArgumentException newInvalidArraySizeException() { - return new IllegalArgumentException("nameValuePairs must be arrays of [name, value] pairs"); - } - - private static void validateHeaders(CharSequence... keyValuePairs) { - for (int i = 0; i < keyValuePairs.length; i += 2) { - HttpNameValidator.validateName(keyValuePairs[i]); - } - } - - private CharSequence get0(CharSequence name) { - final int nameHash = AsciiString.hashCode(name); - for (int i = 0; i < nameValuePairs.length; i += 2) { - CharSequence roName = nameValuePairs[i]; - if (AsciiString.hashCode(roName) == nameHash && contentEqualsIgnoreCase(roName, name)) { - // Suppress a warning out of bounds access since the constructor allows only pairs - return nameValuePairs[i + 1]; // lgtm[java/index-out-of-bounds] - } - } - return null; - } - - @Override - public String get(String name) { - CharSequence value = get0(name); - return value == null ? null : value.toString(); - } - - @Override - public Integer getInt(CharSequence name) { - CharSequence value = get0(name); - return value == null ? null : INSTANCE.convertToInt(value); - } - - @Override - public int getInt(CharSequence name, int defaultValue) { - CharSequence value = get0(name); - return value == null ? defaultValue : INSTANCE.convertToInt(value); - } - - @Override - public Short getShort(CharSequence name) { - CharSequence value = get0(name); - return value == null ? null : INSTANCE.convertToShort(value); - } - - @Override - public short getShort(CharSequence name, short defaultValue) { - CharSequence value = get0(name); - return value == null ? defaultValue : INSTANCE.convertToShort(value); - } - - @Override - public Long getTimeMillis(CharSequence name) { - CharSequence value = get0(name); - return value == null ? null : INSTANCE.convertToTimeMillis(value); - } - - @Override - public long getTimeMillis(CharSequence name, long defaultValue) { - CharSequence value = get0(name); - return value == null ? defaultValue : INSTANCE.convertToTimeMillis(value); - } - - @Override - public List getAll(String name) { - if (isEmpty()) { - return Collections.emptyList(); - } - final int nameHash = AsciiString.hashCode(name); - List values = new ArrayList<>(4); - for (int i = 0; i < nameValuePairs.length; i += 2) { - CharSequence roName = nameValuePairs[i]; - if (AsciiString.hashCode(roName) == nameHash && contentEqualsIgnoreCase(roName, name)) { - values.add(nameValuePairs[i + 1].toString()); // lgtm[java/index-out-of-bounds] - } - } - return values; - } - - @Override - public List> entries() { - if (isEmpty()) { - return Collections.emptyList(); - } - List> entries = new ArrayList<>(size()); - for (int i = 0; i < nameValuePairs.length; i += 2) { - entries.add(new SimpleImmutableEntry<>(nameValuePairs[i].toString(), - nameValuePairs[i + 1].toString())); // lgtm[java/index-out-of-bounds] - } - return entries; - } - - @Override - public boolean contains(String name) { - return get0(name) != null; - } - - @Override - public boolean contains(String name, String value, boolean ignoreCase) { - return containsValue(name, value, ignoreCase); - } - - @Override - public boolean containsValue(CharSequence name, CharSequence value, boolean ignoreCase) { - if (ignoreCase) { - for (int i = 0; i < nameValuePairs.length; i += 2) { - if (contentEqualsIgnoreCase(nameValuePairs[i], name) && - contentEqualsIgnoreCase(nameValuePairs[i + 1], value)) { // lgtm[java/index-out-of-bounds] - return true; - } - } - } else { - for (int i = 0; i < nameValuePairs.length; i += 2) { - if (contentEqualsIgnoreCase(nameValuePairs[i], name) && - contentEquals(nameValuePairs[i + 1], value)) { // lgtm[java/index-out-of-bounds] - return true; - } - } - } - return false; - } - - @Override - public Iterator valueStringIterator(CharSequence name) { - return new ReadOnlyStringValueIterator(name); - } - - @Override - public Iterator valueCharSequenceIterator(CharSequence name) { - return new ReadOnlyValueIterator(name); - } - - @Override - public Iterator> iterator() { - return new ReadOnlyStringIterator(); - } - - @Override - public Iterator> iteratorCharSequence() { - return new ReadOnlyIterator(); - } - - @Override - public boolean isEmpty() { - return nameValuePairs.length == 0; - } - - @Override - public int size() { - return nameValuePairs.length >>> 1; - } - - @Override - public Set names() { - if (isEmpty()) { - return Collections.emptySet(); - } - Set names = new LinkedHashSet<>(size()); - for (int i = 0; i < nameValuePairs.length; i += 2) { - names.add(nameValuePairs[i].toString()); - } - return names; - } - - @Override - public HttpHeaders add(String name, Object value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public HttpHeaders add(String name, Iterable values) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public HttpHeaders addInt(CharSequence name, int value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public HttpHeaders addShort(CharSequence name, short value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public HttpHeaders set(String name, Object value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public HttpHeaders set(String name, Iterable values) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public HttpHeaders setInt(CharSequence name, int value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public HttpHeaders setShort(CharSequence name, short value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public HttpHeaders remove(String name) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public HttpHeaders clear() { - throw new UnsupportedOperationException("read only"); - } - - private final class ReadOnlyIterator implements Map.Entry, - Iterator> { - private CharSequence key; - private CharSequence value; - private int nextNameIndex; - - @Override - public boolean hasNext() { - return nextNameIndex != nameValuePairs.length; - } - - @Override - public Map.Entry next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - key = nameValuePairs[nextNameIndex]; - value = nameValuePairs[nextNameIndex + 1]; - nextNameIndex += 2; - return this; - } - - @Override - public void remove() { - throw new UnsupportedOperationException("read only"); - } - - @Override - public CharSequence getKey() { - return key; - } - - @Override - public CharSequence getValue() { - return value; - } - - @Override - public CharSequence setValue(CharSequence value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public String toString() { - return key.toString() + '=' + value.toString(); - } - } - - private final class ReadOnlyStringIterator implements Map.Entry, - Iterator> { - private String key; - private String value; - private int nextNameIndex; - - @Override - public boolean hasNext() { - return nextNameIndex != nameValuePairs.length; - } - - @Override - public Map.Entry next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - key = nameValuePairs[nextNameIndex].toString(); - value = nameValuePairs[nextNameIndex + 1].toString(); - nextNameIndex += 2; - return this; - } - - @Override - public void remove() { - throw new UnsupportedOperationException("read only"); - } - - @Override - public String getKey() { - return key; - } - - @Override - public String getValue() { - return value; - } - - @Override - public String setValue(String value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public String toString() { - return key + '=' + value; - } - } - - private final class ReadOnlyStringValueIterator implements Iterator { - private final CharSequence name; - private final int nameHash; - private int nextNameIndex; - - ReadOnlyStringValueIterator(CharSequence name) { - this.name = name; - nameHash = AsciiString.hashCode(name); - nextNameIndex = findNextValue(); - } - - @Override - public boolean hasNext() { - return nextNameIndex != -1; - } - - @Override - public String next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - String value = nameValuePairs[nextNameIndex + 1].toString(); - nextNameIndex = findNextValue(); - return value; - } - - @Override - public void remove() { - throw new UnsupportedOperationException("read only"); - } - - private int findNextValue() { - for (int i = nextNameIndex; i < nameValuePairs.length; i += 2) { - final CharSequence roName = nameValuePairs[i]; - if (nameHash == AsciiString.hashCode(roName) && contentEqualsIgnoreCase(name, roName)) { - return i; - } - } - return -1; - } - } - - private final class ReadOnlyValueIterator implements Iterator { - private final CharSequence name; - private final int nameHash; - private int nextNameIndex; - - ReadOnlyValueIterator(CharSequence name) { - this.name = name; - nameHash = AsciiString.hashCode(name); - nextNameIndex = findNextValue(); - } - - @Override - public boolean hasNext() { - return nextNameIndex != -1; - } - - @Override - public CharSequence next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - CharSequence value = nameValuePairs[nextNameIndex + 1]; - nextNameIndex = findNextValue(); - return value; - } - - @Override - public void remove() { - throw new UnsupportedOperationException("read only"); - } - - private int findNextValue() { - for (int i = nextNameIndex; i < nameValuePairs.length; i += 2) { - final CharSequence roName = nameValuePairs[i]; - if (nameHash == AsciiString.hashCode(roName) && contentEqualsIgnoreCase(name, roName)) { - return i; - } - } - return -1; - } - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/cookie/ClientCookieDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/cookie/ClientCookieDecoder.java deleted file mode 100644 index 63bfed75fe..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/cookie/ClientCookieDecoder.java +++ /dev/null @@ -1,263 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.cookie; - -import io.netty.handler.codec.DateFormatter; -import io.netty.handler.codec.http.cookie.CookieHeaderNames.SameSite; - -import java.util.Date; - -import static java.util.Objects.requireNonNull; - -/** - * A RFC6265 compliant cookie decoder to be used client side. - * - * It will store the way the raw value was wrapped in {@link Cookie#setWrap(boolean)} so it can be - * eventually sent back to the Origin server as is. - * - * @see ClientCookieEncoder - */ -public final class ClientCookieDecoder extends CookieDecoder { - - /** - * Strict encoder that validates that name and value chars are in the valid scope - * defined in RFC6265 - */ - public static final ClientCookieDecoder STRICT = new ClientCookieDecoder(true); - - /** - * Lax instance that doesn't validate name and value - */ - public static final ClientCookieDecoder LAX = new ClientCookieDecoder(false); - - private ClientCookieDecoder(boolean strict) { - super(strict); - } - - /** - * Decodes the specified Set-Cookie HTTP header value into a {@link Cookie}. - * - * @return the decoded {@link Cookie} - */ - public Cookie decode(String header) { - final int headerLen = requireNonNull(header, "header").length(); - - if (headerLen == 0) { - return null; - } - - CookieBuilder cookieBuilder = null; - - loop: for (int i = 0;;) { - - // Skip spaces and separators. - for (;;) { - if (i == headerLen) { - break loop; - } - char c = header.charAt(i); - if (c == ',') { - // Having multiple cookies in a single Set-Cookie header is - // deprecated, modern browsers only parse the first one - break loop; - - } else if (c == '\t' || c == '\n' || c == 0x0b || c == '\f' - || c == '\r' || c == ' ' || c == ';') { - i++; - continue; - } - break; - } - - int nameBegin = i; - int nameEnd; - int valueBegin; - int valueEnd; - - for (;;) { - char curChar = header.charAt(i); - if (curChar == ';') { - // NAME; (no value till ';') - nameEnd = i; - valueBegin = valueEnd = -1; - break; - - } else if (curChar == '=') { - // NAME=VALUE - nameEnd = i; - i++; - if (i == headerLen) { - // NAME= (empty value, i.e. nothing after '=') - valueBegin = valueEnd = 0; - break; - } - - valueBegin = i; - // NAME=VALUE; - int semiPos = header.indexOf(';', i); - valueEnd = i = semiPos > 0 ? semiPos : headerLen; - break; - } else { - i++; - } - - if (i == headerLen) { - // NAME (no value till the end of string) - nameEnd = headerLen; - valueBegin = valueEnd = -1; - break; - } - } - - if (valueEnd > 0 && header.charAt(valueEnd - 1) == ',') { - // old multiple cookies separator, skipping it - valueEnd--; - } - - if (cookieBuilder == null) { - // cookie name-value pair - DefaultCookie cookie = initCookie(header, nameBegin, nameEnd, valueBegin, valueEnd); - - if (cookie == null) { - return null; - } - - cookieBuilder = new CookieBuilder(cookie, header); - } else { - // cookie attribute - cookieBuilder.appendAttribute(nameBegin, nameEnd, valueBegin, valueEnd); - } - } - return cookieBuilder != null ? cookieBuilder.cookie() : null; - } - - private static class CookieBuilder { - - private final String header; - private final DefaultCookie cookie; - private String domain; - private String path; - private long maxAge = Long.MIN_VALUE; - private int expiresStart; - private int expiresEnd; - private boolean secure; - private boolean httpOnly; - private SameSite sameSite; - - CookieBuilder(DefaultCookie cookie, String header) { - this.cookie = cookie; - this.header = header; - } - - private long mergeMaxAgeAndExpires() { - // max age has precedence over expires - if (maxAge != Long.MIN_VALUE) { - return maxAge; - } else if (isValueDefined(expiresStart, expiresEnd)) { - Date expiresDate = DateFormatter.parseHttpDate(header, expiresStart, expiresEnd); - if (expiresDate != null) { - long maxAgeMillis = expiresDate.getTime() - System.currentTimeMillis(); - return maxAgeMillis / 1000 + (maxAgeMillis % 1000 != 0 ? 1 : 0); - } - } - return Long.MIN_VALUE; - } - - Cookie cookie() { - cookie.setDomain(domain); - cookie.setPath(path); - cookie.setMaxAge(mergeMaxAgeAndExpires()); - cookie.setSecure(secure); - cookie.setHttpOnly(httpOnly); - cookie.setSameSite(sameSite); - return cookie; - } - - /** - * Parse and store a key-value pair. First one is considered to be the - * cookie name/value. Unknown attribute names are silently discarded. - * - * @param keyStart - * where the key starts in the header - * @param keyEnd - * where the key ends in the header - * @param valueStart - * where the value starts in the header - * @param valueEnd - * where the value ends in the header - */ - void appendAttribute(int keyStart, int keyEnd, int valueStart, int valueEnd) { - int length = keyEnd - keyStart; - - if (length == 4) { - parse4(keyStart, valueStart, valueEnd); - } else if (length == 6) { - parse6(keyStart, valueStart, valueEnd); - } else if (length == 7) { - parse7(keyStart, valueStart, valueEnd); - } else if (length == 8) { - parse8(keyStart, valueStart, valueEnd); - } - } - - private void parse4(int nameStart, int valueStart, int valueEnd) { - if (header.regionMatches(true, nameStart, CookieHeaderNames.PATH, 0, 4)) { - path = computeValue(valueStart, valueEnd); - } - } - - private void parse6(int nameStart, int valueStart, int valueEnd) { - if (header.regionMatches(true, nameStart, CookieHeaderNames.DOMAIN, 0, 5)) { - domain = computeValue(valueStart, valueEnd); - } else if (header.regionMatches(true, nameStart, CookieHeaderNames.SECURE, 0, 5)) { - secure = true; - } - } - - private void setMaxAge(String value) { - try { - maxAge = Math.max(Long.parseLong(value), 0L); - } catch (NumberFormatException e1) { - // ignore failure to parse -> treat as session cookie - } - } - - private void parse7(int nameStart, int valueStart, int valueEnd) { - if (header.regionMatches(true, nameStart, CookieHeaderNames.EXPIRES, 0, 7)) { - expiresStart = valueStart; - expiresEnd = valueEnd; - } else if (header.regionMatches(true, nameStart, CookieHeaderNames.MAX_AGE, 0, 7)) { - setMaxAge(computeValue(valueStart, valueEnd)); - } - } - - private void parse8(int nameStart, int valueStart, int valueEnd) { - if (header.regionMatches(true, nameStart, CookieHeaderNames.HTTPONLY, 0, 8)) { - httpOnly = true; - } else if (header.regionMatches(true, nameStart, CookieHeaderNames.SAMESITE, 0, 8)) { - sameSite = SameSite.of(computeValue(valueStart, valueEnd)); - } - } - - private static boolean isValueDefined(int valueStart, int valueEnd) { - return valueStart != -1 && valueStart != valueEnd; - } - - private String computeValue(int valueStart, int valueEnd) { - return isValueDefined(valueStart, valueEnd) ? header.substring(valueStart, valueEnd) : null; - } - } -} 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 deleted file mode 100644 index 951d9045e9..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/cookie/ClientCookieEncoder.java +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.cookie; - -import static io.netty.handler.codec.http.cookie.CookieUtil.add; -import static io.netty.handler.codec.http.cookie.CookieUtil.addQuoted; -import static io.netty.handler.codec.http.cookie.CookieUtil.stringBuilder; -import static io.netty.handler.codec.http.cookie.CookieUtil.stripTrailingSeparator; -import static io.netty.handler.codec.http.cookie.CookieUtil.stripTrailingSeparatorOrNull; -import static java.util.Objects.requireNonNull; - -import io.netty.handler.codec.http.HttpRequest; -import io.netty.util.internal.InternalThreadLocalMap; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Comparator; -import java.util.Iterator; -import java.util.List; - -/** - * A RFC6265 compliant cookie encoder to be used client side, so - * only name=value pairs are sent. - * - * Note that multiple cookies are supposed to be sent at once in a single "Cookie" header. - * - *

- * // Example
- * {@link HttpRequest} req = ...;
- * res.setHeader("Cookie", {@link ClientCookieEncoder}.encode("JSESSIONID", "1234"));
- * 
- * - * @see ClientCookieDecoder - */ -public final class ClientCookieEncoder extends CookieEncoder { - - /** - * Strict encoder that validates that name and value chars are in the valid scope and (for methods that accept - * multiple cookies) sorts cookies into order of decreasing path length, as specified in RFC6265. - */ - public static final ClientCookieEncoder STRICT = new ClientCookieEncoder(true); - - /** - * Lax instance that doesn't validate name and value, and (for methods that accept multiple cookies) keeps - * cookies in the order in which they were given. - */ - public static final ClientCookieEncoder LAX = new ClientCookieEncoder(false); - - private ClientCookieEncoder(boolean strict) { - super(strict); - } - - /** - * Encodes the specified cookie into a Cookie header value. - * - * @param name - * the cookie name - * @param value - * the cookie value - * @return a Rfc6265 style Cookie header value - */ - public String encode(String name, String value) { - return encode(new DefaultCookie(name, value)); - } - - /** - * Encodes the specified cookie into a Cookie header value. - * - * @param cookie the specified cookie - * @return a Rfc6265 style Cookie header value - */ - public String encode(Cookie cookie) { - StringBuilder buf = stringBuilder(); - encode(buf, requireNonNull(cookie, "cookie")); - return stripTrailingSeparator(buf); - } - - /** - * Sort cookies into decreasing order of path length, breaking ties by sorting into increasing chronological - * order of creation time, as recommended by RFC 6265. - */ - // package-private for testing only - static final Comparator COOKIE_COMPARATOR = (c1, c2) -> { - String path1 = c1.path(); - String path2 = c2.path(); - // Cookies with unspecified path default to the path of the request. We don't - // know the request path here, but we assume that the length of an unspecified - // path is longer than any specified path (i.e. pathless cookies come first), - // because setting cookies with a path longer than the request path is of - // limited use. - int len1 = path1 == null ? Integer.MAX_VALUE : path1.length(); - int len2 = path2 == null ? Integer.MAX_VALUE : path2.length(); - - // Rely on Arrays.sort's stability to retain creation order in cases where - // cookies have same path length. - return len2 - len1; - }; - - /** - * Encodes the specified cookies into a single Cookie header value. - * - * @param cookies - * some cookies - * @return a Rfc6265 style Cookie header value, null if no cookies are passed. - */ - public String encode(Cookie... cookies) { - if (requireNonNull(cookies, "cookies").length == 0) { - return null; - } - - StringBuilder buf = stringBuilder(); - if (strict) { - if (cookies.length == 1) { - encode(buf, cookies[0]); - } else { - Cookie[] cookiesSorted = Arrays.copyOf(cookies, cookies.length); - Arrays.sort(cookiesSorted, COOKIE_COMPARATOR); - for (Cookie c : cookiesSorted) { - encode(buf, c); - } - } - } else { - for (Cookie c : cookies) { - encode(buf, c); - } - } - return stripTrailingSeparatorOrNull(buf); - } - - /** - * Encodes the specified cookies into a single Cookie header value. - * - * @param cookies - * some cookies - * @return a Rfc6265 style Cookie header value, null if no cookies are passed. - */ - public String encode(Collection cookies) { - if (requireNonNull(cookies, "cookies").isEmpty()) { - return null; - } - - StringBuilder buf = stringBuilder(); - if (strict) { - if (cookies.size() == 1) { - encode(buf, cookies.iterator().next()); - } else { - Cookie[] cookiesSorted = cookies.toArray(new Cookie[0]); - Arrays.sort(cookiesSorted, COOKIE_COMPARATOR); - for (Cookie c : cookiesSorted) { - encode(buf, c); - } - } - } else { - for (Cookie c : cookies) { - encode(buf, c); - } - } - return stripTrailingSeparatorOrNull(buf); - } - - /** - * Encodes the specified cookies into a single Cookie header value. - * - * @param cookies some cookies - * @return a Rfc6265 style Cookie header value, null if no cookies are passed. - */ - public String encode(Iterable cookies) { - Iterator cookiesIt = requireNonNull(cookies, "cookies").iterator(); - if (!cookiesIt.hasNext()) { - return null; - } - - StringBuilder buf = stringBuilder(); - if (strict) { - Cookie firstCookie = cookiesIt.next(); - if (!cookiesIt.hasNext()) { - encode(buf, firstCookie); - } else { - List cookiesList = InternalThreadLocalMap.get().arrayList(); - cookiesList.add(firstCookie); - while (cookiesIt.hasNext()) { - cookiesList.add(cookiesIt.next()); - } - Cookie[] cookiesSorted = cookiesList.toArray(new Cookie[0]); - Arrays.sort(cookiesSorted, COOKIE_COMPARATOR); - for (Cookie c : cookiesSorted) { - encode(buf, c); - } - } - } else { - while (cookiesIt.hasNext()) { - encode(buf, cookiesIt.next()); - } - } - return stripTrailingSeparatorOrNull(buf); - } - - private void encode(StringBuilder buf, Cookie c) { - final String name = c.name(); - final String value = c.value() != null ? c.value() : ""; - - validateCookie(name, value); - - if (c.wrap()) { - addQuoted(buf, name, value); - } else { - add(buf, name, value); - } - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/cookie/Cookie.java b/codec-http/src/main/java/io/netty/handler/codec/http/cookie/Cookie.java deleted file mode 100644 index ea5183ec74..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/cookie/Cookie.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.cookie; - -/** - * An interface defining an - * HTTP cookie. - */ -public interface Cookie extends Comparable { - - /** - * Constant for undefined MaxAge attribute value. - */ - long UNDEFINED_MAX_AGE = Long.MIN_VALUE; - - /** - * Returns the name of this {@link Cookie}. - * - * @return The name of this {@link Cookie} - */ - String name(); - - /** - * Returns the value of this {@link Cookie}. - * - * @return The value of this {@link Cookie} - */ - String value(); - - /** - * Sets the value of this {@link Cookie}. - * - * @param value The value to set - */ - void setValue(String value); - - /** - * Returns true if the raw value of this {@link Cookie}, - * was wrapped with double quotes in original Set-Cookie header. - * - * @return If the value of this {@link Cookie} is to be wrapped - */ - boolean wrap(); - - /** - * Sets true if the value of this {@link Cookie} - * is to be wrapped with double quotes. - * - * @param wrap true if wrap - */ - void setWrap(boolean wrap); - - /** - * Returns the domain of this {@link Cookie}. - * - * @return The domain of this {@link Cookie} - */ - String domain(); - - /** - * Sets the domain of this {@link Cookie}. - * - * @param domain The domain to use - */ - void setDomain(String domain); - - /** - * Returns the path of this {@link Cookie}. - * - * @return The {@link Cookie}'s path - */ - String path(); - - /** - * Sets the path of this {@link Cookie}. - * - * @param path The path to use for this {@link Cookie} - */ - void setPath(String path); - - /** - * Returns the maximum age of this {@link Cookie} in seconds or {@link Cookie#UNDEFINED_MAX_AGE} if unspecified - * - * @return The maximum age of this {@link Cookie} - */ - long maxAge(); - - /** - * Sets the maximum age of this {@link Cookie} in seconds. - * If an age of {@code 0} is specified, this {@link Cookie} will be - * automatically removed by browser because it will expire immediately. - * If {@link Cookie#UNDEFINED_MAX_AGE} is specified, this {@link Cookie} will be removed when the - * browser is closed. - * - * @param maxAge The maximum age of this {@link Cookie} in seconds - */ - void setMaxAge(long maxAge); - - /** - * Checks to see if this {@link Cookie} is secure - * - * @return True if this {@link Cookie} is secure, otherwise false - */ - boolean isSecure(); - - /** - * Sets the security getStatus of this {@link Cookie} - * - * @param secure True if this {@link Cookie} is to be secure, otherwise false - */ - void setSecure(boolean secure); - - /** - * Checks to see if this {@link Cookie} can only be accessed via HTTP. - * If this returns true, the {@link Cookie} cannot be accessed through - * client side script - But only if the browser supports it. - * For more information, please look here - * - * @return True if this {@link Cookie} is HTTP-only or false if it isn't - */ - boolean isHttpOnly(); - - /** - * Determines if this {@link Cookie} is HTTP only. - * If set to true, this {@link Cookie} cannot be accessed by a client - * side script. However, this works only if the browser supports it. - * For for information, please look - * here. - * - * @param httpOnly True if the {@link Cookie} is HTTP only, otherwise false. - */ - void setHttpOnly(boolean httpOnly); -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/cookie/CookieDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/cookie/CookieDecoder.java deleted file mode 100644 index 97edaa42c9..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/cookie/CookieDecoder.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.cookie; - -import static io.netty.handler.codec.http.cookie.CookieUtil.firstInvalidCookieNameOctet; -import static io.netty.handler.codec.http.cookie.CookieUtil.firstInvalidCookieValueOctet; -import static io.netty.handler.codec.http.cookie.CookieUtil.unwrapValue; - -import java.nio.CharBuffer; - -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -/** - * Parent of Client and Server side cookie decoders - */ -public abstract class CookieDecoder { - - private final InternalLogger logger = InternalLoggerFactory.getInstance(getClass()); - - private final boolean strict; - - protected CookieDecoder(boolean strict) { - this.strict = strict; - } - - protected DefaultCookie initCookie(String header, int nameBegin, int nameEnd, int valueBegin, int valueEnd) { - if (nameBegin == -1 || nameBegin == nameEnd) { - logger.debug("Skipping cookie with null name"); - return null; - } - - if (valueBegin == -1) { - logger.debug("Skipping cookie with null value"); - return null; - } - - CharSequence wrappedValue = CharBuffer.wrap(header, valueBegin, valueEnd); - CharSequence unwrappedValue = unwrapValue(wrappedValue); - if (unwrappedValue == null) { - logger.debug("Skipping cookie because starting quotes are not properly balanced in '{}'", - wrappedValue); - return null; - } - - final String name = header.substring(nameBegin, nameEnd); - - int invalidOctetPos; - if (strict && (invalidOctetPos = firstInvalidCookieNameOctet(name)) >= 0) { - if (logger.isDebugEnabled()) { - logger.debug("Skipping cookie because name '{}' contains invalid char '{}'", - name, name.charAt(invalidOctetPos)); - } - return null; - } - - final boolean wrap = unwrappedValue.length() != valueEnd - valueBegin; - - if (strict && (invalidOctetPos = firstInvalidCookieValueOctet(unwrappedValue)) >= 0) { - if (logger.isDebugEnabled()) { - logger.debug("Skipping cookie because value '{}' contains invalid char '{}'", - unwrappedValue, unwrappedValue.charAt(invalidOctetPos)); - } - return null; - } - - DefaultCookie cookie = new DefaultCookie(name, unwrappedValue.toString()); - cookie.setWrap(wrap); - return cookie; - } -} 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 deleted file mode 100644 index a1f20f1e82..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/cookie/CookieEncoder.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.cookie; - -import static io.netty.handler.codec.http.cookie.CookieUtil.firstInvalidCookieNameOctet; -import static io.netty.handler.codec.http.cookie.CookieUtil.firstInvalidCookieValueOctet; -import static io.netty.handler.codec.http.cookie.CookieUtil.unwrapValue; - -/** - * Parent of Client and Server side cookie encoders - */ -public abstract class CookieEncoder { - - protected final boolean strict; - - protected CookieEncoder(boolean strict) { - this.strict = strict; - } - - protected void validateCookie(String name, String value) { - if (strict) { - int pos; - - if ((pos = firstInvalidCookieNameOctet(name)) >= 0) { - throw new IllegalArgumentException("Cookie name contains an invalid char: " + name.charAt(pos)); - } - - CharSequence unwrappedValue = unwrapValue(value); - if (unwrappedValue == null) { - throw new IllegalArgumentException("Cookie value wrapping quotes are not balanced: " + value); - } - - if ((pos = firstInvalidCookieValueOctet(unwrappedValue)) >= 0) { - throw new IllegalArgumentException("Cookie value contains an invalid char: " + - unwrappedValue.charAt(pos)); - } - } - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/cookie/CookieHeaderNames.java b/codec-http/src/main/java/io/netty/handler/codec/http/cookie/CookieHeaderNames.java deleted file mode 100644 index 7e3881ebef..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/cookie/CookieHeaderNames.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.cookie; - -public final class CookieHeaderNames { - public static final String PATH = "Path"; - - public static final String EXPIRES = "Expires"; - - public static final String MAX_AGE = "Max-Age"; - - public static final String DOMAIN = "Domain"; - - public static final String SECURE = "Secure"; - - public static final String HTTPONLY = "HTTPOnly"; - - public static final String SAMESITE = "SameSite"; - - /** - * Possible values for the SameSite attribute. - * See changes to RFC6265bis - */ - public enum SameSite { - Lax, - Strict, - None; - - /** - * Return the enum value corresponding to the passed in same-site-flag, using a case insensitive comparison. - * - * @param name value for the SameSite Attribute - * @return enum value for the provided name or null - */ - static SameSite of(String name) { - if (name != null) { - for (SameSite each : SameSite.class.getEnumConstants()) { - if (each.name().equalsIgnoreCase(name)) { - return each; - } - } - } - return null; - } - } - - private CookieHeaderNames() { - // Unused. - } -} 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 deleted file mode 100644 index 64aa6bfe80..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/cookie/CookieUtil.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.cookie; - -import io.netty.handler.codec.http.HttpConstants; -import io.netty.util.internal.InternalThreadLocalMap; - -import java.util.BitSet; - -final class CookieUtil { - - private static final BitSet VALID_COOKIE_NAME_OCTETS = validCookieNameOctets(); - - private static final BitSet VALID_COOKIE_VALUE_OCTETS = validCookieValueOctets(); - - private static final BitSet VALID_COOKIE_ATTRIBUTE_VALUE_OCTETS = validCookieAttributeValueOctets(); - - // token = 1* - // separators = "(" | ")" | "<" | ">" | "@" - // | "," | ";" | ":" | "\" | <"> - // | "/" | "[" | "]" | "?" | "=" - // | "{" | "}" | SP | HT - private static BitSet validCookieNameOctets() { - BitSet bits = new BitSet(); - for (int i = 32; i < 127; i++) { - bits.set(i); - } - int[] separators = new int[] - { '(', ')', '<', '>', '@', ',', ';', ':', '\\', '"', '/', '[', ']', '?', '=', '{', '}', ' ', '\t' }; - for (int separator : separators) { - bits.set(separator, false); - } - return bits; - } - - // cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E - // US-ASCII characters excluding CTLs, whitespace, DQUOTE, comma, semicolon, and backslash - private static BitSet validCookieValueOctets() { - BitSet bits = new BitSet(); - bits.set(0x21); - for (int i = 0x23; i <= 0x2B; i++) { - bits.set(i); - } - for (int i = 0x2D; i <= 0x3A; i++) { - bits.set(i); - } - for (int i = 0x3C; i <= 0x5B; i++) { - bits.set(i); - } - for (int i = 0x5D; i <= 0x7E; i++) { - bits.set(i); - } - return bits; - } - - // path-value = - private static BitSet validCookieAttributeValueOctets() { - BitSet bits = new BitSet(); - for (int i = 32; i < 127; i++) { - bits.set(i); - } - bits.set(';', false); - return bits; - } - - static StringBuilder stringBuilder() { - return InternalThreadLocalMap.get().stringBuilder(); - } - - /** - * @param buf a buffer where some cookies were maybe encoded - * @return the buffer String without the trailing separator, or null if no cookie was appended. - */ - static String stripTrailingSeparatorOrNull(StringBuilder buf) { - return buf.length() == 0 ? null : stripTrailingSeparator(buf); - } - - static String stripTrailingSeparator(StringBuilder buf) { - if (buf.length() > 0) { - buf.setLength(buf.length() - 2); - } - return buf.toString(); - } - - static void add(StringBuilder sb, String name, long val) { - sb.append(name); - sb.append('='); - sb.append(val); - sb.append(';'); - sb.append(HttpConstants.SP_CHAR); - } - - static void add(StringBuilder sb, String name, String val) { - sb.append(name); - sb.append('='); - sb.append(val); - sb.append(';'); - sb.append(HttpConstants.SP_CHAR); - } - - static void add(StringBuilder sb, String name) { - sb.append(name); - sb.append(';'); - sb.append(HttpConstants.SP_CHAR); - } - - static void addQuoted(StringBuilder sb, String name, String val) { - if (val == null) { - val = ""; - } - - sb.append(name); - sb.append('='); - sb.append('"'); - sb.append(val); - sb.append('"'); - sb.append(';'); - sb.append(HttpConstants.SP_CHAR); - } - - static int firstInvalidCookieNameOctet(CharSequence cs) { - return firstInvalidOctet(cs, VALID_COOKIE_NAME_OCTETS); - } - - static int firstInvalidCookieValueOctet(CharSequence cs) { - return firstInvalidOctet(cs, VALID_COOKIE_VALUE_OCTETS); - } - - static int firstInvalidOctet(CharSequence cs, BitSet bits) { - for (int i = 0; i < cs.length(); i++) { - char c = cs.charAt(i); - if (!bits.get(c)) { - return i; - } - } - return -1; - } - - static CharSequence unwrapValue(CharSequence cs) { - final int len = cs.length(); - if (len > 0 && cs.charAt(0) == '"') { - if (len >= 2 && cs.charAt(len - 1) == '"') { - // properly balanced - return len == 2 ? "" : cs.subSequence(1, len - 1); - } else { - return null; - } - } - return cs; - } - - static String validateAttributeValue(String name, String value) { - if (value == null) { - return null; - } - value = value.trim(); - if (value.isEmpty()) { - return null; - } - int i = firstInvalidOctet(value, VALID_COOKIE_ATTRIBUTE_VALUE_OCTETS); - if (i != -1) { - throw new IllegalArgumentException(name + " contains the prohibited characters: " + value.charAt(i)); - } - return value; - } - - private CookieUtil() { - // Unused - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/cookie/DefaultCookie.java b/codec-http/src/main/java/io/netty/handler/codec/http/cookie/DefaultCookie.java deleted file mode 100644 index 2973a076c9..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/cookie/DefaultCookie.java +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.cookie; - -import io.netty.handler.codec.http.cookie.CookieHeaderNames.SameSite; - -import static io.netty.handler.codec.http.cookie.CookieUtil.stringBuilder; -import static io.netty.handler.codec.http.cookie.CookieUtil.validateAttributeValue; -import static io.netty.util.internal.ObjectUtil.checkNonEmptyAfterTrim; -import static java.util.Objects.requireNonNull; - -/** - * The default {@link Cookie} implementation. - */ -public class DefaultCookie implements Cookie { - - private final String name; - private String value; - private boolean wrap; - private String domain; - private String path; - private long maxAge = UNDEFINED_MAX_AGE; - private boolean secure; - private boolean httpOnly; - private SameSite sameSite; - - /** - * Creates a new cookie with the specified name and value. - */ - public DefaultCookie(String name, String value) { - this.name = checkNonEmptyAfterTrim(name, "name"); - setValue(value); - } - - @Override - public String name() { - return name; - } - - @Override - public String value() { - return value; - } - - @Override - public void setValue(String value) { - this.value = requireNonNull(value, "value"); - } - - @Override - public boolean wrap() { - return wrap; - } - - @Override - public void setWrap(boolean wrap) { - this.wrap = wrap; - } - - @Override - public String domain() { - return domain; - } - - @Override - public void setDomain(String domain) { - this.domain = validateAttributeValue("domain", domain); - } - - @Override - public String path() { - return path; - } - - @Override - public void setPath(String path) { - this.path = validateAttributeValue("path", path); - } - - @Override - public long maxAge() { - return maxAge; - } - - @Override - public void setMaxAge(long maxAge) { - this.maxAge = maxAge; - } - - @Override - public boolean isSecure() { - return secure; - } - - @Override - public void setSecure(boolean secure) { - this.secure = secure; - } - - @Override - public boolean isHttpOnly() { - return httpOnly; - } - - @Override - public void setHttpOnly(boolean httpOnly) { - this.httpOnly = httpOnly; - } - - /** - * Checks to see if this {@link Cookie} can be sent along cross-site requests. - * For more information, please look - * here - * @return same-site-flag value - */ - public SameSite sameSite() { - return sameSite; - } - - /** - * Determines if this this {@link Cookie} can be sent along cross-site requests. - * For more information, please look - * here - * @param sameSite same-site-flag value - */ - public void setSameSite(SameSite sameSite) { - this.sameSite = sameSite; - } - - @Override - public int hashCode() { - return name().hashCode(); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (!(o instanceof Cookie)) { - return false; - } - - Cookie that = (Cookie) o; - if (!name().equals(that.name())) { - return false; - } - - if (path() == null) { - if (that.path() != null) { - return false; - } - } else if (that.path() == null) { - return false; - } else if (!path().equals(that.path())) { - return false; - } - - if (domain() == null) { - if (that.domain() != null) { - return false; - } - } else { - return domain().equalsIgnoreCase(that.domain()); - } - - return true; - } - - @Override - public int compareTo(Cookie c) { - int v = name().compareTo(c.name()); - if (v != 0) { - return v; - } - - if (path() == null) { - if (c.path() != null) { - return -1; - } - } else if (c.path() == null) { - return 1; - } else { - v = path().compareTo(c.path()); - if (v != 0) { - return v; - } - } - - if (domain() == null) { - if (c.domain() != null) { - return -1; - } - } else if (c.domain() == null) { - return 1; - } else { - v = domain().compareToIgnoreCase(c.domain()); - return v; - } - - return 0; - } - - @Override - public String toString() { - StringBuilder buf = stringBuilder() - .append(name()) - .append('=') - .append(value()); - if (domain() != null) { - buf.append(", domain=") - .append(domain()); - } - if (path() != null) { - buf.append(", path=") - .append(path()); - } - if (maxAge() >= 0) { - buf.append(", maxAge=") - .append(maxAge()) - .append('s'); - } - if (isSecure()) { - buf.append(", secure"); - } - if (isHttpOnly()) { - buf.append(", HTTPOnly"); - } - if (sameSite() != null) { - buf.append(", SameSite=").append(sameSite()); - } - return buf.toString(); - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/cookie/ServerCookieDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/cookie/ServerCookieDecoder.java deleted file mode 100644 index 397b37710d..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/cookie/ServerCookieDecoder.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.cookie; - -import static java.util.Objects.requireNonNull; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Set; -import java.util.TreeSet; - -/** - * A RFC6265 compliant cookie decoder to be used server side. - * - * Only name and value fields are expected, so old fields are not populated (path, domain, etc). - * - * Old RFC2965 cookies are still supported, - * old fields will simply be ignored. - * - * @see ServerCookieEncoder - */ -public final class ServerCookieDecoder extends CookieDecoder { - - private static final String RFC2965_VERSION = "$Version"; - - private static final String RFC2965_PATH = "$" + CookieHeaderNames.PATH; - - private static final String RFC2965_DOMAIN = "$" + CookieHeaderNames.DOMAIN; - - private static final String RFC2965_PORT = "$Port"; - - /** - * Strict encoder that validates that name and value chars are in the valid scope - * defined in RFC6265 - */ - public static final ServerCookieDecoder STRICT = new ServerCookieDecoder(true); - - /** - * Lax instance that doesn't validate name and value - */ - public static final ServerCookieDecoder LAX = new ServerCookieDecoder(false); - - private ServerCookieDecoder(boolean strict) { - super(strict); - } - - /** - * Decodes the specified {@code Cookie} HTTP header value into a {@link Cookie}. Unlike {@link #decode(String)}, - * this includes all cookie values present, even if they have the same name. - * - * @return the decoded {@link Cookie} - */ - public List decodeAll(String header) { - List cookies = new ArrayList<>(); - decode(cookies, header); - return Collections.unmodifiableList(cookies); - } - - /** - * Decodes the specified {@code Cookie} HTTP header value into a {@link Cookie}. - * - * @return the decoded {@link Cookie} - */ - public Set decode(String header) { - Set cookies = new TreeSet<>(); - decode(cookies, header); - return cookies; - } - - /** - * Decodes the specified {@code Cookie} HTTP header value into a {@link Cookie}. - */ - private void decode(Collection cookies, String header) { - final int headerLen = requireNonNull(header, "header").length(); - - if (headerLen == 0) { - return; - } - - int i = 0; - - boolean rfc2965Style = false; - if (header.regionMatches(true, 0, RFC2965_VERSION, 0, RFC2965_VERSION.length())) { - // RFC 2965 style cookie, move to after version value - i = header.indexOf(';') + 1; - rfc2965Style = true; - } - - loop: for (;;) { - - // Skip spaces and separators. - for (;;) { - if (i == headerLen) { - break loop; - } - char c = header.charAt(i); - if (c == '\t' || c == '\n' || c == 0x0b || c == '\f' - || c == '\r' || c == ' ' || c == ',' || c == ';') { - i++; - continue; - } - break; - } - - int nameBegin = i; - int nameEnd; - int valueBegin; - int valueEnd; - - for (;;) { - - char curChar = header.charAt(i); - if (curChar == ';') { - // NAME; (no value till ';') - nameEnd = i; - valueBegin = valueEnd = -1; - break; - - } else if (curChar == '=') { - // NAME=VALUE - nameEnd = i; - i++; - if (i == headerLen) { - // NAME= (empty value, i.e. nothing after '=') - valueBegin = valueEnd = 0; - break; - } - - valueBegin = i; - // NAME=VALUE; - int semiPos = header.indexOf(';', i); - valueEnd = i = semiPos > 0 ? semiPos : headerLen; - break; - } else { - i++; - } - - if (i == headerLen) { - // NAME (no value till the end of string) - nameEnd = headerLen; - valueBegin = valueEnd = -1; - break; - } - } - - if (rfc2965Style && (header.regionMatches(nameBegin, RFC2965_PATH, 0, RFC2965_PATH.length()) || - header.regionMatches(nameBegin, RFC2965_DOMAIN, 0, RFC2965_DOMAIN.length()) || - header.regionMatches(nameBegin, RFC2965_PORT, 0, RFC2965_PORT.length()))) { - - // skip obsolete RFC2965 fields - continue; - } - - DefaultCookie cookie = initCookie(header, nameBegin, nameEnd, valueBegin, valueEnd); - if (cookie != null) { - cookies.add(cookie); - } - } - } -} 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 deleted file mode 100644 index 9bfebb1fb9..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/cookie/ServerCookieEncoder.java +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.cookie; - -import io.netty.handler.codec.DateFormatter; -import io.netty.handler.codec.http.HttpConstants; -import io.netty.handler.codec.http.HttpResponse; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import static io.netty.handler.codec.http.cookie.CookieUtil.add; -import static io.netty.handler.codec.http.cookie.CookieUtil.addQuoted; -import static io.netty.handler.codec.http.cookie.CookieUtil.stringBuilder; -import static io.netty.handler.codec.http.cookie.CookieUtil.stripTrailingSeparator; -import static java.util.Objects.requireNonNull; - -/** - * A RFC6265 compliant cookie encoder to be used server side, - * so some fields are sent (Version is typically ignored). - * - * As Netty's Cookie merges Expires and MaxAge into one single field, only Max-Age field is sent. - * - * Note that multiple cookies must be sent as separate "Set-Cookie" headers. - * - *
- * // Example
- * {@link HttpResponse} res = ...;
- * res.setHeader("Set-Cookie", {@link ServerCookieEncoder}.encode("JSESSIONID", "1234"));
- * 
- * - * @see ServerCookieDecoder - */ -public final class ServerCookieEncoder extends CookieEncoder { - - /** - * Strict encoder that validates that name and value chars are in the valid scope - * defined in RFC6265, and (for methods that accept multiple cookies) that only - * one cookie is encoded with any given name. (If multiple cookies have the same - * name, the last one is the one that is encoded.) - */ - public static final ServerCookieEncoder STRICT = new ServerCookieEncoder(true); - - /** - * Lax instance that doesn't validate name and value, and that allows multiple - * cookies with the same name. - */ - public static final ServerCookieEncoder LAX = new ServerCookieEncoder(false); - - private ServerCookieEncoder(boolean strict) { - super(strict); - } - - /** - * Encodes the specified cookie name-value pair into a Set-Cookie header value. - * - * @param name the cookie name - * @param value the cookie value - * @return a single Set-Cookie header value - */ - public String encode(String name, String value) { - return encode(new DefaultCookie(name, value)); - } - - /** - * Encodes the specified cookie into a Set-Cookie header value. - * - * @param cookie the cookie - * @return a single Set-Cookie header value - */ - public String encode(Cookie cookie) { - final String name = requireNonNull(cookie, "cookie").name(); - final String value = cookie.value() != null ? cookie.value() : ""; - - validateCookie(name, value); - - StringBuilder buf = stringBuilder(); - - if (cookie.wrap()) { - addQuoted(buf, name, value); - } else { - add(buf, name, value); - } - - if (cookie.maxAge() != Long.MIN_VALUE) { - add(buf, CookieHeaderNames.MAX_AGE, cookie.maxAge()); - Date expires = new Date(cookie.maxAge() * 1000 + System.currentTimeMillis()); - buf.append(CookieHeaderNames.EXPIRES); - buf.append('='); - DateFormatter.append(expires, buf); - buf.append(';'); - buf.append(HttpConstants.SP_CHAR); - } - - if (cookie.path() != null) { - add(buf, CookieHeaderNames.PATH, cookie.path()); - } - - if (cookie.domain() != null) { - add(buf, CookieHeaderNames.DOMAIN, cookie.domain()); - } - if (cookie.isSecure()) { - add(buf, CookieHeaderNames.SECURE); - } - if (cookie.isHttpOnly()) { - add(buf, CookieHeaderNames.HTTPONLY); - } - if (cookie instanceof DefaultCookie) { - DefaultCookie c = (DefaultCookie) cookie; - if (c.sameSite() != null) { - add(buf, CookieHeaderNames.SAMESITE, c.sameSite().name()); - } - } - - return stripTrailingSeparator(buf); - } - - /** Deduplicate a list of encoded cookies by keeping only the last instance with a given name. - * - * @param encoded The list of encoded cookies. - * @param nameToLastIndex A map from cookie name to index of last cookie instance. - * @return The encoded list with all but the last instance of a named cookie. - */ - private static List dedup(List encoded, Map nameToLastIndex) { - boolean[] isLastInstance = new boolean[encoded.size()]; - for (int idx : nameToLastIndex.values()) { - isLastInstance[idx] = true; - } - List dedupd = new ArrayList<>(nameToLastIndex.size()); - for (int i = 0, n = encoded.size(); i < n; i++) { - if (isLastInstance[i]) { - dedupd.add(encoded.get(i)); - } - } - return dedupd; - } - - /** - * Batch encodes cookies into Set-Cookie header values. - * - * @param cookies a bunch of cookies - * @return the corresponding bunch of Set-Cookie headers - */ - public List encode(Cookie... cookies) { - if (requireNonNull(cookies, "cookies").length == 0) { - return Collections.emptyList(); - } - - List encoded = new ArrayList<>(cookies.length); - Map nameToIndex = strict && cookies.length > 1 ? new HashMap<>() : null; - boolean hasDupdName = false; - for (int i = 0; i < cookies.length; i++) { - Cookie c = cookies[i]; - encoded.add(encode(c)); - if (nameToIndex != null) { - hasDupdName |= nameToIndex.put(c.name(), i) != null; - } - } - return hasDupdName ? dedup(encoded, nameToIndex) : encoded; - } - - /** - * Batch encodes cookies into Set-Cookie header values. - * - * @param cookies a bunch of cookies - * @return the corresponding bunch of Set-Cookie headers - */ - public List encode(Collection cookies) { - if (requireNonNull(cookies, "cookies").isEmpty()) { - return Collections.emptyList(); - } - - List encoded = new ArrayList<>(cookies.size()); - Map nameToIndex = strict && cookies.size() > 1 ? new HashMap<>() : null; - int i = 0; - boolean hasDupdName = false; - for (Cookie c : cookies) { - encoded.add(encode(c)); - if (nameToIndex != null) { - hasDupdName |= nameToIndex.put(c.name(), i++) != null; - } - } - return hasDupdName ? dedup(encoded, nameToIndex) : encoded; - } - - /** - * Batch encodes cookies into Set-Cookie header values. - * - * @param cookies a bunch of cookies - * @return the corresponding bunch of Set-Cookie headers - */ - public List encode(Iterable cookies) { - Iterator cookiesIt = requireNonNull(cookies, "cookies").iterator(); - if (!cookiesIt.hasNext()) { - return Collections.emptyList(); - } - - List encoded = new ArrayList<>(); - Cookie firstCookie = cookiesIt.next(); - Map nameToIndex = strict && cookiesIt.hasNext() ? new HashMap<>() : null; - int i = 0; - encoded.add(encode(firstCookie)); - boolean hasDupdName = nameToIndex != null && nameToIndex.put(firstCookie.name(), i++) != null; - while (cookiesIt.hasNext()) { - Cookie c = cookiesIt.next(); - encoded.add(encode(c)); - if (nameToIndex != null) { - hasDupdName |= nameToIndex.put(c.name(), i++) != null; - } - } - return hasDupdName ? dedup(encoded, nameToIndex) : encoded; - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/cookie/package-info.java b/codec-http/src/main/java/io/netty/handler/codec/http/cookie/package-info.java deleted file mode 100644 index d67e10b8b5..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/cookie/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * This package contains Cookie related classes. - */ -package io.netty.handler.codec.http.cookie; diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/cors/CorsConfig.java b/codec-http/src/main/java/io/netty/handler/codec/http/cors/CorsConfig.java deleted file mode 100644 index 0a75514fc8..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/cors/CorsConfig.java +++ /dev/null @@ -1,438 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version - * 2.0 (the "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http.cors; - -import io.netty.handler.codec.http.DefaultHttpHeaders; -import io.netty.handler.codec.http.EmptyHttpHeaders; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.util.internal.StringUtil; - -import java.util.Collections; -import java.util.Date; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.concurrent.Callable; - -/** - * Configuration for Cross-Origin Resource Sharing (CORS). - */ -public final class CorsConfig { - - private final Set origins; - private final boolean anyOrigin; - private final boolean enabled; - private final Set exposeHeaders; - private final boolean allowCredentials; - private final long maxAge; - private final Set allowedRequestMethods; - private final Set allowedRequestHeaders; - private final boolean allowNullOrigin; - private final Map> preflightHeaders; - private final boolean shortCircuit; - - CorsConfig(final CorsConfigBuilder builder) { - origins = new LinkedHashSet<>(builder.origins); - anyOrigin = builder.anyOrigin; - enabled = builder.enabled; - exposeHeaders = builder.exposeHeaders; - allowCredentials = builder.allowCredentials; - maxAge = builder.maxAge; - allowedRequestMethods = builder.requestMethods; - allowedRequestHeaders = builder.requestHeaders; - allowNullOrigin = builder.allowNullOrigin; - preflightHeaders = builder.preflightHeaders; - shortCircuit = builder.shortCircuit; - } - - /** - * Determines if support for CORS is enabled. - * - * @return {@code true} if support for CORS is enabled, false otherwise. - */ - public boolean isCorsSupportEnabled() { - return enabled; - } - - /** - * Determines whether a wildcard origin, '*', is supported. - * - * @return {@code boolean} true if any origin is allowed. - */ - public boolean isAnyOriginSupported() { - return anyOrigin; - } - - /** - * Returns the allowed origin. This can either be a wildcard or an origin value. - * - * @return the value that will be used for the CORS response header 'Access-Control-Allow-Origin' - */ - public String origin() { - return origins.isEmpty() ? "*" : origins.iterator().next(); - } - - /** - * Returns the set of allowed origins. - * - * @return {@code Set} the allowed origins. - */ - public Set origins() { - return origins; - } - - /** - * Web browsers may set the 'Origin' request header to 'null' if a resource is loaded - * from the local file system. - * - * If isNullOriginAllowed is true then the server will response with the wildcard for the - * the CORS response header 'Access-Control-Allow-Origin'. - * - * @return {@code true} if a 'null' origin should be supported. - */ - public boolean isNullOriginAllowed() { - return allowNullOrigin; - } - - /** - * Returns a set of headers to be exposed to calling clients. - * - * During a simple CORS request only certain response headers are made available by the - * browser, for example using: - *
-     * xhr.getResponseHeader("Content-Type");
-     * 
- * The headers that are available by default are: - *
    - *
  • Cache-Control
  • - *
  • Content-Language
  • - *
  • Content-Type
  • - *
  • Expires
  • - *
  • Last-Modified
  • - *
  • Pragma
  • - *
- * To expose other headers they need to be specified, which is what this method enables by - * adding the headers names to the CORS 'Access-Control-Expose-Headers' response header. - * - * @return {@code List} a list of the headers to expose. - */ - public Set exposedHeaders() { - return Collections.unmodifiableSet(exposeHeaders); - } - - /** - * Determines if cookies are supported for CORS requests. - * - * By default cookies are not included in CORS requests but if isCredentialsAllowed returns - * true cookies will be added to CORS requests. Setting this value to true will set the - * CORS 'Access-Control-Allow-Credentials' response header to true. - * - * Please note that cookie support needs to be enabled on the client side as well. - * The client needs to opt-in to send cookies by calling: - *
-     * xhr.withCredentials = true;
-     * 
- * The default value for 'withCredentials' is false in which case no cookies are sent. - * Setting this to true will included cookies in cross origin requests. - * - * @return {@code true} if cookies are supported. - */ - public boolean isCredentialsAllowed() { - return allowCredentials; - } - - /** - * Gets the maxAge setting. - * - * When making a preflight request the client has to perform two request with can be inefficient. - * This setting will set the CORS 'Access-Control-Max-Age' response header and enables the - * caching of the preflight response for the specified time. During this time no preflight - * request will be made. - * - * @return {@code long} the time in seconds that a preflight request may be cached. - */ - public long maxAge() { - return maxAge; - } - - /** - * Returns the allowed set of Request Methods. The Http methods that should be returned in the - * CORS 'Access-Control-Request-Method' response header. - * - * @return {@code Set} of {@link HttpMethod}s that represent the allowed Request Methods. - */ - public Set allowedRequestMethods() { - return Collections.unmodifiableSet(allowedRequestMethods); - } - - /** - * Returns the allowed set of Request Headers. - * - * The header names returned from this method will be used to set the CORS - * 'Access-Control-Allow-Headers' response header. - * - * @return {@code Set} of strings that represent the allowed Request Headers. - */ - public Set allowedRequestHeaders() { - return Collections.unmodifiableSet(allowedRequestHeaders); - } - - /** - * Returns HTTP response headers that should be added to a CORS preflight response. - * - * @return {@link HttpHeaders} the HTTP response headers to be added. - */ - public HttpHeaders preflightResponseHeaders() { - if (preflightHeaders.isEmpty()) { - return EmptyHttpHeaders.INSTANCE; - } - final HttpHeaders preflightHeaders = new DefaultHttpHeaders(); - for (Entry> entry : this.preflightHeaders.entrySet()) { - final Object value = getValue(entry.getValue()); - if (value instanceof Iterable) { - preflightHeaders.add(entry.getKey(), (Iterable) value); - } else { - preflightHeaders.add(entry.getKey(), value); - } - } - return preflightHeaders; - } - - /** - * Determines whether a CORS request should be rejected if it's invalid before being - * further processing. - * - * CORS headers are set after a request is processed. This may not always be desired - * and this setting will check that the Origin is valid and if it is not valid no - * further processing will take place, and an error will be returned to the calling client. - * - * @return {@code true} if a CORS request should short-circuit upon receiving an invalid Origin header. - */ - public boolean isShortCircuit() { - return shortCircuit; - } - - /** - * @deprecated Use {@link #isShortCircuit()} instead. - */ - @Deprecated - public boolean isShortCurcuit() { - return isShortCircuit(); - } - - private static T getValue(final Callable callable) { - try { - return callable.call(); - } catch (final Exception e) { - throw new IllegalStateException("Could not generate value for callable [" + callable + ']', e); - } - } - - @Override - public String toString() { - return StringUtil.simpleClassName(this) + "[enabled=" + enabled + - ", origins=" + origins + - ", anyOrigin=" + anyOrigin + - ", exposedHeaders=" + exposeHeaders + - ", isCredentialsAllowed=" + allowCredentials + - ", maxAge=" + maxAge + - ", allowedRequestMethods=" + allowedRequestMethods + - ", allowedRequestHeaders=" + allowedRequestHeaders + - ", preflightHeaders=" + preflightHeaders + ']'; - } - - /** - * @deprecated Use {@link CorsConfigBuilder#forAnyOrigin()} instead. - */ - @Deprecated - public static Builder withAnyOrigin() { - return new Builder(); - } - - /** - * @deprecated Use {@link CorsConfigBuilder#forOrigin(String)} instead. - */ - @Deprecated - public static Builder withOrigin(final String origin) { - if ("*".equals(origin)) { - return new Builder(); - } - return new Builder(origin); - } - - /** - * @deprecated Use {@link CorsConfigBuilder#forOrigins(String...)} instead. - */ - @Deprecated - public static Builder withOrigins(final String... origins) { - return new Builder(origins); - } - - /** - * @deprecated Use {@link CorsConfigBuilder} instead. - */ - @Deprecated - public static class Builder { - - private final CorsConfigBuilder builder; - - /** - * @deprecated Use {@link CorsConfigBuilder} instead. - */ - @Deprecated - public Builder(final String... origins) { - builder = new CorsConfigBuilder(origins); - } - - /** - * @deprecated Use {@link CorsConfigBuilder} instead. - */ - @Deprecated - public Builder() { - builder = new CorsConfigBuilder(); - } - - /** - * @deprecated Use {@link CorsConfigBuilder#allowNullOrigin()} instead. - */ - @Deprecated - public Builder allowNullOrigin() { - builder.allowNullOrigin(); - return this; - } - - /** - * @deprecated Use {@link CorsConfigBuilder#disable()} instead. - */ - @Deprecated - public Builder disable() { - builder.disable(); - return this; - } - - /** - * @deprecated Use {@link CorsConfigBuilder#exposeHeaders(String...)} instead. - */ - @Deprecated - public Builder exposeHeaders(final String... headers) { - builder.exposeHeaders(headers); - return this; - } - - /** - * @deprecated Use {@link CorsConfigBuilder#allowCredentials()} instead. - */ - @Deprecated - public Builder allowCredentials() { - builder.allowCredentials(); - return this; - } - - /** - * @deprecated Use {@link CorsConfigBuilder#maxAge(long)} instead. - */ - @Deprecated - public Builder maxAge(final long max) { - builder.maxAge(max); - return this; - } - - /** - * @deprecated Use {@link CorsConfigBuilder#allowedRequestMethods(HttpMethod...)} instead. - */ - @Deprecated - public Builder allowedRequestMethods(final HttpMethod... methods) { - builder.allowedRequestMethods(methods); - return this; - } - - /** - * @deprecated Use {@link CorsConfigBuilder#allowedRequestHeaders(String...)} instead. - */ - @Deprecated - public Builder allowedRequestHeaders(final String... headers) { - builder.allowedRequestHeaders(headers); - return this; - } - - /** - * @deprecated Use {@link CorsConfigBuilder#preflightResponseHeader(CharSequence, Object...)} instead. - */ - @Deprecated - public Builder preflightResponseHeader(final CharSequence name, final Object... values) { - builder.preflightResponseHeader(name, values); - return this; - } - - /** - * @deprecated Use {@link CorsConfigBuilder#preflightResponseHeader(CharSequence, Iterable)} instead. - */ - @Deprecated - public Builder preflightResponseHeader(final CharSequence name, final Iterable value) { - builder.preflightResponseHeader(name, value); - return this; - } - - /** - * @deprecated Use {@link CorsConfigBuilder#preflightResponseHeader(CharSequence, Callable)} instead. - */ - @Deprecated - public Builder preflightResponseHeader(final String name, final Callable valueGenerator) { - builder.preflightResponseHeader(name, valueGenerator); - return this; - } - - /** - * @deprecated Use {@link CorsConfigBuilder#noPreflightResponseHeaders()} instead. - */ - @Deprecated - public Builder noPreflightResponseHeaders() { - builder.noPreflightResponseHeaders(); - return this; - } - - /** - * @deprecated Use {@link CorsConfigBuilder#build()} instead. - */ - @Deprecated - public CorsConfig build() { - return builder.build(); - } - - /** - * @deprecated Use {@link CorsConfigBuilder#shortCircuit()} instead. - */ - @Deprecated - public Builder shortCurcuit() { - builder.shortCircuit(); - return this; - } - } - - /** - * @deprecated Removed without alternatives. - */ - @Deprecated - public static final class DateValueGenerator implements Callable { - - @Override - public Date call() throws Exception { - return new Date(); - } - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/cors/CorsConfigBuilder.java b/codec-http/src/main/java/io/netty/handler/codec/http/cors/CorsConfigBuilder.java deleted file mode 100644 index c415bdcc84..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/cors/CorsConfigBuilder.java +++ /dev/null @@ -1,406 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version - * 2.0 (the "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http.cors; - -import static io.netty.util.internal.ObjectUtil.checkNotNullWithIAE; - -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpMethod; - -import java.util.Arrays; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.Callable; - -/** - * Builder used to configure and build a {@link CorsConfig} instance. - */ -public final class CorsConfigBuilder { - - /** - * Creates a Builder instance with it's origin set to '*'. - * - * @return Builder to support method chaining. - */ - public static CorsConfigBuilder forAnyOrigin() { - return new CorsConfigBuilder(); - } - - /** - * Creates a {@link CorsConfigBuilder} instance with the specified origin. - * - * @return {@link CorsConfigBuilder} to support method chaining. - */ - public static CorsConfigBuilder forOrigin(final String origin) { - if ("*".equals(origin)) { - return new CorsConfigBuilder(); - } - return new CorsConfigBuilder(origin); - } - - /** - * Creates a {@link CorsConfigBuilder} instance with the specified origins. - * - * @return {@link CorsConfigBuilder} to support method chaining. - */ - public static CorsConfigBuilder forOrigins(final String... origins) { - return new CorsConfigBuilder(origins); - } - - final Set origins; - final boolean anyOrigin; - boolean allowNullOrigin; - boolean enabled = true; - boolean allowCredentials; - final Set exposeHeaders = new HashSet<>(); - long maxAge; - final Set requestMethods = new HashSet<>(); - final Set requestHeaders = new HashSet<>(); - final Map> preflightHeaders = new HashMap<>(); - private boolean noPreflightHeaders; - boolean shortCircuit; - - /** - * Creates a new Builder instance with the origin passed in. - * - * @param origins the origin to be used for this builder. - */ - CorsConfigBuilder(final String... origins) { - this.origins = new LinkedHashSet<>(Arrays.asList(origins)); - anyOrigin = false; - } - - /** - * Creates a new Builder instance allowing any origin, "*" which is the - * wildcard origin. - * - */ - CorsConfigBuilder() { - anyOrigin = true; - origins = Collections.emptySet(); - } - - /** - * Web browsers may set the 'Origin' request header to 'null' if a resource is loaded - * from the local file system. Calling this method will enable a successful CORS response - * with a {@code "null"} value for the CORS response header 'Access-Control-Allow-Origin'. - * - * @return {@link CorsConfigBuilder} to support method chaining. - */ - public CorsConfigBuilder allowNullOrigin() { - allowNullOrigin = true; - return this; - } - - /** - * Disables CORS support. - * - * @return {@link CorsConfigBuilder} to support method chaining. - */ - public CorsConfigBuilder disable() { - enabled = false; - return this; - } - - /** - * Specifies the headers to be exposed to calling clients. - * - * During a simple CORS request, only certain response headers are made available by the - * browser, for example using: - *
-     * xhr.getResponseHeader("Content-Type");
-     * 
- * - * The headers that are available by default are: - *
    - *
  • Cache-Control
  • - *
  • Content-Language
  • - *
  • Content-Type
  • - *
  • Expires
  • - *
  • Last-Modified
  • - *
  • Pragma
  • - *
- * - * To expose other headers they need to be specified which is what this method enables by - * adding the headers to the CORS 'Access-Control-Expose-Headers' response header. - * - * @param headers the values to be added to the 'Access-Control-Expose-Headers' response header - * @return {@link CorsConfigBuilder} to support method chaining. - */ - public CorsConfigBuilder exposeHeaders(final String... headers) { - exposeHeaders.addAll(Arrays.asList(headers)); - return this; - } - - /** - * Specifies the headers to be exposed to calling clients. - * - * During a simple CORS request, only certain response headers are made available by the - * browser, for example using: - *
-     * xhr.getResponseHeader(HttpHeaderNames.CONTENT_TYPE);
-     * 
- * - * The headers that are available by default are: - *
    - *
  • Cache-Control
  • - *
  • Content-Language
  • - *
  • Content-Type
  • - *
  • Expires
  • - *
  • Last-Modified
  • - *
  • Pragma
  • - *
- * - * To expose other headers they need to be specified which is what this method enables by - * adding the headers to the CORS 'Access-Control-Expose-Headers' response header. - * - * @param headers the values to be added to the 'Access-Control-Expose-Headers' response header - * @return {@link CorsConfigBuilder} to support method chaining. - */ - public CorsConfigBuilder exposeHeaders(final CharSequence... headers) { - for (CharSequence header: headers) { - exposeHeaders.add(header.toString()); - } - return this; - } - - /** - * By default cookies are not included in CORS requests, but this method will enable cookies to - * be added to CORS requests. Calling this method will set the CORS 'Access-Control-Allow-Credentials' - * response header to true. - * - * Please note, that cookie support needs to be enabled on the client side as well. - * The client needs to opt-in to send cookies by calling: - *
-     * xhr.withCredentials = true;
-     * 
- * The default value for 'withCredentials' is false in which case no cookies are sent. - * Setting this to true will included cookies in cross origin requests. - * - * @return {@link CorsConfigBuilder} to support method chaining. - */ - public CorsConfigBuilder allowCredentials() { - allowCredentials = true; - return this; - } - - /** - * When making a preflight request the client has to perform two request with can be inefficient. - * This setting will set the CORS 'Access-Control-Max-Age' response header and enables the - * caching of the preflight response for the specified time. During this time no preflight - * request will be made. - * - * @param max the maximum time, in seconds, that the preflight response may be cached. - * @return {@link CorsConfigBuilder} to support method chaining. - */ - public CorsConfigBuilder maxAge(final long max) { - maxAge = max; - return this; - } - - /** - * Specifies the allowed set of HTTP Request Methods that should be returned in the - * CORS 'Access-Control-Request-Method' response header. - * - * @param methods the {@link HttpMethod}s that should be allowed. - * @return {@link CorsConfigBuilder} to support method chaining. - */ - public CorsConfigBuilder allowedRequestMethods(final HttpMethod... methods) { - requestMethods.addAll(Arrays.asList(methods)); - return this; - } - - /** - * Specifies the if headers that should be returned in the CORS 'Access-Control-Allow-Headers' - * response header. - * - * If a client specifies headers on the request, for example by calling: - *
-     * xhr.setRequestHeader('My-Custom-Header', "SomeValue");
-     * 
- * the server will receive the above header name in the 'Access-Control-Request-Headers' of the - * preflight request. The server will then decide if it allows this header to be sent for the - * real request (remember that a preflight is not the real request but a request asking the server - * if it allow a request). - * - * @param headers the headers to be added to the preflight 'Access-Control-Allow-Headers' response header. - * @return {@link CorsConfigBuilder} to support method chaining. - */ - public CorsConfigBuilder allowedRequestHeaders(final String... headers) { - requestHeaders.addAll(Arrays.asList(headers)); - return this; - } - - /** - * Specifies the if headers that should be returned in the CORS 'Access-Control-Allow-Headers' - * response header. - * - * If a client specifies headers on the request, for example by calling: - *
-     * xhr.setRequestHeader('My-Custom-Header', "SomeValue");
-     * 
- * the server will receive the above header name in the 'Access-Control-Request-Headers' of the - * preflight request. The server will then decide if it allows this header to be sent for the - * real request (remember that a preflight is not the real request but a request asking the server - * if it allow a request). - * - * @param headers the headers to be added to the preflight 'Access-Control-Allow-Headers' response header. - * @return {@link CorsConfigBuilder} to support method chaining. - */ - public CorsConfigBuilder allowedRequestHeaders(final CharSequence... headers) { - for (CharSequence header: headers) { - requestHeaders.add(header.toString()); - } - return this; - } - - /** - * Returns HTTP response headers that should be added to a CORS preflight response. - * - * An intermediary like a load balancer might require that a CORS preflight request - * have certain headers set. This enables such headers to be added. - * - * @param name the name of the HTTP header. - * @param values the values for the HTTP header. - * @return {@link CorsConfigBuilder} to support method chaining. - */ - public CorsConfigBuilder preflightResponseHeader(final CharSequence name, final Object... values) { - if (values.length == 1) { - preflightHeaders.put(name, new ConstantValueGenerator(values[0])); - } else { - preflightResponseHeader(name, Arrays.asList(values)); - } - return this; - } - - /** - * Returns HTTP response headers that should be added to a CORS preflight response. - * - * An intermediary like a load balancer might require that a CORS preflight request - * have certain headers set. This enables such headers to be added. - * - * @param name the name of the HTTP header. - * @param value the values for the HTTP header. - * @param the type of values that the Iterable contains. - * @return {@link CorsConfigBuilder} to support method chaining. - */ - public CorsConfigBuilder preflightResponseHeader(final CharSequence name, final Iterable value) { - preflightHeaders.put(name, new ConstantValueGenerator(value)); - return this; - } - - /** - * Returns HTTP response headers that should be added to a CORS preflight response. - * - * An intermediary like a load balancer might require that a CORS preflight request - * have certain headers set. This enables such headers to be added. - * - * Some values must be dynamically created when the HTTP response is created, for - * example the 'Date' response header. This can be accomplished by using a Callable - * which will have its 'call' method invoked when the HTTP response is created. - * - * @param name the name of the HTTP header. - * @param valueGenerator a Callable which will be invoked at HTTP response creation. - * @param the type of the value that the Callable can return. - * @return {@link CorsConfigBuilder} to support method chaining. - */ - public CorsConfigBuilder preflightResponseHeader(final CharSequence name, final Callable valueGenerator) { - preflightHeaders.put(name, valueGenerator); - return this; - } - - /** - * Specifies that no preflight response headers should be added to a preflight response. - * - * @return {@link CorsConfigBuilder} to support method chaining. - */ - public CorsConfigBuilder noPreflightResponseHeaders() { - noPreflightHeaders = true; - return this; - } - - /** - * Specifies that a CORS request should be rejected if it's invalid before being - * further processing. - * - * CORS headers are set after a request is processed. This may not always be desired - * and this setting will check that the Origin is valid and if it is not valid no - * further processing will take place, and an error will be returned to the calling client. - * - * @return {@link CorsConfigBuilder} to support method chaining. - */ - public CorsConfigBuilder shortCircuit() { - shortCircuit = true; - return this; - } - - /** - * Builds a {@link CorsConfig} with settings specified by previous method calls. - * - * @return {@link CorsConfig} the configured CorsConfig instance. - */ - public CorsConfig build() { - if (preflightHeaders.isEmpty() && !noPreflightHeaders) { - preflightHeaders.put(HttpHeaderNames.DATE, DateValueGenerator.INSTANCE); - preflightHeaders.put(HttpHeaderNames.CONTENT_LENGTH, new ConstantValueGenerator("0")); - } - return new CorsConfig(this); - } - - /** - * This class is used for preflight HTTP response values that do not need to be - * generated, but instead the value is "static" in that the same value will be returned - * for each call. - */ - private static final class ConstantValueGenerator implements Callable { - - private final Object value; - - /** - * Sole constructor. - * - * @param value the value that will be returned when the call method is invoked. - */ - private ConstantValueGenerator(final Object value) { - this.value = checkNotNullWithIAE(value, "value"); - } - - @Override - public Object call() { - return value; - } - } - - /** - * This callable is used for the DATE preflight HTTP response HTTP header. - * It's value must be generated when the response is generated, hence will be - * different for every call. - */ - private static final class DateValueGenerator implements Callable { - - static final DateValueGenerator INSTANCE = new DateValueGenerator(); - - @Override - public Date call() throws Exception { - return new Date(); - } - } -} 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 deleted file mode 100644 index a6996f5f37..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/cors/CorsHandler.java +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version - * 2.0 (the "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http.cors; - -import io.netty.channel.ChannelFutureListeners; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http.DefaultFullHttpResponse; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.HttpUtil; -import io.netty.util.concurrent.Future; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.util.Collections; -import java.util.List; - -import static io.netty.handler.codec.http.HttpMethod.OPTIONS; -import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN; -import static io.netty.handler.codec.http.HttpResponseStatus.OK; -import static io.netty.util.ReferenceCountUtil.release; -import static io.netty.util.internal.ObjectUtil.checkNonEmpty; -import static java.util.Objects.requireNonNull; - -/** - * Handles Cross Origin Resource Sharing (CORS) requests. - *

- * This handler can be configured using one or more {@link CorsConfig}, please - * refer to this class for details about the configuration options available. - */ -public class CorsHandler implements ChannelHandler { - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(CorsHandler.class); - private static final String ANY_ORIGIN = "*"; - private static final String NULL_ORIGIN = "null"; - private CorsConfig config; - - private HttpRequest request; - private final List configList; - private final boolean isShortCircuit; - - /** - * Creates a new instance with a single {@link CorsConfig}. - */ - public CorsHandler(final CorsConfig config) { - this(Collections.singletonList(requireNonNull(config, "config")), config.isShortCircuit()); - } - - /** - * Creates a new instance with the specified config list. If more than one - * config matches a certain origin, the first in the List will be used. - * - * @param configList List of {@link CorsConfig} - * @param isShortCircuit Same as {@link CorsConfig#isShortCurcuit()} but applicable to all supplied configs. - */ - public CorsHandler(final List configList, boolean isShortCircuit) { - checkNonEmpty(configList, "configList"); - this.configList = configList; - this.isShortCircuit = isShortCircuit; - } - - @Override - public void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception { - if (msg instanceof HttpRequest) { - request = (HttpRequest) msg; - final String origin = request.headers().get(HttpHeaderNames.ORIGIN); - config = getForOrigin(origin); - if (isPreflightRequest(request)) { - handlePreflight(ctx, request); - return; - } - if (isShortCircuit && !(origin == null || config != null)) { - forbidden(ctx, request); - return; - } - } - ctx.fireChannelRead(msg); - } - - private void handlePreflight(final ChannelHandlerContext ctx, final HttpRequest request) { - final HttpResponse response = new DefaultFullHttpResponse(request.protocolVersion(), OK, true, true); - if (setOrigin(response)) { - setAllowMethods(response); - setAllowHeaders(response); - setAllowCredentials(response); - setMaxAge(response); - setPreflightHeaders(response); - } - if (!response.headers().contains(HttpHeaderNames.CONTENT_LENGTH)) { - response.headers().set(HttpHeaderNames.CONTENT_LENGTH, HttpHeaderValues.ZERO); - } - release(request); - respond(ctx, request, response); - } - - /** - * This is a non CORS specification feature which enables the setting of preflight - * response headers that might be required by intermediaries. - * - * @param response the HttpResponse to which the preflight response headers should be added. - */ - private void setPreflightHeaders(final HttpResponse response) { - response.headers().add(config.preflightResponseHeaders()); - } - - private CorsConfig getForOrigin(String requestOrigin) { - for (CorsConfig corsConfig : configList) { - if (corsConfig.isAnyOriginSupported()) { - return corsConfig; - } - if (corsConfig.origins().contains(requestOrigin)) { - return corsConfig; - } - if (corsConfig.isNullOriginAllowed() || NULL_ORIGIN.equals(requestOrigin)) { - return corsConfig; - } - } - return null; - } - - private boolean setOrigin(final HttpResponse response) { - final String origin = request.headers().get(HttpHeaderNames.ORIGIN); - if (origin != null && config != null) { - if (NULL_ORIGIN.equals(origin) && config.isNullOriginAllowed()) { - setNullOrigin(response); - return true; - } - if (config.isAnyOriginSupported()) { - if (config.isCredentialsAllowed()) { - echoRequestOrigin(response); - setVaryHeader(response); - } else { - setAnyOrigin(response); - } - return true; - } - if (config.origins().contains(origin)) { - setOrigin(response, origin); - setVaryHeader(response); - return true; - } - logger.debug("Request origin [{}]] was not among the configured origins [{}]", origin, config.origins()); - } - return false; - } - - private void echoRequestOrigin(final HttpResponse response) { - setOrigin(response, request.headers().get(HttpHeaderNames.ORIGIN)); - } - - private static void setVaryHeader(final HttpResponse response) { - response.headers().set(HttpHeaderNames.VARY, HttpHeaderNames.ORIGIN); - } - - private static void setAnyOrigin(final HttpResponse response) { - setOrigin(response, ANY_ORIGIN); - } - - private static void setNullOrigin(final HttpResponse response) { - setOrigin(response, NULL_ORIGIN); - } - - private static void setOrigin(final HttpResponse response, final String origin) { - response.headers().set(HttpHeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN, origin); - } - - private void setAllowCredentials(final HttpResponse response) { - if (config.isCredentialsAllowed() - && !response.headers().get(HttpHeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN).equals(ANY_ORIGIN)) { - response.headers().set(HttpHeaderNames.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true"); - } - } - - private static boolean isPreflightRequest(final HttpRequest request) { - final HttpHeaders headers = request.headers(); - return OPTIONS.equals(request.method()) && - headers.contains(HttpHeaderNames.ORIGIN) && - headers.contains(HttpHeaderNames.ACCESS_CONTROL_REQUEST_METHOD); - } - - private void setExposeHeaders(final HttpResponse response) { - if (!config.exposedHeaders().isEmpty()) { - response.headers().set(HttpHeaderNames.ACCESS_CONTROL_EXPOSE_HEADERS, config.exposedHeaders()); - } - } - - private void setAllowMethods(final HttpResponse response) { - response.headers().set(HttpHeaderNames.ACCESS_CONTROL_ALLOW_METHODS, config.allowedRequestMethods()); - } - - private void setAllowHeaders(final HttpResponse response) { - response.headers().set(HttpHeaderNames.ACCESS_CONTROL_ALLOW_HEADERS, config.allowedRequestHeaders()); - } - - private void setMaxAge(final HttpResponse response) { - response.headers().set(HttpHeaderNames.ACCESS_CONTROL_MAX_AGE, config.maxAge()); - } - - @Override - public Future write(final ChannelHandlerContext ctx, final Object msg) { - if (config != null && config.isCorsSupportEnabled() && msg instanceof HttpResponse) { - final HttpResponse response = (HttpResponse) msg; - if (setOrigin(response)) { - setAllowCredentials(response); - setExposeHeaders(response); - } - } - return ctx.write(msg); - } - - private static void forbidden(final ChannelHandlerContext ctx, final HttpRequest request) { - HttpResponse response = new DefaultFullHttpResponse( - request.protocolVersion(), FORBIDDEN, ctx.alloc().buffer(0)); - response.headers().set(HttpHeaderNames.CONTENT_LENGTH, HttpHeaderValues.ZERO); - release(request); - respond(ctx, request, response); - } - - private static void respond( - final ChannelHandlerContext ctx, - final HttpRequest request, - final HttpResponse response) { - - final boolean keepAlive = HttpUtil.isKeepAlive(request); - - HttpUtil.setKeepAlive(response, keepAlive); - - Future future = ctx.writeAndFlush(response); - if (!keepAlive) { - future.addListener(ctx, ChannelFutureListeners.CLOSE); - } - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/cors/package-info.java b/codec-http/src/main/java/io/netty/handler/codec/http/cors/package-info.java deleted file mode 100644 index 6c1570f4f7..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/cors/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * This package contains Cross Origin Resource Sharing (CORS) related classes. - */ -package io.netty.handler.codec.http.cors; diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/AbstractDiskHttpData.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/AbstractDiskHttpData.java deleted file mode 100644 index f97317d9ab..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/AbstractDiskHttpData.java +++ /dev/null @@ -1,461 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.http.HttpConstants; -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 java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.RandomAccessFile; -import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; -import java.nio.charset.Charset; -import java.nio.file.Files; - -import static io.netty.buffer.Unpooled.EMPTY_BUFFER; -import static io.netty.buffer.Unpooled.wrappedBuffer; -import static java.util.Objects.requireNonNull; - -/** - * Abstract Disk HttpData implementation - */ -public abstract class AbstractDiskHttpData extends AbstractHttpData { - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(AbstractDiskHttpData.class); - - private File file; - private boolean isRenamed; - private FileChannel fileChannel; - - protected AbstractDiskHttpData(String name, Charset charset, long size) { - super(name, charset, size); - } - - /** - * - * @return the real DiskFilename (basename) - */ - protected abstract String getDiskFilename(); - /** - * - * @return the default prefix - */ - protected abstract String getPrefix(); - /** - * - * @return the default base Directory - */ - protected abstract String getBaseDirectory(); - /** - * - * @return the default postfix - */ - protected abstract String getPostfix(); - /** - * - * @return True if the file should be deleted on Exit by default - */ - protected abstract boolean deleteOnExit(); - - /** - * @return a new Temp File from getDiskFilename(), default prefix, postfix and baseDirectory - */ - private File tempFile() throws IOException { - String newpostfix; - String diskFilename = getDiskFilename(); - if (diskFilename != null) { - newpostfix = '_' + diskFilename; - } else { - newpostfix = getPostfix(); - } - File tmpFile; - if (getBaseDirectory() == null) { - // create a temporary file - tmpFile = PlatformDependent.createTempFile(getPrefix(), newpostfix, null); - } else { - tmpFile = PlatformDependent.createTempFile(getPrefix(), newpostfix, new File( - getBaseDirectory())); - } - if (deleteOnExit()) { - // See https://github.com/netty/netty/issues/10351 - DeleteFileOnExitHook.add(tmpFile.getPath()); - } - return tmpFile; - } - - @Override - public void setContent(ByteBuf buffer) throws IOException { - requireNonNull(buffer, "buffer"); - try { - size = buffer.readableBytes(); - checkSize(size); - if (definedSize > 0 && definedSize < size) { - throw new IOException("Out of size: " + size + " > " + definedSize); - } - if (file == null) { - file = tempFile(); - } - if (buffer.readableBytes() == 0) { - // empty file - if (!file.createNewFile()) { - if (file.length() == 0) { - return; - } else { - if (!file.delete() || !file.createNewFile()) { - throw new IOException("file exists already: " + file); - } - } - } - return; - } - try (RandomAccessFile accessFile = new RandomAccessFile(file, "rw")) { - accessFile.setLength(0); - FileChannel localfileChannel = accessFile.getChannel(); - ByteBuffer byteBuffer = buffer.nioBuffer(); - int written = 0; - while (written < size) { - written += localfileChannel.write(byteBuffer); - } - buffer.readerIndex(buffer.readerIndex() + written); - localfileChannel.force(false); - } - setCompleted(); - } finally { - // Release the buffer as it was retained before and we not need a reference to it at all - // See https://github.com/netty/netty/issues/1516 - buffer.release(); - } - } - - @Override - public void addContent(ByteBuf buffer, boolean last) - throws IOException { - if (buffer != null) { - try { - int localsize = buffer.readableBytes(); - checkSize(size + localsize); - if (definedSize > 0 && definedSize < size + localsize) { - throw new IOException("Out of size: " + (size + localsize) + - " > " + definedSize); - } - if (file == null) { - file = tempFile(); - } - if (fileChannel == null) { - RandomAccessFile accessFile = new RandomAccessFile(file, "rw"); - fileChannel = accessFile.getChannel(); - } - int remaining = localsize; - long position = fileChannel.position(); - int index = buffer.readerIndex(); - while (remaining > 0) { - int written = buffer.getBytes(index, fileChannel, position, remaining); - if (written < 0) { - break; - } - remaining -= written; - position += written; - index += written; - } - fileChannel.position(position); - buffer.readerIndex(index); - size += localsize - remaining; - } finally { - // Release the buffer as it was retained before and we not need a reference to it at all - // See https://github.com/netty/netty/issues/1516 - buffer.release(); - } - } - if (last) { - if (file == null) { - file = tempFile(); - } - if (fileChannel == null) { - RandomAccessFile accessFile = new RandomAccessFile(file, "rw"); - fileChannel = accessFile.getChannel(); - } - try { - fileChannel.force(false); - } finally { - fileChannel.close(); - } - fileChannel = null; - setCompleted(); - } else { - requireNonNull(buffer, "buffer"); - } - } - - @Override - public void setContent(File file) throws IOException { - long size = file.length(); - checkSize(size); - this.size = size; - if (this.file != null) { - delete(); - } - this.file = file; - isRenamed = true; - setCompleted(); - } - - @Override - public void setContent(InputStream inputStream) throws IOException { - requireNonNull(inputStream, "inputStream"); - if (file != null) { - delete(); - } - file = tempFile(); - int written = 0; - try (RandomAccessFile accessFile = new RandomAccessFile(file, "rw")) { - accessFile.setLength(0); - FileChannel localfileChannel = accessFile.getChannel(); - byte[] bytes = new byte[4096 * 4]; - ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); - int read = inputStream.read(bytes); - while (read > 0) { - byteBuffer.position(read).flip(); - written += localfileChannel.write(byteBuffer); - checkSize(written); - read = inputStream.read(bytes); - } - localfileChannel.force(false); - } - size = written; - if (definedSize > 0 && definedSize < size) { - if (!file.delete()) { - logger.warn("Failed to delete: {}", file); - } - file = null; - throw new IOException("Out of size: " + size + " > " + definedSize); - } - isRenamed = true; - setCompleted(); - } - - @Override - public void delete() { - if (fileChannel != null) { - try { - fileChannel.force(false); - } catch (IOException e) { - logger.warn("Failed to force.", e); - } finally { - try { - fileChannel.close(); - } catch (IOException e) { - logger.warn("Failed to close a file.", e); - } - } - fileChannel = null; - } - if (!isRenamed) { - String filePath = null; - - if (file != null && file.exists()) { - filePath = file.getPath(); - if (!file.delete()) { - filePath = null; - logger.warn("Failed to delete: {}", file); - } - } - - // If you turn on deleteOnExit make sure it is executed. - if (deleteOnExit() && filePath != null) { - DeleteFileOnExitHook.remove(filePath); - } - file = null; - } - } - - @Override - public byte[] get() throws IOException { - if (file == null) { - return EmptyArrays.EMPTY_BYTES; - } - return readFrom(file); - } - - @Override - public ByteBuf getByteBuf() throws IOException { - if (file == null) { - return EMPTY_BUFFER; - } - byte[] array = readFrom(file); - return wrappedBuffer(array); - } - - @Override - public ByteBuf getChunk(int length) throws IOException { - if (file == null || length == 0) { - return EMPTY_BUFFER; - } - if (fileChannel == null) { - RandomAccessFile accessFile = new RandomAccessFile(file, "r"); - fileChannel = accessFile.getChannel(); - } - int read = 0; - ByteBuffer byteBuffer = ByteBuffer.allocate(length); - try { - while (read < length) { - int readnow = fileChannel.read(byteBuffer); - if (readnow == -1) { - fileChannel.close(); - fileChannel = null; - break; - } - read += readnow; - } - } catch (IOException e) { - fileChannel.close(); - fileChannel = null; - throw e; - } - if (read == 0) { - return EMPTY_BUFFER; - } - byteBuffer.flip(); - ByteBuf buffer = wrappedBuffer(byteBuffer); - buffer.readerIndex(0); - buffer.writerIndex(read); - return buffer; - } - - @Override - public String getString() throws IOException { - return getString(HttpConstants.DEFAULT_CHARSET); - } - - @Override - public String getString(Charset encoding) throws IOException { - if (file == null) { - return ""; - } - if (encoding == null) { - byte[] array = readFrom(file); - return new String(array, HttpConstants.DEFAULT_CHARSET.name()); - } - byte[] array = readFrom(file); - return new String(array, encoding.name()); - } - - @Override - public boolean isInMemory() { - return false; - } - - @Override - public boolean renameTo(File dest) throws IOException { - requireNonNull(dest, "dest"); - if (file == null) { - throw new IOException("No file defined so cannot be renamed"); - } - if (!file.renameTo(dest)) { - // must copy - IOException exception = null; - RandomAccessFile inputAccessFile = null; - RandomAccessFile outputAccessFile = null; - long chunkSize = 8196; - long position = 0; - try { - inputAccessFile = new RandomAccessFile(file, "r"); - outputAccessFile = new RandomAccessFile(dest, "rw"); - FileChannel in = inputAccessFile.getChannel(); - FileChannel out = outputAccessFile.getChannel(); - while (position < size) { - if (chunkSize < size - position) { - chunkSize = size - position; - } - position += in.transferTo(position, chunkSize, out); - } - } catch (IOException e) { - exception = e; - } finally { - if (inputAccessFile != null) { - try { - inputAccessFile.close(); - } catch (IOException e) { - if (exception == null) { // Choose to report the first exception - exception = e; - } else { - logger.warn("Multiple exceptions detected, the following will be suppressed {}", e); - } - } - } - if (outputAccessFile != null) { - try { - outputAccessFile.close(); - } catch (IOException e) { - if (exception == null) { // Choose to report the first exception - exception = e; - } else { - logger.warn("Multiple exceptions detected, the following will be suppressed {}", e); - } - } - } - } - if (exception != null) { - throw exception; - } - if (position == size) { - if (!file.delete()) { - logger.warn("Failed to delete: {}", file); - } - file = dest; - isRenamed = true; - return true; - } else { - if (!dest.delete()) { - logger.warn("Failed to delete: {}", dest); - } - return false; - } - } - file = dest; - isRenamed = true; - return true; - } - - /** - * Utility function - * - * @return the array of bytes - */ - private static byte[] readFrom(File src) throws IOException { - return Files.readAllBytes(src.toPath()); - } - - @Override - public File getFile() throws IOException { - return file; - } - - @Override - public HttpData touch() { - return this; - } - - @Override - public HttpData touch(Object hint) { - return this; - } -} 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 deleted file mode 100644 index 6bb6decea8..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/AbstractHttpData.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import static java.util.Objects.requireNonNull; -import static io.netty.util.internal.ObjectUtil.checkNonEmpty; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelException; -import io.netty.handler.codec.http.HttpConstants; -import io.netty.util.AbstractReferenceCounted; - -import java.io.IOException; -import java.nio.charset.Charset; -import java.util.regex.Pattern; - -/** - * Abstract HttpData implementation - */ -public abstract class AbstractHttpData extends AbstractReferenceCounted implements HttpData { - - private static final Pattern STRIP_PATTERN = Pattern.compile("(?:^\\s+|\\s+$|\\n)"); - private static final Pattern REPLACE_PATTERN = Pattern.compile("[\\r\\t]"); - - private final String name; - protected long definedSize; - protected long size; - private Charset charset = HttpConstants.DEFAULT_CHARSET; - private boolean completed; - private long maxSize = DefaultHttpDataFactory.MAXSIZE; - - protected AbstractHttpData(String name, Charset charset, long size) { - requireNonNull(name, "name"); - - name = REPLACE_PATTERN.matcher(name).replaceAll(" "); - name = STRIP_PATTERN.matcher(name).replaceAll(""); - - this.name = checkNonEmpty(name, "name"); - if (charset != null) { - setCharset(charset); - } - definedSize = size; - } - - @Override - public long getMaxSize() { - return maxSize; - } - - @Override - public void setMaxSize(long maxSize) { - this.maxSize = maxSize; - } - - @Override - public void checkSize(long newSize) throws IOException { - if (maxSize >= 0 && newSize > maxSize) { - throw new IOException("Size exceed allowed maximum capacity"); - } - } - - @Override - public String getName() { - return name; - } - - @Override - public boolean isCompleted() { - return completed; - } - - protected void setCompleted() { - completed = true; - } - - @Override - public Charset getCharset() { - return charset; - } - - @Override - public void setCharset(Charset charset) { - requireNonNull(charset, "charset"); - this.charset = charset; - } - - @Override - public long length() { - return size; - } - - @Override - public long definedLength() { - return definedSize; - } - - @Override - public ByteBuf content() { - try { - return getByteBuf(); - } catch (IOException e) { - throw new ChannelException(e); - } - } - - @Override - protected void deallocate() { - delete(); - } - - @Override - public HttpData retain() { - super.retain(); - return this; - } - - @Override - public HttpData retain(int increment) { - super.retain(increment); - return this; - } - - @Override - public abstract HttpData touch(); - - @Override - public abstract HttpData touch(Object hint); -} 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 deleted file mode 100644 index 545a5a95b3..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/AbstractMemoryHttpData.java +++ /dev/null @@ -1,301 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.CompositeByteBuf; -import io.netty.handler.codec.http.HttpConstants; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.RandomAccessFile; -import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; -import java.nio.charset.Charset; - -import static io.netty.buffer.Unpooled.EMPTY_BUFFER; -import static io.netty.buffer.Unpooled.buffer; -import static io.netty.buffer.Unpooled.compositeBuffer; -import static io.netty.buffer.Unpooled.wrappedBuffer; -import static java.util.Objects.requireNonNull; - -/** - * Abstract Memory HttpData implementation - */ -public abstract class AbstractMemoryHttpData extends AbstractHttpData { - - private ByteBuf byteBuf; - private int chunkPosition; - - protected AbstractMemoryHttpData(String name, Charset charset, long size) { - super(name, charset, size); - byteBuf = EMPTY_BUFFER; - } - - @Override - public void setContent(ByteBuf buffer) throws IOException { - requireNonNull(buffer, "buffer"); - long localsize = buffer.readableBytes(); - try { - checkSize(localsize); - } catch (IOException e) { - buffer.release(); - throw e; - } - if (definedSize > 0 && definedSize < localsize) { - buffer.release(); - throw new IOException("Out of size: " + localsize + " > " + - definedSize); - } - if (byteBuf != null) { - byteBuf.release(); - } - byteBuf = buffer; - size = localsize; - setCompleted(); - } - - @Override - public void setContent(InputStream inputStream) throws IOException { - requireNonNull(inputStream, "inputStream"); - byte[] bytes = new byte[4096 * 4]; - ByteBuf buffer = buffer(); - int written = 0; - try { - int read = inputStream.read(bytes); - while (read > 0) { - buffer.writeBytes(bytes, 0, read); - written += read; - checkSize(written); - read = inputStream.read(bytes); - } - } catch (IOException e) { - buffer.release(); - throw e; - } - size = written; - if (definedSize > 0 && definedSize < size) { - buffer.release(); - throw new IOException("Out of size: " + size + " > " + definedSize); - } - if (byteBuf != null) { - byteBuf.release(); - } - byteBuf = buffer; - setCompleted(); - } - - @Override - public void addContent(ByteBuf buffer, boolean last) - throws IOException { - if (buffer != null) { - long localsize = buffer.readableBytes(); - try { - checkSize(size + localsize); - } catch (IOException e) { - buffer.release(); - throw e; - } - if (definedSize > 0 && definedSize < size + localsize) { - buffer.release(); - throw new IOException("Out of size: " + (size + localsize) + - " > " + definedSize); - } - size += localsize; - if (byteBuf == null) { - byteBuf = buffer; - } else if (localsize == 0) { - // Nothing to add and byteBuf already exists - buffer.release(); - } else if (byteBuf.readableBytes() == 0) { - // Previous buffer is empty, so just replace it - byteBuf.release(); - byteBuf = buffer; - } else if (byteBuf instanceof CompositeByteBuf) { - CompositeByteBuf cbb = (CompositeByteBuf) byteBuf; - cbb.addComponent(true, buffer); - } else { - CompositeByteBuf cbb = compositeBuffer(Integer.MAX_VALUE); - cbb.addComponents(true, byteBuf, buffer); - byteBuf = cbb; - } - } - if (last) { - setCompleted(); - } else { - requireNonNull(buffer, "buffer"); - } - } - - @Override - public void setContent(File file) throws IOException { - requireNonNull(file, "file"); - long newsize = file.length(); - if (newsize > Integer.MAX_VALUE) { - throw new IllegalArgumentException("File too big to be loaded in memory"); - } - checkSize(newsize); - RandomAccessFile accessFile = new RandomAccessFile(file, "r"); - ByteBuffer byteBuffer; - try { - FileChannel fileChannel = accessFile.getChannel(); - try { - byte[] array = new byte[(int) newsize]; - byteBuffer = ByteBuffer.wrap(array); - int read = 0; - while (read < newsize) { - read += fileChannel.read(byteBuffer); - } - } finally { - fileChannel.close(); - } - } finally { - accessFile.close(); - } - byteBuffer.flip(); - if (byteBuf != null) { - byteBuf.release(); - } - byteBuf = wrappedBuffer(Integer.MAX_VALUE, byteBuffer); - size = newsize; - setCompleted(); - } - - @Override - public void delete() { - if (byteBuf != null) { - byteBuf.release(); - byteBuf = null; - } - } - - @Override - public byte[] get() { - if (byteBuf == null) { - return EMPTY_BUFFER.array(); - } - byte[] array = new byte[byteBuf.readableBytes()]; - byteBuf.getBytes(byteBuf.readerIndex(), array); - return array; - } - - @Override - public String getString() { - return getString(HttpConstants.DEFAULT_CHARSET); - } - - @Override - public String getString(Charset encoding) { - if (byteBuf == null) { - return ""; - } - if (encoding == null) { - encoding = HttpConstants.DEFAULT_CHARSET; - } - return byteBuf.toString(encoding); - } - - /** - * Utility to go from a In Memory FileUpload - * to a Disk (or another implementation) FileUpload - * @return the attached ByteBuf containing the actual bytes - */ - @Override - public ByteBuf getByteBuf() { - return byteBuf; - } - - @Override - public ByteBuf getChunk(int length) throws IOException { - if (byteBuf == null || length == 0 || byteBuf.readableBytes() == 0) { - chunkPosition = 0; - return EMPTY_BUFFER; - } - int sizeLeft = byteBuf.readableBytes() - chunkPosition; - if (sizeLeft == 0) { - chunkPosition = 0; - return EMPTY_BUFFER; - } - int sliceLength = length; - if (sizeLeft < length) { - sliceLength = sizeLeft; - } - ByteBuf chunk = byteBuf.retainedSlice(chunkPosition, sliceLength); - chunkPosition += sliceLength; - return chunk; - } - - @Override - public boolean isInMemory() { - return true; - } - - @Override - public boolean renameTo(File dest) throws IOException { - requireNonNull(dest, "dest"); - if (byteBuf == null) { - // empty file - if (!dest.createNewFile()) { - throw new IOException("file exists already: " + dest); - } - return true; - } - int length = byteBuf.readableBytes(); - long written = 0; - RandomAccessFile accessFile = new RandomAccessFile(dest, "rw"); - try { - FileChannel fileChannel = accessFile.getChannel(); - try { - if (byteBuf.nioBufferCount() == 1) { - ByteBuffer byteBuffer = byteBuf.nioBuffer(); - while (written < length) { - written += fileChannel.write(byteBuffer); - } - } else { - ByteBuffer[] byteBuffers = byteBuf.nioBuffers(); - while (written < length) { - written += fileChannel.write(byteBuffers); - } - } - fileChannel.force(false); - } finally { - fileChannel.close(); - } - } finally { - accessFile.close(); - } - return written == length; - } - - @Override - public File getFile() throws IOException { - throw new IOException("Not represented by a file"); - } - - @Override - public HttpData touch() { - return touch(null); - } - - @Override - public HttpData touch(Object hint) { - if (byteBuf != null) { - byteBuf.touch(hint); - } - return this; - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/Attribute.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/Attribute.java deleted file mode 100644 index 5250cc5f36..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/Attribute.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.buffer.ByteBuf; - -import java.io.IOException; - -/** - * Attribute interface - */ -public interface Attribute extends HttpData { - /** - * Returns the value of this HttpData. - */ - String getValue() throws IOException; - - /** - * Sets the value of this HttpData. - */ - void setValue(String value) throws IOException; - - @Override - Attribute copy(); - - @Override - Attribute duplicate(); - - @Override - Attribute retainedDuplicate(); - - @Override - Attribute replace(ByteBuf content); - - @Override - Attribute retain(); - - @Override - Attribute retain(int increment); - - @Override - Attribute touch(); - - @Override - Attribute touch(Object hint); -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/CaseIgnoringComparator.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/CaseIgnoringComparator.java deleted file mode 100644 index 034b74b16e..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/CaseIgnoringComparator.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import java.io.Serializable; -import java.util.Comparator; - -final class CaseIgnoringComparator implements Comparator, Serializable { - - private static final long serialVersionUID = 4582133183775373862L; - - static final CaseIgnoringComparator INSTANCE = new CaseIgnoringComparator(); - - private CaseIgnoringComparator() { - } - - @Override - public int compare(CharSequence o1, CharSequence o2) { - int o1Length = o1.length(); - int o2Length = o2.length(); - int min = Math.min(o1Length, o2Length); - for (int i = 0; i < min; i++) { - char c1 = o1.charAt(i); - char c2 = o2.charAt(i); - if (c1 != c2) { - c1 = Character.toUpperCase(c1); - c2 = Character.toUpperCase(c2); - if (c1 != c2) { - c1 = Character.toLowerCase(c1); - c2 = Character.toLowerCase(c2); - if (c1 != c2) { - return c1 - c2; - } - } - } - } - return o1Length - o2Length; - } - - private Object readResolve() { - return INSTANCE; - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/DefaultHttpDataFactory.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/DefaultHttpDataFactory.java deleted file mode 100644 index 84c056963d..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/DefaultHttpDataFactory.java +++ /dev/null @@ -1,348 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.handler.codec.http.DefaultHttpRequest; -import io.netty.handler.codec.http.HttpConstants; -import io.netty.handler.codec.http.HttpRequest; - -import java.io.IOException; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Collections; -import java.util.IdentityHashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -/** - * Default factory giving {@link Attribute} and {@link FileUpload} according to constructor. - * - *

According to the constructor, {@link Attribute} and {@link FileUpload} can be:

- *
    - *
  • MemoryAttribute, DiskAttribute or MixedAttribute
  • - *
  • MemoryFileUpload, DiskFileUpload or MixedFileUpload
  • - *
- * A good example of releasing HttpData once all work is done is as follow:
- *
{@code
- *   for (InterfaceHttpData httpData: decoder.getBodyHttpDatas()) {
- *     httpData.release();
- *     factory.removeHttpDataFromClean(request, httpData);
- *   }
- *   factory.cleanAllHttpData();
- *   decoder.destroy();
- *  }
- */ -public class DefaultHttpDataFactory implements HttpDataFactory { - - /** - * Proposed default MINSIZE as 16 KB. - */ - public static final long MINSIZE = 0x4000; - /** - * Proposed default MAXSIZE = -1 as UNLIMITED - */ - public static final long MAXSIZE = -1; - - private final boolean useDisk; - - private final boolean checkSize; - - private long minSize; - - private long maxSize = MAXSIZE; - - private Charset charset = HttpConstants.DEFAULT_CHARSET; - - private String baseDir; - - private boolean deleteOnExit; // false is a good default cause true leaks - - /** - * Keep all {@link HttpData}s until cleaning methods are called. - * We need to use {@link IdentityHashMap} because different requests may be equal. - * See {@link DefaultHttpRequest#hashCode} and {@link DefaultHttpRequest#equals}. - * Similarly, when removing data items, we need to check their identities because - * different data items may be equal. - */ - private final Map> requestFileDeleteMap = - Collections.synchronizedMap(new IdentityHashMap<>()); - - /** - * HttpData will be in memory if less than default size (16KB). - * The type will be Mixed. - */ - public DefaultHttpDataFactory() { - useDisk = false; - checkSize = true; - minSize = MINSIZE; - } - - public DefaultHttpDataFactory(Charset charset) { - this(); - this.charset = charset; - } - - /** - * HttpData will be always on Disk if useDisk is True, else always in Memory if False - */ - public DefaultHttpDataFactory(boolean useDisk) { - this.useDisk = useDisk; - checkSize = false; - } - - public DefaultHttpDataFactory(boolean useDisk, Charset charset) { - this(useDisk); - this.charset = charset; - } - /** - * HttpData will be on Disk if the size of the file is greater than minSize, else it - * will be in memory. The type will be Mixed. - */ - public DefaultHttpDataFactory(long minSize) { - useDisk = false; - checkSize = true; - this.minSize = minSize; - } - - public DefaultHttpDataFactory(long minSize, Charset charset) { - this(minSize); - this.charset = charset; - } - - /** - * Override global {@link DiskAttribute#baseDirectory} and {@link DiskFileUpload#baseDirectory} values. - * - * @param baseDir directory path where to store disk attributes and file uploads. - */ - public void setBaseDir(String baseDir) { - this.baseDir = baseDir; - } - - /** - * Override global {@link DiskAttribute#deleteOnExitTemporaryFile} and - * {@link DiskFileUpload#deleteOnExitTemporaryFile} values. - * - * @param deleteOnExit true if temporary files should be deleted with the JVM, false otherwise. - */ - public void setDeleteOnExit(boolean deleteOnExit) { - this.deleteOnExit = deleteOnExit; - } - - @Override - public void setMaxLimit(long maxSize) { - this.maxSize = maxSize; - } - - /** - * @return the associated list of {@link HttpData} for the request - */ - private List getList(HttpRequest request) { - List list = requestFileDeleteMap.get(request); - if (list == null) { - list = new ArrayList<>(); - requestFileDeleteMap.put(request, list); - } - return list; - } - - @Override - public Attribute createAttribute(HttpRequest request, String name) { - if (useDisk) { - Attribute attribute = new DiskAttribute(name, charset, baseDir, deleteOnExit); - attribute.setMaxSize(maxSize); - List list = getList(request); - list.add(attribute); - return attribute; - } - if (checkSize) { - Attribute attribute = new MixedAttribute(name, minSize, charset, baseDir, deleteOnExit); - attribute.setMaxSize(maxSize); - List list = getList(request); - list.add(attribute); - return attribute; - } - MemoryAttribute attribute = new MemoryAttribute(name); - attribute.setMaxSize(maxSize); - return attribute; - } - - @Override - public Attribute createAttribute(HttpRequest request, String name, long definedSize) { - if (useDisk) { - Attribute attribute = new DiskAttribute(name, definedSize, charset, baseDir, deleteOnExit); - attribute.setMaxSize(maxSize); - List list = getList(request); - list.add(attribute); - return attribute; - } - if (checkSize) { - Attribute attribute = new MixedAttribute(name, definedSize, minSize, charset, baseDir, deleteOnExit); - attribute.setMaxSize(maxSize); - List list = getList(request); - list.add(attribute); - return attribute; - } - MemoryAttribute attribute = new MemoryAttribute(name, definedSize); - attribute.setMaxSize(maxSize); - return attribute; - } - - /** - * Utility method - */ - private static void checkHttpDataSize(HttpData data) { - try { - data.checkSize(data.length()); - } catch (IOException ignored) { - throw new IllegalArgumentException("Attribute bigger than maxSize allowed"); - } - } - - @Override - public Attribute createAttribute(HttpRequest request, String name, String value) { - if (useDisk) { - Attribute attribute; - try { - attribute = new DiskAttribute(name, value, charset, baseDir, deleteOnExit); - attribute.setMaxSize(maxSize); - } catch (IOException e) { - // revert to Mixed mode - attribute = new MixedAttribute(name, value, minSize, charset, baseDir, deleteOnExit); - attribute.setMaxSize(maxSize); - } - checkHttpDataSize(attribute); - List list = getList(request); - list.add(attribute); - return attribute; - } - if (checkSize) { - Attribute attribute = new MixedAttribute(name, value, minSize, charset, baseDir, deleteOnExit); - attribute.setMaxSize(maxSize); - checkHttpDataSize(attribute); - List list = getList(request); - list.add(attribute); - return attribute; - } - try { - MemoryAttribute attribute = new MemoryAttribute(name, value, charset); - attribute.setMaxSize(maxSize); - checkHttpDataSize(attribute); - return attribute; - } catch (IOException e) { - throw new IllegalArgumentException(e); - } - } - - @Override - public FileUpload createFileUpload(HttpRequest request, String name, String filename, - String contentType, String contentTransferEncoding, Charset charset, - long size) { - if (useDisk) { - FileUpload fileUpload = new DiskFileUpload(name, filename, contentType, - contentTransferEncoding, charset, size, baseDir, deleteOnExit); - fileUpload.setMaxSize(maxSize); - checkHttpDataSize(fileUpload); - List list = getList(request); - list.add(fileUpload); - return fileUpload; - } - if (checkSize) { - FileUpload fileUpload = new MixedFileUpload(name, filename, contentType, - contentTransferEncoding, charset, size, minSize, baseDir, deleteOnExit); - fileUpload.setMaxSize(maxSize); - checkHttpDataSize(fileUpload); - List list = getList(request); - list.add(fileUpload); - return fileUpload; - } - MemoryFileUpload fileUpload = new MemoryFileUpload(name, filename, contentType, - contentTransferEncoding, charset, size); - fileUpload.setMaxSize(maxSize); - checkHttpDataSize(fileUpload); - return fileUpload; - } - - @Override - public void removeHttpDataFromClean(HttpRequest request, InterfaceHttpData data) { - if (!(data instanceof HttpData)) { - return; - } - - // Do not use getList because it adds empty list to requestFileDeleteMap - // if request is not found - List list = requestFileDeleteMap.get(request); - if (list == null) { - return; - } - - // Can't simply call list.remove(data), because different data items may be equal. - // Need to check identity. - Iterator i = list.iterator(); - while (i.hasNext()) { - HttpData n = i.next(); - if (n == data) { - i.remove(); - - // Remove empty list to avoid memory leak - if (list.isEmpty()) { - requestFileDeleteMap.remove(request); - } - - return; - } - } - } - - @Override - public void cleanRequestHttpData(HttpRequest request) { - List list = requestFileDeleteMap.remove(request); - if (list != null) { - for (HttpData data : list) { - data.release(); - } - } - } - - @Override - public void cleanAllHttpData() { - Iterator>> i = requestFileDeleteMap.entrySet().iterator(); - while (i.hasNext()) { - Entry> e = i.next(); - - // Calling i.remove() here will cause "java.lang.IllegalStateException: Entry was removed" - // at e.getValue() below - - List list = e.getValue(); - for (HttpData data : list) { - data.release(); - } - - i.remove(); - } - } - - @Override - public void cleanRequestHttpDatas(HttpRequest request) { - cleanRequestHttpData(request); - } - - @Override - public void cleanAllHttpDatas() { - cleanAllHttpData(); - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/DeleteFileOnExitHook.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/DeleteFileOnExitHook.java deleted file mode 100644 index f93208d721..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/DeleteFileOnExitHook.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import java.io.File; -import java.util.Collections; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -/** - * DeleteFileOnExitHook. - */ -final class DeleteFileOnExitHook { - private static final Set FILES = Collections.newSetFromMap(new ConcurrentHashMap()); - - private DeleteFileOnExitHook() { - } - - static { - // DeleteOnExitHook must be the last shutdown hook to be invoked. - // Application shutdown hooks may add the first file to the - // delete on exit list and cause the DeleteOnExitHook to be - // registered during shutdown in progress. - Runtime.getRuntime().addShutdownHook(new Thread() { - - @Override - public void run() { - runHook(); - } - }); - } - - /** - * Remove from the pool to reduce space footprint. - * - * @param file tmp file path - */ - public static void remove(String file) { - FILES.remove(file); - } - - /** - * Add to the hook and clean up when the program exits. - * - * @param file tmp file path - */ - public static void add(String file) { - FILES.add(file); - } - - /** - * Check in the hook files. - * - * @param file target file - * @return true or false - */ - public static boolean checkFileExist(String file) { - return FILES.contains(file); - } - - /** - * Clean up all the files. - */ - static void runHook() { - for (String filename : FILES) { - new File(filename).delete(); - } - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/DiskAttribute.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/DiskAttribute.java deleted file mode 100644 index 25c3e53ae5..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/DiskAttribute.java +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelException; -import io.netty.handler.codec.http.HttpConstants; - -import java.io.IOException; -import java.nio.charset.Charset; - -import static io.netty.buffer.Unpooled.wrappedBuffer; -import static java.util.Objects.requireNonNull; - -/** - * Disk implementation of Attributes - */ -public class DiskAttribute extends AbstractDiskHttpData implements Attribute { - public static String baseDirectory; - - public static boolean deleteOnExitTemporaryFile = true; - - public static final String prefix = "Attr_"; - - public static final String postfix = ".att"; - - private String baseDir; - - private boolean deleteOnExit; - - /** - * Constructor used for huge Attribute - */ - public DiskAttribute(String name) { - this(name, HttpConstants.DEFAULT_CHARSET); - } - - public DiskAttribute(String name, String baseDir, boolean deleteOnExit) { - this(name, HttpConstants.DEFAULT_CHARSET); - this.baseDir = baseDir == null ? baseDirectory : baseDir; - this.deleteOnExit = deleteOnExit; - } - - public DiskAttribute(String name, long definedSize) { - this(name, definedSize, HttpConstants.DEFAULT_CHARSET, baseDirectory, deleteOnExitTemporaryFile); - } - - public DiskAttribute(String name, long definedSize, String baseDir, boolean deleteOnExit) { - this(name, definedSize, HttpConstants.DEFAULT_CHARSET); - this.baseDir = baseDir == null ? baseDirectory : baseDir; - this.deleteOnExit = deleteOnExit; - } - - public DiskAttribute(String name, Charset charset) { - this(name, charset, baseDirectory, deleteOnExitTemporaryFile); - } - - public DiskAttribute(String name, Charset charset, String baseDir, boolean deleteOnExit) { - super(name, charset, 0); - this.baseDir = baseDir == null ? baseDirectory : baseDir; - this.deleteOnExit = deleteOnExit; - } - - public DiskAttribute(String name, long definedSize, Charset charset) { - this(name, definedSize, charset, baseDirectory, deleteOnExitTemporaryFile); - } - - public DiskAttribute(String name, long definedSize, Charset charset, String baseDir, boolean deleteOnExit) { - super(name, charset, definedSize); - this.baseDir = baseDir == null ? baseDirectory : baseDir; - this.deleteOnExit = deleteOnExit; - } - - public DiskAttribute(String name, String value) throws IOException { - this(name, value, HttpConstants.DEFAULT_CHARSET); - } - - public DiskAttribute(String name, String value, Charset charset) throws IOException { - this(name, value, charset, baseDirectory, deleteOnExitTemporaryFile); - } - - public DiskAttribute(String name, String value, Charset charset, - String baseDir, boolean deleteOnExit) throws IOException { - super(name, charset, 0); // Attribute have no default size - setValue(value); - this.baseDir = baseDir == null ? baseDirectory : baseDir; - this.deleteOnExit = deleteOnExit; - } - - @Override - public HttpDataType getHttpDataType() { - return HttpDataType.Attribute; - } - - @Override - public String getValue() throws IOException { - byte [] bytes = get(); - return new String(bytes, getCharset()); - } - - @Override - public void setValue(String value) throws IOException { - requireNonNull(value, "value"); - byte [] bytes = value.getBytes(getCharset()); - checkSize(bytes.length); - ByteBuf buffer = wrappedBuffer(bytes); - if (definedSize > 0) { - definedSize = buffer.readableBytes(); - } - setContent(buffer); - } - - @Override - public void addContent(ByteBuf buffer, boolean last) throws IOException { - final long newDefinedSize = size + buffer.readableBytes(); - try { - checkSize(newDefinedSize); - } catch (IOException e) { - buffer.release(); - throw e; - } - if (definedSize > 0 && definedSize < newDefinedSize) { - definedSize = newDefinedSize; - } - super.addContent(buffer, last); - } - - @Override - public int hashCode() { - return getName().hashCode(); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof Attribute)) { - return false; - } - Attribute attribute = (Attribute) o; - return getName().equalsIgnoreCase(attribute.getName()); - } - - @Override - public int compareTo(InterfaceHttpData o) { - if (!(o instanceof Attribute)) { - throw new ClassCastException("Cannot compare " + getHttpDataType() + - " with " + o.getHttpDataType()); - } - return compareTo((Attribute) o); - } - - public int compareTo(Attribute o) { - return getName().compareToIgnoreCase(o.getName()); - } - - @Override - public String toString() { - try { - return getName() + '=' + getValue(); - } catch (IOException e) { - return getName() + '=' + e; - } - } - - @Override - protected boolean deleteOnExit() { - return deleteOnExit; - } - - @Override - protected String getBaseDirectory() { - return baseDir; - } - - @Override - protected String getDiskFilename() { - return getName() + postfix; - } - - @Override - protected String getPostfix() { - return postfix; - } - - @Override - protected String getPrefix() { - return prefix; - } - - @Override - public Attribute copy() { - final ByteBuf content = content(); - return replace(content != null ? content.copy() : null); - } - - @Override - public Attribute duplicate() { - final ByteBuf content = content(); - return replace(content != null ? content.duplicate() : null); - } - - @Override - public Attribute retainedDuplicate() { - ByteBuf content = content(); - if (content != null) { - content = content.retainedDuplicate(); - boolean success = false; - try { - Attribute duplicate = replace(content); - success = true; - return duplicate; - } finally { - if (!success) { - content.release(); - } - } - } else { - return replace(null); - } - } - - @Override - public Attribute replace(ByteBuf content) { - DiskAttribute attr = new DiskAttribute(getName(), baseDir, deleteOnExit); - attr.setCharset(getCharset()); - if (content != null) { - try { - attr.setContent(content); - } catch (IOException e) { - throw new ChannelException(e); - } - } - return attr; - } - - @Override - public Attribute retain(int increment) { - super.retain(increment); - return this; - } - - @Override - public Attribute retain() { - super.retain(); - return this; - } - - @Override - public Attribute touch() { - super.touch(); - return this; - } - - @Override - public Attribute touch(Object hint) { - super.touch(hint); - return this; - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/DiskFileUpload.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/DiskFileUpload.java deleted file mode 100644 index 66a21d7f32..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/DiskFileUpload.java +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelException; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; - -import java.io.File; -import java.io.IOException; -import java.nio.charset.Charset; - -/** - * Disk FileUpload implementation that stores file into real files - */ -public class DiskFileUpload extends AbstractDiskHttpData implements FileUpload { - public static String baseDirectory; - - public static boolean deleteOnExitTemporaryFile = true; - - public static final String prefix = "FUp_"; - - public static final String postfix = ".tmp"; - - private final String baseDir; - - private final boolean deleteOnExit; - - private String filename; - - private String contentType; - - private String contentTransferEncoding; - - public DiskFileUpload(String name, String filename, String contentType, - String contentTransferEncoding, Charset charset, long size, String baseDir, boolean deleteOnExit) { - super(name, charset, size); - setFilename(filename); - setContentType(contentType); - setContentTransferEncoding(contentTransferEncoding); - this.baseDir = baseDir == null ? baseDirectory : baseDir; - this.deleteOnExit = deleteOnExit; - } - - public DiskFileUpload(String name, String filename, String contentType, - String contentTransferEncoding, Charset charset, long size) { - this(name, filename, contentType, contentTransferEncoding, - charset, size, baseDirectory, deleteOnExitTemporaryFile); - } - - @Override - public HttpDataType getHttpDataType() { - return HttpDataType.FileUpload; - } - - @Override - public String getFilename() { - return filename; - } - - @Override - public void setFilename(String filename) { - requireNonNull(filename, "filename"); - this.filename = filename; - } - - @Override - public int hashCode() { - return FileUploadUtil.hashCode(this); - } - - @Override - public boolean equals(Object o) { - return o instanceof FileUpload && FileUploadUtil.equals(this, (FileUpload) o); - } - - @Override - public int compareTo(InterfaceHttpData o) { - if (!(o instanceof FileUpload)) { - throw new ClassCastException("Cannot compare " + getHttpDataType() + - " with " + o.getHttpDataType()); - } - return compareTo((FileUpload) o); - } - - public int compareTo(FileUpload o) { - return FileUploadUtil.compareTo(this, o); - } - - @Override - public void setContentType(String contentType) { - requireNonNull(contentType, "contentType"); - this.contentType = contentType; - } - - @Override - public String getContentType() { - return contentType; - } - - @Override - public String getContentTransferEncoding() { - return contentTransferEncoding; - } - - @Override - public void setContentTransferEncoding(String contentTransferEncoding) { - this.contentTransferEncoding = contentTransferEncoding; - } - - @Override - public String toString() { - File file = null; - try { - file = getFile(); - } catch (IOException e) { - // Should not occur. - } - - return HttpHeaderNames.CONTENT_DISPOSITION + ": " + - HttpHeaderValues.FORM_DATA + "; " + HttpHeaderValues.NAME + "=\"" + getName() + - "\"; " + HttpHeaderValues.FILENAME + "=\"" + filename + "\"\r\n" + - HttpHeaderNames.CONTENT_TYPE + ": " + contentType + - (getCharset() != null? "; " + HttpHeaderValues.CHARSET + '=' + getCharset().name() + "\r\n" : "\r\n") + - HttpHeaderNames.CONTENT_LENGTH + ": " + length() + "\r\n" + - "Completed: " + isCompleted() + - "\r\nIsInMemory: " + isInMemory() + "\r\nRealFile: " + - (file != null ? file.getAbsolutePath() : "null") + " DeleteAfter: " + deleteOnExit; - } - - @Override - protected boolean deleteOnExit() { - return deleteOnExit; - } - - @Override - protected String getBaseDirectory() { - return baseDir; - } - - @Override - protected String getDiskFilename() { - return "upload"; - } - - @Override - protected String getPostfix() { - return postfix; - } - - @Override - protected String getPrefix() { - return prefix; - } - - @Override - public FileUpload copy() { - final ByteBuf content = content(); - return replace(content != null ? content.copy() : null); - } - - @Override - public FileUpload duplicate() { - final ByteBuf content = content(); - return replace(content != null ? content.duplicate() : null); - } - - @Override - public FileUpload retainedDuplicate() { - ByteBuf content = content(); - if (content != null) { - content = content.retainedDuplicate(); - boolean success = false; - try { - FileUpload duplicate = replace(content); - success = true; - return duplicate; - } finally { - if (!success) { - content.release(); - } - } - } else { - return replace(null); - } - } - - @Override - public FileUpload replace(ByteBuf content) { - DiskFileUpload upload = new DiskFileUpload( - getName(), getFilename(), getContentType(), getContentTransferEncoding(), getCharset(), size, - baseDir, deleteOnExit); - if (content != null) { - try { - upload.setContent(content); - } catch (IOException e) { - throw new ChannelException(e); - } - } - return upload; - } - - @Override - public FileUpload retain(int increment) { - super.retain(increment); - return this; - } - - @Override - public FileUpload retain() { - super.retain(); - return this; - } - - @Override - public FileUpload touch() { - super.touch(); - return this; - } - - @Override - public FileUpload touch(Object hint) { - super.touch(hint); - return this; - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/FileUpload.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/FileUpload.java deleted file mode 100644 index 35b97411f9..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/FileUpload.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.buffer.ByteBuf; - -/** - * FileUpload interface that could be in memory, on temporary file or any other implementations. - * - * Most methods are inspired from java.io.File API. - */ -public interface FileUpload extends HttpData { - /** - * Returns the original filename in the client's filesystem, - * as provided by the browser (or other client software). - * @return the original filename - */ - String getFilename(); - - /** - * Set the original filename - */ - void setFilename(String filename); - - /** - * Set the Content Type passed by the browser if defined - * @param contentType Content Type to set - must be not null - */ - void setContentType(String contentType); - - /** - * Returns the content type passed by the browser or null if not defined. - * @return the content type passed by the browser or null if not defined. - */ - String getContentType(); - - /** - * Set the Content-Transfer-Encoding type from String as 7bit, 8bit or binary - */ - void setContentTransferEncoding(String contentTransferEncoding); - - /** - * Returns the Content-Transfer-Encoding - * @return the Content-Transfer-Encoding - */ - String getContentTransferEncoding(); - - @Override - FileUpload copy(); - - @Override - FileUpload duplicate(); - - @Override - FileUpload retainedDuplicate(); - - @Override - FileUpload replace(ByteBuf content); - - @Override - FileUpload retain(); - - @Override - FileUpload retain(int increment); - - @Override - FileUpload touch(); - - @Override - FileUpload touch(Object hint); -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/FileUploadUtil.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/FileUploadUtil.java deleted file mode 100644 index 6fa8131f60..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/FileUploadUtil.java +++ /dev/null @@ -1,33 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -final class FileUploadUtil { - - private FileUploadUtil() { } - - static int hashCode(FileUpload upload) { - return upload.getName().hashCode(); - } - - static boolean equals(FileUpload upload1, FileUpload upload2) { - return upload1.getName().equalsIgnoreCase(upload2.getName()); - } - - static int compareTo(FileUpload upload1, FileUpload upload2) { - return upload1.getName().compareToIgnoreCase(upload2.getName()); - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpData.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpData.java deleted file mode 100644 index 0238677f3a..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpData.java +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufHolder; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.Charset; - -/** - * Extended interface for InterfaceHttpData - */ -public interface HttpData extends InterfaceHttpData, ByteBufHolder { - - /** - * Returns the maxSize for this HttpData. - */ - long getMaxSize(); - - /** - * Set the maxSize for this HttpData. When limit will be reached, an exception will be raised. - * Setting it to (-1) means no limitation. - * - * By default, to be set from the HttpDataFactory. - */ - void setMaxSize(long maxSize); - - /** - * Check if the new size is not reaching the max limit allowed. - * The limit is always computed in terms of bytes. - */ - void checkSize(long newSize) throws IOException; - - /** - * Set the content from the ChannelBuffer (erase any previous data) - *

{@link ByteBuf#release()} ownership of {@code buffer} is transferred to this {@link HttpData}. - * - * @param buffer Must be not null. - * @throws IOException If an IO error occurs when setting the content of this HttpData. - */ - void setContent(ByteBuf buffer) throws IOException; - - /** - * Add the content from the ChannelBuffer - *

{@link ByteBuf#release()} ownership of {@code buffer} is transferred to this {@link HttpData}. - * - * @param buffer - * must be not null except if last is set to False - * @param last - * True of the buffer is the last one - * @throws IOException If an IO error occurs while adding content to this HttpData. - */ - void addContent(ByteBuf buffer, boolean last) throws IOException; - - /** - * Set the content from the file (erase any previous data) - * - * @param file Must be not null. - * @throws IOException If an IO error occurs when setting the content of this HttpData. - */ - void setContent(File file) throws IOException; - - /** - * Set the content from the inputStream (erase any previous data) - * - * @param inputStream Must be not null. - * @throws IOException If an IO error occurs when setting the content of this HttpData. - */ - void setContent(InputStream inputStream) throws IOException; - - /** - * - * @return True if the InterfaceHttpData is completed (all data are stored) - */ - boolean isCompleted(); - - /** - * Returns the size in byte of the InterfaceHttpData - * - * @return the size of the InterfaceHttpData - */ - long length(); - - /** - * Returns the defined length of the HttpData. - * - * If no Content-Length is provided in the request, the defined length is - * always 0 (whatever during decoding or in final state). - * - * If Content-Length is provided in the request, this is this given defined length. - * This value does not change, whatever during decoding or in the final state. - * - * This method could be used for instance to know the amount of bytes transmitted for - * one particular HttpData, for example one {@link FileUpload} or any known big {@link Attribute}. - * - * @return the defined length of the HttpData - */ - long definedLength(); - - /** - * Deletes the underlying storage for a file item, including deleting any - * associated temporary disk file. - */ - void delete(); - - /** - * Returns the contents of the file item as an array of bytes.
- * Note: this method will allocate a lot of memory, if the data is currently stored on the file system. - * - * @return the contents of the file item as an array of bytes. - * @throws IOException If an IO error occurs while reading the data contents of this HttpData. - */ - byte[] get() throws IOException; - - /** - * Returns the content of the file item as a ByteBuf.
- * Note: this method will allocate a lot of memory, if the data is currently stored on the file system. - * - * @return the content of the file item as a ByteBuf - * @throws IOException If an IO error occurs while reading the data contents of this HttpData. - */ - ByteBuf getByteBuf() throws IOException; - - /** - * Returns a ChannelBuffer for the content from the current position with at - * most length read bytes, increasing the current position of the Bytes - * read. Once it arrives at the end, it returns an EMPTY_BUFFER and it - * resets the current position to 0. - * - * @return a ChannelBuffer for the content from the current position or an - * EMPTY_BUFFER if there is no more data to return - */ - ByteBuf getChunk(int length) throws IOException; - - /** - * Returns the contents of the file item as a String, using the default - * character encoding. - * - * @return the contents of the file item as a String, using the default - * character encoding. - * @throws IOException If an IO error occurs while reading the data contents of this HttpData. - */ - String getString() throws IOException; - - /** - * Returns the contents of the file item as a String, using the specified - * charset. - * - * @param encoding - * the charset to use - * @return the contents of the file item as a String, using the specified - * charset. - * @throws IOException If an IO error occurs while reading the data contents of this HttpData. - */ - String getString(Charset encoding) throws IOException; - - /** - * Set the Charset passed by the browser if defined - * - * @param charset - * Charset to set - must be not null - */ - void setCharset(Charset charset); - - /** - * Returns the Charset passed by the browser or null if not defined. - * - * @return the Charset passed by the browser or null if not defined. - */ - Charset getCharset(); - - /** - * A convenience getMethod to write an uploaded item to disk. If a previous one - * exists, it will be deleted. Once this getMethod is called, if successful, - * the new file will be out of the cleaner of the factory that creates the - * original InterfaceHttpData object. - * - * @param dest Destination file - must be not null. - * @return {@code true} if the write is successful. - * @throws IOException If an IO error occurs while renaming the underlying file of this HttpData. - */ - boolean renameTo(File dest) throws IOException; - - /** - * Provides a hint as to whether or not the file contents will be read from - * memory. - * - * @return True if the file contents is in memory. - */ - boolean isInMemory(); - - /** - * - * @return the associated File if this data is represented in a file - * @exception IOException - * if this data is not represented by a file - */ - File getFile() throws IOException; - - @Override - HttpData copy(); - - @Override - HttpData duplicate(); - - @Override - HttpData retainedDuplicate(); - - @Override - HttpData replace(ByteBuf content); - - @Override - HttpData retain(); - - @Override - HttpData retain(int increment); - - @Override - HttpData touch(); - - @Override - HttpData touch(Object hint); -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpDataFactory.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpDataFactory.java deleted file mode 100644 index 630c6d1af7..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpDataFactory.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.handler.codec.http.HttpRequest; - -import java.nio.charset.Charset; - -/** - * Interface to enable creation of InterfaceHttpData objects - */ -public interface HttpDataFactory { - - /** - * To set a max size limitation on fields. Exceeding it will generate an ErrorDataDecoderException. - * A value of -1 means no limitation (default). - */ - void setMaxLimit(long max); - - /** - * - * @param request associated request - * @return a new Attribute with no value - */ - Attribute createAttribute(HttpRequest request, String name); - - /** - * @param request associated request - * @param name name of the attribute - * @param definedSize defined size from request for this attribute - * @return a new Attribute with no value - */ - Attribute createAttribute(HttpRequest request, String name, long definedSize); - - /** - * @param request associated request - * @return a new Attribute - */ - Attribute createAttribute(HttpRequest request, String name, String value); - - /** - * @param request associated request - * @param size the size of the Uploaded file - * @return a new FileUpload - */ - FileUpload createFileUpload(HttpRequest request, String name, String filename, - String contentType, String contentTransferEncoding, Charset charset, - long size); - - /** - * Remove the given InterfaceHttpData from clean list (will not delete the file, except if the file - * is still a temporary one as setup at construction) - * @param request associated request - */ - void removeHttpDataFromClean(HttpRequest request, InterfaceHttpData data); - - /** - * Remove all InterfaceHttpData from virtual File storage from clean list for the request - * - * @param request associated request - */ - void cleanRequestHttpData(HttpRequest request); - - /** - * Remove all InterfaceHttpData from virtual File storage from clean list for all requests - */ - void cleanAllHttpData(); - - /** - * @deprecated Use {@link #cleanRequestHttpData(HttpRequest)} instead. - */ - @Deprecated - void cleanRequestHttpDatas(HttpRequest request); - - /** - * @deprecated Use {@link #cleanAllHttpData()} instead. - */ - @Deprecated - void cleanAllHttpDatas(); -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostBodyUtil.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostBodyUtil.java deleted file mode 100644 index a404cae7fb..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostBodyUtil.java +++ /dev/null @@ -1,275 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.http.HttpConstants; - -/** - * Shared Static object between HttpMessageDecoder, HttpPostRequestDecoder and HttpPostRequestEncoder - */ -final class HttpPostBodyUtil { - - public static final int chunkSize = 8096; - - /** - * Default Content-Type in binary form - */ - public static final String DEFAULT_BINARY_CONTENT_TYPE = "application/octet-stream"; - - /** - * Default Content-Type in Text form - */ - public static final String DEFAULT_TEXT_CONTENT_TYPE = "text/plain"; - - static final String BIT_7_STRING = "7bit"; - - static final String BIT_8_STRING = "8bit"; - - static final String BINARY_STRING = "binary"; - - /** - * Allowed mechanism for multipart - * mechanism := "7bit" - / "8bit" - / "binary" - Not allowed: "quoted-printable" - / "base64" - */ - public enum TransferEncodingMechanism { - /** - * Default encoding - */ - BIT7(BIT_7_STRING), - /** - * Short lines but not in ASCII - no encoding - */ - BIT8(BIT_8_STRING), - /** - * Could be long text not in ASCII - no encoding - */ - BINARY(BINARY_STRING); - - private final String value; - - TransferEncodingMechanism(String value) { - this.value = value; - } - - public String value() { - return value; - } - - @Override - public String toString() { - return value; - } - } - - private HttpPostBodyUtil() { - } - - /** - * This class intends to decrease the CPU in seeking ahead some bytes in - * HttpPostRequestDecoder - */ - static class SeekAheadOptimize { - byte[] bytes; - int readerIndex; - int pos; - int origPos; - int limit; - ByteBuf buffer; - - /** - * @param buffer buffer with a backing byte array - */ - SeekAheadOptimize(ByteBuf buffer) { - if (!buffer.hasArray()) { - throw new IllegalArgumentException("buffer hasn't backing byte array"); - } - this.buffer = buffer; - bytes = buffer.array(); - readerIndex = buffer.readerIndex(); - origPos = pos = buffer.arrayOffset() + readerIndex; - limit = buffer.arrayOffset() + buffer.writerIndex(); - } - - /** - * - * @param minus this value will be used as (currentPos - minus) to set - * the current readerIndex in the buffer. - */ - void setReadPosition(int minus) { - pos -= minus; - readerIndex = getReadPosition(pos); - buffer.readerIndex(readerIndex); - } - - /** - * - * @param index raw index of the array (pos in general) - * @return the value equivalent of raw index to be used in readerIndex(value) - */ - int getReadPosition(int index) { - return index - origPos + readerIndex; - } - } - - /** - * Find the first non whitespace - * @return the rank of the first non whitespace - */ - static int findNonWhitespace(String sb, int offset) { - int result; - for (result = offset; result < sb.length(); result ++) { - if (!Character.isWhitespace(sb.charAt(result))) { - break; - } - } - return result; - } - - /** - * Find the end of String - * @return the rank of the end of string - */ - static int findEndOfString(String sb) { - int result; - for (result = sb.length(); result > 0; result --) { - if (!Character.isWhitespace(sb.charAt(result - 1))) { - break; - } - } - return result; - } - - /** - * Try to find first LF or CRLF as Line Breaking - * - * @param buffer the buffer to search in - * @param index the index to start from in the buffer - * @return a relative position from index > 0 if LF or CRLF is found - * or < 0 if not found - */ - static int findLineBreak(ByteBuf buffer, int index) { - int toRead = buffer.readableBytes() - (index - buffer.readerIndex()); - int posFirstChar = buffer.bytesBefore(index, toRead, HttpConstants.LF); - if (posFirstChar == -1) { - // No LF, so neither CRLF - return -1; - } - if (posFirstChar > 0 && buffer.getByte(index + posFirstChar - 1) == HttpConstants.CR) { - posFirstChar--; - } - return posFirstChar; - } - - /** - * Try to find last LF or CRLF as Line Breaking - * - * @param buffer the buffer to search in - * @param index the index to start from in the buffer - * @return a relative position from index > 0 if LF or CRLF is found - * or < 0 if not found - */ - static int findLastLineBreak(ByteBuf buffer, int index) { - int candidate = findLineBreak(buffer, index); - int findCRLF = 0; - if (candidate >= 0) { - if (buffer.getByte(index + candidate) == HttpConstants.CR) { - findCRLF = 2; - } else { - findCRLF = 1; - } - candidate += findCRLF; - } - int next; - while (candidate > 0 && (next = findLineBreak(buffer, index + candidate)) >= 0) { - candidate += next; - if (buffer.getByte(index + candidate) == HttpConstants.CR) { - findCRLF = 2; - } else { - findCRLF = 1; - } - candidate += findCRLF; - } - return candidate - findCRLF; - } - - /** - * Try to find the delimiter, with LF or CRLF in front of it (added as delimiters) if needed - * - * @param buffer the buffer to search in - * @param index the index to start from in the buffer - * @param delimiter the delimiter as byte array - * @param precededByLineBreak true if it must be preceded by LF or CRLF, else false - * @return a relative position from index > 0 if delimiter found designing the start of it - * (including LF or CRLF is asked) - * or a number < 0 if delimiter is not found - * @throws IndexOutOfBoundsException - * if {@code offset + delimiter.length} is greater than {@code buffer.capacity} - */ - static int findDelimiter(ByteBuf buffer, int index, byte[] delimiter, boolean precededByLineBreak) { - final int delimiterLength = delimiter.length; - final int readerIndex = buffer.readerIndex(); - final int writerIndex = buffer.writerIndex(); - int toRead = writerIndex - index; - int newOffset = index; - boolean delimiterNotFound = true; - while (delimiterNotFound && delimiterLength <= toRead) { - // Find first position: delimiter - int posDelimiter = buffer.bytesBefore(newOffset, toRead, delimiter[0]); - if (posDelimiter < 0) { - return -1; - } - newOffset += posDelimiter; - toRead -= posDelimiter; - // Now check for delimiter - if (toRead >= delimiterLength) { - delimiterNotFound = false; - for (int i = 0; i < delimiterLength; i++) { - if (buffer.getByte(newOffset + i) != delimiter[i]) { - newOffset++; - toRead--; - delimiterNotFound = true; - break; - } - } - } - if (!delimiterNotFound) { - // Delimiter found, find if necessary: LF or CRLF - if (precededByLineBreak && newOffset > readerIndex) { - if (buffer.getByte(newOffset - 1) == HttpConstants.LF) { - newOffset--; - // Check if CR before: not mandatory to be there - if (newOffset > readerIndex && buffer.getByte(newOffset - 1) == HttpConstants.CR) { - newOffset--; - } - } else { - // Delimiter with Line Break could be further: iterate after first char of delimiter - newOffset++; - toRead--; - delimiterNotFound = true; - continue; - } - } - return newOffset - readerIndex; - } - } - return -1; - } -} 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 deleted file mode 100644 index cc389cba8f..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostMultipartRequestDecoder.java +++ /dev/null @@ -1,1352 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.http.HttpConstants; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.LastHttpContent; -import io.netty.handler.codec.http.QueryStringDecoder; -import io.netty.handler.codec.http.multipart.HttpPostBodyUtil.SeekAheadOptimize; -import io.netty.handler.codec.http.multipart.HttpPostBodyUtil.TransferEncodingMechanism; -import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.EndOfDataDecoderException; -import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.ErrorDataDecoderException; -import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.MultiPartStatus; -import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.NotEnoughDataDecoderException; -import io.netty.util.CharsetUtil; -import io.netty.util.internal.InternalThreadLocalMap; -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.StringUtil; - -import java.io.IOException; -import java.nio.charset.Charset; -import java.nio.charset.IllegalCharsetNameException; -import java.nio.charset.UnsupportedCharsetException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; - -import static io.netty.handler.codec.http.multipart.HttpPostBodyUtil.BINARY_STRING; -import static io.netty.handler.codec.http.multipart.HttpPostBodyUtil.BIT_7_STRING; -import static io.netty.handler.codec.http.multipart.HttpPostBodyUtil.BIT_8_STRING; -import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; -import static java.util.Objects.requireNonNull; - - -/** - * This decoder will decode Body and can handle POST BODY. - * - * You MUST call {@link #destroy()} after completion to release all resources. - * - */ -public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequestDecoder { - - /** - * Factory used to create InterfaceHttpData - */ - private final HttpDataFactory factory; - - /** - * Request to decode - */ - private final HttpRequest request; - - /** - * Default charset to use - */ - private Charset charset; - - /** - * Does the last chunk already received - */ - private boolean isLastChunk; - - /** - * HttpDatas from Body - */ - private final List bodyListHttpData = new ArrayList<>(); - - /** - * HttpDatas as Map from Body - */ - private final Map> bodyMapHttpData = new TreeMap<>( - CaseIgnoringComparator.INSTANCE); - - /** - * The current channelBuffer - */ - private ByteBuf undecodedChunk; - - /** - * Body HttpDatas current position - */ - private int bodyListHttpDataRank; - - /** - * If multipart, this is the boundary for the global multipart - */ - private final String multipartDataBoundary; - - /** - * If multipart, there could be internal multiparts (mixed) to the global - * multipart. Only one level is allowed. - */ - private String multipartMixedBoundary; - - /** - * Current getStatus - */ - private MultiPartStatus currentStatus = MultiPartStatus.NOTSTARTED; - - /** - * Used in Multipart - */ - private Map currentFieldAttributes; - - /** - * The current FileUpload that is currently in decode process - */ - private FileUpload currentFileUpload; - - /** - * The current Attribute that is currently in decode process - */ - private Attribute currentAttribute; - - private boolean destroyed; - - private int discardThreshold = HttpPostRequestDecoder.DEFAULT_DISCARD_THRESHOLD; - - /** - * - * @param request - * the request to decode - * @throws NullPointerException - * for request - * @throws ErrorDataDecoderException - * if the default charset was wrong when decoding or other - * errors - */ - public HttpPostMultipartRequestDecoder(HttpRequest request) { - this(new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE), request, HttpConstants.DEFAULT_CHARSET); - } - - /** - * - * @param factory - * the factory used to create InterfaceHttpData - * @param request - * the request to decode - * @throws NullPointerException - * for request or factory - * @throws ErrorDataDecoderException - * if the default charset was wrong when decoding or other - * errors - */ - public HttpPostMultipartRequestDecoder(HttpDataFactory factory, HttpRequest request) { - this(factory, request, HttpConstants.DEFAULT_CHARSET); - } - - /** - * - * @param factory - * the factory used to create InterfaceHttpData - * @param request - * the request to decode - * @param charset - * the charset to use as default - * @throws NullPointerException - * for request or charset or factory - * @throws ErrorDataDecoderException - * if the default charset was wrong when decoding or other - * errors - */ - public HttpPostMultipartRequestDecoder(HttpDataFactory factory, HttpRequest request, Charset charset) { - this.request = requireNonNull(request, "request"); - this.charset = requireNonNull(charset, "charset"); - this.factory = requireNonNull(factory, "factory"); - // Fill default values - - String contentTypeValue = this.request.headers().get(HttpHeaderNames.CONTENT_TYPE); - if (contentTypeValue == null) { - throw new ErrorDataDecoderException("No '" + HttpHeaderNames.CONTENT_TYPE + "' header present."); - } - - String[] dataBoundary = HttpPostRequestDecoder.getMultipartDataBoundary(contentTypeValue); - if (dataBoundary != null) { - multipartDataBoundary = dataBoundary[0]; - if (dataBoundary.length > 1 && dataBoundary[1] != null) { - try { - this.charset = Charset.forName(dataBoundary[1]); - } catch (IllegalCharsetNameException e) { - throw new ErrorDataDecoderException(e); - } - } - } else { - multipartDataBoundary = null; - } - currentStatus = MultiPartStatus.HEADERDELIMITER; - - try { - if (request instanceof HttpContent) { - // Offer automatically if the given request is als type of HttpContent - // See #1089 - offer((HttpContent) request); - } else { - parseBody(); - } - } catch (Throwable e) { - destroy(); - PlatformDependent.throwException(e); - } - } - - private void checkDestroyed() { - if (destroyed) { - throw new IllegalStateException(HttpPostMultipartRequestDecoder.class.getSimpleName() - + " was destroyed already"); - } - } - - /** - * True if this request is a Multipart request - * - * @return True if this request is a Multipart request - */ - @Override - public boolean isMultipart() { - checkDestroyed(); - return true; - } - - /** - * Set the amount of bytes after which read bytes in the buffer should be discarded. - * Setting this lower gives lower memory usage but with the overhead of more memory copies. - * Use {@code 0} to disable it. - */ - @Override - public void setDiscardThreshold(int discardThreshold) { - this.discardThreshold = checkPositiveOrZero(discardThreshold, "discardThreshold"); - } - - /** - * Return the threshold in bytes after which read data in the buffer should be discarded. - */ - @Override - public int getDiscardThreshold() { - return discardThreshold; - } - - /** - * This getMethod returns a List of all HttpDatas from body.
- * - * If chunked, all chunks must have been offered using offer() getMethod. If - * not, NotEnoughDataDecoderException will be raised. - * - * @return the list of HttpDatas from Body part for POST getMethod - * @throws NotEnoughDataDecoderException - * Need more chunks - */ - @Override - public List getBodyHttpDatas() { - checkDestroyed(); - - if (!isLastChunk) { - throw new NotEnoughDataDecoderException(); - } - return bodyListHttpData; - } - - /** - * This getMethod returns a List of all HttpDatas with the given name from - * body.
- * - * If chunked, all chunks must have been offered using offer() getMethod. If - * not, NotEnoughDataDecoderException will be raised. - * - * @return All Body HttpDatas with the given name (ignore case) - * @throws NotEnoughDataDecoderException - * need more chunks - */ - @Override - public List getBodyHttpDatas(String name) { - checkDestroyed(); - - if (!isLastChunk) { - throw new NotEnoughDataDecoderException(); - } - return bodyMapHttpData.get(name); - } - - /** - * This getMethod returns the first InterfaceHttpData with the given name from - * body.
- * - * If chunked, all chunks must have been offered using offer() getMethod. If - * not, NotEnoughDataDecoderException will be raised. - * - * @return The first Body InterfaceHttpData with the given name (ignore - * case) - * @throws NotEnoughDataDecoderException - * need more chunks - */ - @Override - public InterfaceHttpData getBodyHttpData(String name) { - checkDestroyed(); - - if (!isLastChunk) { - throw new NotEnoughDataDecoderException(); - } - List list = bodyMapHttpData.get(name); - if (list != null) { - return list.get(0); - } - return null; - } - - /** - * Initialized the internals from a new chunk - * - * @param content - * the new received chunk - * @throws ErrorDataDecoderException - * if there is a problem with the charset decoding or other - * errors - */ - @Override - public HttpPostMultipartRequestDecoder offer(HttpContent content) { - checkDestroyed(); - - if (content instanceof LastHttpContent) { - isLastChunk = true; - } - - ByteBuf buf = content.content(); - if (undecodedChunk == null) { - undecodedChunk = - // Since the Handler will release the incoming later on, we need to copy it - // - // We are explicit allocate a buffer and NOT calling copy() as otherwise it may set a maxCapacity - // which is not really usable for us as we may exceed it once we add more bytes. - buf.alloc().buffer(buf.readableBytes()).writeBytes(buf); - } else { - undecodedChunk.writeBytes(buf); - } - parseBody(); - if (undecodedChunk != null && undecodedChunk.writerIndex() > discardThreshold) { - if (undecodedChunk.refCnt() == 1) { - // It's safe to call discardBytes() as we are the only owner of the buffer. - undecodedChunk.discardReadBytes(); - } else { - // There seems to be multiple references of the buffer. Let's copy the data and release the buffer to - // ensure we can give back memory to the system. - ByteBuf buffer = undecodedChunk.alloc().buffer(undecodedChunk.readableBytes()); - buffer.writeBytes(undecodedChunk); - undecodedChunk.release(); - undecodedChunk = buffer; - } - } - return this; - } - - /** - * True if at current getStatus, there is an available decoded - * InterfaceHttpData from the Body. - * - * This getMethod works for chunked and not chunked request. - * - * @return True if at current getStatus, there is a decoded InterfaceHttpData - * @throws EndOfDataDecoderException - * No more data will be available - */ - @Override - public boolean hasNext() { - checkDestroyed(); - - if (currentStatus == MultiPartStatus.EPILOGUE) { - // OK except if end of list - if (bodyListHttpDataRank >= bodyListHttpData.size()) { - throw new EndOfDataDecoderException(); - } - } - return !bodyListHttpData.isEmpty() && bodyListHttpDataRank < bodyListHttpData.size(); - } - - /** - * Returns the next available InterfaceHttpData or null if, at the time it - * is called, there is no more available InterfaceHttpData. A subsequent - * call to offer(httpChunk) could enable more data. - * - * Be sure to call {@link InterfaceHttpData#release()} after you are done - * with processing to make sure to not leak any resources - * - * @return the next available InterfaceHttpData or null if none - * @throws EndOfDataDecoderException - * No more data will be available - */ - @Override - public InterfaceHttpData next() { - checkDestroyed(); - - if (hasNext()) { - return bodyListHttpData.get(bodyListHttpDataRank++); - } - return null; - } - - @Override - public InterfaceHttpData currentPartialHttpData() { - if (currentFileUpload != null) { - return currentFileUpload; - } else { - return currentAttribute; - } - } - - /** - * This getMethod will parse as much as possible data and fill the list and map - * - * @throws ErrorDataDecoderException - * if there is a problem with the charset decoding or other - * errors - */ - private void parseBody() { - if (currentStatus == MultiPartStatus.PREEPILOGUE || currentStatus == MultiPartStatus.EPILOGUE) { - if (isLastChunk) { - currentStatus = MultiPartStatus.EPILOGUE; - } - return; - } - parseBodyMultipart(); - } - - /** - * Utility function to add a new decoded data - */ - protected void addHttpData(InterfaceHttpData data) { - if (data == null) { - return; - } - List datas = bodyMapHttpData.get(data.getName()); - if (datas == null) { - datas = new ArrayList<>(1); - bodyMapHttpData.put(data.getName(), datas); - } - datas.add(data); - bodyListHttpData.add(data); - } - - /** - * Parse the Body for multipart - * - * @throws ErrorDataDecoderException - * if there is a problem with the charset decoding or other - * errors - */ - private void parseBodyMultipart() { - if (undecodedChunk == null || undecodedChunk.readableBytes() == 0) { - // nothing to decode - return; - } - InterfaceHttpData data = decodeMultipart(currentStatus); - while (data != null) { - addHttpData(data); - if (currentStatus == MultiPartStatus.PREEPILOGUE || currentStatus == MultiPartStatus.EPILOGUE) { - break; - } - data = decodeMultipart(currentStatus); - } - } - - /** - * Decode a multipart request by pieces
- *
- * NOTSTARTED PREAMBLE (
- * (HEADERDELIMITER DISPOSITION (FIELD | FILEUPLOAD))*
- * (HEADERDELIMITER DISPOSITION MIXEDPREAMBLE
- * (MIXEDDELIMITER MIXEDDISPOSITION MIXEDFILEUPLOAD)+
- * MIXEDCLOSEDELIMITER)*
- * CLOSEDELIMITER)+ EPILOGUE
- * - * Inspired from HttpMessageDecoder - * - * @return the next decoded InterfaceHttpData or null if none until now. - * @throws ErrorDataDecoderException - * if an error occurs - */ - private InterfaceHttpData decodeMultipart(MultiPartStatus state) { - switch (state) { - case NOTSTARTED: - throw new ErrorDataDecoderException("Should not be called with the current getStatus"); - case PREAMBLE: - // Content-type: multipart/form-data, boundary=AaB03x - throw new ErrorDataDecoderException("Should not be called with the current getStatus"); - case HEADERDELIMITER: { - // --AaB03x or --AaB03x-- - return findMultipartDelimiter(multipartDataBoundary, MultiPartStatus.DISPOSITION, - MultiPartStatus.PREEPILOGUE); - } - case DISPOSITION: { - // content-disposition: form-data; name="field1" - // content-disposition: form-data; name="pics"; filename="file1.txt" - // and other immediate values like - // Content-type: image/gif - // Content-Type: text/plain - // Content-Type: text/plain; charset=ISO-8859-1 - // Content-Transfer-Encoding: binary - // The following line implies a change of mode (mixed mode) - // Content-type: multipart/mixed, boundary=BbC04y - return findMultipartDisposition(); - } - case FIELD: { - // Now get value according to Content-Type and Charset - Charset localCharset = null; - Attribute charsetAttribute = currentFieldAttributes.get(HttpHeaderValues.CHARSET); - if (charsetAttribute != null) { - try { - localCharset = Charset.forName(charsetAttribute.getValue()); - } catch (IOException | UnsupportedCharsetException e) { - throw new ErrorDataDecoderException(e); - } - } - Attribute nameAttribute = currentFieldAttributes.get(HttpHeaderValues.NAME); - if (currentAttribute == null) { - Attribute lengthAttribute = currentFieldAttributes - .get(HttpHeaderNames.CONTENT_LENGTH); - long size; - try { - size = lengthAttribute != null? Long.parseLong(lengthAttribute - .getValue()) : 0L; - } catch (IOException e) { - throw new ErrorDataDecoderException(e); - } catch (NumberFormatException ignored) { - size = 0; - } - try { - if (size > 0) { - currentAttribute = factory.createAttribute(request, - cleanString(nameAttribute.getValue()), size); - } else { - currentAttribute = factory.createAttribute(request, - cleanString(nameAttribute.getValue())); - } - } catch (NullPointerException | IOException | IllegalArgumentException e) { - throw new ErrorDataDecoderException(e); - } - if (localCharset != null) { - currentAttribute.setCharset(localCharset); - } - } - // load data - if (!loadDataMultipartOptimized(undecodedChunk, multipartDataBoundary, currentAttribute)) { - // Delimiter is not found. Need more chunks. - return null; - } - Attribute finalAttribute = currentAttribute; - currentAttribute = null; - currentFieldAttributes = null; - // ready to load the next one - currentStatus = MultiPartStatus.HEADERDELIMITER; - return finalAttribute; - } - case FILEUPLOAD: { - // eventually restart from existing FileUpload - return getFileUpload(multipartDataBoundary); - } - case MIXEDDELIMITER: { - // --AaB03x or --AaB03x-- - // Note that currentFieldAttributes exists - return findMultipartDelimiter(multipartMixedBoundary, MultiPartStatus.MIXEDDISPOSITION, - MultiPartStatus.HEADERDELIMITER); - } - case MIXEDDISPOSITION: { - return findMultipartDisposition(); - } - case MIXEDFILEUPLOAD: { - // eventually restart from existing FileUpload - return getFileUpload(multipartMixedBoundary); - } - case PREEPILOGUE: - return null; - case EPILOGUE: - return null; - default: - throw new ErrorDataDecoderException("Shouldn't reach here."); - } - } - - /** - * Skip control Characters - */ - private static void skipControlCharacters(ByteBuf undecodedChunk) { - if (!undecodedChunk.hasArray()) { - try { - skipControlCharactersStandard(undecodedChunk); - } catch (IndexOutOfBoundsException e1) { - throw new NotEnoughDataDecoderException(e1); - } - return; - } - SeekAheadOptimize sao = new SeekAheadOptimize(undecodedChunk); - while (sao.pos < sao.limit) { - char c = (char) (sao.bytes[sao.pos++] & 0xFF); - if (!Character.isISOControl(c) && !Character.isWhitespace(c)) { - sao.setReadPosition(1); - return; - } - } - throw new NotEnoughDataDecoderException("Access out of bounds"); - } - - private static void skipControlCharactersStandard(ByteBuf undecodedChunk) { - for (;;) { - char c = (char) undecodedChunk.readUnsignedByte(); - if (!Character.isISOControl(c) && !Character.isWhitespace(c)) { - undecodedChunk.readerIndex(undecodedChunk.readerIndex() - 1); - break; - } - } - } - - /** - * Find the next Multipart Delimiter - * - * @param delimiter - * delimiter to find - * @param dispositionStatus - * the next getStatus if the delimiter is a start - * @param closeDelimiterStatus - * the next getStatus if the delimiter is a close delimiter - * @return the next InterfaceHttpData if any - * @throws ErrorDataDecoderException If no multipart delimiter is found, or an error occurs during decoding. - */ - private InterfaceHttpData findMultipartDelimiter(String delimiter, MultiPartStatus dispositionStatus, - MultiPartStatus closeDelimiterStatus) { - // --AaB03x or --AaB03x-- - int readerIndex = undecodedChunk.readerIndex(); - try { - skipControlCharacters(undecodedChunk); - } catch (NotEnoughDataDecoderException ignored) { - undecodedChunk.readerIndex(readerIndex); - return null; - } - skipOneLine(); - String newline; - try { - newline = readDelimiterOptimized(undecodedChunk, delimiter, charset); - } catch (NotEnoughDataDecoderException ignored) { - undecodedChunk.readerIndex(readerIndex); - return null; - } - if (newline.equals(delimiter)) { - currentStatus = dispositionStatus; - return decodeMultipart(dispositionStatus); - } - if (newline.equals(delimiter + "--")) { - // CLOSEDELIMITER or MIXED CLOSEDELIMITER found - currentStatus = closeDelimiterStatus; - if (currentStatus == MultiPartStatus.HEADERDELIMITER) { - // MIXEDCLOSEDELIMITER - // end of the Mixed part - currentFieldAttributes = null; - return decodeMultipart(MultiPartStatus.HEADERDELIMITER); - } - return null; - } - undecodedChunk.readerIndex(readerIndex); - throw new ErrorDataDecoderException("No Multipart delimiter found"); - } - - /** - * Find the next Disposition - * - * @return the next InterfaceHttpData if any - */ - private InterfaceHttpData findMultipartDisposition() { - int readerIndex = undecodedChunk.readerIndex(); - if (currentStatus == MultiPartStatus.DISPOSITION) { - currentFieldAttributes = new TreeMap<>(CaseIgnoringComparator.INSTANCE); - } - // read many lines until empty line with newline found! Store all data - while (!skipOneLine()) { - String newline; - try { - skipControlCharacters(undecodedChunk); - newline = readLineOptimized(undecodedChunk, charset); - } catch (NotEnoughDataDecoderException ignored) { - undecodedChunk.readerIndex(readerIndex); - return null; - } - String[] contents = splitMultipartHeader(newline); - if (HttpHeaderNames.CONTENT_DISPOSITION.contentEqualsIgnoreCase(contents[0])) { - boolean checkSecondArg; - if (currentStatus == MultiPartStatus.DISPOSITION) { - checkSecondArg = HttpHeaderValues.FORM_DATA.contentEqualsIgnoreCase(contents[1]); - } else { - checkSecondArg = HttpHeaderValues.ATTACHMENT.contentEqualsIgnoreCase(contents[1]) - || HttpHeaderValues.FILE.contentEqualsIgnoreCase(contents[1]); - } - if (checkSecondArg) { - // read next values and store them in the map as Attribute - for (int i = 2; i < contents.length; i++) { - String[] values = contents[i].split("=", 2); - Attribute attribute; - try { - attribute = getContentDispositionAttribute(values); - } catch (NullPointerException | IllegalArgumentException e) { - throw new ErrorDataDecoderException(e); - } - currentFieldAttributes.put(attribute.getName(), attribute); - } - } - } else if (HttpHeaderNames.CONTENT_TRANSFER_ENCODING.contentEqualsIgnoreCase(contents[0])) { - Attribute attribute; - try { - attribute = factory.createAttribute(request, HttpHeaderNames.CONTENT_TRANSFER_ENCODING.toString(), - cleanString(contents[1])); - } catch (NullPointerException | IllegalArgumentException e) { - throw new ErrorDataDecoderException(e); - } - - currentFieldAttributes.put(HttpHeaderNames.CONTENT_TRANSFER_ENCODING, attribute); - } else if (HttpHeaderNames.CONTENT_LENGTH.contentEqualsIgnoreCase(contents[0])) { - Attribute attribute; - try { - attribute = factory.createAttribute(request, HttpHeaderNames.CONTENT_LENGTH.toString(), - cleanString(contents[1])); - } catch (NullPointerException | IllegalArgumentException e) { - throw new ErrorDataDecoderException(e); - } - - currentFieldAttributes.put(HttpHeaderNames.CONTENT_LENGTH, attribute); - } else if (HttpHeaderNames.CONTENT_TYPE.contentEqualsIgnoreCase(contents[0])) { - // Take care of possible "multipart/mixed" - if (HttpHeaderValues.MULTIPART_MIXED.contentEqualsIgnoreCase(contents[1])) { - if (currentStatus == MultiPartStatus.DISPOSITION) { - String values = StringUtil.substringAfter(contents[2], '='); - multipartMixedBoundary = "--" + values; - currentStatus = MultiPartStatus.MIXEDDELIMITER; - return decodeMultipart(MultiPartStatus.MIXEDDELIMITER); - } else { - throw new ErrorDataDecoderException("Mixed Multipart found in a previous Mixed Multipart"); - } - } else { - for (int i = 1; i < contents.length; i++) { - final String charsetHeader = HttpHeaderValues.CHARSET.toString(); - if (contents[i].regionMatches(true, 0, charsetHeader, 0, charsetHeader.length())) { - String values = StringUtil.substringAfter(contents[i], '='); - Attribute attribute; - try { - attribute = factory.createAttribute(request, charsetHeader, cleanString(values)); - } catch (NullPointerException | IllegalArgumentException e) { - throw new ErrorDataDecoderException(e); - } - currentFieldAttributes.put(HttpHeaderValues.CHARSET, attribute); - } else { - Attribute attribute; - try { - attribute = factory.createAttribute(request, - cleanString(contents[0]), contents[i]); - } catch (NullPointerException | IllegalArgumentException e) { - throw new ErrorDataDecoderException(e); - } - currentFieldAttributes.put(attribute.getName(), attribute); - } - } - } - } - } - // Is it a FileUpload - Attribute filenameAttribute = currentFieldAttributes.get(HttpHeaderValues.FILENAME); - if (currentStatus == MultiPartStatus.DISPOSITION) { - if (filenameAttribute != null) { - // FileUpload - currentStatus = MultiPartStatus.FILEUPLOAD; - // do not change the buffer position - return decodeMultipart(MultiPartStatus.FILEUPLOAD); - } else { - // Field - currentStatus = MultiPartStatus.FIELD; - // do not change the buffer position - return decodeMultipart(MultiPartStatus.FIELD); - } - } else { - if (filenameAttribute != null) { - // FileUpload - currentStatus = MultiPartStatus.MIXEDFILEUPLOAD; - // do not change the buffer position - return decodeMultipart(MultiPartStatus.MIXEDFILEUPLOAD); - } else { - // Field is not supported in MIXED mode - throw new ErrorDataDecoderException("Filename not found"); - } - } - } - - private static final String FILENAME_ENCODED = HttpHeaderValues.FILENAME.toString() + '*'; - - private Attribute getContentDispositionAttribute(String... values) { - String name = cleanString(values[0]); - String value = values[1]; - - // Filename can be token, quoted or encoded. See https://tools.ietf.org/html/rfc5987 - if (HttpHeaderValues.FILENAME.contentEquals(name)) { - // Value is quoted or token. Strip if quoted: - int last = value.length() - 1; - if (last > 0 && - value.charAt(0) == HttpConstants.DOUBLE_QUOTE && - value.charAt(last) == HttpConstants.DOUBLE_QUOTE) { - value = value.substring(1, last); - } - } else if (FILENAME_ENCODED.equals(name)) { - try { - name = HttpHeaderValues.FILENAME.toString(); - String[] split = cleanString(value).split("'", 3); - value = QueryStringDecoder.decodeComponent(split[2], Charset.forName(split[0])); - } catch (ArrayIndexOutOfBoundsException | UnsupportedCharsetException e) { - throw new ErrorDataDecoderException(e); - } - } else { - // otherwise we need to clean the value - value = cleanString(value); - } - return factory.createAttribute(request, name, value); - } - - /** - * Get the FileUpload (new one or current one) - * - * @param delimiter - * the delimiter to use - * @return the InterfaceHttpData if any - * @throws ErrorDataDecoderException If an error occurs when decoding the multipart data. - */ - protected InterfaceHttpData getFileUpload(String delimiter) { - // eventually restart from existing FileUpload - // Now get value according to Content-Type and Charset - Attribute encoding = currentFieldAttributes.get(HttpHeaderNames.CONTENT_TRANSFER_ENCODING); - Charset localCharset = charset; - // Default - TransferEncodingMechanism mechanism = TransferEncodingMechanism.BIT7; - if (encoding != null) { - String code; - try { - code = encoding.getValue().toLowerCase(); - } catch (IOException e) { - throw new ErrorDataDecoderException(e); - } - switch (code) { - case BIT_7_STRING: - localCharset = CharsetUtil.US_ASCII; - break; - case BIT_8_STRING: - localCharset = CharsetUtil.ISO_8859_1; - mechanism = TransferEncodingMechanism.BIT8; - break; - case BINARY_STRING: - // no real charset, so let the default - mechanism = TransferEncodingMechanism.BINARY; - break; - default: - throw new ErrorDataDecoderException("TransferEncoding Unknown: " + code); - } - } - Attribute charsetAttribute = currentFieldAttributes.get(HttpHeaderValues.CHARSET); - if (charsetAttribute != null) { - try { - localCharset = Charset.forName(charsetAttribute.getValue()); - } catch (IOException | UnsupportedCharsetException e) { - throw new ErrorDataDecoderException(e); - } - } - if (currentFileUpload == null) { - Attribute filenameAttribute = currentFieldAttributes.get(HttpHeaderValues.FILENAME); - Attribute nameAttribute = currentFieldAttributes.get(HttpHeaderValues.NAME); - Attribute contentTypeAttribute = currentFieldAttributes.get(HttpHeaderNames.CONTENT_TYPE); - Attribute lengthAttribute = currentFieldAttributes.get(HttpHeaderNames.CONTENT_LENGTH); - long size; - try { - size = lengthAttribute != null ? Long.parseLong(lengthAttribute.getValue()) : 0L; - } catch (IOException e) { - throw new ErrorDataDecoderException(e); - } catch (NumberFormatException ignored) { - size = 0; - } - try { - String contentType; - if (contentTypeAttribute != null) { - contentType = contentTypeAttribute.getValue(); - } else { - contentType = HttpPostBodyUtil.DEFAULT_BINARY_CONTENT_TYPE; - } - currentFileUpload = factory.createFileUpload(request, - cleanString(nameAttribute.getValue()), cleanString(filenameAttribute.getValue()), - contentType, mechanism.value(), localCharset, - size); - } catch (NullPointerException | IllegalArgumentException | IOException e) { - throw new ErrorDataDecoderException(e); - } - } - // load data as much as possible - if (!loadDataMultipartOptimized(undecodedChunk, delimiter, currentFileUpload)) { - // Delimiter is not found. Need more chunks. - return null; - } - if (currentFileUpload.isCompleted()) { - // ready to load the next one - if (currentStatus == MultiPartStatus.FILEUPLOAD) { - currentStatus = MultiPartStatus.HEADERDELIMITER; - currentFieldAttributes = null; - } else { - currentStatus = MultiPartStatus.MIXEDDELIMITER; - cleanMixedAttributes(); - } - FileUpload fileUpload = currentFileUpload; - currentFileUpload = null; - return fileUpload; - } - // do not change the buffer position - // since some can be already saved into FileUpload - // So do not change the currentStatus - return null; - } - - /** - * Destroy the {@link HttpPostMultipartRequestDecoder} and release all it resources. After this method - * was called it is not possible to operate on it anymore. - */ - @Override - public void destroy() { - // Release all data items, including those not yet pulled, only file based items - cleanFiles(); - // Clean Memory based data - for (InterfaceHttpData httpData : bodyListHttpData) { - // Might have been already released by the user - if (httpData.refCnt() > 0) { - httpData.release(); - } - } - - destroyed = true; - - if (undecodedChunk != null && undecodedChunk.refCnt() > 0) { - undecodedChunk.release(); - undecodedChunk = null; - } - } - - /** - * Clean all HttpDatas (on Disk) for the current request. - */ - @Override - public void cleanFiles() { - checkDestroyed(); - - factory.cleanRequestHttpData(request); - } - - /** - * Remove the given FileUpload from the list of FileUploads to clean - */ - @Override - public void removeHttpDataFromClean(InterfaceHttpData data) { - checkDestroyed(); - - factory.removeHttpDataFromClean(request, data); - } - - /** - * Remove all Attributes that should be cleaned between two FileUpload in - * Mixed mode - */ - private void cleanMixedAttributes() { - currentFieldAttributes.remove(HttpHeaderValues.CHARSET); - currentFieldAttributes.remove(HttpHeaderNames.CONTENT_LENGTH); - currentFieldAttributes.remove(HttpHeaderNames.CONTENT_TRANSFER_ENCODING); - currentFieldAttributes.remove(HttpHeaderNames.CONTENT_TYPE); - currentFieldAttributes.remove(HttpHeaderValues.FILENAME); - } - - /** - * Read one line up to the CRLF or LF - * - * @return the String from one line - * @throws NotEnoughDataDecoderException - * Need more chunks and reset the {@code readerIndex} to the previous - * value - */ - private static String readLineOptimized(ByteBuf undecodedChunk, Charset charset) { - int readerIndex = undecodedChunk.readerIndex(); - ByteBuf line = null; - try { - if (undecodedChunk.isReadable()) { - int posLfOrCrLf = HttpPostBodyUtil.findLineBreak(undecodedChunk, undecodedChunk.readerIndex()); - if (posLfOrCrLf <= 0) { - throw new NotEnoughDataDecoderException(); - } - try { - line = undecodedChunk.alloc().heapBuffer(posLfOrCrLf); - line.writeBytes(undecodedChunk, posLfOrCrLf); - - byte nextByte = undecodedChunk.readByte(); - if (nextByte == HttpConstants.CR) { - // force read next byte since LF is the following one - undecodedChunk.readByte(); - } - return line.toString(charset); - } finally { - line.release(); - } - } - } catch (IndexOutOfBoundsException e) { - undecodedChunk.readerIndex(readerIndex); - throw new NotEnoughDataDecoderException(e); - } - undecodedChunk.readerIndex(readerIndex); - throw new NotEnoughDataDecoderException(); - } - - /** - * Read one line up to --delimiter or --delimiter-- and if existing the CRLF - * or LF Read one line up to --delimiter or --delimiter-- and if existing - * the CRLF or LF. Note that CRLF or LF are mandatory for opening delimiter - * (--delimiter) but not for closing delimiter (--delimiter--) since some - * clients does not include CRLF in this case. - * - * @param delimiter - * of the form --string, such that '--' is already included - * @return the String from one line as the delimiter searched (opening or - * closing) - * @throws NotEnoughDataDecoderException - * Need more chunks and reset the {@code readerIndex} to the previous - * value - */ - private static String readDelimiterOptimized(ByteBuf undecodedChunk, String delimiter, Charset charset) { - final int readerIndex = undecodedChunk.readerIndex(); - final byte[] bdelimiter = delimiter.getBytes(charset); - final int delimiterLength = bdelimiter.length; - try { - int delimiterPos = HttpPostBodyUtil.findDelimiter(undecodedChunk, readerIndex, bdelimiter, false); - if (delimiterPos < 0) { - // delimiter not found so break here ! - undecodedChunk.readerIndex(readerIndex); - throw new NotEnoughDataDecoderException(); - } - StringBuilder sb = new StringBuilder(delimiter); - undecodedChunk.readerIndex(readerIndex + delimiterPos + delimiterLength); - // Now check if either opening delimiter or closing delimiter - if (undecodedChunk.isReadable()) { - byte nextByte = undecodedChunk.readByte(); - // first check for opening delimiter - if (nextByte == HttpConstants.CR) { - nextByte = undecodedChunk.readByte(); - if (nextByte == HttpConstants.LF) { - return sb.toString(); - } else { - // error since CR must be followed by LF - // delimiter not found so break here ! - undecodedChunk.readerIndex(readerIndex); - throw new NotEnoughDataDecoderException(); - } - } else if (nextByte == HttpConstants.LF) { - return sb.toString(); - } else if (nextByte == '-') { - sb.append('-'); - // second check for closing delimiter - nextByte = undecodedChunk.readByte(); - if (nextByte == '-') { - sb.append('-'); - // now try to find if CRLF or LF there - if (undecodedChunk.isReadable()) { - nextByte = undecodedChunk.readByte(); - if (nextByte == HttpConstants.CR) { - nextByte = undecodedChunk.readByte(); - if (nextByte == HttpConstants.LF) { - return sb.toString(); - } else { - // error CR without LF - // delimiter not found so break here ! - undecodedChunk.readerIndex(readerIndex); - throw new NotEnoughDataDecoderException(); - } - } else if (nextByte == HttpConstants.LF) { - return sb.toString(); - } else { - // No CRLF but ok however (Adobe Flash uploader) - // minus 1 since we read one char ahead but - // should not - undecodedChunk.readerIndex(undecodedChunk.readerIndex() - 1); - return sb.toString(); - } - } - // FIXME what do we do here? - // either considering it is fine, either waiting for - // more data to come? - // lets try considering it is fine... - return sb.toString(); - } - // only one '-' => not enough - // whatever now => error since incomplete - } - } - } catch (IndexOutOfBoundsException e) { - undecodedChunk.readerIndex(readerIndex); - throw new NotEnoughDataDecoderException(e); - } - undecodedChunk.readerIndex(readerIndex); - throw new NotEnoughDataDecoderException(); - } - - /** - * Rewrite buffer in order to skip lengthToSkip bytes from current readerIndex, - * such that any readable bytes available after readerIndex + lengthToSkip (so before writerIndex) - * are moved at readerIndex position, - * therefore decreasing writerIndex of lengthToSkip at the end of the process. - * - * @param buffer the buffer to rewrite from current readerIndex - * @param lengthToSkip the size to skip from readerIndex - */ - private static void rewriteCurrentBuffer(ByteBuf buffer, int lengthToSkip) { - if (lengthToSkip == 0) { - return; - } - final int readerIndex = buffer.readerIndex(); - final int readableBytes = buffer.readableBytes(); - if (readableBytes == lengthToSkip) { - buffer.readerIndex(readerIndex); - buffer.writerIndex(readerIndex); - return; - } - buffer.setBytes(readerIndex, buffer, readerIndex + lengthToSkip, readableBytes - lengthToSkip); - buffer.readerIndex(readerIndex); - buffer.writerIndex(readerIndex + readableBytes - lengthToSkip); - } - - /** - * Load the field value or file data from a Multipart request - * - * @return {@code true} if the last chunk is loaded (boundary delimiter found), {@code false} if need more chunks - */ - private static boolean loadDataMultipartOptimized(ByteBuf undecodedChunk, String delimiter, HttpData httpData) { - if (!undecodedChunk.isReadable()) { - return false; - } - final int startReaderIndex = undecodedChunk.readerIndex(); - final byte[] bdelimiter = delimiter.getBytes(httpData.getCharset()); - int posDelimiter = HttpPostBodyUtil.findDelimiter(undecodedChunk, startReaderIndex, bdelimiter, true); - if (posDelimiter < 0) { - // Not found but however perhaps because incomplete so search LF or CRLF from the end. - // Possible last bytes contain partially delimiter - // (delimiter is possibly partially there, at least 1 missing byte), - // therefore searching last delimiter.length +1 (+1 for CRLF instead of LF) - int lastPosition = undecodedChunk.readableBytes() - bdelimiter.length - 1; - if (lastPosition < 0) { - // Not enough bytes, but at most delimiter.length bytes available so can still try to find CRLF there - lastPosition = 0; - } - posDelimiter = HttpPostBodyUtil.findLastLineBreak(undecodedChunk, startReaderIndex + lastPosition); - if (posDelimiter < 0) { - // not found so this chunk can be fully added - ByteBuf content = undecodedChunk.copy(); - try { - httpData.addContent(content, false); - } catch (IOException e) { - throw new ErrorDataDecoderException(e); - } - undecodedChunk.readerIndex(startReaderIndex); - undecodedChunk.writerIndex(startReaderIndex); - return false; - } - // posDelimiter is not from startReaderIndex but from startReaderIndex + lastPosition - posDelimiter += lastPosition; - if (posDelimiter == 0) { - // Nothing to add - return false; - } - // Not fully but still some bytes to provide: httpData is not yet finished since delimiter not found - ByteBuf content = undecodedChunk.copy(startReaderIndex, posDelimiter); - try { - httpData.addContent(content, false); - } catch (IOException e) { - throw new ErrorDataDecoderException(e); - } - rewriteCurrentBuffer(undecodedChunk, posDelimiter); - return false; - } - // Delimiter found at posDelimiter, including LF or CRLF, so httpData has its last chunk - ByteBuf content = undecodedChunk.copy(startReaderIndex, posDelimiter); - try { - httpData.addContent(content, true); - } catch (IOException e) { - throw new ErrorDataDecoderException(e); - } - rewriteCurrentBuffer(undecodedChunk, posDelimiter); - return true; - } - - /** - * Clean the String from any unallowed character - * - * @return the cleaned String - */ - private static String cleanString(String field) { - int size = field.length(); - StringBuilder sb = new StringBuilder(size); - for (int i = 0; i < size; i++) { - char nextChar = field.charAt(i); - switch (nextChar) { - case HttpConstants.COLON: - case HttpConstants.COMMA: - case HttpConstants.EQUALS: - case HttpConstants.SEMICOLON: - case HttpConstants.HT: - sb.append(HttpConstants.SP_CHAR); - break; - case HttpConstants.DOUBLE_QUOTE: - // nothing added, just removes it - break; - default: - sb.append(nextChar); - break; - } - } - return sb.toString().trim(); - } - - /** - * Skip one empty line - * - * @return True if one empty line was skipped - */ - private boolean skipOneLine() { - if (!undecodedChunk.isReadable()) { - return false; - } - byte nextByte = undecodedChunk.readByte(); - if (nextByte == HttpConstants.CR) { - if (!undecodedChunk.isReadable()) { - undecodedChunk.readerIndex(undecodedChunk.readerIndex() - 1); - return false; - } - nextByte = undecodedChunk.readByte(); - if (nextByte == HttpConstants.LF) { - return true; - } - undecodedChunk.readerIndex(undecodedChunk.readerIndex() - 2); - return false; - } - if (nextByte == HttpConstants.LF) { - return true; - } - undecodedChunk.readerIndex(undecodedChunk.readerIndex() - 1); - return false; - } - - /** - * Split one header in Multipart - * - * @return an array of String where rank 0 is the name of the header, - * follows by several values that were separated by ';' or ',' - */ - private static String[] splitMultipartHeader(String sb) { - ArrayList headers = new ArrayList<>(1); - int nameStart; - int nameEnd; - int colonEnd; - int valueStart; - int valueEnd; - nameStart = HttpPostBodyUtil.findNonWhitespace(sb, 0); - for (nameEnd = nameStart; nameEnd < sb.length(); nameEnd++) { - char ch = sb.charAt(nameEnd); - if (ch == ':' || Character.isWhitespace(ch)) { - break; - } - } - for (colonEnd = nameEnd; colonEnd < sb.length(); colonEnd++) { - if (sb.charAt(colonEnd) == ':') { - colonEnd++; - break; - } - } - valueStart = HttpPostBodyUtil.findNonWhitespace(sb, colonEnd); - valueEnd = HttpPostBodyUtil.findEndOfString(sb); - headers.add(sb.substring(nameStart, nameEnd)); - String svalue = (valueStart >= valueEnd) ? StringUtil.EMPTY_STRING : sb.substring(valueStart, valueEnd); - String[] values; - if (svalue.indexOf(';') >= 0) { - values = splitMultipartHeaderValues(svalue); - } else { - values = svalue.split(","); - } - for (String value : values) { - headers.add(value.trim()); - } - String[] array = new String[headers.size()]; - for (int i = 0; i < headers.size(); i++) { - array[i] = headers.get(i); - } - return array; - } - - /** - * Split one header value in Multipart - * @return an array of String where values that were separated by ';' or ',' - */ - private static String[] splitMultipartHeaderValues(String svalue) { - List values = InternalThreadLocalMap.get().arrayList(1); - boolean inQuote = false; - boolean escapeNext = false; - int start = 0; - for (int i = 0; i < svalue.length(); i++) { - char c = svalue.charAt(i); - if (inQuote) { - if (escapeNext) { - escapeNext = false; - } else { - if (c == '\\') { - escapeNext = true; - } else if (c == '"') { - inQuote = false; - } - } - } else { - if (c == '"') { - inQuote = true; - } else if (c == ';') { - values.add(svalue.substring(start, i)); - start = i + 1; - } - } - } - values.add(svalue.substring(start)); - return values.toArray(new String[0]); - } - - /** - * This method is package private intentionally in order to allow during tests - * to access to the amount of memory allocated (capacity) within the private - * ByteBuf undecodedChunk - * - * @return the number of bytes the internal buffer can contain - */ - int getCurrentAllocatedCapacity() { - return undecodedChunk.capacity(); - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestDecoder.java deleted file mode 100644 index 7f6a681d51..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestDecoder.java +++ /dev/null @@ -1,341 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import static java.util.Objects.requireNonNull; - -import io.netty.handler.codec.DecoderException; -import io.netty.handler.codec.http.HttpConstants; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.util.internal.StringUtil; - -import java.nio.charset.Charset; -import java.util.List; - -/** - * This decoder will decode Body and can handle POST BODY. - * - * You MUST call {@link #destroy()} after completion to release all resources. - * - */ -public class HttpPostRequestDecoder implements InterfaceHttpPostRequestDecoder { - - static final int DEFAULT_DISCARD_THRESHOLD = 10 * 1024 * 1024; - - private final InterfaceHttpPostRequestDecoder decoder; - - /** - * - * @param request - * the request to decode - * @throws NullPointerException - * for request - * @throws ErrorDataDecoderException - * if the default charset was wrong when decoding or other - * errors - */ - public HttpPostRequestDecoder(HttpRequest request) { - this(new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE), request, HttpConstants.DEFAULT_CHARSET); - } - - /** - * - * @param factory - * the factory used to create InterfaceHttpData - * @param request - * the request to decode - * @throws NullPointerException - * for request or factory - * @throws ErrorDataDecoderException - * if the default charset was wrong when decoding or other - * errors - */ - public HttpPostRequestDecoder(HttpDataFactory factory, HttpRequest request) { - this(factory, request, HttpConstants.DEFAULT_CHARSET); - } - - /** - * - * @param factory - * the factory used to create InterfaceHttpData - * @param request - * the request to decode - * @param charset - * the charset to use as default - * @throws NullPointerException - * for request or charset or factory - * @throws ErrorDataDecoderException - * if the default charset was wrong when decoding or other - * errors - */ - public HttpPostRequestDecoder(HttpDataFactory factory, HttpRequest request, Charset charset) { - requireNonNull(factory, "factory"); - requireNonNull(request, "request"); - requireNonNull(charset, "charset"); - // Fill default values - if (isMultipart(request)) { - decoder = new HttpPostMultipartRequestDecoder(factory, request, charset); - } else { - decoder = new HttpPostStandardRequestDecoder(factory, request, charset); - } - } - - /** - * states follow NOTSTARTED PREAMBLE ( (HEADERDELIMITER DISPOSITION (FIELD | - * FILEUPLOAD))* (HEADERDELIMITER DISPOSITION MIXEDPREAMBLE (MIXEDDELIMITER - * MIXEDDISPOSITION MIXEDFILEUPLOAD)+ MIXEDCLOSEDELIMITER)* CLOSEDELIMITER)+ - * EPILOGUE - * - * First getStatus is: NOSTARTED - * - * Content-type: multipart/form-data, boundary=AaB03x => PREAMBLE in Header - * - * --AaB03x => HEADERDELIMITER content-disposition: form-data; name="field1" - * => DISPOSITION - * - * Joe Blow => FIELD --AaB03x => HEADERDELIMITER content-disposition: - * form-data; name="pics" => DISPOSITION Content-type: multipart/mixed, - * boundary=BbC04y - * - * --BbC04y => MIXEDDELIMITER Content-disposition: attachment; - * filename="file1.txt" => MIXEDDISPOSITION Content-Type: text/plain - * - * ... contents of file1.txt ... => MIXEDFILEUPLOAD --BbC04y => - * MIXEDDELIMITER Content-disposition: file; filename="file2.gif" => - * MIXEDDISPOSITION Content-type: image/gif Content-Transfer-Encoding: - * binary - * - * ...contents of file2.gif... => MIXEDFILEUPLOAD --BbC04y-- => - * MIXEDCLOSEDELIMITER --AaB03x-- => CLOSEDELIMITER - * - * Once CLOSEDELIMITER is found, last getStatus is EPILOGUE - */ - protected enum MultiPartStatus { - NOTSTARTED, PREAMBLE, HEADERDELIMITER, DISPOSITION, FIELD, FILEUPLOAD, MIXEDPREAMBLE, MIXEDDELIMITER, - MIXEDDISPOSITION, MIXEDFILEUPLOAD, MIXEDCLOSEDELIMITER, CLOSEDELIMITER, PREEPILOGUE, EPILOGUE - } - - /** - * Check if the given request is a multipart request - * @return True if the request is a Multipart request - */ - public static boolean isMultipart(HttpRequest request) { - String mimeType = request.headers().get(HttpHeaderNames.CONTENT_TYPE); - if (mimeType != null && mimeType.startsWith(HttpHeaderValues.MULTIPART_FORM_DATA.toString())) { - return getMultipartDataBoundary(mimeType) != null; - } - return false; - } - - /** - * Check from the request ContentType if this request is a Multipart request. - * @return an array of String if multipartDataBoundary exists with the multipartDataBoundary - * as first element, charset if any as second (missing if not set), else null - */ - protected static String[] getMultipartDataBoundary(String contentType) { - // Check if Post using "multipart/form-data; boundary=--89421926422648 [; charset=xxx]" - String[] headerContentType = splitHeaderContentType(contentType); - final String multiPartHeader = HttpHeaderValues.MULTIPART_FORM_DATA.toString(); - if (headerContentType[0].regionMatches(true, 0, multiPartHeader, 0 , multiPartHeader.length())) { - int mrank; - int crank; - final String boundaryHeader = HttpHeaderValues.BOUNDARY.toString(); - if (headerContentType[1].regionMatches(true, 0, boundaryHeader, 0, boundaryHeader.length())) { - mrank = 1; - crank = 2; - } else if (headerContentType[2].regionMatches(true, 0, boundaryHeader, 0, boundaryHeader.length())) { - mrank = 2; - crank = 1; - } else { - return null; - } - String boundary = StringUtil.substringAfter(headerContentType[mrank], '='); - if (boundary == null) { - throw new ErrorDataDecoderException("Needs a boundary value"); - } - if (boundary.charAt(0) == '"') { - String bound = boundary.trim(); - int index = bound.length() - 1; - if (bound.charAt(index) == '"') { - boundary = bound.substring(1, index); - } - } - final String charsetHeader = HttpHeaderValues.CHARSET.toString(); - if (headerContentType[crank].regionMatches(true, 0, charsetHeader, 0, charsetHeader.length())) { - String charset = StringUtil.substringAfter(headerContentType[crank], '='); - if (charset != null) { - return new String[] {"--" + boundary, charset}; - } - } - return new String[] {"--" + boundary}; - } - return null; - } - - @Override - public boolean isMultipart() { - return decoder.isMultipart(); - } - - @Override - public void setDiscardThreshold(int discardThreshold) { - decoder.setDiscardThreshold(discardThreshold); - } - - @Override - public int getDiscardThreshold() { - return decoder.getDiscardThreshold(); - } - - @Override - public List getBodyHttpDatas() { - return decoder.getBodyHttpDatas(); - } - - @Override - public List getBodyHttpDatas(String name) { - return decoder.getBodyHttpDatas(name); - } - - @Override - public InterfaceHttpData getBodyHttpData(String name) { - return decoder.getBodyHttpData(name); - } - - @Override - public InterfaceHttpPostRequestDecoder offer(HttpContent content) { - return decoder.offer(content); - } - - @Override - public boolean hasNext() { - return decoder.hasNext(); - } - - @Override - public InterfaceHttpData next() { - return decoder.next(); - } - - @Override - public InterfaceHttpData currentPartialHttpData() { - return decoder.currentPartialHttpData(); - } - - @Override - public void destroy() { - decoder.destroy(); - } - - @Override - public void cleanFiles() { - decoder.cleanFiles(); - } - - @Override - public void removeHttpDataFromClean(InterfaceHttpData data) { - decoder.removeHttpDataFromClean(data); - } - - /** - * Split the very first line (Content-Type value) in 3 Strings - * - * @return the array of 3 Strings - */ - private static String[] splitHeaderContentType(String sb) { - int aStart; - int aEnd; - int bStart; - int bEnd; - int cStart; - int cEnd; - aStart = HttpPostBodyUtil.findNonWhitespace(sb, 0); - aEnd = sb.indexOf(';'); - if (aEnd == -1) { - return new String[] { sb, "", "" }; - } - bStart = HttpPostBodyUtil.findNonWhitespace(sb, aEnd + 1); - if (sb.charAt(aEnd - 1) == ' ') { - aEnd--; - } - bEnd = sb.indexOf(';', bStart); - if (bEnd == -1) { - bEnd = HttpPostBodyUtil.findEndOfString(sb); - return new String[] { sb.substring(aStart, aEnd), sb.substring(bStart, bEnd), "" }; - } - cStart = HttpPostBodyUtil.findNonWhitespace(sb, bEnd + 1); - if (sb.charAt(bEnd - 1) == ' ') { - bEnd--; - } - cEnd = HttpPostBodyUtil.findEndOfString(sb); - return new String[] { sb.substring(aStart, aEnd), sb.substring(bStart, bEnd), sb.substring(cStart, cEnd) }; - } - - /** - * Exception when try reading data from request in chunked format, and not - * enough data are available (need more chunks) - */ - public static class NotEnoughDataDecoderException extends DecoderException { - private static final long serialVersionUID = -7846841864603865638L; - - public NotEnoughDataDecoderException() { - } - - public NotEnoughDataDecoderException(String msg) { - super(msg); - } - - public NotEnoughDataDecoderException(Throwable cause) { - super(cause); - } - - public NotEnoughDataDecoderException(String msg, Throwable cause) { - super(msg, cause); - } - } - - /** - * Exception when the body is fully decoded, even if there is still data - */ - public static class EndOfDataDecoderException extends DecoderException { - private static final long serialVersionUID = 1336267941020800769L; - } - - /** - * Exception when an error occurs while decoding - */ - public static class ErrorDataDecoderException extends DecoderException { - private static final long serialVersionUID = 5020247425493164465L; - - public ErrorDataDecoderException() { - } - - public ErrorDataDecoderException(String msg) { - super(msg); - } - - public ErrorDataDecoderException(Throwable cause) { - super(cause); - } - - public ErrorDataDecoderException(String msg, Throwable cause) { - super(msg, cause); - } - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestEncoder.java deleted file mode 100755 index 425718714b..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestEncoder.java +++ /dev/null @@ -1,1346 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.DecoderResult; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.DefaultHttpContent; -import io.netty.handler.codec.http.EmptyHttpHeaders; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.HttpConstants; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpUtil; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http.LastHttpContent; -import io.netty.handler.stream.ChunkedInput; -import io.netty.util.internal.StringUtil; - -import java.io.File; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.List; -import java.util.ListIterator; -import java.util.Map; -import java.util.concurrent.ThreadLocalRandom; -import java.util.regex.Pattern; - -import static io.netty.buffer.Unpooled.wrappedBuffer; -import static java.util.AbstractMap.SimpleImmutableEntry; -import static java.util.Objects.requireNonNull; - -/** - * This encoder will help to encode Request for a FORM as POST. - * - *

According to RFC 7231, POST, PUT and OPTIONS allow to have a body. - * This encoder will support widely all methods except TRACE since the RFC notes - * for GET, DELETE, HEAD and CONNECT: (replaces XXX by one of these methods)

- *

"A payload within a XXX request message has no defined semantics; - * sending a payload body on a XXX request might cause some existing - * implementations to reject the request."

- *

On the contrary, for TRACE method, RFC says:

- *

"A client MUST NOT send a message body in a TRACE request."

- */ -public class HttpPostRequestEncoder implements ChunkedInput { - - /** - * Different modes to use to encode form data. - */ - public enum EncoderMode { - /** - * Legacy mode which should work for most. It is known to not work with OAUTH. For OAUTH use - * {@link EncoderMode#RFC3986}. The W3C form recommendations this for submitting post form data. - */ - RFC1738, - - /** - * Mode which is more new and is used for OAUTH - */ - RFC3986, - - /** - * The HTML5 spec disallows mixed mode in multipart/form-data - * requests. More concretely this means that more files submitted - * under the same name will not be encoded using mixed mode, but - * will be treated as distinct fields. - * - * Reference: - * https://www.w3.org/TR/html5/forms.html#multipart-form-data - */ - HTML5 - } - - @SuppressWarnings("rawtypes") - private static final Map.Entry[] percentEncodings; - - static { - percentEncodings = new Map.Entry[] { - new SimpleImmutableEntry<>(Pattern.compile("\\*"), "%2A"), - new SimpleImmutableEntry<>(Pattern.compile("\\+"), "%20"), - new SimpleImmutableEntry<>(Pattern.compile("~"), "%7E") - }; - } - - /** - * Factory used to create InterfaceHttpData - */ - private final HttpDataFactory factory; - - /** - * Request to encode - */ - private final HttpRequest request; - - /** - * Default charset to use - */ - private final Charset charset; - - /** - * Chunked false by default - */ - private boolean isChunked; - - /** - * InterfaceHttpData for Body (without encoding) - */ - private final List bodyListDatas; - /** - * The final Multipart List of InterfaceHttpData including encoding - */ - final List multipartHttpDatas; - - /** - * Does this request is a Multipart request - */ - private final boolean isMultipart; - - /** - * If multipart, this is the boundary for the flobal multipart - */ - String multipartDataBoundary; - - /** - * If multipart, there could be internal multiparts (mixed) to the global multipart. Only one level is allowed. - */ - String multipartMixedBoundary; - /** - * To check if the header has been finalized - */ - private boolean headerFinalized; - - private final EncoderMode encoderMode; - - /** - * - * @param request - * the request to encode - * @param multipart - * True if the FORM is a ENCTYPE="multipart/form-data" - * @throws NullPointerException - * for request - * @throws ErrorDataEncoderException - * if the request is a TRACE - */ - public HttpPostRequestEncoder(HttpRequest request, boolean multipart) throws ErrorDataEncoderException { - this(new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE), request, multipart, - HttpConstants.DEFAULT_CHARSET, EncoderMode.RFC1738); - } - - /** - * - * @param factory - * the factory used to create InterfaceHttpData - * @param request - * the request to encode - * @param multipart - * True if the FORM is a ENCTYPE="multipart/form-data" - * @throws NullPointerException - * for request and factory - * @throws ErrorDataEncoderException - * if the request is a TRACE - */ - public HttpPostRequestEncoder(HttpDataFactory factory, HttpRequest request, boolean multipart) - throws ErrorDataEncoderException { - this(factory, request, multipart, HttpConstants.DEFAULT_CHARSET, EncoderMode.RFC1738); - } - - /** - * - * @param factory - * the factory used to create InterfaceHttpData - * @param request - * the request to encode - * @param multipart - * True if the FORM is a ENCTYPE="multipart/form-data" - * @param charset - * the charset to use as default - * @param encoderMode - * the mode for the encoder to use. See {@link EncoderMode} for the details. - * @throws NullPointerException - * for request or charset or factory - * @throws ErrorDataEncoderException - * if the request is a TRACE - */ - public HttpPostRequestEncoder( - HttpDataFactory factory, HttpRequest request, boolean multipart, Charset charset, - EncoderMode encoderMode) - throws ErrorDataEncoderException { - this.request = requireNonNull(request, "request"); - this.charset = requireNonNull(charset, "charset"); - this.factory = requireNonNull(factory, "factory"); - if (HttpMethod.TRACE.equals(request.method())) { - throw new ErrorDataEncoderException("Cannot create a Encoder if request is a TRACE"); - } - // Fill default values - bodyListDatas = new ArrayList<>(); - // default mode - isLastChunk = false; - isLastChunkSent = false; - isMultipart = multipart; - multipartHttpDatas = new ArrayList<>(); - this.encoderMode = encoderMode; - if (isMultipart) { - initDataMultipart(); - } - } - - /** - * Clean all HttpDatas (on Disk) for the current request. - */ - public void cleanFiles() { - factory.cleanRequestHttpData(request); - } - - /** - * Does the last non empty chunk already encoded so that next chunk will be empty (last chunk) - */ - private boolean isLastChunk; - /** - * Last chunk already sent - */ - private boolean isLastChunkSent; - /** - * The current FileUpload that is currently in encode process - */ - private FileUpload currentFileUpload; - /** - * While adding a FileUpload, is the multipart currently in Mixed Mode - */ - private boolean duringMixedMode; - /** - * Global Body size - */ - private long globalBodySize; - /** - * Global Transfer progress - */ - private long globalProgress; - - /** - * True if this request is a Multipart request - * - * @return True if this request is a Multipart request - */ - public boolean isMultipart() { - return isMultipart; - } - - /** - * Init the delimiter for Global Part (Data). - */ - private void initDataMultipart() { - multipartDataBoundary = getNewMultipartDelimiter(); - } - - /** - * Init the delimiter for Mixed Part (Mixed). - */ - private void initMixedMultipart() { - multipartMixedBoundary = getNewMultipartDelimiter(); - } - - /** - * - * @return a newly generated Delimiter (either for DATA or MIXED) - */ - private static String getNewMultipartDelimiter() { - // construct a generated delimiter - return Long.toHexString(ThreadLocalRandom.current().nextLong()); - } - - /** - * This getMethod returns a List of all InterfaceHttpData from body part.
- - * @return the list of InterfaceHttpData from Body part - */ - public List getBodyListAttributes() { - return bodyListDatas; - } - - /** - * Set the Body HttpDatas list - * - * @throws NullPointerException - * for datas - * @throws ErrorDataEncoderException - * if the encoding is in error or if the finalize were already done - */ - public void setBodyHttpDatas(List datas) throws ErrorDataEncoderException { - requireNonNull(datas, "datas"); - globalBodySize = 0; - bodyListDatas.clear(); - currentFileUpload = null; - duringMixedMode = false; - multipartHttpDatas.clear(); - for (InterfaceHttpData data : datas) { - addBodyHttpData(data); - } - } - - /** - * Add a simple attribute in the body as Name=Value - * - * @param name - * name of the parameter - * @param value - * the value of the parameter - * @throws NullPointerException - * for name - * @throws ErrorDataEncoderException - * if the encoding is in error or if the finalize were already done - */ - public void addBodyAttribute(String name, String value) throws ErrorDataEncoderException { - String svalue = value != null? value : StringUtil.EMPTY_STRING; - Attribute data = factory.createAttribute(request, requireNonNull(name, "name"), svalue); - addBodyHttpData(data); - } - - /** - * Add a file as a FileUpload - * - * @param name - * the name of the parameter - * @param file - * the file to be uploaded (if not Multipart mode, only the filename will be included) - * @param contentType - * the associated contentType for the File - * @param isText - * True if this file should be transmitted in Text format (else binary) - * @throws NullPointerException - * for name and file - * @throws ErrorDataEncoderException - * if the encoding is in error or if the finalize were already done - */ - public void addBodyFileUpload(String name, File file, String contentType, boolean isText) - throws ErrorDataEncoderException { - addBodyFileUpload(name, file.getName(), file, contentType, isText); - } - - /** - * Add a file as a FileUpload - * - * @param name - * the name of the parameter - * @param file - * the file to be uploaded (if not Multipart mode, only the filename will be included) - * @param filename - * the filename to use for this File part, empty String will be ignored by - * the encoder - * @param contentType - * the associated contentType for the File - * @param isText - * True if this file should be transmitted in Text format (else binary) - * @throws NullPointerException - * for name and file - * @throws ErrorDataEncoderException - * if the encoding is in error or if the finalize were already done - */ - public void addBodyFileUpload(String name, String filename, File file, String contentType, boolean isText) - throws ErrorDataEncoderException { - requireNonNull(name, "name"); - requireNonNull(file, "file"); - if (filename == null) { - filename = StringUtil.EMPTY_STRING; - } - String scontentType = contentType; - String contentTransferEncoding = null; - if (contentType == null) { - if (isText) { - scontentType = HttpPostBodyUtil.DEFAULT_TEXT_CONTENT_TYPE; - } else { - scontentType = HttpPostBodyUtil.DEFAULT_BINARY_CONTENT_TYPE; - } - } - if (!isText) { - contentTransferEncoding = HttpPostBodyUtil.TransferEncodingMechanism.BINARY.value(); - } - FileUpload fileUpload = factory.createFileUpload(request, name, filename, scontentType, - contentTransferEncoding, null, file.length()); - try { - fileUpload.setContent(file); - } catch (IOException e) { - throw new ErrorDataEncoderException(e); - } - addBodyHttpData(fileUpload); - } - - /** - * Add a series of Files associated with one File parameter - * - * @param name - * the name of the parameter - * @param file - * the array of files - * @param contentType - * the array of content Types associated with each file - * @param isText - * the array of isText attribute (False meaning binary mode) for each file - * @throws IllegalArgumentException - * also throws if array have different sizes - * @throws ErrorDataEncoderException - * if the encoding is in error or if the finalize were already done - */ - public void addBodyFileUploads(String name, File[] file, String[] contentType, boolean[] isText) - throws ErrorDataEncoderException { - if (file.length != contentType.length && file.length != isText.length) { - throw new IllegalArgumentException("Different array length"); - } - for (int i = 0; i < file.length; i++) { - addBodyFileUpload(name, file[i], contentType[i], isText[i]); - } - } - - /** - * Add the InterfaceHttpData to the Body list - * - * @throws NullPointerException - * for data - * @throws ErrorDataEncoderException - * if the encoding is in error or if the finalize were already done - */ - public void addBodyHttpData(InterfaceHttpData data) throws ErrorDataEncoderException { - if (headerFinalized) { - throw new ErrorDataEncoderException("Cannot add value once finalized"); - } - bodyListDatas.add(requireNonNull(data, "data")); - if (!isMultipart) { - if (data instanceof Attribute) { - Attribute attribute = (Attribute) data; - try { - // name=value& with encoded name and attribute - String key = encodeAttribute(attribute.getName(), charset); - String value = encodeAttribute(attribute.getValue(), charset); - Attribute newattribute = factory.createAttribute(request, key, value); - multipartHttpDatas.add(newattribute); - globalBodySize += newattribute.getName().length() + 1 + newattribute.length() + 1; - } catch (IOException e) { - throw new ErrorDataEncoderException(e); - } - } else if (data instanceof FileUpload) { - // since not Multipart, only name=filename => Attribute - FileUpload fileUpload = (FileUpload) data; - // name=filename& with encoded name and filename - String key = encodeAttribute(fileUpload.getName(), charset); - String value = encodeAttribute(fileUpload.getFilename(), charset); - Attribute newattribute = factory.createAttribute(request, key, value); - multipartHttpDatas.add(newattribute); - globalBodySize += newattribute.getName().length() + 1 + newattribute.length() + 1; - } - return; - } - /* - * Logic: - * if not Attribute: - * add Data to body list - * if (duringMixedMode) - * add endmixedmultipart delimiter - * currentFileUpload = null - * duringMixedMode = false; - * add multipart delimiter, multipart body header and Data to multipart list - * reset currentFileUpload, duringMixedMode - * if FileUpload: take care of multiple file for one field => mixed mode - * if (duringMixedMode) - * if (currentFileUpload.name == data.name) - * add mixedmultipart delimiter, mixedmultipart body header and Data to multipart list - * else - * add endmixedmultipart delimiter, multipart body header and Data to multipart list - * currentFileUpload = data - * duringMixedMode = false; - * else - * if (currentFileUpload.name == data.name) - * change multipart body header of previous file into multipart list to - * mixedmultipart start, mixedmultipart body header - * add mixedmultipart delimiter, mixedmultipart body header and Data to multipart list - * duringMixedMode = true - * else - * add multipart delimiter, multipart body header and Data to multipart list - * currentFileUpload = data - * duringMixedMode = false; - * Do not add last delimiter! Could be: - * if duringmixedmode: endmixedmultipart + endmultipart - * else only endmultipart - */ - if (data instanceof Attribute) { - if (duringMixedMode) { - InternalAttribute internal = new InternalAttribute(charset); - internal.addValue("\r\n--" + multipartMixedBoundary + "--"); - multipartHttpDatas.add(internal); - multipartMixedBoundary = null; - currentFileUpload = null; - duringMixedMode = false; - } - InternalAttribute internal = new InternalAttribute(charset); - if (!multipartHttpDatas.isEmpty()) { - // previously a data field so CRLF - internal.addValue("\r\n"); - } - internal.addValue("--" + multipartDataBoundary + "\r\n"); - // content-disposition: form-data; name="field1" - Attribute attribute = (Attribute) data; - internal.addValue(HttpHeaderNames.CONTENT_DISPOSITION + ": " + HttpHeaderValues.FORM_DATA + "; " - + HttpHeaderValues.NAME + "=\"" + attribute.getName() + "\"\r\n"); - // Add Content-Length: xxx - internal.addValue(HttpHeaderNames.CONTENT_LENGTH + ": " + - attribute.length() + "\r\n"); - Charset localcharset = attribute.getCharset(); - if (localcharset != null) { - // Content-Type: text/plain; charset=charset - internal.addValue(HttpHeaderNames.CONTENT_TYPE + ": " + - HttpPostBodyUtil.DEFAULT_TEXT_CONTENT_TYPE + "; " + - HttpHeaderValues.CHARSET + '=' - + localcharset.name() + "\r\n"); - } - // CRLF between body header and data - internal.addValue("\r\n"); - multipartHttpDatas.add(internal); - multipartHttpDatas.add(data); - globalBodySize += attribute.length() + internal.size(); - } else if (data instanceof FileUpload) { - FileUpload fileUpload = (FileUpload) data; - InternalAttribute internal = new InternalAttribute(charset); - if (!multipartHttpDatas.isEmpty()) { - // previously a data field so CRLF - internal.addValue("\r\n"); - } - boolean localMixed; - if (duringMixedMode) { - if (currentFileUpload != null && currentFileUpload.getName().equals(fileUpload.getName())) { - // continue a mixed mode - - localMixed = true; - } else { - // end a mixed mode - - // add endmixedmultipart delimiter, multipart body header - // and - // Data to multipart list - internal.addValue("--" + multipartMixedBoundary + "--"); - multipartHttpDatas.add(internal); - multipartMixedBoundary = null; - // start a new one (could be replaced if mixed start again - // from here - internal = new InternalAttribute(charset); - internal.addValue("\r\n"); - localMixed = false; - // new currentFileUpload and no more in Mixed mode - currentFileUpload = fileUpload; - duringMixedMode = false; - } - } else { - if (encoderMode != EncoderMode.HTML5 && currentFileUpload != null - && currentFileUpload.getName().equals(fileUpload.getName())) { - // create a new mixed mode (from previous file) - - // change multipart body header of previous file into - // multipart list to - // mixedmultipart start, mixedmultipart body header - - // change Internal (size()-2 position in multipartHttpDatas) - // from (line starting with *) - // --AaB03x - // * Content-Disposition: form-data; name="files"; - // filename="file1.txt" - // Content-Type: text/plain - // to (lines starting with *) - // --AaB03x - // * Content-Disposition: form-data; name="files" - // * Content-Type: multipart/mixed; boundary=BbC04y - // * - // * --BbC04y - // * Content-Disposition: attachment; filename="file1.txt" - // Content-Type: text/plain - initMixedMultipart(); - InternalAttribute pastAttribute = (InternalAttribute) multipartHttpDatas.get(multipartHttpDatas - .size() - 2); - // remove past size - globalBodySize -= pastAttribute.size(); - StringBuilder replacement = new StringBuilder( - 139 + multipartDataBoundary.length() + multipartMixedBoundary.length() * 2 + - fileUpload.getFilename().length() + fileUpload.getName().length()) - - .append("--") - .append(multipartDataBoundary) - .append("\r\n") - - .append(HttpHeaderNames.CONTENT_DISPOSITION) - .append(": ") - .append(HttpHeaderValues.FORM_DATA) - .append("; ") - .append(HttpHeaderValues.NAME) - .append("=\"") - .append(fileUpload.getName()) - .append("\"\r\n") - - .append(HttpHeaderNames.CONTENT_TYPE) - .append(": ") - .append(HttpHeaderValues.MULTIPART_MIXED) - .append("; ") - .append(HttpHeaderValues.BOUNDARY) - .append('=') - .append(multipartMixedBoundary) - .append("\r\n\r\n") - - .append("--") - .append(multipartMixedBoundary) - .append("\r\n") - - .append(HttpHeaderNames.CONTENT_DISPOSITION) - .append(": ") - .append(HttpHeaderValues.ATTACHMENT); - - if (!fileUpload.getFilename().isEmpty()) { - replacement.append("; ") - .append(HttpHeaderValues.FILENAME) - .append("=\"") - .append(currentFileUpload.getFilename()) - .append('"'); - } - - replacement.append("\r\n"); - - pastAttribute.setValue(replacement.toString(), 1); - pastAttribute.setValue("", 2); - - // update past size - globalBodySize += pastAttribute.size(); - - // now continue - // add mixedmultipart delimiter, mixedmultipart body header - // and - // Data to multipart list - localMixed = true; - duringMixedMode = true; - } else { - // a simple new multipart - // add multipart delimiter, multipart body header and Data - // to multipart list - localMixed = false; - currentFileUpload = fileUpload; - duringMixedMode = false; - } - } - - if (localMixed) { - // add mixedmultipart delimiter, mixedmultipart body header and - // Data to multipart list - internal.addValue("--" + multipartMixedBoundary + "\r\n"); - - if (fileUpload.getFilename().isEmpty()) { - // Content-Disposition: attachment - internal.addValue(HttpHeaderNames.CONTENT_DISPOSITION + ": " - + HttpHeaderValues.ATTACHMENT + "\r\n"); - } else { - // Content-Disposition: attachment; filename="file1.txt" - internal.addValue(HttpHeaderNames.CONTENT_DISPOSITION + ": " - + HttpHeaderValues.ATTACHMENT + "; " - + HttpHeaderValues.FILENAME + "=\"" + fileUpload.getFilename() + "\"\r\n"); - } - } else { - internal.addValue("--" + multipartDataBoundary + "\r\n"); - - if (fileUpload.getFilename().isEmpty()) { - // Content-Disposition: form-data; name="files"; - internal.addValue(HttpHeaderNames.CONTENT_DISPOSITION + ": " + HttpHeaderValues.FORM_DATA + "; " - + HttpHeaderValues.NAME + "=\"" + fileUpload.getName() + "\"\r\n"); - } else { - // Content-Disposition: form-data; name="files"; - // filename="file1.txt" - internal.addValue(HttpHeaderNames.CONTENT_DISPOSITION + ": " + HttpHeaderValues.FORM_DATA + "; " - + HttpHeaderValues.NAME + "=\"" + fileUpload.getName() + "\"; " - + HttpHeaderValues.FILENAME + "=\"" + fileUpload.getFilename() + "\"\r\n"); - } - } - // Add Content-Length: xxx - internal.addValue(HttpHeaderNames.CONTENT_LENGTH + ": " + - fileUpload.length() + "\r\n"); - // Content-Type: image/gif - // Content-Type: text/plain; charset=ISO-8859-1 - // Content-Transfer-Encoding: binary - internal.addValue(HttpHeaderNames.CONTENT_TYPE + ": " + fileUpload.getContentType()); - String contentTransferEncoding = fileUpload.getContentTransferEncoding(); - if (contentTransferEncoding != null - && contentTransferEncoding.equals(HttpPostBodyUtil.TransferEncodingMechanism.BINARY.value())) { - internal.addValue("\r\n" + HttpHeaderNames.CONTENT_TRANSFER_ENCODING + ": " - + HttpPostBodyUtil.TransferEncodingMechanism.BINARY.value() + "\r\n\r\n"); - } else if (fileUpload.getCharset() != null) { - internal.addValue("; " + HttpHeaderValues.CHARSET + '=' + fileUpload.getCharset().name() + "\r\n\r\n"); - } else { - internal.addValue("\r\n\r\n"); - } - multipartHttpDatas.add(internal); - multipartHttpDatas.add(data); - globalBodySize += fileUpload.length() + internal.size(); - } - } - - /** - * Iterator to be used when encoding will be called chunk after chunk - */ - private ListIterator iterator; - - /** - * Finalize the request by preparing the Header in the request and returns the request ready to be sent.
- * Once finalized, no data must be added.
- * If the request does not need chunk (isChunked() == false), this request is the only object to send to the remote - * server. - * - * @return the request object (chunked or not according to size of body) - * @throws ErrorDataEncoderException - * if the encoding is in error or if the finalize were already done - */ - public HttpRequest finalizeRequest() throws ErrorDataEncoderException { - // Finalize the multipartHttpDatas - if (!headerFinalized) { - if (isMultipart) { - InternalAttribute internal = new InternalAttribute(charset); - if (duringMixedMode) { - internal.addValue("\r\n--" + multipartMixedBoundary + "--"); - } - internal.addValue("\r\n--" + multipartDataBoundary + "--\r\n"); - multipartHttpDatas.add(internal); - multipartMixedBoundary = null; - currentFileUpload = null; - duringMixedMode = false; - globalBodySize += internal.size(); - } - headerFinalized = true; - } else { - throw new ErrorDataEncoderException("Header already encoded"); - } - - HttpHeaders headers = request.headers(); - List contentTypes = headers.getAll(HttpHeaderNames.CONTENT_TYPE); - List transferEncoding = headers.getAll(HttpHeaderNames.TRANSFER_ENCODING); - if (contentTypes != null) { - headers.remove(HttpHeaderNames.CONTENT_TYPE); - for (String contentType : contentTypes) { - // "multipart/form-data; boundary=--89421926422648" - String lowercased = contentType.toLowerCase(); - if (lowercased.startsWith(HttpHeaderValues.MULTIPART_FORM_DATA.toString()) || - lowercased.startsWith(HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED.toString())) { - // ignore - } else { - headers.add(HttpHeaderNames.CONTENT_TYPE, contentType); - } - } - } - if (isMultipart) { - String value = HttpHeaderValues.MULTIPART_FORM_DATA + "; " + HttpHeaderValues.BOUNDARY + '=' - + multipartDataBoundary; - headers.add(HttpHeaderNames.CONTENT_TYPE, value); - } else { - // Not multipart - headers.add(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED); - } - // Now consider size for chunk or not - long realSize = globalBodySize; - if (!isMultipart) { - realSize -= 1; // last '&' removed - } - iterator = multipartHttpDatas.listIterator(); - - headers.set(HttpHeaderNames.CONTENT_LENGTH, String.valueOf(realSize)); - if (realSize > HttpPostBodyUtil.chunkSize || isMultipart) { - isChunked = true; - if (transferEncoding != null) { - headers.remove(HttpHeaderNames.TRANSFER_ENCODING); - for (CharSequence v : transferEncoding) { - if (HttpHeaderValues.CHUNKED.contentEqualsIgnoreCase(v)) { - // ignore - } else { - headers.add(HttpHeaderNames.TRANSFER_ENCODING, v); - } - } - } - HttpUtil.setTransferEncodingChunked(request, true); - - // wrap to hide the possible content - return new WrappedHttpRequest(request); - } else { - // get the only one body and set it to the request - HttpContent chunk = nextChunk(); - if (request instanceof FullHttpRequest) { - FullHttpRequest fullRequest = (FullHttpRequest) request; - ByteBuf chunkContent = chunk.content(); - if (fullRequest.content() != chunkContent) { - fullRequest.content().clear().writeBytes(chunkContent); - chunkContent.release(); - } - return fullRequest; - } else { - return new WrappedFullHttpRequest(request, chunk); - } - } - } - - /** - * @return True if the request is by Chunk - */ - public boolean isChunked() { - return isChunked; - } - - /** - * Encode one attribute - * - * @return the encoded attribute - * @throws ErrorDataEncoderException - * if the encoding is in error - */ - @SuppressWarnings("unchecked") - private String encodeAttribute(String s, Charset charset) throws ErrorDataEncoderException { - if (s == null) { - return ""; - } - try { - String encoded = URLEncoder.encode(s, charset.name()); - if (encoderMode == EncoderMode.RFC3986) { - for (Map.Entry entry : percentEncodings) { - String replacement = entry.getValue(); - encoded = entry.getKey().matcher(encoded).replaceAll(replacement); - } - } - return encoded; - } catch (UnsupportedEncodingException e) { - throw new ErrorDataEncoderException(charset.name(), e); - } - } - - /** - * The ByteBuf currently used by the encoder - */ - private ByteBuf currentBuffer; - /** - * The current InterfaceHttpData to encode (used if more chunks are available) - */ - private InterfaceHttpData currentData; - /** - * If not multipart, does the currentBuffer stands for the Key or for the Value - */ - private boolean isKey = true; - - /** - * - * @return the next ByteBuf to send as an HttpChunk and modifying currentBuffer accordingly - */ - private ByteBuf fillByteBuf() { - int length = currentBuffer.readableBytes(); - if (length > HttpPostBodyUtil.chunkSize) { - return currentBuffer.readRetainedSlice(HttpPostBodyUtil.chunkSize); - } else { - // to continue - ByteBuf slice = currentBuffer; - currentBuffer = null; - return slice; - } - } - - /** - * From the current context (currentBuffer and currentData), returns the next HttpChunk (if possible) trying to get - * sizeleft bytes more into the currentBuffer. This is the Multipart version. - * - * @param sizeleft - * the number of bytes to try to get from currentData - * @return the next HttpChunk or null if not enough bytes were found - * @throws ErrorDataEncoderException - * if the encoding is in error - */ - private HttpContent encodeNextChunkMultipart(int sizeleft) throws ErrorDataEncoderException { - if (currentData == null) { - return null; - } - ByteBuf buffer; - if (currentData instanceof InternalAttribute) { - buffer = ((InternalAttribute) currentData).toByteBuf(); - currentData = null; - } else { - try { - buffer = ((HttpData) currentData).getChunk(sizeleft); - } catch (IOException e) { - throw new ErrorDataEncoderException(e); - } - if (buffer.capacity() == 0) { - // end for current InterfaceHttpData, need more data - currentData = null; - return null; - } - } - if (currentBuffer == null) { - currentBuffer = buffer; - } else { - currentBuffer = wrappedBuffer(currentBuffer, buffer); - } - if (currentBuffer.readableBytes() < HttpPostBodyUtil.chunkSize) { - currentData = null; - return null; - } - buffer = fillByteBuf(); - return new DefaultHttpContent(buffer); - } - - /** - * From the current context (currentBuffer and currentData), returns the next HttpChunk (if possible) trying to get - * sizeleft bytes more into the currentBuffer. This is the UrlEncoded version. - * - * @param sizeleft - * the number of bytes to try to get from currentData - * @return the next HttpChunk or null if not enough bytes were found - * @throws ErrorDataEncoderException - * if the encoding is in error - */ - private HttpContent encodeNextChunkUrlEncoded(int sizeleft) throws ErrorDataEncoderException { - if (currentData == null) { - return null; - } - int size = sizeleft; - ByteBuf buffer; - - // Set name= - if (isKey) { - String key = currentData.getName(); - buffer = wrappedBuffer(key.getBytes(charset)); - isKey = false; - if (currentBuffer == null) { - currentBuffer = wrappedBuffer(buffer, wrappedBuffer("=".getBytes(charset))); - } else { - currentBuffer = wrappedBuffer(currentBuffer, buffer, wrappedBuffer("=".getBytes(charset))); - } - // continue - size -= buffer.readableBytes() + 1; - if (currentBuffer.readableBytes() >= HttpPostBodyUtil.chunkSize) { - buffer = fillByteBuf(); - return new DefaultHttpContent(buffer); - } - } - - // Put value into buffer - try { - buffer = ((HttpData) currentData).getChunk(size); - } catch (IOException e) { - throw new ErrorDataEncoderException(e); - } - - // Figure out delimiter - ByteBuf delimiter = null; - if (buffer.readableBytes() < size) { - isKey = true; - delimiter = iterator.hasNext() ? wrappedBuffer("&".getBytes(charset)) : null; - } - - // End for current InterfaceHttpData, need potentially more data - if (buffer.capacity() == 0) { - currentData = null; - if (currentBuffer == null) { - if (delimiter == null) { - return null; - } else { - currentBuffer = delimiter; - } - } else { - if (delimiter != null) { - currentBuffer = wrappedBuffer(currentBuffer, delimiter); - } - } - if (currentBuffer.readableBytes() >= HttpPostBodyUtil.chunkSize) { - buffer = fillByteBuf(); - return new DefaultHttpContent(buffer); - } - return null; - } - - // Put it all together: name=value& - if (currentBuffer == null) { - if (delimiter != null) { - currentBuffer = wrappedBuffer(buffer, delimiter); - } else { - currentBuffer = buffer; - } - } else { - if (delimiter != null) { - currentBuffer = wrappedBuffer(currentBuffer, buffer, delimiter); - } else { - currentBuffer = wrappedBuffer(currentBuffer, buffer); - } - } - - // end for current InterfaceHttpData, need more data - if (currentBuffer.readableBytes() < HttpPostBodyUtil.chunkSize) { - currentData = null; - isKey = true; - return null; - } - - buffer = fillByteBuf(); - return new DefaultHttpContent(buffer); - } - - @Override - public void close() throws Exception { - // NO since the user can want to reuse (broadcast for instance) - // cleanFiles(); - } - - @Deprecated - @Override - public HttpContent readChunk(ChannelHandlerContext ctx) throws Exception { - return readChunk(ctx.alloc()); - } - - /** - * Returns the next available HttpChunk. The caller is responsible to test if this chunk is the last one (isLast()), - * in order to stop calling this getMethod. - * - * @return the next available HttpChunk - * @throws ErrorDataEncoderException - * if the encoding is in error - */ - @Override - public HttpContent readChunk(ByteBufAllocator allocator) throws Exception { - if (isLastChunkSent) { - return null; - } else { - HttpContent nextChunk = nextChunk(); - globalProgress += nextChunk.content().readableBytes(); - return nextChunk; - } - } - - /** - * Returns the next available HttpChunk. The caller is responsible to test if this chunk is the last one (isLast()), - * in order to stop calling this getMethod. - * - * @return the next available HttpChunk - * @throws ErrorDataEncoderException - * if the encoding is in error - */ - private HttpContent nextChunk() throws ErrorDataEncoderException { - if (isLastChunk) { - isLastChunkSent = true; - return LastHttpContent.EMPTY_LAST_CONTENT; - } - // first test if previous buffer is not empty - int size = calculateRemainingSize(); - if (size <= 0) { - // NextChunk from buffer - ByteBuf buffer = fillByteBuf(); - return new DefaultHttpContent(buffer); - } - // size > 0 - if (currentData != null) { - // continue to read data - HttpContent chunk; - if (isMultipart) { - chunk = encodeNextChunkMultipart(size); - } else { - chunk = encodeNextChunkUrlEncoded(size); - } - if (chunk != null) { - // NextChunk from data - return chunk; - } - size = calculateRemainingSize(); - } - if (!iterator.hasNext()) { - return lastChunk(); - } - while (size > 0 && iterator.hasNext()) { - currentData = iterator.next(); - HttpContent chunk; - if (isMultipart) { - chunk = encodeNextChunkMultipart(size); - } else { - chunk = encodeNextChunkUrlEncoded(size); - } - if (chunk == null) { - // not enough - size = calculateRemainingSize(); - continue; - } - // NextChunk from data - return chunk; - } - // end since no more data - return lastChunk(); - } - - private int calculateRemainingSize() { - int size = HttpPostBodyUtil.chunkSize; - if (currentBuffer != null) { - size -= currentBuffer.readableBytes(); - } - return size; - } - - private HttpContent lastChunk() { - isLastChunk = true; - if (currentBuffer == null) { - isLastChunkSent = true; - // LastChunk with no more data - return LastHttpContent.EMPTY_LAST_CONTENT; - } - // NextChunk as last non empty from buffer - ByteBuf buffer = currentBuffer; - currentBuffer = null; - return new DefaultHttpContent(buffer); - } - - @Override - public boolean isEndOfInput() throws Exception { - return isLastChunkSent; - } - - @Override - public long length() { - return isMultipart? globalBodySize : globalBodySize - 1; - } - - @Override - public long progress() { - return globalProgress; - } - - /** - * Exception when an error occurs while encoding - */ - public static class ErrorDataEncoderException extends Exception { - private static final long serialVersionUID = 5020247425493164465L; - - public ErrorDataEncoderException() { - } - - public ErrorDataEncoderException(String msg) { - super(msg); - } - - public ErrorDataEncoderException(Throwable cause) { - super(cause); - } - - public ErrorDataEncoderException(String msg, Throwable cause) { - super(msg, cause); - } - } - - private static class WrappedHttpRequest implements HttpRequest { - private final HttpRequest request; - WrappedHttpRequest(HttpRequest request) { - this.request = request; - } - - @Override - public HttpRequest setProtocolVersion(HttpVersion version) { - request.setProtocolVersion(version); - return this; - } - - @Override - public HttpRequest setMethod(HttpMethod method) { - request.setMethod(method); - return this; - } - - @Override - public HttpRequest setUri(String uri) { - request.setUri(uri); - return this; - } - - @Override - public HttpMethod getMethod() { - return request.method(); - } - - @Override - public HttpMethod method() { - return request.method(); - } - - @Override - public String getUri() { - return request.uri(); - } - - @Override - public String uri() { - return request.uri(); - } - - @Override - public HttpVersion getProtocolVersion() { - return request.protocolVersion(); - } - - @Override - public HttpVersion protocolVersion() { - return request.protocolVersion(); - } - - @Override - public HttpHeaders headers() { - return request.headers(); - } - - @Override - public DecoderResult decoderResult() { - return request.decoderResult(); - } - - @Override - @Deprecated - public DecoderResult getDecoderResult() { - return request.getDecoderResult(); - } - - @Override - public void setDecoderResult(DecoderResult result) { - request.setDecoderResult(result); - } - } - - private static final class WrappedFullHttpRequest extends WrappedHttpRequest implements FullHttpRequest { - private final HttpContent content; - - private WrappedFullHttpRequest(HttpRequest request, HttpContent content) { - super(request); - this.content = content; - } - - @Override - public FullHttpRequest setProtocolVersion(HttpVersion version) { - super.setProtocolVersion(version); - return this; - } - - @Override - public FullHttpRequest setMethod(HttpMethod method) { - super.setMethod(method); - return this; - } - - @Override - public FullHttpRequest setUri(String uri) { - super.setUri(uri); - return this; - } - - @Override - public FullHttpRequest copy() { - return replace(content().copy()); - } - - @Override - public FullHttpRequest duplicate() { - return replace(content().duplicate()); - } - - @Override - public FullHttpRequest retainedDuplicate() { - return replace(content().retainedDuplicate()); - } - - @Override - public FullHttpRequest replace(ByteBuf content) { - DefaultFullHttpRequest duplicate = new DefaultFullHttpRequest(protocolVersion(), method(), uri(), content); - duplicate.headers().set(headers()); - duplicate.trailingHeaders().set(trailingHeaders()); - return duplicate; - } - - @Override - public FullHttpRequest retain(int increment) { - content.retain(increment); - return this; - } - - @Override - public FullHttpRequest retain() { - content.retain(); - return this; - } - - @Override - public FullHttpRequest touch() { - content.touch(); - return this; - } - - @Override - public FullHttpRequest touch(Object hint) { - content.touch(hint); - return this; - } - - @Override - public ByteBuf content() { - return content.content(); - } - - @Override - public HttpHeaders trailingHeaders() { - if (content instanceof LastHttpContent) { - return ((LastHttpContent) content).trailingHeaders(); - } else { - return EmptyHttpHeaders.INSTANCE; - } - } - - @Override - public int refCnt() { - return content.refCnt(); - } - - @Override - public boolean release() { - return content.release(); - } - - @Override - public boolean release(int decrement) { - return content.release(decrement); - } - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostStandardRequestDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostStandardRequestDecoder.java deleted file mode 100644 index 1ee3115214..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostStandardRequestDecoder.java +++ /dev/null @@ -1,762 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.handler.codec.http.HttpConstants; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.LastHttpContent; -import io.netty.handler.codec.http.QueryStringDecoder; -import io.netty.handler.codec.http.multipart.HttpPostBodyUtil.SeekAheadOptimize; -import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.EndOfDataDecoderException; -import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.ErrorDataDecoderException; -import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.MultiPartStatus; -import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.NotEnoughDataDecoderException; -import io.netty.util.ByteProcessor; -import io.netty.util.internal.StringUtil; - -import java.io.IOException; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; - -import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; -import static java.util.Objects.requireNonNull; - - -/** - * This decoder will decode Body and can handle POST BODY. - * - * You MUST call {@link #destroy()} after completion to release all resources. - * - */ -public class HttpPostStandardRequestDecoder implements InterfaceHttpPostRequestDecoder { - - /** - * Factory used to create InterfaceHttpData - */ - private final HttpDataFactory factory; - - /** - * Request to decode - */ - private final HttpRequest request; - - /** - * Default charset to use - */ - private final Charset charset; - - /** - * Does the last chunk already received - */ - private boolean isLastChunk; - - /** - * HttpDatas from Body - */ - private final List bodyListHttpData = new ArrayList<>(); - - /** - * HttpDatas as Map from Body - */ - private final Map> bodyMapHttpData = new TreeMap<>( - CaseIgnoringComparator.INSTANCE); - - /** - * The current channelBuffer - */ - private ByteBuf undecodedChunk; - - /** - * Body HttpDatas current position - */ - private int bodyListHttpDataRank; - - /** - * Current getStatus - */ - private MultiPartStatus currentStatus = MultiPartStatus.NOTSTARTED; - - /** - * The current Attribute that is currently in decode process - */ - private Attribute currentAttribute; - - private boolean destroyed; - - private int discardThreshold = HttpPostRequestDecoder.DEFAULT_DISCARD_THRESHOLD; - - /** - * - * @param request - * the request to decode - * @throws NullPointerException - * for request - * @throws ErrorDataDecoderException - * if the default charset was wrong when decoding or other - * errors - */ - public HttpPostStandardRequestDecoder(HttpRequest request) { - this(new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE), request, HttpConstants.DEFAULT_CHARSET); - } - - /** - * - * @param factory - * the factory used to create InterfaceHttpData - * @param request - * the request to decode - * @throws NullPointerException - * for request or factory - * @throws ErrorDataDecoderException - * if the default charset was wrong when decoding or other - * errors - */ - public HttpPostStandardRequestDecoder(HttpDataFactory factory, HttpRequest request) { - this(factory, request, HttpConstants.DEFAULT_CHARSET); - } - - /** - * - * @param factory - * the factory used to create InterfaceHttpData - * @param request - * the request to decode - * @param charset - * the charset to use as default - * @throws NullPointerException - * for request or charset or factory - * @throws ErrorDataDecoderException - * if the default charset was wrong when decoding or other - * errors - */ - public HttpPostStandardRequestDecoder(HttpDataFactory factory, HttpRequest request, Charset charset) { - this.request = requireNonNull(request, "request"); - this.charset = requireNonNull(charset, "charset"); - this.factory = requireNonNull(factory, "factory"); - try { - if (request instanceof HttpContent) { - // Offer automatically if the given request is as type of HttpContent - // See #1089 - offer((HttpContent) request); - } else { - parseBody(); - } - } catch (Throwable e) { - destroy(); - throw e; - } - } - - private void checkDestroyed() { - if (destroyed) { - throw new IllegalStateException(HttpPostStandardRequestDecoder.class.getSimpleName() - + " was destroyed already"); - } - } - - /** - * True if this request is a Multipart request - * - * @return True if this request is a Multipart request - */ - @Override - public boolean isMultipart() { - checkDestroyed(); - return false; - } - - /** - * Set the amount of bytes after which read bytes in the buffer should be discarded. - * Setting this lower gives lower memory usage but with the overhead of more memory copies. - * Use {@code 0} to disable it. - */ - @Override - public void setDiscardThreshold(int discardThreshold) { - this.discardThreshold = checkPositiveOrZero(discardThreshold, "discardThreshold"); - } - - /** - * Return the threshold in bytes after which read data in the buffer should be discarded. - */ - @Override - public int getDiscardThreshold() { - return discardThreshold; - } - - /** - * This getMethod returns a List of all HttpDatas from body.
- * - * If chunked, all chunks must have been offered using offer() getMethod. If - * not, NotEnoughDataDecoderException will be raised. - * - * @return the list of HttpDatas from Body part for POST getMethod - * @throws NotEnoughDataDecoderException - * Need more chunks - */ - @Override - public List getBodyHttpDatas() { - checkDestroyed(); - - if (!isLastChunk) { - throw new NotEnoughDataDecoderException(); - } - return bodyListHttpData; - } - - /** - * This getMethod returns a List of all HttpDatas with the given name from - * body.
- * - * If chunked, all chunks must have been offered using offer() getMethod. If - * not, NotEnoughDataDecoderException will be raised. - * - * @return All Body HttpDatas with the given name (ignore case) - * @throws NotEnoughDataDecoderException - * need more chunks - */ - @Override - public List getBodyHttpDatas(String name) { - checkDestroyed(); - - if (!isLastChunk) { - throw new NotEnoughDataDecoderException(); - } - return bodyMapHttpData.get(name); - } - - /** - * This getMethod returns the first InterfaceHttpData with the given name from - * body.
- * - * If chunked, all chunks must have been offered using offer() getMethod. If - * not, NotEnoughDataDecoderException will be raised. - * - * @return The first Body InterfaceHttpData with the given name (ignore - * case) - * @throws NotEnoughDataDecoderException - * need more chunks - */ - @Override - public InterfaceHttpData getBodyHttpData(String name) { - checkDestroyed(); - - if (!isLastChunk) { - throw new NotEnoughDataDecoderException(); - } - List list = bodyMapHttpData.get(name); - if (list != null) { - return list.get(0); - } - return null; - } - - /** - * Initialized the internals from a new chunk - * - * @param content - * the new received chunk - * @throws ErrorDataDecoderException - * if there is a problem with the charset decoding or other - * errors - */ - @Override - public HttpPostStandardRequestDecoder offer(HttpContent content) { - checkDestroyed(); - - if (content instanceof LastHttpContent) { - isLastChunk = true; - } - - ByteBuf buf = content.content(); - if (undecodedChunk == null) { - undecodedChunk = - // Since the Handler will release the incoming later on, we need to copy it - // - // We are explicit allocate a buffer and NOT calling copy() as otherwise it may set a maxCapacity - // which is not really usable for us as we may exceed it once we add more bytes. - buf.alloc().buffer(buf.readableBytes()).writeBytes(buf); - } else { - undecodedChunk.writeBytes(buf); - } - parseBody(); - if (undecodedChunk != null && undecodedChunk.writerIndex() > discardThreshold) { - if (undecodedChunk.refCnt() == 1) { - // It's safe to call discardBytes() as we are the only owner of the buffer. - undecodedChunk.discardReadBytes(); - } else { - // There seems to be multiple references of the buffer. Let's copy the data and release the buffer to - // ensure we can give back memory to the system. - ByteBuf buffer = undecodedChunk.alloc().buffer(undecodedChunk.readableBytes()); - buffer.writeBytes(undecodedChunk); - undecodedChunk.release(); - undecodedChunk = buffer; - } - } - return this; - } - - /** - * True if at current getStatus, there is an available decoded - * InterfaceHttpData from the Body. - * - * This getMethod works for chunked and not chunked request. - * - * @return True if at current getStatus, there is a decoded InterfaceHttpData - * @throws EndOfDataDecoderException - * No more data will be available - */ - @Override - public boolean hasNext() { - checkDestroyed(); - - if (currentStatus == MultiPartStatus.EPILOGUE) { - // OK except if end of list - if (bodyListHttpDataRank >= bodyListHttpData.size()) { - throw new EndOfDataDecoderException(); - } - } - return !bodyListHttpData.isEmpty() && bodyListHttpDataRank < bodyListHttpData.size(); - } - - /** - * Returns the next available InterfaceHttpData or null if, at the time it - * is called, there is no more available InterfaceHttpData. A subsequent - * call to offer(httpChunk) could enable more data. - * - * Be sure to call {@link InterfaceHttpData#release()} after you are done - * with processing to make sure to not leak any resources - * - * @return the next available InterfaceHttpData or null if none - * @throws EndOfDataDecoderException - * No more data will be available - */ - @Override - public InterfaceHttpData next() { - checkDestroyed(); - - if (hasNext()) { - return bodyListHttpData.get(bodyListHttpDataRank++); - } - return null; - } - - @Override - public InterfaceHttpData currentPartialHttpData() { - return currentAttribute; - } - - /** - * This getMethod will parse as much as possible data and fill the list and map - * - * @throws ErrorDataDecoderException - * if there is a problem with the charset decoding or other - * errors - */ - private void parseBody() { - if (currentStatus == MultiPartStatus.PREEPILOGUE || currentStatus == MultiPartStatus.EPILOGUE) { - if (isLastChunk) { - currentStatus = MultiPartStatus.EPILOGUE; - } - return; - } - parseBodyAttributes(); - } - - /** - * Utility function to add a new decoded data - */ - protected void addHttpData(InterfaceHttpData data) { - if (data == null) { - return; - } - List datas = bodyMapHttpData.computeIfAbsent( - data.getName(), k -> new ArrayList<>(1)); - datas.add(data); - bodyListHttpData.add(data); - } - - /** - * This getMethod fill the map and list with as much Attribute as possible from - * Body in not Multipart mode. - * - * @throws ErrorDataDecoderException - * if there is a problem with the charset decoding or other - * errors - */ - private void parseBodyAttributesStandard() { - int firstpos = undecodedChunk.readerIndex(); - int currentpos = firstpos; - int equalpos; - int ampersandpos; - if (currentStatus == MultiPartStatus.NOTSTARTED) { - currentStatus = MultiPartStatus.DISPOSITION; - } - boolean contRead = true; - try { - while (undecodedChunk.isReadable() && contRead) { - char read = (char) undecodedChunk.readUnsignedByte(); - currentpos++; - switch (currentStatus) { - case DISPOSITION:// search '=' - if (read == '=') { - currentStatus = MultiPartStatus.FIELD; - equalpos = currentpos - 1; - String key = decodeAttribute(undecodedChunk.toString(firstpos, equalpos - firstpos, charset), - charset); - currentAttribute = factory.createAttribute(request, key); - firstpos = currentpos; - } else if (read == '&') { // special empty FIELD - currentStatus = MultiPartStatus.DISPOSITION; - ampersandpos = currentpos - 1; - String key = decodeAttribute( - undecodedChunk.toString(firstpos, ampersandpos - firstpos, charset), charset); - currentAttribute = factory.createAttribute(request, key); - currentAttribute.setValue(""); // empty - addHttpData(currentAttribute); - currentAttribute = null; - firstpos = currentpos; - contRead = true; - } - break; - case FIELD:// search '&' or end of line - if (read == '&') { - currentStatus = MultiPartStatus.DISPOSITION; - ampersandpos = currentpos - 1; - setFinalBuffer(undecodedChunk.retainedSlice(firstpos, ampersandpos - firstpos)); - firstpos = currentpos; - contRead = true; - } else if (read == HttpConstants.CR) { - if (undecodedChunk.isReadable()) { - read = (char) undecodedChunk.readUnsignedByte(); - currentpos++; - if (read == HttpConstants.LF) { - currentStatus = MultiPartStatus.PREEPILOGUE; - ampersandpos = currentpos - 2; - setFinalBuffer(undecodedChunk.retainedSlice(firstpos, ampersandpos - firstpos)); - firstpos = currentpos; - contRead = false; - } else { - // Error - throw new ErrorDataDecoderException("Bad end of line"); - } - } else { - currentpos--; - } - } else if (read == HttpConstants.LF) { - currentStatus = MultiPartStatus.PREEPILOGUE; - ampersandpos = currentpos - 1; - setFinalBuffer(undecodedChunk.retainedSlice(firstpos, ampersandpos - firstpos)); - firstpos = currentpos; - contRead = false; - } - break; - default: - // just stop - contRead = false; - } - } - if (isLastChunk && currentAttribute != null) { - // special case - ampersandpos = currentpos; - if (ampersandpos > firstpos) { - setFinalBuffer(undecodedChunk.retainedSlice(firstpos, ampersandpos - firstpos)); - } else if (!currentAttribute.isCompleted()) { - setFinalBuffer(Unpooled.EMPTY_BUFFER); - } - firstpos = currentpos; - currentStatus = MultiPartStatus.EPILOGUE; - } else if (contRead && currentAttribute != null && currentStatus == MultiPartStatus.FIELD) { - // reset index except if to continue in case of FIELD getStatus - currentAttribute.addContent(undecodedChunk.retainedSlice(firstpos, currentpos - firstpos), - false); - firstpos = currentpos; - } - undecodedChunk.readerIndex(firstpos); - } catch (ErrorDataDecoderException e) { - // error while decoding - undecodedChunk.readerIndex(firstpos); - throw e; - } catch (IOException | IllegalArgumentException e) { - // error while decoding - undecodedChunk.readerIndex(firstpos); - throw new ErrorDataDecoderException(e); - } - } - - /** - * This getMethod fill the map and list with as much Attribute as possible from - * Body in not Multipart mode. - * - * @throws ErrorDataDecoderException - * if there is a problem with the charset decoding or other - * errors - */ - private void parseBodyAttributes() { - if (undecodedChunk == null) { - return; - } - if (!undecodedChunk.hasArray()) { - parseBodyAttributesStandard(); - return; - } - SeekAheadOptimize sao = new SeekAheadOptimize(undecodedChunk); - int firstpos = undecodedChunk.readerIndex(); - int currentpos = firstpos; - int equalpos; - int ampersandpos; - if (currentStatus == MultiPartStatus.NOTSTARTED) { - currentStatus = MultiPartStatus.DISPOSITION; - } - boolean contRead = true; - try { - loop: while (sao.pos < sao.limit) { - char read = (char) (sao.bytes[sao.pos++] & 0xFF); - currentpos++; - switch (currentStatus) { - case DISPOSITION:// search '=' - if (read == '=') { - currentStatus = MultiPartStatus.FIELD; - equalpos = currentpos - 1; - String key = decodeAttribute(undecodedChunk.toString(firstpos, equalpos - firstpos, charset), - charset); - currentAttribute = factory.createAttribute(request, key); - firstpos = currentpos; - } else if (read == '&') { // special empty FIELD - currentStatus = MultiPartStatus.DISPOSITION; - ampersandpos = currentpos - 1; - String key = decodeAttribute( - undecodedChunk.toString(firstpos, ampersandpos - firstpos, charset), charset); - currentAttribute = factory.createAttribute(request, key); - currentAttribute.setValue(""); // empty - addHttpData(currentAttribute); - currentAttribute = null; - firstpos = currentpos; - contRead = true; - } - break; - case FIELD:// search '&' or end of line - if (read == '&') { - currentStatus = MultiPartStatus.DISPOSITION; - ampersandpos = currentpos - 1; - setFinalBuffer(undecodedChunk.retainedSlice(firstpos, ampersandpos - firstpos)); - firstpos = currentpos; - contRead = true; - } else if (read == HttpConstants.CR) { - if (sao.pos < sao.limit) { - read = (char) (sao.bytes[sao.pos++] & 0xFF); - currentpos++; - if (read == HttpConstants.LF) { - currentStatus = MultiPartStatus.PREEPILOGUE; - ampersandpos = currentpos - 2; - sao.setReadPosition(0); - setFinalBuffer(undecodedChunk.retainedSlice(firstpos, ampersandpos - firstpos)); - firstpos = currentpos; - contRead = false; - break loop; - } else { - // Error - sao.setReadPosition(0); - throw new ErrorDataDecoderException("Bad end of line"); - } - } else { - if (sao.limit > 0) { - currentpos--; - } - } - } else if (read == HttpConstants.LF) { - currentStatus = MultiPartStatus.PREEPILOGUE; - ampersandpos = currentpos - 1; - sao.setReadPosition(0); - setFinalBuffer(undecodedChunk.retainedSlice(firstpos, ampersandpos - firstpos)); - firstpos = currentpos; - contRead = false; - break loop; - } - break; - default: - // just stop - sao.setReadPosition(0); - contRead = false; - break loop; - } - } - if (isLastChunk && currentAttribute != null) { - // special case - ampersandpos = currentpos; - if (ampersandpos > firstpos) { - setFinalBuffer(undecodedChunk.retainedSlice(firstpos, ampersandpos - firstpos)); - } else if (!currentAttribute.isCompleted()) { - setFinalBuffer(Unpooled.EMPTY_BUFFER); - } - firstpos = currentpos; - currentStatus = MultiPartStatus.EPILOGUE; - } else if (contRead && currentAttribute != null && currentStatus == MultiPartStatus.FIELD) { - // reset index except if to continue in case of FIELD getStatus - currentAttribute.addContent(undecodedChunk.retainedSlice(firstpos, currentpos - firstpos), - false); - firstpos = currentpos; - } - undecodedChunk.readerIndex(firstpos); - } catch (ErrorDataDecoderException e) { - // error while decoding - undecodedChunk.readerIndex(firstpos); - throw e; - } catch (IOException | IllegalArgumentException e) { - // error while decoding - undecodedChunk.readerIndex(firstpos); - throw new ErrorDataDecoderException(e); - } - } - - private void setFinalBuffer(ByteBuf buffer) throws IOException { - currentAttribute.addContent(buffer, true); - ByteBuf decodedBuf = decodeAttribute(currentAttribute.getByteBuf(), charset); - if (decodedBuf != null) { // override content only when ByteBuf needed decoding - currentAttribute.setContent(decodedBuf); - } - addHttpData(currentAttribute); - currentAttribute = null; - } - - /** - * Decode component - * - * @return the decoded component - */ - private static String decodeAttribute(String s, Charset charset) { - try { - return QueryStringDecoder.decodeComponent(s, charset); - } catch (IllegalArgumentException e) { - throw new ErrorDataDecoderException("Bad string: '" + s + '\'', e); - } - } - - private static ByteBuf decodeAttribute(ByteBuf b, Charset charset) { - int firstEscaped = b.forEachByte(new UrlEncodedDetector()); - if (firstEscaped == -1) { - return null; // nothing to decode - } - - ByteBuf buf = b.alloc().buffer(b.readableBytes()); - UrlDecoder urlDecode = new UrlDecoder(buf); - int idx = b.forEachByte(urlDecode); - if (urlDecode.nextEscapedIdx != 0) { // incomplete hex byte - if (idx == -1) { - idx = b.readableBytes() - 1; - } - idx -= urlDecode.nextEscapedIdx - 1; - buf.release(); - throw new ErrorDataDecoderException( - String.format("Invalid hex byte at index '%d' in string: '%s'", idx, b.toString(charset))); - } - - return buf; - } - - /** - * Destroy the {@link HttpPostStandardRequestDecoder} and release all it resources. After this method - * was called it is not possible to operate on it anymore. - */ - @Override - public void destroy() { - // Release all data items, including those not yet pulled, only file based items - cleanFiles(); - // Clean Memory based data - for (InterfaceHttpData httpData : bodyListHttpData) { - // Might have been already released by the user - if (httpData.refCnt() > 0) { - httpData.release(); - } - } - - destroyed = true; - - if (undecodedChunk != null && undecodedChunk.refCnt() > 0) { - undecodedChunk.release(); - undecodedChunk = null; - } - } - - /** - * Clean all {@link HttpData}s for the current request. - */ - @Override - public void cleanFiles() { - checkDestroyed(); - - factory.cleanRequestHttpData(request); - } - - /** - * Remove the given FileUpload from the list of FileUploads to clean - */ - @Override - public void removeHttpDataFromClean(InterfaceHttpData data) { - checkDestroyed(); - - factory.removeHttpDataFromClean(request, data); - } - - private static final class UrlEncodedDetector implements ByteProcessor { - @Override - public boolean process(byte value) { - return value != '%' && value != '+'; - } - } - - private static final class UrlDecoder implements ByteProcessor { - - private final ByteBuf output; - private int nextEscapedIdx; - private byte hiByte; - - UrlDecoder(ByteBuf output) { - this.output = output; - } - - @Override - public boolean process(byte value) { - if (nextEscapedIdx != 0) { - if (nextEscapedIdx == 1) { - hiByte = value; - ++nextEscapedIdx; - } else { - int hi = StringUtil.decodeHexNibble((char) hiByte); - int lo = StringUtil.decodeHexNibble((char) value); - if (hi == -1 || lo == -1) { - ++nextEscapedIdx; - return false; - } - output.writeByte((hi << 4) + lo); - nextEscapedIdx = 0; - } - } else if (value == '%') { - nextEscapedIdx = 1; - } else if (value == '+') { - output.writeByte(' '); - } else { - output.writeByte(value); - } - return true; - } - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/InterfaceHttpData.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/InterfaceHttpData.java deleted file mode 100644 index 8c153294ac..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/InterfaceHttpData.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.util.ReferenceCounted; - -/** - * Interface for all Objects that could be encoded/decoded using HttpPostRequestEncoder/Decoder - */ -public interface InterfaceHttpData extends Comparable, ReferenceCounted { - enum HttpDataType { - Attribute, FileUpload, InternalAttribute - } - - /** - * Returns the name of this InterfaceHttpData. - */ - String getName(); - - /** - * - * @return The HttpDataType - */ - HttpDataType getHttpDataType(); - - @Override - InterfaceHttpData retain(); - - @Override - InterfaceHttpData retain(int increment); - - @Override - InterfaceHttpData touch(); - - @Override - InterfaceHttpData touch(Object hint); -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/InterfaceHttpPostRequestDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/InterfaceHttpPostRequestDecoder.java deleted file mode 100755 index 21ac13cf06..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/InterfaceHttpPostRequestDecoder.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.handler.codec.http.HttpContent; - -import java.util.List; - -/** - * This decoder will decode Body and can handle POST BODY. - * - * You MUST call {@link #destroy()} after completion to release all resources. - */ -public interface InterfaceHttpPostRequestDecoder { - /** - * True if this request is a Multipart request - * - * @return True if this request is a Multipart request - */ - boolean isMultipart(); - - /** - * Set the amount of bytes after which read bytes in the buffer should be discarded. - * Setting this lower gives lower memory usage but with the overhead of more memory copies. - * Use {@code 0} to disable it. - */ - void setDiscardThreshold(int discardThreshold); - - /** - * Return the threshold in bytes after which read data in the buffer should be discarded. - */ - int getDiscardThreshold(); - - /** - * This getMethod returns a List of all HttpDatas from body.
- * - * If chunked, all chunks must have been offered using offer() getMethod. If - * not, NotEnoughDataDecoderException will be raised. - * - * @return the list of HttpDatas from Body part for POST getMethod - * @throws HttpPostRequestDecoder.NotEnoughDataDecoderException - * Need more chunks - */ - List getBodyHttpDatas(); - - /** - * This getMethod returns a List of all HttpDatas with the given name from - * body.
- * - * If chunked, all chunks must have been offered using offer() getMethod. If - * not, NotEnoughDataDecoderException will be raised. - * - * @return All Body HttpDatas with the given name (ignore case) - * @throws HttpPostRequestDecoder.NotEnoughDataDecoderException - * need more chunks - */ - List getBodyHttpDatas(String name); - - /** - * This getMethod returns the first InterfaceHttpData with the given name from - * body.
- * - * If chunked, all chunks must have been offered using offer() getMethod. If - * not, NotEnoughDataDecoderException will be raised. - * - * @return The first Body InterfaceHttpData with the given name (ignore - * case) - * @throws HttpPostRequestDecoder.NotEnoughDataDecoderException - * need more chunks - */ - InterfaceHttpData getBodyHttpData(String name); - - /** - * Initialized the internals from a new chunk - * - * @param content - * the new received chunk - * @throws HttpPostRequestDecoder.ErrorDataDecoderException - * if there is a problem with the charset decoding or other - * errors - */ - InterfaceHttpPostRequestDecoder offer(HttpContent content); - - /** - * True if at current getStatus, there is an available decoded - * InterfaceHttpData from the Body. - * - * This getMethod works for chunked and not chunked request. - * - * @return True if at current getStatus, there is a decoded InterfaceHttpData - * @throws HttpPostRequestDecoder.EndOfDataDecoderException - * No more data will be available - */ - boolean hasNext(); - - /** - * Returns the next available InterfaceHttpData or null if, at the time it - * is called, there is no more available InterfaceHttpData. A subsequent - * call to offer(httpChunk) could enable more data. - * - * Be sure to call {@link InterfaceHttpData#release()} after you are done - * with processing to make sure to not leak any resources - * - * @return the next available InterfaceHttpData or null if none - * @throws HttpPostRequestDecoder.EndOfDataDecoderException - * No more data will be available - */ - InterfaceHttpData next(); - - /** - * Returns the current InterfaceHttpData if currently in decoding status, - * meaning all data are not yet within, or null if there is no InterfaceHttpData - * currently in decoding status (either because none yet decoded or none currently partially - * decoded). Full decoded ones are accessible through hasNext() and next() methods. - * - * @return the current InterfaceHttpData if currently in decoding status or null if none. - */ - InterfaceHttpData currentPartialHttpData(); - - /** - * Destroy the {@link InterfaceHttpPostRequestDecoder} and release all it resources. After this method - * was called it is not possible to operate on it anymore. - */ - void destroy(); - - /** - * Clean all HttpDatas (on Disk) for the current request. - */ - void cleanFiles(); - - /** - * Remove the given FileUpload from the list of FileUploads to clean - */ - void removeHttpDataFromClean(InterfaceHttpData data); -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/InternalAttribute.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/InternalAttribute.java deleted file mode 100644 index 731c902808..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/InternalAttribute.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.AbstractReferenceCounted; - -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.List; - -/** - * This Attribute is only for Encoder use to insert special command between object if needed - * (like Multipart Mixed mode) - */ -final class InternalAttribute extends AbstractReferenceCounted implements InterfaceHttpData { - private final List value = new ArrayList<>(); - private final Charset charset; - private int size; - - InternalAttribute(Charset charset) { - this.charset = charset; - } - - @Override - public HttpDataType getHttpDataType() { - return HttpDataType.InternalAttribute; - } - - public void addValue(String value) { - requireNonNull(value, "value"); - ByteBuf buf = Unpooled.copiedBuffer(value, charset); - this.value.add(buf); - size += buf.readableBytes(); - } - - public void addValue(String value, int rank) { - requireNonNull(value, "value"); - ByteBuf buf = Unpooled.copiedBuffer(value, charset); - this.value.add(rank, buf); - size += buf.readableBytes(); - } - - public void setValue(String value, int rank) { - requireNonNull(value, "value"); - ByteBuf buf = Unpooled.copiedBuffer(value, charset); - ByteBuf old = this.value.set(rank, buf); - if (old != null) { - size -= old.readableBytes(); - old.release(); - } - size += buf.readableBytes(); - } - - @Override - public int hashCode() { - return getName().hashCode(); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof InternalAttribute)) { - return false; - } - InternalAttribute attribute = (InternalAttribute) o; - return getName().equalsIgnoreCase(attribute.getName()); - } - - @Override - public int compareTo(InterfaceHttpData o) { - if (!(o instanceof InternalAttribute)) { - throw new ClassCastException("Cannot compare " + getHttpDataType() + - " with " + o.getHttpDataType()); - } - return compareTo((InternalAttribute) o); - } - - public int compareTo(InternalAttribute o) { - return getName().compareToIgnoreCase(o.getName()); - } - - @Override - public String toString() { - StringBuilder result = new StringBuilder(); - for (ByteBuf elt : value) { - result.append(elt.toString(charset)); - } - return result.toString(); - } - - public int size() { - return size; - } - - public ByteBuf toByteBuf() { - return Unpooled.compositeBuffer().addComponents(value).writerIndex(size()).readerIndex(0); - } - - @Override - public String getName() { - return "InternalAttribute"; - } - - @Override - protected void deallocate() { - // Do nothing - } - - @Override - public InterfaceHttpData retain() { - for (ByteBuf buf: value) { - buf.retain(); - } - return this; - } - - @Override - public InterfaceHttpData retain(int increment) { - for (ByteBuf buf: value) { - buf.retain(increment); - } - return this; - } - - @Override - public InterfaceHttpData touch() { - for (ByteBuf buf: value) { - buf.touch(); - } - return this; - } - - @Override - public InterfaceHttpData touch(Object hint) { - for (ByteBuf buf: value) { - buf.touch(hint); - } - return this; - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/MemoryAttribute.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/MemoryAttribute.java deleted file mode 100644 index 150cc9b031..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/MemoryAttribute.java +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelException; -import io.netty.handler.codec.http.HttpConstants; - -import java.io.IOException; -import java.nio.charset.Charset; - -import static io.netty.buffer.Unpooled.*; -import static java.util.Objects.requireNonNull; - -/** - * Memory implementation of Attributes - */ -public class MemoryAttribute extends AbstractMemoryHttpData implements Attribute { - - public MemoryAttribute(String name) { - this(name, HttpConstants.DEFAULT_CHARSET); - } - - public MemoryAttribute(String name, long definedSize) { - this(name, definedSize, HttpConstants.DEFAULT_CHARSET); - } - - public MemoryAttribute(String name, Charset charset) { - super(name, charset, 0); - } - - public MemoryAttribute(String name, long definedSize, Charset charset) { - super(name, charset, definedSize); - } - - public MemoryAttribute(String name, String value) throws IOException { - this(name, value, HttpConstants.DEFAULT_CHARSET); // Attribute have no default size - } - - public MemoryAttribute(String name, String value, Charset charset) throws IOException { - super(name, charset, 0); // Attribute have no default size - setValue(value); - } - - @Override - public HttpDataType getHttpDataType() { - return HttpDataType.Attribute; - } - - @Override - public String getValue() { - return getByteBuf().toString(getCharset()); - } - - @Override - public void setValue(String value) throws IOException { - requireNonNull(value, "value"); - byte [] bytes = value.getBytes(getCharset()); - checkSize(bytes.length); - ByteBuf buffer = wrappedBuffer(bytes); - if (definedSize > 0) { - definedSize = buffer.readableBytes(); - } - setContent(buffer); - } - - @Override - public void addContent(ByteBuf buffer, boolean last) throws IOException { - int localsize = buffer.readableBytes(); - try { - checkSize(size + localsize); - } catch (IOException e) { - buffer.release(); - throw e; - } - if (definedSize > 0 && definedSize < size + localsize) { - definedSize = size + localsize; - } - super.addContent(buffer, last); - } - - @Override - public int hashCode() { - return getName().hashCode(); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof Attribute)) { - return false; - } - Attribute attribute = (Attribute) o; - return getName().equalsIgnoreCase(attribute.getName()); - } - - @Override - public int compareTo(InterfaceHttpData other) { - if (!(other instanceof Attribute)) { - throw new ClassCastException("Cannot compare " + getHttpDataType() + - " with " + other.getHttpDataType()); - } - return compareTo((Attribute) other); - } - - public int compareTo(Attribute o) { - return getName().compareToIgnoreCase(o.getName()); - } - - @Override - public String toString() { - return getName() + '=' + getValue(); - } - - @Override - public Attribute copy() { - final ByteBuf content = content(); - return replace(content != null ? content.copy() : null); - } - - @Override - public Attribute duplicate() { - final ByteBuf content = content(); - return replace(content != null ? content.duplicate() : null); - } - - @Override - public Attribute retainedDuplicate() { - ByteBuf content = content(); - if (content != null) { - content = content.retainedDuplicate(); - boolean success = false; - try { - Attribute duplicate = replace(content); - success = true; - return duplicate; - } finally { - if (!success) { - content.release(); - } - } - } else { - return replace(null); - } - } - - @Override - public Attribute replace(ByteBuf content) { - MemoryAttribute attr = new MemoryAttribute(getName()); - attr.setCharset(getCharset()); - if (content != null) { - try { - attr.setContent(content); - } catch (IOException e) { - throw new ChannelException(e); - } - } - return attr; - } - - @Override - public Attribute retain() { - super.retain(); - return this; - } - - @Override - public Attribute retain(int increment) { - super.retain(increment); - return this; - } - - @Override - public Attribute touch() { - super.touch(); - return this; - } - - @Override - public Attribute touch(Object hint) { - super.touch(hint); - return this; - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/MemoryFileUpload.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/MemoryFileUpload.java deleted file mode 100644 index 22b75d3fea..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/MemoryFileUpload.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelException; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; - -import java.io.IOException; -import java.nio.charset.Charset; - -/** - * Default FileUpload implementation that stores file into memory.

- * - * Warning: be aware of the memory limitation. - */ -public class MemoryFileUpload extends AbstractMemoryHttpData implements FileUpload { - - private String filename; - - private String contentType; - - private String contentTransferEncoding; - - public MemoryFileUpload(String name, String filename, String contentType, - String contentTransferEncoding, Charset charset, long size) { - super(name, charset, size); - setFilename(filename); - setContentType(contentType); - setContentTransferEncoding(contentTransferEncoding); - } - - @Override - public HttpDataType getHttpDataType() { - return HttpDataType.FileUpload; - } - - @Override - public String getFilename() { - return filename; - } - - @Override - public void setFilename(String filename) { - requireNonNull(filename, "filename"); - this.filename = filename; - } - - @Override - public int hashCode() { - return FileUploadUtil.hashCode(this); - } - - @Override - public boolean equals(Object o) { - return o instanceof FileUpload && FileUploadUtil.equals(this, (FileUpload) o); - } - - @Override - public int compareTo(InterfaceHttpData o) { - if (!(o instanceof FileUpload)) { - throw new ClassCastException("Cannot compare " + getHttpDataType() + - " with " + o.getHttpDataType()); - } - return compareTo((FileUpload) o); - } - - public int compareTo(FileUpload o) { - return FileUploadUtil.compareTo(this, o); - } - - @Override - public void setContentType(String contentType) { - requireNonNull(contentType, "contentType"); - this.contentType = contentType; - } - - @Override - public String getContentType() { - return contentType; - } - - @Override - public String getContentTransferEncoding() { - return contentTransferEncoding; - } - - @Override - public void setContentTransferEncoding(String contentTransferEncoding) { - this.contentTransferEncoding = contentTransferEncoding; - } - - @Override - public String toString() { - return HttpHeaderNames.CONTENT_DISPOSITION + ": " + - HttpHeaderValues.FORM_DATA + "; " + HttpHeaderValues.NAME + "=\"" + getName() + - "\"; " + HttpHeaderValues.FILENAME + "=\"" + filename + "\"\r\n" + - HttpHeaderNames.CONTENT_TYPE + ": " + contentType + - (getCharset() != null? "; " + HttpHeaderValues.CHARSET + '=' + getCharset().name() + "\r\n" : "\r\n") + - HttpHeaderNames.CONTENT_LENGTH + ": " + length() + "\r\n" + - "Completed: " + isCompleted() + - "\r\nIsInMemory: " + isInMemory(); - } - - @Override - public FileUpload copy() { - final ByteBuf content = content(); - return replace(content != null ? content.copy() : content); - } - - @Override - public FileUpload duplicate() { - final ByteBuf content = content(); - return replace(content != null ? content.duplicate() : content); - } - - @Override - public FileUpload retainedDuplicate() { - ByteBuf content = content(); - if (content != null) { - content = content.retainedDuplicate(); - boolean success = false; - try { - FileUpload duplicate = replace(content); - success = true; - return duplicate; - } finally { - if (!success) { - content.release(); - } - } - } else { - return replace(null); - } - } - - @Override - public FileUpload replace(ByteBuf content) { - MemoryFileUpload upload = new MemoryFileUpload( - getName(), getFilename(), getContentType(), getContentTransferEncoding(), getCharset(), size); - if (content != null) { - try { - upload.setContent(content); - return upload; - } catch (IOException e) { - throw new ChannelException(e); - } - } - return upload; - } - - @Override - public FileUpload retain() { - super.retain(); - return this; - } - - @Override - public FileUpload retain(int increment) { - super.retain(increment); - return this; - } - - @Override - public FileUpload touch() { - super.touch(); - return this; - } - - @Override - public FileUpload touch(Object hint) { - super.touch(hint); - return this; - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/MixedAttribute.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/MixedAttribute.java deleted file mode 100644 index f148e4e256..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/MixedAttribute.java +++ /dev/null @@ -1,359 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.http.HttpConstants; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.Charset; - -/** - * Mixed implementation using both in Memory and in File with a limit of size - */ -public class MixedAttribute implements Attribute { - private final String baseDir; - private final boolean deleteOnExit; - private Attribute attribute; - - private final long limitSize; - private long maxSize = DefaultHttpDataFactory.MAXSIZE; - - public MixedAttribute(String name, long limitSize) { - this(name, limitSize, HttpConstants.DEFAULT_CHARSET); - } - - public MixedAttribute(String name, long definedSize, long limitSize) { - this(name, definedSize, limitSize, HttpConstants.DEFAULT_CHARSET); - } - - public MixedAttribute(String name, long limitSize, Charset charset) { - this(name, limitSize, charset, DiskAttribute.baseDirectory, DiskAttribute.deleteOnExitTemporaryFile); - } - - public MixedAttribute(String name, long limitSize, Charset charset, String baseDir, boolean deleteOnExit) { - this.limitSize = limitSize; - attribute = new MemoryAttribute(name, charset); - this.baseDir = baseDir; - this.deleteOnExit = deleteOnExit; - } - - public MixedAttribute(String name, long definedSize, long limitSize, Charset charset) { - this(name, definedSize, limitSize, charset, - DiskAttribute.baseDirectory, DiskAttribute.deleteOnExitTemporaryFile); - } - - public MixedAttribute(String name, long definedSize, long limitSize, Charset charset, - String baseDir, boolean deleteOnExit) { - this.limitSize = limitSize; - attribute = new MemoryAttribute(name, definedSize, charset); - this.baseDir = baseDir; - this.deleteOnExit = deleteOnExit; - } - - public MixedAttribute(String name, String value, long limitSize) { - this(name, value, limitSize, HttpConstants.DEFAULT_CHARSET, - DiskAttribute.baseDirectory, DiskFileUpload.deleteOnExitTemporaryFile); - } - - public MixedAttribute(String name, String value, long limitSize, Charset charset) { - this(name, value, limitSize, charset, - DiskAttribute.baseDirectory, DiskFileUpload.deleteOnExitTemporaryFile); - } - - public MixedAttribute(String name, String value, long limitSize, Charset charset, - String baseDir, boolean deleteOnExit) { - this.limitSize = limitSize; - if (value.length() > this.limitSize) { - try { - attribute = new DiskAttribute(name, value, charset, baseDir, deleteOnExit); - } catch (IOException e) { - // revert to Memory mode - try { - attribute = new MemoryAttribute(name, value, charset); - } catch (IOException ignore) { - throw new IllegalArgumentException(e); - } - } - } else { - try { - attribute = new MemoryAttribute(name, value, charset); - } catch (IOException e) { - throw new IllegalArgumentException(e); - } - } - this.baseDir = baseDir; - this.deleteOnExit = deleteOnExit; - } - - @Override - public long getMaxSize() { - return maxSize; - } - - @Override - public void setMaxSize(long maxSize) { - this.maxSize = maxSize; - attribute.setMaxSize(maxSize); - } - - @Override - public void checkSize(long newSize) throws IOException { - if (maxSize >= 0 && newSize > maxSize) { - throw new IOException("Size exceed allowed maximum capacity"); - } - } - - @Override - public void addContent(ByteBuf buffer, boolean last) throws IOException { - if (attribute instanceof MemoryAttribute) { - try { - checkSize(attribute.length() + buffer.readableBytes()); - if (attribute.length() + buffer.readableBytes() > limitSize) { - DiskAttribute diskAttribute = new DiskAttribute(attribute - .getName(), attribute.definedLength(), baseDir, deleteOnExit); - diskAttribute.setMaxSize(maxSize); - if (((MemoryAttribute) attribute).getByteBuf() != null) { - diskAttribute.addContent(((MemoryAttribute) attribute) - .getByteBuf(), false); - } - attribute = diskAttribute; - } - } catch (IOException e) { - buffer.release(); - throw e; - } - } - attribute.addContent(buffer, last); - } - - @Override - public void delete() { - attribute.delete(); - } - - @Override - public byte[] get() throws IOException { - return attribute.get(); - } - - @Override - public ByteBuf getByteBuf() throws IOException { - return attribute.getByteBuf(); - } - - @Override - public Charset getCharset() { - return attribute.getCharset(); - } - - @Override - public String getString() throws IOException { - return attribute.getString(); - } - - @Override - public String getString(Charset encoding) throws IOException { - return attribute.getString(encoding); - } - - @Override - public boolean isCompleted() { - return attribute.isCompleted(); - } - - @Override - public boolean isInMemory() { - return attribute.isInMemory(); - } - - @Override - public long length() { - return attribute.length(); - } - - @Override - public long definedLength() { - return attribute.definedLength(); - } - - @Override - public boolean renameTo(File dest) throws IOException { - return attribute.renameTo(dest); - } - - @Override - public void setCharset(Charset charset) { - attribute.setCharset(charset); - } - - @Override - public void setContent(ByteBuf buffer) throws IOException { - try { - checkSize(buffer.readableBytes()); - } catch (IOException e) { - buffer.release(); - throw e; - } - if (buffer.readableBytes() > limitSize) { - if (attribute instanceof MemoryAttribute) { - // change to Disk - attribute = new DiskAttribute(attribute.getName(), attribute.definedLength(), baseDir, deleteOnExit); - attribute.setMaxSize(maxSize); - } - } - attribute.setContent(buffer); - } - - @Override - public void setContent(File file) throws IOException { - checkSize(file.length()); - if (file.length() > limitSize) { - if (attribute instanceof MemoryAttribute) { - // change to Disk - attribute = new DiskAttribute(attribute.getName(), attribute.definedLength(), baseDir, deleteOnExit); - attribute.setMaxSize(maxSize); - } - } - attribute.setContent(file); - } - - @Override - public void setContent(InputStream inputStream) throws IOException { - if (attribute instanceof MemoryAttribute) { - // change to Disk even if we don't know the size - attribute = new DiskAttribute(attribute.getName(), attribute.definedLength(), baseDir, deleteOnExit); - attribute.setMaxSize(maxSize); - } - attribute.setContent(inputStream); - } - - @Override - public HttpDataType getHttpDataType() { - return attribute.getHttpDataType(); - } - - @Override - public String getName() { - return attribute.getName(); - } - - @Override - public int hashCode() { - return attribute.hashCode(); - } - - @Override - public boolean equals(Object obj) { - return attribute.equals(obj); - } - - @Override - public int compareTo(InterfaceHttpData o) { - return attribute.compareTo(o); - } - - @Override - public String toString() { - return "Mixed: " + attribute; - } - - @Override - public String getValue() throws IOException { - return attribute.getValue(); - } - - @Override - public void setValue(String value) throws IOException { - attribute.setValue(value); - } - - @Override - public ByteBuf getChunk(int length) throws IOException { - return attribute.getChunk(length); - } - - @Override - public File getFile() throws IOException { - return attribute.getFile(); - } - - @Override - public Attribute copy() { - return attribute.copy(); - } - - @Override - public Attribute duplicate() { - return attribute.duplicate(); - } - - @Override - public Attribute retainedDuplicate() { - return attribute.retainedDuplicate(); - } - - @Override - public Attribute replace(ByteBuf content) { - return attribute.replace(content); - } - - @Override - public ByteBuf content() { - return attribute.content(); - } - - @Override - public int refCnt() { - return attribute.refCnt(); - } - - @Override - public Attribute retain() { - attribute.retain(); - return this; - } - - @Override - public Attribute retain(int increment) { - attribute.retain(increment); - return this; - } - - @Override - public Attribute touch() { - attribute.touch(); - return this; - } - - @Override - public Attribute touch(Object hint) { - attribute.touch(hint); - return this; - } - - @Override - public boolean release() { - return attribute.release(); - } - - @Override - public boolean release(int decrement) { - return attribute.release(decrement); - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/MixedFileUpload.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/MixedFileUpload.java deleted file mode 100644 index 547a363983..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/MixedFileUpload.java +++ /dev/null @@ -1,372 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.buffer.ByteBuf; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.Charset; - -/** - * Mixed implementation using both in Memory and in File with a limit of size - */ -public class MixedFileUpload implements FileUpload { - - private final String baseDir; - - private final boolean deleteOnExit; - - private FileUpload fileUpload; - - private final long limitSize; - - private final long definedSize; - private long maxSize = DefaultHttpDataFactory.MAXSIZE; - - public MixedFileUpload(String name, String filename, String contentType, - String contentTransferEncoding, Charset charset, long size, - long limitSize) { - this(name, filename, contentType, contentTransferEncoding, - charset, size, limitSize, DiskFileUpload.baseDirectory, DiskFileUpload.deleteOnExitTemporaryFile); - } - - public MixedFileUpload(String name, String filename, String contentType, - String contentTransferEncoding, Charset charset, long size, - long limitSize, String baseDir, boolean deleteOnExit) { - this.limitSize = limitSize; - if (size > this.limitSize) { - fileUpload = new DiskFileUpload(name, filename, contentType, - contentTransferEncoding, charset, size); - } else { - fileUpload = new MemoryFileUpload(name, filename, contentType, - contentTransferEncoding, charset, size); - } - definedSize = size; - this.baseDir = baseDir; - this.deleteOnExit = deleteOnExit; - } - - @Override - public long getMaxSize() { - return maxSize; - } - - @Override - public void setMaxSize(long maxSize) { - this.maxSize = maxSize; - fileUpload.setMaxSize(maxSize); - } - - @Override - public void checkSize(long newSize) throws IOException { - if (maxSize >= 0 && newSize > maxSize) { - throw new IOException("Size exceed allowed maximum capacity"); - } - } - - @Override - public void addContent(ByteBuf buffer, boolean last) - throws IOException { - if (fileUpload instanceof MemoryFileUpload) { - try { - checkSize(fileUpload.length() + buffer.readableBytes()); - if (fileUpload.length() + buffer.readableBytes() > limitSize) { - DiskFileUpload diskFileUpload = new DiskFileUpload(fileUpload - .getName(), fileUpload.getFilename(), fileUpload - .getContentType(), fileUpload - .getContentTransferEncoding(), fileUpload.getCharset(), - definedSize, baseDir, deleteOnExit); - diskFileUpload.setMaxSize(maxSize); - ByteBuf data = fileUpload.getByteBuf(); - if (data != null && data.isReadable()) { - diskFileUpload.addContent(data.retain(), false); - } - // release old upload - fileUpload.release(); - - fileUpload = diskFileUpload; - } - } catch (IOException e) { - buffer.release(); - throw e; - } - } - fileUpload.addContent(buffer, last); - } - - @Override - public void delete() { - fileUpload.delete(); - } - - @Override - public byte[] get() throws IOException { - return fileUpload.get(); - } - - @Override - public ByteBuf getByteBuf() throws IOException { - return fileUpload.getByteBuf(); - } - - @Override - public Charset getCharset() { - return fileUpload.getCharset(); - } - - @Override - public String getContentType() { - return fileUpload.getContentType(); - } - - @Override - public String getContentTransferEncoding() { - return fileUpload.getContentTransferEncoding(); - } - - @Override - public String getFilename() { - return fileUpload.getFilename(); - } - - @Override - public String getString() throws IOException { - return fileUpload.getString(); - } - - @Override - public String getString(Charset encoding) throws IOException { - return fileUpload.getString(encoding); - } - - @Override - public boolean isCompleted() { - return fileUpload.isCompleted(); - } - - @Override - public boolean isInMemory() { - return fileUpload.isInMemory(); - } - - @Override - public long length() { - return fileUpload.length(); - } - - @Override - public long definedLength() { - return fileUpload.definedLength(); - } - - @Override - public boolean renameTo(File dest) throws IOException { - return fileUpload.renameTo(dest); - } - - @Override - public void setCharset(Charset charset) { - fileUpload.setCharset(charset); - } - - @Override - public void setContent(ByteBuf buffer) throws IOException { - try { - checkSize(buffer.readableBytes()); - } catch (IOException e) { - buffer.release(); - throw e; - } - if (buffer.readableBytes() > limitSize) { - if (fileUpload instanceof MemoryFileUpload) { - FileUpload memoryUpload = fileUpload; - // change to Disk - fileUpload = new DiskFileUpload(memoryUpload - .getName(), memoryUpload.getFilename(), memoryUpload - .getContentType(), memoryUpload - .getContentTransferEncoding(), memoryUpload.getCharset(), - definedSize, baseDir, deleteOnExit); - fileUpload.setMaxSize(maxSize); - - // release old upload - memoryUpload.release(); - } - } - fileUpload.setContent(buffer); - } - - @Override - public void setContent(File file) throws IOException { - checkSize(file.length()); - if (file.length() > limitSize) { - if (fileUpload instanceof MemoryFileUpload) { - FileUpload memoryUpload = fileUpload; - - // change to Disk - fileUpload = new DiskFileUpload(memoryUpload - .getName(), memoryUpload.getFilename(), memoryUpload - .getContentType(), memoryUpload - .getContentTransferEncoding(), memoryUpload.getCharset(), - definedSize, baseDir, deleteOnExit); - fileUpload.setMaxSize(maxSize); - - // release old upload - memoryUpload.release(); - } - } - fileUpload.setContent(file); - } - - @Override - public void setContent(InputStream inputStream) throws IOException { - if (fileUpload instanceof MemoryFileUpload) { - FileUpload memoryUpload = fileUpload; - - // change to Disk - fileUpload = new DiskFileUpload(fileUpload - .getName(), fileUpload.getFilename(), fileUpload - .getContentType(), fileUpload - .getContentTransferEncoding(), fileUpload.getCharset(), - definedSize, baseDir, deleteOnExit); - fileUpload.setMaxSize(maxSize); - - // release old upload - memoryUpload.release(); - } - fileUpload.setContent(inputStream); - } - - @Override - public void setContentType(String contentType) { - fileUpload.setContentType(contentType); - } - - @Override - public void setContentTransferEncoding(String contentTransferEncoding) { - fileUpload.setContentTransferEncoding(contentTransferEncoding); - } - - @Override - public void setFilename(String filename) { - fileUpload.setFilename(filename); - } - - @Override - public HttpDataType getHttpDataType() { - return fileUpload.getHttpDataType(); - } - - @Override - public String getName() { - return fileUpload.getName(); - } - - @Override - public int hashCode() { - return fileUpload.hashCode(); - } - - @Override - public boolean equals(Object obj) { - return fileUpload.equals(obj); - } - - @Override - public int compareTo(InterfaceHttpData o) { - return fileUpload.compareTo(o); - } - - @Override - public String toString() { - return "Mixed: " + fileUpload; - } - - @Override - public ByteBuf getChunk(int length) throws IOException { - return fileUpload.getChunk(length); - } - - @Override - public File getFile() throws IOException { - return fileUpload.getFile(); - } - - @Override - public FileUpload copy() { - return fileUpload.copy(); - } - - @Override - public FileUpload duplicate() { - return fileUpload.duplicate(); - } - - @Override - public FileUpload retainedDuplicate() { - return fileUpload.retainedDuplicate(); - } - - @Override - public FileUpload replace(ByteBuf content) { - return fileUpload.replace(content); - } - - @Override - public ByteBuf content() { - return fileUpload.content(); - } - - @Override - public int refCnt() { - return fileUpload.refCnt(); - } - - @Override - public FileUpload retain() { - fileUpload.retain(); - return this; - } - - @Override - public FileUpload retain(int increment) { - fileUpload.retain(increment); - return this; - } - - @Override - public FileUpload touch() { - fileUpload.touch(); - return this; - } - - @Override - public FileUpload touch(Object hint) { - fileUpload.touch(hint); - return this; - } - - @Override - public boolean release() { - return fileUpload.release(); - } - - @Override - public boolean release(int decrement) { - return fileUpload.release(decrement); - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/package-info.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/package-info.java deleted file mode 100644 index 9575df5309..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * HTTP multipart support. - */ -package io.netty.handler.codec.http.multipart; diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/package-info.java b/codec-http/src/main/java/io/netty/handler/codec/http/package-info.java deleted file mode 100644 index 305e125fec..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * Encoder, decoder and their related message types for HTTP. - */ -package io.netty.handler.codec.http; diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/BinaryWebSocketFrame.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/BinaryWebSocketFrame.java deleted file mode 100644 index 9ea4288b17..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/BinaryWebSocketFrame.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; - -/** - * Web Socket frame containing binary data. - */ -public class BinaryWebSocketFrame extends WebSocketFrame { - - /** - * Creates a new empty binary frame. - */ - public BinaryWebSocketFrame() { - super(Unpooled.buffer(0)); - } - - /** - * Creates a new binary frame with the specified binary data. The final fragment flag is set to true. - * - * @param binaryData - * the content of the frame. - */ - public BinaryWebSocketFrame(ByteBuf binaryData) { - super(binaryData); - } - - /** - * Creates a new binary frame with the specified binary data and the final fragment flag. - * - * @param finalFragment - * flag indicating if this frame is the final fragment - * @param rsv - * reserved bits used for protocol extensions - * @param binaryData - * the content of the frame. - */ - public BinaryWebSocketFrame(boolean finalFragment, int rsv, ByteBuf binaryData) { - super(finalFragment, rsv, binaryData); - } - - @Override - public BinaryWebSocketFrame copy() { - return (BinaryWebSocketFrame) super.copy(); - } - - @Override - public BinaryWebSocketFrame duplicate() { - return (BinaryWebSocketFrame) super.duplicate(); - } - - @Override - public BinaryWebSocketFrame retainedDuplicate() { - return (BinaryWebSocketFrame) super.retainedDuplicate(); - } - - @Override - public BinaryWebSocketFrame replace(ByteBuf content) { - return new BinaryWebSocketFrame(isFinalFragment(), rsv(), content); - } - - @Override - public BinaryWebSocketFrame retain() { - super.retain(); - return this; - } - - @Override - public BinaryWebSocketFrame retain(int increment) { - super.retain(increment); - return this; - } - - @Override - public BinaryWebSocketFrame touch() { - super.touch(); - return this; - } - - @Override - public BinaryWebSocketFrame touch(Object hint) { - super.touch(hint); - return this; - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/CloseWebSocketFrame.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/CloseWebSocketFrame.java deleted file mode 100644 index 1703203902..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/CloseWebSocketFrame.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.CharsetUtil; -import io.netty.util.internal.StringUtil; - -/** - * Web Socket Frame for closing the connection. - */ -public class CloseWebSocketFrame extends WebSocketFrame { - - /** - * Creates a new empty close frame. - */ - public CloseWebSocketFrame() { - super(Unpooled.buffer(0)); - } - - /** - * Creates a new empty close frame with closing status code and reason text - * - * @param status - * Status code as per RFC 6455. For - * example, 1000 indicates normal closure. - */ - public CloseWebSocketFrame(WebSocketCloseStatus status) { - this(requireValidStatusCode(status.code()), status.reasonText()); - } - - /** - * Creates a new empty close frame with closing status code and reason text - * - * @param status - * Status code as per RFC 6455. For - * example, 1000 indicates normal closure. - * @param reasonText - * Reason text. Set to null if no text. - */ - public CloseWebSocketFrame(WebSocketCloseStatus status, String reasonText) { - this(requireValidStatusCode(status.code()), reasonText); - } - - /** - * Creates a new empty close frame with closing status code and reason text - * - * @param statusCode - * Integer status code as per RFC 6455. For - * example, 1000 indicates normal closure. - * @param reasonText - * Reason text. Set to null if no text. - */ - public CloseWebSocketFrame(int statusCode, String reasonText) { - this(true, 0, requireValidStatusCode(statusCode), reasonText); - } - - /** - * Creates a new close frame with no losing status code and no reason text - * - * @param finalFragment - * flag indicating if this frame is the final fragment - * @param rsv - * reserved bits used for protocol extensions. - */ - public CloseWebSocketFrame(boolean finalFragment, int rsv) { - this(finalFragment, rsv, Unpooled.buffer(0)); - } - - /** - * Creates a new close frame with closing status code and reason text - * - * @param finalFragment - * flag indicating if this frame is the final fragment - * @param rsv - * reserved bits used for protocol extensions - * @param statusCode - * Integer status code as per RFC 6455. For - * example, 1000 indicates normal closure. - * @param reasonText - * Reason text. Set to null if no text. - */ - public CloseWebSocketFrame(boolean finalFragment, int rsv, int statusCode, String reasonText) { - super(finalFragment, rsv, newBinaryData(requireValidStatusCode(statusCode), reasonText)); - } - - private static ByteBuf newBinaryData(int statusCode, String reasonText) { - if (reasonText == null) { - reasonText = StringUtil.EMPTY_STRING; - } - - ByteBuf binaryData = Unpooled.buffer(2 + reasonText.length()); - binaryData.writeShort(statusCode); - if (!reasonText.isEmpty()) { - binaryData.writeCharSequence(reasonText, CharsetUtil.UTF_8); - } - - binaryData.readerIndex(0); - return binaryData; - } - - /** - * Creates a new close frame - * - * @param finalFragment - * flag indicating if this frame is the final fragment - * @param rsv - * reserved bits used for protocol extensions - * @param binaryData - * the content of the frame. Must be 2 byte integer followed by optional UTF-8 encoded string. - */ - public CloseWebSocketFrame(boolean finalFragment, int rsv, ByteBuf binaryData) { - super(finalFragment, rsv, binaryData); - } - - /** - * Returns the closing status code as per RFC 6455. If - * a status code is set, -1 is returned. - */ - public int statusCode() { - ByteBuf binaryData = content(); - if (binaryData == null || binaryData.capacity() == 0) { - return -1; - } - - binaryData.readerIndex(0); - return binaryData.getShort(0); - } - - /** - * Returns the reason text as per RFC 6455 If a reason - * text is not supplied, an empty string is returned. - */ - public String reasonText() { - ByteBuf binaryData = content(); - if (binaryData == null || binaryData.capacity() <= 2) { - return ""; - } - - binaryData.readerIndex(2); - String reasonText = binaryData.toString(CharsetUtil.UTF_8); - binaryData.readerIndex(0); - - return reasonText; - } - - @Override - public CloseWebSocketFrame copy() { - return (CloseWebSocketFrame) super.copy(); - } - - @Override - public CloseWebSocketFrame duplicate() { - return (CloseWebSocketFrame) super.duplicate(); - } - - @Override - public CloseWebSocketFrame retainedDuplicate() { - return (CloseWebSocketFrame) super.retainedDuplicate(); - } - - @Override - public CloseWebSocketFrame replace(ByteBuf content) { - return new CloseWebSocketFrame(isFinalFragment(), rsv(), content); - } - - @Override - public CloseWebSocketFrame retain() { - super.retain(); - return this; - } - - @Override - public CloseWebSocketFrame retain(int increment) { - super.retain(increment); - return this; - } - - @Override - public CloseWebSocketFrame touch() { - super.touch(); - return this; - } - - @Override - public CloseWebSocketFrame touch(Object hint) { - super.touch(hint); - return this; - } - - static int requireValidStatusCode(int statusCode) { - if (WebSocketCloseStatus.isValidStatusCode(statusCode)) { - return statusCode; - } else { - throw new IllegalArgumentException("WebSocket close status code does NOT comply with RFC-6455: " + - statusCode); - } - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/ContinuationWebSocketFrame.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/ContinuationWebSocketFrame.java deleted file mode 100644 index 166bda00b5..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/ContinuationWebSocketFrame.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.CharsetUtil; - -/** - * Web Socket continuation frame containing continuation text or binary data. This is used for - * fragmented messages where the contents of a messages is contained more than 1 frame. - */ -public class ContinuationWebSocketFrame extends WebSocketFrame { - - /** - * Creates a new empty continuation frame. - */ - public ContinuationWebSocketFrame() { - this(Unpooled.buffer(0)); - } - - /** - * Creates a new continuation frame with the specified binary data. The final fragment flag is - * set to true. - * - * @param binaryData the content of the frame. - */ - public ContinuationWebSocketFrame(ByteBuf binaryData) { - super(binaryData); - } - - /** - * Creates a new continuation frame with the specified binary data. - * - * @param finalFragment - * flag indicating if this frame is the final fragment - * @param rsv - * reserved bits used for protocol extensions - * @param binaryData - * the content of the frame. - */ - public ContinuationWebSocketFrame(boolean finalFragment, int rsv, ByteBuf binaryData) { - super(finalFragment, rsv, binaryData); - } - - /** - * Creates a new continuation frame with the specified text data - * - * @param finalFragment - * flag indicating if this frame is the final fragment - * @param rsv - * reserved bits used for protocol extensions - * @param text - * text content of the frame. - */ - public ContinuationWebSocketFrame(boolean finalFragment, int rsv, String text) { - this(finalFragment, rsv, fromText(text)); - } - - /** - * Returns the text data in this frame. - */ - public String text() { - return content().toString(CharsetUtil.UTF_8); - } - - /** - * Sets the string for this frame. - * - * @param text - * text to store. - */ - private static ByteBuf fromText(String text) { - if (text == null || text.isEmpty()) { - return Unpooled.EMPTY_BUFFER; - } else { - return Unpooled.copiedBuffer(text, CharsetUtil.UTF_8); - } - } - - @Override - public ContinuationWebSocketFrame copy() { - return (ContinuationWebSocketFrame) super.copy(); - } - - @Override - public ContinuationWebSocketFrame duplicate() { - return (ContinuationWebSocketFrame) super.duplicate(); - } - - @Override - public ContinuationWebSocketFrame retainedDuplicate() { - return (ContinuationWebSocketFrame) super.retainedDuplicate(); - } - - @Override - public ContinuationWebSocketFrame replace(ByteBuf content) { - return new ContinuationWebSocketFrame(isFinalFragment(), rsv(), content); - } - - @Override - public ContinuationWebSocketFrame retain() { - super.retain(); - return this; - } - - @Override - public ContinuationWebSocketFrame retain(int increment) { - super.retain(increment); - return this; - } - - @Override - public ContinuationWebSocketFrame touch() { - super.touch(); - return this; - } - - @Override - public ContinuationWebSocketFrame touch(Object hint) { - super.touch(hint); - return this; - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/CorruptedWebSocketFrameException.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/CorruptedWebSocketFrameException.java deleted file mode 100644 index 92022dfb58..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/CorruptedWebSocketFrameException.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.handler.codec.CorruptedFrameException; -import io.netty.handler.codec.DecoderException; - -/** - * An {@link DecoderException} which is thrown when the received {@link WebSocketFrame} data could not be decoded by - * an inbound handler. - */ -public final class CorruptedWebSocketFrameException extends CorruptedFrameException { - - private static final long serialVersionUID = 3918055132492988338L; - - private final WebSocketCloseStatus closeStatus; - - /** - * Creates a new instance. - */ - public CorruptedWebSocketFrameException() { - this(WebSocketCloseStatus.PROTOCOL_ERROR, null, null); - } - - /** - * Creates a new instance. - */ - public CorruptedWebSocketFrameException(WebSocketCloseStatus status, String message, Throwable cause) { - super(message == null ? status.reasonText() : message, cause); - closeStatus = status; - } - - /** - * Creates a new instance. - */ - public CorruptedWebSocketFrameException(WebSocketCloseStatus status, String message) { - this(status, message, null); - } - - /** - * Creates a new instance. - */ - public CorruptedWebSocketFrameException(WebSocketCloseStatus status, Throwable cause) { - this(status, null, cause); - } - - public WebSocketCloseStatus closeStatus() { - return closeStatus; - } - -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/PingWebSocketFrame.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/PingWebSocketFrame.java deleted file mode 100644 index 7208bb8b2e..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/PingWebSocketFrame.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; - -/** - * Web Socket frame containing binary data. - */ -public class PingWebSocketFrame extends WebSocketFrame { - - /** - * Creates a new empty ping frame. - */ - public PingWebSocketFrame() { - super(true, 0, Unpooled.buffer(0)); - } - - /** - * Creates a new ping frame with the specified binary data. - * - * @param binaryData - * the content of the frame. - */ - public PingWebSocketFrame(ByteBuf binaryData) { - super(binaryData); - } - - /** - * Creates a new ping frame with the specified binary data. - * - * @param finalFragment - * flag indicating if this frame is the final fragment - * @param rsv - * reserved bits used for protocol extensions - * @param binaryData - * the content of the frame. - */ - public PingWebSocketFrame(boolean finalFragment, int rsv, ByteBuf binaryData) { - super(finalFragment, rsv, binaryData); - } - - @Override - public PingWebSocketFrame copy() { - return (PingWebSocketFrame) super.copy(); - } - - @Override - public PingWebSocketFrame duplicate() { - return (PingWebSocketFrame) super.duplicate(); - } - - @Override - public PingWebSocketFrame retainedDuplicate() { - return (PingWebSocketFrame) super.retainedDuplicate(); - } - - @Override - public PingWebSocketFrame replace(ByteBuf content) { - return new PingWebSocketFrame(isFinalFragment(), rsv(), content); - } - - @Override - public PingWebSocketFrame retain() { - super.retain(); - return this; - } - - @Override - public PingWebSocketFrame retain(int increment) { - super.retain(increment); - return this; - } - - @Override - public PingWebSocketFrame touch() { - super.touch(); - return this; - } - - @Override - public PingWebSocketFrame touch(Object hint) { - super.touch(hint); - return this; - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/PongWebSocketFrame.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/PongWebSocketFrame.java deleted file mode 100644 index 79cb9a7fab..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/PongWebSocketFrame.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; - -/** - * Web Socket frame containing binary data. - */ -public class PongWebSocketFrame extends WebSocketFrame { - - /** - * Creates a new empty pong frame. - */ - public PongWebSocketFrame() { - super(Unpooled.buffer(0)); - } - - /** - * Creates a new pong frame with the specified binary data. - * - * @param binaryData - * the content of the frame. - */ - public PongWebSocketFrame(ByteBuf binaryData) { - super(binaryData); - } - - /** - * Creates a new pong frame with the specified binary data - * - * @param finalFragment - * flag indicating if this frame is the final fragment - * @param rsv - * reserved bits used for protocol extensions - * @param binaryData - * the content of the frame. - */ - public PongWebSocketFrame(boolean finalFragment, int rsv, ByteBuf binaryData) { - super(finalFragment, rsv, binaryData); - } - - @Override - public PongWebSocketFrame copy() { - return (PongWebSocketFrame) super.copy(); - } - - @Override - public PongWebSocketFrame duplicate() { - return (PongWebSocketFrame) super.duplicate(); - } - - @Override - public PongWebSocketFrame retainedDuplicate() { - return (PongWebSocketFrame) super.retainedDuplicate(); - } - - @Override - public PongWebSocketFrame replace(ByteBuf content) { - return new PongWebSocketFrame(isFinalFragment(), rsv(), content); - } - - @Override - public PongWebSocketFrame retain() { - super.retain(); - return this; - } - - @Override - public PongWebSocketFrame retain(int increment) { - super.retain(increment); - return this; - } - - @Override - public PongWebSocketFrame touch() { - super.touch(); - return this; - } - - @Override - public PongWebSocketFrame touch(Object hint) { - super.touch(hint); - return this; - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/TextWebSocketFrame.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/TextWebSocketFrame.java deleted file mode 100644 index f520cf6a64..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/TextWebSocketFrame.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.CharsetUtil; - -/** - * Web Socket text frame. - */ -public class TextWebSocketFrame extends WebSocketFrame { - - /** - * Creates a new empty text frame. - */ - public TextWebSocketFrame() { - super(Unpooled.buffer(0)); - } - - /** - * Creates a new text frame with the specified text string. The final fragment flag is set to true. - * - * @param text - * String to put in the frame. - */ - public TextWebSocketFrame(String text) { - super(fromText(text)); - } - - /** - * Creates a new text frame with the specified binary data. The final fragment flag is set to true. - * - * @param binaryData - * the content of the frame. - */ - public TextWebSocketFrame(ByteBuf binaryData) { - super(binaryData); - } - - /** - * Creates a new text frame with the specified text string. The final fragment flag is set to true. - * - * @param finalFragment - * flag indicating if this frame is the final fragment - * @param rsv - * reserved bits used for protocol extensions - * @param text - * String to put in the frame. - */ - public TextWebSocketFrame(boolean finalFragment, int rsv, String text) { - super(finalFragment, rsv, fromText(text)); - } - - private static ByteBuf fromText(String text) { - if (text == null || text.isEmpty()) { - return Unpooled.EMPTY_BUFFER; - } else { - return Unpooled.copiedBuffer(text, CharsetUtil.UTF_8); - } - } - - /** - * Creates a new text frame with the specified binary data and the final fragment flag. - * - * @param finalFragment - * flag indicating if this frame is the final fragment - * @param rsv - * reserved bits used for protocol extensions - * @param binaryData - * the content of the frame. - */ - public TextWebSocketFrame(boolean finalFragment, int rsv, ByteBuf binaryData) { - super(finalFragment, rsv, binaryData); - } - - /** - * Returns the text data in this frame. - */ - public String text() { - return content().toString(CharsetUtil.UTF_8); - } - - @Override - public TextWebSocketFrame copy() { - return (TextWebSocketFrame) super.copy(); - } - - @Override - public TextWebSocketFrame duplicate() { - return (TextWebSocketFrame) super.duplicate(); - } - - @Override - public TextWebSocketFrame retainedDuplicate() { - return (TextWebSocketFrame) super.retainedDuplicate(); - } - - @Override - public TextWebSocketFrame replace(ByteBuf content) { - return new TextWebSocketFrame(isFinalFragment(), rsv(), content); - } - - @Override - public TextWebSocketFrame retain() { - super.retain(); - return this; - } - - @Override - public TextWebSocketFrame retain(int increment) { - super.retain(increment); - return this; - } - - @Override - public TextWebSocketFrame touch() { - super.touch(); - return this; - } - - @Override - public TextWebSocketFrame touch(Object hint) { - super.touch(hint); - return this; - } -} 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 deleted file mode 100644 index 437247b332..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/Utf8FrameValidator.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelFutureListeners; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.CorruptedFrameException; - -/** - * - */ -public class Utf8FrameValidator implements ChannelHandler { - - private int fragmentedFramesCount; - private Utf8Validator utf8Validator; - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - if (msg instanceof WebSocketFrame) { - WebSocketFrame frame = (WebSocketFrame) msg; - - try { - // Processing for possible fragmented messages for text and binary - // frames - if (((WebSocketFrame) msg).isFinalFragment()) { - // Final frame of the sequence. Apparently ping frames are - // allowed in the middle of a fragmented message - if (!(frame instanceof PingWebSocketFrame)) { - fragmentedFramesCount = 0; - - // Check text for UTF8 correctness - if ((frame instanceof TextWebSocketFrame) || - (utf8Validator != null && utf8Validator.isChecking())) { - // Check UTF-8 correctness for this payload - checkUTF8String(frame.content()); - - // This does a second check to make sure UTF-8 - // correctness for entire text message - utf8Validator.finish(); - } - } - } else { - // Not final frame so we can expect more frames in the - // fragmented sequence - if (fragmentedFramesCount == 0) { - // First text or binary frame for a fragmented set - if (frame instanceof TextWebSocketFrame) { - checkUTF8String(frame.content()); - } - } else { - // Subsequent frames - only check if init frame is text - if (utf8Validator != null && utf8Validator.isChecking()) { - checkUTF8String(frame.content()); - } - } - - // Increment counter - fragmentedFramesCount++; - } - } catch (CorruptedWebSocketFrameException e) { - frame.release(); - throw e; - } - } - - ctx.fireChannelRead(msg); - } - - 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(ctx, ChannelFutureListeners.CLOSE); - } - ctx.fireExceptionCaught(cause); - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/Utf8Validator.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/Utf8Validator.java deleted file mode 100644 index 0928698aff..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/Utf8Validator.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -/* - * Adaptation of https://bjoern.hoehrmann.de/utf-8/decoder/dfa/ - * - * Copyright (c) 2008-2009 Bjoern Hoehrmann - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software - * and associated documentation files (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, publish, distribute, - * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.buffer.ByteBuf; -import io.netty.util.ByteProcessor; - -/** - * Checks UTF8 bytes for validity - */ -final class Utf8Validator implements ByteProcessor { - private static final int UTF8_ACCEPT = 0; - private static final int UTF8_REJECT = 12; - - private static final byte[] TYPES = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, - 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 11, 6, 6, 6, 5, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8 }; - - private static final byte[] STATES = { 0, 12, 24, 36, 60, 96, 84, 12, 12, 12, 48, 72, 12, 12, - 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 0, 12, 12, 12, 12, 12, 0, 12, 0, 12, 12, - 12, 24, 12, 12, 12, 12, 12, 24, 12, 24, 12, 12, 12, 12, 12, 12, 12, 12, 12, 24, 12, 12, - 12, 12, 12, 24, 12, 12, 12, 12, 12, 12, 12, 24, 12, 12, 12, 12, 12, 12, 12, 12, 12, 36, - 12, 36, 12, 12, 12, 36, 12, 12, 12, 12, 12, 36, 12, 36, 12, 12, 12, 36, 12, 12, 12, 12, - 12, 12, 12, 12, 12, 12 }; - - @SuppressWarnings("RedundantFieldInitialization") - private int state = UTF8_ACCEPT; - private int codep; - private boolean checking; - - public void check(ByteBuf buffer) { - checking = true; - buffer.forEachByte(this); - } - - public void finish() { - checking = false; - codep = 0; - if (state != UTF8_ACCEPT) { - state = UTF8_ACCEPT; - throw new CorruptedWebSocketFrameException( - WebSocketCloseStatus.INVALID_PAYLOAD_DATA, "bytes are not UTF-8"); - } - } - - @Override - public boolean process(byte b) { - byte type = TYPES[b & 0xFF]; - - codep = state != UTF8_ACCEPT ? b & 0x3f | codep << 6 : 0xff >> type & b; - - state = STATES[state + type]; - - if (state == UTF8_REJECT) { - checking = false; - throw new CorruptedWebSocketFrameException( - WebSocketCloseStatus.INVALID_PAYLOAD_DATA, "bytes are not UTF-8"); - } - return true; - } - - public boolean isChecking() { - return checking; - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket00FrameDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket00FrameDecoder.java deleted file mode 100644 index b1ac6556c4..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket00FrameDecoder.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.ReplayingDecoder; -import io.netty.handler.codec.TooLongFrameException; - -import java.util.Objects; - -import static io.netty.buffer.ByteBufUtil.readBytes; - -/** - * Decodes {@link ByteBuf}s into {@link WebSocketFrame}s. - *

- * For the detailed instruction on adding add Web Socket support to your HTTP server, take a look into the - * WebSocketServer example located in the {@code io.netty.example.http.websocket} package. - */ -public class WebSocket00FrameDecoder extends ReplayingDecoder implements WebSocketFrameDecoder { - - static final int DEFAULT_MAX_FRAME_SIZE = 16384; - - private final long maxFrameSize; - private boolean receivedClosingHandshake; - - public WebSocket00FrameDecoder() { - this(DEFAULT_MAX_FRAME_SIZE); - } - - /** - * Creates a new instance of {@code WebSocketFrameDecoder} with the specified {@code maxFrameSize}. If the client - * sends a frame size larger than {@code maxFrameSize}, the channel will be closed. - * - * @param maxFrameSize - * the maximum frame size to decode - */ - public WebSocket00FrameDecoder(int maxFrameSize) { - this.maxFrameSize = maxFrameSize; - } - - /** - * Creates a new instance of {@code WebSocketFrameDecoder} with the specified {@code maxFrameSize}. If the client - * sends a frame size larger than {@code maxFrameSize}, the channel will be closed. - * - * @param decoderConfig - * Frames decoder configuration. - */ - public WebSocket00FrameDecoder(WebSocketDecoderConfig decoderConfig) { - this.maxFrameSize = Objects.requireNonNull(decoderConfig, "decoderConfig").maxFramePayloadLength(); - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - // Discard all data received if closing handshake was received before. - if (receivedClosingHandshake) { - in.skipBytes(actualReadableBytes()); - return; - } - - // Decode a frame otherwise. - byte type = in.readByte(); - WebSocketFrame frame; - if ((type & 0x80) == 0x80) { - // If the MSB on type is set, decode the frame length - frame = decodeBinaryFrame(ctx, type, in); - } else { - // Decode a 0xff terminated UTF-8 string - frame = decodeTextFrame(ctx, in); - } - - if (frame != null) { - ctx.fireChannelRead(frame); - } - } - - private WebSocketFrame decodeBinaryFrame(ChannelHandlerContext ctx, byte type, ByteBuf buffer) { - long frameSize = 0; - int lengthFieldSize = 0; - byte b; - do { - b = buffer.readByte(); - frameSize <<= 7; - frameSize |= b & 0x7f; - if (frameSize > maxFrameSize) { - throw new TooLongFrameException(); - } - lengthFieldSize++; - if (lengthFieldSize > 8) { - // Perhaps a malicious peer? - throw new TooLongFrameException(); - } - } while ((b & 0x80) == 0x80); - - if (type == (byte) 0xFF && frameSize == 0) { - receivedClosingHandshake = true; - return new CloseWebSocketFrame(true, 0, ctx.alloc().buffer(0)); - } - ByteBuf payload = readBytes(ctx.alloc(), buffer, (int) frameSize); - return new BinaryWebSocketFrame(payload); - } - - private WebSocketFrame decodeTextFrame(ChannelHandlerContext ctx, ByteBuf buffer) { - int ridx = buffer.readerIndex(); - int rbytes = actualReadableBytes(); - int delimPos = buffer.indexOf(ridx, ridx + rbytes, (byte) 0xFF); - if (delimPos == -1) { - // Frame delimiter (0xFF) not found - if (rbytes > maxFrameSize) { - // Frame length exceeded the maximum - throw new TooLongFrameException(); - } else { - // Wait until more data is received - return null; - } - } - - int frameSize = delimPos - ridx; - if (frameSize > maxFrameSize) { - throw new TooLongFrameException(); - } - - ByteBuf binaryData = readBytes(ctx.alloc(), buffer, frameSize); - buffer.skipBytes(1); - - int ffDelimPos = binaryData.indexOf(binaryData.readerIndex(), binaryData.writerIndex(), (byte) 0xFF); - if (ffDelimPos >= 0) { - binaryData.release(); - throw new IllegalArgumentException("a text frame should not contain 0xFF."); - } - - return new TextWebSocketFrame(binaryData); - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket00FrameEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket00FrameEncoder.java deleted file mode 100644 index fbea998804..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket00FrameEncoder.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToMessageEncoder; - -import java.util.List; - -/** - * Encodes a {@link WebSocketFrame} into a {@link ByteBuf}. - *

- * For the detailed instruction on adding add Web Socket support to your HTTP server, take a look into the - * WebSocketServer example located in the {@code io.netty.example.http.websocket} package. - */ -@Sharable -public class WebSocket00FrameEncoder extends MessageToMessageEncoder implements WebSocketFrameEncoder { - private static final ByteBuf _0X00 = Unpooled.unreleasableBuffer( - Unpooled.directBuffer(1, 1).writeByte((byte) 0x00)); - private static final ByteBuf _0XFF = Unpooled.unreleasableBuffer( - Unpooled.directBuffer(1, 1).writeByte((byte) 0xFF)); - private static final ByteBuf _0XFF_0X00 = Unpooled.unreleasableBuffer( - Unpooled.directBuffer(2, 2).writeByte((byte) 0xFF).writeByte((byte) 0x00)); - - @Override - protected void encode(ChannelHandlerContext ctx, WebSocketFrame msg, List out) throws Exception { - if (msg instanceof TextWebSocketFrame) { - // Text frame - ByteBuf data = msg.content(); - - out.add(_0X00.duplicate()); - out.add(data.retain()); - out.add(_0XFF.duplicate()); - } else if (msg instanceof CloseWebSocketFrame) { - // Close frame, needs to call duplicate to allow multiple writes. - // See https://github.com/netty/netty/issues/2768 - out.add(_0XFF_0X00.duplicate()); - } else { - // Binary frame - ByteBuf data = msg.content(); - int dataLen = data.readableBytes(); - - ByteBuf buf = ctx.alloc().buffer(5); - boolean release = true; - try { - // Encode type. - buf.writeByte((byte) 0x80); - - // Encode length. - int b1 = dataLen >>> 28 & 0x7F; - int b2 = dataLen >>> 14 & 0x7F; - int b3 = dataLen >>> 7 & 0x7F; - int b4 = dataLen & 0x7F; - if (b1 == 0) { - if (b2 == 0) { - if (b3 != 0) { - buf.writeByte(b3 | 0x80); - } - buf.writeByte(b4); - } else { - buf.writeByte(b2 | 0x80); - buf.writeByte(b3 | 0x80); - buf.writeByte(b4); - } - } else { - buf.writeByte(b1 | 0x80); - buf.writeByte(b2 | 0x80); - buf.writeByte(b3 | 0x80); - buf.writeByte(b4); - } - - // Encode binary data. - out.add(buf); - out.add(data.retain()); - release = false; - } finally { - if (release) { - buf.release(); - } - } - } - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket07FrameDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket07FrameDecoder.java deleted file mode 100644 index 466004f372..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket07FrameDecoder.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -// (BSD License: https://www.opensource.org/licenses/bsd-license) -// -// Copyright (c) 2011, Joe Walnes and contributors -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or -// without modification, are permitted provided that the -// following conditions are met: -// -// * Redistributions of source code must retain the above -// copyright notice, this list of conditions and the -// following disclaimer. -// -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other -// materials provided with the distribution. -// -// * Neither the name of the Webbit nor the names of -// its contributors may be used to endorse or promote products -// derived from this software without specific prior written -// permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND -// CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE -// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR -// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT -// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -package io.netty.handler.codec.http.websocketx; - -/** - * Decodes a web socket frame from wire protocol version 7 format. V7 is essentially the same as V8. - */ -public class WebSocket07FrameDecoder extends WebSocket08FrameDecoder { - - /** - * Constructor - * - * @param expectMaskedFrames - * Web socket servers must set this to true processed incoming masked payload. Client implementations - * must set this to false. - * @param allowExtensions - * Flag to allow reserved extension bits to be used or not - * @param maxFramePayloadLength - * Maximum length of a frame's payload. Setting this to an appropriate value for you application - * helps check for denial of services attacks. - */ - public WebSocket07FrameDecoder(boolean expectMaskedFrames, boolean allowExtensions, int maxFramePayloadLength) { - this(WebSocketDecoderConfig.newBuilder() - .expectMaskedFrames(expectMaskedFrames) - .allowExtensions(allowExtensions) - .maxFramePayloadLength(maxFramePayloadLength) - .build()); - } - - /** - * Constructor - * - * @param expectMaskedFrames - * Web socket servers must set this to true processed incoming masked payload. Client implementations - * must set this to false. - * @param allowExtensions - * Flag to allow reserved extension bits to be used or not - * @param maxFramePayloadLength - * Maximum length of a frame's payload. Setting this to an appropriate value for you application - * helps check for denial of services attacks. - * @param allowMaskMismatch - * When set to true, frames which are not masked properly according to the standard will still be - * accepted. - */ - public WebSocket07FrameDecoder(boolean expectMaskedFrames, boolean allowExtensions, int maxFramePayloadLength, - boolean allowMaskMismatch) { - this(WebSocketDecoderConfig.newBuilder() - .expectMaskedFrames(expectMaskedFrames) - .allowExtensions(allowExtensions) - .maxFramePayloadLength(maxFramePayloadLength) - .allowMaskMismatch(allowMaskMismatch) - .build()); - } - - /** - * Constructor - * - * @param decoderConfig - * Frames decoder configuration. - */ - public WebSocket07FrameDecoder(WebSocketDecoderConfig decoderConfig) { - super(decoderConfig); - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket07FrameEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket07FrameEncoder.java deleted file mode 100644 index f2604c9364..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket07FrameEncoder.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -// (BSD License: https://www.opensource.org/licenses/bsd-license) -// -// Copyright (c) 2011, Joe Walnes and contributors -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or -// without modification, are permitted provided that the -// following conditions are met: -// -// * Redistributions of source code must retain the above -// copyright notice, this list of conditions and the -// following disclaimer. -// -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other -// materials provided with the distribution. -// -// * Neither the name of the Webbit nor the names of -// its contributors may be used to endorse or promote products -// derived from this software without specific prior written -// permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND -// CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE -// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR -// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT -// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -package io.netty.handler.codec.http.websocketx; - -/** - *

- * Encodes a web socket frame into wire protocol version 7 format. V7 is essentially the same as V8. - *

- */ -public class WebSocket07FrameEncoder extends WebSocket08FrameEncoder { - - /** - * Constructor - * - * @param maskPayload - * Web socket clients must set this to true to mask payload. Server implementations must set this to - * false. - */ - public WebSocket07FrameEncoder(boolean maskPayload) { - super(maskPayload); - } -} 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 deleted file mode 100644 index 5fe2874ecf..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoder.java +++ /dev/null @@ -1,496 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -// (BSD License: https://www.opensource.org/licenses/bsd-license) -// -// Copyright (c) 2011, Joe Walnes and contributors -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or -// without modification, are permitted provided that the -// following conditions are met: -// -// * Redistributions of source code must retain the above -// copyright notice, this list of conditions and the -// following disclaimer. -// -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other -// materials provided with the distribution. -// -// * Neither the name of the Webbit nor the names of -// its contributors may be used to endorse or promote products -// derived from this software without specific prior written -// permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND -// CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE -// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR -// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT -// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -package io.netty.handler.codec.http.websocketx; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelFutureListeners; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.ByteToMessageDecoder; -import io.netty.handler.codec.TooLongFrameException; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.nio.ByteOrder; -import java.util.Objects; - -import static io.netty.buffer.ByteBufUtil.readBytes; - -/** - * Decodes a web socket frame from wire protocol version 8 format. This code was forked from webbit and modified. - */ -public class WebSocket08FrameDecoder extends ByteToMessageDecoder - implements WebSocketFrameDecoder { - - enum State { - READING_FIRST, - READING_SECOND, - READING_SIZE, - MASKING_KEY, - PAYLOAD, - CORRUPT - } - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(WebSocket08FrameDecoder.class); - - private static final byte OPCODE_CONT = 0x0; - private static final byte OPCODE_TEXT = 0x1; - private static final byte OPCODE_BINARY = 0x2; - private static final byte OPCODE_CLOSE = 0x8; - private static final byte OPCODE_PING = 0x9; - private static final byte OPCODE_PONG = 0xA; - - private final WebSocketDecoderConfig config; - - private int fragmentedFramesCount; - private boolean frameFinalFlag; - private boolean frameMasked; - private int frameRsv; - private int frameOpcode; - private long framePayloadLength; - private byte[] maskingKey; - private int framePayloadLen1; - private boolean receivedClosingHandshake; - private State state = State.READING_FIRST; - - /** - * Constructor - * - * @param expectMaskedFrames - * Web socket servers must set this to true processed incoming masked payload. Client implementations - * must set this to false. - * @param allowExtensions - * Flag to allow reserved extension bits to be used or not - * @param maxFramePayloadLength - * Maximum length of a frame's payload. Setting this to an appropriate value for you application - * helps check for denial of services attacks. - */ - public WebSocket08FrameDecoder(boolean expectMaskedFrames, boolean allowExtensions, int maxFramePayloadLength) { - this(expectMaskedFrames, allowExtensions, maxFramePayloadLength, false); - } - - /** - * Constructor - * - * @param expectMaskedFrames - * Web socket servers must set this to true processed incoming masked payload. Client implementations - * must set this to false. - * @param allowExtensions - * Flag to allow reserved extension bits to be used or not - * @param maxFramePayloadLength - * Maximum length of a frame's payload. Setting this to an appropriate value for you application - * helps check for denial of services attacks. - * @param allowMaskMismatch - * When set to true, frames which are not masked properly according to the standard will still be - * accepted. - */ - public WebSocket08FrameDecoder(boolean expectMaskedFrames, boolean allowExtensions, int maxFramePayloadLength, - boolean allowMaskMismatch) { - this(WebSocketDecoderConfig.newBuilder() - .expectMaskedFrames(expectMaskedFrames) - .allowExtensions(allowExtensions) - .maxFramePayloadLength(maxFramePayloadLength) - .allowMaskMismatch(allowMaskMismatch) - .build()); - } - - /** - * Constructor - * - * @param decoderConfig - * Frames decoder configuration. - */ - public WebSocket08FrameDecoder(WebSocketDecoderConfig decoderConfig) { - this.config = Objects.requireNonNull(decoderConfig, "decoderConfig"); - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - // Discard all data received if closing handshake was received before. - if (receivedClosingHandshake) { - in.skipBytes(actualReadableBytes()); - return; - } - - switch (state) { - case READING_FIRST: - if (!in.isReadable()) { - return; - } - - framePayloadLength = 0; - - // FIN, RSV, OPCODE - byte b = in.readByte(); - frameFinalFlag = (b & 0x80) != 0; - frameRsv = (b & 0x70) >> 4; - frameOpcode = b & 0x0F; - - if (logger.isTraceEnabled()) { - logger.trace("Decoding WebSocket Frame opCode={}", frameOpcode); - } - - state = State.READING_SECOND; - case READING_SECOND: - if (!in.isReadable()) { - return; - } - // MASK, PAYLOAD LEN 1 - b = in.readByte(); - frameMasked = (b & 0x80) != 0; - framePayloadLen1 = b & 0x7F; - - if (frameRsv != 0 && !config.allowExtensions()) { - protocolViolation(ctx, in, "RSV != 0 and no extension negotiated, RSV:" + frameRsv); - return; - } - - if (!config.allowMaskMismatch() && config.expectMaskedFrames() != frameMasked) { - protocolViolation(ctx, in, "received a frame that is not masked as expected"); - return; - } - - if (frameOpcode > 7) { // control frame (have MSB in opcode set) - - // control frames MUST NOT be fragmented - if (!frameFinalFlag) { - protocolViolation(ctx, in, "fragmented control frame"); - return; - } - - // control frames MUST have payload 125 octets or less - if (framePayloadLen1 > 125) { - protocolViolation(ctx, in, "control frame with payload length > 125 octets"); - return; - } - - // check for reserved control frame opcodes - if (!(frameOpcode == OPCODE_CLOSE || frameOpcode == OPCODE_PING - || frameOpcode == OPCODE_PONG)) { - protocolViolation(ctx, in, "control frame using reserved opcode " + frameOpcode); - return; - } - - // close frame : if there is a body, the first two bytes of the - // body MUST be a 2-byte unsigned integer (in network byte - // order) representing a getStatus code - if (frameOpcode == 8 && framePayloadLen1 == 1) { - protocolViolation(ctx, in, "received close control frame with payload len 1"); - return; - } - } else { // data frame - // check for reserved data frame opcodes - if (!(frameOpcode == OPCODE_CONT || frameOpcode == OPCODE_TEXT - || frameOpcode == OPCODE_BINARY)) { - protocolViolation(ctx, in, "data frame using reserved opcode " + frameOpcode); - return; - } - - // check opcode vs message fragmentation state 1/2 - if (fragmentedFramesCount == 0 && frameOpcode == OPCODE_CONT) { - protocolViolation(ctx, in, "received continuation data frame outside fragmented message"); - return; - } - - // check opcode vs message fragmentation state 2/2 - if (fragmentedFramesCount != 0 && frameOpcode != OPCODE_CONT) { - protocolViolation(ctx, in, - "received non-continuation data frame while inside fragmented message"); - return; - } - } - - state = State.READING_SIZE; - case READING_SIZE: - - // Read frame payload length - if (framePayloadLen1 == 126) { - if (in.readableBytes() < 2) { - return; - } - framePayloadLength = in.readUnsignedShort(); - if (framePayloadLength < 126) { - protocolViolation(ctx, in, "invalid data frame length (not using minimal length encoding)"); - return; - } - } else if (framePayloadLen1 == 127) { - if (in.readableBytes() < 8) { - return; - } - framePayloadLength = in.readLong(); - // TODO: check if it's bigger than 0x7FFFFFFFFFFFFFFF, Maybe - // just check if it's negative? - - if (framePayloadLength < 65536) { - protocolViolation(ctx, in, "invalid data frame length (not using minimal length encoding)"); - return; - } - } else { - framePayloadLength = framePayloadLen1; - } - - if (framePayloadLength > config.maxFramePayloadLength()) { - protocolViolation(ctx, in, WebSocketCloseStatus.MESSAGE_TOO_BIG, - "Max frame length of " + config.maxFramePayloadLength() + " has been exceeded."); - return; - } - - if (logger.isTraceEnabled()) { - logger.trace("Decoding WebSocket Frame length={}", framePayloadLength); - } - - state = State.MASKING_KEY; - case MASKING_KEY: - if (frameMasked) { - if (in.readableBytes() < 4) { - return; - } - if (maskingKey == null) { - maskingKey = new byte[4]; - } - in.readBytes(maskingKey); - } - state = State.PAYLOAD; - case PAYLOAD: - if (in.readableBytes() < framePayloadLength) { - return; - } - - ByteBuf payloadBuffer = null; - try { - payloadBuffer = readBytes(ctx.alloc(), in, toFrameLength(framePayloadLength)); - - // Now we have all the data, the next checkpoint must be the next - // frame - state = State.READING_FIRST; - - // Unmask data if needed - if (frameMasked) { - unmask(payloadBuffer); - } - - // Processing ping/pong/close frames because they cannot be - // fragmented - if (frameOpcode == OPCODE_PING) { - WebSocketFrame frame = new PingWebSocketFrame(frameFinalFlag, frameRsv, payloadBuffer); - payloadBuffer = null; - ctx.fireChannelRead(frame); - return; - } - if (frameOpcode == OPCODE_PONG) { - WebSocketFrame frame = new PongWebSocketFrame(frameFinalFlag, frameRsv, payloadBuffer); - payloadBuffer = null; - ctx.fireChannelRead(frame); - return; - } - if (frameOpcode == OPCODE_CLOSE) { - receivedClosingHandshake = true; - checkCloseFrameBody(ctx, payloadBuffer); - WebSocketFrame frame = new CloseWebSocketFrame(frameFinalFlag, frameRsv, payloadBuffer); - payloadBuffer = null; - ctx.fireChannelRead(frame); - return; - } - - // Processing for possible fragmented messages for text and binary - // frames - if (frameFinalFlag) { - // Final frame of the sequence. Apparently ping frames are - // allowed in the middle of a fragmented message - fragmentedFramesCount = 0; - } else { - // Increment counter - fragmentedFramesCount++; - } - - // Return the frame - if (frameOpcode == OPCODE_TEXT) { - WebSocketFrame frame = new TextWebSocketFrame(frameFinalFlag, frameRsv, payloadBuffer); - payloadBuffer = null; - ctx.fireChannelRead(frame); - return; - } else if (frameOpcode == OPCODE_BINARY) { - WebSocketFrame frame = new BinaryWebSocketFrame(frameFinalFlag, frameRsv, payloadBuffer); - payloadBuffer = null; - ctx.fireChannelRead(frame); - return; - } else if (frameOpcode == OPCODE_CONT) { - WebSocketFrame frame = new ContinuationWebSocketFrame(frameFinalFlag, frameRsv, payloadBuffer); - payloadBuffer = null; - ctx.fireChannelRead(frame); - return; - } else { - throw new UnsupportedOperationException("Cannot decode web socket frame with opcode: " - + frameOpcode); - } - } finally { - if (payloadBuffer != null) { - payloadBuffer.release(); - } - } - case CORRUPT: - if (in.isReadable()) { - // If we don't keep reading Netty will throw an exception saying - // we can't return null if no bytes read and state not changed. - in.readByte(); - } - return; - default: - throw new Error("Shouldn't reach here."); - } - } - - private void unmask(ByteBuf frame) { - int i = frame.readerIndex(); - int end = frame.writerIndex(); - - ByteOrder order = frame.order(); - - // Remark: & 0xFF is necessary because Java will do signed expansion from - // byte to int which we don't want. - int intMask = ((maskingKey[0] & 0xFF) << 24) - | ((maskingKey[1] & 0xFF) << 16) - | ((maskingKey[2] & 0xFF) << 8) - | (maskingKey[3] & 0xFF); - - // If the byte order of our buffers it little endian we have to bring our mask - // into the same format, because getInt() and writeInt() will use a reversed byte order - if (order == ByteOrder.LITTLE_ENDIAN) { - intMask = Integer.reverseBytes(intMask); - } - - for (; i + 3 < end; i += 4) { - int unmasked = frame.getInt(i) ^ intMask; - frame.setInt(i, unmasked); - } - for (; i < end; i++) { - frame.setByte(i, frame.getByte(i) ^ maskingKey[i % 4]); - } - } - - private void protocolViolation(ChannelHandlerContext ctx, ByteBuf in, String reason) { - protocolViolation(ctx, in, WebSocketCloseStatus.PROTOCOL_ERROR, reason); - } - - private void protocolViolation(ChannelHandlerContext ctx, ByteBuf in, WebSocketCloseStatus status, String reason) { - protocolViolation(ctx, in, new CorruptedWebSocketFrameException(status, reason)); - } - - private void protocolViolation(ChannelHandlerContext ctx, ByteBuf in, CorruptedWebSocketFrameException ex) { - state = State.CORRUPT; - int readableBytes = in.readableBytes(); - if (readableBytes > 0) { - // Fix for memory leak, caused by ByteToMessageDecoder#channelRead: - // buffer 'cumulation' is released ONLY when no more readable bytes available. - in.skipBytes(readableBytes); - } - if (ctx.channel().isActive() && config.closeOnProtocolViolation()) { - Object closeMessage; - if (receivedClosingHandshake) { - closeMessage = Unpooled.EMPTY_BUFFER; - } else { - WebSocketCloseStatus closeStatus = ex.closeStatus(); - String reasonText = ex.getMessage(); - if (reasonText == null) { - reasonText = closeStatus.reasonText(); - } - closeMessage = new CloseWebSocketFrame(closeStatus, reasonText); - } - ctx.writeAndFlush(closeMessage).addListener(ctx, ChannelFutureListeners.CLOSE); - } - throw ex; - } - - private static int toFrameLength(long l) { - if (l > Integer.MAX_VALUE) { - throw new TooLongFrameException("Length:" + l); - } else { - return (int) l; - } - } - - /** */ - protected void checkCloseFrameBody( - ChannelHandlerContext ctx, ByteBuf buffer) { - if (buffer == null || !buffer.isReadable()) { - return; - } - if (buffer.readableBytes() == 1) { - protocolViolation(ctx, buffer, WebSocketCloseStatus.INVALID_PAYLOAD_DATA, "Invalid close frame body"); - } - - // Save reader index - int idx = buffer.readerIndex(); - buffer.readerIndex(0); - - // Must have 2 byte integer within the valid range - int statusCode = buffer.readShort(); - if (!WebSocketCloseStatus.isValidStatusCode(statusCode)) { - protocolViolation(ctx, buffer, "Invalid close frame getStatus code: " + statusCode); - } - - // May have UTF-8 message - if (buffer.isReadable()) { - try { - new Utf8Validator().check(buffer); - } catch (CorruptedWebSocketFrameException ex) { - protocolViolation(ctx, buffer, ex); - } - } - - // Restore reader index - buffer.readerIndex(idx); - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameEncoder.java deleted file mode 100644 index af1fdace05..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameEncoder.java +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -// (BSD License: https://www.opensource.org/licenses/bsd-license) -// -// Copyright (c) 2011, Joe Walnes and contributors -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or -// without modification, are permitted provided that the -// following conditions are met: -// -// * Redistributions of source code must retain the above -// copyright notice, this list of conditions and the -// following disclaimer. -// -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other -// materials provided with the distribution. -// -// * Neither the name of the Webbit nor the names of -// its contributors may be used to endorse or promote products -// derived from this software without specific prior written -// permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND -// CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE -// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR -// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT -// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -package io.netty.handler.codec.http.websocketx; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToMessageEncoder; -import io.netty.handler.codec.TooLongFrameException; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.List; -import java.util.concurrent.ThreadLocalRandom; - -/** - *

- * Encodes a web socket frame into wire protocol version 8 format. This code was forked from webbit and modified. - *

- */ -public class WebSocket08FrameEncoder extends MessageToMessageEncoder implements WebSocketFrameEncoder { - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(WebSocket08FrameEncoder.class); - - private static final byte OPCODE_CONT = 0x0; - private static final byte OPCODE_TEXT = 0x1; - private static final byte OPCODE_BINARY = 0x2; - private static final byte OPCODE_CLOSE = 0x8; - private static final byte OPCODE_PING = 0x9; - private static final byte OPCODE_PONG = 0xA; - - /** - * The size threshold for gathering writes. Non-Masked messages bigger than this size will be be sent fragmented as - * a header and a content ByteBuf whereas messages smaller than the size will be merged into a single buffer and - * sent at once.
- * Masked messages will always be sent at once. - */ - private static final int GATHERING_WRITE_THRESHOLD = 1024; - - private final boolean maskPayload; - - /** - * Constructor - * - * @param maskPayload - * Web socket clients must set this to true to mask payload. Server implementations must set this to - * false. - */ - public WebSocket08FrameEncoder(boolean maskPayload) { - this.maskPayload = maskPayload; - } - - @Override - protected void encode(ChannelHandlerContext ctx, WebSocketFrame msg, List out) throws Exception { - final ByteBuf data = msg.content(); - byte[] mask; - - byte opcode; - if (msg instanceof TextWebSocketFrame) { - opcode = OPCODE_TEXT; - } else if (msg instanceof PingWebSocketFrame) { - opcode = OPCODE_PING; - } else if (msg instanceof PongWebSocketFrame) { - opcode = OPCODE_PONG; - } else if (msg instanceof CloseWebSocketFrame) { - opcode = OPCODE_CLOSE; - } else if (msg instanceof BinaryWebSocketFrame) { - opcode = OPCODE_BINARY; - } else if (msg instanceof ContinuationWebSocketFrame) { - opcode = OPCODE_CONT; - } else { - throw new UnsupportedOperationException("Cannot encode frame of type: " + msg.getClass().getName()); - } - - int length = data.readableBytes(); - - if (logger.isTraceEnabled()) { - logger.trace("Encoding WebSocket Frame opCode={} length={}", opcode, length); - } - - int b0 = 0; - if (msg.isFinalFragment()) { - b0 |= 1 << 7; - } - b0 |= msg.rsv() % 8 << 4; - b0 |= opcode % 128; - - if (opcode == OPCODE_PING && length > 125) { - throw new TooLongFrameException("invalid payload for PING (payload length must be <= 125, was " - + length); - } - - boolean release = true; - ByteBuf buf = null; - try { - int maskLength = maskPayload ? 4 : 0; - if (length <= 125) { - int size = 2 + maskLength; - if (maskPayload || length <= GATHERING_WRITE_THRESHOLD) { - size += length; - } - buf = ctx.alloc().buffer(size); - buf.writeByte(b0); - byte b = (byte) (maskPayload ? 0x80 | (byte) length : (byte) length); - buf.writeByte(b); - } else if (length <= 0xFFFF) { - int size = 4 + maskLength; - if (maskPayload || length <= GATHERING_WRITE_THRESHOLD) { - size += length; - } - buf = ctx.alloc().buffer(size); - buf.writeByte(b0); - buf.writeByte(maskPayload ? 0xFE : 126); - buf.writeByte(length >>> 8 & 0xFF); - buf.writeByte(length & 0xFF); - } else { - int size = 10 + maskLength; - if (maskPayload || length <= GATHERING_WRITE_THRESHOLD) { - size += length; - } - buf = ctx.alloc().buffer(size); - buf.writeByte(b0); - buf.writeByte(maskPayload ? 0xFF : 127); - buf.writeLong(length); - } - - // Write payload - if (maskPayload) { - int random = ThreadLocalRandom.current().nextInt(); - mask = ByteBuffer.allocate(4).putInt(random).array(); - buf.writeBytes(mask); - - ByteOrder srcOrder = data.order(); - ByteOrder dstOrder = buf.order(); - - int counter = 0; - int i = data.readerIndex(); - int end = data.writerIndex(); - - if (srcOrder == dstOrder) { - // Use the optimized path only when byte orders match - // Remark: & 0xFF is necessary because Java will do signed expansion from - // byte to int which we don't want. - int intMask = ((mask[0] & 0xFF) << 24) - | ((mask[1] & 0xFF) << 16) - | ((mask[2] & 0xFF) << 8) - | (mask[3] & 0xFF); - - // If the byte order of our buffers it little endian we have to bring our mask - // into the same format, because getInt() and writeInt() will use a reversed byte order - if (srcOrder == ByteOrder.LITTLE_ENDIAN) { - intMask = Integer.reverseBytes(intMask); - } - - for (; i + 3 < end; i += 4) { - int intData = data.getInt(i); - buf.writeInt(intData ^ intMask); - } - } - for (; i < end; i++) { - byte byteData = data.getByte(i); - buf.writeByte(byteData ^ mask[counter++ % 4]); - } - out.add(buf); - } else { - if (buf.writableBytes() >= data.readableBytes()) { - // merge buffers as this is cheaper then a gathering write if the payload is small enough - buf.writeBytes(data); - out.add(buf); - } else { - out.add(buf); - out.add(data.retain()); - } - } - release = false; - } finally { - if (release && buf != null) { - buf.release(); - } - } - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket13FrameDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket13FrameDecoder.java deleted file mode 100644 index bb8363c4c8..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket13FrameDecoder.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -// (BSD License: https://www.opensource.org/licenses/bsd-license) -// -// Copyright (c) 2011, Joe Walnes and contributors -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or -// without modification, are permitted provided that the -// following conditions are met: -// -// * Redistributions of source code must retain the above -// copyright notice, this list of conditions and the -// following disclaimer. -// -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other -// materials provided with the distribution. -// -// * Neither the name of the Webbit nor the names of -// its contributors may be used to endorse or promote products -// derived from this software without specific prior written -// permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND -// CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE -// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR -// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT -// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -package io.netty.handler.codec.http.websocketx; - -/** - * Decodes a web socket frame from wire protocol version 13 format. V13 is essentially the same as V8. - */ -public class WebSocket13FrameDecoder extends WebSocket08FrameDecoder { - - /** - * Constructor - * - * @param expectMaskedFrames - * Web socket servers must set this to true processed incoming masked payload. Client implementations - * must set this to false. - * @param allowExtensions - * Flag to allow reserved extension bits to be used or not - * @param maxFramePayloadLength - * Maximum length of a frame's payload. Setting this to an appropriate value for you application - * helps check for denial of services attacks. - */ - public WebSocket13FrameDecoder(boolean expectMaskedFrames, boolean allowExtensions, int maxFramePayloadLength) { - this(expectMaskedFrames, allowExtensions, maxFramePayloadLength, false); - } - - /** - * Constructor - * - * @param expectMaskedFrames - * Web socket servers must set this to true processed incoming masked payload. Client implementations - * must set this to false. - * @param allowExtensions - * Flag to allow reserved extension bits to be used or not - * @param maxFramePayloadLength - * Maximum length of a frame's payload. Setting this to an appropriate value for you application - * helps check for denial of services attacks. - * @param allowMaskMismatch - * When set to true, frames which are not masked properly according to the standard will still be - * accepted. - */ - public WebSocket13FrameDecoder(boolean expectMaskedFrames, boolean allowExtensions, int maxFramePayloadLength, - boolean allowMaskMismatch) { - this(WebSocketDecoderConfig.newBuilder() - .expectMaskedFrames(expectMaskedFrames) - .allowExtensions(allowExtensions) - .maxFramePayloadLength(maxFramePayloadLength) - .allowMaskMismatch(allowMaskMismatch) - .build()); - } - - /** - * Constructor - * - * @param decoderConfig - * Frames decoder configuration. - */ - public WebSocket13FrameDecoder(WebSocketDecoderConfig decoderConfig) { - super(decoderConfig); - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket13FrameEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket13FrameEncoder.java deleted file mode 100644 index 3586b83b92..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket13FrameEncoder.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -// (BSD License: https://www.opensource.org/licenses/bsd-license) -// -// Copyright (c) 2011, Joe Walnes and contributors -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or -// without modification, are permitted provided that the -// following conditions are met: -// -// * Redistributions of source code must retain the above -// copyright notice, this list of conditions and the -// following disclaimer. -// -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other -// materials provided with the distribution. -// -// * Neither the name of the Webbit nor the names of -// its contributors may be used to endorse or promote products -// derived from this software without specific prior written -// permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND -// CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE -// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR -// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT -// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -package io.netty.handler.codec.http.websocketx; - -/** - *

- * Encodes a web socket frame into wire protocol version 13 format. V13 is essentially the same as V8. - *

- */ -public class WebSocket13FrameEncoder extends WebSocket08FrameEncoder { - - /** - * Constructor - * - * @param maskPayload - * Web socket clients must set this to true to mask payload. Server implementations must set this to - * false. - */ - public WebSocket13FrameEncoder(boolean maskPayload) { - super(maskPayload); - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketChunkedInput.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketChunkedInput.java deleted file mode 100644 index 8c9c53007d..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketChunkedInput.java +++ /dev/null @@ -1,115 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.stream.ChunkedInput; - -/** - * A {@link ChunkedInput} that fetches data chunk by chunk for use with WebSocket chunked transfers. - *

- * Each chunk from the input data will be wrapped within a {@link ContinuationWebSocketFrame}. - * At the end of the input data, {@link ContinuationWebSocketFrame} with finalFragment will be written. - *

- */ -public final class WebSocketChunkedInput implements ChunkedInput { - private final ChunkedInput input; - private final int rsv; - - /** - * Creates a new instance using the specified input. - * @param input {@link ChunkedInput} containing data to write - */ - public WebSocketChunkedInput(ChunkedInput input) { - this(input, 0); - } - - /** - * Creates a new instance using the specified input. - * @param input {@link ChunkedInput} containing data to write - * @param rsv RSV1, RSV2, RSV3 used for extensions - * - * @throws NullPointerException if {@code input} is null - */ - public WebSocketChunkedInput(ChunkedInput input, int rsv) { - this.input = requireNonNull(input, "input"); - this.rsv = rsv; - } - - /** - * @return {@code true} if and only if there is no data left in the stream - * and the stream has reached at its end. - */ - @Override - public boolean isEndOfInput() throws Exception { - return input.isEndOfInput(); - } - - /** - * Releases the resources associated with the input. - */ - @Override - public void close() throws Exception { - input.close(); - } - - /** - * @deprecated Use {@link #readChunk(ByteBufAllocator)}. - * - * Fetches a chunked data from the stream. Once this method returns the last chunk - * and thus the stream has reached at its end, any subsequent {@link #isEndOfInput()} - * call must return {@code true}. - * - * @param ctx {@link ChannelHandlerContext} context of channelHandler - * @return {@link WebSocketFrame} contain chunk of data - */ - @Deprecated - @Override - public WebSocketFrame readChunk(ChannelHandlerContext ctx) throws Exception { - return readChunk(ctx.alloc()); - } - - /** - * Fetches a chunked data from the stream. Once this method returns the last chunk - * and thus the stream has reached at its end, any subsequent {@link #isEndOfInput()} - * call must return {@code true}. - * - * @param allocator {@link ByteBufAllocator} - * @return {@link WebSocketFrame} contain chunk of data - */ - @Override - public WebSocketFrame readChunk(ByteBufAllocator allocator) throws Exception { - ByteBuf buf = input.readChunk(allocator); - if (buf == null) { - return null; - } - return new ContinuationWebSocketFrame(input.isEndOfInput(), rsv, buf); - } - - @Override - public long length() { - return input.length(); - } - - @Override - public long progress() { - return input.progress(); - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshakeException.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshakeException.java deleted file mode 100644 index 69f1839dc2..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshakeException.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.handler.codec.http.DefaultHttpResponse; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.util.ReferenceCounted; - -/** - * Client exception during handshaking process. - * - *

IMPORTANT: This exception does not contain any {@link ReferenceCounted} fields - * e.g. {@link FullHttpResponse}, so no special treatment is needed. - */ -public final class WebSocketClientHandshakeException extends WebSocketHandshakeException { - - private static final long serialVersionUID = 1L; - - private final HttpResponse response; - - public WebSocketClientHandshakeException(String message) { - this(message, null); - } - - public WebSocketClientHandshakeException(String message, HttpResponse httpResponse) { - super(message); - if (httpResponse != null) { - response = new DefaultHttpResponse(httpResponse.protocolVersion(), - httpResponse.status(), httpResponse.headers()); - } else { - response = null; - } - } - - /** - * Returns a {@link HttpResponse response} if exception occurs during response validation otherwise {@code null}. - */ - public HttpResponse response() { - return response; - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker.java deleted file mode 100644 index 9aabd260e3..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker.java +++ /dev/null @@ -1,577 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelOutboundInvoker; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.SimpleChannelInboundHandler; -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.HttpContentDecompressor; -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.HttpResponse; -import io.netty.handler.codec.http.HttpResponseDecoder; -import io.netty.handler.codec.http.HttpScheme; -import io.netty.util.NetUtil; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; - -import java.net.URI; -import java.nio.channels.ClosedChannelException; -import java.util.Locale; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; - -import static java.util.Objects.requireNonNull; - -/** - * Base class for web socket client handshake implementations - */ -public abstract class WebSocketClientHandshaker { - - private static final String HTTP_SCHEME_PREFIX = HttpScheme.HTTP + "://"; - private static final String HTTPS_SCHEME_PREFIX = HttpScheme.HTTPS + "://"; - protected static final int DEFAULT_FORCE_CLOSE_TIMEOUT_MILLIS = 10000; - - private final URI uri; - - private final WebSocketVersion version; - - private volatile boolean handshakeComplete; - - private volatile long forceCloseTimeoutMillis = DEFAULT_FORCE_CLOSE_TIMEOUT_MILLIS; - - private volatile int forceCloseInit; - - private static final AtomicIntegerFieldUpdater FORCE_CLOSE_INIT_UPDATER = - AtomicIntegerFieldUpdater.newUpdater(WebSocketClientHandshaker.class, "forceCloseInit"); - - private volatile boolean forceCloseComplete; - - private final String expectedSubprotocol; - - private volatile String actualSubprotocol; - - protected final HttpHeaders customHeaders; - - private final int maxFramePayloadLength; - - private final boolean absoluteUpgradeUrl; - - /** - * Base constructor - * - * @param uri - * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be - * sent to this URL. - * @param version - * Version of web socket specification to use to connect to the server - * @param subprotocol - * Sub protocol request sent to the server. - * @param customHeaders - * Map of custom headers to add to the client request - * @param maxFramePayloadLength - * Maximum length of a frame's payload - */ - protected WebSocketClientHandshaker(URI uri, WebSocketVersion version, String subprotocol, - HttpHeaders customHeaders, int maxFramePayloadLength) { - this(uri, version, subprotocol, customHeaders, maxFramePayloadLength, DEFAULT_FORCE_CLOSE_TIMEOUT_MILLIS); - } - - /** - * Base constructor - * - * @param uri - * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be - * sent to this URL. - * @param version - * Version of web socket specification to use to connect to the server - * @param subprotocol - * Sub protocol request sent to the server. - * @param customHeaders - * Map of custom headers to add to the client request - * @param maxFramePayloadLength - * Maximum length of a frame's payload - * @param forceCloseTimeoutMillis - * Close the connection if it was not closed by the server after timeout specified - */ - protected WebSocketClientHandshaker(URI uri, WebSocketVersion version, String subprotocol, - HttpHeaders customHeaders, int maxFramePayloadLength, - long forceCloseTimeoutMillis) { - this(uri, version, subprotocol, customHeaders, maxFramePayloadLength, forceCloseTimeoutMillis, false); - } - - /** - * Base constructor - * - * @param uri - * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be - * sent to this URL. - * @param version - * Version of web socket specification to use to connect to the server - * @param subprotocol - * Sub protocol request sent to the server. - * @param customHeaders - * Map of custom headers to add to the client request - * @param maxFramePayloadLength - * Maximum length of a frame's payload - * @param forceCloseTimeoutMillis - * Close the connection if it was not closed by the server after timeout specified - * @param absoluteUpgradeUrl - * Use an absolute url for the Upgrade request, typically when connecting through an HTTP proxy over - * clear HTTP - */ - protected WebSocketClientHandshaker(URI uri, WebSocketVersion version, String subprotocol, - HttpHeaders customHeaders, int maxFramePayloadLength, - long forceCloseTimeoutMillis, boolean absoluteUpgradeUrl) { - this.uri = uri; - this.version = version; - expectedSubprotocol = subprotocol; - this.customHeaders = customHeaders; - this.maxFramePayloadLength = maxFramePayloadLength; - this.forceCloseTimeoutMillis = forceCloseTimeoutMillis; - this.absoluteUpgradeUrl = absoluteUpgradeUrl; - } - - /** - * Returns the URI to the web socket. e.g. "ws://myhost.com/path" - */ - public URI uri() { - return uri; - } - - /** - * Version of the web socket specification that is being used - */ - public WebSocketVersion version() { - return version; - } - - /** - * Returns the max length for any frame's payload - */ - public int maxFramePayloadLength() { - return maxFramePayloadLength; - } - - /** - * Flag to indicate if the opening handshake is complete - */ - public boolean isHandshakeComplete() { - return handshakeComplete; - } - - private void setHandshakeComplete() { - handshakeComplete = true; - } - - /** - * Returns the CSV of requested subprotocol(s) sent to the server as specified in the constructor - */ - public String expectedSubprotocol() { - return expectedSubprotocol; - } - - /** - * Returns the subprotocol response sent by the server. Only available after end of handshake. - * Null if no subprotocol was requested or confirmed by the server. - */ - public String actualSubprotocol() { - return actualSubprotocol; - } - - private void setActualSubprotocol(String actualSubprotocol) { - this.actualSubprotocol = actualSubprotocol; - } - - public long forceCloseTimeoutMillis() { - return forceCloseTimeoutMillis; - } - - /** - * Flag to indicate if the closing handshake was initiated because of timeout. - * For testing only. - */ - protected boolean isForceCloseComplete() { - return forceCloseComplete; - } - - /** - * Sets timeout to close the connection if it was not closed by the server. - * - * @param forceCloseTimeoutMillis - * Close the connection if it was not closed by the server after timeout specified - */ - public WebSocketClientHandshaker setForceCloseTimeoutMillis(long forceCloseTimeoutMillis) { - this.forceCloseTimeoutMillis = forceCloseTimeoutMillis; - return this; - } - - /** - * Begins the opening handshake - * - * @param channel - * Channel - */ - public Future handshake(Channel channel) { - requireNonNull(channel, "channel"); - ChannelPipeline pipeline = channel.pipeline(); - HttpResponseDecoder decoder = pipeline.get(HttpResponseDecoder.class); - if (decoder == null) { - HttpClientCodec codec = pipeline.get(HttpClientCodec.class); - if (codec == null) { - return channel.newFailedFuture(new IllegalStateException("ChannelPipeline does not contain " + - "an HttpResponseDecoder or HttpClientCodec")); - } - } - - FullHttpRequest request = newHandshakeRequest(); - - Promise promise = channel.newPromise(); - channel.writeAndFlush(request).addListener(channel, (ch, future) -> { - if (future.isSuccess()) { - ChannelPipeline p = ch.pipeline(); - ChannelHandlerContext ctx = p.context(HttpRequestEncoder.class); - if (ctx == null) { - ctx = p.context(HttpClientCodec.class); - } - if (ctx == null) { - promise.setFailure(new IllegalStateException("ChannelPipeline does not contain " + - "an HttpRequestEncoder or HttpClientCodec")); - return; - } - p.addAfter(ctx.name(), "ws-encoder", newWebSocketEncoder()); - - promise.setSuccess(null); - } else { - promise.setFailure(future.cause()); - } - }); - return promise.asFuture(); - } - - /** - * Returns a new {@link FullHttpRequest) which will be used for the handshake. - */ - protected abstract FullHttpRequest newHandshakeRequest(); - - /** - * Validates and finishes the opening handshake initiated by {@link #handshake}}. - * - * @param channel - * Channel - * @param response - * HTTP response containing the closing handshake details - */ - public final void finishHandshake(Channel channel, FullHttpResponse response) { - verify(response); - - // Verify the subprotocol that we received from the server. - // This must be one of our expected subprotocols - or null/empty if we didn't want to speak a subprotocol - String receivedProtocol = response.headers().get(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL); - receivedProtocol = receivedProtocol != null ? receivedProtocol.trim() : null; - String expectedProtocol = expectedSubprotocol != null ? expectedSubprotocol : ""; - boolean protocolValid = false; - - if (expectedProtocol.isEmpty() && receivedProtocol == null) { - // No subprotocol required and none received - protocolValid = true; - setActualSubprotocol(expectedSubprotocol); // null or "" - we echo what the user requested - } else if (!expectedProtocol.isEmpty() && receivedProtocol != null && !receivedProtocol.isEmpty()) { - // We require a subprotocol and received one -> verify it - for (String protocol : expectedProtocol.split(",")) { - if (protocol.trim().equals(receivedProtocol)) { - protocolValid = true; - setActualSubprotocol(receivedProtocol); - break; - } - } - } // else mixed cases - which are all errors - - if (!protocolValid) { - throw new WebSocketClientHandshakeException(String.format( - "Invalid subprotocol. Actual: %s. Expected one of: %s", - receivedProtocol, expectedSubprotocol), response); - } - - setHandshakeComplete(); - - final ChannelPipeline p = channel.pipeline(); - // Remove decompressor from pipeline if its in use - HttpContentDecompressor decompressor = p.get(HttpContentDecompressor.class); - if (decompressor != null) { - p.remove(decompressor); - } - - // Remove aggregator if present before - HttpObjectAggregator aggregator = p.get(HttpObjectAggregator.class); - if (aggregator != null) { - p.remove(aggregator); - } - - ChannelHandlerContext ctx = p.context(HttpResponseDecoder.class); - if (ctx == null) { - ctx = p.context(HttpClientCodec.class); - if (ctx == null) { - throw new IllegalStateException("ChannelPipeline does not contain " + - "an HttpRequestEncoder or HttpClientCodec"); - } - final HttpClientCodec codec = (HttpClientCodec) ctx.handler(); - // Remove the encoder part of the codec as the user may start writing frames after this method returns. - codec.removeOutboundHandler(); - - p.addAfter(ctx.name(), "ws-decoder", newWebsocketDecoder()); - - // Delay the removal of the decoder so the user can setup the pipeline if needed to handle - // WebSocketFrame messages. - // See https://github.com/netty/netty/issues/4533 - channel.executor().execute(() -> p.remove(codec)); - } else { - if (p.get(HttpRequestEncoder.class) != null) { - // Remove the encoder part of the codec as the user may start writing frames after this method returns. - p.remove(HttpRequestEncoder.class); - } - final ChannelHandlerContext context = ctx; - p.addAfter(context.name(), "ws-decoder", newWebsocketDecoder()); - - // Delay the removal of the decoder so the user can setup the pipeline if needed to handle - // WebSocketFrame messages. - // See https://github.com/netty/netty/issues/4533 - channel.executor().execute(() -> p.remove(context.handler())); - } - } - - /** - * Process the opening handshake initiated by {@link #handshake}}. - * - * @param channel - * Channel - * @param response - * HTTP response containing the closing handshake details - * @return future - * the {@link Future} which is notified once the handshake completes. - */ - public final Future processHandshake(final Channel channel, HttpResponse response) { - if (response instanceof FullHttpResponse) { - try { - finishHandshake(channel, (FullHttpResponse) response); - return channel.newSucceededFuture(); - } catch (Throwable cause) { - return channel.newFailedFuture(cause); - } - } else { - ChannelPipeline p = channel.pipeline(); - ChannelHandlerContext ctx = p.context(HttpResponseDecoder.class); - if (ctx == null) { - ctx = p.context(HttpClientCodec.class); - if (ctx == null) { - return channel.newFailedFuture(new IllegalStateException("ChannelPipeline does not contain " + - "an HttpResponseDecoder or HttpClientCodec")); - } - } - - Promise promise = channel.newPromise(); - // Add aggregator and ensure we feed the HttpResponse so it is aggregated. A limit of 8192 should be more - // then enough for the websockets handshake payload. - // - // TODO: Make handshake work without HttpObjectAggregator at all. - String aggregatorName = "httpAggregator"; - p.addAfter(ctx.name(), aggregatorName, new HttpObjectAggregator(8192)); - p.addAfter(aggregatorName, "handshaker", new SimpleChannelInboundHandler() { - @Override - protected void messageReceived(ChannelHandlerContext ctx, FullHttpResponse msg) throws Exception { - // Remove ourself and do the actual handshake - ctx.pipeline().remove(this); - try { - finishHandshake(channel, msg); - promise.setSuccess(null); - } catch (Throwable cause) { - promise.setFailure(cause); - } - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - // Remove ourself and fail the handshake promise. - ctx.pipeline().remove(this); - promise.setFailure(cause); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - // Fail promise if Channel was closed - if (!promise.isDone()) { - promise.tryFailure(new ClosedChannelException()); - } - ctx.fireChannelInactive(); - } - }); - try { - ctx.fireChannelRead(ReferenceCountUtil.retain(response)); - } catch (Throwable cause) { - promise.setFailure(cause); - } - return promise.asFuture(); - } - } - - /** - * Verify the {@link FullHttpResponse} and throws a {@link WebSocketHandshakeException} if something is wrong. - */ - protected abstract void verify(FullHttpResponse response); - - /** - * Returns the decoder to use after handshake is complete. - */ - protected abstract WebSocketFrameDecoder newWebsocketDecoder(); - - /** - * Returns the encoder to use after the handshake is complete. - */ - protected abstract WebSocketFrameEncoder newWebSocketEncoder(); - - /** - * Performs the closing handshake. - * - * When called from within a {@link ChannelHandler} you most likely want to use - * {@link #close(ChannelHandlerContext, CloseWebSocketFrame)}. - * - * @param channel - * Channel - * @param frame - * Closing Frame that was received - */ - public Future close(Channel channel, CloseWebSocketFrame frame) { - requireNonNull(channel, "channel"); - return close0(channel, channel, frame); - } - - /** - * Performs the closing handshake - * - * @param ctx - * the {@link ChannelHandlerContext} to use. - * @param frame - * Closing Frame that was received - */ - public Future close(ChannelHandlerContext ctx, CloseWebSocketFrame frame) { - requireNonNull(ctx, "ctx"); - return close0(ctx, ctx.channel(), frame); - } - - private Future close0(final ChannelOutboundInvoker invoker, final Channel channel, - CloseWebSocketFrame frame) { - Future f = invoker.writeAndFlush(frame); - final long forceCloseTimeoutMillis = this.forceCloseTimeoutMillis; - final WebSocketClientHandshaker handshaker = this; - if (forceCloseTimeoutMillis <= 0 || !channel.isActive() || forceCloseInit != 0) { - return f; - } - - f.addListener(future -> { - // If flush operation failed, there is no reason to expect - // a server to receive CloseFrame. Thus this should be handled - // by the application separately. - // Also, close might be called twice from different threads. - if (future.isSuccess() && channel.isActive() && - FORCE_CLOSE_INIT_UPDATER.compareAndSet(handshaker, 0, 1)) { - final Future forceCloseFuture = channel.executor().schedule(() -> { - if (channel.isActive()) { - channel.close(); - forceCloseComplete = true; - } - }, forceCloseTimeoutMillis, TimeUnit.MILLISECONDS); - - channel.closeFuture().addListener(ignore -> { - forceCloseFuture.cancel(); - }); - } - }); - return f; - } - - /** - * Return the constructed raw path for the give {@link URI}. - */ - protected String upgradeUrl(URI wsURL) { - if (absoluteUpgradeUrl) { - return wsURL.toString(); - } - - String path = wsURL.getRawPath(); - path = path == null || path.isEmpty() ? "/" : path; - String query = wsURL.getRawQuery(); - return query != null && !query.isEmpty() ? path + '?' + query : path; - } - - static CharSequence websocketHostValue(URI wsURL) { - int port = wsURL.getPort(); - if (port == -1) { - return wsURL.getHost(); - } - String host = wsURL.getHost(); - String scheme = wsURL.getScheme(); - if (port == HttpScheme.HTTP.port()) { - return HttpScheme.HTTP.name().contentEquals(scheme) - || WebSocketScheme.WS.name().contentEquals(scheme) ? - host : NetUtil.toSocketAddressString(host, port); - } - if (port == HttpScheme.HTTPS.port()) { - return HttpScheme.HTTPS.name().contentEquals(scheme) - || WebSocketScheme.WSS.name().contentEquals(scheme) ? - host : NetUtil.toSocketAddressString(host, port); - } - - // if the port is not standard (80/443) its needed to add the port to the header. - // See https://tools.ietf.org/html/rfc6454#section-6.2 - return NetUtil.toSocketAddressString(host, port); - } - - static CharSequence websocketOriginValue(URI wsURL) { - String scheme = wsURL.getScheme(); - final String schemePrefix; - int port = wsURL.getPort(); - final int defaultPort; - if (WebSocketScheme.WSS.name().contentEquals(scheme) - || HttpScheme.HTTPS.name().contentEquals(scheme) - || (scheme == null && port == WebSocketScheme.WSS.port())) { - - schemePrefix = HTTPS_SCHEME_PREFIX; - defaultPort = WebSocketScheme.WSS.port(); - } else { - schemePrefix = HTTP_SCHEME_PREFIX; - defaultPort = WebSocketScheme.WS.port(); - } - - // Convert uri-host to lower case (by RFC 6454, chapter 4 "Origin of a URI") - String host = wsURL.getHost().toLowerCase(Locale.US); - - if (port != defaultPort && port != -1) { - // if the port is not standard (80/443) its needed to add the port to the header. - // See https://tools.ietf.org/html/rfc6454#section-6.2 - return schemePrefix + NetUtil.toSocketAddressString(host, port); - } - return schemePrefix + host; - } -} 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 deleted file mode 100644 index 71860369bc..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00.java +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpVersion; - -import java.net.URI; -import java.nio.ByteBuffer; -import java.util.concurrent.ThreadLocalRandom; - -/** - *

- * Performs client side opening and closing handshakes for web socket specification version draft-ietf-hybi-thewebsocketprotocol- - * 00 - *

- *

- * A very large portion of this code was taken from the Netty 3.2 HTTP example. - *

- */ -public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker { - - private ByteBuf expectedChallengeResponseBytes; - - /** - * Creates a new instance with the specified destination WebSocket location and version to initiate. - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be - * sent to this URL. - * @param version - * Version of web socket specification to use to connect to the server - * @param subprotocol - * Sub protocol request sent to the server. - * @param customHeaders - * Map of custom headers to add to the client request - * @param maxFramePayloadLength - * Maximum length of a frame's payload - */ - public WebSocketClientHandshaker00(URI webSocketURL, WebSocketVersion version, String subprotocol, - HttpHeaders customHeaders, int maxFramePayloadLength) { - this(webSocketURL, version, subprotocol, customHeaders, maxFramePayloadLength, - DEFAULT_FORCE_CLOSE_TIMEOUT_MILLIS); - } - - /** - * Creates a new instance with the specified destination WebSocket location and version to initiate. - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be - * sent to this URL. - * @param version - * Version of web socket specification to use to connect to the server - * @param subprotocol - * Sub protocol request sent to the server. - * @param customHeaders - * Map of custom headers to add to the client request - * @param maxFramePayloadLength - * Maximum length of a frame's payload - * @param forceCloseTimeoutMillis - * Close the connection if it was not closed by the server after timeout specified - */ - public WebSocketClientHandshaker00(URI webSocketURL, WebSocketVersion version, String subprotocol, - HttpHeaders customHeaders, int maxFramePayloadLength, - long forceCloseTimeoutMillis) { - this(webSocketURL, version, subprotocol, customHeaders, maxFramePayloadLength, forceCloseTimeoutMillis, false); - } - - /** - * Creates a new instance with the specified destination WebSocket location and version to initiate. - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be - * sent to this URL. - * @param version - * Version of web socket specification to use to connect to the server - * @param subprotocol - * Sub protocol request sent to the server. - * @param customHeaders - * Map of custom headers to add to the client request - * @param maxFramePayloadLength - * Maximum length of a frame's payload - * @param forceCloseTimeoutMillis - * Close the connection if it was not closed by the server after timeout specified - * @param absoluteUpgradeUrl - * Use an absolute url for the Upgrade request, typically when connecting through an HTTP proxy over - * clear HTTP - */ - WebSocketClientHandshaker00(URI webSocketURL, WebSocketVersion version, String subprotocol, - HttpHeaders customHeaders, int maxFramePayloadLength, - long forceCloseTimeoutMillis, boolean absoluteUpgradeUrl) { - super(webSocketURL, version, subprotocol, customHeaders, maxFramePayloadLength, forceCloseTimeoutMillis, - absoluteUpgradeUrl); - } - - /** - *

- * Sends the opening request to the server: - *

- * - *
-     * GET /demo HTTP/1.1
-     * Upgrade: WebSocket
-     * Connection: Upgrade
-     * Host: example.com
-     * Origin: http://example.com
-     * Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5
-     * Sec-WebSocket-Key2: 12998 5 Y3 1  .P00
-     *
-     * ^n:ds[4U
-     * 
- * - */ - @Override - protected FullHttpRequest newHandshakeRequest() { - // Make keys - int spaces1 = ThreadLocalRandom.current().nextInt(1, 13); - int spaces2 = ThreadLocalRandom.current().nextInt(1, 13); - - int max1 = Integer.MAX_VALUE / spaces1; - int max2 = Integer.MAX_VALUE / spaces2; - - int number1 = ThreadLocalRandom.current().nextInt(0, max1); - int number2 = ThreadLocalRandom.current().nextInt(0, max2); - - int product1 = number1 * spaces1; - int product2 = number2 * spaces2; - - String key1 = Integer.toString(product1); - String key2 = Integer.toString(product2); - - key1 = insertRandomCharacters(key1); - key2 = insertRandomCharacters(key2); - - key1 = insertSpaces(key1, spaces1); - key2 = insertSpaces(key2, spaces2); - - byte[] key3 = WebSocketUtil.randomBytes(8); - - ByteBuffer buffer = ByteBuffer.allocate(4); - buffer.putInt(number1); - byte[] number1Array = buffer.array(); - buffer = ByteBuffer.allocate(4); - buffer.putInt(number2); - byte[] number2Array = buffer.array(); - - byte[] challenge = new byte[16]; - System.arraycopy(number1Array, 0, challenge, 0, 4); - System.arraycopy(number2Array, 0, challenge, 4, 4); - System.arraycopy(key3, 0, challenge, 8, 8); - expectedChallengeResponseBytes = Unpooled.wrappedBuffer(WebSocketUtil.md5(challenge)); - - URI wsURL = uri(); - - // Format request - FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, upgradeUrl(wsURL), - Unpooled.wrappedBuffer(key3)); - HttpHeaders headers = request.headers(); - - if (customHeaders != null) { - headers.add(customHeaders); - } - - headers.set(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET) - .set(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE) - .set(HttpHeaderNames.HOST, websocketHostValue(wsURL)) - .set(HttpHeaderNames.SEC_WEBSOCKET_KEY1, key1) - .set(HttpHeaderNames.SEC_WEBSOCKET_KEY2, key2); - - if (!headers.contains(HttpHeaderNames.ORIGIN)) { - headers.set(HttpHeaderNames.ORIGIN, websocketOriginValue(wsURL)); - } - - 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: https://www.ietf.org/mail-archive/web/hybi/current/msg02149.html - headers.set(HttpHeaderNames.CONTENT_LENGTH, key3.length); - return request; - } - - /** - *

- * Process server response: - *

- * - *
-     * HTTP/1.1 101 WebSocket Protocol Handshake
-     * Upgrade: WebSocket
-     * Connection: Upgrade
-     * Sec-WebSocket-Origin: http://example.com
-     * Sec-WebSocket-Location: ws://example.com/demo
-     * Sec-WebSocket-Protocol: sample
-     *
-     * 8jKS'y:G*Co,Wxa-
-     * 
- * - * @param response - * HTTP response returned from the server for the request sent by beginOpeningHandshake00(). - * @throws WebSocketHandshakeException If the handshake or challenge is invalid. - */ - @Override - protected void verify(FullHttpResponse response) { - HttpResponseStatus status = response.status(); - if (!HttpResponseStatus.SWITCHING_PROTOCOLS.equals(status)) { - throw new WebSocketClientHandshakeException("Invalid handshake response getStatus: " + status, response); - } - - HttpHeaders headers = response.headers(); - CharSequence upgrade = headers.get(HttpHeaderNames.UPGRADE); - if (!HttpHeaderValues.WEBSOCKET.contentEqualsIgnoreCase(upgrade)) { - throw new WebSocketClientHandshakeException("Invalid handshake response upgrade: " + upgrade, response); - } - - if (!headers.containsValue(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE, true)) { - throw new WebSocketClientHandshakeException("Invalid handshake response connection: " - + headers.get(HttpHeaderNames.CONNECTION), response); - } - - ByteBuf challenge = response.content(); - if (!challenge.equals(expectedChallengeResponseBytes)) { - throw new WebSocketClientHandshakeException("Invalid challenge", response); - } - } - - private static String insertRandomCharacters(String key) { - int count = ThreadLocalRandom.current().nextInt(1, 13); - - char[] randomChars = new char[count]; - int randCount = 0; - while (randCount < count) { - int rand = ThreadLocalRandom.current().nextInt(0x22, 0x7e); - if (rand < 0x2f || 0x3a < rand) { - randomChars[randCount] = (char) rand; - randCount += 1; - } - } - - for (int i = 0; i < count; i++) { - int split = ThreadLocalRandom.current().nextInt(0, key.length() + 1); - String part1 = key.substring(0, split); - String part2 = key.substring(split); - key = part1 + randomChars[i] + part2; - } - - return key; - } - - private static String insertSpaces(String key, int spaces) { - for (int i = 0; i < spaces; i++) { - int split = ThreadLocalRandom.current().nextInt(1, key.length()); - String part1 = key.substring(0, split); - String part2 = key.substring(split); - key = part1 + ' ' + part2; - } - - return key; - } - - @Override - protected WebSocketFrameDecoder newWebsocketDecoder() { - return new WebSocket00FrameDecoder(maxFramePayloadLength()); - } - - @Override - protected WebSocketFrameEncoder newWebSocketEncoder() { - return new WebSocket00FrameEncoder(); - } - - @Override - public WebSocketClientHandshaker00 setForceCloseTimeoutMillis(long forceCloseTimeoutMillis) { - super.setForceCloseTimeoutMillis(forceCloseTimeoutMillis); - return this; - } - -} 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 deleted file mode 100644 index 6b7587ebf4..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker07.java +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.buffer.Unpooled; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.util.CharsetUtil; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.net.URI; - -/** - *

- * Performs client side opening and closing handshakes for web socket specification version draft-ietf-hybi-thewebsocketprotocol- - * 10 - *

- */ -public class WebSocketClientHandshaker07 extends WebSocketClientHandshaker { - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(WebSocketClientHandshaker07.class); - public static final String MAGIC_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - - private String expectedChallengeResponseString; - - private final boolean allowExtensions; - private final boolean performMasking; - private final boolean allowMaskMismatch; - - /** - * Creates a new instance. - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be - * sent to this URL. - * @param version - * Version of web socket specification to use to connect to the server - * @param subprotocol - * Sub protocol request sent to the server. - * @param allowExtensions - * Allow extensions to be used in the reserved bits of the web socket frame - * @param customHeaders - * Map of custom headers to add to the client request - * @param maxFramePayloadLength - * Maximum length of a frame's payload - */ - public WebSocketClientHandshaker07(URI webSocketURL, WebSocketVersion version, String subprotocol, - boolean allowExtensions, HttpHeaders customHeaders, int maxFramePayloadLength) { - this(webSocketURL, version, subprotocol, allowExtensions, customHeaders, maxFramePayloadLength, true, false); - } - - /** - * Creates a new instance. - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be - * sent to this URL. - * @param version - * Version of web socket specification to use to connect to the server - * @param subprotocol - * Sub protocol request sent to the server. - * @param allowExtensions - * Allow extensions to be used in the reserved bits of the web socket frame - * @param customHeaders - * Map of custom headers to add to the client request - * @param maxFramePayloadLength - * Maximum length of a frame's payload - * @param performMasking - * Whether to mask all written websocket frames. This must be set to true in order to be fully compatible - * with the websocket specifications. Client applications that communicate with a non-standard server - * which doesn't require masking might set this to false to achieve a higher performance. - * @param allowMaskMismatch - * When set to true, frames which are not masked properly according to the standard will still be - * accepted. - */ - public WebSocketClientHandshaker07(URI webSocketURL, WebSocketVersion version, String subprotocol, - boolean allowExtensions, HttpHeaders customHeaders, int maxFramePayloadLength, - boolean performMasking, boolean allowMaskMismatch) { - this(webSocketURL, version, subprotocol, allowExtensions, customHeaders, maxFramePayloadLength, performMasking, - allowMaskMismatch, DEFAULT_FORCE_CLOSE_TIMEOUT_MILLIS); - } - - /** - * Creates a new instance. - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be - * sent to this URL. - * @param version - * Version of web socket specification to use to connect to the server - * @param subprotocol - * Sub protocol request sent to the server. - * @param allowExtensions - * Allow extensions to be used in the reserved bits of the web socket frame - * @param customHeaders - * Map of custom headers to add to the client request - * @param maxFramePayloadLength - * Maximum length of a frame's payload - * @param performMasking - * Whether to mask all written websocket frames. This must be set to true in order to be fully compatible - * with the websocket specifications. Client applications that communicate with a non-standard server - * which doesn't require masking might set this to false to achieve a higher performance. - * @param allowMaskMismatch - * When set to true, frames which are not masked properly according to the standard will still be - * accepted - * @param forceCloseTimeoutMillis - * Close the connection if it was not closed by the server after timeout specified. - */ - public WebSocketClientHandshaker07(URI webSocketURL, WebSocketVersion version, String subprotocol, - boolean allowExtensions, HttpHeaders customHeaders, int maxFramePayloadLength, - boolean performMasking, boolean allowMaskMismatch, long forceCloseTimeoutMillis) { - this(webSocketURL, version, subprotocol, allowExtensions, customHeaders, maxFramePayloadLength, performMasking, - allowMaskMismatch, forceCloseTimeoutMillis, false); - } - - /** - * Creates a new instance. - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be - * sent to this URL. - * @param version - * Version of web socket specification to use to connect to the server - * @param subprotocol - * Sub protocol request sent to the server. - * @param allowExtensions - * Allow extensions to be used in the reserved bits of the web socket frame - * @param customHeaders - * Map of custom headers to add to the client request - * @param maxFramePayloadLength - * Maximum length of a frame's payload - * @param performMasking - * Whether to mask all written websocket frames. This must be set to true in order to be fully compatible - * with the websocket specifications. Client applications that communicate with a non-standard server - * which doesn't require masking might set this to false to achieve a higher performance. - * @param allowMaskMismatch - * When set to true, frames which are not masked properly according to the standard will still be - * accepted - * @param forceCloseTimeoutMillis - * Close the connection if it was not closed by the server after timeout specified. - * @param absoluteUpgradeUrl - * Use an absolute url for the Upgrade request, typically when connecting through an HTTP proxy over - * clear HTTP - */ - WebSocketClientHandshaker07(URI webSocketURL, WebSocketVersion version, String subprotocol, - boolean allowExtensions, HttpHeaders customHeaders, int maxFramePayloadLength, - boolean performMasking, boolean allowMaskMismatch, long forceCloseTimeoutMillis, - boolean absoluteUpgradeUrl) { - super(webSocketURL, version, subprotocol, customHeaders, maxFramePayloadLength, forceCloseTimeoutMillis, - absoluteUpgradeUrl); - this.allowExtensions = allowExtensions; - this.performMasking = performMasking; - this.allowMaskMismatch = allowMaskMismatch; - } - - /** - * /** - *

- * Sends the opening request to the server: - *

- * - *
-     * GET /chat HTTP/1.1
-     * Host: server.example.com
-     * Upgrade: websocket
-     * Connection: Upgrade
-     * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
-     * Sec-WebSocket-Origin: http://example.com
-     * Sec-WebSocket-Protocol: chat, superchat
-     * Sec-WebSocket-Version: 7
-     * 
- * - */ - @Override - protected FullHttpRequest newHandshakeRequest() { - URI wsURL = uri(); - - // Get 16 bit nonce and base 64 encode it - byte[] nonce = WebSocketUtil.randomBytes(16); - String key = WebSocketUtil.base64(nonce); - - String acceptSeed = key + MAGIC_GUID; - byte[] sha1 = WebSocketUtil.sha1(acceptSeed.getBytes(CharsetUtil.US_ASCII)); - expectedChallengeResponseString = WebSocketUtil.base64(sha1); - - if (logger.isDebugEnabled()) { - logger.debug( - "WebSocket version 07 client handshake key: {}, expected response: {}", - key, expectedChallengeResponseString); - } - - // Format request - FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, upgradeUrl(wsURL), - Unpooled.EMPTY_BUFFER); - HttpHeaders headers = request.headers(); - - if (customHeaders != null) { - headers.add(customHeaders); - if (!headers.contains(HttpHeaderNames.HOST)) { - // Only add HOST header if customHeaders did not contain it. - // - // See https://github.com/netty/netty/issues/10101 - headers.set(HttpHeaderNames.HOST, websocketHostValue(wsURL)); - } - } else { - headers.set(HttpHeaderNames.HOST, websocketHostValue(wsURL)); - } - - headers.set(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET) - .set(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE) - .set(HttpHeaderNames.SEC_WEBSOCKET_KEY, key); - - if (!headers.contains(HttpHeaderNames.SEC_WEBSOCKET_ORIGIN)) { - headers.set(HttpHeaderNames.SEC_WEBSOCKET_ORIGIN, websocketOriginValue(wsURL)); - } - - String expectedSubprotocol = expectedSubprotocol(); - if (expectedSubprotocol != null && !expectedSubprotocol.isEmpty()) { - headers.set(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol); - } - - headers.set(HttpHeaderNames.SEC_WEBSOCKET_VERSION, version().toAsciiString()); - return request; - } - - /** - *

- * Process server response: - *

- * - *
-     * HTTP/1.1 101 Switching Protocols
-     * Upgrade: websocket
-     * Connection: Upgrade
-     * Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
-     * Sec-WebSocket-Protocol: chat
-     * 
- * - * @param response - * HTTP response returned from the server for the request sent by beginOpeningHandshake00(). - * @throws WebSocketHandshakeException If the handshake or challenge is invalid. - */ - @Override - protected void verify(FullHttpResponse response) { - HttpResponseStatus status = response.status(); - if (!HttpResponseStatus.SWITCHING_PROTOCOLS.equals(status)) { - throw new WebSocketClientHandshakeException("Invalid handshake response getStatus: " + status, response); - } - - HttpHeaders headers = response.headers(); - CharSequence upgrade = headers.get(HttpHeaderNames.UPGRADE); - if (!HttpHeaderValues.WEBSOCKET.contentEqualsIgnoreCase(upgrade)) { - throw new WebSocketClientHandshakeException("Invalid handshake response upgrade: " + upgrade, response); - } - - if (!headers.containsValue(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE, true)) { - throw new WebSocketClientHandshakeException("Invalid handshake response connection: " - + headers.get(HttpHeaderNames.CONNECTION), response); - } - - CharSequence accept = headers.get(HttpHeaderNames.SEC_WEBSOCKET_ACCEPT); - if (accept == null || !accept.equals(expectedChallengeResponseString)) { - throw new WebSocketClientHandshakeException(String.format( - "Invalid challenge. Actual: %s. Expected: %s", accept, expectedChallengeResponseString), response); - } - } - - @Override - protected WebSocketFrameDecoder newWebsocketDecoder() { - return new WebSocket07FrameDecoder(false, allowExtensions, maxFramePayloadLength(), allowMaskMismatch); - } - - @Override - protected WebSocketFrameEncoder newWebSocketEncoder() { - return new WebSocket07FrameEncoder(performMasking); - } - - @Override - public WebSocketClientHandshaker07 setForceCloseTimeoutMillis(long forceCloseTimeoutMillis) { - super.setForceCloseTimeoutMillis(forceCloseTimeoutMillis); - return this; - } - -} 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 deleted file mode 100644 index 50dd4599fc..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker08.java +++ /dev/null @@ -1,308 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.buffer.Unpooled; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.util.CharsetUtil; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.net.URI; - -/** - *

- * Performs client side opening and closing handshakes for web socket specification version draft-ietf-hybi-thewebsocketprotocol- - * 10 - *

- */ -public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker { - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(WebSocketClientHandshaker08.class); - - public static final String MAGIC_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - - private String expectedChallengeResponseString; - - private final boolean allowExtensions; - private final boolean performMasking; - private final boolean allowMaskMismatch; - - /** - * Creates a new instance. - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be - * sent to this URL. - * @param version - * Version of web socket specification to use to connect to the server - * @param subprotocol - * Sub protocol request sent to the server. - * @param allowExtensions - * Allow extensions to be used in the reserved bits of the web socket frame - * @param customHeaders - * Map of custom headers to add to the client request - * @param maxFramePayloadLength - * Maximum length of a frame's payload - */ - public WebSocketClientHandshaker08(URI webSocketURL, WebSocketVersion version, String subprotocol, - boolean allowExtensions, HttpHeaders customHeaders, int maxFramePayloadLength) { - this(webSocketURL, version, subprotocol, allowExtensions, customHeaders, maxFramePayloadLength, true, - false, DEFAULT_FORCE_CLOSE_TIMEOUT_MILLIS); - } - - /** - * Creates a new instance. - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be - * sent to this URL. - * @param version - * Version of web socket specification to use to connect to the server - * @param subprotocol - * Sub protocol request sent to the server. - * @param allowExtensions - * Allow extensions to be used in the reserved bits of the web socket frame - * @param customHeaders - * Map of custom headers to add to the client request - * @param maxFramePayloadLength - * Maximum length of a frame's payload - * @param performMasking - * Whether to mask all written websocket frames. This must be set to true in order to be fully compatible - * with the websocket specifications. Client applications that communicate with a non-standard server - * which doesn't require masking might set this to false to achieve a higher performance. - * @param allowMaskMismatch - * When set to true, frames which are not masked properly according to the standard will still be - * accepted - */ - public WebSocketClientHandshaker08(URI webSocketURL, WebSocketVersion version, String subprotocol, - boolean allowExtensions, HttpHeaders customHeaders, int maxFramePayloadLength, - boolean performMasking, boolean allowMaskMismatch) { - this(webSocketURL, version, subprotocol, allowExtensions, customHeaders, maxFramePayloadLength, performMasking, - allowMaskMismatch, DEFAULT_FORCE_CLOSE_TIMEOUT_MILLIS); - } - - /** - * Creates a new instance. - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be - * sent to this URL. - * @param version - * Version of web socket specification to use to connect to the server - * @param subprotocol - * Sub protocol request sent to the server. - * @param allowExtensions - * Allow extensions to be used in the reserved bits of the web socket frame - * @param customHeaders - * Map of custom headers to add to the client request - * @param maxFramePayloadLength - * Maximum length of a frame's payload - * @param performMasking - * Whether to mask all written websocket frames. This must be set to true in order to be fully compatible - * with the websocket specifications. Client applications that communicate with a non-standard server - * which doesn't require masking might set this to false to achieve a higher performance. - * @param allowMaskMismatch - * When set to true, frames which are not masked properly according to the standard will still be - * accepted - * @param forceCloseTimeoutMillis - * Close the connection if it was not closed by the server after timeout specified. - */ - public WebSocketClientHandshaker08(URI webSocketURL, WebSocketVersion version, String subprotocol, - boolean allowExtensions, HttpHeaders customHeaders, int maxFramePayloadLength, - boolean performMasking, boolean allowMaskMismatch, long forceCloseTimeoutMillis) { - this(webSocketURL, version, subprotocol, allowExtensions, customHeaders, maxFramePayloadLength, performMasking, - allowMaskMismatch, forceCloseTimeoutMillis, false); - } - - /** - * Creates a new instance. - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be - * sent to this URL. - * @param version - * Version of web socket specification to use to connect to the server - * @param subprotocol - * Sub protocol request sent to the server. - * @param allowExtensions - * Allow extensions to be used in the reserved bits of the web socket frame - * @param customHeaders - * Map of custom headers to add to the client request - * @param maxFramePayloadLength - * Maximum length of a frame's payload - * @param performMasking - * Whether to mask all written websocket frames. This must be set to true in order to be fully compatible - * with the websocket specifications. Client applications that communicate with a non-standard server - * which doesn't require masking might set this to false to achieve a higher performance. - * @param allowMaskMismatch - * When set to true, frames which are not masked properly according to the standard will still be - * accepted - * @param forceCloseTimeoutMillis - * Close the connection if it was not closed by the server after timeout specified. - * @param absoluteUpgradeUrl - * Use an absolute url for the Upgrade request, typically when connecting through an HTTP proxy over - * clear HTTP - */ - WebSocketClientHandshaker08(URI webSocketURL, WebSocketVersion version, String subprotocol, - boolean allowExtensions, HttpHeaders customHeaders, int maxFramePayloadLength, - boolean performMasking, boolean allowMaskMismatch, long forceCloseTimeoutMillis, - boolean absoluteUpgradeUrl) { - super(webSocketURL, version, subprotocol, customHeaders, maxFramePayloadLength, forceCloseTimeoutMillis, - absoluteUpgradeUrl); - this.allowExtensions = allowExtensions; - this.performMasking = performMasking; - this.allowMaskMismatch = allowMaskMismatch; - } - - /** - * /** - *

- * Sends the opening request to the server: - *

- * - *
-     * GET /chat HTTP/1.1
-     * Host: server.example.com
-     * Upgrade: websocket
-     * Connection: Upgrade
-     * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
-     * Sec-WebSocket-Origin: http://example.com
-     * Sec-WebSocket-Protocol: chat, superchat
-     * Sec-WebSocket-Version: 8
-     * 
- * - */ - @Override - protected FullHttpRequest newHandshakeRequest() { - URI wsURL = uri(); - - // Get 16 bit nonce and base 64 encode it - byte[] nonce = WebSocketUtil.randomBytes(16); - String key = WebSocketUtil.base64(nonce); - - String acceptSeed = key + MAGIC_GUID; - byte[] sha1 = WebSocketUtil.sha1(acceptSeed.getBytes(CharsetUtil.US_ASCII)); - expectedChallengeResponseString = WebSocketUtil.base64(sha1); - - if (logger.isDebugEnabled()) { - logger.debug( - "WebSocket version 08 client handshake key: {}, expected response: {}", - key, expectedChallengeResponseString); - } - - // Format request - FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, upgradeUrl(wsURL), - Unpooled.EMPTY_BUFFER); - HttpHeaders headers = request.headers(); - - if (customHeaders != null) { - headers.add(customHeaders); - if (!headers.contains(HttpHeaderNames.HOST)) { - // Only add HOST header if customHeaders did not contain it. - // - // See https://github.com/netty/netty/issues/10101 - headers.set(HttpHeaderNames.HOST, websocketHostValue(wsURL)); - } - } else { - headers.set(HttpHeaderNames.HOST, websocketHostValue(wsURL)); - } - - headers.set(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET) - .set(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE) - .set(HttpHeaderNames.SEC_WEBSOCKET_KEY, key); - - if (!headers.contains(HttpHeaderNames.SEC_WEBSOCKET_ORIGIN)) { - headers.set(HttpHeaderNames.SEC_WEBSOCKET_ORIGIN, websocketOriginValue(wsURL)); - } - - String expectedSubprotocol = expectedSubprotocol(); - if (expectedSubprotocol != null && !expectedSubprotocol.isEmpty()) { - headers.set(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol); - } - - headers.set(HttpHeaderNames.SEC_WEBSOCKET_VERSION, version().toAsciiString()); - return request; - } - - /** - *

- * Process server response: - *

- * - *
-     * HTTP/1.1 101 Switching Protocols
-     * Upgrade: websocket
-     * Connection: Upgrade
-     * Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
-     * Sec-WebSocket-Protocol: chat
-     * 
- * - * @param response - * HTTP response returned from the server for the request sent by beginOpeningHandshake00(). - * @throws WebSocketHandshakeException If the handshake or challenge is invalid. - */ - @Override - protected void verify(FullHttpResponse response) { - HttpResponseStatus status = response.status(); - if (!HttpResponseStatus.SWITCHING_PROTOCOLS.equals(status)) { - throw new WebSocketClientHandshakeException("Invalid handshake response getStatus: " + status, response); - } - - HttpHeaders headers = response.headers(); - CharSequence upgrade = headers.get(HttpHeaderNames.UPGRADE); - if (!HttpHeaderValues.WEBSOCKET.contentEqualsIgnoreCase(upgrade)) { - throw new WebSocketClientHandshakeException("Invalid handshake response upgrade: " + upgrade, response); - } - - if (!headers.containsValue(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE, true)) { - throw new WebSocketClientHandshakeException("Invalid handshake response connection: " - + headers.get(HttpHeaderNames.CONNECTION), response); - } - - CharSequence accept = headers.get(HttpHeaderNames.SEC_WEBSOCKET_ACCEPT); - if (accept == null || !accept.equals(expectedChallengeResponseString)) { - throw new WebSocketClientHandshakeException(String.format( - "Invalid challenge. Actual: %s. Expected: %s", accept, expectedChallengeResponseString), response); - } - } - - @Override - protected WebSocketFrameDecoder newWebsocketDecoder() { - return new WebSocket08FrameDecoder(false, allowExtensions, maxFramePayloadLength(), allowMaskMismatch); - } - - @Override - protected WebSocketFrameEncoder newWebSocketEncoder() { - return new WebSocket08FrameEncoder(performMasking); - } - - @Override - public WebSocketClientHandshaker08 setForceCloseTimeoutMillis(long forceCloseTimeoutMillis) { - super.setForceCloseTimeoutMillis(forceCloseTimeoutMillis); - return this; - } - -} 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 deleted file mode 100644 index 70805611fa..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker13.java +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.buffer.Unpooled; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.util.CharsetUtil; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.net.URI; - -/** - *

- * Performs client side opening and closing handshakes for web socket specification version draft-ietf-hybi-thewebsocketprotocol- - * 17 - *

- */ -public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker { - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(WebSocketClientHandshaker13.class); - - public static final String MAGIC_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - - private String expectedChallengeResponseString; - - private final boolean allowExtensions; - private final boolean performMasking; - private final boolean allowMaskMismatch; - - /** - * Creates a new instance. - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be - * sent to this URL. - * @param version - * Version of web socket specification to use to connect to the server - * @param subprotocol - * Sub protocol request sent to the server. - * @param allowExtensions - * Allow extensions to be used in the reserved bits of the web socket frame - * @param customHeaders - * Map of custom headers to add to the client request - * @param maxFramePayloadLength - * Maximum length of a frame's payload - */ - public WebSocketClientHandshaker13(URI webSocketURL, WebSocketVersion version, String subprotocol, - boolean allowExtensions, HttpHeaders customHeaders, int maxFramePayloadLength) { - this(webSocketURL, version, subprotocol, allowExtensions, customHeaders, maxFramePayloadLength, - true, false); - } - - /** - * Creates a new instance. - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be - * sent to this URL. - * @param version - * Version of web socket specification to use to connect to the server - * @param subprotocol - * Sub protocol request sent to the server. - * @param allowExtensions - * Allow extensions to be used in the reserved bits of the web socket frame - * @param customHeaders - * Map of custom headers to add to the client request - * @param maxFramePayloadLength - * Maximum length of a frame's payload - * @param performMasking - * Whether to mask all written websocket frames. This must be set to true in order to be fully compatible - * with the websocket specifications. Client applications that communicate with a non-standard server - * which doesn't require masking might set this to false to achieve a higher performance. - * @param allowMaskMismatch - * When set to true, frames which are not masked properly according to the standard will still be - * accepted. - */ - public WebSocketClientHandshaker13(URI webSocketURL, WebSocketVersion version, String subprotocol, - boolean allowExtensions, HttpHeaders customHeaders, int maxFramePayloadLength, - boolean performMasking, boolean allowMaskMismatch) { - this(webSocketURL, version, subprotocol, allowExtensions, customHeaders, maxFramePayloadLength, - performMasking, allowMaskMismatch, DEFAULT_FORCE_CLOSE_TIMEOUT_MILLIS); - } - - /** - * Creates a new instance. - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be - * sent to this URL. - * @param version - * Version of web socket specification to use to connect to the server - * @param subprotocol - * Sub protocol request sent to the server. - * @param allowExtensions - * Allow extensions to be used in the reserved bits of the web socket frame - * @param customHeaders - * Map of custom headers to add to the client request - * @param maxFramePayloadLength - * Maximum length of a frame's payload - * @param performMasking - * Whether to mask all written websocket frames. This must be set to true in order to be fully compatible - * with the websocket specifications. Client applications that communicate with a non-standard server - * which doesn't require masking might set this to false to achieve a higher performance. - * @param allowMaskMismatch - * When set to true, frames which are not masked properly according to the standard will still be - * accepted - * @param forceCloseTimeoutMillis - * Close the connection if it was not closed by the server after timeout specified. - */ - public WebSocketClientHandshaker13(URI webSocketURL, WebSocketVersion version, String subprotocol, - boolean allowExtensions, HttpHeaders customHeaders, int maxFramePayloadLength, - boolean performMasking, boolean allowMaskMismatch, - long forceCloseTimeoutMillis) { - this(webSocketURL, version, subprotocol, allowExtensions, customHeaders, maxFramePayloadLength, performMasking, - allowMaskMismatch, forceCloseTimeoutMillis, false); - } - - /** - * Creates a new instance. - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be - * sent to this URL. - * @param version - * Version of web socket specification to use to connect to the server - * @param subprotocol - * Sub protocol request sent to the server. - * @param allowExtensions - * Allow extensions to be used in the reserved bits of the web socket frame - * @param customHeaders - * Map of custom headers to add to the client request - * @param maxFramePayloadLength - * Maximum length of a frame's payload - * @param performMasking - * Whether to mask all written websocket frames. This must be set to true in order to be fully compatible - * with the websocket specifications. Client applications that communicate with a non-standard server - * which doesn't require masking might set this to false to achieve a higher performance. - * @param allowMaskMismatch - * When set to true, frames which are not masked properly according to the standard will still be - * accepted - * @param forceCloseTimeoutMillis - * Close the connection if it was not closed by the server after timeout specified. - * @param absoluteUpgradeUrl - * Use an absolute url for the Upgrade request, typically when connecting through an HTTP proxy over - * clear HTTP - */ - WebSocketClientHandshaker13(URI webSocketURL, WebSocketVersion version, String subprotocol, - boolean allowExtensions, HttpHeaders customHeaders, int maxFramePayloadLength, - boolean performMasking, boolean allowMaskMismatch, - long forceCloseTimeoutMillis, boolean absoluteUpgradeUrl) { - super(webSocketURL, version, subprotocol, customHeaders, maxFramePayloadLength, forceCloseTimeoutMillis, - absoluteUpgradeUrl); - this.allowExtensions = allowExtensions; - this.performMasking = performMasking; - this.allowMaskMismatch = allowMaskMismatch; - } - - /** - * /** - *

- * Sends the opening request to the server: - *

- * - *
-     * GET /chat HTTP/1.1
-     * Host: server.example.com
-     * Upgrade: websocket
-     * Connection: Upgrade
-     * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
-     * Origin: http://example.com
-     * Sec-WebSocket-Protocol: chat, superchat
-     * Sec-WebSocket-Version: 13
-     * 
- * - */ - @Override - protected FullHttpRequest newHandshakeRequest() { - URI wsURL = uri(); - - // Get 16 bit nonce and base 64 encode it - byte[] nonce = WebSocketUtil.randomBytes(16); - String key = WebSocketUtil.base64(nonce); - - String acceptSeed = key + MAGIC_GUID; - byte[] sha1 = WebSocketUtil.sha1(acceptSeed.getBytes(CharsetUtil.US_ASCII)); - expectedChallengeResponseString = WebSocketUtil.base64(sha1); - - if (logger.isDebugEnabled()) { - logger.debug( - "WebSocket version 13 client handshake key: {}, expected response: {}", - key, expectedChallengeResponseString); - } - - // Format request - FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, upgradeUrl(wsURL), - Unpooled.EMPTY_BUFFER); - HttpHeaders headers = request.headers(); - - if (customHeaders != null) { - headers.add(customHeaders); - if (!headers.contains(HttpHeaderNames.HOST)) { - // Only add HOST header if customHeaders did not contain it. - // - // See https://github.com/netty/netty/issues/10101 - headers.set(HttpHeaderNames.HOST, websocketHostValue(wsURL)); - } - } else { - headers.set(HttpHeaderNames.HOST, websocketHostValue(wsURL)); - } - - headers.set(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET) - .set(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE) - .set(HttpHeaderNames.SEC_WEBSOCKET_KEY, key); - - if (!headers.contains(HttpHeaderNames.ORIGIN)) { - headers.set(HttpHeaderNames.ORIGIN, websocketOriginValue(wsURL)); - } - - String expectedSubprotocol = expectedSubprotocol(); - if (expectedSubprotocol != null && !expectedSubprotocol.isEmpty()) { - headers.set(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol); - } - - headers.set(HttpHeaderNames.SEC_WEBSOCKET_VERSION, version().toAsciiString()); - return request; - } - - /** - *

- * Process server response: - *

- * - *
-     * HTTP/1.1 101 Switching Protocols
-     * Upgrade: websocket
-     * Connection: Upgrade
-     * Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
-     * Sec-WebSocket-Protocol: chat
-     * 
- * - * @param response - * HTTP response returned from the server for the request sent by beginOpeningHandshake00(). - * @throws WebSocketHandshakeException if handshake response is invalid. - */ - @Override - protected void verify(FullHttpResponse response) { - HttpResponseStatus status = response.status(); - if (!HttpResponseStatus.SWITCHING_PROTOCOLS.equals(status)) { - throw new WebSocketClientHandshakeException("Invalid handshake response getStatus: " + status, response); - } - - HttpHeaders headers = response.headers(); - CharSequence upgrade = headers.get(HttpHeaderNames.UPGRADE); - if (!HttpHeaderValues.WEBSOCKET.contentEqualsIgnoreCase(upgrade)) { - throw new WebSocketClientHandshakeException("Invalid handshake response upgrade: " + upgrade, response); - } - - if (!headers.containsValue(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE, true)) { - throw new WebSocketClientHandshakeException("Invalid handshake response connection: " - + headers.get(HttpHeaderNames.CONNECTION), response); - } - - CharSequence accept = headers.get(HttpHeaderNames.SEC_WEBSOCKET_ACCEPT); - if (accept == null || !accept.equals(expectedChallengeResponseString)) { - throw new WebSocketClientHandshakeException(String.format( - "Invalid challenge. Actual: %s. Expected: %s", accept, expectedChallengeResponseString), response); - } - } - - @Override - protected WebSocketFrameDecoder newWebsocketDecoder() { - return new WebSocket13FrameDecoder(false, allowExtensions, maxFramePayloadLength(), allowMaskMismatch); - } - - @Override - protected WebSocketFrameEncoder newWebSocketEncoder() { - return new WebSocket13FrameEncoder(performMasking); - } - - @Override - public WebSocketClientHandshaker13 setForceCloseTimeoutMillis(long forceCloseTimeoutMillis) { - super.setForceCloseTimeoutMillis(forceCloseTimeoutMillis); - return this; - } - -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshakerFactory.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshakerFactory.java deleted file mode 100644 index a5dabdcbd5..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshakerFactory.java +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.handler.codec.http.HttpHeaders; - -import java.net.URI; - -import static io.netty.handler.codec.http.websocketx.WebSocketVersion.*; - -/** - * Creates a new {@link WebSocketClientHandshaker} of desired protocol version. - */ -public final class WebSocketClientHandshakerFactory { - - /** - * Private constructor so this static class cannot be instanced. - */ - private WebSocketClientHandshakerFactory() { - } - - /** - * Creates a new handshaker. - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". - * Subsequent web socket frames will be sent to this URL. - * @param version - * Version of web socket specification to use to connect to the server - * @param subprotocol - * Sub protocol request sent to the server. Null if no sub-protocol support is required. - * @param allowExtensions - * Allow extensions to be used in the reserved bits of the web socket frame - * @param customHeaders - * Custom HTTP headers to send during the handshake - */ - public static WebSocketClientHandshaker newHandshaker( - URI webSocketURL, WebSocketVersion version, String subprotocol, - boolean allowExtensions, HttpHeaders customHeaders) { - return newHandshaker(webSocketURL, version, subprotocol, allowExtensions, customHeaders, 65536); - } - - /** - * Creates a new handshaker. - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". - * Subsequent web socket frames will be sent to this URL. - * @param version - * Version of web socket specification to use to connect to the server - * @param subprotocol - * Sub protocol request sent to the server. Null if no sub-protocol support is required. - * @param allowExtensions - * Allow extensions to be used in the reserved bits of the web socket frame - * @param customHeaders - * Custom HTTP headers to send during the handshake - * @param maxFramePayloadLength - * Maximum allowable frame payload length. Setting this value to your application's - * requirement may reduce denial of service attacks using long data frames. - */ - public static WebSocketClientHandshaker newHandshaker( - URI webSocketURL, WebSocketVersion version, String subprotocol, - boolean allowExtensions, HttpHeaders customHeaders, int maxFramePayloadLength) { - return newHandshaker(webSocketURL, version, subprotocol, allowExtensions, customHeaders, - maxFramePayloadLength, true, false); - } - - /** - * Creates a new handshaker. - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". - * Subsequent web socket frames will be sent to this URL. - * @param version - * Version of web socket specification to use to connect to the server - * @param subprotocol - * Sub protocol request sent to the server. Null if no sub-protocol support is required. - * @param allowExtensions - * Allow extensions to be used in the reserved bits of the web socket frame - * @param customHeaders - * Custom HTTP headers to send during the handshake - * @param maxFramePayloadLength - * Maximum allowable frame payload length. Setting this value to your application's - * requirement may reduce denial of service attacks using long data frames. - * @param performMasking - * Whether to mask all written websocket frames. This must be set to true in order to be fully compatible - * with the websocket specifications. Client applications that communicate with a non-standard server - * which doesn't require masking might set this to false to achieve a higher performance. - * @param allowMaskMismatch - * When set to true, frames which are not masked properly according to the standard will still be - * accepted. - */ - public static WebSocketClientHandshaker newHandshaker( - URI webSocketURL, WebSocketVersion version, String subprotocol, - boolean allowExtensions, HttpHeaders customHeaders, int maxFramePayloadLength, - boolean performMasking, boolean allowMaskMismatch) { - return newHandshaker(webSocketURL, version, subprotocol, allowExtensions, customHeaders, - maxFramePayloadLength, performMasking, allowMaskMismatch, -1); - } - - /** - * Creates a new handshaker. - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". - * Subsequent web socket frames will be sent to this URL. - * @param version - * Version of web socket specification to use to connect to the server - * @param subprotocol - * Sub protocol request sent to the server. Null if no sub-protocol support is required. - * @param allowExtensions - * Allow extensions to be used in the reserved bits of the web socket frame - * @param customHeaders - * Custom HTTP headers to send during the handshake - * @param maxFramePayloadLength - * Maximum allowable frame payload length. Setting this value to your application's - * requirement may reduce denial of service attacks using long data frames. - * @param performMasking - * Whether to mask all written websocket frames. This must be set to true in order to be fully compatible - * with the websocket specifications. Client applications that communicate with a non-standard server - * which doesn't require masking might set this to false to achieve a higher performance. - * @param allowMaskMismatch - * When set to true, frames which are not masked properly according to the standard will still be - * accepted. - * @param forceCloseTimeoutMillis - * Close the connection if it was not closed by the server after timeout specified - */ - public static WebSocketClientHandshaker newHandshaker( - URI webSocketURL, WebSocketVersion version, String subprotocol, - boolean allowExtensions, HttpHeaders customHeaders, int maxFramePayloadLength, - boolean performMasking, boolean allowMaskMismatch, long forceCloseTimeoutMillis) { - if (version == V13) { - return new WebSocketClientHandshaker13( - webSocketURL, V13, subprotocol, allowExtensions, customHeaders, - maxFramePayloadLength, performMasking, allowMaskMismatch, forceCloseTimeoutMillis); - } - if (version == V08) { - return new WebSocketClientHandshaker08( - webSocketURL, V08, subprotocol, allowExtensions, customHeaders, - maxFramePayloadLength, performMasking, allowMaskMismatch, forceCloseTimeoutMillis); - } - if (version == V07) { - return new WebSocketClientHandshaker07( - webSocketURL, V07, subprotocol, allowExtensions, customHeaders, - maxFramePayloadLength, performMasking, allowMaskMismatch, forceCloseTimeoutMillis); - } - if (version == V00) { - return new WebSocketClientHandshaker00( - webSocketURL, V00, subprotocol, customHeaders, maxFramePayloadLength, forceCloseTimeoutMillis); - } - - throw new WebSocketClientHandshakeException("Protocol version " + version + " not supported."); - } - - /** - * Creates a new handshaker. - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". - * Subsequent web socket frames will be sent to this URL. - * @param version - * Version of web socket specification to use to connect to the server - * @param subprotocol - * Sub protocol request sent to the server. Null if no sub-protocol support is required. - * @param allowExtensions - * Allow extensions to be used in the reserved bits of the web socket frame - * @param customHeaders - * Custom HTTP headers to send during the handshake - * @param maxFramePayloadLength - * Maximum allowable frame payload length. Setting this value to your application's - * requirement may reduce denial of service attacks using long data frames. - * @param performMasking - * Whether to mask all written websocket frames. This must be set to true in order to be fully compatible - * with the websocket specifications. Client applications that communicate with a non-standard server - * which doesn't require masking might set this to false to achieve a higher performance. - * @param allowMaskMismatch - * When set to true, frames which are not masked properly according to the standard will still be - * accepted. - * @param forceCloseTimeoutMillis - * Close the connection if it was not closed by the server after timeout specified - * @param absoluteUpgradeUrl - * Use an absolute url for the Upgrade request, typically when connecting through an HTTP proxy over - * clear HTTP - */ - public static WebSocketClientHandshaker newHandshaker( - URI webSocketURL, WebSocketVersion version, String subprotocol, - boolean allowExtensions, HttpHeaders customHeaders, int maxFramePayloadLength, - boolean performMasking, boolean allowMaskMismatch, long forceCloseTimeoutMillis, boolean absoluteUpgradeUrl) { - if (version == V13) { - return new WebSocketClientHandshaker13( - webSocketURL, V13, subprotocol, allowExtensions, customHeaders, - maxFramePayloadLength, performMasking, allowMaskMismatch, forceCloseTimeoutMillis, absoluteUpgradeUrl); - } - if (version == V08) { - return new WebSocketClientHandshaker08( - webSocketURL, V08, subprotocol, allowExtensions, customHeaders, - maxFramePayloadLength, performMasking, allowMaskMismatch, forceCloseTimeoutMillis, absoluteUpgradeUrl); - } - if (version == V07) { - return new WebSocketClientHandshaker07( - webSocketURL, V07, subprotocol, allowExtensions, customHeaders, - maxFramePayloadLength, performMasking, allowMaskMismatch, forceCloseTimeoutMillis, absoluteUpgradeUrl); - } - if (version == V00) { - return new WebSocketClientHandshaker00( - webSocketURL, V00, subprotocol, customHeaders, - maxFramePayloadLength, forceCloseTimeoutMillis, absoluteUpgradeUrl); - } - - throw new WebSocketClientHandshakeException("Protocol version " + version + " not supported."); - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientProtocolConfig.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientProtocolConfig.java deleted file mode 100644 index 461e4687f7..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientProtocolConfig.java +++ /dev/null @@ -1,392 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.handler.codec.http.EmptyHttpHeaders; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler.ClientHandshakeStateEvent; - -import java.net.URI; -import java.util.Objects; - -import static io.netty.handler.codec.http.websocketx.WebSocketServerProtocolConfig.DEFAULT_HANDSHAKE_TIMEOUT_MILLIS; -import static io.netty.util.internal.ObjectUtil.checkPositive; - -/** - * WebSocket server configuration. - */ -public final class WebSocketClientProtocolConfig { - - static final boolean DEFAULT_PERFORM_MASKING = true; - static final boolean DEFAULT_ALLOW_MASK_MISMATCH = false; - static final boolean DEFAULT_HANDLE_CLOSE_FRAMES = true; - static final boolean DEFAULT_DROP_PONG_FRAMES = true; - - private final URI webSocketUri; - private final String subprotocol; - private final WebSocketVersion version; - private final boolean allowExtensions; - private final HttpHeaders customHeaders; - private final int maxFramePayloadLength; - private final boolean performMasking; - private final boolean allowMaskMismatch; - private final boolean handleCloseFrames; - private final WebSocketCloseStatus sendCloseFrame; - private final boolean dropPongFrames; - private final long handshakeTimeoutMillis; - private final long forceCloseTimeoutMillis; - private final boolean absoluteUpgradeUrl; - - private WebSocketClientProtocolConfig( - URI webSocketUri, - String subprotocol, - WebSocketVersion version, - boolean allowExtensions, - HttpHeaders customHeaders, - int maxFramePayloadLength, - boolean performMasking, - boolean allowMaskMismatch, - boolean handleCloseFrames, - WebSocketCloseStatus sendCloseFrame, - boolean dropPongFrames, - long handshakeTimeoutMillis, - long forceCloseTimeoutMillis, - boolean absoluteUpgradeUrl - ) { - this.webSocketUri = webSocketUri; - this.subprotocol = subprotocol; - this.version = version; - this.allowExtensions = allowExtensions; - this.customHeaders = customHeaders; - this.maxFramePayloadLength = maxFramePayloadLength; - this.performMasking = performMasking; - this.allowMaskMismatch = allowMaskMismatch; - this.forceCloseTimeoutMillis = forceCloseTimeoutMillis; - this.handleCloseFrames = handleCloseFrames; - this.sendCloseFrame = sendCloseFrame; - this.dropPongFrames = dropPongFrames; - this.handshakeTimeoutMillis = checkPositive(handshakeTimeoutMillis, "handshakeTimeoutMillis"); - this.absoluteUpgradeUrl = absoluteUpgradeUrl; - } - - public URI webSocketUri() { - return webSocketUri; - } - - public String subprotocol() { - return subprotocol; - } - - public WebSocketVersion version() { - return version; - } - - public boolean allowExtensions() { - return allowExtensions; - } - - public HttpHeaders customHeaders() { - return customHeaders; - } - - public int maxFramePayloadLength() { - return maxFramePayloadLength; - } - - public boolean performMasking() { - return performMasking; - } - - public boolean allowMaskMismatch() { - return allowMaskMismatch; - } - - public boolean handleCloseFrames() { - return handleCloseFrames; - } - - public WebSocketCloseStatus sendCloseFrame() { - return sendCloseFrame; - } - - public boolean dropPongFrames() { - return dropPongFrames; - } - - public long handshakeTimeoutMillis() { - return handshakeTimeoutMillis; - } - - public long forceCloseTimeoutMillis() { - return forceCloseTimeoutMillis; - } - - public boolean absoluteUpgradeUrl() { - return absoluteUpgradeUrl; - } - - @Override - public String toString() { - return "WebSocketClientProtocolConfig" + - " {webSocketUri=" + webSocketUri + - ", subprotocol=" + subprotocol + - ", version=" + version + - ", allowExtensions=" + allowExtensions + - ", customHeaders=" + customHeaders + - ", maxFramePayloadLength=" + maxFramePayloadLength + - ", performMasking=" + performMasking + - ", allowMaskMismatch=" + allowMaskMismatch + - ", handleCloseFrames=" + handleCloseFrames + - ", sendCloseFrame=" + sendCloseFrame + - ", dropPongFrames=" + dropPongFrames + - ", handshakeTimeoutMillis=" + handshakeTimeoutMillis + - ", forceCloseTimeoutMillis=" + forceCloseTimeoutMillis + - ", absoluteUpgradeUrl=" + absoluteUpgradeUrl + - "}"; - } - - public Builder toBuilder() { - return new Builder(this); - } - - public static Builder newBuilder() { - return new Builder( - URI.create("https://localhost/"), - null, - WebSocketVersion.V13, - false, - EmptyHttpHeaders.INSTANCE, - 65536, - DEFAULT_PERFORM_MASKING, - DEFAULT_ALLOW_MASK_MISMATCH, - DEFAULT_HANDLE_CLOSE_FRAMES, - WebSocketCloseStatus.NORMAL_CLOSURE, - DEFAULT_DROP_PONG_FRAMES, - DEFAULT_HANDSHAKE_TIMEOUT_MILLIS, - -1, - false); - } - - public static final class Builder { - private URI webSocketUri; - private String subprotocol; - private WebSocketVersion version; - private boolean allowExtensions; - private HttpHeaders customHeaders; - private int maxFramePayloadLength; - private boolean performMasking; - private boolean allowMaskMismatch; - private boolean handleCloseFrames; - private WebSocketCloseStatus sendCloseFrame; - private boolean dropPongFrames; - private long handshakeTimeoutMillis; - private long forceCloseTimeoutMillis; - private boolean absoluteUpgradeUrl; - - private Builder(WebSocketClientProtocolConfig clientConfig) { - this(Objects.requireNonNull(clientConfig, "clientConfig").webSocketUri(), - clientConfig.subprotocol(), - clientConfig.version(), - clientConfig.allowExtensions(), - clientConfig.customHeaders(), - clientConfig.maxFramePayloadLength(), - clientConfig.performMasking(), - clientConfig.allowMaskMismatch(), - clientConfig.handleCloseFrames(), - clientConfig.sendCloseFrame(), - clientConfig.dropPongFrames(), - clientConfig.handshakeTimeoutMillis(), - clientConfig.forceCloseTimeoutMillis(), - clientConfig.absoluteUpgradeUrl()); - } - - private Builder(URI webSocketUri, - String subprotocol, - WebSocketVersion version, - boolean allowExtensions, - HttpHeaders customHeaders, - int maxFramePayloadLength, - boolean performMasking, - boolean allowMaskMismatch, - boolean handleCloseFrames, - WebSocketCloseStatus sendCloseFrame, - boolean dropPongFrames, - long handshakeTimeoutMillis, - long forceCloseTimeoutMillis, - boolean absoluteUpgradeUrl) { - this.webSocketUri = webSocketUri; - this.subprotocol = subprotocol; - this.version = version; - this.allowExtensions = allowExtensions; - this.customHeaders = customHeaders; - this.maxFramePayloadLength = maxFramePayloadLength; - this.performMasking = performMasking; - this.allowMaskMismatch = allowMaskMismatch; - this.handleCloseFrames = handleCloseFrames; - this.sendCloseFrame = sendCloseFrame; - this.dropPongFrames = dropPongFrames; - this.handshakeTimeoutMillis = handshakeTimeoutMillis; - this.forceCloseTimeoutMillis = forceCloseTimeoutMillis; - this.absoluteUpgradeUrl = absoluteUpgradeUrl; - } - - /** - * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be - * sent to this URL. - */ - public Builder webSocketUri(String webSocketUri) { - return webSocketUri(URI.create(webSocketUri)); - } - - /** - * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be - * sent to this URL. - */ - public Builder webSocketUri(URI webSocketUri) { - this.webSocketUri = webSocketUri; - return this; - } - - /** - * Sub protocol request sent to the server. - */ - public Builder subprotocol(String subprotocol) { - this.subprotocol = subprotocol; - return this; - } - - /** - * Version of web socket specification to use to connect to the server - */ - public Builder version(WebSocketVersion version) { - this.version = version; - return this; - } - - /** - * Allow extensions to be used in the reserved bits of the web socket frame - */ - public Builder allowExtensions(boolean allowExtensions) { - this.allowExtensions = allowExtensions; - return this; - } - - /** - * Map of custom headers to add to the client request - */ - public Builder customHeaders(HttpHeaders customHeaders) { - this.customHeaders = customHeaders; - return this; - } - - /** - * Maximum length of a frame's payload - */ - public Builder maxFramePayloadLength(int maxFramePayloadLength) { - this.maxFramePayloadLength = maxFramePayloadLength; - return this; - } - - /** - * Whether to mask all written websocket frames. This must be set to true in order to be fully compatible - * with the websocket specifications. Client applications that communicate with a non-standard server - * which doesn't require masking might set this to false to achieve a higher performance. - */ - public Builder performMasking(boolean performMasking) { - this.performMasking = performMasking; - return this; - } - - /** - * When set to true, frames which are not masked properly according to the standard will still be accepted. - */ - public Builder allowMaskMismatch(boolean allowMaskMismatch) { - this.allowMaskMismatch = allowMaskMismatch; - return this; - } - - /** - * {@code true} if close frames should not be forwarded and just close the channel - */ - public Builder handleCloseFrames(boolean handleCloseFrames) { - this.handleCloseFrames = handleCloseFrames; - return this; - } - - /** - * Close frame to send, when close frame was not send manually. Or {@code null} to disable proper close. - */ - public Builder sendCloseFrame(WebSocketCloseStatus sendCloseFrame) { - this.sendCloseFrame = sendCloseFrame; - return this; - } - - /** - * {@code true} if pong frames should not be forwarded - */ - public Builder dropPongFrames(boolean dropPongFrames) { - this.dropPongFrames = dropPongFrames; - return this; - } - - /** - * Handshake timeout in mills, when handshake timeout, will trigger user - * event {@link ClientHandshakeStateEvent#HANDSHAKE_TIMEOUT} - */ - public Builder handshakeTimeoutMillis(long handshakeTimeoutMillis) { - this.handshakeTimeoutMillis = handshakeTimeoutMillis; - return this; - } - - /** - * Close the connection if it was not closed by the server after timeout specified - */ - public Builder forceCloseTimeoutMillis(long forceCloseTimeoutMillis) { - this.forceCloseTimeoutMillis = forceCloseTimeoutMillis; - return this; - } - - /** - * Use an absolute url for the Upgrade request, typically when connecting through an HTTP proxy over clear HTTP - */ - public Builder absoluteUpgradeUrl(boolean absoluteUpgradeUrl) { - this.absoluteUpgradeUrl = absoluteUpgradeUrl; - return this; - } - - /** - * Build unmodifiable client protocol configuration. - */ - public WebSocketClientProtocolConfig build() { - return new WebSocketClientProtocolConfig( - webSocketUri, - subprotocol, - version, - allowExtensions, - customHeaders, - maxFramePayloadLength, - performMasking, - allowMaskMismatch, - handleCloseFrames, - sendCloseFrame, - dropPongFrames, - handshakeTimeoutMillis, - forceCloseTimeoutMillis, - absoluteUpgradeUrl - ); - } - } -} 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 deleted file mode 100644 index ceb2a4e87d..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientProtocolHandler.java +++ /dev/null @@ -1,391 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelPipeline; -import io.netty.handler.codec.http.HttpHeaders; - -import java.net.URI; -import java.util.Objects; - -import static io.netty.handler.codec.http.websocketx.WebSocketClientProtocolConfig.DEFAULT_ALLOW_MASK_MISMATCH; -import static io.netty.handler.codec.http.websocketx.WebSocketClientProtocolConfig.DEFAULT_DROP_PONG_FRAMES; -import static io.netty.handler.codec.http.websocketx.WebSocketClientProtocolConfig.DEFAULT_HANDLE_CLOSE_FRAMES; -import static io.netty.handler.codec.http.websocketx.WebSocketClientProtocolConfig.DEFAULT_PERFORM_MASKING; -import static io.netty.handler.codec.http.websocketx.WebSocketServerProtocolConfig.DEFAULT_HANDSHAKE_TIMEOUT_MILLIS; - -/** - * This handler does all the heavy lifting for you to run a websocket client. - * - * It takes care of websocket handshaking as well as processing of Ping, Pong frames. Text and Binary - * data frames are passed to the next handler in the pipeline (implemented by you) for processing. - * Also the close frame is passed to the next handler as you may want inspect it before close the connection if - * the {@code handleCloseFrames} is {@code false}, default is {@code true}. - * - * This implementation will establish the websocket connection once the connection to the remote server was complete. - * - * To know once a handshake was done you can intercept the - * {@link ChannelHandler#userEventTriggered(ChannelHandlerContext, Object)} and check if the event was of type - * {@link ClientHandshakeStateEvent#HANDSHAKE_ISSUED} or {@link ClientHandshakeStateEvent#HANDSHAKE_COMPLETE}. - */ -public class WebSocketClientProtocolHandler extends WebSocketProtocolHandler { - private final WebSocketClientHandshaker handshaker; - private final WebSocketClientProtocolConfig clientConfig; - - /** - * Returns the used handshaker - */ - public WebSocketClientHandshaker handshaker() { - return handshaker; - } - - /** - * Events that are fired to notify about handshake status - */ - public enum ClientHandshakeStateEvent { - /** - * The Handshake was timed out - */ - HANDSHAKE_TIMEOUT, - - /** - * The Handshake was started but the server did not response yet to the request - */ - HANDSHAKE_ISSUED, - - /** - * The Handshake was complete succesful and so the channel was upgraded to websockets - */ - HANDSHAKE_COMPLETE - } - - /** - * Base constructor - * - * @param clientConfig - * Client protocol configuration. - */ - public WebSocketClientProtocolHandler(WebSocketClientProtocolConfig clientConfig) { - super(Objects.requireNonNull(clientConfig, "clientConfig").dropPongFrames(), - clientConfig.sendCloseFrame(), clientConfig.forceCloseTimeoutMillis()); - this.handshaker = WebSocketClientHandshakerFactory.newHandshaker( - clientConfig.webSocketUri(), - clientConfig.version(), - clientConfig.subprotocol(), - clientConfig.allowExtensions(), - clientConfig.customHeaders(), - clientConfig.maxFramePayloadLength(), - clientConfig.performMasking(), - clientConfig.allowMaskMismatch(), - clientConfig.forceCloseTimeoutMillis(), - clientConfig.absoluteUpgradeUrl() - ); - this.clientConfig = clientConfig; - } - - /** - * Base constructor - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be - * sent to this URL. - * @param version - * Version of web socket specification to use to connect to the server - * @param subprotocol - * Sub protocol request sent to the server. - * @param customHeaders - * Map of custom headers to add to the client request - * @param maxFramePayloadLength - * Maximum length of a frame's payload - * @param handleCloseFrames - * {@code true} if close frames should not be forwarded and just close the channel - * @param performMasking - * Whether to mask all written websocket frames. This must be set to true in order to be fully compatible - * with the websocket specifications. Client applications that communicate with a non-standard server - * which doesn't require masking might set this to false to achieve a higher performance. - * @param allowMaskMismatch - * When set to true, frames which are not masked properly according to the standard will still be - * accepted. - */ - public WebSocketClientProtocolHandler(URI webSocketURL, WebSocketVersion version, String subprotocol, - boolean allowExtensions, HttpHeaders customHeaders, - int maxFramePayloadLength, boolean handleCloseFrames, - boolean performMasking, boolean allowMaskMismatch) { - this(webSocketURL, version, subprotocol, allowExtensions, customHeaders, maxFramePayloadLength, - handleCloseFrames, performMasking, allowMaskMismatch, DEFAULT_HANDSHAKE_TIMEOUT_MILLIS); - } - - /** - * Base constructor - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be - * sent to this URL. - * @param version - * Version of web socket specification to use to connect to the server - * @param subprotocol - * Sub protocol request sent to the server. - * @param customHeaders - * Map of custom headers to add to the client request - * @param maxFramePayloadLength - * Maximum length of a frame's payload - * @param handleCloseFrames - * {@code true} if close frames should not be forwarded and just close the channel - * @param performMasking - * Whether to mask all written websocket frames. This must be set to true in order to be fully compatible - * with the websocket specifications. Client applications that communicate with a non-standard server - * which doesn't require masking might set this to false to achieve a higher performance. - * @param allowMaskMismatch - * When set to true, frames which are not masked properly according to the standard will still be - * accepted. - * @param handshakeTimeoutMillis - * Handshake timeout in mills, when handshake timeout, will trigger user - * event {@link ClientHandshakeStateEvent#HANDSHAKE_TIMEOUT} - */ - public WebSocketClientProtocolHandler(URI webSocketURL, WebSocketVersion version, String subprotocol, - boolean allowExtensions, HttpHeaders customHeaders, - int maxFramePayloadLength, boolean handleCloseFrames, boolean performMasking, - boolean allowMaskMismatch, long handshakeTimeoutMillis) { - this(WebSocketClientHandshakerFactory.newHandshaker(webSocketURL, version, subprotocol, - allowExtensions, customHeaders, maxFramePayloadLength, - performMasking, allowMaskMismatch), - handleCloseFrames, handshakeTimeoutMillis); - } - - /** - * Base constructor - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be - * sent to this URL. - * @param version - * Version of web socket specification to use to connect to the server - * @param subprotocol - * Sub protocol request sent to the server. - * @param customHeaders - * Map of custom headers to add to the client request - * @param maxFramePayloadLength - * Maximum length of a frame's payload - * @param handleCloseFrames - * {@code true} if close frames should not be forwarded and just close the channel - */ - public WebSocketClientProtocolHandler(URI webSocketURL, WebSocketVersion version, String subprotocol, - boolean allowExtensions, HttpHeaders customHeaders, - int maxFramePayloadLength, boolean handleCloseFrames) { - this(webSocketURL, version, subprotocol, allowExtensions, customHeaders, maxFramePayloadLength, - handleCloseFrames, DEFAULT_HANDSHAKE_TIMEOUT_MILLIS); - } - - /** - * Base constructor - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be - * sent to this URL. - * @param version - * Version of web socket specification to use to connect to the server - * @param subprotocol - * Sub protocol request sent to the server. - * @param customHeaders - * Map of custom headers to add to the client request - * @param maxFramePayloadLength - * Maximum length of a frame's payload - * @param handleCloseFrames - * {@code true} if close frames should not be forwarded and just close the channel - * @param handshakeTimeoutMillis - * Handshake timeout in mills, when handshake timeout, will trigger user - * event {@link ClientHandshakeStateEvent#HANDSHAKE_TIMEOUT} - */ - public WebSocketClientProtocolHandler(URI webSocketURL, WebSocketVersion version, String subprotocol, - boolean allowExtensions, HttpHeaders customHeaders, int maxFramePayloadLength, - boolean handleCloseFrames, long handshakeTimeoutMillis) { - this(webSocketURL, version, subprotocol, allowExtensions, customHeaders, maxFramePayloadLength, - handleCloseFrames, DEFAULT_PERFORM_MASKING, DEFAULT_ALLOW_MASK_MISMATCH, handshakeTimeoutMillis); - } - - /** - * Base constructor - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be - * sent to this URL. - * @param version - * Version of web socket specification to use to connect to the server - * @param subprotocol - * Sub protocol request sent to the server. - * @param customHeaders - * Map of custom headers to add to the client request - * @param maxFramePayloadLength - * Maximum length of a frame's payload - */ - public WebSocketClientProtocolHandler(URI webSocketURL, WebSocketVersion version, String subprotocol, - boolean allowExtensions, HttpHeaders customHeaders, - int maxFramePayloadLength) { - this(webSocketURL, version, subprotocol, allowExtensions, - customHeaders, maxFramePayloadLength, DEFAULT_HANDSHAKE_TIMEOUT_MILLIS); - } - - /** - * Base constructor - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be - * sent to this URL. - * @param version - * Version of web socket specification to use to connect to the server - * @param subprotocol - * Sub protocol request sent to the server. - * @param customHeaders - * Map of custom headers to add to the client request - * @param maxFramePayloadLength - * Maximum length of a frame's payload - * @param handshakeTimeoutMillis - * Handshake timeout in mills, when handshake timeout, will trigger user - * event {@link ClientHandshakeStateEvent#HANDSHAKE_TIMEOUT} - */ - public WebSocketClientProtocolHandler(URI webSocketURL, WebSocketVersion version, String subprotocol, - boolean allowExtensions, HttpHeaders customHeaders, - int maxFramePayloadLength, long handshakeTimeoutMillis) { - this(webSocketURL, version, subprotocol, allowExtensions, customHeaders, - maxFramePayloadLength, DEFAULT_HANDLE_CLOSE_FRAMES, handshakeTimeoutMillis); - } - - /** - * 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 - */ - public WebSocketClientProtocolHandler(WebSocketClientHandshaker handshaker, boolean handleCloseFrames) { - this(handshaker, handleCloseFrames, DEFAULT_HANDSHAKE_TIMEOUT_MILLIS); - } - - /** - * 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 handshakeTimeoutMillis - * Handshake timeout in mills, when handshake timeout, will trigger user - * event {@link ClientHandshakeStateEvent#HANDSHAKE_TIMEOUT} - */ - public WebSocketClientProtocolHandler(WebSocketClientHandshaker handshaker, boolean handleCloseFrames, - long handshakeTimeoutMillis) { - this(handshaker, handleCloseFrames, DEFAULT_DROP_PONG_FRAMES, handshakeTimeoutMillis); - } - - /** - * 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) { - this(handshaker, handleCloseFrames, dropPongFrames, DEFAULT_HANDSHAKE_TIMEOUT_MILLIS); - } - - /** - * 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 - * @param handshakeTimeoutMillis - * Handshake timeout in mills, when handshake timeout, will trigger user - * event {@link ClientHandshakeStateEvent#HANDSHAKE_TIMEOUT} - */ - public WebSocketClientProtocolHandler(WebSocketClientHandshaker handshaker, boolean handleCloseFrames, - boolean dropPongFrames, long handshakeTimeoutMillis) { - super(dropPongFrames); - this.handshaker = handshaker; - this.clientConfig = WebSocketClientProtocolConfig.newBuilder() - .handleCloseFrames(handleCloseFrames) - .handshakeTimeoutMillis(handshakeTimeoutMillis) - .build(); - } - - /** - * Base constructor - * - * @param handshaker - * The {@link WebSocketClientHandshaker} which will be used to issue the handshake once the connection - * was established to the remote peer. - */ - public WebSocketClientProtocolHandler(WebSocketClientHandshaker handshaker) { - this(handshaker, DEFAULT_HANDSHAKE_TIMEOUT_MILLIS); - } - - /** - * 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 handshakeTimeoutMillis - * Handshake timeout in mills, when handshake timeout, will trigger user - * event {@link ClientHandshakeStateEvent#HANDSHAKE_TIMEOUT} - */ - public WebSocketClientProtocolHandler(WebSocketClientHandshaker handshaker, long handshakeTimeoutMillis) { - this(handshaker, DEFAULT_HANDLE_CLOSE_FRAMES, handshakeTimeoutMillis); - } - - @Override - protected void decode(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception { - if (clientConfig.handleCloseFrames() && frame instanceof CloseWebSocketFrame) { - ctx.close(); - return; - } - super.decode(ctx, frame); - } - - @Override - protected WebSocketClientHandshakeException buildHandshakeException(String message) { - return new WebSocketClientHandshakeException(message); - } - - @Override - public void handlerAdded(ChannelHandlerContext ctx) { - ChannelPipeline cp = ctx.pipeline(); - if (cp.get(WebSocketClientProtocolHandshakeHandler.class) == null) { - // Add the WebSocketClientProtocolHandshakeHandler before this one. - ctx.pipeline().addBefore(ctx.name(), WebSocketClientProtocolHandshakeHandler.class.getName(), - new WebSocketClientProtocolHandshakeHandler(handshaker, clientConfig.handshakeTimeoutMillis())); - } - if (cp.get(Utf8FrameValidator.class) == null) { - // Add the UFT8 checking before this one. - ctx.pipeline().addBefore(ctx.name(), Utf8FrameValidator.class.getName(), - new Utf8FrameValidator()); - } - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientProtocolHandshakeHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientProtocolHandshakeHandler.java deleted file mode 100644 index 7d6cf101be..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientProtocolHandshakeHandler.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler.ClientHandshakeStateEvent; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; - -import java.util.concurrent.TimeUnit; - -import static io.netty.util.internal.ObjectUtil.checkPositive; - -class WebSocketClientProtocolHandshakeHandler implements ChannelHandler { - - private static final long DEFAULT_HANDSHAKE_TIMEOUT_MS = 10000L; - - private final WebSocketClientHandshaker handshaker; - private final long handshakeTimeoutMillis; - private ChannelHandlerContext ctx; - private Promise handshakePromise; - - WebSocketClientProtocolHandshakeHandler(WebSocketClientHandshaker handshaker) { - this(handshaker, DEFAULT_HANDSHAKE_TIMEOUT_MS); - } - - WebSocketClientProtocolHandshakeHandler(WebSocketClientHandshaker handshaker, long handshakeTimeoutMillis) { - this.handshaker = handshaker; - this.handshakeTimeoutMillis = checkPositive(handshakeTimeoutMillis, "handshakeTimeoutMillis"); - } - - @Override - public void handlerAdded(ChannelHandlerContext ctx) throws Exception { - this.ctx = ctx; - handshakePromise = ctx.newPromise(); - } - - @Override - public void channelActive(final ChannelHandlerContext ctx) throws Exception { - ctx.fireChannelActive(); - handshaker.handshake(ctx.channel()).addListener(future -> { - if (future.isFailed()) { - handshakePromise.tryFailure(future.cause()); - ctx.fireExceptionCaught(future.cause()); - } else { - ctx.fireUserEventTriggered( - WebSocketClientProtocolHandler.ClientHandshakeStateEvent.HANDSHAKE_ISSUED); - } - }); - applyHandshakeTimeout(); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - if (!handshakePromise.isDone()) { - handshakePromise.tryFailure(new WebSocketClientHandshakeException("channel closed with handshake " + - "in progress")); - } - - ctx.fireChannelInactive(); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - if (!(msg instanceof FullHttpResponse)) { - ctx.fireChannelRead(msg); - return; - } - - FullHttpResponse response = (FullHttpResponse) msg; - try { - if (!handshaker.isHandshakeComplete()) { - handshaker.finishHandshake(ctx.channel(), response); - handshakePromise.trySuccess(null); - ctx.fireUserEventTriggered( - WebSocketClientProtocolHandler.ClientHandshakeStateEvent.HANDSHAKE_COMPLETE); - ctx.pipeline().remove(this); - return; - } - throw new IllegalStateException("WebSocketClientHandshaker should have been non finished yet"); - } finally { - response.release(); - } - } - - private void applyHandshakeTimeout() { - final Promise localHandshakePromise = handshakePromise; - if (handshakeTimeoutMillis <= 0 || localHandshakePromise.isDone()) { - return; - } - - final Future timeoutFuture = ctx.executor().schedule(() -> { - if (localHandshakePromise.isDone()) { - return; - } - - if (localHandshakePromise.tryFailure(new WebSocketClientHandshakeException("handshake timed out"))) { - ctx.flush() - .fireUserEventTriggered(ClientHandshakeStateEvent.HANDSHAKE_TIMEOUT) - .close(); - } - }, handshakeTimeoutMillis, TimeUnit.MILLISECONDS); - - // Cancel the handshake timeout when handshake is finished. - localHandshakePromise.asFuture().addListener(f -> timeoutFuture.cancel()); - } - - /** - * This method is visible for testing. - * - * @return current handshake future - */ - Future getHandshakeFuture() { - return handshakePromise.asFuture(); - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketCloseStatus.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketCloseStatus.java deleted file mode 100644 index a15a2cddad..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketCloseStatus.java +++ /dev/null @@ -1,330 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import java.util.Objects; - -/** - * WebSocket status codes specified in RFC-6455. - *
- *
- * RFC-6455 The WebSocket Protocol, December 2011:
- * https://tools.ietf.org/html/rfc6455#section-7.4.1
- *
- * WebSocket Protocol Registries, April 2019:
- * https://www.iana.org/assignments/websocket/websocket.xhtml
- *
- * 7.4.1.  Defined Status Codes
- *
- * Endpoints MAY use the following pre-defined status codes when sending
- * a Close frame.
- *
- * 1000
- *
- *    1000 indicates a normal closure, meaning that the purpose for
- *    which the connection was established has been fulfilled.
- *
- * 1001
- *
- *    1001 indicates that an endpoint is "going away", such as a server
- *    going down or a browser having navigated away from a page.
- *
- * 1002
- *
- *    1002 indicates that an endpoint is terminating the connection due
- *    to a protocol error.
- *
- * 1003
- *
- *    1003 indicates that an endpoint is terminating the connection
- *    because it has received a type of data it cannot accept (e.g., an
- *    endpoint that understands only text data MAY send this if it
- *    receives a binary message).
- *
- * 1004
- *
- *    Reserved. The specific meaning might be defined in the future.
- *
- * 1005
- *
- *    1005 is a reserved value and MUST NOT be set as a status code in a
- *    Close control frame by an endpoint. It is designated for use in
- *    applications expecting a status code to indicate that no status
- *    code was actually present.
- *
- * 1006
- *
- *    1006 is a reserved value and MUST NOT be set as a status code in a
- *    Close control frame by an endpoint. It is designated for use in
- *    applications expecting a status code to indicate that the
- *    connection was closed abnormally, e.g., without sending or
- *    receiving a Close control frame.
- *
- * 1007
- *
- *    1007 indicates that an endpoint is terminating the connection
- *    because it has received data within a message that was not
- *    consistent with the type of the message (e.g., non-UTF-8 [RFC3629]
- *    data within a text message).
- *
- * 1008
- *
- *    1008 indicates that an endpoint is terminating the connection
- *    because it has received a message that violates its policy. This
- *    is a generic status code that can be returned when there is no
- *    other more suitable status code (e.g., 1003 or 1009) or if there
- *    is a need to hide specific details about the policy.
- *
- * 1009
- *
- *    1009 indicates that an endpoint is terminating the connection
- *    because it has received a message that is too big for it to
- *    process.
- *
- * 1010
- *
- *    1010 indicates that an endpoint (client) is terminating the
- *    connection because it has expected the server to negotiate one or
- *    more extension, but the server didn't return them in the response
- *    message of the WebSocket handshake. The list of extensions that
- *    are needed SHOULD appear in the /reason/ part of the Close frame.
- *    Note that this status code is not used by the server, because it
- *    can fail the WebSocket handshake instead.
- *
- * 1011
- *
- *    1011 indicates that a server is terminating the connection because
- *    it encountered an unexpected condition that prevented it from
- *    fulfilling the request.
- *
- * 1012 (IANA Registry, Non RFC-6455)
- *
- *    1012 indicates that the service is restarted. a client may reconnect,
- *    and if it choses to do, should reconnect using a randomized delay
- *    of 5 - 30 seconds.
- *
- * 1013 (IANA Registry, Non RFC-6455)
- *
- *    1013 indicates that the service is experiencing overload. a client
- *    should only connect to a different IP (when there are multiple for the
- *    target) or reconnect to the same IP upon user action.
- *
- * 1014 (IANA Registry, Non RFC-6455)
- *
- *    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.
- *
- * 1015
- *
- *    1015 is a reserved value and MUST NOT be set as a status code in a
- *    Close control frame by an endpoint. It is designated for use in
- *    applications expecting a status code to indicate that the
- *    connection was closed due to a failure to perform a TLS handshake
- *    (e.g., the server certificate can't be verified).
- *
- *
- * 7.4.2. Reserved Status Code Ranges
- *
- * 0-999
- *
- *    Status codes in the range 0-999 are not used.
- *
- * 1000-2999
- *
- *    Status codes in the range 1000-2999 are reserved for definition by
- *    this protocol, its future revisions, and extensions specified in a
- *    permanent and readily available public specification.
- *
- * 3000-3999
- *
- *    Status codes in the range 3000-3999 are reserved for use by
- *    libraries, frameworks, and applications. These status codes are
- *    registered directly with IANA. The interpretation of these codes
- *    is undefined by this protocol.
- *
- * 4000-4999
- *
- *    Status codes in the range 4000-4999 are reserved for private use
- *    and thus can't be registered. Such codes can be used by prior
- *    agreements between WebSocket applications. The interpretation of
- *    these codes is undefined by this protocol.
- * 
- *

- * While {@link WebSocketCloseStatus} is enum-like structure, its instances should NOT be compared by reference. - * Instead, either {@link #equals(Object)} should be used or direct comparison of {@link #code()} value. - */ -public final class WebSocketCloseStatus implements Comparable { - - public static final WebSocketCloseStatus NORMAL_CLOSURE = - new WebSocketCloseStatus(1000, "Bye"); - - public static final WebSocketCloseStatus ENDPOINT_UNAVAILABLE = - new WebSocketCloseStatus(1001, "Endpoint unavailable"); - - public static final WebSocketCloseStatus PROTOCOL_ERROR = - new WebSocketCloseStatus(1002, "Protocol error"); - - public static final WebSocketCloseStatus INVALID_MESSAGE_TYPE = - new WebSocketCloseStatus(1003, "Invalid message type"); - - public static final WebSocketCloseStatus INVALID_PAYLOAD_DATA = - new WebSocketCloseStatus(1007, "Invalid payload data"); - - public static final WebSocketCloseStatus POLICY_VIOLATION = - new WebSocketCloseStatus(1008, "Policy violation"); - - public static final WebSocketCloseStatus MESSAGE_TOO_BIG = - new WebSocketCloseStatus(1009, "Message too big"); - - public static final WebSocketCloseStatus MANDATORY_EXTENSION = - new WebSocketCloseStatus(1010, "Mandatory extension"); - - public static final WebSocketCloseStatus INTERNAL_SERVER_ERROR = - new WebSocketCloseStatus(1011, "Internal server error"); - - public static final WebSocketCloseStatus SERVICE_RESTART = - new WebSocketCloseStatus(1012, "Service Restart"); - - public static final WebSocketCloseStatus TRY_AGAIN_LATER = - new WebSocketCloseStatus(1013, "Try Again Later"); - - public static final WebSocketCloseStatus BAD_GATEWAY = - new WebSocketCloseStatus(1014, "Bad Gateway"); - - // 1004, 1005, 1006, 1015 are reserved and should never be used by user - //public static final WebSocketCloseStatus SPECIFIC_MEANING = register(1004, "..."); - - public static final WebSocketCloseStatus EMPTY = - new WebSocketCloseStatus(1005, "Empty", false); - - public static final WebSocketCloseStatus ABNORMAL_CLOSURE = - new WebSocketCloseStatus(1006, "Abnormal closure", false); - - public static final WebSocketCloseStatus TLS_HANDSHAKE_FAILED = - new WebSocketCloseStatus(1015, "TLS handshake failed", false); - - private final int statusCode; - private final String reasonText; - private String text; - - public WebSocketCloseStatus(int statusCode, String reasonText) { - this(statusCode, reasonText, true); - } - - public WebSocketCloseStatus(int statusCode, String reasonText, boolean validate) { - if (validate && !isValidStatusCode(statusCode)) { - throw new IllegalArgumentException( - "WebSocket close status code does NOT comply with RFC-6455: " + statusCode); - } - this.statusCode = statusCode; - this.reasonText = Objects.requireNonNull(reasonText, "reasonText"); - } - - public int code() { - return statusCode; - } - - public String reasonText() { - return reasonText; - } - - /** - * Order of {@link WebSocketCloseStatus} only depends on {@link #code()}. - */ - @Override - public int compareTo(WebSocketCloseStatus o) { - return code() - o.code(); - } - - /** - * Equality of {@link WebSocketCloseStatus} only depends on {@link #code()}. - */ - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (null == o || getClass() != o.getClass()) { - return false; - } - - WebSocketCloseStatus that = (WebSocketCloseStatus) o; - - return statusCode == that.statusCode; - } - - @Override - public int hashCode() { - return statusCode; - } - - @Override - public String toString() { - String text = this.text; - if (text == null) { - // E.g.: "1000 Bye", "1009 Message too big" - this.text = text = code() + " " + reasonText(); - } - return text; - } - - public static boolean isValidStatusCode(int code) { - return code < 0 || - 1000 <= code && code <= 1003 || - 1007 <= code && code <= 1014 || - 3000 <= code; - } - - public static WebSocketCloseStatus valueOf(int code) { - switch (code) { - case 1000: - return NORMAL_CLOSURE; - case 1001: - return ENDPOINT_UNAVAILABLE; - case 1002: - return PROTOCOL_ERROR; - case 1003: - return INVALID_MESSAGE_TYPE; - case 1005: - return EMPTY; - case 1006: - return ABNORMAL_CLOSURE; - case 1007: - return INVALID_PAYLOAD_DATA; - case 1008: - return POLICY_VIOLATION; - case 1009: - return MESSAGE_TOO_BIG; - case 1010: - return MANDATORY_EXTENSION; - case 1011: - return INTERNAL_SERVER_ERROR; - case 1012: - return SERVICE_RESTART; - case 1013: - return TRY_AGAIN_LATER; - case 1014: - return BAD_GATEWAY; - case 1015: - return TLS_HANDSHAKE_FAILED; - default: - return new WebSocketCloseStatus(code, "Close status #" + code); - } - } - -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketDecoderConfig.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketDecoderConfig.java deleted file mode 100644 index 93a2b31eee..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketDecoderConfig.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import java.util.Objects; - -/** - * Frames decoder configuration. - */ -public final class WebSocketDecoderConfig { - - static final WebSocketDecoderConfig DEFAULT = - new WebSocketDecoderConfig(65536, true, false, false, true, true); - - private final int maxFramePayloadLength; - private final boolean expectMaskedFrames; - private final boolean allowMaskMismatch; - private final boolean allowExtensions; - private final boolean closeOnProtocolViolation; - private final boolean withUTF8Validator; - - /** - * Constructor - * - * @param maxFramePayloadLength - * Maximum length of a frame's payload. Setting this to an appropriate value for you application - * helps check for denial of services attacks. - * @param expectMaskedFrames - * Web socket servers must set this to true processed incoming masked payload. Client implementations - * must set this to false. - * @param allowMaskMismatch - * Allows to loosen the masking requirement on received frames. When this is set to false then also - * frames which are not masked properly according to the standard will still be accepted. - * @param allowExtensions - * Flag to allow reserved extension bits to be used or not - * @param closeOnProtocolViolation - * Flag to send close frame immediately on any protocol violation.ion. - * @param withUTF8Validator - * Allows you to avoid adding of Utf8FrameValidator to the pipeline on the - * WebSocketServerProtocolHandler creation. This is useful (less overhead) - * when you use only BinaryWebSocketFrame within your web socket connection. - */ - private WebSocketDecoderConfig(int maxFramePayloadLength, boolean expectMaskedFrames, boolean allowMaskMismatch, - boolean allowExtensions, boolean closeOnProtocolViolation, - boolean withUTF8Validator) { - this.maxFramePayloadLength = maxFramePayloadLength; - this.expectMaskedFrames = expectMaskedFrames; - this.allowMaskMismatch = allowMaskMismatch; - this.allowExtensions = allowExtensions; - this.closeOnProtocolViolation = closeOnProtocolViolation; - this.withUTF8Validator = withUTF8Validator; - } - - public int maxFramePayloadLength() { - return maxFramePayloadLength; - } - - public boolean expectMaskedFrames() { - return expectMaskedFrames; - } - - public boolean allowMaskMismatch() { - return allowMaskMismatch; - } - - public boolean allowExtensions() { - return allowExtensions; - } - - public boolean closeOnProtocolViolation() { - return closeOnProtocolViolation; - } - - public boolean withUTF8Validator() { - return withUTF8Validator; - } - - @Override - public String toString() { - return "WebSocketDecoderConfig" + - " [maxFramePayloadLength=" + maxFramePayloadLength + - ", expectMaskedFrames=" + expectMaskedFrames + - ", allowMaskMismatch=" + allowMaskMismatch + - ", allowExtensions=" + allowExtensions + - ", closeOnProtocolViolation=" + closeOnProtocolViolation + - ", withUTF8Validator=" + withUTF8Validator + - "]"; - } - - public Builder toBuilder() { - return new Builder(this); - } - - public static Builder newBuilder() { - return new Builder(DEFAULT); - } - - public static final class Builder { - private int maxFramePayloadLength; - private boolean expectMaskedFrames; - private boolean allowMaskMismatch; - private boolean allowExtensions; - private boolean closeOnProtocolViolation; - private boolean withUTF8Validator; - - private Builder(WebSocketDecoderConfig decoderConfig) { - Objects.requireNonNull(decoderConfig, "decoderConfig"); - maxFramePayloadLength = decoderConfig.maxFramePayloadLength(); - expectMaskedFrames = decoderConfig.expectMaskedFrames(); - allowMaskMismatch = decoderConfig.allowMaskMismatch(); - allowExtensions = decoderConfig.allowExtensions(); - closeOnProtocolViolation = decoderConfig.closeOnProtocolViolation(); - withUTF8Validator = decoderConfig.withUTF8Validator(); - } - - public Builder maxFramePayloadLength(int maxFramePayloadLength) { - this.maxFramePayloadLength = maxFramePayloadLength; - return this; - } - - public Builder expectMaskedFrames(boolean expectMaskedFrames) { - this.expectMaskedFrames = expectMaskedFrames; - return this; - } - - public Builder allowMaskMismatch(boolean allowMaskMismatch) { - this.allowMaskMismatch = allowMaskMismatch; - return this; - } - - public Builder allowExtensions(boolean allowExtensions) { - this.allowExtensions = allowExtensions; - return this; - } - - public Builder closeOnProtocolViolation(boolean closeOnProtocolViolation) { - this.closeOnProtocolViolation = closeOnProtocolViolation; - return this; - } - - public Builder withUTF8Validator(boolean withUTF8Validator) { - this.withUTF8Validator = withUTF8Validator; - return this; - } - - public WebSocketDecoderConfig build() { - return new WebSocketDecoderConfig( - maxFramePayloadLength, expectMaskedFrames, allowMaskMismatch, - allowExtensions, closeOnProtocolViolation, withUTF8Validator); - } - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketFrame.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketFrame.java deleted file mode 100644 index 3cd0d1844f..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketFrame.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.DefaultByteBufHolder; -import io.netty.util.internal.StringUtil; - -/** - * Base class for web socket frames. - */ -public abstract class WebSocketFrame extends DefaultByteBufHolder { - - /** - * Flag to indicate if this frame is the final fragment in a message. The first fragment (frame) may also be the - * final fragment. - */ - private final boolean finalFragment; - - /** - * RSV1, RSV2, RSV3 used for extensions - */ - private final int rsv; - - protected WebSocketFrame(ByteBuf binaryData) { - this(true, 0, binaryData); - } - - protected WebSocketFrame(boolean finalFragment, int rsv, ByteBuf binaryData) { - super(binaryData); - this.finalFragment = finalFragment; - this.rsv = rsv; - } - - /** - * Flag to indicate if this frame is the final fragment in a message. The first fragment (frame) may also be the - * final fragment. - */ - public boolean isFinalFragment() { - return finalFragment; - } - - /** - * Bits used for extensions to the standard. - */ - public int rsv() { - return rsv; - } - - @Override - public WebSocketFrame copy() { - return (WebSocketFrame) super.copy(); - } - - @Override - public WebSocketFrame duplicate() { - return (WebSocketFrame) super.duplicate(); - } - - @Override - public WebSocketFrame retainedDuplicate() { - return (WebSocketFrame) super.retainedDuplicate(); - } - - @Override - public abstract WebSocketFrame replace(ByteBuf content); - - @Override - public String toString() { - return StringUtil.simpleClassName(this) + "(data: " + contentToString() + ')'; - } - - @Override - public WebSocketFrame retain() { - super.retain(); - return this; - } - - @Override - public WebSocketFrame retain(int increment) { - super.retain(increment); - return this; - } - - @Override - public WebSocketFrame touch() { - super.touch(); - return this; - } - - @Override - public WebSocketFrame touch(Object hint) { - super.touch(hint); - return this; - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketFrameAggregator.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketFrameAggregator.java deleted file mode 100644 index ef500c8dbd..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketFrameAggregator.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelPipeline; -import io.netty.handler.codec.MessageAggregator; -import io.netty.handler.codec.TooLongFrameException; - -/** - * Handler that aggregate fragmented WebSocketFrame's. - * - * Be aware if PING/PONG/CLOSE frames are send in the middle of a fragmented {@link WebSocketFrame} they will - * just get forwarded to the next handler in the pipeline. - */ -public class WebSocketFrameAggregator - extends MessageAggregator { - - /** - * Creates a new instance - * - * @param maxContentLength If the size of the aggregated frame exceeds this value, - * a {@link TooLongFrameException} is thrown. - */ - public WebSocketFrameAggregator(int maxContentLength) { - super(maxContentLength); - } - - @Override - protected boolean isStartMessage(WebSocketFrame msg) throws Exception { - return msg instanceof TextWebSocketFrame || msg instanceof BinaryWebSocketFrame; - } - - @Override - protected boolean isContentMessage(WebSocketFrame msg) throws Exception { - return msg instanceof ContinuationWebSocketFrame; - } - - @Override - protected boolean isLastContentMessage(ContinuationWebSocketFrame msg) throws Exception { - return isContentMessage(msg) && msg.isFinalFragment(); - } - - @Override - protected boolean isAggregated(WebSocketFrame msg) throws Exception { - if (msg.isFinalFragment()) { - return !isContentMessage(msg); - } - - return !isStartMessage(msg) && !isContentMessage(msg); - } - - @Override - protected boolean isContentLengthInvalid(WebSocketFrame start, int maxContentLength) { - return false; - } - - @Override - protected Object newContinueResponse(WebSocketFrame start, int maxContentLength, ChannelPipeline pipeline) { - return null; - } - - @Override - protected boolean closeAfterContinueResponse(Object msg) throws Exception { - throw new UnsupportedOperationException(); - } - - @Override - protected boolean ignoreContentAfterContinueResponse(Object msg) throws Exception { - throw new UnsupportedOperationException(); - } - - @Override - protected WebSocketFrame beginAggregation(WebSocketFrame start, ByteBuf content) throws Exception { - if (start instanceof TextWebSocketFrame) { - return new TextWebSocketFrame(true, start.rsv(), content); - } - - if (start instanceof BinaryWebSocketFrame) { - return new BinaryWebSocketFrame(true, start.rsv(), content); - } - - // Should not reach here. - throw new Error(); - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketFrameDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketFrameDecoder.java deleted file mode 100644 index 6964d882ca..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketFrameDecoder.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelPipeline; - -/** - * Marker interface which all WebSocketFrame decoders need to implement. - * - * This makes it easier to access the added encoder later in the {@link ChannelPipeline}. - */ -public interface WebSocketFrameDecoder extends ChannelHandler { -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketFrameEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketFrameEncoder.java deleted file mode 100644 index 08ab94855a..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketFrameEncoder.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelPipeline; - -/** - * Marker interface which all WebSocketFrame encoders need to implement. - * - * This makes it easier to access the added encoder later in the {@link ChannelPipeline}. - */ -public interface WebSocketFrameEncoder extends ChannelHandler { -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketHandshakeException.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketHandshakeException.java deleted file mode 100644 index dd0bf61194..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketHandshakeException.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -/** - * Exception during handshaking process - */ -public class WebSocketHandshakeException extends RuntimeException { - - private static final long serialVersionUID = 1L; - - public WebSocketHandshakeException(String s) { - super(s); - } - - public WebSocketHandshakeException(String s, Throwable throwable) { - super(s, throwable); - } -} 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 deleted file mode 100644 index 3436a04d50..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketProtocolHandler.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - - -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToMessageDecoder; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; - -import java.nio.channels.ClosedChannelException; -import java.util.concurrent.TimeUnit; - -abstract class WebSocketProtocolHandler extends MessageToMessageDecoder { - - private final boolean dropPongFrames; - private final WebSocketCloseStatus closeStatus; - private final long forceCloseTimeoutMillis; - private Promise closeSent; - - /** - * 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, null, 0L); - } - - WebSocketProtocolHandler(boolean dropPongFrames, - WebSocketCloseStatus closeStatus, - long forceCloseTimeoutMillis) { - this.dropPongFrames = dropPongFrames; - this.closeStatus = closeStatus; - this.forceCloseTimeoutMillis = forceCloseTimeoutMillis; - } - - @Override - protected void decode(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception { - if (frame instanceof PingWebSocketFrame) { - frame.content().retain(); - ctx.channel().writeAndFlush(new PongWebSocketFrame(frame.content())); - readIfNeeded(ctx); - return; - } - if (frame instanceof PongWebSocketFrame && dropPongFrames) { - readIfNeeded(ctx); - return; - } - - ctx.fireChannelRead(frame.retain()); - } - - private static void readIfNeeded(ChannelHandlerContext ctx) { - if (!ctx.channel().config().isAutoRead()) { - ctx.read(); - } - } - - @Override - public Future close(final ChannelHandlerContext ctx) { - if (closeStatus == null || !ctx.channel().isActive()) { - return ctx.close(); - } - final Future future = closeSent == null ? - write(ctx, new CloseWebSocketFrame(closeStatus)) : closeSent.asFuture(); - - flush(ctx); - applyCloseSentTimeout(ctx); - Promise promise = ctx.newPromise(); - future.addListener(f -> ctx.close().cascadeTo(promise)); - return promise.asFuture(); - } - - @Override - public Future write(final ChannelHandlerContext ctx, Object msg) { - if (closeSent != null) { - ReferenceCountUtil.release(msg); - return ctx.newFailedFuture(new ClosedChannelException()); - } - if (msg instanceof CloseWebSocketFrame) { - Promise promise = ctx.newPromise(); - closeSent(promise); - ctx.write(msg).cascadeTo(closeSent); - return promise.asFuture(); - } - return ctx.write(msg); - } - - void closeSent(Promise promise) { - closeSent = promise; - } - - private void applyCloseSentTimeout(ChannelHandlerContext ctx) { - if (closeSent.isDone() || forceCloseTimeoutMillis < 0) { - return; - } - - Future timeoutTask = ctx.executor().schedule(() -> { - if (!closeSent.isDone()) { - closeSent.tryFailure(buildHandshakeException("send close frame timed out")); - } - }, forceCloseTimeoutMillis, TimeUnit.MILLISECONDS); - - closeSent.asFuture().addListener(future -> timeoutTask.cancel()); - } - - /** - * Returns a {@link WebSocketHandshakeException} that depends on which client or server pipeline - * this handler belongs. Should be overridden in implementation otherwise a default exception is used. - */ - protected WebSocketHandshakeException buildHandshakeException(String message) { - return new WebSocketHandshakeException(message); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - ctx.fireExceptionCaught(cause); - ctx.close(); - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketScheme.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketScheme.java deleted file mode 100644 index 056d6b057b..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketScheme.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.util.AsciiString; - -/** - * Defines the common schemes used for the WebSocket protocol as defined by - * rfc6455. - */ -public final class WebSocketScheme { - /** - * Scheme for non-secure WebSocket connection. - */ - public static final WebSocketScheme WS = new WebSocketScheme(80, "ws"); - - /** - * Scheme for secure WebSocket connection. - */ - public static final WebSocketScheme WSS = new WebSocketScheme(443, "wss"); - - private final int port; - private final AsciiString name; - - private WebSocketScheme(int port, String name) { - this.port = port; - this.name = AsciiString.cached(name); - } - - public AsciiString name() { - return name; - } - - public int port() { - return port; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof WebSocketScheme)) { - return false; - } - WebSocketScheme other = (WebSocketScheme) o; - return other.port() == port && other.name().equals(name); - } - - @Override - public int hashCode() { - return port * 31 + name.hashCode(); - } - - @Override - public String toString() { - return name.toString(); - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakeException.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakeException.java deleted file mode 100644 index 456cf19f07..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakeException.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.handler.codec.http.DefaultHttpRequest; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.util.ReferenceCounted; - -/** - * Server exception during handshaking process. - * - *

IMPORTANT: This exception does not contain any {@link ReferenceCounted} fields - * e.g. {@link FullHttpRequest}, so no special treatment is needed. - */ -public final class WebSocketServerHandshakeException extends WebSocketHandshakeException { - - private static final long serialVersionUID = 1L; - - private final HttpRequest request; - - public WebSocketServerHandshakeException(String message) { - this(message, null); - } - - public WebSocketServerHandshakeException(String message, HttpRequest httpRequest) { - super(message); - if (httpRequest != null) { - request = new DefaultHttpRequest(httpRequest.protocolVersion(), httpRequest.method(), - httpRequest.uri(), httpRequest.headers()); - } else { - request = null; - } - } - - /** - * Returns a {@link HttpRequest request} if exception occurs during request validation otherwise {@code null}. - */ - public HttpRequest request() { - return request; - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker.java deleted file mode 100644 index 7cac8e8340..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker.java +++ /dev/null @@ -1,404 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelFutureListeners; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelOutboundInvoker; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpContentCompressor; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpObjectAggregator; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpRequestDecoder; -import io.netty.handler.codec.http.HttpResponseEncoder; -import io.netty.handler.codec.http.HttpServerCodec; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; -import io.netty.util.internal.EmptyArrays; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.nio.channels.ClosedChannelException; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.Set; - -import static java.util.Objects.requireNonNull; - -/** - * Base class for server side web socket opening and closing handshakes - */ -public abstract class WebSocketServerHandshaker { - protected static final InternalLogger logger = InternalLoggerFactory.getInstance(WebSocketServerHandshaker.class); - - private final String uri; - - private final String[] subprotocols; - - private final WebSocketVersion version; - - private final WebSocketDecoderConfig decoderConfig; - - private String selectedSubprotocol; - - /** - * Use this as wildcard to support all requested sub-protocols - */ - public static final String SUB_PROTOCOL_WILDCARD = "*"; - - /** - * Constructor specifying the destination web socket location - * - * @param version - * the protocol version - * @param uri - * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be - * sent to this URL. - * @param subprotocols - * CSV of supported protocols. Null if sub protocols not supported. - * @param maxFramePayloadLength - * Maximum length of a frame's payload - */ - protected WebSocketServerHandshaker( - WebSocketVersion version, String uri, String subprotocols, - int maxFramePayloadLength) { - this(version, uri, subprotocols, WebSocketDecoderConfig.newBuilder() - .maxFramePayloadLength(maxFramePayloadLength) - .build()); - } - - /** - * Constructor specifying the destination web socket location - * - * @param version - * the protocol version - * @param uri - * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be - * sent to this URL. - * @param subprotocols - * CSV of supported protocols. Null if sub protocols not supported. - * @param decoderConfig - * Frames decoder configuration. - */ - protected WebSocketServerHandshaker( - WebSocketVersion version, String uri, String subprotocols, WebSocketDecoderConfig decoderConfig) { - this.version = version; - this.uri = uri; - if (subprotocols != null) { - String[] subprotocolArray = subprotocols.split(","); - for (int i = 0; i < subprotocolArray.length; i++) { - subprotocolArray[i] = subprotocolArray[i].trim(); - } - this.subprotocols = subprotocolArray; - } else { - this.subprotocols = EmptyArrays.EMPTY_STRINGS; - } - this.decoderConfig = requireNonNull(decoderConfig, "decoderConfig"); - } - - /** - * Returns the URL of the web socket - */ - public String uri() { - return uri; - } - - /** - * Returns the CSV of supported sub protocols - */ - public Set subprotocols() { - Set ret = new LinkedHashSet<>(); - Collections.addAll(ret, subprotocols); - return ret; - } - - /** - * Returns the version of the specification being supported - */ - public WebSocketVersion version() { - return version; - } - - /** - * Gets the maximum length for any frame's payload. - * - * @return The maximum length for a frame's payload - */ - public int maxFramePayloadLength() { - return decoderConfig.maxFramePayloadLength(); - } - - /** - * Gets this decoder configuration. - * - * @return This decoder configuration. - */ - public WebSocketDecoderConfig decoderConfig() { - return decoderConfig; - } - - /** - * Performs the opening handshake. When call this method you MUST NOT retain the - * {@link FullHttpRequest} which is passed in. - * - * @param channel - * Channel - * @param req - * HTTP Request - * @return future - * The {@link Future} which is notified once the opening handshake completes - */ - public Future handshake(Channel channel, FullHttpRequest req) { - return handshake(channel, req, null); - } - - /** - * Performs the opening handshake - * - * When call this method you MUST NOT retain the {@link FullHttpRequest} which is passed in. - * - * @param channel - * Channel - * @param req - * HTTP Request - * @param responseHeaders - * Extra headers to add to the handshake response or {@code null} if no extra headers should be added - * @return future - * the {@link Future} which is notified when the opening handshake is done - */ - public final Future handshake(Channel channel, FullHttpRequest req, HttpHeaders responseHeaders) { - - if (logger.isDebugEnabled()) { - logger.debug("{} WebSocket version {} server handshake", channel, version()); - } - FullHttpResponse response = newHandshakeResponse(req, responseHeaders); - ChannelPipeline p = channel.pipeline(); - if (p.get(HttpObjectAggregator.class) != null) { - p.remove(HttpObjectAggregator.class); - } - if (p.get(HttpContentCompressor.class) != null) { - p.remove(HttpContentCompressor.class); - } - ChannelHandlerContext ctx = p.context(HttpRequestDecoder.class); - final String encoderName; - if (ctx == null) { - // this means the user use an HttpServerCodec - ctx = p.context(HttpServerCodec.class); - if (ctx == null) { - return channel.newFailedFuture( - new IllegalStateException("No HttpDecoder and no HttpServerCodec in the pipeline")); - } - p.addBefore(ctx.name(), "wsencoder", newWebSocketEncoder()); - p.addBefore(ctx.name(), "wsdecoder", newWebsocketDecoder()); - encoderName = ctx.name(); - } else { - p.replace(ctx.name(), "wsdecoder", newWebsocketDecoder()); - - encoderName = p.context(HttpResponseEncoder.class).name(); - p.addBefore(encoderName, "wsencoder", newWebSocketEncoder()); - } - return channel.writeAndFlush(response).addListener(channel, (ch, future) -> { - if (future.isSuccess()) { - ChannelPipeline p1 = ch.pipeline(); - p1.remove(encoderName); - } - }); - } - - /** - * Performs the opening handshake. When call this method you MUST NOT retain the - * {@link FullHttpRequest} which is passed in. - * - * @param channel - * Channel - * @param req - * HTTP Request - * @return future - * The {@link Future} which is notified once the opening handshake completes - */ - public Future handshake(Channel channel, HttpRequest req) { - return handshake(channel, req, null); - } - - /** - * Performs the opening handshake - * - * When call this method you MUST NOT retain the {@link HttpRequest} which is passed in. - * - * @param channel - * Channel - * @param req - * HTTP Request - * @param responseHeaders - * Extra headers to add to the handshake response or {@code null} if no extra headers should be added - * @return future - * the {@link Future} which is notified when the opening handshake is done - */ - public final Future handshake(final Channel channel, HttpRequest req, - final HttpHeaders responseHeaders) { - - if (req instanceof FullHttpRequest) { - return handshake(channel, (FullHttpRequest) req, responseHeaders); - } - if (logger.isDebugEnabled()) { - logger.debug("{} WebSocket version {} server handshake", channel, version()); - } - ChannelPipeline p = channel.pipeline(); - ChannelHandlerContext ctx = p.context(HttpRequestDecoder.class); - if (ctx == null) { - // this means the user use an HttpServerCodec - ctx = p.context(HttpServerCodec.class); - if (ctx == null) { - return channel.newFailedFuture( - new IllegalStateException("No HttpDecoder and no HttpServerCodec in the pipeline")); - } - } - - Promise promise = channel.newPromise(); - // Add aggregator and ensure we feed the HttpRequest so it is aggregated. A limit o 8192 should be more then - // enough for the websockets handshake payload. - // - // TODO: Make handshake work without HttpObjectAggregator at all. - String aggregatorName = "httpAggregator"; - p.addAfter(ctx.name(), aggregatorName, new HttpObjectAggregator(8192)); - p.addAfter(aggregatorName, "handshaker", new SimpleChannelInboundHandler() { - @Override - protected void messageReceived(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception { - // Remove ourself and do the actual handshake - ctx.pipeline().remove(this); - handshake(channel, msg, responseHeaders); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - // Remove ourself and fail the handshake promise. - promise.tryFailure(cause); - ctx.fireExceptionCaught(cause); - ctx.pipeline().remove(this); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - // Fail promise if Channel was closed - if (!promise.isDone()) { - promise.tryFailure(new ClosedChannelException()); - } - ctx.fireChannelInactive(); - } - }); - try { - ctx.fireChannelRead(ReferenceCountUtil.retain(req)); - } catch (Throwable cause) { - promise.setFailure(cause); - } - return promise.asFuture(); - } - - /** - * Returns a new {@link FullHttpResponse) which will be used for as response to the handshake request. - */ - protected abstract FullHttpResponse newHandshakeResponse(FullHttpRequest req, - HttpHeaders responseHeaders); - /** - * Performs the closing handshake. - * - * When called from within a {@link ChannelHandler} you most likely want to use - * {@link #close(ChannelHandlerContext, CloseWebSocketFrame)}. - * - * @param channel - * the {@link Channel} to use. - * @param frame - * Closing Frame that was received. - */ - public Future close(Channel channel, CloseWebSocketFrame frame) { - requireNonNull(channel, "channel"); - return close0(channel, frame); - } - - /** - * Performs the closing handshake. - * - * @param ctx - * the {@link ChannelHandlerContext} to use. - * @param frame - * Closing Frame that was received. - */ - public Future close(ChannelHandlerContext ctx, CloseWebSocketFrame frame) { - requireNonNull(ctx, "ctx"); - return close0(ctx, frame); - } - - private static Future close0(ChannelOutboundInvoker invoker, CloseWebSocketFrame frame) { - return invoker.writeAndFlush(frame).addListener(invoker, ChannelFutureListeners.CLOSE); - } - - /** - * Selects the first matching supported sub protocol - * - * @param requestedSubprotocols - * CSV of protocols to be supported. e.g. "chat, superchat" - * @return First matching supported sub protocol. Null if not found. - */ - protected String selectSubprotocol(String requestedSubprotocols) { - if (requestedSubprotocols == null || subprotocols.length == 0) { - return null; - } - - String[] requestedSubprotocolArray = requestedSubprotocols.split(","); - for (String p: requestedSubprotocolArray) { - String requestedSubprotocol = p.trim(); - - for (String supportedSubprotocol: subprotocols) { - if (SUB_PROTOCOL_WILDCARD.equals(supportedSubprotocol) - || requestedSubprotocol.equals(supportedSubprotocol)) { - selectedSubprotocol = requestedSubprotocol; - return requestedSubprotocol; - } - } - } - - // No match found - return null; - } - - /** - * Returns the selected subprotocol. Null if no subprotocol has been selected. - *

- * This is only available AFTER handshake() has been called. - *

- */ - public String selectedSubprotocol() { - return selectedSubprotocol; - } - - /** - * Returns the decoder to use after handshake is complete. - */ - protected abstract WebSocketFrameDecoder newWebsocketDecoder(); - - /** - * Returns the encoder to use after the handshake is complete. - */ - protected abstract WebSocketFrameEncoder newWebSocketEncoder(); - -} 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 deleted file mode 100644 index 92cd057eea..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00.java +++ /dev/null @@ -1,235 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http.DefaultFullHttpResponse; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.util.concurrent.Future; - -import java.util.regex.Pattern; - -import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; - -/** - *

- * Performs server side opening and closing handshakes for web socket specification version draft-ietf-hybi-thewebsocketprotocol- - * 00 - *

- *

- * A very large portion of this code was taken from the Netty 3.2 HTTP example. - *

- */ -public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker { - - private static final Pattern BEGINNING_DIGIT = Pattern.compile("[^0-9]"); - private static final Pattern BEGINNING_SPACE = Pattern.compile("[^ ]"); - - /** - * Constructor specifying the destination web socket location - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be - * sent to this URL. - * @param subprotocols - * CSV of supported protocols - * @param maxFramePayloadLength - * Maximum allowable frame payload length. Setting this value to your application's requirement may - * reduce denial of service attacks using long data frames. - */ - public WebSocketServerHandshaker00(String webSocketURL, String subprotocols, int maxFramePayloadLength) { - this(webSocketURL, subprotocols, WebSocketDecoderConfig.newBuilder() - .maxFramePayloadLength(maxFramePayloadLength) - .build()); - } - - /** - * Constructor specifying the destination web socket location - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be - * sent to this URL. - * @param subprotocols - * CSV of supported protocols - * @param decoderConfig - * Frames decoder configuration. - */ - public WebSocketServerHandshaker00(String webSocketURL, String subprotocols, WebSocketDecoderConfig decoderConfig) { - super(WebSocketVersion.V00, webSocketURL, subprotocols, decoderConfig); - } - - /** - *

- * Handle the web socket handshake for the web socket specification HyBi version 0 and lower. This standard - * is really a rehash of hixie-76 and - * hixie-75. - *

- * - *

- * Browser request to the server: - *

- * - *
-     * GET /demo HTTP/1.1
-     * Upgrade: WebSocket
-     * Connection: Upgrade
-     * Host: example.com
-     * Origin: http://example.com
-     * Sec-WebSocket-Protocol: chat, sample
-     * Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5
-     * Sec-WebSocket-Key2: 12998 5 Y3 1  .P00
-     *
-     * ^n:ds[4U
-     * 
- * - *

- * Server response: - *

- * - *
-     * HTTP/1.1 101 WebSocket Protocol Handshake
-     * Upgrade: WebSocket
-     * Connection: Upgrade
-     * Sec-WebSocket-Origin: http://example.com
-     * Sec-WebSocket-Location: ws://example.com/demo
-     * Sec-WebSocket-Protocol: sample
-     *
-     * 8jKS'y:G*Co,Wxa-
-     * 
- */ - @Override - protected FullHttpResponse newHandshakeResponse(FullHttpRequest req, HttpHeaders headers) { - - // Serve the WebSocket handshake request. - if (!req.headers().containsValue(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE, true) - || !HttpHeaderValues.WEBSOCKET.contentEqualsIgnoreCase(req.headers().get(HttpHeaderNames.UPGRADE))) { - throw new WebSocketServerHandshakeException("not a WebSocket handshake request: missing upgrade", req); - } - - // Hixie 75 does not contain these headers while Hixie 76 does - boolean isHixie76 = req.headers().contains(HttpHeaderNames.SEC_WEBSOCKET_KEY1) && - req.headers().contains(HttpHeaderNames.SEC_WEBSOCKET_KEY2); - - String origin = req.headers().get(HttpHeaderNames.ORIGIN); - //throw before allocating FullHttpResponse - if (origin == null && !isHixie76) { - throw new WebSocketServerHandshakeException("Missing origin header, got only " + req.headers().names(), - req); - } - - // Create the WebSocket handshake response. - FullHttpResponse res = new DefaultFullHttpResponse(HTTP_1_1, new HttpResponseStatus(101, - isHixie76 ? "WebSocket Protocol Handshake" : "Web Socket Protocol Handshake"), - req.content().alloc().buffer(0)); - if (headers != null) { - res.headers().add(headers); - } - - res.headers().set(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET) - .set(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE); - - // Fill in the headers and contents depending on handshake getMethod. - if (isHixie76) { - // New handshake getMethod with a challenge: - res.headers().add(HttpHeaderNames.SEC_WEBSOCKET_ORIGIN, origin); - res.headers().add(HttpHeaderNames.SEC_WEBSOCKET_LOCATION, uri()); - - String subprotocols = req.headers().get(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL); - if (subprotocols != null) { - String selectedSubprotocol = selectSubprotocol(subprotocols); - if (selectedSubprotocol == null) { - if (logger.isDebugEnabled()) { - logger.debug("Requested subprotocol(s) not supported: {}", subprotocols); - } - } else { - res.headers().set(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol); - } - } - - // Calculate the answer of the challenge. - String key1 = req.headers().get(HttpHeaderNames.SEC_WEBSOCKET_KEY1); - String key2 = req.headers().get(HttpHeaderNames.SEC_WEBSOCKET_KEY2); - int a = (int) (Long.parseLong(BEGINNING_DIGIT.matcher(key1).replaceAll("")) / - BEGINNING_SPACE.matcher(key1).replaceAll("").length()); - int b = (int) (Long.parseLong(BEGINNING_DIGIT.matcher(key2).replaceAll("")) / - BEGINNING_SPACE.matcher(key2).replaceAll("").length()); - long c = req.content().readLong(); - ByteBuf input = Unpooled.wrappedBuffer(new byte[16]).setIndex(0, 0); - input.writeInt(a); - input.writeInt(b); - input.writeLong(c); - res.content().writeBytes(WebSocketUtil.md5(input.array())); - } else { - // Old Hixie 75 handshake getMethod with no challenge: - res.headers().add(HttpHeaderNames.WEBSOCKET_ORIGIN, origin); - res.headers().add(HttpHeaderNames.WEBSOCKET_LOCATION, uri()); - - String protocol = req.headers().get(HttpHeaderNames.WEBSOCKET_PROTOCOL); - if (protocol != null) { - res.headers().set(HttpHeaderNames.WEBSOCKET_PROTOCOL, selectSubprotocol(protocol)); - } - } - return res; - } - - /** - * Echo back the closing frame - * - * @param channel - * the {@link Channel} to use. - * @param frame - * Web Socket frame that was received. - * @return the {@link Future} which will be notified once the operations completes. - */ - @Override - public Future close(Channel channel, CloseWebSocketFrame frame) { - return channel.writeAndFlush(frame); - } - - /** - * Echo back the closing frame - * - * @param ctx - * the {@link ChannelHandlerContext} to use. - * @param frame - * Closing Frame that was received. - * @return the {@link ChannelFuture} which will be notified once the operations completes. - */ - @Override - public Future close(ChannelHandlerContext ctx, CloseWebSocketFrame frame) { - return ctx.writeAndFlush(frame); - } - - @Override - protected WebSocketFrameDecoder newWebsocketDecoder() { - return new WebSocket00FrameDecoder(decoderConfig()); - } - - @Override - protected WebSocketFrameEncoder newWebSocketEncoder() { - return new WebSocket00FrameEncoder(); - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker07.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker07.java deleted file mode 100644 index 3356e7dc5a..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker07.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.handler.codec.http.DefaultFullHttpResponse; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.util.CharsetUtil; - -import static io.netty.handler.codec.http.HttpVersion.*; - -/** - *

- * Performs server side opening and closing handshakes for web socket specification version draft-ietf-hybi-thewebsocketprotocol- - * 10 - *

- */ -public class WebSocketServerHandshaker07 extends WebSocketServerHandshaker { - - public static final String WEBSOCKET_07_ACCEPT_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - - /** - * Constructor specifying the destination web socket location - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". - * Subsequent web socket frames will be sent to this URL. - * @param subprotocols - * CSV of supported protocols - * @param allowExtensions - * Allow extensions to be used in the reserved bits of the web socket frame - * @param maxFramePayloadLength - * Maximum allowable frame payload length. Setting this value to your application's - * requirement may reduce denial of service attacks using long data frames. - */ - public WebSocketServerHandshaker07( - String webSocketURL, String subprotocols, boolean allowExtensions, int maxFramePayloadLength) { - this(webSocketURL, subprotocols, allowExtensions, maxFramePayloadLength, false); - } - - /** - * Constructor specifying the destination web socket location - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". - * Subsequent web socket frames will be sent to this URL. - * @param subprotocols - * CSV of supported protocols - * @param allowExtensions - * Allow extensions to be used in the reserved bits of the web socket frame - * @param maxFramePayloadLength - * Maximum allowable frame payload length. Setting this value to your application's - * requirement may reduce denial of service attacks using long data frames. - * @param allowMaskMismatch - * When set to true, frames which are not masked properly according to the standard will still be - * accepted. - */ - public WebSocketServerHandshaker07( - String webSocketURL, String subprotocols, boolean allowExtensions, int maxFramePayloadLength, - boolean allowMaskMismatch) { - this(webSocketURL, subprotocols, WebSocketDecoderConfig.newBuilder() - .allowExtensions(allowExtensions) - .maxFramePayloadLength(maxFramePayloadLength) - .allowMaskMismatch(allowMaskMismatch) - .build()); - } - - /** - * Constructor specifying the destination web socket location - * - * @param decoderConfig - * Frames decoder configuration. - */ - public WebSocketServerHandshaker07(String webSocketURL, String subprotocols, WebSocketDecoderConfig decoderConfig) { - super(WebSocketVersion.V07, webSocketURL, subprotocols, decoderConfig); - } - - /** - *

- * Handle the web socket handshake for the web socket specification HyBi version 7. - *

- * - *

- * Browser request to the server: - *

- * - *
-     * GET /chat HTTP/1.1
-     * Host: server.example.com
-     * Upgrade: websocket
-     * Connection: Upgrade
-     * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
-     * Sec-WebSocket-Origin: http://example.com
-     * Sec-WebSocket-Protocol: chat, superchat
-     * Sec-WebSocket-Version: 7
-     * 
- * - *

- * Server response: - *

- * - *
-     * HTTP/1.1 101 Switching Protocols
-     * Upgrade: websocket
-     * Connection: Upgrade
-     * Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
-     * Sec-WebSocket-Protocol: chat
-     * 
- */ - @Override - protected FullHttpResponse newHandshakeResponse(FullHttpRequest req, HttpHeaders headers) { - CharSequence key = req.headers().get(HttpHeaderNames.SEC_WEBSOCKET_KEY); - if (key == null) { - throw new WebSocketServerHandshakeException("not a WebSocket request: missing key", req); - } - - FullHttpResponse res = - new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.SWITCHING_PROTOCOLS, - req.content().alloc().buffer(0)); - - if (headers != null) { - res.headers().add(headers); - } - - String acceptSeed = key + WEBSOCKET_07_ACCEPT_GUID; - byte[] sha1 = WebSocketUtil.sha1(acceptSeed.getBytes(CharsetUtil.US_ASCII)); - String accept = WebSocketUtil.base64(sha1); - - if (logger.isDebugEnabled()) { - logger.debug("WebSocket version 07 server handshake key: {}, response: {}.", key, accept); - } - - res.headers().set(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET) - .set(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE) - .set(HttpHeaderNames.SEC_WEBSOCKET_ACCEPT, accept); - - String subprotocols = req.headers().get(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL); - if (subprotocols != null) { - String selectedSubprotocol = selectSubprotocol(subprotocols); - if (selectedSubprotocol == null) { - if (logger.isDebugEnabled()) { - logger.debug("Requested subprotocol(s) not supported: {}", subprotocols); - } - } else { - res.headers().set(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol); - } - } - return res; - } - - @Override - protected WebSocketFrameDecoder newWebsocketDecoder() { - return new WebSocket07FrameDecoder(decoderConfig()); - } - - @Override - protected WebSocketFrameEncoder newWebSocketEncoder() { - return new WebSocket07FrameEncoder(false); - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08.java deleted file mode 100644 index 8a13a94e22..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.handler.codec.http.DefaultFullHttpResponse; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.util.CharsetUtil; - -import static io.netty.handler.codec.http.HttpVersion.*; - -/** - *

- * Performs server side opening and closing handshakes for web socket specification version draft-ietf-hybi-thewebsocketprotocol- - * 10 - *

- */ -public class WebSocketServerHandshaker08 extends WebSocketServerHandshaker { - - public static final String WEBSOCKET_08_ACCEPT_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - - /** - * Constructor specifying the destination web socket location - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". - * Subsequent web socket frames will be sent to this URL. - * @param subprotocols - * CSV of supported protocols - * @param allowExtensions - * Allow extensions to be used in the reserved bits of the web socket frame - * @param maxFramePayloadLength - * Maximum allowable frame payload length. Setting this value to your application's - * requirement may reduce denial of service attacks using long data frames. - */ - public WebSocketServerHandshaker08( - String webSocketURL, String subprotocols, boolean allowExtensions, int maxFramePayloadLength) { - this(webSocketURL, subprotocols, allowExtensions, maxFramePayloadLength, false); - } - - /** - * Constructor specifying the destination web socket location - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". - * Subsequent web socket frames will be sent to this URL. - * @param subprotocols - * CSV of supported protocols - * @param allowExtensions - * Allow extensions to be used in the reserved bits of the web socket frame - * @param maxFramePayloadLength - * Maximum allowable frame payload length. Setting this value to your application's - * requirement may reduce denial of service attacks using long data frames. - * @param allowMaskMismatch - * When set to true, frames which are not masked properly according to the standard will still be - * accepted. - */ - public WebSocketServerHandshaker08( - String webSocketURL, String subprotocols, boolean allowExtensions, int maxFramePayloadLength, - boolean allowMaskMismatch) { - this(webSocketURL, subprotocols, WebSocketDecoderConfig.newBuilder() - .allowExtensions(allowExtensions) - .maxFramePayloadLength(maxFramePayloadLength) - .allowMaskMismatch(allowMaskMismatch) - .build()); - } - - /** - * Constructor specifying the destination web socket location - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". - * Subsequent web socket frames will be sent to this URL. - * @param subprotocols - * CSV of supported protocols - * @param decoderConfig - * Frames decoder configuration. - */ - public WebSocketServerHandshaker08( - String webSocketURL, String subprotocols, WebSocketDecoderConfig decoderConfig) { - super(WebSocketVersion.V08, webSocketURL, subprotocols, decoderConfig); - } - - /** - *

- * Handle the web socket handshake for the web socket specification HyBi version 8 to 10. Version 8, 9 and - * 10 share the same wire protocol. - *

- * - *

- * Browser request to the server: - *

- * - *
-     * GET /chat HTTP/1.1
-     * Host: server.example.com
-     * Upgrade: websocket
-     * Connection: Upgrade
-     * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
-     * Sec-WebSocket-Origin: http://example.com
-     * Sec-WebSocket-Protocol: chat, superchat
-     * Sec-WebSocket-Version: 8
-     * 
- * - *

- * Server response: - *

- * - *
-     * HTTP/1.1 101 Switching Protocols
-     * Upgrade: websocket
-     * Connection: Upgrade
-     * Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
-     * Sec-WebSocket-Protocol: chat
-     * 
- */ - @Override - protected FullHttpResponse newHandshakeResponse(FullHttpRequest req, HttpHeaders headers) { - CharSequence key = req.headers().get(HttpHeaderNames.SEC_WEBSOCKET_KEY); - if (key == null) { - throw new WebSocketServerHandshakeException("not a WebSocket request: missing key", req); - } - - FullHttpResponse res = new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.SWITCHING_PROTOCOLS, - req.content().alloc().buffer(0)); - - if (headers != null) { - res.headers().add(headers); - } - - String acceptSeed = key + WEBSOCKET_08_ACCEPT_GUID; - byte[] sha1 = WebSocketUtil.sha1(acceptSeed.getBytes(CharsetUtil.US_ASCII)); - String accept = WebSocketUtil.base64(sha1); - - if (logger.isDebugEnabled()) { - logger.debug("WebSocket version 08 server handshake key: {}, response: {}", key, accept); - } - - res.headers().set(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET) - .set(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE) - .set(HttpHeaderNames.SEC_WEBSOCKET_ACCEPT, accept); - - String subprotocols = req.headers().get(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL); - if (subprotocols != null) { - String selectedSubprotocol = selectSubprotocol(subprotocols); - if (selectedSubprotocol == null) { - if (logger.isDebugEnabled()) { - logger.debug("Requested subprotocol(s) not supported: {}", subprotocols); - } - } else { - res.headers().set(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol); - } - } - return res; - } - - @Override - protected WebSocketFrameDecoder newWebsocketDecoder() { - return new WebSocket08FrameDecoder(decoderConfig()); - } - - @Override - protected WebSocketFrameEncoder newWebSocketEncoder() { - return new WebSocket08FrameEncoder(false); - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13.java deleted file mode 100644 index 869c65eb50..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.handler.codec.http.DefaultFullHttpResponse; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.util.CharsetUtil; - -import static io.netty.handler.codec.http.HttpVersion.*; - -/** - *

- * Performs server side opening and closing handshakes for RFC 6455 - * (originally web socket specification draft-ietf-hybi-thewebsocketprotocol-17). - *

- */ -public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker { - - public static final String WEBSOCKET_13_ACCEPT_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - - /** - * Constructor specifying the destination web socket location - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web - * socket frames will be sent to this URL. - * @param subprotocols - * CSV of supported protocols - * @param allowExtensions - * Allow extensions to be used in the reserved bits of the web socket frame - * @param maxFramePayloadLength - * Maximum allowable frame payload length. Setting this value to your application's - * requirement may reduce denial of service attacks using long data frames. - */ - public WebSocketServerHandshaker13( - String webSocketURL, String subprotocols, boolean allowExtensions, int maxFramePayloadLength) { - this(webSocketURL, subprotocols, allowExtensions, maxFramePayloadLength, false); - } - - /** - * Constructor specifying the destination web socket location - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web - * socket frames will be sent to this URL. - * @param subprotocols - * CSV of supported protocols - * @param allowExtensions - * Allow extensions to be used in the reserved bits of the web socket frame - * @param maxFramePayloadLength - * Maximum allowable frame payload length. Setting this value to your application's - * requirement may reduce denial of service attacks using long data frames. - * @param allowMaskMismatch - * When set to true, frames which are not masked properly according to the standard will still be - * accepted. - */ - public WebSocketServerHandshaker13( - String webSocketURL, String subprotocols, boolean allowExtensions, int maxFramePayloadLength, - boolean allowMaskMismatch) { - this(webSocketURL, subprotocols, WebSocketDecoderConfig.newBuilder() - .allowExtensions(allowExtensions) - .maxFramePayloadLength(maxFramePayloadLength) - .allowMaskMismatch(allowMaskMismatch) - .build()); - } - - /** - * Constructor specifying the destination web socket location - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web - * socket frames will be sent to this URL. - * @param subprotocols - * CSV of supported protocols - * @param decoderConfig - * Frames decoder configuration. - */ - public WebSocketServerHandshaker13( - String webSocketURL, String subprotocols, WebSocketDecoderConfig decoderConfig) { - super(WebSocketVersion.V13, webSocketURL, subprotocols, decoderConfig); - } - - /** - *

- * Handle the web socket handshake for the web socket specification HyBi versions 13-17. Versions 13-17 - * share the same wire protocol. - *

- * - *

- * Browser request to the server: - *

- * - *
-     * GET /chat HTTP/1.1
-     * Host: server.example.com
-     * Upgrade: websocket
-     * Connection: Upgrade
-     * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
-     * Origin: http://example.com
-     * Sec-WebSocket-Protocol: chat, superchat
-     * Sec-WebSocket-Version: 13
-     * 
- * - *

- * Server response: - *

- * - *
-     * HTTP/1.1 101 Switching Protocols
-     * Upgrade: websocket
-     * Connection: Upgrade
-     * Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
-     * Sec-WebSocket-Protocol: chat
-     * 
- */ - @Override - protected FullHttpResponse newHandshakeResponse(FullHttpRequest req, HttpHeaders headers) { - CharSequence key = req.headers().get(HttpHeaderNames.SEC_WEBSOCKET_KEY); - if (key == null) { - throw new WebSocketServerHandshakeException("not a WebSocket request: missing key", req); - } - - FullHttpResponse res = new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.SWITCHING_PROTOCOLS, - req.content().alloc().buffer(0)); - if (headers != null) { - res.headers().add(headers); - } - - String acceptSeed = key + WEBSOCKET_13_ACCEPT_GUID; - byte[] sha1 = WebSocketUtil.sha1(acceptSeed.getBytes(CharsetUtil.US_ASCII)); - String accept = WebSocketUtil.base64(sha1); - - if (logger.isDebugEnabled()) { - logger.debug("WebSocket version 13 server handshake key: {}, response: {}", key, accept); - } - - res.headers().set(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET) - .set(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE) - .set(HttpHeaderNames.SEC_WEBSOCKET_ACCEPT, accept); - - String subprotocols = req.headers().get(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL); - if (subprotocols != null) { - String selectedSubprotocol = selectSubprotocol(subprotocols); - if (selectedSubprotocol == null) { - if (logger.isDebugEnabled()) { - logger.debug("Requested subprotocol(s) not supported: {}", subprotocols); - } - } else { - res.headers().set(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol); - } - } - return res; - } - - @Override - protected WebSocketFrameDecoder newWebsocketDecoder() { - return new WebSocket13FrameDecoder(decoderConfig()); - } - - @Override - protected WebSocketFrameEncoder newWebSocketEncoder() { - return new WebSocket13FrameEncoder(false); - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakerFactory.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakerFactory.java deleted file mode 100644 index 387f88fd07..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakerFactory.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.channel.Channel; - -import io.netty.handler.codec.http.DefaultFullHttpResponse; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpUtil; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.util.concurrent.Future; - -import java.util.Objects; - -/** - * Auto-detects the version of the Web Socket protocol in use and creates a new proper - * {@link WebSocketServerHandshaker}. - */ -public class WebSocketServerHandshakerFactory { - - private final String webSocketURL; - - private final String subprotocols; - - private final WebSocketDecoderConfig decoderConfig; - - /** - * Constructor specifying the destination web socket location - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". - * Subsequent web socket frames will be sent to this URL. - * @param subprotocols - * CSV of supported protocols. Null if sub protocols not supported. - * @param allowExtensions - * Allow extensions to be used in the reserved bits of the web socket frame - */ - public WebSocketServerHandshakerFactory( - String webSocketURL, String subprotocols, boolean allowExtensions) { - this(webSocketURL, subprotocols, allowExtensions, 65536); - } - - /** - * Constructor specifying the destination web socket location - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". - * Subsequent web socket frames will be sent to this URL. - * @param subprotocols - * CSV of supported protocols. Null if sub protocols not supported. - * @param allowExtensions - * Allow extensions to be used in the reserved bits of the web socket frame - * @param maxFramePayloadLength - * Maximum allowable frame payload length. Setting this value to your application's - * requirement may reduce denial of service attacks using long data frames. - */ - public WebSocketServerHandshakerFactory( - String webSocketURL, String subprotocols, boolean allowExtensions, - int maxFramePayloadLength) { - this(webSocketURL, subprotocols, allowExtensions, maxFramePayloadLength, false); - } - - /** - * Constructor specifying the destination web socket location - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". - * Subsequent web socket frames will be sent to this URL. - * @param subprotocols - * CSV of supported protocols. Null if sub protocols not supported. - * @param allowExtensions - * Allow extensions to be used in the reserved bits of the web socket frame - * @param maxFramePayloadLength - * Maximum allowable frame payload length. Setting this value to your application's - * requirement may reduce denial of service attacks using long data frames. - * @param allowMaskMismatch - * When set to true, frames which are not masked properly according to the standard will still be - * accepted. - */ - public WebSocketServerHandshakerFactory( - String webSocketURL, String subprotocols, boolean allowExtensions, - int maxFramePayloadLength, boolean allowMaskMismatch) { - this(webSocketURL, subprotocols, WebSocketDecoderConfig.newBuilder() - .allowExtensions(allowExtensions) - .maxFramePayloadLength(maxFramePayloadLength) - .allowMaskMismatch(allowMaskMismatch) - .build()); - } - - /** - * Constructor specifying the destination web socket location - * - * @param webSocketURL - * URL for web socket communications. e.g "ws://myhost.com/mypath". - * Subsequent web socket frames will be sent to this URL. - * @param subprotocols - * CSV of supported protocols. Null if sub protocols not supported. - * @param decoderConfig - * Frames decoder options. - */ - public WebSocketServerHandshakerFactory( - String webSocketURL, String subprotocols, WebSocketDecoderConfig decoderConfig) { - this.webSocketURL = webSocketURL; - this.subprotocols = subprotocols; - this.decoderConfig = Objects.requireNonNull(decoderConfig, "decoderConfig"); - } - - /** - * Instances a new handshaker - * - * @return A new WebSocketServerHandshaker for the requested web socket version. Null if web - * socket version is not supported. - */ - public WebSocketServerHandshaker newHandshaker(HttpRequest req) { - - CharSequence version = req.headers().get(HttpHeaderNames.SEC_WEBSOCKET_VERSION); - if (version != null) { - if (version.equals(WebSocketVersion.V13.toHttpHeaderValue())) { - // Version 13 of the wire protocol - RFC 6455 (version 17 of the draft hybi specification). - return new WebSocketServerHandshaker13( - webSocketURL, subprotocols, decoderConfig); - } else if (version.equals(WebSocketVersion.V08.toHttpHeaderValue())) { - // Version 8 of the wire protocol - version 10 of the draft hybi specification. - return new WebSocketServerHandshaker08( - webSocketURL, subprotocols, decoderConfig); - } else if (version.equals(WebSocketVersion.V07.toHttpHeaderValue())) { - // Version 8 of the wire protocol - version 07 of the draft hybi specification. - return new WebSocketServerHandshaker07( - webSocketURL, subprotocols, decoderConfig); - } else { - return null; - } - } else { - // Assume version 00 where version header was not specified - return new WebSocketServerHandshaker00(webSocketURL, subprotocols, decoderConfig); - } - } - - /** - * @deprecated use {@link #sendUnsupportedVersionResponse(Channel)} - */ - @Deprecated - public static void sendUnsupportedWebSocketVersionResponse(Channel channel) { - sendUnsupportedVersionResponse(channel); - } - - /** - * Return that we need cannot not support the web socket version - */ - public static Future sendUnsupportedVersionResponse(Channel channel) { - HttpResponse res = new DefaultFullHttpResponse( - HttpVersion.HTTP_1_1, - HttpResponseStatus.UPGRADE_REQUIRED, channel.alloc().buffer(0)); - res.headers().set(HttpHeaderNames.SEC_WEBSOCKET_VERSION, WebSocketVersion.V13.toHttpHeaderValue()); - HttpUtil.setContentLength(res, 0); - return channel.writeAndFlush(res); - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolConfig.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolConfig.java deleted file mode 100644 index 6f84edd559..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolConfig.java +++ /dev/null @@ -1,297 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler.ClientHandshakeStateEvent; - -import java.util.Objects; - -import static io.netty.util.internal.ObjectUtil.checkPositive; - -/** - * WebSocket server configuration. - */ -public final class WebSocketServerProtocolConfig { - - static final long DEFAULT_HANDSHAKE_TIMEOUT_MILLIS = 10000L; - - private final String websocketPath; - private final String subprotocols; - private final boolean checkStartsWith; - private final long handshakeTimeoutMillis; - private final long forceCloseTimeoutMillis; - private final boolean handleCloseFrames; - private final WebSocketCloseStatus sendCloseFrame; - private final boolean dropPongFrames; - private final WebSocketDecoderConfig decoderConfig; - - private WebSocketServerProtocolConfig( - String websocketPath, - String subprotocols, - boolean checkStartsWith, - long handshakeTimeoutMillis, - long forceCloseTimeoutMillis, - boolean handleCloseFrames, - WebSocketCloseStatus sendCloseFrame, - boolean dropPongFrames, - WebSocketDecoderConfig decoderConfig - ) { - this.websocketPath = websocketPath; - this.subprotocols = subprotocols; - this.checkStartsWith = checkStartsWith; - this.handshakeTimeoutMillis = checkPositive(handshakeTimeoutMillis, "handshakeTimeoutMillis"); - this.forceCloseTimeoutMillis = forceCloseTimeoutMillis; - this.handleCloseFrames = handleCloseFrames; - this.sendCloseFrame = sendCloseFrame; - this.dropPongFrames = dropPongFrames; - this.decoderConfig = decoderConfig == null ? WebSocketDecoderConfig.DEFAULT : decoderConfig; - } - - public String websocketPath() { - return websocketPath; - } - - public String subprotocols() { - return subprotocols; - } - - public boolean checkStartsWith() { - return checkStartsWith; - } - - public long handshakeTimeoutMillis() { - return handshakeTimeoutMillis; - } - - public long forceCloseTimeoutMillis() { - return forceCloseTimeoutMillis; - } - - public boolean handleCloseFrames() { - return handleCloseFrames; - } - - public WebSocketCloseStatus sendCloseFrame() { - return sendCloseFrame; - } - - public boolean dropPongFrames() { - return dropPongFrames; - } - - public WebSocketDecoderConfig decoderConfig() { - return decoderConfig; - } - - @Override - public String toString() { - return "WebSocketServerProtocolConfig" + - " {websocketPath=" + websocketPath + - ", subprotocols=" + subprotocols + - ", checkStartsWith=" + checkStartsWith + - ", handshakeTimeoutMillis=" + handshakeTimeoutMillis + - ", forceCloseTimeoutMillis=" + forceCloseTimeoutMillis + - ", handleCloseFrames=" + handleCloseFrames + - ", sendCloseFrame=" + sendCloseFrame + - ", dropPongFrames=" + dropPongFrames + - ", decoderConfig=" + decoderConfig + - "}"; - } - - public Builder toBuilder() { - return new Builder(this); - } - - public static Builder newBuilder() { - return new Builder("/", null, false, DEFAULT_HANDSHAKE_TIMEOUT_MILLIS, 0L, - true, WebSocketCloseStatus.NORMAL_CLOSURE, true, WebSocketDecoderConfig.DEFAULT); - } - - public static final class Builder { - private String websocketPath; - private String subprotocols; - private boolean checkStartsWith; - private long handshakeTimeoutMillis; - private long forceCloseTimeoutMillis; - private boolean handleCloseFrames; - private WebSocketCloseStatus sendCloseFrame; - private boolean dropPongFrames; - private WebSocketDecoderConfig decoderConfig; - private WebSocketDecoderConfig.Builder decoderConfigBuilder; - - private Builder(WebSocketServerProtocolConfig serverConfig) { - this(Objects.requireNonNull(serverConfig, "serverConfig").websocketPath(), - serverConfig.subprotocols(), - serverConfig.checkStartsWith(), - serverConfig.handshakeTimeoutMillis(), - serverConfig.forceCloseTimeoutMillis(), - serverConfig.handleCloseFrames(), - serverConfig.sendCloseFrame(), - serverConfig.dropPongFrames(), - serverConfig.decoderConfig() - ); - } - - private Builder(String websocketPath, - String subprotocols, - boolean checkStartsWith, - long handshakeTimeoutMillis, - long forceCloseTimeoutMillis, - boolean handleCloseFrames, - WebSocketCloseStatus sendCloseFrame, - boolean dropPongFrames, - WebSocketDecoderConfig decoderConfig) { - this.websocketPath = websocketPath; - this.subprotocols = subprotocols; - this.checkStartsWith = checkStartsWith; - this.handshakeTimeoutMillis = handshakeTimeoutMillis; - this.forceCloseTimeoutMillis = forceCloseTimeoutMillis; - this.handleCloseFrames = handleCloseFrames; - this.sendCloseFrame = sendCloseFrame; - this.dropPongFrames = dropPongFrames; - this.decoderConfig = decoderConfig; - } - - /** - * URI path component to handle websocket upgrade requests on. - */ - public Builder websocketPath(String websocketPath) { - this.websocketPath = websocketPath; - return this; - } - - /** - * CSV of supported protocols - */ - public Builder subprotocols(String subprotocols) { - this.subprotocols = subprotocols; - return this; - } - - /** - * {@code true} to handle all requests, where URI path component starts from - * {@link WebSocketServerProtocolConfig#websocketPath()}, {@code false} for exact match (default). - */ - public Builder checkStartsWith(boolean checkStartsWith) { - this.checkStartsWith = checkStartsWith; - return this; - } - - /** - * Handshake timeout in mills, when handshake timeout, will trigger user - * event {@link ClientHandshakeStateEvent#HANDSHAKE_TIMEOUT} - */ - public Builder handshakeTimeoutMillis(long handshakeTimeoutMillis) { - this.handshakeTimeoutMillis = handshakeTimeoutMillis; - return this; - } - - /** - * Close the connection if it was not closed by the client after timeout specified - */ - public Builder forceCloseTimeoutMillis(long forceCloseTimeoutMillis) { - this.forceCloseTimeoutMillis = forceCloseTimeoutMillis; - return this; - } - - /** - * {@code true} if close frames should not be forwarded and just close the channel - */ - public Builder handleCloseFrames(boolean handleCloseFrames) { - this.handleCloseFrames = handleCloseFrames; - return this; - } - - /** - * Close frame to send, when close frame was not send manually. Or {@code null} to disable proper close. - */ - public Builder sendCloseFrame(WebSocketCloseStatus sendCloseFrame) { - this.sendCloseFrame = sendCloseFrame; - return this; - } - - /** - * {@code true} if pong frames should not be forwarded - */ - public Builder dropPongFrames(boolean dropPongFrames) { - this.dropPongFrames = dropPongFrames; - return this; - } - - /** - * Frames decoder configuration. - */ - public Builder decoderConfig(WebSocketDecoderConfig decoderConfig) { - this.decoderConfig = decoderConfig == null ? WebSocketDecoderConfig.DEFAULT : decoderConfig; - this.decoderConfigBuilder = null; - return this; - } - - private WebSocketDecoderConfig.Builder decoderConfigBuilder() { - if (decoderConfigBuilder == null) { - decoderConfigBuilder = decoderConfig.toBuilder(); - } - return decoderConfigBuilder; - } - - public Builder maxFramePayloadLength(int maxFramePayloadLength) { - decoderConfigBuilder().maxFramePayloadLength(maxFramePayloadLength); - return this; - } - - public Builder expectMaskedFrames(boolean expectMaskedFrames) { - decoderConfigBuilder().expectMaskedFrames(expectMaskedFrames); - return this; - } - - public Builder allowMaskMismatch(boolean allowMaskMismatch) { - decoderConfigBuilder().allowMaskMismatch(allowMaskMismatch); - return this; - } - - public Builder allowExtensions(boolean allowExtensions) { - decoderConfigBuilder().allowExtensions(allowExtensions); - return this; - } - - public Builder closeOnProtocolViolation(boolean closeOnProtocolViolation) { - decoderConfigBuilder().closeOnProtocolViolation(closeOnProtocolViolation); - return this; - } - - public Builder withUTF8Validator(boolean withUTF8Validator) { - decoderConfigBuilder().withUTF8Validator(withUTF8Validator); - return this; - } - - /** - * Build unmodifiable server protocol configuration. - */ - public WebSocketServerProtocolConfig build() { - return new WebSocketServerProtocolConfig( - websocketPath, - subprotocols, - checkStartsWith, - handshakeTimeoutMillis, - forceCloseTimeoutMillis, - handleCloseFrames, - sendCloseFrame, - dropPongFrames, - decoderConfigBuilder == null ? decoderConfig : decoderConfigBuilder.build() - ); - } - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandler.java deleted file mode 100644 index 37ca319c79..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandler.java +++ /dev/null @@ -1,275 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.netty.channel.ChannelFutureListeners; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.handler.codec.http.DefaultFullHttpResponse; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.util.AttributeKey; -import io.netty.util.concurrent.Promise; - -import java.util.Objects; - -import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; -import static io.netty.handler.codec.http.websocketx.WebSocketServerProtocolConfig.DEFAULT_HANDSHAKE_TIMEOUT_MILLIS; - -/** - * This handler does all the heavy lifting for you to run a websocket server. - * - * It takes care of websocket handshaking as well as processing of control frames (Close, Ping, Pong). Text and Binary - * data frames are passed to the next handler in the pipeline (implemented by you) for processing. - * - * See io.netty.example.http.websocketx.html5.WebSocketServer for usage. - * - * The implementation of this handler assumes that you just want to run a websocket server and not process other types - * HTTP requests (like GET and POST). If you wish to support both HTTP requests and websockets in the one server, refer - * to the io.netty.example.http.websocketx.server.WebSocketServer example. - * - * To know once a handshake was done you can intercept the - * {@link ChannelHandler#userEventTriggered(ChannelHandlerContext, Object)} and check if the event was instance - * of {@link HandshakeComplete}, the event will contain extra information about the handshake such as the request and - * selected subprotocol. - */ -public class WebSocketServerProtocolHandler extends WebSocketProtocolHandler { - - /** - * Events that are fired to notify about handshake status - */ - public enum ServerHandshakeStateEvent { - /** - * The Handshake was completed successfully and the channel was upgraded to websockets. - * - * @deprecated in favor of {@link HandshakeComplete} class, - * it provides extra information about the handshake - */ - @Deprecated - HANDSHAKE_COMPLETE, - - /** - * The Handshake was timed out - */ - HANDSHAKE_TIMEOUT - } - - /** - * The Handshake was completed successfully and the channel was upgraded to websockets. - */ - public static final class HandshakeComplete { - private final String requestUri; - private final HttpHeaders requestHeaders; - private final String selectedSubprotocol; - - HandshakeComplete(String requestUri, HttpHeaders requestHeaders, String selectedSubprotocol) { - this.requestUri = requestUri; - this.requestHeaders = requestHeaders; - this.selectedSubprotocol = selectedSubprotocol; - } - - public String requestUri() { - return requestUri; - } - - public HttpHeaders requestHeaders() { - return requestHeaders; - } - - public String selectedSubprotocol() { - return selectedSubprotocol; - } - } - - private static final AttributeKey HANDSHAKER_ATTR_KEY = - AttributeKey.valueOf(WebSocketServerHandshaker.class, "HANDSHAKER"); - - private final WebSocketServerProtocolConfig serverConfig; - - /** - * Base constructor - * - * @param serverConfig - * Server protocol configuration. - */ - public WebSocketServerProtocolHandler(WebSocketServerProtocolConfig serverConfig) { - super(Objects.requireNonNull(serverConfig, "serverConfig").dropPongFrames(), - serverConfig.sendCloseFrame(), - serverConfig.forceCloseTimeoutMillis() - ); - this.serverConfig = serverConfig; - } - - public WebSocketServerProtocolHandler(String websocketPath) { - this(websocketPath, DEFAULT_HANDSHAKE_TIMEOUT_MILLIS); - } - - public WebSocketServerProtocolHandler(String websocketPath, long handshakeTimeoutMillis) { - this(websocketPath, false, handshakeTimeoutMillis); - } - - public WebSocketServerProtocolHandler(String websocketPath, boolean checkStartsWith) { - this(websocketPath, checkStartsWith, DEFAULT_HANDSHAKE_TIMEOUT_MILLIS); - } - - public WebSocketServerProtocolHandler(String websocketPath, boolean checkStartsWith, long handshakeTimeoutMillis) { - this(websocketPath, null, false, 65536, false, checkStartsWith, handshakeTimeoutMillis); - } - - public WebSocketServerProtocolHandler(String websocketPath, String subprotocols) { - this(websocketPath, subprotocols, DEFAULT_HANDSHAKE_TIMEOUT_MILLIS); - } - - public WebSocketServerProtocolHandler(String websocketPath, String subprotocols, long handshakeTimeoutMillis) { - this(websocketPath, subprotocols, false, handshakeTimeoutMillis); - } - - public WebSocketServerProtocolHandler(String websocketPath, String subprotocols, boolean allowExtensions) { - this(websocketPath, subprotocols, allowExtensions, DEFAULT_HANDSHAKE_TIMEOUT_MILLIS); - } - - public WebSocketServerProtocolHandler(String websocketPath, String subprotocols, boolean allowExtensions, - long handshakeTimeoutMillis) { - this(websocketPath, subprotocols, allowExtensions, 65536, handshakeTimeoutMillis); - } - - public WebSocketServerProtocolHandler(String websocketPath, String subprotocols, - boolean allowExtensions, int maxFrameSize) { - this(websocketPath, subprotocols, allowExtensions, maxFrameSize, DEFAULT_HANDSHAKE_TIMEOUT_MILLIS); - } - - public WebSocketServerProtocolHandler(String websocketPath, String subprotocols, - boolean allowExtensions, int maxFrameSize, long handshakeTimeoutMillis) { - this(websocketPath, subprotocols, allowExtensions, maxFrameSize, false, handshakeTimeoutMillis); - } - - public WebSocketServerProtocolHandler(String websocketPath, String subprotocols, - boolean allowExtensions, int maxFrameSize, boolean allowMaskMismatch) { - this(websocketPath, subprotocols, allowExtensions, maxFrameSize, allowMaskMismatch, - DEFAULT_HANDSHAKE_TIMEOUT_MILLIS); - } - - public WebSocketServerProtocolHandler(String websocketPath, String subprotocols, boolean allowExtensions, - int maxFrameSize, boolean allowMaskMismatch, long handshakeTimeoutMillis) { - this(websocketPath, subprotocols, allowExtensions, maxFrameSize, allowMaskMismatch, false, - handshakeTimeoutMillis); - } - - public WebSocketServerProtocolHandler(String websocketPath, String subprotocols, - boolean allowExtensions, int maxFrameSize, boolean allowMaskMismatch, boolean checkStartsWith) { - this(websocketPath, subprotocols, allowExtensions, maxFrameSize, allowMaskMismatch, checkStartsWith, - DEFAULT_HANDSHAKE_TIMEOUT_MILLIS); - } - - public WebSocketServerProtocolHandler(String websocketPath, String subprotocols, - boolean allowExtensions, int maxFrameSize, boolean allowMaskMismatch, - boolean checkStartsWith, long handshakeTimeoutMillis) { - this(websocketPath, subprotocols, allowExtensions, maxFrameSize, allowMaskMismatch, checkStartsWith, true, - handshakeTimeoutMillis); - } - - public WebSocketServerProtocolHandler(String websocketPath, String subprotocols, - boolean allowExtensions, int maxFrameSize, boolean allowMaskMismatch, - boolean checkStartsWith, boolean dropPongFrames) { - this(websocketPath, subprotocols, allowExtensions, maxFrameSize, allowMaskMismatch, checkStartsWith, - dropPongFrames, DEFAULT_HANDSHAKE_TIMEOUT_MILLIS); - } - - public WebSocketServerProtocolHandler(String websocketPath, String subprotocols, boolean allowExtensions, - int maxFrameSize, boolean allowMaskMismatch, boolean checkStartsWith, - boolean dropPongFrames, long handshakeTimeoutMillis) { - this(websocketPath, subprotocols, checkStartsWith, dropPongFrames, handshakeTimeoutMillis, - WebSocketDecoderConfig.newBuilder() - .maxFramePayloadLength(maxFrameSize) - .allowMaskMismatch(allowMaskMismatch) - .allowExtensions(allowExtensions) - .build()); - } - - public WebSocketServerProtocolHandler(String websocketPath, String subprotocols, boolean checkStartsWith, - boolean dropPongFrames, long handshakeTimeoutMillis, - WebSocketDecoderConfig decoderConfig) { - this(WebSocketServerProtocolConfig.newBuilder() - .websocketPath(websocketPath) - .subprotocols(subprotocols) - .checkStartsWith(checkStartsWith) - .handshakeTimeoutMillis(handshakeTimeoutMillis) - .dropPongFrames(dropPongFrames) - .decoderConfig(decoderConfig) - .build()); - } - - @Override - public void handlerAdded(ChannelHandlerContext ctx) { - ChannelPipeline cp = ctx.pipeline(); - if (cp.get(WebSocketServerProtocolHandshakeHandler.class) == null) { - // Add the WebSocketHandshakeHandler before this one. - cp.addBefore(ctx.name(), WebSocketServerProtocolHandshakeHandler.class.getName(), - new WebSocketServerProtocolHandshakeHandler(serverConfig)); - } - if (serverConfig.decoderConfig().withUTF8Validator() && cp.get(Utf8FrameValidator.class) == null) { - // Add the UFT8 checking before this one. - cp.addBefore(ctx.name(), Utf8FrameValidator.class.getName(), - new Utf8FrameValidator()); - } - } - - @Override - protected void decode(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception { - if (serverConfig.handleCloseFrames() && frame instanceof CloseWebSocketFrame) { - WebSocketServerHandshaker handshaker = getHandshaker(ctx.channel()); - if (handshaker != null) { - frame.retain(); - Promise promise = ctx.newPromise(); - closeSent(promise); - handshaker.close(ctx, (CloseWebSocketFrame) frame).cascadeTo(promise); - } else { - ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ctx, ChannelFutureListeners.CLOSE); - } - return; - } - super.decode(ctx, frame); - } - - @Override - protected WebSocketServerHandshakeException buildHandshakeException(String message) { - return new WebSocketServerHandshakeException(message); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - if (cause instanceof WebSocketHandshakeException) { - FullHttpResponse response = new DefaultFullHttpResponse( - HTTP_1_1, HttpResponseStatus.BAD_REQUEST, Unpooled.wrappedBuffer(cause.getMessage().getBytes())); - ctx.channel().writeAndFlush(response).addListener(ctx, ChannelFutureListeners.CLOSE); - } else { - ctx.fireExceptionCaught(cause); - ctx.close(); - } - } - - static WebSocketServerHandshaker getHandshaker(Channel channel) { - return channel.attr(HANDSHAKER_ATTR_KEY).get(); - } - - static void setHandshaker(Channel channel, WebSocketServerHandshaker handshaker) { - channel.attr(HANDSHAKER_ATTR_KEY).set(handshaker); - } -} 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 deleted file mode 100644 index 1968267c3d..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandshakeHandler.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.channel.ChannelFutureListeners; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.handler.codec.http.DefaultFullHttpResponse; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler.ServerHandshakeStateEvent; -import io.netty.handler.ssl.SslHandler; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; - -import java.util.Objects; -import java.util.concurrent.TimeUnit; - -import static io.netty.handler.codec.http.HttpMethod.GET; -import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN; -import static io.netty.handler.codec.http.HttpUtil.isKeepAlive; -import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; - -/** - * Handles the HTTP handshake (the HTTP Upgrade request) for {@link WebSocketServerProtocolHandler}. - */ -class WebSocketServerProtocolHandshakeHandler implements ChannelHandler { - - private final WebSocketServerProtocolConfig serverConfig; - private Promise handshakePromise; - - WebSocketServerProtocolHandshakeHandler(WebSocketServerProtocolConfig serverConfig) { - this.serverConfig = Objects.requireNonNull(serverConfig, "serverConfig"); - } - - @Override - public void handlerAdded(ChannelHandlerContext ctx) { - handshakePromise = ctx.newPromise(); - } - - @Override - public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception { - final FullHttpRequest req = (FullHttpRequest) msg; - if (!isWebSocketPath(req)) { - ctx.fireChannelRead(msg); - return; - } - - try { - if (!GET.equals(req.method())) { - sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN, ctx.alloc().buffer(0))); - return; - } - - final WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory( - getWebSocketLocation(ctx.pipeline(), req, serverConfig.websocketPath()), - serverConfig.subprotocols(), serverConfig.decoderConfig()); - final WebSocketServerHandshaker handshaker = wsFactory.newHandshaker(req); - Promise localHandshakePromise = handshakePromise; - if (handshaker == null) { - WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel()); - } else { - // Ensure we set the handshaker and replace this handler before we - // trigger the actual handshake. Otherwise we may receive websocket bytes in this handler - // before we had a chance to replace it. - // - // See https://github.com/netty/netty/issues/9471. - WebSocketServerProtocolHandler.setHandshaker(ctx.channel(), handshaker); - - Future handshakeFuture = handshaker.handshake(ctx.channel(), req); - handshakeFuture.addListener(future -> { - if (future.isFailed()) { - localHandshakePromise.tryFailure(future.cause()); - ctx.fireExceptionCaught(future.cause()); - } else { - localHandshakePromise.trySuccess(null); - // Kept for compatibility - ctx.fireUserEventTriggered( - WebSocketServerProtocolHandler.ServerHandshakeStateEvent.HANDSHAKE_COMPLETE); - ctx.fireUserEventTriggered( - new WebSocketServerProtocolHandler.HandshakeComplete( - req.uri(), req.headers(), handshaker.selectedSubprotocol())); - } - ctx.pipeline().remove(this); - }); - applyHandshakeTimeout(ctx); - } - } finally { - req.release(); - } - } - - private boolean isWebSocketPath(FullHttpRequest req) { - String websocketPath = serverConfig.websocketPath(); - String uri = req.uri(); - boolean checkStartUri = uri.startsWith(websocketPath); - boolean checkNextUri = "/".equals(websocketPath) || checkNextUri(uri, websocketPath); - return serverConfig.checkStartsWith() ? (checkStartUri && checkNextUri) : uri.equals(websocketPath); - } - - private boolean checkNextUri(String uri, String websocketPath) { - int len = websocketPath.length(); - if (uri.length() > len) { - char nextUri = uri.charAt(len); - return nextUri == '/' || nextUri == '?'; - } - return true; - } - - private static void sendHttpResponse(ChannelHandlerContext ctx, HttpRequest req, HttpResponse res) { - Future f = ctx.writeAndFlush(res); - if (!isKeepAlive(req) || res.status().code() != 200) { - f.addListener(ctx, ChannelFutureListeners.CLOSE); - } - } - - private static String getWebSocketLocation(ChannelPipeline cp, HttpRequest req, String path) { - String protocol = "ws"; - if (cp.get(SslHandler.class) != null) { - // SSL in use so use Secure WebSockets - protocol = "wss"; - } - String host = req.headers().get(HttpHeaderNames.HOST); - return protocol + "://" + host + path; - } - - private void applyHandshakeTimeout(ChannelHandlerContext ctx) { - Promise localHandshakePromise = handshakePromise; - final long handshakeTimeoutMillis = serverConfig.handshakeTimeoutMillis(); - if (handshakeTimeoutMillis <= 0 || localHandshakePromise.isDone()) { - return; - } - - final Future timeoutFuture = ctx.executor().schedule(() -> { - if (!localHandshakePromise.isDone() && - localHandshakePromise.tryFailure(new WebSocketServerHandshakeException("handshake timed out"))) { - ctx.flush() - .fireUserEventTriggered(ServerHandshakeStateEvent.HANDSHAKE_TIMEOUT) - .close(); - } - }, handshakeTimeoutMillis, TimeUnit.MILLISECONDS); - - // Cancel the handshake timeout when handshake is finished. - localHandshakePromise.asFuture().addListener(f -> timeoutFuture.cancel()); - } -} 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 deleted file mode 100644 index 09cd09d082..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketUtil.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.util.concurrent.FastThreadLocal; - -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Base64; -import java.util.concurrent.ThreadLocalRandom; - -/** - * A utility class mainly for use by web sockets - */ -final class WebSocketUtil { - - private static final FastThreadLocal MD5 = new FastThreadLocal() { - @Override - protected MessageDigest initialValue() throws Exception { - try { - //Try to get a MessageDigest that uses MD5 - //Suppress a warning about weak hash algorithm - //since it's defined in draft-ietf-hybi-thewebsocketprotocol-00 - return MessageDigest.getInstance("MD5"); // lgtm [java/weak-cryptographic-algorithm] - } catch (NoSuchAlgorithmException e) { - //This shouldn't happen! How old is the computer? - throw new InternalError("MD5 not supported on this platform - Outdated?"); - } - } - }; - - private static final FastThreadLocal SHA1 = new FastThreadLocal() { - @Override - protected MessageDigest initialValue() throws Exception { - try { - //Try to get a MessageDigest that uses SHA1 - //Suppress a warning about weak hash algorithm - //since it's defined in draft-ietf-hybi-thewebsocketprotocol-00 - return MessageDigest.getInstance("SHA1"); // lgtm [java/weak-cryptographic-algorithm] - } catch (NoSuchAlgorithmException e) { - //This shouldn't happen! How old is the computer? - throw new InternalError("SHA-1 not supported on this platform - Outdated?"); - } - } - }; - - /** - * Performs a MD5 hash on the specified data - * - * @param data The data to hash - * @return The hashed data - */ - static byte[] md5(byte[] data) { - // TODO(normanmaurer): Create md5 method that not need MessageDigest. - return digest(MD5, data); - } - - /** - * Performs a SHA-1 hash on the specified data - * - * @param data The data to hash - * @return The hashed data - */ - static byte[] sha1(byte[] data) { - // TODO(normanmaurer): Create sha1 method that not need MessageDigest. - return digest(SHA1, data); - } - - private static byte[] digest(FastThreadLocal digestFastThreadLocal, byte[] data) { - MessageDigest digest = digestFastThreadLocal.get(); - digest.reset(); - return digest.digest(data); - } - - /** - * Performs base64 encoding on the specified data - * - * @param data The data to encode - * @return An encoded string containing the data - */ - static String base64(byte[] data) { - return Base64.getEncoder().encodeToString(data); - } - /** - * Creates an arbitrary number of random bytes - * - * @param size the number of random bytes to create - * @return An array of random bytes - */ - static byte[] randomBytes(int size) { - byte[] bytes = new byte[size]; - ThreadLocalRandom.current().nextBytes(bytes); - return bytes; - } - - /** - * A private constructor to ensure that instances of this class cannot be made - */ - private WebSocketUtil() { - // Unused - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketVersion.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketVersion.java deleted file mode 100644 index 0901ceb656..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketVersion.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.util.AsciiString; - -/** - *

- * Versions of the web socket specification. - *

- *

- * A specification is tied to one wire protocol version but a protocol version may have use by more than 1 version of - * the specification. - *

- */ -public enum WebSocketVersion { - - UNKNOWN(AsciiString.cached("unknown")), - - /** - * draft-ietf-hybi-thewebsocketprotocol- 00. - */ - V00(AsciiString.cached("0")), - - /** - * draft-ietf-hybi-thewebsocketprotocol- 07 - */ - V07(AsciiString.cached("7")), - - /** - * draft-ietf-hybi-thewebsocketprotocol- 10 - */ - V08(AsciiString.cached("8")), - - /** - * RFC 6455. This was originally draft-ietf-hybi-thewebsocketprotocol- - * 17 - */ - V13(AsciiString.cached("13")); - - private final AsciiString headerValue; - - WebSocketVersion(AsciiString headerValue) { - this.headerValue = headerValue; - } - /** - * @return Value for HTTP Header 'Sec-WebSocket-Version' - */ - public String toHttpHeaderValue() { - return toAsciiString().toString(); - } - - AsciiString toAsciiString() { - return headerValue; - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketClientExtension.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketClientExtension.java deleted file mode 100644 index 1d4a089ef2..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketClientExtension.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx.extensions; - -/** - * Created once the handshake phase is done. - */ -public interface WebSocketClientExtension extends WebSocketExtension { - -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketClientExtensionHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketClientExtensionHandler.java deleted file mode 100644 index 3c496ca08b..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketClientExtensionHandler.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx.extensions; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.CodecException; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.util.concurrent.Future; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; - -import static io.netty.util.internal.ObjectUtil.checkNonEmpty; - -/** - * This handler negotiates and initializes the WebSocket Extensions. - * - * This implementation negotiates the extension with the server in a defined order, - * ensures that the successfully negotiated extensions are consistent between them, - * and initializes the channel pipeline with the extension decoder and encoder. - * - * Find a basic implementation for compression extensions at - * io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketClientCompressionHandler. - */ -public class WebSocketClientExtensionHandler implements ChannelHandler { - - private final List extensionHandshakers; - - /** - * Constructor - * - * @param extensionHandshakers - * The extension handshaker in priority order. A handshaker could be repeated many times - * with fallback configuration. - */ - public WebSocketClientExtensionHandler(WebSocketClientExtensionHandshaker... extensionHandshakers) { - this.extensionHandshakers = Arrays.asList(checkNonEmpty(extensionHandshakers, "extensionHandshakers")); - } - - @Override - public Future write(final ChannelHandlerContext ctx, Object msg) { - if (msg instanceof HttpRequest && WebSocketExtensionUtil.isWebsocketUpgrade(((HttpRequest) msg).headers())) { - HttpRequest request = (HttpRequest) msg; - String headerValue = request.headers().getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS); - List extraExtensions = - new ArrayList(extensionHandshakers.size()); - for (WebSocketClientExtensionHandshaker extensionHandshaker : extensionHandshakers) { - extraExtensions.add(extensionHandshaker.newRequestData()); - } - String newHeaderValue = WebSocketExtensionUtil - .computeMergeExtensionsHeaderValue(headerValue, extraExtensions); - - request.headers().set(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS, newHeaderValue); - } - - return ctx.write(msg); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) - throws Exception { - if (msg instanceof HttpResponse) { - HttpResponse response = (HttpResponse) msg; - - if (WebSocketExtensionUtil.isWebsocketUpgrade(response.headers())) { - String extensionsHeader = response.headers().getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS); - - if (extensionsHeader != null) { - List extensions = - WebSocketExtensionUtil.extractExtensions(extensionsHeader); - List validExtensions = - new ArrayList<>(extensions.size()); - int rsv = 0; - - for (WebSocketExtensionData extensionData : extensions) { - Iterator extensionHandshakersIterator = - extensionHandshakers.iterator(); - WebSocketClientExtension validExtension = null; - - while (validExtension == null && extensionHandshakersIterator.hasNext()) { - WebSocketClientExtensionHandshaker extensionHandshaker = - extensionHandshakersIterator.next(); - validExtension = extensionHandshaker.handshakeExtension(extensionData); - } - - if (validExtension != null && ((validExtension.rsv() & rsv) == 0)) { - rsv = rsv | validExtension.rsv(); - validExtensions.add(validExtension); - } else { - throw new CodecException( - "invalid WebSocket Extension handshake for \"" + extensionsHeader + '"'); - } - } - - for (WebSocketClientExtension validExtension : validExtensions) { - WebSocketExtensionDecoder decoder = validExtension.newExtensionDecoder(); - WebSocketExtensionEncoder encoder = validExtension.newExtensionEncoder(); - ctx.pipeline().addAfter(ctx.name(), decoder.getClass().getName(), decoder); - ctx.pipeline().addAfter(ctx.name(), encoder.getClass().getName(), encoder); - } - } - ctx.fireChannelRead(msg); - ctx.pipeline().remove(this); - return; - } - } - - ctx.fireChannelRead(msg); - } -} - diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketClientExtensionHandshaker.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketClientExtensionHandshaker.java deleted file mode 100644 index 4812966f82..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketClientExtensionHandshaker.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx.extensions; - - -/** - * Handshakes a client extension with the server. - */ -public interface WebSocketClientExtensionHandshaker { - - /** - * Return extension configuration to submit to the server. - * - * @return the desired extension configuration. - */ - WebSocketExtensionData newRequestData(); - - /** - * Handshake based on server response. It should always succeed because server response - * should be a request acknowledge. - * - * @param extensionData - * the extension configuration sent by the server. - * @return an initialized extension if handshake phase succeed or null if failed. - */ - WebSocketClientExtension handshakeExtension(WebSocketExtensionData extensionData); - -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtension.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtension.java deleted file mode 100644 index 30bfb4ca83..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtension.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx.extensions; - -/** - * Created once the handshake phase is done. - */ -public interface WebSocketExtension { - - int RSV1 = 0x04; - int RSV2 = 0x02; - int RSV3 = 0x01; - - /** - * @return the reserved bit value to ensure that no other extension should interfere. - */ - int rsv(); - - /** - * @return create the extension encoder. - */ - WebSocketExtensionEncoder newExtensionEncoder(); - - /** - * @return create the extension decoder. - */ - WebSocketExtensionDecoder newExtensionDecoder(); - -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtensionData.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtensionData.java deleted file mode 100644 index fecae3b232..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtensionData.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx.extensions; - -import static java.util.Objects.requireNonNull; - -import java.util.Collections; -import java.util.Map; - -/** - * A WebSocket Extension data from the Sec-WebSocket-Extensions header. - * - * See io.netty.handler.codec.http.HttpHeaders.Names.SEC_WEBSOCKET_EXTENSIONS. - */ -public final class WebSocketExtensionData { - - private final String name; - private final Map parameters; - - public WebSocketExtensionData(String name, Map parameters) { - requireNonNull(name, "name"); - requireNonNull(parameters, "parameters"); - this.name = name; - this.parameters = Collections.unmodifiableMap(parameters); - } - - /** - * @return the extension name. - */ - public String name() { - return name; - } - - /** - * @return the extension optional parameters. - */ - public Map parameters() { - return parameters; - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtensionDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtensionDecoder.java deleted file mode 100644 index d86b1ad8a2..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtensionDecoder.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx.extensions; - -import io.netty.handler.codec.MessageToMessageDecoder; -import io.netty.handler.codec.http.websocketx.WebSocketFrame; - -/** - * Convenient class for io.netty.handler.codec.http.websocketx.extensions.WebSocketExtension decoder. - */ -public abstract class WebSocketExtensionDecoder extends MessageToMessageDecoder { - -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtensionEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtensionEncoder.java deleted file mode 100644 index 96a84f8aa5..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtensionEncoder.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx.extensions; - -import io.netty.handler.codec.MessageToMessageEncoder; -import io.netty.handler.codec.http.websocketx.WebSocketFrame; - -/** - * Convenient class for io.netty.handler.codec.http.websocketx.extensions.WebSocketExtension encoder. - */ -public abstract class WebSocketExtensionEncoder extends MessageToMessageEncoder { - -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtensionFilter.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtensionFilter.java deleted file mode 100644 index af9ca75e27..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtensionFilter.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx.extensions; - -import io.netty.handler.codec.http.websocketx.WebSocketFrame; - -/** - * Filter that is responsible to skip the evaluation of a certain extension - * according to standard. - */ -public interface WebSocketExtensionFilter { - - /** - * A {@link WebSocketExtensionFilter} that never skip the evaluation of an - * any given extensions {@link WebSocketExtension}. - */ - WebSocketExtensionFilter NEVER_SKIP = frame -> false; - - /** - * A {@link WebSocketExtensionFilter} that always skip the evaluation of an - * any given extensions {@link WebSocketExtension}. - */ - WebSocketExtensionFilter ALWAYS_SKIP = frame -> true; - - /** - * Returns {@code true} if the evaluation of the extension must skipped - * for the given frame otherwise {@code false}. - */ - boolean mustSkip(WebSocketFrame frame); - -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtensionFilterProvider.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtensionFilterProvider.java deleted file mode 100644 index 4633e76154..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtensionFilterProvider.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx.extensions; - -/** - * Extension filter provider that is responsible to provide filters for a certain {@link WebSocketExtension} extension. - */ -public interface WebSocketExtensionFilterProvider { - - WebSocketExtensionFilterProvider DEFAULT = new WebSocketExtensionFilterProvider() { - @Override - public WebSocketExtensionFilter encoderFilter() { - return WebSocketExtensionFilter.NEVER_SKIP; - } - - @Override - public WebSocketExtensionFilter decoderFilter() { - return WebSocketExtensionFilter.NEVER_SKIP; - } - }; - - /** - * Returns the extension filter for {@link WebSocketExtensionEncoder} encoder. - */ - WebSocketExtensionFilter encoderFilter(); - - /** - * Returns the extension filter for {@link WebSocketExtensionDecoder} decoder. - */ - WebSocketExtensionFilter decoderFilter(); - -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtensionUtil.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtensionUtil.java deleted file mode 100644 index d1fe9584b2..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtensionUtil.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx.extensions; - -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpHeaders; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public final class WebSocketExtensionUtil { - - private static final String EXTENSION_SEPARATOR = ","; - private static final String PARAMETER_SEPARATOR = ";"; - private static final char PARAMETER_EQUAL = '='; - - private static final Pattern PARAMETER = Pattern.compile("^([^=]+)(=[\\\"]?([^\\\"]+)[\\\"]?)?$"); - - static boolean isWebsocketUpgrade(HttpHeaders headers) { - //this contains check does not allocate an iterator, and most requests are not upgrades - //so we do the contains check first before checking for specific values - return headers.contains(HttpHeaderNames.UPGRADE) && - headers.containsValue(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE, true) && - headers.contains(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET, true); - } - - public static List extractExtensions(String extensionHeader) { - String[] rawExtensions = extensionHeader.split(EXTENSION_SEPARATOR); - if (rawExtensions.length > 0) { - List extensions = new ArrayList<>(rawExtensions.length); - for (String rawExtension : rawExtensions) { - String[] extensionParameters = rawExtension.split(PARAMETER_SEPARATOR); - String name = extensionParameters[0].trim(); - Map parameters; - if (extensionParameters.length > 1) { - parameters = new HashMap<>(extensionParameters.length - 1); - for (int i = 1; i < extensionParameters.length; i++) { - String parameter = extensionParameters[i].trim(); - Matcher parameterMatcher = PARAMETER.matcher(parameter); - if (parameterMatcher.matches() && parameterMatcher.group(1) != null) { - parameters.put(parameterMatcher.group(1), parameterMatcher.group(3)); - } - } - } else { - parameters = Collections.emptyMap(); - } - extensions.add(new WebSocketExtensionData(name, parameters)); - } - return extensions; - } else { - return Collections.emptyList(); - } - } - - static String computeMergeExtensionsHeaderValue(String userDefinedHeaderValue, - List extraExtensions) { - List userDefinedExtensions = - userDefinedHeaderValue != null ? - extractExtensions(userDefinedHeaderValue) : - Collections.emptyList(); - - for (WebSocketExtensionData userDefined: userDefinedExtensions) { - WebSocketExtensionData matchingExtra = null; - int i; - for (i = 0; i < extraExtensions.size(); i ++) { - WebSocketExtensionData extra = extraExtensions.get(i); - if (extra.name().equals(userDefined.name())) { - matchingExtra = extra; - break; - } - } - if (matchingExtra == null) { - extraExtensions.add(userDefined); - } else { - // merge with higher precedence to user defined parameters - Map mergedParameters = new HashMap(matchingExtra.parameters()); - mergedParameters.putAll(userDefined.parameters()); - extraExtensions.set(i, new WebSocketExtensionData(matchingExtra.name(), mergedParameters)); - } - } - - StringBuilder sb = new StringBuilder(150); - - for (WebSocketExtensionData data: extraExtensions) { - sb.append(data.name()); - for (Entry parameter : data.parameters().entrySet()) { - sb.append(PARAMETER_SEPARATOR); - sb.append(parameter.getKey()); - if (parameter.getValue() != null) { - sb.append(PARAMETER_EQUAL); - sb.append(parameter.getValue()); - } - } - sb.append(EXTENSION_SEPARATOR); - } - - if (!extraExtensions.isEmpty()) { - sb.setLength(sb.length() - EXTENSION_SEPARATOR.length()); - } - - return sb.toString(); - } - - private WebSocketExtensionUtil() { - // Unused - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketServerExtension.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketServerExtension.java deleted file mode 100644 index 9f3451666f..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketServerExtension.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx.extensions; - - -/** - * Created once the handshake phase is done. - */ -public interface WebSocketServerExtension extends WebSocketExtension { - - /** - * Return an extension configuration to submit to the client as an acknowledge. - * - * @return the acknowledged extension configuration. - */ - WebSocketExtensionData newResponseData(); - -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketServerExtensionHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketServerExtensionHandler.java deleted file mode 100644 index 5be980fb78..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketServerExtensionHandler.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx.extensions; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.FutureListener; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; - -import static io.netty.util.internal.ObjectUtil.checkNonEmpty; - -/** - * This handler negotiates and initializes the WebSocket Extensions. - * - * It negotiates the extensions based on the client desired order, - * ensures that the successfully negotiated extensions are consistent between them, - * and initializes the channel pipeline with the extension decoder and encoder. - * - * Find a basic implementation for compression extensions at - * io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketServerCompressionHandler. - */ -public class WebSocketServerExtensionHandler implements ChannelHandler { - - private final List extensionHandshakers; - - private List validExtensions; - - /** - * Constructor - * - * @param extensionHandshakers - * The extension handshaker in priority order. A handshaker could be repeated many times - * with fallback configuration. - */ - public WebSocketServerExtensionHandler(WebSocketServerExtensionHandshaker... extensionHandshakers) { - this.extensionHandshakers = Arrays.asList(checkNonEmpty(extensionHandshakers, "extensionHandshakers")); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - if (msg instanceof HttpRequest) { - HttpRequest request = (HttpRequest) msg; - - if (WebSocketExtensionUtil.isWebsocketUpgrade(request.headers())) { - String extensionsHeader = request.headers().getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS); - - if (extensionsHeader != null) { - List extensions = - WebSocketExtensionUtil.extractExtensions(extensionsHeader); - int rsv = 0; - - for (WebSocketExtensionData extensionData : extensions) { - Iterator extensionHandshakersIterator = - extensionHandshakers.iterator(); - WebSocketServerExtension validExtension = null; - - while (validExtension == null && extensionHandshakersIterator.hasNext()) { - WebSocketServerExtensionHandshaker extensionHandshaker = - extensionHandshakersIterator.next(); - validExtension = extensionHandshaker.handshakeExtension(extensionData); - } - - if (validExtension != null && ((validExtension.rsv() & rsv) == 0)) { - if (validExtensions == null) { - validExtensions = new ArrayList<>(1); - } - rsv = rsv | validExtension.rsv(); - validExtensions.add(validExtension); - } - } - } - } - } - - ctx.fireChannelRead(msg); - } - - @Override - public Future write(final ChannelHandlerContext ctx, Object msg) { - if (msg instanceof HttpResponse) { - HttpResponse httpResponse = (HttpResponse) msg; - //checking the status is faster than looking at headers - //so we do this first - if (HttpResponseStatus.SWITCHING_PROTOCOLS.equals(httpResponse.status())) { - HttpHeaders headers = httpResponse.headers(); - - FutureListener listener = null; - if (WebSocketExtensionUtil.isWebsocketUpgrade(headers)) { - if (validExtensions != null) { - String headerValue = headers.getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS); - List extraExtensions = - new ArrayList(extensionHandshakers.size()); - for (WebSocketServerExtension extension : validExtensions) { - extraExtensions.add(extension.newResponseData()); - } - String newHeaderValue = WebSocketExtensionUtil - .computeMergeExtensionsHeaderValue(headerValue, extraExtensions); - listener = future -> { - if (future.isSuccess()) { - for (WebSocketServerExtension extension : validExtensions) { - WebSocketExtensionDecoder decoder = extension.newExtensionDecoder(); - WebSocketExtensionEncoder encoder = extension.newExtensionEncoder(); - String name = ctx.name(); - ctx.pipeline() - - .addAfter(name, decoder.getClass().getName(), decoder) - .addAfter(name, encoder.getClass().getName(), encoder); - } - } - }; - - if (newHeaderValue != null) { - headers.set(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS, newHeaderValue); - } - } - Future f = ctx.write(httpResponse); - if (listener != null) { - f.addListener(listener); - } - f.addListener(future -> { - if (future.isSuccess()) { - ctx.pipeline().remove(this); - } - }); - return f; - } - } - } - return ctx.write(msg); - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketServerExtensionHandshaker.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketServerExtensionHandshaker.java deleted file mode 100644 index 599b1b46ed..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketServerExtensionHandshaker.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx.extensions; - - -/** - * Handshakes a client extension based on this server capabilities. - */ -public interface WebSocketServerExtensionHandshaker { - - /** - * Handshake based on client request. It must failed with null if server cannot handle it. - * - * @param extensionData - * the extension configuration sent by the client. - * @return an initialized extension if handshake phase succeed or null if failed. - */ - WebSocketServerExtension handshakeExtension(WebSocketExtensionData extensionData); - -} 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 deleted file mode 100644 index 1aa2aabf08..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/DeflateDecoder.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx.extensions.compression; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.CompositeByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.CodecException; -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.ContinuationWebSocketFrame; -import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; -import io.netty.handler.codec.http.websocketx.WebSocketFrame; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionDecoder; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionFilter; - -import java.util.Objects; - -/** - * Deflate implementation of a payload decompressor for - * io.netty.handler.codec.http.websocketx.WebSocketFrame. - */ -abstract class DeflateDecoder extends WebSocketExtensionDecoder { - - static final ByteBuf FRAME_TAIL = Unpooled.unreleasableBuffer( - Unpooled.wrappedBuffer(new byte[] {0x00, 0x00, (byte) 0xff, (byte) 0xff})) - .asReadOnly(); - - static final ByteBuf EMPTY_DEFLATE_BLOCK = Unpooled.unreleasableBuffer( - Unpooled.wrappedBuffer(new byte[] { 0x00 })) - .asReadOnly(); - - private final boolean noContext; - private final WebSocketExtensionFilter extensionDecoderFilter; - - private EmbeddedChannel decoder; - - /** - * Constructor - * - * @param noContext true to disable context takeover. - * @param extensionDecoderFilter extension decoder filter. - */ - DeflateDecoder(boolean noContext, WebSocketExtensionFilter extensionDecoderFilter) { - this.noContext = noContext; - this.extensionDecoderFilter = Objects.requireNonNull(extensionDecoderFilter, "extensionDecoderFilter"); - } - - /** - * Returns the extension decoder filter. - */ - protected WebSocketExtensionFilter extensionDecoderFilter() { - return extensionDecoderFilter; - } - - protected abstract boolean appendFrameTail(WebSocketFrame msg); - - protected abstract int newRsv(WebSocketFrame msg); - - @Override - protected void decode(ChannelHandlerContext ctx, WebSocketFrame msg) throws Exception { - final ByteBuf decompressedContent = decompressContent(ctx, msg); - - final WebSocketFrame outMsg; - if (msg instanceof TextWebSocketFrame) { - outMsg = new TextWebSocketFrame(msg.isFinalFragment(), newRsv(msg), decompressedContent); - } else if (msg instanceof BinaryWebSocketFrame) { - outMsg = new BinaryWebSocketFrame(msg.isFinalFragment(), newRsv(msg), decompressedContent); - } else if (msg instanceof ContinuationWebSocketFrame) { - outMsg = new ContinuationWebSocketFrame(msg.isFinalFragment(), newRsv(msg), decompressedContent); - } else { - throw new CodecException("unexpected frame type: " + msg.getClass().getName()); - } - - ctx.fireChannelRead(outMsg); - } - - @Override - public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { - cleanup(); - super.handlerRemoved(ctx); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - cleanup(); - super.channelInactive(ctx); - } - - private ByteBuf decompressContent(ChannelHandlerContext ctx, WebSocketFrame msg) { - if (decoder == null) { - if (!(msg instanceof TextWebSocketFrame) && !(msg instanceof BinaryWebSocketFrame)) { - throw new CodecException("unexpected initial frame type: " + msg.getClass().getName()); - } - decoder = new EmbeddedChannel(ZlibCodecFactory.newZlibDecoder(ZlibWrapper.NONE)); - } - - boolean readable = msg.content().isReadable(); - boolean emptyDeflateBlock = EMPTY_DEFLATE_BLOCK.equals(msg.content()); - - decoder.writeInbound(msg.content().retain()); - if (appendFrameTail(msg)) { - decoder.writeInbound(FRAME_TAIL.duplicate()); - } - - CompositeByteBuf compositeDecompressedContent = ctx.alloc().compositeBuffer(); - for (;;) { - ByteBuf partUncompressedContent = decoder.readInbound(); - if (partUncompressedContent == null) { - break; - } - if (!partUncompressedContent.isReadable()) { - partUncompressedContent.release(); - continue; - } - compositeDecompressedContent.addComponent(true, partUncompressedContent); - } - // Correctly handle empty frames - // See https://github.com/netty/netty/issues/4348 - if (!emptyDeflateBlock && readable && compositeDecompressedContent.numComponents() <= 0) { - // Sometimes after fragmentation the last frame - // May contain left-over data that doesn't affect decompression - if (!(msg instanceof ContinuationWebSocketFrame)) { - compositeDecompressedContent.release(); - throw new CodecException("cannot read uncompressed buffer"); - } - } - - if (msg.isFinalFragment() && noContext) { - cleanup(); - } - - return compositeDecompressedContent; - } - - private void cleanup() { - if (decoder != null) { - // Clean-up the previous encoder if not cleaned up correctly. - decoder.finishAndReleaseAll(); - decoder = null; - } - } -} 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 deleted file mode 100644 index 43525de477..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/DeflateEncoder.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx.extensions.compression; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.CompositeByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.CodecException; -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.ContinuationWebSocketFrame; -import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; -import io.netty.handler.codec.http.websocketx.WebSocketFrame; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionEncoder; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionFilter; - -import java.util.List; -import java.util.Objects; - -import static io.netty.handler.codec.http.websocketx.extensions.compression.PerMessageDeflateDecoder.*; - -/** - * Deflate implementation of a payload compressor for - * io.netty.handler.codec.http.websocketx.WebSocketFrame. - */ -abstract class DeflateEncoder extends WebSocketExtensionEncoder { - - private final int compressionLevel; - private final int windowSize; - private final boolean noContext; - private final WebSocketExtensionFilter extensionEncoderFilter; - - private EmbeddedChannel encoder; - - /** - * Constructor - * @param compressionLevel compression level of the compressor. - * @param windowSize maximum size of the window compressor buffer. - * @param noContext true to disable context takeover. - * @param extensionEncoderFilter extension encoder filter. - */ - DeflateEncoder(int compressionLevel, int windowSize, boolean noContext, - WebSocketExtensionFilter extensionEncoderFilter) { - this.compressionLevel = compressionLevel; - this.windowSize = windowSize; - this.noContext = noContext; - this.extensionEncoderFilter = Objects.requireNonNull(extensionEncoderFilter, "extensionEncoderFilter"); - } - - /** - * Returns the extension encoder filter. - */ - protected WebSocketExtensionFilter extensionEncoderFilter() { - return extensionEncoderFilter; - } - - /** - * @param msg the current frame. - * @return the rsv bits to set in the compressed frame. - */ - protected abstract int rsv(WebSocketFrame msg); - - /** - * @param msg the current frame. - * @return true if compressed payload tail needs to be removed. - */ - protected abstract boolean removeFrameTail(WebSocketFrame msg); - - @Override - protected void encode(ChannelHandlerContext ctx, WebSocketFrame msg, List out) throws Exception { - final ByteBuf compressedContent; - if (msg.content().isReadable()) { - compressedContent = compressContent(ctx, msg); - } else if (msg.isFinalFragment()) { - // Set empty DEFLATE block manually for unknown buffer size - // https://tools.ietf.org/html/rfc7692#section-7.2.3.6 - compressedContent = EMPTY_DEFLATE_BLOCK.duplicate(); - } else { - throw new CodecException("cannot compress content buffer"); - } - - final WebSocketFrame outMsg; - if (msg instanceof TextWebSocketFrame) { - outMsg = new TextWebSocketFrame(msg.isFinalFragment(), rsv(msg), compressedContent); - } else if (msg instanceof BinaryWebSocketFrame) { - outMsg = new BinaryWebSocketFrame(msg.isFinalFragment(), rsv(msg), compressedContent); - } else if (msg instanceof ContinuationWebSocketFrame) { - outMsg = new ContinuationWebSocketFrame(msg.isFinalFragment(), rsv(msg), compressedContent); - } else { - throw new CodecException("unexpected frame type: " + msg.getClass().getName()); - } - - out.add(outMsg); - } - - @Override - public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { - cleanup(); - super.handlerRemoved(ctx); - } - - private ByteBuf compressContent(ChannelHandlerContext ctx, WebSocketFrame msg) { - if (encoder == null) { - encoder = new EmbeddedChannel(ZlibCodecFactory.newZlibEncoder( - ZlibWrapper.NONE, compressionLevel, windowSize, 8)); - } - - encoder.writeOutbound(msg.content().retain()); - - CompositeByteBuf fullCompressedContent = ctx.alloc().compositeBuffer(); - for (;;) { - ByteBuf partCompressedContent = encoder.readOutbound(); - if (partCompressedContent == null) { - break; - } - if (!partCompressedContent.isReadable()) { - partCompressedContent.release(); - continue; - } - fullCompressedContent.addComponent(true, partCompressedContent); - } - - if (fullCompressedContent.numComponents() <= 0) { - fullCompressedContent.release(); - throw new CodecException("cannot read compressed buffer"); - } - - if (msg.isFinalFragment() && noContext) { - cleanup(); - } - - ByteBuf compressedContent; - if (removeFrameTail(msg)) { - int realLength = fullCompressedContent.readableBytes() - FRAME_TAIL.readableBytes(); - compressedContent = fullCompressedContent.slice(0, realLength); - } else { - compressedContent = fullCompressedContent; - } - - return compressedContent; - } - - private void cleanup() { - if (encoder != null) { - // Clean-up the previous encoder if not cleaned up correctly. - encoder.finishAndReleaseAll(); - encoder = null; - } - } -} 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 deleted file mode 100644 index dbeeefc15c..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/DeflateFrameClientExtensionHandshaker.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx.extensions.compression; - -import io.netty.handler.codec.http.websocketx.extensions.WebSocketClientExtension; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketClientExtensionHandshaker; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionData; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionDecoder; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionEncoder; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionFilterProvider; - -import java.util.Collections; -import java.util.Objects; - -import static io.netty.handler.codec.http.websocketx.extensions.compression.DeflateFrameServerExtensionHandshaker.*; - -/** - * perframe-deflate - * handshake implementation. - */ -public final class DeflateFrameClientExtensionHandshaker implements WebSocketClientExtensionHandshaker { - - private final int compressionLevel; - private final boolean useWebkitExtensionName; - private final WebSocketExtensionFilterProvider extensionFilterProvider; - - /** - * Constructor with default configuration. - */ - public DeflateFrameClientExtensionHandshaker(boolean useWebkitExtensionName) { - this(6, useWebkitExtensionName); - } - - /** - * Constructor with custom configuration. - * - * @param compressionLevel - * Compression level between 0 and 9 (default is 6). - */ - public DeflateFrameClientExtensionHandshaker(int compressionLevel, boolean useWebkitExtensionName) { - this(compressionLevel, useWebkitExtensionName, WebSocketExtensionFilterProvider.DEFAULT); - } - - /** - * Constructor with custom configuration. - * - * @param compressionLevel - * Compression level between 0 and 9 (default is 6). - * @param extensionFilterProvider - * provides client extension filters for per frame deflate encoder and decoder. - */ - public DeflateFrameClientExtensionHandshaker(int compressionLevel, boolean useWebkitExtensionName, - WebSocketExtensionFilterProvider extensionFilterProvider) { - if (compressionLevel < 0 || compressionLevel > 9) { - throw new IllegalArgumentException( - "compressionLevel: " + compressionLevel + " (expected: 0-9)"); - } - this.compressionLevel = compressionLevel; - this.useWebkitExtensionName = useWebkitExtensionName; - this.extensionFilterProvider = Objects.requireNonNull(extensionFilterProvider, "extensionFilterProvider"); - } - - @Override - public WebSocketExtensionData newRequestData() { - return new WebSocketExtensionData( - useWebkitExtensionName ? X_WEBKIT_DEFLATE_FRAME_EXTENSION : DEFLATE_FRAME_EXTENSION, - Collections.emptyMap()); - } - - @Override - public WebSocketClientExtension handshakeExtension(WebSocketExtensionData extensionData) { - if (!X_WEBKIT_DEFLATE_FRAME_EXTENSION.equals(extensionData.name()) && - !DEFLATE_FRAME_EXTENSION.equals(extensionData.name())) { - return null; - } - - if (extensionData.parameters().isEmpty()) { - return new DeflateFrameClientExtension(compressionLevel, extensionFilterProvider); - } else { - return null; - } - } - - private static class DeflateFrameClientExtension implements WebSocketClientExtension { - - private final int compressionLevel; - private final WebSocketExtensionFilterProvider extensionFilterProvider; - - DeflateFrameClientExtension(int compressionLevel, WebSocketExtensionFilterProvider extensionFilterProvider) { - this.compressionLevel = compressionLevel; - this.extensionFilterProvider = extensionFilterProvider; - } - - @Override - public int rsv() { - return RSV1; - } - - @Override - public WebSocketExtensionEncoder newExtensionEncoder() { - return new PerFrameDeflateEncoder(compressionLevel, 15, false, - extensionFilterProvider.encoderFilter()); - } - - @Override - public WebSocketExtensionDecoder newExtensionDecoder() { - return new PerFrameDeflateDecoder(false, extensionFilterProvider.decoderFilter()); - } - } - -} 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 deleted file mode 100644 index d1129f7de1..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/DeflateFrameServerExtensionHandshaker.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx.extensions.compression; - -import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionData; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionDecoder; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionEncoder; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionFilterProvider; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketServerExtension; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketServerExtensionHandshaker; - -import java.util.Collections; -import java.util.Objects; - -/** - * perframe-deflate - * handshake implementation. - */ -public final class DeflateFrameServerExtensionHandshaker implements WebSocketServerExtensionHandshaker { - - static final String X_WEBKIT_DEFLATE_FRAME_EXTENSION = "x-webkit-deflate-frame"; - static final String DEFLATE_FRAME_EXTENSION = "deflate-frame"; - - private final int compressionLevel; - private final WebSocketExtensionFilterProvider extensionFilterProvider; - - /** - * Constructor with default configuration. - */ - public DeflateFrameServerExtensionHandshaker() { - this(6); - } - - /** - * Constructor with custom configuration. - * - * @param compressionLevel - * Compression level between 0 and 9 (default is 6). - */ - public DeflateFrameServerExtensionHandshaker(int compressionLevel) { - this(compressionLevel, WebSocketExtensionFilterProvider.DEFAULT); - } - - /** - * Constructor with custom configuration. - * - * @param compressionLevel - * Compression level between 0 and 9 (default is 6). - * @param extensionFilterProvider - * provides server extension filters for per frame deflate encoder and decoder. - */ - public DeflateFrameServerExtensionHandshaker(int compressionLevel, - WebSocketExtensionFilterProvider extensionFilterProvider) { - if (compressionLevel < 0 || compressionLevel > 9) { - throw new IllegalArgumentException( - "compressionLevel: " + compressionLevel + " (expected: 0-9)"); - } - this.compressionLevel = compressionLevel; - this.extensionFilterProvider = Objects.requireNonNull(extensionFilterProvider, "extensionFilterProvider"); - } - - @Override - public WebSocketServerExtension handshakeExtension(WebSocketExtensionData extensionData) { - if (!X_WEBKIT_DEFLATE_FRAME_EXTENSION.equals(extensionData.name()) && - !DEFLATE_FRAME_EXTENSION.equals(extensionData.name())) { - return null; - } - - if (extensionData.parameters().isEmpty()) { - return new DeflateFrameServerExtension(compressionLevel, extensionData.name(), extensionFilterProvider); - } else { - return null; - } - } - - private static class DeflateFrameServerExtension implements WebSocketServerExtension { - - private final String extensionName; - private final int compressionLevel; - private final WebSocketExtensionFilterProvider extensionFilterProvider; - - DeflateFrameServerExtension(int compressionLevel, String extensionName, - WebSocketExtensionFilterProvider extensionFilterProvider) { - this.extensionName = extensionName; - this.compressionLevel = compressionLevel; - this.extensionFilterProvider = extensionFilterProvider; - } - - @Override - public int rsv() { - return RSV1; - } - - @Override - public WebSocketExtensionEncoder newExtensionEncoder() { - return new PerFrameDeflateEncoder(compressionLevel, 15, false, - extensionFilterProvider.encoderFilter()); - } - - @Override - public WebSocketExtensionDecoder newExtensionDecoder() { - return new PerFrameDeflateDecoder(false, extensionFilterProvider.decoderFilter()); - } - - @Override - public WebSocketExtensionData newResponseData() { - return new WebSocketExtensionData(extensionName, Collections.emptyMap()); - } - } - -} 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 deleted file mode 100644 index a987e2c640..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerFrameDeflateDecoder.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx.extensions.compression; - -import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; -import io.netty.handler.codec.http.websocketx.ContinuationWebSocketFrame; -import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; -import io.netty.handler.codec.http.websocketx.WebSocketFrame; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtension; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionFilter; - -/** - * Per-frame implementation of deflate decompressor. - */ -class PerFrameDeflateDecoder extends DeflateDecoder { - - /** - * Constructor - * - * @param noContext true to disable context takeover. - */ - PerFrameDeflateDecoder(boolean noContext) { - super(noContext, WebSocketExtensionFilter.NEVER_SKIP); - } - - /** - * Constructor - * - * @param noContext true to disable context takeover. - * @param extensionDecoderFilter extension decoder filter for per frame deflate decoder. - */ - PerFrameDeflateDecoder(boolean noContext, WebSocketExtensionFilter extensionDecoderFilter) { - super(noContext, extensionDecoderFilter); - } - - @Override - public boolean acceptInboundMessage(Object msg) throws Exception { - if (!super.acceptInboundMessage(msg)) { - return false; - } - - WebSocketFrame wsFrame = (WebSocketFrame) msg; - if (extensionDecoderFilter().mustSkip(wsFrame)) { - return false; - } - - return (msg instanceof TextWebSocketFrame || msg instanceof BinaryWebSocketFrame || - msg instanceof ContinuationWebSocketFrame) && - (wsFrame.rsv() & WebSocketExtension.RSV1) > 0; - } - - @Override - protected int newRsv(WebSocketFrame msg) { - return msg.rsv() ^ WebSocketExtension.RSV1; - } - - @Override - protected boolean appendFrameTail(WebSocketFrame msg) { - return true; - } - -} 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 deleted file mode 100644 index 9cd3ede1d0..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerFrameDeflateEncoder.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx.extensions.compression; - -import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; -import io.netty.handler.codec.http.websocketx.ContinuationWebSocketFrame; -import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; -import io.netty.handler.codec.http.websocketx.WebSocketFrame; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtension; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionFilter; - -/** - * Per-frame implementation of deflate compressor. - */ -class PerFrameDeflateEncoder extends DeflateEncoder { - - /** - * Constructor - * - * @param compressionLevel compression level of the compressor. - * @param windowSize maximum size of the window compressor buffer. - * @param noContext true to disable context takeover. - */ - PerFrameDeflateEncoder(int compressionLevel, int windowSize, boolean noContext) { - super(compressionLevel, windowSize, noContext, WebSocketExtensionFilter.NEVER_SKIP); - } - - /** - * Constructor - * - * @param compressionLevel compression level of the compressor. - * @param windowSize maximum size of the window compressor buffer. - * @param noContext true to disable context takeover. - * @param extensionEncoderFilter extension encoder filter for per frame deflate encoder. - */ - PerFrameDeflateEncoder(int compressionLevel, int windowSize, boolean noContext, - WebSocketExtensionFilter extensionEncoderFilter) { - super(compressionLevel, windowSize, noContext, extensionEncoderFilter); - } - - @Override - public boolean acceptOutboundMessage(Object msg) throws Exception { - if (!super.acceptOutboundMessage(msg)) { - return false; - } - - WebSocketFrame wsFrame = (WebSocketFrame) msg; - if (extensionEncoderFilter().mustSkip(wsFrame)) { - return false; - } - - return (msg instanceof TextWebSocketFrame || msg instanceof BinaryWebSocketFrame || - msg instanceof ContinuationWebSocketFrame) && - wsFrame.content().readableBytes() > 0 && - (wsFrame.rsv() & WebSocketExtension.RSV1) == 0; - } - - @Override - protected int rsv(WebSocketFrame msg) { - return msg.rsv() | WebSocketExtension.RSV1; - } - - @Override - protected boolean removeFrameTail(WebSocketFrame msg) { - return true; - } - -} 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 deleted file mode 100644 index bf24a13baf..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateClientExtensionHandshaker.java +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx.extensions.compression; - -import io.netty.handler.codec.compression.ZlibCodecFactory; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketClientExtension; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketClientExtensionHandshaker; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionData; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionDecoder; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionEncoder; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionFilterProvider; - -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map.Entry; -import java.util.Objects; - -import static io.netty.handler.codec.http.websocketx.extensions.compression.PerMessageDeflateServerExtensionHandshaker.*; - -/** - * permessage-deflate - * handshake implementation. - */ -public final class PerMessageDeflateClientExtensionHandshaker implements WebSocketClientExtensionHandshaker { - - private final int compressionLevel; - private final boolean allowClientWindowSize; - private final int requestedServerWindowSize; - private final boolean allowClientNoContext; - private final boolean requestedServerNoContext; - private final WebSocketExtensionFilterProvider extensionFilterProvider; - - /** - * Constructor with default configuration. - */ - public PerMessageDeflateClientExtensionHandshaker() { - this(6, ZlibCodecFactory.isSupportingWindowSizeAndMemLevel(), MAX_WINDOW_SIZE, false, false); - } - - /** - * Constructor with custom configuration. - * - * @param compressionLevel - * Compression level between 0 and 9 (default is 6). - * @param allowClientWindowSize - * allows WebSocket server to customize the client inflater window size - * (default is false). - * @param requestedServerWindowSize - * indicates the requested sever window size to use if server inflater is customizable. - * @param allowClientNoContext - * allows WebSocket server to activate client_no_context_takeover - * (default is false). - * @param requestedServerNoContext - * indicates if client needs to activate server_no_context_takeover - * if server is compatible with (default is false). - */ - public PerMessageDeflateClientExtensionHandshaker(int compressionLevel, - boolean allowClientWindowSize, int requestedServerWindowSize, - boolean allowClientNoContext, boolean requestedServerNoContext) { - this(compressionLevel, allowClientWindowSize, requestedServerWindowSize, - allowClientNoContext, requestedServerNoContext, WebSocketExtensionFilterProvider.DEFAULT); - } - - /** - * Constructor with custom configuration. - * - * @param compressionLevel - * Compression level between 0 and 9 (default is 6). - * @param allowClientWindowSize - * allows WebSocket server to customize the client inflater window size - * (default is false). - * @param requestedServerWindowSize - * indicates the requested sever window size to use if server inflater is customizable. - * @param allowClientNoContext - * allows WebSocket server to activate client_no_context_takeover - * (default is false). - * @param requestedServerNoContext - * indicates if client needs to activate server_no_context_takeover - * if server is compatible with (default is false). - * @param extensionFilterProvider - * provides client extension filters for per message deflate encoder and decoder. - */ - public PerMessageDeflateClientExtensionHandshaker(int compressionLevel, - boolean allowClientWindowSize, int requestedServerWindowSize, - boolean allowClientNoContext, boolean requestedServerNoContext, - WebSocketExtensionFilterProvider extensionFilterProvider) { - - if (requestedServerWindowSize > MAX_WINDOW_SIZE || requestedServerWindowSize < MIN_WINDOW_SIZE) { - throw new IllegalArgumentException( - "requestedServerWindowSize: " + requestedServerWindowSize + " (expected: 8-15)"); - } - if (compressionLevel < 0 || compressionLevel > 9) { - throw new IllegalArgumentException( - "compressionLevel: " + compressionLevel + " (expected: 0-9)"); - } - this.compressionLevel = compressionLevel; - this.allowClientWindowSize = allowClientWindowSize; - this.requestedServerWindowSize = requestedServerWindowSize; - this.allowClientNoContext = allowClientNoContext; - this.requestedServerNoContext = requestedServerNoContext; - this.extensionFilterProvider = Objects.requireNonNull(extensionFilterProvider, "extensionFilterProvider"); - } - - @Override - public WebSocketExtensionData newRequestData() { - HashMap parameters = new HashMap<>(4); - if (requestedServerNoContext) { - parameters.put(SERVER_NO_CONTEXT, null); - } - if (allowClientNoContext) { - parameters.put(CLIENT_NO_CONTEXT, null); - } - if (requestedServerWindowSize != MAX_WINDOW_SIZE) { - parameters.put(SERVER_MAX_WINDOW, Integer.toString(requestedServerWindowSize)); - } - if (allowClientWindowSize) { - parameters.put(CLIENT_MAX_WINDOW, null); - } - return new WebSocketExtensionData(PERMESSAGE_DEFLATE_EXTENSION, parameters); - } - - @Override - public WebSocketClientExtension handshakeExtension(WebSocketExtensionData extensionData) { - if (!PERMESSAGE_DEFLATE_EXTENSION.equals(extensionData.name())) { - return null; - } - - boolean succeed = true; - int clientWindowSize = MAX_WINDOW_SIZE; - int serverWindowSize = MAX_WINDOW_SIZE; - boolean serverNoContext = false; - boolean clientNoContext = false; - - Iterator> parametersIterator = - extensionData.parameters().entrySet().iterator(); - while (succeed && parametersIterator.hasNext()) { - Entry parameter = parametersIterator.next(); - - if (CLIENT_MAX_WINDOW.equalsIgnoreCase(parameter.getKey())) { - // allowed client_window_size_bits - if (allowClientWindowSize) { - clientWindowSize = Integer.parseInt(parameter.getValue()); - if (clientWindowSize > MAX_WINDOW_SIZE || clientWindowSize < MIN_WINDOW_SIZE) { - succeed = false; - } - } else { - succeed = false; - } - } else if (SERVER_MAX_WINDOW.equalsIgnoreCase(parameter.getKey())) { - // acknowledged server_window_size_bits - serverWindowSize = Integer.parseInt(parameter.getValue()); - if (serverWindowSize > MAX_WINDOW_SIZE || serverWindowSize < MIN_WINDOW_SIZE) { - succeed = false; - } - } else if (CLIENT_NO_CONTEXT.equalsIgnoreCase(parameter.getKey())) { - // allowed client_no_context_takeover - if (allowClientNoContext) { - clientNoContext = true; - } else { - succeed = false; - } - } else if (SERVER_NO_CONTEXT.equalsIgnoreCase(parameter.getKey())) { - // acknowledged server_no_context_takeover - serverNoContext = true; - } else { - // unknown parameter - succeed = false; - } - } - - if ((requestedServerNoContext && !serverNoContext) || - requestedServerWindowSize < serverWindowSize) { - succeed = false; - } - - if (succeed) { - return new PermessageDeflateExtension(serverNoContext, serverWindowSize, - clientNoContext, clientWindowSize, extensionFilterProvider); - } else { - return null; - } - } - - private final class PermessageDeflateExtension implements WebSocketClientExtension { - - private final boolean serverNoContext; - private final int serverWindowSize; - private final boolean clientNoContext; - private final int clientWindowSize; - private final WebSocketExtensionFilterProvider extensionFilterProvider; - - @Override - public int rsv() { - return RSV1; - } - - PermessageDeflateExtension(boolean serverNoContext, int serverWindowSize, - boolean clientNoContext, int clientWindowSize, - WebSocketExtensionFilterProvider extensionFilterProvider) { - this.serverNoContext = serverNoContext; - this.serverWindowSize = serverWindowSize; - this.clientNoContext = clientNoContext; - this.clientWindowSize = clientWindowSize; - this.extensionFilterProvider = extensionFilterProvider; - } - - @Override - public WebSocketExtensionEncoder newExtensionEncoder() { - return new PerMessageDeflateEncoder(compressionLevel, clientWindowSize, clientNoContext, - extensionFilterProvider.encoderFilter()); - } - - @Override - public WebSocketExtensionDecoder newExtensionDecoder() { - return new PerMessageDeflateDecoder(serverNoContext, extensionFilterProvider.decoderFilter()); - } - } - -} 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 deleted file mode 100644 index 5928dc32bc..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateDecoder.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx.extensions.compression; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; -import io.netty.handler.codec.http.websocketx.ContinuationWebSocketFrame; -import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; -import io.netty.handler.codec.http.websocketx.WebSocketFrame; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtension; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionFilter; - -/** - * Per-message implementation of deflate decompressor. - */ -class PerMessageDeflateDecoder extends DeflateDecoder { - - private boolean compressing; - - /** - * Constructor - * - * @param noContext true to disable context takeover. - */ - PerMessageDeflateDecoder(boolean noContext) { - super(noContext, WebSocketExtensionFilter.NEVER_SKIP); - } - - /** - * Constructor - * - * @param noContext true to disable context takeover. - * @param extensionDecoderFilter extension decoder for per message deflate decoder. - */ - PerMessageDeflateDecoder(boolean noContext, WebSocketExtensionFilter extensionDecoderFilter) { - super(noContext, extensionDecoderFilter); - } - - @Override - public boolean acceptInboundMessage(Object msg) throws Exception { - if (!super.acceptInboundMessage(msg)) { - return false; - } - - WebSocketFrame wsFrame = (WebSocketFrame) msg; - if (extensionDecoderFilter().mustSkip(wsFrame)) { - if (compressing) { - throw new IllegalStateException("Cannot skip per message deflate decoder, compression in progress"); - } - return false; - } - - return ((wsFrame instanceof TextWebSocketFrame || wsFrame instanceof BinaryWebSocketFrame) && - (wsFrame.rsv() & WebSocketExtension.RSV1) > 0) || - (wsFrame instanceof ContinuationWebSocketFrame && compressing); - } - - @Override - protected int newRsv(WebSocketFrame msg) { - return (msg.rsv() & WebSocketExtension.RSV1) > 0? - msg.rsv() ^ WebSocketExtension.RSV1 : msg.rsv(); - } - - @Override - protected boolean appendFrameTail(WebSocketFrame msg) { - return msg.isFinalFragment(); - } - - @Override - protected void decode(ChannelHandlerContext ctx, WebSocketFrame msg) throws Exception { - boolean isFinal = msg.isFinalFragment(); - super.decode(ctx, msg); - - if (isFinal) { - compressing = false; - } else if (msg instanceof TextWebSocketFrame || msg instanceof BinaryWebSocketFrame) { - compressing = true; - } - } - -} 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 deleted file mode 100644 index 9e3d274d8d..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateEncoder.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx.extensions.compression; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; -import io.netty.handler.codec.http.websocketx.ContinuationWebSocketFrame; -import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; -import io.netty.handler.codec.http.websocketx.WebSocketFrame; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtension; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionFilter; - -import java.util.List; - -/** - * Per-message implementation of deflate compressor. - */ -class PerMessageDeflateEncoder extends DeflateEncoder { - - private boolean compressing; - - /** - * Constructor - * - * @param compressionLevel compression level of the compressor. - * @param windowSize maximum size of the window compressor buffer. - * @param noContext true to disable context takeover. - */ - PerMessageDeflateEncoder(int compressionLevel, int windowSize, boolean noContext) { - super(compressionLevel, windowSize, noContext, WebSocketExtensionFilter.NEVER_SKIP); - } - - /** - * Constructor - * - * @param compressionLevel compression level of the compressor. - * @param windowSize maximum size of the window compressor buffer. - * @param noContext true to disable context takeover. - * @param extensionEncoderFilter extension filter for per message deflate encoder. - */ - PerMessageDeflateEncoder(int compressionLevel, int windowSize, boolean noContext, - WebSocketExtensionFilter extensionEncoderFilter) { - super(compressionLevel, windowSize, noContext, extensionEncoderFilter); - } - - @Override - public boolean acceptOutboundMessage(Object msg) throws Exception { - if (!super.acceptOutboundMessage(msg)) { - return false; - } - - WebSocketFrame wsFrame = (WebSocketFrame) msg; - if (extensionEncoderFilter().mustSkip(wsFrame)) { - if (compressing) { - throw new IllegalStateException("Cannot skip per message deflate encoder, compression in progress"); - } - return false; - } - - return ((wsFrame instanceof TextWebSocketFrame || wsFrame instanceof BinaryWebSocketFrame) && - (wsFrame.rsv() & WebSocketExtension.RSV1) == 0) || - (wsFrame instanceof ContinuationWebSocketFrame && compressing); - } - - @Override - protected int rsv(WebSocketFrame msg) { - return msg instanceof TextWebSocketFrame || msg instanceof BinaryWebSocketFrame? - msg.rsv() | WebSocketExtension.RSV1 : msg.rsv(); - } - - @Override - protected boolean removeFrameTail(WebSocketFrame msg) { - return msg.isFinalFragment(); - } - - @Override - protected void encode(ChannelHandlerContext ctx, WebSocketFrame msg, - List out) throws Exception { - super.encode(ctx, msg, out); - - if (msg.isFinalFragment()) { - compressing = false; - } else if (msg instanceof TextWebSocketFrame || msg instanceof BinaryWebSocketFrame) { - compressing = true; - } - } - -} 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 deleted file mode 100644 index 63d0f1c03d..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateServerExtensionHandshaker.java +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx.extensions.compression; - -import io.netty.handler.codec.compression.ZlibCodecFactory; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionData; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionDecoder; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionEncoder; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionFilterProvider; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketServerExtension; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketServerExtensionHandshaker; - -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map.Entry; -import java.util.Objects; - -/** - * permessage-deflate - * handshake implementation. - */ -public final class PerMessageDeflateServerExtensionHandshaker implements WebSocketServerExtensionHandshaker { - - public static final int MIN_WINDOW_SIZE = 8; - public static final int MAX_WINDOW_SIZE = 15; - - static final String PERMESSAGE_DEFLATE_EXTENSION = "permessage-deflate"; - static final String CLIENT_MAX_WINDOW = "client_max_window_bits"; - static final String SERVER_MAX_WINDOW = "server_max_window_bits"; - static final String CLIENT_NO_CONTEXT = "client_no_context_takeover"; - static final String SERVER_NO_CONTEXT = "server_no_context_takeover"; - - private final int compressionLevel; - private final boolean allowServerWindowSize; - private final int preferredClientWindowSize; - private final boolean allowServerNoContext; - private final boolean preferredClientNoContext; - private final WebSocketExtensionFilterProvider extensionFilterProvider; - - /** - * Constructor with default configuration. - */ - public PerMessageDeflateServerExtensionHandshaker() { - this(6, ZlibCodecFactory.isSupportingWindowSizeAndMemLevel(), MAX_WINDOW_SIZE, false, false); - } - - /** - * Constructor with custom configuration. - * - * @param compressionLevel - * Compression level between 0 and 9 (default is 6). - * @param allowServerWindowSize - * allows WebSocket client to customize the server inflater window size - * (default is false). - * @param preferredClientWindowSize - * indicates the preferred client window size to use if client inflater is customizable. - * @param allowServerNoContext - * allows WebSocket client to activate server_no_context_takeover - * (default is false). - * @param preferredClientNoContext - * indicates if server prefers to activate client_no_context_takeover - * if client is compatible with (default is false). - */ - public PerMessageDeflateServerExtensionHandshaker(int compressionLevel, boolean allowServerWindowSize, - int preferredClientWindowSize, - boolean allowServerNoContext, boolean preferredClientNoContext) { - this(compressionLevel, allowServerWindowSize, preferredClientWindowSize, allowServerNoContext, - preferredClientNoContext, WebSocketExtensionFilterProvider.DEFAULT); - } - - /** - * Constructor with custom configuration. - * - * @param compressionLevel - * Compression level between 0 and 9 (default is 6). - * @param allowServerWindowSize - * allows WebSocket client to customize the server inflater window size - * (default is false). - * @param preferredClientWindowSize - * indicates the preferred client window size to use if client inflater is customizable. - * @param allowServerNoContext - * allows WebSocket client to activate server_no_context_takeover - * (default is false). - * @param preferredClientNoContext - * indicates if server prefers to activate client_no_context_takeover - * if client is compatible with (default is false). - * @param extensionFilterProvider - * provides server extension filters for per message deflate encoder and decoder. - */ - public PerMessageDeflateServerExtensionHandshaker(int compressionLevel, boolean allowServerWindowSize, - int preferredClientWindowSize, - boolean allowServerNoContext, boolean preferredClientNoContext, - WebSocketExtensionFilterProvider extensionFilterProvider) { - if (preferredClientWindowSize > MAX_WINDOW_SIZE || preferredClientWindowSize < MIN_WINDOW_SIZE) { - throw new IllegalArgumentException( - "preferredServerWindowSize: " + preferredClientWindowSize + " (expected: 8-15)"); - } - if (compressionLevel < 0 || compressionLevel > 9) { - throw new IllegalArgumentException( - "compressionLevel: " + compressionLevel + " (expected: 0-9)"); - } - this.compressionLevel = compressionLevel; - this.allowServerWindowSize = allowServerWindowSize; - this.preferredClientWindowSize = preferredClientWindowSize; - this.allowServerNoContext = allowServerNoContext; - this.preferredClientNoContext = preferredClientNoContext; - this.extensionFilterProvider = Objects.requireNonNull(extensionFilterProvider, "extensionFilterProvider"); - } - - @Override - public WebSocketServerExtension handshakeExtension(WebSocketExtensionData extensionData) { - if (!PERMESSAGE_DEFLATE_EXTENSION.equals(extensionData.name())) { - return null; - } - - boolean deflateEnabled = true; - int clientWindowSize = MAX_WINDOW_SIZE; - int serverWindowSize = MAX_WINDOW_SIZE; - boolean serverNoContext = false; - boolean clientNoContext = false; - - Iterator> parametersIterator = - extensionData.parameters().entrySet().iterator(); - while (deflateEnabled && parametersIterator.hasNext()) { - Entry parameter = parametersIterator.next(); - - if (CLIENT_MAX_WINDOW.equalsIgnoreCase(parameter.getKey())) { - // use preferred clientWindowSize because client is compatible with customization - clientWindowSize = preferredClientWindowSize; - } else if (SERVER_MAX_WINDOW.equalsIgnoreCase(parameter.getKey())) { - // use provided windowSize if it is allowed - if (allowServerWindowSize) { - serverWindowSize = Integer.parseInt(parameter.getValue()); - if (serverWindowSize > MAX_WINDOW_SIZE || serverWindowSize < MIN_WINDOW_SIZE) { - deflateEnabled = false; - } - } else { - deflateEnabled = false; - } - } else if (CLIENT_NO_CONTEXT.equalsIgnoreCase(parameter.getKey())) { - // use preferred clientNoContext because client is compatible with customization - clientNoContext = preferredClientNoContext; - } else if (SERVER_NO_CONTEXT.equalsIgnoreCase(parameter.getKey())) { - // use server no context if allowed - if (allowServerNoContext) { - serverNoContext = true; - } else { - deflateEnabled = false; - } - } else { - // unknown parameter - deflateEnabled = false; - } - } - - if (deflateEnabled) { - return new PermessageDeflateExtension(compressionLevel, serverNoContext, - serverWindowSize, clientNoContext, clientWindowSize, extensionFilterProvider); - } else { - return null; - } - } - - private static class PermessageDeflateExtension implements WebSocketServerExtension { - - private final int compressionLevel; - private final boolean serverNoContext; - private final int serverWindowSize; - private final boolean clientNoContext; - private final int clientWindowSize; - private final WebSocketExtensionFilterProvider extensionFilterProvider; - - PermessageDeflateExtension(int compressionLevel, boolean serverNoContext, - int serverWindowSize, boolean clientNoContext, int clientWindowSize, - WebSocketExtensionFilterProvider extensionFilterProvider) { - this.compressionLevel = compressionLevel; - this.serverNoContext = serverNoContext; - this.serverWindowSize = serverWindowSize; - this.clientNoContext = clientNoContext; - this.clientWindowSize = clientWindowSize; - this.extensionFilterProvider = extensionFilterProvider; - } - - @Override - public int rsv() { - return RSV1; - } - - @Override - public WebSocketExtensionEncoder newExtensionEncoder() { - return new PerMessageDeflateEncoder(compressionLevel, serverWindowSize, serverNoContext, - extensionFilterProvider.encoderFilter()); - } - - @Override - public WebSocketExtensionDecoder newExtensionDecoder() { - return new PerMessageDeflateDecoder(clientNoContext, extensionFilterProvider.decoderFilter()); - } - - @Override - public WebSocketExtensionData newResponseData() { - HashMap parameters = new HashMap<>(4); - if (serverNoContext) { - parameters.put(SERVER_NO_CONTEXT, null); - } - if (clientNoContext) { - parameters.put(CLIENT_NO_CONTEXT, null); - } - if (serverWindowSize != MAX_WINDOW_SIZE) { - parameters.put(SERVER_MAX_WINDOW, Integer.toString(serverWindowSize)); - } - if (clientWindowSize != MAX_WINDOW_SIZE) { - parameters.put(CLIENT_MAX_WINDOW, Integer.toString(clientWindowSize)); - } - return new WebSocketExtensionData(PERMESSAGE_DEFLATE_EXTENSION, parameters); - } - } - -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/WebSocketClientCompressionHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/WebSocketClientCompressionHandler.java deleted file mode 100644 index 383215b7bc..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/WebSocketClientCompressionHandler.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx.extensions.compression; - -import io.netty.channel.ChannelHandler; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketClientExtensionHandler; - -/** - * Extends io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketClientExtensionHandler - * to handle the most common WebSocket Compression Extensions. - * - * See io.netty.example.http.websocketx.client.WebSocketClient for usage. - */ -@ChannelHandler.Sharable -public final class WebSocketClientCompressionHandler extends WebSocketClientExtensionHandler { - - public static final WebSocketClientCompressionHandler INSTANCE = new WebSocketClientCompressionHandler(); - - private WebSocketClientCompressionHandler() { - super(new PerMessageDeflateClientExtensionHandshaker(), - new DeflateFrameClientExtensionHandshaker(false), - new DeflateFrameClientExtensionHandshaker(true)); - } - -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/WebSocketServerCompressionHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/WebSocketServerCompressionHandler.java deleted file mode 100644 index ff07d8890e..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/WebSocketServerCompressionHandler.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx.extensions.compression; - -import io.netty.handler.codec.http.websocketx.extensions.WebSocketServerExtensionHandler; - -/** - * Extends io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketServerExtensionHandler - * to handle the most common WebSocket Compression Extensions. - * - * See io.netty.example.http.websocketx.html5.WebSocketServer for usage. - */ -public class WebSocketServerCompressionHandler extends WebSocketServerExtensionHandler { - - /** - * Constructor with default configuration. - */ - public WebSocketServerCompressionHandler() { - super(new PerMessageDeflateServerExtensionHandshaker(), - new DeflateFrameServerExtensionHandshaker()); - } - -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/package-info.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/package-info.java deleted file mode 100644 index ca029c141f..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/compression/package-info.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * Encoder, decoder, handshakers to handle most common WebSocket Compression Extensions. - *

- * This package supports different web socket extensions. - * The specification currently supported are: - *

- *

- *

- * See io.netty.example.http.websocketx.client.WebSocketClient and - * io.netty.example.http.websocketx.html5.WebSocketServer for usage. - *

- */ -package io.netty.handler.codec.http.websocketx.extensions.compression; diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/package-info.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/package-info.java deleted file mode 100644 index 89cef75039..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/package-info.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * Encoder, decoder, handshakers to handle - * WebSocket Extensions. - * - * See WebSocketServerExtensionHandler for more details. - */ -package io.netty.handler.codec.http.websocketx.extensions; diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/package-info.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/package-info.java deleted file mode 100644 index 6a203364b0..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/package-info.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * Encoder, decoder, handshakers and their related message types for - * Web Socket data frames. - *

- * This package supports different web socket specification versions (hence the X suffix). - * The specification current supported are: - *

- *

- *

- * For the detailed instruction on adding add Web Socket support to your HTTP - * server, take a look into the WebSocketServerX example located in the - * {@code io.netty.example.http.websocket} package. - *

- */ -package io.netty.handler.codec.http.websocketx; - diff --git a/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspDecoder.java deleted file mode 100644 index b1ebaca519..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspDecoder.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.rtsp; - -import java.util.regex.Pattern; - -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.DefaultFullHttpResponse; -import io.netty.handler.codec.http.DefaultHttpRequest; -import io.netty.handler.codec.http.DefaultHttpResponse; -import io.netty.handler.codec.http.HttpMessage; -import io.netty.handler.codec.http.HttpObjectDecoder; -import io.netty.handler.codec.http.HttpResponseStatus; - -/** - * Decodes {@link io.netty.buffer.ByteBuf}s into RTSP messages represented in - * {@link HttpMessage}s. - *

- *

Parameters that prevents excessive memory consumption

- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
NameMeaning
{@code maxInitialLineLength}The maximum length of the initial line - * (e.g. {@code "SETUP / RTSP/1.0"} or {@code "RTSP/1.0 200 OK"}) - * If the length of the initial line exceeds this value, a - * {@link io.netty.handler.codec.TooLongFrameException} will be raised.
{@code maxHeaderSize}The maximum length of all headers. If the sum of the length of each - * header exceeds this value, a {@link io.netty.handler.codec.TooLongFrameException} will be - * raised.
{@code maxContentLength}The maximum length of the content. If the content length exceeds this - * value, a {@link io.netty.handler.codec.TooLongFrameException} will be raised.
- */ -public class RtspDecoder extends HttpObjectDecoder { - /** - * Status code for unknown responses. - */ - private static final HttpResponseStatus UNKNOWN_STATUS = - new HttpResponseStatus(999, "Unknown"); - /** - * True if the message to decode is a request. - * False if the message to decode is a response. - */ - private boolean isDecodingRequest; - - /** - * Regex used on first line in message to detect if it is a response. - */ - private static final Pattern versionPattern = Pattern.compile("RTSP/\\d\\.\\d"); - - /** - * Constant for default max content length. - */ - public static final int DEFAULT_MAX_CONTENT_LENGTH = 8192; - - /** - * Creates a new instance with the default - * {@code maxInitialLineLength (4096)}, {@code maxHeaderSize (8192)}, and - * {@code maxContentLength (8192)}. - */ - public RtspDecoder() { - this(DEFAULT_MAX_INITIAL_LINE_LENGTH, - DEFAULT_MAX_HEADER_SIZE, - DEFAULT_MAX_CONTENT_LENGTH); - } - - /** - * Creates a new instance with the specified parameters. - * @param maxInitialLineLength The max allowed length of initial line - * @param maxHeaderSize The max allowed size of header - * @param maxContentLength The max allowed content length - */ - public RtspDecoder(final int maxInitialLineLength, - final int maxHeaderSize, - final int maxContentLength) { - super(maxInitialLineLength, maxHeaderSize, false); - } - - /** - * Creates a new instance with the specified parameters. - * @param maxInitialLineLength The max allowed length of initial line - * @param maxHeaderSize The max allowed size of header - * @param maxContentLength The max allowed content length - * @param validateHeaders Set to true if headers should be validated - */ - public RtspDecoder(final int maxInitialLineLength, - final int maxHeaderSize, - final int maxContentLength, - final boolean validateHeaders) { - super(maxInitialLineLength, - maxHeaderSize, - false, - validateHeaders); - } - - @Override - protected HttpMessage createMessage(final String[] initialLine) - throws Exception { - // If the first element of the initial line is a version string then - // this is a response - if (versionPattern.matcher(initialLine[0]).matches()) { - isDecodingRequest = false; - return new DefaultHttpResponse(RtspVersions.valueOf(initialLine[0]), - new HttpResponseStatus(Integer.parseInt(initialLine[1]), - initialLine[2]), - validateHeaders); - } else { - isDecodingRequest = true; - return new DefaultHttpRequest(RtspVersions.valueOf(initialLine[2]), - RtspMethods.valueOf(initialLine[0]), - initialLine[1], - validateHeaders); - } - } - - @Override - protected boolean isContentAlwaysEmpty(final HttpMessage msg) { - // Unlike HTTP, RTSP always assumes zero-length body if Content-Length - // header is absent. - return super.isContentAlwaysEmpty(msg) || !msg.headers().contains(RtspHeaderNames.CONTENT_LENGTH); - } - - @Override - protected HttpMessage createInvalidMessage() { - if (isDecodingRequest) { - return new DefaultFullHttpRequest(RtspVersions.RTSP_1_0, - RtspMethods.OPTIONS, "/bad-request", validateHeaders); - } else { - return new DefaultFullHttpResponse(RtspVersions.RTSP_1_0, - UNKNOWN_STATUS, - validateHeaders); - } - } - - @Override - protected boolean isDecodingRequest() { - return isDecodingRequest; - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspEncoder.java deleted file mode 100644 index 20987beadf..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspEncoder.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.rtsp; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.handler.codec.UnsupportedMessageTypeException; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpMessage; -import io.netty.handler.codec.http.HttpObjectEncoder; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.util.CharsetUtil; -import io.netty.util.internal.StringUtil; - -import static io.netty.handler.codec.http.HttpConstants.*; - -/** - * Encodes an RTSP message represented in {@link HttpMessage} or an {@link HttpContent} into - * a {@link ByteBuf}. - */ -public class RtspEncoder extends HttpObjectEncoder { - private static final int CRLF_SHORT = (CR << 8) | LF; - - @Override - public boolean acceptOutboundMessage(final Object msg) - throws Exception { - return super.acceptOutboundMessage(msg) && ((msg instanceof HttpRequest) || (msg instanceof HttpResponse)); - } - - @Override - protected void encodeInitialLine(final ByteBuf buf, final HttpMessage message) - throws Exception { - if (message instanceof HttpRequest) { - HttpRequest request = (HttpRequest) message; - ByteBufUtil.copy(request.method().asciiName(), buf); - buf.writeByte(SP); - buf.writeCharSequence(request.uri(), CharsetUtil.UTF_8); - buf.writeByte(SP); - buf.writeCharSequence(request.protocolVersion().toString(), CharsetUtil.US_ASCII); - ByteBufUtil.writeShortBE(buf, CRLF_SHORT); - } else if (message instanceof HttpResponse) { - HttpResponse response = (HttpResponse) message; - buf.writeCharSequence(response.protocolVersion().toString(), CharsetUtil.US_ASCII); - buf.writeByte(SP); - ByteBufUtil.copy(response.status().codeAsText(), buf); - buf.writeByte(SP); - buf.writeCharSequence(response.status().reasonPhrase(), CharsetUtil.US_ASCII); - ByteBufUtil.writeShortBE(buf, CRLF_SHORT); - } else { - throw new UnsupportedMessageTypeException("Unsupported type " - + StringUtil.simpleClassName(message)); - } - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspHeaderNames.java b/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspHeaderNames.java deleted file mode 100644 index f9c025c31c..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspHeaderNames.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.rtsp; - -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.util.AsciiString; - -/** - * Standard RTSP header names. - *

- * These are all defined as lowercase to support HTTP/2 requirements while also not - * violating RTSP/1.x requirements. New header names should always be lowercase. - */ -public final class RtspHeaderNames { - /** - * {@code "accept"} - */ - public static final AsciiString ACCEPT = HttpHeaderNames.ACCEPT; - /** - * {@code "accept-encoding"} - */ - public static final AsciiString ACCEPT_ENCODING = HttpHeaderNames.ACCEPT_ENCODING; - /** - * {@code "accept-language"} - */ - public static final AsciiString ACCEPT_LANGUAGE = HttpHeaderNames.ACCEPT_LANGUAGE; - /** - * {@code "allow"} - */ - public static final AsciiString ALLOW = AsciiString.cached("allow"); - /** - * {@code "authorization"} - */ - public static final AsciiString AUTHORIZATION = HttpHeaderNames.AUTHORIZATION; - /** - * {@code "bandwidth"} - */ - public static final AsciiString BANDWIDTH = AsciiString.cached("bandwidth"); - /** - * {@code "blocksize"} - */ - public static final AsciiString BLOCKSIZE = AsciiString.cached("blocksize"); - /** - * {@code "cache-control"} - */ - public static final AsciiString CACHE_CONTROL = HttpHeaderNames.CACHE_CONTROL; - /** - * {@code "conference"} - */ - public static final AsciiString CONFERENCE = AsciiString.cached("conference"); - /** - * {@code "connection"} - */ - public static final AsciiString CONNECTION = HttpHeaderNames.CONNECTION; - /** - * {@code "content-base"} - */ - public static final AsciiString CONTENT_BASE = HttpHeaderNames.CONTENT_BASE; - /** - * {@code "content-encoding"} - */ - public static final AsciiString CONTENT_ENCODING = HttpHeaderNames.CONTENT_ENCODING; - /** - * {@code "content-language"} - */ - public static final AsciiString CONTENT_LANGUAGE = HttpHeaderNames.CONTENT_LANGUAGE; - /** - * {@code "content-length"} - */ - public static final AsciiString CONTENT_LENGTH = HttpHeaderNames.CONTENT_LENGTH; - /** - * {@code "content-location"} - */ - public static final AsciiString CONTENT_LOCATION = HttpHeaderNames.CONTENT_LOCATION; - /** - * {@code "content-type"} - */ - public static final AsciiString CONTENT_TYPE = HttpHeaderNames.CONTENT_TYPE; - /** - * {@code "cseq"} - */ - public static final AsciiString CSEQ = AsciiString.cached("cseq"); - /** - * {@code "cate"} - */ - public static final AsciiString DATE = HttpHeaderNames.DATE; - /** - * {@code "expires"} - */ - public static final AsciiString EXPIRES = HttpHeaderNames.EXPIRES; - /** - * {@code "from"} - */ - public static final AsciiString FROM = HttpHeaderNames.FROM; - /** - * {@code "host"} - */ - public static final AsciiString HOST = HttpHeaderNames.HOST; - /** - * {@code "if-match"} - */ - public static final AsciiString IF_MATCH = HttpHeaderNames.IF_MATCH; - /** - * {@code "if-modified-since"} - */ - public static final AsciiString IF_MODIFIED_SINCE = HttpHeaderNames.IF_MODIFIED_SINCE; - /** - * {@code "keymgmt"} - */ - public static final AsciiString KEYMGMT = AsciiString.cached("keymgmt"); - /** - * {@code "last-modified"} - */ - public static final AsciiString LAST_MODIFIED = HttpHeaderNames.LAST_MODIFIED; - /** - * {@code "proxy-authenticate"} - */ - public static final AsciiString PROXY_AUTHENTICATE = HttpHeaderNames.PROXY_AUTHENTICATE; - /** - * {@code "proxy-require"} - */ - public static final AsciiString PROXY_REQUIRE = AsciiString.cached("proxy-require"); - /** - * {@code "public"} - */ - public static final AsciiString PUBLIC = AsciiString.cached("public"); - /** - * {@code "range"} - */ - public static final AsciiString RANGE = HttpHeaderNames.RANGE; - /** - * {@code "referer"} - */ - public static final AsciiString REFERER = HttpHeaderNames.REFERER; - /** - * {@code "require"} - */ - public static final AsciiString REQUIRE = AsciiString.cached("require"); - /** - * {@code "retry-after"} - */ - public static final AsciiString RETRT_AFTER = HttpHeaderNames.RETRY_AFTER; - /** - * {@code "rtp-info"} - */ - public static final AsciiString RTP_INFO = AsciiString.cached("rtp-info"); - /** - * {@code "scale"} - */ - public static final AsciiString SCALE = AsciiString.cached("scale"); - /** - * {@code "session"} - */ - public static final AsciiString SESSION = AsciiString.cached("session"); - /** - * {@code "server"} - */ - public static final AsciiString SERVER = HttpHeaderNames.SERVER; - /** - * {@code "speed"} - */ - public static final AsciiString SPEED = AsciiString.cached("speed"); - /** - * {@code "timestamp"} - */ - public static final AsciiString TIMESTAMP = AsciiString.cached("timestamp"); - /** - * {@code "transport"} - */ - public static final AsciiString TRANSPORT = AsciiString.cached("transport"); - /** - * {@code "unsupported"} - */ - public static final AsciiString UNSUPPORTED = AsciiString.cached("unsupported"); - /** - * {@code "user-agent"} - */ - public static final AsciiString USER_AGENT = HttpHeaderNames.USER_AGENT; - /** - * {@code "vary"} - */ - public static final AsciiString VARY = HttpHeaderNames.VARY; - /** - * {@code "via"} - */ - public static final AsciiString VIA = HttpHeaderNames.VIA; - /** - * {@code "www-authenticate"} - */ - public static final AsciiString WWW_AUTHENTICATE = HttpHeaderNames.WWW_AUTHENTICATE; - - private RtspHeaderNames() { } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspHeaderValues.java b/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspHeaderValues.java deleted file mode 100644 index 8c50276803..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspHeaderValues.java +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.rtsp; - -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.util.AsciiString; - -/** - * Standard RTSP header names. - */ -public final class RtspHeaderValues { - /** - * {@code "append"} - */ - public static final AsciiString APPEND = AsciiString.cached("append"); - /** - * {@code "AVP"} - */ - public static final AsciiString AVP = AsciiString.cached("AVP"); - /** - * {@code "bytes"} - */ - public static final AsciiString BYTES = HttpHeaderValues.BYTES; - /** - * {@code "charset"} - */ - public static final AsciiString CHARSET = HttpHeaderValues.CHARSET; - /** - * {@code "client_port"} - */ - public static final AsciiString CLIENT_PORT = AsciiString.cached("client_port"); - /** - * {@code "clock"} - */ - public static final AsciiString CLOCK = AsciiString.cached("clock"); - /** - * {@code "close"} - */ - public static final AsciiString CLOSE = HttpHeaderValues.CLOSE; - /** - * {@code "compress"} - */ - public static final AsciiString COMPRESS = HttpHeaderValues.COMPRESS; - /** - * {@code "100-continue"} - */ - public static final AsciiString CONTINUE = HttpHeaderValues.CONTINUE; - /** - * {@code "deflate"} - */ - public static final AsciiString DEFLATE = HttpHeaderValues.DEFLATE; - /** - * {@code "destination"} - */ - public static final AsciiString DESTINATION = AsciiString.cached("destination"); - /** - * {@code "gzip"} - */ - public static final AsciiString GZIP = HttpHeaderValues.GZIP; - /** - * {@code "identity"} - */ - public static final AsciiString IDENTITY = HttpHeaderValues.IDENTITY; - /** - * {@code "interleaved"} - */ - public static final AsciiString INTERLEAVED = AsciiString.cached("interleaved"); - /** - * {@code "keep-alive"} - */ - public static final AsciiString KEEP_ALIVE = HttpHeaderValues.KEEP_ALIVE; - /** - * {@code "layers"} - */ - public static final AsciiString LAYERS = AsciiString.cached("layers"); - /** - * {@code "max-age"} - */ - public static final AsciiString MAX_AGE = HttpHeaderValues.MAX_AGE; - /** - * {@code "max-stale"} - */ - public static final AsciiString MAX_STALE = HttpHeaderValues.MAX_STALE; - /** - * {@code "min-fresh"} - */ - public static final AsciiString MIN_FRESH = HttpHeaderValues.MIN_FRESH; - /** - * {@code "mode"} - */ - public static final AsciiString MODE = AsciiString.cached("mode"); - /** - * {@code "multicast"} - */ - public static final AsciiString MULTICAST = AsciiString.cached("multicast"); - /** - * {@code "must-revalidate"} - */ - public static final AsciiString MUST_REVALIDATE = HttpHeaderValues.MUST_REVALIDATE; - /** - * {@code "none"} - */ - public static final AsciiString NONE = HttpHeaderValues.NONE; - /** - * {@code "no-cache"} - */ - public static final AsciiString NO_CACHE = HttpHeaderValues.NO_CACHE; - /** - * {@code "no-transform"} - */ - public static final AsciiString NO_TRANSFORM = HttpHeaderValues.NO_TRANSFORM; - /** - * {@code "only-if-cached"} - */ - public static final AsciiString ONLY_IF_CACHED = HttpHeaderValues.ONLY_IF_CACHED; - /** - * {@code "port"} - */ - public static final AsciiString PORT = AsciiString.cached("port"); - /** - * {@code "private"} - */ - public static final AsciiString PRIVATE = HttpHeaderValues.PRIVATE; - /** - * {@code "proxy-revalidate"} - */ - public static final AsciiString PROXY_REVALIDATE = HttpHeaderValues.PROXY_REVALIDATE; - /** - * {@code "public"} - */ - public static final AsciiString PUBLIC = HttpHeaderValues.PUBLIC; - /** - * {@code "RTP"} - */ - public static final AsciiString RTP = AsciiString.cached("RTP"); - /** - * {@code "rtptime"} - */ - public static final AsciiString RTPTIME = AsciiString.cached("rtptime"); - /** - * {@code "seq"} - */ - public static final AsciiString SEQ = AsciiString.cached("seq"); - /** - * {@code "server_port"} - */ - public static final AsciiString SERVER_PORT = AsciiString.cached("server_port"); - /** - * {@code "ssrc"} - */ - public static final AsciiString SSRC = AsciiString.cached("ssrc"); - /** - * {@code "TCP"} - */ - public static final AsciiString TCP = AsciiString.cached("TCP"); - /** - * {@code "time"} - */ - public static final AsciiString TIME = AsciiString.cached("time"); - /** - * {@code "timeout"} - */ - public static final AsciiString TIMEOUT = AsciiString.cached("timeout"); - /** - * {@code "ttl"} - */ - public static final AsciiString TTL = AsciiString.cached("ttl"); - /** - * {@code "UDP"} - */ - public static final AsciiString UDP = AsciiString.cached("UDP"); - /** - * {@code "unicast"} - */ - public static final AsciiString UNICAST = AsciiString.cached("unicast"); - /** - * {@code "url"} - */ - public static final AsciiString URL = AsciiString.cached("url"); - - private RtspHeaderValues() { } -} 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 deleted file mode 100644 index 929eaa1ce6..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspMethods.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.rtsp; - -import static io.netty.util.internal.ObjectUtil.checkNonEmptyAfterTrim; - -import io.netty.handler.codec.http.HttpMethod; - -import java.util.HashMap; -import java.util.Map; - -/** - * The request getMethod of RTSP. - */ -public final class RtspMethods { - - /** - * The OPTIONS getMethod represents a request for information about the communication options - * available on the request/response chain identified by the Request-URI. This getMethod allows - * the client to determine the options and/or requirements associated with a resource, or the - * capabilities of a server, without implying a resource action or initiating a resource - * retrieval. - */ - public static final HttpMethod OPTIONS = HttpMethod.OPTIONS; - - /** - * 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 = 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 = 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 = HttpMethod.valueOf("SETUP"); - - /** - * The PLAY getMethod tells the server to start sending data via the - * mechanism specified in SETUP. - */ - 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 = 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 = 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 = 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 = HttpMethod.valueOf("SET_PARAMETER"); - - /** - * The REDIRECT request informs the client that it must connect to another - * server location. - */ - 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 = HttpMethod.valueOf("RECORD"); - - private static final Map methodMap = new HashMap<>(); - - static { - methodMap.put(DESCRIBE.toString(), DESCRIBE); - methodMap.put(ANNOUNCE.toString(), ANNOUNCE); - methodMap.put(GET_PARAMETER.toString(), GET_PARAMETER); - methodMap.put(OPTIONS.toString(), OPTIONS); - methodMap.put(PAUSE.toString(), PAUSE); - methodMap.put(PLAY.toString(), PLAY); - methodMap.put(RECORD.toString(), RECORD); - methodMap.put(REDIRECT.toString(), REDIRECT); - methodMap.put(SETUP.toString(), SETUP); - methodMap.put(SET_PARAMETER.toString(), SET_PARAMETER); - methodMap.put(TEARDOWN.toString(), TEARDOWN); - } - - /** - * Returns the {@link HttpMethod} represented by the specified name. - * If the specified name is a standard RTSP getMethod name, a cached instance - * will be returned. Otherwise, a new instance will be returned. - */ - public static HttpMethod valueOf(String name) { - name = checkNonEmptyAfterTrim(name, "name").toUpperCase(); - HttpMethod result = methodMap.get(name); - if (result != null) { - return result; - } else { - return HttpMethod.valueOf(name); - } - } - - private RtspMethods() { - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspResponseStatuses.java b/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspResponseStatuses.java deleted file mode 100644 index 8a2ad158a5..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspResponseStatuses.java +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.rtsp; - -import io.netty.handler.codec.http.HttpResponseStatus; - -/** - * The getStatus code and its description of a RTSP response. - */ -public final class RtspResponseStatuses { - - /** - * 100 Continue - */ - public static final HttpResponseStatus CONTINUE = HttpResponseStatus.CONTINUE; - - /** - * 200 OK - */ - public static final HttpResponseStatus OK = HttpResponseStatus.OK; - - /** - * 201 Created - */ - public static final HttpResponseStatus CREATED = HttpResponseStatus.CREATED; - - /** - * 250 Low on Storage Space - */ - public static final HttpResponseStatus LOW_STORAGE_SPACE = new HttpResponseStatus( - 250, "Low on Storage Space"); - - /** - * 300 Multiple Choices - */ - public static final HttpResponseStatus MULTIPLE_CHOICES = HttpResponseStatus.MULTIPLE_CHOICES; - - /** - * 301 Moved Permanently - */ - public static final HttpResponseStatus MOVED_PERMANENTLY = HttpResponseStatus.MOVED_PERMANENTLY; - - /** - * 302 Moved Temporarily - */ - public static final HttpResponseStatus MOVED_TEMPORARILY = new HttpResponseStatus( - 302, "Moved Temporarily"); - /** - * 304 Not Modified - */ - public static final HttpResponseStatus NOT_MODIFIED = HttpResponseStatus.NOT_MODIFIED; - - /** - * 305 Use Proxy - */ - public static final HttpResponseStatus USE_PROXY = HttpResponseStatus.USE_PROXY; - - /** - * 400 Bad Request - */ - public static final HttpResponseStatus BAD_REQUEST = HttpResponseStatus.BAD_REQUEST; - - /** - * 401 Unauthorized - */ - public static final HttpResponseStatus UNAUTHORIZED = HttpResponseStatus.UNAUTHORIZED; - - /** - * 402 Payment Required - */ - public static final HttpResponseStatus PAYMENT_REQUIRED = HttpResponseStatus.PAYMENT_REQUIRED; - - /** - * 403 Forbidden - */ - public static final HttpResponseStatus FORBIDDEN = HttpResponseStatus.FORBIDDEN; - - /** - * 404 Not Found - */ - public static final HttpResponseStatus NOT_FOUND = HttpResponseStatus.NOT_FOUND; - - /** - * 405 Method Not Allowed - */ - public static final HttpResponseStatus METHOD_NOT_ALLOWED = HttpResponseStatus.METHOD_NOT_ALLOWED; - - /** - * 406 Not Acceptable - */ - public static final HttpResponseStatus NOT_ACCEPTABLE = HttpResponseStatus.NOT_ACCEPTABLE; - - /** - * 407 Proxy Authentication Required - */ - public static final HttpResponseStatus PROXY_AUTHENTICATION_REQUIRED = - HttpResponseStatus.PROXY_AUTHENTICATION_REQUIRED; - - /** - * 408 Request Timeout - */ - public static final HttpResponseStatus REQUEST_TIMEOUT = HttpResponseStatus.REQUEST_TIMEOUT; - - /** - * 410 Gone - */ - public static final HttpResponseStatus GONE = HttpResponseStatus.GONE; - - /** - * 411 Length Required - */ - public static final HttpResponseStatus LENGTH_REQUIRED = HttpResponseStatus.LENGTH_REQUIRED; - - /** - * 412 Precondition Failed - */ - public static final HttpResponseStatus PRECONDITION_FAILED = HttpResponseStatus.PRECONDITION_FAILED; - - /** - * 413 Request Entity Too Large - */ - public static final HttpResponseStatus REQUEST_ENTITY_TOO_LARGE = HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE; - - /** - * 414 Request-URI Too Long - */ - public static final HttpResponseStatus REQUEST_URI_TOO_LONG = HttpResponseStatus.REQUEST_URI_TOO_LONG; - - /** - * 415 Unsupported Media Type - */ - public static final HttpResponseStatus UNSUPPORTED_MEDIA_TYPE = HttpResponseStatus.UNSUPPORTED_MEDIA_TYPE; - - /** - * 451 Parameter Not Understood - */ - public static final HttpResponseStatus PARAMETER_NOT_UNDERSTOOD = new HttpResponseStatus( - 451, "Parameter Not Understood"); - - /** - * 452 Conference Not Found - */ - public static final HttpResponseStatus CONFERENCE_NOT_FOUND = new HttpResponseStatus( - 452, "Conference Not Found"); - - /** - * 453 Not Enough Bandwidth - */ - public static final HttpResponseStatus NOT_ENOUGH_BANDWIDTH = new HttpResponseStatus( - 453, "Not Enough Bandwidth"); - - /** - * 454 Session Not Found - */ - public static final HttpResponseStatus SESSION_NOT_FOUND = new HttpResponseStatus( - 454, "Session Not Found"); - - /** - * 455 Method Not Valid in This State - */ - public static final HttpResponseStatus METHOD_NOT_VALID = new HttpResponseStatus( - 455, "Method Not Valid in This State"); - - /** - * 456 Header Field Not Valid for Resource - */ - public static final HttpResponseStatus HEADER_FIELD_NOT_VALID = new HttpResponseStatus( - 456, "Header Field Not Valid for Resource"); - - /** - * 457 Invalid Range - */ - public static final HttpResponseStatus INVALID_RANGE = new HttpResponseStatus( - 457, "Invalid Range"); - - /** - * 458 Parameter Is Read-Only - */ - public static final HttpResponseStatus PARAMETER_IS_READONLY = new HttpResponseStatus( - 458, "Parameter Is Read-Only"); - - /** - * 459 Aggregate operation not allowed - */ - public static final HttpResponseStatus AGGREGATE_OPERATION_NOT_ALLOWED = new HttpResponseStatus( - 459, "Aggregate operation not allowed"); - - /** - * 460 Only Aggregate operation allowed - */ - public static final HttpResponseStatus ONLY_AGGREGATE_OPERATION_ALLOWED = new HttpResponseStatus( - 460, "Only Aggregate operation allowed"); - - /** - * 461 Unsupported transport - */ - public static final HttpResponseStatus UNSUPPORTED_TRANSPORT = new HttpResponseStatus( - 461, "Unsupported transport"); - - /** - * 462 Destination unreachable - */ - public static final HttpResponseStatus DESTINATION_UNREACHABLE = new HttpResponseStatus( - 462, "Destination unreachable"); - - /** - * 463 Key management failure - */ - public static final HttpResponseStatus KEY_MANAGEMENT_FAILURE = new HttpResponseStatus( - 463, "Key management failure"); - - /** - * 500 Internal Server Error - */ - public static final HttpResponseStatus INTERNAL_SERVER_ERROR = HttpResponseStatus.INTERNAL_SERVER_ERROR; - - /** - * 501 Not Implemented - */ - public static final HttpResponseStatus NOT_IMPLEMENTED = HttpResponseStatus.NOT_IMPLEMENTED; - - /** - * 502 Bad Gateway - */ - public static final HttpResponseStatus BAD_GATEWAY = HttpResponseStatus.BAD_GATEWAY; - - /** - * 503 Service Unavailable - */ - public static final HttpResponseStatus SERVICE_UNAVAILABLE = HttpResponseStatus.SERVICE_UNAVAILABLE; - - /** - * 504 Gateway Timeout - */ - public static final HttpResponseStatus GATEWAY_TIMEOUT = HttpResponseStatus.GATEWAY_TIMEOUT; - - /** - * 505 RTSP Version not supported - */ - public static final HttpResponseStatus RTSP_VERSION_NOT_SUPPORTED = new HttpResponseStatus( - 505, "RTSP Version not supported"); - - /** - * 551 Option not supported - */ - public static final HttpResponseStatus OPTION_NOT_SUPPORTED = new HttpResponseStatus( - 551, "Option not supported"); - - /** - * Returns the {@link HttpResponseStatus} represented by the specified code. - * If the specified code is a standard RTSP getStatus code, a cached instance - * will be returned. Otherwise, a new instance will be returned. - */ - public static HttpResponseStatus valueOf(int code) { - switch (code) { - case 250: return LOW_STORAGE_SPACE; - case 302: return MOVED_TEMPORARILY; - case 451: return PARAMETER_NOT_UNDERSTOOD; - case 452: return CONFERENCE_NOT_FOUND; - case 453: return NOT_ENOUGH_BANDWIDTH; - case 454: return SESSION_NOT_FOUND; - case 455: return METHOD_NOT_VALID; - case 456: return HEADER_FIELD_NOT_VALID; - case 457: return INVALID_RANGE; - case 458: return PARAMETER_IS_READONLY; - case 459: return AGGREGATE_OPERATION_NOT_ALLOWED; - case 460: return ONLY_AGGREGATE_OPERATION_ALLOWED; - case 461: return UNSUPPORTED_TRANSPORT; - case 462: return DESTINATION_UNREACHABLE; - case 463: return KEY_MANAGEMENT_FAILURE; - case 505: return RTSP_VERSION_NOT_SUPPORTED; - case 551: return OPTION_NOT_SUPPORTED; - default: return HttpResponseStatus.valueOf(code); - } - } - - private RtspResponseStatuses() { - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspVersions.java b/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspVersions.java deleted file mode 100644 index d8f13c16b7..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspVersions.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.rtsp; - -import static java.util.Objects.requireNonNull; - -import io.netty.handler.codec.http.HttpVersion; - -/** - * The version of RTSP. - */ -public final class RtspVersions { - - /** - * RTSP/1.0 - */ - public static final HttpVersion RTSP_1_0 = new HttpVersion("RTSP", 1, 0, true); - - /** - * Returns an existing or new {@link HttpVersion} instance which matches to - * the specified RTSP version string. If the specified {@code text} is - * equal to {@code "RTSP/1.0"}, {@link #RTSP_1_0} will be returned. - * Otherwise, a new {@link HttpVersion} instance will be returned. - */ - public static HttpVersion valueOf(String text) { - requireNonNull(text, "text"); - - text = text.trim().toUpperCase(); - if ("RTSP/1.0".equals(text)) { - return RTSP_1_0; - } - - return new HttpVersion(text, true); - } - - private RtspVersions() { - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/rtsp/package-info.java b/codec-http/src/main/java/io/netty/handler/codec/rtsp/package-info.java deleted file mode 100644 index 5af8ae0882..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/rtsp/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * An RTSP - * extension based on the HTTP codec. - */ -package io.netty.handler.codec.rtsp; diff --git a/codec-http/src/main/resources/META-INF/native-image/io.netty/codec-http/native-image.properties b/codec-http/src/main/resources/META-INF/native-image/io.netty/codec-http/native-image.properties deleted file mode 100644 index c20ae73746..0000000000 --- a/codec-http/src/main/resources/META-INF/native-image/io.netty/codec-http/native-image.properties +++ /dev/null @@ -1,16 +0,0 @@ -# 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: -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -Args = --initialize-at-build-time=io.netty \ - --initialize-at-run-time=io.netty.handler.codec.http.HttpObjectEncoder,io.netty.handler.codec.http.websocketx.WebSocket00FrameEncoder,io.netty.handler.codec.http.websocketx.extensions.compression.DeflateDecoder,io.netty.handler.codec.compression.BrotliDecoder 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 deleted file mode 100644 index 4aa78227f6..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/CombinedHttpHeadersTest.java +++ /dev/null @@ -1,381 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.handler.codec.http.HttpHeadersTestUtils.HeaderValue; -import org.junit.jupiter.api.Test; - -import java.util.Arrays; -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.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.hasSize; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class CombinedHttpHeadersTest { - private static final CharSequence HEADER_NAME = "testHeader"; - - @Test - public void addCharSequencesCsv() { - final CombinedHttpHeaders headers = newCombinedHttpHeaders(); - headers.add(HEADER_NAME, HeaderValue.THREE.asList()); - assertCsvValues(headers, HeaderValue.THREE); - } - - @Test - public void addCharSequencesCsvWithExistingHeader() { - final CombinedHttpHeaders headers = newCombinedHttpHeaders(); - headers.add(HEADER_NAME, HeaderValue.THREE.asList()); - headers.add(HEADER_NAME, HeaderValue.FIVE.subset(4)); - assertCsvValues(headers, HeaderValue.FIVE); - } - - @Test - public void addCombinedHeadersWhenEmpty() { - final CombinedHttpHeaders headers = newCombinedHttpHeaders(); - final CombinedHttpHeaders otherHeaders = newCombinedHttpHeaders(); - otherHeaders.add(HEADER_NAME, "a"); - otherHeaders.add(HEADER_NAME, "b"); - headers.add(otherHeaders); - assertEquals("a,b", headers.get(HEADER_NAME)); - } - - @Test - public void addCombinedHeadersWhenNotEmpty() { - final CombinedHttpHeaders headers = newCombinedHttpHeaders(); - headers.add(HEADER_NAME, "a"); - final CombinedHttpHeaders otherHeaders = newCombinedHttpHeaders(); - otherHeaders.add(HEADER_NAME, "b"); - otherHeaders.add(HEADER_NAME, "c"); - headers.add(otherHeaders); - assertEquals("a,b,c", headers.get(HEADER_NAME)); - } - - @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(); - headers.add(HEADER_NAME, "a"); - final CombinedHttpHeaders otherHeaders = newCombinedHttpHeaders(); - otherHeaders.add(HEADER_NAME, "b"); - otherHeaders.add(HEADER_NAME, "c"); - headers.set(otherHeaders); - assertEquals("b,c", headers.get(HEADER_NAME)); - } - - @Test - public void addUncombinedHeaders() { - final CombinedHttpHeaders headers = newCombinedHttpHeaders(); - headers.add(HEADER_NAME, "a"); - final DefaultHttpHeaders otherHeaders = new DefaultHttpHeaders(); - otherHeaders.add(HEADER_NAME, "b"); - otherHeaders.add(HEADER_NAME, "c"); - headers.add(otherHeaders); - assertEquals("a,b,c", headers.get(HEADER_NAME)); - } - - @Test - public void setUncombinedHeaders() { - final CombinedHttpHeaders headers = newCombinedHttpHeaders(); - headers.add(HEADER_NAME, "a"); - final DefaultHttpHeaders otherHeaders = new DefaultHttpHeaders(); - otherHeaders.add(HEADER_NAME, "b"); - otherHeaders.add(HEADER_NAME, "c"); - headers.set(otherHeaders); - assertEquals("b,c", headers.get(HEADER_NAME)); - } - - @Test - public void addCharSequencesCsvWithValueContainingComma() { - final CombinedHttpHeaders headers = newCombinedHttpHeaders(); - headers.add(HEADER_NAME, HeaderValue.SIX_QUOTED.subset(4)); - assertTrue(contentEquals(HeaderValue.SIX_QUOTED.subsetAsCsvString(4), headers.get(HEADER_NAME))); - assertEquals(HeaderValue.SIX_QUOTED.subset(4), headers.getAll(HEADER_NAME)); - } - - @Test - public void addCharSequencesCsvWithValueContainingCommas() { - final CombinedHttpHeaders headers = newCombinedHttpHeaders(); - headers.add(HEADER_NAME, HeaderValue.EIGHT.subset(6)); - assertTrue(contentEquals(HeaderValue.EIGHT.subsetAsCsvString(6), headers.get(HEADER_NAME))); - assertEquals(HeaderValue.EIGHT.subset(6), headers.getAll(HEADER_NAME)); - } - - @Test - public void addCharSequencesCsvNullValue() { - final CombinedHttpHeaders headers = newCombinedHttpHeaders(); - final String value = null; - assertThrows(NullPointerException.class, () -> headers.add(HEADER_NAME, value)); - } - - @Test - public void addCharSequencesCsvMultipleTimes() { - final CombinedHttpHeaders headers = newCombinedHttpHeaders(); - for (int i = 0; i < 5; ++i) { - headers.add(HEADER_NAME, "value"); - } - assertTrue(contentEquals("value,value,value,value,value", headers.get(HEADER_NAME))); - } - - @Test - public void addCharSequenceCsv() { - final CombinedHttpHeaders headers = newCombinedHttpHeaders(); - addValues(headers, HeaderValue.ONE, HeaderValue.TWO, HeaderValue.THREE); - assertCsvValues(headers, HeaderValue.THREE); - } - - @Test - public void addCharSequenceCsvSingleValue() { - final CombinedHttpHeaders headers = newCombinedHttpHeaders(); - addValues(headers, HeaderValue.ONE); - assertCsvValue(headers, HeaderValue.ONE); - } - - @Test - public void addIterableCsv() { - final CombinedHttpHeaders headers = newCombinedHttpHeaders(); - headers.add(HEADER_NAME, HeaderValue.THREE.asList()); - assertCsvValues(headers, HeaderValue.THREE); - } - - @Test - public void addIterableCsvWithExistingHeader() { - final CombinedHttpHeaders headers = newCombinedHttpHeaders(); - headers.add(HEADER_NAME, HeaderValue.THREE.asList()); - headers.add(HEADER_NAME, HeaderValue.FIVE.subset(4)); - assertCsvValues(headers, HeaderValue.FIVE); - } - - @Test - public void addIterableCsvSingleValue() { - final CombinedHttpHeaders headers = newCombinedHttpHeaders(); - headers.add(HEADER_NAME, HeaderValue.ONE.asList()); - assertCsvValue(headers, HeaderValue.ONE); - } - - @Test - public void addIterableCsvEmpty() { - final CombinedHttpHeaders headers = newCombinedHttpHeaders(); - headers.add(HEADER_NAME, Collections.emptyList()); - assertEquals(Collections.singletonList(""), headers.getAll(HEADER_NAME)); - } - - @Test - public void addObjectCsv() { - final CombinedHttpHeaders headers = newCombinedHttpHeaders(); - addObjectValues(headers, HeaderValue.ONE, HeaderValue.TWO, HeaderValue.THREE); - assertCsvValues(headers, HeaderValue.THREE); - } - - @Test - public void addObjectsCsv() { - final CombinedHttpHeaders headers = newCombinedHttpHeaders(); - headers.add(HEADER_NAME, HeaderValue.THREE.asList()); - assertCsvValues(headers, HeaderValue.THREE); - } - - @Test - public void addObjectsIterableCsv() { - final CombinedHttpHeaders headers = newCombinedHttpHeaders(); - headers.add(HEADER_NAME, HeaderValue.THREE.asList()); - assertCsvValues(headers, HeaderValue.THREE); - } - - @Test - public void addObjectsCsvWithExistingHeader() { - final CombinedHttpHeaders headers = newCombinedHttpHeaders(); - headers.add(HEADER_NAME, HeaderValue.THREE.asList()); - headers.add(HEADER_NAME, HeaderValue.FIVE.subset(4)); - assertCsvValues(headers, HeaderValue.FIVE); - } - - @Test - public void setCharSequenceCsv() { - final CombinedHttpHeaders headers = newCombinedHttpHeaders(); - headers.set(HEADER_NAME, HeaderValue.THREE.asList()); - assertCsvValues(headers, HeaderValue.THREE); - } - - @Test - public void setIterableCsv() { - final CombinedHttpHeaders headers = newCombinedHttpHeaders(); - headers.set(HEADER_NAME, HeaderValue.THREE.asList()); - assertCsvValues(headers, HeaderValue.THREE); - } - - @Test - public void setObjectObjectsCsv() { - final CombinedHttpHeaders headers = newCombinedHttpHeaders(); - headers.set(HEADER_NAME, HeaderValue.THREE.asList()); - assertCsvValues(headers, HeaderValue.THREE); - } - - @Test - public void setObjectIterableCsv() { - final CombinedHttpHeaders headers = newCombinedHttpHeaders(); - headers.set(HEADER_NAME, HeaderValue.THREE.asList()); - assertCsvValues(headers, HeaderValue.THREE); - } - - private static CombinedHttpHeaders newCombinedHttpHeaders() { - return new CombinedHttpHeaders(true); - } - - private static void assertCsvValues(final CombinedHttpHeaders headers, final HeaderValue headerValue) { - assertTrue(contentEquals(headerValue.asCsv(), headers.get(HEADER_NAME))); - assertEquals(headerValue.asList(), headers.getAll(HEADER_NAME)); - } - - private static void assertCsvValue(final CombinedHttpHeaders headers, final HeaderValue headerValue) { - assertTrue(contentEquals(headerValue.toString(), headers.get(HEADER_NAME))); - assertTrue(contentEquals(headerValue.toString(), headers.getAll(HEADER_NAME).get(0))); - } - - private static void addValues(final CombinedHttpHeaders headers, HeaderValue... headerValues) { - for (HeaderValue v: headerValues) { - headers.add(HEADER_NAME, v.toString()); - } - } - - private static void addObjectValues(final CombinedHttpHeaders headers, HeaderValue... headerValues) { - for (HeaderValue v: headerValues) { - headers.add(HEADER_NAME, v.toString()); - } - } - - @Test - public void testGetAll() { - final CombinedHttpHeaders headers = newCombinedHttpHeaders(); - headers.set(HEADER_NAME, Arrays.asList("a", "b", "c")); - assertEquals(Arrays.asList("a", "b", "c"), headers.getAll(HEADER_NAME)); - headers.set(HEADER_NAME, Arrays.asList("a,", "b,", "c,")); - assertEquals(Arrays.asList("a,", "b,", "c,"), headers.getAll(HEADER_NAME)); - headers.set(HEADER_NAME, Arrays.asList("a\"", "b\"", "c\"")); - assertEquals(Arrays.asList("a\"", "b\"", "c\""), headers.getAll(HEADER_NAME)); - headers.set(HEADER_NAME, Arrays.asList("\"a\"", "\"b\"", "\"c\"")); - assertEquals(Arrays.asList("a", "b", "c"), headers.getAll(HEADER_NAME)); - headers.set(HEADER_NAME, "a,b,c"); - assertEquals(Collections.singletonList("a,b,c"), headers.getAll(HEADER_NAME)); - headers.set(HEADER_NAME, "\"a,b,c\""); - assertEquals(Collections.singletonList("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(); - headers.set(HEADER_NAME, Arrays.asList("\ta", " ", " b ", "\t \t")); - headers.add(HEADER_NAME, " c, d \t"); - - assertEquals(Arrays.asList("a", "", "b", "", "c, d"), headers.getAll(HEADER_NAME)); - assertEquals("a,,b,,\"c, d\"", headers.get(HEADER_NAME)); - - assertTrue(headers.containsValue(HEADER_NAME, "a", true)); - assertTrue(headers.containsValue(HEADER_NAME, " a ", true)); - assertTrue(headers.containsValue(HEADER_NAME, "a", true)); - assertFalse(headers.containsValue(HEADER_NAME, "a,b", true)); - - assertFalse(headers.containsValue(HEADER_NAME, " c, d ", true)); - assertFalse(headers.containsValue(HEADER_NAME, "c, d", true)); - assertTrue(headers.containsValue(HEADER_NAME, " c ", true)); - assertTrue(headers.containsValue(HEADER_NAME, "d", true)); - - assertTrue(headers.containsValue(HEADER_NAME, "\t", true)); - assertTrue(headers.containsValue(HEADER_NAME, "", true)); - - assertFalse(headers.containsValue(HEADER_NAME, "e", true)); - - HttpHeaders copiedHeaders = newCombinedHttpHeaders().add(headers); - assertEquals(Arrays.asList("a", "", "b", "", "c, d"), copiedHeaders.getAll(HEADER_NAME)); - } - - @Test - public void valueIterator() { - final CombinedHttpHeaders headers = newCombinedHttpHeaders(); - headers.set(HEADER_NAME, Arrays.asList("\ta", " ", " b ", "\t \t")); - headers.add(HEADER_NAME, " c, d \t"); - - assertFalse(headers.valueStringIterator("foo").hasNext()); - assertValueIterator(headers.valueStringIterator(HEADER_NAME)); - assertFalse(headers.valueCharSequenceIterator("foo").hasNext()); - 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()); - assertTrue(strItr.hasNext()); - assertEquals("", strItr.next()); - assertTrue(strItr.hasNext()); - assertEquals("b", strItr.next()); - assertTrue(strItr.hasNext()); - assertEquals("", strItr.next()); - assertTrue(strItr.hasNext()); - assertEquals("c, d", strItr.next()); - assertFalse(strItr.hasNext()); - } -} 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 deleted file mode 100644 index 37a8bb747c..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/DefaultHttpHeadersTest.java +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.handler.codec.http.HttpHeadersTestUtils.HeaderValue; -import io.netty.util.AsciiString; -import io.netty.util.internal.StringUtil; -import org.junit.jupiter.api.Test; - -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; -import java.util.Set; - -import static io.netty.handler.codec.http.HttpHeadersTestUtils.of; -import static io.netty.util.AsciiString.contentEquals; -import static java.util.Arrays.asList; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class DefaultHttpHeadersTest { - private static final CharSequence HEADER_NAME = "testHeader"; - - @Test - public void nullHeaderNameNotAllowed() { - assertThrows(IllegalArgumentException.class, () -> new DefaultHttpHeaders().add(null, "foo")); - } - - @Test - public void emptyHeaderNameNotAllowed() { - assertThrows(IllegalArgumentException.class, - () -> new DefaultHttpHeaders().add(StringUtil.EMPTY_STRING, "foo")); - } - - @Test - public void keysShouldBeCaseInsensitive() { - DefaultHttpHeaders headers = new DefaultHttpHeaders(); - headers.add(of("Name"), of("value1")); - headers.add(of("name"), of("value2")); - headers.add(of("NAME"), of("value3")); - assertEquals(3, headers.size()); - - List values = asList("value1", "value2", "value3"); - - assertEquals(values, headers.getAll(of("NAME"))); - assertEquals(values, headers.getAll(of("name"))); - assertEquals(values, headers.getAll(of("Name"))); - assertEquals(values, headers.getAll(of("nAmE"))); - } - - @Test - public void keysShouldBeCaseInsensitiveInHeadersEquals() { - DefaultHttpHeaders headers1 = new DefaultHttpHeaders(); - headers1.add(of("name1"), Arrays.asList("value1", "value2", "value3")); - headers1.add(of("nAmE2"), of("value4")); - - DefaultHttpHeaders headers2 = new DefaultHttpHeaders(); - headers2.add(of("naMe1"), Arrays.asList("value1", "value2", "value3")); - headers2.add(of("NAME2"), of("value4")); - - assertEquals(headers1, headers1); - assertEquals(headers2, headers2); - assertEquals(headers1, headers2); - assertEquals(headers2, headers1); - assertEquals(headers1.hashCode(), headers2.hashCode()); - } - - @Test - public void testStringKeyRetrievedAsAsciiString() { - final HttpHeaders headers = new DefaultHttpHeaders(false); - - // Test adding String key and retrieving it using a AsciiString key - final String connection = "keep-alive"; - headers.add(of("Connection"), connection); - - // Passes - final String value = headers.getAsString(HttpHeaderNames.CONNECTION.toString()); - assertNotNull(value); - assertEquals(connection, value); - - // Passes - final String value2 = headers.getAsString(HttpHeaderNames.CONNECTION); - assertNotNull(value2); - assertEquals(connection, value2); - } - - @Test - public void testAsciiStringKeyRetrievedAsString() { - final HttpHeaders headers = new DefaultHttpHeaders(false); - - // Test adding AsciiString key and retrieving it using a String key - final String cacheControl = "no-cache"; - headers.add(HttpHeaderNames.CACHE_CONTROL, cacheControl); - - final String value = headers.getAsString(HttpHeaderNames.CACHE_CONTROL); - assertNotNull(value); - assertEquals(cacheControl, value); - - final String value2 = headers.getAsString(HttpHeaderNames.CACHE_CONTROL.toString()); - assertNotNull(value2); - assertEquals(cacheControl, value2); - } - - @Test - public void testRemoveTransferEncodingIgnoreCase() { - HttpMessage message = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - message.headers().set(HttpHeaderNames.TRANSFER_ENCODING, "Chunked"); - assertFalse(message.headers().isEmpty()); - HttpUtil.setTransferEncodingChunked(message, false); - assertTrue(message.headers().isEmpty()); - } - - // Test for https://github.com/netty/netty/issues/1690 - @Test - public void testGetOperations() { - HttpHeaders headers = new DefaultHttpHeaders(); - headers.add(of("Foo"), of("1")); - headers.add(of("Foo"), of("2")); - - assertEquals("1", headers.get(of("Foo"))); - - List values = headers.getAll(of("Foo")); - assertEquals(2, values.size()); - assertEquals("1", values.get(0)); - assertEquals("2", values.get(1)); - } - - @Test - public void testEqualsIgnoreCase() { - assertThat(AsciiString.contentEqualsIgnoreCase(null, null), is(true)); - assertThat(AsciiString.contentEqualsIgnoreCase(null, "foo"), is(false)); - assertThat(AsciiString.contentEqualsIgnoreCase("bar", null), is(false)); - assertThat(AsciiString.contentEqualsIgnoreCase("FoO", "fOo"), is(true)); - } - - @Test - public void testSetNullHeaderValueValidate() { - HttpHeaders headers = new DefaultHttpHeaders(true); - assertThrows(NullPointerException.class, () -> headers.set(of("test"), (CharSequence) null)); - } - - @Test - public void testSetNullHeaderValueNotValidate() { - HttpHeaders headers = new DefaultHttpHeaders(false); - assertThrows(NullPointerException.class, () -> headers.set(of("test"), (CharSequence) null)); - } - - @Test - public void addCharSequences() { - final DefaultHttpHeaders headers = newDefaultDefaultHttpHeaders(); - headers.add(HEADER_NAME, HeaderValue.THREE.asList()); - assertDefaultValues(headers, HeaderValue.THREE); - } - - @Test - public void addIterable() { - final DefaultHttpHeaders headers = newDefaultDefaultHttpHeaders(); - headers.add(HEADER_NAME, HeaderValue.THREE.asList()); - assertDefaultValues(headers, HeaderValue.THREE); - } - - @Test - public void addObjects() { - final DefaultHttpHeaders headers = newDefaultDefaultHttpHeaders(); - headers.add(HEADER_NAME, HeaderValue.THREE.asList()); - assertDefaultValues(headers, HeaderValue.THREE); - } - - @Test - public void setCharSequences() { - final DefaultHttpHeaders headers = newDefaultDefaultHttpHeaders(); - headers.set(HEADER_NAME, HeaderValue.THREE.asList()); - assertDefaultValues(headers, HeaderValue.THREE); - } - - @Test - public void setIterable() { - final DefaultHttpHeaders headers = newDefaultDefaultHttpHeaders(); - headers.set(HEADER_NAME, HeaderValue.THREE.asList()); - assertDefaultValues(headers, HeaderValue.THREE); - } - - @Test - public void setObjectObjects() { - final DefaultHttpHeaders headers = newDefaultDefaultHttpHeaders(); - headers.set(HEADER_NAME, HeaderValue.THREE.asList()); - assertDefaultValues(headers, HeaderValue.THREE); - } - - @Test - public void setObjectIterable() { - final DefaultHttpHeaders headers = newDefaultDefaultHttpHeaders(); - headers.set(HEADER_NAME, HeaderValue.THREE.asList()); - assertDefaultValues(headers, HeaderValue.THREE); - } - - @Test - public void toStringOnEmptyHeaders() { - assertEquals("DefaultHttpHeaders[]", newDefaultDefaultHttpHeaders().toString()); - } - - @Test - public void toStringOnSingleHeader() { - assertEquals("DefaultHttpHeaders[foo: bar]", newDefaultDefaultHttpHeaders() - .add("foo", "bar") - .toString()); - } - - @Test - public void toStringOnMultipleHeaders() { - assertEquals("DefaultHttpHeaders[foo: bar, baz: qix]", newDefaultDefaultHttpHeaders() - .add("foo", "bar") - .add("baz", "qix") - .toString()); - } - - @Test - public void providesHeaderNamesAsArray() throws Exception { - Set nettyHeaders = new DefaultHttpHeaders() - .add(HttpHeaderNames.CONTENT_LENGTH, 10) - .names(); - - String[] namesArray = nettyHeaders.toArray(new String[0]); - assertArrayEquals(namesArray, new String[] { HttpHeaderNames.CONTENT_LENGTH.toString() }); - } - - private static void assertDefaultValues(final DefaultHttpHeaders headers, final HeaderValue headerValue) { - assertTrue(contentEquals(headerValue.asList().get(0), headers.get(HEADER_NAME))); - List expected = headerValue.asList(); - List actual = headers.getAll(HEADER_NAME); - assertEquals(expected.size(), actual.size()); - Iterator eItr = expected.iterator(); - Iterator aItr = actual.iterator(); - while (eItr.hasNext()) { - assertTrue(contentEquals(eItr.next(), aItr.next())); - } - } - - private static DefaultHttpHeaders newDefaultDefaultHttpHeaders() { - return new DefaultHttpHeaders(true); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/DefaultHttpRequestTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/DefaultHttpRequestTest.java deleted file mode 100644 index 9ddb597ae9..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/DefaultHttpRequestTest.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.util.AsciiString; -import org.junit.jupiter.api.Test; - -import static io.netty.handler.codec.http.HttpHeadersTestUtils.of; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class DefaultHttpRequestTest { - - @Test - public void testHeaderRemoval() { - HttpMessage m = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/"); - HttpHeaders h = m.headers(); - - // Insert sample keys. - for (int i = 0; i < 1000; i ++) { - h.set(of(String.valueOf(i)), AsciiString.EMPTY_STRING); - } - - // Remove in reversed order. - for (int i = 999; i >= 0; i --) { - h.remove(of(String.valueOf(i))); - } - - // Check if random access returns nothing. - for (int i = 0; i < 1000; i ++) { - assertNull(h.get(of(String.valueOf(i)))); - } - - // Check if sequential access returns nothing. - assertTrue(h.isEmpty()); - } -} 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 deleted file mode 100644 index a713059864..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/DefaultHttpResponseTest.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.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()); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpChunkedInputTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpChunkedInputTest.java deleted file mode 100644 index c0904c15a4..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpChunkedInputTest.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.stream.ChunkedFile; -import io.netty.handler.stream.ChunkedInput; -import io.netty.handler.stream.ChunkedNioFile; -import io.netty.handler.stream.ChunkedNioStream; -import io.netty.handler.stream.ChunkedStream; -import io.netty.handler.stream.ChunkedWriteHandler; -import io.netty.util.internal.PlatformDependent; -import org.junit.jupiter.api.Test; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.nio.channels.Channels; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class HttpChunkedInputTest { - private static final byte[] BYTES = new byte[1024 * 64]; - private static final File TMP; - - static { - for (int i = 0; i < BYTES.length; i++) { - BYTES[i] = (byte) i; - } - - FileOutputStream out = null; - try { - TMP = PlatformDependent.createTempFile("netty-chunk-", ".tmp", null); - TMP.deleteOnExit(); - out = new FileOutputStream(TMP); - out.write(BYTES); - out.flush(); - } catch (IOException e) { - throw new RuntimeException(e); - } finally { - if (out != null) { - try { - out.close(); - } catch (IOException e) { - // ignore - } - } - } - } - - @Test - public void testChunkedStream() { - check(new HttpChunkedInput(new ChunkedStream(new ByteArrayInputStream(BYTES)))); - } - - @Test - public void testChunkedNioStream() { - check(new HttpChunkedInput(new ChunkedNioStream(Channels.newChannel(new ByteArrayInputStream(BYTES))))); - } - - @Test - public void testChunkedFile() throws IOException { - check(new HttpChunkedInput(new ChunkedFile(TMP))); - } - - @Test - public void testChunkedNioFile() throws IOException { - check(new HttpChunkedInput(new ChunkedNioFile(TMP))); - } - - @Test - public void testWrappedReturnNull() throws Exception { - HttpChunkedInput input = new HttpChunkedInput(new ChunkedInput() { - @Override - public boolean isEndOfInput() throws Exception { - return false; - } - - @Override - public void close() throws Exception { - // NOOP - } - - @Override - public ByteBuf readChunk(ChannelHandlerContext ctx) throws Exception { - return null; - } - - @Override - public ByteBuf readChunk(ByteBufAllocator allocator) throws Exception { - return null; - } - - @Override - public long length() { - return 0; - } - - @Override - public long progress() { - return 0; - } - }); - assertNull(input.readChunk(ByteBufAllocator.DEFAULT)); - } - - private static void check(ChunkedInput... inputs) { - EmbeddedChannel ch = new EmbeddedChannel(new ChunkedWriteHandler()); - - for (ChunkedInput input : inputs) { - ch.writeOutbound(input); - } - - assertTrue(ch.finish()); - - int i = 0; - int read = 0; - HttpContent lastHttpContent = null; - for (;;) { - HttpContent httpContent = ch.readOutbound(); - if (httpContent == null) { - break; - } - if (lastHttpContent != null) { - assertTrue(lastHttpContent instanceof DefaultHttpContent, "Chunk must be DefaultHttpContent"); - } - - ByteBuf buffer = httpContent.content(); - while (buffer.isReadable()) { - assertEquals(BYTES[i++], buffer.readByte()); - read++; - if (i == BYTES.length) { - i = 0; - } - } - buffer.release(); - - // Save last chunk - lastHttpContent = httpContent; - } - - assertEquals(BYTES.length * inputs.length, read); - assertSame(LastHttpContent.EMPTY_LAST_CONTENT, lastHttpContent, - "Last chunk must be LastHttpContent.EMPTY_LAST_CONTENT"); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpClientCodecTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpClientCodecTest.java deleted file mode 100644 index 972f2c5a17..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpClientCodecTest.java +++ /dev/null @@ -1,423 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -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.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelOption; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.codec.CodecException; -import io.netty.handler.codec.PrematureChannelClosureException; -import io.netty.util.CharsetUtil; -import io.netty.util.NetUtil; -import io.netty.util.concurrent.Future; -import org.hamcrest.CoreMatchers; -import org.junit.jupiter.api.Test; - -import java.net.InetSocketAddress; -import java.util.concurrent.CountDownLatch; - -import static io.netty.util.ReferenceCountUtil.release; -import static java.util.concurrent.TimeUnit.SECONDS; -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.nullValue; -import static org.hamcrest.CoreMatchers.sameInstance; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.not; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - -public class HttpClientCodecTest { - - private static final String EMPTY_RESPONSE = "HTTP/1.0 200 OK\r\nContent-Length: 0\r\n\r\n"; - private static final String RESPONSE = "HTTP/1.0 200 OK\r\n" + "Date: Fri, 31 Dec 1999 23:59:59 GMT\r\n" + - "Content-Type: text/html\r\n" + "Content-Length: 28\r\n" + "\r\n" - + "\r\n"; - private static final String INCOMPLETE_CHUNKED_RESPONSE = "HTTP/1.1 200 OK\r\n" + "Content-Type: text/plain\r\n" + - "Transfer-Encoding: chunked\r\n" + "\r\n" + - "5\r\n" + "first\r\n" + "6\r\n" + "second\r\n" + "0\r\n"; - private static final String CHUNKED_RESPONSE = INCOMPLETE_CHUNKED_RESPONSE + "\r\n"; - - @Test - public void testConnectWithResponseContent() { - HttpClientCodec codec = new HttpClientCodec(4096, 8192, true); - EmbeddedChannel ch = new EmbeddedChannel(codec); - - sendRequestAndReadResponse(ch, HttpMethod.CONNECT, RESPONSE); - ch.finish(); - } - - @Test - public void testFailsNotOnRequestResponseChunked() { - HttpClientCodec codec = new HttpClientCodec(4096, 8192, true); - EmbeddedChannel ch = new EmbeddedChannel(codec); - - sendRequestAndReadResponse(ch, HttpMethod.GET, CHUNKED_RESPONSE); - ch.finish(); - } - - @Test - public void testFailsOnMissingResponse() { - HttpClientCodec codec = new HttpClientCodec(4096, 8192, true); - EmbeddedChannel ch = new EmbeddedChannel(codec); - - assertTrue(ch.writeOutbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, - "http://localhost/"))); - ByteBuf buffer = ch.readOutbound(); - assertNotNull(buffer); - buffer.release(); - try { - ch.finish(); - fail(); - } catch (CodecException e) { - assertTrue(e instanceof PrematureChannelClosureException); - } - } - - @Test - public void testFailsOnIncompleteChunkedResponse() { - HttpClientCodec codec = new HttpClientCodec(4096, 8192, true); - EmbeddedChannel ch = new EmbeddedChannel(codec); - - ch.writeOutbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost/")); - ByteBuf buffer = ch.readOutbound(); - assertNotNull(buffer); - buffer.release(); - assertNull(ch.readInbound()); - ch.writeInbound(Unpooled.copiedBuffer(INCOMPLETE_CHUNKED_RESPONSE, CharsetUtil.ISO_8859_1)); - assertThat(ch.readInbound(), instanceOf(HttpResponse.class)); - ((HttpContent) ch.readInbound()).release(); // Chunk 'first' - ((HttpContent) ch.readInbound()).release(); // Chunk 'second' - assertNull(ch.readInbound()); - - try { - ch.finish(); - fail(); - } catch (CodecException e) { - assertTrue(e instanceof PrematureChannelClosureException); - } - } - - @Test - public void testServerCloseSocketInputProvidesData() throws Exception { - ServerBootstrap sb = new ServerBootstrap(); - Bootstrap cb = new Bootstrap(); - final CountDownLatch serverChannelLatch = new CountDownLatch(1); - final CountDownLatch responseReceivedLatch = new CountDownLatch(1); - try { - sb.group(new MultithreadEventLoopGroup(2, NioHandler.newFactory())); - sb.channel(NioServerSocketChannel.class); - sb.childHandler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - // Don't use the HttpServerCodec, because we don't want to have content-length or anything added. - ch.pipeline().addLast(new HttpRequestDecoder(4096, 8192, true)); - ch.pipeline().addLast(new HttpObjectAggregator(4096)); - ch.pipeline().addLast(new SimpleChannelInboundHandler() { - @Override - protected void messageReceived(ChannelHandlerContext ctx, FullHttpRequest msg) { - // This is just a simple demo...don't block in IO - assertTrue(ctx.channel() instanceof SocketChannel); - final SocketChannel sChannel = (SocketChannel) ctx.channel(); - /* - The point of this test is to not add any content-length or content-encoding headers - and the client should still handle this. - See RFC 7230, 3.3.3: https://tools.ietf.org/html/rfc7230#section-3.3.3. - */ - sChannel.writeAndFlush(Unpooled.wrappedBuffer(("HTTP/1.0 200 OK\r\n" + - "Date: Fri, 31 Dec 1999 23:59:59 GMT\r\n" + - "Content-Type: text/html\r\n\r\n").getBytes(CharsetUtil.ISO_8859_1))) - .addListener(future -> { - assertTrue(future.isSuccess()); - sChannel.writeAndFlush(Unpooled.wrappedBuffer( - "hello half closed!\r\n" - .getBytes(CharsetUtil.ISO_8859_1))) - .addListener(future1 -> { - assertTrue(future1.isSuccess()); - sChannel.shutdownOutput(); - }); - }); - } - }); - serverChannelLatch.countDown(); - } - }); - - cb.group(new MultithreadEventLoopGroup(1, NioHandler.newFactory())); - cb.channel(NioSocketChannel.class); - cb.option(ChannelOption.ALLOW_HALF_CLOSURE, true); - cb.handler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - ch.pipeline().addLast(new HttpClientCodec(4096, 8192, true, true)); - ch.pipeline().addLast(new HttpObjectAggregator(4096)); - ch.pipeline().addLast(new SimpleChannelInboundHandler() { - @Override - protected void messageReceived(ChannelHandlerContext ctx, FullHttpResponse msg) { - responseReceivedLatch.countDown(); - } - }); - } - }); - - Channel serverChannel = sb.bind(new InetSocketAddress(0)).get(); - int port = ((InetSocketAddress) serverChannel.localAddress()).getPort(); - - Future ccf = cb.connect(new InetSocketAddress(NetUtil.LOCALHOST, port)); - assertTrue(ccf.awaitUninterruptibly().isSuccess()); - Channel clientChannel = ccf.get(); - assertTrue(serverChannelLatch.await(5, SECONDS)); - clientChannel.writeAndFlush(new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/")); - assertTrue(responseReceivedLatch.await(5, SECONDS)); - } finally { - sb.config().group().shutdownGracefully(); - sb.config().childGroup().shutdownGracefully(); - cb.config().group().shutdownGracefully(); - } - } - - @Test - public void testContinueParsingAfterConnect() throws Exception { - testAfterConnect(true); - } - - @Test - public void testPassThroughAfterConnect() throws Exception { - testAfterConnect(false); - } - - private static void testAfterConnect(final boolean parseAfterConnect) throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new HttpClientCodec(4096, 8192, true, true, parseAfterConnect)); - - Consumer connectResponseConsumer = new Consumer(); - sendRequestAndReadResponse(ch, HttpMethod.CONNECT, EMPTY_RESPONSE, connectResponseConsumer); - assertTrue(connectResponseConsumer.getReceivedCount() > 0, "No connect response messages received."); - Consumer responseConsumer = new Consumer() { - @Override - void accept(Object object) { - if (parseAfterConnect) { - assertThat("Unexpected response message type.", object, instanceOf(HttpObject.class)); - } else { - assertThat("Unexpected response message type.", object, not(instanceOf(HttpObject.class))); - } - } - }; - sendRequestAndReadResponse(ch, HttpMethod.GET, RESPONSE, responseConsumer); - assertTrue(responseConsumer.getReceivedCount() > 0, "No response messages received."); - assertFalse(ch.finish(), "Channel finish failed."); - } - - private static void sendRequestAndReadResponse(EmbeddedChannel ch, HttpMethod httpMethod, String response) { - sendRequestAndReadResponse(ch, httpMethod, response, new Consumer()); - } - - private static void sendRequestAndReadResponse(EmbeddedChannel ch, HttpMethod httpMethod, String response, - Consumer responseConsumer) { - assertTrue(ch.writeOutbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, httpMethod, "http://localhost/")), - "Channel outbound write failed."); - assertTrue(ch.writeInbound(Unpooled.copiedBuffer(response, CharsetUtil.ISO_8859_1)), - "Channel inbound write failed."); - - for (;;) { - Object msg = ch.readOutbound(); - if (msg == null) { - break; - } - release(msg); - } - for (;;) { - Object msg = ch.readInbound(); - if (msg == null) { - break; - } - responseConsumer.onResponse(msg); - release(msg); - } - } - - private static class Consumer { - - private int receivedCount; - - final void onResponse(Object object) { - receivedCount++; - accept(object); - } - - void accept(Object object) { - // Default noop. - } - - int getReceivedCount() { - return receivedCount; - } - } - - @Test - public void testDecodesFinalResponseAfterSwitchingProtocols() { - String SWITCHING_PROTOCOLS_RESPONSE = "HTTP/1.1 101 Switching Protocols\r\n" + - "Connection: Upgrade\r\n" + - "Upgrade: TLS/1.2, HTTP/1.1\r\n\r\n"; - - HttpClientCodec codec = new HttpClientCodec(4096, 8192, true); - EmbeddedChannel ch = new EmbeddedChannel(codec, new HttpObjectAggregator(1024)); - - HttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost/"); - request.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE); - request.headers().set(HttpHeaderNames.UPGRADE, "TLS/1.2"); - assertTrue(ch.writeOutbound(request), "Channel outbound write failed."); - - assertTrue(ch.writeInbound(Unpooled.copiedBuffer(SWITCHING_PROTOCOLS_RESPONSE, CharsetUtil.ISO_8859_1)), - "Channel inbound write failed."); - Object switchingProtocolsResponse = ch.readInbound(); - assertNotNull(switchingProtocolsResponse, "No response received"); - assertThat("Response was not decoded", switchingProtocolsResponse, instanceOf(FullHttpResponse.class)); - ((FullHttpResponse) switchingProtocolsResponse).release(); - - assertTrue(ch.writeInbound(Unpooled.copiedBuffer(RESPONSE, CharsetUtil.ISO_8859_1)), - "Channel inbound write failed"); - Object finalResponse = ch.readInbound(); - assertNotNull(finalResponse, "No response received"); - assertThat("Response was not decoded", finalResponse, instanceOf(FullHttpResponse.class)); - ((FullHttpResponse) finalResponse).release(); - assertTrue(ch.finishAndReleaseAll(), "Channel finish failed"); - } - - @Test - public void testWebSocket00Response() { - byte[] data = ("HTTP/1.1 101 WebSocket Protocol Handshake\r\n" + - "Upgrade: WebSocket\r\n" + - "Connection: Upgrade\r\n" + - "Sec-WebSocket-Origin: http://localhost:8080\r\n" + - "Sec-WebSocket-Location: ws://localhost/some/path\r\n" + - "\r\n" + - "1234567812345678").getBytes(); - EmbeddedChannel ch = new EmbeddedChannel(new HttpClientCodec()); - assertTrue(ch.writeInbound(Unpooled.wrappedBuffer(data))); - - HttpResponse res = ch.readInbound(); - assertThat(res.protocolVersion(), sameInstance(HttpVersion.HTTP_1_1)); - assertThat(res.status(), is(HttpResponseStatus.SWITCHING_PROTOCOLS)); - HttpContent content = ch.readInbound(); - assertThat(content.content().readableBytes(), is(16)); - content.release(); - - assertThat(ch.finish(), is(false)); - - assertThat(ch.readInbound(), is(nullValue())); - } - - @Test - public void testWebDavResponse() { - byte[] data = ("HTTP/1.1 102 Processing\r\n" + - "Status-URI: Status-URI:http://status.com; 404\r\n" + - "\r\n" + - "1234567812345678").getBytes(); - EmbeddedChannel ch = new EmbeddedChannel(new HttpClientCodec()); - assertTrue(ch.writeInbound(Unpooled.wrappedBuffer(data))); - - HttpResponse res = ch.readInbound(); - assertThat(res.protocolVersion(), sameInstance(HttpVersion.HTTP_1_1)); - assertThat(res.status(), is(HttpResponseStatus.PROCESSING)); - HttpContent content = ch.readInbound(); - // HTTP 102 is not allowed to have content. - assertThat(content.content().readableBytes(), is(0)); - content.release(); - - assertThat(ch.finish(), is(false)); - } - - @Test - public void testInformationalResponseKeepsPairsInSync() { - byte[] data = ("HTTP/1.1 102 Processing\r\n" + - "Status-URI: Status-URI:http://status.com; 404\r\n" + - "\r\n").getBytes(); - byte[] data2 = ("HTTP/1.1 200 OK\r\n" + - "Content-Length: 8\r\n" + - "\r\n" + - "12345678").getBytes(); - EmbeddedChannel ch = new EmbeddedChannel(new HttpClientCodec()); - assertTrue(ch.writeOutbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.HEAD, "/"))); - ByteBuf buffer = ch.readOutbound(); - buffer.release(); - assertNull(ch.readOutbound()); - assertTrue(ch.writeInbound(Unpooled.wrappedBuffer(data))); - HttpResponse res = ch.readInbound(); - assertThat(res.protocolVersion(), sameInstance(HttpVersion.HTTP_1_1)); - assertThat(res.status(), is(HttpResponseStatus.PROCESSING)); - HttpContent content = ch.readInbound(); - // HTTP 102 is not allowed to have content. - assertThat(content.content().readableBytes(), is(0)); - assertThat(content, CoreMatchers.instanceOf(LastHttpContent.class)); - content.release(); - - assertTrue(ch.writeOutbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/"))); - buffer = ch.readOutbound(); - buffer.release(); - assertNull(ch.readOutbound()); - assertTrue(ch.writeInbound(Unpooled.wrappedBuffer(data2))); - - res = ch.readInbound(); - assertThat(res.protocolVersion(), sameInstance(HttpVersion.HTTP_1_1)); - assertThat(res.status(), is(HttpResponseStatus.OK)); - content = ch.readInbound(); - // HTTP 200 has content. - assertThat(content.content().readableBytes(), is(8)); - assertThat(content, CoreMatchers.instanceOf(LastHttpContent.class)); - content.release(); - - assertThat(ch.finish(), is(false)); - } - - @Test - public void testMultipleResponses() { - String response = "HTTP/1.1 200 OK\r\n" + - "Content-Length: 0\r\n\r\n"; - - HttpClientCodec codec = new HttpClientCodec(4096, 8192, true); - EmbeddedChannel ch = new EmbeddedChannel(codec, new HttpObjectAggregator(1024)); - - HttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost/"); - assertTrue(ch.writeOutbound(request)); - - assertTrue(ch.writeInbound(Unpooled.copiedBuffer(response, CharsetUtil.UTF_8))); - assertTrue(ch.writeInbound(Unpooled.copiedBuffer(response, CharsetUtil.UTF_8))); - FullHttpResponse resp = ch.readInbound(); - assertTrue(resp.decoderResult().isSuccess()); - resp.release(); - - resp = ch.readInbound(); - assertTrue(resp.decoderResult().isSuccess()); - resp.release(); - assertTrue(ch.finishAndReleaseAll()); - } - -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpClientUpgradeHandlerTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpClientUpgradeHandlerTest.java deleted file mode 100644 index dde84466cc..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpClientUpgradeHandlerTest.java +++ /dev/null @@ -1,199 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.embedded.EmbeddedChannel; - -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class HttpClientUpgradeHandlerTest { - - private static final class FakeSourceCodec implements HttpClientUpgradeHandler.SourceCodec { - @Override - public void prepareUpgradeFrom(ChannelHandlerContext ctx) { - } - - @Override - public void upgradeFrom(ChannelHandlerContext ctx) { - } - } - - private static final class FakeUpgradeCodec implements HttpClientUpgradeHandler.UpgradeCodec { - @Override - public CharSequence protocol() { - return "fancyhttp"; - } - - @Override - public Collection setUpgradeHeaders(ChannelHandlerContext ctx, HttpRequest upgradeRequest) { - return Collections.emptyList(); - } - - @Override - public void upgradeTo(ChannelHandlerContext ctx, FullHttpResponse upgradeResponse) throws Exception { - } - } - - private static final class UserEventCatcher implements ChannelHandler { - private Object evt; - - public Object getUserEvent() { - return evt; - } - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { - this.evt = evt; - } - } - - @Test - public void testSuccessfulUpgrade() { - HttpClientUpgradeHandler.SourceCodec sourceCodec = new FakeSourceCodec(); - HttpClientUpgradeHandler.UpgradeCodec upgradeCodec = new FakeUpgradeCodec(); - HttpClientUpgradeHandler handler = new HttpClientUpgradeHandler(sourceCodec, upgradeCodec, 1024); - UserEventCatcher catcher = new UserEventCatcher(); - EmbeddedChannel channel = new EmbeddedChannel(catcher); - channel.pipeline().addFirst("upgrade", handler); - - assertTrue( - channel.writeOutbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "netty.io"))); - FullHttpRequest request = channel.readOutbound(); - - assertEquals(2, request.headers().size()); - assertTrue(request.headers().contains(HttpHeaderNames.UPGRADE, "fancyhttp", false)); - assertTrue(request.headers().contains("connection", "upgrade", false)); - assertTrue(request.release()); - assertEquals(HttpClientUpgradeHandler.UpgradeEvent.UPGRADE_ISSUED, catcher.getUserEvent()); - - HttpResponse upgradeResponse = - new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.SWITCHING_PROTOCOLS); - - upgradeResponse.headers().add(HttpHeaderNames.UPGRADE, "fancyhttp"); - assertFalse(channel.writeInbound(upgradeResponse)); - assertFalse(channel.writeInbound(LastHttpContent.EMPTY_LAST_CONTENT)); - - assertEquals(HttpClientUpgradeHandler.UpgradeEvent.UPGRADE_SUCCESSFUL, catcher.getUserEvent()); - assertNull(channel.pipeline().get("upgrade")); - - assertTrue(channel.writeInbound(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK))); - FullHttpResponse response = channel.readInbound(); - assertEquals(HttpResponseStatus.OK, response.status()); - assertTrue(response.release()); - assertFalse(channel.finish()); - } - - @Test - public void testUpgradeRejected() { - HttpClientUpgradeHandler.SourceCodec sourceCodec = new FakeSourceCodec(); - HttpClientUpgradeHandler.UpgradeCodec upgradeCodec = new FakeUpgradeCodec(); - HttpClientUpgradeHandler handler = new HttpClientUpgradeHandler(sourceCodec, upgradeCodec, 1024); - UserEventCatcher catcher = new UserEventCatcher(); - EmbeddedChannel channel = new EmbeddedChannel(catcher); - channel.pipeline().addFirst("upgrade", handler); - - assertTrue( - channel.writeOutbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "netty.io"))); - FullHttpRequest request = channel.readOutbound(); - - assertEquals(2, request.headers().size()); - assertTrue(request.headers().contains(HttpHeaderNames.UPGRADE, "fancyhttp", false)); - assertTrue(request.headers().contains("connection", "upgrade", false)); - assertTrue(request.release()); - assertEquals(HttpClientUpgradeHandler.UpgradeEvent.UPGRADE_ISSUED, catcher.getUserEvent()); - - HttpResponse upgradeResponse = - new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.SWITCHING_PROTOCOLS); - upgradeResponse.headers().add(HttpHeaderNames.UPGRADE, "fancyhttp"); - assertTrue(channel.writeInbound(new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK))); - assertTrue(channel.writeInbound(LastHttpContent.EMPTY_LAST_CONTENT)); - - assertEquals(HttpClientUpgradeHandler.UpgradeEvent.UPGRADE_REJECTED, catcher.getUserEvent()); - assertNull(channel.pipeline().get("upgrade")); - - HttpResponse response = channel.readInbound(); - assertEquals(HttpResponseStatus.OK, response.status()); - - LastHttpContent last = channel.readInbound(); - assertEquals(LastHttpContent.EMPTY_LAST_CONTENT, last); - assertFalse(last.release()); - assertFalse(channel.finish()); - } - - @Test - public void testEarlyBailout() { - HttpClientUpgradeHandler.SourceCodec sourceCodec = new FakeSourceCodec(); - HttpClientUpgradeHandler.UpgradeCodec upgradeCodec = new FakeUpgradeCodec(); - HttpClientUpgradeHandler handler = new HttpClientUpgradeHandler(sourceCodec, upgradeCodec, 1024); - UserEventCatcher catcher = new UserEventCatcher(); - EmbeddedChannel channel = new EmbeddedChannel(catcher); - channel.pipeline().addFirst("upgrade", handler); - - assertTrue( - channel.writeOutbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "netty.io"))); - FullHttpRequest request = channel.readOutbound(); - - assertEquals(2, request.headers().size()); - assertTrue(request.headers().contains(HttpHeaderNames.UPGRADE, "fancyhttp", false)); - assertTrue(request.headers().contains("connection", "upgrade", false)); - assertTrue(request.release()); - assertEquals(HttpClientUpgradeHandler.UpgradeEvent.UPGRADE_ISSUED, catcher.getUserEvent()); - - HttpResponse upgradeResponse = - new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.SWITCHING_PROTOCOLS); - upgradeResponse.headers().add(HttpHeaderNames.UPGRADE, "fancyhttp"); - assertTrue(channel.writeInbound(new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK))); - - assertEquals(HttpClientUpgradeHandler.UpgradeEvent.UPGRADE_REJECTED, catcher.getUserEvent()); - assertNull(channel.pipeline().get("upgrade")); - - HttpResponse response = channel.readInbound(); - assertEquals(HttpResponseStatus.OK, response.status()); - assertFalse(channel.finish()); - } - - @Test - public void dontStripConnectionHeaders() { - HttpClientUpgradeHandler.SourceCodec sourceCodec = new FakeSourceCodec(); - HttpClientUpgradeHandler.UpgradeCodec upgradeCodec = new FakeUpgradeCodec(); - HttpClientUpgradeHandler handler = new HttpClientUpgradeHandler(sourceCodec, upgradeCodec, 1024); - UserEventCatcher catcher = new UserEventCatcher(); - EmbeddedChannel channel = new EmbeddedChannel(catcher); - channel.pipeline().addFirst("upgrade", handler); - - DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "netty.io"); - request.headers().add("connection", "extra"); - request.headers().add("extra", "value"); - assertTrue(channel.writeOutbound(request)); - FullHttpRequest readRequest = channel.readOutbound(); - - List connectionHeaders = readRequest.headers().getAll("connection"); - assertTrue(connectionHeaders.contains("extra")); - assertTrue(readRequest.release()); - assertFalse(channel.finish()); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpContentCompressorOptionsTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpContentCompressorOptionsTest.java deleted file mode 100644 index 285be33f50..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpContentCompressorOptionsTest.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.compression.StandardCompressionOptions; - -import org.junit.jupiter.api.Test; - -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.not; -import static org.hamcrest.CoreMatchers.nullValue; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -class HttpContentCompressorOptionsTest { - - @Test - void testGetBrTargetContentEncoding() { - HttpContentCompressor compressor = new HttpContentCompressor( - StandardCompressionOptions.gzip(), - StandardCompressionOptions.deflate(), - StandardCompressionOptions.brotli(), - StandardCompressionOptions.zstd() - ); - - String[] tests = { - // Accept-Encoding -> Content-Encoding - "", null, - "*", "br", - "*;q=0.0", null, - "br", "br", - "compress, br;q=0.5", "br", - "br; q=0.5, identity", "br", - "br; q=0, deflate", "br", - }; - for (int i = 0; i < tests.length; i += 2) { - String acceptEncoding = tests[i]; - String contentEncoding = tests[i + 1]; - String targetEncoding = compressor.determineEncoding(acceptEncoding); - assertEquals(contentEncoding, targetEncoding); - } - } - - @Test - void testGetZstdTargetContentEncoding() { - HttpContentCompressor compressor = new HttpContentCompressor( - StandardCompressionOptions.gzip(), - StandardCompressionOptions.deflate(), - StandardCompressionOptions.brotli(), - StandardCompressionOptions.zstd() - ); - - String[] tests = { - // Accept-Encoding -> Content-Encoding - "", null, - "*;q=0.0", null, - "zstd", "zstd", - "compress, zstd;q=0.5", "zstd", - "zstd; q=0.5, identity", "zstd", - "zstd; q=0, deflate", "zstd", - }; - for (int i = 0; i < tests.length; i += 2) { - String acceptEncoding = tests[i]; - String contentEncoding = tests[i + 1]; - String targetEncoding = compressor.determineEncoding(acceptEncoding); - assertEquals(contentEncoding, targetEncoding); - } - } - - @Test - void testAcceptEncodingHttpRequest() { - EmbeddedChannel ch = new EmbeddedChannel(new HttpContentCompressor(null)); - ch.writeInbound(newRequest()); - FullHttpRequest fullHttpRequest = ch.readInbound(); - fullHttpRequest.release(); - - HttpResponse res = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - res.headers().set(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED); - ch.writeOutbound(res); - - assertEncodedResponse(ch); - - assertTrue(ch.close().isSuccess()); - } - - private static void assertEncodedResponse(EmbeddedChannel ch) { - Object o = ch.readOutbound(); - assertThat(o, is(instanceOf(HttpResponse.class))); - - assertEncodedResponse((HttpResponse) o); - } - - private static void assertEncodedResponse(HttpResponse res) { - assertThat(res, is(not(instanceOf(HttpContent.class)))); - assertThat(res.headers().get(HttpHeaderNames.TRANSFER_ENCODING), is("chunked")); - assertThat(res.headers().get(HttpHeaderNames.CONTENT_LENGTH), is(nullValue())); - assertThat(res.headers().get(HttpHeaderNames.CONTENT_ENCODING), is("br")); - } - - private static FullHttpRequest newRequest() { - FullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/"); - req.headers().set(HttpHeaderNames.ACCEPT_ENCODING, "br, zstd, gzip, deflate"); - return req; - } -} 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 deleted file mode 100644 index be1a91ced0..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpContentCompressorTest.java +++ /dev/null @@ -1,749 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.buffer.ByteBuf; -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; -import io.netty.util.ReferenceCountUtil; -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; - -import static io.netty.handler.codec.http.HttpHeadersTestUtils.of; -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.not; -import static org.hamcrest.CoreMatchers.nullValue; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - -public class HttpContentCompressorTest { - - @Test - public void testGetTargetContentEncoding() throws Exception { - HttpContentCompressor compressor = new HttpContentCompressor(); - - String[] tests = { - // Accept-Encoding -> Content-Encoding - "", null, - "*", "gzip", - "*;q=0.0", null, - "gzip", "gzip", - "compress, gzip;q=0.5", "gzip", - "gzip; q=0.5, identity", "gzip", - "gzip ; q=0.1", "gzip", - "gzip; q=0, deflate", "deflate", - " deflate ; q=0 , *;q=0.5", "gzip", - }; - for (int i = 0; i < tests.length; i += 2) { - String acceptEncoding = tests[i]; - String contentEncoding = tests[i + 1]; - ZlibWrapper targetWrapper = compressor.determineWrapper(acceptEncoding); - String targetEncoding = null; - if (targetWrapper != null) { - switch (targetWrapper) { - case GZIP: - targetEncoding = "gzip"; - break; - case ZLIB: - targetEncoding = "deflate"; - break; - default: - fail(); - } - } - assertEquals(contentEncoding, targetEncoding); - } - } - - @Test - public void testSplitContent() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new HttpContentCompressor()); - ch.writeInbound(newRequest()); - - ch.writeOutbound(new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK)); - ch.writeOutbound(new DefaultHttpContent(Unpooled.copiedBuffer("Hell", CharsetUtil.US_ASCII))); - ch.writeOutbound(new DefaultHttpContent(Unpooled.copiedBuffer("o, w", CharsetUtil.US_ASCII))); - ch.writeOutbound(new DefaultLastHttpContent(Unpooled.copiedBuffer("orld", CharsetUtil.US_ASCII))); - - assertEncodedResponse(ch); - - HttpContent chunk; - chunk = ch.readOutbound(); - assertThat(ByteBufUtil.hexDump(chunk.content()), is("1f8b0800000000000000f248cdc901000000ffff")); - chunk.release(); - - chunk = ch.readOutbound(); - assertThat(ByteBufUtil.hexDump(chunk.content()), is("cad7512807000000ffff")); - chunk.release(); - - chunk = ch.readOutbound(); - assertThat(ByteBufUtil.hexDump(chunk.content()), is("ca2fca4901000000ffff")); - chunk.release(); - - chunk = ch.readOutbound(); - assertThat(ByteBufUtil.hexDump(chunk.content()), is("0300c2a99ae70c000000")); - assertThat(chunk, is(instanceOf(HttpContent.class))); - chunk.release(); - - chunk = ch.readOutbound(); - assertThat(chunk.content().isReadable(), is(false)); - assertThat(chunk, is(instanceOf(LastHttpContent.class))); - chunk.release(); - - assertThat(ch.readOutbound(), is(nullValue())); - } - - @Test - public void testChunkedContent() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new HttpContentCompressor()); - ch.writeInbound(newRequest()); - - HttpResponse res = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - res.headers().set(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED); - ch.writeOutbound(res); - - assertEncodedResponse(ch); - - ch.writeOutbound(new DefaultHttpContent(Unpooled.copiedBuffer("Hell", CharsetUtil.US_ASCII))); - ch.writeOutbound(new DefaultHttpContent(Unpooled.copiedBuffer("o, w", CharsetUtil.US_ASCII))); - ch.writeOutbound(new DefaultLastHttpContent(Unpooled.copiedBuffer("orld", CharsetUtil.US_ASCII))); - - HttpContent chunk; - chunk = ch.readOutbound(); - assertThat(ByteBufUtil.hexDump(chunk.content()), is("1f8b0800000000000000f248cdc901000000ffff")); - chunk.release(); - - chunk = ch.readOutbound(); - assertThat(ByteBufUtil.hexDump(chunk.content()), is("cad7512807000000ffff")); - chunk.release(); - - chunk = ch.readOutbound(); - assertThat(ByteBufUtil.hexDump(chunk.content()), is("ca2fca4901000000ffff")); - chunk.release(); - - chunk = ch.readOutbound(); - assertThat(ByteBufUtil.hexDump(chunk.content()), is("0300c2a99ae70c000000")); - assertThat(chunk, is(instanceOf(HttpContent.class))); - chunk.release(); - - chunk = ch.readOutbound(); - assertThat(chunk.content().isReadable(), is(false)); - assertThat(chunk, is(instanceOf(LastHttpContent.class))); - chunk.release(); - - assertThat(ch.readOutbound(), is(nullValue())); - } - - @Test - public void testChunkedContentWithAssembledResponse() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new HttpContentCompressor()); - ch.writeInbound(newRequest()); - - HttpResponse res = new AssembledHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, - Unpooled.copiedBuffer("Hell", CharsetUtil.US_ASCII)); - res.headers().set(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED); - ch.writeOutbound(res); - - assertAssembledEncodedResponse(ch); - - ch.writeOutbound(new DefaultHttpContent(Unpooled.copiedBuffer("o, w", CharsetUtil.US_ASCII))); - ch.writeOutbound(new DefaultLastHttpContent(Unpooled.copiedBuffer("orld", CharsetUtil.US_ASCII))); - - HttpContent chunk; - chunk = ch.readOutbound(); - assertThat(ByteBufUtil.hexDump(chunk.content()), is("1f8b0800000000000000f248cdc901000000ffff")); - chunk.release(); - - chunk = ch.readOutbound(); - assertThat(ByteBufUtil.hexDump(chunk.content()), is("cad7512807000000ffff")); - chunk.release(); - - chunk = ch.readOutbound(); - assertThat(ByteBufUtil.hexDump(chunk.content()), is("ca2fca4901000000ffff")); - chunk.release(); - - chunk = ch.readOutbound(); - assertThat(ByteBufUtil.hexDump(chunk.content()), is("0300c2a99ae70c000000")); - assertThat(chunk, is(instanceOf(HttpContent.class))); - chunk.release(); - - chunk = ch.readOutbound(); - assertThat(chunk.content().isReadable(), is(false)); - assertThat(chunk, is(instanceOf(LastHttpContent.class))); - chunk.release(); - - assertThat(ch.readOutbound(), is(nullValue())); - } - - @Test - public void testChunkedContentWithAssembledResponseIdentityEncoding() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new HttpContentCompressor()); - ch.writeInbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/")); - - HttpResponse res = new AssembledHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, - Unpooled.copiedBuffer("Hell", CharsetUtil.US_ASCII)); - res.headers().set(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED); - ch.writeOutbound(res); - - ch.writeOutbound(new DefaultHttpContent(Unpooled.copiedBuffer("o, w", CharsetUtil.US_ASCII))); - ch.writeOutbound(new DefaultLastHttpContent(Unpooled.copiedBuffer("orld", CharsetUtil.US_ASCII))); - - HttpContent chunk; - chunk = ch.readOutbound(); - assertThat(chunk.content().toString(StandardCharsets.UTF_8), is("Hell")); - chunk.release(); - - chunk = ch.readOutbound(); - assertThat(chunk.content().toString(StandardCharsets.UTF_8), is("o, w")); - chunk.release(); - - chunk = ch.readOutbound(); - assertThat(chunk.content().toString(StandardCharsets.UTF_8), is("orld")); - assertThat(chunk, is(instanceOf(LastHttpContent.class))); - chunk.release(); - - assertThat(ch.readOutbound(), is(nullValue())); - } - - @Test - public void testContentWithAssembledResponseIdentityEncodingHttp10() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new HttpContentCompressor()); - ch.writeInbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_0, HttpMethod.GET, "/")); - - HttpResponse res = new AssembledHttpResponse(HttpVersion.HTTP_1_0, HttpResponseStatus.OK, - Unpooled.copiedBuffer("Hell", CharsetUtil.US_ASCII)); - ch.writeOutbound(res); - - ch.writeOutbound(new DefaultHttpContent(Unpooled.copiedBuffer("o, w", CharsetUtil.US_ASCII))); - ch.writeOutbound(new DefaultLastHttpContent(Unpooled.copiedBuffer("orld", CharsetUtil.US_ASCII))); - - HttpContent chunk; - chunk = ch.readOutbound(); - assertThat(chunk.content().toString(StandardCharsets.UTF_8), is("Hell")); - chunk.release(); - - chunk = ch.readOutbound(); - assertThat(chunk.content().toString(StandardCharsets.UTF_8), is("o, w")); - chunk.release(); - - chunk = ch.readOutbound(); - assertThat(chunk.content().toString(StandardCharsets.UTF_8), is("orld")); - assertThat(chunk, is(instanceOf(LastHttpContent.class))); - chunk.release(); - - assertThat(ch.readOutbound(), is(nullValue())); - } - - @Test - public void testChunkedContentWithTrailingHeader() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new HttpContentCompressor()); - ch.writeInbound(newRequest()); - - HttpResponse res = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - res.headers().set(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED); - ch.writeOutbound(res); - - assertEncodedResponse(ch); - - ch.writeOutbound(new DefaultHttpContent(Unpooled.copiedBuffer("Hell", CharsetUtil.US_ASCII))); - ch.writeOutbound(new DefaultHttpContent(Unpooled.copiedBuffer("o, w", CharsetUtil.US_ASCII))); - LastHttpContent content = new DefaultLastHttpContent(Unpooled.copiedBuffer("orld", CharsetUtil.US_ASCII)); - content.trailingHeaders().set(of("X-Test"), of("Netty")); - ch.writeOutbound(content); - - HttpContent chunk; - chunk = ch.readOutbound(); - assertThat(ByteBufUtil.hexDump(chunk.content()), is("1f8b0800000000000000f248cdc901000000ffff")); - chunk.release(); - - chunk = ch.readOutbound(); - assertThat(ByteBufUtil.hexDump(chunk.content()), is("cad7512807000000ffff")); - chunk.release(); - - chunk = ch.readOutbound(); - assertThat(ByteBufUtil.hexDump(chunk.content()), is("ca2fca4901000000ffff")); - chunk.release(); - - chunk = ch.readOutbound(); - assertThat(ByteBufUtil.hexDump(chunk.content()), is("0300c2a99ae70c000000")); - assertThat(chunk, is(instanceOf(HttpContent.class))); - chunk.release(); - - chunk = ch.readOutbound(); - 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())); - } - - @Test - public void testFullContentWithContentLength() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new HttpContentCompressor()); - ch.writeInbound(newRequest()); - - FullHttpResponse fullRes = new DefaultFullHttpResponse( - HttpVersion.HTTP_1_1, HttpResponseStatus.OK, - Unpooled.copiedBuffer("Hello, World", CharsetUtil.US_ASCII)); - fullRes.headers().set(HttpHeaderNames.CONTENT_LENGTH, fullRes.content().readableBytes()); - ch.writeOutbound(fullRes); - - HttpResponse res = ch.readOutbound(); - assertThat(res, is(not(instanceOf(HttpContent.class)))); - - assertThat(res.headers().get(HttpHeaderNames.TRANSFER_ENCODING), is(nullValue())); - assertThat(res.headers().get(HttpHeaderNames.CONTENT_ENCODING), is("gzip")); - - long contentLengthHeaderValue = HttpUtil.getContentLength(res); - long observedLength = 0; - - HttpContent c = ch.readOutbound(); - observedLength += c.content().readableBytes(); - assertThat(ByteBufUtil.hexDump(c.content()), is("1f8b0800000000000000f248cdc9c9d75108cf2fca4901000000ffff")); - c.release(); - - c = ch.readOutbound(); - observedLength += c.content().readableBytes(); - assertThat(ByteBufUtil.hexDump(c.content()), is("0300c6865b260c000000")); - c.release(); - - LastHttpContent last = ch.readOutbound(); - assertThat(last.content().readableBytes(), is(0)); - last.release(); - - assertThat(ch.readOutbound(), is(nullValue())); - assertEquals(contentLengthHeaderValue, observedLength); - } - - @Test - public void testFullContent() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new HttpContentCompressor()); - ch.writeInbound(newRequest()); - - FullHttpResponse res = new DefaultFullHttpResponse( - HttpVersion.HTTP_1_1, HttpResponseStatus.OK, - Unpooled.copiedBuffer("Hello, World", CharsetUtil.US_ASCII)); - ch.writeOutbound(res); - - assertEncodedResponse(ch); - HttpContent c = ch.readOutbound(); - assertThat(ByteBufUtil.hexDump(c.content()), is("1f8b0800000000000000f248cdc9c9d75108cf2fca4901000000ffff")); - c.release(); - - c = ch.readOutbound(); - assertThat(ByteBufUtil.hexDump(c.content()), is("0300c6865b260c000000")); - c.release(); - - LastHttpContent last = ch.readOutbound(); - assertThat(last.content().readableBytes(), is(0)); - last.release(); - - assertThat(ch.readOutbound(), is(nullValue())); - } - - /** - * If the length of the content is unknown, {@link HttpContentEncoder} should not skip encoding the content - * even if the actual length is turned out to be 0. - */ - @Test - public void testEmptySplitContent() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new HttpContentCompressor()); - ch.writeInbound(newRequest()); - - ch.writeOutbound(new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK)); - assertEncodedResponse(ch); - - ch.writeOutbound(LastHttpContent.EMPTY_LAST_CONTENT); - HttpContent chunk = ch.readOutbound(); - assertThat(ByteBufUtil.hexDump(chunk.content()), is("1f8b080000000000000003000000000000000000")); - assertThat(chunk, is(instanceOf(HttpContent.class))); - chunk.release(); - - chunk = ch.readOutbound(); - assertThat(chunk.content().isReadable(), is(false)); - assertThat(chunk, is(instanceOf(LastHttpContent.class))); - chunk.release(); - - assertThat(ch.readOutbound(), is(nullValue())); - } - - /** - * If the length of the content is 0 for sure, {@link HttpContentEncoder} should skip encoding. - */ - @Test - public void testEmptyFullContent() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new HttpContentCompressor()); - ch.writeInbound(newRequest()); - - FullHttpResponse res = new DefaultFullHttpResponse( - HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.EMPTY_BUFFER); - ch.writeOutbound(res); - - Object o = ch.readOutbound(); - assertThat(o, is(instanceOf(FullHttpResponse.class))); - - res = (FullHttpResponse) o; - assertThat(res.headers().get(HttpHeaderNames.TRANSFER_ENCODING), is(nullValue())); - - // Content encoding shouldn't be modified. - assertThat(res.headers().get(HttpHeaderNames.CONTENT_ENCODING), is(nullValue())); - assertThat(res.content().readableBytes(), is(0)); - assertThat(res.content().toString(CharsetUtil.US_ASCII), is("")); - res.release(); - - assertThat(ch.readOutbound(), is(nullValue())); - } - - @Test - public void testEmptyFullContentWithTrailer() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new HttpContentCompressor()); - ch.writeInbound(newRequest()); - - FullHttpResponse res = new DefaultFullHttpResponse( - HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.EMPTY_BUFFER); - res.trailingHeaders().set(of("X-Test"), of("Netty")); - ch.writeOutbound(res); - - Object o = ch.readOutbound(); - assertThat(o, is(instanceOf(FullHttpResponse.class))); - - res = (FullHttpResponse) o; - assertThat(res.headers().get(HttpHeaderNames.TRANSFER_ENCODING), is(nullValue())); - - // Content encoding shouldn't be modified. - assertThat(res.headers().get(HttpHeaderNames.CONTENT_ENCODING), is(nullValue())); - 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())); - } - - @Test - public void test100Continue() throws Exception { - FullHttpRequest request = newRequest(); - HttpUtil.set100ContinueExpected(request, true); - - EmbeddedChannel ch = new EmbeddedChannel(new HttpContentCompressor()); - ch.writeInbound(request); - - FullHttpResponse continueResponse = new DefaultFullHttpResponse( - HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE, Unpooled.EMPTY_BUFFER); - - ch.writeOutbound(continueResponse); - - FullHttpResponse res = new DefaultFullHttpResponse( - HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.EMPTY_BUFFER); - res.trailingHeaders().set(of("X-Test"), of("Netty")); - ch.writeOutbound(res); - - Object o = ch.readOutbound(); - assertThat(o, is(instanceOf(FullHttpResponse.class))); - - res = (FullHttpResponse) o; - assertSame(continueResponse, res); - res.release(); - - o = ch.readOutbound(); - assertThat(o, is(instanceOf(FullHttpResponse.class))); - - res = (FullHttpResponse) o; - assertThat(res.headers().get(HttpHeaderNames.TRANSFER_ENCODING), is(nullValue())); - - // Content encoding shouldn't be modified. - assertThat(res.headers().get(HttpHeaderNames.CONTENT_ENCODING), is(nullValue())); - 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())); - } - - @Test - public void testTooManyResponses() throws Exception { - FullHttpRequest request = newRequest(); - EmbeddedChannel ch = new EmbeddedChannel(new HttpContentCompressor()); - ch.writeInbound(request); - - ch.writeOutbound(new DefaultFullHttpResponse( - HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.EMPTY_BUFFER)); - - try { - ch.writeOutbound(new DefaultFullHttpResponse( - HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.EMPTY_BUFFER)); - fail(); - } catch (EncoderException e) { - assertTrue(e.getCause() instanceof IllegalStateException); - } - assertTrue(ch.finish()); - for (;;) { - Object message = ch.readOutbound(); - if (message == null) { - break; - } - ReferenceCountUtil.release(message); - } - for (;;) { - Object message = ch.readInbound(); - if (message == null) { - break; - } - ReferenceCountUtil.release(message); - } - } - - @Test - public void testIdentity() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new HttpContentCompressor()); - assertTrue(ch.writeInbound(newRequest())); - - FullHttpResponse res = new DefaultFullHttpResponse( - HttpVersion.HTTP_1_1, HttpResponseStatus.OK, - Unpooled.copiedBuffer("Hello, World", CharsetUtil.US_ASCII)); - int len = res.content().readableBytes(); - res.headers().set(HttpHeaderNames.CONTENT_LENGTH, len); - res.headers().set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.IDENTITY); - assertTrue(ch.writeOutbound(res)); - - FullHttpResponse response = ch.readOutbound(); - assertEquals(String.valueOf(len), response.headers().get(HttpHeaderNames.CONTENT_LENGTH)); - assertEquals(HttpHeaderValues.IDENTITY.toString(), response.headers().get(HttpHeaderNames.CONTENT_ENCODING)); - assertEquals("Hello, World", response.content().toString(CharsetUtil.US_ASCII)); - response.release(); - - assertTrue(ch.finishAndReleaseAll()); - } - - @Test - public void testCustomEncoding() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new HttpContentCompressor()); - assertTrue(ch.writeInbound(newRequest())); - - FullHttpResponse res = new DefaultFullHttpResponse( - HttpVersion.HTTP_1_1, HttpResponseStatus.OK, - Unpooled.copiedBuffer("Hello, World", CharsetUtil.US_ASCII)); - int len = res.content().readableBytes(); - res.headers().set(HttpHeaderNames.CONTENT_LENGTH, len); - res.headers().set(HttpHeaderNames.CONTENT_ENCODING, "ascii"); - assertTrue(ch.writeOutbound(res)); - - FullHttpResponse response = ch.readOutbound(); - assertEquals(String.valueOf(len), response.headers().get(HttpHeaderNames.CONTENT_LENGTH)); - assertEquals("ascii", response.headers().get(HttpHeaderNames.CONTENT_ENCODING)); - assertEquals("Hello, World", response.content().toString(CharsetUtil.US_ASCII)); - response.release(); - - assertTrue(ch.finishAndReleaseAll()); - } - - @Test - public void testCompressThresholdAllCompress() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new HttpContentCompressor()); - assertTrue(ch.writeInbound(newRequest())); - - FullHttpResponse res1023 = new DefaultFullHttpResponse( - HttpVersion.HTTP_1_1, HttpResponseStatus.OK, - Unpooled.wrappedBuffer(new byte[1023])); - assertTrue(ch.writeOutbound(res1023)); - DefaultHttpResponse response1023 = ch.readOutbound(); - assertThat(response1023.headers().get(HttpHeaderNames.CONTENT_ENCODING), is("gzip")); - ch.releaseOutbound(); - - assertTrue(ch.writeInbound(newRequest())); - FullHttpResponse res1024 = new DefaultFullHttpResponse( - HttpVersion.HTTP_1_1, HttpResponseStatus.OK, - Unpooled.wrappedBuffer(new byte[1024])); - assertTrue(ch.writeOutbound(res1024)); - DefaultHttpResponse response1024 = ch.readOutbound(); - assertThat(response1024.headers().get(HttpHeaderNames.CONTENT_ENCODING), is("gzip")); - assertTrue(ch.finishAndReleaseAll()); - } - - @Test - public void testCompressThresholdNotCompress() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new HttpContentCompressor(6, 15, 8, 1024)); - assertTrue(ch.writeInbound(newRequest())); - - FullHttpResponse res1023 = new DefaultFullHttpResponse( - HttpVersion.HTTP_1_1, HttpResponseStatus.OK, - Unpooled.wrappedBuffer(new byte[1023])); - assertTrue(ch.writeOutbound(res1023)); - DefaultHttpResponse response1023 = ch.readOutbound(); - assertFalse(response1023.headers().contains(HttpHeaderNames.CONTENT_ENCODING)); - ch.releaseOutbound(); - - assertTrue(ch.writeInbound(newRequest())); - FullHttpResponse res1024 = new DefaultFullHttpResponse( - HttpVersion.HTTP_1_1, HttpResponseStatus.OK, - Unpooled.wrappedBuffer(new byte[1024])); - assertTrue(ch.writeOutbound(res1024)); - DefaultHttpResponse response1024 = ch.readOutbound(); - assertThat(response1024.headers().get(HttpHeaderNames.CONTENT_ENCODING), is("gzip")); - assertTrue(ch.finishAndReleaseAll()); - } - - @Test - public void testMultipleAcceptEncodingHeaders() { - FullHttpRequest request = newRequest(); - request.headers().set(HttpHeaderNames.ACCEPT_ENCODING, "unknown; q=1.0") - .add(HttpHeaderNames.ACCEPT_ENCODING, "gzip; q=0.5") - .add(HttpHeaderNames.ACCEPT_ENCODING, "deflate; q=0"); - - EmbeddedChannel ch = new EmbeddedChannel(new HttpContentCompressor()); - - assertTrue(ch.writeInbound(request)); - - FullHttpResponse res = new DefaultFullHttpResponse( - HttpVersion.HTTP_1_1, HttpResponseStatus.OK, - Unpooled.copiedBuffer("Gzip Win", CharsetUtil.US_ASCII)); - assertTrue(ch.writeOutbound(res)); - - assertEncodedResponse(ch); - HttpContent c = ch.readOutbound(); - assertThat(ByteBufUtil.hexDump(c.content()), is("1f8b080000000000000072afca2c5008cfcc03000000ffff")); - c.release(); - - c = ch.readOutbound(); - assertThat(ByteBufUtil.hexDump(c.content()), is("03001f2ebf0f08000000")); - c.release(); - - LastHttpContent last = ch.readOutbound(); - assertThat(last.content().readableBytes(), is(0)); - last.release(); - - assertThat(ch.readOutbound(), is(nullValue())); - assertTrue(ch.finishAndReleaseAll()); - } - - private static FullHttpRequest newRequest() { - FullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/"); - req.headers().set(HttpHeaderNames.ACCEPT_ENCODING, "gzip"); - return req; - } - - private static void assertEncodedResponse(EmbeddedChannel ch) { - Object o = ch.readOutbound(); - assertThat(o, is(instanceOf(HttpResponse.class))); - - HttpResponse res = (HttpResponse) o; - assertThat(res, is(not(instanceOf(HttpContent.class)))); - assertThat(res.headers().get(HttpHeaderNames.TRANSFER_ENCODING), is("chunked")); - assertThat(res.headers().get(HttpHeaderNames.CONTENT_LENGTH), is(nullValue())); - assertThat(res.headers().get(HttpHeaderNames.CONTENT_ENCODING), is("gzip")); - } - - private static void assertAssembledEncodedResponse(EmbeddedChannel ch) { - Object o = ch.readOutbound(); - assertThat(o, is(instanceOf(AssembledHttpResponse.class))); - - AssembledHttpResponse res = (AssembledHttpResponse) o; - try { - assertThat(res, is(instanceOf(HttpContent.class))); - assertThat(res.headers().get(HttpHeaderNames.TRANSFER_ENCODING), is("chunked")); - assertThat(res.headers().get(HttpHeaderNames.CONTENT_LENGTH), is(nullValue())); - assertThat(res.headers().get(HttpHeaderNames.CONTENT_ENCODING), is("gzip")); - } finally { - res.release(); - } - } - - static class AssembledHttpResponse extends DefaultHttpResponse implements HttpContent { - - private final ByteBuf content; - - AssembledHttpResponse(HttpVersion version, HttpResponseStatus status, ByteBuf content) { - super(version, status); - this.content = content; - } - - @Override - public HttpContent copy() { - throw new UnsupportedOperationException(); - } - - @Override - public HttpContent duplicate() { - throw new UnsupportedOperationException(); - } - - @Override - public HttpContent retainedDuplicate() { - throw new UnsupportedOperationException(); - } - - @Override - public HttpContent replace(ByteBuf content) { - throw new UnsupportedOperationException(); - } - - @Override - public AssembledHttpResponse retain() { - content.retain(); - return this; - } - - @Override - public AssembledHttpResponse retain(int increment) { - content.retain(increment); - return this; - } - - @Override - public ByteBuf content() { - return content; - } - - @Override - public int refCnt() { - return content.refCnt(); - } - - @Override - public boolean release() { - return content.release(); - } - - @Override - public boolean release(int decrement) { - return content.release(decrement); - } - - @Override - public AssembledHttpResponse touch() { - content.touch(); - return this; - } - - @Override - public AssembledHttpResponse touch(Object hint) { - content.touch(hint); - return this; - } - } -} 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 deleted file mode 100644 index 00e0fbfae5..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpContentDecoderTest.java +++ /dev/null @@ -1,842 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.http; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.CodecException; -import io.netty.handler.codec.DecoderException; -import io.netty.handler.codec.compression.Brotli; -import io.netty.handler.codec.compression.ZlibCodecFactory; -import io.netty.handler.codec.compression.ZlibDecoder; -import io.netty.handler.codec.compression.ZlibEncoder; -import io.netty.handler.codec.compression.ZlibWrapper; -import io.netty.util.CharsetUtil; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.internal.PlatformDependent; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.DisabledIf; - -import java.util.ArrayList; -import java.util.List; -import java.util.Queue; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; - -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - -public class HttpContentDecoderTest { - private static final String HELLO_WORLD = "hello, world"; - private static final byte[] GZ_HELLO_WORLD = { - 31, -117, 8, 8, 12, 3, -74, 84, 0, 3, 50, 0, -53, 72, -51, -55, -55, - -41, 81, 40, -49, 47, -54, 73, 1, 0, 58, 114, -85, -1, 12, 0, 0, 0 - }; - private static final String SAMPLE_STRING = "Hello, I am Meow!. A small kitten. :)" + - "I sleep all day, and meow all night."; - private static final byte[] SAMPLE_BZ_BYTES = new byte[]{27, 72, 0, 0, -60, -102, 91, -86, 103, 20, - -28, -23, 54, -101, 11, -106, -16, -32, -95, -61, -37, 94, -16, 97, -40, -93, -56, 18, 21, 86, - -110, 82, -41, 102, -89, 20, 11, 10, -68, -31, 96, -116, -55, -80, -31, -91, 96, -64, 83, 51, - -39, 13, -21, 92, -16, -119, 124, -31, 18, 78, -1, 91, 82, 105, -116, -95, -22, -11, -70, -45, 0}; - - @Test - public void testBinaryDecompression() throws Exception { - // baseline test: zlib library and test helpers work correctly. - byte[] helloWorld = gzDecompress(GZ_HELLO_WORLD); - assertEquals(HELLO_WORLD.length(), helloWorld.length); - assertEquals(HELLO_WORLD, new String(helloWorld, CharsetUtil.US_ASCII)); - - String fullCycleTest = "full cycle test"; - byte[] compressed = gzCompress(fullCycleTest.getBytes(CharsetUtil.US_ASCII)); - byte[] decompressed = gzDecompress(compressed); - assertEquals(decompressed.length, fullCycleTest.length()); - assertEquals(fullCycleTest, new String(decompressed, CharsetUtil.US_ASCII)); - } - - @Test - public void testRequestDecompression() { - // baseline test: request decoder, content decompressor && request aggregator work as expected - HttpRequestDecoder decoder = new HttpRequestDecoder(); - HttpContentDecoder decompressor = new HttpContentDecompressor(); - HttpObjectAggregator aggregator = new HttpObjectAggregator(1024); - EmbeddedChannel channel = new EmbeddedChannel(decoder, decompressor, aggregator); - - String headers = "POST / HTTP/1.1\r\n" + - "Content-Length: " + GZ_HELLO_WORLD.length + "\r\n" + - "Content-Encoding: gzip\r\n" + - "\r\n"; - ByteBuf buf = Unpooled.copiedBuffer(headers.getBytes(CharsetUtil.US_ASCII), GZ_HELLO_WORLD); - assertTrue(channel.writeInbound(buf)); - - Object o = channel.readInbound(); - assertThat(o, is(instanceOf(FullHttpRequest.class))); - FullHttpRequest req = (FullHttpRequest) o; - assertEquals(HELLO_WORLD.length(), req.headers().getInt(HttpHeaderNames.CONTENT_LENGTH).intValue()); - assertEquals(HELLO_WORLD, req.content().toString(CharsetUtil.US_ASCII)); - req.release(); - - assertHasInboundMessages(channel, false); - assertHasOutboundMessages(channel, false); - 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(ob2, is(instanceOf(HttpContent.class))); - HttpContent content = (HttpContent) ob2; - assertEquals(HELLO_WORLD, content.content().toString(CharsetUtil.US_ASCII)); - content.release(); - - Object ob3 = channel.readInbound(); - assertThat(ob3, is(instanceOf(LastHttpContent.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 - HttpResponseDecoder decoder = new HttpResponseDecoder(); - HttpContentDecoder decompressor = new HttpContentDecompressor(); - HttpObjectAggregator aggregator = new HttpObjectAggregator(1024); - EmbeddedChannel channel = new EmbeddedChannel(decoder, decompressor, aggregator); - - String headers = "HTTP/1.1 200 OK\r\n" + - "Content-Length: " + GZ_HELLO_WORLD.length + "\r\n" + - "Content-Encoding: gzip\r\n" + - "\r\n"; - ByteBuf buf = Unpooled.copiedBuffer(headers.getBytes(CharsetUtil.US_ASCII), GZ_HELLO_WORLD); - assertTrue(channel.writeInbound(buf)); - - Object o = channel.readInbound(); - assertThat(o, is(instanceOf(FullHttpResponse.class))); - FullHttpResponse resp = (FullHttpResponse) o; - assertEquals(HELLO_WORLD.length(), resp.headers().getInt(HttpHeaderNames.CONTENT_LENGTH).intValue()); - assertEquals(HELLO_WORLD, resp.content().toString(CharsetUtil.US_ASCII)); - resp.release(); - - assertHasInboundMessages(channel, false); - assertHasOutboundMessages(channel, false); - assertFalse(channel.finish()); // assert that no messages are left in channel - } - - @DisabledIf(value = "isNotSupported", disabledReason = "Brotli is not supported on this platform") - @Test - public void testResponseBrotliDecompression() throws Throwable { - Brotli.ensureAvailability(); - - HttpResponseDecoder decoder = new HttpResponseDecoder(); - HttpContentDecoder decompressor = new HttpContentDecompressor(); - HttpObjectAggregator aggregator = new HttpObjectAggregator(Integer.MAX_VALUE); - EmbeddedChannel channel = new EmbeddedChannel(decoder, decompressor, aggregator); - - String headers = "HTTP/1.1 200 OK\r\n" + - "Content-Length: " + SAMPLE_BZ_BYTES.length + "\r\n" + - "Content-Encoding: br\r\n" + - "\r\n"; - ByteBuf buf = Unpooled.wrappedBuffer(headers.getBytes(CharsetUtil.US_ASCII), SAMPLE_BZ_BYTES); - assertTrue(channel.writeInbound(buf)); - - Object o = channel.readInbound(); - assertThat(o, is(instanceOf(FullHttpResponse.class))); - FullHttpResponse resp = (FullHttpResponse) o; - assertNull(resp.headers().get(HttpHeaderNames.CONTENT_ENCODING), "Content-Encoding header should be removed"); - assertEquals(SAMPLE_STRING, resp.content().toString(CharsetUtil.UTF_8), - "Response body should match uncompressed string"); - resp.release(); - - assertHasInboundMessages(channel, false); - assertHasOutboundMessages(channel, false); - assertFalse(channel.finish()); // assert that no messages are left in channel - } - - @DisabledIf(value = "isNotSupported", disabledReason = "Brotli is not supported on this platform") - @Test - public void testResponseChunksBrotliDecompression() throws Throwable { - Brotli.ensureAvailability(); - - HttpResponseDecoder decoder = new HttpResponseDecoder(); - HttpContentDecoder decompressor = new HttpContentDecompressor(); - HttpObjectAggregator aggregator = new HttpObjectAggregator(Integer.MAX_VALUE); - EmbeddedChannel channel = new EmbeddedChannel(decoder, decompressor, aggregator); - - String headers = "HTTP/1.1 200 OK\r\n" + - "Content-Length: " + SAMPLE_BZ_BYTES.length + "\r\n" + - "Content-Encoding: br\r\n" + - "\r\n"; - - assertFalse(channel.writeInbound(Unpooled.wrappedBuffer(headers.getBytes(CharsetUtil.US_ASCII)))); - - int offset = 0; - while (offset < SAMPLE_BZ_BYTES.length) { - int len = Math.min(1500, SAMPLE_BZ_BYTES.length - offset); - boolean available = channel.writeInbound(Unpooled.wrappedBuffer(SAMPLE_BZ_BYTES, offset, len)); - offset += 1500; - if (offset < SAMPLE_BZ_BYTES.length) { - assertFalse(available); - } else { - assertTrue(available); - } - } - - Object o = channel.readInbound(); - assertThat(o, is(instanceOf(FullHttpResponse.class))); - FullHttpResponse resp = (FullHttpResponse) o; - assertEquals(SAMPLE_STRING, resp.content().toString(CharsetUtil.UTF_8), - "Response body should match uncompressed string"); - resp.release(); - - assertHasInboundMessages(channel, false); - assertHasOutboundMessages(channel, false); - assertFalse(channel.finish()); // assert that no messages are left in channel - } - - @Test - public void testExpectContinueResponse1() { - // request with header "Expect: 100-continue" must be replied with one "100 Continue" response - // case 1: no ContentDecoder in chain at all (baseline test) - HttpRequestDecoder decoder = new HttpRequestDecoder(); - HttpObjectAggregator aggregator = new HttpObjectAggregator(1024); - EmbeddedChannel channel = new EmbeddedChannel(decoder, aggregator); - String req = "POST / HTTP/1.1\r\n" + - "Content-Length: " + GZ_HELLO_WORLD.length + "\r\n" + - "Expect: 100-continue\r\n" + - "\r\n"; - // note: the following writeInbound() returns false as there is no message is inbound buffer - // until HttpObjectAggregator caches composes a complete message. - // however, http response "100 continue" must be sent as soon as headers are received - assertFalse(channel.writeInbound(Unpooled.wrappedBuffer(req.getBytes()))); - - Object o = channel.readOutbound(); - assertThat(o, is(instanceOf(FullHttpResponse.class))); - FullHttpResponse r = (FullHttpResponse) o; - assertEquals(100, r.status().code()); - assertTrue(channel.writeInbound(Unpooled.copiedBuffer(GZ_HELLO_WORLD))); - r.release(); - - assertHasInboundMessages(channel, true); - assertHasOutboundMessages(channel, false); - assertFalse(channel.finish()); - } - - @Test - public void testExpectContinueResponse2() { - // request with header "Expect: 100-continue" must be replied with one "100 Continue" response - // case 2: contentDecoder is in chain, but the content is not encoded, should be no-op - HttpRequestDecoder decoder = new HttpRequestDecoder(); - HttpContentDecoder decompressor = new HttpContentDecompressor(); - HttpObjectAggregator aggregator = new HttpObjectAggregator(1024); - EmbeddedChannel channel = new EmbeddedChannel(decoder, decompressor, aggregator); - String req = "POST / HTTP/1.1\r\n" + - "Content-Length: " + GZ_HELLO_WORLD.length + "\r\n" + - "Expect: 100-continue\r\n" + - "\r\n"; - assertFalse(channel.writeInbound(Unpooled.wrappedBuffer(req.getBytes()))); - - Object o = channel.readOutbound(); - assertThat(o, is(instanceOf(FullHttpResponse.class))); - FullHttpResponse r = (FullHttpResponse) o; - assertEquals(100, r.status().code()); - r.release(); - assertTrue(channel.writeInbound(Unpooled.copiedBuffer(GZ_HELLO_WORLD))); - - assertHasInboundMessages(channel, true); - assertHasOutboundMessages(channel, false); - assertFalse(channel.finish()); - } - - @Test - public void testExpectContinueResponse3() { - // request with header "Expect: 100-continue" must be replied with one "100 Continue" response - // case 3: ContentDecoder is in chain and content is encoded - HttpRequestDecoder decoder = new HttpRequestDecoder(); - HttpContentDecoder decompressor = new HttpContentDecompressor(); - HttpObjectAggregator aggregator = new HttpObjectAggregator(1024); - EmbeddedChannel channel = new EmbeddedChannel(decoder, decompressor, aggregator); - String req = "POST / HTTP/1.1\r\n" + - "Content-Length: " + GZ_HELLO_WORLD.length + "\r\n" + - "Expect: 100-continue\r\n" + - "Content-Encoding: gzip\r\n" + - "\r\n"; - assertFalse(channel.writeInbound(Unpooled.wrappedBuffer(req.getBytes()))); - - Object o = channel.readOutbound(); - assertThat(o, is(instanceOf(FullHttpResponse.class))); - FullHttpResponse r = (FullHttpResponse) o; - assertEquals(100, r.status().code()); - r.release(); - assertTrue(channel.writeInbound(Unpooled.copiedBuffer(GZ_HELLO_WORLD))); - - assertHasInboundMessages(channel, true); - assertHasOutboundMessages(channel, false); - assertFalse(channel.finish()); - } - - @Test - public void testExpectContinueResponse4() { - // request with header "Expect: 100-continue" must be replied with one "100 Continue" response - // case 4: ObjectAggregator is up in chain - HttpRequestDecoder decoder = new HttpRequestDecoder(); - HttpObjectAggregator aggregator = new HttpObjectAggregator(1024); - HttpContentDecoder decompressor = new HttpContentDecompressor(); - EmbeddedChannel channel = new EmbeddedChannel(decoder, aggregator, decompressor); - String req = "POST / HTTP/1.1\r\n" + - "Content-Length: " + GZ_HELLO_WORLD.length + "\r\n" + - "Expect: 100-continue\r\n" + - "Content-Encoding: gzip\r\n" + - "\r\n"; - assertFalse(channel.writeInbound(Unpooled.wrappedBuffer(req.getBytes()))); - - Object o = channel.readOutbound(); - assertThat(o, is(instanceOf(FullHttpResponse.class))); - FullHttpResponse r = (FullHttpResponse) o; - assertEquals(100, r.status().code()); - r.release(); - assertTrue(channel.writeInbound(Unpooled.copiedBuffer(GZ_HELLO_WORLD))); - - assertHasInboundMessages(channel, true); - assertHasOutboundMessages(channel, false); - assertFalse(channel.finish()); - } - - @Test - public void testExpectContinueResetHttpObjectDecoder() { - // request with header "Expect: 100-continue" must be replied with one "100 Continue" response - // case 5: Test that HttpObjectDecoder correctly resets its internal state after a failed expectation. - HttpRequestDecoder decoder = new HttpRequestDecoder(); - final int maxBytes = 10; - HttpObjectAggregator aggregator = new HttpObjectAggregator(maxBytes); - final AtomicReference secondRequestRef = new AtomicReference<>(); - EmbeddedChannel channel = new EmbeddedChannel(decoder, aggregator, new ChannelHandler() { - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - if (msg instanceof FullHttpRequest) { - if (!secondRequestRef.compareAndSet(null, (FullHttpRequest) msg)) { - ((FullHttpRequest) msg).release(); - } - } else { - ReferenceCountUtil.release(msg); - } - } - }); - String req1 = "POST /1 HTTP/1.1\r\n" + - "Content-Length: " + (maxBytes + 1) + "\r\n" + - "Expect: 100-continue\r\n" + - "\r\n"; - assertFalse(channel.writeInbound(Unpooled.wrappedBuffer(req1.getBytes(CharsetUtil.US_ASCII)))); - - FullHttpResponse resp = channel.readOutbound(); - assertEquals(HttpStatusClass.CLIENT_ERROR, resp.status().codeClass()); - resp.release(); - - String req2 = "POST /2 HTTP/1.1\r\n" + - "Content-Length: " + maxBytes + "\r\n" + - "Expect: 100-continue\r\n" + - "\r\n"; - assertFalse(channel.writeInbound(Unpooled.wrappedBuffer(req2.getBytes(CharsetUtil.US_ASCII)))); - - resp = channel.readOutbound(); - assertEquals(100, resp.status().code()); - resp.release(); - - byte[] content = new byte[maxBytes]; - assertFalse(channel.writeInbound(Unpooled.wrappedBuffer(content))); - - FullHttpRequest req = secondRequestRef.get(); - assertNotNull(req); - assertEquals("/2", req.uri()); - assertEquals(10, req.content().readableBytes()); - req.release(); - - assertHasInboundMessages(channel, false); - assertHasOutboundMessages(channel, false); - assertFalse(channel.finish()); - } - - @Test - public void testRequestContentLength1() { - // case 1: test that ContentDecompressor either sets the correct Content-Length header - // or removes it completely (handlers down the chain must rely on LastHttpContent object) - - // force content to be in more than one chunk (5 bytes/chunk) - HttpRequestDecoder decoder = new HttpRequestDecoder(4096, 4096); - HttpContentDecoder decompressor = new HttpContentDecompressor(); - EmbeddedChannel channel = new EmbeddedChannel(decoder, decompressor); - String headers = "POST / HTTP/1.1\r\n" + - "Content-Length: " + GZ_HELLO_WORLD.length + "\r\n" + - "Content-Encoding: gzip\r\n" + - "\r\n"; - ByteBuf buf = Unpooled.copiedBuffer(headers.getBytes(CharsetUtil.US_ASCII), GZ_HELLO_WORLD); - assertTrue(channel.writeInbound(buf)); - - Queue req = channel.inboundMessages(); - assertTrue(req.size() >= 1); - Object o = req.peek(); - assertThat(o, is(instanceOf(HttpRequest.class))); - HttpRequest r = (HttpRequest) o; - String v = r.headers().get(HttpHeaderNames.CONTENT_LENGTH); - Long value = v == null ? null : Long.parseLong(v); - assertTrue(value == null || value.longValue() == HELLO_WORLD.length()); - - assertHasInboundMessages(channel, true); - assertHasOutboundMessages(channel, false); - assertFalse(channel.finish()); - } - - @Test - public void testRequestContentLength2() { - // case 2: if HttpObjectAggregator is down the chain, then correct Content-Length header must be set - - // force content to be in more than one chunk (5 bytes/chunk) - HttpRequestDecoder decoder = new HttpRequestDecoder(4096, 4096); - HttpContentDecoder decompressor = new HttpContentDecompressor(); - HttpObjectAggregator aggregator = new HttpObjectAggregator(1024); - EmbeddedChannel channel = new EmbeddedChannel(decoder, decompressor, aggregator); - String headers = "POST / HTTP/1.1\r\n" + - "Content-Length: " + GZ_HELLO_WORLD.length + "\r\n" + - "Content-Encoding: gzip\r\n" + - "\r\n"; - ByteBuf buf = Unpooled.copiedBuffer(headers.getBytes(CharsetUtil.US_ASCII), GZ_HELLO_WORLD); - assertTrue(channel.writeInbound(buf)); - - Object o = channel.readInbound(); - assertThat(o, is(instanceOf(FullHttpRequest.class))); - FullHttpRequest r = (FullHttpRequest) o; - String v = r.headers().get(HttpHeaderNames.CONTENT_LENGTH); - Long value = v == null ? null : Long.parseLong(v); - - r.release(); - assertNotNull(value); - assertEquals(HELLO_WORLD.length(), value.longValue()); - - assertHasInboundMessages(channel, false); - assertHasOutboundMessages(channel, false); - assertFalse(channel.finish()); - } - - @Test - public void testResponseContentLength1() { - // case 1: test that ContentDecompressor either sets the correct Content-Length header - // or removes it completely (handlers down the chain must rely on LastHttpContent object) - - // force content to be in more than one chunk (5 bytes/chunk) - HttpResponseDecoder decoder = new HttpResponseDecoder(4096, 4096); - HttpContentDecoder decompressor = new HttpContentDecompressor(); - EmbeddedChannel channel = new EmbeddedChannel(decoder, decompressor); - String headers = "HTTP/1.1 200 OK\r\n" + - "Content-Length: " + GZ_HELLO_WORLD.length + "\r\n" + - "Content-Encoding: gzip\r\n" + - "\r\n"; - ByteBuf buf = Unpooled.copiedBuffer(headers.getBytes(CharsetUtil.US_ASCII), GZ_HELLO_WORLD); - assertTrue(channel.writeInbound(buf)); - - Queue resp = channel.inboundMessages(); - assertTrue(resp.size() >= 1); - Object o = resp.peek(); - assertThat(o, is(instanceOf(HttpResponse.class))); - HttpResponse r = (HttpResponse) o; - - assertFalse(r.headers().contains(HttpHeaderNames.CONTENT_LENGTH), "Content-Length header not removed."); - - String transferEncoding = r.headers().get(HttpHeaderNames.TRANSFER_ENCODING); - assertNotNull(transferEncoding, "Content-length as well as transfer-encoding not set."); - assertEquals(HttpHeaderValues.CHUNKED.toString(), transferEncoding, "Unexpected transfer-encoding value."); - - assertHasInboundMessages(channel, true); - assertHasOutboundMessages(channel, false); - assertFalse(channel.finish()); - } - - @Test - public void testResponseContentLength2() { - // case 2: if HttpObjectAggregator is down the chain, then correct Content-Length header must be set - - // force content to be in more than one chunk (5 bytes/chunk) - HttpResponseDecoder decoder = new HttpResponseDecoder(4096, 4096); - HttpContentDecoder decompressor = new HttpContentDecompressor(); - HttpObjectAggregator aggregator = new HttpObjectAggregator(1024); - EmbeddedChannel channel = new EmbeddedChannel(decoder, decompressor, aggregator); - String headers = "HTTP/1.1 200 OK\r\n" + - "Content-Length: " + GZ_HELLO_WORLD.length + "\r\n" + - "Content-Encoding: gzip\r\n" + - "\r\n"; - ByteBuf buf = Unpooled.copiedBuffer(headers.getBytes(CharsetUtil.US_ASCII), GZ_HELLO_WORLD); - assertTrue(channel.writeInbound(buf)); - - Object o = channel.readInbound(); - assertThat(o, is(instanceOf(FullHttpResponse.class))); - FullHttpResponse r = (FullHttpResponse) o; - String v = r.headers().get(HttpHeaderNames.CONTENT_LENGTH); - Long value = v == null ? null : Long.parseLong(v); - assertNotNull(value); - assertEquals(HELLO_WORLD.length(), value.longValue()); - r.release(); - - assertHasInboundMessages(channel, false); - assertHasOutboundMessages(channel, false); - assertFalse(channel.finish()); - } - - @Test - public void testFullHttpRequest() { - // test that ContentDecoder can be used after the ObjectAggregator - HttpRequestDecoder decoder = new HttpRequestDecoder(4096, 4096); - HttpObjectAggregator aggregator = new HttpObjectAggregator(1024); - HttpContentDecoder decompressor = new HttpContentDecompressor(); - EmbeddedChannel channel = new EmbeddedChannel(decoder, aggregator, decompressor); - String headers = "POST / HTTP/1.1\r\n" + - "Content-Length: " + GZ_HELLO_WORLD.length + "\r\n" + - "Content-Encoding: gzip\r\n" + - "\r\n"; - assertTrue(channel.writeInbound(Unpooled.copiedBuffer(headers.getBytes(), GZ_HELLO_WORLD))); - - Queue req = channel.inboundMessages(); - assertTrue(req.size() > 1); - int contentLength = 0; - contentLength = calculateContentLength(req, contentLength); - - byte[] receivedContent = readContent(req, contentLength, true); - - assertEquals(HELLO_WORLD, new String(receivedContent, CharsetUtil.US_ASCII)); - - assertHasInboundMessages(channel, true); - assertHasOutboundMessages(channel, false); - assertFalse(channel.finish()); - } - - @Test - public void testFullHttpResponse() { - // test that ContentDecoder can be used after the ObjectAggregator - HttpResponseDecoder decoder = new HttpResponseDecoder(4096, 4096); - HttpObjectAggregator aggregator = new HttpObjectAggregator(1024); - HttpContentDecoder decompressor = new HttpContentDecompressor(); - EmbeddedChannel channel = new EmbeddedChannel(decoder, aggregator, decompressor); - String headers = "HTTP/1.1 200 OK\r\n" + - "Content-Length: " + GZ_HELLO_WORLD.length + "\r\n" + - "Content-Encoding: gzip\r\n" + - "\r\n"; - assertTrue(channel.writeInbound(Unpooled.copiedBuffer(headers.getBytes(), GZ_HELLO_WORLD))); - - Queue resp = channel.inboundMessages(); - assertTrue(resp.size() > 1); - int contentLength = 0; - contentLength = calculateContentLength(resp, contentLength); - - byte[] receivedContent = readContent(resp, contentLength, true); - - assertEquals(HELLO_WORLD, new String(receivedContent, CharsetUtil.US_ASCII)); - - assertHasInboundMessages(channel, true); - assertHasOutboundMessages(channel, false); - assertFalse(channel.finish()); - } - - // See https://github.com/netty/netty/issues/5892 - @Test - public void testFullHttpResponseEOF() { - // test that ContentDecoder can be used after the ObjectAggregator - HttpResponseDecoder decoder = new HttpResponseDecoder(4096, 4096); - HttpContentDecoder decompressor = new HttpContentDecompressor(); - EmbeddedChannel channel = new EmbeddedChannel(decoder, decompressor); - String headers = "HTTP/1.1 200 OK\r\n" + - "Content-Encoding: gzip\r\n" + - "\r\n"; - assertTrue(channel.writeInbound(Unpooled.copiedBuffer(headers.getBytes(), GZ_HELLO_WORLD))); - // This should terminate it. - assertTrue(channel.finish()); - - Queue resp = channel.inboundMessages(); - assertTrue(resp.size() > 1); - int contentLength = 0; - contentLength = calculateContentLength(resp, contentLength); - - byte[] receivedContent = readContent(resp, contentLength, false); - - assertEquals(HELLO_WORLD, new String(receivedContent, CharsetUtil.US_ASCII)); - - assertHasInboundMessages(channel, true); - assertHasOutboundMessages(channel, false); - assertFalse(channel.finish()); - } - - @Test - public void testCleanupThrows() { - HttpContentDecoder decoder = new HttpContentDecoder() { - @Override - protected EmbeddedChannel newContentDecoder(String contentEncoding) throws Exception { - return new EmbeddedChannel(new ChannelHandler() { - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - ctx.fireExceptionCaught(new DecoderException()); - ctx.fireChannelInactive(); - } - }); - } - }; - - final AtomicBoolean channelInactiveCalled = new AtomicBoolean(); - EmbeddedChannel channel = new EmbeddedChannel(decoder, new ChannelHandler() { - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - assertTrue(channelInactiveCalled.compareAndSet(false, true)); - ctx.fireChannelInactive(); - } - }); - assertTrue(channel.writeInbound(new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/"))); - HttpContent content = new DefaultHttpContent(Unpooled.buffer().writeZero(10)); - assertTrue(channel.writeInbound(content)); - assertEquals(1, content.refCnt()); - try { - channel.finishAndReleaseAll(); - fail(); - } catch (CodecException expected) { - // expected - } - assertTrue(channelInactiveCalled.get()); - assertEquals(0, content.refCnt()); - } - - @Test - public void testTransferCodingGZIP() { - String requestStr = "POST / HTTP/1.1\r\n" + - "Content-Length: " + GZ_HELLO_WORLD.length + "\r\n" + - "Transfer-Encoding: gzip\r\n" + - "\r\n"; - HttpRequestDecoder decoder = new HttpRequestDecoder(); - HttpContentDecoder decompressor = new HttpContentDecompressor(); - EmbeddedChannel channel = new EmbeddedChannel(decoder, decompressor); - - channel.writeInbound(Unpooled.copiedBuffer(requestStr.getBytes())); - channel.writeInbound(Unpooled.copiedBuffer(GZ_HELLO_WORLD)); - - HttpRequest request = channel.readInbound(); - assertTrue(request.decoderResult().isSuccess()); - assertFalse(request.headers().contains(HttpHeaderNames.CONTENT_LENGTH)); - - HttpContent content = channel.readInbound(); - assertTrue(content.decoderResult().isSuccess()); - assertEquals(HELLO_WORLD, content.content().toString(CharsetUtil.US_ASCII)); - content.release(); - - LastHttpContent lastHttpContent = channel.readInbound(); - assertTrue(lastHttpContent.decoderResult().isSuccess()); - lastHttpContent.release(); - - assertHasInboundMessages(channel, false); - assertHasOutboundMessages(channel, false); - assertFalse(channel.finish()); - channel.releaseInbound(); - } - - @Test - public void testTransferCodingGZIPAndChunked() { - String requestStr = "POST / HTTP/1.1\r\n" + - "Host: example.com\r\n" + - "Content-Type: application/x-www-form-urlencoded\r\n" + - "Trailer: My-Trailer\r\n" + - "Transfer-Encoding: gzip, chunked\r\n" + - "\r\n"; - HttpRequestDecoder decoder = new HttpRequestDecoder(); - HttpContentDecoder decompressor = new HttpContentDecompressor(); - EmbeddedChannel channel = new EmbeddedChannel(decoder, decompressor); - - assertTrue(channel.writeInbound(Unpooled.copiedBuffer(requestStr, 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", CharsetUtil.US_ASCII))); - - HttpRequest request = channel.readInbound(); - assertTrue(request.decoderResult().isSuccess()); - assertTrue(request.headers().containsValue(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED, true)); - assertFalse(request.headers().contains(HttpHeaderNames.CONTENT_LENGTH)); - - HttpContent chunk1 = channel.readInbound(); - assertTrue(chunk1.decoderResult().isSuccess()); - assertEquals(HELLO_WORLD, chunk1.content().toString(CharsetUtil.US_ASCII)); - chunk1.release(); - - LastHttpContent chunk2 = channel.readInbound(); - assertTrue(chunk2.decoderResult().isSuccess()); - assertEquals("42", chunk2.trailingHeaders().get("My-Trailer")); - chunk2.release(); - - assertFalse(channel.finish()); - channel.releaseInbound(); - } - - private static byte[] gzDecompress(byte[] input) { - ZlibDecoder decoder = ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP); - EmbeddedChannel channel = new EmbeddedChannel(decoder); - assertTrue(channel.writeInbound(Unpooled.copiedBuffer(input))); - assertTrue(channel.finish()); // close the channel to indicate end-of-data - - int outputSize = 0; - ByteBuf o; - List inbound = new ArrayList<>(); - while ((o = channel.readInbound()) != null) { - inbound.add(o); - outputSize += o.readableBytes(); - } - - byte[] output = new byte[outputSize]; - int readCount = 0; - for (ByteBuf b : inbound) { - int readableBytes = b.readableBytes(); - b.readBytes(output, readCount, readableBytes); - b.release(); - readCount += readableBytes; - } - assertTrue(channel.inboundMessages().isEmpty() && channel.outboundMessages().isEmpty()); - return output; - } - - private static byte[] readContent(Queue req, int contentLength, boolean hasTransferEncoding) { - byte[] receivedContent = new byte[contentLength]; - int readCount = 0; - for (Object o : req) { - if (o instanceof HttpContent) { - ByteBuf b = ((HttpContent) o).content(); - int readableBytes = b.readableBytes(); - b.readBytes(receivedContent, readCount, readableBytes); - readCount += readableBytes; - } - if (o instanceof HttpMessage) { - assertEquals(hasTransferEncoding, - ((HttpMessage) o).headers().contains(HttpHeaderNames.TRANSFER_ENCODING)); - } - } - return receivedContent; - } - - private static int calculateContentLength(Queue req, int contentLength) { - for (Object o : req) { - if (o instanceof HttpContent) { - assertTrue(((HttpContent) o).refCnt() > 0); - ByteBuf b = ((HttpContent) o).content(); - contentLength += b.readableBytes(); - } - } - return contentLength; - } - - private static byte[] gzCompress(byte[] input) { - ZlibEncoder encoder = ZlibCodecFactory.newZlibEncoder(ZlibWrapper.GZIP); - EmbeddedChannel channel = new EmbeddedChannel(encoder); - assertTrue(channel.writeOutbound(Unpooled.wrappedBuffer(input))); - assertTrue(channel.finish()); // close the channel to indicate end-of-data - - int outputSize = 0; - ByteBuf o; - List outbound = new ArrayList<>(); - while ((o = channel.readOutbound()) != null) { - outbound.add(o); - outputSize += o.readableBytes(); - } - - byte[] output = new byte[outputSize]; - int readCount = 0; - for (ByteBuf b : outbound) { - int readableBytes = b.readableBytes(); - b.readBytes(output, readCount, readableBytes); - b.release(); - readCount += readableBytes; - } - assertTrue(channel.inboundMessages().isEmpty() && channel.outboundMessages().isEmpty()); - return output; - } - - private static void assertHasInboundMessages(EmbeddedChannel channel, boolean hasMessages) { - Object o; - if (hasMessages) { - while (true) { - o = channel.readInbound(); - assertNotNull(o); - ReferenceCountUtil.release(o); - if (o instanceof LastHttpContent) { - break; - } - } - } else { - o = channel.readInbound(); - assertNull(o); - } - } - - private static void assertHasOutboundMessages(EmbeddedChannel channel, boolean hasMessages) { - Object o; - if (hasMessages) { - while (true) { - o = channel.readOutbound(); - assertNotNull(o); - ReferenceCountUtil.release(o); - if (o instanceof LastHttpContent) { - break; - } - } - } else { - o = channel.readOutbound(); - assertNull(o); - } - } - - static boolean isNotSupported() { - return PlatformDependent.isOsx() && "aarch_64".equals(PlatformDependent.normalizedArch()); - } -} 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 deleted file mode 100644 index 557ee17ba1..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpContentDecompressorTest.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.embedded.EmbeddedChannel; -import org.junit.jupiter.api.Test; - -import java.util.concurrent.atomic.AtomicInteger; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -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 ChannelHandler() { - @Override - public void read(ChannelHandlerContext ctx) { - readCalled.incrementAndGet(); - ctx.read(); - } - }, new HttpContentDecompressor(), new ChannelHandler() { - @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); - - assertTrue(channel.writeInbound(response)); - - // we triggered read explicitly - assertEquals(1, readCalled.get()); - - assertTrue(channel.readInbound() instanceof HttpResponse); - - 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. - assertEquals(2, readCalled.get()); - assertFalse(channel.finishAndReleaseAll()); - } -} 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 deleted file mode 100644 index bba88b5755..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpContentEncoderTest.java +++ /dev/null @@ -1,465 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.http; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandler; -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.EncoderException; -import io.netty.handler.codec.MessageToByteEncoder; -import io.netty.util.CharsetUtil; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; - -import java.util.concurrent.atomic.AtomicBoolean; - -import static io.netty.handler.codec.http.HttpHeadersTestUtils.of; -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.not; -import static org.hamcrest.CoreMatchers.nullValue; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class HttpContentEncoderTest { - - private static final class TestEncoder extends HttpContentEncoder { - @Override - protected Result beginEncode(HttpResponse httpResponse, String acceptEncoding) { - return new Result("test", new EmbeddedChannel(new MessageToByteEncoder() { - @Override - protected void encode(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) throws Exception { - out.writeBytes(String.valueOf(in.readableBytes()).getBytes(CharsetUtil.US_ASCII)); - in.skipBytes(in.readableBytes()); - } - })); - } - } - - @Test - public void testSplitContent() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new TestEncoder()); - ch.writeInbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/")); - - ch.writeOutbound(new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK)); - ch.writeOutbound(new DefaultHttpContent(Unpooled.wrappedBuffer(new byte[3]))); - ch.writeOutbound(new DefaultHttpContent(Unpooled.wrappedBuffer(new byte[2]))); - ch.writeOutbound(new DefaultLastHttpContent(Unpooled.wrappedBuffer(new byte[1]))); - - assertEncodedResponse(ch); - - HttpContent chunk; - chunk = ch.readOutbound(); - assertThat(chunk.content().toString(CharsetUtil.US_ASCII), is("3")); - chunk.release(); - - chunk = ch.readOutbound(); - assertThat(chunk.content().toString(CharsetUtil.US_ASCII), is("2")); - chunk.release(); - - chunk = ch.readOutbound(); - assertThat(chunk.content().toString(CharsetUtil.US_ASCII), is("1")); - chunk.release(); - - chunk = ch.readOutbound(); - assertThat(chunk.content().isReadable(), is(false)); - assertThat(chunk, is(instanceOf(LastHttpContent.class))); - chunk.release(); - - assertThat(ch.readOutbound(), is(nullValue())); - } - - @Test - public void testChunkedContent() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new TestEncoder()); - ch.writeInbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/")); - - HttpResponse res = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - res.headers().set(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED); - ch.writeOutbound(res); - - assertEncodedResponse(ch); - - ch.writeOutbound(new DefaultHttpContent(Unpooled.wrappedBuffer(new byte[3]))); - ch.writeOutbound(new DefaultHttpContent(Unpooled.wrappedBuffer(new byte[2]))); - ch.writeOutbound(new DefaultLastHttpContent(Unpooled.wrappedBuffer(new byte[1]))); - - HttpContent chunk; - chunk = ch.readOutbound(); - assertThat(chunk.content().toString(CharsetUtil.US_ASCII), is("3")); - chunk.release(); - - chunk = ch.readOutbound(); - assertThat(chunk.content().toString(CharsetUtil.US_ASCII), is("2")); - chunk.release(); - - chunk = ch.readOutbound(); - assertThat(chunk.content().toString(CharsetUtil.US_ASCII), is("1")); - assertThat(chunk, is(instanceOf(HttpContent.class))); - chunk.release(); - - chunk = ch.readOutbound(); - assertThat(chunk.content().isReadable(), is(false)); - assertThat(chunk, is(instanceOf(LastHttpContent.class))); - chunk.release(); - - assertThat(ch.readOutbound(), is(nullValue())); - } - - @Test - public void testChunkedContentWithTrailingHeader() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new TestEncoder()); - ch.writeInbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/")); - - HttpResponse res = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - res.headers().set(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED); - ch.writeOutbound(res); - - assertEncodedResponse(ch); - - ch.writeOutbound(new DefaultHttpContent(Unpooled.wrappedBuffer(new byte[3]))); - ch.writeOutbound(new DefaultHttpContent(Unpooled.wrappedBuffer(new byte[2]))); - LastHttpContent content = new DefaultLastHttpContent(Unpooled.wrappedBuffer(new byte[1])); - content.trailingHeaders().set(of("X-Test"), of("Netty")); - ch.writeOutbound(content); - - HttpContent chunk; - chunk = ch.readOutbound(); - assertThat(chunk.content().toString(CharsetUtil.US_ASCII), is("3")); - chunk.release(); - - chunk = ch.readOutbound(); - assertThat(chunk.content().toString(CharsetUtil.US_ASCII), is("2")); - chunk.release(); - - chunk = ch.readOutbound(); - assertThat(chunk.content().toString(CharsetUtil.US_ASCII), is("1")); - assertThat(chunk, is(instanceOf(HttpContent.class))); - chunk.release(); - - chunk = ch.readOutbound(); - 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())); - } - - @Test - public void testFullContentWithContentLength() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new TestEncoder()); - ch.writeInbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/")); - - FullHttpResponse fullRes = new DefaultFullHttpResponse( - HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(new byte[42])); - fullRes.headers().set(HttpHeaderNames.CONTENT_LENGTH, 42); - ch.writeOutbound(fullRes); - - HttpResponse res = ch.readOutbound(); - assertThat(res, is(not(instanceOf(HttpContent.class)))); - assertThat(res.headers().get(HttpHeaderNames.TRANSFER_ENCODING), is(nullValue())); - assertThat(res.headers().get(HttpHeaderNames.CONTENT_LENGTH), is("2")); - assertThat(res.headers().get(HttpHeaderNames.CONTENT_ENCODING), is("test")); - - HttpContent c = ch.readOutbound(); - assertThat(c.content().readableBytes(), is(2)); - assertThat(c.content().toString(CharsetUtil.US_ASCII), is("42")); - c.release(); - - LastHttpContent last = ch.readOutbound(); - assertThat(last.content().readableBytes(), is(0)); - last.release(); - - assertThat(ch.readOutbound(), is(nullValue())); - } - - @Test - public void testFullContent() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new TestEncoder()); - ch.writeInbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/")); - - FullHttpResponse res = new DefaultFullHttpResponse( - HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(new byte[42])); - ch.writeOutbound(res); - - assertEncodedResponse(ch); - HttpContent c = ch.readOutbound(); - assertThat(c.content().readableBytes(), is(2)); - assertThat(c.content().toString(CharsetUtil.US_ASCII), is("42")); - c.release(); - - LastHttpContent last = ch.readOutbound(); - assertThat(last.content().readableBytes(), is(0)); - last.release(); - - assertThat(ch.readOutbound(), is(nullValue())); - } - - /** - * If the length of the content is unknown, {@link HttpContentEncoder} should not skip encoding the content - * even if the actual length is turned out to be 0. - */ - @Test - public void testEmptySplitContent() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new TestEncoder()); - ch.writeInbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/")); - - ch.writeOutbound(new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK)); - assertEncodedResponse(ch); - - ch.writeOutbound(LastHttpContent.EMPTY_LAST_CONTENT); - HttpContent chunk = ch.readOutbound(); - assertThat(chunk.content().toString(CharsetUtil.US_ASCII), is("0")); - assertThat(chunk, is(instanceOf(HttpContent.class))); - chunk.release(); - - chunk = ch.readOutbound(); - assertThat(chunk.content().isReadable(), is(false)); - assertThat(chunk, is(instanceOf(LastHttpContent.class))); - chunk.release(); - - assertThat(ch.readOutbound(), is(nullValue())); - } - - /** - * If the length of the content is 0 for sure, {@link HttpContentEncoder} should skip encoding. - */ - @Test - public void testEmptyFullContent() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new TestEncoder()); - ch.writeInbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/")); - - FullHttpResponse res = new DefaultFullHttpResponse( - HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.EMPTY_BUFFER); - ch.writeOutbound(res); - - Object o = ch.readOutbound(); - assertThat(o, is(instanceOf(FullHttpResponse.class))); - - res = (FullHttpResponse) o; - assertThat(res.headers().get(HttpHeaderNames.TRANSFER_ENCODING), is(nullValue())); - - // Content encoding shouldn't be modified. - assertThat(res.headers().get(HttpHeaderNames.CONTENT_ENCODING), is(nullValue())); - assertThat(res.content().readableBytes(), is(0)); - assertThat(res.content().toString(CharsetUtil.US_ASCII), is("")); - res.release(); - - assertThat(ch.readOutbound(), is(nullValue())); - } - - @Test - public void testEmptyFullContentWithTrailer() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new TestEncoder()); - ch.writeInbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/")); - - FullHttpResponse res = new DefaultFullHttpResponse( - HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.EMPTY_BUFFER); - res.trailingHeaders().set(of("X-Test"), of("Netty")); - ch.writeOutbound(res); - - Object o = ch.readOutbound(); - assertThat(o, is(instanceOf(FullHttpResponse.class))); - - res = (FullHttpResponse) o; - assertThat(res.headers().get(HttpHeaderNames.TRANSFER_ENCODING), is(nullValue())); - - // Content encoding shouldn't be modified. - assertThat(res.headers().get(HttpHeaderNames.CONTENT_ENCODING), is(nullValue())); - 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())); - } - - @Test - public void testEmptyHeadResponse() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new TestEncoder()); - HttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.HEAD, "/"); - ch.writeInbound(req); - - HttpResponse res = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - res.headers().set(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED); - ch.writeOutbound(res); - ch.writeOutbound(LastHttpContent.EMPTY_LAST_CONTENT); - - assertEmptyResponse(ch); - } - - @Test - public void testHttp304Response() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new TestEncoder()); - HttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/"); - req.headers().set(HttpHeaderNames.ACCEPT_ENCODING, HttpHeaderValues.GZIP); - ch.writeInbound(req); - - HttpResponse res = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.NOT_MODIFIED); - res.headers().set(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED); - ch.writeOutbound(res); - ch.writeOutbound(LastHttpContent.EMPTY_LAST_CONTENT); - - assertEmptyResponse(ch); - } - - @Test - public void testConnect200Response() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new TestEncoder()); - HttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.CONNECT, "google.com:80"); - ch.writeInbound(req); - - HttpResponse res = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - res.headers().set(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED); - ch.writeOutbound(res); - ch.writeOutbound(LastHttpContent.EMPTY_LAST_CONTENT); - - assertEmptyResponse(ch); - } - - @Test - public void testConnectFailureResponse() throws Exception { - String content = "Not allowed by configuration"; - - EmbeddedChannel ch = new EmbeddedChannel(new TestEncoder()); - HttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.CONNECT, "google.com:80"); - ch.writeInbound(req); - - HttpResponse res = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.METHOD_NOT_ALLOWED); - res.headers().set(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED); - ch.writeOutbound(res); - ch.writeOutbound(new DefaultHttpContent(Unpooled.wrappedBuffer(content.getBytes(CharsetUtil.UTF_8)))); - ch.writeOutbound(LastHttpContent.EMPTY_LAST_CONTENT); - - assertEncodedResponse(ch); - Object o = ch.readOutbound(); - assertThat(o, is(instanceOf(HttpContent.class))); - HttpContent chunk = (HttpContent) o; - assertThat(chunk.content().toString(CharsetUtil.US_ASCII), is("28")); - chunk.release(); - - chunk = ch.readOutbound(); - assertThat(chunk.content().isReadable(), is(true)); - assertThat(chunk.content().toString(CharsetUtil.US_ASCII), is("0")); - chunk.release(); - - chunk = ch.readOutbound(); - assertThat(chunk, is(instanceOf(LastHttpContent.class))); - chunk.release(); - assertThat(ch.readOutbound(), is(nullValue())); - } - - @Test - public void testHttp1_0() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new TestEncoder()); - FullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_0, HttpMethod.GET, "/"); - assertTrue(ch.writeInbound(req)); - - HttpResponse res = new DefaultHttpResponse(HttpVersion.HTTP_1_0, HttpResponseStatus.OK); - res.headers().set(HttpHeaderNames.CONTENT_LENGTH, HttpHeaderValues.ZERO); - assertTrue(ch.writeOutbound(res)); - assertTrue(ch.writeOutbound(LastHttpContent.EMPTY_LAST_CONTENT)); - assertTrue(ch.finish()); - - FullHttpRequest request = ch.readInbound(); - assertTrue(request.release()); - assertNull(ch.readInbound()); - - HttpResponse response = ch.readOutbound(); - assertSame(res, response); - - LastHttpContent content = ch.readOutbound(); - assertSame(LastHttpContent.EMPTY_LAST_CONTENT, content); - content.release(); - assertNull(ch.readOutbound()); - } - - @Test - public void testCleanupThrows() { - HttpContentEncoder encoder = new HttpContentEncoder() { - @Override - protected Result beginEncode(HttpResponse httpResponse, String acceptEncoding) throws Exception { - return new Result("myencoding", new EmbeddedChannel( - new ChannelHandler() { - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - ctx.fireExceptionCaught(new EncoderException()); - ctx.fireChannelInactive(); - } - })); - } - }; - - final AtomicBoolean channelInactiveCalled = new AtomicBoolean(); - EmbeddedChannel channel = new EmbeddedChannel(encoder, new ChannelHandler() { - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - assertTrue(channelInactiveCalled.compareAndSet(false, true)); - ctx.fireChannelInactive(); - } - }); - assertTrue(channel.writeInbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/"))); - assertTrue(channel.writeOutbound(new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK))); - HttpContent content = new DefaultHttpContent(Unpooled.buffer().writeZero(10)); - assertTrue(channel.writeOutbound(content)); - assertEquals(1, content.refCnt()); - assertThrows(CodecException.class, new Executable() { - @Override - public void execute() { - channel.finishAndReleaseAll(); - } - }); - - assertTrue(channelInactiveCalled.get()); - assertEquals(0, content.refCnt()); - } - - private static void assertEmptyResponse(EmbeddedChannel ch) { - Object o = ch.readOutbound(); - assertThat(o, is(instanceOf(HttpResponse.class))); - - HttpResponse res = (HttpResponse) o; - assertThat(res, is(not(instanceOf(HttpContent.class)))); - assertThat(res.headers().get(HttpHeaderNames.TRANSFER_ENCODING), is("chunked")); - assertThat(res.headers().get(HttpHeaderNames.CONTENT_LENGTH), is(nullValue())); - - HttpContent chunk = ch.readOutbound(); - assertThat(chunk, is(instanceOf(LastHttpContent.class))); - chunk.release(); - assertThat(ch.readOutbound(), is(nullValue())); - } - - private static void assertEncodedResponse(EmbeddedChannel ch) { - Object o = ch.readOutbound(); - assertThat(o, is(instanceOf(HttpResponse.class))); - - HttpResponse res = (HttpResponse) o; - assertThat(res, is(not(instanceOf(HttpContent.class)))); - assertThat(res.headers().get(HttpHeaderNames.TRANSFER_ENCODING), is("chunked")); - assertThat(res.headers().get(HttpHeaderNames.CONTENT_LENGTH), is(nullValue())); - assertThat(res.headers().get(HttpHeaderNames.CONTENT_ENCODING), is("test")); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpHeadersTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpHeadersTest.java deleted file mode 100644 index e7134d5a2e..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpHeadersTest.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.util.AsciiString; -import org.junit.jupiter.api.Test; - -import java.util.List; - -import static io.netty.handler.codec.http.HttpHeadersTestUtils.of; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class HttpHeadersTest { - - @Test - public void testRemoveTransferEncodingIgnoreCase() { - HttpMessage message = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - message.headers().set(HttpHeaderNames.TRANSFER_ENCODING, "Chunked"); - assertFalse(message.headers().isEmpty()); - HttpUtil.setTransferEncodingChunked(message, false); - assertTrue(message.headers().isEmpty()); - } - - // Test for https://github.com/netty/netty/issues/1690 - @Test - public void testGetOperations() { - HttpHeaders headers = new DefaultHttpHeaders(); - headers.add(of("Foo"), of("1")); - headers.add(of("Foo"), of("2")); - - assertEquals("1", headers.get(of("Foo"))); - - List values = headers.getAll(of("Foo")); - assertEquals(2, values.size()); - assertEquals("1", values.get(0)); - assertEquals("2", values.get(1)); - } - - @Test - public void testEqualsIgnoreCase() { - assertThat(AsciiString.contentEqualsIgnoreCase(null, null), is(true)); - assertThat(AsciiString.contentEqualsIgnoreCase(null, "foo"), is(false)); - assertThat(AsciiString.contentEqualsIgnoreCase("bar", null), is(false)); - assertThat(AsciiString.contentEqualsIgnoreCase("FoO", "fOo"), is(true)); - } - - @Test - public void testSetNullHeaderValueValidate() { - HttpHeaders headers = new DefaultHttpHeaders(true); - assertThrows(NullPointerException.class, () -> headers.set(of("test"), (CharSequence) null)); - } - - @Test - public void testSetNullHeaderValueNotValidate() { - HttpHeaders headers = new DefaultHttpHeaders(false); - assertThrows(NullPointerException.class, () -> headers.set(of("test"), (CharSequence) null)); - } - - @Test - public void testAddSelf() { - HttpHeaders headers = new DefaultHttpHeaders(false); - assertThrows(IllegalArgumentException.class, () -> headers.add(headers)); - } - - @Test - public void testSetSelfIsNoOp() { - HttpHeaders headers = new DefaultHttpHeaders(false); - headers.add("name", "value"); - headers.set(headers); - assertEquals(1, headers.size()); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpHeadersTestUtils.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpHeadersTestUtils.java deleted file mode 100644 index 84e614fef2..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpHeadersTestUtils.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static io.netty.util.internal.StringUtil.COMMA; -import static io.netty.util.internal.StringUtil.DOUBLE_QUOTE; - -/** - * Utility methods for {@link HttpHeaders} related unit tests. - */ -public final class HttpHeadersTestUtils { - enum HeaderValue { - UNKNOWN("Unknown", 0), - ONE("One", 1), - TWO("Two", 2), - THREE("Three", 3), - FOUR("Four", 4), - FIVE("Five", 5), - SIX_QUOTED("Six,", 6), - SEVEN_QUOTED("Seven; , GMT", 7), - EIGHT("Eight", 8); - - private final int nr; - private final String value; - private List array; - - HeaderValue(final String value, final int nr) { - this.nr = nr; - this.value = value; - } - - @Override - public String toString() { - return value; - } - - public List asList() { - if (array == null) { - List list = new ArrayList<>(nr); - for (int i = 1; i <= nr; i++) { - list.add(of(i).toString()); - } - array = list; - } - return array; - } - - public List subset(int from) { - assert from > 0; - --from; - final int size = nr - from; - final int end = from + size; - List list = new ArrayList<>(size); - List fullList = asList(); - for (int i = from; i < end; ++i) { - list.add(fullList.get(i)); - } - return list; - } - - public String subsetAsCsvString(final int from) { - final List subset = subset(from); - return asCsv(subset); - } - - public String asCsv(final List arr) { - if (arr == null || arr.isEmpty()) { - return ""; - } - final StringBuilder sb = new StringBuilder(arr.size() * 10); - final int end = arr.size() - 1; - for (int i = 0; i < end; ++i) { - quoted(sb, arr.get(i)).append(COMMA); - } - quoted(sb, arr.get(end)); - return sb.toString(); - } - - public CharSequence asCsv() { - return asCsv(asList()); - } - - private static StringBuilder quoted(final StringBuilder sb, final CharSequence value) { - if (contains(value, COMMA) && !contains(value, DOUBLE_QUOTE)) { - return sb.append(DOUBLE_QUOTE).append(value).append(DOUBLE_QUOTE); - } - return sb.append(value); - } - - private static boolean contains(CharSequence value, char c) { - for (int i = 0; i < value.length(); ++i) { - if (value.charAt(i) == c) { - return true; - } - } - return false; - } - - private static final Map MAP; - - static { - final Map map = new HashMap<>(); - for (HeaderValue v : values()) { - final int nr = v.nr; - map.put(Integer.valueOf(nr), v); - } - MAP = map; - } - - public static HeaderValue of(final int nr) { - final HeaderValue v = MAP.get(Integer.valueOf(nr)); - return v == null ? UNKNOWN : v; - } - } - - public static CharSequence of(String s) { - return s; - } - - private HttpHeadersTestUtils() { } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpInvalidMessageTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpInvalidMessageTest.java deleted file mode 100644 index d5e96eefa2..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpInvalidMessageTest.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.DecoderResult; -import io.netty.util.CharsetUtil; -import org.junit.jupiter.api.Test; - -import java.util.Random; - -import static io.netty.handler.codec.http.HttpHeadersTestUtils.of; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class HttpInvalidMessageTest { - - private final Random rnd = new Random(); - - @Test - public void testRequestWithBadInitialLine() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new HttpRequestDecoder()); - ch.writeInbound(Unpooled.copiedBuffer("GET / HTTP/1.0 with extra\r\n", CharsetUtil.UTF_8)); - HttpRequest req = ch.readInbound(); - DecoderResult dr = req.decoderResult(); - assertFalse(dr.isSuccess()); - assertTrue(dr.isFailure()); - ensureInboundTrafficDiscarded(ch); - } - - @Test - public void testRequestWithBadHeader() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new HttpRequestDecoder()); - ch.writeInbound(Unpooled.copiedBuffer("GET /maybe-something HTTP/1.0\r\n", CharsetUtil.UTF_8)); - ch.writeInbound(Unpooled.copiedBuffer("Good_Name: Good Value\r\n", CharsetUtil.UTF_8)); - ch.writeInbound(Unpooled.copiedBuffer("Bad=Name: Bad Value\r\n", CharsetUtil.UTF_8)); - ch.writeInbound(Unpooled.copiedBuffer("\r\n", CharsetUtil.UTF_8)); - HttpRequest req = ch.readInbound(); - DecoderResult dr = req.decoderResult(); - assertFalse(dr.isSuccess()); - assertTrue(dr.isFailure()); - assertEquals("Good Value", req.headers().get(of("Good_Name"))); - assertEquals("/maybe-something", req.uri()); - ensureInboundTrafficDiscarded(ch); - } - - @Test - public void testResponseWithBadInitialLine() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new HttpResponseDecoder()); - ch.writeInbound(Unpooled.copiedBuffer("HTTP/1.0 BAD_CODE Bad Server\r\n", CharsetUtil.UTF_8)); - HttpResponse res = ch.readInbound(); - DecoderResult dr = res.decoderResult(); - assertFalse(dr.isSuccess()); - assertTrue(dr.isFailure()); - ensureInboundTrafficDiscarded(ch); - } - - @Test - public void testResponseWithBadHeader() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new HttpResponseDecoder()); - ch.writeInbound(Unpooled.copiedBuffer("HTTP/1.0 200 Maybe OK\r\n", CharsetUtil.UTF_8)); - ch.writeInbound(Unpooled.copiedBuffer("Good_Name: Good Value\r\n", CharsetUtil.UTF_8)); - ch.writeInbound(Unpooled.copiedBuffer("Bad=Name: Bad Value\r\n", CharsetUtil.UTF_8)); - ch.writeInbound(Unpooled.copiedBuffer("\r\n", CharsetUtil.UTF_8)); - HttpResponse res = ch.readInbound(); - DecoderResult dr = res.decoderResult(); - assertFalse(dr.isSuccess()); - assertTrue(dr.isFailure()); - assertEquals("Maybe OK", res.status().reasonPhrase()); - assertEquals("Good Value", res.headers().get(of("Good_Name"))); - ensureInboundTrafficDiscarded(ch); - } - - @Test - public void testBadChunk() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new HttpRequestDecoder()); - ch.writeInbound(Unpooled.copiedBuffer("GET / HTTP/1.0\r\n", CharsetUtil.UTF_8)); - ch.writeInbound(Unpooled.copiedBuffer("Transfer-Encoding: chunked\r\n\r\n", CharsetUtil.UTF_8)); - ch.writeInbound(Unpooled.copiedBuffer("BAD_LENGTH\r\n", CharsetUtil.UTF_8)); - - HttpRequest req = ch.readInbound(); - assertTrue(req.decoderResult().isSuccess()); - - LastHttpContent chunk = ch.readInbound(); - DecoderResult dr = chunk.decoderResult(); - assertFalse(dr.isSuccess()); - assertTrue(dr.isFailure()); - ensureInboundTrafficDiscarded(ch); - } - - private void ensureInboundTrafficDiscarded(EmbeddedChannel ch) { - // Generate a lot of random traffic to ensure that it's discarded silently. - byte[] data = new byte[1048576]; - rnd.nextBytes(data); - - ByteBuf buf = Unpooled.wrappedBuffer(data); - for (int i = 0; i < 4096; i ++) { - buf.setIndex(0, data.length); - ch.writeInbound(buf.retain()); - ch.checkException(); - assertNull(ch.readInbound()); - } - buf.release(); - } -} 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 deleted file mode 100644 index 085effd2f6..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpObjectAggregatorTest.java +++ /dev/null @@ -1,748 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.CompositeByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.embedded.EmbeddedChannel; -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.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; -import org.mockito.Mockito; - -import java.nio.channels.ClosedChannelException; -import java.util.List; - -import static io.netty.handler.codec.http.HttpHeadersTestUtils.of; -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class HttpObjectAggregatorTest { - - @Test - public void testAggregate() { - HttpObjectAggregator aggr = new HttpObjectAggregator(1024 * 1024); - EmbeddedChannel embedder = new EmbeddedChannel(aggr); - - HttpRequest message = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost"); - message.headers().set(of("X-Test"), true); - HttpContent chunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII)); - HttpContent chunk2 = new DefaultHttpContent(Unpooled.copiedBuffer("test2", CharsetUtil.US_ASCII)); - HttpContent chunk3 = new DefaultLastHttpContent(Unpooled.EMPTY_BUFFER); - assertFalse(embedder.writeInbound(message)); - assertFalse(embedder.writeInbound(chunk1)); - assertFalse(embedder.writeInbound(chunk2)); - - // this should trigger a channelRead event so return true - assertTrue(embedder.writeInbound(chunk3)); - assertTrue(embedder.finish()); - FullHttpRequest aggregatedMessage = embedder.readInbound(); - assertNotNull(aggregatedMessage); - - assertEquals(chunk1.content().readableBytes() + chunk2.content().readableBytes(), - HttpUtil.getContentLength(aggregatedMessage)); - assertEquals(Boolean.TRUE.toString(), aggregatedMessage.headers().get(of("X-Test"))); - checkContentBuffer(aggregatedMessage); - assertNull(embedder.readInbound()); - } - - private static void checkContentBuffer(FullHttpRequest aggregatedMessage) { - CompositeByteBuf buffer = (CompositeByteBuf) aggregatedMessage.content(); - assertEquals(2, buffer.numComponents()); - List buffers = buffer.decompose(0, buffer.capacity()); - assertEquals(2, buffers.size()); - for (ByteBuf buf: buffers) { - // This should be false as we decompose the buffer before to not have deep hierarchy - assertFalse(buf instanceof CompositeByteBuf); - } - aggregatedMessage.release(); - } - - @Test - public void testAggregateWithTrailer() { - HttpObjectAggregator aggr = new HttpObjectAggregator(1024 * 1024); - EmbeddedChannel embedder = new EmbeddedChannel(aggr); - HttpRequest message = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost"); - message.headers().set(of("X-Test"), true); - HttpUtil.setTransferEncodingChunked(message, true); - HttpContent chunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII)); - HttpContent chunk2 = new DefaultHttpContent(Unpooled.copiedBuffer("test2", CharsetUtil.US_ASCII)); - LastHttpContent trailer = new DefaultLastHttpContent(); - trailer.trailingHeaders().set(of("X-Trailer"), true); - - assertFalse(embedder.writeInbound(message)); - assertFalse(embedder.writeInbound(chunk1)); - assertFalse(embedder.writeInbound(chunk2)); - - // this should trigger a channelRead event so return true - assertTrue(embedder.writeInbound(trailer)); - assertTrue(embedder.finish()); - FullHttpRequest aggregatedMessage = embedder.readInbound(); - assertNotNull(aggregatedMessage); - - assertEquals(chunk1.content().readableBytes() + chunk2.content().readableBytes(), - HttpUtil.getContentLength(aggregatedMessage)); - assertEquals(Boolean.TRUE.toString(), aggregatedMessage.headers().get(of("X-Test"))); - assertEquals(Boolean.TRUE.toString(), aggregatedMessage.trailingHeaders().get(of("X-Trailer"))); - checkContentBuffer(aggregatedMessage); - assertNull(embedder.readInbound()); - } - - @Test - public void testOversizedRequest() { - final EmbeddedChannel embedder = new EmbeddedChannel(new HttpObjectAggregator(4)); - HttpRequest message = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.PUT, "http://localhost"); - HttpContent chunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII)); - HttpContent chunk2 = new DefaultHttpContent(Unpooled.copiedBuffer("test2", CharsetUtil.US_ASCII)); - final HttpContent chunk3 = LastHttpContent.EMPTY_LAST_CONTENT; - - assertFalse(embedder.writeInbound(message)); - assertFalse(embedder.writeInbound(chunk1)); - assertFalse(embedder.writeInbound(chunk2)); - - FullHttpResponse response = embedder.readOutbound(); - assertEquals(HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE, response.status()); - assertEquals("0", response.headers().get(HttpHeaderNames.CONTENT_LENGTH)); - assertFalse(embedder.isOpen()); - - assertThrows(ClosedChannelException.class, new Executable() { - @Override - public void execute() { - embedder.writeInbound(chunk3); - } - }); - - assertFalse(embedder.finish()); - } - - @Test - public void testOversizedRequestWithContentLengthAndDecoder() { - EmbeddedChannel embedder = new EmbeddedChannel(new HttpRequestDecoder(), new HttpObjectAggregator(4, false)); - assertFalse(embedder.writeInbound(Unpooled.copiedBuffer( - "PUT /upload HTTP/1.1\r\n" + - "Content-Length: 5\r\n\r\n", CharsetUtil.US_ASCII))); - - assertNull(embedder.readInbound()); - - FullHttpResponse response = embedder.readOutbound(); - assertEquals(HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE, response.status()); - assertEquals("0", response.headers().get(HttpHeaderNames.CONTENT_LENGTH)); - - assertTrue(embedder.isOpen()); - - assertFalse(embedder.writeInbound(Unpooled.wrappedBuffer(new byte[] { 1, 2, 3, 4 }))); - assertFalse(embedder.writeInbound(Unpooled.wrappedBuffer(new byte[] { 5 }))); - - assertNull(embedder.readOutbound()); - - assertFalse(embedder.writeInbound(Unpooled.copiedBuffer( - "PUT /upload HTTP/1.1\r\n" + - "Content-Length: 2\r\n\r\n", CharsetUtil.US_ASCII))); - - assertEquals(HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE, response.status()); - assertEquals("0", response.headers().get(HttpHeaderNames.CONTENT_LENGTH)); - - assertThat(response, instanceOf(LastHttpContent.class)); - ReferenceCountUtil.release(response); - - assertTrue(embedder.isOpen()); - - assertFalse(embedder.writeInbound(Unpooled.copiedBuffer(new byte[] { 1 }))); - assertNull(embedder.readOutbound()); - assertTrue(embedder.writeInbound(Unpooled.copiedBuffer(new byte[] { 2 }))); - assertNull(embedder.readOutbound()); - - FullHttpRequest request = embedder.readInbound(); - assertEquals(HttpVersion.HTTP_1_1, request.protocolVersion()); - assertEquals(HttpMethod.PUT, request.method()); - assertEquals("/upload", request.uri()); - assertEquals(2, HttpUtil.getContentLength(request)); - - byte[] actual = new byte[request.content().readableBytes()]; - request.content().readBytes(actual); - assertArrayEquals(new byte[] { 1, 2 }, actual); - request.release(); - - assertFalse(embedder.finish()); - } - - @Test - public void testOversizedRequestWithoutKeepAlive() { - // send an HTTP/1.0 request with no keep-alive header - HttpRequest message = new DefaultHttpRequest(HttpVersion.HTTP_1_0, HttpMethod.PUT, "http://localhost"); - HttpUtil.setContentLength(message, 5); - checkOversizedRequest(message); - } - - @Test - public void testOversizedRequestWithContentLength() { - HttpRequest message = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.PUT, "http://localhost"); - HttpUtil.setContentLength(message, 5); - checkOversizedRequest(message); - } - - private static void checkOversizedRequest(HttpRequest message) { - final EmbeddedChannel embedder = new EmbeddedChannel(new HttpObjectAggregator(4)); - - assertFalse(embedder.writeInbound(message)); - HttpResponse response = embedder.readOutbound(); - assertEquals(HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE, response.status()); - assertEquals("0", response.headers().get(HttpHeaderNames.CONTENT_LENGTH)); - - assertThat(response, instanceOf(LastHttpContent.class)); - ReferenceCountUtil.release(response); - - if (serverShouldCloseConnection(message, response)) { - assertFalse(embedder.isOpen()); - - assertThrows(ClosedChannelException.class, new Executable() { - @Override - public void execute() { - embedder.writeInbound(new DefaultHttpContent(Unpooled.EMPTY_BUFFER)); - } - }); - - assertFalse(embedder.finish()); - } else { - assertTrue(embedder.isOpen()); - assertFalse(embedder.writeInbound(new DefaultHttpContent(Unpooled.copiedBuffer(new byte[8])))); - assertFalse(embedder.writeInbound(new DefaultHttpContent(Unpooled.copiedBuffer(new byte[8])))); - - // Now start a new message and ensure we will not reject it again. - HttpRequest message2 = new DefaultHttpRequest(HttpVersion.HTTP_1_0, HttpMethod.PUT, "http://localhost"); - HttpUtil.setContentLength(message, 2); - - assertFalse(embedder.writeInbound(message2)); - assertNull(embedder.readOutbound()); - assertFalse(embedder.writeInbound(new DefaultHttpContent(Unpooled.copiedBuffer(new byte[] { 1 })))); - assertNull(embedder.readOutbound()); - assertTrue(embedder.writeInbound(new DefaultLastHttpContent(Unpooled.copiedBuffer(new byte[] { 2 })))); - assertNull(embedder.readOutbound()); - - FullHttpRequest request = embedder.readInbound(); - assertEquals(message2.protocolVersion(), request.protocolVersion()); - assertEquals(message2.method(), request.method()); - assertEquals(message2.uri(), request.uri()); - assertEquals(2, HttpUtil.getContentLength(request)); - - byte[] actual = new byte[request.content().readableBytes()]; - request.content().readBytes(actual); - assertArrayEquals(new byte[] { 1, 2 }, actual); - request.release(); - - assertFalse(embedder.finish()); - } - } - - private static boolean serverShouldCloseConnection(HttpRequest message, HttpResponse response) { - // If the response wasn't keep-alive, the server should close the connection. - if (!HttpUtil.isKeepAlive(response)) { - return true; - } - // The connection should only be kept open if Expect: 100-continue is set, - // or if keep-alive is on. - if (HttpUtil.is100ContinueExpected(message)) { - return false; - } - if (HttpUtil.isKeepAlive(message)) { - return false; - } - return true; - } - - @Test - public void testOversizedResponse() { - final EmbeddedChannel embedder = new EmbeddedChannel(new HttpObjectAggregator(4)); - HttpResponse message = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - HttpContent chunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII)); - final HttpContent chunk2 = new DefaultHttpContent(Unpooled.copiedBuffer("test2", CharsetUtil.US_ASCII)); - - assertFalse(embedder.writeInbound(message)); - assertFalse(embedder.writeInbound(chunk1)); - - assertThrows(TooLongFrameException.class, new Executable() { - @Override - public void execute() { - embedder.writeInbound(chunk2); - } - }); - - assertFalse(embedder.isOpen()); - assertFalse(embedder.finish()); - } - - @Test - public void testInvalidConstructorUsage() { - assertThrows(IllegalArgumentException.class, new Executable() { - @Override - public void execute() { - new HttpObjectAggregator(-1); - } - }); - } - - @Test - public void testInvalidMaxCumulationBufferComponents() { - final HttpObjectAggregator aggr = new HttpObjectAggregator(Integer.MAX_VALUE); - assertThrows(IllegalArgumentException.class, new Executable() { - @Override - public void execute() { - aggr.setMaxCumulationBufferComponents(1); - } - }); - } - - @Test - public void testSetMaxCumulationBufferComponentsAfterInit() throws Exception { - final HttpObjectAggregator aggr = new HttpObjectAggregator(Integer.MAX_VALUE); - ChannelHandlerContext ctx = Mockito.mock(ChannelHandlerContext.class); - aggr.handlerAdded(ctx); - Mockito.verifyNoMoreInteractions(ctx); - assertThrows(IllegalStateException.class, new Executable() { - @Override - public void execute() { - aggr.setMaxCumulationBufferComponents(10); - } - }); - } - - @Test - public void testAggregateTransferEncodingChunked() { - HttpObjectAggregator aggr = new HttpObjectAggregator(1024 * 1024); - EmbeddedChannel embedder = new EmbeddedChannel(aggr); - - HttpRequest message = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.PUT, "http://localhost"); - message.headers().set(of("X-Test"), true); - message.headers().set(of("Transfer-Encoding"), of("Chunked")); - HttpContent chunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII)); - HttpContent chunk2 = new DefaultHttpContent(Unpooled.copiedBuffer("test2", CharsetUtil.US_ASCII)); - HttpContent chunk3 = LastHttpContent.EMPTY_LAST_CONTENT; - assertFalse(embedder.writeInbound(message)); - assertFalse(embedder.writeInbound(chunk1)); - assertFalse(embedder.writeInbound(chunk2)); - - // this should trigger a channelRead event so return true - assertTrue(embedder.writeInbound(chunk3)); - assertTrue(embedder.finish()); - FullHttpRequest aggregatedMessage = embedder.readInbound(); - assertNotNull(aggregatedMessage); - - assertEquals(chunk1.content().readableBytes() + chunk2.content().readableBytes(), - HttpUtil.getContentLength(aggregatedMessage)); - assertEquals(Boolean.TRUE.toString(), aggregatedMessage.headers().get(of("X-Test"))); - checkContentBuffer(aggregatedMessage); - assertNull(embedder.readInbound()); - } - - @Test - public void testBadRequest() { - EmbeddedChannel ch = new EmbeddedChannel(new HttpRequestDecoder(), new HttpObjectAggregator(1024 * 1024)); - ch.writeInbound(Unpooled.copiedBuffer("GET / HTTP/1.0 with extra\r\n", CharsetUtil.UTF_8)); - Object inbound = ch.readInbound(); - assertThat(inbound, is(instanceOf(FullHttpRequest.class))); - assertTrue(((DecoderResultProvider) inbound).decoderResult().isFailure()); - assertNull(ch.readInbound()); - ch.finish(); - } - - @Test - public void testBadResponse() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new HttpResponseDecoder(), new HttpObjectAggregator(1024 * 1024)); - ch.writeInbound(Unpooled.copiedBuffer("HTTP/1.0 BAD_CODE Bad Server\r\n", CharsetUtil.UTF_8)); - Object inbound = ch.readInbound(); - assertThat(inbound, is(instanceOf(FullHttpResponse.class))); - assertTrue(((DecoderResultProvider) inbound).decoderResult().isFailure()); - assertNull(ch.readInbound()); - ch.finish(); - } - - @Test - public void testOversizedRequestWith100Continue() { - EmbeddedChannel embedder = new EmbeddedChannel(new HttpObjectAggregator(8)); - - // Send an oversized request with 100 continue. - HttpRequest message = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.PUT, "http://localhost"); - HttpUtil.set100ContinueExpected(message, true); - HttpUtil.setContentLength(message, 16); - - HttpContent chunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("some", CharsetUtil.US_ASCII)); - HttpContent chunk2 = new DefaultHttpContent(Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII)); - HttpContent chunk3 = LastHttpContent.EMPTY_LAST_CONTENT; - - // Send a request with 100-continue + large Content-Length header value. - assertFalse(embedder.writeInbound(message)); - - // The aggregator should respond with '413.' - FullHttpResponse response = embedder.readOutbound(); - assertEquals(HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE, response.status()); - assertEquals("0", response.headers().get(HttpHeaderNames.CONTENT_LENGTH)); - - // An ill-behaving client could continue to send data without a respect, and such data should be discarded. - assertFalse(embedder.writeInbound(chunk1)); - - // The aggregator should not close the connection because keep-alive is on. - assertTrue(embedder.isOpen()); - - // Now send a valid request. - HttpRequest message2 = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.PUT, "http://localhost"); - - assertFalse(embedder.writeInbound(message2)); - assertFalse(embedder.writeInbound(chunk2)); - assertTrue(embedder.writeInbound(chunk3)); - - FullHttpRequest fullMsg = embedder.readInbound(); - assertNotNull(fullMsg); - - assertEquals( - chunk2.content().readableBytes() + chunk3.content().readableBytes(), - HttpUtil.getContentLength(fullMsg)); - - assertEquals(HttpUtil.getContentLength(fullMsg), fullMsg.content().readableBytes()); - - fullMsg.release(); - assertFalse(embedder.finish()); - } - - @Test - public void testUnsupportedExpectHeaderExpectation() { - runUnsupportedExceptHeaderExceptionTest(true); - runUnsupportedExceptHeaderExceptionTest(false); - } - - private static void runUnsupportedExceptHeaderExceptionTest(final boolean close) { - final HttpObjectAggregator aggregator; - final int maxContentLength = 4; - if (close) { - aggregator = new HttpObjectAggregator(maxContentLength, true); - } else { - aggregator = new HttpObjectAggregator(maxContentLength); - } - final EmbeddedChannel embedder = new EmbeddedChannel(new HttpRequestDecoder(), aggregator); - - assertFalse(embedder.writeInbound(Unpooled.copiedBuffer( - "GET / HTTP/1.1\r\n" + - "Expect: chocolate=yummy\r\n" + - "Content-Length: 100\r\n\r\n", CharsetUtil.US_ASCII))); - assertNull(embedder.readInbound()); - - final FullHttpResponse response = embedder.readOutbound(); - assertEquals(HttpResponseStatus.EXPECTATION_FAILED, response.status()); - assertEquals("0", response.headers().get(HttpHeaderNames.CONTENT_LENGTH)); - response.release(); - - if (close) { - assertFalse(embedder.isOpen()); - } else { - // keep-alive is on by default in HTTP/1.1, so the connection should be still alive - assertTrue(embedder.isOpen()); - - // the decoder should be reset by the aggregator at this point and be able to decode the next request - assertTrue(embedder.writeInbound(Unpooled.copiedBuffer("GET / HTTP/1.1\r\n\r\n", CharsetUtil.US_ASCII))); - - final FullHttpRequest request = embedder.readInbound(); - assertThat(request.method(), is(HttpMethod.GET)); - assertThat(request.uri(), is("/")); - assertThat(request.content().readableBytes(), is(0)); - request.release(); - } - - assertFalse(embedder.finish()); - } - - @Test - public void testValidRequestWith100ContinueAndDecoder() { - EmbeddedChannel embedder = new EmbeddedChannel(new HttpRequestDecoder(), new HttpObjectAggregator(100)); - embedder.writeInbound(Unpooled.copiedBuffer( - "GET /upload HTTP/1.1\r\n" + - "Expect: 100-continue\r\n" + - "Content-Length: 0\r\n\r\n", CharsetUtil.US_ASCII)); - - FullHttpResponse response = embedder.readOutbound(); - assertEquals(HttpResponseStatus.CONTINUE, response.status()); - FullHttpRequest request = embedder.readInbound(); - assertFalse(request.headers().contains(HttpHeaderNames.EXPECT)); - request.release(); - response.release(); - assertFalse(embedder.finish()); - } - - @Test - public void testOversizedRequestWith100ContinueAndDecoder() { - EmbeddedChannel embedder = new EmbeddedChannel(new HttpRequestDecoder(), new HttpObjectAggregator(4)); - embedder.writeInbound(Unpooled.copiedBuffer( - "PUT /upload HTTP/1.1\r\n" + - "Expect: 100-continue\r\n" + - "Content-Length: 100\r\n\r\n", CharsetUtil.US_ASCII)); - - assertNull(embedder.readInbound()); - - FullHttpResponse response = embedder.readOutbound(); - assertEquals(HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE, response.status()); - assertEquals("0", response.headers().get(HttpHeaderNames.CONTENT_LENGTH)); - - // Keep-alive is on by default in HTTP/1.1, so the connection should be still alive. - assertTrue(embedder.isOpen()); - - // The decoder should be reset by the aggregator at this point and be able to decode the next request. - embedder.writeInbound(Unpooled.copiedBuffer("GET /max-upload-size HTTP/1.1\r\n\r\n", CharsetUtil.US_ASCII)); - - FullHttpRequest request = embedder.readInbound(); - assertThat(request.method(), is(HttpMethod.GET)); - assertThat(request.uri(), is("/max-upload-size")); - assertThat(request.content().readableBytes(), is(0)); - request.release(); - - assertFalse(embedder.finish()); - } - - @Test - public void testOversizedRequestWith100ContinueAndDecoderCloseConnection() { - EmbeddedChannel embedder = new EmbeddedChannel(new HttpRequestDecoder(), new HttpObjectAggregator(4, true)); - embedder.writeInbound(Unpooled.copiedBuffer( - "PUT /upload HTTP/1.1\r\n" + - "Expect: 100-continue\r\n" + - "Content-Length: 100\r\n\r\n", CharsetUtil.US_ASCII)); - - assertNull(embedder.readInbound()); - - FullHttpResponse response = embedder.readOutbound(); - assertEquals(HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE, response.status()); - assertEquals("0", response.headers().get(HttpHeaderNames.CONTENT_LENGTH)); - - // We are forcing the connection closed if an expectation is exceeded. - assertFalse(embedder.isOpen()); - assertFalse(embedder.finish()); - } - - @Test - public void testRequestAfterOversized100ContinueAndDecoder() { - EmbeddedChannel embedder = new EmbeddedChannel(new HttpRequestDecoder(), new HttpObjectAggregator(15)); - - // Write first request with Expect: 100-continue. - HttpRequest message = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.PUT, "http://localhost"); - HttpUtil.set100ContinueExpected(message, true); - HttpUtil.setContentLength(message, 16); - - HttpContent chunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("some", CharsetUtil.US_ASCII)); - HttpContent chunk2 = new DefaultHttpContent(Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII)); - HttpContent chunk3 = LastHttpContent.EMPTY_LAST_CONTENT; - - // Send a request with 100-continue + large Content-Length header value. - assertFalse(embedder.writeInbound(message)); - - // The aggregator should respond with '413'. - FullHttpResponse response = embedder.readOutbound(); - assertEquals(HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE, response.status()); - assertEquals("0", response.headers().get(HttpHeaderNames.CONTENT_LENGTH)); - - // An ill-behaving client could continue to send data without a respect, and such data should be discarded. - assertFalse(embedder.writeInbound(chunk1)); - - // The aggregator should not close the connection because keep-alive is on. - assertTrue(embedder.isOpen()); - - // Now send a valid request. - HttpRequest message2 = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.PUT, "http://localhost"); - - assertFalse(embedder.writeInbound(message2)); - assertFalse(embedder.writeInbound(chunk2)); - assertTrue(embedder.writeInbound(chunk3)); - - FullHttpRequest fullMsg = embedder.readInbound(); - assertNotNull(fullMsg); - - assertEquals( - chunk2.content().readableBytes() + chunk3.content().readableBytes(), - HttpUtil.getContentLength(fullMsg)); - - assertEquals(HttpUtil.getContentLength(fullMsg), fullMsg.content().readableBytes()); - - fullMsg.release(); - assertFalse(embedder.finish()); - } - - @Test - public void testReplaceAggregatedRequest() { - EmbeddedChannel embedder = new EmbeddedChannel(new HttpObjectAggregator(1024 * 1024)); - - Exception boom = new Exception("boom"); - HttpRequest req = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost"); - req.setDecoderResult(DecoderResult.failure(boom)); - - assertTrue(embedder.writeInbound(req) && embedder.finish()); - - FullHttpRequest aggregatedReq = embedder.readInbound(); - FullHttpRequest replacedReq = aggregatedReq.replace(Unpooled.EMPTY_BUFFER); - - assertEquals(replacedReq.decoderResult(), aggregatedReq.decoderResult()); - aggregatedReq.release(); - replacedReq.release(); - } - - @Test - public void testReplaceAggregatedResponse() { - EmbeddedChannel embedder = new EmbeddedChannel(new HttpObjectAggregator(1024 * 1024)); - - Exception boom = new Exception("boom"); - HttpResponse rep = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - rep.setDecoderResult(DecoderResult.failure(boom)); - - assertTrue(embedder.writeInbound(rep) && embedder.finish()); - - FullHttpResponse aggregatedRep = embedder.readInbound(); - FullHttpResponse replacedRep = aggregatedRep.replace(Unpooled.EMPTY_BUFFER); - - assertEquals(replacedRep.decoderResult(), aggregatedRep.decoderResult()); - 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-http/src/test/java/io/netty/handler/codec/http/HttpRequestDecoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpRequestDecoderTest.java deleted file mode 100644 index 53d9778f2a..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpRequestDecoderTest.java +++ /dev/null @@ -1,509 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.TooLongFrameException; -import io.netty.util.AsciiString; -import io.netty.util.CharsetUtil; -import org.junit.jupiter.api.Test; - -import java.util.List; - -import static io.netty.handler.codec.http.HttpHeaderNames.*; -import static io.netty.handler.codec.http.HttpHeadersTestUtils.of; -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.nullValue; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class HttpRequestDecoderTest { - private static final byte[] CONTENT_CRLF_DELIMITERS = createContent("\r\n"); - private static final byte[] CONTENT_LF_DELIMITERS = createContent("\n"); - private static final byte[] CONTENT_MIXED_DELIMITERS = createContent("\r\n", "\n"); - private static final int CONTENT_LENGTH = 8; - - private static byte[] createContent(String... lineDelimiters) { - String lineDelimiter; - String lineDelimiter2; - if (lineDelimiters.length == 2) { - lineDelimiter = lineDelimiters[0]; - lineDelimiter2 = lineDelimiters[1]; - } else { - lineDelimiter = lineDelimiters[0]; - lineDelimiter2 = lineDelimiters[0]; - } - return ("GET /some/path?foo=bar&wibble=eek HTTP/1.1" + "\r\n" + - "Upgrade: WebSocket" + lineDelimiter2 + - "Connection: Upgrade" + lineDelimiter + - "Host: localhost" + lineDelimiter2 + - "Origin: http://localhost:8080" + lineDelimiter + - "Sec-WebSocket-Key1: 10 28 8V7 8 48 0" + lineDelimiter2 + - "Sec-WebSocket-Key2: 8 Xt754O3Q3QW 0 _60" + lineDelimiter + - "Content-Length: " + CONTENT_LENGTH + lineDelimiter2 + - "\r\n" + - "12345678").getBytes(CharsetUtil.US_ASCII); - } - - @Test - public void testDecodeWholeRequestAtOnceCRLFDelimiters() { - testDecodeWholeRequestAtOnce(CONTENT_CRLF_DELIMITERS); - } - - @Test - public void testDecodeWholeRequestAtOnceLFDelimiters() { - testDecodeWholeRequestAtOnce(CONTENT_LF_DELIMITERS); - } - - @Test - public void testDecodeWholeRequestAtOnceMixedDelimiters() { - testDecodeWholeRequestAtOnce(CONTENT_MIXED_DELIMITERS); - } - - private static void testDecodeWholeRequestAtOnce(byte[] content) { - EmbeddedChannel channel = new EmbeddedChannel(new HttpRequestDecoder()); - assertTrue(channel.writeInbound(Unpooled.copiedBuffer(content))); - HttpRequest req = channel.readInbound(); - assertNotNull(req); - checkHeaders(req.headers()); - LastHttpContent c = channel.readInbound(); - assertEquals(CONTENT_LENGTH, c.content().readableBytes()); - assertEquals( - Unpooled.wrappedBuffer(content, content.length - CONTENT_LENGTH, CONTENT_LENGTH), - c.content().readSlice(CONTENT_LENGTH)); - c.release(); - - assertFalse(channel.finish()); - assertNull(channel.readInbound()); - } - - private static void checkHeaders(HttpHeaders headers) { - assertEquals(7, headers.names().size()); - checkHeader(headers, "Upgrade", "WebSocket"); - checkHeader(headers, "Connection", "Upgrade"); - checkHeader(headers, "Host", "localhost"); - checkHeader(headers, "Origin", "http://localhost:8080"); - checkHeader(headers, "Sec-WebSocket-Key1", "10 28 8V7 8 48 0"); - checkHeader(headers, "Sec-WebSocket-Key2", "8 Xt754O3Q3QW 0 _60"); - checkHeader(headers, "Content-Length", String.valueOf(CONTENT_LENGTH)); - } - - private static void checkHeader(HttpHeaders headers, String name, String value) { - List header1 = headers.getAll(of(name)); - assertEquals(1, header1.size()); - assertEquals(value, header1.get(0)); - } - - @Test - public void testDecodeWholeRequestInMultipleStepsCRLFDelimiters() { - testDecodeWholeRequestInMultipleSteps(CONTENT_CRLF_DELIMITERS); - } - - @Test - public void testDecodeWholeRequestInMultipleStepsLFDelimiters() { - testDecodeWholeRequestInMultipleSteps(CONTENT_LF_DELIMITERS); - } - - @Test - public void testDecodeWholeRequestInMultipleStepsMixedDelimiters() { - testDecodeWholeRequestInMultipleSteps(CONTENT_MIXED_DELIMITERS); - } - - private static void testDecodeWholeRequestInMultipleSteps(byte[] content) { - for (int i = 1; i < content.length; i++) { - testDecodeWholeRequestInMultipleSteps(content, i); - } - } - - private static void testDecodeWholeRequestInMultipleSteps(byte[] content, int fragmentSize) { - EmbeddedChannel channel = new EmbeddedChannel(new HttpRequestDecoder()); - int headerLength = content.length - CONTENT_LENGTH; - - // split up the header - for (int a = 0; a < headerLength;) { - int amount = fragmentSize; - if (a + amount > headerLength) { - amount = headerLength - a; - } - - // if header is done it should produce an HttpRequest - channel.writeInbound(Unpooled.copiedBuffer(content, a, amount)); - a += amount; - } - - for (int i = CONTENT_LENGTH; i > 0; i --) { - // Should produce HttpContent - channel.writeInbound(Unpooled.copiedBuffer(content, content.length - i, 1)); - } - - HttpRequest req = channel.readInbound(); - assertNotNull(req); - checkHeaders(req.headers()); - - for (int i = CONTENT_LENGTH; i > 1; i --) { - HttpContent c = channel.readInbound(); - assertEquals(1, c.content().readableBytes()); - assertEquals(content[content.length - i], c.content().readByte()); - c.release(); - } - - LastHttpContent c = channel.readInbound(); - assertEquals(1, c.content().readableBytes()); - assertEquals(content[content.length - 1], c.content().readByte()); - c.release(); - - assertFalse(channel.finish()); - assertNull(channel.readInbound()); - } - - @Test - public void testMultiLineHeader() { - EmbeddedChannel channel = new EmbeddedChannel(new HttpRequestDecoder()); - String crlf = "\r\n"; - String request = "GET /some/path HTTP/1.1" + crlf + - "Host: localhost" + crlf + - "MyTestHeader: part1" + crlf + - " newLinePart2" + crlf + - "MyTestHeader2: part21" + crlf + - "\t newLinePart22" - + crlf + crlf; - assertTrue(channel.writeInbound(Unpooled.copiedBuffer(request, CharsetUtil.US_ASCII))); - HttpRequest req = channel.readInbound(); - assertEquals("part1 newLinePart2", req.headers().get(of("MyTestHeader"))); - assertEquals("part21 newLinePart22", req.headers().get(of("MyTestHeader2"))); - - LastHttpContent c = channel.readInbound(); - c.release(); - - assertFalse(channel.finish()); - assertNull(channel.readInbound()); - } - - @Test - public void testEmptyHeaderValue() { - EmbeddedChannel channel = new EmbeddedChannel(new HttpRequestDecoder()); - String crlf = "\r\n"; - String request = "GET /some/path HTTP/1.1" + crlf + - "Host: localhost" + crlf + - "EmptyHeader:" + crlf + crlf; - channel.writeInbound(Unpooled.copiedBuffer(request, CharsetUtil.US_ASCII)); - HttpRequest req = channel.readInbound(); - assertEquals("", req.headers().get(of("EmptyHeader"))); - } - - @Test - public void test100Continue() { - HttpRequestDecoder decoder = new HttpRequestDecoder(); - EmbeddedChannel channel = new EmbeddedChannel(decoder); - String oversized = - "PUT /file HTTP/1.1\r\n" + - "Expect: 100-continue\r\n" + - "Content-Length: 1048576000\r\n\r\n"; - - channel.writeInbound(Unpooled.copiedBuffer(oversized, CharsetUtil.US_ASCII)); - assertThat(channel.readInbound(), is(instanceOf(HttpRequest.class))); - - // At this point, we assume that we sent '413 Entity Too Large' to the peer without closing the connection - // so that the client can try again. - decoder.reset(); - - String query = "GET /max-file-size HTTP/1.1\r\n\r\n"; - channel.writeInbound(Unpooled.copiedBuffer(query, CharsetUtil.US_ASCII)); - assertThat(channel.readInbound(), is(instanceOf(HttpRequest.class))); - assertThat(channel.readInbound(), is(instanceOf(LastHttpContent.class))); - - assertThat(channel.finish(), is(false)); - } - - @Test - public void test100ContinueWithBadClient() { - HttpRequestDecoder decoder = new HttpRequestDecoder(); - EmbeddedChannel channel = new EmbeddedChannel(decoder); - String oversized = - "PUT /file HTTP/1.1\r\n" + - "Expect: 100-continue\r\n" + - "Content-Length: 1048576000\r\n\r\n" + - "WAY_TOO_LARGE_DATA_BEGINS"; - - channel.writeInbound(Unpooled.copiedBuffer(oversized, CharsetUtil.US_ASCII)); - assertThat(channel.readInbound(), is(instanceOf(HttpRequest.class))); - - HttpContent prematureData = channel.readInbound(); - prematureData.release(); - - assertThat(channel.readInbound(), is(nullValue())); - - // At this point, we assume that we sent '413 Entity Too Large' to the peer without closing the connection - // so that the client can try again. - decoder.reset(); - - String query = "GET /max-file-size HTTP/1.1\r\n\r\n"; - channel.writeInbound(Unpooled.copiedBuffer(query, CharsetUtil.US_ASCII)); - assertThat(channel.readInbound(), is(instanceOf(HttpRequest.class))); - assertThat(channel.readInbound(), is(instanceOf(LastHttpContent.class))); - - assertThat(channel.finish(), is(false)); - } - - @Test - public void testMessagesSplitBetweenMultipleBuffers() { - EmbeddedChannel channel = new EmbeddedChannel(new HttpRequestDecoder()); - String crlf = "\r\n"; - String str1 = "GET /some/path HTTP/1.1" + crlf + - "Host: localhost1" + crlf + crlf + - "GET /some/other/path HTTP/1.0" + crlf + - "Hos"; - String str2 = "t: localhost2" + crlf + - "content-length: 0" + crlf + crlf; - channel.writeInbound(Unpooled.copiedBuffer(str1, CharsetUtil.US_ASCII)); - HttpRequest req = channel.readInbound(); - assertEquals(HttpVersion.HTTP_1_1, req.protocolVersion()); - assertEquals("/some/path", req.uri()); - assertEquals(1, req.headers().size()); - assertTrue(AsciiString.contentEqualsIgnoreCase("localhost1", req.headers().get(HOST))); - LastHttpContent cnt = channel.readInbound(); - cnt.release(); - - channel.writeInbound(Unpooled.copiedBuffer(str2, CharsetUtil.US_ASCII)); - req = channel.readInbound(); - assertEquals(HttpVersion.HTTP_1_0, req.protocolVersion()); - assertEquals("/some/other/path", req.uri()); - assertEquals(2, req.headers().size()); - assertTrue(AsciiString.contentEqualsIgnoreCase("localhost2", req.headers().get(HOST))); - assertTrue(AsciiString.contentEqualsIgnoreCase("0", req.headers().get(HttpHeaderNames.CONTENT_LENGTH))); - cnt = channel.readInbound(); - cnt.release(); - assertFalse(channel.finishAndReleaseAll()); - } - - @Test - public void testTooLargeInitialLine() { - EmbeddedChannel channel = new EmbeddedChannel(new HttpRequestDecoder(10, 1024)); - String requestStr = "GET /some/path HTTP/1.1\r\n" + - "Host: localhost1\r\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 TooLongFrameException); - assertFalse(channel.finish()); - } - - @Test - public void testTooLargeInitialLineWithWSOnly() { - testTooLargeInitialLineWithControlCharsOnly(" "); - } - - @Test - public void testTooLargeInitialLineWithCRLFOnly() { - testTooLargeInitialLineWithControlCharsOnly("\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"); - } - - private static void testTooLargeInitialLineWithControlCharsOnly(String controlChars) { - EmbeddedChannel channel = new EmbeddedChannel(new HttpRequestDecoder(15, 1024)); - String requestStr = controlChars + "GET / HTTP/1.1\r\n" + - "Host: localhost1\r\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 TooLongFrameException); - assertFalse(channel.finish()); - } - - @Test - public void testInitialLineWithLeadingControlChars() { - EmbeddedChannel channel = new EmbeddedChannel(new HttpRequestDecoder()); - String crlf = "\r\n"; - String request = crlf + "GET /some/path HTTP/1.1" + crlf + - "Host: localhost" + crlf + crlf; - assertTrue(channel.writeInbound(Unpooled.copiedBuffer(request, CharsetUtil.US_ASCII))); - HttpRequest req = channel.readInbound(); - assertEquals(HttpMethod.GET, req.method()); - assertEquals("/some/path", req.uri()); - assertEquals(HttpVersion.HTTP_1_1, req.protocolVersion()); - assertTrue(channel.finishAndReleaseAll()); - } - - @Test - public void testTooLargeHeaders() { - EmbeddedChannel channel = new EmbeddedChannel(new HttpRequestDecoder(1024, 10)); - String requestStr = "GET /some/path HTTP/1.1\r\n" + - "Host: localhost1\r\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 TooLongFrameException); - assertFalse(channel.finish()); - } - - @Test - public void testWhitespace() { - String requestStr = "GET /some/path HTTP/1.1\r\n" + - "Transfer-Encoding : chunked\r\n" + - "Host: netty.io\r\n\r\n"; - testInvalidHeaders0(requestStr); - } - - @Test - public void testWhitespaceBeforeTransferEncoding01() { - String requestStr = "GET /some/path HTTP/1.1\r\n" + - " Transfer-Encoding : chunked\r\n" + - "Content-Length: 1\r\n" + - "Host: netty.io\r\n\r\n" + - "a"; - testInvalidHeaders0(requestStr); - } - - @Test - public void testWhitespaceBeforeTransferEncoding02() { - String requestStr = "POST / HTTP/1.1" + - " Transfer-Encoding : chunked\r\n" + - "Host: target.com" + - "Content-Length: 65\r\n\r\n" + - "0\r\n\r\n" + - "GET /maliciousRequest HTTP/1.1\r\n" + - "Host: evilServer.com\r\n" + - "Foo: x"; - testInvalidHeaders0(requestStr); - } - - @Test - public void testHeaderWithNoValueAndMissingColon() { - 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 testContentLengthAndTransferEncodingHeadersWithVerticalTab() { - testContentLengthAndTransferEncodingHeadersWithInvalidSeparator((char) 0x0b, false); - testContentLengthAndTransferEncodingHeadersWithInvalidSeparator((char) 0x0b, true); - } - - @Test - public void testContentLengthAndTransferEncodingHeadersWithCR() { - testContentLengthAndTransferEncodingHeadersWithInvalidSeparator((char) 0x0d, false); - testContentLengthAndTransferEncodingHeadersWithInvalidSeparator((char) 0x0d, true); - } - - private static void testContentLengthAndTransferEncodingHeadersWithInvalidSeparator( - char separator, boolean extraLine) { - String requestStr = "POST / HTTP/1.1\r\n" + - "Host: example.com\r\n" + - "Connection: close\r\n" + - "Content-Length: 9\r\n" + - "Transfer-Encoding:" + separator + "chunked\r\n\r\n" + - (extraLine ? "0\r\n\r\n" : "") + - "something\r\n\r\n"; - 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"; - EmbeddedChannel channel = new EmbeddedChannel(new HttpRequestDecoder()); - assertTrue(channel.writeInbound(Unpooled.copiedBuffer(requestStr, CharsetUtil.US_ASCII))); - HttpRequest request = channel.readInbound(); - assertFalse(request.decoderResult().isFailure()); - assertTrue(request.headers().contains("Transfer-Encoding", "chunked", false)); - assertFalse(request.headers().contains("Content-Length")); - LastHttpContent c = channel.readInbound(); - assertFalse(channel.finish()); - } - - @Test - public void testHttpMessageDecoderResult() { - String requestStr = "PUT /some/path HTTP/1.1\r\n" + - "Content-Length: 11\r\n" + - "Connection: close\r\n\r\n" + - "Lorem ipsum"; - EmbeddedChannel channel = new EmbeddedChannel(new HttpRequestDecoder()); - assertTrue(channel.writeInbound(Unpooled.copiedBuffer(requestStr, CharsetUtil.US_ASCII))); - HttpRequest request = channel.readInbound(); - assertTrue(request.decoderResult().isSuccess()); - assertThat(request.decoderResult(), instanceOf(HttpMessageDecoderResult.class)); - HttpMessageDecoderResult decoderResult = (HttpMessageDecoderResult) request.decoderResult(); - assertThat(decoderResult.initialLineLength(), is(23)); - assertThat(decoderResult.headerSize(), is(35)); - assertThat(decoderResult.totalSize(), is(58)); - HttpContent c = channel.readInbound(); - c.release(); - assertFalse(channel.finish()); - } - - 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(); - 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/HttpRequestEncoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpRequestEncoderTest.java deleted file mode 100644 index d4c32b650e..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpRequestEncoderTest.java +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.DecoderResult; -import io.netty.util.CharsetUtil; -import io.netty.util.IllegalReferenceCountException; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.charset.Charset; -import java.util.concurrent.ExecutionException; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.is; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** - */ -public class HttpRequestEncoderTest { - - @SuppressWarnings("deprecation") - private static ByteBuf[] getBuffers() { - return new ByteBuf[]{ - Unpooled.buffer(128).order(ByteOrder.BIG_ENDIAN), - Unpooled.buffer(128).order(ByteOrder.LITTLE_ENDIAN), - Unpooled.wrappedBuffer(ByteBuffer.allocate(128).order(ByteOrder.BIG_ENDIAN)).writerIndex(0), - Unpooled.wrappedBuffer(ByteBuffer.allocate(128).order(ByteOrder.LITTLE_ENDIAN)).writerIndex(0) - }; - } - - @Test - public void testUriWithoutPath() throws Exception { - for (ByteBuf buffer : getBuffers()) { - HttpRequestEncoder encoder = new HttpRequestEncoder(); - encoder.encodeInitialLine(buffer, new DefaultHttpRequest(HttpVersion.HTTP_1_1, - HttpMethod.GET, "http://localhost")); - String req = buffer.toString(Charset.forName("US-ASCII")); - assertEquals("GET http://localhost/ HTTP/1.1\r\n", req); - buffer.release(); - } - } - - @Test - public void testUriWithoutPath2() throws Exception { - for (ByteBuf buffer : getBuffers()) { - HttpRequestEncoder encoder = new HttpRequestEncoder(); - encoder.encodeInitialLine(buffer, new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, - "http://localhost:9999?p1=v1")); - String req = buffer.toString(Charset.forName("US-ASCII")); - assertEquals("GET http://localhost:9999/?p1=v1 HTTP/1.1\r\n", req); - buffer.release(); - } - } - - @Test - public void testUriWithEmptyPath() throws Exception { - for (ByteBuf buffer : getBuffers()) { - HttpRequestEncoder encoder = new HttpRequestEncoder(); - encoder.encodeInitialLine(buffer, new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, - "http://localhost:9999/?p1=v1")); - String req = buffer.toString(Charset.forName("US-ASCII")); - assertEquals("GET http://localhost:9999/?p1=v1 HTTP/1.1\r\n", req); - buffer.release(); - } - } - - @Test - public void testUriWithPath() throws Exception { - for (ByteBuf buffer : getBuffers()) { - HttpRequestEncoder encoder = new HttpRequestEncoder(); - encoder.encodeInitialLine(buffer, new DefaultHttpRequest(HttpVersion.HTTP_1_1, - HttpMethod.GET, "http://localhost/")); - String req = buffer.toString(Charset.forName("US-ASCII")); - assertEquals("GET http://localhost/ HTTP/1.1\r\n", req); - buffer.release(); - } - } - - @Test - public void testAbsPath() throws Exception { - for (ByteBuf buffer : getBuffers()) { - HttpRequestEncoder encoder = new HttpRequestEncoder(); - encoder.encodeInitialLine(buffer, new DefaultHttpRequest(HttpVersion.HTTP_1_1, - HttpMethod.GET, "/")); - String req = buffer.toString(Charset.forName("US-ASCII")); - assertEquals("GET / HTTP/1.1\r\n", req); - buffer.release(); - } - } - - @Test - public void testEmptyAbsPath() throws Exception { - for (ByteBuf buffer : getBuffers()) { - HttpRequestEncoder encoder = new HttpRequestEncoder(); - encoder.encodeInitialLine(buffer, new DefaultHttpRequest(HttpVersion.HTTP_1_1, - HttpMethod.GET, "")); - String req = buffer.toString(Charset.forName("US-ASCII")); - assertEquals("GET / HTTP/1.1\r\n", req); - buffer.release(); - } - } - - @Test - public void testQueryStringPath() throws Exception { - for (ByteBuf buffer : getBuffers()) { - HttpRequestEncoder encoder = new HttpRequestEncoder(); - encoder.encodeInitialLine(buffer, new DefaultHttpRequest(HttpVersion.HTTP_1_1, - HttpMethod.GET, "/?url=http://example.com")); - String req = buffer.toString(Charset.forName("US-ASCII")); - assertEquals("GET /?url=http://example.com HTTP/1.1\r\n", req); - buffer.release(); - } - } - - @Test - public void testEmptyReleasedBufferShouldNotWriteEmptyBufferToChannel() throws Exception { - HttpRequestEncoder encoder = new HttpRequestEncoder(); - final EmbeddedChannel channel = new EmbeddedChannel(encoder); - final ByteBuf buf = Unpooled.buffer(); - buf.release(); - ExecutionException e = assertThrows(ExecutionException.class, new Executable() { - @Override - public void execute() throws Throwable { - channel.writeAndFlush(buf).get(); - } - }); - assertThat(e.getCause().getCause(), is(instanceOf(IllegalReferenceCountException.class))); - - channel.finishAndReleaseAll(); - } - - @Test - public void testEmptyBufferShouldPassThrough() throws Exception { - HttpRequestEncoder encoder = new HttpRequestEncoder(); - EmbeddedChannel channel = new EmbeddedChannel(encoder); - ByteBuf buffer = Unpooled.buffer(); - channel.writeAndFlush(buffer).get(); - channel.finishAndReleaseAll(); - assertEquals(0, buffer.refCnt()); - } - - @Test - public void testEmptyContentsChunked() throws Exception { - testEmptyContents(true, false); - } - - @Test - public void testEmptyContentsChunkedWithTrailers() throws Exception { - testEmptyContents(true, true); - } - - @Test - public void testEmptyContentsNotChunked() throws Exception { - testEmptyContents(false, false); - } - - @Test - public void testEmptyContentNotsChunkedWithTrailers() throws Exception { - testEmptyContents(false, true); - } - - private void testEmptyContents(boolean chunked, boolean trailers) throws Exception { - HttpRequestEncoder encoder = new HttpRequestEncoder(); - EmbeddedChannel channel = new EmbeddedChannel(encoder); - HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/"); - if (chunked) { - HttpUtil.setTransferEncodingChunked(request, true); - } - assertTrue(channel.writeOutbound(request)); - - ByteBuf contentBuffer = Unpooled.buffer(); - assertTrue(channel.writeOutbound(new DefaultHttpContent(contentBuffer))); - - ByteBuf lastContentBuffer = Unpooled.buffer(); - LastHttpContent last = new DefaultLastHttpContent(lastContentBuffer); - if (trailers) { - last.trailingHeaders().set("X-Netty-Test", "true"); - } - assertTrue(channel.writeOutbound(last)); - - // Ensure we only produce ByteBuf instances. - ByteBuf head = channel.readOutbound(); - assertTrue(head.release()); - - ByteBuf content = channel.readOutbound(); - content.release(); - - ByteBuf lastContent = channel.readOutbound(); - lastContent.release(); - assertFalse(channel.finish()); - } - - /** - * A test that checks for a NPE that would occur if when processing {@link LastHttpContent#EMPTY_LAST_CONTENT} - * when a certain initialization order of {@link EmptyHttpHeaders} would occur. - */ - @Test - public void testForChunkedRequestNpe() throws Exception { - EmbeddedChannel channel = new EmbeddedChannel(new HttpRequestEncoder()); - assertTrue(channel.writeOutbound(new CustomHttpRequest())); - assertTrue(channel.writeOutbound(new DefaultHttpContent(Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII)))); - assertTrue(channel.writeOutbound(LastHttpContent.EMPTY_LAST_CONTENT)); - assertTrue(channel.finishAndReleaseAll()); - } - - /** - * This class is required to triggered the desired initialization order of {@link EmptyHttpHeaders}. - * If {@link DefaultHttpRequest} is used, the {@link HttpHeaders} class will be initialized before {@link HttpUtil} - * and the test won't trigger the original issue. - */ - private static final class CustomHttpRequest implements HttpRequest { - - @Override - public DecoderResult decoderResult() { - return DecoderResult.SUCCESS; - } - - @Override - public void setDecoderResult(DecoderResult result) { - } - - @Override - public DecoderResult getDecoderResult() { - return decoderResult(); - } - - @Override - public HttpVersion getProtocolVersion() { - return HttpVersion.HTTP_1_1; - } - - @Override - public HttpVersion protocolVersion() { - return getProtocolVersion(); - } - - @Override - public HttpHeaders headers() { - DefaultHttpHeaders headers = new DefaultHttpHeaders(); - headers.add("Transfer-Encoding", "chunked"); - return headers; - } - - @Override - public HttpMethod getMethod() { - return HttpMethod.POST; - } - - @Override - public HttpMethod method() { - return getMethod(); - } - - @Override - public HttpRequest setMethod(HttpMethod method) { - return this; - } - - @Override - public String getUri() { - return "/"; - } - - @Override - public String uri() { - return "/"; - } - - @Override - public HttpRequest setUri(String uri) { - return this; - } - - @Override - public HttpRequest setProtocolVersion(HttpVersion version) { - return this; - } - } -} 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 deleted file mode 100644 index 8ad4066583..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpResponseDecoderTest.java +++ /dev/null @@ -1,701 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.PrematureChannelClosureException; -import io.netty.handler.codec.TooLongFrameException; -import io.netty.util.CharsetUtil; -import org.junit.jupiter.api.Test; - -import java.util.Arrays; -import java.util.List; - -import static io.netty.handler.codec.http.HttpHeadersTestUtils.of; -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.not; -import static org.hamcrest.CoreMatchers.nullValue; -import static org.hamcrest.CoreMatchers.sameInstance; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class HttpResponseDecoderTest { - - /** - * The size of headers should be calculated correctly even if a single header is split into multiple fragments. - * @see #3445 - */ - @Test - public void testMaxHeaderSize1() { - final int maxHeaderSize = 8192; - - final EmbeddedChannel ch = new EmbeddedChannel(new HttpResponseDecoder(4096, maxHeaderSize)); - final char[] bytes = new char[maxHeaderSize / 2 - 4]; - Arrays.fill(bytes, 'a'); - - ch.writeInbound(Unpooled.copiedBuffer("HTTP/1.1 200 OK\r\n", CharsetUtil.US_ASCII)); - - // Write two 4096-byte headers (= 8192 bytes) - ch.writeInbound(Unpooled.copiedBuffer("A:", CharsetUtil.US_ASCII)); - ch.writeInbound(Unpooled.copiedBuffer(bytes, CharsetUtil.US_ASCII)); - ch.writeInbound(Unpooled.copiedBuffer("\r\n", CharsetUtil.US_ASCII)); - assertNull(ch.readInbound()); - ch.writeInbound(Unpooled.copiedBuffer("B:", CharsetUtil.US_ASCII)); - ch.writeInbound(Unpooled.copiedBuffer(bytes, CharsetUtil.US_ASCII)); - ch.writeInbound(Unpooled.copiedBuffer("\r\n", CharsetUtil.US_ASCII)); - ch.writeInbound(Unpooled.copiedBuffer("\r\n", CharsetUtil.US_ASCII)); - - HttpResponse res = ch.readInbound(); - assertNull(res.decoderResult().cause()); - assertTrue(res.decoderResult().isSuccess()); - - assertNull(ch.readInbound()); - assertTrue(ch.finish()); - assertThat(ch.readInbound(), instanceOf(LastHttpContent.class)); - } - - /** - * Complementary test case of {@link #testMaxHeaderSize1()} When it actually exceeds the maximum, it should fail. - */ - @Test - public void testMaxHeaderSize2() { - final int maxHeaderSize = 8192; - - final EmbeddedChannel ch = new EmbeddedChannel(new HttpResponseDecoder(4096, maxHeaderSize)); - final char[] bytes = new char[maxHeaderSize / 2 - 2]; - Arrays.fill(bytes, 'a'); - - ch.writeInbound(Unpooled.copiedBuffer("HTTP/1.1 200 OK\r\n", CharsetUtil.US_ASCII)); - - // Write a 4096-byte header and a 4097-byte header to test an off-by-one case (= 8193 bytes) - ch.writeInbound(Unpooled.copiedBuffer("A:", CharsetUtil.US_ASCII)); - ch.writeInbound(Unpooled.copiedBuffer(bytes, CharsetUtil.US_ASCII)); - ch.writeInbound(Unpooled.copiedBuffer("\r\n", CharsetUtil.US_ASCII)); - assertNull(ch.readInbound()); - ch.writeInbound(Unpooled.copiedBuffer("B: ", CharsetUtil.US_ASCII)); // Note an extra space. - ch.writeInbound(Unpooled.copiedBuffer(bytes, CharsetUtil.US_ASCII)); - ch.writeInbound(Unpooled.copiedBuffer("\r\n", CharsetUtil.US_ASCII)); - ch.writeInbound(Unpooled.copiedBuffer("\r\n", CharsetUtil.US_ASCII)); - - HttpResponse res = ch.readInbound(); - assertTrue(res.decoderResult().cause() instanceof TooLongFrameException); - - assertFalse(ch.finish()); - assertNull(ch.readInbound()); - } - - @Test - public void testResponseChunked() { - EmbeddedChannel ch = new EmbeddedChannel(new HttpResponseDecoder()); - ch.writeInbound(Unpooled.copiedBuffer("HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n", - CharsetUtil.US_ASCII)); - - HttpResponse res = ch.readInbound(); - assertThat(res.protocolVersion(), sameInstance(HttpVersion.HTTP_1_1)); - assertThat(res.status(), is(HttpResponseStatus.OK)); - - byte[] data = new byte[64]; - for (int i = 0; i < data.length; i++) { - data[i] = (byte) i; - } - - for (int i = 0; i < 10; i++) { - assertFalse(ch.writeInbound(Unpooled.copiedBuffer(Integer.toHexString(data.length) + "\r\n", - CharsetUtil.US_ASCII))); - assertTrue(ch.writeInbound(Unpooled.copiedBuffer(data))); - HttpContent content = ch.readInbound(); - assertEquals(data.length, content.content().readableBytes()); - - byte[] decodedData = new byte[data.length]; - content.content().readBytes(decodedData); - assertArrayEquals(data, decodedData); - content.release(); - - assertFalse(ch.writeInbound(Unpooled.copiedBuffer("\r\n", CharsetUtil.US_ASCII))); - } - - // Write the last chunk. - ch.writeInbound(Unpooled.copiedBuffer("0\r\n\r\n", CharsetUtil.US_ASCII)); - - // Ensure the last chunk was decoded. - LastHttpContent content = ch.readInbound(); - assertFalse(content.content().isReadable()); - content.release(); - - ch.finish(); - assertNull(ch.readInbound()); - } - - @Test - public void testClosureWithoutContentLength1() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new HttpResponseDecoder()); - ch.writeInbound(Unpooled.copiedBuffer("HTTP/1.1 200 OK\r\n\r\n", CharsetUtil.US_ASCII)); - - // Read the response headers. - HttpResponse res = ch.readInbound(); - assertThat(res.protocolVersion(), sameInstance(HttpVersion.HTTP_1_1)); - assertThat(res.status(), is(HttpResponseStatus.OK)); - assertThat(ch.readInbound(), is(nullValue())); - - // Close the connection without sending anything. - assertTrue(ch.finish()); - - // The decoder should still produce the last content. - LastHttpContent content = ch.readInbound(); - assertThat(content.content().isReadable(), is(false)); - content.release(); - - // But nothing more. - assertThat(ch.readInbound(), is(nullValue())); - } - - @Test - public void testClosureWithoutContentLength2() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new HttpResponseDecoder()); - - // Write the partial response. - ch.writeInbound(Unpooled.copiedBuffer("HTTP/1.1 200 OK\r\n\r\n12345678", CharsetUtil.US_ASCII)); - - // Read the response headers. - HttpResponse res = ch.readInbound(); - assertThat(res.protocolVersion(), sameInstance(HttpVersion.HTTP_1_1)); - assertThat(res.status(), is(HttpResponseStatus.OK)); - - // Read the partial content. - HttpContent content = ch.readInbound(); - assertThat(content.content().toString(CharsetUtil.US_ASCII), is("12345678")); - assertThat(content, is(not(instanceOf(LastHttpContent.class)))); - content.release(); - - assertThat(ch.readInbound(), is(nullValue())); - - // Close the connection. - assertTrue(ch.finish()); - - // The decoder should still produce the last content. - LastHttpContent lastContent = ch.readInbound(); - assertThat(lastContent.content().isReadable(), is(false)); - lastContent.release(); - - // But nothing more. - assertThat(ch.readInbound(), is(nullValue())); - } - - @Test - public void testPrematureClosureWithChunkedEncoding1() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new HttpResponseDecoder()); - ch.writeInbound( - Unpooled.copiedBuffer("HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n", CharsetUtil.US_ASCII)); - - // Read the response headers. - HttpResponse res = ch.readInbound(); - assertThat(res.protocolVersion(), sameInstance(HttpVersion.HTTP_1_1)); - assertThat(res.status(), is(HttpResponseStatus.OK)); - assertThat(res.headers().get(HttpHeaderNames.TRANSFER_ENCODING), is("chunked")); - assertThat(ch.readInbound(), is(nullValue())); - - // Close the connection without sending anything. - ch.finish(); - // The decoder should not generate the last chunk because it's closed prematurely. - assertThat(ch.readInbound(), is(nullValue())); - } - - @Test - public void testPrematureClosureWithChunkedEncoding2() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new HttpResponseDecoder()); - - // Write the partial response. - ch.writeInbound(Unpooled.copiedBuffer( - "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n8\r\n12345678", CharsetUtil.US_ASCII)); - - // Read the response headers. - HttpResponse res = ch.readInbound(); - assertThat(res.protocolVersion(), sameInstance(HttpVersion.HTTP_1_1)); - assertThat(res.status(), is(HttpResponseStatus.OK)); - assertThat(res.headers().get(HttpHeaderNames.TRANSFER_ENCODING), is("chunked")); - - // Read the partial content. - HttpContent content = ch.readInbound(); - assertThat(content.content().toString(CharsetUtil.US_ASCII), is("12345678")); - assertThat(content, is(not(instanceOf(LastHttpContent.class)))); - content.release(); - - assertThat(ch.readInbound(), is(nullValue())); - - // Close the connection. - ch.finish(); - - // The decoder should not generate the last chunk because it's closed prematurely. - assertThat(ch.readInbound(), is(nullValue())); - } - - @Test - public void testLastResponseWithEmptyHeaderAndEmptyContent() { - EmbeddedChannel ch = new EmbeddedChannel(new HttpResponseDecoder()); - ch.writeInbound(Unpooled.copiedBuffer("HTTP/1.1 200 OK\r\n\r\n", CharsetUtil.US_ASCII)); - - HttpResponse res = ch.readInbound(); - assertThat(res.protocolVersion(), sameInstance(HttpVersion.HTTP_1_1)); - assertThat(res.status(), is(HttpResponseStatus.OK)); - assertThat(ch.readInbound(), is(nullValue())); - - assertThat(ch.finish(), is(true)); - - LastHttpContent content = ch.readInbound(); - assertThat(content.content().isReadable(), is(false)); - content.release(); - - assertThat(ch.readInbound(), is(nullValue())); - } - - @Test - public void testLastResponseWithoutContentLengthHeader() { - EmbeddedChannel ch = new EmbeddedChannel(new HttpResponseDecoder()); - ch.writeInbound(Unpooled.copiedBuffer("HTTP/1.1 200 OK\r\n\r\n", CharsetUtil.US_ASCII)); - - HttpResponse res = ch.readInbound(); - assertThat(res.protocolVersion(), sameInstance(HttpVersion.HTTP_1_1)); - assertThat(res.status(), is(HttpResponseStatus.OK)); - assertThat(ch.readInbound(), is(nullValue())); - - ch.writeInbound(Unpooled.wrappedBuffer(new byte[1024])); - HttpContent content = ch.readInbound(); - assertThat(content.content().readableBytes(), is(1024)); - content.release(); - - assertThat(ch.finish(), is(true)); - - LastHttpContent lastContent = ch.readInbound(); - assertThat(lastContent.content().isReadable(), is(false)); - lastContent.release(); - - assertThat(ch.readInbound(), is(nullValue())); - } - - @Test - public void testLastResponseWithHeaderRemoveTrailingSpaces() { - EmbeddedChannel ch = new EmbeddedChannel(new HttpResponseDecoder()); - ch.writeInbound(Unpooled.copiedBuffer( - "HTTP/1.1 200 OK\r\nX-Header: h2=h2v2; Expires=Wed, 09-Jun-2021 10:18:14 GMT \r\n\r\n", - CharsetUtil.US_ASCII)); - - HttpResponse res = ch.readInbound(); - assertThat(res.protocolVersion(), sameInstance(HttpVersion.HTTP_1_1)); - assertThat(res.status(), is(HttpResponseStatus.OK)); - assertThat(res.headers().get(of("X-Header")), is("h2=h2v2; Expires=Wed, 09-Jun-2021 10:18:14 GMT")); - assertThat(ch.readInbound(), is(nullValue())); - - ch.writeInbound(Unpooled.wrappedBuffer(new byte[1024])); - HttpContent content = ch.readInbound(); - assertThat(content.content().readableBytes(), is(1024)); - content.release(); - - assertThat(ch.finish(), is(true)); - - LastHttpContent lastContent = ch.readInbound(); - assertThat(lastContent.content().isReadable(), is(false)); - lastContent.release(); - - assertThat(ch.readInbound(), is(nullValue())); - } - - @Test - public void testResetContentResponseWithTransferEncoding() { - EmbeddedChannel ch = new EmbeddedChannel(new HttpResponseDecoder()); - assertTrue(ch.writeInbound(Unpooled.copiedBuffer( - "HTTP/1.1 205 Reset Content\r\n" + - "Transfer-Encoding: chunked\r\n" + - "\r\n" + - "0\r\n" + - "\r\n", - CharsetUtil.US_ASCII))); - - HttpResponse res = ch.readInbound(); - assertThat(res.protocolVersion(), sameInstance(HttpVersion.HTTP_1_1)); - assertThat(res.status(), is(HttpResponseStatus.RESET_CONTENT)); - - LastHttpContent lastContent = ch.readInbound(); - assertThat(lastContent.content().isReadable(), is(false)); - lastContent.release(); - - assertThat(ch.finish(), is(false)); - } - - @Test - public void testLastResponseWithTrailingHeader() { - EmbeddedChannel ch = new EmbeddedChannel(new HttpResponseDecoder()); - ch.writeInbound(Unpooled.copiedBuffer( - "HTTP/1.1 200 OK\r\n" + - "Transfer-Encoding: chunked\r\n" + - "\r\n" + - "0\r\n" + - "Set-Cookie: t1=t1v1\r\n" + - "Set-Cookie: t2=t2v2; Expires=Wed, 09-Jun-2021 10:18:14 GMT\r\n" + - "\r\n", - CharsetUtil.US_ASCII)); - - HttpResponse res = ch.readInbound(); - assertThat(res.protocolVersion(), sameInstance(HttpVersion.HTTP_1_1)); - assertThat(res.status(), is(HttpResponseStatus.OK)); - - LastHttpContent lastContent = ch.readInbound(); - assertThat(lastContent.content().isReadable(), is(false)); - HttpHeaders headers = lastContent.trailingHeaders(); - assertEquals(1, headers.names().size()); - List values = headers.getAll(of("Set-Cookie")); - assertEquals(2, values.size()); - assertTrue(values.contains("t1=t1v1")); - assertTrue(values.contains("t2=t2v2; Expires=Wed, 09-Jun-2021 10:18:14 GMT")); - lastContent.release(); - - assertThat(ch.finish(), is(false)); - assertThat(ch.readInbound(), is(nullValue())); - } - - @Test - public void testLastResponseWithTrailingHeaderFragmented() { - byte[] data = ("HTTP/1.1 200 OK\r\n" + - "Transfer-Encoding: chunked\r\n" + - "\r\n" + - "0\r\n" + - "Set-Cookie: t1=t1v1\r\n" + - "Set-Cookie: t2=t2v2; Expires=Wed, 09-Jun-2021 10:18:14 GMT\r\n" + - "\r\n").getBytes(CharsetUtil.US_ASCII); - - for (int i = 1; i < data.length; i++) { - testLastResponseWithTrailingHeaderFragmented(data, i); - } - } - - private static void testLastResponseWithTrailingHeaderFragmented(byte[] content, int fragmentSize) { - EmbeddedChannel ch = new EmbeddedChannel(new HttpResponseDecoder()); - int headerLength = 47; - // split up the header - for (int a = 0; a < headerLength;) { - int amount = fragmentSize; - if (a + amount > headerLength) { - amount = headerLength - a; - } - - // if header is done it should produce an HttpRequest - boolean headerDone = a + amount == headerLength; - assertEquals(headerDone, ch.writeInbound(Unpooled.copiedBuffer(content, a, amount))); - a += amount; - } - - ch.writeInbound(Unpooled.copiedBuffer(content, headerLength, content.length - headerLength)); - HttpResponse res = ch.readInbound(); - assertThat(res.protocolVersion(), sameInstance(HttpVersion.HTTP_1_1)); - assertThat(res.status(), is(HttpResponseStatus.OK)); - - LastHttpContent lastContent = ch.readInbound(); - assertThat(lastContent.content().isReadable(), is(false)); - HttpHeaders headers = lastContent.trailingHeaders(); - assertEquals(1, headers.names().size()); - List values = headers.getAll(of("Set-Cookie")); - assertEquals(2, values.size()); - assertTrue(values.contains("t1=t1v1")); - assertTrue(values.contains("t2=t2v2; Expires=Wed, 09-Jun-2021 10:18:14 GMT")); - lastContent.release(); - - assertThat(ch.finish(), is(false)); - assertThat(ch.readInbound(), is(nullValue())); - } - - @Test - public void testResponseWithContentLength() { - EmbeddedChannel ch = new EmbeddedChannel(new HttpResponseDecoder()); - ch.writeInbound(Unpooled.copiedBuffer( - "HTTP/1.1 200 OK\r\n" + - "Content-Length: 10\r\n" + - "\r\n", CharsetUtil.US_ASCII)); - - byte[] data = new byte[10]; - for (int i = 0; i < data.length; i++) { - data[i] = (byte) i; - } - ch.writeInbound(Unpooled.copiedBuffer(data, 0, data.length / 2)); - ch.writeInbound(Unpooled.copiedBuffer(data, 5, data.length / 2)); - - HttpResponse res = ch.readInbound(); - assertThat(res.protocolVersion(), sameInstance(HttpVersion.HTTP_1_1)); - assertThat(res.status(), is(HttpResponseStatus.OK)); - - HttpContent firstContent = ch.readInbound(); - assertThat(firstContent.content().readableBytes(), is(5)); - assertEquals(Unpooled.copiedBuffer(data, 0, 5), firstContent.content()); - firstContent.release(); - - LastHttpContent lastContent = ch.readInbound(); - assertEquals(5, lastContent.content().readableBytes()); - assertEquals(Unpooled.copiedBuffer(data, 5, 5), lastContent.content()); - lastContent.release(); - - assertThat(ch.finish(), is(false)); - assertThat(ch.readInbound(), is(nullValue())); - } - - @Test - public void testResponseWithContentLengthFragmented() { - byte[] data = ("HTTP/1.1 200 OK\r\n" + - "Content-Length: 10\r\n" + - "\r\n").getBytes(CharsetUtil.US_ASCII); - - for (int i = 1; i < data.length; i++) { - testResponseWithContentLengthFragmented(data, i); - } - } - - private static void testResponseWithContentLengthFragmented(byte[] header, int fragmentSize) { - EmbeddedChannel ch = new EmbeddedChannel(new HttpResponseDecoder()); - // split up the header - for (int a = 0; a < header.length;) { - int amount = fragmentSize; - if (a + amount > header.length) { - amount = header.length - a; - } - - ch.writeInbound(Unpooled.copiedBuffer(header, a, amount)); - a += amount; - } - byte[] data = new byte[10]; - for (int i = 0; i < data.length; i++) { - data[i] = (byte) i; - } - ch.writeInbound(Unpooled.copiedBuffer(data, 0, data.length / 2)); - ch.writeInbound(Unpooled.copiedBuffer(data, 5, data.length / 2)); - - HttpResponse res = ch.readInbound(); - assertThat(res.protocolVersion(), sameInstance(HttpVersion.HTTP_1_1)); - assertThat(res.status(), is(HttpResponseStatus.OK)); - - HttpContent firstContent = ch.readInbound(); - assertThat(firstContent.content().readableBytes(), is(5)); - assertEquals(Unpooled.wrappedBuffer(data, 0, 5), firstContent.content()); - firstContent.release(); - - LastHttpContent lastContent = ch.readInbound(); - assertEquals(5, lastContent.content().readableBytes()); - assertEquals(Unpooled.wrappedBuffer(data, 5, 5), lastContent.content()); - lastContent.release(); - - assertThat(ch.finish(), is(false)); - assertThat(ch.readInbound(), is(nullValue())); - } - - @Test - public void testWebSocketResponse() { - byte[] data = ("HTTP/1.1 101 WebSocket Protocol Handshake\r\n" + - "Upgrade: WebSocket\r\n" + - "Connection: Upgrade\r\n" + - "Sec-WebSocket-Origin: http://localhost:8080\r\n" + - "Sec-WebSocket-Location: ws://localhost/some/path\r\n" + - "\r\n" + - "1234567812345678").getBytes(); - EmbeddedChannel ch = new EmbeddedChannel(new HttpResponseDecoder()); - ch.writeInbound(Unpooled.wrappedBuffer(data)); - - HttpResponse res = ch.readInbound(); - assertThat(res.protocolVersion(), sameInstance(HttpVersion.HTTP_1_1)); - assertThat(res.status(), is(HttpResponseStatus.SWITCHING_PROTOCOLS)); - HttpContent content = ch.readInbound(); - assertThat(content.content().readableBytes(), is(16)); - content.release(); - - assertThat(ch.finish(), is(false)); - - assertThat(ch.readInbound(), is(nullValue())); - } - - // See https://github.com/netty/netty/issues/2173 - @Test - public void testWebSocketResponseWithDataFollowing() { - byte[] data = ("HTTP/1.1 101 WebSocket Protocol Handshake\r\n" + - "Upgrade: WebSocket\r\n" + - "Connection: Upgrade\r\n" + - "Sec-WebSocket-Origin: http://localhost:8080\r\n" + - "Sec-WebSocket-Location: ws://localhost/some/path\r\n" + - "\r\n" + - "1234567812345678").getBytes(); - byte[] otherData = {1, 2, 3, 4}; - - EmbeddedChannel ch = new EmbeddedChannel(new HttpResponseDecoder()); - ch.writeInbound(Unpooled.copiedBuffer(data, otherData)); - - HttpResponse res = ch.readInbound(); - assertThat(res.protocolVersion(), sameInstance(HttpVersion.HTTP_1_1)); - assertThat(res.status(), is(HttpResponseStatus.SWITCHING_PROTOCOLS)); - HttpContent content = ch.readInbound(); - assertThat(content.content().readableBytes(), is(16)); - content.release(); - - assertThat(ch.finish(), is(true)); - - ByteBuf expected = Unpooled.wrappedBuffer(otherData); - ByteBuf buffer = ch.readInbound(); - try { - assertEquals(expected, buffer); - } finally { - expected.release(); - if (buffer != null) { - buffer.release(); - } - } - } - - @Test - public void testGarbageHeaders() { - // A response without headers - from https://github.com/netty/netty/issues/2103 - byte[] data = ("\r\n" + - "400 Bad Request\r\n" + - "\r\n" + - "

400 Bad Request

\r\n" + - "
nginx/1.1.19
\r\n" + - "\r\n" + - "\r\n").getBytes(); - - EmbeddedChannel ch = new EmbeddedChannel(new HttpResponseDecoder()); - - ch.writeInbound(Unpooled.copiedBuffer(data)); - - // Garbage input should generate the 999 Unknown response. - HttpResponse res = ch.readInbound(); - assertThat(res.protocolVersion(), sameInstance(HttpVersion.HTTP_1_0)); - assertThat(res.status().code(), is(999)); - assertThat(res.decoderResult().isFailure(), is(true)); - assertThat(res.decoderResult().isFinished(), is(true)); - assertThat(ch.readInbound(), is(nullValue())); - - // More garbage should not generate anything (i.e. the decoder discards anything beyond this point.) - ch.writeInbound(Unpooled.copiedBuffer(data)); - assertThat(ch.readInbound(), is(nullValue())); - - // Closing the connection should not generate anything since the protocol has been violated. - ch.finish(); - assertThat(ch.readInbound(), is(nullValue())); - } - - /** - * Tests if the decoder produces one and only {@link LastHttpContent} when an invalid chunk is received and - * the connection is closed. - */ - @Test - public void testGarbageChunk() { - EmbeddedChannel channel = new EmbeddedChannel(new HttpResponseDecoder()); - String responseWithIllegalChunk = - "HTTP/1.1 200 OK\r\n" + - "Transfer-Encoding: chunked\r\n\r\n" + - "NOT_A_CHUNK_LENGTH\r\n"; - - channel.writeInbound(Unpooled.copiedBuffer(responseWithIllegalChunk, CharsetUtil.US_ASCII)); - assertThat(channel.readInbound(), is(instanceOf(HttpResponse.class))); - - // Ensure that the decoder generates the last chunk with correct decoder result. - LastHttpContent invalidChunk = channel.readInbound(); - assertThat(invalidChunk.decoderResult().isFailure(), is(true)); - invalidChunk.release(); - - // And no more messages should be produced by the decoder. - assertThat(channel.readInbound(), is(nullValue())); - - // .. even after the connection is closed. - assertThat(channel.finish(), is(false)); - } - - @Test - public void testConnectionClosedBeforeHeadersReceived() { - EmbeddedChannel channel = new EmbeddedChannel(new HttpResponseDecoder()); - String responseInitialLine = - "HTTP/1.1 200 OK\r\n"; - assertFalse(channel.writeInbound(Unpooled.copiedBuffer(responseInitialLine, CharsetUtil.US_ASCII))); - assertTrue(channel.finish()); - HttpMessage message = channel.readInbound(); - assertTrue(message.decoderResult().isFailure()); - 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()); - } - - @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()); - } - - @Test - public void testHttpMessageDecoderResult() { - String responseStr = "HTTP/1.1 200 OK\r\n" + - "Content-Length: 11\r\n" + - "Connection: close\r\n\r\n" + - "Lorem ipsum"; - EmbeddedChannel channel = new EmbeddedChannel(new HttpResponseDecoder()); - assertTrue(channel.writeInbound(Unpooled.copiedBuffer(responseStr, CharsetUtil.US_ASCII))); - HttpResponse response = channel.readInbound(); - assertTrue(response.decoderResult().isSuccess()); - assertThat(response.decoderResult(), instanceOf(HttpMessageDecoderResult.class)); - HttpMessageDecoderResult decoderResult = (HttpMessageDecoderResult) response.decoderResult(); - assertThat(decoderResult.initialLineLength(), is(15)); - assertThat(decoderResult.headerSize(), is(35)); - assertThat(decoderResult.totalSize(), is(50)); - HttpContent c = channel.readInbound(); - c.release(); - assertFalse(channel.finish()); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpResponseEncoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpResponseEncoderTest.java deleted file mode 100644 index 3ecd918a90..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpResponseEncoderTest.java +++ /dev/null @@ -1,404 +0,0 @@ -/* -* Copyright 2014 The Netty Project -* -* The Netty Project licenses this file to you under the Apache License, -* version 2.0 (the "License"); you may not use this file except in compliance -* with the License. You may obtain a copy of the License at: -* -* https://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -* License for the specific language governing permissions and limitations -* under the License. -*/ -package io.netty.handler.codec.http; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.FileRegion; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.CharsetUtil; -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.nio.channels.WritableByteChannel; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class HttpResponseEncoderTest { - private static final long INTEGER_OVERFLOW = (long) Integer.MAX_VALUE + 1; - private static final FileRegion FILE_REGION = new DummyLongFileRegion(); - - @Test - public void testLargeFileRegionChunked() throws Exception { - EmbeddedChannel channel = new EmbeddedChannel(new HttpResponseEncoder()); - HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - response.headers().set(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED); - assertTrue(channel.writeOutbound(response)); - - ByteBuf buffer = channel.readOutbound(); - - assertEquals("HTTP/1.1 200 OK\r\n" + HttpHeaderNames.TRANSFER_ENCODING + ": " + - HttpHeaderValues.CHUNKED + "\r\n\r\n", buffer.toString(CharsetUtil.US_ASCII)); - buffer.release(); - assertTrue(channel.writeOutbound(FILE_REGION)); - buffer = channel.readOutbound(); - assertEquals("80000000\r\n", buffer.toString(CharsetUtil.US_ASCII)); - buffer.release(); - - FileRegion region = channel.readOutbound(); - assertSame(FILE_REGION, region); - region.release(); - buffer = channel.readOutbound(); - assertEquals("\r\n", buffer.toString(CharsetUtil.US_ASCII)); - buffer.release(); - - assertTrue(channel.writeOutbound(LastHttpContent.EMPTY_LAST_CONTENT)); - buffer = channel.readOutbound(); - assertEquals("0\r\n\r\n", buffer.toString(CharsetUtil.US_ASCII)); - buffer.release(); - - assertFalse(channel.finish()); - } - - private static class DummyLongFileRegion implements FileRegion { - - @Override - public long position() { - return 0; - } - - @Override - public long transfered() { - return 0; - } - - @Override - public long transferred() { - return 0; - } - - @Override - public long count() { - return INTEGER_OVERFLOW; - } - - @Override - public long transferTo(WritableByteChannel target, long position) throws IOException { - throw new UnsupportedOperationException(); - } - - @Override - public FileRegion touch(Object hint) { - return this; - } - - @Override - public FileRegion touch() { - return this; - } - - @Override - public FileRegion retain() { - return this; - } - - @Override - public FileRegion retain(int increment) { - return this; - } - - @Override - public int refCnt() { - return 1; - } - - @Override - public boolean release() { - return false; - } - - @Override - public boolean release(int decrement) { - return false; - } - } - - @Test - public void testEmptyBufferBypass() throws Exception { - EmbeddedChannel channel = new EmbeddedChannel(new HttpResponseEncoder()); - - // Test writing an empty buffer works when the encoder is at ST_INIT. - channel.writeOutbound(Unpooled.EMPTY_BUFFER); - ByteBuf buffer = channel.readOutbound(); - assertThat(buffer, is(sameInstance(Unpooled.EMPTY_BUFFER))); - - // Leave the ST_INIT state. - HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - assertTrue(channel.writeOutbound(response)); - buffer = channel.readOutbound(); - assertEquals("HTTP/1.1 200 OK\r\n\r\n", buffer.toString(CharsetUtil.US_ASCII)); - buffer.release(); - - // Test writing an empty buffer works when the encoder is not at ST_INIT. - channel.writeOutbound(Unpooled.EMPTY_BUFFER); - buffer = channel.readOutbound(); - assertThat(buffer, is(sameInstance(Unpooled.EMPTY_BUFFER))); - - assertFalse(channel.finish()); - } - - @Test - public void testEmptyContentChunked() throws Exception { - testEmptyContent(true); - } - - @Test - public void testEmptyContentNotChunked() throws Exception { - testEmptyContent(false); - } - - private static void testEmptyContent(boolean chunked) throws Exception { - String content = "netty rocks"; - ByteBuf contentBuffer = Unpooled.copiedBuffer(content, CharsetUtil.US_ASCII); - int length = contentBuffer.readableBytes(); - - EmbeddedChannel channel = new EmbeddedChannel(new HttpResponseEncoder()); - HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - if (!chunked) { - HttpUtil.setContentLength(response, length); - } - assertTrue(channel.writeOutbound(response)); - assertTrue(channel.writeOutbound(new DefaultHttpContent(Unpooled.EMPTY_BUFFER))); - assertTrue(channel.writeOutbound(new DefaultLastHttpContent(contentBuffer))); - - ByteBuf buffer = channel.readOutbound(); - if (!chunked) { - assertEquals("HTTP/1.1 200 OK\r\ncontent-length: " + length + "\r\n\r\n", - buffer.toString(CharsetUtil.US_ASCII)); - } else { - assertEquals("HTTP/1.1 200 OK\r\n\r\n", buffer.toString(CharsetUtil.US_ASCII)); - } - buffer.release(); - - // Test writing an empty buffer works when the encoder is not at ST_INIT. - buffer = channel.readOutbound(); - assertEquals(0, buffer.readableBytes()); - buffer.release(); - - buffer = channel.readOutbound(); - assertEquals(length, buffer.readableBytes()); - buffer.release(); - - assertFalse(channel.finish()); - } - - @Test - public void testStatusNoContent() throws Exception { - EmbeddedChannel channel = new EmbeddedChannel(new HttpResponseEncoder()); - assertEmptyResponse(channel, HttpResponseStatus.NO_CONTENT, null, false); - assertFalse(channel.finish()); - } - - @Test - public void testStatusNoContentContentLength() throws Exception { - EmbeddedChannel channel = new EmbeddedChannel(new HttpResponseEncoder()); - assertEmptyResponse(channel, HttpResponseStatus.NO_CONTENT, HttpHeaderNames.CONTENT_LENGTH, true); - assertFalse(channel.finish()); - } - - @Test - public void testStatusNoContentTransferEncoding() throws Exception { - EmbeddedChannel channel = new EmbeddedChannel(new HttpResponseEncoder()); - assertEmptyResponse(channel, HttpResponseStatus.NO_CONTENT, HttpHeaderNames.TRANSFER_ENCODING, true); - assertFalse(channel.finish()); - } - - @Test - public void testStatusNotModified() throws Exception { - EmbeddedChannel channel = new EmbeddedChannel(new HttpResponseEncoder()); - assertEmptyResponse(channel, HttpResponseStatus.NOT_MODIFIED, null, false); - assertFalse(channel.finish()); - } - - @Test - public void testStatusNotModifiedContentLength() throws Exception { - EmbeddedChannel channel = new EmbeddedChannel(new HttpResponseEncoder()); - assertEmptyResponse(channel, HttpResponseStatus.NOT_MODIFIED, HttpHeaderNames.CONTENT_LENGTH, false); - assertFalse(channel.finish()); - } - - @Test - public void testStatusNotModifiedTransferEncoding() throws Exception { - EmbeddedChannel channel = new EmbeddedChannel(new HttpResponseEncoder()); - assertEmptyResponse(channel, HttpResponseStatus.NOT_MODIFIED, HttpHeaderNames.TRANSFER_ENCODING, false); - assertFalse(channel.finish()); - } - - @Test - public void testStatusInformational() throws Exception { - EmbeddedChannel channel = new EmbeddedChannel(new HttpResponseEncoder()); - for (int code = 100; code < 200; code++) { - HttpResponseStatus status = HttpResponseStatus.valueOf(code); - assertEmptyResponse(channel, status, null, false); - } - assertFalse(channel.finish()); - } - - @Test - public void testStatusInformationalContentLength() throws Exception { - EmbeddedChannel channel = new EmbeddedChannel(new HttpResponseEncoder()); - for (int code = 100; code < 200; code++) { - HttpResponseStatus status = HttpResponseStatus.valueOf(code); - assertEmptyResponse(channel, status, HttpHeaderNames.CONTENT_LENGTH, code != 101); - } - assertFalse(channel.finish()); - } - - @Test - public void testStatusInformationalTransferEncoding() throws Exception { - EmbeddedChannel channel = new EmbeddedChannel(new HttpResponseEncoder()); - for (int code = 100; code < 200; code++) { - HttpResponseStatus status = HttpResponseStatus.valueOf(code); - assertEmptyResponse(channel, status, HttpHeaderNames.TRANSFER_ENCODING, code != 101); - } - assertFalse(channel.finish()); - } - - private static void assertEmptyResponse(EmbeddedChannel channel, HttpResponseStatus status, - CharSequence headerName, boolean headerStripped) { - HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, status); - if (HttpHeaderNames.CONTENT_LENGTH.contentEquals(headerName)) { - response.headers().set(headerName, "0"); - } else if (HttpHeaderNames.TRANSFER_ENCODING.contentEquals(headerName)) { - response.headers().set(headerName, HttpHeaderValues.CHUNKED); - } - - assertTrue(channel.writeOutbound(response)); - assertTrue(channel.writeOutbound(LastHttpContent.EMPTY_LAST_CONTENT)); - - ByteBuf buffer = channel.readOutbound(); - StringBuilder responseText = new StringBuilder(); - responseText.append(HttpVersion.HTTP_1_1).append(' ').append(status.toString()).append("\r\n"); - if (!headerStripped && headerName != null) { - responseText.append(headerName).append(": "); - - if (HttpHeaderNames.CONTENT_LENGTH.contentEquals(headerName)) { - responseText.append('0'); - } else { - responseText.append(HttpHeaderValues.CHUNKED); - } - responseText.append("\r\n"); - } - responseText.append("\r\n"); - - assertEquals(responseText.toString(), buffer.toString(CharsetUtil.US_ASCII)); - - buffer.release(); - - buffer = channel.readOutbound(); - buffer.release(); - } - - @Test - public void testEmptyContentsChunked() throws Exception { - testEmptyContents(true, false); - } - - @Test - public void testEmptyContentsChunkedWithTrailers() throws Exception { - testEmptyContents(true, true); - } - - @Test - public void testEmptyContentsNotChunked() throws Exception { - testEmptyContents(false, false); - } - - @Test - public void testEmptyContentNotsChunkedWithTrailers() throws Exception { - testEmptyContents(false, true); - } - - private void testEmptyContents(boolean chunked, boolean trailers) throws Exception { - HttpResponseEncoder encoder = new HttpResponseEncoder(); - EmbeddedChannel channel = new EmbeddedChannel(encoder); - HttpResponse request = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - if (chunked) { - HttpUtil.setTransferEncodingChunked(request, true); - } - assertTrue(channel.writeOutbound(request)); - - ByteBuf contentBuffer = Unpooled.buffer(); - assertTrue(channel.writeOutbound(new DefaultHttpContent(contentBuffer))); - - ByteBuf lastContentBuffer = Unpooled.buffer(); - LastHttpContent last = new DefaultLastHttpContent(lastContentBuffer); - if (trailers) { - last.trailingHeaders().set("X-Netty-Test", "true"); - } - assertTrue(channel.writeOutbound(last)); - - // Ensure we only produce ByteBuf instances. - ByteBuf head = channel.readOutbound(); - assertTrue(head.release()); - - ByteBuf content = channel.readOutbound(); - content.release(); - - ByteBuf lastContent = channel.readOutbound(); - lastContent.release(); - assertFalse(channel.finish()); - } - - @Test - public void testStatusResetContentTransferContentLength() { - testStatusResetContentTransferContentLength0(HttpHeaderNames.CONTENT_LENGTH, Unpooled.buffer().writeLong(8)); - } - - @Test - public void testStatusResetContentTransferEncoding() { - testStatusResetContentTransferContentLength0(HttpHeaderNames.TRANSFER_ENCODING, Unpooled.buffer().writeLong(8)); - } - - private static void testStatusResetContentTransferContentLength0(CharSequence headerName, ByteBuf content) { - EmbeddedChannel channel = new EmbeddedChannel(new HttpResponseEncoder()); - - HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.RESET_CONTENT); - if (HttpHeaderNames.CONTENT_LENGTH.contentEqualsIgnoreCase(headerName)) { - response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes()); - } else { - response.headers().set(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED); - } - - assertTrue(channel.writeOutbound(response)); - assertTrue(channel.writeOutbound(new DefaultHttpContent(content))); - assertTrue(channel.writeOutbound(LastHttpContent.EMPTY_LAST_CONTENT)); - - StringBuilder responseText = new StringBuilder(); - responseText.append(HttpVersion.HTTP_1_1).append(' ') - .append(HttpResponseStatus.RESET_CONTENT).append("\r\n"); - responseText.append(HttpHeaderNames.CONTENT_LENGTH).append(": 0\r\n"); - responseText.append("\r\n"); - - StringBuilder written = new StringBuilder(); - for (;;) { - ByteBuf buffer = channel.readOutbound(); - if (buffer == null) { - break; - } - written.append(buffer.toString(CharsetUtil.US_ASCII)); - buffer.release(); - } - - assertEquals(responseText.toString(), written.toString()); - assertFalse(channel.finish()); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpResponseStatusTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpResponseStatusTest.java deleted file mode 100644 index f190dbd807..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpResponseStatusTest.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.util.AsciiString; -import org.junit.jupiter.api.Test; - -import static io.netty.handler.codec.http.HttpResponseStatus.parseLine; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertThrows; - -public class HttpResponseStatusTest { - @Test - public void parseLineStringJustCode() { - assertSame(HttpResponseStatus.OK, parseLine("200")); - } - - @Test - public void parseLineStringCodeAndPhrase() { - assertSame(HttpResponseStatus.OK, parseLine("200 OK")); - } - - @Test - public void parseLineStringCustomCode() { - HttpResponseStatus customStatus = parseLine("612"); - assertEquals(612, customStatus.code()); - } - - @Test - public void parseLineStringCustomCodeAndPhrase() { - HttpResponseStatus customStatus = parseLine("612 FOO"); - assertEquals(612, customStatus.code()); - assertEquals("FOO", customStatus.reasonPhrase()); - } - - @Test - public void parseLineStringMalformedCode() { - assertThrows(IllegalArgumentException.class, () -> parseLine("200a")); - } - - @Test - public void parseLineStringMalformedCodeWithPhrase() { - assertThrows(IllegalArgumentException.class, () -> parseLine("200a foo")); - } - - @Test - public void parseLineAsciiStringJustCode() { - assertSame(HttpResponseStatus.OK, parseLine(new AsciiString("200"))); - } - - @Test - public void parseLineAsciiStringCodeAndPhrase() { - assertSame(HttpResponseStatus.OK, parseLine(new AsciiString("200 OK"))); - } - - @Test - public void parseLineAsciiStringCustomCode() { - HttpResponseStatus customStatus = parseLine(new AsciiString("612")); - assertEquals(612, customStatus.code()); - } - - @Test - public void parseLineAsciiStringCustomCodeAndPhrase() { - HttpResponseStatus customStatus = parseLine(new AsciiString("612 FOO")); - assertEquals(612, customStatus.code()); - assertEquals("FOO", customStatus.reasonPhrase()); - } - - @Test - public void parseLineAsciiStringMalformedCode() { - assertThrows(IllegalArgumentException.class, () -> parseLine(new AsciiString("200a"))); - } - - @Test - public void parseLineAsciiStringMalformedCodeWithPhrase() { - assertThrows(IllegalArgumentException.class, () -> parseLine(new AsciiString("200a foo"))); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpServerCodecTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpServerCodecTest.java deleted file mode 100644 index 0ffdc9b744..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpServerCodecTest.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.CharsetUtil; -import org.junit.jupiter.api.Test; - -import static org.hamcrest.CoreMatchers.*; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class HttpServerCodecTest { - - /** - * Testcase for https://github.com/netty/netty/issues/433 - */ - @Test - public void testUnfinishedChunkedHttpRequestIsLastFlag() throws Exception { - - int maxChunkSize = 2000; - HttpServerCodec httpServerCodec = new HttpServerCodec(1000, 1000); - EmbeddedChannel decoderEmbedder = new EmbeddedChannel(httpServerCodec); - - int totalContentLength = maxChunkSize * 5; - decoderEmbedder.writeInbound(Unpooled.copiedBuffer( - "PUT /test HTTP/1.1\r\n" + - "Content-Length: " + totalContentLength + "\r\n" + - "\r\n", CharsetUtil.UTF_8)); - - int offeredContentLength = (int) (maxChunkSize * 2.5); - decoderEmbedder.writeInbound(prepareDataChunk(offeredContentLength)); - decoderEmbedder.finish(); - - HttpMessage httpMessage = decoderEmbedder.readInbound(); - assertNotNull(httpMessage); - - boolean empty = true; - int totalBytesPolled = 0; - for (;;) { - HttpContent httpChunk = decoderEmbedder.readInbound(); - if (httpChunk == null) { - break; - } - empty = false; - totalBytesPolled += httpChunk.content().readableBytes(); - assertFalse(httpChunk instanceof LastHttpContent); - httpChunk.release(); - } - assertFalse(empty); - assertEquals(offeredContentLength, totalBytesPolled); - } - - @Test - public void test100Continue() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new HttpServerCodec(), new HttpObjectAggregator(1024)); - - // Send the request headers. - ch.writeInbound(Unpooled.copiedBuffer( - "PUT /upload-large HTTP/1.1\r\n" + - "Expect: 100-continue\r\n" + - "Content-Length: 1\r\n\r\n", CharsetUtil.UTF_8)); - - // Ensure the aggregator generates nothing. - assertThat(ch.readInbound(), is(nullValue())); - - // Ensure the aggregator writes a 100 Continue response. - ByteBuf continueResponse = ch.readOutbound(); - assertThat(continueResponse.toString(CharsetUtil.UTF_8), is("HTTP/1.1 100 Continue\r\n\r\n")); - continueResponse.release(); - - // But nothing more. - assertThat(ch.readOutbound(), is(nullValue())); - - // Send the content of the request. - ch.writeInbound(Unpooled.wrappedBuffer(new byte[] { 42 })); - - // Ensure the aggregator generates a full request. - FullHttpRequest req = ch.readInbound(); - assertThat(req.headers().get(HttpHeaderNames.CONTENT_LENGTH), is("1")); - assertThat(req.content().readableBytes(), is(1)); - assertThat(req.content().readByte(), is((byte) 42)); - req.release(); - - // But nothing more. - assertThat(ch.readInbound(), is(nullValue())); - - // Send the actual response. - FullHttpResponse res = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CREATED); - res.content().writeBytes("OK".getBytes(CharsetUtil.UTF_8)); - res.headers().setInt(HttpHeaderNames.CONTENT_LENGTH, 2); - ch.writeOutbound(res); - - // Ensure the encoder handles the response after handling 100 Continue. - ByteBuf encodedRes = ch.readOutbound(); - assertThat(encodedRes.toString(CharsetUtil.UTF_8), - is("HTTP/1.1 201 Created\r\n" + HttpHeaderNames.CONTENT_LENGTH + ": 2\r\n\r\nOK")); - encodedRes.release(); - - ch.finish(); - } - - @Test - public void testChunkedHeadResponse() { - EmbeddedChannel ch = new EmbeddedChannel(new HttpServerCodec()); - - // Send the request headers. - assertTrue(ch.writeInbound(Unpooled.copiedBuffer( - "HEAD / HTTP/1.1\r\n\r\n", CharsetUtil.UTF_8))); - - HttpRequest request = ch.readInbound(); - assertEquals(HttpMethod.HEAD, request.method()); - LastHttpContent content = ch.readInbound(); - assertFalse(content.content().isReadable()); - content.release(); - - HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - HttpUtil.setTransferEncodingChunked(response, true); - assertTrue(ch.writeOutbound(response)); - assertTrue(ch.writeOutbound(LastHttpContent.EMPTY_LAST_CONTENT)); - assertTrue(ch.finish()); - - ByteBuf buf = ch.readOutbound(); - assertEquals("HTTP/1.1 200 OK\r\ntransfer-encoding: chunked\r\n\r\n", buf.toString(CharsetUtil.US_ASCII)); - buf.release(); - - buf = ch.readOutbound(); - assertFalse(buf.isReadable()); - buf.release(); - - assertFalse(ch.finishAndReleaseAll()); - } - - @Test - public void testChunkedHeadFullHttpResponse() { - EmbeddedChannel ch = new EmbeddedChannel(new HttpServerCodec()); - - // Send the request headers. - assertTrue(ch.writeInbound(Unpooled.copiedBuffer( - "HEAD / HTTP/1.1\r\n\r\n", CharsetUtil.UTF_8))); - - HttpRequest request = ch.readInbound(); - assertEquals(HttpMethod.HEAD, request.method()); - LastHttpContent content = ch.readInbound(); - assertFalse(content.content().isReadable()); - content.release(); - - FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - HttpUtil.setTransferEncodingChunked(response, true); - assertTrue(ch.writeOutbound(response)); - assertTrue(ch.finish()); - - ByteBuf buf = ch.readOutbound(); - assertEquals("HTTP/1.1 200 OK\r\ntransfer-encoding: chunked\r\n\r\n", buf.toString(CharsetUtil.US_ASCII)); - buf.release(); - - assertFalse(ch.finishAndReleaseAll()); - } - - private static ByteBuf prepareDataChunk(int size) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < size; ++i) { - sb.append('a'); - } - return Unpooled.copiedBuffer(sb.toString(), CharsetUtil.UTF_8); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpServerExpectContinueHandlerTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpServerExpectContinueHandlerTest.java deleted file mode 100644 index d908c8d7dd..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpServerExpectContinueHandlerTest.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.ReferenceCountUtil; -import org.junit.jupiter.api.Test; - -import static org.hamcrest.CoreMatchers.*; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class HttpServerExpectContinueHandlerTest { - - @Test - public void shouldRespondToExpectedHeader() { - EmbeddedChannel channel = new EmbeddedChannel(new HttpServerExpectContinueHandler() { - @Override - protected HttpResponse acceptMessage(HttpRequest request) { - HttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE); - response.headers().set("foo", "bar"); - return response; - } - }); - HttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/"); - HttpUtil.set100ContinueExpected(request, true); - - channel.writeInbound(request); - HttpResponse response = channel.readOutbound(); - - assertThat(response.status(), is(HttpResponseStatus.CONTINUE)); - assertThat(response.headers().get("foo"), is("bar")); - ReferenceCountUtil.release(response); - - HttpRequest processedRequest = channel.readInbound(); - assertFalse(processedRequest.headers().contains(HttpHeaderNames.EXPECT)); - ReferenceCountUtil.release(processedRequest); - assertFalse(channel.finishAndReleaseAll()); - } - - @Test - public void shouldAllowCustomResponses() { - EmbeddedChannel channel = new EmbeddedChannel( - new HttpServerExpectContinueHandler() { - @Override - protected HttpResponse acceptMessage(HttpRequest request) { - return null; - } - - @Override - protected HttpResponse rejectResponse(HttpRequest request) { - return new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, - HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE); - } - } - ); - - HttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/"); - HttpUtil.set100ContinueExpected(request, true); - - channel.writeInbound(request); - HttpResponse response = channel.readOutbound(); - - assertThat(response.status(), is(HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE)); - ReferenceCountUtil.release(response); - - // request was swallowed - assertTrue(channel.inboundMessages().isEmpty()); - assertFalse(channel.finishAndReleaseAll()); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpServerKeepAliveHandlerTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpServerKeepAliveHandlerTest.java deleted file mode 100644 index 66462916f0..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpServerKeepAliveHandlerTest.java +++ /dev/null @@ -1,219 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.AsciiString; -import io.netty.util.ReferenceCountUtil; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -import java.util.Arrays; -import java.util.Collection; - -import static io.netty.handler.codec.http.HttpHeaderValues.CLOSE; -import static io.netty.handler.codec.http.HttpHeaderValues.KEEP_ALIVE; -import static io.netty.handler.codec.http.HttpHeaderValues.MULTIPART_MIXED; -import static io.netty.handler.codec.http.HttpResponseStatus.NO_CONTENT; -import static io.netty.handler.codec.http.HttpResponseStatus.OK; -import static io.netty.handler.codec.http.HttpUtil.isContentLengthSet; -import static io.netty.handler.codec.http.HttpUtil.isKeepAlive; -import static io.netty.handler.codec.http.HttpUtil.setContentLength; -import static io.netty.handler.codec.http.HttpUtil.setKeepAlive; -import static io.netty.handler.codec.http.HttpUtil.setTransferEncodingChunked; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class HttpServerKeepAliveHandlerTest { - private static final String REQUEST_KEEP_ALIVE = "REQUEST_KEEP_ALIVE"; - private static final int NOT_SELF_DEFINED_MSG_LENGTH = 0; - private static final int SET_RESPONSE_LENGTH = 1; - private static final int SET_MULTIPART = 2; - private static final int SET_CHUNKED = 4; - - private EmbeddedChannel channel; - - @BeforeEach - public void setUp() { - channel = new EmbeddedChannel(new HttpServerKeepAliveHandler()); - } - - static Collection keepAliveProvider() { - return Arrays.asList(new Object[][] { - { true, HttpVersion.HTTP_1_0, OK, REQUEST_KEEP_ALIVE, SET_RESPONSE_LENGTH, KEEP_ALIVE }, // 0 - { true, HttpVersion.HTTP_1_0, OK, REQUEST_KEEP_ALIVE, SET_MULTIPART, KEEP_ALIVE }, // 1 - { false, HttpVersion.HTTP_1_0, OK, null, SET_RESPONSE_LENGTH, null }, // 2 - { true, HttpVersion.HTTP_1_1, OK, REQUEST_KEEP_ALIVE, SET_RESPONSE_LENGTH, null }, // 3 - { false, HttpVersion.HTTP_1_1, OK, REQUEST_KEEP_ALIVE, SET_RESPONSE_LENGTH, CLOSE }, // 4 - { true, HttpVersion.HTTP_1_1, OK, REQUEST_KEEP_ALIVE, SET_MULTIPART, null }, // 5 - { true, HttpVersion.HTTP_1_1, OK, REQUEST_KEEP_ALIVE, SET_CHUNKED, null }, // 6 - { false, HttpVersion.HTTP_1_1, OK, null, SET_RESPONSE_LENGTH, null }, // 7 - { false, HttpVersion.HTTP_1_0, OK, REQUEST_KEEP_ALIVE, NOT_SELF_DEFINED_MSG_LENGTH, null }, // 8 - { false, HttpVersion.HTTP_1_0, OK, null, NOT_SELF_DEFINED_MSG_LENGTH, null }, // 9 - { false, HttpVersion.HTTP_1_1, OK, REQUEST_KEEP_ALIVE, NOT_SELF_DEFINED_MSG_LENGTH, null }, // 10 - { false, HttpVersion.HTTP_1_1, OK, null, NOT_SELF_DEFINED_MSG_LENGTH, null }, // 11 - { false, HttpVersion.HTTP_1_0, OK, REQUEST_KEEP_ALIVE, SET_RESPONSE_LENGTH, null }, // 12 - { true, HttpVersion.HTTP_1_1, NO_CONTENT, REQUEST_KEEP_ALIVE, NOT_SELF_DEFINED_MSG_LENGTH, null}, // 13 - { false, HttpVersion.HTTP_1_0, NO_CONTENT, null, NOT_SELF_DEFINED_MSG_LENGTH, null} // 14 - }); - } - - @ParameterizedTest - @MethodSource("keepAliveProvider") - public void test_KeepAlive(boolean isKeepAliveResponseExpected, HttpVersion httpVersion, - HttpResponseStatus responseStatus, - String sendKeepAlive, int setSelfDefinedMessageLength, - AsciiString setResponseConnection) throws Exception { - FullHttpRequest request = new DefaultFullHttpRequest(httpVersion, HttpMethod.GET, "/v1/foo/bar"); - setKeepAlive(request, REQUEST_KEEP_ALIVE.equals(sendKeepAlive)); - HttpResponse response = new DefaultFullHttpResponse(httpVersion, responseStatus); - if (setResponseConnection != null) { - response.headers().set(HttpHeaderNames.CONNECTION, setResponseConnection); - } - setupMessageLength(response, setSelfDefinedMessageLength); - - assertTrue(channel.writeInbound(request)); - Object requestForwarded = channel.readInbound(); - assertEquals(request, requestForwarded); - ReferenceCountUtil.release(requestForwarded); - channel.writeAndFlush(response); - HttpResponse writtenResponse = channel.readOutbound(); - - assertEquals(isKeepAliveResponseExpected, channel.isOpen(), "channel.isOpen"); - assertEquals(isKeepAliveResponseExpected, isKeepAlive(writtenResponse), "response keep-alive"); - ReferenceCountUtil.release(writtenResponse); - assertFalse(channel.finishAndReleaseAll()); - } - - static Collection connectionCloseProvider() { - return Arrays.asList(new Object[][] { - { HttpVersion.HTTP_1_0, OK, SET_RESPONSE_LENGTH }, - { HttpVersion.HTTP_1_0, OK, SET_MULTIPART }, - { HttpVersion.HTTP_1_0, OK, NOT_SELF_DEFINED_MSG_LENGTH }, - { HttpVersion.HTTP_1_0, NO_CONTENT, NOT_SELF_DEFINED_MSG_LENGTH }, - { HttpVersion.HTTP_1_1, OK, SET_RESPONSE_LENGTH }, - { HttpVersion.HTTP_1_1, OK, SET_MULTIPART }, - { HttpVersion.HTTP_1_1, OK, NOT_SELF_DEFINED_MSG_LENGTH }, - { HttpVersion.HTTP_1_1, OK, SET_CHUNKED }, - { HttpVersion.HTTP_1_1, NO_CONTENT, NOT_SELF_DEFINED_MSG_LENGTH } - }); - } - - @ParameterizedTest - @MethodSource("connectionCloseProvider") - public void testConnectionCloseHeaderHandledCorrectly( - HttpVersion httpVersion, HttpResponseStatus responseStatus, int setSelfDefinedMessageLength) { - HttpResponse response = new DefaultFullHttpResponse(httpVersion, responseStatus); - response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE); - setupMessageLength(response, setSelfDefinedMessageLength); - - channel.writeAndFlush(response); - HttpResponse writtenResponse = channel.readOutbound(); - - assertFalse(channel.isOpen()); - ReferenceCountUtil.release(writtenResponse); - assertFalse(channel.finishAndReleaseAll()); - } - - @ParameterizedTest - @MethodSource("keepAliveProvider") - public void testPipelineKeepAlive(boolean isKeepAliveResponseExpected, HttpVersion httpVersion, - HttpResponseStatus responseStatus, - String sendKeepAlive, int setSelfDefinedMessageLength, - AsciiString setResponseConnection) { - FullHttpRequest firstRequest = new DefaultFullHttpRequest(httpVersion, HttpMethod.GET, "/v1/foo/bar"); - setKeepAlive(firstRequest, true); - FullHttpRequest secondRequest = new DefaultFullHttpRequest(httpVersion, HttpMethod.GET, "/v1/foo/bar"); - setKeepAlive(secondRequest, REQUEST_KEEP_ALIVE.equals(sendKeepAlive)); - FullHttpRequest finalRequest = new DefaultFullHttpRequest(httpVersion, HttpMethod.GET, "/v1/foo/bar"); - setKeepAlive(finalRequest, false); - FullHttpResponse response = new DefaultFullHttpResponse(httpVersion, responseStatus); - FullHttpResponse informationalResp = new DefaultFullHttpResponse(httpVersion, HttpResponseStatus.PROCESSING); - setKeepAlive(response, true); - setContentLength(response, 0); - setKeepAlive(informationalResp, true); - - assertTrue(channel.writeInbound(firstRequest, secondRequest, finalRequest)); - - Object requestForwarded = channel.readInbound(); - assertEquals(firstRequest, requestForwarded); - ReferenceCountUtil.release(requestForwarded); - - channel.writeAndFlush(response.retainedDuplicate()); - HttpResponse firstResponse = channel.readOutbound(); - assertTrue(channel.isOpen(), "channel.isOpen"); - assertTrue(isKeepAlive(firstResponse), "response keep-alive"); - ReferenceCountUtil.release(firstResponse); - - requestForwarded = channel.readInbound(); - assertEquals(secondRequest, requestForwarded); - ReferenceCountUtil.release(requestForwarded); - - channel.writeAndFlush(informationalResp); - HttpResponse writtenInfoResp = channel.readOutbound(); - assertTrue(channel.isOpen(), "channel.isOpen"); - assertTrue(isKeepAlive(writtenInfoResp), "response keep-alive"); - ReferenceCountUtil.release(writtenInfoResp); - - if (setResponseConnection != null) { - response.headers().set(HttpHeaderNames.CONNECTION, setResponseConnection); - } else { - response.headers().remove(HttpHeaderNames.CONNECTION); - } - setupMessageLength(response, setSelfDefinedMessageLength); - channel.writeAndFlush(response.retainedDuplicate()); - HttpResponse secondResponse = channel.readOutbound(); - assertEquals(isKeepAliveResponseExpected, channel.isOpen(), "channel.isOpen"); - assertEquals(isKeepAliveResponseExpected, isKeepAlive(secondResponse), "response keep-alive"); - ReferenceCountUtil.release(secondResponse); - - requestForwarded = channel.readInbound(); - assertEquals(finalRequest, requestForwarded); - ReferenceCountUtil.release(requestForwarded); - - if (isKeepAliveResponseExpected) { - channel.writeAndFlush(response); - HttpResponse finalResponse = channel.readOutbound(); - assertFalse(channel.isOpen(), "channel.isOpen"); - assertFalse(isKeepAlive(finalResponse), "response keep-alive"); - } - ReferenceCountUtil.release(response); - assertFalse(channel.finishAndReleaseAll()); - } - - private static void setupMessageLength(HttpResponse response, int setSelfDefinedMessageLength) { - switch (setSelfDefinedMessageLength) { - case NOT_SELF_DEFINED_MSG_LENGTH: - if (isContentLengthSet(response)) { - response.headers().remove(HttpHeaderNames.CONTENT_LENGTH); - } - break; - case SET_RESPONSE_LENGTH: - setContentLength(response, 0); - break; - case SET_CHUNKED: - setTransferEncodingChunked(response, true); - break; - case SET_MULTIPART: - response.headers().set(HttpHeaderNames.CONTENT_TYPE, MULTIPART_MIXED.toUpperCase()); - break; - default: - throw new IllegalArgumentException("selfDefinedMessageLength: " + setSelfDefinedMessageLength); - } - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpServerUpgradeHandlerTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpServerUpgradeHandlerTest.java deleted file mode 100644 index 7bc3a329e3..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpServerUpgradeHandlerTest.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeCodec; -import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeCodecFactory; -import io.netty.util.CharsetUtil; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; -import org.junit.jupiter.api.Test; - -import java.util.Collection; -import java.util.Collections; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - -public class HttpServerUpgradeHandlerTest { - - private static class TestUpgradeCodec implements UpgradeCodec { - @Override - public Collection requiredUpgradeHeaders() { - return Collections.emptyList(); - } - - @Override - public boolean prepareUpgradeResponse(ChannelHandlerContext ctx, FullHttpRequest upgradeRequest, - HttpHeaders upgradeHeaders) { - return true; - } - - @Override - public void upgradeTo(ChannelHandlerContext ctx, FullHttpRequest upgradeRequest) { - // Ensure that the HttpServerUpgradeHandler is still installed when this is called - assertEquals(ctx.pipeline().context(HttpServerUpgradeHandler.class), ctx); - assertNotNull(ctx.pipeline().get(HttpServerUpgradeHandler.class)); - - // Add a marker handler to signal that the upgrade has happened - ctx.pipeline().addAfter(ctx.name(), "marker", new ChannelHandler() { }); - } - } - - @Test - public void upgradesPipelineInSameMethodInvocation() { - final HttpServerCodec httpServerCodec = new HttpServerCodec(); - final UpgradeCodecFactory factory = protocol -> new TestUpgradeCodec(); - - ChannelHandler testInStackFrame = new ChannelHandler() { - // marker boolean to signal that we're in the `channelRead` method - private boolean inReadCall; - private boolean writeUpgradeMessage; - private boolean writeFlushed; - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - assertFalse(inReadCall); - assertFalse(writeUpgradeMessage); - - inReadCall = true; - try { - ctx.fireChannelRead(msg); - // All in the same call stack, the upgrade codec should receive the message, - // written the upgrade response, and upgraded the pipeline. - assertTrue(writeUpgradeMessage); - assertFalse(writeFlushed); - assertNull(ctx.pipeline().get(HttpServerCodec.class)); - assertNotNull(ctx.pipeline().get("marker")); - } finally { - inReadCall = false; - } - } - - @Override - public Future write(final ChannelHandlerContext ctx, final Object msg) { - // We ensure that we're in the read call and defer the write so we can - // make sure the pipeline was reformed irrespective of the flush completing. - assertTrue(inReadCall); - writeUpgradeMessage = true; - Promise promise = ctx.newPromise(); - ctx.channel().executor().execute(() -> ctx.write(msg).cascadeTo(promise)); - Future future = promise.asFuture(); - return future.addListener(f -> writeFlushed = true); - } - }; - - HttpServerUpgradeHandler upgradeHandler = new HttpServerUpgradeHandler(httpServerCodec, factory); - - EmbeddedChannel channel = new EmbeddedChannel(testInStackFrame, httpServerCodec, upgradeHandler); - - String upgradeString = "GET / HTTP/1.1\r\n" + - "Host: example.com\r\n" + - "Connection: Upgrade, HTTP2-Settings\r\n" + - "Upgrade: nextprotocol\r\n" + - "HTTP2-Settings: AAMAAABkAAQAAP__\r\n\r\n"; - ByteBuf upgrade = Unpooled.copiedBuffer(upgradeString, CharsetUtil.US_ASCII); - - assertFalse(channel.writeInbound(upgrade)); - assertNull(channel.pipeline().get(HttpServerCodec.class)); - assertNotNull(channel.pipeline().get("marker")); - - channel.flushOutbound(); - ByteBuf upgradeMessage = channel.readOutbound(); - String expectedHttpResponse = "HTTP/1.1 101 Switching Protocols\r\n" + - "connection: upgrade\r\n" + - "upgrade: nextprotocol\r\n\r\n"; - assertEquals(expectedHttpResponse, upgradeMessage.toString(CharsetUtil.US_ASCII)); - assertTrue(upgradeMessage.release()); - assertFalse(channel.finishAndReleaseAll()); - } - - @Test - public void skippedUpgrade() { - final HttpServerCodec httpServerCodec = new HttpServerCodec(); - final UpgradeCodecFactory factory = new UpgradeCodecFactory() { - @Override - public UpgradeCodec newUpgradeCodec(CharSequence protocol) { - fail("Should never be invoked"); - return null; - } - }; - - HttpServerUpgradeHandler upgradeHandler = new HttpServerUpgradeHandler(httpServerCodec, factory) { - @Override - protected boolean shouldHandleUpgradeRequest(HttpRequest req) { - return !req.headers().contains(HttpHeaderNames.UPGRADE, "do-not-upgrade", false); - } - }; - - EmbeddedChannel channel = new EmbeddedChannel(httpServerCodec, upgradeHandler); - - String upgradeString = "GET / HTTP/1.1\r\n" + - "Host: example.com\r\n" + - "Connection: Upgrade\r\n" + - "Upgrade: do-not-upgrade\r\n\r\n"; - ByteBuf upgrade = Unpooled.copiedBuffer(upgradeString, CharsetUtil.US_ASCII); - - // The upgrade request should not be passed to the next handler without any processing. - assertTrue(channel.writeInbound(upgrade)); - assertNotNull(channel.pipeline().get(HttpServerCodec.class)); - assertNull(channel.pipeline().get("marker")); - - HttpRequest req = channel.readInbound(); - assertFalse(req instanceof FullHttpRequest); // Should not be aggregated. - assertTrue(req.headers().contains(HttpHeaderNames.CONNECTION, "Upgrade", false)); - assertTrue(req.headers().contains(HttpHeaderNames.UPGRADE, "do-not-upgrade", false)); - assertTrue(channel.readInbound() instanceof LastHttpContent); - assertNull(channel.readInbound()); - - // No response should be written because we're just passing through. - channel.flushOutbound(); - assertNull(channel.readOutbound()); - assertFalse(channel.finishAndReleaseAll()); - } - - @Test - public void upgradeFail() { - final HttpServerCodec httpServerCodec = new HttpServerCodec(); - final UpgradeCodecFactory factory = new UpgradeCodecFactory() { - @Override - public UpgradeCodec newUpgradeCodec(CharSequence protocol) { - return new TestUpgradeCodec(); - } - }; - - HttpServerUpgradeHandler upgradeHandler = new HttpServerUpgradeHandler(httpServerCodec, factory); - - EmbeddedChannel channel = new EmbeddedChannel(httpServerCodec, upgradeHandler); - - // Build a h2c upgrade request, but without connection header. - String upgradeString = "GET / HTTP/1.1\r\n" + - "Host: example.com\r\n" + - "Upgrade: h2c\r\n\r\n"; - ByteBuf upgrade = Unpooled.copiedBuffer(upgradeString, CharsetUtil.US_ASCII); - - assertTrue(channel.writeInbound(upgrade)); - assertNotNull(channel.pipeline().get(HttpServerCodec.class)); - assertNotNull(channel.pipeline().get(HttpServerUpgradeHandler.class)); // Should not be removed. - assertNull(channel.pipeline().get("marker")); - - HttpRequest req = channel.readInbound(); - assertEquals(HttpVersion.HTTP_1_1, req.protocolVersion()); - assertTrue(req.headers().contains(HttpHeaderNames.UPGRADE, "h2c", false)); - assertFalse(req.headers().contains(HttpHeaderNames.CONNECTION)); - ReferenceCountUtil.release(req); - assertNull(channel.readInbound()); - - // No response should be written because we're just passing through. - channel.flushOutbound(); - assertNull(channel.readOutbound()); - assertFalse(channel.finishAndReleaseAll()); - } -} 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 deleted file mode 100644 index 21871a3be1..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpUtilTest.java +++ /dev/null @@ -1,425 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.util.CharsetUtil; -import io.netty.util.ReferenceCountUtil; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; - -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; - -import static io.netty.handler.codec.http.HttpHeadersTestUtils.of; -import static io.netty.handler.codec.http.HttpUtil.normalizeAndGetContentLength; -import static java.util.Collections.singletonList; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - -public class HttpUtilTest { - - @Test - public void testRemoveTransferEncodingIgnoreCase() { - HttpMessage message = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - message.headers().set(HttpHeaderNames.TRANSFER_ENCODING, "Chunked"); - assertFalse(message.headers().isEmpty()); - HttpUtil.setTransferEncodingChunked(message, false); - assertTrue(message.headers().isEmpty()); - } - - // Test for https://github.com/netty/netty/issues/1690 - @Test - public void testGetOperations() { - HttpHeaders headers = new DefaultHttpHeaders(); - headers.add(of("Foo"), of("1")); - headers.add(of("Foo"), of("2")); - - assertEquals("1", headers.get(of("Foo"))); - - List values = headers.getAll(of("Foo")); - assertEquals(2, values.size()); - assertEquals("1", values.get(0)); - assertEquals("2", values.get(1)); - } - - @Test - public void testGetCharsetAsRawCharSequence() { - String QUOTES_CHARSET_CONTENT_TYPE = "text/html; charset=\"utf8\""; - String SIMPLE_CONTENT_TYPE = "text/html"; - - HttpMessage message = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - message.headers().set(HttpHeaderNames.CONTENT_TYPE, QUOTES_CHARSET_CONTENT_TYPE); - assertEquals("\"utf8\"", HttpUtil.getCharsetAsSequence(message)); - assertEquals("\"utf8\"", HttpUtil.getCharsetAsSequence(QUOTES_CHARSET_CONTENT_TYPE)); - - message.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html"); - assertNull(HttpUtil.getCharsetAsSequence(message)); - assertNull(HttpUtil.getCharsetAsSequence(SIMPLE_CONTENT_TYPE)); - } - - @Test - public void testGetCharset() { - testGetCharsetUtf8("text/html; charset=utf-8"); - } - - @Test - public void testGetCharsetNoSpace() { - testGetCharsetUtf8("text/html;charset=utf-8"); - } - - @Test - public void testGetCharsetQuoted() { - testGetCharsetUtf8("text/html; charset=\"utf-8\""); - } - - @Test - public void testGetCharsetNoSpaceQuoted() { - testGetCharsetUtf8("text/html;charset=\"utf-8\""); - } - - private void testGetCharsetUtf8(String contentType) { - String UPPER_CASE_NORMAL_CONTENT_TYPE = contentType.toUpperCase(); - - HttpMessage message = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - message.headers().set(HttpHeaderNames.CONTENT_TYPE, contentType); - assertEquals(CharsetUtil.UTF_8, HttpUtil.getCharset(message)); - assertEquals(CharsetUtil.UTF_8, HttpUtil.getCharset(contentType)); - - message.headers().set(HttpHeaderNames.CONTENT_TYPE, UPPER_CASE_NORMAL_CONTENT_TYPE); - assertEquals(CharsetUtil.UTF_8, HttpUtil.getCharset(message)); - assertEquals(CharsetUtil.UTF_8, HttpUtil.getCharset(UPPER_CASE_NORMAL_CONTENT_TYPE)); - } - - @Test - public void testGetCharsetNoLeadingQuotes() { - testGetCharsetInvalidQuotes("text/html;charset=utf-8\""); - } - - @Test - public void testGetCharsetNoTrailingQuotes() { - testGetCharsetInvalidQuotes("text/html;charset=\"utf-8"); - } - - @Test - public void testGetCharsetOnlyQuotes() { - testGetCharsetInvalidQuotes("text/html;charset=\"\""); - } - - private static void testGetCharsetInvalidQuotes(String contentType) { - String UPPER_CASE_NORMAL_CONTENT_TYPE = contentType.toUpperCase(); - - HttpMessage message = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - message.headers().set(HttpHeaderNames.CONTENT_TYPE, contentType); - assertEquals(CharsetUtil.ISO_8859_1, HttpUtil.getCharset(message, CharsetUtil.ISO_8859_1)); - assertEquals(CharsetUtil.ISO_8859_1, HttpUtil.getCharset(contentType, CharsetUtil.ISO_8859_1)); - - message.headers().set(HttpHeaderNames.CONTENT_TYPE, UPPER_CASE_NORMAL_CONTENT_TYPE); - assertEquals(CharsetUtil.ISO_8859_1, HttpUtil.getCharset(message, CharsetUtil.ISO_8859_1)); - assertEquals(CharsetUtil.ISO_8859_1, HttpUtil.getCharset(UPPER_CASE_NORMAL_CONTENT_TYPE, - CharsetUtil.ISO_8859_1)); - } - - @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"; - final String CONTENT_TYPE_WITH_INCORRECT_CHARSET = "text/html; charset=UTFFF"; - final String CONTENT_TYPE_WITH_ILLEGAL_CHARSET_NAME = "text/html; charset=!illegal!"; - - HttpMessage message = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - message.headers().set(HttpHeaderNames.CONTENT_TYPE, SIMPLE_CONTENT_TYPE); - assertEquals(CharsetUtil.ISO_8859_1, HttpUtil.getCharset(message)); - assertEquals(CharsetUtil.ISO_8859_1, HttpUtil.getCharset(SIMPLE_CONTENT_TYPE)); - - message.headers().set(HttpHeaderNames.CONTENT_TYPE, SIMPLE_CONTENT_TYPE); - assertEquals(CharsetUtil.UTF_8, HttpUtil.getCharset(message, StandardCharsets.UTF_8)); - assertEquals(CharsetUtil.UTF_8, HttpUtil.getCharset(SIMPLE_CONTENT_TYPE, StandardCharsets.UTF_8)); - - message.headers().set(HttpHeaderNames.CONTENT_TYPE, CONTENT_TYPE_WITH_INCORRECT_CHARSET); - assertEquals(CharsetUtil.ISO_8859_1, HttpUtil.getCharset(message)); - assertEquals(CharsetUtil.ISO_8859_1, HttpUtil.getCharset(CONTENT_TYPE_WITH_INCORRECT_CHARSET)); - - message.headers().set(HttpHeaderNames.CONTENT_TYPE, CONTENT_TYPE_WITH_INCORRECT_CHARSET); - assertEquals(CharsetUtil.UTF_8, HttpUtil.getCharset(message, StandardCharsets.UTF_8)); - assertEquals(CharsetUtil.UTF_8, - HttpUtil.getCharset(CONTENT_TYPE_WITH_INCORRECT_CHARSET, StandardCharsets.UTF_8)); - - message.headers().set(HttpHeaderNames.CONTENT_TYPE, CONTENT_TYPE_WITH_ILLEGAL_CHARSET_NAME); - assertEquals(CharsetUtil.ISO_8859_1, HttpUtil.getCharset(message)); - assertEquals(CharsetUtil.ISO_8859_1, HttpUtil.getCharset(CONTENT_TYPE_WITH_ILLEGAL_CHARSET_NAME)); - - message.headers().set(HttpHeaderNames.CONTENT_TYPE, CONTENT_TYPE_WITH_ILLEGAL_CHARSET_NAME); - assertEquals(CharsetUtil.UTF_8, HttpUtil.getCharset(message, StandardCharsets.UTF_8)); - assertEquals(CharsetUtil.UTF_8, - HttpUtil.getCharset(CONTENT_TYPE_WITH_ILLEGAL_CHARSET_NAME, StandardCharsets.UTF_8)); - } - - @Test - public void testGetMimeType() { - final String SIMPLE_CONTENT_TYPE = "text/html"; - final String NORMAL_CONTENT_TYPE = "text/html; charset=utf-8"; - - HttpMessage message = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - assertNull(HttpUtil.getMimeType(message)); - message.headers().set(HttpHeaderNames.CONTENT_TYPE, ""); - assertNull(HttpUtil.getMimeType(message)); - assertNull(HttpUtil.getMimeType("")); - message.headers().set(HttpHeaderNames.CONTENT_TYPE, SIMPLE_CONTENT_TYPE); - assertEquals("text/html", HttpUtil.getMimeType(message)); - assertEquals("text/html", HttpUtil.getMimeType(SIMPLE_CONTENT_TYPE)); - - message.headers().set(HttpHeaderNames.CONTENT_TYPE, NORMAL_CONTENT_TYPE); - assertEquals("text/html", HttpUtil.getMimeType(message)); - assertEquals("text/html", HttpUtil.getMimeType(NORMAL_CONTENT_TYPE)); - } - - @Test - public void testGetContentLengthThrowsNumberFormatException() { - final HttpMessage message = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - message.headers().set(HttpHeaderNames.CONTENT_LENGTH, "bar"); - try { - HttpUtil.getContentLength(message); - fail(); - } catch (final NumberFormatException e) { - // a number format exception is expected here - } - } - - @Test - public void testGetContentLengthIntDefaultValueThrowsNumberFormatException() { - final HttpMessage message = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - message.headers().set(HttpHeaderNames.CONTENT_LENGTH, "bar"); - try { - HttpUtil.getContentLength(message, 1); - fail(); - } catch (final NumberFormatException e) { - // a number format exception is expected here - } - } - - @Test - public void testGetContentLengthLongDefaultValueThrowsNumberFormatException() { - final HttpMessage message = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - message.headers().set(HttpHeaderNames.CONTENT_LENGTH, "bar"); - try { - HttpUtil.getContentLength(message, 1L); - fail(); - } catch (final NumberFormatException e) { - // a number format exception is expected here - } - } - - @Test - public void testDoubleChunkedHeader() { - HttpMessage message = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - message.headers().add(HttpHeaderNames.TRANSFER_ENCODING, "chunked"); - HttpUtil.setTransferEncodingChunked(message, true); - List expected = singletonList("chunked"); - assertEquals(expected, message.headers().getAll(HttpHeaderNames.TRANSFER_ENCODING)); - } - - private static List allPossibleCasesOfContinue() { - final List cases = new ArrayList<>(); - final String c = "continue"; - for (int i = 0; i < Math.pow(2, c.length()); i++) { - final StringBuilder sb = new StringBuilder(c.length()); - int j = i; - int k = 0; - while (j > 0) { - if ((j & 1) == 1) { - sb.append(Character.toUpperCase(c.charAt(k++))); - } else { - sb.append(c.charAt(k++)); - } - j >>= 1; - } - for (; k < c.length(); k++) { - sb.append(c.charAt(k)); - } - cases.add(sb.toString()); - } - return cases; - } - - @Test - public void testIs100Continue() { - // test all possible cases of 100-continue - for (final String continueCase : allPossibleCasesOfContinue()) { - run100ContinueTest(HttpVersion.HTTP_1_1, "100-" + continueCase, true); - } - run100ContinueTest(HttpVersion.HTTP_1_1, null, false); - run100ContinueTest(HttpVersion.HTTP_1_1, "chocolate=yummy", false); - run100ContinueTest(HttpVersion.HTTP_1_0, "100-continue", false); - final HttpMessage message = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - message.headers().set(HttpHeaderNames.EXPECT, "100-continue"); - run100ContinueTest(message, false); - } - - private static void run100ContinueTest(final HttpVersion version, final String expectations, boolean expect) { - final HttpMessage message = new DefaultFullHttpRequest(version, HttpMethod.GET, "/"); - if (expectations != null) { - message.headers().set(HttpHeaderNames.EXPECT, expectations); - } - run100ContinueTest(message, expect); - } - - private static void run100ContinueTest(final HttpMessage message, final boolean expected) { - assertEquals(expected, HttpUtil.is100ContinueExpected(message)); - ReferenceCountUtil.release(message); - } - - @Test - public void testContainsUnsupportedExpectation() { - // test all possible cases of 100-continue - for (final String continueCase : allPossibleCasesOfContinue()) { - runUnsupportedExpectationTest(HttpVersion.HTTP_1_1, "100-" + continueCase, false); - } - runUnsupportedExpectationTest(HttpVersion.HTTP_1_1, null, false); - runUnsupportedExpectationTest(HttpVersion.HTTP_1_1, "chocolate=yummy", true); - runUnsupportedExpectationTest(HttpVersion.HTTP_1_0, "100-continue", false); - final HttpMessage message = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - message.headers().set("Expect", "100-continue"); - runUnsupportedExpectationTest(message, false); - } - - private static void runUnsupportedExpectationTest(final HttpVersion version, - final String expectations, boolean expect) { - final HttpMessage message = new DefaultFullHttpRequest(version, HttpMethod.GET, "/"); - if (expectations != null) { - message.headers().set("Expect", expectations); - } - runUnsupportedExpectationTest(message, expect); - } - - private static void runUnsupportedExpectationTest(final HttpMessage message, final boolean expected) { - assertEquals(expected, HttpUtil.isUnsupportedExpectation(message)); - ReferenceCountUtil.release(message); - } - - @Test - public void testFormatHostnameForHttpFromResolvedAddressWithHostname() throws Exception { - InetSocketAddress socketAddress = new InetSocketAddress(InetAddress.getByName("localhost"), 8080); - assertEquals("localhost", HttpUtil.formatHostnameForHttp(socketAddress)); - } - - @Test - public void testFormatHostnameForHttpFromUnesolvedAddressWithHostname() { - InetSocketAddress socketAddress = InetSocketAddress.createUnresolved("localhost", 80); - assertEquals("localhost", HttpUtil.formatHostnameForHttp(socketAddress)); - } - - @Test - public void testIpv6() throws Exception { - InetSocketAddress socketAddress = new InetSocketAddress(InetAddress.getByName("::1"), 8080); - assertEquals("[::1]", HttpUtil.formatHostnameForHttp(socketAddress)); - } - - @Test - public void testIpv6Unresolved() { - InetSocketAddress socketAddress = InetSocketAddress.createUnresolved("::1", 8080); - assertEquals("[::1]", HttpUtil.formatHostnameForHttp(socketAddress)); - } - - @Test - public void testIpv4() throws Exception { - InetSocketAddress socketAddress = new InetSocketAddress(InetAddress.getByName("10.0.0.1"), 8080); - assertEquals("10.0.0.1", HttpUtil.formatHostnameForHttp(socketAddress)); - } - - @Test - 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)); - } - - @Test - public void testKeepAliveIfConnectionHeaderMultipleValues() { - HttpMessage http11Message = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, - "http:localhost/http_1_1"); - http11Message.headers().set( - HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE + ", " + HttpHeaderValues.CLOSE); - assertFalse(HttpUtil.isKeepAlive(http11Message)); - - http11Message.headers().set( - HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE + ", Close"); - assertFalse(HttpUtil.isKeepAlive(http11Message)); - - http11Message.headers().set( - HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE + ", " + HttpHeaderValues.UPGRADE); - assertFalse(HttpUtil.isKeepAlive(http11Message)); - - http11Message.headers().set( - HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE + ", " + HttpHeaderValues.KEEP_ALIVE); - assertTrue(HttpUtil.isKeepAlive(http11Message)); - } - - @Test - public void normalizeAndGetContentLengthEmpty() { - testNormalizeAndGetContentLengthInvalidContentLength(""); - } - - @Test - public void normalizeAndGetContentLengthNotANumber() { - testNormalizeAndGetContentLengthInvalidContentLength("foo"); - } - - @Test - public void normalizeAndGetContentLengthNegative() { - testNormalizeAndGetContentLengthInvalidContentLength("-1"); - } - - private static void testNormalizeAndGetContentLengthInvalidContentLength(final String contentLengthField) { - assertThrows(IllegalArgumentException.class, new Executable() { - @Override - public void execute() { - normalizeAndGetContentLength(singletonList(contentLengthField), false, false); - } - }); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/MultipleContentLengthHeadersTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/MultipleContentLengthHeadersTest.java deleted file mode 100644 index 54aadd0fd0..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/MultipleContentLengthHeadersTest.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.CharsetUtil; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -import static io.netty.handler.codec.http.HttpObjectDecoder.DEFAULT_INITIAL_BUFFER_SIZE; -import static io.netty.handler.codec.http.HttpObjectDecoder.DEFAULT_MAX_HEADER_SIZE; -import static io.netty.handler.codec.http.HttpObjectDecoder.DEFAULT_MAX_INITIAL_LINE_LENGTH; -import static io.netty.handler.codec.http.HttpObjectDecoder.DEFAULT_VALIDATE_HEADERS; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.core.IsInstanceOf.instanceOf; - -public class MultipleContentLengthHeadersTest { - - static Collection parameters() { - return Arrays.asList(new Object[][] { - { false, false, false }, - { false, false, true }, - { false, true, false }, - { false, true, true }, - { true, false, false }, - { true, false, true }, - { true, true, false }, - { true, true, true } - }); - } - - private static EmbeddedChannel newChannel(boolean allowDuplicateContentLengths) { - HttpRequestDecoder decoder = new HttpRequestDecoder( - DEFAULT_MAX_INITIAL_LINE_LENGTH, - DEFAULT_MAX_HEADER_SIZE, - DEFAULT_VALIDATE_HEADERS, - DEFAULT_INITIAL_BUFFER_SIZE, - allowDuplicateContentLengths); - return new EmbeddedChannel(decoder); - } - - @ParameterizedTest - @MethodSource("parameters") - public void testMultipleContentLengthHeadersBehavior(boolean allowDuplicateContentLengths, - boolean sameValue, boolean singleField) { - EmbeddedChannel channel = newChannel(allowDuplicateContentLengths); - String requestStr = setupRequestString(sameValue, singleField); - assertThat(channel.writeInbound(Unpooled.copiedBuffer(requestStr, CharsetUtil.US_ASCII)), is(true)); - HttpRequest request = channel.readInbound(); - - if (allowDuplicateContentLengths) { - if (sameValue) { - assertValid(request); - List contentLengths = request.headers().getAll(HttpHeaderNames.CONTENT_LENGTH); - assertThat(contentLengths, contains("1")); - LastHttpContent body = channel.readInbound(); - assertThat(body.content().readableBytes(), is(1)); - assertThat(body.content().readCharSequence(1, CharsetUtil.US_ASCII).toString(), is("a")); - } else { - assertInvalid(request); - } - } else { - assertInvalid(request); - } - assertThat(channel.finish(), is(false)); - } - - private static String setupRequestString(boolean sameValue, boolean singleField) { - String firstValue = "1"; - String secondValue = sameValue ? firstValue : "2"; - String contentLength; - if (singleField) { - contentLength = "Content-Length: " + firstValue + ", " + secondValue + "\r\n\r\n"; - } else { - contentLength = "Content-Length: " + firstValue + "\r\n" + - "Content-Length: " + secondValue + "\r\n\r\n"; - } - return "PUT /some/path HTTP/1.1\r\n" + - contentLength + - "ab"; - } - - @Test - public void testDanglingComma() { - EmbeddedChannel channel = newChannel(false); - String requestStr = "GET /some/path HTTP/1.1\r\n" + - "Content-Length: 1,\r\n" + - "Connection: close\n\n" + - "ab"; - assertThat(channel.writeInbound(Unpooled.copiedBuffer(requestStr, CharsetUtil.US_ASCII)), is(true)); - HttpRequest request = channel.readInbound(); - assertInvalid(request); - assertThat(channel.finish(), is(false)); - } - - private static void assertValid(HttpRequest request) { - assertThat(request.decoderResult().isFailure(), is(false)); - } - - private static void assertInvalid(HttpRequest request) { - assertThat(request.decoderResult().isFailure(), is(true)); - assertThat(request.decoderResult().cause(), instanceOf(IllegalArgumentException.class)); - assertThat(request.decoderResult().cause().getMessage(), - containsString("Multiple Content-Length values found")); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/QueryStringDecoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/QueryStringDecoderTest.java deleted file mode 100644 index b6adc4e2ff..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/QueryStringDecoderTest.java +++ /dev/null @@ -1,384 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.util.CharsetUtil; -import org.junit.jupiter.api.Test; - -import java.net.URI; -import java.net.URISyntaxException; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class QueryStringDecoderTest { - - @Test - public void testBasicUris() throws URISyntaxException { - QueryStringDecoder d = new QueryStringDecoder(new URI("http://localhost/path")); - assertEquals(0, d.parameters().size()); - } - - @Test - public void testBasic() { - QueryStringDecoder d; - - d = new QueryStringDecoder("/foo"); - assertEquals("/foo", d.path()); - assertEquals(0, d.parameters().size()); - - d = new QueryStringDecoder("/foo%20bar"); - assertEquals("/foo bar", d.path()); - assertEquals(0, d.parameters().size()); - - d = new QueryStringDecoder("/foo?a=b=c"); - assertEquals("/foo", d.path()); - assertEquals(1, d.parameters().size()); - assertEquals(1, d.parameters().get("a").size()); - assertEquals("b=c", d.parameters().get("a").get(0)); - - d = new QueryStringDecoder("/foo?a=1&a=2"); - assertEquals("/foo", d.path()); - assertEquals(1, d.parameters().size()); - assertEquals(2, d.parameters().get("a").size()); - assertEquals("1", d.parameters().get("a").get(0)); - assertEquals("2", d.parameters().get("a").get(1)); - - d = new QueryStringDecoder("/foo%20bar?a=1&a=2"); - assertEquals("/foo bar", d.path()); - assertEquals(1, d.parameters().size()); - assertEquals(2, d.parameters().get("a").size()); - assertEquals("1", d.parameters().get("a").get(0)); - assertEquals("2", d.parameters().get("a").get(1)); - - d = new QueryStringDecoder("/foo?a=&a=2"); - assertEquals("/foo", d.path()); - assertEquals(1, d.parameters().size()); - assertEquals(2, d.parameters().get("a").size()); - assertEquals("", d.parameters().get("a").get(0)); - assertEquals("2", d.parameters().get("a").get(1)); - - d = new QueryStringDecoder("/foo?a=1&a="); - assertEquals("/foo", d.path()); - assertEquals(1, d.parameters().size()); - assertEquals(2, d.parameters().get("a").size()); - assertEquals("1", d.parameters().get("a").get(0)); - assertEquals("", d.parameters().get("a").get(1)); - - d = new QueryStringDecoder("/foo?a=1&a=&a="); - assertEquals("/foo", d.path()); - assertEquals(1, d.parameters().size()); - assertEquals(3, d.parameters().get("a").size()); - assertEquals("1", d.parameters().get("a").get(0)); - assertEquals("", d.parameters().get("a").get(1)); - assertEquals("", d.parameters().get("a").get(2)); - - d = new QueryStringDecoder("/foo?a=1=&a==2"); - assertEquals("/foo", d.path()); - assertEquals(1, d.parameters().size()); - assertEquals(2, d.parameters().get("a").size()); - assertEquals("1=", d.parameters().get("a").get(0)); - assertEquals("=2", d.parameters().get("a").get(1)); - - d = new QueryStringDecoder("/foo?abc=1%2023&abc=124%20"); - assertEquals("/foo", d.path()); - assertEquals(1, d.parameters().size()); - assertEquals(2, d.parameters().get("abc").size()); - assertEquals("1 23", d.parameters().get("abc").get(0)); - assertEquals("124 ", d.parameters().get("abc").get(1)); - - d = new QueryStringDecoder("/foo?abc=%7E"); - assertEquals("~", d.parameters().get("abc").get(0)); - } - - @Test - public void testExotic() { - assertQueryString("", ""); - assertQueryString("foo", "foo"); - assertQueryString("foo", "foo?"); - assertQueryString("/foo", "/foo?"); - assertQueryString("/foo", "/foo"); - assertQueryString("?a=", "?a"); - assertQueryString("foo?a=", "foo?a"); - assertQueryString("/foo?a=", "/foo?a"); - assertQueryString("/foo?a=", "/foo?a&"); - assertQueryString("/foo?a=", "/foo?&a"); - assertQueryString("/foo?a=", "/foo?&a&"); - assertQueryString("/foo?a=", "/foo?&=a"); - assertQueryString("/foo?a=", "/foo?=a&"); - assertQueryString("/foo?a=", "/foo?a=&"); - assertQueryString("/foo?a=b&c=d", "/foo?a=b&&c=d"); - assertQueryString("/foo?a=b&c=d", "/foo?a=b&=&c=d"); - assertQueryString("/foo?a=b&c=d", "/foo?a=b&==&c=d"); - assertQueryString("/foo?a=b&c=&x=y", "/foo?a=b&c&x=y"); - assertQueryString("/foo?a=", "/foo?a="); - assertQueryString("/foo?a=", "/foo?&a="); - assertQueryString("/foo?a=b&c=d", "/foo?a=b&c=d"); - assertQueryString("/foo?a=1&a=&a=", "/foo?a=1&a&a="); - } - - @Test - public void testSemicolon() { - assertQueryString("/foo?a=1;2", "/foo?a=1;2", false); - // ";" should be treated as a normal character, see #8855 - assertQueryString("/foo?a=1;2", "/foo?a=1%3B2", true); - } - - @Test - public void testPathSpecific() { - // decode escaped characters - assertEquals("/foo bar/", new QueryStringDecoder("/foo%20bar/?").path()); - assertEquals("/foo\r\n\\bar/", new QueryStringDecoder("/foo%0D%0A\\bar/?").path()); - - // a 'fragment' after '#' should be cuted (see RFC 3986) - assertEquals("", new QueryStringDecoder("#123").path()); - assertEquals("foo", new QueryStringDecoder("foo?bar#anchor").path()); - assertEquals("/foo-bar", new QueryStringDecoder("/foo-bar#anchor").path()); - assertEquals("/foo-bar", new QueryStringDecoder("/foo-bar#a#b?c=d").path()); - - // '+' is not escape ' ' for the path - assertEquals("+", new QueryStringDecoder("+").path()); - assertEquals("/foo+bar/", new QueryStringDecoder("/foo+bar/?").path()); - assertEquals("/foo++", new QueryStringDecoder("/foo++?index.php").path()); - assertEquals("/foo +", new QueryStringDecoder("/foo%20+?index.php").path()); - assertEquals("/foo+ ", new QueryStringDecoder("/foo+%20").path()); - } - - @Test - public void testExcludeFragment() { - // a 'fragment' after '#' should be cuted (see RFC 3986) - assertEquals("a", new QueryStringDecoder("?a#anchor").parameters().keySet().iterator().next()); - assertEquals("b", new QueryStringDecoder("?a=b#anchor").parameters().get("a").get(0)); - assertTrue(new QueryStringDecoder("?#").parameters().isEmpty()); - assertTrue(new QueryStringDecoder("?#anchor").parameters().isEmpty()); - assertTrue(new QueryStringDecoder("#?a=b#anchor").parameters().isEmpty()); - assertTrue(new QueryStringDecoder("?#a=b#anchor").parameters().isEmpty()); - } - - @Test - public void testHashDos() { - StringBuilder buf = new StringBuilder(); - buf.append('?'); - for (int i = 0; i < 65536; i ++) { - buf.append('k'); - buf.append(i); - buf.append("=v"); - buf.append(i); - buf.append('&'); - } - assertEquals(1024, new QueryStringDecoder(buf.toString()).parameters().size()); - } - - @Test - public void testHasPath() { - QueryStringDecoder decoder = new QueryStringDecoder("1=2", false); - assertEquals("", decoder.path()); - Map> params = decoder.parameters(); - assertEquals(1, params.size()); - assertTrue(params.containsKey("1")); - List param = params.get("1"); - assertNotNull(param); - assertEquals(1, param.size()); - assertEquals("2", param.get(0)); - } - - @Test - public void testUrlDecoding() throws Exception { - final String caffe = new String( - // "CaffÊ" but instead of putting the literal E-acute in the - // source file, we directly use the UTF-8 encoding so as to - // not rely on the platform's default encoding (not portable). - new byte[] {'C', 'a', 'f', 'f', (byte) 0xC3, (byte) 0xA9}, - "UTF-8"); - final String[] tests = { - // Encoded -> Decoded or error message substring - "", "", - "foo", "foo", - "f+o", "f o", - "f++", "f ", - "fo%", "unterminated escape sequence at index 2 of: fo%", - "%42", "B", - "%5f", "_", - "f%4", "unterminated escape sequence at index 1 of: f%4", - "%x2", "invalid hex byte 'x2' at index 1 of '%x2'", - "%4x", "invalid hex byte '4x' at index 1 of '%4x'", - "Caff%C3%A9", caffe, - "ŅĐģŅƒŅ‡Đ°ĐšĐŊŅ‹Đš ĐŋŅ€Đ°ĐˇĐ´ĐŊиĐē", "ŅĐģŅƒŅ‡Đ°ĐšĐŊŅ‹Đš ĐŋŅ€Đ°ĐˇĐ´ĐŊиĐē", - "ŅĐģŅƒŅ‡Đ°ĐšĐŊŅ‹Đš%20ĐŋŅ€Đ°ĐˇĐ´ĐŊиĐē", "ŅĐģŅƒŅ‡Đ°ĐšĐŊŅ‹Đš ĐŋŅ€Đ°ĐˇĐ´ĐŊиĐē", - "ŅĐģŅƒŅ‡Đ°ĐšĐŊŅ‹Đš%20ĐŋŅ€Đ°ĐˇĐ´ĐŊиĐē%20%E2%98%BA", "ŅĐģŅƒŅ‡Đ°ĐšĐŊŅ‹Đš ĐŋŅ€Đ°ĐˇĐ´ĐŊиĐē â˜ē", - }; - for (int i = 0; i < tests.length; i += 2) { - final String encoded = tests[i]; - final String expected = tests[i + 1]; - try { - final String decoded = QueryStringDecoder.decodeComponent(encoded); - assertEquals(expected, decoded); - } catch (IllegalArgumentException e) { - assertEquals(expected, e.getMessage()); - } - } - } - - private static void assertQueryString(String expected, String actual) { - assertQueryString(expected, actual, false); - } - - private static void assertQueryString(String expected, String actual, boolean semicolonIsNormalChar) { - QueryStringDecoder ed = new QueryStringDecoder(expected, CharsetUtil.UTF_8, true, - 1024, semicolonIsNormalChar); - QueryStringDecoder ad = new QueryStringDecoder(actual, CharsetUtil.UTF_8, true, - 1024, semicolonIsNormalChar); - assertEquals(ed.path(), ad.path()); - assertEquals(ed.parameters(), ad.parameters()); - } - - // See #189 - @Test - public void testURI() { - URI uri = URI.create("http://localhost:8080/foo?param1=value1¶m2=value2¶m3=value3"); - QueryStringDecoder decoder = new QueryStringDecoder(uri); - assertEquals("/foo", decoder.path()); - assertEquals("/foo", decoder.rawPath()); - assertEquals("param1=value1¶m2=value2¶m3=value3", decoder.rawQuery()); - Map> params = decoder.parameters(); - assertEquals(3, params.size()); - Iterator>> entries = params.entrySet().iterator(); - - Entry> entry = entries.next(); - assertEquals("param1", entry.getKey()); - assertEquals(1, entry.getValue().size()); - assertEquals("value1", entry.getValue().get(0)); - - entry = entries.next(); - assertEquals("param2", entry.getKey()); - assertEquals(1, entry.getValue().size()); - assertEquals("value2", entry.getValue().get(0)); - - entry = entries.next(); - assertEquals("param3", entry.getKey()); - assertEquals(1, entry.getValue().size()); - assertEquals("value3", entry.getValue().get(0)); - - assertFalse(entries.hasNext()); - } - - // See #189 - @Test - public void testURISlashPath() { - URI uri = URI.create("http://localhost:8080/?param1=value1¶m2=value2¶m3=value3"); - QueryStringDecoder decoder = new QueryStringDecoder(uri); - assertEquals("/", decoder.path()); - assertEquals("/", decoder.rawPath()); - assertEquals("param1=value1¶m2=value2¶m3=value3", decoder.rawQuery()); - - Map> params = decoder.parameters(); - assertEquals(3, params.size()); - Iterator>> entries = params.entrySet().iterator(); - - Entry> entry = entries.next(); - assertEquals("param1", entry.getKey()); - assertEquals(1, entry.getValue().size()); - assertEquals("value1", entry.getValue().get(0)); - - entry = entries.next(); - assertEquals("param2", entry.getKey()); - assertEquals(1, entry.getValue().size()); - assertEquals("value2", entry.getValue().get(0)); - - entry = entries.next(); - assertEquals("param3", entry.getKey()); - assertEquals(1, entry.getValue().size()); - assertEquals("value3", entry.getValue().get(0)); - - assertFalse(entries.hasNext()); - } - - // See #189 - @Test - public void testURINoPath() { - URI uri = URI.create("http://localhost:8080?param1=value1¶m2=value2¶m3=value3"); - QueryStringDecoder decoder = new QueryStringDecoder(uri); - assertEquals("", decoder.path()); - assertEquals("", decoder.rawPath()); - assertEquals("param1=value1¶m2=value2¶m3=value3", decoder.rawQuery()); - - Map> params = decoder.parameters(); - assertEquals(3, params.size()); - Iterator>> entries = params.entrySet().iterator(); - - Entry> entry = entries.next(); - assertEquals("param1", entry.getKey()); - assertEquals(1, entry.getValue().size()); - assertEquals("value1", entry.getValue().get(0)); - - entry = entries.next(); - assertEquals("param2", entry.getKey()); - assertEquals(1, entry.getValue().size()); - assertEquals("value2", entry.getValue().get(0)); - - entry = entries.next(); - assertEquals("param3", entry.getKey()); - assertEquals(1, entry.getValue().size()); - assertEquals("value3", entry.getValue().get(0)); - - assertFalse(entries.hasNext()); - } - - // See https://github.com/netty/netty/issues/1833 - @Test - public void testURI2() { - URI uri = URI.create("http://foo.com/images;num=10?query=name;value=123"); - QueryStringDecoder decoder = new QueryStringDecoder(uri); - assertEquals("/images;num=10", decoder.path()); - assertEquals("/images;num=10", decoder.rawPath()); - assertEquals("query=name;value=123", decoder.rawQuery()); - - Map> params = decoder.parameters(); - assertEquals(2, params.size()); - Iterator>> entries = params.entrySet().iterator(); - - Entry> entry = entries.next(); - assertEquals("query", entry.getKey()); - assertEquals(1, entry.getValue().size()); - assertEquals("name", entry.getValue().get(0)); - - entry = entries.next(); - assertEquals("value", entry.getKey()); - assertEquals(1, entry.getValue().size()); - assertEquals("123", entry.getValue().get(0)); - - assertFalse(entries.hasNext()); - } - - @Test - public void testEmptyStrings() { - QueryStringDecoder pathSlash = new QueryStringDecoder("path/"); - assertEquals("path/", pathSlash.rawPath()); - assertEquals("", pathSlash.rawQuery()); - QueryStringDecoder pathQuestion = new QueryStringDecoder("path?"); - assertEquals("path", pathQuestion.rawPath()); - assertEquals("", pathQuestion.rawQuery()); - QueryStringDecoder empty = new QueryStringDecoder(""); - assertEquals("", empty.rawPath()); - assertEquals("", empty.rawQuery()); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/QueryStringEncoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/QueryStringEncoderTest.java deleted file mode 100644 index e30459a06b..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/QueryStringEncoderTest.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import org.junit.jupiter.api.Test; - -import java.net.URI; -import java.nio.charset.Charset; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class QueryStringEncoderTest { - - @Test - public void testDefaultEncoding() throws Exception { - QueryStringEncoder e; - - e = new QueryStringEncoder("/foo"); - e.addParam("a", "b=c"); - assertEquals("/foo?a=b%3Dc", e.toString()); - assertEquals(new URI("/foo?a=b%3Dc"), e.toUri()); - - e = new QueryStringEncoder("/foo/\u00A5"); - e.addParam("a", "\u00A5"); - assertEquals("/foo/\u00A5?a=%C2%A5", e.toString()); - assertEquals(new URI("/foo/\u00A5?a=%C2%A5"), e.toUri()); - - e = new QueryStringEncoder("/foo/\u00A5"); - e.addParam("a", "abc\u00A5"); - assertEquals("/foo/\u00A5?a=abc%C2%A5", e.toString()); - assertEquals(new URI("/foo/\u00A5?a=abc%C2%A5"), e.toUri()); - - e = new QueryStringEncoder("/foo"); - e.addParam("a", "1"); - e.addParam("b", "2"); - assertEquals("/foo?a=1&b=2", e.toString()); - assertEquals(new URI("/foo?a=1&b=2"), e.toUri()); - - e = new QueryStringEncoder("/foo"); - e.addParam("a", "1"); - e.addParam("b", ""); - e.addParam("c", null); - e.addParam("d", null); - assertEquals("/foo?a=1&b=&c&d", e.toString()); - assertEquals(new URI("/foo?a=1&b=&c&d"), e.toUri()); - - e = new QueryStringEncoder("/foo"); - e.addParam("test", "a~b"); - assertEquals("/foo?test=a~b", e.toString()); - assertEquals(new URI("/foo?test=a~b"), e.toUri()); - } - - @Test - public void testNonDefaultEncoding() throws Exception { - QueryStringEncoder e = new QueryStringEncoder("/foo/\u00A5", Charset.forName("UTF-16")); - e.addParam("a", "\u00A5"); - assertEquals("/foo/\u00A5?a=%FE%FF%00%A5", e.toString()); - assertEquals(new URI("/foo/\u00A5?a=%FE%FF%00%A5"), e.toUri()); - } - - @Test - public void testWhitespaceEncoding() throws Exception { - QueryStringEncoder e = new QueryStringEncoder("/foo"); - e.addParam("a", "b c"); - assertEquals("/foo?a=b%20c", e.toString()); - assertEquals(new URI("/foo?a=b%20c"), e.toUri()); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/ReadOnlyHttpHeadersTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/ReadOnlyHttpHeadersTest.java deleted file mode 100644 index 4d32eb4d99..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/ReadOnlyHttpHeadersTest.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.util.AsciiString; -import org.junit.jupiter.api.Test; - -import java.util.Iterator; -import java.util.List; -import java.util.Map.Entry; -import java.util.Set; - -import static io.netty.handler.codec.http.HttpHeaderNames.ACCEPT; -import static io.netty.handler.codec.http.HttpHeaderNames.CONNECTION; -import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH; -import static io.netty.handler.codec.http.HttpHeaderValues.APPLICATION_JSON; -import static io.netty.handler.codec.http.HttpHeaderValues.APPLICATION_OCTET_STREAM; -import static io.netty.handler.codec.http.HttpHeaderValues.CLOSE; -import static io.netty.handler.codec.http.HttpHeaderValues.ZERO; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class ReadOnlyHttpHeadersTest { - @Test - public void getValue() { - ReadOnlyHttpHeaders headers = new ReadOnlyHttpHeaders(true, - ACCEPT, APPLICATION_JSON); - assertFalse(headers.isEmpty()); - assertEquals(1, headers.size()); - assertTrue(APPLICATION_JSON.contentEquals(headers.get(ACCEPT))); - assertTrue(headers.contains(ACCEPT)); - assertNull(headers.get(CONTENT_LENGTH)); - assertFalse(headers.contains(CONTENT_LENGTH)); - } - - @Test - public void charSequenceIterator() { - ReadOnlyHttpHeaders headers = new ReadOnlyHttpHeaders(true, - ACCEPT, APPLICATION_JSON, CONTENT_LENGTH, ZERO, CONNECTION, CLOSE); - assertFalse(headers.isEmpty()); - assertEquals(3, headers.size()); - Iterator> itr = headers.iteratorCharSequence(); - assertTrue(itr.hasNext()); - Entry next = itr.next(); - assertTrue(ACCEPT.contentEqualsIgnoreCase(next.getKey())); - assertTrue(APPLICATION_JSON.contentEqualsIgnoreCase(next.getValue())); - assertTrue(itr.hasNext()); - next = itr.next(); - assertTrue(CONTENT_LENGTH.contentEqualsIgnoreCase(next.getKey())); - assertTrue(ZERO.contentEqualsIgnoreCase(next.getValue())); - assertTrue(itr.hasNext()); - next = itr.next(); - assertTrue(CONNECTION.contentEqualsIgnoreCase(next.getKey())); - assertTrue(CLOSE.contentEqualsIgnoreCase(next.getValue())); - assertFalse(itr.hasNext()); - } - - @Test - public void stringIterator() { - ReadOnlyHttpHeaders headers = new ReadOnlyHttpHeaders(true, - ACCEPT, APPLICATION_JSON, CONTENT_LENGTH, ZERO, CONNECTION, CLOSE); - assertFalse(headers.isEmpty()); - assertEquals(3, headers.size()); - assert3ParisEquals(headers.iterator()); - } - - @Test - public void entries() { - ReadOnlyHttpHeaders headers = new ReadOnlyHttpHeaders(true, - ACCEPT, APPLICATION_JSON, CONTENT_LENGTH, ZERO, CONNECTION, CLOSE); - assertFalse(headers.isEmpty()); - assertEquals(3, headers.size()); - assert3ParisEquals(headers.entries().iterator()); - } - - @Test - public void names() { - ReadOnlyHttpHeaders headers = new ReadOnlyHttpHeaders(true, - ACCEPT, APPLICATION_JSON, CONTENT_LENGTH, ZERO, CONNECTION, CLOSE); - assertFalse(headers.isEmpty()); - assertEquals(3, headers.size()); - Set names = headers.names(); - assertEquals(3, names.size()); - assertTrue(names.contains(ACCEPT.toString())); - assertTrue(names.contains(CONTENT_LENGTH.toString())); - assertTrue(names.contains(CONNECTION.toString())); - } - - @Test - public void getAll() { - ReadOnlyHttpHeaders headers = new ReadOnlyHttpHeaders(false, - ACCEPT, APPLICATION_JSON, CONTENT_LENGTH, ZERO, ACCEPT, APPLICATION_OCTET_STREAM); - assertFalse(headers.isEmpty()); - assertEquals(3, headers.size()); - List names = headers.getAll(ACCEPT); - assertEquals(2, names.size()); - assertTrue(APPLICATION_JSON.contentEqualsIgnoreCase(names.get(0))); - assertTrue(APPLICATION_OCTET_STREAM.contentEqualsIgnoreCase(names.get(1))); - } - - @Test - public void validateNamesFail() { - assertThrows(IllegalArgumentException.class, () -> new ReadOnlyHttpHeaders(true, - ACCEPT, APPLICATION_JSON, AsciiString.cached(" "))); - } - - @Test - public void emptyHeaderName() { - assertThrows(IllegalArgumentException.class, () -> new ReadOnlyHttpHeaders(true, - ACCEPT, APPLICATION_JSON, AsciiString.cached(" "), ZERO)); - } - - @Test - public void headerWithoutValue() { - assertThrows(IllegalArgumentException.class, () -> new ReadOnlyHttpHeaders(false, - ACCEPT, APPLICATION_JSON, CONTENT_LENGTH)); - } - - private static void assert3ParisEquals(Iterator> itr) { - assertTrue(itr.hasNext()); - Entry next = itr.next(); - assertTrue(ACCEPT.contentEqualsIgnoreCase(next.getKey())); - assertTrue(APPLICATION_JSON.contentEqualsIgnoreCase(next.getValue())); - assertTrue(itr.hasNext()); - next = itr.next(); - assertTrue(CONTENT_LENGTH.contentEqualsIgnoreCase(next.getKey())); - assertTrue(ZERO.contentEqualsIgnoreCase(next.getValue())); - assertTrue(itr.hasNext()); - next = itr.next(); - assertTrue(CONNECTION.contentEqualsIgnoreCase(next.getKey())); - assertTrue(CLOSE.contentEqualsIgnoreCase(next.getValue())); - assertFalse(itr.hasNext()); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/cookie/ClientCookieDecoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/cookie/ClientCookieDecoderTest.java deleted file mode 100644 index ca7c7419f3..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/cookie/ClientCookieDecoderTest.java +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.cookie; - -import io.netty.handler.codec.DateFormatter; -import io.netty.handler.codec.http.cookie.CookieHeaderNames.SameSite; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Collection; -import java.util.Date; -import java.util.Iterator; -import java.util.TimeZone; - -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class ClientCookieDecoderTest { - @Test - public void testDecodingSingleCookieV0() { - String cookieString = "myCookie=myValue;expires=" - + DateFormatter.format(new Date(System.currentTimeMillis() + 50000)) - + ";path=/apathsomewhere;domain=.adomainsomewhere;secure;SameSite=None"; - - Cookie cookie = ClientCookieDecoder.STRICT.decode(cookieString); - assertNotNull(cookie); - assertEquals("myValue", cookie.value()); - assertEquals(".adomainsomewhere", cookie.domain()); - assertNotEquals(Long.MIN_VALUE, cookie.maxAge(), - "maxAge should be defined when parsing cookie " + cookieString); - assertTrue(cookie.maxAge() >= 40 && cookie.maxAge() <= 60, - "maxAge should be about 50ms when parsing cookie " + cookieString); - assertEquals("/apathsomewhere", cookie.path()); - assertTrue(cookie.isSecure()); - - assertThat(cookie, is(instanceOf(DefaultCookie.class))); - assertEquals(SameSite.None, ((DefaultCookie) cookie).sameSite()); - } - - @Test - public void testDecodingSingleCookieV0ExtraParamsIgnored() { - String cookieString = "myCookie=myValue;max-age=50;path=/apathsomewhere;" + - "domain=.adomainsomewhere;secure;comment=this is a comment;version=0;" + - "commentURL=http://aurl.com;port=\"80,8080\";discard;"; - Cookie cookie = ClientCookieDecoder.STRICT.decode(cookieString); - assertNotNull(cookie); - assertEquals("myValue", cookie.value()); - assertEquals(".adomainsomewhere", cookie.domain()); - assertEquals(50, cookie.maxAge()); - assertEquals("/apathsomewhere", cookie.path()); - assertTrue(cookie.isSecure()); - } - - @Test - public void testDecodingSingleCookieV1() { - String cookieString = "myCookie=myValue;max-age=50;path=/apathsomewhere;domain=.adomainsomewhere" - + ";secure;comment=this is a comment;version=1;"; - Cookie cookie = ClientCookieDecoder.STRICT.decode(cookieString); - assertEquals("myValue", cookie.value()); - assertNotNull(cookie); - assertEquals(".adomainsomewhere", cookie.domain()); - assertEquals(50, cookie.maxAge()); - assertEquals("/apathsomewhere", cookie.path()); - assertTrue(cookie.isSecure()); - } - - @Test - public void testDecodingSingleCookieV1ExtraParamsIgnored() { - String cookieString = "myCookie=myValue;max-age=50;path=/apathsomewhere;" - + "domain=.adomainsomewhere;secure;comment=this is a comment;version=1;" - + "commentURL=http://aurl.com;port='80,8080';discard;"; - Cookie cookie = ClientCookieDecoder.STRICT.decode(cookieString); - assertNotNull(cookie); - assertEquals("myValue", cookie.value()); - assertEquals(".adomainsomewhere", cookie.domain()); - assertEquals(50, cookie.maxAge()); - assertEquals("/apathsomewhere", cookie.path()); - assertTrue(cookie.isSecure()); - } - - @Test - public void testDecodingSingleCookieV2() { - String cookieString = "myCookie=myValue;max-age=50;path=/apathsomewhere;" - + "domain=.adomainsomewhere;secure;comment=this is a comment;version=2;" - + "commentURL=http://aurl.com;port=\"80,8080\";discard;"; - Cookie cookie = ClientCookieDecoder.STRICT.decode(cookieString); - assertNotNull(cookie); - assertEquals("myValue", cookie.value()); - assertEquals(".adomainsomewhere", cookie.domain()); - assertEquals(50, cookie.maxAge()); - assertEquals("/apathsomewhere", cookie.path()); - assertTrue(cookie.isSecure()); - } - - @Test - public void testDecodingComplexCookie() { - String c1 = "myCookie=myValue;max-age=50;path=/apathsomewhere;" - + "domain=.adomainsomewhere;secure;comment=this is a comment;version=2;" - + "commentURL=\"http://aurl.com\";port='80,8080';discard;"; - - Cookie cookie = ClientCookieDecoder.STRICT.decode(c1); - assertNotNull(cookie); - assertEquals("myValue", cookie.value()); - assertEquals(".adomainsomewhere", cookie.domain()); - assertEquals(50, cookie.maxAge()); - assertEquals("/apathsomewhere", cookie.path()); - assertTrue(cookie.isSecure()); - } - - @Test - public void testDecodingQuotedCookie() { - Collection sources = new ArrayList<>(); - sources.add("a=\"\","); - sources.add("b=\"1\","); - - Collection cookies = new ArrayList<>(); - for (String source : sources) { - cookies.add(ClientCookieDecoder.STRICT.decode(source)); - } - - Iterator it = cookies.iterator(); - Cookie c; - - c = it.next(); - assertEquals("a", c.name()); - assertEquals("", c.value()); - - c = it.next(); - assertEquals("b", c.name()); - assertEquals("1", c.value()); - - assertFalse(it.hasNext()); - } - - @Test - public void testDecodingGoogleAnalyticsCookie() { - String source = "ARPT=LWUKQPSWRTUN04CKKJI; " - + "kw-2E343B92-B097-442c-BFA5-BE371E0325A2=unfinished furniture; " - + "__utma=48461872.1094088325.1258140131.1258140131.1258140131.1; " - + "__utmb=48461872.13.10.1258140131; __utmc=48461872; " - + "__utmz=48461872.1258140131.1.1.utmcsr=overstock.com|utmccn=(referral)|" - + "utmcmd=referral|utmcct=/Home-Garden/Furniture/Clearance,/clearance,/32/dept.html"; - Cookie cookie = ClientCookieDecoder.STRICT.decode(source); - - assertEquals("ARPT", cookie.name()); - assertEquals("LWUKQPSWRTUN04CKKJI", cookie.value()); - } - - @Test - public void testDecodingLongDates() { - Calendar cookieDate = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - cookieDate.set(9999, Calendar.DECEMBER, 31, 23, 59, 59); - long expectedMaxAge = (cookieDate.getTimeInMillis() - System - .currentTimeMillis()) / 1000; - - String source = "Format=EU; expires=Fri, 31-Dec-9999 23:59:59 GMT; path=/"; - - Cookie cookie = ClientCookieDecoder.STRICT.decode(source); - - assertTrue(Math.abs(expectedMaxAge - cookie.maxAge()) < 2); - } - - @Test - public void testDecodingValueWithCommaFails() { - String source = "UserCookie=timeZoneName=(GMT+04:00) Moscow, St. Petersburg, Volgograd&promocode=®ion=BE;" - + " expires=Sat, 01-Dec-2012 10:53:31 GMT; path=/"; - - Cookie cookie = ClientCookieDecoder.STRICT.decode(source); - - assertNull(cookie); - } - - @Test - public void testDecodingWeirdNames1() { - String src = "path=; expires=Mon, 01-Jan-1990 00:00:00 GMT; path=/; domain=.www.google.com"; - Cookie cookie = ClientCookieDecoder.STRICT.decode(src); - assertEquals("path", cookie.name()); - assertEquals("", cookie.value()); - assertEquals("/", cookie.path()); - } - - @Test - public void testDecodingWeirdNames2() { - String src = "HTTPOnly="; - Cookie cookie = ClientCookieDecoder.STRICT.decode(src); - assertEquals("HTTPOnly", cookie.name()); - assertEquals("", cookie.value()); - } - - @Test - public void testDecodingValuesWithCommasAndEqualsFails() { - String src = "A=v=1&lg=en-US,it-IT,it&intl=it&np=1;T=z=E"; - Cookie cookie = ClientCookieDecoder.STRICT.decode(src); - assertNull(cookie); - } - - @Test - public void testDecodingInvalidValuesWithCommaAtStart() { - assertNull(ClientCookieDecoder.STRICT.decode(",")); - assertNull(ClientCookieDecoder.STRICT.decode(",a")); - assertNull(ClientCookieDecoder.STRICT.decode(",a=a")); - } - - @Test - public void testDecodingLongValue() { - String longValue = - "b___$Q__$ha______" + - "%=J^wI__3iD____$=HbQW__3iF____#=J^wI__3iH____%=J^wI__3iM____%=J^wI__3iS____" + - "#=J^wI__3iU____%=J^wI__3iZ____#=J^wI__3i]____%=J^wI__3ig____%=J^wI__3ij____" + - "%=J^wI__3ik____#=J^wI__3il____$=HbQW__3in____%=J^wI__3ip____$=HbQW__3iq____" + - "$=HbQW__3it____%=J^wI__3ix____#=J^wI__3j_____$=HbQW__3j%____$=HbQW__3j'____" + - "%=J^wI__3j(____%=J^wI__9mJ____'=KqtH__=SE__M____" + - "'=KqtH__s1X____$=MMyc__s1_____#=MN#O__ypn____'=KqtH__ypr____'=KqtH_#%h_____" + - "%=KqtH_#%o_____'=KqtH_#)H6______'=KqtH_#]9R____$=H/Lt_#]I6____#=KqtH_#]Z#____%=KqtH_#^*N____" + - "#=KqtH_#^:m____#=KqtH_#_*_____%=J^wI_#`-7____#=KqtH_#`T>____'=KqtH_#`T?____" + - "'=KqtH_#`TA____'=KqtH_#`TB____'=KqtH_#`TG____'=KqtH_#`TP____#=KqtH_#`U_____" + - "'=KqtH_#`U/____'=KqtH_#`U0____#=KqtH_#`U9____'=KqtH_#aEQ____%=KqtH_#b<)____" + - "'=KqtH_#c9-____%=KqtH_#dxC____%=KqtH_#dxE____%=KqtH_#ev$____'=KqtH_#fBi____" + - "#=KqtH_#fBj____'=KqtH_#fG)____'=KqtH_#fG+____'=KqtH_#g*B____'=KqtH_$>hD____+=J^x0_$?lW____'=KqtH_$?ll____'=KqtH_$?lm____" + - "%=KqtH_$?mi____'=KqtH_$?mx____'=KqtH_$D7]____#=J_#p_$D@T____#=J_#p_$V ClientCookieEncoder.STRICT.encode(new DefaultCookie("myCookie", "foo;bar"))); - } - - @Test - public void testComparatorForSamePathLength() { - Cookie cookie = new DefaultCookie("test", "value"); - cookie.setPath("1"); - - Cookie cookie2 = new DefaultCookie("test", "value"); - cookie2.setPath("2"); - - assertEquals(0, ClientCookieEncoder.COOKIE_COMPARATOR.compare(cookie, cookie2)); - assertEquals(0, ClientCookieEncoder.COOKIE_COMPARATOR.compare(cookie2, cookie)); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/cookie/ServerCookieDecoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/cookie/ServerCookieDecoderTest.java deleted file mode 100644 index 78aa88be58..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/cookie/ServerCookieDecoderTest.java +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.cookie; - -import java.util.List; -import org.junit.jupiter.api.Test; - -import java.util.Iterator; -import java.util.Set; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class ServerCookieDecoderTest { - @Test - public void testDecodingSingleCookie() { - String cookieString = "myCookie=myValue"; - Set cookies = ServerCookieDecoder.STRICT.decode(cookieString); - assertEquals(1, cookies.size()); - Cookie cookie = cookies.iterator().next(); - assertNotNull(cookie); - assertEquals("myValue", cookie.value()); - } - - @Test - public void testDecodingMultipleCookies() { - String c1 = "myCookie=myValue;"; - String c2 = "myCookie2=myValue2;"; - String c3 = "myCookie3=myValue3;"; - - Set cookies = ServerCookieDecoder.STRICT.decode(c1 + c2 + c3); - assertEquals(3, cookies.size()); - Iterator it = cookies.iterator(); - Cookie cookie = it.next(); - assertNotNull(cookie); - assertEquals("myValue", cookie.value()); - cookie = it.next(); - assertNotNull(cookie); - assertEquals("myValue2", cookie.value()); - cookie = it.next(); - assertNotNull(cookie); - assertEquals("myValue3", cookie.value()); - } - - @Test - public void testDecodingAllMultipleCookies() { - String c1 = "myCookie=myValue;"; - String c2 = "myCookie=myValue2;"; - String c3 = "myCookie=myValue3;"; - - List cookies = ServerCookieDecoder.STRICT.decodeAll(c1 + c2 + c3); - assertEquals(3, cookies.size()); - Iterator it = cookies.iterator(); - Cookie cookie = it.next(); - assertNotNull(cookie); - assertEquals("myValue", cookie.value()); - cookie = it.next(); - assertNotNull(cookie); - assertEquals("myValue2", cookie.value()); - cookie = it.next(); - assertNotNull(cookie); - assertEquals("myValue3", cookie.value()); - } - - @Test - public void testDecodingGoogleAnalyticsCookie() { - String source = - "ARPT=LWUKQPSWRTUN04CKKJI; " + - "kw-2E343B92-B097-442c-BFA5-BE371E0325A2=unfinished_furniture; " + - "__utma=48461872.1094088325.1258140131.1258140131.1258140131.1; " + - "__utmb=48461872.13.10.1258140131; __utmc=48461872; " + - "__utmz=48461872.1258140131.1.1.utmcsr=overstock.com|utmccn=(referral)|" + - "utmcmd=referral|utmcct=/Home-Garden/Furniture/Clearance/clearance/32/dept.html"; - Set cookies = ServerCookieDecoder.STRICT.decode(source); - Iterator it = cookies.iterator(); - Cookie c; - - c = it.next(); - assertEquals("ARPT", c.name()); - assertEquals("LWUKQPSWRTUN04CKKJI", c.value()); - - c = it.next(); - assertEquals("__utma", c.name()); - assertEquals("48461872.1094088325.1258140131.1258140131.1258140131.1", c.value()); - - c = it.next(); - assertEquals("__utmb", c.name()); - assertEquals("48461872.13.10.1258140131", c.value()); - - c = it.next(); - assertEquals("__utmc", c.name()); - assertEquals("48461872", c.value()); - - c = it.next(); - assertEquals("__utmz", c.name()); - assertEquals("48461872.1258140131.1.1.utmcsr=overstock.com|" + - "utmccn=(referral)|utmcmd=referral|utmcct=/Home-Garden/Furniture/Clearance/clearance/32/dept.html", - c.value()); - - c = it.next(); - assertEquals("kw-2E343B92-B097-442c-BFA5-BE371E0325A2", c.name()); - assertEquals("unfinished_furniture", c.value()); - - assertFalse(it.hasNext()); - } - - @Test - public void testDecodingLongValue() { - String longValue = - "b___$Q__$ha______" + - "%=J^wI__3iD____$=HbQW__3iF____#=J^wI__3iH____%=J^wI__3iM____%=J^wI__3iS____" + - "#=J^wI__3iU____%=J^wI__3iZ____#=J^wI__3i]____%=J^wI__3ig____%=J^wI__3ij____" + - "%=J^wI__3ik____#=J^wI__3il____$=HbQW__3in____%=J^wI__3ip____$=HbQW__3iq____" + - "$=HbQW__3it____%=J^wI__3ix____#=J^wI__3j_____$=HbQW__3j%____$=HbQW__3j'____" + - "%=J^wI__3j(____%=J^wI__9mJ____'=KqtH__=SE__M____" + - "'=KqtH__s1X____$=MMyc__s1_____#=MN#O__ypn____'=KqtH__ypr____'=KqtH_#%h_____" + - "%=KqtH_#%o_____'=KqtH_#)H6______'=KqtH_#]9R____$=H/Lt_#]I6____#=KqtH_#]Z#____%=KqtH_#^*N____" + - "#=KqtH_#^:m____#=KqtH_#_*_____%=J^wI_#`-7____#=KqtH_#`T>____'=KqtH_#`T?____" + - "'=KqtH_#`TA____'=KqtH_#`TB____'=KqtH_#`TG____'=KqtH_#`TP____#=KqtH_#`U_____" + - "'=KqtH_#`U/____'=KqtH_#`U0____#=KqtH_#`U9____'=KqtH_#aEQ____%=KqtH_#b<)____" + - "'=KqtH_#c9-____%=KqtH_#dxC____%=KqtH_#dxE____%=KqtH_#ev$____'=KqtH_#fBi____" + - "#=KqtH_#fBj____'=KqtH_#fG)____'=KqtH_#fG+____'=KqtH_#g*B____'=KqtH_$>hD____+=J^x0_$?lW____'=KqtH_$?ll____'=KqtH_$?lm____" + - "%=KqtH_$?mi____'=KqtH_$?mx____'=KqtH_$D7]____#=J_#p_$D@T____#=J_#p_$V cookies = ServerCookieDecoder.STRICT.decode("bh=\"" + longValue + "\";"); - assertEquals(1, cookies.size()); - Cookie c = cookies.iterator().next(); - assertEquals("bh", c.name()); - assertEquals(longValue, c.value()); - } - - @Test - public void testDecodingOldRFC2965Cookies() { - String source = "$Version=\"1\"; " + - "Part_Number1=\"Riding_Rocket_0023\"; $Path=\"/acme/ammo\"; " + - "Part_Number2=\"Rocket_Launcher_0001\"; $Path=\"/acme\""; - - Set cookies = ServerCookieDecoder.STRICT.decode(source); - Iterator it = cookies.iterator(); - Cookie c; - - c = it.next(); - assertEquals("Part_Number1", c.name()); - assertEquals("Riding_Rocket_0023", c.value()); - - c = it.next(); - assertEquals("Part_Number2", c.name()); - assertEquals("Rocket_Launcher_0001", c.value()); - - assertFalse(it.hasNext()); - } - - @Test - public void testRejectCookieValueWithSemicolon() { - Set cookies = ServerCookieDecoder.STRICT.decode("name=\"foo;bar\";"); - assertTrue(cookies.isEmpty()); - } - - @Test - public void testCaseSensitiveNames() { - Set cookies = ServerCookieDecoder.STRICT.decode("session_id=a; Session_id=b;"); - Iterator it = cookies.iterator(); - Cookie c; - - c = it.next(); - assertEquals("Session_id", c.name()); - assertEquals("b", c.value()); - - c = it.next(); - assertEquals("session_id", c.name()); - assertEquals("a", c.value()); - - assertFalse(it.hasNext()); - } -} 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 deleted file mode 100644 index e48b868f18..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/cookie/ServerCookieEncoderTest.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.cookie; - -import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import io.netty.handler.codec.DateFormatter; - -import java.text.ParseException; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import io.netty.handler.codec.http.cookie.CookieHeaderNames.SameSite; -import org.junit.jupiter.api.Test; - -public class ServerCookieEncoderTest { - - @Test - public void testEncodingSingleCookieV0() throws ParseException { - - int maxAge = 50; - - String result = "myCookie=myValue; Max-Age=50; Expires=(.+?); Path=/apathsomewhere;" + - " Domain=.adomainsomewhere; Secure; SameSite=Lax"; - DefaultCookie cookie = new DefaultCookie("myCookie", "myValue"); - cookie.setDomain(".adomainsomewhere"); - cookie.setMaxAge(maxAge); - cookie.setPath("/apathsomewhere"); - cookie.setSecure(true); - cookie.setSameSite(SameSite.Lax); - - String encodedCookie = ServerCookieEncoder.STRICT.encode(cookie); - - Matcher matcher = Pattern.compile(result).matcher(encodedCookie); - assertTrue(matcher.find()); - Date expiresDate = DateFormatter.parseHttpDate(matcher.group(1)); - long diff = (expiresDate.getTime() - System.currentTimeMillis()) / 1000; - // 2 secs should be fine - assertTrue(Math.abs(diff - maxAge) <= 2); - } - - @Test - public void testEncodingWithNoCookies() { - String encodedCookie1 = ClientCookieEncoder.STRICT.encode(); - List encodedCookie2 = ServerCookieEncoder.STRICT.encode(); - assertNull(encodedCookie1); - assertNotNull(encodedCookie2); - assertTrue(encodedCookie2.isEmpty()); - } - - @Test - public void testEncodingMultipleCookiesStrict() { - List result = new ArrayList<>(); - result.add("cookie2=value2"); - result.add("cookie1=value3"); - Cookie cookie1 = new DefaultCookie("cookie1", "value1"); - Cookie cookie2 = new DefaultCookie("cookie2", "value2"); - Cookie cookie3 = new DefaultCookie("cookie1", "value3"); - List encodedCookies = ServerCookieEncoder.STRICT.encode(cookie1, cookie2, cookie3); - assertEquals(result, encodedCookies); - } - - @Test - public void illegalCharInCookieNameMakesStrictEncoderThrowsException() { - Set illegalChars = new HashSet<>(); - // CTLs - for (int i = 0x00; i <= 0x1F; i++) { - illegalChars.add((char) i); - } - illegalChars.add((char) 0x7F); - // separators - for (char c : new char[] { '(', ')', '<', '>', '@', ',', ';', ':', '\\', '"', '/', '[', ']', - '?', '=', '{', '}', ' ', '\t' }) { - illegalChars.add(c); - } - - int exceptions = 0; - - for (char c : illegalChars) { - try { - ServerCookieEncoder.STRICT.encode(new DefaultCookie("foo" + c + "bar", "value")); - } catch (IllegalArgumentException e) { - exceptions++; - } - } - - assertEquals(illegalChars.size(), exceptions); - } - - @Test - public void illegalCharInCookieValueMakesStrictEncoderThrowsException() { - Set illegalChars = new HashSet<>(); - // CTLs - for (int i = 0x00; i <= 0x1F; i++) { - illegalChars.add((char) i); - } - illegalChars.add((char) 0x7F); - // whitespace, DQUOTE, comma, semicolon, and backslash - for (char c : new char[] { ' ', '"', ',', ';', '\\' }) { - illegalChars.add(c); - } - - int exceptions = 0; - - for (char c : illegalChars) { - try { - ServerCookieEncoder.STRICT.encode(new DefaultCookie("name", "value" + c)); - } catch (IllegalArgumentException e) { - exceptions++; - } - } - - 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<>(); - result.add("cookie1=value1"); - result.add("cookie2=value2"); - result.add("cookie1=value3"); - Cookie cookie1 = new DefaultCookie("cookie1", "value1"); - Cookie cookie2 = new DefaultCookie("cookie2", "value2"); - Cookie cookie3 = new DefaultCookie("cookie1", "value3"); - List encodedCookies = ServerCookieEncoder.LAX.encode(cookie1, cookie2, cookie3); - assertEquals(result, encodedCookies); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/cors/CorsConfigTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/cors/CorsConfigTest.java deleted file mode 100644 index 4784a372a1..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/cors/CorsConfigTest.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version - * 2.0 (the "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http.cors; - -import io.netty.handler.codec.http.EmptyHttpHeaders; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpMethod; -import org.junit.jupiter.api.Test; - -import static io.netty.handler.codec.http.HttpHeadersTestUtils.of; -import static io.netty.handler.codec.http.cors.CorsConfigBuilder.forAnyOrigin; -import static io.netty.handler.codec.http.cors.CorsConfigBuilder.forOrigin; -import static io.netty.handler.codec.http.cors.CorsConfigBuilder.forOrigins; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.hasItems; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.notNullValue; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; - -public class CorsConfigTest { - - @Test - public void disabled() { - final CorsConfig cors = forAnyOrigin().disable().build(); - assertThat(cors.isCorsSupportEnabled(), is(false)); - } - - @Test - public void anyOrigin() { - final CorsConfig cors = forAnyOrigin().build(); - assertThat(cors.isAnyOriginSupported(), is(true)); - assertThat(cors.origin(), is("*")); - assertThat(cors.origins().isEmpty(), is(true)); - } - - @Test - public void wildcardOrigin() { - final CorsConfig cors = forOrigin("*").build(); - assertThat(cors.isAnyOriginSupported(), is(true)); - assertThat(cors.origin(), equalTo("*")); - assertThat(cors.origins().isEmpty(), is(true)); - } - - @Test - public void origin() { - final CorsConfig cors = forOrigin("http://localhost:7888").build(); - assertThat(cors.origin(), is(equalTo("http://localhost:7888"))); - assertThat(cors.isAnyOriginSupported(), is(false)); - } - - @Test - public void origins() { - final String[] origins = {"http://localhost:7888", "https://localhost:7888"}; - final CorsConfig cors = forOrigins(origins).build(); - assertThat(cors.origins(), hasItems(origins)); - assertThat(cors.isAnyOriginSupported(), is(false)); - } - - @Test - public void exposeHeaders() { - final CorsConfig cors = forAnyOrigin().exposeHeaders("custom-header1", "custom-header2").build(); - assertThat(cors.exposedHeaders(), hasItems("custom-header1", "custom-header2")); - } - - @Test - public void allowCredentials() { - final CorsConfig cors = forAnyOrigin().allowCredentials().build(); - assertThat(cors.isCredentialsAllowed(), is(true)); - } - - @Test - public void maxAge() { - final CorsConfig cors = forAnyOrigin().maxAge(3000).build(); - assertThat(cors.maxAge(), is(3000L)); - } - - @Test - public void requestMethods() { - final CorsConfig cors = forAnyOrigin().allowedRequestMethods(HttpMethod.POST, HttpMethod.GET).build(); - assertThat(cors.allowedRequestMethods(), hasItems(HttpMethod.POST, HttpMethod.GET)); - } - - @Test - public void requestHeaders() { - final CorsConfig cors = forAnyOrigin().allowedRequestHeaders("preflight-header1", "preflight-header2").build(); - assertThat(cors.allowedRequestHeaders(), hasItems("preflight-header1", "preflight-header2")); - } - - @Test - public void preflightResponseHeadersSingleValue() { - final CorsConfig cors = forAnyOrigin().preflightResponseHeader("SingleValue", "value").build(); - assertThat(cors.preflightResponseHeaders().get(of("SingleValue")), equalTo("value")); - } - - @Test - public void preflightResponseHeadersMultipleValues() { - final CorsConfig cors = forAnyOrigin().preflightResponseHeader("MultipleValues", "value1", "value2").build(); - assertThat(cors.preflightResponseHeaders().getAll(of("MultipleValues")), hasItems("value1", "value2")); - } - - @Test - public void defaultPreflightResponseHeaders() { - final CorsConfig cors = forAnyOrigin().build(); - assertThat(cors.preflightResponseHeaders().get(HttpHeaderNames.DATE), is(notNullValue())); - assertThat(cors.preflightResponseHeaders().get(HttpHeaderNames.CONTENT_LENGTH), is("0")); - } - - @Test - public void emptyPreflightResponseHeaders() { - final CorsConfig cors = forAnyOrigin().noPreflightResponseHeaders().build(); - assertThat(cors.preflightResponseHeaders(), equalTo((HttpHeaders) EmptyHttpHeaders.INSTANCE)); - } - - @Test - public void shouldThrowIfValueIsNull() { - assertThrows(IllegalArgumentException.class, - () -> forOrigin("*").preflightResponseHeader("HeaderName", new Object[]{null}).build()); - } - - @Test - public void shortCircuit() { - final CorsConfig cors = forOrigin("http://localhost:8080").shortCircuit().build(); - assertThat(cors.isShortCircuit(), is(true)); - } - -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/cors/CorsHandlerTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/cors/CorsHandlerTest.java deleted file mode 100644 index c0e07a7857..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/cors/CorsHandlerTest.java +++ /dev/null @@ -1,556 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version - * 2.0 (the "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http.cors; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.DefaultFullHttpResponse; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.HttpUtil; -import io.netty.util.AsciiString; -import io.netty.util.ReferenceCountUtil; -import org.hamcrest.core.IsEqual; -import org.junit.jupiter.api.Test; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.Callable; - -import static io.netty.handler.codec.http.HttpHeaderNames.*; -import static io.netty.handler.codec.http.HttpHeaderValues.CLOSE; -import static io.netty.handler.codec.http.HttpHeaderValues.KEEP_ALIVE; -import static io.netty.handler.codec.http.HttpHeadersTestUtils.of; -import static io.netty.handler.codec.http.HttpMethod.*; -import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN; -import static io.netty.handler.codec.http.HttpResponseStatus.OK; -import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; -import static io.netty.handler.codec.http.cors.CorsConfigBuilder.*; -import static org.hamcrest.CoreMatchers.*; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.core.IsEqual.equalTo; - -public class CorsHandlerTest { - - @Test - public void nonCorsRequest() { - final HttpResponse response = simpleRequest(forAnyOrigin().build(), null); - assertThat(response.headers().contains(ACCESS_CONTROL_ALLOW_ORIGIN), is(false)); - assertThat(ReferenceCountUtil.release(response), is(true)); - } - - @Test - public void simpleRequestWithAnyOrigin() { - final HttpResponse response = simpleRequest(forAnyOrigin().build(), "http://localhost:7777"); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN), is("*")); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_HEADERS), is(nullValue())); - assertThat(ReferenceCountUtil.release(response), is(true)); - } - - @Test - public void simpleRequestWithNullOrigin() { - final HttpResponse response = simpleRequest(forOrigin("http://test.com").allowNullOrigin() - .allowCredentials() - .build(), "null"); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN), is("null")); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_CREDENTIALS), is(equalTo("true"))); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_HEADERS), is(nullValue())); - assertThat(ReferenceCountUtil.release(response), is(true)); - } - - @Test - public void simpleRequestWithOrigin() { - final String origin = "http://localhost:8888"; - final HttpResponse response = simpleRequest(forOrigin(origin).build(), origin); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN), is(origin)); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_HEADERS), is(nullValue())); - assertThat(ReferenceCountUtil.release(response), is(true)); - } - - @Test - public void simpleRequestWithOrigins() { - final String origin1 = "http://localhost:8888"; - final String origin2 = "https://localhost:8888"; - final String[] origins = {origin1, origin2}; - final HttpResponse response1 = simpleRequest(forOrigins(origins).build(), origin1); - assertThat(response1.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN), is(origin1)); - assertThat(response1.headers().get(ACCESS_CONTROL_ALLOW_HEADERS), is(nullValue())); - assertThat(ReferenceCountUtil.release(response1), is(true)); - - final HttpResponse response2 = simpleRequest(forOrigins(origins).build(), origin2); - assertThat(response2.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN), is(origin2)); - assertThat(response2.headers().get(ACCESS_CONTROL_ALLOW_HEADERS), is(nullValue())); - assertThat(ReferenceCountUtil.release(response2), is(true)); - } - - @Test - public void simpleRequestWithNoMatchingOrigin() { - final String origin = "http://localhost:8888"; - final HttpResponse response = simpleRequest( - forOrigins("https://localhost:8888").build(), origin); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN), is(nullValue())); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_HEADERS), is(nullValue())); - assertThat(ReferenceCountUtil.release(response), is(true)); - } - - @Test - public void preflightDeleteRequestWithCustomHeaders() { - final CorsConfig config = forOrigin("http://localhost:8888") - .allowedRequestMethods(GET, DELETE) - .build(); - final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1"); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN), is("http://localhost:8888")); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_METHODS), containsString("GET")); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_METHODS), containsString("DELETE")); - assertThat(response.headers().get(VARY), equalTo(ORIGIN.toString())); - assertThat(ReferenceCountUtil.release(response), is(true)); - } - - @Test - public void preflightGetRequestWithCustomHeaders() { - final CorsConfig config = forOrigin("http://localhost:8888") - .allowedRequestMethods(OPTIONS, GET, DELETE) - .allowedRequestHeaders("content-type", "xheader1") - .build(); - final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1"); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN), is("http://localhost:8888")); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_METHODS), containsString("OPTIONS")); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_METHODS), containsString("GET")); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_HEADERS), containsString("content-type")); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_HEADERS), containsString("xheader1")); - assertThat(response.headers().get(VARY), equalTo(ORIGIN.toString())); - assertThat(ReferenceCountUtil.release(response), is(true)); - } - - @Test - public void preflightRequestWithDefaultHeaders() { - final CorsConfig config = forOrigin("http://localhost:8888").build(); - final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1"); - assertThat(response.headers().get(CONTENT_LENGTH), is("0")); - assertThat(response.headers().get(DATE), is(notNullValue())); - assertThat(response.headers().get(VARY), equalTo(ORIGIN.toString())); - assertThat(ReferenceCountUtil.release(response), is(true)); - } - - @Test - public void preflightRequestWithCustomHeader() { - final CorsConfig config = forOrigin("http://localhost:8888") - .preflightResponseHeader("CustomHeader", "somevalue") - .build(); - final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1"); - assertThat(response.headers().get(of("CustomHeader")), equalTo("somevalue")); - assertThat(response.headers().get(VARY), equalTo(ORIGIN.toString())); - assertThat(response.headers().get(CONTENT_LENGTH), is("0")); - assertThat(ReferenceCountUtil.release(response), is(true)); - } - - @Test - public void preflightRequestWithUnauthorizedOrigin() { - final String origin = "http://host"; - final CorsConfig config = forOrigin("http://localhost").build(); - final HttpResponse response = preflightRequest(config, origin, "xheader1"); - assertThat(response.headers().contains(ACCESS_CONTROL_ALLOW_ORIGIN), is(false)); - assertThat(ReferenceCountUtil.release(response), is(true)); - } - - @Test - public void preflightRequestWithCustomHeaders() { - final String headerName = "CustomHeader"; - final String value1 = "value1"; - final String value2 = "value2"; - final CorsConfig config = forOrigin("http://localhost:8888") - .preflightResponseHeader(headerName, value1, value2) - .build(); - final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1"); - assertValues(response, headerName, value1, value2); - assertThat(response.headers().get(VARY), equalTo(ORIGIN.toString())); - assertThat(ReferenceCountUtil.release(response), is(true)); - } - - @Test - public void preflightRequestWithCustomHeadersIterable() { - final String headerName = "CustomHeader"; - final String value1 = "value1"; - final String value2 = "value2"; - final CorsConfig config = forOrigin("http://localhost:8888") - .preflightResponseHeader(headerName, Arrays.asList(value1, value2)) - .build(); - final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1"); - assertValues(response, headerName, value1, value2); - assertThat(response.headers().get(VARY), equalTo(ORIGIN.toString())); - assertThat(ReferenceCountUtil.release(response), is(true)); - } - - @Test - public void preflightRequestWithValueGenerator() { - final CorsConfig config = forOrigin("http://localhost:8888") - .preflightResponseHeader("GenHeader", () -> "generatedValue").build(); - final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1"); - assertThat(response.headers().get(of("GenHeader")), equalTo("generatedValue")); - assertThat(response.headers().get(VARY), equalTo(ORIGIN.toString())); - assertThat(ReferenceCountUtil.release(response), is(true)); - } - - @Test - public void preflightRequestWithNullOrigin() { - final String origin = "null"; - final CorsConfig config = forOrigin(origin) - .allowNullOrigin() - .allowCredentials() - .build(); - final HttpResponse response = preflightRequest(config, origin, "content-type, xheader1"); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN), is(equalTo("null"))); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_CREDENTIALS), is(equalTo("true"))); - assertThat(ReferenceCountUtil.release(response), is(true)); - } - - @Test - public void preflightRequestAllowCredentials() { - final String origin = "null"; - final CorsConfig config = forOrigin(origin).allowCredentials().build(); - final HttpResponse response = preflightRequest(config, origin, "content-type, xheader1"); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_CREDENTIALS), is(equalTo("true"))); - assertThat(ReferenceCountUtil.release(response), is(true)); - } - - @Test - public void preflightRequestDoNotAllowCredentials() { - final CorsConfig config = forOrigin("http://localhost:8888").build(); - final HttpResponse response = preflightRequest(config, "http://localhost:8888", ""); - // the only valid value for Access-Control-Allow-Credentials is true. - assertThat(response.headers().contains(ACCESS_CONTROL_ALLOW_CREDENTIALS), is(false)); - assertThat(ReferenceCountUtil.release(response), is(true)); - } - - @Test - public void simpleRequestCustomHeaders() { - final CorsConfig config = forAnyOrigin().exposeHeaders("custom1", "custom2").build(); - final HttpResponse response = simpleRequest(config, "http://localhost:7777"); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN), equalTo("*")); - assertThat(response.headers().get(ACCESS_CONTROL_EXPOSE_HEADERS), containsString("custom1")); - assertThat(response.headers().get(ACCESS_CONTROL_EXPOSE_HEADERS), containsString("custom2")); - assertThat(ReferenceCountUtil.release(response), is(true)); - } - - @Test - public void simpleRequestAllowCredentials() { - final CorsConfig config = forAnyOrigin().allowCredentials().build(); - final HttpResponse response = simpleRequest(config, "http://localhost:7777"); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_CREDENTIALS), equalTo("true")); - assertThat(ReferenceCountUtil.release(response), is(true)); - } - - @Test - public void simpleRequestDoNotAllowCredentials() { - final CorsConfig config = forAnyOrigin().build(); - final HttpResponse response = simpleRequest(config, "http://localhost:7777"); - assertThat(response.headers().contains(ACCESS_CONTROL_ALLOW_CREDENTIALS), is(false)); - assertThat(ReferenceCountUtil.release(response), is(true)); - } - - @Test - public void anyOriginAndAllowCredentialsShouldEchoRequestOrigin() { - final CorsConfig config = forAnyOrigin().allowCredentials().build(); - final HttpResponse response = simpleRequest(config, "http://localhost:7777"); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_CREDENTIALS), equalTo("true")); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN), equalTo("http://localhost:7777")); - assertThat(response.headers().get(VARY), equalTo(ORIGIN.toString())); - assertThat(ReferenceCountUtil.release(response), is(true)); - } - - @Test - public void simpleRequestExposeHeaders() { - final CorsConfig config = forAnyOrigin().exposeHeaders("one", "two").build(); - final HttpResponse response = simpleRequest(config, "http://localhost:7777"); - assertThat(response.headers().get(ACCESS_CONTROL_EXPOSE_HEADERS), containsString("one")); - assertThat(response.headers().get(ACCESS_CONTROL_EXPOSE_HEADERS), containsString("two")); - assertThat(ReferenceCountUtil.release(response), is(true)); - } - - @Test - public void simpleRequestShortCircuit() { - final CorsConfig config = forOrigin("http://localhost:8080").shortCircuit().build(); - final HttpResponse response = simpleRequest(config, "http://localhost:7777"); - assertThat(response.status(), is(FORBIDDEN)); - assertThat(response.headers().get(CONTENT_LENGTH), is("0")); - assertThat(ReferenceCountUtil.release(response), is(true)); - } - - @Test - public void simpleRequestNoShortCircuit() { - final CorsConfig config = forOrigin("http://localhost:8080").build(); - final HttpResponse response = simpleRequest(config, "http://localhost:7777"); - assertThat(response.status(), is(OK)); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN), is(nullValue())); - assertThat(ReferenceCountUtil.release(response), is(true)); - } - - @Test - public void shortCircuitNonCorsRequest() { - final CorsConfig config = forOrigin("https://localhost").shortCircuit().build(); - final HttpResponse response = simpleRequest(config, null); - assertThat(response.status(), is(OK)); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN), is(nullValue())); - assertThat(ReferenceCountUtil.release(response), is(true)); - } - - @Test - public void shortCircuitWithConnectionKeepAliveShouldStayOpen() { - final CorsConfig config = forOrigin("http://localhost:8080").shortCircuit().build(); - final EmbeddedChannel channel = new EmbeddedChannel(new CorsHandler(config)); - final FullHttpRequest request = createHttpRequest(GET); - request.headers().set(ORIGIN, "http://localhost:8888"); - request.headers().set(CONNECTION, KEEP_ALIVE); - - assertThat(channel.writeInbound(request), is(false)); - final HttpResponse response = channel.readOutbound(); - assertThat(HttpUtil.isKeepAlive(response), is(true)); - - assertThat(channel.isOpen(), is(true)); - assertThat(response.status(), is(FORBIDDEN)); - assertThat(ReferenceCountUtil.release(response), is(true)); - assertThat(channel.finish(), is(false)); - } - - @Test - public void shortCircuitWithoutConnectionShouldStayOpen() { - final CorsConfig config = forOrigin("http://localhost:8080").shortCircuit().build(); - final EmbeddedChannel channel = new EmbeddedChannel(new CorsHandler(config)); - final FullHttpRequest request = createHttpRequest(GET); - request.headers().set(ORIGIN, "http://localhost:8888"); - - assertThat(channel.writeInbound(request), is(false)); - final HttpResponse response = channel.readOutbound(); - assertThat(HttpUtil.isKeepAlive(response), is(true)); - - assertThat(channel.isOpen(), is(true)); - assertThat(response.status(), is(FORBIDDEN)); - assertThat(ReferenceCountUtil.release(response), is(true)); - assertThat(channel.finish(), is(false)); - } - - @Test - public void shortCircuitWithConnectionCloseShouldClose() { - final CorsConfig config = forOrigin("http://localhost:8080").shortCircuit().build(); - final EmbeddedChannel channel = new EmbeddedChannel(new CorsHandler(config)); - final FullHttpRequest request = createHttpRequest(GET); - request.headers().set(ORIGIN, "http://localhost:8888"); - request.headers().set(CONNECTION, CLOSE); - - assertThat(channel.writeInbound(request), is(false)); - final HttpResponse response = channel.readOutbound(); - assertThat(HttpUtil.isKeepAlive(response), is(false)); - - assertThat(channel.isOpen(), is(false)); - assertThat(response.status(), is(FORBIDDEN)); - assertThat(ReferenceCountUtil.release(response), is(true)); - assertThat(channel.finish(), is(false)); - } - - @Test - public void preflightRequestShouldReleaseRequest() { - final CorsConfig config = forOrigin("http://localhost:8888") - .preflightResponseHeader("CustomHeader", Arrays.asList("value1", "value2")) - .build(); - final EmbeddedChannel channel = new EmbeddedChannel(new CorsHandler(config)); - final FullHttpRequest request = optionsRequest("http://localhost:8888", "content-type, xheader1", null); - assertThat(channel.writeInbound(request), is(false)); - assertThat(request.refCnt(), is(0)); - assertThat(ReferenceCountUtil.release(channel.readOutbound()), is(true)); - assertThat(channel.finish(), is(false)); - } - - @Test - public void preflightRequestWithConnectionKeepAliveShouldStayOpen() throws Exception { - - final CorsConfig config = forOrigin("http://localhost:8888").build(); - final EmbeddedChannel channel = new EmbeddedChannel(new CorsHandler(config)); - final FullHttpRequest request = optionsRequest("http://localhost:8888", "", KEEP_ALIVE); - assertThat(channel.writeInbound(request), is(false)); - final HttpResponse response = channel.readOutbound(); - assertThat(HttpUtil.isKeepAlive(response), is(true)); - - assertThat(channel.isOpen(), is(true)); - assertThat(response.status(), is(OK)); - assertThat(ReferenceCountUtil.release(response), is(true)); - assertThat(channel.finish(), is(false)); - } - - @Test - public void preflightRequestWithoutConnectionShouldStayOpen() throws Exception { - - final CorsConfig config = forOrigin("http://localhost:8888").build(); - final EmbeddedChannel channel = new EmbeddedChannel(new CorsHandler(config)); - final FullHttpRequest request = optionsRequest("http://localhost:8888", "", null); - assertThat(channel.writeInbound(request), is(false)); - final HttpResponse response = channel.readOutbound(); - assertThat(HttpUtil.isKeepAlive(response), is(true)); - - assertThat(channel.isOpen(), is(true)); - assertThat(response.status(), is(OK)); - assertThat(ReferenceCountUtil.release(response), is(true)); - assertThat(channel.finish(), is(false)); - } - - @Test - public void preflightRequestWithConnectionCloseShouldClose() throws Exception { - - final CorsConfig config = forOrigin("http://localhost:8888").build(); - final EmbeddedChannel channel = new EmbeddedChannel(new CorsHandler(config)); - final FullHttpRequest request = optionsRequest("http://localhost:8888", "", CLOSE); - assertThat(channel.writeInbound(request), is(false)); - final HttpResponse response = channel.readOutbound(); - assertThat(HttpUtil.isKeepAlive(response), is(false)); - - assertThat(channel.isOpen(), is(false)); - assertThat(response.status(), is(OK)); - assertThat(ReferenceCountUtil.release(response), is(true)); - assertThat(channel.finish(), is(false)); - } - - @Test - public void forbiddenShouldReleaseRequest() { - final CorsConfig config = forOrigin("https://localhost").shortCircuit().build(); - final EmbeddedChannel channel = new EmbeddedChannel(new CorsHandler(config), new EchoHandler()); - final FullHttpRequest request = createHttpRequest(GET); - request.headers().set(ORIGIN, "http://localhost:8888"); - assertThat(channel.writeInbound(request), is(false)); - assertThat(request.refCnt(), is(0)); - assertThat(ReferenceCountUtil.release(channel.readOutbound()), is(true)); - assertThat(channel.finish(), is(false)); - } - - @Test - public void differentConfigsPerOrigin() { - String host1 = "http://host1:80"; - String host2 = "http://host2"; - CorsConfig rule1 = forOrigin(host1).allowedRequestMethods(HttpMethod.GET).build(); - CorsConfig rule2 = forOrigin(host2).allowedRequestMethods(HttpMethod.GET, HttpMethod.POST) - .allowCredentials().build(); - - List corsConfigs = Arrays.asList(rule1, rule2); - - final HttpResponse preFlightHost1 = preflightRequest(corsConfigs, host1, "", false); - assertThat(preFlightHost1.headers().get(ACCESS_CONTROL_ALLOW_METHODS), is("GET")); - assertThat(preFlightHost1.headers().getAsString(ACCESS_CONTROL_ALLOW_CREDENTIALS), is(nullValue())); - - final HttpResponse preFlightHost2 = preflightRequest(corsConfigs, host2, "", false); - assertValues(preFlightHost2, ACCESS_CONTROL_ALLOW_METHODS.toString(), "GET", "POST"); - assertThat(preFlightHost2.headers().getAsString(ACCESS_CONTROL_ALLOW_CREDENTIALS), IsEqual.equalTo("true")); - } - - @Test - public void specificConfigPrecedenceOverGeneric() { - String host1 = "http://host1"; - String host2 = "http://host2"; - - CorsConfig forHost1 = forOrigin(host1).allowedRequestMethods(HttpMethod.GET).maxAge(3600L).build(); - CorsConfig allowAll = forAnyOrigin().allowedRequestMethods(HttpMethod.POST, HttpMethod.GET, HttpMethod.OPTIONS) - .maxAge(1800).build(); - - List rules = Arrays.asList(forHost1, allowAll); - - final HttpResponse host1Response = preflightRequest(rules, host1, "", false); - assertThat(host1Response.headers().get(ACCESS_CONTROL_ALLOW_METHODS), is("GET")); - assertThat(host1Response.headers().getAsString(ACCESS_CONTROL_MAX_AGE), equalTo("3600")); - - final HttpResponse host2Response = preflightRequest(rules, host2, "", false); - assertValues(host2Response, ACCESS_CONTROL_ALLOW_METHODS.toString(), "POST", "GET", "OPTIONS"); - assertThat(host2Response.headers().getAsString(ACCESS_CONTROL_ALLOW_ORIGIN), equalTo("*")); - assertThat(host2Response.headers().getAsString(ACCESS_CONTROL_MAX_AGE), equalTo("1800")); - } - - private static HttpResponse simpleRequest(final CorsConfig config, final String origin) { - return simpleRequest(config, origin, null); - } - - private static HttpResponse simpleRequest(final CorsConfig config, - final String origin, - final String requestHeaders) { - return simpleRequest(config, origin, requestHeaders, GET); - } - - private static HttpResponse simpleRequest(final CorsConfig config, - final String origin, - final String requestHeaders, - final HttpMethod method) { - final EmbeddedChannel channel = new EmbeddedChannel(new CorsHandler(config), new EchoHandler()); - final FullHttpRequest httpRequest = createHttpRequest(method); - if (origin != null) { - httpRequest.headers().set(ORIGIN, origin); - } - if (requestHeaders != null) { - httpRequest.headers().set(ACCESS_CONTROL_REQUEST_HEADERS, requestHeaders); - } - assertThat(channel.writeInbound(httpRequest), is(false)); - HttpResponse response = channel.readOutbound(); - assertThat(channel.finish(), is(false)); - return response; - } - - private static HttpResponse preflightRequest(final CorsConfig config, - final String origin, - final String requestHeaders) { - return preflightRequest(Collections.singletonList(config), origin, requestHeaders, config.isShortCircuit()); - } - - private static HttpResponse preflightRequest(final List configs, - final String origin, - final String requestHeaders, - final boolean isSHortCircuit) { - final EmbeddedChannel channel = new EmbeddedChannel(new CorsHandler(configs, isSHortCircuit)); - assertThat(channel.writeInbound(optionsRequest(origin, requestHeaders, null)), is(false)); - HttpResponse response = channel.readOutbound(); - assertThat(channel.finish(), is(false)); - return response; - } - - private static FullHttpRequest optionsRequest(final String origin, - final String requestHeaders, - final AsciiString connection) { - final FullHttpRequest httpRequest = createHttpRequest(OPTIONS); - httpRequest.headers().set(ORIGIN, origin); - httpRequest.headers().set(ACCESS_CONTROL_REQUEST_METHOD, httpRequest.method().toString()); - httpRequest.headers().set(ACCESS_CONTROL_REQUEST_HEADERS, requestHeaders); - if (connection != null) { - httpRequest.headers().set(CONNECTION, connection); - } - - return httpRequest; - } - - private static FullHttpRequest createHttpRequest(HttpMethod method) { - return new DefaultFullHttpRequest(HTTP_1_1, method, "/info"); - } - - private static class EchoHandler extends SimpleChannelInboundHandler { - @Override - public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { - ctx.writeAndFlush(new DefaultFullHttpResponse(HTTP_1_1, OK, true, true)); - } - } - - private static void assertValues(final HttpResponse response, final String headerName, final String... values) { - final String header = response.headers().get(of(headerName)); - for (String value : values) { - assertThat(header, containsString(value)); - } - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/multipart/AbstractDiskHttpDataTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/multipart/AbstractDiskHttpDataTest.java deleted file mode 100644 index e686405306..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/multipart/AbstractDiskHttpDataTest.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.util.internal.PlatformDependent; -import org.junit.jupiter.api.Test; - -import java.io.File; -import java.io.FileOutputStream; -import java.nio.charset.Charset; -import java.util.Arrays; -import java.util.UUID; -import java.util.concurrent.ThreadLocalRandom; - -import static io.netty.util.CharsetUtil.UTF_8; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; - -/** - * {@link AbstractDiskHttpData} test cases - */ -public class AbstractDiskHttpDataTest { - - @Test - public void testGetChunk() throws Exception { - TestHttpData test = new TestHttpData("test", UTF_8, 0); - try { - File tmpFile = PlatformDependent.createTempFile(UUID.randomUUID().toString(), ".tmp", null); - tmpFile.deleteOnExit(); - FileOutputStream fos = new FileOutputStream(tmpFile); - byte[] bytes = new byte[4096]; - ThreadLocalRandom.current().nextBytes(bytes); - try { - fos.write(bytes); - fos.flush(); - } finally { - fos.close(); - } - test.setContent(tmpFile); - ByteBuf buf1 = test.getChunk(1024); - assertEquals(buf1.readerIndex(), 0); - assertEquals(buf1.writerIndex(), 1024); - ByteBuf buf2 = test.getChunk(1024); - assertEquals(buf2.readerIndex(), 0); - assertEquals(buf2.writerIndex(), 1024); - assertFalse(Arrays.equals(ByteBufUtil.getBytes(buf1), ByteBufUtil.getBytes(buf2)), - "Arrays should not be equal"); - } finally { - test.delete(); - } - } - - private static final class TestHttpData extends AbstractDiskHttpData { - - private TestHttpData(String name, Charset charset, long size) { - super(name, charset, size); - } - - @Override - protected String getDiskFilename() { - return null; - } - - @Override - protected String getPrefix() { - return null; - } - - @Override - protected String getBaseDirectory() { - return null; - } - - @Override - protected String getPostfix() { - return null; - } - - @Override - protected boolean deleteOnExit() { - return false; - } - - @Override - public HttpData copy() { - return null; - } - - @Override - public HttpData duplicate() { - return null; - } - - @Override - public HttpData retainedDuplicate() { - return null; - } - - @Override - public HttpData replace(ByteBuf content) { - return null; - } - - @Override - public HttpDataType getHttpDataType() { - return null; - } - - @Override - public int compareTo(InterfaceHttpData o) { - return 0; - } - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/multipart/AbstractMemoryHttpDataTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/multipart/AbstractMemoryHttpDataTest.java deleted file mode 100644 index 4b906e47db..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/multipart/AbstractMemoryHttpDataTest.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufInputStream; -import io.netty.buffer.ByteBufUtil; -import io.netty.buffer.Unpooled; - -import io.netty.util.internal.PlatformDependent; -import org.junit.jupiter.api.Test; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.nio.charset.Charset; -import java.security.SecureRandom; -import java.util.Arrays; -import java.util.Random; -import java.util.UUID; -import java.util.concurrent.ThreadLocalRandom; - -import static io.netty.util.CharsetUtil.*; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** {@link AbstractMemoryHttpData} test cases. */ -public class AbstractMemoryHttpDataTest { - - @Test - public void testSetContentFromFile() throws Exception { - TestHttpData test = new TestHttpData("test", UTF_8, 0); - try { - File tmpFile = PlatformDependent.createTempFile(UUID.randomUUID().toString(), ".tmp", null); - tmpFile.deleteOnExit(); - FileOutputStream fos = new FileOutputStream(tmpFile); - byte[] bytes = new byte[4096]; - ThreadLocalRandom.current().nextBytes(bytes); - try { - fos.write(bytes); - fos.flush(); - } finally { - fos.close(); - } - test.setContent(tmpFile); - ByteBuf buf = test.getByteBuf(); - assertEquals(buf.readerIndex(), 0); - assertEquals(buf.writerIndex(), bytes.length); - assertArrayEquals(bytes, test.get()); - assertArrayEquals(bytes, ByteBufUtil.getBytes(buf)); - } finally { - //release the ByteBuf - test.delete(); - } - } - - @Test - public void testRenameTo() throws Exception { - TestHttpData test = new TestHttpData("test", UTF_8, 0); - try { - File tmpFile = PlatformDependent.createTempFile(UUID.randomUUID().toString(), ".tmp", null); - tmpFile.deleteOnExit(); - final int totalByteCount = 4096; - byte[] bytes = new byte[totalByteCount]; - ThreadLocalRandom.current().nextBytes(bytes); - ByteBuf content = Unpooled.wrappedBuffer(bytes); - test.setContent(content); - boolean succ = test.renameTo(tmpFile); - assertTrue(succ); - FileInputStream fis = new FileInputStream(tmpFile); - try { - byte[] buf = new byte[totalByteCount]; - int count = 0; - int offset = 0; - int size = totalByteCount; - while ((count = fis.read(buf, offset, size)) > 0) { - offset += count; - size -= count; - if (offset >= totalByteCount || size <= 0) { - break; - } - } - assertArrayEquals(bytes, buf); - assertEquals(0, fis.available()); - } finally { - fis.close(); - } - } finally { - //release the ByteBuf in AbstractMemoryHttpData - test.delete(); - } - } - /** - * Provide content into HTTP data with input stream. - * - * @throws Exception In case of any exception. - */ - @Test - public void testSetContentFromStream() throws Exception { - // definedSize=0 - TestHttpData test = new TestHttpData("test", UTF_8, 0); - String contentStr = "foo_test"; - ByteBuf buf = Unpooled.wrappedBuffer(contentStr.getBytes(UTF_8)); - int readerIndex = buf.readerIndex(); - - try (ByteBufInputStream is = new ByteBufInputStream(buf)) { - test.setContent(is); - assertFalse(buf.isReadable()); - assertEquals(test.getString(UTF_8), contentStr); - buf.readerIndex(readerIndex); - assertTrue(ByteBufUtil.equals(buf, test.getByteBuf())); - } - - Random random = new SecureRandom(); - - for (int i = 0; i < 20; i++) { - // Generate input data bytes. - int size = random.nextInt(Short.MAX_VALUE); - byte[] bytes = new byte[size]; - - random.nextBytes(bytes); - - // Generate parsed HTTP data block. - TestHttpData data = new TestHttpData("name", UTF_8, 0); - - data.setContent(new ByteArrayInputStream(bytes)); - - // Validate stored data. - ByteBuf buffer = data.getByteBuf(); - - assertEquals(0, buffer.readerIndex()); - assertEquals(bytes.length, buffer.writerIndex()); - assertArrayEquals(bytes, Arrays.copyOf(buffer.array(), bytes.length)); - assertArrayEquals(bytes, data.get()); - } - } - - /** Memory-based HTTP data implementation for test purposes. */ - private static final class TestHttpData extends AbstractMemoryHttpData { - /** - * Constructs HTTP data for tests. - * - * @param name Name of parsed data block. - * @param charset Used charset for data decoding. - * @param size Expected data block size. - */ - private TestHttpData(String name, Charset charset, long size) { - super(name, charset, size); - } - - @Override - public InterfaceHttpData.HttpDataType getHttpDataType() { - throw reject(); - } - - @Override - public HttpData copy() { - throw reject(); - } - - @Override - public HttpData duplicate() { - throw reject(); - } - - @Override - public HttpData retainedDuplicate() { - throw reject(); - } - - @Override - public HttpData replace(ByteBuf content) { - return null; - } - - @Override - public int compareTo(InterfaceHttpData o) { - throw reject(); - } - - @Override - public int hashCode() { - return super.hashCode(); - } - - @Override - public boolean equals(Object obj) { - return super.equals(obj); - } - - private static UnsupportedOperationException reject() { - throw new UnsupportedOperationException("Should never be called."); - } - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/multipart/DefaultHttpDataFactoryTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/multipart/DefaultHttpDataFactoryTest.java deleted file mode 100644 index 9e6c2e17e4..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/multipart/DefaultHttpDataFactoryTest.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.buffer.Unpooled; -import io.netty.handler.codec.http.DefaultHttpRequest; -import io.netty.handler.codec.http.HttpRequest; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static io.netty.handler.codec.http.HttpHeaderValues.IDENTITY; -import static io.netty.handler.codec.http.HttpMethod.POST; -import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; -import static io.netty.handler.codec.http.multipart.HttpPostBodyUtil.DEFAULT_TEXT_CONTENT_TYPE; -import static io.netty.util.CharsetUtil.UTF_8; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class DefaultHttpDataFactoryTest { - // req1 equals req2 - private static final HttpRequest req1 = new DefaultHttpRequest(HTTP_1_1, POST, "/form"); - private static final HttpRequest req2 = new DefaultHttpRequest(HTTP_1_1, POST, "/form"); - - private DefaultHttpDataFactory factory; - - @BeforeAll - public static void assertReq1EqualsReq2() { - // Before doing anything, assert that the requests are equal - assertEquals(req1.hashCode(), req2.hashCode()); - assertTrue(req1.equals(req2)); - } - - @BeforeEach - public void setupFactory() { - factory = new DefaultHttpDataFactory(); - } - - @AfterEach - public void cleanupFactory() { - factory.cleanAllHttpData(); - } - - @Test - public void customBaseDirAndDeleteOnExit() { - final DefaultHttpDataFactory defaultHttpDataFactory = new DefaultHttpDataFactory(true); - final String dir = "target/DefaultHttpDataFactoryTest/customBaseDirAndDeleteOnExit"; - defaultHttpDataFactory.setBaseDir(dir); - defaultHttpDataFactory.setDeleteOnExit(true); - final Attribute attr = defaultHttpDataFactory.createAttribute(req1, "attribute1"); - final FileUpload fu = defaultHttpDataFactory.createFileUpload( - req1, "attribute1", "f.txt", "text/plain", null, null, 0); - assertEquals(dir, DiskAttribute.class.cast(attr).getBaseDirectory()); - assertEquals(dir, DiskFileUpload.class.cast(fu).getBaseDirectory()); - assertTrue(DiskAttribute.class.cast(attr).deleteOnExit()); - assertTrue(DiskFileUpload.class.cast(fu).deleteOnExit()); - } - - @Test - public void cleanRequestHttpDataShouldIdentifiesRequestsByTheirIdentities() throws Exception { - // Create some data belonging to req1 and req2 - Attribute attribute1 = factory.createAttribute(req1, "attribute1", "value1"); - Attribute attribute2 = factory.createAttribute(req2, "attribute2", "value2"); - FileUpload file1 = factory.createFileUpload( - req1, "file1", "file1.txt", - DEFAULT_TEXT_CONTENT_TYPE, IDENTITY.toString(), UTF_8, 123 - ); - FileUpload file2 = factory.createFileUpload( - req2, "file2", "file2.txt", - DEFAULT_TEXT_CONTENT_TYPE, IDENTITY.toString(), UTF_8, 123 - ); - file1.setContent(Unpooled.copiedBuffer("file1 content", UTF_8)); - file2.setContent(Unpooled.copiedBuffer("file2 content", UTF_8)); - - // Assert that they are not deleted - assertNotNull(attribute1.getByteBuf()); - assertNotNull(attribute2.getByteBuf()); - assertNotNull(file1.getByteBuf()); - assertNotNull(file2.getByteBuf()); - assertEquals(1, attribute1.refCnt()); - assertEquals(1, attribute2.refCnt()); - assertEquals(1, file1.refCnt()); - assertEquals(1, file2.refCnt()); - - // Clean up by req1 - factory.cleanRequestHttpData(req1); - - // Assert that data belonging to req1 has been cleaned up - assertNull(attribute1.getByteBuf()); - assertNull(file1.getByteBuf()); - assertEquals(0, attribute1.refCnt()); - assertEquals(0, file1.refCnt()); - - // But not req2 - assertNotNull(attribute2.getByteBuf()); - assertNotNull(file2.getByteBuf()); - assertEquals(1, attribute2.refCnt()); - assertEquals(1, file2.refCnt()); - } - - @Test - public void removeHttpDataFromCleanShouldIdentifiesDataByTheirIdentities() throws Exception { - // Create some equal data items belonging to the same request - Attribute attribute1 = factory.createAttribute(req1, "attribute", "value"); - Attribute attribute2 = factory.createAttribute(req1, "attribute", "value"); - FileUpload file1 = factory.createFileUpload( - req1, "file", "file.txt", - DEFAULT_TEXT_CONTENT_TYPE, IDENTITY.toString(), UTF_8, 123 - ); - FileUpload file2 = factory.createFileUpload( - req1, "file", "file.txt", - DEFAULT_TEXT_CONTENT_TYPE, IDENTITY.toString(), UTF_8, 123 - ); - file1.setContent(Unpooled.copiedBuffer("file content", UTF_8)); - file2.setContent(Unpooled.copiedBuffer("file content", UTF_8)); - - // Before doing anything, assert that the data items are equal - assertEquals(attribute1.hashCode(), attribute2.hashCode()); - assertTrue(attribute1.equals(attribute2)); - assertEquals(file1.hashCode(), file2.hashCode()); - assertTrue(file1.equals(file2)); - - // Remove attribute2 and file2 from being cleaned up by factory - factory.removeHttpDataFromClean(req1, attribute2); - factory.removeHttpDataFromClean(req1, file2); - - // Clean up by req1 - factory.cleanRequestHttpData(req1); - - // Assert that attribute1 and file1 have been cleaned up - assertNull(attribute1.getByteBuf()); - assertNull(file1.getByteBuf()); - assertEquals(0, attribute1.refCnt()); - assertEquals(0, file1.refCnt()); - - // But not attribute2 and file2 - assertNotNull(attribute2.getByteBuf()); - assertNotNull(file2.getByteBuf()); - assertEquals(1, attribute2.refCnt()); - assertEquals(1, file2.refCnt()); - - // Cleanup attribute2 and file2 manually to avoid memory leak, not via factory - attribute2.release(); - file2.release(); - assertEquals(0, attribute2.refCnt()); - assertEquals(0, file2.refCnt()); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/multipart/DeleteFileOnExitHookTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/multipart/DeleteFileOnExitHookTest.java deleted file mode 100644 index fb1799f204..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/multipart/DeleteFileOnExitHookTest.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.buffer.Unpooled; -import io.netty.handler.codec.http.DefaultHttpRequest; -import io.netty.handler.codec.http.HttpRequest; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.io.File; -import java.io.FilenameFilter; -import java.io.IOException; - -import static io.netty.handler.codec.http.HttpMethod.POST; -import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** - * Test DeleteFileOnExitHook - */ -public class DeleteFileOnExitHookTest { - private static final HttpRequest REQUEST = new DefaultHttpRequest(HTTP_1_1, POST, "/form"); - private static final String HOOK_TEST_TMP = "target/DeleteFileOnExitHookTest/tmp"; - private FileUpload fu; - - @BeforeEach - public void setUp() throws IOException { - DefaultHttpDataFactory defaultHttpDataFactory = new DefaultHttpDataFactory(true); - defaultHttpDataFactory.setBaseDir(HOOK_TEST_TMP); - defaultHttpDataFactory.setDeleteOnExit(true); - - File baseDir = new File(HOOK_TEST_TMP); - baseDir.mkdirs(); // we don't need to clean it since it is in volatile files anyway - - fu = defaultHttpDataFactory.createFileUpload( - REQUEST, "attribute1", "tmp_f.txt", "text/plain", null, null, 0); - fu.setContent(Unpooled.wrappedBuffer(new byte[]{1, 2, 3, 4})); - - assertTrue(fu.getFile().exists()); - } - - @Test - public void testSimulateTriggerDeleteFileOnExitHook() { - - // simulate app exit - DeleteFileOnExitHook.runHook(); - - File[] files = new File(HOOK_TEST_TMP).listFiles(new FilenameFilter() { - @Override - public boolean accept(File dir, String name) { - return name.startsWith(DiskFileUpload.prefix); - } - }); - - assertEquals(0, files.length); - } - - @Test - public void testAfterHttpDataReleaseCheckFileExist() throws IOException { - - String filePath = fu.getFile().getPath(); - assertTrue(DeleteFileOnExitHook.checkFileExist(filePath)); - - fu.release(); - assertFalse(DeleteFileOnExitHook.checkFileExist(filePath)); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/multipart/DiskFileUploadTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/multipart/DiskFileUploadTest.java deleted file mode 100644 index 96e101eecb..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/multipart/DiskFileUploadTest.java +++ /dev/null @@ -1,299 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufInputStream; -import io.netty.buffer.ByteBufUtil; -import io.netty.buffer.Unpooled; -import io.netty.util.CharsetUtil; - -import io.netty.util.internal.PlatformDependent; -import org.junit.jupiter.api.Test; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.UUID; -import java.util.concurrent.ThreadLocalRandom; - -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - -public class DiskFileUploadTest { - @Test - public void testSpecificCustomBaseDir() throws IOException { - File baseDir = new File("target/DiskFileUploadTest/testSpecificCustomBaseDir"); - baseDir.mkdirs(); // we don't need to clean it since it is in volatile files anyway - DiskFileUpload f = - new DiskFileUpload("d1", "d1", "application/json", null, null, 100, - baseDir.getAbsolutePath(), false); - - f.setContent(Unpooled.EMPTY_BUFFER); - - assertTrue(f.getFile().getAbsolutePath().startsWith(baseDir.getAbsolutePath())); - assertTrue(f.getFile().exists()); - assertEquals(0, f.getFile().length()); - f.delete(); - } - - @Test - public final void testDiskFileUploadEquals() { - DiskFileUpload f2 = - new DiskFileUpload("d1", "d1", "application/json", null, null, 100); - assertEquals(f2, f2); - f2.delete(); - } - - @Test - public void testEmptyBufferSetMultipleTimes() throws IOException { - DiskFileUpload f = - new DiskFileUpload("d1", "d1", "application/json", null, null, 100); - - f.setContent(Unpooled.EMPTY_BUFFER); - - assertTrue(f.getFile().exists()); - assertEquals(0, f.getFile().length()); - f.setContent(Unpooled.EMPTY_BUFFER); - assertTrue(f.getFile().exists()); - assertEquals(0, f.getFile().length()); - f.delete(); - } - - @Test - public void testEmptyBufferSetAfterNonEmptyBuffer() throws IOException { - DiskFileUpload f = - new DiskFileUpload("d1", "d1", "application/json", null, null, 100); - - f.setContent(Unpooled.wrappedBuffer(new byte[] { 1, 2, 3, 4 })); - - assertTrue(f.getFile().exists()); - assertEquals(4, f.getFile().length()); - f.setContent(Unpooled.EMPTY_BUFFER); - assertTrue(f.getFile().exists()); - assertEquals(0, f.getFile().length()); - f.delete(); - } - - @Test - public void testNonEmptyBufferSetMultipleTimes() throws IOException { - DiskFileUpload f = - new DiskFileUpload("d1", "d1", "application/json", null, null, 100); - - f.setContent(Unpooled.wrappedBuffer(new byte[] { 1, 2, 3, 4 })); - - assertTrue(f.getFile().exists()); - assertEquals(4, f.getFile().length()); - f.setContent(Unpooled.wrappedBuffer(new byte[] { 1, 2})); - assertTrue(f.getFile().exists()); - assertEquals(2, f.getFile().length()); - f.delete(); - } - - @Test - public void testAddContents() throws Exception { - DiskFileUpload f1 = new DiskFileUpload("file1", "file1", "application/json", null, null, 0); - try { - byte[] jsonBytes = new byte[4096]; - ThreadLocalRandom.current().nextBytes(jsonBytes); - - f1.addContent(Unpooled.wrappedBuffer(jsonBytes, 0, 1024), false); - f1.addContent(Unpooled.wrappedBuffer(jsonBytes, 1024, jsonBytes.length - 1024), true); - assertArrayEquals(jsonBytes, f1.get()); - - File file = f1.getFile(); - assertEquals(jsonBytes.length, file.length()); - - FileInputStream fis = new FileInputStream(file); - try { - byte[] buf = new byte[jsonBytes.length]; - int offset = 0; - int read = 0; - int len = buf.length; - while ((read = fis.read(buf, offset, len)) > 0) { - len -= read; - offset += read; - if (len <= 0 || offset >= buf.length) { - break; - } - } - assertArrayEquals(jsonBytes, buf); - } finally { - fis.close(); - } - } finally { - f1.delete(); - } - } - - @Test - public void testSetContentFromByteBuf() throws Exception { - DiskFileUpload f1 = new DiskFileUpload("file2", "file2", "application/json", null, null, 0); - try { - String json = "{\"hello\":\"world\"}"; - byte[] bytes = json.getBytes(CharsetUtil.UTF_8); - f1.setContent(Unpooled.wrappedBuffer(bytes)); - assertEquals(json, f1.getString()); - assertArrayEquals(bytes, f1.get()); - File file = f1.getFile(); - assertEquals((long) bytes.length, file.length()); - assertArrayEquals(bytes, doReadFile(file, bytes.length)); - } finally { - f1.delete(); - } - } - - @Test - public void testSetContentFromInputStream() throws Exception { - String json = "{\"hello\":\"world\",\"foo\":\"bar\"}"; - DiskFileUpload f1 = new DiskFileUpload("file3", "file3", "application/json", null, null, 0); - try { - byte[] bytes = json.getBytes(CharsetUtil.UTF_8); - ByteBuf buf = Unpooled.wrappedBuffer(bytes); - InputStream is = new ByteBufInputStream(buf); - try { - f1.setContent(is); - assertEquals(json, f1.getString()); - assertArrayEquals(bytes, f1.get()); - File file = f1.getFile(); - assertEquals((long) bytes.length, file.length()); - assertArrayEquals(bytes, doReadFile(file, bytes.length)); - } finally { - buf.release(); - is.close(); - } - } finally { - f1.delete(); - } - } - - @Test - public void testAddContentFromByteBuf() throws Exception { - testAddContentFromByteBuf0(false); - } - - @Test - public void testAddContentFromCompositeByteBuf() throws Exception { - testAddContentFromByteBuf0(true); - } - - private static void testAddContentFromByteBuf0(boolean composite) throws Exception { - DiskFileUpload f1 = new DiskFileUpload("file3", "file3", "application/json", null, null, 0); - try { - byte[] bytes = new byte[4096]; - ThreadLocalRandom.current().nextBytes(bytes); - - final ByteBuf buffer; - - if (composite) { - buffer = Unpooled.compositeBuffer() - .addComponent(true, Unpooled.wrappedBuffer(bytes, 0 , bytes.length / 2)) - .addComponent(true, Unpooled.wrappedBuffer(bytes, bytes.length / 2, bytes.length / 2)); - } else { - buffer = Unpooled.wrappedBuffer(bytes); - } - f1.addContent(buffer, true); - ByteBuf buf = f1.getByteBuf(); - assertEquals(buf.readerIndex(), 0); - assertEquals(buf.writerIndex(), bytes.length); - assertArrayEquals(bytes, ByteBufUtil.getBytes(buf)); - } finally { - //release the ByteBuf - f1.delete(); - } - } - - private static byte[] doReadFile(File file, int maxRead) throws Exception { - FileInputStream fis = new FileInputStream(file); - try { - byte[] buf = new byte[maxRead]; - int offset = 0; - int read = 0; - int len = buf.length; - while ((read = fis.read(buf, offset, len)) > 0) { - len -= read; - offset += read; - if (len <= 0 || offset >= buf.length) { - break; - } - } - return buf; - } finally { - fis.close(); - } - } - - @Test - public void testDelete() throws Exception { - String json = "{\"foo\":\"bar\"}"; - byte[] bytes = json.getBytes(CharsetUtil.UTF_8); - File tmpFile = null; - DiskFileUpload f1 = new DiskFileUpload("file4", "file4", "application/json", null, null, 0); - try { - assertNull(f1.getFile()); - f1.setContent(Unpooled.wrappedBuffer(bytes)); - assertNotNull(tmpFile = f1.getFile()); - } finally { - f1.delete(); - assertNull(f1.getFile()); - assertNotNull(tmpFile); - assertFalse(tmpFile.exists()); - } - } - - @Test - public void setSetContentFromFileExceptionally() throws Exception { - final long maxSize = 4; - DiskFileUpload f1 = new DiskFileUpload("file5", "file5", "application/json", null, null, 0); - f1.setMaxSize(maxSize); - try { - f1.setContent(Unpooled.wrappedBuffer(new byte[(int) maxSize])); - File originalFile = f1.getFile(); - assertNotNull(originalFile); - assertEquals(maxSize, originalFile.length()); - assertEquals(maxSize, f1.length()); - byte[] bytes = new byte[8]; - - ThreadLocalRandom.current().nextBytes(bytes); - File tmpFile = PlatformDependent.createTempFile(UUID.randomUUID().toString(), ".tmp", null); - tmpFile.deleteOnExit(); - FileOutputStream fos = new FileOutputStream(tmpFile); - try { - fos.write(bytes); - fos.flush(); - } finally { - fos.close(); - } - try { - f1.setContent(tmpFile); - fail("should not reach here!"); - } catch (IOException e) { - assertNotNull(f1.getFile()); - assertEquals(originalFile, f1.getFile()); - assertEquals(maxSize, f1.length()); - } - } finally { - f1.delete(); - } - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/multipart/HttpDataTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/multipart/HttpDataTest.java deleted file mode 100644 index 17f90e70fa..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/multipart/HttpDataTest.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.PooledByteBufAllocator; -import io.netty.util.CharsetUtil; -import org.assertj.core.api.ThrowableAssert; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -import java.io.IOException; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.util.Random; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -class HttpDataTest { - private static final byte[] BYTES = new byte[64]; - - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.METHOD) - @ParameterizedTest(name = "{displayName}({0})") - @MethodSource("data") - @interface ParameterizedHttpDataTest { - } - - static HttpData[] data() { - return new HttpData[]{ - new MemoryAttribute("test", 10), - new MemoryFileUpload("test", "", "text/plain", null, CharsetUtil.UTF_8, 10), - new MixedAttribute("test", 10, -1), - new MixedFileUpload("test", "", "text/plain", null, CharsetUtil.UTF_8, 10, -1), - new DiskAttribute("test", 10), - new DiskFileUpload("test", "", "text/plain", null, CharsetUtil.UTF_8, 10) - }; - } - - @BeforeAll - static void setUp() { - Random rndm = new Random(); - rndm.nextBytes(BYTES); - } - - @ParameterizedHttpDataTest - void testAddContentEmptyBuffer(HttpData httpData) throws IOException { - ByteBuf content = PooledByteBufAllocator.DEFAULT.buffer(); - httpData.addContent(content, false); - assertThat(content.refCnt()).isEqualTo(0); - } - - @Test - void testAddContentExceedsDefinedSizeDiskFileUpload() { - doTestAddContentExceedsSize( - new DiskFileUpload("test", "", "application/json", null, CharsetUtil.UTF_8, 10), - "Out of size: 64 > 10"); - } - - @Test - void testAddContentExceedsDefinedSizeMemoryFileUpload() { - doTestAddContentExceedsSize( - new MemoryFileUpload("test", "", "application/json", null, CharsetUtil.UTF_8, 10), - "Out of size: 64 > 10"); - } - - @ParameterizedHttpDataTest - void testAddContentExceedsMaxSize(final HttpData httpData) { - httpData.setMaxSize(10); - doTestAddContentExceedsSize(httpData, "Size exceed allowed maximum capacity"); - } - - @ParameterizedHttpDataTest - void testSetContentExceedsDefinedSize(final HttpData httpData) { - doTestSetContentExceedsSize(httpData, "Out of size: 64 > 10"); - } - - @ParameterizedHttpDataTest - void testSetContentExceedsMaxSize(final HttpData httpData) { - httpData.setMaxSize(10); - doTestSetContentExceedsSize(httpData, "Size exceed allowed maximum capacity"); - } - - private static void doTestAddContentExceedsSize(final HttpData httpData, String expectedMessage) { - final ByteBuf content = PooledByteBufAllocator.DEFAULT.buffer(); - content.writeBytes(BYTES); - - assertThatExceptionOfType(IOException.class) - .isThrownBy(new ThrowableAssert.ThrowingCallable() { - - @Override - public void call() throws Throwable { - httpData.addContent(content, false); - } - }) - .withMessage(expectedMessage); - - assertThat(content.refCnt()).isEqualTo(0); - } - - private static void doTestSetContentExceedsSize(final HttpData httpData, String expectedMessage) { - final ByteBuf content = PooledByteBufAllocator.DEFAULT.buffer(); - content.writeBytes(BYTES); - - assertThatExceptionOfType(IOException.class) - .isThrownBy(new ThrowableAssert.ThrowingCallable() { - - @Override - public void call() throws Throwable { - httpData.setContent(content); - } - }) - .withMessage(expectedMessage); - - assertThat(content.refCnt()).isEqualTo(0); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/multipart/HttpPostMultiPartRequestDecoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/multipart/HttpPostMultiPartRequestDecoderTest.java deleted file mode 100644 index fb7e08202d..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/multipart/HttpPostMultiPartRequestDecoderTest.java +++ /dev/null @@ -1,361 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.DefaultHttpContent; -import io.netty.handler.codec.http.DefaultHttpRequest; -import io.netty.handler.codec.http.DefaultLastHttpContent; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.HttpConstants; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.util.CharsetUtil; -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.util.Arrays; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - -public class HttpPostMultiPartRequestDecoderTest { - - @Test - public void testDecodeFullHttpRequestWithNoContentTypeHeader() { - FullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/"); - try { - new HttpPostMultipartRequestDecoder(req); - fail("Was expecting an ErrorDataDecoderException"); - } catch (HttpPostRequestDecoder.ErrorDataDecoderException expected) { - // expected - } finally { - assertTrue(req.release()); - } - } - - @Test - public void testDecodeFullHttpRequestWithInvalidCharset() { - FullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/"); - req.headers().set(HttpHeaderNames.CONTENT_TYPE, - "multipart/form-data; boundary=--89421926422648 [; charset=UTF-8]"); - - try { - new HttpPostMultipartRequestDecoder(req); - fail("Was expecting an ErrorDataDecoderException"); - } catch (HttpPostRequestDecoder.ErrorDataDecoderException expected) { - // expected - } finally { - assertTrue(req.release()); - } - } - - @Test - public void testDecodeFullHttpRequestWithInvalidPayloadReleaseBuffer() { - String content = "\n--861fbeab-cd20-470c-9609-d40a0f704466\n" + - "Content-Disposition: form-data; name=\"image1\"; filename*=\"'some.jpeg\"\n" + - "Content-Type: image/jpeg\n" + - "Content-Length: 1\n" + - "x\n" + - "--861fbeab-cd20-470c-9609-d40a0f704466--\n"; - - FullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/upload", - Unpooled.copiedBuffer(content, CharsetUtil.US_ASCII)); - req.headers().set("content-type", "multipart/form-data; boundary=861fbeab-cd20-470c-9609-d40a0f704466"); - req.headers().set("content-length", content.length()); - - try { - new HttpPostMultipartRequestDecoder(req); - fail("Was expecting an ErrorDataDecoderException"); - } catch (HttpPostRequestDecoder.ErrorDataDecoderException expected) { - // expected - } finally { - assertTrue(req.release()); - } - } - - @Test - public void testDelimiterExceedLeftSpaceInCurrentBuffer() { - String delimiter = "--861fbeab-cd20-470c-9609-d40a0f704466"; - String suffix = '\n' + delimiter + "--\n"; - byte[] bsuffix = suffix.getBytes(CharsetUtil.UTF_8); - int partOfDelimiter = bsuffix.length / 2; - int bytesLastChunk = 355 - partOfDelimiter; // to try to have an out of bound since content is > delimiter - byte[] bsuffix1 = Arrays.copyOf(bsuffix, partOfDelimiter); - byte[] bsuffix2 = Arrays.copyOfRange(bsuffix, partOfDelimiter, bsuffix.length); - String prefix = delimiter + "\n" + - "Content-Disposition: form-data; name=\"image\"; filename=\"guangzhou.jpeg\"\n" + - "Content-Type: image/jpeg\n" + - "Content-Length: " + bytesLastChunk + "\n\n"; - HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/upload"); - request.headers().set("content-type", "multipart/form-data; boundary=861fbeab-cd20-470c-9609-d40a0f704466"); - request.headers().set("content-length", prefix.length() + bytesLastChunk + suffix.length()); - - // Factory using Memory mode - HttpDataFactory factory = new DefaultHttpDataFactory(false); - HttpPostMultipartRequestDecoder decoder = new HttpPostMultipartRequestDecoder(factory, request); - ByteBuf buf = Unpooled.wrappedBuffer(prefix.getBytes(CharsetUtil.UTF_8)); - decoder.offer(new DefaultHttpContent(buf)); - assertNotNull((HttpData) decoder.currentPartialHttpData()); - buf.release(); - // Chunk less than Delimiter size but containing part of delimiter - byte[] body = new byte[bytesLastChunk + bsuffix1.length]; - Arrays.fill(body, (byte) 2); - for (int i = 0; i < bsuffix1.length; i++) { - body[bytesLastChunk + i] = bsuffix1[i]; - } - ByteBuf content = Unpooled.wrappedBuffer(body); - decoder.offer(new DefaultHttpContent(content)); // Ouf of range before here - assertNotNull(((HttpData) decoder.currentPartialHttpData()).content()); - content.release(); - content = Unpooled.wrappedBuffer(bsuffix2); - decoder.offer(new DefaultHttpContent(content)); - assertNull((HttpData) decoder.currentPartialHttpData()); - content.release(); - decoder.offer(new DefaultLastHttpContent()); - FileUpload data = (FileUpload) decoder.getBodyHttpDatas().get(0); - assertEquals(data.length(), bytesLastChunk); - assertEquals(true, data.isInMemory()); - - InterfaceHttpData[] httpDatas = decoder.getBodyHttpDatas().toArray(new InterfaceHttpData[0]); - for (InterfaceHttpData httpData : httpDatas) { - assertEquals(1, httpData.refCnt(), "Before cleanAllHttpData should be 1"); - } - factory.cleanAllHttpData(); - for (InterfaceHttpData httpData : httpDatas) { - assertEquals(1, httpData.refCnt(), "After cleanAllHttpData should be 1 if in Memory"); - } - decoder.destroy(); - for (InterfaceHttpData httpData : httpDatas) { - assertEquals(0, httpData.refCnt(), "RefCnt should be 0"); - } - } - - private void commonTestBigFileDelimiterInMiddleChunk(HttpDataFactory factory, boolean inMemory) - throws IOException { - int nbChunks = 100; - int bytesPerChunk = 100000; - int bytesLastChunk = 10000; - int fileSize = bytesPerChunk * nbChunks + bytesLastChunk; // set Xmx to a number lower than this and it crashes - - String delimiter = "--861fbeab-cd20-470c-9609-d40a0f704466"; - String prefix = delimiter + "\n" + - "Content-Disposition: form-data; name=\"image\"; filename=\"guangzhou.jpeg\"\n" + - "Content-Type: image/jpeg\n" + - "Content-Length: " + fileSize + "\n" + - "\n"; - - String suffix1 = "\n" + - "--861fbeab-"; - String suffix2 = "cd20-470c-9609-d40a0f704466--\n"; - String suffix = suffix1 + suffix2; - - HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/upload"); - request.headers().set("content-type", "multipart/form-data; boundary=861fbeab-cd20-470c-9609-d40a0f704466"); - request.headers().set("content-length", prefix.length() + fileSize + suffix.length()); - - HttpPostMultipartRequestDecoder decoder = new HttpPostMultipartRequestDecoder(factory, request); - ByteBuf buf = Unpooled.wrappedBuffer(prefix.getBytes(CharsetUtil.UTF_8)); - decoder.offer(new DefaultHttpContent(buf)); - assertNotNull(((HttpData) decoder.currentPartialHttpData()).content()); - buf.release(); - - byte[] body = new byte[bytesPerChunk]; - Arrays.fill(body, (byte) 1); - // Set first bytes as CRLF to ensure it is correctly getting the last CRLF - body[0] = HttpConstants.CR; - body[1] = HttpConstants.LF; - for (int i = 0; i < nbChunks; i++) { - ByteBuf content = Unpooled.wrappedBuffer(body, 0, bytesPerChunk); - decoder.offer(new DefaultHttpContent(content)); // **OutOfMemory previously here** - assertNotNull(((HttpData) decoder.currentPartialHttpData()).content()); - content.release(); - } - - byte[] bsuffix1 = suffix1.getBytes(CharsetUtil.UTF_8); - byte[] previousLastbody = new byte[bytesLastChunk - bsuffix1.length]; - byte[] bdelimiter = delimiter.getBytes(CharsetUtil.UTF_8); - byte[] lastbody = new byte[2 * bsuffix1.length]; - Arrays.fill(previousLastbody, (byte) 1); - previousLastbody[0] = HttpConstants.CR; - previousLastbody[1] = HttpConstants.LF; - Arrays.fill(lastbody, (byte) 1); - // put somewhere a not valid delimiter - for (int i = 0; i < bdelimiter.length; i++) { - previousLastbody[i + 10] = bdelimiter[i]; - } - lastbody[0] = HttpConstants.CR; - lastbody[1] = HttpConstants.LF; - for (int i = 0; i < bsuffix1.length; i++) { - lastbody[bsuffix1.length + i] = bsuffix1[i]; - } - - ByteBuf content2 = Unpooled.wrappedBuffer(previousLastbody, 0, previousLastbody.length); - decoder.offer(new DefaultHttpContent(content2)); - assertNotNull(((HttpData) decoder.currentPartialHttpData()).content()); - content2.release(); - content2 = Unpooled.wrappedBuffer(lastbody, 0, lastbody.length); - decoder.offer(new DefaultHttpContent(content2)); - assertNotNull(((HttpData) decoder.currentPartialHttpData()).content()); - content2.release(); - content2 = Unpooled.wrappedBuffer(suffix2.getBytes(CharsetUtil.UTF_8)); - decoder.offer(new DefaultHttpContent(content2)); - assertNull(decoder.currentPartialHttpData()); - content2.release(); - decoder.offer(new DefaultLastHttpContent()); - - FileUpload data = (FileUpload) decoder.getBodyHttpDatas().get(0); - assertEquals(data.length(), fileSize); - assertEquals(inMemory, data.isInMemory()); - if (data.isInMemory()) { - // To be done only if not inMemory: assertEquals(data.get().length, fileSize); - assertFalse(data.getByteBuf().capacity() < 1024 * 1024, - "Capacity should be higher than 1M"); - } - assertTrue(decoder.getCurrentAllocatedCapacity() < 1024 * 1024, - "Capacity should be less than 1M"); - InterfaceHttpData[] httpDatas = decoder.getBodyHttpDatas().toArray(new InterfaceHttpData[0]); - for (InterfaceHttpData httpData : httpDatas) { - assertEquals(1, httpData.refCnt(), "Before cleanAllHttpData should be 1"); - } - factory.cleanAllHttpData(); - for (InterfaceHttpData httpData : httpDatas) { - assertEquals(inMemory? 1 : 0, httpData.refCnt(), "After cleanAllHttpData should be 1 if in Memory"); - } - decoder.destroy(); - for (InterfaceHttpData httpData : httpDatas) { - assertEquals(0, httpData.refCnt(), "RefCnt should be 0"); - } - } - - @Test - public void testBIgFileUploadDelimiterInMiddleChunkDecoderDiskFactory() throws IOException { - // Factory using Disk mode - HttpDataFactory factory = new DefaultHttpDataFactory(true); - - commonTestBigFileDelimiterInMiddleChunk(factory, false); - } - - @Test - public void testBIgFileUploadDelimiterInMiddleChunkDecoderMemoryFactory() throws IOException { - // Factory using Memory mode - HttpDataFactory factory = new DefaultHttpDataFactory(false); - - commonTestBigFileDelimiterInMiddleChunk(factory, true); - } - - @Test - public void testBIgFileUploadDelimiterInMiddleChunkDecoderMixedFactory() throws IOException { - // Factory using Mixed mode, where file shall be on Disk - HttpDataFactory factory = new DefaultHttpDataFactory(10000); - - commonTestBigFileDelimiterInMiddleChunk(factory, false); - } - - @Test - public void testNotBadReleaseBuffersDuringDecodingDiskFactory() throws IOException { - // Using Disk Factory - HttpDataFactory factory = new DefaultHttpDataFactory(true); - commonNotBadReleaseBuffersDuringDecoding(factory, false); - } - @Test - public void testNotBadReleaseBuffersDuringDecodingMemoryFactory() throws IOException { - // Using Memory Factory - HttpDataFactory factory = new DefaultHttpDataFactory(false); - commonNotBadReleaseBuffersDuringDecoding(factory, true); - } - @Test - public void testNotBadReleaseBuffersDuringDecodingMixedFactory() throws IOException { - // Using Mixed Factory - HttpDataFactory factory = new DefaultHttpDataFactory(100); - commonNotBadReleaseBuffersDuringDecoding(factory, false); - } - - private void commonNotBadReleaseBuffersDuringDecoding(HttpDataFactory factory, boolean inMemory) - throws IOException { - int nbItems = 20; - int bytesPerItem = 1000; - int maxMemory = 500; - - String prefix1 = "\n--861fbeab-cd20-470c-9609-d40a0f704466\n" + - "Content-Disposition: form-data; name=\"image"; - String prefix2 = - "\"; filename=\"guangzhou.jpeg\"\n" + - "Content-Type: image/jpeg\n" + - "Content-Length: " + bytesPerItem + "\n" + "\n"; - - String suffix = "\n--861fbeab-cd20-470c-9609-d40a0f704466--\n"; - - HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/upload"); - request.headers().set("content-type", "multipart/form-data; boundary=861fbeab-cd20-470c-9609-d40a0f704466"); - request.headers().set("content-length", nbItems * (prefix1.length() + prefix2.length() + 2 + bytesPerItem) - + suffix.length()); - HttpPostMultipartRequestDecoder decoder = new HttpPostMultipartRequestDecoder(factory, request); - decoder.setDiscardThreshold(maxMemory); - for (int rank = 0; rank < nbItems; rank++) { - byte[] bp1 = prefix1.getBytes(CharsetUtil.UTF_8); - byte[] bp2 = prefix2.getBytes(CharsetUtil.UTF_8); - byte[] prefix = new byte[bp1.length + 2 + bp2.length]; - for (int i = 0; i < bp1.length; i++) { - prefix[i] = bp1[i]; - } - byte[] brank = Integer.toString(10 + rank).getBytes(CharsetUtil.UTF_8); - prefix[bp1.length] = brank[0]; - prefix[bp1.length + 1] = brank[1]; - for (int i = 0; i < bp2.length; i++) { - prefix[bp1.length + 2 + i] = bp2[i]; - } - ByteBuf buf = Unpooled.wrappedBuffer(prefix); - decoder.offer(new DefaultHttpContent(buf)); - buf.release(); - byte[] body = new byte[bytesPerItem]; - Arrays.fill(body, (byte) rank); - ByteBuf content = Unpooled.wrappedBuffer(body, 0, bytesPerItem); - decoder.offer(new DefaultHttpContent(content)); - content.release(); - } - byte[] lastbody = suffix.getBytes(CharsetUtil.UTF_8); - ByteBuf content2 = Unpooled.wrappedBuffer(lastbody, 0, lastbody.length); - decoder.offer(new DefaultHttpContent(content2)); - content2.release(); - decoder.offer(new DefaultLastHttpContent()); - - for (int rank = 0; rank < nbItems; rank++) { - FileUpload data = (FileUpload) decoder.getBodyHttpData("image" + (10 + rank)); - assertEquals(data.length(), bytesPerItem); - assertEquals(inMemory, data.isInMemory()); - byte[] body = new byte[bytesPerItem]; - Arrays.fill(body, (byte) rank); - assertTrue(Arrays.equals(body, data.get())); - } - // To not be done since will load full file on memory: assertEquals(data.get().length, fileSize); - // Not mandatory since implicitely called during destroy of decoder - for (InterfaceHttpData httpData: decoder.getBodyHttpDatas()) { - httpData.release(); - factory.removeHttpDataFromClean(request, httpData); - } - factory.cleanAllHttpData(); - decoder.destroy(); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/multipart/HttpPostRequestDecoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/multipart/HttpPostRequestDecoderTest.java deleted file mode 100644 index 89c03d9800..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/multipart/HttpPostRequestDecoderTest.java +++ /dev/null @@ -1,992 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.Unpooled; -import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty.handler.codec.DecoderResult; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.DefaultHttpContent; -import io.netty.handler.codec.http.DefaultHttpRequest; -import io.netty.handler.codec.http.DefaultLastHttpContent; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http.LastHttpContent; -import io.netty.util.CharsetUtil; -import org.junit.jupiter.api.Test; - -import java.net.URLEncoder; -import java.nio.charset.UnsupportedCharsetException; -import java.util.Arrays; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - -/** - * {@link HttpPostRequestDecoder} test case. - */ -public class HttpPostRequestDecoderTest { - - @Test - public void testBinaryStreamUploadWithSpace() throws Exception { - testBinaryStreamUpload(true); - } - - // https://github.com/netty/netty/issues/1575 - @Test - public void testBinaryStreamUploadWithoutSpace() throws Exception { - testBinaryStreamUpload(false); - } - - private static void testBinaryStreamUpload(boolean withSpace) throws Exception { - final String boundary = "dLV9Wyq26L_-JQxk6ferf-RT153LhOO"; - final String contentTypeValue; - if (withSpace) { - contentTypeValue = "multipart/form-data; boundary=" + boundary; - } else { - contentTypeValue = "multipart/form-data;boundary=" + boundary; - } - final DefaultHttpRequest req = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, - "http://localhost"); - - req.setDecoderResult(DecoderResult.SUCCESS); - req.headers().add(HttpHeaderNames.CONTENT_TYPE, contentTypeValue); - req.headers().add(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED); - - // Force to use memory-based data. - final DefaultHttpDataFactory inMemoryFactory = new DefaultHttpDataFactory(false); - - for (String data : Arrays.asList("", "\r", "\r\r", "\r\r\r")) { - final String body = - "--" + boundary + "\r\n" + - "Content-Disposition: form-data; name=\"file\"; filename=\"tmp-0.txt\"\r\n" + - "Content-Type: image/gif\r\n" + - "\r\n" + - data + "\r\n" + - "--" + boundary + "--\r\n"; - - // Create decoder instance to test. - final HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(inMemoryFactory, req); - - ByteBuf buf = Unpooled.copiedBuffer(body, CharsetUtil.UTF_8); - decoder.offer(new DefaultHttpContent(buf)); - decoder.offer(new DefaultHttpContent(Unpooled.EMPTY_BUFFER)); - - // Validate it's enough chunks to decode upload. - assertTrue(decoder.hasNext()); - - // Decode binary upload. - MemoryFileUpload upload = (MemoryFileUpload) decoder.next(); - - // Validate data has been parsed correctly as it was passed into request. - assertEquals(data, upload.getString(CharsetUtil.UTF_8), - "Invalid decoded data [data=" + data.replaceAll("\r", "\\\\r") + ", upload=" + upload + ']'); - upload.release(); - decoder.destroy(); - buf.release(); - } - } - - // See https://github.com/netty/netty/issues/1089 - @Test - public void testFullHttpRequestUpload() throws Exception { - final String boundary = "dLV9Wyq26L_-JQxk6ferf-RT153LhOO"; - - final DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, - "http://localhost"); - - req.setDecoderResult(DecoderResult.SUCCESS); - req.headers().add(HttpHeaderNames.CONTENT_TYPE, "multipart/form-data; boundary=" + boundary); - req.headers().add(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED); - - // Force to use memory-based data. - final DefaultHttpDataFactory inMemoryFactory = new DefaultHttpDataFactory(false); - - for (String data : Arrays.asList("", "\r", "\r\r", "\r\r\r")) { - final String body = - "--" + boundary + "\r\n" + - "Content-Disposition: form-data; name=\"file\"; filename=\"tmp-0.txt\"\r\n" + - "Content-Type: image/gif\r\n" + - "\r\n" + - data + "\r\n" + - "--" + boundary + "--\r\n"; - - req.content().writeBytes(body.getBytes(CharsetUtil.UTF_8)); - } - // Create decoder instance to test. - final HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(inMemoryFactory, req); - assertFalse(decoder.getBodyHttpDatas().isEmpty()); - decoder.destroy(); - assertTrue(req.release()); - } - - // See https://github.com/netty/netty/issues/2544 - @Test - public void testMultipartCodecWithCRasEndOfAttribute() throws Exception { - final String boundary = "dLV9Wyq26L_-JQxk6ferf-RT153LhOO"; - - // Force to use memory-based data. - final DefaultHttpDataFactory inMemoryFactory = new DefaultHttpDataFactory(false); - // Build test case - String extradata = "aaaa"; - String[] datas = new String[5]; - for (int i = 0; i < 4; i++) { - datas[i] = extradata; - for (int j = 0; j < i; j++) { - datas[i] += '\r'; - } - } - - for (int i = 0; i < 4; i++) { - final DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, - "http://localhost"); - req.setDecoderResult(DecoderResult.SUCCESS); - req.headers().add(HttpHeaderNames.CONTENT_TYPE, "multipart/form-data; boundary=" + boundary); - req.headers().add(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED); - final String body = - "--" + boundary + "\r\n" + - "Content-Disposition: form-data; name=\"file" + i + "\"\r\n" + - "Content-Type: image/gif\r\n" + - "\r\n" + - datas[i] + "\r\n" + - "--" + boundary + "--\r\n"; - - req.content().writeBytes(body.getBytes(CharsetUtil.UTF_8)); - // Create decoder instance to test. - final HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(inMemoryFactory, req); - assertFalse(decoder.getBodyHttpDatas().isEmpty()); - // Check correctness: data size - InterfaceHttpData httpdata = decoder.getBodyHttpData("file" + i); - assertNotNull(httpdata); - Attribute attribute = (Attribute) httpdata; - byte[] datar = attribute.get(); - assertNotNull(datar); - assertEquals(datas[i].getBytes(CharsetUtil.UTF_8).length, datar.length); - - decoder.destroy(); - assertTrue(req.release()); - } - } - - // See https://github.com/netty/netty/issues/2542 - @Test - public void testQuotedBoundary() throws Exception { - final String boundary = "dLV9Wyq26L_-JQxk6ferf-RT153LhOO"; - - final DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, - "http://localhost"); - - req.setDecoderResult(DecoderResult.SUCCESS); - req.headers().add(HttpHeaderNames.CONTENT_TYPE, "multipart/form-data; boundary=\"" + boundary + '"'); - req.headers().add(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED); - - // Force to use memory-based data. - final DefaultHttpDataFactory inMemoryFactory = new DefaultHttpDataFactory(false); - - for (String data : Arrays.asList("", "\r", "\r\r", "\r\r\r")) { - final String body = - "--" + boundary + "\r\n" + - "Content-Disposition: form-data; name=\"file\"; filename=\"tmp-0.txt\"\r\n" + - "Content-Type: image/gif\r\n" + - "\r\n" + - data + "\r\n" + - "--" + boundary + "--\r\n"; - - req.content().writeBytes(body.getBytes(CharsetUtil.UTF_8)); - } - // Create decoder instance to test. - final HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(inMemoryFactory, req); - assertFalse(decoder.getBodyHttpDatas().isEmpty()); - decoder.destroy(); - assertTrue(req.release()); - } - - // See https://github.com/netty/netty/issues/1848 - @Test - public void testNoZeroOut() throws Exception { - final String boundary = "E832jQp_Rq2ErFmAduHSR8YlMSm0FCY"; - - final DefaultHttpDataFactory aMemFactory = new DefaultHttpDataFactory(false); - - DefaultHttpRequest aRequest = new DefaultHttpRequest(HttpVersion.HTTP_1_1, - HttpMethod.POST, - "http://localhost"); - aRequest.headers().set(HttpHeaderNames.CONTENT_TYPE, - "multipart/form-data; boundary=" + boundary); - aRequest.headers().set(HttpHeaderNames.TRANSFER_ENCODING, - HttpHeaderValues.CHUNKED); - - HttpPostRequestDecoder aDecoder = new HttpPostRequestDecoder(aMemFactory, aRequest); - - final String aData = "some data would be here. the data should be long enough that it " + - "will be longer than the original buffer length of 256 bytes in " + - "the HttpPostRequestDecoder in order to trigger the issue. Some more " + - "data just to be on the safe side."; - - final String body = - "--" + boundary + "\r\n" + - "Content-Disposition: form-data; name=\"root\"\r\n" + - "Content-Type: text/plain\r\n" + - "\r\n" + - aData + - "\r\n" + - "--" + boundary + "--\r\n"; - - byte[] aBytes = body.getBytes(); - - int split = 125; - - ByteBufAllocator aAlloc = new UnpooledByteBufAllocator(true); - ByteBuf aSmallBuf = aAlloc.heapBuffer(split, split); - ByteBuf aLargeBuf = aAlloc.heapBuffer(aBytes.length - split, aBytes.length - split); - - aSmallBuf.writeBytes(aBytes, 0, split); - aLargeBuf.writeBytes(aBytes, split, aBytes.length - split); - - aDecoder.offer(new DefaultHttpContent(aSmallBuf)); - aDecoder.offer(new DefaultHttpContent(aLargeBuf)); - - aDecoder.offer(LastHttpContent.EMPTY_LAST_CONTENT); - - assertTrue(aDecoder.hasNext(), "Should have a piece of data"); - - InterfaceHttpData aDecodedData = aDecoder.next(); - assertEquals(InterfaceHttpData.HttpDataType.Attribute, aDecodedData.getHttpDataType()); - - Attribute aAttr = (Attribute) aDecodedData; - assertEquals(aData, aAttr.getValue()); - - aDecodedData.release(); - aDecoder.destroy(); - aSmallBuf.release(); - aLargeBuf.release(); - } - - // See https://github.com/netty/netty/issues/2305 - @Test - public void testChunkCorrect() throws Exception { - String payload = "town=794649819&town=784444184&town=794649672&town=794657800&town=" + - "794655734&town=794649377&town=794652136&town=789936338&town=789948986&town=" + - "789949643&town=786358677&town=794655880&town=786398977&town=789901165&town=" + - "789913325&town=789903418&town=789903579&town=794645251&town=794694126&town=" + - "794694831&town=794655274&town=789913656&town=794653956&town=794665634&town=" + - "789936598&town=789904658&town=789899210&town=799696252&town=794657521&town=" + - "789904837&town=789961286&town=789958704&town=789948839&town=789933899&town=" + - "793060398&town=794659180&town=794659365&town=799724096&town=794696332&town=" + - "789953438&town=786398499&town=794693372&town=789935439&town=794658041&town=" + - "789917595&town=794655427&town=791930372&town=794652891&town=794656365&town=" + - "789960339&town=794645586&town=794657688&town=794697211&town=789937427&town=" + - "789902813&town=789941130&town=794696907&town=789904328&town=789955151&town=" + - "789911570&town=794655074&town=789939531&town=789935242&town=789903835&town=" + - "789953800&town=794649962&town=789939841&town=789934819&town=789959672&town=" + - "794659043&town=794657035&town=794658938&town=794651746&town=794653732&town=" + - "794653881&town=786397909&town=794695736&town=799724044&town=794695926&town=" + - "789912270&town=794649030&town=794657946&town=794655370&town=794659660&town=" + - "794694617&town=799149862&town=789953234&town=789900476&town=794654995&town=" + - "794671126&town=789908868&town=794652942&town=789955605&town=789901934&town=" + - "789950015&town=789937922&town=789962576&town=786360170&town=789954264&town=" + - "789911738&town=789955416&town=799724187&town=789911879&town=794657462&town=" + - "789912561&town=789913167&town=794655195&town=789938266&town=789952099&town=" + - "794657160&town=789949414&town=794691293&town=794698153&town=789935636&town=" + - "789956374&town=789934635&town=789935475&town=789935085&town=794651425&town=" + - "794654936&town=794655680&town=789908669&town=794652031&town=789951298&town=" + - "789938382&town=794651503&town=794653330&town=817675037&town=789951623&town=" + - "789958999&town=789961555&town=794694050&town=794650241&town=794656286&town=" + - "794692081&town=794660090&town=794665227&town=794665136&town=794669931"; - DefaultHttpRequest defaultHttpRequest = - new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/"); - - HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(defaultHttpRequest); - - int firstChunk = 10; - int middleChunk = 1024; - - byte[] payload1 = payload.substring(0, firstChunk).getBytes(); - byte[] payload2 = payload.substring(firstChunk, firstChunk + middleChunk).getBytes(); - byte[] payload3 = payload.substring(firstChunk + middleChunk, firstChunk + middleChunk * 2).getBytes(); - byte[] payload4 = payload.substring(firstChunk + middleChunk * 2).getBytes(); - - ByteBuf buf1 = Unpooled.directBuffer(payload1.length); - ByteBuf buf2 = Unpooled.directBuffer(payload2.length); - ByteBuf buf3 = Unpooled.directBuffer(payload3.length); - ByteBuf buf4 = Unpooled.directBuffer(payload4.length); - - buf1.writeBytes(payload1); - buf2.writeBytes(payload2); - buf3.writeBytes(payload3); - buf4.writeBytes(payload4); - - decoder.offer(new DefaultHttpContent(buf1)); - decoder.offer(new DefaultHttpContent(buf2)); - decoder.offer(new DefaultHttpContent(buf3)); - decoder.offer(new DefaultLastHttpContent(buf4)); - - assertFalse(decoder.getBodyHttpDatas().isEmpty()); - assertEquals(139, decoder.getBodyHttpDatas().size()); - - Attribute attr = (Attribute) decoder.getBodyHttpData("town"); - assertEquals("794649819", attr.getValue()); - - decoder.destroy(); - buf1.release(); - buf2.release(); - buf3.release(); - buf4.release(); - } - - // See https://github.com/netty/netty/issues/3326 - @Test - public void testFilenameContainingSemicolon() throws Exception { - final String boundary = "dLV9Wyq26L_-JQxk6ferf-RT153LhOO"; - final DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, - "http://localhost"); - req.headers().add(HttpHeaderNames.CONTENT_TYPE, "multipart/form-data; boundary=" + boundary); - // Force to use memory-based data. - final DefaultHttpDataFactory inMemoryFactory = new DefaultHttpDataFactory(false); - final String data = "asdf"; - final String filename = "tmp;0.txt"; - final String body = - "--" + boundary + "\r\n" + - "Content-Disposition: form-data; name=\"file\"; filename=\"" + filename + "\"\r\n" + - "Content-Type: image/gif\r\n" + - "\r\n" + - data + "\r\n" + - "--" + boundary + "--\r\n"; - - req.content().writeBytes(body.getBytes(CharsetUtil.UTF_8.name())); - // Create decoder instance to test. - final HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(inMemoryFactory, req); - assertFalse(decoder.getBodyHttpDatas().isEmpty()); - decoder.destroy(); - assertTrue(req.release()); - } - - @Test - public void testFilenameContainingSemicolon2() throws Exception { - final String boundary = "dLV9Wyq26L_-JQxk6ferf-RT153LhOO"; - final DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, - "http://localhost"); - req.headers().add(HttpHeaderNames.CONTENT_TYPE, "multipart/form-data; boundary=" + boundary); - // Force to use memory-based data. - final DefaultHttpDataFactory inMemoryFactory = new DefaultHttpDataFactory(false); - final String data = "asdf"; - final String filename = "tmp;0.txt"; - final String body = - "--" + boundary + "\r\n" + - "Content-Disposition: form-data; name=\"file\"; filename=\"" + filename + "\"\r\n" + - "Content-Type: image/gif\r\n" + - "\r\n" + - data + "\r\n" + - "--" + boundary + "--\r\n"; - - req.content().writeBytes(body.getBytes(CharsetUtil.UTF_8.name())); - // Create decoder instance to test. - final HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(inMemoryFactory, req); - assertFalse(decoder.getBodyHttpDatas().isEmpty()); - InterfaceHttpData part1 = decoder.getBodyHttpDatas().get(0); - assertTrue(part1 instanceof FileUpload); - FileUpload fileUpload = (FileUpload) part1; - assertEquals("tmp 0.txt", fileUpload.getFilename()); - decoder.destroy(); - assertTrue(req.release()); - } - - @Test - public void testMultipartRequestWithoutContentTypeBody() { - final String boundary = "dLV9Wyq26L_-JQxk6ferf-RT153LhOO"; - - final DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, - "http://localhost"); - - req.setDecoderResult(DecoderResult.SUCCESS); - req.headers().add(HttpHeaderNames.CONTENT_TYPE, "multipart/form-data; boundary=" + boundary); - req.headers().add(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED); - - // Force to use memory-based data. - final DefaultHttpDataFactory inMemoryFactory = new DefaultHttpDataFactory(false); - - for (String data : Arrays.asList("", "\r", "\r\r", "\r\r\r")) { - final String body = - "--" + boundary + "\r\n" + - "Content-Disposition: form-data; name=\"file\"; filename=\"tmp-0.txt\"\r\n" + - "\r\n" + - data + "\r\n" + - "--" + boundary + "--\r\n"; - - req.content().writeBytes(body.getBytes(CharsetUtil.UTF_8)); - } - // Create decoder instance to test without any exception. - final HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(inMemoryFactory, req); - assertFalse(decoder.getBodyHttpDatas().isEmpty()); - decoder.destroy(); - assertTrue(req.release()); - } - - @Test - public void testDecodeOtherMimeHeaderFields() throws Exception { - final String boundary = "74e78d11b0214bdcbc2f86491eeb4902"; - String filecontent = "123456"; - - final String body = "--" + boundary + "\r\n" + - "Content-Disposition: form-data; name=\"file\"; filename=" + "\"" + "attached.txt" + "\"" + - "\r\n" + - "Content-Type: application/octet-stream" + "\r\n" + - "Content-Encoding: gzip" + "\r\n" + - "\r\n" + - filecontent + - "\r\n" + - "--" + boundary + "--"; - - final DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, - HttpMethod.POST, - "http://localhost", - Unpooled.wrappedBuffer(body.getBytes())); - req.headers().add(HttpHeaderNames.CONTENT_TYPE, "multipart/form-data; boundary=" + boundary); - req.headers().add(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED); - final DefaultHttpDataFactory inMemoryFactory = new DefaultHttpDataFactory(false); - final HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(inMemoryFactory, req); - assertFalse(decoder.getBodyHttpDatas().isEmpty()); - InterfaceHttpData part1 = decoder.getBodyHttpDatas().get(0); - assertTrue(part1 instanceof FileUpload, "the item should be a FileUpload"); - FileUpload fileUpload = (FileUpload) part1; - byte[] fileBytes = fileUpload.get(); - assertTrue(filecontent.equals(new String(fileBytes)), "the filecontent should not be decoded"); - decoder.destroy(); - assertTrue(req.release()); - } - - @Test - public void testMultipartRequestWithFileInvalidCharset() throws Exception { - final String boundary = "dLV9Wyq26L_-JQxk6ferf-RT153LhOO"; - final DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, - "http://localhost"); - req.headers().add(HttpHeaderNames.CONTENT_TYPE, "multipart/form-data; boundary=" + boundary); - // Force to use memory-based data. - final DefaultHttpDataFactory inMemoryFactory = new DefaultHttpDataFactory(false); - final String data = "asdf"; - final String filename = "tmp;0.txt"; - final String body = - "--" + boundary + "\r\n" + - "Content-Disposition: form-data; name=\"file\"; filename=\"" + filename + "\"\r\n" + - "Content-Type: image/gif; charset=ABCD\r\n" + - "\r\n" + - data + "\r\n" + - "--" + boundary + "--\r\n"; - - req.content().writeBytes(body.getBytes(CharsetUtil.UTF_8)); - // Create decoder instance to test. - try { - new HttpPostRequestDecoder(inMemoryFactory, req); - fail("Was expecting an ErrorDataDecoderException"); - } catch (HttpPostRequestDecoder.ErrorDataDecoderException e) { - assertTrue(e.getCause() instanceof UnsupportedCharsetException); - } finally { - assertTrue(req.release()); - } - } - - @Test - public void testMultipartRequestWithFieldInvalidCharset() throws Exception { - final String boundary = "dLV9Wyq26L_-JQxk6ferf-RT153LhOO"; - final DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, - "http://localhost"); - req.headers().add(HttpHeaderNames.CONTENT_TYPE, "multipart/form-data; boundary=" + boundary); - // Force to use memory-based data. - final DefaultHttpDataFactory inMemoryFactory = new DefaultHttpDataFactory(false); - final String aData = "some data would be here. the data should be long enough that it " + - "will be longer than the original buffer length of 256 bytes in " + - "the HttpPostRequestDecoder in order to trigger the issue. Some more " + - "data just to be on the safe side."; - final String body = - "--" + boundary + "\r\n" + - "Content-Disposition: form-data; name=\"root\"\r\n" + - "Content-Type: text/plain; charset=ABCD\r\n" + - "\r\n" + - aData + - "\r\n" + - "--" + boundary + "--\r\n"; - - req.content().writeBytes(body.getBytes(CharsetUtil.UTF_8)); - // Create decoder instance to test. - try { - new HttpPostRequestDecoder(inMemoryFactory, req); - fail("Was expecting an ErrorDataDecoderException"); - } catch (HttpPostRequestDecoder.ErrorDataDecoderException e) { - assertTrue(e.getCause() instanceof UnsupportedCharsetException); - } finally { - assertTrue(req.release()); - } - } - - @Test - public void testFormEncodeIncorrect() throws Exception { - LastHttpContent content = new DefaultLastHttpContent( - Unpooled.copiedBuffer("project=netty&&project=netty", CharsetUtil.US_ASCII)); - DefaultHttpRequest req = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/"); - HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(req); - try { - decoder.offer(content); - fail(); - } catch (HttpPostRequestDecoder.ErrorDataDecoderException e) { - assertTrue(e.getCause() instanceof IllegalArgumentException); - } finally { - content.release(); - decoder.destroy(); - } - } - - // https://github.com/netty/netty/pull/7265 - @Test - public void testDecodeContentDispositionFieldParameters() throws Exception { - - final String boundary = "74e78d11b0214bdcbc2f86491eeb4902"; - - String encoding = "utf-8"; - String filename = "attached_Ņ„Đ°ĐšĐģ.txt"; - String filenameEncoded = URLEncoder.encode(filename, encoding); - - final String body = "--" + boundary + "\r\n" + - "Content-Disposition: form-data; name=\"file\"; filename*=" + encoding + "''" + filenameEncoded + - "\r\n\r\n" + - "foo\r\n" + - "\r\n" + - "--" + boundary + "--"; - - final DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, - HttpMethod.POST, - "http://localhost", - Unpooled.wrappedBuffer(body.getBytes())); - - req.headers().add(HttpHeaderNames.CONTENT_TYPE, "multipart/form-data; boundary=" + boundary); - final DefaultHttpDataFactory inMemoryFactory = new DefaultHttpDataFactory(false); - final HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(inMemoryFactory, req); - assertFalse(decoder.getBodyHttpDatas().isEmpty()); - InterfaceHttpData part1 = decoder.getBodyHttpDatas().get(0); - assertTrue(part1 instanceof FileUpload, "the item should be a FileUpload"); - FileUpload fileUpload = (FileUpload) part1; - assertEquals(filename, fileUpload.getFilename(), "the filename should be decoded"); - decoder.destroy(); - assertTrue(req.release()); - } - - // https://github.com/netty/netty/pull/7265 - @Test - public void testDecodeWithLanguageContentDispositionFieldParameters() throws Exception { - - final String boundary = "74e78d11b0214bdcbc2f86491eeb4902"; - - String encoding = "utf-8"; - String filename = "attached_Ņ„Đ°ĐšĐģ.txt"; - String language = "anything"; - String filenameEncoded = URLEncoder.encode(filename, encoding); - - final String body = "--" + boundary + "\r\n" + - "Content-Disposition: form-data; name=\"file\"; filename*=" + - encoding + "'" + language + "'" + filenameEncoded + "\r\n" + - "\r\n" + - "foo\r\n" + - "\r\n" + - "--" + boundary + "--"; - - final DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, - HttpMethod.POST, - "http://localhost", - Unpooled.wrappedBuffer(body.getBytes())); - - req.headers().add(HttpHeaderNames.CONTENT_TYPE, "multipart/form-data; boundary=" + boundary); - final DefaultHttpDataFactory inMemoryFactory = new DefaultHttpDataFactory(false); - final HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(inMemoryFactory, req); - assertFalse(decoder.getBodyHttpDatas().isEmpty()); - InterfaceHttpData part1 = decoder.getBodyHttpDatas().get(0); - assertTrue(part1 instanceof FileUpload, "the item should be a FileUpload"); - FileUpload fileUpload = (FileUpload) part1; - assertEquals(filename, fileUpload.getFilename(), "the filename should be decoded"); - decoder.destroy(); - assertTrue(req.release()); - } - - // https://github.com/netty/netty/pull/7265 - @Test - public void testDecodeMalformedNotEncodedContentDispositionFieldParameters() throws Exception { - - final String boundary = "74e78d11b0214bdcbc2f86491eeb4902"; - - final String body = "--" + boundary + "\r\n" + - "Content-Disposition: form-data; name=\"file\"; filename*=not-encoded\r\n" + - "\r\n" + - "foo\r\n" + - "\r\n" + - "--" + boundary + "--"; - - final DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, - HttpMethod.POST, - "http://localhost", - Unpooled.wrappedBuffer(body.getBytes())); - - req.headers().add(HttpHeaderNames.CONTENT_TYPE, "multipart/form-data; boundary=" + boundary); - - final DefaultHttpDataFactory inMemoryFactory = new DefaultHttpDataFactory(false); - - try { - new HttpPostRequestDecoder(inMemoryFactory, req); - fail("Was expecting an ErrorDataDecoderException"); - } catch (HttpPostRequestDecoder.ErrorDataDecoderException e) { - assertTrue(e.getCause() instanceof ArrayIndexOutOfBoundsException); - } finally { - assertTrue(req.release()); - } - } - - // https://github.com/netty/netty/pull/7265 - @Test - public void testDecodeMalformedBadCharsetContentDispositionFieldParameters() throws Exception { - - final String boundary = "74e78d11b0214bdcbc2f86491eeb4902"; - - final String body = "--" + boundary + "\r\n" + - "Content-Disposition: form-data; name=\"file\"; filename*=not-a-charset''filename\r\n" + - "\r\n" + - "foo\r\n" + - "\r\n" + - "--" + boundary + "--"; - - final DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, - HttpMethod.POST, - "http://localhost", - Unpooled.wrappedBuffer(body.getBytes())); - - req.headers().add(HttpHeaderNames.CONTENT_TYPE, "multipart/form-data; boundary=" + boundary); - - final DefaultHttpDataFactory inMemoryFactory = new DefaultHttpDataFactory(false); - - try { - new HttpPostRequestDecoder(inMemoryFactory, req); - fail("Was expecting an ErrorDataDecoderException"); - } catch (HttpPostRequestDecoder.ErrorDataDecoderException e) { - assertTrue(e.getCause() instanceof UnsupportedCharsetException); - } finally { - assertTrue(req.release()); - } - } - - // https://github.com/netty/netty/issues/7620 - @Test - public void testDecodeMalformedEmptyContentTypeFieldParameters() throws Exception { - final String boundary = "dLV9Wyq26L_-JQxk6ferf-RT153LhOO"; - final DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, - "http://localhost"); - req.headers().add(HttpHeaderNames.CONTENT_TYPE, "multipart/form-data; boundary=" + boundary); - // Force to use memory-based data. - final DefaultHttpDataFactory inMemoryFactory = new DefaultHttpDataFactory(false); - final String data = "asdf"; - final String filename = "tmp-0.txt"; - final String body = - "--" + boundary + "\r\n" + - "Content-Disposition: form-data; name=\"file\"; filename=\"" + filename + "\"\r\n" + - "Content-Type: \r\n" + - "\r\n" + - data + "\r\n" + - "--" + boundary + "--\r\n"; - - req.content().writeBytes(body.getBytes(CharsetUtil.UTF_8.name())); - // Create decoder instance to test. - final HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(inMemoryFactory, req); - assertFalse(decoder.getBodyHttpDatas().isEmpty()); - InterfaceHttpData part1 = decoder.getBodyHttpDatas().get(0); - assertTrue(part1 instanceof FileUpload); - FileUpload fileUpload = (FileUpload) part1; - assertEquals("tmp-0.txt", fileUpload.getFilename()); - decoder.destroy(); - assertTrue(req.release()); - } - - // https://github.com/netty/netty/issues/8575 - @Test - public void testMultipartRequest() throws Exception { - String BOUNDARY = "01f136d9282f"; - - byte[] bodyBytes = ("--" + BOUNDARY + "\n" + - "Content-Disposition: form-data; name=\"msg_id\"\n" + - "\n" + - "15200\n" + - "--" + BOUNDARY + "\n" + - "Content-Disposition: form-data; name=\"msg\"\n" + - "\n" + - "test message\n" + - "--" + BOUNDARY + "--").getBytes(); - ByteBuf byteBuf = Unpooled.directBuffer(bodyBytes.length); - byteBuf.writeBytes(bodyBytes); - - FullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_0, HttpMethod.POST, "/up", byteBuf); - req.headers().add(HttpHeaderNames.CONTENT_TYPE, "multipart/form-data; boundary=" + BOUNDARY); - - HttpPostRequestDecoder decoder = - new HttpPostRequestDecoder(new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE), - req, - CharsetUtil.UTF_8); - - assertTrue(decoder.isMultipart()); - assertFalse(decoder.getBodyHttpDatas().isEmpty()); - assertEquals(2, decoder.getBodyHttpDatas().size()); - - Attribute attrMsg = (Attribute) decoder.getBodyHttpData("msg"); - assertTrue(attrMsg.getByteBuf().isDirect()); - assertEquals("test message", attrMsg.getValue()); - Attribute attrMsgId = (Attribute) decoder.getBodyHttpData("msg_id"); - assertTrue(attrMsgId.getByteBuf().isDirect()); - assertEquals("15200", attrMsgId.getValue()); - - decoder.destroy(); - assertTrue(req.release()); - } - - @Test - public void testNotLeak() { - FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/", - Unpooled.copiedBuffer("a=1&&b=2", CharsetUtil.US_ASCII)); - try { - assertThrows(HttpPostRequestDecoder.ErrorDataDecoderException.class, - () -> new HttpPostStandardRequestDecoder(request).destroy()); - } finally { - assertTrue(request.release()); - } - } - - @Test - public void testNotLeakDirectBufferWhenWrapIllegalArgumentException() { - assertThrows(HttpPostRequestDecoder.ErrorDataDecoderException.class, - () -> testNotLeakWhenWrapIllegalArgumentException(Unpooled.directBuffer())); - } - - @Test - public void testNotLeakHeapBufferWhenWrapIllegalArgumentException() { - assertThrows(HttpPostRequestDecoder.ErrorDataDecoderException.class, - () -> testNotLeakWhenWrapIllegalArgumentException(Unpooled.buffer())); - } - - private static void testNotLeakWhenWrapIllegalArgumentException(ByteBuf buf) { - buf.writeCharSequence("a=b&foo=%22bar%22&==", CharsetUtil.US_ASCII); - FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/", buf); - try { - new HttpPostStandardRequestDecoder(request).destroy(); - } finally { - assertTrue(request.release()); - } - } - - @Test - public void testMultipartFormDataContentType() { - HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/"); - assertFalse(HttpPostRequestDecoder.isMultipart(request)); - - String multipartDataValue = HttpHeaderValues.MULTIPART_FORM_DATA + ";" + "boundary=gc0p4Jq0M2Yt08jU534c0p"; - request.headers().set(HttpHeaderNames.CONTENT_TYPE, ";" + multipartDataValue); - assertFalse(HttpPostRequestDecoder.isMultipart(request)); - - request.headers().set(HttpHeaderNames.CONTENT_TYPE, multipartDataValue); - assertTrue(HttpPostRequestDecoder.isMultipart(request)); - } - - // see https://github.com/netty/netty/issues/10087 - @Test - public void testDecodeWithLanguageContentDispositionFieldParametersForFix() throws Exception { - - final String boundary = "952178786863262625034234"; - - String encoding = "UTF-8"; - String filename = "æĩ‹č¯•test.txt"; - String filenameEncoded = URLEncoder.encode(filename, encoding); - - final String body = "--" + boundary + "\r\n" + - "Content-Disposition: form-data; name=\"file\"; filename*=\"" + - encoding + "''" + filenameEncoded + "\"\r\n" + - "\r\n" + - "foo\r\n" + - "\r\n" + - "--" + boundary + "--"; - - final DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, - HttpMethod.POST, - "http://localhost", - Unpooled.wrappedBuffer(body.getBytes())); - - req.headers().add(HttpHeaderNames.CONTENT_TYPE, "multipart/form-data; boundary=" + boundary); - final DefaultHttpDataFactory inMemoryFactory = new DefaultHttpDataFactory(false); - final HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(inMemoryFactory, req); - assertFalse(decoder.getBodyHttpDatas().isEmpty()); - InterfaceHttpData part1 = decoder.getBodyHttpDatas().get(0); - assertTrue(part1 instanceof FileUpload, "the item should be a FileUpload"); - FileUpload fileUpload = (FileUpload) part1; - assertEquals(filename, fileUpload.getFilename(), "the filename should be decoded"); - - decoder.destroy(); - assertTrue(req.release()); - } - - @Test - public void testDecodeFullHttpRequestWithUrlEncodedBody() throws Exception { - byte[] bodyBytes = "foo=bar&a=b&empty=&city=%3c%22new%22%20york%20city%3e&other_city=los+angeles".getBytes(); - ByteBuf content = Unpooled.directBuffer(bodyBytes.length); - content.writeBytes(bodyBytes); - - FullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/", content); - HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(req); - assertFalse(decoder.getBodyHttpDatas().isEmpty()); - - assertFalse(decoder.getBodyHttpDatas().isEmpty()); - assertEquals(5, decoder.getBodyHttpDatas().size()); - - Attribute attr = (Attribute) decoder.getBodyHttpData("foo"); - assertTrue(attr.getByteBuf().isDirect()); - assertEquals("bar", attr.getValue()); - - attr = (Attribute) decoder.getBodyHttpData("a"); - assertTrue(attr.getByteBuf().isDirect()); - assertEquals("b", attr.getValue()); - - attr = (Attribute) decoder.getBodyHttpData("empty"); - assertTrue(attr.getByteBuf().isDirect()); - assertEquals("", attr.getValue()); - - attr = (Attribute) decoder.getBodyHttpData("city"); - assertTrue(attr.getByteBuf().isDirect()); - assertEquals("<\"new\" york city>", attr.getValue()); - - attr = (Attribute) decoder.getBodyHttpData("other_city"); - assertTrue(attr.getByteBuf().isDirect()); - assertEquals("los angeles", attr.getValue()); - - decoder.destroy(); - assertTrue(req.release()); - } - - @Test - public void testDecodeFullHttpRequestWithUrlEncodedBodyWithBrokenHexByte0() { - byte[] bodyBytes = "foo=bar&a=b&empty=%&city=paris".getBytes(); - ByteBuf content = Unpooled.directBuffer(bodyBytes.length); - content.writeBytes(bodyBytes); - - FullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/", content); - try { - new HttpPostRequestDecoder(req); - fail("Was expecting an ErrorDataDecoderException"); - } catch (HttpPostRequestDecoder.ErrorDataDecoderException e) { - assertEquals("Invalid hex byte at index '0' in string: '%'", e.getMessage()); - } finally { - assertTrue(req.release()); - } - } - - @Test - public void testDecodeFullHttpRequestWithUrlEncodedBodyWithBrokenHexByte1() { - byte[] bodyBytes = "foo=bar&a=b&empty=%2&city=london".getBytes(); - ByteBuf content = Unpooled.directBuffer(bodyBytes.length); - content.writeBytes(bodyBytes); - - FullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/", content); - try { - new HttpPostRequestDecoder(req); - fail("Was expecting an ErrorDataDecoderException"); - } catch (HttpPostRequestDecoder.ErrorDataDecoderException e) { - assertEquals("Invalid hex byte at index '0' in string: '%2'", e.getMessage()); - } finally { - assertTrue(req.release()); - } - } - - @Test - public void testDecodeFullHttpRequestWithUrlEncodedBodyWithInvalidHexNibbleHi() { - byte[] bodyBytes = "foo=bar&a=b&empty=%Zc&city=london".getBytes(); - ByteBuf content = Unpooled.directBuffer(bodyBytes.length); - content.writeBytes(bodyBytes); - - FullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/", content); - try { - new HttpPostRequestDecoder(req); - fail("Was expecting an ErrorDataDecoderException"); - } catch (HttpPostRequestDecoder.ErrorDataDecoderException e) { - assertEquals("Invalid hex byte at index '0' in string: '%Zc'", e.getMessage()); - } finally { - assertTrue(req.release()); - } - } - - @Test - public void testDecodeFullHttpRequestWithUrlEncodedBodyWithInvalidHexNibbleLo() { - byte[] bodyBytes = "foo=bar&a=b&empty=%2g&city=london".getBytes(); - ByteBuf content = Unpooled.directBuffer(bodyBytes.length); - content.writeBytes(bodyBytes); - - FullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/", content); - try { - new HttpPostRequestDecoder(req); - fail("Was expecting an ErrorDataDecoderException"); - } catch (HttpPostRequestDecoder.ErrorDataDecoderException e) { - assertEquals("Invalid hex byte at index '0' in string: '%2g'", e.getMessage()); - } finally { - assertTrue(req.release()); - } - } - - @Test - public void testDecodeMultipartRequest() { - byte[] bodyBytes = ("--be38b42a9ad2713f\n" + - "content-disposition: form-data; name=\"title\"\n" + - "content-length: 10\n" + - "content-type: text/plain; charset=UTF-8\n" + - "\n" + - "bar-stream\n" + - "--be38b42a9ad2713f\n" + - "content-disposition: form-data; name=\"data\"; filename=\"data.json\"\n" + - "content-length: 16\n" + - "content-type: application/json; charset=UTF-8\n" + - "\n" + - "{\"title\":\"Test\"}\n" + - "--be38b42a9ad2713f--").getBytes(); - ByteBuf content = Unpooled.directBuffer(bodyBytes.length); - content.writeBytes(bodyBytes); - FullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/", content); - req.headers().add("Content-Type", "multipart/form-data;boundary=be38b42a9ad2713f"); - - try { - HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(new DefaultHttpDataFactory(false), req); - assertEquals(2, decoder.getBodyHttpDatas().size()); - InterfaceHttpData data = decoder.getBodyHttpData("title"); - assertTrue(data instanceof MemoryAttribute); - assertEquals("bar-stream", ((MemoryAttribute) data).getString()); - assertTrue(data.release()); - data = decoder.getBodyHttpData("data"); - assertTrue(data instanceof MemoryFileUpload); - assertEquals("{\"title\":\"Test\"}", ((MemoryFileUpload) data).getString()); - assertTrue(data.release()); - decoder.destroy(); - } catch (HttpPostRequestDecoder.ErrorDataDecoderException e) { - fail("Was not expecting an exception"); - } finally { - assertTrue(req.release()); - } - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/multipart/HttpPostRequestEncoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/multipart/HttpPostRequestEncoderTest.java deleted file mode 100755 index d1810cc08c..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/multipart/HttpPostRequestEncoderTest.java +++ /dev/null @@ -1,471 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.Unpooled; -import io.netty.handler.codec.http.DefaultHttpRequest; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.HttpConstants; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http.LastHttpContent; -import io.netty.handler.codec.http.multipart.HttpPostRequestEncoder.EncoderMode; -import io.netty.handler.codec.http.multipart.HttpPostRequestEncoder.ErrorDataEncoderException; -import io.netty.util.CharsetUtil; -import io.netty.util.internal.StringUtil; -import org.junit.jupiter.api.Test; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.util.Arrays; -import java.util.List; - -import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_DISPOSITION; -import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH; -import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TRANSFER_ENCODING; -import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - -/** {@link HttpPostRequestEncoder} test case. */ -public class HttpPostRequestEncoderTest { - - @Test - public void testAllowedMethods() throws Exception { - shouldThrowExceptionIfNotAllowed(HttpMethod.CONNECT); - shouldThrowExceptionIfNotAllowed(HttpMethod.PUT); - shouldThrowExceptionIfNotAllowed(HttpMethod.POST); - shouldThrowExceptionIfNotAllowed(HttpMethod.PATCH); - shouldThrowExceptionIfNotAllowed(HttpMethod.DELETE); - shouldThrowExceptionIfNotAllowed(HttpMethod.GET); - shouldThrowExceptionIfNotAllowed(HttpMethod.HEAD); - shouldThrowExceptionIfNotAllowed(HttpMethod.OPTIONS); - try { - shouldThrowExceptionIfNotAllowed(HttpMethod.TRACE); - fail("Should raised an exception with TRACE method"); - } catch (ErrorDataEncoderException e) { - // Exception is willing - } - } - - private void shouldThrowExceptionIfNotAllowed(HttpMethod method) throws Exception { - DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, - method, "http://localhost"); - - HttpPostRequestEncoder encoder = new HttpPostRequestEncoder(request, true); - File file1 = new File(getClass().getResource("/file-01.txt").toURI()); - encoder.addBodyAttribute("foo", "bar"); - encoder.addBodyFileUpload("quux", file1, "text/plain", false); - - String multipartDataBoundary = encoder.multipartDataBoundary; - String content = getRequestBody(encoder); - - String expected = "--" + multipartDataBoundary + "\r\n" + - CONTENT_DISPOSITION + ": form-data; name=\"foo\"" + "\r\n" + - CONTENT_LENGTH + ": 3" + "\r\n" + - CONTENT_TYPE + ": text/plain; charset=UTF-8" + "\r\n" + - "\r\n" + - "bar" + - "\r\n" + - "--" + multipartDataBoundary + "\r\n" + - CONTENT_DISPOSITION + ": form-data; name=\"quux\"; filename=\"file-01.txt\"" + "\r\n" + - CONTENT_LENGTH + ": " + file1.length() + "\r\n" + - CONTENT_TYPE + ": text/plain" + "\r\n" + - CONTENT_TRANSFER_ENCODING + ": binary" + "\r\n" + - "\r\n" + - "File 01" + StringUtil.NEWLINE + - "\r\n" + - "--" + multipartDataBoundary + "--" + "\r\n"; - - assertEquals(expected, content); - } - - @Test - public void testSingleFileUploadNoName() throws Exception { - DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, - HttpMethod.POST, "http://localhost"); - - HttpPostRequestEncoder encoder = new HttpPostRequestEncoder(request, true); - File file1 = new File(getClass().getResource("/file-01.txt").toURI()); - encoder.addBodyAttribute("foo", "bar"); - encoder.addBodyFileUpload("quux", "", file1, "text/plain", false); - - String multipartDataBoundary = encoder.multipartDataBoundary; - String content = getRequestBody(encoder); - - String expected = "--" + multipartDataBoundary + "\r\n" + - CONTENT_DISPOSITION + ": form-data; name=\"foo\"" + "\r\n" + - CONTENT_LENGTH + ": 3" + "\r\n" + - CONTENT_TYPE + ": text/plain; charset=UTF-8" + "\r\n" + - "\r\n" + - "bar" + - "\r\n" + - "--" + multipartDataBoundary + "\r\n" + - CONTENT_DISPOSITION + ": form-data; name=\"quux\"\r\n" + - CONTENT_LENGTH + ": " + file1.length() + "\r\n" + - CONTENT_TYPE + ": text/plain" + "\r\n" + - CONTENT_TRANSFER_ENCODING + ": binary" + "\r\n" + - "\r\n" + - "File 01" + StringUtil.NEWLINE + - "\r\n" + - "--" + multipartDataBoundary + "--" + "\r\n"; - - assertEquals(expected, content); - } - - @Test - public void testMultiFileUploadInMixedMode() throws Exception { - DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, - HttpMethod.POST, "http://localhost"); - - HttpPostRequestEncoder encoder = new HttpPostRequestEncoder(request, true); - File file1 = new File(getClass().getResource("/file-01.txt").toURI()); - File file2 = new File(getClass().getResource("/file-02.txt").toURI()); - File file3 = new File(getClass().getResource("/file-03.txt").toURI()); - encoder.addBodyAttribute("foo", "bar"); - encoder.addBodyFileUpload("quux", file1, "text/plain", false); - encoder.addBodyFileUpload("quux", file2, "text/plain", false); - encoder.addBodyFileUpload("quux", file3, "text/plain", false); - - // We have to query the value of these two fields before finalizing - // the request, which unsets one of them. - String multipartDataBoundary = encoder.multipartDataBoundary; - String multipartMixedBoundary = encoder.multipartMixedBoundary; - String content = getRequestBody(encoder); - - String expected = "--" + multipartDataBoundary + "\r\n" + - CONTENT_DISPOSITION + ": form-data; name=\"foo\"" + "\r\n" + - CONTENT_LENGTH + ": 3" + "\r\n" + - CONTENT_TYPE + ": text/plain; charset=UTF-8" + "\r\n" + - "\r\n" + - "bar" + "\r\n" + - "--" + multipartDataBoundary + "\r\n" + - CONTENT_DISPOSITION + ": form-data; name=\"quux\"" + "\r\n" + - CONTENT_TYPE + ": multipart/mixed; boundary=" + multipartMixedBoundary + "\r\n" + - "\r\n" + - "--" + multipartMixedBoundary + "\r\n" + - CONTENT_DISPOSITION + ": attachment; filename=\"file-01.txt\"" + "\r\n" + - CONTENT_LENGTH + ": " + file1.length() + "\r\n" + - CONTENT_TYPE + ": text/plain" + "\r\n" + - CONTENT_TRANSFER_ENCODING + ": binary" + "\r\n" + - "\r\n" + - "File 01" + StringUtil.NEWLINE + - "\r\n" + - "--" + multipartMixedBoundary + "\r\n" + - CONTENT_DISPOSITION + ": attachment; filename=\"file-02.txt\"" + "\r\n" + - CONTENT_LENGTH + ": " + file2.length() + "\r\n" + - CONTENT_TYPE + ": text/plain" + "\r\n" + - CONTENT_TRANSFER_ENCODING + ": binary" + "\r\n" + - "\r\n" + - "File 02" + StringUtil.NEWLINE + - "\r\n" + - "--" + multipartMixedBoundary + "\r\n" + - CONTENT_DISPOSITION + ": attachment; filename=\"file-03.txt\"" + "\r\n" + - CONTENT_LENGTH + ": " + file3.length() + "\r\n" + - CONTENT_TYPE + ": text/plain" + "\r\n" + - CONTENT_TRANSFER_ENCODING + ": binary" + "\r\n" + - "\r\n" + - "File 03" + StringUtil.NEWLINE + - "\r\n" + - "--" + multipartMixedBoundary + "--" + "\r\n" + - "--" + multipartDataBoundary + "--" + "\r\n"; - - assertEquals(expected, content); - } - - @Test - public void testMultiFileUploadInMixedModeNoName() throws Exception { - DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, - HttpMethod.POST, "http://localhost"); - - HttpPostRequestEncoder encoder = new HttpPostRequestEncoder(request, true); - File file1 = new File(getClass().getResource("/file-01.txt").toURI()); - File file2 = new File(getClass().getResource("/file-02.txt").toURI()); - encoder.addBodyAttribute("foo", "bar"); - encoder.addBodyFileUpload("quux", "", file1, "text/plain", false); - encoder.addBodyFileUpload("quux", "", file2, "text/plain", false); - - // We have to query the value of these two fields before finalizing - // the request, which unsets one of them. - String multipartDataBoundary = encoder.multipartDataBoundary; - String multipartMixedBoundary = encoder.multipartMixedBoundary; - String content = getRequestBody(encoder); - - String expected = "--" + multipartDataBoundary + "\r\n" + - CONTENT_DISPOSITION + ": form-data; name=\"foo\"" + "\r\n" + - CONTENT_LENGTH + ": 3" + "\r\n" + - CONTENT_TYPE + ": text/plain; charset=UTF-8" + "\r\n" + - "\r\n" + - "bar" + "\r\n" + - "--" + multipartDataBoundary + "\r\n" + - CONTENT_DISPOSITION + ": form-data; name=\"quux\"" + "\r\n" + - CONTENT_TYPE + ": multipart/mixed; boundary=" + multipartMixedBoundary + "\r\n" + - "\r\n" + - "--" + multipartMixedBoundary + "\r\n" + - CONTENT_DISPOSITION + ": attachment\r\n" + - CONTENT_LENGTH + ": " + file1.length() + "\r\n" + - CONTENT_TYPE + ": text/plain" + "\r\n" + - CONTENT_TRANSFER_ENCODING + ": binary" + "\r\n" + - "\r\n" + - "File 01" + StringUtil.NEWLINE + - "\r\n" + - "--" + multipartMixedBoundary + "\r\n" + - CONTENT_DISPOSITION + ": attachment\r\n" + - CONTENT_LENGTH + ": " + file2.length() + "\r\n" + - CONTENT_TYPE + ": text/plain" + "\r\n" + - CONTENT_TRANSFER_ENCODING + ": binary" + "\r\n" + - "\r\n" + - "File 02" + StringUtil.NEWLINE + - "\r\n" + - "--" + multipartMixedBoundary + "--" + "\r\n" + - "--" + multipartDataBoundary + "--" + "\r\n"; - - assertEquals(expected, content); - } - - @Test - public void testSingleFileUploadInHtml5Mode() throws Exception { - DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, - HttpMethod.POST, "http://localhost"); - - DefaultHttpDataFactory factory = new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE); - - HttpPostRequestEncoder encoder = new HttpPostRequestEncoder(factory, - request, true, CharsetUtil.UTF_8, EncoderMode.HTML5); - File file1 = new File(getClass().getResource("/file-01.txt").toURI()); - File file2 = new File(getClass().getResource("/file-02.txt").toURI()); - encoder.addBodyAttribute("foo", "bar"); - encoder.addBodyFileUpload("quux", file1, "text/plain", false); - encoder.addBodyFileUpload("quux", file2, "text/plain", false); - - String multipartDataBoundary = encoder.multipartDataBoundary; - String content = getRequestBody(encoder); - - String expected = "--" + multipartDataBoundary + "\r\n" + - CONTENT_DISPOSITION + ": form-data; name=\"foo\"" + "\r\n" + - CONTENT_LENGTH + ": 3" + "\r\n" + - CONTENT_TYPE + ": text/plain; charset=UTF-8" + "\r\n" + - "\r\n" + - "bar" + "\r\n" + - "--" + multipartDataBoundary + "\r\n" + - CONTENT_DISPOSITION + ": form-data; name=\"quux\"; filename=\"file-01.txt\"" + "\r\n" + - CONTENT_LENGTH + ": " + file1.length() + "\r\n" + - CONTENT_TYPE + ": text/plain" + "\r\n" + - CONTENT_TRANSFER_ENCODING + ": binary" + "\r\n" + - "\r\n" + - "File 01" + StringUtil.NEWLINE + "\r\n" + - "--" + multipartDataBoundary + "\r\n" + - CONTENT_DISPOSITION + ": form-data; name=\"quux\"; filename=\"file-02.txt\"" + "\r\n" + - CONTENT_LENGTH + ": " + file2.length() + "\r\n" + - CONTENT_TYPE + ": text/plain" + "\r\n" + - CONTENT_TRANSFER_ENCODING + ": binary" + "\r\n" + - "\r\n" + - "File 02" + StringUtil.NEWLINE + - "\r\n" + - "--" + multipartDataBoundary + "--" + "\r\n"; - - assertEquals(expected, content); - } - - @Test - public void testMultiFileUploadInHtml5Mode() throws Exception { - DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, - HttpMethod.POST, "http://localhost"); - - DefaultHttpDataFactory factory = new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE); - - HttpPostRequestEncoder encoder = new HttpPostRequestEncoder(factory, - request, true, CharsetUtil.UTF_8, EncoderMode.HTML5); - File file1 = new File(getClass().getResource("/file-01.txt").toURI()); - encoder.addBodyAttribute("foo", "bar"); - encoder.addBodyFileUpload("quux", file1, "text/plain", false); - - String multipartDataBoundary = encoder.multipartDataBoundary; - String content = getRequestBody(encoder); - - String expected = "--" + multipartDataBoundary + "\r\n" + - CONTENT_DISPOSITION + ": form-data; name=\"foo\"" + "\r\n" + - CONTENT_LENGTH + ": 3" + "\r\n" + - CONTENT_TYPE + ": text/plain; charset=UTF-8" + "\r\n" + - "\r\n" + - "bar" + - "\r\n" + - "--" + multipartDataBoundary + "\r\n" + - CONTENT_DISPOSITION + ": form-data; name=\"quux\"; filename=\"file-01.txt\"" + "\r\n" + - CONTENT_LENGTH + ": " + file1.length() + "\r\n" + - CONTENT_TYPE + ": text/plain" + "\r\n" + - CONTENT_TRANSFER_ENCODING + ": binary" + "\r\n" + - "\r\n" + - "File 01" + StringUtil.NEWLINE + - "\r\n" + - "--" + multipartDataBoundary + "--" + "\r\n"; - - assertEquals(expected, content); - } - - @Test - public void testHttpPostRequestEncoderSlicedBuffer() throws Exception { - DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, - HttpMethod.POST, "http://localhost"); - - HttpPostRequestEncoder encoder = new HttpPostRequestEncoder(request, true); - // add Form attribute - encoder.addBodyAttribute("getform", "POST"); - encoder.addBodyAttribute("info", "first value"); - encoder.addBodyAttribute("secondinfo", "secondvalue a&"); - encoder.addBodyAttribute("thirdinfo", "short text"); - int length = 100000; - char[] array = new char[length]; - Arrays.fill(array, 'a'); - String longText = new String(array); - encoder.addBodyAttribute("fourthinfo", longText.substring(0, 7470)); - File file1 = new File(getClass().getResource("/file-01.txt").toURI()); - encoder.addBodyFileUpload("myfile", file1, "application/x-zip-compressed", false); - encoder.finalizeRequest(); - while (! encoder.isEndOfInput()) { - HttpContent httpContent = encoder.readChunk((ByteBufAllocator) null); - ByteBuf content = httpContent.content(); - int refCnt = content.refCnt(); - assertTrue((content.unwrap() == content || content.unwrap() == null) && refCnt == 1 || - content.unwrap() != content && refCnt == 2, - "content: " + content + " content.unwrap(): " + content.unwrap() + " refCnt: " + refCnt); - httpContent.release(); - } - encoder.cleanFiles(); - encoder.close(); - } - - private static String getRequestBody(HttpPostRequestEncoder encoder) throws Exception { - encoder.finalizeRequest(); - - List chunks = encoder.multipartHttpDatas; - ByteBuf[] buffers = new ByteBuf[chunks.size()]; - - for (int i = 0; i < buffers.length; i++) { - InterfaceHttpData data = chunks.get(i); - if (data instanceof InternalAttribute) { - buffers[i] = ((InternalAttribute) data).toByteBuf(); - } else if (data instanceof HttpData) { - buffers[i] = ((HttpData) data).getByteBuf(); - } - } - - ByteBuf content = Unpooled.wrappedBuffer(buffers); - String contentStr = content.toString(CharsetUtil.UTF_8); - content.release(); - return contentStr; - } - - @Test - public void testDataIsMultipleOfChunkSize1() throws Exception { - DefaultHttpDataFactory factory = new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE); - DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, - HttpMethod.POST, "http://localhost"); - HttpPostRequestEncoder encoder = new HttpPostRequestEncoder(factory, request, true, - HttpConstants.DEFAULT_CHARSET, HttpPostRequestEncoder.EncoderMode.RFC1738); - - MemoryFileUpload first = new MemoryFileUpload("resources", "", "application/json", null, - CharsetUtil.UTF_8, -1); - first.setMaxSize(-1); - first.setContent(new ByteArrayInputStream(new byte[7955])); - encoder.addBodyHttpData(first); - - MemoryFileUpload second = new MemoryFileUpload("resources2", "", "application/json", null, - CharsetUtil.UTF_8, -1); - second.setMaxSize(-1); - second.setContent(new ByteArrayInputStream(new byte[7928])); - encoder.addBodyHttpData(second); - - assertNotNull(encoder.finalizeRequest()); - - checkNextChunkSize(encoder, 8080); - checkNextChunkSize(encoder, 8080); - - HttpContent httpContent = encoder.readChunk((ByteBufAllocator) null); - assertTrue(httpContent instanceof LastHttpContent, "Expected LastHttpContent is not received"); - httpContent.release(); - - assertTrue(encoder.isEndOfInput(), "Expected end of input is not receive"); - } - - @Test - public void testDataIsMultipleOfChunkSize2() throws Exception { - DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, - HttpMethod.POST, "http://localhost"); - HttpPostRequestEncoder encoder = new HttpPostRequestEncoder(request, true); - int length = 7943; - char[] array = new char[length]; - Arrays.fill(array, 'a'); - String longText = new String(array); - encoder.addBodyAttribute("foo", longText); - - assertNotNull(encoder.finalizeRequest()); - - checkNextChunkSize(encoder, 8080); - - HttpContent httpContent = encoder.readChunk((ByteBufAllocator) null); - assertTrue(httpContent instanceof LastHttpContent, "Expected LastHttpContent is not received"); - httpContent.release(); - - assertTrue(encoder.isEndOfInput(), "Expected end of input is not receive"); - } - - private static void checkNextChunkSize(HttpPostRequestEncoder encoder, int sizeWithoutDelimiter) throws Exception { - // 16 bytes as HttpPostRequestEncoder uses Long.toHexString(...) to generate a hex-string which will be between - // 2 and 16 bytes. - // See https://github.com/netty/netty/blob/4.1/codec-http/src/main/java/io/netty/handler/ - // codec/http/multipart/HttpPostRequestEncoder.java#L291 - int expectedSizeMin = sizeWithoutDelimiter + 2; - int expectedSizeMax = sizeWithoutDelimiter + 16; - - HttpContent httpContent = encoder.readChunk((ByteBufAllocator) null); - - int readable = httpContent.content().readableBytes(); - boolean expectedSize = readable >= expectedSizeMin && readable <= expectedSizeMax; - assertTrue(expectedSize, "Chunk size is not in expected range (" + expectedSizeMin + " - " - + expectedSizeMax + "), was: " + readable); - httpContent.release(); - } - - @Test - public void testEncodeChunkedContent() throws Exception { - HttpRequest req = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/"); - HttpPostRequestEncoder encoder = new HttpPostRequestEncoder(req, false); - - int length = 8077 + 8096; - char[] array = new char[length]; - Arrays.fill(array, 'a'); - String longText = new String(array); - - encoder.addBodyAttribute("data", longText); - encoder.addBodyAttribute("moreData", "abcd"); - - assertNotNull(encoder.finalizeRequest()); - - while (!encoder.isEndOfInput()) { - encoder.readChunk((ByteBufAllocator) null).release(); - } - - assertTrue(encoder.isEndOfInput()); - encoder.cleanFiles(); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/multipart/MemoryFileUploadTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/multipart/MemoryFileUploadTest.java deleted file mode 100644 index 167c8c3c20..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/multipart/MemoryFileUploadTest.java +++ /dev/null @@ -1,30 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class MemoryFileUploadTest { - - @Test - public final void testMemoryFileUploadEquals() { - MemoryFileUpload f1 = - new MemoryFileUpload("m1", "m1", "application/json", null, null, 100); - assertEquals(f1, f1); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/CloseWebSocketFrameTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/CloseWebSocketFrameTest.java deleted file mode 100644 index f1bcc3fc3d..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/CloseWebSocketFrameTest.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the "License"); - * you may not use this file except in compliance with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is - * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and limitations under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import org.assertj.core.api.ThrowableAssert; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -class CloseWebSocketFrameTest { - - @Test - void testInvalidCode() { - doTestInvalidCode(new ThrowableAssert.ThrowingCallable() { - - @Override - public void call() throws RuntimeException { - new CloseWebSocketFrame(WebSocketCloseStatus.ABNORMAL_CLOSURE); - } - }); - - doTestInvalidCode(new ThrowableAssert.ThrowingCallable() { - - @Override - public void call() throws RuntimeException { - new CloseWebSocketFrame(WebSocketCloseStatus.ABNORMAL_CLOSURE, "invalid code"); - } - }); - - doTestInvalidCode(new ThrowableAssert.ThrowingCallable() { - - @Override - public void call() throws RuntimeException { - new CloseWebSocketFrame(1006, "invalid code"); - } - }); - - doTestInvalidCode(new ThrowableAssert.ThrowingCallable() { - - @Override - public void call() throws RuntimeException { - new CloseWebSocketFrame(true, 0, 1006, "invalid code"); - } - }); - } - - @Test - void testValidCode() { - doTestValidCode(new CloseWebSocketFrame(WebSocketCloseStatus.NORMAL_CLOSURE), - WebSocketCloseStatus.NORMAL_CLOSURE.code(), WebSocketCloseStatus.NORMAL_CLOSURE.reasonText()); - - doTestValidCode(new CloseWebSocketFrame(WebSocketCloseStatus.NORMAL_CLOSURE, "valid code"), - WebSocketCloseStatus.NORMAL_CLOSURE.code(), "valid code"); - - doTestValidCode(new CloseWebSocketFrame(1000, "valid code"), 1000, "valid code"); - - doTestValidCode(new CloseWebSocketFrame(true, 0, 1000, "valid code"), 1000, "valid code"); - } - - private static void doTestInvalidCode(ThrowableAssert.ThrowingCallable callable) { - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(callable); - } - - private static void doTestValidCode(CloseWebSocketFrame frame, int expectedCode, String expectedReason) { - assertThat(frame.statusCode()).isEqualTo(expectedCode); - assertThat(frame.reasonText()).isEqualTo(expectedReason); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocket00FrameEncoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocket00FrameEncoderTest.java deleted file mode 100644 index c428e316f7..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocket00FrameEncoderTest.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.embedded.EmbeddedChannel; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class WebSocket00FrameEncoderTest { - - // Test for https://github.com/netty/netty/issues/2768 - @Test - public void testMultipleWebSocketCloseFrames() { - EmbeddedChannel channel = new EmbeddedChannel(new WebSocket00FrameEncoder()); - assertTrue(channel.writeOutbound(new CloseWebSocketFrame())); - assertTrue(channel.writeOutbound(new CloseWebSocketFrame())); - assertTrue(channel.finish()); - assertCloseWebSocketFrame(channel); - assertCloseWebSocketFrame(channel); - assertNull(channel.readOutbound()); - } - - private static void assertCloseWebSocketFrame(EmbeddedChannel channel) { - ByteBuf buf = channel.readOutbound(); - assertEquals(2, buf.readableBytes()); - assertEquals((byte) 0xFF, buf.readByte()); - assertEquals((byte) 0x00, buf.readByte()); - buf.release(); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocket08EncoderDecoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocket08EncoderDecoderTest.java deleted file mode 100644 index 2edc7bd35d..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocket08EncoderDecoderTest.java +++ /dev/null @@ -1,222 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** - * Tests the WebSocket08FrameEncoder and Decoder implementation.
- * Checks whether the combination of encoding and decoding yields the original data.
- * Thereby also the masking behavior is checked. - */ -public class WebSocket08EncoderDecoderTest { - - private ByteBuf binTestData; - private String strTestData; - - private static final int MAX_TESTDATA_LENGTH = 100 * 1024; - - private void initTestData() { - binTestData = Unpooled.buffer(MAX_TESTDATA_LENGTH); - byte j = 0; - for (int i = 0; i < MAX_TESTDATA_LENGTH; i++) { - binTestData.array()[i] = j; - j++; - } - - StringBuilder s = new StringBuilder(); - char c = 'A'; - for (int i = 0; i < MAX_TESTDATA_LENGTH; i++) { - s.append(c); - c++; - if (c == 'Z') { - c = 'A'; - } - } - strTestData = s.toString(); - } - - @Test - public void testWebSocketProtocolViolation() { - // Given - initTestData(); - - int maxPayloadLength = 255; - String errorMessage = "Max frame length of " + maxPayloadLength + " has been exceeded."; - WebSocketCloseStatus expectedStatus = WebSocketCloseStatus.MESSAGE_TOO_BIG; - - // With auto-close - WebSocketDecoderConfig config = WebSocketDecoderConfig.newBuilder() - .maxFramePayloadLength(maxPayloadLength) - .closeOnProtocolViolation(true) - .build(); - EmbeddedChannel inChannel = new EmbeddedChannel(new WebSocket08FrameDecoder(config)); - EmbeddedChannel outChannel = new EmbeddedChannel(new WebSocket08FrameEncoder(true)); - - executeProtocolViolationTest(outChannel, inChannel, maxPayloadLength + 1, expectedStatus, errorMessage); - - CloseWebSocketFrame response = inChannel.readOutbound(); - assertNotNull(response); - assertEquals(expectedStatus.code(), response.statusCode()); - assertEquals(errorMessage, response.reasonText()); - response.release(); - - assertFalse(inChannel.finish()); - assertFalse(outChannel.finish()); - - // Without auto-close - config = WebSocketDecoderConfig.newBuilder() - .maxFramePayloadLength(maxPayloadLength) - .closeOnProtocolViolation(false) - .build(); - inChannel = new EmbeddedChannel(new WebSocket08FrameDecoder(config)); - outChannel = new EmbeddedChannel(new WebSocket08FrameEncoder(true)); - - executeProtocolViolationTest(outChannel, inChannel, maxPayloadLength + 1, expectedStatus, errorMessage); - - response = inChannel.readOutbound(); - assertNull(response); - - assertFalse(inChannel.finish()); - assertFalse(outChannel.finish()); - - // Release test data - binTestData.release(); - } - - private void executeProtocolViolationTest(EmbeddedChannel outChannel, EmbeddedChannel inChannel, - int testDataLength, WebSocketCloseStatus expectedStatus, String errorMessage) { - CorruptedWebSocketFrameException corrupted = null; - - try { - testBinaryWithLen(outChannel, inChannel, testDataLength); - } catch (CorruptedWebSocketFrameException e) { - corrupted = e; - } - - BinaryWebSocketFrame exceedingFrame = inChannel.readInbound(); - assertNull(exceedingFrame); - - assertNotNull(corrupted); - assertEquals(expectedStatus, corrupted.closeStatus()); - assertEquals(errorMessage, corrupted.getMessage()); - } - - @Test - public void testWebSocketEncodingAndDecoding() { - initTestData(); - - // Test without masking - EmbeddedChannel outChannel = new EmbeddedChannel(new WebSocket08FrameEncoder(false)); - EmbeddedChannel inChannel = new EmbeddedChannel(new WebSocket08FrameDecoder(false, false, 1024 * 1024, false)); - executeTests(outChannel, inChannel); - - // Test with activated masking - outChannel = new EmbeddedChannel(new WebSocket08FrameEncoder(true)); - inChannel = new EmbeddedChannel(new WebSocket08FrameDecoder(true, false, 1024 * 1024, false)); - executeTests(outChannel, inChannel); - - // Test with activated masking and an unmasked expecting but forgiving decoder - outChannel = new EmbeddedChannel(new WebSocket08FrameEncoder(true)); - inChannel = new EmbeddedChannel(new WebSocket08FrameDecoder(false, false, 1024 * 1024, true)); - executeTests(outChannel, inChannel); - - // Release test data - binTestData.release(); - } - - private void executeTests(EmbeddedChannel outChannel, EmbeddedChannel inChannel) { - // Test at the boundaries of each message type, because this shifts the position of the mask field - // Test min. 4 lengths to check for problems related to an uneven frame length - executeTests(outChannel, inChannel, 0); - executeTests(outChannel, inChannel, 1); - executeTests(outChannel, inChannel, 2); - executeTests(outChannel, inChannel, 3); - executeTests(outChannel, inChannel, 4); - executeTests(outChannel, inChannel, 5); - - executeTests(outChannel, inChannel, 125); - executeTests(outChannel, inChannel, 126); - executeTests(outChannel, inChannel, 127); - executeTests(outChannel, inChannel, 128); - executeTests(outChannel, inChannel, 129); - - executeTests(outChannel, inChannel, 65535); - executeTests(outChannel, inChannel, 65536); - executeTests(outChannel, inChannel, 65537); - executeTests(outChannel, inChannel, 65538); - executeTests(outChannel, inChannel, 65539); - } - - private void executeTests(EmbeddedChannel outChannel, EmbeddedChannel inChannel, int testDataLength) { - testTextWithLen(outChannel, inChannel, testDataLength); - testBinaryWithLen(outChannel, inChannel, testDataLength); - } - - private void testTextWithLen(EmbeddedChannel outChannel, EmbeddedChannel inChannel, int testDataLength) { - String testStr = strTestData.substring(0, testDataLength); - outChannel.writeOutbound(new TextWebSocketFrame(testStr)); - - transfer(outChannel, inChannel); - - Object decoded = inChannel.readInbound(); - assertNotNull(decoded); - assertTrue(decoded instanceof TextWebSocketFrame); - TextWebSocketFrame txt = (TextWebSocketFrame) decoded; - assertEquals(txt.text(), testStr); - txt.release(); - } - - private void testBinaryWithLen(EmbeddedChannel outChannel, EmbeddedChannel inChannel, int testDataLength) { - binTestData.retain(); // need to retain for sending and still keeping it - binTestData.setIndex(0, testDataLength); // Send only len bytes - outChannel.writeOutbound(new BinaryWebSocketFrame(binTestData)); - - transfer(outChannel, inChannel); - - Object decoded = inChannel.readInbound(); - assertNotNull(decoded); - assertTrue(decoded instanceof BinaryWebSocketFrame); - BinaryWebSocketFrame binFrame = (BinaryWebSocketFrame) decoded; - int readable = binFrame.content().readableBytes(); - assertEquals(readable, testDataLength); - for (int i = 0; i < testDataLength; i++) { - assertEquals(binTestData.getByte(i), binFrame.content().getByte(i)); - } - binFrame.release(); - } - - private void transfer(EmbeddedChannel outChannel, EmbeddedChannel inChannel) { - // Transfer encoded data into decoder - // Loop because there might be multiple frames (gathering write) - for (;;) { - ByteBuf encoded = outChannel.readOutbound(); - if (encoded == null) { - return; - } - inChannel.writeInbound(encoded); - } - } -} 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 deleted file mode 100644 index 4795a8faab..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoderTest.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the "License"); - * you may not use this file except in compliance with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is - * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and limitations under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.embedded.EmbeddedChannel; -import org.junit.jupiter.api.Test; - -import java.util.HashSet; -import java.util.Set; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.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 = mock(ChannelHandlerContext.class); - decoder.handlerAdded(ctx); - decoder.channelInactive(ctx); - 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(); - } - } - } -} 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 deleted file mode 100644 index 9d1606f370..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00Test.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.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, String subprotocol, HttpHeaders headers, - boolean absoluteUpgradeUrl) { - return new WebSocketClientHandshaker00(uri, WebSocketVersion.V00, subprotocol, headers, - 1024, 10000, absoluteUpgradeUrl); - } - - @Override - protected CharSequence getOriginHeaderName() { - return HttpHeaderNames.ORIGIN; - } - - @Override - protected CharSequence getProtocolHeaderName() { - return HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL; - } - - @Override - protected CharSequence[] getHandshakeRequiredHeaderNames() { - return new CharSequence[] { - HttpHeaderNames.CONNECTION, - HttpHeaderNames.UPGRADE, - HttpHeaderNames.HOST, - 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 deleted file mode 100644 index 692bc3bbe6..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker07Test.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -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 org.junit.jupiter.api.Test; - -import java.net.URI; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class WebSocketClientHandshaker07Test extends WebSocketClientHandshakerTest { - - @Test - public void testHostHeaderPreserved() { - URI uri = URI.create("ws://localhost:9999"); - WebSocketClientHandshaker handshaker = newHandshaker(uri, null, - new DefaultHttpHeaders().set(HttpHeaderNames.HOST, "test.netty.io"), false); - - FullHttpRequest request = handshaker.newHandshakeRequest(); - try { - assertEquals("/", request.uri()); - assertEquals("test.netty.io", request.headers().get(HttpHeaderNames.HOST)); - } finally { - request.release(); - } - } - - @Override - protected WebSocketClientHandshaker newHandshaker(URI uri, String subprotocol, HttpHeaders headers, - boolean absoluteUpgradeUrl) { - return new WebSocketClientHandshaker07(uri, WebSocketVersion.V07, subprotocol, false, headers, - 1024, true, false, 10000, - absoluteUpgradeUrl); - } - - @Override - protected CharSequence getOriginHeaderName() { - return HttpHeaderNames.SEC_WEBSOCKET_ORIGIN; - } - - @Override - protected CharSequence getProtocolHeaderName() { - return HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL; - } - - @Override - protected CharSequence[] getHandshakeRequiredHeaderNames() { - return new CharSequence[] { - HttpHeaderNames.UPGRADE, - HttpHeaderNames.CONNECTION, - HttpHeaderNames.SEC_WEBSOCKET_KEY, - HttpHeaderNames.HOST, - 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 deleted file mode 100644 index 34c5fb76d8..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker08Test.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.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, String subprotocol, HttpHeaders headers, - boolean absoluteUpgradeUrl) { - return new WebSocketClientHandshaker08(uri, WebSocketVersion.V08, subprotocol, false, headers, - 1024, true, true, 10000, - absoluteUpgradeUrl); - } -} 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 deleted file mode 100644 index 2371caed59..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker13Test.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaders; - -import java.net.URI; - -public class WebSocketClientHandshaker13Test extends WebSocketClientHandshaker07Test { - - @Override - protected WebSocketClientHandshaker newHandshaker(URI uri, String subprotocol, HttpHeaders headers, - boolean absoluteUpgradeUrl) { - return new WebSocketClientHandshaker13(uri, WebSocketVersion.V13, subprotocol, false, headers, - 1024, true, true, 10000, - absoluteUpgradeUrl); - } - - @Override - protected CharSequence getOriginHeaderName() { - return HttpHeaderNames.ORIGIN; - } - -} 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 deleted file mode 100644 index 76d2b2fb9c..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshakerTest.java +++ /dev/null @@ -1,418 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.CompositeByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.http.DefaultFullHttpResponse; -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; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.util.CharsetUtil; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; - -import java.net.URI; -import java.util.concurrent.TimeUnit; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public abstract class WebSocketClientHandshakerTest { - protected abstract WebSocketClientHandshaker newHandshaker(URI uri, String subprotocol, HttpHeaders headers, - boolean absoluteUpgradeUrl); - - protected WebSocketClientHandshaker newHandshaker(URI uri) { - return newHandshaker(uri, null, null, false); - } - - protected abstract CharSequence getOriginHeaderName(); - - protected abstract CharSequence getProtocolHeaderName(); - - protected abstract CharSequence[] getHandshakeRequiredHeaderNames(); - - @Test - public void hostHeaderWs() { - for (String scheme : new String[]{"ws://", "http://"}) { - for (String host : new String[]{"localhost", "127.0.0.1", "[::1]", "Netty.io"}) { - String enter = scheme + host; - - testHostHeader(enter, host); - testHostHeader(enter + '/', host); - testHostHeader(enter + ":80", host); - testHostHeader(enter + ":443", host + ":443"); - testHostHeader(enter + ":9999", host + ":9999"); - testHostHeader(enter + "/path", host); - testHostHeader(enter + ":80/path", host); - testHostHeader(enter + ":443/path", host + ":443"); - testHostHeader(enter + ":9999/path", host + ":9999"); - } - } - } - - @Test - public void hostHeaderWss() { - for (String scheme : new String[]{"wss://", "https://"}) { - for (String host : new String[]{"localhost", "127.0.0.1", "[::1]", "Netty.io"}) { - String enter = scheme + host; - - testHostHeader(enter, host); - testHostHeader(enter + '/', host); - testHostHeader(enter + ":80", host + ":80"); - testHostHeader(enter + ":443", host); - testHostHeader(enter + ":9999", host + ":9999"); - testHostHeader(enter + "/path", host); - testHostHeader(enter + ":80/path", host + ":80"); - testHostHeader(enter + ":443/path", host); - testHostHeader(enter + ":9999/path", host + ":9999"); - } - } - } - - @Test - public void hostHeaderWithoutScheme() { - testHostHeader("//localhost/", "localhost"); - testHostHeader("//localhost/path", "localhost"); - testHostHeader("//localhost:80/", "localhost:80"); - testHostHeader("//localhost:443/", "localhost:443"); - testHostHeader("//localhost:9999/", "localhost:9999"); - } - - @Test - public void originHeaderWs() { - for (String scheme : new String[]{"ws://", "http://"}) { - for (String host : new String[]{"localhost", "127.0.0.1", "[::1]", "NETTY.IO"}) { - String enter = scheme + host; - String expect = "http://" + host.toLowerCase(); - - testOriginHeader(enter, expect); - testOriginHeader(enter + '/', expect); - testOriginHeader(enter + ":80", expect); - testOriginHeader(enter + ":443", expect + ":443"); - testOriginHeader(enter + ":9999", expect + ":9999"); - testOriginHeader(enter + "/path%20with%20ws", expect); - testOriginHeader(enter + ":80/path%20with%20ws", expect); - testOriginHeader(enter + ":443/path%20with%20ws", expect + ":443"); - testOriginHeader(enter + ":9999/path%20with%20ws", expect + ":9999"); - } - } - } - - @Test - public void originHeaderWss() { - for (String scheme : new String[]{"wss://", "https://"}) { - for (String host : new String[]{"localhost", "127.0.0.1", "[::1]", "NETTY.IO"}) { - String enter = scheme + host; - String expect = "https://" + host.toLowerCase(); - - testOriginHeader(enter, expect); - testOriginHeader(enter + '/', expect); - testOriginHeader(enter + ":80", expect + ":80"); - testOriginHeader(enter + ":443", expect); - testOriginHeader(enter + ":9999", expect + ":9999"); - testOriginHeader(enter + "/path%20with%20ws", expect); - testOriginHeader(enter + ":80/path%20with%20ws", expect + ":80"); - testOriginHeader(enter + ":443/path%20with%20ws", expect); - testOriginHeader(enter + ":9999/path%20with%20ws", expect + ":9999"); - } - } - } - - @Test - public void originHeaderWithoutScheme() { - testOriginHeader("//localhost/", "http://localhost"); - testOriginHeader("//localhost/path", "http://localhost"); - - // http scheme by port - testOriginHeader("//localhost:80/", "http://localhost"); - testOriginHeader("//localhost:80/path", "http://localhost"); - - // https scheme by port - testOriginHeader("//localhost:443/", "https://localhost"); - testOriginHeader("//localhost:443/path", "https://localhost"); - - // http scheme for non standard port - testOriginHeader("//localhost:9999/", "http://localhost:9999"); - testOriginHeader("//localhost:9999/path", "http://localhost:9999"); - - // convert host to lower case - testOriginHeader("//LOCALHOST/", "http://localhost"); - } - - @Test - public void testSetOriginFromCustomHeaders() { - HttpHeaders customHeaders = new DefaultHttpHeaders().set(getOriginHeaderName(), "http://example.com"); - WebSocketClientHandshaker handshaker = newHandshaker(URI.create("ws://server.example.com/chat"), null, - customHeaders, false); - FullHttpRequest request = handshaker.newHandshakeRequest(); - try { - assertEquals("http://example.com", request.headers().get(getOriginHeaderName())); - } finally { - request.release(); - } - } - - private void testHostHeader(String uri, String expected) { - testHeaderDefaultHttp(uri, HttpHeaderNames.HOST, expected); - } - - private void testOriginHeader(String uri, String expected) { - testHeaderDefaultHttp(uri, getOriginHeaderName(), expected); - } - - protected void testHeaderDefaultHttp(String uri, CharSequence header, String expectedValue) { - WebSocketClientHandshaker handshaker = newHandshaker(URI.create(uri)); - FullHttpRequest request = handshaker.newHandshakeRequest(); - try { - assertEquals(expectedValue, request.headers().get(header)); - } finally { - request.release(); - } - } - - @Test - @SuppressWarnings("deprecation") - public void testUpgradeUrl() { - URI uri = URI.create("ws://localhost:9999/path%20with%20ws"); - WebSocketClientHandshaker handshaker = newHandshaker(uri); - FullHttpRequest request = handshaker.newHandshakeRequest(); - try { - assertEquals("/path%20with%20ws", request.getUri()); - } finally { - request.release(); - } - } - - @Test - public void testUpgradeUrlWithQuery() { - URI uri = URI.create("ws://localhost:9999/path%20with%20ws?a=b%20c"); - WebSocketClientHandshaker handshaker = newHandshaker(uri); - FullHttpRequest request = handshaker.newHandshakeRequest(); - try { - assertEquals("/path%20with%20ws?a=b%20c", request.uri()); - } finally { - request.release(); - } - } - - @Test - public void testUpgradeUrlWithoutPath() { - URI uri = URI.create("ws://localhost:9999"); - WebSocketClientHandshaker handshaker = newHandshaker(uri); - FullHttpRequest request = handshaker.newHandshakeRequest(); - try { - assertEquals("/", request.uri()); - } finally { - request.release(); - } - } - - @Test - public void testUpgradeUrlWithoutPathWithQuery() { - URI uri = URI.create("ws://localhost:9999?a=b%20c"); - WebSocketClientHandshaker handshaker = newHandshaker(uri); - FullHttpRequest request = handshaker.newHandshakeRequest(); - try { - assertEquals("/?a=b%20c", request.uri()); - } finally { - request.release(); - } - } - - @Test - public void testAbsoluteUpgradeUrlWithQuery() { - URI uri = URI.create("ws://localhost:9999/path%20with%20ws?a=b%20c"); - WebSocketClientHandshaker handshaker = newHandshaker(uri, null, null, true); - FullHttpRequest request = handshaker.newHandshakeRequest(); - try { - assertEquals("ws://localhost:9999/path%20with%20ws?a=b%20c", request.uri()); - } finally { - request.release(); - } - } - - @Test - @Timeout(value = 3000, unit = TimeUnit.MILLISECONDS) - public void testHttpResponseAndFrameInSameBuffer() { - testHttpResponseAndFrameInSameBuffer(false); - } - - @Test - @Timeout(value = 3000, unit = TimeUnit.MILLISECONDS) - public void testHttpResponseAndFrameInSameBufferCodec() { - testHttpResponseAndFrameInSameBuffer(true); - } - - private void testHttpResponseAndFrameInSameBuffer(boolean codec) { - String url = "ws://localhost:9999/ws"; - final WebSocketClientHandshaker shaker = newHandshaker(URI.create(url)); - final WebSocketClientHandshaker handshaker = new WebSocketClientHandshaker( - shaker.uri(), shaker.version(), null, EmptyHttpHeaders.INSTANCE, Integer.MAX_VALUE, -1) { - @Override - protected FullHttpRequest newHandshakeRequest() { - return shaker.newHandshakeRequest(); - } - - @Override - protected void verify(FullHttpResponse response) { - // Not do any verification, so we not need to care sending the correct headers etc in the test, - // which would just make things more complicated. - } - - @Override - protected WebSocketFrameDecoder newWebsocketDecoder() { - return shaker.newWebsocketDecoder(); - } - - @Override - protected WebSocketFrameEncoder newWebSocketEncoder() { - return shaker.newWebSocketEncoder(); - } - }; - - // 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. - WebSocketServerHandshakerFactory factory = new WebSocketServerHandshakerFactory(url, null, false); - FullHttpRequest request = shaker.newHandshakeRequest(); - WebSocketServerHandshaker socketServerHandshaker = factory.newHandshaker(request); - request.release(); - EmbeddedChannel websocketChannel = new EmbeddedChannel(socketServerHandshaker.newWebSocketEncoder(), - socketServerHandshaker.newWebsocketDecoder()); - assertTrue(websocketChannel.writeOutbound(new BinaryWebSocketFrame(Unpooled.wrappedBuffer(data)))); - - byte[] bytes = "HTTP/1.1 101 Switching Protocols\r\nContent-Length: 0\r\n\r\n".getBytes(CharsetUtil.US_ASCII); - - CompositeByteBuf compositeByteBuf = Unpooled.compositeBuffer(); - compositeByteBuf.addComponent(true, Unpooled.wrappedBuffer(bytes)); - for (;;) { - ByteBuf frameBytes = websocketChannel.readOutbound(); - if (frameBytes == null) { - break; - } - compositeByteBuf.addComponent(true, frameBytes); - } - - EmbeddedChannel ch = new EmbeddedChannel(new HttpObjectAggregator(Integer.MAX_VALUE), - new SimpleChannelInboundHandler() { - @Override - protected void messageReceived(ChannelHandlerContext ctx, FullHttpResponse msg) throws Exception { - handshaker.finishHandshake(ctx.channel(), msg); - ctx.pipeline().remove(this); - } - }); - if (codec) { - ch.pipeline().addFirst(new HttpClientCodec()); - } else { - ch.pipeline().addFirst(new HttpRequestEncoder(), new HttpResponseDecoder()); - } - // We need to first write the request as HttpClientCodec will fail if we receive a response before a request - // was written. - shaker.handshake(ch).syncUninterruptibly(); - for (;;) { - // Just consume the bytes, we are not interested in these. - ByteBuf buf = ch.readOutbound(); - if (buf == null) { - break; - } - buf.release(); - } - assertTrue(ch.writeInbound(compositeByteBuf)); - assertTrue(ch.finish()); - - BinaryWebSocketFrame frame = ch.readInbound(); - ByteBuf expect = Unpooled.wrappedBuffer(data); - try { - assertEquals(expect, frame.content()); - assertTrue(frame.isFinalFragment()); - assertEquals(0, frame.rsv()); - } finally { - expect.release(); - 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 : getHandshakeRequiredHeaderNames()) { - if (!HttpHeaderNames.HOST.equals(header)) { - inputHeaders.add(header, bogusHeaderValue); - } - } - inputHeaders.add(getProtocolHeaderName(), bogusSubProtocol); - - String realSubProtocol = "realSubProtocol"; - WebSocketClientHandshaker handshaker = newHandshaker(uri, realSubProtocol, inputHeaders, false); - 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 : getHandshakeRequiredHeaderNames()) { - 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(); - } - - @Test - public void testWebSocketClientHandshakeException() { - URI uri = URI.create("ws://localhost:9999/exception"); - WebSocketClientHandshaker handshaker = newHandshaker(uri, null, null, false); - FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.UNAUTHORIZED); - response.headers().set(HttpHeaderNames.WWW_AUTHENTICATE, "realm = access token required"); - - try { - handshaker.finishHandshake(null, response); - } catch (WebSocketClientHandshakeException exception) { - assertEquals("Invalid handshake response getStatus: 401 Unauthorized", exception.getMessage()); - assertEquals(HttpResponseStatus.UNAUTHORIZED, exception.response().status()); - assertTrue(exception.response().headers().contains(HttpHeaderNames.WWW_AUTHENTICATE, - "realm = access token required", false)); - } finally { - response.release(); - } - } -} - diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketCloseStatusTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketCloseStatusTest.java deleted file mode 100644 index 9f9e43ea99..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketCloseStatusTest.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is - * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and limitations under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.SortedSet; -import java.util.TreeSet; - -import org.assertj.core.api.ThrowableAssert; -import org.hamcrest.Matchers; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotSame; -import static org.junit.jupiter.api.Assertions.assertSame; - -import static io.netty.handler.codec.http.websocketx.WebSocketCloseStatus.*; - -public class WebSocketCloseStatusTest { - - private final List validCodes = Arrays.asList( - NORMAL_CLOSURE, - ENDPOINT_UNAVAILABLE, - PROTOCOL_ERROR, - INVALID_MESSAGE_TYPE, - INVALID_PAYLOAD_DATA, - POLICY_VIOLATION, - MESSAGE_TOO_BIG, - MANDATORY_EXTENSION, - INTERNAL_SERVER_ERROR, - SERVICE_RESTART, - TRY_AGAIN_LATER, - BAD_GATEWAY - ); - - @Test - public void testToString() { - assertEquals("1000 Bye", NORMAL_CLOSURE.toString()); - } - - @Test - public void testKnownStatuses() { - assertSame(NORMAL_CLOSURE, valueOf(1000)); - assertSame(ENDPOINT_UNAVAILABLE, valueOf(1001)); - assertSame(PROTOCOL_ERROR, valueOf(1002)); - assertSame(INVALID_MESSAGE_TYPE, valueOf(1003)); - assertSame(EMPTY, valueOf(1005)); - assertSame(ABNORMAL_CLOSURE, valueOf(1006)); - assertSame(INVALID_PAYLOAD_DATA, valueOf(1007)); - assertSame(POLICY_VIOLATION, valueOf(1008)); - assertSame(MESSAGE_TOO_BIG, valueOf(1009)); - assertSame(MANDATORY_EXTENSION, valueOf(1010)); - assertSame(INTERNAL_SERVER_ERROR, valueOf(1011)); - assertSame(SERVICE_RESTART, valueOf(1012)); - assertSame(TRY_AGAIN_LATER, valueOf(1013)); - assertSame(BAD_GATEWAY, valueOf(1014)); - assertSame(TLS_HANDSHAKE_FAILED, valueOf(1015)); - } - - @Test - public void testNaturalOrder() { - assertThat(PROTOCOL_ERROR, Matchers.greaterThan(NORMAL_CLOSURE)); - assertThat(PROTOCOL_ERROR, Matchers.greaterThan(valueOf(1001))); - assertThat(PROTOCOL_ERROR, Matchers.comparesEqualTo(PROTOCOL_ERROR)); - assertThat(PROTOCOL_ERROR, Matchers.comparesEqualTo(valueOf(1002))); - assertThat(PROTOCOL_ERROR, Matchers.lessThan(INVALID_MESSAGE_TYPE)); - assertThat(PROTOCOL_ERROR, Matchers.lessThan(valueOf(1007))); - } - - @Test - public void testUserDefinedStatuses() { - // Given, when - WebSocketCloseStatus feedTimeot = new WebSocketCloseStatus(6033, "Feed timed out"); - WebSocketCloseStatus untradablePrice = new WebSocketCloseStatus(6034, "Untradable price"); - - // Then - assertNotSame(feedTimeot, valueOf(6033)); - assertEquals(feedTimeot.code(), 6033); - assertEquals(feedTimeot.reasonText(), "Feed timed out"); - - assertNotSame(untradablePrice, valueOf(6034)); - assertEquals(untradablePrice.code(), 6034); - assertEquals(untradablePrice.reasonText(), "Untradable price"); - } - - @Test - public void testRfc6455CodeValidation() { - // Given - List knownCodes = Arrays.asList( - NORMAL_CLOSURE.code(), - ENDPOINT_UNAVAILABLE.code(), - PROTOCOL_ERROR.code(), - INVALID_MESSAGE_TYPE.code(), - INVALID_PAYLOAD_DATA.code(), - POLICY_VIOLATION.code(), - MESSAGE_TOO_BIG.code(), - MANDATORY_EXTENSION.code(), - INTERNAL_SERVER_ERROR.code(), - SERVICE_RESTART.code(), - TRY_AGAIN_LATER.code(), - BAD_GATEWAY.code() - ); - - SortedSet invalidCodes = new TreeSet(); - - // When - for (int statusCode = Short.MIN_VALUE; statusCode < Short.MAX_VALUE; statusCode++) { - if (!isValidStatusCode(statusCode)) { - invalidCodes.add(statusCode); - } - } - - // Then - assertEquals(0, invalidCodes.first().intValue()); - assertEquals(2999, invalidCodes.last().intValue()); - assertEquals(3000 - validCodes.size(), invalidCodes.size()); - - invalidCodes.retainAll(knownCodes); - assertEquals(invalidCodes, Collections.emptySet()); - } - - @Test - public void testValidationEnabled() { - assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(new ThrowableAssert.ThrowingCallable() { - - @Override - public void call() throws RuntimeException { - new WebSocketCloseStatus(1006, "validation disabled"); - } - }); - } - - @Test - public void testValidationDisabled() { - WebSocketCloseStatus status = new WebSocketCloseStatus(1006, "validation disabled", false); - assertEquals(1006, status.code()); - assertEquals("validation disabled", status.reasonText()); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketFrameAggregatorTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketFrameAggregatorTest.java deleted file mode 100644 index 2339db61c4..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketFrameAggregatorTest.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.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.TooLongFrameException; -import io.netty.util.CharsetUtil; -import io.netty.util.ReferenceCountUtil; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - - -public class WebSocketFrameAggregatorTest { - private static final byte[] content1 = "Content1".getBytes(CharsetUtil.UTF_8); - private static final byte[] content2 = "Content2".getBytes(CharsetUtil.UTF_8); - private static final byte[] content3 = "Content3".getBytes(CharsetUtil.UTF_8); - private static final byte[] aggregatedContent = new byte[content1.length + content2.length + content3.length]; - static { - System.arraycopy(content1, 0, aggregatedContent, 0, content1.length); - System.arraycopy(content2, 0, aggregatedContent, content1.length, content2.length); - System.arraycopy(content3, 0, aggregatedContent, content1.length + content2.length, content3.length); - } - - @Test - public void testAggregationBinary() { - EmbeddedChannel channel = new EmbeddedChannel(new WebSocketFrameAggregator(Integer.MAX_VALUE)); - channel.writeInbound(new BinaryWebSocketFrame(true, 1, Unpooled.wrappedBuffer(content1))); - channel.writeInbound(new BinaryWebSocketFrame(false, 0, Unpooled.wrappedBuffer(content1))); - channel.writeInbound(new ContinuationWebSocketFrame(false, 0, Unpooled.wrappedBuffer(content2))); - channel.writeInbound(new PingWebSocketFrame(Unpooled.wrappedBuffer(content1))); - channel.writeInbound(new PongWebSocketFrame(Unpooled.wrappedBuffer(content1))); - channel.writeInbound(new ContinuationWebSocketFrame(true, 0, Unpooled.wrappedBuffer(content3))); - - assertTrue(channel.finish()); - - BinaryWebSocketFrame frame = channel.readInbound(); - assertTrue(frame.isFinalFragment()); - assertEquals(1, frame.rsv()); - assertArrayEquals(content1, toBytes(frame.content())); - - PingWebSocketFrame frame2 = channel.readInbound(); - assertTrue(frame2.isFinalFragment()); - assertEquals(0, frame2.rsv()); - assertArrayEquals(content1, toBytes(frame2.content())); - - PongWebSocketFrame frame3 = channel.readInbound(); - assertTrue(frame3.isFinalFragment()); - assertEquals(0, frame3.rsv()); - assertArrayEquals(content1, toBytes(frame3.content())); - - BinaryWebSocketFrame frame4 = channel.readInbound(); - assertTrue(frame4.isFinalFragment()); - assertEquals(0, frame4.rsv()); - assertArrayEquals(aggregatedContent, toBytes(frame4.content())); - - assertNull(channel.readInbound()); - } - - @Test - public void testAggregationText() { - EmbeddedChannel channel = new EmbeddedChannel(new WebSocketFrameAggregator(Integer.MAX_VALUE)); - channel.writeInbound(new TextWebSocketFrame(true, 1, Unpooled.wrappedBuffer(content1))); - channel.writeInbound(new TextWebSocketFrame(false, 0, Unpooled.wrappedBuffer(content1))); - channel.writeInbound(new ContinuationWebSocketFrame(false, 0, Unpooled.wrappedBuffer(content2))); - channel.writeInbound(new PingWebSocketFrame(Unpooled.wrappedBuffer(content1))); - channel.writeInbound(new PongWebSocketFrame(Unpooled.wrappedBuffer(content1))); - channel.writeInbound(new ContinuationWebSocketFrame(true, 0, Unpooled.wrappedBuffer(content3))); - - assertTrue(channel.finish()); - - TextWebSocketFrame frame = channel.readInbound(); - assertTrue(frame.isFinalFragment()); - assertEquals(1, frame.rsv()); - assertArrayEquals(content1, toBytes(frame.content())); - - PingWebSocketFrame frame2 = channel.readInbound(); - assertTrue(frame2.isFinalFragment()); - assertEquals(0, frame2.rsv()); - assertArrayEquals(content1, toBytes(frame2.content())); - - PongWebSocketFrame frame3 = channel.readInbound(); - assertTrue(frame3.isFinalFragment()); - assertEquals(0, frame3.rsv()); - assertArrayEquals(content1, toBytes(frame3.content())); - - TextWebSocketFrame frame4 = channel.readInbound(); - assertTrue(frame4.isFinalFragment()); - assertEquals(0, frame4.rsv()); - assertArrayEquals(aggregatedContent, toBytes(frame4.content())); - - assertNull(channel.readInbound()); - } - - @Test - public void textFrameTooBig() throws Exception { - EmbeddedChannel channel = new EmbeddedChannel(new WebSocketFrameAggregator(8)); - channel.writeInbound(new BinaryWebSocketFrame(true, 1, Unpooled.wrappedBuffer(content1))); - channel.writeInbound(new BinaryWebSocketFrame(false, 0, Unpooled.wrappedBuffer(content1))); - try { - channel.writeInbound(new ContinuationWebSocketFrame(false, 0, Unpooled.wrappedBuffer(content2))); - fail(); - } catch (TooLongFrameException e) { - // expected - } - channel.writeInbound(new ContinuationWebSocketFrame(false, 0, Unpooled.wrappedBuffer(content2))); - channel.writeInbound(new ContinuationWebSocketFrame(true, 0, Unpooled.wrappedBuffer(content2))); - - channel.writeInbound(new BinaryWebSocketFrame(true, 1, Unpooled.wrappedBuffer(content1))); - channel.writeInbound(new BinaryWebSocketFrame(false, 0, Unpooled.wrappedBuffer(content1))); - try { - channel.writeInbound(new ContinuationWebSocketFrame(false, 0, Unpooled.wrappedBuffer(content2))); - fail(); - } catch (TooLongFrameException e) { - // expected - } - channel.writeInbound(new ContinuationWebSocketFrame(false, 0, Unpooled.wrappedBuffer(content2))); - channel.writeInbound(new ContinuationWebSocketFrame(true, 0, Unpooled.wrappedBuffer(content2))); - for (;;) { - Object msg = channel.readInbound(); - if (msg == null) { - break; - } - ReferenceCountUtil.release(msg); - } - channel.finish(); - } - - private static byte[] toBytes(ByteBuf buf) { - byte[] bytes = new byte[buf.readableBytes()]; - buf.readBytes(bytes); - buf.release(); - return bytes; - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketHandshakeExceptionTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketHandshakeExceptionTest.java deleted file mode 100644 index e9ec9d7b86..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketHandshakeExceptionTest.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.handler.codec.http.DefaultHttpRequest; -import io.netty.handler.codec.http.DefaultHttpResponse; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpVersion; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; - -public class WebSocketHandshakeExceptionTest { - - @Test - public void testClientExceptionWithoutResponse() { - WebSocketClientHandshakeException clientException = new WebSocketClientHandshakeException("client message"); - - assertNull(clientException.response()); - assertEquals("client message", clientException.getMessage()); - } - - @Test - public void testClientExceptionWithResponse() { - HttpResponse httpResponse = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST); - httpResponse.headers().set("x-header", "x-value"); - WebSocketClientHandshakeException clientException = new WebSocketClientHandshakeException("client message", - httpResponse); - - assertNotNull(clientException.response()); - assertEquals("client message", clientException.getMessage()); - assertEquals(HttpResponseStatus.BAD_REQUEST, clientException.response().status()); - assertEquals(httpResponse.headers(), clientException.response().headers()); - } - - @Test - public void testServerExceptionWithoutRequest() { - WebSocketServerHandshakeException serverException = new WebSocketServerHandshakeException("server message"); - - assertNull(serverException.request()); - assertEquals("server message", serverException.getMessage()); - } - - @Test - public void testClientExceptionWithRequest() { - HttpRequest httpRequest = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, - "ws://localhost:9999/ws"); - httpRequest.headers().set("x-header", "x-value"); - WebSocketServerHandshakeException serverException = new WebSocketServerHandshakeException("server message", - httpRequest); - - assertNotNull(serverException.request()); - assertEquals("server message", serverException.getMessage()); - assertEquals(HttpMethod.GET, serverException.request().method()); - assertEquals(httpRequest.headers(), serverException.request().headers()); - assertEquals(httpRequest.uri(), serverException.request().uri()); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketHandshakeHandOverTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketHandshakeHandOverTest.java deleted file mode 100644 index 0515740896..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketHandshakeHandOverTest.java +++ /dev/null @@ -1,360 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version - * 2.0 (the "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.http.EmptyHttpHeaders; -import io.netty.handler.codec.http.HttpClientCodec; -import io.netty.handler.codec.http.HttpObjectAggregator; -import io.netty.handler.codec.http.HttpServerCodec; -import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler.ClientHandshakeStateEvent; -import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler.ServerHandshakeStateEvent; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; - -import java.net.URI; -import java.util.concurrent.CompletionException; -import java.util.concurrent.TimeUnit; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class WebSocketHandshakeHandOverTest { - - private boolean serverReceivedHandshake; - private WebSocketServerProtocolHandler.HandshakeComplete serverHandshakeComplete; - private boolean clientReceivedHandshake; - private boolean clientReceivedMessage; - private boolean serverReceivedCloseHandshake; - private boolean clientForceClosed; - private boolean clientHandshakeTimeout; - - private final class CloseNoOpServerProtocolHandler extends WebSocketServerProtocolHandler { - CloseNoOpServerProtocolHandler(String websocketPath) { - super(WebSocketServerProtocolConfig.newBuilder() - .websocketPath(websocketPath) - .allowExtensions(false) - .sendCloseFrame(null) - .build()); - } - - @Override - protected void decode(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception { - if (frame instanceof CloseWebSocketFrame) { - serverReceivedCloseHandshake = true; - return; - } - super.decode(ctx, frame); - } - } - - @BeforeEach - public void setUp() { - serverReceivedHandshake = false; - serverHandshakeComplete = null; - clientReceivedHandshake = false; - clientReceivedMessage = false; - serverReceivedCloseHandshake = false; - clientForceClosed = false; - clientHandshakeTimeout = false; - } - - @Test - public void testHandover() throws Exception { - EmbeddedChannel serverChannel = createServerChannel(new SimpleChannelInboundHandler() { - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { - if (evt == ServerHandshakeStateEvent.HANDSHAKE_COMPLETE) { - serverReceivedHandshake = true; - // immediately send a message to the client on connect - ctx.writeAndFlush(new TextWebSocketFrame("abc")); - } else if (evt instanceof WebSocketServerProtocolHandler.HandshakeComplete) { - serverHandshakeComplete = (WebSocketServerProtocolHandler.HandshakeComplete) evt; - } - } - @Override - protected void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { - } - }); - - EmbeddedChannel clientChannel = createClientChannel(new SimpleChannelInboundHandler() { - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { - if (evt == ClientHandshakeStateEvent.HANDSHAKE_COMPLETE) { - clientReceivedHandshake = true; - } - } - @Override - protected void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { - if (msg instanceof TextWebSocketFrame) { - clientReceivedMessage = true; - } - } - }); - - // Transfer the handshake from the client to the server - transferAllDataWithMerge(clientChannel, serverChannel); - assertTrue(serverReceivedHandshake); - assertNotNull(serverHandshakeComplete); - assertEquals("/test", serverHandshakeComplete.requestUri()); - assertEquals(8, serverHandshakeComplete.requestHeaders().size()); - assertEquals("test-proto-2", serverHandshakeComplete.selectedSubprotocol()); - - // Transfer the handshake response and the websocket message to the client - transferAllDataWithMerge(serverChannel, clientChannel); - assertTrue(clientReceivedHandshake); - assertTrue(clientReceivedMessage); - } - - @Test - public void testClientHandshakeTimeout() throws Throwable { - EmbeddedChannel serverChannel = createServerChannel(new SimpleChannelInboundHandler() { - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { - if (evt == ServerHandshakeStateEvent.HANDSHAKE_COMPLETE) { - serverReceivedHandshake = true; - // immediately send a message to the client on connect - ctx.writeAndFlush(new TextWebSocketFrame("abc")); - } else if (evt instanceof WebSocketServerProtocolHandler.HandshakeComplete) { - serverHandshakeComplete = (WebSocketServerProtocolHandler.HandshakeComplete) evt; - } - } - - @Override - protected void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { - } - }); - - EmbeddedChannel clientChannel = createClientChannel(new SimpleChannelInboundHandler() { - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { - if (evt == ClientHandshakeStateEvent.HANDSHAKE_COMPLETE) { - clientReceivedHandshake = true; - } else if (evt == ClientHandshakeStateEvent.HANDSHAKE_TIMEOUT) { - clientHandshakeTimeout = true; - } - } - - @Override - protected void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { - if (msg instanceof TextWebSocketFrame) { - clientReceivedMessage = true; - } - } - }, 100); - // Client send the handshake request to server - transferAllDataWithMerge(clientChannel, serverChannel); - // Server do not send the response back - // transferAllDataWithMerge(serverChannel, clientChannel); - WebSocketClientProtocolHandshakeHandler handshakeHandler = - (WebSocketClientProtocolHandshakeHandler) clientChannel - .pipeline().get(WebSocketClientProtocolHandshakeHandler.class.getName()); - - while (!handshakeHandler.getHandshakeFuture().isDone()) { - Thread.sleep(10); - // We need to run all pending tasks as the handshake timeout is scheduled on the EventLoop. - clientChannel.runScheduledPendingTasks(); - } - assertTrue(clientHandshakeTimeout); - assertFalse(clientReceivedHandshake); - assertFalse(clientReceivedMessage); - // Should throw WebSocketHandshakeException - try { - assertTrue(assertThrows(CompletionException.class, - () -> handshakeHandler.getHandshakeFuture().syncUninterruptibly()) - .getCause() instanceof WebSocketHandshakeException); - } finally { - serverChannel.finishAndReleaseAll(); - } - } - - /** - * Tests a scenario when channel is closed while the handshake is in progress. Validates that the handshake - * future is notified in such cases. - */ - @Test - public void testHandshakeFutureIsNotifiedOnChannelClose() throws Exception { - EmbeddedChannel clientChannel = createClientChannel(null); - EmbeddedChannel serverChannel = createServerChannel(null); - - try { - // Start handshake from client to server but don't complete the handshake for the purpose of this test. - transferAllDataWithMerge(clientChannel, serverChannel); - - final WebSocketClientProtocolHandler clientWsHandler = - clientChannel.pipeline().get(WebSocketClientProtocolHandler.class); - final WebSocketClientProtocolHandshakeHandler clientWsHandshakeHandler = - clientChannel.pipeline().get(WebSocketClientProtocolHandshakeHandler.class); - - final ChannelHandlerContext ctx = clientChannel.pipeline().context(WebSocketClientProtocolHandler.class); - - // Close the channel while the handshake is in progress. The channel could be closed before the handshake is - // complete due to a number of varied reasons. To reproduce the test scenario for this test case, - // we would manually close the channel. - clientWsHandler.close(ctx); - - // At this stage handshake is incomplete but the handshake future should be completed exceptionally since - // channel is closed. - assertTrue(clientWsHandshakeHandler.getHandshakeFuture().isDone()); - } finally { - serverChannel.finishAndReleaseAll(); - clientChannel.finishAndReleaseAll(); - } - } - - @Test - @Timeout(value = 10000, unit = TimeUnit.MILLISECONDS) - public void testClientHandshakerForceClose() throws Exception { - final WebSocketClientHandshaker handshaker = WebSocketClientHandshakerFactory.newHandshaker( - new URI("ws://localhost:1234/test"), WebSocketVersion.V13, null, true, - EmptyHttpHeaders.INSTANCE, Integer.MAX_VALUE, true, false, 20); - - EmbeddedChannel serverChannel = createServerChannel( - new CloseNoOpServerProtocolHandler("/test"), - new SimpleChannelInboundHandler() { - @Override - protected void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { - } - }); - - EmbeddedChannel clientChannel = createClientChannel(handshaker, new SimpleChannelInboundHandler() { - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { - if (evt == ClientHandshakeStateEvent.HANDSHAKE_COMPLETE) { - ctx.channel().closeFuture().addListener(future -> clientForceClosed = true); - handshaker.close(ctx.channel(), new CloseWebSocketFrame()); - } - } - @Override - protected void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { - } - }); - - // Transfer the handshake from the client to the server - transferAllDataWithMerge(clientChannel, serverChannel); - // Transfer the handshake from the server to client - transferAllDataWithMerge(serverChannel, clientChannel); - - // Transfer closing handshake - transferAllDataWithMerge(clientChannel, serverChannel); - assertTrue(serverReceivedCloseHandshake); - // Should not be closed yet as we disabled closing the connection on the server - assertFalse(clientForceClosed); - - while (!clientForceClosed) { - Thread.sleep(10); - // We need to run all pending tasks as the force close timeout is scheduled on the EventLoop. - clientChannel.runPendingTasks(); - } - - // clientForceClosed would be set to TRUE after any close, - // so check here that force close timeout was actually fired - assertTrue(handshaker.isForceCloseComplete()); - - // Both should be empty - assertFalse(serverChannel.finishAndReleaseAll()); - assertFalse(clientChannel.finishAndReleaseAll()); - } - - /** - * Transfers all pending data from the source channel into the destination channel.
- * Merges all data into a single buffer before transmission into the destination. - * @param srcChannel The source channel - * @param dstChannel The destination channel - */ - private static void transferAllDataWithMerge(EmbeddedChannel srcChannel, EmbeddedChannel dstChannel) { - ByteBuf mergedBuffer = null; - for (;;) { - Object srcData = srcChannel.readOutbound(); - - if (srcData != null) { - assertTrue(srcData instanceof ByteBuf); - ByteBuf srcBuf = (ByteBuf) srcData; - try { - if (mergedBuffer == null) { - mergedBuffer = Unpooled.buffer(); - } - mergedBuffer.writeBytes(srcBuf); - } finally { - srcBuf.release(); - } - } else { - break; - } - } - - if (mergedBuffer != null) { - dstChannel.writeInbound(mergedBuffer); - } - } - - private static EmbeddedChannel createClientChannel(ChannelHandler handler) throws Exception { - return createClientChannel(handler, WebSocketClientProtocolConfig.newBuilder() - .webSocketUri("ws://localhost:1234/test") - .subprotocol("test-proto-2") - .build()); - } - - private static EmbeddedChannel createClientChannel(ChannelHandler handler, long timeoutMillis) throws Exception { - return createClientChannel(handler, WebSocketClientProtocolConfig.newBuilder() - .webSocketUri("ws://localhost:1234/test") - .subprotocol("test-proto-2") - .handshakeTimeoutMillis(timeoutMillis) - .build()); - } - - private static EmbeddedChannel createClientChannel(ChannelHandler handler, WebSocketClientProtocolConfig config) { - return new EmbeddedChannel( - new HttpClientCodec(), - new HttpObjectAggregator(8192), - new WebSocketClientProtocolHandler(config), - handler); - } - - private static EmbeddedChannel createClientChannel(WebSocketClientHandshaker handshaker, - ChannelHandler handler) throws Exception { - return new EmbeddedChannel( - new HttpClientCodec(), - new HttpObjectAggregator(8192), - // Note that we're switching off close frames handling on purpose to test forced close on timeout. - new WebSocketClientProtocolHandler(handshaker, false, false), - handler); - } - - private static EmbeddedChannel createServerChannel(ChannelHandler handler) { - return createServerChannel( - new WebSocketServerProtocolHandler("/test", "test-proto-1, test-proto-2", false), - handler); - } - - private static EmbeddedChannel createServerChannel(WebSocketServerProtocolHandler webSocketHandler, - ChannelHandler handler) { - return new EmbeddedChannel( - new HttpServerCodec(), - new HttpObjectAggregator(8192), - webSocketHandler, - handler); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketProtocolHandlerTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketProtocolHandlerTest.java deleted file mode 100644 index b88c460e0a..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketProtocolHandlerTest.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.http.websocketx; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.flow.FlowControlHandler; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.concurrent.Future; -import org.hamcrest.Matchers; -import org.junit.jupiter.api.Test; - -import java.util.concurrent.atomic.AtomicReference; - -import static io.netty.util.CharsetUtil.UTF_8; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** - * Tests common, abstract class functionality in {@link WebSocketClientProtocolHandler}. - */ -public class WebSocketProtocolHandlerTest { - - @Test - public void testPingFrame() { - ByteBuf pingData = Unpooled.copiedBuffer("Hello, world", UTF_8); - EmbeddedChannel channel = new EmbeddedChannel(new WebSocketProtocolHandler() { }); - - PingWebSocketFrame inputMessage = new PingWebSocketFrame(pingData); - assertFalse(channel.writeInbound(inputMessage)); // the message was not propagated inbound - - // a Pong frame was written to the channel - PongWebSocketFrame response = channel.readOutbound(); - assertEquals(pingData, response.content()); - - pingData.release(); - assertFalse(channel.finish()); - } - - @Test - public void testPingPongFlowControlWhenAutoReadIsDisabled() { - String text1 = "Hello, world #1"; - String text2 = "Hello, world #2"; - String text3 = "Hello, world #3"; - String text4 = "Hello, world #4"; - - EmbeddedChannel channel = new EmbeddedChannel(); - channel.config().setAutoRead(false); - channel.pipeline().addLast(new FlowControlHandler()); - channel.pipeline().addLast(new WebSocketProtocolHandler() { }); - - // When - assertFalse(channel.writeInbound( - new PingWebSocketFrame(Unpooled.copiedBuffer(text1, UTF_8)), - new TextWebSocketFrame(text2), - new TextWebSocketFrame(text3), - new PingWebSocketFrame(Unpooled.copiedBuffer(text4, UTF_8)) - )); - - // Then - no messages were handled or propagated - assertNull(channel.readInbound()); - assertNull(channel.readOutbound()); - - // When - channel.read(); - - // Then - pong frame was written to the outbound - PongWebSocketFrame response1 = channel.readOutbound(); - assertEquals(text1, response1.content().toString(UTF_8)); - - // And - one requested message was handled and propagated inbound - TextWebSocketFrame message2 = channel.readInbound(); - assertEquals(text2, message2.text()); - - // And - no more messages were handled or propagated - assertNull(channel.readInbound()); - assertNull(channel.readOutbound()); - - // When - channel.read(); - - // Then - one requested message was handled and propagated inbound - TextWebSocketFrame message3 = channel.readInbound(); - assertEquals(text3, message3.text()); - - // And - no more messages were handled or propagated - // Precisely, ping frame 'text4' was NOT read or handled. - // It would be handle ONLY on the next 'channel.read()' call. - assertNull(channel.readInbound()); - assertNull(channel.readOutbound()); - - // Cleanup - response1.release(); - message2.release(); - message3.release(); - assertFalse(channel.finish()); - } - - @Test - public void testPongFrameDropFrameFalse() { - EmbeddedChannel channel = new EmbeddedChannel(new WebSocketProtocolHandler(false) { }); - - PongWebSocketFrame pingResponse = new PongWebSocketFrame(); - assertTrue(channel.writeInbound(pingResponse)); - - assertPropagatedInbound(pingResponse, channel); - - pingResponse.release(); - assertFalse(channel.finish()); - } - - @Test - public void testPongFrameDropFrameTrue() { - EmbeddedChannel channel = new EmbeddedChannel(new WebSocketProtocolHandler(true) { }); - - PongWebSocketFrame pingResponse = new PongWebSocketFrame(); - assertFalse(channel.writeInbound(pingResponse)); // message was not propagated inbound - } - - @Test - public void testTextFrame() { - EmbeddedChannel channel = new EmbeddedChannel(new WebSocketProtocolHandler() { }); - - TextWebSocketFrame textFrame = new TextWebSocketFrame(); - assertTrue(channel.writeInbound(textFrame)); - - assertPropagatedInbound(textFrame, channel); - - textFrame.release(); - assertFalse(channel.finish()); - } - - @Test - public void testTimeout() throws Exception { - final AtomicReference> ref = new AtomicReference<>(); - WebSocketProtocolHandler handler = new WebSocketProtocolHandler( - false, WebSocketCloseStatus.NORMAL_CLOSURE, 1) { }; - EmbeddedChannel channel = new EmbeddedChannel(new ChannelHandler() { - @Override - public Future write(ChannelHandlerContext ctx, Object msg) { - Future future = ctx.newPromise().asFuture(); - ref.set(future); - ReferenceCountUtil.release(msg); - return future; - } - }, handler); - - Future future = channel.writeAndFlush(new CloseWebSocketFrame()); - ChannelHandlerContext ctx = channel.pipeline().context(WebSocketProtocolHandler.class); - handler.close(ctx); - - do { - Thread.sleep(10); - channel.runPendingTasks(); - } while (!future.isDone()); - - assertThat(future.cause(), Matchers.instanceOf(WebSocketHandshakeException.class)); - assertFalse(ref.get().isDone()); - assertFalse(channel.finish()); - } - - /** - * Asserts that a message was propagated inbound through the channel. - */ - private static void assertPropagatedInbound(T message, EmbeddedChannel channel) { - T propagatedResponse = channel.readInbound(); - assertEquals(message, propagatedResponse); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketRequestBuilder.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketRequestBuilder.java deleted file mode 100644 index 206db33aff..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketRequestBuilder.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version - * 2.0 (the "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpVersion; - -import static io.netty.handler.codec.http.HttpVersion.*; - -public class WebSocketRequestBuilder { - - private HttpVersion httpVersion; - private HttpMethod method; - private String uri; - private String host; - private String upgrade; - private String connection; - private String key; - private String origin; - private WebSocketVersion version; - - public WebSocketRequestBuilder httpVersion(HttpVersion httpVersion) { - this.httpVersion = httpVersion; - return this; - } - - public WebSocketRequestBuilder method(HttpMethod method) { - this.method = method; - return this; - } - - public WebSocketRequestBuilder uri(CharSequence uri) { - if (uri == null) { - this.uri = null; - } else { - this.uri = uri.toString(); - } - return this; - } - - public WebSocketRequestBuilder host(CharSequence host) { - if (host == null) { - this.host = null; - } else { - this.host = host.toString(); - } - return this; - } - - public WebSocketRequestBuilder upgrade(CharSequence upgrade) { - if (upgrade == null) { - this.upgrade = null; - } else { - this.upgrade = upgrade.toString(); - } - return this; - } - - public WebSocketRequestBuilder connection(CharSequence connection) { - if (connection == null) { - this.connection = null; - } else { - this.connection = connection.toString(); - } - return this; - } - - public WebSocketRequestBuilder key(CharSequence key) { - if (key == null) { - this.key = null; - } else { - this.key = key.toString(); - } - return this; - } - - public WebSocketRequestBuilder origin(CharSequence origin) { - if (origin == null) { - this.origin = null; - } else { - this.origin = origin.toString(); - } - return this; - } - - public WebSocketRequestBuilder version13() { - version = WebSocketVersion.V13; - return this; - } - - public WebSocketRequestBuilder version8() { - version = WebSocketVersion.V08; - return this; - } - - public WebSocketRequestBuilder version00() { - version = null; - return this; - } - - public WebSocketRequestBuilder noVersion() { - return this; - } - - public FullHttpRequest build() { - FullHttpRequest req = new DefaultFullHttpRequest(httpVersion, method, uri); - HttpHeaders headers = req.headers(); - - if (host != null) { - headers.set(HttpHeaderNames.HOST, host); - } - if (upgrade != null) { - headers.set(HttpHeaderNames.UPGRADE, upgrade); - } - if (connection != null) { - headers.set(HttpHeaderNames.CONNECTION, connection); - } - if (key != null) { - headers.set(HttpHeaderNames.SEC_WEBSOCKET_KEY, key); - } - if (origin != null) { - if (version == WebSocketVersion.V13 || version == WebSocketVersion.V00) { - headers.set(HttpHeaderNames.ORIGIN, origin); - } else { - headers.set(HttpHeaderNames.SEC_WEBSOCKET_ORIGIN, origin); - } - } - if (version != null) { - headers.set(HttpHeaderNames.SEC_WEBSOCKET_VERSION, version.toHttpHeaderValue()); - } - return req; - } - - public static HttpRequest successful() { - return new WebSocketRequestBuilder().httpVersion(HTTP_1_1) - .method(HttpMethod.GET) - .uri("/test") - .host("server.example.com") - .upgrade(HttpHeaderValues.WEBSOCKET) - .key("dGhlIHNhbXBsZSBub25jZQ==") - .origin("http://example.com") - .version13() - .build(); - } -} 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 deleted file mode 100644 index 2283dc1b72..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00Test.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.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.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpObjectAggregator; -import io.netty.handler.codec.http.HttpRequestDecoder; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.HttpResponseDecoder; -import io.netty.handler.codec.http.HttpResponseEncoder; -import io.netty.handler.codec.http.LastHttpContent; -import io.netty.util.CharsetUtil; -import org.junit.jupiter.api.Test; - -import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.fail; - -public class WebSocketServerHandshaker00Test extends WebSocketServerHandshakerTest { - - @Override - protected WebSocketServerHandshaker newHandshaker(String webSocketURL, String subprotocols, - WebSocketDecoderConfig decoderConfig) { - return new WebSocketServerHandshaker00(webSocketURL, subprotocols, decoderConfig); - } - - @Override - protected WebSocketVersion webSocketVersion() { - return WebSocketVersion.V00; - } - - @Test - public void testPerformOpeningHandshake() { - testPerformOpeningHandshake0(true); - } - - @Test - 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()); - - 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.ORIGIN, "http://example.com"); - req.headers().set(HttpHeaderNames.SEC_WEBSOCKET_KEY1, "4 @1 46546xW%0l 1 5"); - req.headers().set(HttpHeaderNames.SEC_WEBSOCKET_KEY2, "12998 5 Y3 1 .P00"); - req.headers().set(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL, "chat, superchat"); - - if (subProtocol) { - new WebSocketServerHandshaker00( - "ws://example.com/chat", "chat", Integer.MAX_VALUE).handshake(ch, req); - } else { - new WebSocketServerHandshaker00( - "ws://example.com/chat", null, Integer.MAX_VALUE).handshake(ch, req); - } - - EmbeddedChannel ch2 = new EmbeddedChannel(new HttpResponseDecoder()); - ch2.writeInbound((ByteBuf) ch.readOutbound()); - HttpResponse res = ch2.readInbound(); - - assertEquals("ws://example.com/chat", res.headers().get(HttpHeaderNames.SEC_WEBSOCKET_LOCATION)); - - if (subProtocol) { - assertEquals("chat", res.headers().get(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL)); - } else { - assertNull(res.headers().get(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL)); - } - LastHttpContent content = ch2.readInbound(); - - assertEquals("8jKS'y:G*Co,Wxa-", content.content().toString(CharsetUtil.US_ASCII)); - content.release(); - req.release(); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker07Test.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker07Test.java deleted file mode 100644 index 0a13f42915..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker07Test.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -public class WebSocketServerHandshaker07Test extends WebSocketServerHandshakerTest { - - @Override - protected WebSocketServerHandshaker newHandshaker(String webSocketURL, String subprotocols, - WebSocketDecoderConfig decoderConfig) { - return new WebSocketServerHandshaker07(webSocketURL, subprotocols, decoderConfig); - } - - @Override - protected WebSocketVersion webSocketVersion() { - return WebSocketVersion.V07; - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08Test.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08Test.java deleted file mode 100644 index 153d918759..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08Test.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpObjectAggregator; -import io.netty.handler.codec.http.HttpRequestDecoder; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.HttpResponseDecoder; -import io.netty.handler.codec.http.HttpResponseEncoder; -import io.netty.util.ReferenceCountUtil; -import org.junit.jupiter.api.Test; - -import static io.netty.handler.codec.http.HttpVersion.*; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; - -public class WebSocketServerHandshaker08Test extends WebSocketServerHandshakerTest { - - @Override - protected WebSocketServerHandshaker newHandshaker(String webSocketURL, String subprotocols, - WebSocketDecoderConfig decoderConfig) { - return new WebSocketServerHandshaker08(webSocketURL, subprotocols, decoderConfig); - } - - @Override - protected WebSocketVersion webSocketVersion() { - return WebSocketVersion.V08; - } - - @Test - public void testPerformOpeningHandshake() { - testPerformOpeningHandshake0(true); - } - - @Test - public void testPerformOpeningHandshakeSubProtocolNotSupported() { - testPerformOpeningHandshake0(false); - } - - private static void testPerformOpeningHandshake0(boolean subProtocol) { - EmbeddedChannel ch = new EmbeddedChannel( - new HttpObjectAggregator(42), new HttpRequestDecoder(), new HttpResponseEncoder()); - - FullHttpRequest req = new DefaultFullHttpRequest(HTTP_1_1, HttpMethod.GET, "/chat"); - 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_KEY, "dGhlIHNhbXBsZSBub25jZQ=="); - req.headers().set(HttpHeaderNames.SEC_WEBSOCKET_ORIGIN, "http://example.com"); - req.headers().set(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL, "chat, superchat"); - req.headers().set(HttpHeaderNames.SEC_WEBSOCKET_VERSION, "8"); - - if (subProtocol) { - new WebSocketServerHandshaker08( - "ws://example.com/chat", "chat", false, Integer.MAX_VALUE, false).handshake(ch, req); - } else { - new WebSocketServerHandshaker08( - "ws://example.com/chat", null, false, Integer.MAX_VALUE, false).handshake(ch, req); - } - - ByteBuf resBuf = ch.readOutbound(); - - EmbeddedChannel ch2 = new EmbeddedChannel(new HttpResponseDecoder()); - ch2.writeInbound(resBuf); - HttpResponse res = ch2.readInbound(); - - assertEquals( - "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", res.headers().get(HttpHeaderNames.SEC_WEBSOCKET_ACCEPT)); - if (subProtocol) { - assertEquals("chat", res.headers().get(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL)); - } else { - assertNull(res.headers().get(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL)); - } - ReferenceCountUtil.release(res); - req.release(); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13Test.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13Test.java deleted file mode 100644 index 5dc6590a7f..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13Test.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandler; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpObjectAggregator; -import io.netty.handler.codec.http.HttpRequestDecoder; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.HttpResponseDecoder; -import io.netty.handler.codec.http.HttpResponseEncoder; -import io.netty.handler.codec.http.HttpServerCodec; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.ReferenceCounted; -import org.hamcrest.CoreMatchers; -import org.junit.jupiter.api.Test; - -import java.util.Iterator; - -import static io.netty.handler.codec.http.HttpVersion.*; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.fail; - -public class WebSocketServerHandshaker13Test extends WebSocketServerHandshakerTest { - - @Override - protected WebSocketServerHandshaker newHandshaker(String webSocketURL, String subprotocols, - WebSocketDecoderConfig decoderConfig) { - return new WebSocketServerHandshaker13(webSocketURL, subprotocols, decoderConfig); - } - - @Override - protected WebSocketVersion webSocketVersion() { - return WebSocketVersion.V13; - } - - @Test - public void testPerformOpeningHandshake() { - testPerformOpeningHandshake0(true); - } - - @Test - public void testPerformOpeningHandshakeSubProtocolNotSupported() { - testPerformOpeningHandshake0(false); - } - - private static void testPerformOpeningHandshake0(boolean subProtocol) { - EmbeddedChannel ch = new EmbeddedChannel( - new HttpObjectAggregator(42), new HttpResponseEncoder(), new HttpRequestDecoder()); - - if (subProtocol) { - testUpgrade0(ch, new WebSocketServerHandshaker13( - "ws://example.com/chat", "chat", false, Integer.MAX_VALUE, false)); - } else { - testUpgrade0(ch, new WebSocketServerHandshaker13( - "ws://example.com/chat", null, false, Integer.MAX_VALUE, false)); - } - assertFalse(ch.finish()); - } - - @Test - public void testCloseReasonWithEncoderAndDecoder() { - testCloseReason0(new HttpResponseEncoder(), new HttpRequestDecoder()); - } - - @Test - public void testCloseReasonWithCodec() { - testCloseReason0(new HttpServerCodec()); - } - - private static void testCloseReason0(ChannelHandler... handlers) { - EmbeddedChannel ch = new EmbeddedChannel( - new HttpObjectAggregator(42)); - ch.pipeline().addLast(handlers); - testUpgrade0(ch, new WebSocketServerHandshaker13("ws://example.com/chat", "chat", - WebSocketDecoderConfig.newBuilder().maxFramePayloadLength(4).closeOnProtocolViolation(true).build())); - - ch.writeOutbound(new BinaryWebSocketFrame(Unpooled.wrappedBuffer(new byte[8]))); - ByteBuf buffer = ch.readOutbound(); - try { - ch.writeInbound(buffer); - fail(); - } catch (CorruptedWebSocketFrameException expected) { - // expected - } - ReferenceCounted closeMessage = ch.readOutbound(); - assertThat(closeMessage, CoreMatchers.instanceOf(ByteBuf.class)); - closeMessage.release(); - assertFalse(ch.finish()); - } - - private static void testUpgrade0(EmbeddedChannel ch, WebSocketServerHandshaker13 handshaker) { - FullHttpRequest req = new DefaultFullHttpRequest(HTTP_1_1, HttpMethod.GET, "/chat"); - 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_KEY, "dGhlIHNhbXBsZSBub25jZQ=="); - req.headers().set(HttpHeaderNames.SEC_WEBSOCKET_ORIGIN, "http://example.com"); - req.headers().set(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL, "chat, superchat"); - req.headers().set(HttpHeaderNames.SEC_WEBSOCKET_VERSION, "13"); - - handshaker.handshake(ch, req); - - ByteBuf resBuf = ch.readOutbound(); - - EmbeddedChannel ch2 = new EmbeddedChannel(new HttpResponseDecoder()); - ch2.writeInbound(resBuf); - HttpResponse res = ch2.readInbound(); - - assertEquals( - "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", res.headers().get(HttpHeaderNames.SEC_WEBSOCKET_ACCEPT)); - Iterator subProtocols = handshaker.subprotocols().iterator(); - if (subProtocols.hasNext()) { - assertEquals(subProtocols.next(), - res.headers().get(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL)); - } else { - assertNull(res.headers().get(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL)); - } - ReferenceCountUtil.release(res); - req.release(); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakerFactoryTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakerFactoryTest.java deleted file mode 100644 index f978e92220..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakerFactoryTest.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.http.websocketx; - -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpUtil; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.util.ReferenceCountUtil; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - -public class WebSocketServerHandshakerFactoryTest { - - @Test - public void testUnsupportedVersion() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(); - WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ch); - ch.runPendingTasks(); - Object msg = ch.readOutbound(); - - if (!(msg instanceof FullHttpResponse)) { - fail("Got wrong response " + msg); - } - FullHttpResponse response = (FullHttpResponse) msg; - - assertEquals(HttpResponseStatus.UPGRADE_REQUIRED, response.status()); - assertEquals(WebSocketVersion.V13.toHttpHeaderValue(), - response.headers().get(HttpHeaderNames.SEC_WEBSOCKET_VERSION)); - assertTrue(HttpUtil.isContentLengthSet(response)); - assertEquals(0, HttpUtil.getContentLength(response)); - - ReferenceCountUtil.release(response); - assertFalse(ch.finish()); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakerTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakerTest.java deleted file mode 100644 index 754f17ca54..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakerTest.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.DefaultHttpHeaders; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpVersion; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public abstract class WebSocketServerHandshakerTest { - - protected abstract WebSocketServerHandshaker newHandshaker(String webSocketURL, String subprotocols, - WebSocketDecoderConfig decoderConfig); - - protected abstract WebSocketVersion webSocketVersion(); - - @Test - public void testDuplicateHandshakeResponseHeaders() { - WebSocketServerHandshaker serverHandshaker = newHandshaker("ws://example.com/chat", - "chat", WebSocketDecoderConfig.DEFAULT); - FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/chat"); - request.headers() - .set(HttpHeaderNames.HOST, "example.com") - .set(HttpHeaderNames.ORIGIN, "example.com") - .set(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET) - .set(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE) - .set(HttpHeaderNames.SEC_WEBSOCKET_KEY, "dGhlIHNhbXBsZSBub25jZQ==") - .set(HttpHeaderNames.SEC_WEBSOCKET_ORIGIN, "http://example.com") - .set(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL, "chat, superchat") - .set(HttpHeaderNames.WEBSOCKET_PROTOCOL, "chat, superchat") - .set(HttpHeaderNames.SEC_WEBSOCKET_VERSION, webSocketVersion().toAsciiString()); - HttpHeaders customResponseHeaders = new DefaultHttpHeaders(); - // set duplicate required headers and one custom - customResponseHeaders - .set(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE) - .set(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET) - .set(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL, "superchat") - .set(HttpHeaderNames.WEBSOCKET_PROTOCOL, "superchat") - .set("custom", "header"); - - if (webSocketVersion() != WebSocketVersion.V00) { - customResponseHeaders.set(HttpHeaderNames.SEC_WEBSOCKET_ACCEPT, "12345"); - } - - FullHttpResponse response = null; - try { - response = serverHandshaker.newHandshakeResponse(request, customResponseHeaders); - HttpHeaders responseHeaders = response.headers(); - - assertEquals(1, responseHeaders.getAll(HttpHeaderNames.CONNECTION).size()); - assertEquals(1, responseHeaders.getAll(HttpHeaderNames.UPGRADE).size()); - assertTrue(responseHeaders.containsValue("custom", "header", true)); - - if (webSocketVersion() != WebSocketVersion.V00) { - assertFalse(responseHeaders.containsValue(HttpHeaderNames.SEC_WEBSOCKET_ACCEPT, "12345", false)); - assertEquals(1, responseHeaders.getAll(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL).size()); - assertEquals("chat", responseHeaders.get(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL)); - } else { - assertEquals(1, responseHeaders.getAll(HttpHeaderNames.WEBSOCKET_PROTOCOL).size()); - assertEquals("chat", responseHeaders.get(HttpHeaderNames.WEBSOCKET_PROTOCOL)); - } - } finally { - request.release(); - if (response != null) { - response.release(); - } - } - } - - @Test - public void testWebSocketServerHandshakeException() { - WebSocketServerHandshaker serverHandshaker = newHandshaker("ws://example.com/chat", - "chat", WebSocketDecoderConfig.DEFAULT); - - FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, - "ws://example.com/chat"); - request.headers().set("x-client-header", "value"); - try { - serverHandshaker.handshake(null, request, null); - } catch (WebSocketServerHandshakeException exception) { - assertNotNull(exception.getMessage()); - assertEquals(request.headers(), exception.request().headers()); - assertEquals(HttpMethod.GET, exception.request().method()); - } finally { - request.release(); - } - } -} - diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandlerTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandlerTest.java deleted file mode 100644 index c1d327ec74..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandlerTest.java +++ /dev/null @@ -1,486 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version - * 2.0 (the "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http.websocketx; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.embedded.EmbeddedChannel; -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.HttpHeaderValues; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpObjectAggregator; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpRequestDecoder; -import io.netty.handler.codec.http.HttpResponseEncoder; -import io.netty.handler.codec.http.HttpServerCodec; -import io.netty.util.CharsetUtil; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.concurrent.Future; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.ArrayDeque; -import java.util.Queue; - -import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST; -import static io.netty.handler.codec.http.HttpResponseStatus.SWITCHING_PROTOCOLS; -import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class WebSocketServerProtocolHandlerTest { - - private final Queue responses = new ArrayDeque<>(); - - @BeforeEach - public void setUp() { - responses.clear(); - } - - @Test - public void testHttpUpgradeRequest() { - EmbeddedChannel ch = createChannel(new MockOutboundHandler()); - ChannelHandlerContext handshakerCtx = ch.pipeline().context(WebSocketServerProtocolHandshakeHandler.class); - writeUpgradeRequest(ch); - - FullHttpResponse response = responses.remove(); - assertEquals(SWITCHING_PROTOCOLS, response.status()); - response.release(); - assertNotNull(WebSocketServerProtocolHandler.getHandshaker(handshakerCtx.channel())); - assertFalse(ch.finish()); - } - - @Test - public void testWebSocketServerProtocolHandshakeHandlerRemovedAfterHandshake() { - EmbeddedChannel ch = createChannel(new MockOutboundHandler()); - ChannelHandlerContext handshakerCtx = ch.pipeline().context(WebSocketServerProtocolHandshakeHandler.class); - ch.pipeline().addLast(new ChannelHandler() { - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { - if (evt instanceof WebSocketServerProtocolHandler.HandshakeComplete) { - // We should have removed the handler already. - ctx.executor().execute(() -> ctx.pipeline().context(WebSocketServerProtocolHandshakeHandler.class)); - } - } - }); - writeUpgradeRequest(ch); - - FullHttpResponse response = responses.remove(); - assertEquals(SWITCHING_PROTOCOLS, response.status()); - response.release(); - assertNotNull(WebSocketServerProtocolHandler.getHandshaker(handshakerCtx.channel())); - assertFalse(ch.finish()); - } - - @Test - public void testHttpUpgradeRequestInvalidUpgradeHeader() { - EmbeddedChannel ch = createChannel(); - FullHttpRequest httpRequestWithEntity = new WebSocketRequestBuilder().httpVersion(HTTP_1_1) - .method(HttpMethod.GET) - .uri("/test") - .connection("Upgrade") - .version00() - .upgrade("BogusSocket") - .build(); - - ch.writeInbound(httpRequestWithEntity); - - FullHttpResponse response = responses.remove(); - assertEquals(BAD_REQUEST, response.status()); - assertEquals("not a WebSocket handshake request: missing upgrade", getResponseMessage(response)); - response.release(); - assertFalse(ch.finish()); - } - - @Test - public void testHttpUpgradeRequestMissingWSKeyHeader() { - EmbeddedChannel ch = createChannel(); - HttpRequest httpRequest = new WebSocketRequestBuilder().httpVersion(HTTP_1_1) - .method(HttpMethod.GET) - .uri("/test") - .key(null) - .connection("Upgrade") - .upgrade(HttpHeaderValues.WEBSOCKET) - .version13() - .build(); - - ch.writeInbound(httpRequest); - - FullHttpResponse response = responses.remove(); - assertEquals(BAD_REQUEST, response.status()); - assertEquals("not a WebSocket request: missing key", getResponseMessage(response)); - response.release(); - assertFalse(ch.finish()); - } - - @Test - public void testCreateUTF8Validator() { - WebSocketServerProtocolConfig config = WebSocketServerProtocolConfig.newBuilder() - .websocketPath("/test") - .withUTF8Validator(true) - .build(); - - EmbeddedChannel ch = new EmbeddedChannel( - new WebSocketServerProtocolHandler(config), - new HttpRequestDecoder(), - new HttpResponseEncoder(), - new MockOutboundHandler()); - writeUpgradeRequest(ch); - - FullHttpResponse response = responses.remove(); - assertEquals(SWITCHING_PROTOCOLS, response.status()); - response.release(); - - assertNotNull(ch.pipeline().get(Utf8FrameValidator.class)); - } - - @Test - public void testDoNotCreateUTF8Validator() { - WebSocketServerProtocolConfig config = WebSocketServerProtocolConfig.newBuilder() - .websocketPath("/test") - .withUTF8Validator(false) - .build(); - - EmbeddedChannel ch = new EmbeddedChannel( - new WebSocketServerProtocolHandler(config), - new HttpRequestDecoder(), - new HttpResponseEncoder(), - new MockOutboundHandler()); - writeUpgradeRequest(ch); - - FullHttpResponse response = responses.remove(); - assertEquals(SWITCHING_PROTOCOLS, response.status()); - response.release(); - - assertNull(ch.pipeline().get(Utf8FrameValidator.class)); - } - - @Test - public void testHandleTextFrame() { - CustomTextFrameHandler customTextFrameHandler = new CustomTextFrameHandler(); - EmbeddedChannel ch = createChannel(customTextFrameHandler); - writeUpgradeRequest(ch); - - FullHttpResponse response = responses.remove(); - assertEquals(SWITCHING_PROTOCOLS, response.status()); - response.release(); - - if (ch.pipeline().context(HttpRequestDecoder.class) != null) { - // Removing the HttpRequestDecoder because we are writing a TextWebSocketFrame and thus - // decoding is not necessary. - ch.pipeline().remove(HttpRequestDecoder.class); - } - - ch.writeInbound(new TextWebSocketFrame("payload")); - - assertEquals("processed: payload", customTextFrameHandler.getContent()); - assertFalse(ch.finish()); - } - - @Test - public void testCheckWebSocketPathStartWithSlash() { - WebSocketRequestBuilder builder = new WebSocketRequestBuilder().httpVersion(HTTP_1_1) - .method(HttpMethod.GET) - .key(HttpHeaderNames.SEC_WEBSOCKET_KEY) - .connection("Upgrade") - .upgrade(HttpHeaderValues.WEBSOCKET) - .version13(); - - WebSocketServerProtocolConfig config = WebSocketServerProtocolConfig.newBuilder() - .websocketPath("/") - .checkStartsWith(true) - .build(); - - FullHttpResponse response; - - createChannel(config, null).writeInbound(builder.uri("/test").build()); - response = responses.remove(); - assertEquals(SWITCHING_PROTOCOLS, response.status()); - response.release(); - - createChannel(config, null).writeInbound(builder.uri("/?q=v").build()); - response = responses.remove(); - assertEquals(SWITCHING_PROTOCOLS, response.status()); - response.release(); - - createChannel(config, null).writeInbound(builder.uri("/").build()); - response = responses.remove(); - assertEquals(SWITCHING_PROTOCOLS, response.status()); - response.release(); - } - - @Test - public void testCheckValidWebSocketPath() { - HttpRequest httpRequest = new WebSocketRequestBuilder().httpVersion(HTTP_1_1) - .method(HttpMethod.GET) - .uri("/test") - .key(HttpHeaderNames.SEC_WEBSOCKET_KEY) - .connection("Upgrade") - .upgrade(HttpHeaderValues.WEBSOCKET) - .version13() - .build(); - - WebSocketServerProtocolConfig config = WebSocketServerProtocolConfig.newBuilder() - .websocketPath("/test") - .checkStartsWith(true) - .build(); - - EmbeddedChannel ch = new EmbeddedChannel( - new WebSocketServerProtocolHandler(config), - new HttpRequestDecoder(), - new HttpResponseEncoder(), - new MockOutboundHandler()); - ch.writeInbound(httpRequest); - - FullHttpResponse response = responses.remove(); - assertEquals(SWITCHING_PROTOCOLS, response.status()); - response.release(); - } - - @Test - public void testCheckInvalidWebSocketPath() { - HttpRequest httpRequest = new WebSocketRequestBuilder().httpVersion(HTTP_1_1) - .method(HttpMethod.GET) - .uri("/testabc") - .key(HttpHeaderNames.SEC_WEBSOCKET_KEY) - .connection("Upgrade") - .upgrade(HttpHeaderValues.WEBSOCKET) - .version13() - .build(); - - WebSocketServerProtocolConfig config = WebSocketServerProtocolConfig.newBuilder() - .websocketPath("/test") - .checkStartsWith(true) - .build(); - - EmbeddedChannel ch = new EmbeddedChannel( - new WebSocketServerProtocolHandler(config), - new HttpRequestDecoder(), - new HttpResponseEncoder(), - new MockOutboundHandler()); - ch.writeInbound(httpRequest); - - ChannelHandlerContext handshakerCtx = ch.pipeline().context(WebSocketServerProtocolHandshakeHandler.class); - assertNull(WebSocketServerProtocolHandler.getHandshaker(handshakerCtx.channel())); - } - - @Test - public void testExplicitCloseFrameSentWhenServerChannelClosed() throws Exception { - WebSocketCloseStatus closeStatus = WebSocketCloseStatus.ENDPOINT_UNAVAILABLE; - EmbeddedChannel client = createClient(); - EmbeddedChannel server = createServer(); - - assertFalse(server.writeInbound((ByteBuf) client.readOutbound())); - assertFalse(client.writeInbound((ByteBuf) server.readOutbound())); - - // When server channel closed with explicit close-frame - assertTrue(server.writeOutbound(new CloseWebSocketFrame(closeStatus))); - server.close(); - - // Then client receives provided close-frame - assertTrue(client.writeInbound((ByteBuf) server.readOutbound())); - assertFalse(server.isOpen()); - - CloseWebSocketFrame closeMessage = client.readInbound(); - assertEquals(closeMessage.statusCode(), closeStatus.code()); - closeMessage.release(); - - client.close(); - assertTrue(ReferenceCountUtil.release(client.readOutbound())); - assertFalse(client.finishAndReleaseAll()); - assertFalse(server.finishAndReleaseAll()); - } - - @Test - public void testCloseFrameSentWhenServerChannelClosedSilently() throws Exception { - EmbeddedChannel client = createClient(); - EmbeddedChannel server = createServer(); - - assertFalse(server.writeInbound((ByteBuf) client.readOutbound())); - assertFalse(client.writeInbound((ByteBuf) server.readOutbound())); - - // When server channel closed without explicit close-frame - server.close(); - - // Then client receives NORMAL_CLOSURE close-frame - assertTrue(client.writeInbound((ByteBuf) server.readOutbound())); - assertFalse(server.isOpen()); - - CloseWebSocketFrame closeMessage = client.readInbound(); - assertEquals(closeMessage.statusCode(), WebSocketCloseStatus.NORMAL_CLOSURE.code()); - closeMessage.release(); - - client.close(); - assertTrue(ReferenceCountUtil.release(client.readOutbound())); - assertFalse(client.finishAndReleaseAll()); - assertFalse(server.finishAndReleaseAll()); - } - - @Test - public void testExplicitCloseFrameSentWhenClientChannelClosed() throws Exception { - WebSocketCloseStatus closeStatus = WebSocketCloseStatus.INVALID_PAYLOAD_DATA; - EmbeddedChannel client = createClient(); - EmbeddedChannel server = createServer(); - - assertFalse(server.writeInbound((ByteBuf) client.readOutbound())); - assertFalse(client.writeInbound((ByteBuf) server.readOutbound())); - - // When client channel closed with explicit close-frame - assertTrue(client.writeOutbound(new CloseWebSocketFrame(closeStatus))); - client.close(); - - // Then client receives provided close-frame - assertFalse(server.writeInbound((ByteBuf) client.readOutbound())); - assertFalse(client.isOpen()); - assertFalse(server.isOpen()); - - CloseWebSocketFrame closeMessage = decode(server.readOutbound(), CloseWebSocketFrame.class); - assertEquals(closeMessage.statusCode(), closeStatus.code()); - closeMessage.release(); - - assertFalse(client.finishAndReleaseAll()); - assertFalse(server.finishAndReleaseAll()); - } - - @Test - public void testCloseFrameSentWhenClientChannelClosedSilently() throws Exception { - EmbeddedChannel client = createClient(); - EmbeddedChannel server = createServer(); - - assertFalse(server.writeInbound((ByteBuf) client.readOutbound())); - assertFalse(client.writeInbound((ByteBuf) server.readOutbound())); - - // When client channel closed without explicit close-frame - client.close(); - - // Then server receives NORMAL_CLOSURE close-frame - assertFalse(server.writeInbound((ByteBuf) client.readOutbound())); - assertFalse(client.isOpen()); - assertFalse(server.isOpen()); - - CloseWebSocketFrame closeMessage = decode(server.readOutbound(), CloseWebSocketFrame.class); - assertEquals(closeMessage, new CloseWebSocketFrame(WebSocketCloseStatus.NORMAL_CLOSURE)); - closeMessage.release(); - - assertFalse(client.finishAndReleaseAll()); - assertFalse(server.finishAndReleaseAll()); - } - - private EmbeddedChannel createClient(ChannelHandler... handlers) throws Exception { - WebSocketClientProtocolConfig clientConfig = WebSocketClientProtocolConfig.newBuilder() - .webSocketUri("http://test/test") - .dropPongFrames(false) - .handleCloseFrames(false) - .build(); - EmbeddedChannel ch = new EmbeddedChannel(false, false, - new HttpClientCodec(), - new HttpObjectAggregator(8192), - new WebSocketClientProtocolHandler(clientConfig) - ); - ch.pipeline().addLast(handlers); - ch.register(); - return ch; - } - - private EmbeddedChannel createServer(ChannelHandler... handlers) throws Exception { - WebSocketServerProtocolConfig serverConfig = WebSocketServerProtocolConfig.newBuilder() - .websocketPath("/test") - .dropPongFrames(false) - .build(); - EmbeddedChannel ch = new EmbeddedChannel(false, false, - new HttpServerCodec(), - new HttpObjectAggregator(8192), - new WebSocketServerProtocolHandler(serverConfig) - ); - ch.pipeline().addLast(handlers); - ch.register(); - return ch; - } - - @SuppressWarnings("SameParameterValue") - private T decode(ByteBuf input, Class clazz) { - EmbeddedChannel ch = new EmbeddedChannel(new WebSocket13FrameDecoder(true, false, 65536, true)); - assertTrue(ch.writeInbound(input)); - Object decoded = ch.readInbound(); - assertNotNull(decoded); - assertFalse(ch.finish()); - return clazz.cast(decoded); - } - - private EmbeddedChannel createChannel() { - return createChannel(null); - } - - private EmbeddedChannel createChannel(ChannelHandler handler) { - WebSocketServerProtocolConfig serverConfig = WebSocketServerProtocolConfig.newBuilder() - .websocketPath("/test") - .sendCloseFrame(null) - .build(); - return createChannel(serverConfig, handler); - } - - private EmbeddedChannel createChannel(WebSocketServerProtocolConfig serverConfig, ChannelHandler handler) { - return new EmbeddedChannel( - new WebSocketServerProtocolHandler(serverConfig), - new HttpRequestDecoder(), - new HttpResponseEncoder(), - new MockOutboundHandler(), - handler); - } - - private static void writeUpgradeRequest(EmbeddedChannel ch) { - ch.writeInbound(WebSocketRequestBuilder.successful()); - } - - private static String getResponseMessage(FullHttpResponse response) { - return response.content().toString(CharsetUtil.UTF_8); - } - - private class MockOutboundHandler implements ChannelHandler { - - @Override - public Future write(ChannelHandlerContext ctx, Object msg) { - responses.add((FullHttpResponse) msg); - return ctx.newSucceededFuture(); - } - - @Override - public void flush(ChannelHandlerContext ctx) { - } - } - - private static class CustomTextFrameHandler implements ChannelHandler { - private String content; - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - assertNull(content); - content = "processed: " + ((TextWebSocketFrame) msg).text(); - ReferenceCountUtil.release(msg); - } - - String getContent() { - return content; - } - } -} 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 deleted file mode 100644 index 42e900269a..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketUtf8FrameValidatorTest.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.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.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - -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()); - TextWebSocketFrame frame = new TextWebSocketFrame(Unpooled.copiedBuffer(data)); - try { - channel.writeInbound(frame); - fail(); - } catch (CorruptedFrameException e) { - // expected exception - } - assertTrue(channel.finish()); - ByteBuf buf = channel.readOutbound(); - assertNotNull(buf); - try { - assertFalse(buf.isReadable()); - } finally { - buf.release(); - } - assertNull(channel.readOutbound()); - assertEquals(0, frame.refCnt()); - } -} 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 deleted file mode 100644 index 6dc87e4c9d..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketUtilTest.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx; - -import org.junit.jupiter.api.Test; - -import java.util.concurrent.ThreadLocalRandom; - -import static org.junit.jupiter.api.Assertions.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 = ThreadLocalRandom.current().nextInt(min, max + 1); - 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); - } - } - -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketClientExtensionHandlerTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketClientExtensionHandlerTest.java deleted file mode 100644 index 37295b20ae..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketClientExtensionHandlerTest.java +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx.extensions; - -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.CodecException; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpResponse; - -import java.util.Collections; -import java.util.List; - -import org.junit.jupiter.api.Test; - -import static io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionTestUtil.*; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -public class WebSocketClientExtensionHandlerTest { - - WebSocketClientExtensionHandshaker mainHandshakerMock = - mock(WebSocketClientExtensionHandshaker.class, "mainHandshaker"); - WebSocketClientExtensionHandshaker fallbackHandshakerMock = - mock(WebSocketClientExtensionHandshaker.class, "fallbackHandshaker"); - WebSocketClientExtension mainExtensionMock = - mock(WebSocketClientExtension.class, "mainExtension"); - WebSocketClientExtension fallbackExtensionMock = - mock(WebSocketClientExtension.class, "fallbackExtension"); - - @Test - public void testMainSuccess() { - // initialize - when(mainHandshakerMock.newRequestData()). - thenReturn(new WebSocketExtensionData("main", Collections.emptyMap())); - when(mainHandshakerMock.handshakeExtension(any(WebSocketExtensionData.class))).thenReturn(mainExtensionMock); - when(fallbackHandshakerMock.newRequestData()). - thenReturn(new WebSocketExtensionData("fallback", Collections.emptyMap())); - when(mainExtensionMock.rsv()).thenReturn(WebSocketExtension.RSV1); - when(mainExtensionMock.newExtensionEncoder()).thenReturn(new DummyEncoder()); - when(mainExtensionMock.newExtensionDecoder()).thenReturn(new DummyDecoder()); - - // execute - EmbeddedChannel ch = new EmbeddedChannel(new WebSocketClientExtensionHandler( - mainHandshakerMock, fallbackHandshakerMock)); - - HttpRequest req = newUpgradeRequest(null); - ch.writeOutbound(req); - - HttpRequest req2 = ch.readOutbound(); - List reqExts = WebSocketExtensionUtil.extractExtensions( - req2.headers().get(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS)); - - HttpResponse res = newUpgradeResponse("main"); - ch.writeInbound(res); - - HttpResponse res2 = ch.readInbound(); - List resExts = WebSocketExtensionUtil.extractExtensions( - res2.headers().get(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS)); - - // test - assertEquals(2, reqExts.size()); - assertEquals("main", reqExts.get(0).name()); - assertEquals("fallback", reqExts.get(1).name()); - - assertEquals(1, resExts.size()); - assertEquals("main", resExts.get(0).name()); - assertTrue(resExts.get(0).parameters().isEmpty()); - assertNotNull(ch.pipeline().get(DummyDecoder.class)); - assertNotNull(ch.pipeline().get(DummyEncoder.class) != null); - - verify(mainHandshakerMock).newRequestData(); - verify(mainHandshakerMock).handshakeExtension(any(WebSocketExtensionData.class)); - verify(fallbackHandshakerMock).newRequestData(); - verify(mainExtensionMock, atLeastOnce()).rsv(); - verify(mainExtensionMock).newExtensionEncoder(); - verify(mainExtensionMock).newExtensionDecoder(); - } - - @Test - public void testFallbackSuccess() { - // initialize - when(mainHandshakerMock.newRequestData()). - thenReturn(new WebSocketExtensionData("main", Collections.emptyMap())); - when(mainHandshakerMock.handshakeExtension(any(WebSocketExtensionData.class))).thenReturn(null); - when(fallbackHandshakerMock.newRequestData()). - thenReturn(new WebSocketExtensionData("fallback", Collections.emptyMap())); - when(fallbackHandshakerMock.handshakeExtension( - any(WebSocketExtensionData.class))).thenReturn(fallbackExtensionMock); - when(fallbackExtensionMock.rsv()).thenReturn(WebSocketExtension.RSV1); - when(fallbackExtensionMock.newExtensionEncoder()).thenReturn(new DummyEncoder()); - when(fallbackExtensionMock.newExtensionDecoder()).thenReturn(new DummyDecoder()); - - // execute - EmbeddedChannel ch = new EmbeddedChannel(new WebSocketClientExtensionHandler( - mainHandshakerMock, fallbackHandshakerMock)); - - HttpRequest req = newUpgradeRequest(null); - ch.writeOutbound(req); - - HttpRequest req2 = ch.readOutbound(); - List reqExts = WebSocketExtensionUtil.extractExtensions( - req2.headers().get(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS)); - - HttpResponse res = newUpgradeResponse("fallback"); - ch.writeInbound(res); - - HttpResponse res2 = ch.readInbound(); - List resExts = WebSocketExtensionUtil.extractExtensions( - res2.headers().get(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS)); - - // test - assertEquals(2, reqExts.size()); - assertEquals("main", reqExts.get(0).name()); - assertEquals("fallback", reqExts.get(1).name()); - - assertEquals(1, resExts.size()); - assertEquals("fallback", resExts.get(0).name()); - assertTrue(resExts.get(0).parameters().isEmpty()); - assertNotNull(ch.pipeline().get(DummyDecoder.class)); - assertNotNull(ch.pipeline().get(DummyEncoder.class)); - - verify(mainHandshakerMock).newRequestData(); - verify(mainHandshakerMock).handshakeExtension(any(WebSocketExtensionData.class)); - verify(fallbackHandshakerMock).newRequestData(); - verify(fallbackHandshakerMock).handshakeExtension(any(WebSocketExtensionData.class)); - verify(fallbackExtensionMock, atLeastOnce()).rsv(); - verify(fallbackExtensionMock).newExtensionEncoder(); - verify(fallbackExtensionMock).newExtensionDecoder(); - } - - @Test - public void testAllSuccess() { - // initialize - when(mainHandshakerMock.newRequestData()). - thenReturn(new WebSocketExtensionData("main", Collections.emptyMap())); - when(mainHandshakerMock.handshakeExtension( - webSocketExtensionDataMatcher("main"))).thenReturn(mainExtensionMock); - when(mainHandshakerMock.handshakeExtension( - webSocketExtensionDataMatcher("fallback"))).thenReturn(null); - when(fallbackHandshakerMock.newRequestData()). - thenReturn(new WebSocketExtensionData("fallback", Collections.emptyMap())); - when(fallbackHandshakerMock.handshakeExtension( - webSocketExtensionDataMatcher("main"))).thenReturn(null); - when(fallbackHandshakerMock.handshakeExtension( - webSocketExtensionDataMatcher("fallback"))).thenReturn(fallbackExtensionMock); - - DummyEncoder mainEncoder = new DummyEncoder(); - DummyDecoder mainDecoder = new DummyDecoder(); - when(mainExtensionMock.rsv()).thenReturn(WebSocketExtension.RSV1); - when(mainExtensionMock.newExtensionEncoder()).thenReturn(mainEncoder); - when(mainExtensionMock.newExtensionDecoder()).thenReturn(mainDecoder); - - Dummy2Encoder fallbackEncoder = new Dummy2Encoder(); - Dummy2Decoder fallbackDecoder = new Dummy2Decoder(); - when(fallbackExtensionMock.rsv()).thenReturn(WebSocketExtension.RSV2); - when(fallbackExtensionMock.newExtensionEncoder()).thenReturn(fallbackEncoder); - when(fallbackExtensionMock.newExtensionDecoder()).thenReturn(fallbackDecoder); - - // execute - EmbeddedChannel ch = new EmbeddedChannel(new WebSocketClientExtensionHandler( - mainHandshakerMock, fallbackHandshakerMock)); - - HttpRequest req = newUpgradeRequest(null); - ch.writeOutbound(req); - - HttpRequest req2 = ch.readOutbound(); - List reqExts = WebSocketExtensionUtil.extractExtensions( - req2.headers().get(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS)); - - HttpResponse res = newUpgradeResponse("main, fallback"); - ch.writeInbound(res); - - HttpResponse res2 = ch.readInbound(); - List resExts = WebSocketExtensionUtil.extractExtensions( - res2.headers().get(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS)); - - // test - assertEquals(2, reqExts.size()); - assertEquals("main", reqExts.get(0).name()); - assertEquals("fallback", reqExts.get(1).name()); - - assertEquals(2, resExts.size()); - assertEquals("main", resExts.get(0).name()); - assertEquals("fallback", resExts.get(1).name()); - assertNotNull(ch.pipeline().context(mainEncoder)); - assertNotNull(ch.pipeline().context(mainDecoder)); - assertNotNull(ch.pipeline().context(fallbackEncoder)); - assertNotNull(ch.pipeline().context(fallbackDecoder)); - - verify(mainHandshakerMock).newRequestData(); - verify(mainHandshakerMock).handshakeExtension(webSocketExtensionDataMatcher("main")); - verify(mainHandshakerMock).handshakeExtension(webSocketExtensionDataMatcher("fallback")); - verify(fallbackHandshakerMock).newRequestData(); - verify(fallbackHandshakerMock).handshakeExtension(webSocketExtensionDataMatcher("fallback")); - verify(mainExtensionMock, atLeastOnce()).rsv(); - verify(mainExtensionMock).newExtensionEncoder(); - verify(mainExtensionMock).newExtensionDecoder(); - verify(fallbackExtensionMock, atLeastOnce()).rsv(); - verify(fallbackExtensionMock).newExtensionEncoder(); - verify(fallbackExtensionMock).newExtensionDecoder(); - } - - @Test - public void testIfMainAndFallbackUseRSV1WillFail() { - // initialize - when(mainHandshakerMock.newRequestData()). - thenReturn(new WebSocketExtensionData("main", Collections.emptyMap())); - when(mainHandshakerMock.handshakeExtension( - webSocketExtensionDataMatcher("main"))).thenReturn(mainExtensionMock); - when(mainHandshakerMock.handshakeExtension( - webSocketExtensionDataMatcher("fallback"))).thenReturn(null); - when(fallbackHandshakerMock.newRequestData()). - thenReturn(new WebSocketExtensionData("fallback", Collections.emptyMap())); - when(fallbackHandshakerMock.handshakeExtension( - webSocketExtensionDataMatcher("main"))).thenReturn(null); - when(fallbackHandshakerMock.handshakeExtension( - webSocketExtensionDataMatcher("fallback"))).thenReturn(fallbackExtensionMock); - when(mainExtensionMock.rsv()).thenReturn(WebSocketExtension.RSV1); - when(fallbackExtensionMock.rsv()).thenReturn(WebSocketExtension.RSV1); - - // execute - EmbeddedChannel ch = new EmbeddedChannel(new WebSocketClientExtensionHandler( - mainHandshakerMock, fallbackHandshakerMock)); - - HttpRequest req = newUpgradeRequest(null); - ch.writeOutbound(req); - - HttpRequest req2 = ch.readOutbound(); - List reqExts = WebSocketExtensionUtil.extractExtensions( - req2.headers().get(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS)); - - HttpResponse res = newUpgradeResponse("main, fallback"); - try { - ch.writeInbound(res); - } catch (CodecException e) { - return; - } - fail("Expected to encounter a CodecException"); - - // test - assertEquals(2, reqExts.size()); - assertEquals("main", reqExts.get(0).name()); - assertEquals("fallback", reqExts.get(1).name()); - - verify(mainHandshakerMock).newRequestData(); - verify(mainHandshakerMock, atLeastOnce()).handshakeExtension(webSocketExtensionDataMatcher("main")); - verify(mainHandshakerMock, atLeastOnce()).handshakeExtension(webSocketExtensionDataMatcher("fallback")); - - verify(fallbackHandshakerMock).newRequestData(); - verify(fallbackHandshakerMock, atLeastOnce()).handshakeExtension(webSocketExtensionDataMatcher("main")); - verify(fallbackHandshakerMock, atLeastOnce()).handshakeExtension(webSocketExtensionDataMatcher("fallback")); - - verify(mainExtensionMock, atLeastOnce()).rsv(); - verify(fallbackExtensionMock, atLeastOnce()).rsv(); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtensionFilterProviderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtensionFilterProviderTest.java deleted file mode 100644 index ef80950e14..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtensionFilterProviderTest.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx.extensions; - -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -public class WebSocketExtensionFilterProviderTest { - - @Test - public void testDefaultExtensionFilterProvider() { - WebSocketExtensionFilterProvider defaultProvider = WebSocketExtensionFilterProvider.DEFAULT; - assertNotNull(defaultProvider); - - assertEquals(WebSocketExtensionFilter.NEVER_SKIP, defaultProvider.decoderFilter()); - assertEquals(WebSocketExtensionFilter.NEVER_SKIP, defaultProvider.encoderFilter()); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtensionFilterTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtensionFilterTest.java deleted file mode 100644 index 7eced821e8..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtensionFilterTest.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx.extensions; - -import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; -import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame; -import io.netty.handler.codec.http.websocketx.ContinuationWebSocketFrame; -import io.netty.handler.codec.http.websocketx.PingWebSocketFrame; -import io.netty.handler.codec.http.websocketx.PongWebSocketFrame; -import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class WebSocketExtensionFilterTest { - - @Test - public void testNeverSkip() { - WebSocketExtensionFilter neverSkip = WebSocketExtensionFilter.NEVER_SKIP; - - BinaryWebSocketFrame binaryFrame = new BinaryWebSocketFrame(); - assertFalse(neverSkip.mustSkip(binaryFrame)); - assertTrue(binaryFrame.release()); - - TextWebSocketFrame textFrame = new TextWebSocketFrame(); - assertFalse(neverSkip.mustSkip(textFrame)); - assertTrue(textFrame.release()); - - PingWebSocketFrame pingFrame = new PingWebSocketFrame(); - assertFalse(neverSkip.mustSkip(pingFrame)); - assertTrue(pingFrame.release()); - - PongWebSocketFrame pongFrame = new PongWebSocketFrame(); - assertFalse(neverSkip.mustSkip(pongFrame)); - assertTrue(pongFrame.release()); - - CloseWebSocketFrame closeFrame = new CloseWebSocketFrame(); - assertFalse(neverSkip.mustSkip(closeFrame)); - assertTrue(closeFrame.release()); - - ContinuationWebSocketFrame continuationFrame = new ContinuationWebSocketFrame(); - assertFalse(neverSkip.mustSkip(continuationFrame)); - assertTrue(continuationFrame.release()); - } - - @Test - public void testAlwaysSkip() { - WebSocketExtensionFilter neverSkip = WebSocketExtensionFilter.ALWAYS_SKIP; - - BinaryWebSocketFrame binaryFrame = new BinaryWebSocketFrame(); - assertTrue(neverSkip.mustSkip(binaryFrame)); - assertTrue(binaryFrame.release()); - - TextWebSocketFrame textFrame = new TextWebSocketFrame(); - assertTrue(neverSkip.mustSkip(textFrame)); - assertTrue(textFrame.release()); - - PingWebSocketFrame pingFrame = new PingWebSocketFrame(); - assertTrue(neverSkip.mustSkip(pingFrame)); - assertTrue(pingFrame.release()); - - PongWebSocketFrame pongFrame = new PongWebSocketFrame(); - assertTrue(neverSkip.mustSkip(pongFrame)); - assertTrue(pongFrame.release()); - - CloseWebSocketFrame closeFrame = new CloseWebSocketFrame(); - assertTrue(neverSkip.mustSkip(closeFrame)); - assertTrue(closeFrame.release()); - - ContinuationWebSocketFrame continuationFrame = new ContinuationWebSocketFrame(); - assertTrue(neverSkip.mustSkip(continuationFrame)); - assertTrue(continuationFrame.release()); - } -} 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 deleted file mode 100644 index bbfcac6fe2..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtensionTestUtil.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version - * 2.0 (the "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http.websocketx.extensions; - -import java.util.List; - -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http.DefaultHttpRequest; -import io.netty.handler.codec.http.DefaultHttpResponse; -import io.netty.handler.codec.http.websocketx.WebSocketFrame; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpVersion; -import org.mockito.ArgumentMatcher; - -import static org.mockito.Mockito.argThat; - -public final class WebSocketExtensionTestUtil { - - public static HttpRequest newUpgradeRequest(String ext) { - HttpRequest req = new DefaultHttpRequest( - HttpVersion.HTTP_1_1, HttpMethod.GET, "/chat"); - - req.headers().set(HttpHeaderNames.HOST, "server.example.com"); - req.headers().set(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET.toString().toLowerCase()); - req.headers().set(HttpHeaderNames.CONNECTION, "Upgrade"); - req.headers().set(HttpHeaderNames.ORIGIN, "http://example.com"); - if (ext != null) { - req.headers().set(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS, ext); - } - - return req; - } - - public static HttpResponse newUpgradeResponse(String ext) { - HttpResponse res = new DefaultHttpResponse( - HttpVersion.HTTP_1_1, HttpResponseStatus.SWITCHING_PROTOCOLS); - - res.headers().set(HttpHeaderNames.HOST, "server.example.com"); - res.headers().set(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET.toString().toLowerCase()); - res.headers().set(HttpHeaderNames.CONNECTION, "Upgrade"); - res.headers().set(HttpHeaderNames.ORIGIN, "http://example.com"); - if (ext != null) { - res.headers().set(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS, ext); - } - - return res; - } - - static final class WebSocketExtensionDataMatcher implements ArgumentMatcher { - - private final String name; - - WebSocketExtensionDataMatcher(String name) { - this.name = name; - } - - @Override - public boolean matches(WebSocketExtensionData data) { - return data != null && name.equals(data.name()); - } - } - - static WebSocketExtensionData webSocketExtensionDataMatcher(String text) { - return argThat(new WebSocketExtensionDataMatcher(text)); - } - - private WebSocketExtensionTestUtil() { - // unused - } - - static class DummyEncoder extends WebSocketExtensionEncoder { - @Override - protected void encode(ChannelHandlerContext ctx, WebSocketFrame msg, - List out) throws Exception { - // unused - } - } - - static class DummyDecoder extends WebSocketExtensionDecoder { - @Override - protected void decode(ChannelHandlerContext ctx, WebSocketFrame msg) throws Exception { - // unused - } - } - - static class Dummy2Encoder extends WebSocketExtensionEncoder { - @Override - protected void encode(ChannelHandlerContext ctx, WebSocketFrame msg, - List out) throws Exception { - // unused - } - } - - static class Dummy2Decoder extends WebSocketExtensionDecoder { - @Override - protected void decode(ChannelHandlerContext ctx, WebSocketFrame msg) throws Exception { - // unused - } - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtensionUtilTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtensionUtilTest.java deleted file mode 100644 index 41345661cb..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtensionUtilTest.java +++ /dev/null @@ -1,85 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx.extensions; - -import io.netty.handler.codec.http.DefaultHttpHeaders; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpHeaders; -import org.junit.jupiter.api.Test; - -import java.util.List; - -import static io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionUtil.*; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class WebSocketExtensionUtilTest { - - @Test - public void testIsWebsocketUpgrade() { - HttpHeaders headers = new DefaultHttpHeaders(); - assertFalse(isWebsocketUpgrade(headers)); - - headers.add(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET); - assertFalse(isWebsocketUpgrade(headers)); - - headers.add(HttpHeaderNames.CONNECTION, "Keep-Alive, Upgrade"); - assertTrue(isWebsocketUpgrade(headers)); - } - - @Test - public void computeMergeExtensionsHeaderValueWhenNoUserDefinedHeader() { - List extras = extractExtensions("permessage-deflate; client_max_window_bits," + - "permessage-deflate; client_no_context_takeover; client_max_window_bits," + - "deflate-frame," + - "x-webkit-deflate-frame"); - String newHeaderValue = computeMergeExtensionsHeaderValue(null, extras); - assertEquals("permessage-deflate;client_max_window_bits," + - "permessage-deflate;client_no_context_takeover;client_max_window_bits," + - "deflate-frame," + - "x-webkit-deflate-frame", newHeaderValue); - } - - @Test - public void computeMergeExtensionsHeaderValueWhenNoConflictingUserDefinedHeader() { - List extras = extractExtensions("permessage-deflate; client_max_window_bits," + - "permessage-deflate; client_no_context_takeover; client_max_window_bits," + - "deflate-frame," + - "x-webkit-deflate-frame"); - String newHeaderValue = computeMergeExtensionsHeaderValue("foo, bar", extras); - assertEquals("permessage-deflate;client_max_window_bits," + - "permessage-deflate;client_no_context_takeover;client_max_window_bits," + - "deflate-frame," + - "x-webkit-deflate-frame," + - "foo," + - "bar", newHeaderValue); - } - - @Test - public void computeMergeExtensionsHeaderValueWhenConflictingUserDefinedHeader() { - List extras = extractExtensions("permessage-deflate; client_max_window_bits," + - "permessage-deflate; client_no_context_takeover; client_max_window_bits," + - "deflate-frame," + - "x-webkit-deflate-frame"); - String newHeaderValue = computeMergeExtensionsHeaderValue("permessage-deflate; client_max_window_bits", extras); - assertEquals("permessage-deflate;client_max_window_bits," + - "permessage-deflate;client_no_context_takeover;client_max_window_bits," + - "deflate-frame," + - "x-webkit-deflate-frame", newHeaderValue); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketServerExtensionHandlerTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketServerExtensionHandlerTest.java deleted file mode 100644 index 0ff3d8a663..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketServerExtensionHandlerTest.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx.extensions; - -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpResponse; -import org.junit.jupiter.api.Test; - -import java.util.Collections; -import java.util.List; - -import static io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionTestUtil.Dummy2Decoder; -import static io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionTestUtil.Dummy2Encoder; -import static io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionTestUtil.DummyDecoder; -import static io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionTestUtil.DummyEncoder; -import static io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionTestUtil.newUpgradeRequest; -import static io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionTestUtil.newUpgradeResponse; -import static io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionTestUtil.webSocketExtensionDataMatcher; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -public class WebSocketServerExtensionHandlerTest { - - WebSocketServerExtensionHandshaker mainHandshakerMock = - mock(WebSocketServerExtensionHandshaker.class, "mainHandshaker"); - WebSocketServerExtensionHandshaker fallbackHandshakerMock = - mock(WebSocketServerExtensionHandshaker.class, "fallbackHandshaker"); - WebSocketServerExtension mainExtensionMock = - mock(WebSocketServerExtension.class, "mainExtension"); - WebSocketServerExtension fallbackExtensionMock = - mock(WebSocketServerExtension.class, "fallbackExtension"); - - @Test - public void testMainSuccess() { - // initialize - when(mainHandshakerMock.handshakeExtension(webSocketExtensionDataMatcher("main"))). - thenReturn(mainExtensionMock); - when(mainHandshakerMock.handshakeExtension(webSocketExtensionDataMatcher("fallback"))). - thenReturn(null); - - when(fallbackHandshakerMock.handshakeExtension(webSocketExtensionDataMatcher("fallback"))). - thenReturn(fallbackExtensionMock); - when(fallbackHandshakerMock.handshakeExtension(webSocketExtensionDataMatcher("main"))). - thenReturn(null); - - when(mainExtensionMock.rsv()).thenReturn(WebSocketExtension.RSV1); - when(mainExtensionMock.newResponseData()).thenReturn( - new WebSocketExtensionData("main", Collections.emptyMap())); - when(mainExtensionMock.newExtensionEncoder()).thenReturn(new DummyEncoder()); - when(mainExtensionMock.newExtensionDecoder()).thenReturn(new DummyDecoder()); - - when(fallbackExtensionMock.rsv()).thenReturn(WebSocketExtension.RSV1); - - // execute - WebSocketServerExtensionHandler extensionHandler = - new WebSocketServerExtensionHandler(mainHandshakerMock, fallbackHandshakerMock); - EmbeddedChannel ch = new EmbeddedChannel(extensionHandler); - - HttpRequest req = newUpgradeRequest("main, fallback"); - ch.writeInbound(req); - - HttpResponse res = newUpgradeResponse(null); - ch.writeOutbound(res); - - HttpResponse res2 = ch.readOutbound(); - List resExts = WebSocketExtensionUtil.extractExtensions( - res2.headers().get(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS)); - - // test - assertNull(ch.pipeline().context(extensionHandler)); - assertEquals(1, resExts.size()); - assertEquals("main", resExts.get(0).name()); - assertTrue(resExts.get(0).parameters().isEmpty()); - assertNotNull(ch.pipeline().get(DummyDecoder.class)); - assertNotNull(ch.pipeline().get(DummyEncoder.class)); - - verify(mainHandshakerMock, atLeastOnce()).handshakeExtension(webSocketExtensionDataMatcher("main")); - verify(mainHandshakerMock, atLeastOnce()).handshakeExtension(webSocketExtensionDataMatcher("fallback")); - verify(fallbackHandshakerMock, atLeastOnce()).handshakeExtension(webSocketExtensionDataMatcher("fallback")); - - verify(mainExtensionMock, atLeastOnce()).rsv(); - verify(mainExtensionMock).newResponseData(); - verify(mainExtensionMock).newExtensionEncoder(); - verify(mainExtensionMock).newExtensionDecoder(); - verify(fallbackExtensionMock, atLeastOnce()).rsv(); - } - - @Test - public void testCompatibleExtensionTogetherSuccess() { - // initialize - when(mainHandshakerMock.handshakeExtension(webSocketExtensionDataMatcher("main"))). - thenReturn(mainExtensionMock); - when(mainHandshakerMock.handshakeExtension(webSocketExtensionDataMatcher("fallback"))). - thenReturn(null); - - when(fallbackHandshakerMock.handshakeExtension(webSocketExtensionDataMatcher("fallback"))). - thenReturn(fallbackExtensionMock); - when(fallbackHandshakerMock.handshakeExtension(webSocketExtensionDataMatcher("main"))). - thenReturn(null); - - when(mainExtensionMock.rsv()).thenReturn(WebSocketExtension.RSV1); - when(mainExtensionMock.newResponseData()).thenReturn( - new WebSocketExtensionData("main", Collections.emptyMap())); - when(mainExtensionMock.newExtensionEncoder()).thenReturn(new DummyEncoder()); - when(mainExtensionMock.newExtensionDecoder()).thenReturn(new DummyDecoder()); - - when(fallbackExtensionMock.rsv()).thenReturn(WebSocketExtension.RSV2); - when(fallbackExtensionMock.newResponseData()).thenReturn( - new WebSocketExtensionData("fallback", Collections.emptyMap())); - when(fallbackExtensionMock.newExtensionEncoder()).thenReturn(new Dummy2Encoder()); - when(fallbackExtensionMock.newExtensionDecoder()).thenReturn(new Dummy2Decoder()); - - // execute - WebSocketServerExtensionHandler extensionHandler = - new WebSocketServerExtensionHandler(mainHandshakerMock, fallbackHandshakerMock); - EmbeddedChannel ch = new EmbeddedChannel(extensionHandler); - - HttpRequest req = newUpgradeRequest("main, fallback"); - ch.writeInbound(req); - - HttpResponse res = newUpgradeResponse(null); - ch.writeOutbound(res); - - HttpResponse res2 = ch.readOutbound(); - List resExts = WebSocketExtensionUtil.extractExtensions( - res2.headers().get(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS)); - - // test - assertNull(ch.pipeline().context(extensionHandler)); - assertEquals(2, resExts.size()); - assertEquals("main", resExts.get(0).name()); - assertEquals("fallback", resExts.get(1).name()); - assertNotNull(ch.pipeline().get(DummyDecoder.class)); - assertNotNull(ch.pipeline().get(DummyEncoder.class)); - assertNotNull(ch.pipeline().get(Dummy2Decoder.class)); - assertNotNull(ch.pipeline().get(Dummy2Encoder.class)); - - verify(mainHandshakerMock).handshakeExtension(webSocketExtensionDataMatcher("main")); - verify(mainHandshakerMock).handshakeExtension(webSocketExtensionDataMatcher("fallback")); - verify(fallbackHandshakerMock).handshakeExtension(webSocketExtensionDataMatcher("fallback")); - verify(mainExtensionMock, times(2)).rsv(); - verify(mainExtensionMock).newResponseData(); - verify(mainExtensionMock).newExtensionEncoder(); - verify(mainExtensionMock).newExtensionDecoder(); - - verify(fallbackExtensionMock, times(2)).rsv(); - - verify(fallbackExtensionMock).newResponseData(); - verify(fallbackExtensionMock).newExtensionEncoder(); - verify(fallbackExtensionMock).newExtensionDecoder(); - } - - @Test - public void testNoneExtensionMatchingSuccess() { - // initialize - when(mainHandshakerMock.handshakeExtension(webSocketExtensionDataMatcher("unknown"))). - thenReturn(null); - when(mainHandshakerMock.handshakeExtension(webSocketExtensionDataMatcher("unknown2"))). - thenReturn(null); - - when(fallbackHandshakerMock.handshakeExtension(webSocketExtensionDataMatcher("unknown"))). - thenReturn(null); - when(fallbackHandshakerMock.handshakeExtension(webSocketExtensionDataMatcher("unknown2"))). - thenReturn(null); - - // execute - WebSocketServerExtensionHandler extensionHandler = - new WebSocketServerExtensionHandler(mainHandshakerMock, fallbackHandshakerMock); - EmbeddedChannel ch = new EmbeddedChannel(extensionHandler); - - HttpRequest req = newUpgradeRequest("unknown, unknown2"); - ch.writeInbound(req); - - HttpResponse res = newUpgradeResponse(null); - ch.writeOutbound(res); - - HttpResponse res2 = ch.readOutbound(); - - // test - assertNull(ch.pipeline().context(extensionHandler)); - assertFalse(res2.headers().contains(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS)); - - verify(mainHandshakerMock).handshakeExtension(webSocketExtensionDataMatcher("unknown")); - verify(mainHandshakerMock).handshakeExtension(webSocketExtensionDataMatcher("unknown2")); - - verify(fallbackHandshakerMock).handshakeExtension(webSocketExtensionDataMatcher("unknown")); - verify(fallbackHandshakerMock).handshakeExtension(webSocketExtensionDataMatcher("unknown2")); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/compression/DeflateFrameClientExtensionHandshakerTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/compression/DeflateFrameClientExtensionHandshakerTest.java deleted file mode 100644 index e46e0854c7..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/compression/DeflateFrameClientExtensionHandshakerTest.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx.extensions.compression; - -import static io.netty.handler.codec.http.websocketx.extensions.compression. - DeflateFrameServerExtensionHandshaker.*; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import io.netty.handler.codec.http.websocketx.extensions.WebSocketClientExtension; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionData; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import org.junit.jupiter.api.Test; - -public class DeflateFrameClientExtensionHandshakerTest { - - @Test - public void testWebkitDeflateFrameData() { - DeflateFrameClientExtensionHandshaker handshaker = - new DeflateFrameClientExtensionHandshaker(true); - - WebSocketExtensionData data = handshaker.newRequestData(); - - assertEquals(X_WEBKIT_DEFLATE_FRAME_EXTENSION, data.name()); - assertTrue(data.parameters().isEmpty()); - } - - @Test - public void testDeflateFrameData() { - DeflateFrameClientExtensionHandshaker handshaker = - new DeflateFrameClientExtensionHandshaker(false); - - WebSocketExtensionData data = handshaker.newRequestData(); - - assertEquals(DEFLATE_FRAME_EXTENSION, data.name()); - assertTrue(data.parameters().isEmpty()); - } - - @Test - public void testNormalHandshake() { - DeflateFrameClientExtensionHandshaker handshaker = - new DeflateFrameClientExtensionHandshaker(false); - - WebSocketClientExtension extension = handshaker.handshakeExtension( - new WebSocketExtensionData(DEFLATE_FRAME_EXTENSION, Collections.emptyMap())); - - assertNotNull(extension); - assertEquals(WebSocketClientExtension.RSV1, extension.rsv()); - assertTrue(extension.newExtensionDecoder() instanceof PerFrameDeflateDecoder); - assertTrue(extension.newExtensionEncoder() instanceof PerFrameDeflateEncoder); - } - - @Test - public void testFailedHandshake() { - // initialize - DeflateFrameClientExtensionHandshaker handshaker = - new DeflateFrameClientExtensionHandshaker(false); - - Map parameters = new HashMap<>(); - parameters.put("invalid", "12"); - - // execute - WebSocketClientExtension extension = handshaker.handshakeExtension( - new WebSocketExtensionData(DEFLATE_FRAME_EXTENSION, parameters)); - - // test - assertNull(extension); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/compression/DeflateFrameServerExtensionHandshakerTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/compression/DeflateFrameServerExtensionHandshakerTest.java deleted file mode 100644 index 64143cbe4c..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/compression/DeflateFrameServerExtensionHandshakerTest.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx.extensions.compression; - -import static io.netty.handler.codec.http.websocketx.extensions.compression. - DeflateFrameServerExtensionHandshaker.*; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import io.netty.handler.codec.http.websocketx.extensions.WebSocketServerExtension; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionData; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import org.junit.jupiter.api.Test; - -public class DeflateFrameServerExtensionHandshakerTest { - - @Test - public void testNormalHandshake() { - // initialize - DeflateFrameServerExtensionHandshaker handshaker = - new DeflateFrameServerExtensionHandshaker(); - - // execute - WebSocketServerExtension extension = handshaker.handshakeExtension( - new WebSocketExtensionData(DEFLATE_FRAME_EXTENSION, Collections.emptyMap())); - - // test - assertNotNull(extension); - assertEquals(WebSocketServerExtension.RSV1, extension.rsv()); - assertTrue(extension.newExtensionDecoder() instanceof PerFrameDeflateDecoder); - assertTrue(extension.newExtensionEncoder() instanceof PerFrameDeflateEncoder); - } - - @Test - public void testWebkitHandshake() { - // initialize - DeflateFrameServerExtensionHandshaker handshaker = - new DeflateFrameServerExtensionHandshaker(); - - // execute - WebSocketServerExtension extension = handshaker.handshakeExtension( - new WebSocketExtensionData(X_WEBKIT_DEFLATE_FRAME_EXTENSION, Collections.emptyMap())); - - // test - assertNotNull(extension); - assertEquals(WebSocketServerExtension.RSV1, extension.rsv()); - assertTrue(extension.newExtensionDecoder() instanceof PerFrameDeflateDecoder); - assertTrue(extension.newExtensionEncoder() instanceof PerFrameDeflateEncoder); - } - - @Test - public void testFailedHandshake() { - // initialize - DeflateFrameServerExtensionHandshaker handshaker = - new DeflateFrameServerExtensionHandshaker(); - - Map parameters; - parameters = new HashMap<>(); - parameters.put("unknown", "11"); - - // execute - WebSocketServerExtension extension = handshaker.handshakeExtension( - new WebSocketExtensionData(DEFLATE_FRAME_EXTENSION, parameters)); - - // test - assertNull(extension); - } - -} 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 deleted file mode 100644 index 121fd2011b..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerFrameDeflateDecoderTest.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx.extensions.compression; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -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.extensions.WebSocketExtension; -import org.junit.jupiter.api.Test; - -import java.util.Random; - -import static io.netty.handler.codec.http.websocketx.extensions.WebSocketExtension.*; -import static io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionFilter.*; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class PerFrameDeflateDecoderTest { - - private static final Random random = new Random(); - - @Test - public void testCompressedFrame() { - EmbeddedChannel encoderChannel = new EmbeddedChannel( - ZlibCodecFactory.newZlibEncoder(ZlibWrapper.NONE, 9, 15, 8)); - EmbeddedChannel decoderChannel = new EmbeddedChannel(new PerFrameDeflateDecoder(false)); - - // initialize - byte[] payload = new byte[300]; - random.nextBytes(payload); - - assertTrue(encoderChannel.writeOutbound(Unpooled.wrappedBuffer(payload))); - ByteBuf compressedPayload = encoderChannel.readOutbound(); - - BinaryWebSocketFrame compressedFrame = new BinaryWebSocketFrame(true, - RSV1 | RSV3, - compressedPayload.slice(0, compressedPayload.readableBytes() - 4)); - - // execute - assertTrue(decoderChannel.writeInbound(compressedFrame)); - BinaryWebSocketFrame uncompressedFrame = decoderChannel.readInbound(); - - // test - assertNotNull(uncompressedFrame); - assertNotNull(uncompressedFrame.content()); - assertEquals(RSV3, uncompressedFrame.rsv()); - assertEquals(300, uncompressedFrame.content().readableBytes()); - - byte[] finalPayload = new byte[300]; - uncompressedFrame.content().readBytes(finalPayload); - assertArrayEquals(finalPayload, payload); - uncompressedFrame.release(); - } - - @Test - public void testNormalFrame() { - EmbeddedChannel decoderChannel = new EmbeddedChannel(new PerFrameDeflateDecoder(false)); - - // initialize - byte[] payload = new byte[300]; - random.nextBytes(payload); - - BinaryWebSocketFrame frame = new BinaryWebSocketFrame(true, - RSV3, Unpooled.wrappedBuffer(payload)); - - // execute - assertTrue(decoderChannel.writeInbound(frame)); - BinaryWebSocketFrame newFrame = decoderChannel.readInbound(); - - // test - assertNotNull(newFrame); - assertNotNull(newFrame.content()); - assertEquals(RSV3, newFrame.rsv()); - assertEquals(300, newFrame.content().readableBytes()); - - byte[] finalPayload = new byte[300]; - newFrame.content().readBytes(finalPayload); - assertArrayEquals(finalPayload, payload); - newFrame.release(); - } - - // See https://github.com/netty/netty/issues/4348 - @Test - public void testCompressedEmptyFrame() { - EmbeddedChannel encoderChannel = new EmbeddedChannel( - ZlibCodecFactory.newZlibEncoder(ZlibWrapper.NONE, 9, 15, 8)); - EmbeddedChannel decoderChannel = new EmbeddedChannel(new PerFrameDeflateDecoder(false)); - - assertTrue(encoderChannel.writeOutbound(Unpooled.EMPTY_BUFFER)); - ByteBuf compressedPayload = encoderChannel.readOutbound(); - BinaryWebSocketFrame compressedFrame = - new BinaryWebSocketFrame(true, RSV1 | RSV3, compressedPayload); - - // execute - assertTrue(decoderChannel.writeInbound(compressedFrame)); - BinaryWebSocketFrame uncompressedFrame = decoderChannel.readInbound(); - - // test - assertNotNull(uncompressedFrame); - assertNotNull(uncompressedFrame.content()); - assertEquals(RSV3, uncompressedFrame.rsv()); - assertEquals(0, uncompressedFrame.content().readableBytes()); - uncompressedFrame.release(); - } - - @Test - public void testDecompressionSkip() { - EmbeddedChannel encoderChannel = new EmbeddedChannel( - ZlibCodecFactory.newZlibEncoder(ZlibWrapper.NONE, 9, 15, 8)); - EmbeddedChannel decoderChannel = new EmbeddedChannel(new PerFrameDeflateDecoder(false, ALWAYS_SKIP)); - - byte[] payload = new byte[300]; - random.nextBytes(payload); - - assertTrue(encoderChannel.writeOutbound(Unpooled.wrappedBuffer(payload))); - ByteBuf compressedPayload = encoderChannel.readOutbound(); - - BinaryWebSocketFrame compressedBinaryFrame = new BinaryWebSocketFrame( - true, WebSocketExtension.RSV1 | WebSocketExtension.RSV3, compressedPayload); - - assertTrue(decoderChannel.writeInbound(compressedBinaryFrame)); - - BinaryWebSocketFrame inboundBinaryFrame = decoderChannel.readInbound(); - - assertNotNull(inboundBinaryFrame); - assertNotNull(inboundBinaryFrame.content()); - assertEquals(compressedPayload, inboundBinaryFrame.content()); - assertEquals(5, inboundBinaryFrame.rsv()); - - assertTrue(inboundBinaryFrame.release()); - - assertTrue(encoderChannel.finishAndReleaseAll()); - assertFalse(decoderChannel.finish()); - } - -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerFrameDeflateEncoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerFrameDeflateEncoderTest.java deleted file mode 100644 index ceba96f8e7..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerFrameDeflateEncoderTest.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx.extensions.compression; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -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.ContinuationWebSocketFrame; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtension; -import org.junit.jupiter.api.Test; - -import java.util.Random; - -import static io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionFilter.*; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class PerFrameDeflateEncoderTest { - - private static final Random random = new Random(); - - @Test - public void testCompressedFrame() { - EmbeddedChannel encoderChannel = new EmbeddedChannel(new PerFrameDeflateEncoder(9, 15, false)); - EmbeddedChannel decoderChannel = new EmbeddedChannel( - ZlibCodecFactory.newZlibDecoder(ZlibWrapper.NONE)); - - // initialize - byte[] payload = new byte[300]; - random.nextBytes(payload); - BinaryWebSocketFrame frame = new BinaryWebSocketFrame(true, - WebSocketExtension.RSV3, Unpooled.wrappedBuffer(payload)); - - // execute - assertTrue(encoderChannel.writeOutbound(frame)); - BinaryWebSocketFrame compressedFrame = encoderChannel.readOutbound(); - - // test - assertNotNull(compressedFrame); - assertNotNull(compressedFrame.content()); - assertEquals(WebSocketExtension.RSV1 | WebSocketExtension.RSV3, compressedFrame.rsv()); - - assertTrue(decoderChannel.writeInbound(compressedFrame.content())); - assertTrue(decoderChannel.writeInbound(DeflateDecoder.FRAME_TAIL.duplicate())); - ByteBuf uncompressedPayload = decoderChannel.readInbound(); - assertEquals(300, uncompressedPayload.readableBytes()); - - byte[] finalPayload = new byte[300]; - uncompressedPayload.readBytes(finalPayload); - assertArrayEquals(finalPayload, payload); - uncompressedPayload.release(); - } - - @Test - public void testAlreadyCompressedFrame() { - EmbeddedChannel encoderChannel = new EmbeddedChannel(new PerFrameDeflateEncoder(9, 15, false)); - - // initialize - byte[] payload = new byte[300]; - random.nextBytes(payload); - - BinaryWebSocketFrame frame = new BinaryWebSocketFrame(true, - WebSocketExtension.RSV3 | WebSocketExtension.RSV1, Unpooled.wrappedBuffer(payload)); - - // execute - assertTrue(encoderChannel.writeOutbound(frame)); - BinaryWebSocketFrame newFrame = encoderChannel.readOutbound(); - - // test - assertNotNull(newFrame); - assertNotNull(newFrame.content()); - assertEquals(WebSocketExtension.RSV3 | WebSocketExtension.RSV1, newFrame.rsv()); - assertEquals(300, newFrame.content().readableBytes()); - - byte[] finalPayload = new byte[300]; - newFrame.content().readBytes(finalPayload); - assertArrayEquals(finalPayload, payload); - newFrame.release(); - } - - @Test - public void testFramementedFrame() { - EmbeddedChannel encoderChannel = new EmbeddedChannel(new PerFrameDeflateEncoder(9, 15, false)); - EmbeddedChannel decoderChannel = new EmbeddedChannel( - ZlibCodecFactory.newZlibDecoder(ZlibWrapper.NONE)); - - // initialize - byte[] payload1 = new byte[100]; - random.nextBytes(payload1); - byte[] payload2 = new byte[100]; - random.nextBytes(payload2); - byte[] payload3 = new byte[100]; - random.nextBytes(payload3); - - BinaryWebSocketFrame frame1 = new BinaryWebSocketFrame(false, - WebSocketExtension.RSV3, Unpooled.wrappedBuffer(payload1)); - ContinuationWebSocketFrame frame2 = new ContinuationWebSocketFrame(false, - WebSocketExtension.RSV3, Unpooled.wrappedBuffer(payload2)); - ContinuationWebSocketFrame frame3 = new ContinuationWebSocketFrame(true, - WebSocketExtension.RSV3, Unpooled.wrappedBuffer(payload3)); - - // execute - assertTrue(encoderChannel.writeOutbound(frame1)); - assertTrue(encoderChannel.writeOutbound(frame2)); - assertTrue(encoderChannel.writeOutbound(frame3)); - BinaryWebSocketFrame compressedFrame1 = encoderChannel.readOutbound(); - ContinuationWebSocketFrame compressedFrame2 = encoderChannel.readOutbound(); - ContinuationWebSocketFrame compressedFrame3 = encoderChannel.readOutbound(); - - // test - assertNotNull(compressedFrame1); - assertNotNull(compressedFrame2); - assertNotNull(compressedFrame3); - assertEquals(WebSocketExtension.RSV1 | WebSocketExtension.RSV3, compressedFrame1.rsv()); - assertEquals(WebSocketExtension.RSV1 | WebSocketExtension.RSV3, compressedFrame2.rsv()); - assertEquals(WebSocketExtension.RSV1 | WebSocketExtension.RSV3, compressedFrame3.rsv()); - assertFalse(compressedFrame1.isFinalFragment()); - assertFalse(compressedFrame2.isFinalFragment()); - assertTrue(compressedFrame3.isFinalFragment()); - - assertTrue(decoderChannel.writeInbound(compressedFrame1.content())); - assertTrue(decoderChannel.writeInbound(DeflateDecoder.FRAME_TAIL.duplicate())); - ByteBuf uncompressedPayload1 = decoderChannel.readInbound(); - byte[] finalPayload1 = new byte[100]; - uncompressedPayload1.readBytes(finalPayload1); - assertArrayEquals(finalPayload1, payload1); - uncompressedPayload1.release(); - - assertTrue(decoderChannel.writeInbound(compressedFrame2.content())); - assertTrue(decoderChannel.writeInbound(DeflateDecoder.FRAME_TAIL.duplicate())); - ByteBuf uncompressedPayload2 = decoderChannel.readInbound(); - byte[] finalPayload2 = new byte[100]; - uncompressedPayload2.readBytes(finalPayload2); - assertArrayEquals(finalPayload2, payload2); - uncompressedPayload2.release(); - - assertTrue(decoderChannel.writeInbound(compressedFrame3.content())); - assertTrue(decoderChannel.writeInbound(DeflateDecoder.FRAME_TAIL.duplicate())); - ByteBuf uncompressedPayload3 = decoderChannel.readInbound(); - byte[] finalPayload3 = new byte[100]; - uncompressedPayload3.readBytes(finalPayload3); - assertArrayEquals(finalPayload3, payload3); - uncompressedPayload3.release(); - } - - @Test - public void testCompressionSkip() { - EmbeddedChannel encoderChannel = new EmbeddedChannel( - new PerFrameDeflateEncoder(9, 15, false, ALWAYS_SKIP)); - byte[] payload = new byte[300]; - random.nextBytes(payload); - BinaryWebSocketFrame binaryFrame = new BinaryWebSocketFrame(true, - 0, Unpooled.wrappedBuffer(payload)); - - // execute - assertTrue(encoderChannel.writeOutbound(binaryFrame.copy())); - BinaryWebSocketFrame outboundFrame = encoderChannel.readOutbound(); - - // test - assertNotNull(outboundFrame); - assertNotNull(outboundFrame.content()); - assertArrayEquals(payload, ByteBufUtil.getBytes(outboundFrame.content())); - assertEquals(0, outboundFrame.rsv()); - assertTrue(outboundFrame.release()); - - assertFalse(encoderChannel.finish()); - } - -} 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 deleted file mode 100644 index a30b19e3c5..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateClientExtensionHandshakerTest.java +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.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.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -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; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import org.junit.jupiter.api.Test; - -public class PerMessageDeflateClientExtensionHandshakerTest { - - @Test - public void testNormalData() { - PerMessageDeflateClientExtensionHandshaker handshaker = - new PerMessageDeflateClientExtensionHandshaker(); - - WebSocketExtensionData data = handshaker.newRequestData(); - - assertEquals(PERMESSAGE_DEFLATE_EXTENSION, data.name()); - assertEquals(ZlibCodecFactory.isSupportingWindowSizeAndMemLevel() ? 1 : 0, data.parameters().size()); - } - - @Test - public void testCustomData() { - PerMessageDeflateClientExtensionHandshaker handshaker = - new PerMessageDeflateClientExtensionHandshaker(6, true, 10, true, true); - - WebSocketExtensionData data = handshaker.newRequestData(); - - assertEquals(PERMESSAGE_DEFLATE_EXTENSION, data.name()); - assertTrue(data.parameters().containsKey(CLIENT_MAX_WINDOW)); - assertTrue(data.parameters().containsKey(SERVER_MAX_WINDOW)); - assertEquals("10", data.parameters().get(SERVER_MAX_WINDOW)); - assertTrue(data.parameters().containsKey(CLIENT_MAX_WINDOW)); - assertTrue(data.parameters().containsKey(SERVER_MAX_WINDOW)); - } - - @Test - public void testNormalHandshake() { - PerMessageDeflateClientExtensionHandshaker handshaker = - new PerMessageDeflateClientExtensionHandshaker(); - - WebSocketClientExtension extension = handshaker.handshakeExtension( - new WebSocketExtensionData(PERMESSAGE_DEFLATE_EXTENSION, Collections.emptyMap())); - - assertNotNull(extension); - assertEquals(RSV1, extension.rsv()); - assertTrue(extension.newExtensionDecoder() instanceof PerMessageDeflateDecoder); - assertTrue(extension.newExtensionEncoder() instanceof PerMessageDeflateEncoder); - } - - @Test - public void testCustomHandshake() { - WebSocketClientExtension extension; - Map parameters; - - // initialize - PerMessageDeflateClientExtensionHandshaker handshaker = - new PerMessageDeflateClientExtensionHandshaker(6, true, 10, true, true); - - parameters = new HashMap<>(); - parameters.put(CLIENT_MAX_WINDOW, "12"); - parameters.put(SERVER_MAX_WINDOW, "8"); - parameters.put(CLIENT_NO_CONTEXT, null); - parameters.put(SERVER_NO_CONTEXT, null); - - // execute - extension = handshaker.handshakeExtension( - new WebSocketExtensionData(PERMESSAGE_DEFLATE_EXTENSION, parameters)); - - // test - assertNotNull(extension); - assertEquals(RSV1, extension.rsv()); - assertTrue(extension.newExtensionDecoder() instanceof PerMessageDeflateDecoder); - assertTrue(extension.newExtensionEncoder() instanceof PerMessageDeflateEncoder); - - // initialize - parameters = new HashMap<>(); - parameters.put(SERVER_MAX_WINDOW, "10"); - parameters.put(SERVER_NO_CONTEXT, null); - - // execute - extension = handshaker.handshakeExtension( - new WebSocketExtensionData(PERMESSAGE_DEFLATE_EXTENSION, parameters)); - - // test - assertNotNull(extension); - assertEquals(RSV1, extension.rsv()); - assertTrue(extension.newExtensionDecoder() instanceof PerMessageDeflateDecoder); - assertTrue(extension.newExtensionEncoder() instanceof PerMessageDeflateEncoder); - - // initialize - parameters = new HashMap<>(); - - // execute - extension = handshaker.handshakeExtension( - new WebSocketExtensionData(PERMESSAGE_DEFLATE_EXTENSION, parameters)); - - // test - assertNull(extension); - } - - @Test - public void testParameterValidation() { - WebSocketClientExtension extension; - Map parameters; - - PerMessageDeflateClientExtensionHandshaker handshaker = - new PerMessageDeflateClientExtensionHandshaker(6, true, 15, true, false); - - parameters = new HashMap(); - parameters.put(CLIENT_MAX_WINDOW, "15"); - parameters.put(SERVER_MAX_WINDOW, "8"); - extension = handshaker.handshakeExtension(new WebSocketExtensionData(PERMESSAGE_DEFLATE_EXTENSION, parameters)); - - // Test that handshake succeeds when parameters are valid - assertNotNull(extension); - assertEquals(RSV1, extension.rsv()); - assertTrue(extension.newExtensionDecoder() instanceof PerMessageDeflateDecoder); - assertTrue(extension.newExtensionEncoder() instanceof PerMessageDeflateEncoder); - - parameters = new HashMap(); - parameters.put(CLIENT_MAX_WINDOW, "15"); - parameters.put(SERVER_MAX_WINDOW, "7"); - - extension = handshaker.handshakeExtension(new WebSocketExtensionData(PERMESSAGE_DEFLATE_EXTENSION, parameters)); - - // Test that handshake fails when parameters are invalid - assertNull(extension); - } - - @Test - public void testServerNoContextTakeover() { - WebSocketClientExtension extension; - Map parameters; - - PerMessageDeflateClientExtensionHandshaker handshaker = - new PerMessageDeflateClientExtensionHandshaker(6, true, 15, true, false); - - parameters = new HashMap(); - parameters.put(SERVER_NO_CONTEXT, null); - extension = handshaker.handshakeExtension(new WebSocketExtensionData(PERMESSAGE_DEFLATE_EXTENSION, parameters)); - - // Test that handshake succeeds when server responds with `server_no_context_takeover` that we didn't offer - assertNotNull(extension); - assertEquals(RSV1, extension.rsv()); - assertTrue(extension.newExtensionDecoder() instanceof PerMessageDeflateDecoder); - assertTrue(extension.newExtensionEncoder() instanceof PerMessageDeflateEncoder); - - // initialize - handshaker = new PerMessageDeflateClientExtensionHandshaker(6, true, 15, true, true); - - parameters = new HashMap(); - extension = handshaker.handshakeExtension(new WebSocketExtensionData(PERMESSAGE_DEFLATE_EXTENSION, parameters)); - - // Test that handshake fails when client offers `server_no_context_takeover` but server doesn't support it - 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()); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateDecoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateDecoderTest.java deleted file mode 100644 index 7f354669ba..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateDecoderTest.java +++ /dev/null @@ -1,385 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx.extensions.compression; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.DecoderException; -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.ContinuationWebSocketFrame; -import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; -import io.netty.handler.codec.http.websocketx.WebSocketFrame; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtension; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionFilter; -import org.junit.jupiter.api.Test; - -import java.util.Random; - -import static io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionFilter.*; -import static io.netty.handler.codec.http.websocketx.extensions.compression.DeflateDecoder.*; -import static io.netty.util.CharsetUtil.*; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class PerMessageDeflateDecoderTest { - - private static final Random random = new Random(); - - @Test - public void testCompressedFrame() { - EmbeddedChannel encoderChannel = new EmbeddedChannel( - ZlibCodecFactory.newZlibEncoder(ZlibWrapper.NONE, 9, 15, 8)); - EmbeddedChannel decoderChannel = new EmbeddedChannel(new PerMessageDeflateDecoder(false)); - - // initialize - byte[] payload = new byte[300]; - random.nextBytes(payload); - - assertTrue(encoderChannel.writeOutbound(Unpooled.wrappedBuffer(payload))); - ByteBuf compressedPayload = encoderChannel.readOutbound(); - - BinaryWebSocketFrame compressedFrame = new BinaryWebSocketFrame(true, - WebSocketExtension.RSV1 | WebSocketExtension.RSV3, - compressedPayload.slice(0, compressedPayload.readableBytes() - 4)); - - // execute - assertTrue(decoderChannel.writeInbound(compressedFrame)); - BinaryWebSocketFrame uncompressedFrame = decoderChannel.readInbound(); - - // test - assertNotNull(uncompressedFrame); - assertNotNull(uncompressedFrame.content()); - assertEquals(WebSocketExtension.RSV3, uncompressedFrame.rsv()); - assertEquals(300, uncompressedFrame.content().readableBytes()); - - byte[] finalPayload = new byte[300]; - uncompressedFrame.content().readBytes(finalPayload); - assertArrayEquals(finalPayload, payload); - uncompressedFrame.release(); - } - - @Test - public void testNormalFrame() { - EmbeddedChannel decoderChannel = new EmbeddedChannel(new PerMessageDeflateDecoder(false)); - - // initialize - byte[] payload = new byte[300]; - random.nextBytes(payload); - - BinaryWebSocketFrame frame = new BinaryWebSocketFrame(true, - WebSocketExtension.RSV3, Unpooled.wrappedBuffer(payload)); - - // execute - assertTrue(decoderChannel.writeInbound(frame)); - BinaryWebSocketFrame newFrame = decoderChannel.readInbound(); - - // test - assertNotNull(newFrame); - assertNotNull(newFrame.content()); - assertEquals(WebSocketExtension.RSV3, newFrame.rsv()); - assertEquals(300, newFrame.content().readableBytes()); - - byte[] finalPayload = new byte[300]; - newFrame.content().readBytes(finalPayload); - assertArrayEquals(finalPayload, payload); - newFrame.release(); - } - - @Test - public void testFragmentedFrame() { - EmbeddedChannel encoderChannel = new EmbeddedChannel( - ZlibCodecFactory.newZlibEncoder(ZlibWrapper.NONE, 9, 15, 8)); - EmbeddedChannel decoderChannel = new EmbeddedChannel(new PerMessageDeflateDecoder(false)); - - // initialize - byte[] payload = new byte[300]; - random.nextBytes(payload); - - assertTrue(encoderChannel.writeOutbound(Unpooled.wrappedBuffer(payload))); - ByteBuf compressedPayload = encoderChannel.readOutbound(); - compressedPayload = compressedPayload.slice(0, compressedPayload.readableBytes() - 4); - - int oneThird = compressedPayload.readableBytes() / 3; - BinaryWebSocketFrame compressedFrame1 = new BinaryWebSocketFrame(false, - WebSocketExtension.RSV1 | WebSocketExtension.RSV3, - compressedPayload.slice(0, oneThird)); - ContinuationWebSocketFrame compressedFrame2 = new ContinuationWebSocketFrame(false, - WebSocketExtension.RSV3, compressedPayload.slice(oneThird, oneThird)); - ContinuationWebSocketFrame compressedFrame3 = new ContinuationWebSocketFrame(true, - WebSocketExtension.RSV3, compressedPayload.slice(oneThird * 2, - compressedPayload.readableBytes() - oneThird * 2)); - - // execute - assertTrue(decoderChannel.writeInbound(compressedFrame1.retain())); - assertTrue(decoderChannel.writeInbound(compressedFrame2.retain())); - assertTrue(decoderChannel.writeInbound(compressedFrame3)); - BinaryWebSocketFrame uncompressedFrame1 = decoderChannel.readInbound(); - ContinuationWebSocketFrame uncompressedFrame2 = decoderChannel.readInbound(); - ContinuationWebSocketFrame uncompressedFrame3 = decoderChannel.readInbound(); - - // test - assertNotNull(uncompressedFrame1); - assertNotNull(uncompressedFrame2); - assertNotNull(uncompressedFrame3); - assertEquals(WebSocketExtension.RSV3, uncompressedFrame1.rsv()); - assertEquals(WebSocketExtension.RSV3, uncompressedFrame2.rsv()); - assertEquals(WebSocketExtension.RSV3, uncompressedFrame3.rsv()); - - ByteBuf finalPayloadWrapped = Unpooled.wrappedBuffer(uncompressedFrame1.content(), - uncompressedFrame2.content(), uncompressedFrame3.content()); - assertEquals(300, finalPayloadWrapped.readableBytes()); - - byte[] finalPayload = new byte[300]; - finalPayloadWrapped.readBytes(finalPayload); - assertArrayEquals(finalPayload, payload); - finalPayloadWrapped.release(); - } - - @Test - public void testMultiCompressedPayloadWithinFrame() { - EmbeddedChannel encoderChannel = new EmbeddedChannel( - ZlibCodecFactory.newZlibEncoder(ZlibWrapper.NONE, 9, 15, 8)); - EmbeddedChannel decoderChannel = new EmbeddedChannel(new PerMessageDeflateDecoder(false)); - - // initialize - byte[] payload1 = new byte[100]; - random.nextBytes(payload1); - byte[] payload2 = new byte[100]; - random.nextBytes(payload2); - - assertTrue(encoderChannel.writeOutbound(Unpooled.wrappedBuffer(payload1))); - ByteBuf compressedPayload1 = encoderChannel.readOutbound(); - assertTrue(encoderChannel.writeOutbound(Unpooled.wrappedBuffer(payload2))); - ByteBuf compressedPayload2 = encoderChannel.readOutbound(); - - BinaryWebSocketFrame compressedFrame = new BinaryWebSocketFrame(true, - WebSocketExtension.RSV1 | WebSocketExtension.RSV3, - Unpooled.wrappedBuffer( - compressedPayload1, - compressedPayload2.slice(0, compressedPayload2.readableBytes() - 4))); - - // execute - assertTrue(decoderChannel.writeInbound(compressedFrame)); - BinaryWebSocketFrame uncompressedFrame = decoderChannel.readInbound(); - - // test - assertNotNull(uncompressedFrame); - assertNotNull(uncompressedFrame.content()); - assertEquals(WebSocketExtension.RSV3, uncompressedFrame.rsv()); - assertEquals(200, uncompressedFrame.content().readableBytes()); - - byte[] finalPayload1 = new byte[100]; - uncompressedFrame.content().readBytes(finalPayload1); - assertArrayEquals(finalPayload1, payload1); - byte[] finalPayload2 = new byte[100]; - uncompressedFrame.content().readBytes(finalPayload2); - assertArrayEquals(finalPayload2, payload2); - uncompressedFrame.release(); - } - - @Test - public void testDecompressionSkipForBinaryFrame() { - EmbeddedChannel encoderChannel = new EmbeddedChannel( - ZlibCodecFactory.newZlibEncoder(ZlibWrapper.NONE, 9, 15, 8)); - EmbeddedChannel decoderChannel = new EmbeddedChannel(new PerMessageDeflateDecoder(false, ALWAYS_SKIP)); - - byte[] payload = new byte[300]; - random.nextBytes(payload); - - assertTrue(encoderChannel.writeOutbound(Unpooled.wrappedBuffer(payload))); - ByteBuf compressedPayload = encoderChannel.readOutbound(); - - BinaryWebSocketFrame compressedBinaryFrame = new BinaryWebSocketFrame(true, WebSocketExtension.RSV1, - compressedPayload); - assertTrue(decoderChannel.writeInbound(compressedBinaryFrame)); - - WebSocketFrame inboundFrame = decoderChannel.readInbound(); - - assertEquals(WebSocketExtension.RSV1, inboundFrame.rsv()); - assertEquals(compressedPayload, inboundFrame.content()); - assertTrue(inboundFrame.release()); - - assertTrue(encoderChannel.finishAndReleaseAll()); - assertFalse(decoderChannel.finish()); - } - - @Test - public void testSelectivityDecompressionSkip() { - WebSocketExtensionFilter selectivityDecompressionFilter = - frame -> frame instanceof TextWebSocketFrame && frame.content().readableBytes() < 100; - EmbeddedChannel encoderChannel = new EmbeddedChannel( - ZlibCodecFactory.newZlibEncoder(ZlibWrapper.NONE, 9, 15, 8)); - EmbeddedChannel decoderChannel = new EmbeddedChannel( - new PerMessageDeflateDecoder(false, selectivityDecompressionFilter)); - - String textPayload = "compressed payload"; - byte[] binaryPayload = new byte[300]; - random.nextBytes(binaryPayload); - - assertTrue(encoderChannel.writeOutbound(Unpooled.wrappedBuffer(textPayload.getBytes(UTF_8)))); - assertTrue(encoderChannel.writeOutbound(Unpooled.wrappedBuffer(binaryPayload))); - ByteBuf compressedTextPayload = encoderChannel.readOutbound(); - ByteBuf compressedBinaryPayload = encoderChannel.readOutbound(); - - TextWebSocketFrame compressedTextFrame = new TextWebSocketFrame(true, WebSocketExtension.RSV1, - compressedTextPayload); - BinaryWebSocketFrame compressedBinaryFrame = new BinaryWebSocketFrame(true, WebSocketExtension.RSV1, - compressedBinaryPayload); - - assertTrue(decoderChannel.writeInbound(compressedTextFrame)); - assertTrue(decoderChannel.writeInbound(compressedBinaryFrame)); - - TextWebSocketFrame inboundTextFrame = decoderChannel.readInbound(); - BinaryWebSocketFrame inboundBinaryFrame = decoderChannel.readInbound(); - - assertEquals(WebSocketExtension.RSV1, inboundTextFrame.rsv()); - assertEquals(compressedTextPayload, inboundTextFrame.content()); - assertTrue(inboundTextFrame.release()); - - assertEquals(0, inboundBinaryFrame.rsv()); - assertArrayEquals(binaryPayload, ByteBufUtil.getBytes(inboundBinaryFrame.content())); - assertTrue(inboundBinaryFrame.release()); - - assertTrue(encoderChannel.finishAndReleaseAll()); - assertFalse(decoderChannel.finish()); - } - - @Test - public void testIllegalStateWhenDecompressionInProgress() { - WebSocketExtensionFilter selectivityDecompressionFilter = frame -> frame.content().readableBytes() < 100; - - EmbeddedChannel encoderChannel = new EmbeddedChannel( - ZlibCodecFactory.newZlibEncoder(ZlibWrapper.NONE, 9, 15, 8)); - EmbeddedChannel decoderChannel = new EmbeddedChannel( - new PerMessageDeflateDecoder(false, selectivityDecompressionFilter)); - - byte[] firstPayload = new byte[200]; - random.nextBytes(firstPayload); - - byte[] finalPayload = new byte[50]; - random.nextBytes(finalPayload); - - assertTrue(encoderChannel.writeOutbound(Unpooled.wrappedBuffer(firstPayload))); - assertTrue(encoderChannel.writeOutbound(Unpooled.wrappedBuffer(finalPayload))); - ByteBuf compressedFirstPayload = encoderChannel.readOutbound(); - ByteBuf compressedFinalPayload = encoderChannel.readOutbound(); - assertTrue(encoderChannel.finishAndReleaseAll()); - - BinaryWebSocketFrame firstPart = new BinaryWebSocketFrame(false, WebSocketExtension.RSV1, - compressedFirstPayload); - ContinuationWebSocketFrame finalPart = new ContinuationWebSocketFrame(true, WebSocketExtension.RSV1, - compressedFinalPayload); - assertTrue(decoderChannel.writeInbound(firstPart)); - - BinaryWebSocketFrame outboundFirstPart = decoderChannel.readInbound(); - //first part is decompressed - assertEquals(0, outboundFirstPart.rsv()); - assertArrayEquals(firstPayload, ByteBufUtil.getBytes(outboundFirstPart.content())); - assertTrue(outboundFirstPart.release()); - - //final part throwing exception - try { - assertThrows(DecoderException.class, () -> decoderChannel.writeInbound(finalPart)); - } finally { - assertTrue(finalPart.release()); - assertFalse(encoderChannel.finishAndReleaseAll()); - } - } - - @Test - public void testEmptyFrameDecompression() { - EmbeddedChannel decoderChannel = new EmbeddedChannel(new PerMessageDeflateDecoder(false)); - - TextWebSocketFrame emptyDeflateBlockFrame = new TextWebSocketFrame(true, WebSocketExtension.RSV1, - EMPTY_DEFLATE_BLOCK); - - assertTrue(decoderChannel.writeInbound(emptyDeflateBlockFrame)); - TextWebSocketFrame emptyBufferFrame = decoderChannel.readInbound(); - - assertFalse(emptyBufferFrame.content().isReadable()); - - // Composite empty buffer - assertTrue(emptyBufferFrame.release()); - assertFalse(decoderChannel.finish()); - } - - @Test - public void testFragmentedFrameWithLeftOverInLastFragment() { - String hexDump = "677170647a777a737574656b707a787a6f6a7561756578756f6b7868616371716c657a6d64697479766d726f6" + - "269746c6376777464776f6f72767a726f64667278676764687775786f6762766d776d706b76697773777a7072" + - "6a6a737279707a7078697a6c69616d7461656d646278626d786f66666e686e776a7a7461746d7a776668776b6" + - "f6f736e73746575637a6d727a7175707a6e74627578687871767771697a71766c64626d78726d6d7675756877" + - "62667963626b687a726d676e646263776e67797264706d6c6863626577616967706a78636a72697464756e627" + - "977616f79736475676f76736f7178746a7a7479626c64636b6b6778637768746c62"; - EmbeddedChannel encoderChannel = new EmbeddedChannel( - ZlibCodecFactory.newZlibEncoder(ZlibWrapper.NONE, 9, 15, 8)); - EmbeddedChannel decoderChannel = new EmbeddedChannel(new PerMessageDeflateDecoder(false)); - - ByteBuf originPayload = Unpooled.wrappedBuffer(ByteBufUtil.decodeHexDump(hexDump)); - assertTrue(encoderChannel.writeOutbound(originPayload.duplicate().retain())); - - ByteBuf compressedPayload = encoderChannel.readOutbound(); - compressedPayload = compressedPayload.slice(0, compressedPayload.readableBytes() - 4); - - int oneThird = compressedPayload.readableBytes() / 3; - - TextWebSocketFrame compressedFrame1 = new TextWebSocketFrame( - false, WebSocketExtension.RSV1, compressedPayload.slice(0, oneThird)); - ContinuationWebSocketFrame compressedFrame2 = new ContinuationWebSocketFrame( - false, WebSocketExtension.RSV3, compressedPayload.slice(oneThird, oneThird)); - ContinuationWebSocketFrame compressedFrame3 = new ContinuationWebSocketFrame( - false, WebSocketExtension.RSV3, compressedPayload.slice(oneThird * 2, oneThird)); - int offset = oneThird * 3; - ContinuationWebSocketFrame compressedFrameWithExtraData = new ContinuationWebSocketFrame( - true, WebSocketExtension.RSV3, compressedPayload.slice(offset, - compressedPayload.readableBytes() - offset)); - - // check that last fragment contains only one extra byte - assertEquals(1, compressedFrameWithExtraData.content().readableBytes()); - assertEquals(1, compressedFrameWithExtraData.content().getByte(0)); - - // write compressed frames - assertTrue(decoderChannel.writeInbound(compressedFrame1.retain())); - assertTrue(decoderChannel.writeInbound(compressedFrame2.retain())); - assertTrue(decoderChannel.writeInbound(compressedFrame3.retain())); - assertTrue(decoderChannel.writeInbound(compressedFrameWithExtraData)); - - // read uncompressed frames - TextWebSocketFrame uncompressedFrame1 = decoderChannel.readInbound(); - ContinuationWebSocketFrame uncompressedFrame2 = decoderChannel.readInbound(); - ContinuationWebSocketFrame uncompressedFrame3 = decoderChannel.readInbound(); - ContinuationWebSocketFrame uncompressedExtraData = decoderChannel.readInbound(); - assertFalse(uncompressedExtraData.content().isReadable()); - - ByteBuf uncompressedPayload = Unpooled.wrappedBuffer(uncompressedFrame1.content(), uncompressedFrame2.content(), - uncompressedFrame3.content(), uncompressedExtraData.content()); - assertEquals(originPayload, uncompressedPayload); - - assertTrue(originPayload.release()); - assertTrue(uncompressedPayload.release()); - - assertTrue(encoderChannel.finishAndReleaseAll()); - assertFalse(decoderChannel.finish()); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateEncoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateEncoderTest.java deleted file mode 100644 index 97e41def27..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateEncoderTest.java +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx.extensions.compression; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.EncoderException; -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.ContinuationWebSocketFrame; -import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; -import io.netty.handler.codec.http.websocketx.WebSocketFrame; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtension; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionFilter; -import org.junit.jupiter.api.Test; - -import java.util.Arrays; -import java.util.Random; - -import static io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionFilter.*; -import static io.netty.handler.codec.http.websocketx.extensions.compression.DeflateDecoder.*; -import static io.netty.util.CharsetUtil.*; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class PerMessageDeflateEncoderTest { - - private static final Random random = new Random(); - - @Test - public void testCompressedFrame() { - EmbeddedChannel encoderChannel = new EmbeddedChannel(new PerMessageDeflateEncoder(9, 15, false)); - EmbeddedChannel decoderChannel = new EmbeddedChannel( - ZlibCodecFactory.newZlibDecoder(ZlibWrapper.NONE)); - - // initialize - byte[] payload = new byte[300]; - random.nextBytes(payload); - BinaryWebSocketFrame frame = new BinaryWebSocketFrame(true, - WebSocketExtension.RSV3, Unpooled.wrappedBuffer(payload)); - - // execute - assertTrue(encoderChannel.writeOutbound(frame)); - BinaryWebSocketFrame compressedFrame = encoderChannel.readOutbound(); - - // test - assertNotNull(compressedFrame); - assertNotNull(compressedFrame.content()); - assertEquals(WebSocketExtension.RSV1 | WebSocketExtension.RSV3, compressedFrame.rsv()); - - assertTrue(decoderChannel.writeInbound(compressedFrame.content())); - assertTrue(decoderChannel.writeInbound(DeflateDecoder.FRAME_TAIL.duplicate())); - ByteBuf uncompressedPayload = decoderChannel.readInbound(); - assertEquals(300, uncompressedPayload.readableBytes()); - - byte[] finalPayload = new byte[300]; - uncompressedPayload.readBytes(finalPayload); - assertArrayEquals(finalPayload, payload); - uncompressedPayload.release(); - } - - @Test - public void testAlreadyCompressedFrame() { - EmbeddedChannel encoderChannel = new EmbeddedChannel(new PerMessageDeflateEncoder(9, 15, false)); - - // initialize - byte[] payload = new byte[300]; - random.nextBytes(payload); - - BinaryWebSocketFrame frame = new BinaryWebSocketFrame(true, - WebSocketExtension.RSV3 | WebSocketExtension.RSV1, - Unpooled.wrappedBuffer(payload)); - - // execute - assertTrue(encoderChannel.writeOutbound(frame)); - BinaryWebSocketFrame newFrame = encoderChannel.readOutbound(); - - // test - assertNotNull(newFrame); - assertNotNull(newFrame.content()); - assertEquals(WebSocketExtension.RSV3 | WebSocketExtension.RSV1, newFrame.rsv()); - assertEquals(300, newFrame.content().readableBytes()); - - byte[] finalPayload = new byte[300]; - newFrame.content().readBytes(finalPayload); - assertArrayEquals(finalPayload, payload); - newFrame.release(); - } - - @Test - public void testFragmentedFrame() { - EmbeddedChannel encoderChannel = new EmbeddedChannel(new PerMessageDeflateEncoder(9, 15, false, - NEVER_SKIP)); - EmbeddedChannel decoderChannel = new EmbeddedChannel( - ZlibCodecFactory.newZlibDecoder(ZlibWrapper.NONE)); - - // initialize - byte[] payload1 = new byte[100]; - random.nextBytes(payload1); - byte[] payload2 = new byte[100]; - random.nextBytes(payload2); - byte[] payload3 = new byte[100]; - random.nextBytes(payload3); - - BinaryWebSocketFrame frame1 = new BinaryWebSocketFrame(false, - WebSocketExtension.RSV3, - Unpooled.wrappedBuffer(payload1)); - ContinuationWebSocketFrame frame2 = new ContinuationWebSocketFrame(false, - WebSocketExtension.RSV3, - Unpooled.wrappedBuffer(payload2)); - ContinuationWebSocketFrame frame3 = new ContinuationWebSocketFrame(true, - WebSocketExtension.RSV3, - Unpooled.wrappedBuffer(payload3)); - - // execute - assertTrue(encoderChannel.writeOutbound(frame1)); - assertTrue(encoderChannel.writeOutbound(frame2)); - assertTrue(encoderChannel.writeOutbound(frame3)); - BinaryWebSocketFrame compressedFrame1 = encoderChannel.readOutbound(); - ContinuationWebSocketFrame compressedFrame2 = encoderChannel.readOutbound(); - ContinuationWebSocketFrame compressedFrame3 = encoderChannel.readOutbound(); - - // test - assertNotNull(compressedFrame1); - assertNotNull(compressedFrame2); - assertNotNull(compressedFrame3); - assertEquals(WebSocketExtension.RSV1 | WebSocketExtension.RSV3, compressedFrame1.rsv()); - assertEquals(WebSocketExtension.RSV3, compressedFrame2.rsv()); - assertEquals(WebSocketExtension.RSV3, compressedFrame3.rsv()); - assertFalse(compressedFrame1.isFinalFragment()); - assertFalse(compressedFrame2.isFinalFragment()); - assertTrue(compressedFrame3.isFinalFragment()); - - assertTrue(decoderChannel.writeInbound(compressedFrame1.content())); - ByteBuf uncompressedPayload1 = decoderChannel.readInbound(); - byte[] finalPayload1 = new byte[100]; - uncompressedPayload1.readBytes(finalPayload1); - assertArrayEquals(finalPayload1, payload1); - uncompressedPayload1.release(); - - assertTrue(decoderChannel.writeInbound(compressedFrame2.content())); - ByteBuf uncompressedPayload2 = decoderChannel.readInbound(); - byte[] finalPayload2 = new byte[100]; - uncompressedPayload2.readBytes(finalPayload2); - assertArrayEquals(finalPayload2, payload2); - uncompressedPayload2.release(); - - assertTrue(decoderChannel.writeInbound(compressedFrame3.content())); - assertTrue(decoderChannel.writeInbound(DeflateDecoder.FRAME_TAIL.duplicate())); - ByteBuf uncompressedPayload3 = decoderChannel.readInbound(); - byte[] finalPayload3 = new byte[100]; - uncompressedPayload3.readBytes(finalPayload3); - assertArrayEquals(finalPayload3, payload3); - uncompressedPayload3.release(); - } - - @Test - public void testCompressionSkipForBinaryFrame() { - EmbeddedChannel encoderChannel = new EmbeddedChannel(new PerMessageDeflateEncoder(9, 15, false, - ALWAYS_SKIP)); - byte[] payload = new byte[300]; - random.nextBytes(payload); - - WebSocketFrame binaryFrame = new BinaryWebSocketFrame(Unpooled.wrappedBuffer(payload)); - - assertTrue(encoderChannel.writeOutbound(binaryFrame.copy())); - WebSocketFrame outboundFrame = encoderChannel.readOutbound(); - - assertEquals(0, outboundFrame.rsv()); - assertArrayEquals(payload, ByteBufUtil.getBytes(outboundFrame.content())); - assertTrue(outboundFrame.release()); - - assertFalse(encoderChannel.finish()); - } - - @Test - public void testSelectivityCompressionSkip() { - WebSocketExtensionFilter selectivityCompressionFilter = - frame -> (frame instanceof TextWebSocketFrame || frame instanceof BinaryWebSocketFrame) - && frame.content().readableBytes() < 100; - EmbeddedChannel encoderChannel = new EmbeddedChannel( - new PerMessageDeflateEncoder(9, 15, false, selectivityCompressionFilter)); - EmbeddedChannel decoderChannel = new EmbeddedChannel( - ZlibCodecFactory.newZlibDecoder(ZlibWrapper.NONE)); - - String textPayload = "not compressed payload"; - byte[] binaryPayload = new byte[101]; - random.nextBytes(binaryPayload); - - WebSocketFrame textFrame = new TextWebSocketFrame(textPayload); - BinaryWebSocketFrame binaryFrame = new BinaryWebSocketFrame(Unpooled.wrappedBuffer(binaryPayload)); - - assertTrue(encoderChannel.writeOutbound(textFrame)); - assertTrue(encoderChannel.writeOutbound(binaryFrame)); - - WebSocketFrame outboundTextFrame = encoderChannel.readOutbound(); - - //compression skipped for textFrame - assertEquals(0, outboundTextFrame.rsv()); - assertEquals(textPayload, outboundTextFrame.content().toString(UTF_8)); - assertTrue(outboundTextFrame.release()); - - WebSocketFrame outboundBinaryFrame = encoderChannel.readOutbound(); - - //compression not skipped for binaryFrame - assertEquals(WebSocketExtension.RSV1, outboundBinaryFrame.rsv()); - - assertTrue(decoderChannel.writeInbound(outboundBinaryFrame.content().retain())); - ByteBuf uncompressedBinaryPayload = decoderChannel.readInbound(); - - assertArrayEquals(binaryPayload, ByteBufUtil.getBytes(uncompressedBinaryPayload)); - - assertTrue(outboundBinaryFrame.release()); - assertTrue(uncompressedBinaryPayload.release()); - - assertFalse(encoderChannel.finish()); - assertFalse(decoderChannel.finish()); - } - - @Test - public void testIllegalStateWhenCompressionInProgress() { - WebSocketExtensionFilter selectivityCompressionFilter = frame -> frame.content().readableBytes() < 100; - EmbeddedChannel encoderChannel = new EmbeddedChannel( - new PerMessageDeflateEncoder(9, 15, false, selectivityCompressionFilter)); - - byte[] firstPayload = new byte[200]; - random.nextBytes(firstPayload); - - byte[] finalPayload = new byte[90]; - random.nextBytes(finalPayload); - - BinaryWebSocketFrame firstPart = new BinaryWebSocketFrame(false, 0, Unpooled.wrappedBuffer(firstPayload)); - ContinuationWebSocketFrame finalPart = new ContinuationWebSocketFrame(true, 0, - Unpooled.wrappedBuffer(finalPayload)); - assertTrue(encoderChannel.writeOutbound(firstPart)); - - BinaryWebSocketFrame outboundFirstPart = encoderChannel.readOutbound(); - //first part is compressed - assertEquals(WebSocketExtension.RSV1, outboundFirstPart.rsv()); - assertFalse(Arrays.equals(firstPayload, ByteBufUtil.getBytes(outboundFirstPart.content()))); - assertTrue(outboundFirstPart.release()); - - //final part throwing exception - try { - assertThrows(EncoderException.class, () -> encoderChannel.writeOutbound(finalPart)); - } finally { - assertTrue(finalPart.release()); - assertFalse(encoderChannel.finishAndReleaseAll()); - } - } - - @Test - public void testEmptyFrameCompression() { - EmbeddedChannel encoderChannel = new EmbeddedChannel(new PerMessageDeflateEncoder(9, 15, false)); - - TextWebSocketFrame emptyFrame = new TextWebSocketFrame(""); - - assertTrue(encoderChannel.writeOutbound(emptyFrame)); - TextWebSocketFrame emptyDeflateFrame = encoderChannel.readOutbound(); - - assertEquals(WebSocketExtension.RSV1, emptyDeflateFrame.rsv()); - assertTrue(ByteBufUtil.equals(EMPTY_DEFLATE_BLOCK, emptyDeflateFrame.content())); - // Unreleasable buffer - assertFalse(emptyDeflateFrame.release()); - - assertFalse(encoderChannel.finish()); - } - - @Test - public void testCodecExceptionForNotFinEmptyFrame() { - EmbeddedChannel encoderChannel = new EmbeddedChannel(new PerMessageDeflateEncoder(9, 15, false)); - - TextWebSocketFrame emptyNotFinFrame = new TextWebSocketFrame(false, 0, ""); - - try { - assertThrows(EncoderException.class, () -> encoderChannel.writeOutbound(emptyNotFinFrame)); - } finally { - // EmptyByteBuf buffer - assertFalse(emptyNotFinFrame.release()); - assertFalse(encoderChannel.finish()); - } - } - -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateServerExtensionHandshakerTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateServerExtensionHandshakerTest.java deleted file mode 100644 index 8a23a0a828..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/compression/PerMessageDeflateServerExtensionHandshakerTest.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx.extensions.compression; - -import static io.netty.handler.codec.http.websocketx.extensions.compression. - PerMessageDeflateServerExtensionHandshaker.*; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import io.netty.handler.codec.http.websocketx.extensions.WebSocketServerExtension; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionData; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import org.junit.jupiter.api.Test; - -public class PerMessageDeflateServerExtensionHandshakerTest { - - @Test - public void testNormalHandshake() { - WebSocketServerExtension extension; - WebSocketExtensionData data; - Map parameters; - - // initialize - PerMessageDeflateServerExtensionHandshaker handshaker = - new PerMessageDeflateServerExtensionHandshaker(); - - // execute - extension = handshaker.handshakeExtension( - new WebSocketExtensionData(PERMESSAGE_DEFLATE_EXTENSION, Collections.emptyMap())); - - // test - assertNotNull(extension); - assertEquals(WebSocketServerExtension.RSV1, extension.rsv()); - assertTrue(extension.newExtensionDecoder() instanceof PerMessageDeflateDecoder); - assertTrue(extension.newExtensionEncoder() instanceof PerMessageDeflateEncoder); - - // execute - data = extension.newResponseData(); - - assertEquals(PERMESSAGE_DEFLATE_EXTENSION, data.name()); - assertTrue(data.parameters().isEmpty()); - - // initialize - parameters = new HashMap<>(); - parameters.put(CLIENT_MAX_WINDOW, null); - parameters.put(CLIENT_NO_CONTEXT, null); - - // execute - extension = handshaker.handshakeExtension( - new WebSocketExtensionData(PERMESSAGE_DEFLATE_EXTENSION, Collections.emptyMap())); - - // test - assertNotNull(extension); - assertEquals(WebSocketServerExtension.RSV1, extension.rsv()); - assertTrue(extension.newExtensionDecoder() instanceof PerMessageDeflateDecoder); - assertTrue(extension.newExtensionEncoder() instanceof PerMessageDeflateEncoder); - - // execute - data = extension.newResponseData(); - - // test - assertEquals(PERMESSAGE_DEFLATE_EXTENSION, data.name()); - assertTrue(data.parameters().isEmpty()); - - // initialize - parameters = new HashMap<>(); - parameters.put(SERVER_MAX_WINDOW, "12"); - parameters.put(SERVER_NO_CONTEXT, null); - - // execute - extension = handshaker.handshakeExtension( - new WebSocketExtensionData(PERMESSAGE_DEFLATE_EXTENSION, parameters)); - - // test - assertNull(extension); - } - - @Test - public void testCustomHandshake() { - WebSocketServerExtension extension; - Map parameters; - WebSocketExtensionData data; - - // initialize - PerMessageDeflateServerExtensionHandshaker handshaker = - new PerMessageDeflateServerExtensionHandshaker(6, true, 10, true, true); - - parameters = new HashMap<>(); - parameters.put(CLIENT_MAX_WINDOW, null); - parameters.put(SERVER_MAX_WINDOW, "12"); - parameters.put(CLIENT_NO_CONTEXT, null); - parameters.put(SERVER_NO_CONTEXT, null); - - // execute - extension = handshaker.handshakeExtension( - new WebSocketExtensionData(PERMESSAGE_DEFLATE_EXTENSION, parameters)); - - // test - assertNotNull(extension); - assertEquals(WebSocketServerExtension.RSV1, extension.rsv()); - assertTrue(extension.newExtensionDecoder() instanceof PerMessageDeflateDecoder); - assertTrue(extension.newExtensionEncoder() instanceof PerMessageDeflateEncoder); - - // execute - data = extension.newResponseData(); - - // test - assertEquals(PERMESSAGE_DEFLATE_EXTENSION, data.name()); - assertTrue(data.parameters().containsKey(CLIENT_MAX_WINDOW)); - assertEquals("10", data.parameters().get(CLIENT_MAX_WINDOW)); - assertTrue(data.parameters().containsKey(SERVER_MAX_WINDOW)); - assertEquals("12", data.parameters().get(SERVER_MAX_WINDOW)); - assertTrue(data.parameters().containsKey(CLIENT_MAX_WINDOW)); - assertTrue(data.parameters().containsKey(SERVER_MAX_WINDOW)); - - // initialize - parameters = new HashMap<>(); - parameters.put(SERVER_MAX_WINDOW, "12"); - parameters.put(SERVER_NO_CONTEXT, null); - - // execute - extension = handshaker.handshakeExtension( - new WebSocketExtensionData(PERMESSAGE_DEFLATE_EXTENSION, parameters)); - - // test - assertNotNull(extension); - assertEquals(WebSocketServerExtension.RSV1, extension.rsv()); - assertTrue(extension.newExtensionDecoder() instanceof PerMessageDeflateDecoder); - assertTrue(extension.newExtensionEncoder() instanceof PerMessageDeflateEncoder); - - // execute - data = extension.newResponseData(); - - // test - assertEquals(PERMESSAGE_DEFLATE_EXTENSION, data.name()); - assertEquals(2, data.parameters().size()); - assertTrue(data.parameters().containsKey(SERVER_MAX_WINDOW)); - assertEquals("12", data.parameters().get(SERVER_MAX_WINDOW)); - assertTrue(data.parameters().containsKey(SERVER_NO_CONTEXT)); - - // initialize - parameters = new HashMap<>(); - - // execute - extension = handshaker.handshakeExtension( - new WebSocketExtensionData(PERMESSAGE_DEFLATE_EXTENSION, parameters)); - // test - assertNotNull(extension); - - // execute - data = extension.newResponseData(); - - // test - assertEquals(PERMESSAGE_DEFLATE_EXTENSION, data.name()); - assertTrue(data.parameters().isEmpty()); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/compression/WebSocketServerCompressionHandlerTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/compression/WebSocketServerCompressionHandlerTest.java deleted file mode 100644 index ad3005f5b2..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/compression/WebSocketServerCompressionHandlerTest.java +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.websocketx.extensions.compression; - -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionData; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionUtil; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketServerExtensionHandler; - -import java.util.List; - -import org.junit.jupiter.api.Test; - -import static io.netty.handler.codec.http.websocketx.extensions.compression. - PerMessageDeflateServerExtensionHandshaker.*; -import static io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionTestUtil.*; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class WebSocketServerCompressionHandlerTest { - - @Test - public void testNormalSuccess() { - EmbeddedChannel ch = new EmbeddedChannel(new WebSocketServerCompressionHandler()); - - HttpRequest req = newUpgradeRequest(PERMESSAGE_DEFLATE_EXTENSION); - ch.writeInbound(req); - - HttpResponse res = newUpgradeResponse(null); - ch.writeOutbound(res); - - HttpResponse res2 = ch.readOutbound(); - List exts = WebSocketExtensionUtil.extractExtensions( - res2.headers().get(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS)); - - assertEquals(PERMESSAGE_DEFLATE_EXTENSION, exts.get(0).name()); - assertTrue(exts.get(0).parameters().isEmpty()); - assertNotNull(ch.pipeline().get(PerMessageDeflateDecoder.class)); - assertNotNull(ch.pipeline().get(PerMessageDeflateEncoder.class)); - } - - @Test - public void testClientWindowSizeSuccess() { - EmbeddedChannel ch = new EmbeddedChannel(new WebSocketServerExtensionHandler( - new PerMessageDeflateServerExtensionHandshaker(6, false, 10, false, false))); - - HttpRequest req = newUpgradeRequest(PERMESSAGE_DEFLATE_EXTENSION + "; " + CLIENT_MAX_WINDOW); - ch.writeInbound(req); - - HttpResponse res = newUpgradeResponse(null); - ch.writeOutbound(res); - - HttpResponse res2 = ch.readOutbound(); - List exts = WebSocketExtensionUtil.extractExtensions( - res2.headers().get(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS)); - - assertEquals(PERMESSAGE_DEFLATE_EXTENSION, exts.get(0).name()); - assertEquals("10", exts.get(0).parameters().get(CLIENT_MAX_WINDOW)); - assertNotNull(ch.pipeline().get(PerMessageDeflateDecoder.class)); - assertNotNull(ch.pipeline().get(PerMessageDeflateEncoder.class)); - } - - @Test - public void testClientWindowSizeUnavailable() { - EmbeddedChannel ch = new EmbeddedChannel(new WebSocketServerExtensionHandler( - new PerMessageDeflateServerExtensionHandshaker(6, false, 10, false, false))); - - HttpRequest req = newUpgradeRequest(PERMESSAGE_DEFLATE_EXTENSION); - ch.writeInbound(req); - - HttpResponse res = newUpgradeResponse(null); - ch.writeOutbound(res); - - HttpResponse res2 = ch.readOutbound(); - List exts = WebSocketExtensionUtil.extractExtensions( - res2.headers().get(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS)); - - assertEquals(PERMESSAGE_DEFLATE_EXTENSION, exts.get(0).name()); - assertTrue(exts.get(0).parameters().isEmpty()); - assertNotNull(ch.pipeline().get(PerMessageDeflateDecoder.class)); - assertNotNull(ch.pipeline().get(PerMessageDeflateEncoder.class)); - } - - @Test - public void testServerWindowSizeSuccess() { - EmbeddedChannel ch = new EmbeddedChannel(new WebSocketServerExtensionHandler( - new PerMessageDeflateServerExtensionHandshaker(6, true, 15, false, false))); - - HttpRequest req = newUpgradeRequest(PERMESSAGE_DEFLATE_EXTENSION + "; " + SERVER_MAX_WINDOW + "=10"); - ch.writeInbound(req); - - HttpResponse res = newUpgradeResponse(null); - ch.writeOutbound(res); - - HttpResponse res2 = ch.readOutbound(); - List exts = WebSocketExtensionUtil.extractExtensions( - res2.headers().get(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS)); - - assertEquals(PERMESSAGE_DEFLATE_EXTENSION, exts.get(0).name()); - assertEquals("10", exts.get(0).parameters().get(SERVER_MAX_WINDOW)); - assertNotNull(ch.pipeline().get(PerMessageDeflateDecoder.class)); - assertNotNull(ch.pipeline().get(PerMessageDeflateEncoder.class)); - } - - @Test - public void testServerWindowSizeDisable() { - EmbeddedChannel ch = new EmbeddedChannel(new WebSocketServerExtensionHandler( - new PerMessageDeflateServerExtensionHandshaker(6, false, 15, false, false))); - - HttpRequest req = newUpgradeRequest(PERMESSAGE_DEFLATE_EXTENSION + "; " + SERVER_MAX_WINDOW + "=10"); - ch.writeInbound(req); - - HttpResponse res = newUpgradeResponse(null); - ch.writeOutbound(res); - - HttpResponse res2 = ch.readOutbound(); - - assertFalse(res2.headers().contains(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS)); - assertNull(ch.pipeline().get(PerMessageDeflateDecoder.class)); - assertNull(ch.pipeline().get(PerMessageDeflateEncoder.class)); - } - - @Test - public void testServerNoContext() { - EmbeddedChannel ch = new EmbeddedChannel(new WebSocketServerCompressionHandler()); - - HttpRequest req = newUpgradeRequest(PERMESSAGE_DEFLATE_EXTENSION + "; " + SERVER_NO_CONTEXT); - ch.writeInbound(req); - - HttpResponse res = newUpgradeResponse(null); - ch.writeOutbound(res); - - HttpResponse res2 = ch.readOutbound(); - - assertFalse(res2.headers().contains(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS)); - assertNull(ch.pipeline().get(PerMessageDeflateDecoder.class)); - assertNull(ch.pipeline().get(PerMessageDeflateEncoder.class)); - } - - @Test - public void testClientNoContext() { - EmbeddedChannel ch = new EmbeddedChannel(new WebSocketServerCompressionHandler()); - - HttpRequest req = newUpgradeRequest(PERMESSAGE_DEFLATE_EXTENSION + "; " + CLIENT_NO_CONTEXT); - ch.writeInbound(req); - - HttpResponse res = newUpgradeResponse(null); - ch.writeOutbound(res); - - HttpResponse res2 = ch.readOutbound(); - List exts = WebSocketExtensionUtil.extractExtensions( - res2.headers().get(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS)); - - assertEquals(PERMESSAGE_DEFLATE_EXTENSION, exts.get(0).name()); - assertTrue(exts.get(0).parameters().isEmpty()); - assertNotNull(ch.pipeline().get(PerMessageDeflateDecoder.class)); - assertNotNull(ch.pipeline().get(PerMessageDeflateEncoder.class)); - } - - @Test - public void testServerWindowSizeDisableThenFallback() { - EmbeddedChannel ch = new EmbeddedChannel(new WebSocketServerExtensionHandler( - new PerMessageDeflateServerExtensionHandshaker(6, false, 15, false, false))); - - HttpRequest req = newUpgradeRequest(PERMESSAGE_DEFLATE_EXTENSION + "; " + SERVER_MAX_WINDOW + "=10, " + - PERMESSAGE_DEFLATE_EXTENSION); - ch.writeInbound(req); - - HttpResponse res = newUpgradeResponse(null); - ch.writeOutbound(res); - - HttpResponse res2 = ch.readOutbound(); - List exts = WebSocketExtensionUtil.extractExtensions( - res2.headers().get(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS)); - - assertEquals(PERMESSAGE_DEFLATE_EXTENSION, exts.get(0).name()); - assertTrue(exts.get(0).parameters().isEmpty()); - assertNotNull(ch.pipeline().get(PerMessageDeflateDecoder.class)); - assertNotNull(ch.pipeline().get(PerMessageDeflateEncoder.class)); - } - -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/rtsp/RtspDecoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/rtsp/RtspDecoderTest.java deleted file mode 100644 index 489491214f..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/rtsp/RtspDecoderTest.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.rtsp; - -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpObject; -import io.netty.handler.codec.http.HttpObjectAggregator; -import org.junit.jupiter.api.Test; - - -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** - * Test cases for RTSP decoder. - */ -public class RtspDecoderTest { - - /** - * There was a problem when an ANNOUNCE request was issued by the server, - * i.e. entered through the response decoder. First the decoder failed to - * parse the ANNOUNCE request, then it stopped receiving any more - * responses. This test verifies that the issue is solved. - */ - @Test - public void testReceiveAnnounce() { - byte[] data1 = ("ANNOUNCE rtsp://172.20.184.218:554/d3abaaa7-65f2-" - + "42b4-8d6b-379f492fcf0f RTSP/1.0\r\n" - + "CSeq: 2\r\n" - + "Session: 2777476816092819869\r\n" - + "x-notice: 5402 \"Session Terminated by Server\" " - + "event-date=20150514T075303Z\r\n" - + "Range: npt=0\r\n\r\n").getBytes(); - - byte[] data2 = ("RTSP/1.0 200 OK\r\n" + - "Server: Orbit2x\r\n" + - "CSeq: 172\r\n" + - "Session: 2547019973447939919\r\n" + - "\r\n").getBytes(); - - EmbeddedChannel ch = new EmbeddedChannel(new RtspDecoder(), - new HttpObjectAggregator(1048576)); - ch.writeInbound(Unpooled.wrappedBuffer(data1), - Unpooled.wrappedBuffer(data2)); - - HttpObject res1 = ch.readInbound(); - assertNotNull(res1); - assertTrue(res1 instanceof FullHttpRequest); - ((FullHttpRequest) res1).release(); - - HttpObject res2 = ch.readInbound(); - assertNotNull(res2); - assertTrue(res2 instanceof FullHttpResponse); - ((FullHttpResponse) res2).release(); - } -} diff --git a/codec-http/src/test/java/io/netty/handler/codec/rtsp/RtspEncoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/rtsp/RtspEncoderTest.java deleted file mode 100644 index 4af61a1073..0000000000 --- a/codec-http/src/test/java/io/netty/handler/codec/rtsp/RtspEncoderTest.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.rtsp; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.DefaultFullHttpResponse; -import io.netty.handler.codec.http.DefaultHttpRequest; -import io.netty.handler.codec.http.DefaultHttpResponse; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.util.CharsetUtil; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -/** - * Test cases for RTSP encoder. - */ -public class RtspEncoderTest { - - /** - * Test of a SETUP request, with no body. - */ - @Test - public void testSendSetupRequest() { - String expected = "SETUP rtsp://172.10.20.30:554/d3abaaa7-65f2-42b4-" - + "8d6b-379f492fcf0f RTSP/1.0\r\n" - + "transport: MP2T/DVBC/UDP;unicast;client=01234567;" - + "source=172.10.20.30;" - + "destination=1.1.1.1;client_port=6922\r\n" - + "cseq: 1\r\n" - + "\r\n"; - - HttpRequest request = new DefaultHttpRequest(RtspVersions.RTSP_1_0, - RtspMethods.SETUP, - "rtsp://172.10.20.30:554/d3abaaa7-65f2-42b4-8d6b-379f492fcf0f"); - request.headers().add(RtspHeaderNames.TRANSPORT, - "MP2T/DVBC/UDP;unicast;client=01234567;source=172.10.20.30;" + - "destination=1.1.1.1;client_port=6922"); - request.headers().add(RtspHeaderNames.CSEQ, "1"); - - EmbeddedChannel ch = new EmbeddedChannel(new RtspEncoder()); - ch.writeOutbound(request); - - ByteBuf buf = ch.readOutbound(); - String actual = buf.toString(CharsetUtil.UTF_8); - buf.release(); - assertEquals(expected, actual); - } - - /** - * Test of a GET_PARAMETER request, with body. - */ - @Test - public void testSendGetParameterRequest() { - String expected = "GET_PARAMETER rtsp://172.10.20.30:554 RTSP/1.0\r\n" - + "session: 2547019973447939919\r\n" - + "cseq: 3\r\n" - + "content-length: 31\r\n" - + "content-type: text/parameters\r\n" - + "\r\n" - + "stream_state\r\n" - + "position\r\n" - + "scale\r\n"; - - byte[] content = ("stream_state\r\n" - + "position\r\n" - + "scale\r\n").getBytes(CharsetUtil.UTF_8); - - FullHttpRequest request = new DefaultFullHttpRequest( - RtspVersions.RTSP_1_0, - RtspMethods.GET_PARAMETER, - "rtsp://172.10.20.30:554"); - request.headers().add(RtspHeaderNames.SESSION, "2547019973447939919"); - request.headers().add(RtspHeaderNames.CSEQ, "3"); - request.headers().add(RtspHeaderNames.CONTENT_LENGTH, - "" + content.length); - request.headers().add(RtspHeaderNames.CONTENT_TYPE, "text/parameters"); - request.content().writeBytes(content); - - EmbeddedChannel ch = new EmbeddedChannel(new RtspEncoder()); - ch.writeOutbound(request); - - ByteBuf buf = ch.readOutbound(); - String actual = buf.toString(CharsetUtil.UTF_8); - buf.release(); - assertEquals(expected, actual); - } - - /** - * Test of a 200 OK response, without body. - */ - @Test - public void testSend200OkResponseWithoutBody() { - String expected = "RTSP/1.0 200 OK\r\n" - + "server: Testserver\r\n" - + "cseq: 1\r\n" - + "session: 2547019973447939919\r\n" - + "\r\n"; - - HttpResponse response = new DefaultHttpResponse(RtspVersions.RTSP_1_0, - RtspResponseStatuses.OK); - response.headers().add(RtspHeaderNames.SERVER, "Testserver"); - response.headers().add(RtspHeaderNames.CSEQ, "1"); - response.headers().add(RtspHeaderNames.SESSION, "2547019973447939919"); - - EmbeddedChannel ch = new EmbeddedChannel(new RtspEncoder()); - ch.writeOutbound(response); - - ByteBuf buf = ch.readOutbound(); - String actual = buf.toString(CharsetUtil.UTF_8); - buf.release(); - assertEquals(expected, actual); - } - - /** - * Test of a 200 OK response, with body. - */ - @Test - public void testSend200OkResponseWithBody() { - String expected = "RTSP/1.0 200 OK\r\n" - + "server: Testserver\r\n" - + "session: 2547019973447939919\r\n" - + "content-type: text/parameters\r\n" - + "content-length: 50\r\n" - + "cseq: 3\r\n" - + "\r\n" - + "position: 24\r\n" - + "stream_state: playing\r\n" - + "scale: 1.00\r\n"; - - byte[] content = ("position: 24\r\n" - + "stream_state: playing\r\n" - + "scale: 1.00\r\n").getBytes(CharsetUtil.UTF_8); - - FullHttpResponse response = - new DefaultFullHttpResponse(RtspVersions.RTSP_1_0, - RtspResponseStatuses.OK); - response.headers().add(RtspHeaderNames.SERVER, "Testserver"); - response.headers().add(RtspHeaderNames.SESSION, "2547019973447939919"); - response.headers().add(RtspHeaderNames.CONTENT_TYPE, - "text/parameters"); - response.headers().add(RtspHeaderNames.CONTENT_LENGTH, - "" + content.length); - response.headers().add(RtspHeaderNames.CSEQ, "3"); - response.content().writeBytes(content); - - EmbeddedChannel ch = new EmbeddedChannel(new RtspEncoder()); - ch.writeOutbound(response); - - ByteBuf buf = ch.readOutbound(); - String actual = buf.toString(CharsetUtil.UTF_8); - buf.release(); - assertEquals(expected, actual); - } -} diff --git a/codec-http/src/test/resources/file-01.txt b/codec-http/src/test/resources/file-01.txt deleted file mode 100644 index a94c45f2bf..0000000000 --- a/codec-http/src/test/resources/file-01.txt +++ /dev/null @@ -1 +0,0 @@ -File 01 diff --git a/codec-http/src/test/resources/file-02.txt b/codec-http/src/test/resources/file-02.txt deleted file mode 100644 index e2e0c12c45..0000000000 --- a/codec-http/src/test/resources/file-02.txt +++ /dev/null @@ -1 +0,0 @@ -File 02 diff --git a/codec-http/src/test/resources/file-03.txt b/codec-http/src/test/resources/file-03.txt deleted file mode 100644 index b545f1b452..0000000000 --- a/codec-http/src/test/resources/file-03.txt +++ /dev/null @@ -1 +0,0 @@ -File 03 diff --git a/codec-http2/pom.xml b/codec-http2/pom.xml deleted file mode 100644 index fd47bcb870..0000000000 --- a/codec-http2/pom.xml +++ /dev/null @@ -1,105 +0,0 @@ - - - - - 4.0.0 - - io.netty - netty-parent - 5.0.0.Final-SNAPSHOT - - - netty-codec-http2 - jar - - Netty/Codec/HTTP2 - - - io.netty.codec.http2 - - --add-exports java.base/sun.security.x509=ALL-UNNAMED - - - - - ${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 - ${project.version} - - - com.jcraft - jzlib - true - - - com.google.code.gson - gson - - - org.mockito - mockito-core - - - org.bouncycastle - bcpkix-jdk15on - test - - - ${project.groupId} - ${tcnative.artifactId} - ${tcnative.classifier} - test - true - - - com.aayushatharva.brotli4j - brotli4j - true - - - com.github.luben - zstd-jni - true - - - - diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/AbstractHttp2ConnectionHandlerBuilder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/AbstractHttp2ConnectionHandlerBuilder.java deleted file mode 100644 index be21e2c693..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/AbstractHttp2ConnectionHandlerBuilder.java +++ /dev/null @@ -1,596 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.channel.Channel; -import io.netty.handler.codec.http2.Http2HeadersEncoder.SensitivityDetector; -import io.netty.util.internal.UnstableApi; - -import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_HEADER_LIST_SIZE; -import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_MAX_RESERVED_STREAMS; -import static io.netty.handler.codec.http2.Http2PromisedRequestVerifier.ALWAYS_VERIFY; -import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; -import static java.util.Objects.requireNonNull; - -/** - * Abstract base class which defines commonly used features required to build {@link Http2ConnectionHandler} instances. - * - *

Three ways to build a {@link Http2ConnectionHandler}

- *

Let the builder create a {@link Http2ConnectionHandler}

- * Simply call all the necessary setter methods, and then use {@link #build()} to build a new - * {@link Http2ConnectionHandler}. Setting the following properties are prohibited because they are used for - * other ways of building a {@link Http2ConnectionHandler}. - * conflicts with this option: - *
    - *
  • {@link #connection(Http2Connection)}
  • - *
  • {@link #codec(Http2ConnectionDecoder, Http2ConnectionEncoder)}
  • - *
- * - * - *

Let the builder use the {@link Http2ConnectionHandler} you specified

- * Call {@link #connection(Http2Connection)} to tell the builder that you want to build the handler from the - * {@link Http2Connection} you specified. Setting the following properties are prohibited and thus will trigger - * an {@link IllegalStateException} because they conflict with this option. - *
    - *
  • {@link #server(boolean)}
  • - *
  • {@link #codec(Http2ConnectionDecoder, Http2ConnectionEncoder)}
  • - *
- * - *

Let the builder use the {@link Http2ConnectionDecoder} and {@link Http2ConnectionEncoder} you specified

- * Call {@link #codec(Http2ConnectionDecoder, Http2ConnectionEncoder)} to tell the builder that you want to built the - * handler from the {@link Http2ConnectionDecoder} and {@link Http2ConnectionEncoder} you specified. Setting the - * following properties are prohibited and thus will trigger an {@link IllegalStateException} because they conflict - * with this option: - *
    - *
  • {@link #server(boolean)}
  • - *
  • {@link #connection(Http2Connection)}
  • - *
  • {@link #frameLogger(Http2FrameLogger)}
  • - *
  • {@link #headerSensitivityDetector(SensitivityDetector)}
  • - *
  • {@link #encoderEnforceMaxConcurrentStreams(boolean)}
  • - *
  • {@link #encoderIgnoreMaxHeaderListSize(boolean)}
  • - *
- * - *

Exposing necessary methods in a subclass

- * {@link #build()} method and all property access methods are {@code protected}. Choose the methods to expose to the - * users of your builder implementation and make them {@code public}. - * - * @param The type of handler created by this builder. - * @param The concrete type of this builder. - */ -@UnstableApi -public abstract class AbstractHttp2ConnectionHandlerBuilder> { - - private static final SensitivityDetector DEFAULT_HEADER_SENSITIVITY_DETECTOR = Http2HeadersEncoder.NEVER_SENSITIVE; - - // The properties that can always be set. - private Http2Settings initialSettings = Http2Settings.defaultSettings(); - private Http2FrameListener frameListener; - private long gracefulShutdownTimeoutMillis = Http2CodecUtil.DEFAULT_GRACEFUL_SHUTDOWN_TIMEOUT_MILLIS; - private boolean decoupleCloseAndGoAway; - - // The property that will prohibit connection() and codec() if set by server(), - // because this property is used only when this builder creates an Http2Connection. - private Boolean isServer; - private Integer maxReservedStreams; - - // The property that will prohibit server() and codec() if set by connection(). - private Http2Connection connection; - - // The properties that will prohibit server() and connection() if set by codec(). - private Http2ConnectionDecoder decoder; - private Http2ConnectionEncoder encoder; - - // The properties that are: - // * mutually exclusive against codec() and - // * OK to use with server() and connection() - private Boolean validateHeaders; - private Http2FrameLogger frameLogger; - private SensitivityDetector headerSensitivityDetector; - private Boolean encoderEnforceMaxConcurrentStreams; - private Boolean encoderIgnoreMaxHeaderListSize; - private Http2PromisedRequestVerifier promisedRequestVerifier = ALWAYS_VERIFY; - private boolean autoAckSettingsFrame = true; - private boolean autoAckPingFrame = true; - private int maxQueuedControlFrames = Http2CodecUtil.DEFAULT_MAX_QUEUED_CONTROL_FRAMES; - private int maxConsecutiveEmptyFrames = 2; - - /** - * Sets the {@link Http2Settings} to use for the initial connection settings exchange. - */ - protected Http2Settings initialSettings() { - return initialSettings; - } - - /** - * Sets the {@link Http2Settings} to use for the initial connection settings exchange. - */ - protected B initialSettings(Http2Settings settings) { - initialSettings = requireNonNull(settings, "settings"); - return self(); - } - - /** - * Returns the listener of inbound frames. - * - * @return {@link Http2FrameListener} if set, or {@code null} if not set. - */ - protected Http2FrameListener frameListener() { - return frameListener; - } - - /** - * Sets the listener of inbound frames. - * This listener will only be set if the decoder's listener is {@code null}. - */ - protected B frameListener(Http2FrameListener frameListener) { - this.frameListener = requireNonNull(frameListener, "frameListener"); - return self(); - } - - /** - * Returns the graceful shutdown timeout of the {@link Http2Connection} in milliseconds. Returns -1 if the - * timeout is indefinite. - */ - protected long gracefulShutdownTimeoutMillis() { - return gracefulShutdownTimeoutMillis; - } - - /** - * Sets the graceful shutdown timeout of the {@link Http2Connection} in milliseconds. - */ - protected B gracefulShutdownTimeoutMillis(long gracefulShutdownTimeoutMillis) { - if (gracefulShutdownTimeoutMillis < -1) { - throw new IllegalArgumentException("gracefulShutdownTimeoutMillis: " + gracefulShutdownTimeoutMillis + - " (expected: -1 for indefinite or >= 0)"); - } - this.gracefulShutdownTimeoutMillis = gracefulShutdownTimeoutMillis; - return self(); - } - - /** - * Returns if {@link #build()} will to create a {@link Http2Connection} in server mode ({@code true}) - * or client mode ({@code false}). - */ - protected boolean isServer() { - return isServer != null ? isServer : true; - } - - /** - * Sets if {@link #build()} will to create a {@link Http2Connection} in server mode ({@code true}) - * or client mode ({@code false}). - */ - protected B server(boolean isServer) { - enforceConstraint("server", "connection", connection); - enforceConstraint("server", "codec", decoder); - enforceConstraint("server", "codec", encoder); - - this.isServer = isServer; - return self(); - } - - /** - * Get the maximum number of streams which can be in the reserved state at any given time. - *

- * By default this value will be ignored on the server for local endpoint. This is because the RFC provides - * no way to explicitly communicate a limit to how many states can be in the reserved state, and instead relies - * on the peer to send RST_STREAM frames when they will be rejected. - */ - protected int maxReservedStreams() { - return maxReservedStreams != null ? maxReservedStreams : DEFAULT_MAX_RESERVED_STREAMS; - } - - /** - * Set the maximum number of streams which can be in the reserved state at any given time. - */ - protected B maxReservedStreams(int maxReservedStreams) { - enforceConstraint("server", "connection", connection); - enforceConstraint("server", "codec", decoder); - enforceConstraint("server", "codec", encoder); - - this.maxReservedStreams = checkPositiveOrZero(maxReservedStreams, "maxReservedStreams"); - return self(); - } - - /** - * Returns the {@link Http2Connection} to use. - * - * @return {@link Http2Connection} if set, or {@code null} if not set. - */ - protected Http2Connection connection() { - return connection; - } - - /** - * Sets the {@link Http2Connection} to use. - */ - protected B connection(Http2Connection connection) { - enforceConstraint("connection", "maxReservedStreams", maxReservedStreams); - enforceConstraint("connection", "server", isServer); - enforceConstraint("connection", "codec", decoder); - enforceConstraint("connection", "codec", encoder); - - this.connection = requireNonNull(connection, "connection"); - - return self(); - } - - /** - * Returns the {@link Http2ConnectionDecoder} to use. - * - * @return {@link Http2ConnectionDecoder} if set, or {@code null} if not set. - */ - protected Http2ConnectionDecoder decoder() { - return decoder; - } - - /** - * Returns the {@link Http2ConnectionEncoder} to use. - * - * @return {@link Http2ConnectionEncoder} if set, or {@code null} if not set. - */ - protected Http2ConnectionEncoder encoder() { - return encoder; - } - - /** - * Sets the {@link Http2ConnectionDecoder} and {@link Http2ConnectionEncoder} to use. - */ - protected B codec(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder) { - enforceConstraint("codec", "server", isServer); - enforceConstraint("codec", "maxReservedStreams", maxReservedStreams); - enforceConstraint("codec", "connection", connection); - enforceConstraint("codec", "frameLogger", frameLogger); - enforceConstraint("codec", "validateHeaders", validateHeaders); - enforceConstraint("codec", "headerSensitivityDetector", headerSensitivityDetector); - enforceConstraint("codec", "encoderEnforceMaxConcurrentStreams", encoderEnforceMaxConcurrentStreams); - - requireNonNull(decoder, "decoder"); - requireNonNull(encoder, "encoder"); - - if (decoder.connection() != encoder.connection()) { - throw new IllegalArgumentException("The specified encoder and decoder have different connections."); - } - - this.decoder = decoder; - this.encoder = encoder; - - return self(); - } - - /** - * Returns if HTTP headers should be validated according to - * RFC 7540, 8.1.2.6. - */ - protected boolean isValidateHeaders() { - return validateHeaders != null ? validateHeaders : true; - } - - /** - * Sets if HTTP headers should be validated according to - * RFC 7540, 8.1.2.6. - */ - protected B validateHeaders(boolean validateHeaders) { - enforceNonCodecConstraints("validateHeaders"); - this.validateHeaders = validateHeaders; - return self(); - } - - /** - * Returns the logger that is used for the encoder and decoder. - * - * @return {@link Http2FrameLogger} if set, or {@code null} if not set. - */ - protected Http2FrameLogger frameLogger() { - return frameLogger; - } - - /** - * Sets the logger that is used for the encoder and decoder. - */ - protected B frameLogger(Http2FrameLogger frameLogger) { - enforceNonCodecConstraints("frameLogger"); - this.frameLogger = requireNonNull(frameLogger, "frameLogger"); - return self(); - } - - /** - * Returns if the encoder should queue frames if the maximum number of concurrent streams - * would otherwise be exceeded. - */ - protected boolean encoderEnforceMaxConcurrentStreams() { - return encoderEnforceMaxConcurrentStreams != null ? encoderEnforceMaxConcurrentStreams : false; - } - - /** - * Sets if the encoder should queue frames if the maximum number of concurrent streams - * would otherwise be exceeded. - */ - protected B encoderEnforceMaxConcurrentStreams(boolean encoderEnforceMaxConcurrentStreams) { - enforceNonCodecConstraints("encoderEnforceMaxConcurrentStreams"); - this.encoderEnforceMaxConcurrentStreams = encoderEnforceMaxConcurrentStreams; - return self(); - } - - /** - * Returns the maximum number of queued control frames that are allowed before the connection is closed. - * This allows to protected against various attacks that can lead to high CPU / memory usage if the remote-peer - * floods us with frames that would have us produce control frames, but stops to read from the underlying socket. - * - * {@code 0} means no protection is in place. - */ - protected int encoderEnforceMaxQueuedControlFrames() { - return maxQueuedControlFrames; - } - - /** - * Sets the maximum number of queued control frames that are allowed before the connection is closed. - * This allows to protected against various attacks that can lead to high CPU / memory usage if the remote-peer - * floods us with frames that would have us produce control frames, but stops to read from the underlying socket. - * - * {@code 0} means no protection should be applied. - */ - protected B encoderEnforceMaxQueuedControlFrames(int maxQueuedControlFrames) { - enforceNonCodecConstraints("encoderEnforceMaxQueuedControlFrames"); - this.maxQueuedControlFrames = checkPositiveOrZero(maxQueuedControlFrames, "maxQueuedControlFrames"); - return self(); - } - - /** - * Returns the {@link SensitivityDetector} to use. - */ - protected SensitivityDetector headerSensitivityDetector() { - return headerSensitivityDetector != null ? headerSensitivityDetector : DEFAULT_HEADER_SENSITIVITY_DETECTOR; - } - - /** - * Sets the {@link SensitivityDetector} to use. - */ - protected B headerSensitivityDetector(SensitivityDetector headerSensitivityDetector) { - enforceNonCodecConstraints("headerSensitivityDetector"); - this.headerSensitivityDetector = requireNonNull(headerSensitivityDetector, "headerSensitivityDetector"); - return self(); - } - - /** - * Sets if the SETTINGS_MAX_HEADER_LIST_SIZE - * should be ignored when encoding headers. - * @param ignoreMaxHeaderListSize {@code true} to ignore - * SETTINGS_MAX_HEADER_LIST_SIZE. - * @return this. - */ - protected B encoderIgnoreMaxHeaderListSize(boolean ignoreMaxHeaderListSize) { - enforceNonCodecConstraints("encoderIgnoreMaxHeaderListSize"); - encoderIgnoreMaxHeaderListSize = ignoreMaxHeaderListSize; - return self(); - } - - /** - * Does nothing, do not call. - * - * @deprecated Huffman decoding no longer depends on having a decode capacity. - */ - @Deprecated - protected B initialHuffmanDecodeCapacity(int initialHuffmanDecodeCapacity) { - return self(); - } - - /** - * Set the {@link Http2PromisedRequestVerifier} to use. - * @return this. - */ - protected B promisedRequestVerifier(Http2PromisedRequestVerifier promisedRequestVerifier) { - enforceNonCodecConstraints("promisedRequestVerifier"); - this.promisedRequestVerifier = requireNonNull(promisedRequestVerifier, "promisedRequestVerifier"); - return self(); - } - - /** - * Get the {@link Http2PromisedRequestVerifier} to use. - * @return the {@link Http2PromisedRequestVerifier} to use. - */ - protected Http2PromisedRequestVerifier promisedRequestVerifier() { - return promisedRequestVerifier; - } - - /** - * Returns the maximum number of consecutive empty DATA frames (without end_of_stream flag) that are allowed before - * the connection is closed. This allows to protected against the remote peer flooding us with such frames and - * so use up a lot of CPU. There is no valid use-case for empty DATA frames without end_of_stream flag. - * - * {@code 0} means no protection is in place. - */ - protected int decoderEnforceMaxConsecutiveEmptyDataFrames() { - return maxConsecutiveEmptyFrames; - } - - /** - * Sets the maximum number of consecutive empty DATA frames (without end_of_stream flag) that are allowed before - * the connection is closed. This allows to protected against the remote peer flooding us with such frames and - * so use up a lot of CPU. There is no valid use-case for empty DATA frames without end_of_stream flag. - * - * {@code 0} means no protection should be applied. - */ - protected B decoderEnforceMaxConsecutiveEmptyDataFrames(int maxConsecutiveEmptyFrames) { - enforceNonCodecConstraints("maxConsecutiveEmptyFrames"); - this.maxConsecutiveEmptyFrames = checkPositiveOrZero( - maxConsecutiveEmptyFrames, "maxConsecutiveEmptyFrames"); - return self(); - } - - /** - * Determine if settings frame should automatically be acknowledged and applied. - * @return this. - */ - protected B autoAckSettingsFrame(boolean autoAckSettings) { - enforceNonCodecConstraints("autoAckSettingsFrame"); - autoAckSettingsFrame = autoAckSettings; - return self(); - } - - /** - * Determine if the SETTINGS frames should be automatically acknowledged and applied. - * @return {@code true} if the SETTINGS frames should be automatically acknowledged and applied. - */ - protected boolean isAutoAckSettingsFrame() { - return autoAckSettingsFrame; - } - - /** - * Determine if PING frame should automatically be acknowledged or not. - * @return this. - */ - protected B autoAckPingFrame(boolean autoAckPingFrame) { - enforceNonCodecConstraints("autoAckPingFrame"); - this.autoAckPingFrame = autoAckPingFrame; - return self(); - } - - /** - * Determine if the PING frames should be automatically acknowledged or not. - * @return {@code true} if the PING frames should be automatically acknowledged. - */ - protected boolean isAutoAckPingFrame() { - return autoAckPingFrame; - } - - /** - * Determine if the {@link Channel#close()} should be coupled with goaway and graceful close. - * @param decoupleCloseAndGoAway {@code true} to make {@link Channel#close()} directly close the underlying - * transport, and not attempt graceful closure via GOAWAY. - * @return {@code this}. - */ - protected B decoupleCloseAndGoAway(boolean decoupleCloseAndGoAway) { - this.decoupleCloseAndGoAway = decoupleCloseAndGoAway; - return self(); - } - - /** - * Determine if the {@link Channel#close()} should be coupled with goaway and graceful close. - */ - protected boolean decoupleCloseAndGoAway() { - return decoupleCloseAndGoAway; - } - - /** - * Create a new {@link Http2ConnectionHandler}. - */ - protected T build() { - if (encoder != null) { - assert decoder != null; - return buildFromCodec(decoder, encoder); - } - - Http2Connection connection = this.connection; - if (connection == null) { - connection = new DefaultHttp2Connection(isServer(), maxReservedStreams()); - } - - return buildFromConnection(connection); - } - - private T buildFromConnection(Http2Connection connection) { - Long maxHeaderListSize = initialSettings.maxHeaderListSize(); - Http2FrameReader reader = new DefaultHttp2FrameReader(new DefaultHttp2HeadersDecoder(isValidateHeaders(), - maxHeaderListSize == null ? DEFAULT_HEADER_LIST_SIZE : maxHeaderListSize, - /* initialHuffmanDecodeCapacity= */ -1)); - Http2FrameWriter writer = encoderIgnoreMaxHeaderListSize == null ? - new DefaultHttp2FrameWriter(headerSensitivityDetector()) : - new DefaultHttp2FrameWriter(headerSensitivityDetector(), encoderIgnoreMaxHeaderListSize); - - if (frameLogger != null) { - reader = new Http2InboundFrameLogger(reader, frameLogger); - writer = new Http2OutboundFrameLogger(writer, frameLogger); - } - - Http2ConnectionEncoder encoder = new DefaultHttp2ConnectionEncoder(connection, writer); - boolean encoderEnforceMaxConcurrentStreams = encoderEnforceMaxConcurrentStreams(); - - if (maxQueuedControlFrames != 0) { - encoder = new Http2ControlFrameLimitEncoder(encoder, maxQueuedControlFrames); - } - if (encoderEnforceMaxConcurrentStreams) { - if (connection.isServer()) { - encoder.close(); - reader.close(); - throw new IllegalArgumentException( - "encoderEnforceMaxConcurrentStreams: " + encoderEnforceMaxConcurrentStreams + - " not supported for server"); - } - encoder = new StreamBufferingEncoder(encoder); - } - - DefaultHttp2ConnectionDecoder decoder = new DefaultHttp2ConnectionDecoder(connection, encoder, reader, - promisedRequestVerifier(), isAutoAckSettingsFrame(), isAutoAckPingFrame()); - return buildFromCodec(decoder, encoder); - } - - private T buildFromCodec(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder) { - int maxConsecutiveEmptyDataFrames = decoderEnforceMaxConsecutiveEmptyDataFrames(); - if (maxConsecutiveEmptyDataFrames > 0) { - decoder = new Http2EmptyDataFrameConnectionDecoder(decoder, maxConsecutiveEmptyDataFrames); - } - final T handler; - try { - // Call the abstract build method - handler = build(decoder, encoder, initialSettings); - } catch (Throwable t) { - encoder.close(); - decoder.close(); - throw new IllegalStateException("failed to build an Http2ConnectionHandler", t); - } - - // Setup post build options - handler.gracefulShutdownTimeoutMillis(gracefulShutdownTimeoutMillis); - if (handler.decoder().frameListener() == null) { - handler.decoder().frameListener(frameListener); - } - return handler; - } - - /** - * Implement this method to create a new {@link Http2ConnectionHandler} or its subtype instance. - *

- * The return of this method will be subject to the following: - *

    - *
  • {@link #frameListener(Http2FrameListener)} will be set if not already set in the decoder
  • - *
  • {@link #gracefulShutdownTimeoutMillis(long)} will always be set
  • - *
- */ - protected abstract T build(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder, - Http2Settings initialSettings) throws Exception; - - /** - * Returns {@code this}. - */ - @SuppressWarnings("unchecked") - protected final B self() { - return (B) this; - } - - private void enforceNonCodecConstraints(String rejected) { - enforceConstraint(rejected, "server/connection", decoder); - enforceConstraint(rejected, "server/connection", encoder); - } - - private static void enforceConstraint(String methodName, String rejectorName, Object value) { - if (value != null) { - throw new IllegalStateException( - methodName + "() cannot be called because " + rejectorName + "() has been called already."); - } - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/AbstractHttp2StreamChannel.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/AbstractHttp2StreamChannel.java deleted file mode 100644 index 7b99ab9bb5..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/AbstractHttp2StreamChannel.java +++ /dev/null @@ -1,950 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelConfig; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelId; -import io.netty.channel.ChannelMetadata; -import io.netty.channel.ChannelOutboundBuffer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.DefaultChannelConfig; -import io.netty.channel.DefaultChannelPipeline; -import io.netty.channel.EventLoop; -import io.netty.channel.MessageSizeEstimator; -import io.netty.channel.RecvByteBufAllocator; -import io.netty.channel.WriteBufferWaterMark; -import io.netty.handler.codec.http2.Http2FrameCodec.DefaultHttp2FrameStream; -import io.netty.util.DefaultAttributeMap; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.io.IOException; -import java.net.SocketAddress; -import java.nio.channels.ClosedChannelException; -import java.util.ArrayDeque; -import java.util.Queue; -import java.util.concurrent.RejectedExecutionException; -import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; -import java.util.concurrent.atomic.AtomicLongFieldUpdater; - -import static io.netty.handler.codec.http2.Http2CodecUtil.isStreamIdValid; -import static java.lang.Math.min; - -abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements Http2StreamChannel { - - static final Http2FrameStreamVisitor WRITABLE_VISITOR = stream -> { - final AbstractHttp2StreamChannel childChannel = (AbstractHttp2StreamChannel) - ((DefaultHttp2FrameStream) stream).attachment; - childChannel.trySetWritable(); - return true; - }; - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(AbstractHttp2StreamChannel.class); - - private static final ChannelMetadata METADATA = new ChannelMetadata(false, 16); - - /** - * Number of bytes to consider non-payload messages. 9 is arbitrary, but also the minimum size of an HTTP/2 frame. - * Primarily is non-zero. - */ - private static final int MIN_HTTP2_FRAME_SIZE = 9; - - /** - * Returns the flow-control size for DATA frames, and {@value MIN_HTTP2_FRAME_SIZE} for all other frames. - */ - private static final class FlowControlledFrameSizeEstimator implements MessageSizeEstimator { - - static final FlowControlledFrameSizeEstimator INSTANCE = new FlowControlledFrameSizeEstimator(); - - private static final Handle HANDLE_INSTANCE = msg -> { - return msg instanceof Http2DataFrame ? - // Guard against overflow. - (int) min(Integer.MAX_VALUE, ((Http2DataFrame) msg).initialFlowControlledBytes() + - (long) MIN_HTTP2_FRAME_SIZE) : MIN_HTTP2_FRAME_SIZE; - }; - - @Override - public Handle newHandle() { - return HANDLE_INSTANCE; - } - } - - private static final AtomicLongFieldUpdater TOTAL_PENDING_SIZE_UPDATER = - AtomicLongFieldUpdater.newUpdater(AbstractHttp2StreamChannel.class, "totalPendingSize"); - - private static final AtomicIntegerFieldUpdater UNWRITABLE_UPDATER = - AtomicIntegerFieldUpdater.newUpdater(AbstractHttp2StreamChannel.class, "unwritable"); - - private static void windowUpdateFrameWriteComplete(Channel streamChannel, Future future) { - Throwable cause = future.cause(); - if (cause != null) { - Throwable unwrappedCause; - // Unwrap if needed - if (cause instanceof Http2FrameStreamException && (unwrappedCause = cause.getCause()) != null) { - cause = unwrappedCause; - } - - // Notify the child-channel and close it. - streamChannel.pipeline().fireExceptionCaught(cause); - streamChannel.unsafe().close(streamChannel.newPromise()); - } - } - - /** - * The current status of the read-processing for a {@link AbstractHttp2StreamChannel}. - */ - 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 - } - - private final Http2StreamChannelConfig config = new Http2StreamChannelConfig(this); - private final Http2ChannelUnsafe unsafe = new Http2ChannelUnsafe(); - private final ChannelId channelId; - private final ChannelPipeline pipeline; - private final DefaultHttp2FrameStream stream; - private final Promise closePromise; - - private volatile boolean registered; - - private volatile long totalPendingSize; - private volatile int unwritable; - - // Cached to reduce GC - private Runnable fireChannelWritabilityChangedTask; - - private boolean outboundClosed; - private int flowControlledBytes; - - /** - * 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 ReadStatus readStatus = ReadStatus.IDLE; - - private Queue inboundBuffer; - - /** {@code true} after the first HEADERS frame has been written **/ - private boolean firstFrameWritten; - private boolean readCompletePending; - - AbstractHttp2StreamChannel(DefaultHttp2FrameStream stream, int id, ChannelHandler inboundHandler) { - this.stream = stream; - stream.attachment = this; - pipeline = new DefaultChannelPipeline(this) { - @Override - protected void incrementPendingOutboundBytes(long size) { - AbstractHttp2StreamChannel.this.incrementPendingOutboundBytes(size, true); - } - - @Override - protected void decrementPendingOutboundBytes(long size) { - AbstractHttp2StreamChannel.this.decrementPendingOutboundBytes(size, true); - } - }; - - closePromise = pipeline.newPromise(); - channelId = new Http2StreamChannelId(parent().id(), id); - - if (inboundHandler != null) { - // Add the handler to the pipeline now that we are registered. - pipeline.addLast(inboundHandler); - } - } - - private void incrementPendingOutboundBytes(long size, boolean invokeLater) { - if (size == 0) { - return; - } - - long newWriteBufferSize = TOTAL_PENDING_SIZE_UPDATER.addAndGet(this, size); - if (newWriteBufferSize > config().getWriteBufferHighWaterMark()) { - setUnwritable(invokeLater); - } - } - - private void decrementPendingOutboundBytes(long size, boolean invokeLater) { - if (size == 0) { - return; - } - - long newWriteBufferSize = TOTAL_PENDING_SIZE_UPDATER.addAndGet(this, -size); - // Once the totalPendingSize dropped below the low water-mark we can mark the child channel - // as writable again. Before doing so we also need to ensure the parent channel is writable to - // prevent excessive buffering in the parent outbound buffer. If the parent is not writable - // we will mark the child channel as writable once the parent becomes writable by calling - // trySetWritable() later. - if (newWriteBufferSize < config().getWriteBufferLowWaterMark() && parent().isWritable()) { - setWritable(invokeLater); - } - } - - final void trySetWritable() { - // The parent is writable again but the child channel itself may still not be writable. - // Lets try to set the child channel writable to match the state of the parent channel - // if (and only if) the totalPendingSize is smaller then the low water-mark. - // If this is not the case we will try again later once we drop under it. - if (totalPendingSize < config().getWriteBufferLowWaterMark()) { - setWritable(false); - } - } - - private void setWritable(boolean invokeLater) { - for (;;) { - final int oldValue = unwritable; - final int newValue = oldValue & ~1; - if (UNWRITABLE_UPDATER.compareAndSet(this, oldValue, newValue)) { - if (oldValue != 0 && newValue == 0) { - fireChannelWritabilityChanged(invokeLater); - } - break; - } - } - } - - private void setUnwritable(boolean invokeLater) { - for (;;) { - final int oldValue = unwritable; - final int newValue = oldValue | 1; - if (UNWRITABLE_UPDATER.compareAndSet(this, oldValue, newValue)) { - if (oldValue == 0) { - fireChannelWritabilityChanged(invokeLater); - } - break; - } - } - } - - private void fireChannelWritabilityChanged(boolean invokeLater) { - final ChannelPipeline pipeline = pipeline(); - if (invokeLater) { - Runnable task = fireChannelWritabilityChangedTask; - if (task == null) { - fireChannelWritabilityChangedTask = task = pipeline::fireChannelWritabilityChanged; - } - executor().execute(task); - } else { - pipeline.fireChannelWritabilityChanged(); - } - } - @Override - public Http2FrameStream stream() { - return stream; - } - - void closeOutbound() { - outboundClosed = true; - } - - void streamClosed() { - unsafe.readEOS(); - // Attempt to drain any queued data from the queue and deliver it to the application before closing this - // channel. - unsafe.doBeginRead(); - } - - @Override - public ChannelMetadata metadata() { - return METADATA; - } - - @Override - public ChannelConfig config() { - return config; - } - - @Override - public boolean isOpen() { - return !closePromise.isDone(); - } - - @Override - public boolean isActive() { - return isOpen(); - } - - @Override - public boolean isWritable() { - return unwritable == 0; - } - - @Override - public ChannelId id() { - return channelId; - } - - @Override - public EventLoop executor() { - return parent().executor(); - } - - @Override - public Channel parent() { - return parentContext().channel(); - } - - @Override - public boolean isRegistered() { - return registered; - } - - @Override - public SocketAddress localAddress() { - return parent().localAddress(); - } - - @Override - public SocketAddress remoteAddress() { - return parent().remoteAddress(); - } - - @Override - public Future closeFuture() { - return closePromise.asFuture(); - } - - @Override - public long bytesBeforeUnwritable() { - long bytes = config().getWriteBufferHighWaterMark() - totalPendingSize; - // If bytes is negative we know we are not writable, but if bytes is non-negative we have to check - // writability. Note that totalPendingSize and isWritable() use different volatile variables that are not - // synchronized together. totalPendingSize will be updated before isWritable(). - if (bytes > 0) { - return isWritable() ? bytes : 0; - } - return 0; - } - - @Override - public long bytesBeforeWritable() { - long bytes = totalPendingSize - config().getWriteBufferLowWaterMark(); - // If bytes is negative we know we are writable, but if bytes is non-negative we have to check writability. - // Note that totalPendingSize and isWritable() use different volatile variables that are not synchronized - // together. totalPendingSize will be updated before isWritable(). - if (bytes > 0) { - return isWritable() ? 0 : bytes; - } - return 0; - } - - @Override - public Unsafe unsafe() { - return unsafe; - } - - @Override - public ChannelPipeline pipeline() { - return pipeline; - } - - @Override - public int hashCode() { - return id().hashCode(); - } - - @Override - public boolean equals(Object o) { - return this == o; - } - - @Override - public int compareTo(Channel o) { - if (this == o) { - return 0; - } - - return id().compareTo(o.id()); - } - - @Override - public String toString() { - return parent().toString() + "(H2 - " + stream + ')'; - } - - /** - * Receive a read message. This does not notify handlers unless a read is in progress on the - * channel. - */ - void fireChildRead(Http2Frame frame) { - assert executor().inEventLoop(); - if (!isActive()) { - ReferenceCountUtil.release(frame); - } 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 RecvByteBufAllocator.Handle allocHandle = unsafe.recvBufAllocHandle(); - unsafe.doRead0(frame, allocHandle); - // 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()) { - maybeAddChannelToReadCompletePendingQueue(); - } else { - unsafe.notifyReadComplete(allocHandle, true); - } - } else { - if (inboundBuffer == null) { - inboundBuffer = new ArrayDeque<>(4); - } - inboundBuffer.add(frame); - } - } - - void fireChildReadComplete() { - assert executor().inEventLoop(); - assert readStatus != ReadStatus.IDLE || !readCompletePending; - unsafe.notifyReadComplete(unsafe.recvBufAllocHandle(), false); - } - - private final class Http2ChannelUnsafe implements Unsafe { - @SuppressWarnings("deprecation") - private RecvByteBufAllocator.Handle recvHandle; - private boolean writeDoneAndNoFlush; - private boolean closeInitiated; - private boolean readEOS; - - @Override - public void connect(final SocketAddress remoteAddress, - SocketAddress localAddress, Promise promise) { - if (!promise.setUncancellable()) { - return; - } - promise.setFailure(new UnsupportedOperationException()); - } - - @Override - public RecvByteBufAllocator.Handle recvBufAllocHandle() { - if (recvHandle == null) { - recvHandle = config().getRecvByteBufAllocator().newHandle(); - recvHandle.reset(config()); - } - return recvHandle; - } - - @Override - public SocketAddress localAddress() { - return parent().unsafe().localAddress(); - } - - @Override - public SocketAddress remoteAddress() { - return parent().unsafe().remoteAddress(); - } - - @Override - public void register(Promise promise) { - if (!promise.setUncancellable()) { - return; - } - if (registered) { - promise.setFailure(new UnsupportedOperationException("Re-register is not supported")); - return; - } - - registered = true; - - promise.setSuccess(null); - - pipeline().fireChannelRegistered(); - if (isActive()) { - pipeline().fireChannelActive(); - if (config().isAutoRead()) { - read(); - } - } - } - - @Override - public void bind(SocketAddress localAddress, Promise promise) { - if (!promise.setUncancellable()) { - return; - } - promise.setFailure(new UnsupportedOperationException()); - } - - @Override - public void disconnect(Promise promise) { - close(promise); - } - - @Override - public void close(final Promise promise) { - if (!promise.setUncancellable()) { - return; - } - if (closeInitiated) { - if (closePromise.isDone()) { - // Closed already. - promise.setSuccess(null); - } else { - // This means close() was called before so we just register a listener and return - closeFuture().addListener(promise, (p, future) -> p.setSuccess(null)); - } - return; - } - closeInitiated = true; - // Just set to false as removing from an underlying queue would even be more expensive. - readCompletePending = false; - - final boolean wasActive = isActive(); - - // There is no need to update the local window as once the stream is closed all the pending bytes will be - // given back to the connection window by the controller itself. - - // Only ever send a reset frame if the connection is still alive and if the stream was created before - // as otherwise we may send a RST on a stream in an invalid state and cause a connection error. - if (parent().isActive() && !readEOS && isStreamIdValid(stream.id())) { - Http2StreamFrame resetFrame = new DefaultHttp2ResetFrame(Http2Error.CANCEL).stream(stream()); - write(resetFrame, newPromise()); - flush(); - } - - if (inboundBuffer != null) { - for (;;) { - Object msg = inboundBuffer.poll(); - if (msg == null) { - break; - } - ReferenceCountUtil.release(msg); - } - inboundBuffer = null; - } - - // The promise should be notified before we call fireChannelInactive(). - outboundClosed = true; - closePromise.setSuccess(null); - promise.setSuccess(null); - - fireChannelInactiveAndDeregister(newPromise(), wasActive); - } - - @Override - public void closeForcibly() { - close(newPromise()); - } - - @Override - public void deregister(Promise promise) { - fireChannelInactiveAndDeregister(promise, false); - } - - private void fireChannelInactiveAndDeregister(Promise promise, - final boolean fireChannelInactive) { - if (!promise.setUncancellable()) { - return; - } - - if (!registered) { - promise.setSuccess(null); - 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(()-> { - if (fireChannelInactive) { - pipeline.fireChannelInactive(); - } - // 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(); - } - safeSetSuccess(promise); - }); - } - - private void safeSetSuccess(Promise promise) { - if (!promise.trySuccess(null)) { - 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. - executor().execute(task); - } catch (RejectedExecutionException e) { - logger.warn("Can't invoke task later as EventLoop rejected it", e); - } - } - - @Override - public void beginRead() { - if (!isActive()) { - return; - } - updateLocalWindowIfNeeded(); - - switch (readStatus) { - case IDLE: - readStatus = ReadStatus.IN_PROGRESS; - doBeginRead(); - break; - case IN_PROGRESS: - readStatus = ReadStatus.REQUESTED; - break; - default: - break; - } - } - - private Object pollQueuedMessage() { - return inboundBuffer == null ? null : inboundBuffer.poll(); - } - - void doBeginRead() { - // Process messages until there are none left (or the user stopped requesting) and also handle EOS. - while (readStatus != ReadStatus.IDLE) { - Object message = pollQueuedMessage(); - if (message == null) { - if (readEOS) { - unsafe.closeForcibly(); - } - // We need to double check that there is nothing left to flush such as a - // window update frame. - flush(); - break; - } - final RecvByteBufAllocator.Handle allocHandle = recvBufAllocHandle(); - allocHandle.reset(config()); - boolean continueReading = false; - do { - doRead0((Http2Frame) message, allocHandle); - } while ((readEOS || (continueReading = allocHandle.continueReading())) - && (message = pollQueuedMessage()) != null); - - if (continueReading && isParentReadInProgress() && !readEOS) { - // Currently the parent and child channel are on the same EventLoop thread. If the parent is - // currently reading it is possible 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. - maybeAddChannelToReadCompletePendingQueue(); - } else { - notifyReadComplete(allocHandle, true); - } - } - } - - void readEOS() { - readEOS = true; - } - - private void updateLocalWindowIfNeeded() { - if (flowControlledBytes != 0) { - int bytes = flowControlledBytes; - flowControlledBytes = 0; - Future future = write0(parentContext(), new DefaultHttp2WindowUpdateFrame(bytes).stream(stream)); - // window update frames are commonly swallowed by the Http2FrameCodec and the promise is synchronously - // completed but the flow controller _may_ have generated a wire level WINDOW_UPDATE. Therefore we need, - // to assume there was a write done that needs to be flushed or we risk flow control starvation. - writeDoneAndNoFlush = true; - // Add a listener which will notify and teardown the stream - // when a window update fails if needed or check the result of the future directly if it was completed - // already. - // See https://github.com/netty/netty/issues/9663 - if (future.isDone()) { - windowUpdateFrameWriteComplete(AbstractHttp2StreamChannel.this, future); - } else { - future.addListener(AbstractHttp2StreamChannel.this, - AbstractHttp2StreamChannel::windowUpdateFrameWriteComplete); - } - } - } - - void notifyReadComplete(RecvByteBufAllocator.Handle allocHandle, boolean forceReadComplete) { - if (!readCompletePending && !forceReadComplete) { - return; - } - // Set to false just in case we added the channel multiple times before. - readCompletePending = false; - - if (readStatus == ReadStatus.REQUESTED) { - readStatus = ReadStatus.IN_PROGRESS; - } else { - readStatus = ReadStatus.IDLE; - } - - allocHandle.readComplete(); - pipeline().fireChannelReadComplete(); - if (config().isAutoRead()) { - read(); - } - - // 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) { - final int bytes; - if (frame instanceof Http2DataFrame) { - bytes = ((Http2DataFrame) frame).initialFlowControlledBytes(); - - // It is important that we increment the flowControlledBytes before we call fireChannelRead(...) - // as it may cause a read() that will call updateLocalWindowIfNeeded() and we need to ensure - // in this case that we accounted for it. - // - // See https://github.com/netty/netty/issues/9663 - flowControlledBytes += bytes; - } else { - bytes = MIN_HTTP2_FRAME_SIZE; - } - // Update before firing event through the pipeline to be consistent with other Channel implementation. - allocHandle.attemptedBytesRead(bytes); - allocHandle.lastBytesRead(bytes); - allocHandle.incMessagesRead(1); - - pipeline().fireChannelRead(frame); - } - - @Override - public void write(Object msg, Promise promise) { - // After this point its not possible to cancel a write anymore. - if (!promise.setUncancellable()) { - ReferenceCountUtil.release(msg); - return; - } - - if (!isActive() || - // Once the outbound side was closed we should not allow header / data frames - outboundClosed && (msg instanceof Http2HeadersFrame || msg instanceof Http2DataFrame)) { - ReferenceCountUtil.release(msg); - promise.setFailure(new ClosedChannelException()); - return; - } - - try { - if (msg instanceof Http2StreamFrame) { - Http2StreamFrame frame = validateStreamFrame((Http2StreamFrame) msg).stream(stream()); - writeHttp2StreamFrame(frame, promise); - } else { - String msgStr = msg.toString(); - ReferenceCountUtil.release(msg); - promise.setFailure(new IllegalArgumentException( - "Message must be an " + StringUtil.simpleClassName(Http2StreamFrame.class) + - ": " + msgStr)); - } - } catch (Throwable t) { - promise.tryFailure(t); - } - } - - private void writeHttp2StreamFrame(Http2StreamFrame frame, Promise promise) { - if (!firstFrameWritten && !isStreamIdValid(stream().id()) && !(frame instanceof Http2HeadersFrame)) { - ReferenceCountUtil.release(frame); - promise.setFailure( - new IllegalArgumentException("The first frame must be a headers frame. Was: " - + frame.name())); - return; - } - - final boolean firstWrite; - if (firstFrameWritten) { - firstWrite = false; - } else { - firstWrite = firstFrameWritten = true; - } - - Future f = write0(parentContext(), frame); - if (f.isDone()) { - if (firstWrite) { - firstWriteComplete(f, promise); - } else { - writeComplete(f, promise); - } - } else { - final long bytes = FlowControlledFrameSizeEstimator.HANDLE_INSTANCE.size(frame); - incrementPendingOutboundBytes(bytes, false); - f.addListener(future -> { - if (firstWrite) { - firstWriteComplete(future, promise); - } else { - writeComplete(future, promise); - } - decrementPendingOutboundBytes(bytes, false); - }); - writeDoneAndNoFlush = true; - } - } - - private void firstWriteComplete(Future future, Promise promise) { - Throwable cause = future.cause(); - if (cause == null) { - promise.setSuccess(null); - } else { - // If the first write fails there is not much we can do, just close - closeForcibly(); - promise.setFailure(wrapStreamClosedError(cause)); - } - } - - private void writeComplete(Future future, Promise promise) { - Throwable cause = future.cause(); - if (cause == null) { - promise.setSuccess(null); - } else { - Throwable error = wrapStreamClosedError(cause); - // To make it more consistent with AbstractChannel we handle all IOExceptions here. - if (error instanceof IOException) { - if (config.isAutoClose()) { - // Close channel if needed. - closeForcibly(); - } else { - // TODO: Once Http2StreamChannel extends DuplexChannel we should call shutdownOutput(...) - outboundClosed = true; - } - } - promise.setFailure(error); - } - } - - private Throwable wrapStreamClosedError(Throwable cause) { - // If the error was caused by STREAM_CLOSED we should use a ClosedChannelException to better - // mimic other transports and make it easier to reason about what exceptions to expect. - if (cause instanceof Http2Exception && ((Http2Exception) cause).error() == Http2Error.STREAM_CLOSED) { - return new ClosedChannelException().initCause(cause); - } - return cause; - } - - private Http2StreamFrame validateStreamFrame(Http2StreamFrame frame) { - if (frame.stream() != null && frame.stream() != stream) { - String msgString = frame.toString(); - ReferenceCountUtil.release(frame); - throw new IllegalArgumentException( - "Stream " + frame.stream() + " must not be set on the frame: " + msgString); - } - return frame; - } - - @Override - public void flush() { - // 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 || isParentReadInProgress()) { - // There is nothing to flush so this is a NOOP. - return; - } - // We need to set this to false before we call flush0(...) as FutureListener may produce more data - // that are explicit flushed. - writeDoneAndNoFlush = false; - flush0(parentContext()); - } - - @Override - public ChannelOutboundBuffer outboundBuffer() { - // Always return null as we not use the ChannelOutboundBuffer and not even support it. - return null; - } - } - - /** - * {@link ChannelConfig} so that the high and low writebuffer watermarks can reflect the outbound flow control - * window, without having to create a new {@link WriteBufferWaterMark} object whenever the flow control window - * changes. - */ - private static final class Http2StreamChannelConfig extends DefaultChannelConfig { - Http2StreamChannelConfig(Channel channel) { - super(channel); - } - - @Override - public MessageSizeEstimator getMessageSizeEstimator() { - return FlowControlledFrameSizeEstimator.INSTANCE; - } - - @Override - public ChannelConfig setMessageSizeEstimator(MessageSizeEstimator estimator) { - throw new UnsupportedOperationException(); - } - - @Override - public ChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator) { - if (!(allocator.newHandle() instanceof RecvByteBufAllocator.ExtendedHandle)) { - throw new IllegalArgumentException("allocator.newHandle() must return an object of type: " + - RecvByteBufAllocator.ExtendedHandle.class); - } - super.setRecvByteBufAllocator(allocator); - return this; - } - } - - private void maybeAddChannelToReadCompletePendingQueue() { - if (!readCompletePending) { - readCompletePending = true; - addChannelToReadCompletePendingQueue(); - } - } - - protected void flush0(ChannelHandlerContext ctx) { - ctx.flush(); - } - - protected Future write0(ChannelHandlerContext ctx, Object msg) { - return ctx.write(msg); - } - - protected abstract boolean isParentReadInProgress(); - protected abstract void addChannelToReadCompletePendingQueue(); - protected abstract ChannelHandlerContext parentContext(); -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/AbstractHttp2StreamFrame.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/AbstractHttp2StreamFrame.java deleted file mode 100644 index 1d5c1d01e2..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/AbstractHttp2StreamFrame.java +++ /dev/null @@ -1,59 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.util.internal.UnstableApi; - -/** - * Abstract implementation of {@link Http2StreamFrame}. - */ -@UnstableApi -public abstract class AbstractHttp2StreamFrame implements Http2StreamFrame { - - private Http2FrameStream stream; - - @Override - public AbstractHttp2StreamFrame stream(Http2FrameStream stream) { - this.stream = stream; - return this; - } - - @Override - public Http2FrameStream stream() { - return stream; - } - - /** - * Returns {@code true} if {@code o} has equal {@code stream} to this object. - */ - @Override - public boolean equals(Object o) { - if (!(o instanceof Http2StreamFrame)) { - return false; - } - Http2StreamFrame other = (Http2StreamFrame) o; - return stream == other.stream() || stream != null && stream.equals(other.stream()); - } - - @Override - public int hashCode() { - Http2FrameStream stream = this.stream; - if (stream == null) { - return super.hashCode(); - } - return stream.hashCode(); - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/AbstractInboundHttp2ToHttpAdapterBuilder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/AbstractInboundHttp2ToHttpAdapterBuilder.java deleted file mode 100644 index a6e820341c..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/AbstractInboundHttp2ToHttpAdapterBuilder.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.handler.codec.TooLongFrameException; -import io.netty.util.internal.UnstableApi; - -import static java.util.Objects.requireNonNull; - -/** - * A skeletal builder implementation of {@link InboundHttp2ToHttpAdapter} and its subtypes. - */ -@UnstableApi -public abstract class AbstractInboundHttp2ToHttpAdapterBuilder< - T extends InboundHttp2ToHttpAdapter, B extends AbstractInboundHttp2ToHttpAdapterBuilder> { - - private final Http2Connection connection; - private int maxContentLength; - private boolean validateHttpHeaders; - private boolean propagateSettings; - - /** - * Creates a new {@link InboundHttp2ToHttpAdapter} builder for the specified {@link Http2Connection}. - * - * @param connection the object which will provide connection notification events - * for the current connection - */ - protected AbstractInboundHttp2ToHttpAdapterBuilder(Http2Connection connection) { - this.connection = requireNonNull(connection, "connection"); - } - - @SuppressWarnings("unchecked") - protected final B self() { - return (B) this; - } - - /** - * Returns the {@link Http2Connection}. - */ - protected Http2Connection connection() { - return connection; - } - - /** - * Returns the maximum length of the message content. - */ - protected int maxContentLength() { - return maxContentLength; - } - - /** - * Specifies the maximum length of the message content. - * - * @param maxContentLength the maximum length of the message content. If the length of the message content - * exceeds this value, a {@link TooLongFrameException} will be raised - * @return {@link AbstractInboundHttp2ToHttpAdapterBuilder} the builder for the {@link InboundHttp2ToHttpAdapter} - */ - protected B maxContentLength(int maxContentLength) { - this.maxContentLength = maxContentLength; - return self(); - } - - /** - * Return {@code true} if HTTP header validation should be performed. - */ - protected boolean isValidateHttpHeaders() { - return validateHttpHeaders; - } - - /** - * Specifies whether validation of HTTP headers should be performed. - * - * @param validate - *
    - *
  • {@code true} to validate HTTP headers in the http-codec
  • - *
  • {@code false} not to validate HTTP headers in the http-codec
  • - *
- * @return {@link AbstractInboundHttp2ToHttpAdapterBuilder} the builder for the {@link InboundHttp2ToHttpAdapter} - */ - protected B validateHttpHeaders(boolean validate) { - validateHttpHeaders = validate; - return self(); - } - - /** - * Returns {@code true} if a read settings frame should be propagated along the channel pipeline. - */ - protected boolean isPropagateSettings() { - return propagateSettings; - } - - /** - * Specifies whether a read settings frame should be propagated along the channel pipeline. - * - * @param propagate if {@code true} read settings will be passed along the pipeline. This can be useful - * to clients that need hold off sending data until they have received the settings. - * @return {@link AbstractInboundHttp2ToHttpAdapterBuilder} the builder for the {@link InboundHttp2ToHttpAdapter} - */ - protected B propagateSettings(boolean propagate) { - propagateSettings = propagate; - return self(); - } - - /** - * Builds/creates a new {@link InboundHttp2ToHttpAdapter} instance using this builder's current settings. - */ - protected T build() { - final T instance; - try { - instance = build(connection(), maxContentLength(), - isValidateHttpHeaders(), isPropagateSettings()); - } catch (Throwable t) { - throw new IllegalStateException("failed to create a new InboundHttp2ToHttpAdapter", t); - } - connection.addListener(instance); - return instance; - } - - /** - * Creates a new {@link InboundHttp2ToHttpAdapter} with the specified properties. - */ - protected abstract T build(Http2Connection connection, int maxContentLength, - boolean validateHttpHeaders, boolean propagateSettings) throws Exception; -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/CharSequenceMap.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/CharSequenceMap.java deleted file mode 100644 index 21371b49e4..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/CharSequenceMap.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.handler.codec.DefaultHeaders; -import io.netty.handler.codec.UnsupportedValueConverter; -import io.netty.handler.codec.ValueConverter; -import io.netty.util.internal.UnstableApi; - -import static io.netty.util.AsciiString.CASE_INSENSITIVE_HASHER; -import static io.netty.util.AsciiString.CASE_SENSITIVE_HASHER; - -/** - * Internal use only! - */ -@UnstableApi -public final class CharSequenceMap extends DefaultHeaders> { - public CharSequenceMap() { - this(true); - } - - public CharSequenceMap(boolean caseSensitive) { - this(caseSensitive, UnsupportedValueConverter.instance()); - } - - public CharSequenceMap(boolean caseSensitive, ValueConverter valueConverter) { - super(caseSensitive ? CASE_SENSITIVE_HASHER : CASE_INSENSITIVE_HASHER, valueConverter); - } - - @SuppressWarnings("unchecked") - public CharSequenceMap(boolean caseSensitive, ValueConverter valueConverter, int arraySizeHint) { - super(caseSensitive ? CASE_SENSITIVE_HASHER : CASE_INSENSITIVE_HASHER, valueConverter, - NameValidator.NOT_NULL, arraySizeHint); - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/CleartextHttp2ServerUpgradeHandler.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/CleartextHttp2ServerUpgradeHandler.java deleted file mode 100644 index 2e83f666d0..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/CleartextHttp2ServerUpgradeHandler.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.ByteToMessageDecoder; -import io.netty.handler.codec.http.HttpServerCodec; -import io.netty.handler.codec.http.HttpServerUpgradeHandler; -import io.netty.util.internal.UnstableApi; - -import static io.netty.buffer.Unpooled.unreleasableBuffer; -import static io.netty.handler.codec.http2.Http2CodecUtil.connectionPrefaceBuf; - -import static java.util.Objects.requireNonNull; - -/** - * Performing cleartext upgrade, by h2c HTTP upgrade or Prior Knowledge. - * This handler config pipeline for h2c upgrade when handler added. - * And will update pipeline once it detect the connection is starting HTTP/2 by - * prior knowledge or not. - */ -@UnstableApi -public final class CleartextHttp2ServerUpgradeHandler extends ByteToMessageDecoder { - private static final ByteBuf CONNECTION_PREFACE = unreleasableBuffer(connectionPrefaceBuf()); - - private final HttpServerCodec httpServerCodec; - private final HttpServerUpgradeHandler httpServerUpgradeHandler; - private final ChannelHandler http2ServerHandler; - - /** - * Creates the channel handler provide cleartext HTTP/2 upgrade from HTTP - * upgrade or prior knowledge - * - * @param httpServerCodec the http server codec - * @param httpServerUpgradeHandler the http server upgrade handler for HTTP/2 - * @param http2ServerHandler the http2 server handler, will be added into pipeline - * when starting HTTP/2 by prior knowledge - */ - public CleartextHttp2ServerUpgradeHandler(HttpServerCodec httpServerCodec, - HttpServerUpgradeHandler httpServerUpgradeHandler, - ChannelHandler http2ServerHandler) { - this.httpServerCodec = requireNonNull(httpServerCodec, "httpServerCodec"); - this.httpServerUpgradeHandler = requireNonNull(httpServerUpgradeHandler, "httpServerUpgradeHandler"); - this.http2ServerHandler = requireNonNull(http2ServerHandler, "http2ServerHandler"); - } - - @Override - public void handlerAdded0(ChannelHandlerContext ctx) throws Exception { - ctx.pipeline() - .addAfter(ctx.name(), null, httpServerUpgradeHandler) - .addAfter(ctx.name(), null, httpServerCodec); - } - - /** - * Peek inbound message to determine current connection wants to start HTTP/2 - * by HTTP upgrade or prior knowledge - */ - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - int prefaceLength = CONNECTION_PREFACE.readableBytes(); - int bytesRead = Math.min(in.readableBytes(), prefaceLength); - - if (!ByteBufUtil.equals(CONNECTION_PREFACE, CONNECTION_PREFACE.readerIndex(), - in, in.readerIndex(), bytesRead)) { - ctx.pipeline().remove(this); - } else if (bytesRead == prefaceLength) { - // Full h2 preface match, removed source codec, using http2 codec to handle - // following network traffic - ctx.pipeline() - .remove(httpServerCodec) - .remove(httpServerUpgradeHandler); - - ctx.pipeline().addAfter(ctx.name(), null, http2ServerHandler); - ctx.fireUserEventTriggered(PriorKnowledgeUpgradeEvent.INSTANCE); - - ctx.pipeline().remove(this); - } - } - - /** - * User event that is fired to notify about HTTP/2 protocol is started. - */ - public static final class PriorKnowledgeUpgradeEvent { - private static final PriorKnowledgeUpgradeEvent INSTANCE = new PriorKnowledgeUpgradeEvent(); - - private PriorKnowledgeUpgradeEvent() { - } - } -} 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 deleted file mode 100644 index 545a7366f1..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/CompressorHttp2ConnectionEncoder.java +++ /dev/null @@ -1,392 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.ByteToMessageDecoder; -import io.netty.handler.codec.compression.BrotliEncoder; -import io.netty.handler.codec.compression.BrotliOptions; -import io.netty.handler.codec.compression.CompressionOptions; -import io.netty.handler.codec.compression.DeflateOptions; -import io.netty.handler.codec.compression.GzipOptions; -import io.netty.handler.codec.compression.StandardCompressionOptions; -import io.netty.handler.codec.compression.ZlibCodecFactory; -import io.netty.handler.codec.compression.ZlibWrapper; -import io.netty.handler.codec.compression.ZstdEncoder; -import io.netty.handler.codec.compression.ZstdOptions; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; -import io.netty.util.concurrent.PromiseCombiner; -import io.netty.util.internal.ObjectUtil; -import io.netty.util.internal.UnstableApi; - -import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_ENCODING; -import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH; -import static io.netty.handler.codec.http.HttpHeaderValues.BR; -import static io.netty.handler.codec.http.HttpHeaderValues.DEFLATE; -import static io.netty.handler.codec.http.HttpHeaderValues.GZIP; -import static io.netty.handler.codec.http.HttpHeaderValues.IDENTITY; -import static io.netty.handler.codec.http.HttpHeaderValues.X_DEFLATE; -import static io.netty.handler.codec.http.HttpHeaderValues.X_GZIP; -import static io.netty.handler.codec.http.HttpHeaderValues.ZSTD; - -/** - * A decorating HTTP2 encoder that will compress data frames according to the {@code content-encoding} header for each - * stream. The compression provided by this class will be applied to the data for the entire stream. - */ -@UnstableApi -public class CompressorHttp2ConnectionEncoder extends DecoratingHttp2ConnectionEncoder { - // We cannot remove this because it'll be breaking change - public static final int DEFAULT_COMPRESSION_LEVEL = 6; - public static final int DEFAULT_WINDOW_BITS = 15; - public static final int DEFAULT_MEM_LEVEL = 8; - - private int compressionLevel; - private int windowBits; - private int memLevel; - private final Http2Connection.PropertyKey propertyKey; - - private final boolean supportsCompressionOptions; - - private BrotliOptions brotliOptions; - private GzipOptions gzipCompressionOptions; - private DeflateOptions deflateOptions; - private ZstdOptions zstdOptions; - - /** - * Create a new {@link CompressorHttp2ConnectionEncoder} instance - * with default implementation of {@link StandardCompressionOptions} - */ - public CompressorHttp2ConnectionEncoder(Http2ConnectionEncoder delegate) { - this(delegate, StandardCompressionOptions.brotli(), StandardCompressionOptions.gzip(), - StandardCompressionOptions.deflate()); - } - - /** - * Create a new {@link CompressorHttp2ConnectionEncoder} instance - */ - @Deprecated - public CompressorHttp2ConnectionEncoder(Http2ConnectionEncoder delegate, int compressionLevel, int windowBits, - int memLevel) { - super(delegate); - this.compressionLevel = ObjectUtil.checkInRange(compressionLevel, 0, 9, "compressionLevel"); - this.windowBits = ObjectUtil.checkInRange(windowBits, 9, 15, "windowBits"); - this.memLevel = ObjectUtil.checkInRange(memLevel, 1, 9, "memLevel"); - - propertyKey = connection().newKey(); - connection().addListener(new Http2ConnectionAdapter() { - @Override - public void onStreamRemoved(Http2Stream stream) { - final EmbeddedChannel compressor = stream.getProperty(propertyKey); - if (compressor != null) { - cleanup(stream, compressor); - } - } - }); - - supportsCompressionOptions = false; - } - - /** - * Create a new {@link CompressorHttp2ConnectionEncoder} with - * specified {@link StandardCompressionOptions} - */ - public CompressorHttp2ConnectionEncoder(Http2ConnectionEncoder delegate, - CompressionOptions... compressionOptionsArgs) { - super(delegate); - ObjectUtil.checkNotNull(compressionOptionsArgs, "CompressionOptions"); - ObjectUtil.deepCheckNotNull("CompressionOptions", compressionOptionsArgs); - - for (CompressionOptions compressionOptions : compressionOptionsArgs) { - if (compressionOptions instanceof BrotliOptions) { - brotliOptions = (BrotliOptions) compressionOptions; - } else if (compressionOptions instanceof GzipOptions) { - gzipCompressionOptions = (GzipOptions) compressionOptions; - } else if (compressionOptions instanceof DeflateOptions) { - deflateOptions = (DeflateOptions) compressionOptions; - } else if (compressionOptions instanceof ZstdOptions) { - zstdOptions = (ZstdOptions) compressionOptions; - } else { - throw new IllegalArgumentException("Unsupported " + CompressionOptions.class.getSimpleName() + - ": " + compressionOptions); - } - } - - supportsCompressionOptions = true; - - propertyKey = connection().newKey(); - connection().addListener(new Http2ConnectionAdapter() { - @Override - public void onStreamRemoved(Http2Stream stream) { - final EmbeddedChannel compressor = stream.getProperty(propertyKey); - if (compressor != null) { - cleanup(stream, compressor); - } - } - }); - } - - @Override - public Future writeData(final ChannelHandlerContext ctx, final int streamId, ByteBuf data, int padding, - final boolean endOfStream) { - final Http2Stream stream = connection().stream(streamId); - final EmbeddedChannel channel = stream == null ? null : (EmbeddedChannel) stream.getProperty(propertyKey); - if (channel == null) { - // The compressor may be null if no compatible encoding type was found in this stream's headers - return super.writeData(ctx, streamId, data, padding, endOfStream); - } - - try { - // The channel will release the buffer after being written - channel.writeOutbound(data); - ByteBuf buf = nextReadableBuf(channel); - if (buf == null) { - if (endOfStream) { - if (channel.finish()) { - buf = nextReadableBuf(channel); - } - return super.writeData(ctx, streamId, buf == null ? Unpooled.EMPTY_BUFFER : buf, padding, - true); - } - // END_STREAM is not set and the assumption is data is still forthcoming. - return ctx.newSucceededFuture(); - } - - Promise promise = ctx.newPromise(); - PromiseCombiner combiner = new PromiseCombiner(ctx.executor()); - for (;;) { - ByteBuf nextBuf = nextReadableBuf(channel); - boolean compressedEndOfStream = nextBuf == null && endOfStream; - if (compressedEndOfStream && channel.finish()) { - nextBuf = nextReadableBuf(channel); - compressedEndOfStream = nextBuf == null; - } - - Future future = super.writeData(ctx, streamId, buf, padding, compressedEndOfStream); - combiner.add(future); - if (nextBuf == null) { - break; - } - - padding = 0; // Padding is only communicated once on the first iteration - buf = nextBuf; - } - combiner.finish(promise); - return promise.asFuture(); - } catch (Throwable cause) { - return ctx.newFailedFuture(cause); - } finally { - if (endOfStream) { - cleanup(stream, channel); - } - } - } - - @Override - public Future writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding, - boolean endStream) { - try { - // Determine if compression is required and sanitize the headers. - EmbeddedChannel compressor = newCompressor(ctx, headers, endStream); - - // Write the headers and create the stream object. - Future future = super.writeHeaders(ctx, streamId, headers, padding, endStream); - - // After the stream object has been created, then attach the compressor as a property for data compression. - bindCompressorToStream(compressor, streamId); - - return future; - } catch (Throwable e) { - return ctx.newFailedFuture(e); - } - } - - @Override - public Future writeHeaders(final ChannelHandlerContext ctx, final int streamId, final Http2Headers headers, - final int streamDependency, final short weight, final boolean exclusive, final int padding, - final boolean endOfStream) { - try { - // Determine if compression is required and sanitize the headers. - EmbeddedChannel compressor = newCompressor(ctx, headers, endOfStream); - - // Write the headers and create the stream object. - Future future = super.writeHeaders(ctx, streamId, headers, streamDependency, weight, exclusive, - padding, endOfStream); - - // After the stream object has been created, then attach the compressor as a property for data compression. - bindCompressorToStream(compressor, streamId); - - return future; - } catch (Throwable e) { - return ctx.newFailedFuture(e); - } - } - - /** - * Returns a new {@link EmbeddedChannel} that encodes the HTTP2 message content encoded in the specified - * {@code contentEncoding}. - * - * @param ctx the context. - * @param contentEncoding the value of the {@code content-encoding} header - * @return a new {@link ByteToMessageDecoder} if the specified encoding is supported. {@code null} otherwise - * (alternatively, you can throw a {@link Http2Exception} to block unknown encoding). - * @throws Http2Exception If the specified encoding is not not supported and warrants an exception - */ - protected EmbeddedChannel newContentCompressor(ChannelHandlerContext ctx, CharSequence contentEncoding) - throws Http2Exception { - if (GZIP.contentEqualsIgnoreCase(contentEncoding) || X_GZIP.contentEqualsIgnoreCase(contentEncoding)) { - return newCompressionChannel(ctx, ZlibWrapper.GZIP); - } - if (DEFLATE.contentEqualsIgnoreCase(contentEncoding) || X_DEFLATE.contentEqualsIgnoreCase(contentEncoding)) { - return newCompressionChannel(ctx, ZlibWrapper.ZLIB); - } - if (brotliOptions != null && BR.contentEqualsIgnoreCase(contentEncoding)) { - return new EmbeddedChannel(ctx.channel().id(), ctx.channel().metadata().hasDisconnect(), - ctx.channel().config(), new BrotliEncoder(brotliOptions.parameters())); - } - if (zstdOptions != null && ZSTD.contentEqualsIgnoreCase(contentEncoding)) { - return new EmbeddedChannel(ctx.channel().id(), ctx.channel().metadata().hasDisconnect(), - ctx.channel().config(), new ZstdEncoder(zstdOptions.compressionLevel(), - zstdOptions.blockSize(), zstdOptions.maxEncodeSize())); - } - // 'identity' or unsupported - return null; - } - - /** - * Returns the expected content encoding of the decoded content. Returning {@code contentEncoding} is the default - * behavior, which is the case for most compressors. - * - * @param contentEncoding the value of the {@code content-encoding} header - * @return the expected content encoding of the new content. - * @throws Http2Exception if the {@code contentEncoding} is not supported and warrants an exception - */ - protected CharSequence getTargetContentEncoding(CharSequence contentEncoding) throws Http2Exception { - return contentEncoding; - } - - /** - * Generate a new instance of an {@link EmbeddedChannel} capable of compressing data - * @param ctx the context. - * @param wrapper Defines what type of encoder should be used - */ - private EmbeddedChannel newCompressionChannel(final ChannelHandlerContext ctx, ZlibWrapper wrapper) { - if (supportsCompressionOptions) { - if (wrapper == ZlibWrapper.GZIP && gzipCompressionOptions != null) { - return new EmbeddedChannel(ctx.channel().id(), ctx.channel().metadata().hasDisconnect(), - ctx.channel().config(), ZlibCodecFactory.newZlibEncoder(wrapper, - gzipCompressionOptions.compressionLevel(), gzipCompressionOptions.windowBits(), - gzipCompressionOptions.memLevel())); - } else if (wrapper == ZlibWrapper.ZLIB && deflateOptions != null) { - return new EmbeddedChannel(ctx.channel().id(), ctx.channel().metadata().hasDisconnect(), - ctx.channel().config(), ZlibCodecFactory.newZlibEncoder(wrapper, - deflateOptions.compressionLevel(), deflateOptions.windowBits(), - deflateOptions.memLevel())); - } else { - throw new IllegalArgumentException("Unsupported ZlibWrapper: " + wrapper); - } - } else { - return new EmbeddedChannel(ctx.channel().id(), ctx.channel().metadata().hasDisconnect(), - ctx.channel().config(), ZlibCodecFactory.newZlibEncoder(wrapper, compressionLevel, windowBits, - memLevel)); - } - } - - /** - * Checks if a new compressor object is needed for the stream identified by {@code streamId}. This method will - * modify the {@code content-encoding} header contained in {@code headers}. - * - * @param ctx the context. - * @param headers Object representing headers which are to be written - * @param endOfStream Indicates if the stream has ended - * @return The channel used to compress data. - * @throws Http2Exception if any problems occur during initialization. - */ - private EmbeddedChannel newCompressor(ChannelHandlerContext ctx, Http2Headers headers, boolean endOfStream) - throws Http2Exception { - if (endOfStream) { - return null; - } - - CharSequence encoding = headers.get(CONTENT_ENCODING); - if (encoding == null) { - encoding = IDENTITY; - } - final EmbeddedChannel compressor = newContentCompressor(ctx, encoding); - if (compressor != null) { - CharSequence targetContentEncoding = getTargetContentEncoding(encoding); - if (IDENTITY.contentEqualsIgnoreCase(targetContentEncoding)) { - headers.remove(CONTENT_ENCODING); - } else { - headers.set(CONTENT_ENCODING, targetContentEncoding); - } - - // The content length will be for the decompressed data. Since we will compress the data - // this content-length will not be correct. Instead of queuing messages or delaying sending - // header frames...just remove the content-length header - headers.remove(CONTENT_LENGTH); - } - - return compressor; - } - - /** - * Called after the super class has written the headers and created any associated stream objects. - * @param compressor The compressor associated with the stream identified by {@code streamId}. - * @param streamId The stream id for which the headers were written. - */ - private void bindCompressorToStream(EmbeddedChannel compressor, int streamId) { - if (compressor != null) { - Http2Stream stream = connection().stream(streamId); - if (stream != null) { - stream.setProperty(propertyKey, compressor); - } - } - } - - /** - * Release remaining content from {@link EmbeddedChannel} and remove the compressor from the {@link Http2Stream}. - * - * @param stream The stream for which {@code compressor} is the compressor for - * @param compressor The compressor for {@code stream} - */ - void cleanup(Http2Stream stream, EmbeddedChannel compressor) { - compressor.finishAndReleaseAll(); - stream.removeProperty(propertyKey); - } - - /** - * Read the next compressed {@link ByteBuf} from the {@link EmbeddedChannel} or {@code null} if one does not exist. - * - * @param compressor The channel to read from - * @return The next decoded {@link ByteBuf} from the {@link EmbeddedChannel} or {@code null} if one does not exist - */ - private static ByteBuf nextReadableBuf(EmbeddedChannel compressor) { - for (;;) { - final ByteBuf buf = compressor.readOutbound(); - if (buf == null) { - return null; - } - if (!buf.isReadable()) { - buf.release(); - continue; - } - return buf; - } - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DecoratingHttp2ConnectionDecoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DecoratingHttp2ConnectionDecoder.java deleted file mode 100644 index e84b27f318..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DecoratingHttp2ConnectionDecoder.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.internal.UnstableApi; - -/** - * Decorator around another {@link Http2ConnectionDecoder} instance. - */ -@UnstableApi -public class DecoratingHttp2ConnectionDecoder implements Http2ConnectionDecoder { - private final Http2ConnectionDecoder delegate; - - public DecoratingHttp2ConnectionDecoder(Http2ConnectionDecoder delegate) { - this.delegate = requireNonNull(delegate, "delegate"); - } - - @Override - public void lifecycleManager(Http2LifecycleManager lifecycleManager) { - delegate.lifecycleManager(lifecycleManager); - } - - @Override - public Http2Connection connection() { - return delegate.connection(); - } - - @Override - public Http2LocalFlowController flowController() { - return delegate.flowController(); - } - - @Override - public void frameListener(Http2FrameListener listener) { - delegate.frameListener(listener); - } - - @Override - public Http2FrameListener frameListener() { - return delegate.frameListener(); - } - - @Override - public void decodeFrame(ChannelHandlerContext ctx, ByteBuf in) throws Http2Exception { - delegate.decodeFrame(ctx, in); - } - - @Override - public Http2Settings localSettings() { - return delegate.localSettings(); - } - - @Override - public boolean prefaceReceived() { - return delegate.prefaceReceived(); - } - - @Override - public void close() { - delegate.close(); - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DecoratingHttp2ConnectionEncoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DecoratingHttp2ConnectionEncoder.java deleted file mode 100644 index 43e2791f44..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DecoratingHttp2ConnectionEncoder.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.util.internal.UnstableApi; - -import static java.util.Objects.requireNonNull; - -/** - * A decorator around another {@link Http2ConnectionEncoder} instance. - */ -@UnstableApi -public class DecoratingHttp2ConnectionEncoder extends DecoratingHttp2FrameWriter implements Http2ConnectionEncoder, - Http2SettingsReceivedConsumer { - private final Http2ConnectionEncoder delegate; - - public DecoratingHttp2ConnectionEncoder(Http2ConnectionEncoder delegate) { - super(delegate); - this.delegate = requireNonNull(delegate, "delegate"); - } - - @Override - public void lifecycleManager(Http2LifecycleManager lifecycleManager) { - delegate.lifecycleManager(lifecycleManager); - } - - @Override - public Http2Connection connection() { - return delegate.connection(); - } - - @Override - public Http2RemoteFlowController flowController() { - return delegate.flowController(); - } - - @Override - public Http2FrameWriter frameWriter() { - return delegate.frameWriter(); - } - - @Override - public Http2Settings pollSentSettings() { - return delegate.pollSentSettings(); - } - - @Override - public void remoteSettings(Http2Settings settings) throws Http2Exception { - delegate.remoteSettings(settings); - } - - @Override - public void consumeReceivedSettings(Http2Settings settings) { - if (delegate instanceof Http2SettingsReceivedConsumer) { - ((Http2SettingsReceivedConsumer) delegate).consumeReceivedSettings(settings); - } else { - throw new IllegalStateException("delegate " + delegate + " is not an instance of " + - Http2SettingsReceivedConsumer.class); - } - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DecoratingHttp2FrameWriter.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DecoratingHttp2FrameWriter.java deleted file mode 100644 index b8b0d3e938..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DecoratingHttp2FrameWriter.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.concurrent.Future; -import io.netty.util.internal.UnstableApi; - -import static java.util.Objects.requireNonNull; - -/** - * Decorator around another {@link Http2FrameWriter} instance. - */ -@UnstableApi -public class DecoratingHttp2FrameWriter implements Http2FrameWriter { - private final Http2FrameWriter delegate; - - public DecoratingHttp2FrameWriter(Http2FrameWriter delegate) { - this.delegate = requireNonNull(delegate, "delegate"); - } - - @Override - public Future writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding, - boolean endStream) { - return delegate.writeData(ctx, streamId, data, padding, endStream); - } - - @Override - public Future writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding, - boolean endStream) { - return delegate.writeHeaders(ctx, streamId, headers, padding, endStream); - } - - @Override - public Future writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, - int streamDependency, short weight, boolean exclusive, int padding, - boolean endStream) { - return delegate - .writeHeaders(ctx, streamId, headers, streamDependency, weight, exclusive, padding, endStream); - } - - @Override - public Future writePriority(ChannelHandlerContext ctx, int streamId, int streamDependency, short weight, - boolean exclusive) { - return delegate.writePriority(ctx, streamId, streamDependency, weight, exclusive); - } - - @Override - public Future writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode) { - return delegate.writeRstStream(ctx, streamId, errorCode); - } - - @Override - public Future writeSettings(ChannelHandlerContext ctx, Http2Settings settings) { - return delegate.writeSettings(ctx, settings); - } - - @Override - public Future writeSettingsAck(ChannelHandlerContext ctx) { - return delegate.writeSettingsAck(ctx); - } - - @Override - public Future writePing(ChannelHandlerContext ctx, boolean ack, long data) { - return delegate.writePing(ctx, ack, data); - } - - @Override - public Future writePushPromise(ChannelHandlerContext ctx, int streamId, int promisedStreamId, - Http2Headers headers, int padding) { - return delegate.writePushPromise(ctx, streamId, promisedStreamId, headers, padding); - } - - @Override - public Future writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData) { - return delegate.writeGoAway(ctx, lastStreamId, errorCode, debugData); - } - - @Override - public Future writeWindowUpdate(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement) { - return delegate.writeWindowUpdate(ctx, streamId, windowSizeIncrement); - } - - @Override - public Future writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags, - ByteBuf payload) { - return delegate.writeFrame(ctx, frameType, streamId, flags, payload); - } - - @Override - public Configuration configuration() { - return delegate.configuration(); - } - - @Override - public void close() { - delegate.close(); - } -} 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 deleted file mode 100644 index 5f599a369c..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Connection.java +++ /dev/null @@ -1,1060 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.http2.Http2Stream.State; -import io.netty.util.collection.IntObjectHashMap; -import io.netty.util.collection.IntObjectMap; -import io.netty.util.collection.IntObjectMap.PrimitiveEntry; -import io.netty.util.concurrent.Promise; -import io.netty.util.internal.EmptyArrays; -import io.netty.util.internal.UnstableApi; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Queue; -import java.util.Set; - -import static io.netty.handler.codec.http2.Http2CodecUtil.CONNECTION_STREAM_ID; -import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_MAX_RESERVED_STREAMS; -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.Http2Error.REFUSED_STREAM; -import static io.netty.handler.codec.http2.Http2Exception.closedStreamError; -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.Http2Stream.State.CLOSED; -import static io.netty.handler.codec.http2.Http2Stream.State.HALF_CLOSED_LOCAL; -import static io.netty.handler.codec.http2.Http2Stream.State.HALF_CLOSED_REMOTE; -import static io.netty.handler.codec.http2.Http2Stream.State.IDLE; -import static io.netty.handler.codec.http2.Http2Stream.State.OPEN; -import static io.netty.handler.codec.http2.Http2Stream.State.RESERVED_LOCAL; -import static io.netty.handler.codec.http2.Http2Stream.State.RESERVED_REMOTE; -import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; -import static java.lang.Integer.MAX_VALUE; -import static java.util.Objects.requireNonNull; - -/** - * Simple implementation of {@link Http2Connection}. - */ -@UnstableApi -public class DefaultHttp2Connection implements Http2Connection { - private static final InternalLogger logger = InternalLoggerFactory.getInstance(DefaultHttp2Connection.class); - // Fields accessed by inner classes - final IntObjectMap streamMap = new IntObjectHashMap(); - final PropertyKeyRegistry propertyKeyRegistry = new PropertyKeyRegistry(); - final ConnectionStream connectionStream = new ConnectionStream(); - final DefaultEndpoint localEndpoint; - final DefaultEndpoint remoteEndpoint; - - /** - * We chose a {@link List} over a {@link Set} to avoid allocating an {@link Iterator} objects when iterating over - * the listeners. - *

- * Initial size of 4 because the default configuration currently has 3 listeners - * (local/remote flow controller and {@link StreamByteDistributor}) and we leave room for 1 extra. - * We could be more aggressive but the ArrayList resize will double the size if we are too small. - */ - final List listeners = new ArrayList<>(4); - final ActiveStreams activeStreams; - Promise closePromise; - - /** - * Creates a new connection with the given settings. - * @param server whether or not this end-point is the server-side of the HTTP/2 connection. - */ - public DefaultHttp2Connection(boolean server) { - this(server, DEFAULT_MAX_RESERVED_STREAMS); - } - - /** - * Creates a new connection with the given settings. - * @param server whether or not this end-point is the server-side of the HTTP/2 connection. - * @param maxReservedStreams The maximum amount of streams which can exist in the reserved state for each endpoint. - */ - public DefaultHttp2Connection(boolean server, int maxReservedStreams) { - activeStreams = new ActiveStreams(listeners); - // Reserved streams are excluded from the SETTINGS_MAX_CONCURRENT_STREAMS limit according to [1] and the RFC - // doesn't define a way to communicate the limit on reserved streams. We rely upon the peer to send RST_STREAM - // in response to any locally enforced limits being exceeded [2]. - // [1] https://tools.ietf.org/html/rfc7540#section-5.1.2 - // [2] https://tools.ietf.org/html/rfc7540#section-8.2.2 - localEndpoint = new DefaultEndpoint<>(server, server ? MAX_VALUE : maxReservedStreams); - remoteEndpoint = new DefaultEndpoint<>(!server, maxReservedStreams); - - // Add the connection stream to the map. - streamMap.put(connectionStream.id(), connectionStream); - } - - /** - * Determine if {@link #close(Promise)} has been called and no more streams are allowed to be created. - */ - final boolean isClosed() { - return closePromise != null; - } - - @Override - public void close(final Promise promise) { - requireNonNull(promise, "promise"); - // Since we allow this method to be called multiple times, we must make sure that all the promises are notified - // when all streams are removed and the close operation completes. - if (closePromise != null) { - if (closePromise == promise) { - // Do nothing - } else { - closePromise.asFuture().cascadeTo(promise); - } - } else { - closePromise = promise; - } - if (isStreamMapEmpty()) { - promise.trySuccess(null); - return; - } - - Iterator> itr = streamMap.entries().iterator(); - // We must take care while iterating the streamMap as to not modify while iterating in case there are other code - // paths iterating over the active streams. - if (activeStreams.allowModifications()) { - activeStreams.incrementPendingIterations(); - try { - while (itr.hasNext()) { - DefaultStream stream = (DefaultStream) itr.next().value(); - if (stream.id() != CONNECTION_STREAM_ID) { - // If modifications of the activeStream map is allowed, then a stream close operation will also - // modify the streamMap. Pass the iterator in so that remove will be called to prevent - // concurrent modification exceptions. - stream.close(itr); - } - } - } finally { - activeStreams.decrementPendingIterations(); - } - } else { - while (itr.hasNext()) { - Http2Stream stream = itr.next().value(); - if (stream.id() != CONNECTION_STREAM_ID) { - // We are not allowed to make modifications, so the close calls will be executed after this - // iteration completes. - stream.close(); - } - } - } - } - - @Override - public void addListener(Listener listener) { - listeners.add(listener); - } - - @Override - public void removeListener(Listener listener) { - listeners.remove(listener); - } - - @Override - public boolean isServer() { - return localEndpoint.isServer(); - } - - @Override - public Http2Stream connectionStream() { - return connectionStream; - } - - @Override - public Http2Stream stream(int streamId) { - return streamMap.get(streamId); - } - - @Override - public boolean streamMayHaveExisted(int streamId) { - return remoteEndpoint.mayHaveCreatedStream(streamId) || localEndpoint.mayHaveCreatedStream(streamId); - } - - @Override - public int numActiveStreams() { - return activeStreams.size(); - } - - @Override - public Http2Stream forEachActiveStream(Http2StreamVisitor visitor) throws Http2Exception { - return activeStreams.forEachActiveStream(visitor); - } - - @Override - public Endpoint local() { - return localEndpoint; - } - - @Override - public Endpoint remote() { - return remoteEndpoint; - } - - @Override - public boolean goAwayReceived() { - return localEndpoint.lastStreamKnownByPeer >= 0; - } - - @Override - 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 { - listeners.get(i).onGoAwayReceived(lastKnownStream, errorCode, debugData); - } catch (Throwable cause) { - logger.error("Caught Throwable from listener onGoAwayReceived.", cause); - } - } - - closeStreamsGreaterThanLastKnownStreamId(lastKnownStream, localEndpoint); - } - - @Override - public boolean goAwaySent() { - return remoteEndpoint.lastStreamKnownByPeer >= 0; - } - - @Override - 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 { - listeners.get(i).onGoAwaySent(lastKnownStream, errorCode, debugData); - } catch (Throwable cause) { - logger.error("Caught Throwable from listener onGoAwaySent.", cause); - } - } - - closeStreamsGreaterThanLastKnownStreamId(lastKnownStream, remoteEndpoint); - return true; - } - - private void closeStreamsGreaterThanLastKnownStreamId(final int lastKnownStream, - final DefaultEndpoint endpoint) throws Http2Exception { - forEachActiveStream(stream -> { - if (stream.id() > lastKnownStream && endpoint.isValidStreamId(stream.id())) { - stream.close(); - } - return true; - }); - } - - /** - * Determine if {@link #streamMap} only contains the connection stream. - */ - private boolean isStreamMapEmpty() { - return streamMap.size() == 1; - } - - /** - * Remove a stream from the {@link #streamMap}. - * @param stream the stream to remove. - * @param itr an iterator that may be pointing to the stream during iteration and {@link Iterator#remove()} will be - * used if non-{@code null}. - */ - void removeStream(DefaultStream stream, Iterator itr) { - final boolean removed; - if (itr == null) { - removed = streamMap.remove(stream.id()) != null; - } else { - itr.remove(); - removed = true; - } - - if (removed) { - for (int i = 0; i < listeners.size(); i++) { - try { - listeners.get(i).onStreamRemoved(stream); - } catch (Throwable cause) { - logger.error("Caught Throwable from listener onStreamRemoved.", cause); - } - } - - if (closePromise != null && isStreamMapEmpty()) { - closePromise.trySuccess(null); - } - } - } - - static State activeState(int streamId, State initialState, boolean isLocal, boolean halfClosed) - throws Http2Exception { - switch (initialState) { - case IDLE: - return halfClosed ? isLocal ? HALF_CLOSED_LOCAL : HALF_CLOSED_REMOTE : OPEN; - case RESERVED_LOCAL: - return HALF_CLOSED_REMOTE; - case RESERVED_REMOTE: - return HALF_CLOSED_LOCAL; - default: - throw streamError(streamId, PROTOCOL_ERROR, "Attempting to open a stream in an invalid state: " - + initialState); - } - } - - void notifyHalfClosed(Http2Stream stream) { - for (int i = 0; i < listeners.size(); i++) { - try { - listeners.get(i).onStreamHalfClosed(stream); - } catch (Throwable cause) { - logger.error("Caught Throwable from listener onStreamHalfClosed.", cause); - } - } - } - - void notifyClosed(Http2Stream stream) { - for (int i = 0; i < listeners.size(); i++) { - try { - listeners.get(i).onStreamClosed(stream); - } catch (Throwable cause) { - logger.error("Caught Throwable from listener onStreamClosed.", cause); - } - } - } - - @Override - public PropertyKey newKey() { - return propertyKeyRegistry.newKey(); - } - - /** - * Verifies that the key is valid and returns it as the internal {@link DefaultPropertyKey} type. - * - * @throws NullPointerException if the key is {@code null}. - * @throws ClassCastException if the key is not of type {@link DefaultPropertyKey}. - * @throws IllegalArgumentException if the key was not created by this connection. - */ - final DefaultPropertyKey verifyKey(PropertyKey key) { - return requireNonNull((DefaultPropertyKey) key, "key").verifyConnection(this); - } - - /** - * Simple stream implementation. Streams can be compared to each other by priority. - */ - private class DefaultStream implements Http2Stream { - private static final byte META_STATE_SENT_RST = 1; - private static final byte META_STATE_SENT_HEADERS = 1 << 1; - private static final byte META_STATE_SENT_TRAILERS = 1 << 2; - private static final byte META_STATE_SENT_PUSHPROMISE = 1 << 3; - private static final byte META_STATE_RECV_HEADERS = 1 << 4; - private static final byte META_STATE_RECV_TRAILERS = 1 << 5; - private final int id; - private final PropertyMap properties = new PropertyMap(); - private State state; - private byte metaState; - - DefaultStream(int id, State state) { - this.id = id; - this.state = state; - } - - @Override - public final int id() { - return id; - } - - @Override - public final State state() { - return state; - } - - @Override - public boolean isResetSent() { - return (metaState & META_STATE_SENT_RST) != 0; - } - - @Override - public Http2Stream resetSent() { - metaState |= META_STATE_SENT_RST; - return this; - } - - @Override - public Http2Stream headersSent(boolean isInformational) { - if (!isInformational) { - metaState |= isHeadersSent() ? META_STATE_SENT_TRAILERS : META_STATE_SENT_HEADERS; - } - return this; - } - - @Override - public boolean isHeadersSent() { - return (metaState & META_STATE_SENT_HEADERS) != 0; - } - - @Override - public boolean isTrailersSent() { - return (metaState & META_STATE_SENT_TRAILERS) != 0; - } - - @Override - public Http2Stream headersReceived(boolean isInformational) { - if (!isInformational) { - metaState |= isHeadersReceived() ? META_STATE_RECV_TRAILERS : META_STATE_RECV_HEADERS; - } - return this; - } - - @Override - public boolean isHeadersReceived() { - return (metaState & META_STATE_RECV_HEADERS) != 0; - } - - @Override - public boolean isTrailersReceived() { - return (metaState & META_STATE_RECV_TRAILERS) != 0; - } - - @Override - public Http2Stream pushPromiseSent() { - metaState |= META_STATE_SENT_PUSHPROMISE; - return this; - } - - @Override - public boolean isPushPromiseSent() { - return (metaState & META_STATE_SENT_PUSHPROMISE) != 0; - } - - @Override - public final V setProperty(PropertyKey key, V value) { - return properties.add(verifyKey(key), value); - } - - @Override - public final V getProperty(PropertyKey key) { - return properties.get(verifyKey(key)); - } - - @Override - public final V removeProperty(PropertyKey key) { - return properties.remove(verifyKey(key)); - } - - @Override - public Http2Stream open(boolean halfClosed) throws Http2Exception { - state = activeState(id, state, isLocal(), halfClosed); - final DefaultEndpoint endpoint = createdBy(); - if (!endpoint.canOpenStream()) { - throw connectionError(PROTOCOL_ERROR, "Maximum active streams violated for this endpoint: " + - endpoint.maxActiveStreams()); - } - - activate(); - return this; - } - - void activate() { - // If the stream is opened in a half-closed state, the headers must have either - // been sent if this is a local stream, or received if it is a remote stream. - if (state == HALF_CLOSED_LOCAL) { - headersSent(/*isInformational*/ false); - } else if (state == HALF_CLOSED_REMOTE) { - headersReceived(/*isInformational*/ false); - } - activeStreams.activate(this); - } - - Http2Stream close(Iterator itr) { - if (state == CLOSED) { - return this; - } - - state = CLOSED; - - --createdBy().numStreams; - activeStreams.deactivate(this, itr); - return this; - } - - @Override - public Http2Stream close() { - return close(null); - } - - @Override - public Http2Stream closeLocalSide() { - switch (state) { - case OPEN: - state = HALF_CLOSED_LOCAL; - notifyHalfClosed(this); - break; - case HALF_CLOSED_LOCAL: - break; - default: - close(); - break; - } - return this; - } - - @Override - public Http2Stream closeRemoteSide() { - switch (state) { - case OPEN: - state = HALF_CLOSED_REMOTE; - notifyHalfClosed(this); - break; - case HALF_CLOSED_REMOTE: - break; - default: - close(); - break; - } - return this; - } - - DefaultEndpoint createdBy() { - return localEndpoint.isValidStreamId(id) ? localEndpoint : remoteEndpoint; - } - - final boolean isLocal() { - return localEndpoint.isValidStreamId(id); - } - - /** - * Provides the lazy initialization for the {@link DefaultStream} data map. - */ - private class PropertyMap { - Object[] values = EmptyArrays.EMPTY_OBJECTS; - - V add(DefaultPropertyKey key, V value) { - resizeIfNecessary(key.index); - @SuppressWarnings("unchecked") - V prevValue = (V) values[key.index]; - values[key.index] = value; - return prevValue; - } - - @SuppressWarnings("unchecked") - V get(DefaultPropertyKey key) { - if (key.index >= values.length) { - return null; - } - return (V) values[key.index]; - } - - @SuppressWarnings("unchecked") - V remove(DefaultPropertyKey key) { - V prevValue = null; - if (key.index < values.length) { - prevValue = (V) values[key.index]; - values[key.index] = null; - } - return prevValue; - } - - void resizeIfNecessary(int index) { - if (index >= values.length) { - values = Arrays.copyOf(values, propertyKeyRegistry.size()); - } - } - } - } - - /** - * Stream class representing the connection, itself. - */ - private final class ConnectionStream extends DefaultStream { - ConnectionStream() { - super(CONNECTION_STREAM_ID, IDLE); - } - - @Override - public boolean isResetSent() { - return false; - } - - @Override - DefaultEndpoint createdBy() { - return null; - } - - @Override - public Http2Stream resetSent() { - throw new UnsupportedOperationException(); - } - - @Override - public Http2Stream open(boolean halfClosed) { - throw new UnsupportedOperationException(); - } - - @Override - public Http2Stream close() { - throw new UnsupportedOperationException(); - } - - @Override - public Http2Stream closeLocalSide() { - throw new UnsupportedOperationException(); - } - - @Override - public Http2Stream closeRemoteSide() { - throw new UnsupportedOperationException(); - } - - @Override - public Http2Stream headersSent(boolean isInformational) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isHeadersSent() { - throw new UnsupportedOperationException(); - } - - @Override - public Http2Stream pushPromiseSent() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isPushPromiseSent() { - throw new UnsupportedOperationException(); - } - } - - /** - * Simple endpoint implementation. - */ - private final class DefaultEndpoint implements Endpoint { - private final boolean server; - /** - * The minimum stream ID allowed when creating the next stream. This only applies at the time the stream is - * created. If the ID of the stream being created is less than this value, stream creation will fail. Upon - * successful creation of a stream, this value is incremented to the next valid stream ID. - */ - private int nextStreamIdToCreate; - /** - * Used for reservation of stream IDs. Stream IDs can be reserved in advance by applications before the streams - * are actually created. For example, applications may choose to buffer stream creation attempts as a way of - * working around {@code SETTINGS_MAX_CONCURRENT_STREAMS}, in which case they will reserve stream IDs for each - * buffered stream. - */ - private int nextReservationStreamId; - private int lastStreamKnownByPeer = -1; - private boolean pushToAllowed; - private F flowController; - private int maxStreams; - private int maxActiveStreams; - private final int maxReservedStreams; - // Fields accessed by inner classes - int numActiveStreams; - int numStreams; - - DefaultEndpoint(boolean server, int maxReservedStreams) { - this.server = server; - - // Determine the starting stream ID for this endpoint. Client-initiated streams - // are odd and server-initiated streams are even. Zero is reserved for the - // connection. Stream 1 is reserved client-initiated stream for responding to an - // upgrade from HTTP 1.1. - if (server) { - nextStreamIdToCreate = 2; - nextReservationStreamId = 0; - } else { - nextStreamIdToCreate = 1; - // For manually created client-side streams, 1 is reserved for HTTP upgrade, so start at 3. - nextReservationStreamId = 1; - } - - // Push is disallowed by default for servers and allowed for clients. - pushToAllowed = !server; - maxActiveStreams = MAX_VALUE; - this.maxReservedStreams = checkPositiveOrZero(maxReservedStreams, "maxReservedStreams"); - updateMaxStreams(); - } - - @Override - public int incrementAndGetNextStreamId() { - return nextReservationStreamId >= 0 ? nextReservationStreamId += 2 : nextReservationStreamId; - } - - private void incrementExpectedStreamId(int streamId) { - if (streamId > nextReservationStreamId && nextReservationStreamId >= 0) { - nextReservationStreamId = streamId; - } - nextStreamIdToCreate = streamId + 2; - ++numStreams; - } - - @Override - public boolean isValidStreamId(int streamId) { - return streamId > 0 && server == ((streamId & 1) == 0); - } - - @Override - public boolean mayHaveCreatedStream(int streamId) { - return isValidStreamId(streamId) && streamId <= lastStreamCreated(); - } - - @Override - public boolean canOpenStream() { - return numActiveStreams < maxActiveStreams; - } - - @Override - public DefaultStream createStream(int streamId, boolean halfClosed) throws Http2Exception { - State state = activeState(streamId, IDLE, isLocal(), halfClosed); - - checkNewStreamAllowed(streamId, state); - - // Create and initialize the stream. - DefaultStream stream = new DefaultStream(streamId, state); - - incrementExpectedStreamId(streamId); - - addStream(stream); - - stream.activate(); - return stream; - } - - @Override - public boolean created(Http2Stream stream) { - return stream instanceof DefaultStream && ((DefaultStream) stream).createdBy() == this; - } - - @Override - public boolean isServer() { - return server; - } - - @Override - public DefaultStream reservePushStream(int streamId, Http2Stream parent) throws Http2Exception { - if (parent == null) { - throw connectionError(PROTOCOL_ERROR, "Parent stream missing"); - } - if (isLocal() ? !parent.state().localSideOpen() : !parent.state().remoteSideOpen()) { - throw connectionError(PROTOCOL_ERROR, "Stream %d is not open for sending push promise", parent.id()); - } - if (!opposite().allowPushTo()) { - throw connectionError(PROTOCOL_ERROR, "Server push not allowed to opposite endpoint"); - } - State state = isLocal() ? RESERVED_LOCAL : RESERVED_REMOTE; - checkNewStreamAllowed(streamId, state); - - // Create and initialize the stream. - DefaultStream stream = new DefaultStream(streamId, state); - - incrementExpectedStreamId(streamId); - - // Register the stream. - addStream(stream); - return stream; - } - - private void addStream(DefaultStream stream) { - // Add the stream to the map and priority tree. - streamMap.put(stream.id(), stream); - - // Notify the listeners of the event. - for (int i = 0; i < listeners.size(); i++) { - try { - listeners.get(i).onStreamAdded(stream); - } catch (Throwable cause) { - logger.error("Caught Throwable from listener onStreamAdded.", cause); - } - } - } - - @Override - public void allowPushTo(boolean allow) { - if (allow && server) { - throw new IllegalArgumentException("Servers do not allow push"); - } - pushToAllowed = allow; - } - - @Override - public boolean allowPushTo() { - return pushToAllowed; - } - - @Override - public int numActiveStreams() { - return numActiveStreams; - } - - @Override - public int maxActiveStreams() { - return maxActiveStreams; - } - - @Override - public void maxActiveStreams(int maxActiveStreams) { - this.maxActiveStreams = maxActiveStreams; - updateMaxStreams(); - } - - @Override - public int lastStreamCreated() { - return nextStreamIdToCreate > 1 ? nextStreamIdToCreate - 2 : 0; - } - - @Override - public int lastStreamKnownByPeer() { - return lastStreamKnownByPeer; - } - - private void lastStreamKnownByPeer(int lastKnownStream) { - lastStreamKnownByPeer = lastKnownStream; - } - - @Override - public F flowController() { - return flowController; - } - - @Override - public void flowController(F flowController) { - this.flowController = requireNonNull(flowController, "flowController"); - } - - @Override - public Endpoint opposite() { - return isLocal() ? remoteEndpoint : localEndpoint; - } - - private void updateMaxStreams() { - maxStreams = (int) Math.min(MAX_VALUE, (long) maxActiveStreams + maxReservedStreams); - } - - private void checkNewStreamAllowed(int streamId, State state) throws Http2Exception { - assert state != IDLE; - if (lastStreamKnownByPeer >= 0 && streamId > lastStreamKnownByPeer) { - throw streamError(streamId, REFUSED_STREAM, - "Cannot create stream %d greater than Last-Stream-ID %d from GOAWAY.", - streamId, lastStreamKnownByPeer); - } - if (!isValidStreamId(streamId)) { - if (streamId < 0) { - throw new Http2NoMoreStreamIdsException(); - } - throw connectionError(PROTOCOL_ERROR, "Request stream %d is not correct for %s connection", streamId, - server ? "server" : "client"); - } - // This check must be after all id validated checks, but before the max streams check because it may be - // recoverable to some degree for handling frames which can be sent on closed streams. - if (streamId < nextStreamIdToCreate) { - throw closedStreamError(PROTOCOL_ERROR, "Request stream %d is behind the next expected stream %d", - streamId, nextStreamIdToCreate); - } - if (nextStreamIdToCreate <= 0) { - // We exhausted the stream id space that we can use. Let's signal this back but also signal that - // we still may want to process active streams. - throw new Http2Exception(REFUSED_STREAM, "Stream IDs are exhausted for this endpoint.", - Http2Exception.ShutdownHint.GRACEFUL_SHUTDOWN); - } - boolean isReserved = state == RESERVED_LOCAL || state == RESERVED_REMOTE; - if (!isReserved && !canOpenStream() || isReserved && numStreams >= maxStreams) { - throw streamError(streamId, REFUSED_STREAM, "Maximum active streams violated for this endpoint: " + - (isReserved ? maxStreams : maxActiveStreams)); - } - if (isClosed()) { - throw connectionError(INTERNAL_ERROR, "Attempted to create stream id %d after connection was closed", - streamId); - } - } - - private boolean isLocal() { - return this == localEndpoint; - } - } - - /** - * Allows events which would modify the collection of active streams to be queued while iterating via {@link - * #forEachActiveStream(Http2StreamVisitor)}. - */ - interface Event { - /** - * Trigger the original intention of this event. Expect to modify the active streams list. - *

- * If a {@link RuntimeException} object is thrown it will be logged and not propagated. - * Throwing from this method is not supported and is considered a programming error. - */ - void process(); - } - - /** - * Manages the list of currently active streams. Queues any {@link Event}s that would modify the list of - * active streams in order to prevent modification while iterating. - */ - private final class ActiveStreams { - private final List listeners; - private final Queue pendingEvents = new ArrayDeque<>(4); - private final Set streams = new LinkedHashSet<>(); - private int pendingIterations; - - ActiveStreams(List listeners) { - this.listeners = listeners; - } - - public int size() { - return streams.size(); - } - - public void activate(final DefaultStream stream) { - if (allowModifications()) { - addToActiveStreams(stream); - } else { - pendingEvents.add(() -> addToActiveStreams(stream)); - } - } - - public void deactivate(final DefaultStream stream, final Iterator itr) { - if (allowModifications() || itr != null) { - removeFromActiveStreams(stream, itr); - } else { - pendingEvents.add(() -> removeFromActiveStreams(stream, itr)); - } - } - - public Http2Stream forEachActiveStream(Http2StreamVisitor visitor) throws Http2Exception { - incrementPendingIterations(); - try { - for (Http2Stream stream : streams) { - if (!visitor.visit(stream)) { - return stream; - } - } - return null; - } finally { - decrementPendingIterations(); - } - } - - void addToActiveStreams(DefaultStream stream) { - if (streams.add(stream)) { - // Update the number of active streams initiated by the endpoint. - stream.createdBy().numActiveStreams++; - - for (int i = 0; i < listeners.size(); i++) { - try { - listeners.get(i).onStreamActive(stream); - } catch (Throwable cause) { - logger.error("Caught Throwable from listener onStreamActive.", cause); - } - } - } - } - - void removeFromActiveStreams(DefaultStream stream, Iterator itr) { - if (streams.remove(stream)) { - // Update the number of active streams initiated by the endpoint. - stream.createdBy().numActiveStreams--; - notifyClosed(stream); - } - removeStream(stream, itr); - } - - boolean allowModifications() { - return pendingIterations == 0; - } - - void incrementPendingIterations() { - ++pendingIterations; - } - - void decrementPendingIterations() { - --pendingIterations; - if (allowModifications()) { - for (;;) { - Event event = pendingEvents.poll(); - if (event == null) { - break; - } - try { - event.process(); - } catch (Throwable cause) { - logger.error("Caught Throwable while processing pending ActiveStreams$Event.", cause); - } - } - } - } - } - - /** - * Implementation of {@link PropertyKey} that specifies the index position of the property. - */ - final class DefaultPropertyKey implements PropertyKey { - final int index; - - DefaultPropertyKey(int index) { - this.index = index; - } - - DefaultPropertyKey verifyConnection(Http2Connection connection) { - if (connection != DefaultHttp2Connection.this) { - throw new IllegalArgumentException("Using a key that was not created by this connection"); - } - return this; - } - } - - /** - * A registry of all stream property keys known by this connection. - */ - private final class PropertyKeyRegistry { - /** - * Initial size of 4 because the default configuration currently has 3 listeners - * (local/remote flow controller and {@link StreamByteDistributor}) and we leave room for 1 extra. - * We could be more aggressive but the ArrayList resize will double the size if we are too small. - */ - final List keys = new ArrayList<>(4); - - /** - * Registers a new property key. - */ - DefaultPropertyKey newKey() { - DefaultPropertyKey key = new DefaultPropertyKey(keys.size()); - keys.add(key); - return key; - } - - int size() { - return keys.size(); - } - } -} 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 deleted file mode 100644 index 34d3faeea8..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoder.java +++ /dev/null @@ -1,805 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpStatusClass; -import io.netty.handler.codec.http.HttpUtil; -import io.netty.handler.codec.http2.Http2Connection.Endpoint; -import io.netty.util.internal.UnstableApi; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.util.List; - -import static io.netty.handler.codec.http.HttpStatusClass.INFORMATIONAL; -import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_PRIORITY_WEIGHT; -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.Http2Error.STREAM_CLOSED; -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.Http2PromisedRequestVerifier.ALWAYS_VERIFY; -import static io.netty.handler.codec.http2.Http2Stream.State.CLOSED; -import static io.netty.handler.codec.http2.Http2Stream.State.HALF_CLOSED_REMOTE; -import static java.lang.Integer.MAX_VALUE; -import static java.lang.Math.min; -import static java.util.Objects.requireNonNull; - -/** - * Provides the default implementation for processing inbound frame events and delegates to a - * {@link Http2FrameListener} - *

- * This class will read HTTP/2 frames and delegate the events to a {@link Http2FrameListener} - *

- * This interface enforces inbound flow control functionality through - * {@link Http2LocalFlowController} - */ -@UnstableApi -public class DefaultHttp2ConnectionDecoder implements Http2ConnectionDecoder { - private static final InternalLogger logger = InternalLoggerFactory.getInstance(DefaultHttp2ConnectionDecoder.class); - private Http2FrameListener internalFrameListener = new PrefaceFrameListener(); - private final Http2Connection connection; - private Http2LifecycleManager lifecycleManager; - private final Http2ConnectionEncoder encoder; - private final Http2FrameReader frameReader; - private Http2FrameListener listener; - private final Http2PromisedRequestVerifier requestVerifier; - private final Http2SettingsReceivedConsumer settingsReceivedConsumer; - private final boolean autoAckPing; - private final Http2Connection.PropertyKey contentLengthKey; - - public DefaultHttp2ConnectionDecoder(Http2Connection connection, - Http2ConnectionEncoder encoder, - Http2FrameReader frameReader) { - this(connection, encoder, frameReader, ALWAYS_VERIFY); - } - - public DefaultHttp2ConnectionDecoder(Http2Connection connection, - Http2ConnectionEncoder encoder, - Http2FrameReader frameReader, - Http2PromisedRequestVerifier requestVerifier) { - this(connection, encoder, frameReader, requestVerifier, true); - } - - /** - * Create a new instance. - * @param connection The {@link Http2Connection} associated with this decoder. - * @param encoder The {@link Http2ConnectionEncoder} associated with this decoder. - * @param frameReader Responsible for reading/parsing the raw frames. As opposed to this object which applies - * h2 semantics on top of the frames. - * @param requestVerifier Determines if push promised streams are valid. - * @param autoAckSettings {@code false} to disable automatically applying and sending settings acknowledge frame. - * The {@code Http2ConnectionEncoder} is expected to be an instance of {@link Http2SettingsReceivedConsumer} and - * will apply the earliest received but not yet ACKed SETTINGS when writing the SETTINGS ACKs. - * {@code true} to enable automatically applying and sending settings acknowledge frame. - */ - public DefaultHttp2ConnectionDecoder(Http2Connection connection, - Http2ConnectionEncoder encoder, - Http2FrameReader frameReader, - Http2PromisedRequestVerifier requestVerifier, - boolean autoAckSettings) { - this(connection, encoder, frameReader, requestVerifier, autoAckSettings, true); - } - - /** - * Create a new instance. - * @param connection The {@link Http2Connection} associated with this decoder. - * @param encoder The {@link Http2ConnectionEncoder} associated with this decoder. - * @param frameReader Responsible for reading/parsing the raw frames. As opposed to this object which applies - * h2 semantics on top of the frames. - * @param requestVerifier Determines if push promised streams are valid. - * @param autoAckSettings {@code false} to disable automatically applying and sending settings acknowledge frame. - * The {@code Http2ConnectionEncoder} is expected to be an instance of - * {@link Http2SettingsReceivedConsumer} and will apply the earliest received but not yet - * ACKed SETTINGS when writing the SETTINGS ACKs. {@code true} to enable automatically - * applying and sending settings acknowledge frame. - * @param autoAckPing {@code false} to disable automatically sending ping acknowledge frame. {@code true} to enable - * automatically sending ping ack frame. - */ - public DefaultHttp2ConnectionDecoder(Http2Connection connection, - Http2ConnectionEncoder encoder, - Http2FrameReader frameReader, - Http2PromisedRequestVerifier requestVerifier, - boolean autoAckSettings, - boolean autoAckPing) { - this.autoAckPing = autoAckPing; - if (autoAckSettings) { - settingsReceivedConsumer = null; - } else { - if (!(encoder instanceof Http2SettingsReceivedConsumer)) { - throw new IllegalArgumentException("disabling autoAckSettings requires the encoder to be a " + - Http2SettingsReceivedConsumer.class); - } - settingsReceivedConsumer = (Http2SettingsReceivedConsumer) encoder; - } - this.connection = requireNonNull(connection, "connection"); - contentLengthKey = this.connection.newKey(); - this.frameReader = requireNonNull(frameReader, "frameReader"); - this.encoder = requireNonNull(encoder, "encoder"); - this.requestVerifier = requireNonNull(requestVerifier, "requestVerifier"); - if (connection.local().flowController() == null) { - connection.local().flowController(new DefaultHttp2LocalFlowController(connection)); - } - connection.local().flowController().frameWriter(encoder.frameWriter()); - } - - @Override - public void lifecycleManager(Http2LifecycleManager lifecycleManager) { - this.lifecycleManager = requireNonNull(lifecycleManager, "lifecycleManager"); - } - - @Override - public Http2Connection connection() { - return connection; - } - - @Override - public final Http2LocalFlowController flowController() { - return connection.local().flowController(); - } - - @Override - public void frameListener(Http2FrameListener listener) { - this.listener = requireNonNull(listener, "listener"); - } - - @Override - public Http2FrameListener frameListener() { - return listener; - } - - @Override - public boolean prefaceReceived() { - return FrameReadListener.class == internalFrameListener.getClass(); - } - - @Override - public void decodeFrame(ChannelHandlerContext ctx, ByteBuf in) throws Http2Exception { - frameReader.readFrame(ctx, in, internalFrameListener); - } - - @Override - public Http2Settings localSettings() { - Http2Settings settings = new Http2Settings(); - Http2FrameReader.Configuration config = frameReader.configuration(); - Http2HeadersDecoder.Configuration headersConfig = config.headersConfiguration(); - Http2FrameSizePolicy frameSizePolicy = config.frameSizePolicy(); - settings.initialWindowSize(flowController().initialWindowSize()); - settings.maxConcurrentStreams(connection.remote().maxActiveStreams()); - settings.headerTableSize(headersConfig.maxHeaderTableSize()); - settings.maxFrameSize(frameSizePolicy.maxFrameSize()); - settings.maxHeaderListSize(headersConfig.maxHeaderListSize()); - if (!connection.isServer()) { - // Only set the pushEnabled flag if this is a client endpoint. - settings.pushEnabled(connection.local().allowPushTo()); - } - return settings; - } - - @Override - public void close() { - frameReader.close(); - } - - /** - * Calculate the threshold in bytes which should trigger a {@code GO_AWAY} if a set of headers exceeds this amount. - * @param maxHeaderListSize - * SETTINGS_MAX_HEADER_LIST_SIZE for the local - * endpoint. - * @return the threshold in bytes which should trigger a {@code GO_AWAY} if a set of headers exceeds this amount. - */ - protected long calculateMaxHeaderListSizeGoAway(long maxHeaderListSize) { - return Http2CodecUtil.calculateMaxHeaderListSizeGoAway(maxHeaderListSize); - } - - private int unconsumedBytes(Http2Stream stream) { - return flowController().unconsumedBytes(stream); - } - - void onGoAwayRead0(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData) - throws Http2Exception { - listener.onGoAwayRead(ctx, lastStreamId, errorCode, debugData); - connection.goAwayReceived(lastStreamId, errorCode, debugData); - } - - void onUnknownFrame0(ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags, - ByteBuf payload) throws Http2Exception { - listener.onUnknownFrame(ctx, frameType, streamId, flags, payload); - } - - // See https://tools.ietf.org/html/rfc7540#section-8.1.2.6 - private void verifyContentLength(Http2Stream stream, int data, boolean isEnd) throws Http2Exception { - ContentLength contentLength = stream.getProperty(contentLengthKey); - if (contentLength != null) { - try { - contentLength.increaseReceivedBytes(connection.isServer(), stream.id(), data, isEnd); - } finally { - if (isEnd) { - stream.removeProperty(contentLengthKey); - } - } - } - } - - /** - * Handles all inbound frames from the network. - */ - private final class FrameReadListener implements Http2FrameListener { - @Override - public int onDataRead(final ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding, - boolean endOfStream) throws Http2Exception { - Http2Stream stream = connection.stream(streamId); - Http2LocalFlowController flowController = flowController(); - int readable = data.readableBytes(); - int bytesToReturn = readable + padding; - - final boolean shouldIgnore; - try { - shouldIgnore = shouldIgnoreHeadersOrDataFrame(ctx, streamId, stream, "DATA"); - } catch (Http2Exception e) { - // Ignoring this frame. We still need to count the frame towards the connection flow control - // window, but we immediately mark all bytes as consumed. - flowController.receiveFlowControlledFrame(stream, data, padding, endOfStream); - flowController.consumeBytes(stream, bytesToReturn); - throw e; - } catch (Throwable t) { - throw connectionError(INTERNAL_ERROR, t, "Unhandled error on data stream id %d", streamId); - } - - if (shouldIgnore) { - // Ignoring this frame. We still need to count the frame towards the connection flow control - // window, but we immediately mark all bytes as consumed. - flowController.receiveFlowControlledFrame(stream, data, padding, endOfStream); - flowController.consumeBytes(stream, bytesToReturn); - - // Verify that the stream may have existed after we apply flow control. - verifyStreamMayHaveExisted(streamId); - - // All bytes have been consumed. - return bytesToReturn; - } - Http2Exception error = null; - switch (stream.state()) { - case OPEN: - case HALF_CLOSED_LOCAL: - break; - case HALF_CLOSED_REMOTE: - case CLOSED: - error = streamError(stream.id(), STREAM_CLOSED, "Stream %d in unexpected state: %s", - stream.id(), stream.state()); - break; - default: - error = streamError(stream.id(), PROTOCOL_ERROR, - "Stream %d in unexpected state: %s", stream.id(), stream.state()); - break; - } - - int unconsumedBytes = unconsumedBytes(stream); - try { - flowController.receiveFlowControlledFrame(stream, data, padding, endOfStream); - // Update the unconsumed bytes after flow control is applied. - unconsumedBytes = unconsumedBytes(stream); - - // If the stream is in an invalid state to receive the frame, throw the error. - if (error != null) { - throw error; - } - - verifyContentLength(stream, readable, endOfStream); - - // Call back the application and retrieve the number of bytes that have been - // immediately processed. - bytesToReturn = listener.onDataRead(ctx, streamId, data, padding, endOfStream); - - if (endOfStream) { - lifecycleManager.closeStreamRemote(stream, ctx.newSucceededFuture()); - } - - return bytesToReturn; - } catch (Http2Exception | RuntimeException e) { - // If an exception happened during delivery, the listener may have returned part - // of the bytes before the error occurred. If that's the case, subtract that from - // the total processed bytes so that we don't return too many bytes. - int delta = unconsumedBytes - unconsumedBytes(stream); - bytesToReturn -= delta; - throw e; - } finally { - // If appropriate, return the processed bytes to the flow controller. - flowController.consumeBytes(stream, bytesToReturn); - } - } - - @Override - public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding, - boolean endOfStream) throws Http2Exception { - onHeadersRead(ctx, streamId, headers, 0, DEFAULT_PRIORITY_WEIGHT, false, padding, endOfStream); - } - - @Override - public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int streamDependency, - short weight, boolean exclusive, int padding, boolean endOfStream) throws Http2Exception { - Http2Stream stream = connection.stream(streamId); - boolean allowHalfClosedRemote = false; - boolean isTrailers = false; - if (stream == null && !connection.streamMayHaveExisted(streamId)) { - stream = connection.remote().createStream(streamId, endOfStream); - // Allow the state to be HALF_CLOSE_REMOTE if we're creating it in that state. - allowHalfClosedRemote = stream.state() == HALF_CLOSED_REMOTE; - } else if (stream != null) { - isTrailers = stream.isHeadersReceived(); - } - - if (shouldIgnoreHeadersOrDataFrame(ctx, streamId, stream, "HEADERS")) { - return; - } - - boolean isInformational = !connection.isServer() && - HttpStatusClass.valueOf(headers.status()) == INFORMATIONAL; - if ((isInformational || !endOfStream) && stream.isHeadersReceived() || stream.isTrailersReceived()) { - throw streamError(streamId, PROTOCOL_ERROR, - "Stream %d received too many headers EOS: %s state: %s", - streamId, endOfStream, stream.state()); - } - - switch (stream.state()) { - case RESERVED_REMOTE: - stream.open(endOfStream); - break; - case OPEN: - case HALF_CLOSED_LOCAL: - // Allowed to receive headers in these states. - break; - case HALF_CLOSED_REMOTE: - if (!allowHalfClosedRemote) { - throw streamError(stream.id(), STREAM_CLOSED, "Stream %d in unexpected state: %s", - stream.id(), stream.state()); - } - break; - case CLOSED: - throw streamError(stream.id(), STREAM_CLOSED, "Stream %d in unexpected state: %s", - stream.id(), stream.state()); - default: - // Connection error. - throw connectionError(PROTOCOL_ERROR, "Stream %d in unexpected state: %s", stream.id(), - stream.state()); - } - - if (!isTrailers) { - // extract the content-length header - List contentLength = headers.getAll(HttpHeaderNames.CONTENT_LENGTH); - if (contentLength != null && !contentLength.isEmpty()) { - try { - long cLength = HttpUtil.normalizeAndGetContentLength(contentLength, false, true); - if (cLength != -1) { - headers.setLong(HttpHeaderNames.CONTENT_LENGTH, cLength); - stream.setProperty(contentLengthKey, new ContentLength(cLength)); - } - } catch (IllegalArgumentException e) { - throw streamError(stream.id(), PROTOCOL_ERROR, e, - "Multiple content-length headers received"); - } - } - } - - stream.headersReceived(isInformational); - verifyContentLength(stream, 0, endOfStream); - encoder.flowController().updateDependencyTree(streamId, streamDependency, weight, exclusive); - listener.onHeadersRead(ctx, streamId, headers, streamDependency, - weight, exclusive, padding, endOfStream); - // If the headers completes this stream, close it. - if (endOfStream) { - lifecycleManager.closeStreamRemote(stream, ctx.newSucceededFuture()); - } - } - - @Override - public void onPriorityRead(ChannelHandlerContext ctx, int streamId, int streamDependency, short weight, - boolean exclusive) throws Http2Exception { - encoder.flowController().updateDependencyTree(streamId, streamDependency, weight, exclusive); - - listener.onPriorityRead(ctx, streamId, streamDependency, weight, exclusive); - } - - @Override - public void onRstStreamRead(ChannelHandlerContext ctx, int streamId, long errorCode) throws Http2Exception { - Http2Stream stream = connection.stream(streamId); - if (stream == null) { - verifyStreamMayHaveExisted(streamId); - return; - } - - switch(stream.state()) { - case IDLE: - throw connectionError(PROTOCOL_ERROR, "RST_STREAM received for IDLE stream %d", streamId); - case CLOSED: - return; // RST_STREAM frames must be ignored for closed streams. - default: - break; - } - - listener.onRstStreamRead(ctx, streamId, errorCode); - - lifecycleManager.closeStream(stream, ctx.newSucceededFuture()); - } - - @Override - public void onSettingsAckRead(ChannelHandlerContext ctx) throws Http2Exception { - // Apply oldest outstanding local settings here. This is a synchronization point between endpoints. - Http2Settings settings = encoder.pollSentSettings(); - - if (settings != null) { - applyLocalSettings(settings); - } - - listener.onSettingsAckRead(ctx); - } - - /** - * Applies settings sent from the local endpoint. - *

- * This method is only called after the local settings have been acknowledged from the remote endpoint. - */ - private void applyLocalSettings(Http2Settings settings) throws Http2Exception { - Boolean pushEnabled = settings.pushEnabled(); - final Http2FrameReader.Configuration config = frameReader.configuration(); - final Http2HeadersDecoder.Configuration headerConfig = config.headersConfiguration(); - final Http2FrameSizePolicy frameSizePolicy = config.frameSizePolicy(); - if (pushEnabled != null) { - if (connection.isServer()) { - throw connectionError(PROTOCOL_ERROR, "Server sending SETTINGS frame with ENABLE_PUSH specified"); - } - connection.local().allowPushTo(pushEnabled); - } - - Long maxConcurrentStreams = settings.maxConcurrentStreams(); - if (maxConcurrentStreams != null) { - connection.remote().maxActiveStreams((int) min(maxConcurrentStreams, MAX_VALUE)); - } - - Long headerTableSize = settings.headerTableSize(); - if (headerTableSize != null) { - headerConfig.maxHeaderTableSize(headerTableSize); - } - - Long maxHeaderListSize = settings.maxHeaderListSize(); - if (maxHeaderListSize != null) { - headerConfig.maxHeaderListSize(maxHeaderListSize, calculateMaxHeaderListSizeGoAway(maxHeaderListSize)); - } - - Integer maxFrameSize = settings.maxFrameSize(); - if (maxFrameSize != null) { - frameSizePolicy.maxFrameSize(maxFrameSize); - } - - Integer initialWindowSize = settings.initialWindowSize(); - if (initialWindowSize != null) { - flowController().initialWindowSize(initialWindowSize); - } - } - - @Override - public void onSettingsRead(final ChannelHandlerContext ctx, Http2Settings settings) throws Http2Exception { - if (settingsReceivedConsumer == null) { - // Acknowledge receipt of the settings. We should do this before we process the settings to ensure our - // remote peer applies these settings before any subsequent frames that we may send which depend upon - // these new settings. See https://github.com/netty/netty/issues/6520. - encoder.writeSettingsAck(ctx); - - encoder.remoteSettings(settings); - } else { - settingsReceivedConsumer.consumeReceivedSettings(settings); - } - - listener.onSettingsRead(ctx, settings); - } - - @Override - public void onPingRead(ChannelHandlerContext ctx, long data) throws Http2Exception { - if (autoAckPing) { - // Send an ack back to the remote client. - encoder.writePing(ctx, true, data); - } - listener.onPingRead(ctx, data); - } - - @Override - public void onPingAckRead(ChannelHandlerContext ctx, long data) throws Http2Exception { - listener.onPingAckRead(ctx, data); - } - - @Override - public void onPushPromiseRead(ChannelHandlerContext ctx, int streamId, int promisedStreamId, - Http2Headers headers, int padding) throws Http2Exception { - // A client cannot push. - if (connection().isServer()) { - throw connectionError(PROTOCOL_ERROR, "A client cannot push."); - } - - Http2Stream parentStream = connection.stream(streamId); - - if (shouldIgnoreHeadersOrDataFrame(ctx, streamId, parentStream, "PUSH_PROMISE")) { - return; - } - - switch (parentStream.state()) { - case OPEN: - case HALF_CLOSED_LOCAL: - // Allowed to receive push promise in these states. - break; - default: - // Connection error. - throw connectionError(PROTOCOL_ERROR, - "Stream %d in unexpected state for receiving push promise: %s", - parentStream.id(), parentStream.state()); - } - - if (!requestVerifier.isAuthoritative(ctx, headers)) { - throw streamError(promisedStreamId, PROTOCOL_ERROR, - "Promised request on stream %d for promised stream %d is not authoritative", - streamId, promisedStreamId); - } - if (!requestVerifier.isCacheable(headers)) { - throw streamError(promisedStreamId, PROTOCOL_ERROR, - "Promised request on stream %d for promised stream %d is not known to be cacheable", - streamId, promisedStreamId); - } - if (!requestVerifier.isSafe(headers)) { - throw streamError(promisedStreamId, PROTOCOL_ERROR, - "Promised request on stream %d for promised stream %d is not known to be safe", - streamId, promisedStreamId); - } - - // Reserve the push stream based with a priority based on the current stream's priority. - connection.remote().reservePushStream(promisedStreamId, parentStream); - - listener.onPushPromiseRead(ctx, streamId, promisedStreamId, headers, padding); - } - - @Override - public void onGoAwayRead(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData) - throws Http2Exception { - onGoAwayRead0(ctx, lastStreamId, errorCode, debugData); - } - - @Override - public void onWindowUpdateRead(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement) - throws Http2Exception { - Http2Stream stream = connection.stream(streamId); - if (stream == null || stream.state() == CLOSED || streamCreatedAfterGoAwaySent(streamId)) { - // Ignore this frame. - verifyStreamMayHaveExisted(streamId); - return; - } - - // Update the outbound flow control window. - encoder.flowController().incrementWindowSize(stream, windowSizeIncrement); - - listener.onWindowUpdateRead(ctx, streamId, windowSizeIncrement); - } - - @Override - public void onUnknownFrame(ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags, - ByteBuf payload) throws Http2Exception { - onUnknownFrame0(ctx, frameType, streamId, flags, payload); - } - - /** - * Helper method to determine if a frame that has the semantics of headers or data should be ignored for the - * {@code stream} (which may be {@code null}) associated with {@code streamId}. - */ - private boolean shouldIgnoreHeadersOrDataFrame(ChannelHandlerContext ctx, int streamId, Http2Stream stream, - String frameName) throws Http2Exception { - if (stream == null) { - if (streamCreatedAfterGoAwaySent(streamId)) { - logger.info("{} ignoring {} frame for stream {}. Stream sent after GOAWAY sent", - ctx.channel(), frameName, streamId); - return true; - } - - // Make sure it's not an out-of-order frame, like a rogue DATA frame, for a stream that could - // never have existed. - verifyStreamMayHaveExisted(streamId); - - // Its possible that this frame would result in stream ID out of order creation (PROTOCOL ERROR) and its - // also possible that this frame is received on a CLOSED stream (STREAM_CLOSED after a RST_STREAM is - // sent). We don't have enough information to know for sure, so we choose the lesser of the two errors. - throw streamError(streamId, STREAM_CLOSED, "Received %s frame for an unknown stream %d", - frameName, streamId); - } - 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; - } - - /** - * Helper method for determining whether or not to ignore inbound frames. A stream is considered to be created - * after a {@code GOAWAY} is sent if the following conditions hold: - *

- *

    - *
  • A {@code GOAWAY} must have been sent by the local endpoint
  • - *
  • The {@code streamId} must identify a legitimate stream id for the remote endpoint to be creating
  • - *
  • {@code streamId} is greater than the Last Known Stream ID which was sent by the local endpoint - * in the last {@code GOAWAY} frame
  • - *
- *

- */ - private boolean streamCreatedAfterGoAwaySent(int streamId) { - Endpoint remote = connection.remote(); - return connection.goAwaySent() && remote.isValidStreamId(streamId) && - streamId > remote.lastStreamKnownByPeer(); - } - - private void verifyStreamMayHaveExisted(int streamId) throws Http2Exception { - if (!connection.streamMayHaveExisted(streamId)) { - throw connectionError(PROTOCOL_ERROR, "Stream %d does not exist", streamId); - } - } - } - - private final class PrefaceFrameListener implements Http2FrameListener { - /** - * Verifies that the HTTP/2 connection preface has been received from the remote endpoint. - * It is possible that the current call to - * {@link Http2FrameReader#readFrame(ChannelHandlerContext, ByteBuf, Http2FrameListener)} will have multiple - * frames to dispatch. So it may be OK for this class to get legitimate frames for the first readFrame. - */ - private void verifyPrefaceReceived() throws Http2Exception { - if (!prefaceReceived()) { - throw connectionError(PROTOCOL_ERROR, "Received non-SETTINGS as first frame."); - } - } - - @Override - public int onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding, boolean endOfStream) - throws Http2Exception { - verifyPrefaceReceived(); - return internalFrameListener.onDataRead(ctx, streamId, data, padding, endOfStream); - } - - @Override - public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding, - boolean endOfStream) throws Http2Exception { - verifyPrefaceReceived(); - internalFrameListener.onHeadersRead(ctx, streamId, headers, padding, endOfStream); - } - - @Override - public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int streamDependency, - short weight, boolean exclusive, int padding, boolean endOfStream) throws Http2Exception { - verifyPrefaceReceived(); - internalFrameListener.onHeadersRead(ctx, streamId, headers, streamDependency, weight, - exclusive, padding, endOfStream); - } - - @Override - public void onPriorityRead(ChannelHandlerContext ctx, int streamId, int streamDependency, short weight, - boolean exclusive) throws Http2Exception { - verifyPrefaceReceived(); - internalFrameListener.onPriorityRead(ctx, streamId, streamDependency, weight, exclusive); - } - - @Override - public void onRstStreamRead(ChannelHandlerContext ctx, int streamId, long errorCode) throws Http2Exception { - verifyPrefaceReceived(); - internalFrameListener.onRstStreamRead(ctx, streamId, errorCode); - } - - @Override - public void onSettingsAckRead(ChannelHandlerContext ctx) throws Http2Exception { - verifyPrefaceReceived(); - internalFrameListener.onSettingsAckRead(ctx); - } - - @Override - public void onSettingsRead(ChannelHandlerContext ctx, Http2Settings settings) throws Http2Exception { - // The first settings should change the internalFrameListener to the "real" listener - // that expects the preface to be verified. - if (!prefaceReceived()) { - internalFrameListener = new FrameReadListener(); - } - internalFrameListener.onSettingsRead(ctx, settings); - } - - @Override - public void onPingRead(ChannelHandlerContext ctx, long data) throws Http2Exception { - verifyPrefaceReceived(); - internalFrameListener.onPingRead(ctx, data); - } - - @Override - public void onPingAckRead(ChannelHandlerContext ctx, long data) throws Http2Exception { - verifyPrefaceReceived(); - internalFrameListener.onPingAckRead(ctx, data); - } - - @Override - public void onPushPromiseRead(ChannelHandlerContext ctx, int streamId, int promisedStreamId, - Http2Headers headers, int padding) throws Http2Exception { - verifyPrefaceReceived(); - internalFrameListener.onPushPromiseRead(ctx, streamId, promisedStreamId, headers, padding); - } - - @Override - public void onGoAwayRead(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData) - throws Http2Exception { - onGoAwayRead0(ctx, lastStreamId, errorCode, debugData); - } - - @Override - public void onWindowUpdateRead(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement) - throws Http2Exception { - verifyPrefaceReceived(); - internalFrameListener.onWindowUpdateRead(ctx, streamId, windowSizeIncrement); - } - - @Override - public void onUnknownFrame(ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags, - ByteBuf payload) throws Http2Exception { - onUnknownFrame0(ctx, frameType, streamId, flags, payload); - } - } - - private static final class ContentLength { - private final long expected; - private long seen; - - ContentLength(long expected) { - this.expected = expected; - } - - void increaseReceivedBytes(boolean server, int streamId, int bytes, boolean isEnd) throws Http2Exception { - seen += bytes; - // Check for overflow - if (seen < 0) { - throw streamError(streamId, PROTOCOL_ERROR, - "Received amount of data did overflow and so not match content-length header %d", expected); - } - // Check if we received more data then what was advertised via the content-length header. - if (seen > expected) { - throw streamError(streamId, PROTOCOL_ERROR, - "Received amount of data %d does not match content-length header %d", seen, expected); - } - - if (isEnd) { - if (seen == 0 && !server) { - // This may be a response to a HEAD request, let's just allow it. - return; - } - - // Check that we really saw what was told via the content-length header. - if (expected > seen) { - throw streamError(streamId, PROTOCOL_ERROR, - "Received amount of data %d does not match content-length header %d", seen, expected); - } - } - } - } -} 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 deleted file mode 100644 index bdba4f1383..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionEncoder.java +++ /dev/null @@ -1,626 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.CoalescingBufferQueue; -import io.netty.handler.codec.http.HttpStatusClass; -import io.netty.handler.codec.http2.Http2CodecUtil.SimpleChannelPromiseAggregator; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.FutureListener; -import io.netty.util.concurrent.Promise; -import io.netty.util.internal.UnstableApi; - -import java.util.ArrayDeque; -import java.util.Queue; - -import static io.netty.handler.codec.http.HttpStatusClass.INFORMATIONAL; -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.util.internal.ObjectUtil.checkPositiveOrZero; -import static java.lang.Integer.MAX_VALUE; -import static java.lang.Math.min; -import static java.util.Objects.requireNonNull; - -/** - * Default implementation of {@link Http2ConnectionEncoder}. - */ -@UnstableApi -public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Http2SettingsReceivedConsumer { - private final Http2FrameWriter frameWriter; - private final Http2Connection connection; - private Http2LifecycleManager lifecycleManager; - // We prefer ArrayDeque to LinkedList because later will produce more GC. - // This initial capacity is plenty for SETTINGS traffic. - private final Queue outstandingLocalSettingsQueue = new ArrayDeque<>(4); - private Queue outstandingRemoteSettingsQueue; - - public DefaultHttp2ConnectionEncoder(Http2Connection connection, Http2FrameWriter frameWriter) { - this.connection = requireNonNull(connection, "connection"); - this.frameWriter = requireNonNull(frameWriter, "frameWriter"); - if (connection.remote().flowController() == null) { - connection.remote().flowController(new DefaultHttp2RemoteFlowController(connection)); - } - } - - @Override - public void lifecycleManager(Http2LifecycleManager lifecycleManager) { - this.lifecycleManager = requireNonNull(lifecycleManager, "lifecycleManager"); - } - - @Override - public Http2FrameWriter frameWriter() { - return frameWriter; - } - - @Override - public Http2Connection connection() { - return connection; - } - - @Override - public final Http2RemoteFlowController flowController() { - return connection().remote().flowController(); - } - - @Override - public void remoteSettings(Http2Settings settings) throws Http2Exception { - Boolean pushEnabled = settings.pushEnabled(); - Http2FrameWriter.Configuration config = configuration(); - Http2HeadersEncoder.Configuration outboundHeaderConfig = config.headersConfiguration(); - Http2FrameSizePolicy outboundFrameSizePolicy = config.frameSizePolicy(); - if (pushEnabled != null) { - if (!connection.isServer() && pushEnabled) { - throw connectionError(PROTOCOL_ERROR, - "Client received a value of ENABLE_PUSH specified to other than 0"); - } - connection.remote().allowPushTo(pushEnabled); - } - - Long maxConcurrentStreams = settings.maxConcurrentStreams(); - if (maxConcurrentStreams != null) { - connection.local().maxActiveStreams((int) min(maxConcurrentStreams, MAX_VALUE)); - } - - Long headerTableSize = settings.headerTableSize(); - if (headerTableSize != null) { - outboundHeaderConfig.maxHeaderTableSize((int) min(headerTableSize, MAX_VALUE)); - } - - Long maxHeaderListSize = settings.maxHeaderListSize(); - if (maxHeaderListSize != null) { - outboundHeaderConfig.maxHeaderListSize(maxHeaderListSize); - } - - Integer maxFrameSize = settings.maxFrameSize(); - if (maxFrameSize != null) { - outboundFrameSizePolicy.maxFrameSize(maxFrameSize); - } - - Integer initialWindowSize = settings.initialWindowSize(); - if (initialWindowSize != null) { - flowController().initialWindowSize(initialWindowSize); - } - } - - @Override - public Future writeData(final ChannelHandlerContext ctx, final int streamId, ByteBuf data, int padding, - final boolean endOfStream) { - final Http2Stream stream; - try { - stream = requireStream(streamId); - - // Verify that the stream is in the appropriate state for sending DATA frames. - switch (stream.state()) { - case OPEN: - case HALF_CLOSED_REMOTE: - // Allowed sending DATA frames in these states. - break; - default: - throw new IllegalStateException("Stream " + stream.id() + " in unexpected state " + stream.state()); - } - } catch (Throwable e) { - data.release(); - return ctx.newFailedFuture(e); - } - - Promise promise = ctx.newPromise(); - // Hand control of the frame to the flow controller. - flowController().addFlowControlled(stream, - new FlowControlledData(stream, data, padding, endOfStream, promise, ctx.channel())); - return promise.asFuture(); - } - - @Override - public Future writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding, - boolean endStream) { - return writeHeaders0(ctx, streamId, headers, false, 0, (short) 0, false, padding, endStream); - } - - private static boolean validateHeadersSentState(Http2Stream stream, Http2Headers headers, boolean isServer, - boolean endOfStream) { - boolean isInformational = isServer && HttpStatusClass.valueOf(headers.status()) == INFORMATIONAL; - if ((isInformational || !endOfStream) && stream.isHeadersSent() || stream.isTrailersSent()) { - throw new IllegalStateException("Stream " + stream.id() + " sent too many headers EOS: " + endOfStream); - } - return isInformational; - } - - @Override - public Future writeHeaders(final ChannelHandlerContext ctx, final int streamId, - final Http2Headers headers, final int streamDependency, final short weight, - final boolean exclusive, final int padding, final boolean endOfStream) { - return writeHeaders0(ctx, streamId, headers, true, streamDependency, - weight, exclusive, padding, endOfStream); - } - - /** - * Write headers via {@link Http2FrameWriter}. If {@code hasPriority} is {@code false} it will ignore the - * {@code streamDependency}, {@code weight} and {@code exclusive} parameters. - */ - private static Future sendHeaders(Http2FrameWriter frameWriter, ChannelHandlerContext ctx, int streamId, - Http2Headers headers, final boolean hasPriority, - int streamDependency, final short weight, - boolean exclusive, final int padding, - boolean endOfStream) { - if (hasPriority) { - return frameWriter.writeHeaders(ctx, streamId, headers, streamDependency, - weight, exclusive, padding, endOfStream); - } - return frameWriter.writeHeaders(ctx, streamId, headers, padding, endOfStream); - } - - private Future writeHeaders0(final ChannelHandlerContext ctx, final int streamId, - final Http2Headers headers, final boolean hasPriority, - final int streamDependency, final short weight, - final boolean exclusive, final int padding, - final boolean endOfStream) { - try { - Http2Stream stream = connection.stream(streamId); - if (stream == null) { - try { - // 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)) { - return ctx.newFailedFuture( - new IllegalStateException("Stream no longer exists: " + streamId, cause)); - } - throw cause; - } - } else { - switch (stream.state()) { - case RESERVED_LOCAL: - stream.open(endOfStream); - break; - case OPEN: - case HALF_CLOSED_REMOTE: - // Allowed sending headers in these states. - break; - default: - throw new IllegalStateException("Stream " + stream.id() + " in unexpected state " + - stream.state()); - } - } - - // Trailing headers must go through flow control if there are other frames queued in flow control - // for this stream. - Http2RemoteFlowController flowController = flowController(); - if (!endOfStream || !flowController.hasFlowControlled(stream)) { - // The behavior here should mirror that in FlowControlledHeaders - - boolean isInformational = validateHeadersSentState(stream, headers, connection.isServer(), endOfStream); - - Future future = sendHeaders(frameWriter, ctx, streamId, headers, hasPriority, streamDependency, - weight, exclusive, padding, endOfStream); - - // Writing headers may fail during the encode state if they violate HPACK limits. - - if (future.isSuccess() || !future.isDone()) { - // Synchronously set the headersSent flag to ensure that we do not subsequently write - // other headers containing pseudo-header fields. - // - // This just sets internal stream state which is used elsewhere in the codec and doesn't - // necessarily mean the write will complete successfully. - stream.headersSent(isInformational); - - if (!future.isSuccess()) { - // Either the future is not done or failed in the meantime. - notifyLifecycleManagerOnError(future, ctx); - } - } else { - Throwable failureCause = future.cause(); - 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 { - Promise promise = ctx.newPromise(); - // Pass headers to the flow-controller so it can maintain their sequence relative to DATA frames. - flowController.addFlowControlled(stream, - new FlowControlledHeaders(stream, headers, hasPriority, streamDependency, - weight, exclusive, padding, true, promise)); - return promise.asFuture(); - } - } catch (Throwable t) { - lifecycleManager.onError(ctx, true, t); - return ctx.newFailedFuture(t); - } - } - - @Override - public Future writePriority(ChannelHandlerContext ctx, int streamId, int streamDependency, short weight, - boolean exclusive) { - return frameWriter.writePriority(ctx, streamId, streamDependency, weight, exclusive); - } - - @Override - public Future writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode) { - // Delegate to the lifecycle manager for proper updating of connection state. - return lifecycleManager.resetStream(ctx, streamId, errorCode); - } - - @Override - public Future writeSettings(ChannelHandlerContext ctx, Http2Settings settings) { - outstandingLocalSettingsQueue.add(settings); - try { - Boolean pushEnabled = settings.pushEnabled(); - if (pushEnabled != null && connection.isServer()) { - throw connectionError(PROTOCOL_ERROR, "Server sending SETTINGS frame with ENABLE_PUSH specified"); - } - } catch (Throwable e) { - return ctx.newFailedFuture(e); - } - - return frameWriter.writeSettings(ctx, settings); - } - - @Override - public Future writeSettingsAck(ChannelHandlerContext ctx) { - if (outstandingRemoteSettingsQueue == null) { - return frameWriter.writeSettingsAck(ctx); - } - Http2Settings settings = outstandingRemoteSettingsQueue.poll(); - if (settings == null) { - return ctx.newFailedFuture(new Http2Exception(INTERNAL_ERROR, "attempted to write a SETTINGS ACK with no " + - " pending SETTINGS")); - } - SimpleChannelPromiseAggregator aggregator = - new SimpleChannelPromiseAggregator(ctx.newPromise(), ctx.executor()); - // Acknowledge receipt of the settings. We should do this before we process the settings to ensure our - // remote peer applies these settings before any subsequent frames that we may send which depend upon - // these new settings. See https://github.com/netty/netty/issues/6520. - frameWriter.writeSettingsAck(ctx).cascadeTo(aggregator.newPromise()); - - // We create a "new promise" to make sure that status from both the write and the application are taken into - // account independently. - Promise applySettingsPromise = aggregator.newPromise(); - try { - remoteSettings(settings); - applySettingsPromise.setSuccess(null); - } catch (Throwable e) { - applySettingsPromise.setFailure(e); - lifecycleManager.onError(ctx, true, e); - } - return aggregator.doneAllocatingPromises(); - } - - @Override - public Future writePing(ChannelHandlerContext ctx, boolean ack, long data) { - return frameWriter.writePing(ctx, ack, data); - } - - @Override - public Future writePushPromise(ChannelHandlerContext ctx, int streamId, int promisedStreamId, - Http2Headers headers, int padding) { - try { - if (connection.goAwayReceived()) { - throw connectionError(PROTOCOL_ERROR, "Sending PUSH_PROMISE after GO_AWAY received."); - } - - Http2Stream stream = requireStream(streamId); - // Reserve the promised stream. - connection.local().reservePushStream(promisedStreamId, stream); - - Future future = frameWriter.writePushPromise(ctx, streamId, promisedStreamId, headers, padding); - // Writing headers may fail during the encode state if they violate HPACK limits. - if (future.isSuccess() || !future.isDone()) { - // This just sets internal stream state which is used elsewhere in the codec and doesn't - // necessarily mean the write will complete successfully. - stream.pushPromiseSent(); - - if (!future.isSuccess()) { - // Either the future is not done or failed in the meantime. - notifyLifecycleManagerOnError(future, ctx); - } - } else { - Throwable failureCause = future.cause(); - lifecycleManager.onError(ctx, true, failureCause); - } - return future; - } catch (Throwable t) { - lifecycleManager.onError(ctx, true, t); - return ctx.newFailedFuture(t); - } - } - - @Override - public Future writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData) { - return lifecycleManager.goAway(ctx, lastStreamId, errorCode, debugData); - } - - @Override - public Future writeWindowUpdate(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement) { - return ctx.newFailedFuture(new UnsupportedOperationException("Use the Http2[Inbound|Outbound]FlowController" + - " objects to control window sizes")); - } - - @Override - public Future writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags, - ByteBuf payload) { - return frameWriter.writeFrame(ctx, frameType, streamId, flags, payload); - } - - @Override - public void close() { - frameWriter.close(); - } - - @Override - public Http2Settings pollSentSettings() { - return outstandingLocalSettingsQueue.poll(); - } - - @Override - public Configuration configuration() { - return frameWriter.configuration(); - } - - private Http2Stream requireStream(int streamId) { - Http2Stream stream = connection.stream(streamId); - if (stream == null) { - final String message; - if (connection.streamMayHaveExisted(streamId)) { - message = "Stream no longer exists: " + streamId; - } else { - message = "Stream does not exist: " + streamId; - } - throw new IllegalArgumentException(message); - } - return stream; - } - - @Override - public void consumeReceivedSettings(Http2Settings settings) { - if (outstandingRemoteSettingsQueue == null) { - outstandingRemoteSettingsQueue = new ArrayDeque<>(2); - } - outstandingRemoteSettingsQueue.add(settings); - } - - /** - * Wrap a DATA frame so it can be written subject to flow-control. Note that this implementation assumes it - * only writes padding once for the entire payload as opposed to writing it once per-frame. This makes the - * {@link #size} calculation deterministic thereby greatly simplifying the implementation. - *

- * If frame-splitting is required to fit within max-frame-size and flow-control constraints we ensure that - * the passed promise is not completed until last frame write. - *

- */ - private final class FlowControlledData extends FlowControlledBase { - private final CoalescingBufferQueue queue; - private int dataSize; - - FlowControlledData(Http2Stream stream, ByteBuf buf, int padding, boolean endOfStream, - Promise promise, Channel channel) { - super(stream, padding, endOfStream, promise); - queue = new CoalescingBufferQueue(channel); - queue.add(buf, promise); - dataSize = queue.readableBytes(); - } - - @Override - public int size() { - return dataSize + padding; - } - - @Override - 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); - } - - @Override - public void write(ChannelHandlerContext ctx, int allowedBytes) { - int queuedData = queue.readableBytes(); - if (!endOfStream) { - if (queuedData == 0) { - 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). - Promise writePromise = ctx.newPromise(); - writePromise.asFuture().addListener(this); - ctx.write(queue.remove(0, writePromise)).cascadeTo(writePromise); - } - return; - } - - if (allowedBytes == 0) { - return; - } - } - - // Determine how much data to write. - int writableData = min(queuedData, allowedBytes); - Promise writePromise = ctx.newPromise(); - writePromise.asFuture().addListener(this); - ByteBuf toWrite = queue.remove(writableData, writePromise); - dataSize = queue.readableBytes(); - - // Determine how much padding to write. - int writablePadding = min(allowedBytes - writableData, padding); - padding -= writablePadding; - - // Write the frame(s). - frameWriter().writeData(ctx, stream.id(), toWrite, writablePadding, - endOfStream && size() == 0).cascadeTo(writePromise); - } - - @Override - public boolean merge(ChannelHandlerContext ctx, Http2RemoteFlowController.FlowControlled next) { - FlowControlledData nextData; - if (FlowControlledData.class != next.getClass() || - MAX_VALUE - (nextData = (FlowControlledData) next).size() < size()) { - return false; - } - nextData.queue.copyTo(queue); - dataSize = queue.readableBytes(); - // Given that we're merging data into a frame it doesn't really make sense to accumulate padding. - padding = Math.max(padding, nextData.padding); - endOfStream = nextData.endOfStream; - return true; - } - } - - private void notifyLifecycleManagerOnError(Future future, final ChannelHandlerContext ctx) { - future.addListener(future1 -> { - Throwable cause = future1.cause(); - if (cause != null) { - lifecycleManager.onError(ctx, true, cause); - } - }); - } - - /** - * Wrap headers so they can be written subject to flow-control. While headers do not have cost against the - * flow-control window their order with respect to other frames must be maintained, hence if a DATA frame is - * blocked on flow-control a HEADER frame must wait until this frame has been written. - */ - private final class FlowControlledHeaders extends FlowControlledBase { - private final Http2Headers headers; - private final boolean hasPriority; - private final int streamDependency; - private final short weight; - private final boolean exclusive; - - FlowControlledHeaders(Http2Stream stream, Http2Headers headers, boolean hasPriority, - int streamDependency, short weight, boolean exclusive, - int padding, boolean endOfStream, Promise promise) { - super(stream, padding, endOfStream, promise); - this.headers = headers; - this.hasPriority = hasPriority; - this.streamDependency = streamDependency; - this.weight = weight; - this.exclusive = exclusive; - } - - @Override - public int size() { - return 0; - } - - @Override - public void error(ChannelHandlerContext ctx, Throwable cause) { - if (ctx != null) { - lifecycleManager.onError(ctx, true, cause); - } - promise.tryFailure(cause); - } - - @Override - public void write(ChannelHandlerContext ctx, int allowedBytes) { - boolean isInformational = validateHeadersSentState(stream, headers, connection.isServer(), endOfStream); - // The code is currently requiring adding this listener before writing, in order to call onError() before - // closeStreamLocal(). - promise.asFuture().addListener(this); - - Future f = sendHeaders(frameWriter, ctx, stream.id(), headers, hasPriority, streamDependency, - weight, exclusive, padding, endOfStream); - f.cascadeTo(promise); - // Writing headers may fail during the encode state if they violate HPACK limits. - if (!f.isFailed()) { // "not failed" means either not done, or completed successfully. - // This just sets internal stream state which is used elsewhere in the codec and doesn't - // necessarily mean the write will complete successfully. - stream.headersSent(isInformational); - } - } - - @Override - public boolean merge(ChannelHandlerContext ctx, Http2RemoteFlowController.FlowControlled next) { - return false; - } - } - - /** - * Common base type for payloads to deliver via flow-control. - */ - public abstract class FlowControlledBase implements Http2RemoteFlowController.FlowControlled, FutureListener { - protected final Http2Stream stream; - protected Promise promise; - protected boolean endOfStream; - protected int padding; - - FlowControlledBase(final Http2Stream stream, int padding, boolean endOfStream, - final Promise promise) { - checkPositiveOrZero(padding, "padding"); - this.padding = padding; - this.endOfStream = endOfStream; - this.stream = stream; - this.promise = promise; - } - - @Override - public void writeComplete() { - if (endOfStream) { - lifecycleManager.closeStreamLocal(stream, promise.asFuture()); - } - } - - @Override - public void operationComplete(Future future) { - if (future.isFailed()) { - error(flowController().channelHandlerContext(), future.cause()); - } - } - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2DataFrame.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2DataFrame.java deleted file mode 100644 index 8856831ba3..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2DataFrame.java +++ /dev/null @@ -1,198 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.buffer.Unpooled; -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.UnstableApi; - -import static io.netty.handler.codec.http2.Http2CodecUtil.verifyPadding; -import static java.util.Objects.requireNonNull; - -/** - * The default {@link Http2DataFrame} implementation. - */ -@UnstableApi -public final class DefaultHttp2DataFrame extends AbstractHttp2StreamFrame implements Http2DataFrame { - private final ByteBuf content; - private final boolean endStream; - private final int padding; - private final int initialFlowControlledBytes; - - /** - * Equivalent to {@code new DefaultHttp2DataFrame(content, false)}. - * - * @param content non-{@code null} payload - */ - public DefaultHttp2DataFrame(ByteBuf content) { - this(content, false); - } - - /** - * Equivalent to {@code new DefaultHttp2DataFrame(Unpooled.EMPTY_BUFFER, endStream)}. - * - * @param endStream whether this data should terminate the stream - */ - public DefaultHttp2DataFrame(boolean endStream) { - this(Unpooled.EMPTY_BUFFER, endStream); - } - - /** - * Equivalent to {@code new DefaultHttp2DataFrame(content, endStream, 0)}. - * - * @param content non-{@code null} payload - * @param endStream whether this data should terminate the stream - */ - public DefaultHttp2DataFrame(ByteBuf content, boolean endStream) { - this(content, endStream, 0); - } - - /** - * Construct a new data message. - * - * @param content non-{@code null} payload - * @param endStream whether this data should terminate the stream - * @param padding additional bytes that should be added to obscure the true content size. Must be between 0 and - * 256 (inclusive). - */ - public DefaultHttp2DataFrame(ByteBuf content, boolean endStream, int padding) { - this.content = requireNonNull(content, "content"); - this.endStream = endStream; - verifyPadding(padding); - this.padding = padding; - if (content().readableBytes() + (long) padding > Integer.MAX_VALUE) { - throw new IllegalArgumentException("content + padding must be <= Integer.MAX_VALUE"); - } - initialFlowControlledBytes = content().readableBytes() + padding; - } - - @Override - public DefaultHttp2DataFrame stream(Http2FrameStream stream) { - super.stream(stream); - return this; - } - - @Override - public String name() { - return "DATA"; - } - - @Override - public boolean isEndStream() { - return endStream; - } - - @Override - public int padding() { - return padding; - } - - @Override - public ByteBuf content() { - return ByteBufUtil.ensureAccessible(content); - } - - @Override - public int initialFlowControlledBytes() { - return initialFlowControlledBytes; - } - - @Override - public DefaultHttp2DataFrame copy() { - return replace(content().copy()); - } - - @Override - public DefaultHttp2DataFrame duplicate() { - return replace(content().duplicate()); - } - - @Override - public DefaultHttp2DataFrame retainedDuplicate() { - return replace(content().retainedDuplicate()); - } - - @Override - public DefaultHttp2DataFrame replace(ByteBuf content) { - return new DefaultHttp2DataFrame(content, endStream, padding); - } - - @Override - public int refCnt() { - return content.refCnt(); - } - - @Override - public boolean release() { - return content.release(); - } - - @Override - public boolean release(int decrement) { - return content.release(decrement); - } - - @Override - public DefaultHttp2DataFrame retain() { - content.retain(); - return this; - } - - @Override - public DefaultHttp2DataFrame retain(int increment) { - content.retain(increment); - return this; - } - - @Override - public String toString() { - return StringUtil.simpleClassName(this) + "(stream=" + stream() + ", content=" + content - + ", endStream=" + endStream + ", padding=" + padding + ')'; - } - - @Override - public DefaultHttp2DataFrame touch() { - content.touch(); - return this; - } - - @Override - public DefaultHttp2DataFrame touch(Object hint) { - content.touch(hint); - return this; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof DefaultHttp2DataFrame)) { - return false; - } - DefaultHttp2DataFrame other = (DefaultHttp2DataFrame) o; - return super.equals(other) && content.equals(other.content()) - && endStream == other.endStream && padding == other.padding; - } - - @Override - public int hashCode() { - int hash = super.hashCode(); - hash = hash * 31 + content.hashCode(); - hash = hash * 31 + (endStream ? 0 : 1); - hash = hash * 31 + padding; - return hash; - } -} 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 deleted file mode 100644 index 953b4b8bfd..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2FrameReader.java +++ /dev/null @@ -1,771 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http2.Http2FrameReader.Configuration; -import io.netty.util.internal.UnstableApi; - -import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_MAX_FRAME_SIZE; -import static io.netty.handler.codec.http2.Http2CodecUtil.FRAME_HEADER_LENGTH; -import static io.netty.handler.codec.http2.Http2CodecUtil.INT_FIELD_LENGTH; -import static io.netty.handler.codec.http2.Http2CodecUtil.PING_FRAME_PAYLOAD_LENGTH; -import static io.netty.handler.codec.http2.Http2CodecUtil.PRIORITY_ENTRY_LENGTH; -import static io.netty.handler.codec.http2.Http2CodecUtil.SETTINGS_INITIAL_WINDOW_SIZE; -import static io.netty.handler.codec.http2.Http2CodecUtil.SETTING_ENTRY_LENGTH; -import static io.netty.handler.codec.http2.Http2CodecUtil.headerListSizeExceeded; -import static io.netty.handler.codec.http2.Http2CodecUtil.isMaxFrameSizeValid; -import static io.netty.handler.codec.http2.Http2CodecUtil.readUnsignedInt; -import static io.netty.handler.codec.http2.Http2Error.FLOW_CONTROL_ERROR; -import static io.netty.handler.codec.http2.Http2Error.FRAME_SIZE_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.Http2FrameTypes.CONTINUATION; -import static io.netty.handler.codec.http2.Http2FrameTypes.DATA; -import static io.netty.handler.codec.http2.Http2FrameTypes.GO_AWAY; -import static io.netty.handler.codec.http2.Http2FrameTypes.HEADERS; -import static io.netty.handler.codec.http2.Http2FrameTypes.PING; -import static io.netty.handler.codec.http2.Http2FrameTypes.PRIORITY; -import static io.netty.handler.codec.http2.Http2FrameTypes.PUSH_PROMISE; -import static io.netty.handler.codec.http2.Http2FrameTypes.RST_STREAM; -import static io.netty.handler.codec.http2.Http2FrameTypes.SETTINGS; -import static io.netty.handler.codec.http2.Http2FrameTypes.WINDOW_UPDATE; - -/** - * A {@link Http2FrameReader} that supports all frame types defined by the HTTP/2 specification. - */ -@UnstableApi -public class DefaultHttp2FrameReader implements Http2FrameReader, Http2FrameSizePolicy, Configuration { - private final Http2HeadersDecoder headersDecoder; - - /** - * {@code true} = reading headers, {@code false} = reading payload. - */ - private boolean readingHeaders = true; - /** - * Once set to {@code true} the value will never change. This is set to {@code true} if an unrecoverable error which - * renders the connection unusable. - */ - private boolean readError; - private byte frameType; - private int streamId; - private Http2Flags flags; - private int payloadLength; - private HeadersContinuation headersContinuation; - private int maxFrameSize; - - /** - * Create a new instance. - *

- * Header names will be validated. - */ - public DefaultHttp2FrameReader() { - this(true); - } - - /** - * Create a new instance. - * @param validateHeaders {@code true} to validate headers. {@code false} to not validate headers. - * @see DefaultHttp2HeadersDecoder(boolean) - */ - public DefaultHttp2FrameReader(boolean validateHeaders) { - this(new DefaultHttp2HeadersDecoder(validateHeaders)); - } - - public DefaultHttp2FrameReader(Http2HeadersDecoder headersDecoder) { - this.headersDecoder = headersDecoder; - maxFrameSize = DEFAULT_MAX_FRAME_SIZE; - } - - @Override - public Http2HeadersDecoder.Configuration headersConfiguration() { - return headersDecoder.configuration(); - } - - @Override - public Configuration configuration() { - return this; - } - - @Override - public Http2FrameSizePolicy frameSizePolicy() { - return this; - } - - @Override - public void maxFrameSize(int max) throws Http2Exception { - if (!isMaxFrameSizeValid(max)) { - throw streamError(streamId, FRAME_SIZE_ERROR, - "Invalid MAX_FRAME_SIZE specified in sent settings: %d", max); - } - maxFrameSize = max; - } - - @Override - public int maxFrameSize() { - return maxFrameSize; - } - - @Override - public void close() { - closeHeadersContinuation(); - } - - private void closeHeadersContinuation() { - if (headersContinuation != null) { - headersContinuation.close(); - headersContinuation = null; - } - } - - @Override - public void readFrame(ChannelHandlerContext ctx, ByteBuf input, Http2FrameListener listener) - throws Http2Exception { - if (readError) { - input.skipBytes(input.readableBytes()); - return; - } - try { - do { - if (readingHeaders) { - processHeaderState(input); - if (readingHeaders) { - // Wait until the entire header has arrived. - return; - } - } - - // The header is complete, fall into the next case to process the payload. - // This is to ensure the proper handling of zero-length payloads. In this - // case, we don't want to loop around because there may be no more data - // available, causing us to exit the loop. Instead, we just want to perform - // the first pass at payload processing now. - processPayloadState(ctx, input, listener); - if (!readingHeaders) { - // Wait until the entire payload has arrived. - return; - } - } while (input.isReadable()); - } catch (Http2Exception e) { - readError = !Http2Exception.isStreamError(e); - throw e; - } catch (Throwable e) { - readError = true; - throw e; - } - } - - private void processHeaderState(ByteBuf in) throws Http2Exception { - if (in.readableBytes() < FRAME_HEADER_LENGTH) { - // Wait until the entire frame header has been read. - return; - } - - // Read the header and prepare the unmarshaller to read the frame. - payloadLength = in.readUnsignedMedium(); - if (payloadLength > maxFrameSize) { - throw connectionError(FRAME_SIZE_ERROR, "Frame length: %d exceeds maximum: %d", payloadLength, - maxFrameSize); - } - frameType = in.readByte(); - flags = new Http2Flags(in.readUnsignedByte()); - streamId = readUnsignedInt(in); - - // We have consumed the data, next time we read we will be expecting to read the frame payload. - readingHeaders = false; - - switch (frameType) { - case DATA: - verifyDataFrame(); - break; - case HEADERS: - verifyHeadersFrame(); - break; - case PRIORITY: - verifyPriorityFrame(); - break; - case RST_STREAM: - verifyRstStreamFrame(); - break; - case SETTINGS: - verifySettingsFrame(); - break; - case PUSH_PROMISE: - verifyPushPromiseFrame(); - break; - case PING: - verifyPingFrame(); - break; - case GO_AWAY: - verifyGoAwayFrame(); - break; - case WINDOW_UPDATE: - verifyWindowUpdateFrame(); - break; - case CONTINUATION: - verifyContinuationFrame(); - break; - default: - // Unknown frame type, could be an extension. - verifyUnknownFrame(); - break; - } - } - - private void processPayloadState(ChannelHandlerContext ctx, ByteBuf in, Http2FrameListener listener) - throws Http2Exception { - if (in.readableBytes() < payloadLength) { - // Wait until the entire payload has been read. - return; - } - - // 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; - - // Read the payload and fire the frame event to the listener. - switch (frameType) { - case DATA: - readDataFrame(ctx, in, payloadEndIndex, listener); - break; - case HEADERS: - readHeadersFrame(ctx, in, payloadEndIndex, listener); - break; - case PRIORITY: - readPriorityFrame(ctx, in, listener); - break; - case RST_STREAM: - readRstStreamFrame(ctx, in, listener); - break; - case SETTINGS: - readSettingsFrame(ctx, in, listener); - break; - case PUSH_PROMISE: - readPushPromiseFrame(ctx, in, payloadEndIndex, listener); - break; - case PING: - readPingFrame(ctx, in.readLong(), listener); - break; - case GO_AWAY: - readGoAwayFrame(ctx, in, payloadEndIndex, listener); - break; - case WINDOW_UPDATE: - readWindowUpdateFrame(ctx, in, listener); - break; - case CONTINUATION: - readContinuationFrame(in, payloadEndIndex, listener); - break; - default: - readUnknownFrame(ctx, in, payloadEndIndex, listener); - break; - } - in.readerIndex(payloadEndIndex); - } - - private void verifyDataFrame() throws Http2Exception { - verifyAssociatedWithAStream(); - verifyNotProcessingHeaders(); - - if (payloadLength < flags.getPaddingPresenceFieldLength()) { - throw streamError(streamId, FRAME_SIZE_ERROR, - "Frame length %d too small.", payloadLength); - } - } - - private void verifyHeadersFrame() throws Http2Exception { - verifyAssociatedWithAStream(); - verifyNotProcessingHeaders(); - - int requiredLength = flags.getPaddingPresenceFieldLength() + flags.getNumPriorityBytes(); - if (payloadLength < requiredLength) { - throw streamError(streamId, FRAME_SIZE_ERROR, - "Frame length too small." + payloadLength); - } - } - - private void verifyPriorityFrame() throws Http2Exception { - verifyAssociatedWithAStream(); - verifyNotProcessingHeaders(); - - if (payloadLength != PRIORITY_ENTRY_LENGTH) { - throw streamError(streamId, FRAME_SIZE_ERROR, - "Invalid frame length %d.", payloadLength); - } - } - - private void verifyRstStreamFrame() throws Http2Exception { - verifyAssociatedWithAStream(); - verifyNotProcessingHeaders(); - - if (payloadLength != INT_FIELD_LENGTH) { - throw connectionError(FRAME_SIZE_ERROR, "Invalid frame length %d.", payloadLength); - } - } - - private void verifySettingsFrame() throws Http2Exception { - verifyNotProcessingHeaders(); - if (streamId != 0) { - throw connectionError(PROTOCOL_ERROR, "A stream ID must be zero."); - } - if (flags.ack() && payloadLength > 0) { - throw connectionError(FRAME_SIZE_ERROR, "Ack settings frame must have an empty payload."); - } - if (payloadLength % SETTING_ENTRY_LENGTH > 0) { - throw connectionError(FRAME_SIZE_ERROR, "Frame length %d invalid.", payloadLength); - } - } - - private void verifyPushPromiseFrame() throws Http2Exception { - verifyNotProcessingHeaders(); - - // Subtract the length of the promised stream ID field, to determine the length of the - // rest of the payload (header block fragment + payload). - int minLength = flags.getPaddingPresenceFieldLength() + INT_FIELD_LENGTH; - if (payloadLength < minLength) { - throw streamError(streamId, FRAME_SIZE_ERROR, - "Frame length %d too small.", payloadLength); - } - } - - private void verifyPingFrame() throws Http2Exception { - verifyNotProcessingHeaders(); - if (streamId != 0) { - throw connectionError(PROTOCOL_ERROR, "A stream ID must be zero."); - } - if (payloadLength != PING_FRAME_PAYLOAD_LENGTH) { - throw connectionError(FRAME_SIZE_ERROR, - "Frame length %d incorrect size for ping.", payloadLength); - } - } - - private void verifyGoAwayFrame() throws Http2Exception { - verifyNotProcessingHeaders(); - - if (streamId != 0) { - throw connectionError(PROTOCOL_ERROR, "A stream ID must be zero."); - } - if (payloadLength < 8) { - throw connectionError(FRAME_SIZE_ERROR, "Frame length %d too small.", payloadLength); - } - } - - private void verifyWindowUpdateFrame() throws Http2Exception { - verifyNotProcessingHeaders(); - verifyStreamOrConnectionId(streamId, "Stream ID"); - - if (payloadLength != INT_FIELD_LENGTH) { - throw connectionError(FRAME_SIZE_ERROR, "Invalid frame length %d.", payloadLength); - } - } - - private void verifyContinuationFrame() throws Http2Exception { - verifyAssociatedWithAStream(); - - if (headersContinuation == null) { - throw connectionError(PROTOCOL_ERROR, "Received %s frame but not currently processing headers.", - frameType); - } - - if (streamId != headersContinuation.getStreamId()) { - throw connectionError(PROTOCOL_ERROR, "Continuation stream ID does not match pending headers. " - + "Expected %d, but received %d.", headersContinuation.getStreamId(), streamId); - } - - if (payloadLength < flags.getPaddingPresenceFieldLength()) { - throw streamError(streamId, FRAME_SIZE_ERROR, - "Frame length %d too small for padding.", payloadLength); - } - } - - private void verifyUnknownFrame() throws Http2Exception { - verifyNotProcessingHeaders(); - } - - 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(payloadEndIndex - payload.readerIndex(), padding); - - ByteBuf data = payload.readSlice(dataLength); - listener.onDataRead(ctx, streamId, data, padding, flags.endOfStream()); - } - - private void readHeadersFrame(final ChannelHandlerContext ctx, ByteBuf payload, int payloadEndIndex, - Http2FrameListener listener) throws Http2Exception { - final int headersStreamId = streamId; - final Http2Flags headersFlags = flags; - final int padding = readPadding(payload); - verifyPadding(padding); - - // The callback that is invoked is different depending on whether priority information - // is present in the headers frame. - if (flags.priorityPresent()) { - long word1 = payload.readUnsignedInt(); - final boolean exclusive = (word1 & 0x80000000L) != 0; - final int streamDependency = (int) (word1 & 0x7FFFFFFFL); - if (streamDependency == streamId) { - throw streamError(streamId, PROTOCOL_ERROR, "A stream cannot depend on itself."); - } - final short weight = (short) (payload.readUnsignedByte() + 1); - final int lenToRead = lengthWithoutTrailingPadding(payloadEndIndex - payload.readerIndex(), padding); - - // Create a handler that invokes the listener when the header block is complete. - headersContinuation = new HeadersContinuation() { - @Override - public int getStreamId() { - return headersStreamId; - } - - @Override - public void processFragment(boolean endOfHeaders, ByteBuf fragment, int len, - Http2FrameListener listener) throws Http2Exception { - final HeadersBlockBuilder hdrBlockBuilder = headersBlockBuilder(); - hdrBlockBuilder.addFragment(fragment, len, ctx.alloc(), endOfHeaders); - if (endOfHeaders) { - listener.onHeadersRead(ctx, headersStreamId, hdrBlockBuilder.headers(), streamDependency, - weight, exclusive, padding, headersFlags.endOfStream()); - } - } - }; - - // Process the initial fragment, invoking the listener's callback if end of headers. - headersContinuation.processFragment(flags.endOfHeaders(), payload, lenToRead, listener); - resetHeadersContinuationIfEnd(flags.endOfHeaders()); - return; - } - - // The priority fields are not present in the frame. Prepare a continuation that invokes - // the listener callback without priority information. - headersContinuation = new HeadersContinuation() { - @Override - public int getStreamId() { - return headersStreamId; - } - - @Override - public void processFragment(boolean endOfHeaders, ByteBuf fragment, int len, - Http2FrameListener listener) throws Http2Exception { - final HeadersBlockBuilder hdrBlockBuilder = headersBlockBuilder(); - hdrBlockBuilder.addFragment(fragment, len, ctx.alloc(), endOfHeaders); - if (endOfHeaders) { - listener.onHeadersRead(ctx, headersStreamId, hdrBlockBuilder.headers(), padding, - headersFlags.endOfStream()); - } - } - }; - - // Process the initial fragment, invoking the listener's callback if end of headers. - int len = lengthWithoutTrailingPadding(payloadEndIndex - payload.readerIndex(), padding); - headersContinuation.processFragment(flags.endOfHeaders(), payload, len, listener); - resetHeadersContinuationIfEnd(flags.endOfHeaders()); - } - - private void resetHeadersContinuationIfEnd(boolean endOfHeaders) { - if (endOfHeaders) { - closeHeadersContinuation(); - } - } - - private void readPriorityFrame(ChannelHandlerContext ctx, ByteBuf payload, - Http2FrameListener listener) throws Http2Exception { - long word1 = payload.readUnsignedInt(); - boolean exclusive = (word1 & 0x80000000L) != 0; - int streamDependency = (int) (word1 & 0x7FFFFFFFL); - if (streamDependency == streamId) { - throw streamError(streamId, PROTOCOL_ERROR, "A stream cannot depend on itself."); - } - short weight = (short) (payload.readUnsignedByte() + 1); - listener.onPriorityRead(ctx, streamId, streamDependency, weight, exclusive); - } - - private void readRstStreamFrame(ChannelHandlerContext ctx, ByteBuf payload, - Http2FrameListener listener) throws Http2Exception { - long errorCode = payload.readUnsignedInt(); - listener.onRstStreamRead(ctx, streamId, errorCode); - } - - private void readSettingsFrame(ChannelHandlerContext ctx, ByteBuf payload, - Http2FrameListener listener) throws Http2Exception { - if (flags.ack()) { - listener.onSettingsAckRead(ctx); - } else { - int numSettings = payloadLength / SETTING_ENTRY_LENGTH; - Http2Settings settings = new Http2Settings(); - for (int index = 0; index < numSettings; ++index) { - char id = (char) payload.readUnsignedShort(); - long value = payload.readUnsignedInt(); - try { - settings.put(id, Long.valueOf(value)); - } catch (IllegalArgumentException e) { - if (id == SETTINGS_INITIAL_WINDOW_SIZE) { - throw connectionError(FLOW_CONTROL_ERROR, e, e.getMessage()); - } - throw connectionError(PROTOCOL_ERROR, e, e.getMessage()); - } - } - listener.onSettingsRead(ctx, settings); - } - } - - private void readPushPromiseFrame(final ChannelHandlerContext ctx, ByteBuf payload, int payloadEndIndex, - Http2FrameListener listener) throws Http2Exception { - final int pushPromiseStreamId = streamId; - final int padding = readPadding(payload); - verifyPadding(padding); - final int promisedStreamId = readUnsignedInt(payload); - - // Create a handler that invokes the listener when the header block is complete. - headersContinuation = new HeadersContinuation() { - @Override - public int getStreamId() { - return pushPromiseStreamId; - } - - @Override - public void processFragment(boolean endOfHeaders, ByteBuf fragment, int len, - Http2FrameListener listener) throws Http2Exception { - headersBlockBuilder().addFragment(fragment, len, ctx.alloc(), endOfHeaders); - if (endOfHeaders) { - listener.onPushPromiseRead(ctx, pushPromiseStreamId, promisedStreamId, - headersBlockBuilder().headers(), padding); - } - } - }; - - // Process the initial fragment, invoking the listener's callback if end of headers. - int len = lengthWithoutTrailingPadding(payloadEndIndex - payload.readerIndex(), padding); - headersContinuation.processFragment(flags.endOfHeaders(), payload, len, listener); - resetHeadersContinuationIfEnd(flags.endOfHeaders()); - } - - private void readPingFrame(ChannelHandlerContext ctx, long data, - Http2FrameListener listener) throws Http2Exception { - if (flags.ack()) { - listener.onPingAckRead(ctx, data); - } else { - listener.onPingRead(ctx, data); - } - } - - 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(payloadEndIndex - payload.readerIndex()); - listener.onGoAwayRead(ctx, lastStreamId, errorCode, debugData); - } - - private void readWindowUpdateFrame(ChannelHandlerContext ctx, ByteBuf payload, - Http2FrameListener listener) throws Http2Exception { - int windowSizeIncrement = readUnsignedInt(payload); - if (windowSizeIncrement == 0) { - throw streamError(streamId, PROTOCOL_ERROR, - "Received WINDOW_UPDATE with delta 0 for stream: %d", streamId); - } - listener.onWindowUpdateRead(ctx, streamId, windowSizeIncrement); - } - - private void readContinuationFrame(ByteBuf payload, int payloadEndIndex, Http2FrameListener listener) - throws Http2Exception { - // Process the initial fragment, invoking the listener's callback if end of headers. - headersContinuation.processFragment(flags.endOfHeaders(), payload, - payloadEndIndex - payload.readerIndex(), listener); - resetHeadersContinuationIfEnd(flags.endOfHeaders()); - } - - 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); - } - - /** - * If padding is present in the payload, reads the next byte as padding. The padding also includes the one byte - * width of the pad length field. Otherwise, returns zero. - */ - private int readPadding(ByteBuf payload) { - if (!flags.paddingPresent()) { - return 0; - } - return payload.readUnsignedByte() + 1; - } - - private void verifyPadding(int padding) throws Http2Exception { - int len = lengthWithoutTrailingPadding(payloadLength, padding); - if (len < 0) { - throw connectionError(PROTOCOL_ERROR, "Frame payload too small for padding."); - } - } - - /** - * The padding parameter consists of the 1 byte pad length field and the trailing padding bytes. This method - * returns the number of readable bytes without the trailing padding. - */ - private static int lengthWithoutTrailingPadding(int readableBytes, int padding) { - return padding == 0 - ? readableBytes - : readableBytes - (padding - 1); - } - - /** - * Base class for processing of HEADERS and PUSH_PROMISE header blocks that potentially span - * multiple frames. The implementation of this interface will perform the final callback to the - * {@link Http2FrameListener} once the end of headers is reached. - */ - private abstract class HeadersContinuation { - private final HeadersBlockBuilder builder = new HeadersBlockBuilder(); - - /** - * Returns the stream for which headers are currently being processed. - */ - abstract int getStreamId(); - - /** - * Processes the next fragment for the current header block. - * - * @param endOfHeaders whether the fragment is the last in the header block. - * @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, int len, - Http2FrameListener listener) throws Http2Exception; - - final HeadersBlockBuilder headersBlockBuilder() { - return builder; - } - - /** - * Free any allocated resources. - */ - final void close() { - builder.close(); - } - } - - /** - * Utility class to help with construction of the headers block that may potentially span - * multiple frames. - */ - protected class HeadersBlockBuilder { - private ByteBuf headerBlock; - - /** - * The local header size maximum has been exceeded while accumulating bytes. - * @throws Http2Exception A connection error indicating too much data has been received. - */ - private void headerSizeExceeded() throws Http2Exception { - close(); - headerListSizeExceeded(headersDecoder.configuration().maxHeaderListSizeGoAway()); - } - - /** - * Adds a fragment to the block. - * - * @param fragment the fragment of the headers block to be added. - * @param alloc allocator for new blocks if needed. - * @param endOfHeaders flag indicating whether the current frame is the end of the headers. - * 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, int len, ByteBufAllocator alloc, - boolean endOfHeaders) throws Http2Exception { - if (headerBlock == null) { - 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.readRetainedSlice(len); - } else { - headerBlock = alloc.buffer(len).writeBytes(fragment, len); - } - return; - } - if (headersDecoder.configuration().maxHeaderListSizeGoAway() - len < - headerBlock.readableBytes()) { - headerSizeExceeded(); - } - if (headerBlock.isWritable(len)) { - // The buffer can hold the requested bytes, just write it directly. - 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() + len); - buf.writeBytes(headerBlock).writeBytes(fragment, len); - headerBlock.release(); - headerBlock = buf; - } - } - - /** - * Builds the headers from the completed headers block. After this is called, this builder - * should not be called again. - */ - Http2Headers headers() throws Http2Exception { - try { - return headersDecoder.decodeHeaders(streamId, headerBlock); - } finally { - close(); - } - } - - /** - * Closes this builder and frees any resources. - */ - void close() { - if (headerBlock != null) { - headerBlock.release(); - headerBlock = null; - } - - // Clear the member variable pointing at this instance. - headersContinuation = null; - } - } - - /** - * Verify that current state is not processing on header block - * @throws Http2Exception thrown if {@link #headersContinuation} is not null - */ - private void verifyNotProcessingHeaders() throws Http2Exception { - if (headersContinuation != null) { - throw connectionError(PROTOCOL_ERROR, "Received frame of type %s while processing headers on stream %d.", - frameType, headersContinuation.getStreamId()); - } - } - - private void verifyAssociatedWithAStream() throws Http2Exception { - if (streamId == 0) { - throw connectionError(PROTOCOL_ERROR, "Frame of type %s must be associated with a stream.", frameType); - } - } - - private static void verifyStreamOrConnectionId(int streamId, String argumentName) - throws Http2Exception { - if (streamId < 0) { - throw connectionError(PROTOCOL_ERROR, "%s must be >= 0", argumentName); - } - } -} 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 deleted file mode 100644 index 6fd8a158cf..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2FrameWriter.java +++ /dev/null @@ -1,627 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http2.Http2CodecUtil.SimpleChannelPromiseAggregator; -import io.netty.handler.codec.http2.Http2FrameWriter.Configuration; -import io.netty.handler.codec.http2.Http2HeadersEncoder.SensitivityDetector; -import io.netty.util.concurrent.Future; -import io.netty.util.internal.UnstableApi; - -import static io.netty.buffer.Unpooled.directBuffer; -import static io.netty.buffer.Unpooled.unreleasableBuffer; -import static io.netty.handler.codec.http2.Http2CodecUtil.CONTINUATION_FRAME_HEADER_LENGTH; -import static io.netty.handler.codec.http2.Http2CodecUtil.DATA_FRAME_HEADER_LENGTH; -import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_MAX_FRAME_SIZE; -import static io.netty.handler.codec.http2.Http2CodecUtil.FRAME_HEADER_LENGTH; -import static io.netty.handler.codec.http2.Http2CodecUtil.GO_AWAY_FRAME_HEADER_LENGTH; -import static io.netty.handler.codec.http2.Http2CodecUtil.HEADERS_FRAME_HEADER_LENGTH; -import static io.netty.handler.codec.http2.Http2CodecUtil.INT_FIELD_LENGTH; -import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_UNSIGNED_BYTE; -import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_UNSIGNED_INT; -import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_WEIGHT; -import static io.netty.handler.codec.http2.Http2CodecUtil.MIN_WEIGHT; -import static io.netty.handler.codec.http2.Http2CodecUtil.PING_FRAME_PAYLOAD_LENGTH; -import static io.netty.handler.codec.http2.Http2CodecUtil.PRIORITY_ENTRY_LENGTH; -import static io.netty.handler.codec.http2.Http2CodecUtil.PRIORITY_FRAME_LENGTH; -import static io.netty.handler.codec.http2.Http2CodecUtil.PUSH_PROMISE_FRAME_HEADER_LENGTH; -import static io.netty.handler.codec.http2.Http2CodecUtil.RST_STREAM_FRAME_LENGTH; -import static io.netty.handler.codec.http2.Http2CodecUtil.SETTING_ENTRY_LENGTH; -import static io.netty.handler.codec.http2.Http2CodecUtil.WINDOW_UPDATE_FRAME_LENGTH; -import static io.netty.handler.codec.http2.Http2CodecUtil.isMaxFrameSizeValid; -import static io.netty.handler.codec.http2.Http2CodecUtil.verifyPadding; -import static io.netty.handler.codec.http2.Http2CodecUtil.writeFrameHeaderInternal; -import static io.netty.handler.codec.http2.Http2Error.FRAME_SIZE_ERROR; -import static io.netty.handler.codec.http2.Http2Exception.connectionError; -import static io.netty.handler.codec.http2.Http2FrameTypes.CONTINUATION; -import static io.netty.handler.codec.http2.Http2FrameTypes.DATA; -import static io.netty.handler.codec.http2.Http2FrameTypes.GO_AWAY; -import static io.netty.handler.codec.http2.Http2FrameTypes.HEADERS; -import static io.netty.handler.codec.http2.Http2FrameTypes.PING; -import static io.netty.handler.codec.http2.Http2FrameTypes.PRIORITY; -import static io.netty.handler.codec.http2.Http2FrameTypes.PUSH_PROMISE; -import static io.netty.handler.codec.http2.Http2FrameTypes.RST_STREAM; -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.checkPositive; -import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; -import static java.lang.Math.max; -import static java.lang.Math.min; -import static java.util.Objects.requireNonNull; - -/** - * A {@link Http2FrameWriter} that supports all frame types defined by the HTTP/2 specification. - */ -@UnstableApi -public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSizePolicy, Configuration { - private static final String STREAM_ID = "Stream ID"; - private static final String STREAM_DEPENDENCY = "Stream Dependency"; - /** - * This buffer is allocated to the maximum size of the padding field, and filled with zeros. - * When padding is needed it can be taken as a slice of this buffer. Users should call {@link ByteBuf#retain()} - * before using their slice. - */ - private static final ByteBuf ZERO_BUFFER = - unreleasableBuffer(directBuffer(MAX_UNSIGNED_BYTE).writeZero(MAX_UNSIGNED_BYTE)).asReadOnly(); - - private final Http2HeadersEncoder headersEncoder; - private int maxFrameSize; - - public DefaultHttp2FrameWriter() { - this(new DefaultHttp2HeadersEncoder()); - } - - public DefaultHttp2FrameWriter(SensitivityDetector headersSensitivityDetector) { - this(new DefaultHttp2HeadersEncoder(headersSensitivityDetector)); - } - - public DefaultHttp2FrameWriter(SensitivityDetector headersSensitivityDetector, boolean ignoreMaxHeaderListSize) { - this(new DefaultHttp2HeadersEncoder(headersSensitivityDetector, ignoreMaxHeaderListSize)); - } - - public DefaultHttp2FrameWriter(Http2HeadersEncoder headersEncoder) { - this.headersEncoder = headersEncoder; - maxFrameSize = DEFAULT_MAX_FRAME_SIZE; - } - - @Override - public Configuration configuration() { - return this; - } - - @Override - public Http2HeadersEncoder.Configuration headersConfiguration() { - return headersEncoder.configuration(); - } - - @Override - public Http2FrameSizePolicy frameSizePolicy() { - return this; - } - - @Override - public void maxFrameSize(int max) throws Http2Exception { - if (!isMaxFrameSizeValid(max)) { - throw connectionError(FRAME_SIZE_ERROR, "Invalid MAX_FRAME_SIZE specified in sent settings: %d", max); - } - maxFrameSize = max; - } - - @Override - public int maxFrameSize() { - return maxFrameSize; - } - - @Override - public void close() { } - - @Override - public Future writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data, - int padding, boolean endStream) { - final SimpleChannelPromiseAggregator promiseAggregator = - new SimpleChannelPromiseAggregator(ctx.newPromise(), ctx.executor()); - ByteBuf frameHeader = null; - try { - verifyStreamId(streamId, STREAM_ID); - verifyPadding(padding); - - int remainingData = data.readableBytes(); - Http2Flags flags = new Http2Flags(); - flags.endOfStream(false); - flags.paddingPresent(false); - // Fast path to write frames of payload size maxFrameSize first. - if (remainingData > maxFrameSize) { - frameHeader = ctx.alloc().buffer(FRAME_HEADER_LENGTH); - writeFrameHeaderInternal(frameHeader, maxFrameSize, DATA, flags, streamId); - do { - // Write the header. - ctx.write(frameHeader.retainedSlice()).cascadeTo(promiseAggregator.newPromise()); - - // Write the payload. - ctx.write(data.readRetainedSlice(maxFrameSize)).cascadeTo(promiseAggregator.newPromise()); - - remainingData -= maxFrameSize; - // Stop iterating if remainingData == maxFrameSize so we can take care of reference counts below. - } while (remainingData > maxFrameSize); - } - - if (padding == 0) { - // Write the header. - if (frameHeader != null) { - frameHeader.release(); - frameHeader = null; - } - ByteBuf frameHeader2 = ctx.alloc().buffer(FRAME_HEADER_LENGTH); - flags.endOfStream(endStream); - writeFrameHeaderInternal(frameHeader2, remainingData, DATA, flags, streamId); - ctx.write(frameHeader2).cascadeTo(promiseAggregator.newPromise()); - - // Write the payload. - ByteBuf lastFrame = data.readSlice(remainingData); - data = null; - ctx.write(lastFrame).cascadeTo(promiseAggregator.newPromise()); - } else { - if (remainingData != maxFrameSize) { - if (frameHeader != null) { - frameHeader.release(); - frameHeader = null; - } - } else { - remainingData -= maxFrameSize; - // Write the header. - ByteBuf lastFrame; - if (frameHeader == null) { - lastFrame = ctx.alloc().buffer(FRAME_HEADER_LENGTH); - writeFrameHeaderInternal(lastFrame, maxFrameSize, DATA, flags, streamId); - } else { - lastFrame = frameHeader.slice(); - frameHeader = null; - } - ctx.write(lastFrame).cascadeTo(promiseAggregator.newPromise()); - - // Write the payload. - lastFrame = data.readableBytes() != maxFrameSize ? data.readSlice(maxFrameSize) : data; - data = null; - ctx.write(lastFrame).cascadeTo(promiseAggregator.newPromise()); - } - - do { - int frameDataBytes = min(remainingData, maxFrameSize); - int framePaddingBytes = min(padding, max(0, maxFrameSize - 1 - frameDataBytes)); - - // Decrement the remaining counters. - padding -= framePaddingBytes; - remainingData -= frameDataBytes; - - // Write the header. - ByteBuf frameHeader2 = ctx.alloc().buffer(DATA_FRAME_HEADER_LENGTH); - flags.endOfStream(endStream && remainingData == 0 && padding == 0); - flags.paddingPresent(framePaddingBytes > 0); - writeFrameHeaderInternal(frameHeader2, framePaddingBytes + frameDataBytes, DATA, flags, streamId); - writePaddingLength(frameHeader2, framePaddingBytes); - ctx.write(frameHeader2).cascadeTo(promiseAggregator.newPromise()); - - // Write the payload. - if (data != null) { // Make sure Data is not null - if (remainingData == 0) { - ByteBuf lastFrame = data.readSlice(frameDataBytes); - data = null; - ctx.write(lastFrame).cascadeTo(promiseAggregator.newPromise()); - } else { - ctx.write(data.readRetainedSlice(frameDataBytes)) - .cascadeTo(promiseAggregator.newPromise()); - } - } - // Write the frame padding. - if (paddingBytes(framePaddingBytes) > 0) { - ctx.write(ZERO_BUFFER.slice(0, paddingBytes(framePaddingBytes))) - .cascadeTo(promiseAggregator.newPromise()); - } - } while (remainingData != 0 || padding != 0); - } - } catch (Throwable cause) { - if (frameHeader != null) { - frameHeader.release(); - } - // Use a try/finally here in case the data has been released before calling this method. This is not - // necessary above because we internally allocate frameHeader. - try { - if (data != null && - // Check if the data was released already. - data.refCnt() > 0) { - data.release(); - } - } finally { - promiseAggregator.setFailure(cause); - promiseAggregator.doneAllocatingPromises(); - } - return promiseAggregator; - } - return promiseAggregator.doneAllocatingPromises(); - } - - @Override - public Future writeHeaders(ChannelHandlerContext ctx, int streamId, - Http2Headers headers, int padding, boolean endStream) { - return writeHeadersInternal(ctx, streamId, headers, padding, endStream, - false, 0, (short) 0, false); - } - - @Override - public Future writeHeaders(ChannelHandlerContext ctx, int streamId, - Http2Headers headers, int streamDependency, short weight, boolean exclusive, - int padding, boolean endStream) { - return writeHeadersInternal(ctx, streamId, headers, padding, endStream, - true, streamDependency, weight, exclusive); - } - - @Override - public Future writePriority(ChannelHandlerContext ctx, int streamId, - int streamDependency, short weight, boolean exclusive) { - try { - verifyStreamId(streamId, STREAM_ID); - verifyStreamOrConnectionId(streamDependency, STREAM_DEPENDENCY); - verifyWeight(weight); - - ByteBuf buf = ctx.alloc().buffer(PRIORITY_FRAME_LENGTH); - writeFrameHeaderInternal(buf, PRIORITY_ENTRY_LENGTH, PRIORITY, new Http2Flags(), streamId); - buf.writeInt(exclusive ? (int) (0x80000000L | streamDependency) : streamDependency); - // Adjust the weight so that it fits into a single byte on the wire. - buf.writeByte(weight - 1); - return ctx.write(buf); - } catch (Throwable t) { - return ctx.newFailedFuture(t); - } - } - - @Override - public Future writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode) { - try { - verifyStreamId(streamId, STREAM_ID); - verifyErrorCode(errorCode); - - ByteBuf buf = ctx.alloc().buffer(RST_STREAM_FRAME_LENGTH); - writeFrameHeaderInternal(buf, INT_FIELD_LENGTH, RST_STREAM, new Http2Flags(), streamId); - buf.writeInt((int) errorCode); - return ctx.write(buf); - } catch (Throwable t) { - return ctx.newFailedFuture(t); - } - } - - @Override - public Future writeSettings(ChannelHandlerContext ctx, Http2Settings settings) { - try { - requireNonNull(settings, "settings"); - int payloadLength = SETTING_ENTRY_LENGTH * settings.size(); - ByteBuf buf = ctx.alloc().buffer(FRAME_HEADER_LENGTH + payloadLength); - writeFrameHeaderInternal(buf, payloadLength, SETTINGS, new Http2Flags(), 0); - for (Http2Settings.PrimitiveEntry entry : settings.entries()) { - buf.writeChar(entry.key()); - buf.writeInt(entry.value().intValue()); - } - return ctx.write(buf); - } catch (Throwable t) { - return ctx.newFailedFuture(t); - } - } - - @Override - public Future writeSettingsAck(ChannelHandlerContext ctx) { - try { - ByteBuf buf = ctx.alloc().buffer(FRAME_HEADER_LENGTH); - writeFrameHeaderInternal(buf, 0, SETTINGS, new Http2Flags().ack(true), 0); - return ctx.write(buf); - } catch (Throwable t) { - return ctx.newFailedFuture(t); - } - } - - @Override - public Future writePing(ChannelHandlerContext ctx, boolean ack, long data) { - Http2Flags flags = ack ? new Http2Flags().ack(true) : new Http2Flags(); - ByteBuf buf = ctx.alloc().buffer(FRAME_HEADER_LENGTH + PING_FRAME_PAYLOAD_LENGTH); - // Assume nothing below will throw until buf is written. That way we don't have to take care of ownership - // in the catch block. - writeFrameHeaderInternal(buf, PING_FRAME_PAYLOAD_LENGTH, PING, flags, 0); - buf.writeLong(data); - return ctx.write(buf); - } - - @Override - public Future writePushPromise(ChannelHandlerContext ctx, int streamId, int promisedStreamId, - Http2Headers headers, int padding) { - ByteBuf headerBlock = null; - SimpleChannelPromiseAggregator promiseAggregator = - new SimpleChannelPromiseAggregator(ctx.newPromise(), ctx.executor()); - try { - verifyStreamId(streamId, STREAM_ID); - verifyStreamId(promisedStreamId, "Promised Stream ID"); - verifyPadding(padding); - - // Encode the entire header block into an intermediate buffer. - headerBlock = ctx.alloc().buffer(); - headersEncoder.encodeHeaders(streamId, headers, headerBlock); - - // Read the first fragment (possibly everything). - Http2Flags flags = new Http2Flags().paddingPresent(padding > 0); - // INT_FIELD_LENGTH is for the length of the promisedStreamId - int nonFragmentLength = INT_FIELD_LENGTH + padding; - int maxFragmentLength = maxFrameSize - nonFragmentLength; - ByteBuf fragment = headerBlock.readRetainedSlice(min(headerBlock.readableBytes(), maxFragmentLength)); - - flags.endOfHeaders(!headerBlock.isReadable()); - - int payloadLength = fragment.readableBytes() + nonFragmentLength; - ByteBuf buf = ctx.alloc().buffer(PUSH_PROMISE_FRAME_HEADER_LENGTH); - writeFrameHeaderInternal(buf, payloadLength, PUSH_PROMISE, flags, streamId); - writePaddingLength(buf, padding); - - // Write out the promised stream ID. - buf.writeInt(promisedStreamId); - ctx.write(buf).cascadeTo(promiseAggregator.newPromise()); - - // Write the first fragment. - ctx.write(fragment).cascadeTo(promiseAggregator.newPromise()); - - // Write out the padding, if any. - if (paddingBytes(padding) > 0) { - ctx.write(ZERO_BUFFER.slice(0, paddingBytes(padding))).cascadeTo(promiseAggregator.newPromise()); - } - - if (!flags.endOfHeaders()) { - writeContinuationFrames(ctx, streamId, headerBlock, promiseAggregator); - } - } catch (Http2Exception e) { - promiseAggregator.setFailure(e); - } catch (Throwable t) { - promiseAggregator.setFailure(t); - promiseAggregator.doneAllocatingPromises(); - throw t; - } finally { - if (headerBlock != null) { - headerBlock.release(); - } - } - return promiseAggregator.doneAllocatingPromises(); - } - - @Override - public Future writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode, - ByteBuf debugData) { - SimpleChannelPromiseAggregator promiseAggregator = - new SimpleChannelPromiseAggregator(ctx.newPromise(), ctx.executor()); - try { - verifyStreamOrConnectionId(lastStreamId, "Last Stream ID"); - verifyErrorCode(errorCode); - - int payloadLength = 8 + debugData.readableBytes(); - ByteBuf buf = ctx.alloc().buffer(GO_AWAY_FRAME_HEADER_LENGTH); - // Assume nothing below will throw until buf is written. That way we don't have to take care of ownership - // in the catch block. - writeFrameHeaderInternal(buf, payloadLength, GO_AWAY, new Http2Flags(), 0); - buf.writeInt(lastStreamId); - buf.writeInt((int) errorCode); - ctx.write(buf).cascadeTo(promiseAggregator.newPromise()); - } catch (Throwable t) { - try { - debugData.release(); - } finally { - promiseAggregator.setFailure(t); - promiseAggregator.doneAllocatingPromises(); - } - return promiseAggregator; - } - - try { - ctx.write(debugData).cascadeTo(promiseAggregator.newPromise()); - } catch (Throwable t) { - promiseAggregator.setFailure(t); - } - return promiseAggregator.doneAllocatingPromises(); - } - - @Override - public Future writeWindowUpdate(ChannelHandlerContext ctx, int streamId, - int windowSizeIncrement) { - try { - verifyStreamOrConnectionId(streamId, STREAM_ID); - verifyWindowSizeIncrement(windowSizeIncrement); - - ByteBuf buf = ctx.alloc().buffer(WINDOW_UPDATE_FRAME_LENGTH); - writeFrameHeaderInternal(buf, INT_FIELD_LENGTH, WINDOW_UPDATE, new Http2Flags(), streamId); - buf.writeInt(windowSizeIncrement); - return ctx.write(buf); - } catch (Throwable t) { - return ctx.newFailedFuture(t); - } - } - - @Override - public Future writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId, - Http2Flags flags, ByteBuf payload) { - SimpleChannelPromiseAggregator promiseAggregator = - new SimpleChannelPromiseAggregator(ctx.newPromise(), ctx.executor()); - try { - verifyStreamOrConnectionId(streamId, STREAM_ID); - ByteBuf buf = ctx.alloc().buffer(FRAME_HEADER_LENGTH); - // Assume nothing below will throw until buf is written. That way we don't have to take care of ownership - // in the catch block. - writeFrameHeaderInternal(buf, payload.readableBytes(), frameType, flags, streamId); - ctx.write(buf).cascadeTo(promiseAggregator.newPromise()); - } catch (Throwable t) { - try { - payload.release(); - } finally { - promiseAggregator.setFailure(t); - promiseAggregator.doneAllocatingPromises(); - } - return promiseAggregator; - } - try { - ctx.write(payload).cascadeTo(promiseAggregator.newPromise()); - } catch (Throwable t) { - promiseAggregator.setFailure(t); - } - return promiseAggregator.doneAllocatingPromises(); - } - - private Future writeHeadersInternal(ChannelHandlerContext ctx, - int streamId, Http2Headers headers, int padding, boolean endStream, - boolean hasPriority, int streamDependency, short weight, boolean exclusive) { - ByteBuf headerBlock = null; - SimpleChannelPromiseAggregator promiseAggregator = - new SimpleChannelPromiseAggregator(ctx.newPromise(), ctx.executor()); - try { - verifyStreamId(streamId, STREAM_ID); - if (hasPriority) { - verifyStreamOrConnectionId(streamDependency, STREAM_DEPENDENCY); - verifyPadding(padding); - verifyWeight(weight); - } - - // Encode the entire header block. - headerBlock = ctx.alloc().buffer(); - headersEncoder.encodeHeaders(streamId, headers, headerBlock); - - Http2Flags flags = - new Http2Flags().endOfStream(endStream).priorityPresent(hasPriority).paddingPresent(padding > 0); - - // Read the first fragment (possibly everything). - int nonFragmentBytes = padding + flags.getNumPriorityBytes(); - int maxFragmentLength = maxFrameSize - nonFragmentBytes; - ByteBuf fragment = headerBlock.readRetainedSlice(min(headerBlock.readableBytes(), maxFragmentLength)); - - // Set the end of headers flag for the first frame. - flags.endOfHeaders(!headerBlock.isReadable()); - - int payloadLength = fragment.readableBytes() + nonFragmentBytes; - ByteBuf buf = ctx.alloc().buffer(HEADERS_FRAME_HEADER_LENGTH); - writeFrameHeaderInternal(buf, payloadLength, HEADERS, flags, streamId); - writePaddingLength(buf, padding); - - if (hasPriority) { - buf.writeInt(exclusive ? (int) (0x80000000L | streamDependency) : streamDependency); - - // Adjust the weight so that it fits into a single byte on the wire. - buf.writeByte(weight - 1); - } - ctx.write(buf).cascadeTo(promiseAggregator.newPromise()); - - // Write the first fragment. - ctx.write(fragment).cascadeTo(promiseAggregator.newPromise()); - - // Write out the padding, if any. - if (paddingBytes(padding) > 0) { - ctx.write(ZERO_BUFFER.slice(0, paddingBytes(padding))) - .cascadeTo(promiseAggregator.newPromise()); - } - - if (!flags.endOfHeaders()) { - writeContinuationFrames(ctx, streamId, headerBlock, promiseAggregator); - } - } catch (Http2Exception e) { - promiseAggregator.setFailure(e); - } catch (Throwable t) { - promiseAggregator.setFailure(t); - promiseAggregator.doneAllocatingPromises(); - throw t; - } finally { - if (headerBlock != null) { - headerBlock.release(); - } - } - return promiseAggregator.doneAllocatingPromises(); - } - - /** - * Writes as many continuation frames as needed until {@code padding} and {@code headerBlock} are consumed. - */ - private Future writeContinuationFrames(ChannelHandlerContext ctx, int streamId, - ByteBuf headerBlock, SimpleChannelPromiseAggregator promiseAggregator) { - Http2Flags flags = new Http2Flags(); - - if (headerBlock.isReadable()) { - // The frame header (and padding) only changes on the last frame, so allocate it once and re-use - int fragmentReadableBytes = min(headerBlock.readableBytes(), maxFrameSize); - ByteBuf buf = ctx.alloc().buffer(CONTINUATION_FRAME_HEADER_LENGTH); - writeFrameHeaderInternal(buf, fragmentReadableBytes, CONTINUATION, flags, streamId); - - do { - fragmentReadableBytes = min(headerBlock.readableBytes(), maxFrameSize); - ByteBuf fragment = headerBlock.readRetainedSlice(fragmentReadableBytes); - - if (headerBlock.isReadable()) { - ctx.write(buf.retain()).cascadeTo(promiseAggregator.newPromise()); - } else { - // The frame header is different for the last frame, so re-allocate and release the old buffer - flags = flags.endOfHeaders(true); - buf.release(); - buf = ctx.alloc().buffer(CONTINUATION_FRAME_HEADER_LENGTH); - writeFrameHeaderInternal(buf, fragmentReadableBytes, CONTINUATION, flags, streamId); - ctx.write(buf).cascadeTo(promiseAggregator.newPromise()); - } - - ctx.write(fragment).cascadeTo(promiseAggregator.newPromise()); - - } while (headerBlock.isReadable()); - } - return promiseAggregator; - } - - /** - * Returns the number of padding bytes that should be appended to the end of a frame. - */ - private static int paddingBytes(int padding) { - // The padding parameter contains the 1 byte pad length field as well as the trailing padding bytes. - // Subtract 1, so to only get the number of padding bytes that need to be appended to the end of a frame. - return padding - 1; - } - - private static void writePaddingLength(ByteBuf buf, int padding) { - if (padding > 0) { - // It is assumed that the padding length has been bounds checked before this - // Minus 1, as the pad length field is included in the padding parameter and is 1 byte wide. - buf.writeByte(padding - 1); - } - } - - private static void verifyStreamId(int streamId, String argumentName) { - checkPositive(streamId, argumentName); - } - - private static void verifyStreamOrConnectionId(int streamId, String argumentName) { - checkPositiveOrZero(streamId, argumentName); - } - - private static void verifyWeight(short weight) { - if (weight < MIN_WEIGHT || weight > MAX_WEIGHT) { - throw new IllegalArgumentException("Invalid weight: " + weight); - } - } - - private static void verifyErrorCode(long errorCode) { - if (errorCode < 0 || errorCode > MAX_UNSIGNED_INT) { - throw new IllegalArgumentException("Invalid errorCode: " + errorCode); - } - } - - private static void verifyWindowSizeIncrement(int windowSizeIncrement) { - checkPositiveOrZero(windowSizeIncrement, "windowSizeIncrement"); - } -} 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 deleted file mode 100644 index 8940d06fb6..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2GoAwayFrame.java +++ /dev/null @@ -1,179 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.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; -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.UnstableApi; - -/** - * The default {@link Http2GoAwayFrame} implementation. - */ -@UnstableApi -public final class DefaultHttp2GoAwayFrame extends DefaultByteBufHolder implements Http2GoAwayFrame { - - private final long errorCode; - private final int lastStreamId; - private int extraStreamIds; - - /** - * Equivalent to {@code new DefaultHttp2GoAwayFrame(error.code())}. - * - * @param error non-{@code null} reason for the go away - */ - public DefaultHttp2GoAwayFrame(Http2Error error) { - this(error.code()); - } - - /** - * Equivalent to {@code new DefaultHttp2GoAwayFrame(content, Unpooled.EMPTY_BUFFER)}. - * - * @param errorCode reason for the go away - */ - public DefaultHttp2GoAwayFrame(long errorCode) { - this(errorCode, Unpooled.EMPTY_BUFFER); - } - - /** - * - * - * @param error non-{@code null} reason for the go away - * @param content non-{@code null} debug data - */ - public DefaultHttp2GoAwayFrame(Http2Error error, ByteBuf content) { - this(error.code(), content); - } - - /** - * Construct a new GOAWAY message. - * - * @param errorCode reason for the go away - * @param content non-{@code null} debug data - */ - public DefaultHttp2GoAwayFrame(long errorCode, ByteBuf content) { - this(-1, errorCode, content); - } - - /** - * Construct a new GOAWAY message. - * - * This constructor is for internal use only. A user should not have to specify a specific last stream identifier, - * but use {@link #setExtraStreamIds(int)} instead. - */ - DefaultHttp2GoAwayFrame(int lastStreamId, long errorCode, ByteBuf content) { - super(content); - this.errorCode = errorCode; - this.lastStreamId = lastStreamId; - } - - @Override - public String name() { - return "GOAWAY"; - } - - @Override - public long errorCode() { - return errorCode; - } - - @Override - public int extraStreamIds() { - return extraStreamIds; - } - - @Override - public Http2GoAwayFrame setExtraStreamIds(int extraStreamIds) { - checkPositiveOrZero(extraStreamIds, "extraStreamIds"); - this.extraStreamIds = extraStreamIds; - return this; - } - - @Override - public int lastStreamId() { - return lastStreamId; - } - - @Override - public Http2GoAwayFrame copy() { - return new DefaultHttp2GoAwayFrame(lastStreamId, errorCode, content().copy()); - } - - @Override - public Http2GoAwayFrame duplicate() { - return (Http2GoAwayFrame) super.duplicate(); - } - - @Override - public Http2GoAwayFrame retainedDuplicate() { - return (Http2GoAwayFrame) super.retainedDuplicate(); - } - - @Override - public Http2GoAwayFrame replace(ByteBuf content) { - return new DefaultHttp2GoAwayFrame(errorCode, content).setExtraStreamIds(extraStreamIds); - } - - @Override - public Http2GoAwayFrame retain() { - super.retain(); - return this; - } - - @Override - public Http2GoAwayFrame retain(int increment) { - super.retain(increment); - return this; - } - - @Override - public Http2GoAwayFrame touch() { - super.touch(); - return this; - } - - @Override - public Http2GoAwayFrame touch(Object hint) { - super.touch(hint); - return this; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof DefaultHttp2GoAwayFrame)) { - return false; - } - DefaultHttp2GoAwayFrame other = (DefaultHttp2GoAwayFrame) o; - return errorCode == other.errorCode && extraStreamIds == other.extraStreamIds && super.equals(other); - } - - @Override - public int hashCode() { - int hash = super.hashCode(); - hash = hash * 31 + (int) (errorCode ^ errorCode >>> 32); - hash = hash * 31 + extraStreamIds; - return hash; - } - - @Override - public String toString() { - return StringUtil.simpleClassName(this) + "(errorCode=" + errorCode + ", content=" + content() - + ", extraStreamIds=" + extraStreamIds + ", lastStreamId=" + lastStreamId + ')'; - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Headers.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Headers.java deleted file mode 100644 index 4ac21bbf41..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Headers.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.handler.codec.CharSequenceValueConverter; -import io.netty.handler.codec.DefaultHeaders; -import io.netty.util.AsciiString; -import io.netty.util.ByteProcessor; -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.UnstableApi; - -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.hasPseudoHeaderFormat; -import static io.netty.util.AsciiString.CASE_INSENSITIVE_HASHER; -import static io.netty.util.AsciiString.CASE_SENSITIVE_HASHER; -import static io.netty.util.AsciiString.isUpperCase; - -@UnstableApi -public class DefaultHttp2Headers - extends DefaultHeaders implements Http2Headers { - private static final ByteProcessor HTTP2_NAME_VALIDATOR_PROCESSOR = value -> !isUpperCase(value); - static final NameValidator HTTP2_NAME_VALIDATOR = name -> { - if (name == null || name.length() == 0) { - PlatformDependent.throwException(connectionError(PROTOCOL_ERROR, - "empty headers are not allowed [%s]", name)); - } - if (name instanceof AsciiString) { - int index = ((AsciiString) name).forEachByte(HTTP2_NAME_VALIDATOR_PROCESSOR); - if (index != -1) { - PlatformDependent.throwException(connectionError(PROTOCOL_ERROR, - "invalid header name [%s]", name)); - } - } else { - for (int i = 0; i < name.length(); ++i) { - if (isUpperCase(name.charAt(i))) { - PlatformDependent.throwException(connectionError(PROTOCOL_ERROR, - "invalid header name [%s]", name)); - } - } - } - }; - - private HeaderEntry firstNonPseudo = head; - - /** - * Create a new instance. - *

- * Header names will be validated according to - * rfc7540. - */ - public DefaultHttp2Headers() { - this(true); - } - - /** - * Create a new instance. - * @param validate {@code true} to validate header names according to - * rfc7540. {@code false} to not validate header names. - */ - @SuppressWarnings("unchecked") - public DefaultHttp2Headers(boolean validate) { - // Case sensitive compare is used because it is cheaper, and header validation can be used to catch invalid - // headers. - super(CASE_SENSITIVE_HASHER, - CharSequenceValueConverter.INSTANCE, - validate ? HTTP2_NAME_VALIDATOR : NameValidator.NOT_NULL); - } - - /** - * Create a new instance. - * @param validate {@code true} to validate header names according to - * rfc7540. {@code false} to not validate header names. - * @param arraySizeHint A hint as to how large the hash data structure should be. - * The next positive power of two will be used. An upper bound may be enforced. - */ - @SuppressWarnings("unchecked") - public DefaultHttp2Headers(boolean validate, int arraySizeHint) { - // Case sensitive compare is used because it is cheaper, and header validation can be used to catch invalid - // headers. - super(CASE_SENSITIVE_HASHER, - CharSequenceValueConverter.INSTANCE, - validate ? HTTP2_NAME_VALIDATOR : NameValidator.NOT_NULL, - arraySizeHint); - } - - @Override - public Http2Headers clear() { - firstNonPseudo = head; - return super.clear(); - } - - @Override - public boolean equals(Object o) { - return o instanceof Http2Headers && equals((Http2Headers) o, CASE_SENSITIVE_HASHER); - } - - @Override - public int hashCode() { - return hashCode(CASE_SENSITIVE_HASHER); - } - - @Override - public Http2Headers method(CharSequence value) { - set(PseudoHeaderName.METHOD.value(), value); - return this; - } - - @Override - public Http2Headers scheme(CharSequence value) { - set(PseudoHeaderName.SCHEME.value(), value); - return this; - } - - @Override - public Http2Headers authority(CharSequence value) { - set(PseudoHeaderName.AUTHORITY.value(), value); - return this; - } - - @Override - public Http2Headers path(CharSequence value) { - set(PseudoHeaderName.PATH.value(), value); - return this; - } - - @Override - public Http2Headers status(CharSequence value) { - set(PseudoHeaderName.STATUS.value(), value); - return this; - } - - @Override - public CharSequence method() { - return get(PseudoHeaderName.METHOD.value()); - } - - @Override - public CharSequence scheme() { - return get(PseudoHeaderName.SCHEME.value()); - } - - @Override - public CharSequence authority() { - return get(PseudoHeaderName.AUTHORITY.value()); - } - - @Override - public CharSequence path() { - return get(PseudoHeaderName.PATH.value()); - } - - @Override - public CharSequence status() { - return get(PseudoHeaderName.STATUS.value()); - } - - @Override - public boolean contains(CharSequence name, CharSequence value) { - return contains(name, value, false); - } - - @Override - public boolean contains(CharSequence name, CharSequence value, boolean caseInsensitive) { - return contains(name, value, caseInsensitive ? CASE_INSENSITIVE_HASHER : CASE_SENSITIVE_HASHER); - } - - @Override - protected final HeaderEntry newHeaderEntry(int h, CharSequence name, CharSequence value, - HeaderEntry next) { - return new Http2HeaderEntry(h, name, value, next); - } - - private final class Http2HeaderEntry extends HeaderEntry { - Http2HeaderEntry(int hash, CharSequence key, CharSequence value, - HeaderEntry next) { - super(hash, key); - this.value = value; - this.next = next; - - // Make sure the pseudo headers fields are first in iteration order - if (hasPseudoHeaderFormat(key)) { - after = firstNonPseudo; - before = firstNonPseudo.before(); - } else { - after = head; - before = head.before(); - if (firstNonPseudo == head) { - firstNonPseudo = this; - } - } - pointNeighborsToThis(); - } - - @Override - protected void remove() { - if (this == firstNonPseudo) { - firstNonPseudo = firstNonPseudo.after(); - } - super.remove(); - } - } -} 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 deleted file mode 100644 index 98cc6154af..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2HeadersDecoder.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.util.internal.UnstableApi; - -import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_HEADER_LIST_SIZE; -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; -import static java.util.Objects.requireNonNull; - -@UnstableApi -public class DefaultHttp2HeadersDecoder implements Http2HeadersDecoder, Http2HeadersDecoder.Configuration { - private static final float HEADERS_COUNT_WEIGHT_NEW = 1 / 5f; - private static final float HEADERS_COUNT_WEIGHT_HISTORICAL = 1 - HEADERS_COUNT_WEIGHT_NEW; - - 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 - * structure for storing headers should be. - */ - private float headerArraySizeAccumulator = 8; - - public DefaultHttp2HeadersDecoder() { - this(true); - } - - public DefaultHttp2HeadersDecoder(boolean validateHeaders) { - this(validateHeaders, DEFAULT_HEADER_LIST_SIZE); - } - - /** - * Create a new instance. - * @param validateHeaders {@code true} to validate headers are valid according to the RFC. - * @param maxHeaderListSize This is the only setting that can be configured before notifying the peer. - * This is because SETTINGS_MAX_HEADER_LIST_SIZE - * allows a lower than advertised limit from being enforced, and the default limit is unlimited - * (which is dangerous). - */ - public DefaultHttp2HeadersDecoder(boolean validateHeaders, long maxHeaderListSize) { - this(validateHeaders, maxHeaderListSize, /* initialHuffmanDecodeCapacity= */ -1); - } - - /** - * Create a new instance. - * @param validateHeaders {@code true} to validate headers are valid according to the RFC. - * @param maxHeaderListSize This is the only setting that can be configured before notifying the peer. - * This is because SETTINGS_MAX_HEADER_LIST_SIZE - * allows a lower than advertised limit from being enforced, and the default limit is unlimited - * (which is dangerous). - * @param initialHuffmanDecodeCapacity Does nothing, do not use. - */ - public DefaultHttp2HeadersDecoder(boolean validateHeaders, long maxHeaderListSize, - @Deprecated int initialHuffmanDecodeCapacity) { - this(validateHeaders, new HpackDecoder(maxHeaderListSize)); - } - - /** - * Exposed Used for testing only! Default values used in the initial settings frame are overridden intentionally - * for testing but violate the RFC if used outside the scope of testing. - */ - DefaultHttp2HeadersDecoder(boolean validateHeaders, HpackDecoder hpackDecoder) { - this.hpackDecoder = requireNonNull(hpackDecoder, "hpackDecoder"); - this.validateHeaders = validateHeaders; - maxHeaderListSizeGoAway = - Http2CodecUtil.calculateMaxHeaderListSizeGoAway(hpackDecoder.getMaxHeaderListSize()); - } - - @Override - public void maxHeaderTableSize(long max) throws Http2Exception { - hpackDecoder.setMaxHeaderTableSize(max); - } - - @Override - public long maxHeaderTableSize() { - return hpackDecoder.getMaxHeaderTableSize(); - } - - @Override - public void maxHeaderListSize(long max, long goAwayMax) throws Http2Exception { - 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); - maxHeaderListSizeGoAway = goAwayMax; - } - - @Override - public long maxHeaderListSize() { - return hpackDecoder.getMaxHeaderListSize(); - } - - @Override - public long maxHeaderListSizeGoAway() { - return maxHeaderListSizeGoAway; - } - - @Override - public Configuration configuration() { - return this; - } - - @Override - public Http2Headers decodeHeaders(int streamId, ByteBuf headerBlock) throws Http2Exception { - try { - final Http2Headers headers = newHeaders(); - hpackDecoder.decode(streamId, headerBlock, headers, validateHeaders); - headerArraySizeAccumulator = HEADERS_COUNT_WEIGHT_NEW * headers.size() + - HEADERS_COUNT_WEIGHT_HISTORICAL * headerArraySizeAccumulator; - return headers; - } catch (Http2Exception e) { - throw e; - } catch (Throwable e) { - // Default handler for any other types of errors that may have occurred. For example, - // the Header builder throws IllegalArgumentException if the key or value was invalid - // for any reason (e.g. the key was an invalid pseudo-header). - throw connectionError(COMPRESSION_ERROR, e, e.getMessage()); - } - } - - /** - * A weighted moving average estimating how many headers are expected during the decode process. - * @return an estimate of how many headers are expected during the decode process. - */ - protected final int numberOfHeadersGuess() { - return (int) headerArraySizeAccumulator; - } - - /** - * Determines if the headers should be validated as a result of the decode operation. - * @return {@code true} if the headers should be validated as a result of the decode operation. - */ - protected final boolean validateHeaders() { - return validateHeaders; - } - - /** - * Create a new {@link Http2Headers} object which will store the results of the decode operation. - * @return a new {@link Http2Headers} object which will store the results of the decode operation. - */ - protected Http2Headers newHeaders() { - return new DefaultHttp2Headers(validateHeaders, (int) headerArraySizeAccumulator); - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2HeadersEncoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2HeadersEncoder.java deleted file mode 100644 index 7e932b5630..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2HeadersEncoder.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.internal.UnstableApi; - -import static io.netty.handler.codec.http2.Http2Error.COMPRESSION_ERROR; -import static io.netty.handler.codec.http2.Http2Exception.connectionError; -import static java.util.Objects.requireNonNull; - -@UnstableApi -public class DefaultHttp2HeadersEncoder implements Http2HeadersEncoder, Http2HeadersEncoder.Configuration { - private final HpackEncoder hpackEncoder; - private final SensitivityDetector sensitivityDetector; - private final ByteBuf tableSizeChangeOutput = Unpooled.buffer(); - - public DefaultHttp2HeadersEncoder() { - this(NEVER_SENSITIVE); - } - - public DefaultHttp2HeadersEncoder(SensitivityDetector sensitivityDetector) { - this(sensitivityDetector, new HpackEncoder()); - } - - public DefaultHttp2HeadersEncoder(SensitivityDetector sensitivityDetector, boolean ignoreMaxHeaderListSize) { - this(sensitivityDetector, new HpackEncoder(ignoreMaxHeaderListSize)); - } - - public DefaultHttp2HeadersEncoder(SensitivityDetector sensitivityDetector, boolean ignoreMaxHeaderListSize, - int dynamicTableArraySizeHint) { - this(sensitivityDetector, ignoreMaxHeaderListSize, dynamicTableArraySizeHint, HpackEncoder.HUFF_CODE_THRESHOLD); - } - - public DefaultHttp2HeadersEncoder(SensitivityDetector sensitivityDetector, boolean ignoreMaxHeaderListSize, - int dynamicTableArraySizeHint, int huffCodeThreshold) { - this(sensitivityDetector, - new HpackEncoder(ignoreMaxHeaderListSize, dynamicTableArraySizeHint, huffCodeThreshold)); - } - - /** - * Exposed Used for testing only! Default values used in the initial settings frame are overridden intentionally - * for testing but violate the RFC if used outside the scope of testing. - */ - DefaultHttp2HeadersEncoder(SensitivityDetector sensitivityDetector, HpackEncoder hpackEncoder) { - this.sensitivityDetector = requireNonNull(sensitivityDetector, "sensitiveDetector"); - this.hpackEncoder = requireNonNull(hpackEncoder, "hpackEncoder"); - } - - @Override - public void encodeHeaders(int streamId, Http2Headers headers, ByteBuf buffer) throws Http2Exception { - try { - // If there was a change in the table size, serialize the output from the hpackEncoder - // resulting from that change. - if (tableSizeChangeOutput.isReadable()) { - buffer.writeBytes(tableSizeChangeOutput); - tableSizeChangeOutput.clear(); - } - - hpackEncoder.encodeHeaders(streamId, buffer, headers, sensitivityDetector); - } catch (Http2Exception e) { - throw e; - } catch (Throwable t) { - throw connectionError(COMPRESSION_ERROR, t, "Failed encoding headers block: %s", t.getMessage()); - } - } - - @Override - public void maxHeaderTableSize(long max) throws Http2Exception { - hpackEncoder.setMaxHeaderTableSize(tableSizeChangeOutput, max); - } - - @Override - public long maxHeaderTableSize() { - return hpackEncoder.getMaxHeaderTableSize(); - } - - @Override - public void maxHeaderListSize(long max) throws Http2Exception { - hpackEncoder.setMaxHeaderListSize(max); - } - - @Override - public long maxHeaderListSize() { - return hpackEncoder.getMaxHeaderListSize(); - } - - @Override - public Configuration configuration() { - return this; - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2HeadersFrame.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2HeadersFrame.java deleted file mode 100644 index df691dcd3a..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2HeadersFrame.java +++ /dev/null @@ -1,116 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.UnstableApi; - -import static io.netty.handler.codec.http2.Http2CodecUtil.verifyPadding; -import static java.util.Objects.requireNonNull; - -/** - * The default {@link Http2HeadersFrame} implementation. - */ -@UnstableApi -public final class DefaultHttp2HeadersFrame extends AbstractHttp2StreamFrame implements Http2HeadersFrame { - private final Http2Headers headers; - private final boolean endStream; - private final int padding; - - /** - * Equivalent to {@code new DefaultHttp2HeadersFrame(headers, false)}. - * - * @param headers the non-{@code null} headers to send - */ - public DefaultHttp2HeadersFrame(Http2Headers headers) { - this(headers, false); - } - - /** - * Equivalent to {@code new DefaultHttp2HeadersFrame(headers, endStream, 0)}. - * - * @param headers the non-{@code null} headers to send - */ - public DefaultHttp2HeadersFrame(Http2Headers headers, boolean endStream) { - this(headers, endStream, 0); - } - - /** - * Construct a new headers message. - * - * @param headers the non-{@code null} headers to send - * @param endStream whether these headers should terminate the stream - * @param padding additional bytes that should be added to obscure the true content size. Must be between 0 and - * 256 (inclusive). - */ - public DefaultHttp2HeadersFrame(Http2Headers headers, boolean endStream, int padding) { - this.headers = requireNonNull(headers, "headers"); - this.endStream = endStream; - verifyPadding(padding); - this.padding = padding; - } - - @Override - public DefaultHttp2HeadersFrame stream(Http2FrameStream stream) { - super.stream(stream); - return this; - } - - @Override - public String name() { - return "HEADERS"; - } - - @Override - public Http2Headers headers() { - return headers; - } - - @Override - public boolean isEndStream() { - return endStream; - } - - @Override - public int padding() { - return padding; - } - - @Override - public String toString() { - return StringUtil.simpleClassName(this) + "(stream=" + stream() + ", headers=" + headers - + ", endStream=" + endStream + ", padding=" + padding + ')'; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof DefaultHttp2HeadersFrame)) { - return false; - } - DefaultHttp2HeadersFrame other = (DefaultHttp2HeadersFrame) o; - return super.equals(other) && headers.equals(other.headers) - && endStream == other.endStream && padding == other.padding; - } - - @Override - public int hashCode() { - int hash = super.hashCode(); - hash = hash * 31 + headers.hashCode(); - hash = hash * 31 + (endStream ? 0 : 1); - hash = hash * 31 + padding; - return hash; - } -} 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 deleted file mode 100644 index 5714de66e4..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2LocalFlowController.java +++ /dev/null @@ -1,650 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http2.Http2Exception.CompositeStreamException; -import io.netty.handler.codec.http2.Http2Exception.StreamException; -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.UnstableApi; - -import static io.netty.handler.codec.http2.Http2CodecUtil.CONNECTION_STREAM_ID; -import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_WINDOW_SIZE; -import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_INITIAL_WINDOW_SIZE; -import static io.netty.handler.codec.http2.Http2CodecUtil.MIN_INITIAL_WINDOW_SIZE; -import static io.netty.handler.codec.http2.Http2Error.FLOW_CONTROL_ERROR; -import static io.netty.handler.codec.http2.Http2Error.INTERNAL_ERROR; -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.checkPositiveOrZero; -import static java.lang.Math.max; -import static java.lang.Math.min; -import static java.util.Objects.requireNonNull; - -/** - * Basic implementation of {@link Http2LocalFlowController}. - *

- * This class is NOT thread safe. The assumption is all methods must be invoked from a single thread. - * Typically this thread is the event loop thread for the {@link ChannelHandlerContext} managed by this class. - */ -@UnstableApi -public class DefaultHttp2LocalFlowController implements Http2LocalFlowController { - /** - * The default ratio of window size to initial window size below which a {@code WINDOW_UPDATE} - * is sent to expand the window. - */ - public static final float DEFAULT_WINDOW_UPDATE_RATIO = 0.5f; - - private final Http2Connection connection; - private final Http2Connection.PropertyKey stateKey; - private Http2FrameWriter frameWriter; - private ChannelHandlerContext ctx; - private float windowUpdateRatio; - private int initialWindowSize = DEFAULT_WINDOW_SIZE; - - public DefaultHttp2LocalFlowController(Http2Connection connection) { - this(connection, DEFAULT_WINDOW_UPDATE_RATIO, false); - } - - /** - * Constructs a controller with the given settings. - * - * @param connection the connection state. - * @param windowUpdateRatio the window percentage below which to send a {@code WINDOW_UPDATE}. - * @param autoRefillConnectionWindow if {@code true}, effectively disables the connection window - * in the flow control algorithm as they will always refill automatically without requiring the - * application to consume the bytes. When enabled, the maximum bytes you must be prepared to - * queue is proportional to {@code maximum number of concurrent streams * the initial window - * size per stream} - * (SETTINGS_MAX_CONCURRENT_STREAMS - * SETTINGS_INITIAL_WINDOW_SIZE). - */ - public DefaultHttp2LocalFlowController(Http2Connection connection, - float windowUpdateRatio, - boolean autoRefillConnectionWindow) { - this.connection = requireNonNull(connection, "connection"); - windowUpdateRatio(windowUpdateRatio); - - // Add a flow state for the connection. - stateKey = connection.newKey(); - FlowState connectionState = autoRefillConnectionWindow ? - new AutoRefillState(connection.connectionStream(), initialWindowSize) : - new DefaultState(connection.connectionStream(), initialWindowSize); - connection.connectionStream().setProperty(stateKey, connectionState); - - // Register for notification of new streams. - connection.addListener(new Http2ConnectionAdapter() { - @Override - public void onStreamAdded(Http2Stream stream) { - // Unconditionally used the reduced flow control state because it requires no object allocation - // and the DefaultFlowState will be allocated in onStreamActive. - stream.setProperty(stateKey, REDUCED_FLOW_STATE); - } - - @Override - public void onStreamActive(Http2Stream stream) { - // Need to be sure the stream's initial window is adjusted for SETTINGS - // frames which may have been exchanged while it was in IDLE - stream.setProperty(stateKey, new DefaultState(stream, initialWindowSize)); - } - - @Override - public void onStreamClosed(Http2Stream stream) { - try { - // When a stream is closed, consume any remaining bytes so that they - // are restored to the connection window. - FlowState state = state(stream); - int unconsumedBytes = state.unconsumedBytes(); - if (ctx != null && unconsumedBytes > 0) { - if (consumeAllBytes(state, unconsumedBytes)) { - // As the user has no real control on when this callback is used we should better - // call flush() if we produced any window update to ensure we not stale. - ctx.flush(); - } - } - } catch (Http2Exception e) { - PlatformDependent.throwException(e); - } finally { - // Unconditionally reduce the amount of memory required for flow control because there is no - // object allocation costs associated with doing so and the stream will not have any more - // local flow control state to keep track of anymore. - stream.setProperty(stateKey, REDUCED_FLOW_STATE); - } - } - }); - } - - @Override - public DefaultHttp2LocalFlowController frameWriter(Http2FrameWriter frameWriter) { - this.frameWriter = requireNonNull(frameWriter, "frameWriter"); - return this; - } - - @Override - public void channelHandlerContext(ChannelHandlerContext ctx) { - this.ctx = requireNonNull(ctx, "ctx"); - } - - @Override - public void initialWindowSize(int newWindowSize) throws Http2Exception { - assert ctx == null || ctx.executor().inEventLoop(); - int delta = newWindowSize - initialWindowSize; - initialWindowSize = newWindowSize; - - WindowUpdateVisitor visitor = new WindowUpdateVisitor(delta); - connection.forEachActiveStream(visitor); - visitor.throwIfError(); - } - - @Override - public int initialWindowSize() { - return initialWindowSize; - } - - @Override - public int windowSize(Http2Stream stream) { - return state(stream).windowSize(); - } - - @Override - public int initialWindowSize(Http2Stream stream) { - return state(stream).initialWindowSize(); - } - - @Override - public void incrementWindowSize(Http2Stream stream, int delta) throws Http2Exception { - assert ctx != null && ctx.executor().inEventLoop(); - FlowState state = state(stream); - // Just add the delta to the stream-specific initial window size so that the next time the window - // expands it will grow to the new initial size. - state.incrementInitialStreamWindow(delta); - state.writeWindowUpdateIfNeeded(); - } - - @Override - public boolean consumeBytes(Http2Stream stream, int numBytes) throws Http2Exception { - assert ctx != null && ctx.executor().inEventLoop(); - checkPositiveOrZero(numBytes, "numBytes"); - if (numBytes == 0) { - return false; - } - - // Streams automatically consume all remaining bytes when they are closed, so just ignore - // if already closed. - if (stream != null && !isClosed(stream)) { - if (stream.id() == CONNECTION_STREAM_ID) { - throw new UnsupportedOperationException("Returning bytes for the connection window is not supported"); - } - - return consumeAllBytes(state(stream), numBytes); - } - return false; - } - - private boolean consumeAllBytes(FlowState state, int numBytes) throws Http2Exception { - return connectionState().consumeBytes(numBytes) | state.consumeBytes(numBytes); - } - - @Override - public int unconsumedBytes(Http2Stream stream) { - return state(stream).unconsumedBytes(); - } - - private static void checkValidRatio(float ratio) { - if (Double.compare(ratio, 0.0) <= 0 || Double.compare(ratio, 1.0) >= 0) { - throw new IllegalArgumentException("Invalid ratio: " + ratio); - } - } - - /** - * The window update ratio is used to determine when a window update must be sent. If the ratio - * of bytes processed since the last update has meet or exceeded this ratio then a window update will - * be sent. This is the global window update ratio that will be used for new streams. - * @param ratio the ratio to use when checking if a {@code WINDOW_UPDATE} is determined necessary for new streams. - * @throws IllegalArgumentException If the ratio is out of bounds (0, 1). - */ - public void windowUpdateRatio(float ratio) { - assert ctx == null || ctx.executor().inEventLoop(); - checkValidRatio(ratio); - windowUpdateRatio = ratio; - } - - /** - * The window update ratio is used to determine when a window update must be sent. If the ratio - * of bytes processed since the last update has meet or exceeded this ratio then a window update will - * be sent. This is the global window update ratio that will be used for new streams. - */ - public float windowUpdateRatio() { - return windowUpdateRatio; - } - - /** - * The window update ratio is used to determine when a window update must be sent. If the ratio - * of bytes processed since the last update has meet or exceeded this ratio then a window update will - * be sent. This window update ratio will only be applied to {@code streamId}. - *

- * Note it is the responsibly of the caller to ensure that the the - * initial {@code SETTINGS} frame is sent before this is called. It would - * be considered a {@link Http2Error#PROTOCOL_ERROR} if a {@code WINDOW_UPDATE} - * was generated by this method before the initial {@code SETTINGS} frame is sent. - * @param stream the stream for which {@code ratio} applies to. - * @param ratio the ratio to use when checking if a {@code WINDOW_UPDATE} is determined necessary. - * @throws Http2Exception If a protocol-error occurs while generating {@code WINDOW_UPDATE} frames - */ - public void windowUpdateRatio(Http2Stream stream, float ratio) throws Http2Exception { - assert ctx != null && ctx.executor().inEventLoop(); - checkValidRatio(ratio); - FlowState state = state(stream); - state.windowUpdateRatio(ratio); - state.writeWindowUpdateIfNeeded(); - } - - /** - * The window update ratio is used to determine when a window update must be sent. If the ratio - * of bytes processed since the last update has meet or exceeded this ratio then a window update will - * be sent. This window update ratio will only be applied to {@code streamId}. - * @throws Http2Exception If no stream corresponding to {@code stream} could be found. - */ - public float windowUpdateRatio(Http2Stream stream) throws Http2Exception { - return state(stream).windowUpdateRatio(); - } - - @Override - public void receiveFlowControlledFrame(Http2Stream stream, ByteBuf data, int padding, - boolean endOfStream) throws Http2Exception { - assert ctx != null && ctx.executor().inEventLoop(); - int dataLength = data.readableBytes() + padding; - - // Apply the connection-level flow control - FlowState connectionState = connectionState(); - connectionState.receiveFlowControlledFrame(dataLength); - - if (stream != null && !isClosed(stream)) { - // Apply the stream-level flow control - FlowState state = state(stream); - state.endOfStream(endOfStream); - state.receiveFlowControlledFrame(dataLength); - } else if (dataLength > 0) { - // Immediately consume the bytes for the connection window. - connectionState.consumeBytes(dataLength); - } - } - - private FlowState connectionState() { - return connection.connectionStream().getProperty(stateKey); - } - - private FlowState state(Http2Stream stream) { - return stream.getProperty(stateKey); - } - - private static boolean isClosed(Http2Stream stream) { - return stream.state() == Http2Stream.State.CLOSED; - } - - /** - * Flow control state that does autorefill of the flow control window when the data is - * received. - */ - private final class AutoRefillState extends DefaultState { - AutoRefillState(Http2Stream stream, int initialWindowSize) { - super(stream, initialWindowSize); - } - - @Override - public void receiveFlowControlledFrame(int dataLength) throws Http2Exception { - super.receiveFlowControlledFrame(dataLength); - // Need to call the super to consume the bytes, since this.consumeBytes does nothing. - super.consumeBytes(dataLength); - } - - @Override - public boolean consumeBytes(int numBytes) throws Http2Exception { - // Do nothing, since the bytes are already consumed upon receiving the data. - return false; - } - } - - /** - * Flow control window state for an individual stream. - */ - private class DefaultState implements FlowState { - private final Http2Stream stream; - - /** - * The actual flow control window that is decremented as soon as {@code DATA} arrives. - */ - private int window; - - /** - * A view of {@link #window} that is used to determine when to send {@code WINDOW_UPDATE} - * frames. Decrementing this window for received {@code DATA} frames is delayed until the - * application has indicated that the data has been fully processed. This prevents sending - * a {@code WINDOW_UPDATE} until the number of processed bytes drops below the threshold. - */ - private int processedWindow; - - /** - * This is what is used to determine how many bytes need to be returned relative to {@link #processedWindow}. - * Each stream has their own initial window size. - */ - private int initialStreamWindowSize; - - /** - * This is used to determine when {@link #processedWindow} is sufficiently far away from - * {@link #initialStreamWindowSize} such that a {@code WINDOW_UPDATE} should be sent. - * Each stream has their own window update ratio. - */ - private float streamWindowUpdateRatio; - - private int lowerBound; - private boolean endOfStream; - - DefaultState(Http2Stream stream, int initialWindowSize) { - this.stream = stream; - window(initialWindowSize); - streamWindowUpdateRatio = windowUpdateRatio; - } - - @Override - public void window(int initialWindowSize) { - assert ctx == null || ctx.executor().inEventLoop(); - window = processedWindow = initialStreamWindowSize = initialWindowSize; - } - - @Override - public int windowSize() { - return window; - } - - @Override - public int initialWindowSize() { - return initialStreamWindowSize; - } - - @Override - public void endOfStream(boolean endOfStream) { - this.endOfStream = endOfStream; - } - - @Override - public float windowUpdateRatio() { - return streamWindowUpdateRatio; - } - - @Override - public void windowUpdateRatio(float ratio) { - assert ctx == null || ctx.executor().inEventLoop(); - streamWindowUpdateRatio = ratio; - } - - @Override - public void incrementInitialStreamWindow(int delta) { - // Clip the delta so that the resulting initialStreamWindowSize falls within the allowed range. - int newValue = (int) min(MAX_INITIAL_WINDOW_SIZE, - max(MIN_INITIAL_WINDOW_SIZE, initialStreamWindowSize + (long) delta)); - delta = newValue - initialStreamWindowSize; - - initialStreamWindowSize += delta; - } - - @Override - public void incrementFlowControlWindows(int delta) throws Http2Exception { - if (delta > 0 && window > MAX_INITIAL_WINDOW_SIZE - delta) { - throw streamError(stream.id(), FLOW_CONTROL_ERROR, - "Flow control window overflowed for stream: %d", stream.id()); - } - - window += delta; - processedWindow += delta; - lowerBound = min(delta, 0); - } - - @Override - public void receiveFlowControlledFrame(int dataLength) throws Http2Exception { - assert dataLength >= 0; - - // Apply the delta. Even if we throw an exception we want to have taken this delta into account. - window -= dataLength; - - // Window size can become negative if we sent a SETTINGS frame that reduces the - // size of the transfer window after the peer has written data frames. - // The value is bounded by the length that SETTINGS frame decrease the window. - // This difference is stored for the connection when writing the SETTINGS frame - // and is cleared once we send a WINDOW_UPDATE frame. - if (window < lowerBound) { - throw streamError(stream.id(), FLOW_CONTROL_ERROR, - "Flow control window exceeded for stream: %d", stream.id()); - } - } - - private void returnProcessedBytes(int delta) throws Http2Exception { - if (processedWindow - delta < window) { - throw streamError(stream.id(), INTERNAL_ERROR, - "Attempting to return too many bytes for stream %d", stream.id()); - } - processedWindow -= delta; - } - - @Override - public boolean consumeBytes(int numBytes) throws Http2Exception { - // Return the bytes processed and update the window. - returnProcessedBytes(numBytes); - return writeWindowUpdateIfNeeded(); - } - - @Override - public int unconsumedBytes() { - return processedWindow - window; - } - - @Override - public boolean writeWindowUpdateIfNeeded() throws Http2Exception { - if (endOfStream || initialStreamWindowSize <= 0 || - // If the stream is already closed there is no need to try to write a window update for it. - isClosed(stream)) { - return false; - } - - int threshold = (int) (initialStreamWindowSize * streamWindowUpdateRatio); - if (processedWindow <= threshold) { - writeWindowUpdate(); - return true; - } - return false; - } - - /** - * Called to perform a window update for this stream (or connection). Updates the window size back - * to the size of the initial window and sends a window update frame to the remote endpoint. - */ - private void writeWindowUpdate() throws Http2Exception { - // Expand the window for this stream back to the size of the initial window. - int deltaWindowSize = initialStreamWindowSize - processedWindow; - try { - incrementFlowControlWindows(deltaWindowSize); - } catch (Throwable t) { - throw connectionError(INTERNAL_ERROR, t, - "Attempting to return too many bytes for stream %d", stream.id()); - } - - // Send a window update for the stream/connection. - frameWriter.writeWindowUpdate(ctx, stream.id(), deltaWindowSize); - } - } - - /** - * The local flow control state for a single stream that is not in a state where flow controlled frames cannot - * be exchanged. - */ - private static final FlowState REDUCED_FLOW_STATE = new FlowState() { - - @Override - public int windowSize() { - return 0; - } - - @Override - public int initialWindowSize() { - return 0; - } - - @Override - public void window(int initialWindowSize) { - throw new UnsupportedOperationException(); - } - - @Override - public void incrementInitialStreamWindow(int delta) { - // This operation needs to be supported during the initial settings exchange when - // the peer has not yet acknowledged this peer being activated. - } - - @Override - public boolean writeWindowUpdateIfNeeded() throws Http2Exception { - throw new UnsupportedOperationException(); - } - - @Override - public boolean consumeBytes(int numBytes) throws Http2Exception { - return false; - } - - @Override - public int unconsumedBytes() { - return 0; - } - - @Override - public float windowUpdateRatio() { - throw new UnsupportedOperationException(); - } - - @Override - public void windowUpdateRatio(float ratio) { - throw new UnsupportedOperationException(); - } - - @Override - public void receiveFlowControlledFrame(int dataLength) throws Http2Exception { - throw new UnsupportedOperationException(); - } - - @Override - public void incrementFlowControlWindows(int delta) throws Http2Exception { - // This operation needs to be supported during the initial settings exchange when - // the peer has not yet acknowledged this peer being activated. - } - - @Override - public void endOfStream(boolean endOfStream) { - throw new UnsupportedOperationException(); - } - }; - - /** - * An abstraction which provides specific extensions used by local flow control. - */ - private interface FlowState { - - int windowSize(); - - int initialWindowSize(); - - void window(int initialWindowSize); - - /** - * Increment the initial window size for this stream. - * @param delta The amount to increase the initial window size by. - */ - void incrementInitialStreamWindow(int delta); - - /** - * Updates the flow control window for this stream if it is appropriate. - * - * @return true if {@code WINDOW_UPDATE} was written, false otherwise. - */ - boolean writeWindowUpdateIfNeeded() throws Http2Exception; - - /** - * Indicates that the application has consumed {@code numBytes} from the connection or stream and is - * ready to receive more data. - * - * @param numBytes the number of bytes to be returned to the flow control window. - * @return true if {@code WINDOW_UPDATE} was written, false otherwise. - * @throws Http2Exception If the number of bytes is too great for the current window, - * or an internal error occurs. - */ - boolean consumeBytes(int numBytes) throws Http2Exception; - - int unconsumedBytes(); - - float windowUpdateRatio(); - - void windowUpdateRatio(float ratio); - - /** - * A flow control event has occurred and we should decrement the amount of available bytes for this stream. - * @param dataLength The amount of data to for which this stream is no longer eligible to use for flow control. - * @throws Http2Exception If too much data is used relative to how much is available. - */ - void receiveFlowControlledFrame(int dataLength) throws Http2Exception; - - /** - * Increment the windows which are used to determine many bytes have been processed. - * @param delta The amount to increment the window by. - * @throws Http2Exception if integer overflow occurs on the window. - */ - void incrementFlowControlWindows(int delta) throws Http2Exception; - - void endOfStream(boolean endOfStream); - } - - /** - * Provides a means to iterate over all active streams and increment the flow control windows. - */ - private final class WindowUpdateVisitor implements Http2StreamVisitor { - private CompositeStreamException compositeException; - private final int delta; - - WindowUpdateVisitor(int delta) { - this.delta = delta; - } - - @Override - public boolean visit(Http2Stream stream) throws Http2Exception { - try { - // Increment flow control window first so state will be consistent if overflow is detected. - FlowState state = state(stream); - state.incrementFlowControlWindows(delta); - state.incrementInitialStreamWindow(delta); - } catch (StreamException e) { - if (compositeException == null) { - compositeException = new CompositeStreamException(e.error(), 4); - } - compositeException.add(e); - } - return true; - } - - public void throwIfError() throws CompositeStreamException { - if (compositeException != null) { - throw compositeException; - } - } - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2PingFrame.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2PingFrame.java deleted file mode 100644 index f102bd1da3..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2PingFrame.java +++ /dev/null @@ -1,75 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.UnstableApi; - -/** - * The default {@link Http2PingFrame} implementation. - */ -@UnstableApi -public class DefaultHttp2PingFrame implements Http2PingFrame { - - private final long content; - private final boolean ack; - - public DefaultHttp2PingFrame(long content) { - this(content, false); - } - - public DefaultHttp2PingFrame(long content, boolean ack) { - this.content = content; - this.ack = ack; - } - - @Override - public boolean ack() { - return ack; - } - - @Override - public String name() { - return "PING"; - } - - @Override - public long content() { - return content; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof Http2PingFrame)) { - return false; - } - Http2PingFrame other = (Http2PingFrame) o; - return ack == other.ack() && content == other.content(); - } - - @Override - public int hashCode() { - int hash = super.hashCode(); - hash = hash * 31 + (ack ? 1 : 0); - return hash; - } - - @Override - public String toString() { - return StringUtil.simpleClassName(this) + "(content=" + content + ", ack=" + ack + ')'; - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2PriorityFrame.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2PriorityFrame.java deleted file mode 100644 index 5e073d56af..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2PriorityFrame.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.util.internal.UnstableApi; - -/** - * Default implementation of {@linkplain Http2PriorityFrame} - */ -@UnstableApi -public final class DefaultHttp2PriorityFrame extends AbstractHttp2StreamFrame implements Http2PriorityFrame { - - private final int streamDependency; - private final short weight; - private final boolean exclusive; - - public DefaultHttp2PriorityFrame(int streamDependency, short weight, boolean exclusive) { - this.streamDependency = streamDependency; - this.weight = weight; - this.exclusive = exclusive; - } - - @Override - public int streamDependency() { - return streamDependency; - } - - @Override - public short weight() { - return weight; - } - - @Override - public boolean exclusive() { - return exclusive; - } - - @Override - public DefaultHttp2PriorityFrame stream(Http2FrameStream stream) { - super.stream(stream); - return this; - } - - @Override - public String name() { - return "PRIORITY_FRAME"; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof DefaultHttp2PriorityFrame)) { - return false; - } - DefaultHttp2PriorityFrame other = (DefaultHttp2PriorityFrame) o; - boolean same = super.equals(other); - return same && streamDependency == other.streamDependency - && weight == other.weight && exclusive == other.exclusive; - } - - @Override - public int hashCode() { - int hash = super.hashCode(); - hash = hash * 31 + streamDependency; - hash = hash * 31 + weight; - hash = hash * 31 + (exclusive ? 1 : 0); - return hash; - } - - @Override - public String toString() { - return "DefaultHttp2PriorityFrame(" + - "stream=" + stream() + - ", streamDependency=" + streamDependency + - ", weight=" + weight + - ", exclusive=" + exclusive + - ')'; - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2PushPromiseFrame.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2PushPromiseFrame.java deleted file mode 100644 index f9fd987109..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2PushPromiseFrame.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.util.internal.UnstableApi; - -/** - * Default implementation of {@link Http2PushPromiseFrame} - */ -@UnstableApi -public final class DefaultHttp2PushPromiseFrame implements Http2PushPromiseFrame { - - private Http2FrameStream pushStreamFrame; - private final Http2Headers http2Headers; - private Http2FrameStream streamFrame; - private final int padding; - private final int promisedStreamId; - - public DefaultHttp2PushPromiseFrame(Http2Headers http2Headers) { - this(http2Headers, 0); - } - - public DefaultHttp2PushPromiseFrame(Http2Headers http2Headers, int padding) { - this(http2Headers, padding, -1); - } - - DefaultHttp2PushPromiseFrame(Http2Headers http2Headers, int padding, int promisedStreamId) { - this.http2Headers = http2Headers; - this.padding = padding; - this.promisedStreamId = promisedStreamId; - } - - @Override - public Http2StreamFrame pushStream(Http2FrameStream stream) { - pushStreamFrame = stream; - return this; - } - - @Override - public Http2FrameStream pushStream() { - return pushStreamFrame; - } - - @Override - public Http2Headers http2Headers() { - return http2Headers; - } - - @Override - public int padding() { - return padding; - } - - @Override - public int promisedStreamId() { - if (pushStreamFrame != null) { - return pushStreamFrame.id(); - } else { - return promisedStreamId; - } - } - - @Override - public Http2PushPromiseFrame stream(Http2FrameStream stream) { - streamFrame = stream; - return this; - } - - @Override - public Http2FrameStream stream() { - return streamFrame; - } - - @Override - public String name() { - return "PUSH_PROMISE_FRAME"; - } - - @Override - public String toString() { - return "DefaultHttp2PushPromiseFrame{" + - "pushStreamFrame=" + pushStreamFrame + - ", http2Headers=" + http2Headers + - ", streamFrame=" + streamFrame + - ", padding=" + padding + - '}'; - } -} 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 deleted file mode 100644 index 8d140978ea..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2RemoteFlowController.java +++ /dev/null @@ -1,765 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.internal.UnstableApi; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.util.ArrayDeque; -import java.util.Deque; - -import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_WINDOW_SIZE; -import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_WEIGHT; -import static io.netty.handler.codec.http2.Http2CodecUtil.MIN_WEIGHT; -import static io.netty.handler.codec.http2.Http2Error.FLOW_CONTROL_ERROR; -import static io.netty.handler.codec.http2.Http2Error.INTERNAL_ERROR; -import static io.netty.handler.codec.http2.Http2Error.STREAM_CLOSED; -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.checkPositiveOrZero; -import static java.lang.Math.max; -import static java.lang.Math.min; -import static java.util.Objects.requireNonNull; - -/** - * Basic implementation of {@link Http2RemoteFlowController}. - *

- * This class is NOT thread safe. The assumption is all methods must be invoked from a single thread. - * Typically this thread is the event loop thread for the {@link ChannelHandlerContext} managed by this class. - */ -@UnstableApi -public class DefaultHttp2RemoteFlowController implements Http2RemoteFlowController { - private static final InternalLogger logger = - InternalLoggerFactory.getInstance(DefaultHttp2RemoteFlowController.class); - private static final int MIN_WRITABLE_CHUNK = 32 * 1024; - private final Http2Connection connection; - private final Http2Connection.PropertyKey stateKey; - private final StreamByteDistributor streamByteDistributor; - private final FlowState connectionState; - private int initialWindowSize = DEFAULT_WINDOW_SIZE; - private WritabilityMonitor monitor; - private ChannelHandlerContext ctx; - - public DefaultHttp2RemoteFlowController(Http2Connection connection) { - this(connection, (Listener) null); - } - - public DefaultHttp2RemoteFlowController(Http2Connection connection, - StreamByteDistributor streamByteDistributor) { - this(connection, streamByteDistributor, null); - } - - public DefaultHttp2RemoteFlowController(Http2Connection connection, final Listener listener) { - this(connection, new WeightedFairQueueByteDistributor(connection), listener); - } - - public DefaultHttp2RemoteFlowController(Http2Connection connection, - StreamByteDistributor streamByteDistributor, - final Listener listener) { - this.connection = requireNonNull(connection, "connection"); - this.streamByteDistributor = requireNonNull(streamByteDistributor, "streamWriteDistributor"); - - // Add a flow state for the connection. - stateKey = connection.newKey(); - connectionState = new FlowState(connection.connectionStream()); - connection.connectionStream().setProperty(stateKey, connectionState); - - // Monitor may depend upon connectionState, and so initialize after connectionState - listener(listener); - monitor.windowSize(connectionState, initialWindowSize); - - // Register for notification of new streams. - connection.addListener(new Http2ConnectionAdapter() { - @Override - public void onStreamAdded(Http2Stream stream) { - // If the stream state is not open then the stream is not yet eligible for flow controlled frames and - // only requires the ReducedFlowState. Otherwise the full amount of memory is required. - stream.setProperty(stateKey, new FlowState(stream)); - } - - @Override - public void onStreamActive(Http2Stream stream) { - // If the object was previously created, but later activated then we have to ensure the proper - // initialWindowSize is used. - monitor.windowSize(state(stream), initialWindowSize); - } - - @Override - public void onStreamClosed(Http2Stream stream) { - // Any pending frames can never be written, cancel and - // write errors for any pending frames. - state(stream).cancel(STREAM_CLOSED, null); - } - - @Override - public void onStreamHalfClosed(Http2Stream stream) { - if (HALF_CLOSED_LOCAL == stream.state()) { - /* - * When this method is called there should not be any - * pending frames left if the API is used correctly. However, - * it is possible that a erroneous application can sneak - * in a frame even after having already written a frame with the - * END_STREAM flag set, as the stream state might not transition - * immediately to HALF_CLOSED_LOCAL / CLOSED due to flow control - * delaying the write. - * - * This is to cancel any such illegal writes. - */ - state(stream).cancel(STREAM_CLOSED, null); - } - } - }); - } - - /** - * {@inheritDoc} - *

- * Any queued {@link FlowControlled} objects will be sent. - */ - @Override - public void channelHandlerContext(ChannelHandlerContext ctx) throws Http2Exception { - this.ctx = requireNonNull(ctx, "ctx"); - - // Writing the pending bytes will not check writability change and instead a writability change notification - // to be provided by an explicit call. - channelWritabilityChanged(); - - // Don't worry about cleaning up queued frames here if ctx is null. It is expected that all streams will be - // closed and the queue cleanup will occur when the stream state transitions occur. - - // If any frames have been queued up, we should send them now that we have a channel context. - if (isChannelWritable()) { - writePendingBytes(); - } - } - - @Override - public ChannelHandlerContext channelHandlerContext() { - return ctx; - } - - @Override - public void initialWindowSize(int newWindowSize) throws Http2Exception { - assert ctx == null || ctx.executor().inEventLoop(); - monitor.initialWindowSize(newWindowSize); - } - - @Override - public int initialWindowSize() { - return initialWindowSize; - } - - @Override - public int windowSize(Http2Stream stream) { - return state(stream).windowSize(); - } - - @Override - public boolean isWritable(Http2Stream stream) { - return monitor.isWritable(state(stream)); - } - - @Override - public void channelWritabilityChanged() throws Http2Exception { - monitor.channelWritabilityChange(); - } - - @Override - public void updateDependencyTree(int childStreamId, int parentStreamId, short weight, boolean exclusive) { - // It is assumed there are all validated at a higher level. For example in the Http2FrameReader. - assert weight >= MIN_WEIGHT && weight <= MAX_WEIGHT : "Invalid weight"; - assert childStreamId != parentStreamId : "A stream cannot depend on itself"; - assert childStreamId > 0 && parentStreamId >= 0 : "childStreamId must be > 0. parentStreamId must be >= 0."; - - streamByteDistributor.updateDependencyTree(childStreamId, parentStreamId, weight, exclusive); - } - - private boolean isChannelWritable() { - return ctx != null && isChannelWritable0(); - } - - private boolean isChannelWritable0() { - return ctx.channel().isWritable(); - } - - @Override - public void listener(Listener listener) { - monitor = listener == null ? new WritabilityMonitor() : new ListenerWritabilityMonitor(listener); - } - - @Override - public void incrementWindowSize(Http2Stream stream, int delta) throws Http2Exception { - assert ctx == null || ctx.executor().inEventLoop(); - monitor.incrementWindowSize(state(stream), delta); - } - - @Override - public void addFlowControlled(Http2Stream stream, FlowControlled frame) { - // The context can be null assuming the frame will be queued and send later when the context is set. - assert ctx == null || ctx.executor().inEventLoop(); - requireNonNull(frame, "frame"); - try { - monitor.enqueueFrame(state(stream), frame); - } catch (Throwable t) { - frame.error(ctx, t); - } - } - - @Override - public boolean hasFlowControlled(Http2Stream stream) { - return state(stream).hasFrame(); - } - - private FlowState state(Http2Stream stream) { - return (FlowState) stream.getProperty(stateKey); - } - - /** - * Returns the flow control window for the entire connection. - */ - private int connectionWindowSize() { - return connectionState.windowSize(); - } - - private int minUsableChannelBytes() { - // The current allocation algorithm values "fairness" and doesn't give any consideration to "goodput". It - // is possible that 1 byte will be allocated to many streams. In an effort to try to make "goodput" - // reasonable with the current allocation algorithm we have this "cheap" check up front to ensure there is - // an "adequate" amount of connection window before allocation is attempted. This is not foolproof as if the - // number of streams is >= this minimal number then we may still have the issue, but the idea is to narrow the - // circumstances in which this can happen without rewriting the allocation algorithm. - return max(ctx.channel().config().getWriteBufferLowWaterMark(), MIN_WRITABLE_CHUNK); - } - - private int maxUsableChannelBytes() { - // If the channel isWritable, allow at least minUsableChannelBytes. - int channelWritableBytes = (int) min(Integer.MAX_VALUE, ctx.channel().bytesBeforeUnwritable()); - int usableBytes = channelWritableBytes > 0 ? max(channelWritableBytes, minUsableChannelBytes()) : 0; - - // Clip the usable bytes by the connection window. - return min(connectionState.windowSize(), usableBytes); - } - - /** - * The amount of bytes that can be supported by underlying {@link io.netty.channel.Channel} without - * queuing "too-much". - */ - private int writableBytes() { - return min(connectionWindowSize(), maxUsableChannelBytes()); - } - - @Override - public void writePendingBytes() throws Http2Exception { - monitor.writePendingBytes(); - } - - /** - * The remote flow control state for a single stream. - */ - private final class FlowState implements StreamByteDistributor.StreamState { - private final Http2Stream stream; - private final Deque pendingWriteQueue; - private int window; - private long pendingBytes; - private boolean markedWritable; - - /** - * Set to true while a frame is being written, false otherwise. - */ - private boolean writing; - /** - * Set to true if cancel() was called. - */ - private boolean cancelled; - - FlowState(Http2Stream stream) { - this.stream = stream; - pendingWriteQueue = new ArrayDeque<>(2); - } - - /** - * Determine if the stream associated with this object is writable. - * @return {@code true} if the stream associated with this object is writable. - */ - boolean isWritable() { - return windowSize() > pendingBytes() && !cancelled; - } - - /** - * The stream this state is associated with. - */ - @Override - public Http2Stream stream() { - return stream; - } - - /** - * Returns the parameter from the last call to {@link #markedWritability(boolean)}. - */ - boolean markedWritability() { - return markedWritable; - } - - /** - * Save the state of writability. - */ - void markedWritability(boolean isWritable) { - this.markedWritable = isWritable; - } - - @Override - public int windowSize() { - return window; - } - - /** - * Reset the window size for this stream. - */ - void windowSize(int initialWindowSize) { - window = initialWindowSize; - } - - /** - * Write the allocated bytes for this stream. - * @return the number of bytes written for a stream or {@code -1} if no write occurred. - */ - int writeAllocatedBytes(int allocated) { - final int initialAllocated = allocated; - int writtenBytes; - // In case an exception is thrown we want to remember it and pass it to cancel(Throwable). - Throwable cause = null; - FlowControlled frame; - try { - assert !writing; - writing = true; - - // Write the remainder of frames that we are allowed to - boolean writeOccurred = false; - while (!cancelled && (frame = peek()) != null) { - int maxBytes = min(allocated, writableWindow()); - if (maxBytes <= 0 && frame.size() > 0) { - // The frame still has data, but the amount of allocated bytes has been exhausted. - // Don't write needless empty frames. - break; - } - writeOccurred = true; - int initialFrameSize = frame.size(); - try { - frame.write(ctx, max(0, maxBytes)); - if (frame.size() == 0) { - // This frame has been fully written, remove this frame and notify it. - // Since we remove this frame first, we're guaranteed that its error - // method will not be called when we call cancel. - pendingWriteQueue.remove(); - frame.writeComplete(); - } - } finally { - // Decrement allocated by how much was actually written. - allocated -= initialFrameSize - frame.size(); - } - } - - if (!writeOccurred) { - // Either there was no frame, or the amount of allocated bytes has been exhausted. - return -1; - } - - } catch (Throwable t) { - // Mark the state as cancelled, we'll clear the pending queue via cancel() below. - cancelled = true; - cause = t; - } finally { - writing = false; - // Make sure we always decrement the flow control windows - // by the bytes written. - writtenBytes = initialAllocated - allocated; - - decrementPendingBytes(writtenBytes, false); - decrementFlowControlWindow(writtenBytes); - - // If a cancellation occurred while writing, call cancel again to - // clear and error all of the pending writes. - if (cancelled) { - cancel(INTERNAL_ERROR, cause); - } - } - return writtenBytes; - } - - /** - * Increments the flow control window for this stream by the given delta and returns the new value. - */ - int incrementStreamWindow(int delta) throws Http2Exception { - if (delta > 0 && Integer.MAX_VALUE - delta < window) { - throw streamError(stream.id(), FLOW_CONTROL_ERROR, - "Window size overflow for stream: %d", stream.id()); - } - window += delta; - - streamByteDistributor.updateStreamableBytes(this); - return window; - } - - /** - * Returns the maximum writable window (minimum of the stream and connection windows). - */ - private int writableWindow() { - return min(window, connectionWindowSize()); - } - - @Override - public long pendingBytes() { - return pendingBytes; - } - - /** - * Adds the {@code frame} to the pending queue and increments the pending byte count. - */ - void enqueueFrame(FlowControlled frame) { - FlowControlled last = pendingWriteQueue.peekLast(); - if (last == null) { - enqueueFrameWithoutMerge(frame); - return; - } - - int lastSize = last.size(); - if (last.merge(ctx, frame)) { - incrementPendingBytes(last.size() - lastSize, true); - return; - } - enqueueFrameWithoutMerge(frame); - } - - private void enqueueFrameWithoutMerge(FlowControlled frame) { - pendingWriteQueue.offer(frame); - // This must be called after adding to the queue in order so that hasFrame() is - // updated before updating the stream state. - incrementPendingBytes(frame.size(), true); - } - - @Override - public boolean hasFrame() { - return !pendingWriteQueue.isEmpty(); - } - - /** - * Returns the head of the pending queue, or {@code null} if empty. - */ - private FlowControlled peek() { - return pendingWriteQueue.peek(); - } - - /** - * Clears the pending queue and writes errors for each remaining frame. - * @param error the {@link Http2Error} to use. - * @param cause the {@link Throwable} that caused this method to be invoked. - */ - void cancel(Http2Error error, Throwable cause) { - cancelled = true; - // Ensure that the queue can't be modified while we are writing. - if (writing) { - return; - } - - FlowControlled frame = pendingWriteQueue.poll(); - if (frame != null) { - // Only create exception once and reuse to reduce overhead of filling in the stacktrace. - final Http2Exception exception = streamError(stream.id(), error, cause, - "Stream closed before write could take place"); - do { - writeError(frame, exception); - frame = pendingWriteQueue.poll(); - } while (frame != null); - } - - streamByteDistributor.updateStreamableBytes(this); - - monitor.stateCancelled(this); - } - - /** - * Increments the number of pending bytes for this node and optionally updates the - * {@link StreamByteDistributor}. - */ - private void incrementPendingBytes(int numBytes, boolean updateStreamableBytes) { - pendingBytes += numBytes; - monitor.incrementPendingBytes(numBytes); - if (updateStreamableBytes) { - streamByteDistributor.updateStreamableBytes(this); - } - } - - /** - * If this frame is in the pending queue, decrements the number of pending bytes for the stream. - */ - private void decrementPendingBytes(int bytes, boolean updateStreamableBytes) { - incrementPendingBytes(-bytes, updateStreamableBytes); - } - - /** - * Decrement the per stream and connection flow control window by {@code bytes}. - */ - private void decrementFlowControlWindow(int bytes) { - try { - int negativeBytes = -bytes; - connectionState.incrementStreamWindow(negativeBytes); - incrementStreamWindow(negativeBytes); - } catch (Http2Exception e) { - // Should never get here since we're decrementing. - throw new IllegalStateException("Invalid window state when writing frame: " + e.getMessage(), e); - } - } - - /** - * Discards this {@link FlowControlled}, writing an error. If this frame is in the pending queue, - * the unwritten bytes are removed from this branch of the priority tree. - */ - private void writeError(FlowControlled frame, Http2Exception cause) { - assert ctx != null; - decrementPendingBytes(frame.size(), true); - frame.error(ctx, cause); - } - } - - /** - * Abstract class which provides common functionality for writability monitor implementations. - */ - private class WritabilityMonitor implements StreamByteDistributor.Writer { - private boolean inWritePendingBytes; - private long totalPendingBytes; - - @Override - public final void write(Http2Stream stream, int numBytes) { - state(stream).writeAllocatedBytes(numBytes); - } - - /** - * Called when the writability of the underlying channel changes. - * @throws Http2Exception If a write occurs and an exception happens in the write operation. - */ - void channelWritabilityChange() throws Http2Exception { } - - /** - * Called when the state is cancelled. - * @param state the state that was cancelled. - */ - void stateCancelled(FlowState state) { } - - /** - * Set the initial window size for {@code state}. - * @param state the state to change the initial window size for. - * @param initialWindowSize the size of the window in bytes. - */ - void windowSize(FlowState state, int initialWindowSize) { - state.windowSize(initialWindowSize); - } - - /** - * Increment the window size for a particular stream. - * @param state the state associated with the stream whose window is being incremented. - * @param delta The amount to increment by. - * @throws Http2Exception If this operation overflows the window for {@code state}. - */ - void incrementWindowSize(FlowState state, int delta) throws Http2Exception { - state.incrementStreamWindow(delta); - } - - /** - * Add a frame to be sent via flow control. - * @param state The state associated with the stream which the {@code frame} is associated with. - * @param frame the frame to enqueue. - * @throws Http2Exception If a writability error occurs. - */ - void enqueueFrame(FlowState state, FlowControlled frame) throws Http2Exception { - state.enqueueFrame(frame); - } - - /** - * Increment the total amount of pending bytes for all streams. When any stream's pending bytes changes - * method should be called. - * @param delta The amount to increment by. - */ - final void incrementPendingBytes(int delta) { - totalPendingBytes += delta; - - // Notification of writibilty change should be delayed until the end of the top level event. - // This is to ensure the flow controller is more consistent state before calling external listener methods. - } - - /** - * Determine if the stream associated with {@code state} is writable. - * @param state The state which is associated with the stream to test writability for. - * @return {@code true} if {@link FlowState#stream()} is writable. {@code false} otherwise. - */ - final boolean isWritable(FlowState state) { - return isWritableConnection() && state.isWritable(); - } - - final void writePendingBytes() throws Http2Exception { - // Reentry is not permitted during the byte distribution process. It may lead to undesirable distribution of - // bytes and even infinite loops. We protect against reentry and make sure each call has an opportunity to - // cause a distribution to occur. This may be useful for example if the channel's writability changes from - // Writable -> Not Writable (because we are writing) -> Writable (because the user flushed to make more room - // in the channel outbound buffer). - if (inWritePendingBytes) { - return; - } - inWritePendingBytes = true; - try { - int bytesToWrite = writableBytes(); - // Make sure we always write at least once, regardless if we have bytesToWrite or not. - // This ensures that zero-length frames will always be written. - for (;;) { - if (!streamByteDistributor.distribute(bytesToWrite, this) || - (bytesToWrite = writableBytes()) <= 0 || - !isChannelWritable0()) { - break; - } - } - } finally { - inWritePendingBytes = false; - } - } - - void initialWindowSize(int newWindowSize) throws Http2Exception { - checkPositiveOrZero(newWindowSize, "newWindowSize"); - - final int delta = newWindowSize - initialWindowSize; - initialWindowSize = newWindowSize; - connection.forEachActiveStream(stream -> { - state(stream).incrementStreamWindow(delta); - return true; - }); - - if (delta > 0 && isChannelWritable()) { - // The window size increased, send any pending frames for all streams. - writePendingBytes(); - } - } - - final boolean isWritableConnection() { - return connectionState.windowSize() - totalPendingBytes > 0 && isChannelWritable(); - } - } - - /** - * Writability of a {@code stream} is calculated using the following: - *

-     * Connection Window - Total Queued Bytes > 0 &&
-     * Stream Window - Bytes Queued for Stream > 0 &&
-     * isChannelWritable()
-     * 
- */ - private final class ListenerWritabilityMonitor extends WritabilityMonitor implements Http2StreamVisitor { - private final Listener listener; - - ListenerWritabilityMonitor(Listener listener) { - this.listener = listener; - } - - @Override - public boolean visit(Http2Stream stream) throws Http2Exception { - FlowState state = state(stream); - if (isWritable(state) != state.markedWritability()) { - notifyWritabilityChanged(state); - } - return true; - } - - @Override - void windowSize(FlowState state, int initialWindowSize) { - super.windowSize(state, initialWindowSize); - try { - checkStateWritability(state); - } catch (Http2Exception e) { - throw new RuntimeException("Caught unexpected exception from window", e); - } - } - - @Override - void incrementWindowSize(FlowState state, int delta) throws Http2Exception { - super.incrementWindowSize(state, delta); - checkStateWritability(state); - } - - @Override - void initialWindowSize(int newWindowSize) throws Http2Exception { - super.initialWindowSize(newWindowSize); - if (isWritableConnection()) { - // If the write operation does not occur we still need to check all streams because they - // may have transitioned from writable to not writable. - checkAllWritabilityChanged(); - } - } - - @Override - void enqueueFrame(FlowState state, FlowControlled frame) throws Http2Exception { - super.enqueueFrame(state, frame); - checkConnectionThenStreamWritabilityChanged(state); - } - - @Override - void stateCancelled(FlowState state) { - try { - checkConnectionThenStreamWritabilityChanged(state); - } catch (Http2Exception e) { - throw new RuntimeException("Caught unexpected exception from checkAllWritabilityChanged", e); - } - } - - @Override - void channelWritabilityChange() throws Http2Exception { - if (connectionState.markedWritability() != isChannelWritable()) { - checkAllWritabilityChanged(); - } - } - - private void checkStateWritability(FlowState state) throws Http2Exception { - if (isWritable(state) != state.markedWritability()) { - if (state == connectionState) { - checkAllWritabilityChanged(); - } else { - notifyWritabilityChanged(state); - } - } - } - - private void notifyWritabilityChanged(FlowState state) { - state.markedWritability(!state.markedWritability()); - try { - listener.writabilityChanged(state.stream); - } catch (Throwable cause) { - logger.error("Caught Throwable from listener.writabilityChanged", cause); - } - } - - private void checkConnectionThenStreamWritabilityChanged(FlowState state) throws Http2Exception { - // It is possible that the connection window and/or the individual stream writability could change. - if (isWritableConnection() != connectionState.markedWritability()) { - checkAllWritabilityChanged(); - } else if (isWritable(state) != state.markedWritability()) { - notifyWritabilityChanged(state); - } - } - - private void checkAllWritabilityChanged() throws Http2Exception { - // Make sure we mark that we have notified as a result of this change. - connectionState.markedWritability(isWritableConnection()); - connection.forEachActiveStream(this); - } - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ResetFrame.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ResetFrame.java deleted file mode 100644 index 9ebd5e057b..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ResetFrame.java +++ /dev/null @@ -1,85 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.UnstableApi; - -import static java.util.Objects.requireNonNull; - -/** - * The default {@link Http2ResetFrame} implementation. - */ -@UnstableApi -public final class DefaultHttp2ResetFrame extends AbstractHttp2StreamFrame implements Http2ResetFrame { - - private final long errorCode; - - /** - * Construct a reset message. - * - * @param error the non-{@code null} reason for reset - */ - public DefaultHttp2ResetFrame(Http2Error error) { - errorCode = requireNonNull(error, "error").code(); - } - - /** - * Construct a reset message. - * - * @param errorCode the reason for reset - */ - public DefaultHttp2ResetFrame(long errorCode) { - this.errorCode = errorCode; - } - - @Override - public DefaultHttp2ResetFrame stream(Http2FrameStream stream) { - super.stream(stream); - return this; - } - - @Override - public String name() { - return "RST_STREAM"; - } - - @Override - public long errorCode() { - return errorCode; - } - - @Override - public String toString() { - return StringUtil.simpleClassName(this) + "(stream=" + stream() + ", errorCode=" + errorCode + ')'; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof DefaultHttp2ResetFrame)) { - return false; - } - DefaultHttp2ResetFrame other = (DefaultHttp2ResetFrame) o; - return super.equals(o) && errorCode == other.errorCode; - } - - @Override - public int hashCode() { - int hash = super.hashCode(); - hash = hash * 31 + (int) (errorCode ^ errorCode >>> 32); - return hash; - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2SettingsAckFrame.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2SettingsAckFrame.java deleted file mode 100644 index ea1c9ba900..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2SettingsAckFrame.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.util.internal.StringUtil; - -/** - * The default {@link Http2SettingsAckFrame} implementation. - */ -final class DefaultHttp2SettingsAckFrame implements Http2SettingsAckFrame { - @Override - public String name() { - return "SETTINGS(ACK)"; - } - - @Override - public String toString() { - return StringUtil.simpleClassName(this); - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2SettingsFrame.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2SettingsFrame.java deleted file mode 100644 index 0064c8d4bb..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2SettingsFrame.java +++ /dev/null @@ -1,64 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.http2; - -import static java.util.Objects.requireNonNull; - -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.UnstableApi; - -/** - * The default {@link Http2SettingsFrame} implementation. - */ -@UnstableApi -public class DefaultHttp2SettingsFrame implements Http2SettingsFrame { - - private final Http2Settings settings; - - public DefaultHttp2SettingsFrame(Http2Settings settings) { - this.settings = requireNonNull(settings, "settings"); - } - - @Override - public Http2Settings settings() { - return settings; - } - - @Override - public String name() { - return "SETTINGS"; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof Http2SettingsFrame)) { - return false; - } - Http2SettingsFrame other = (Http2SettingsFrame) o; - return settings.equals(other.settings()); - } - - @Override - public int hashCode() { - return settings.hashCode(); - } - - @Override - public String toString() { - return StringUtil.simpleClassName(this) + "(settings=" + settings + ')'; - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2UnknownFrame.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2UnknownFrame.java deleted file mode 100644 index 66a3c6dc0c..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2UnknownFrame.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.DefaultByteBufHolder; -import io.netty.buffer.Unpooled; -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.UnstableApi; - -@UnstableApi -public final class DefaultHttp2UnknownFrame extends DefaultByteBufHolder implements Http2UnknownFrame { - private final byte frameType; - private final Http2Flags flags; - private Http2FrameStream stream; - - public DefaultHttp2UnknownFrame(byte frameType, Http2Flags flags) { - this(frameType, flags, Unpooled.EMPTY_BUFFER); - } - - public DefaultHttp2UnknownFrame(byte frameType, Http2Flags flags, ByteBuf data) { - super(data); - this.frameType = frameType; - this.flags = flags; - } - - @Override - public Http2FrameStream stream() { - return stream; - } - - @Override - public DefaultHttp2UnknownFrame stream(Http2FrameStream stream) { - this.stream = stream; - return this; - } - - @Override - public byte frameType() { - return frameType; - } - - @Override - public Http2Flags flags() { - return flags; - } - - @Override - public String name() { - return "UNKNOWN"; - } - - @Override - public DefaultHttp2UnknownFrame copy() { - return replace(content().copy()); - } - - @Override - public DefaultHttp2UnknownFrame duplicate() { - return replace(content().duplicate()); - } - - @Override - public DefaultHttp2UnknownFrame retainedDuplicate() { - return replace(content().retainedDuplicate()); - } - - @Override - public DefaultHttp2UnknownFrame replace(ByteBuf content) { - return new DefaultHttp2UnknownFrame(frameType, flags, content).stream(stream); - } - - @Override - public DefaultHttp2UnknownFrame retain() { - super.retain(); - return this; - } - - @Override - public DefaultHttp2UnknownFrame retain(int increment) { - super.retain(increment); - return this; - } - - @Override - public String toString() { - return StringUtil.simpleClassName(this) + "(frameType=" + frameType + ", stream=" + stream + - ", flags=" + flags + ", content=" + contentToString() + ')'; - } - - @Override - public DefaultHttp2UnknownFrame touch() { - super.touch(); - return this; - } - - @Override - public DefaultHttp2UnknownFrame touch(Object hint) { - super.touch(hint); - return this; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof DefaultHttp2UnknownFrame)) { - return false; - } - DefaultHttp2UnknownFrame other = (DefaultHttp2UnknownFrame) o; - Http2FrameStream otherStream = other.stream(); - return (stream == otherStream || otherStream != null && otherStream.equals(stream)) - && flags.equals(other.flags()) - && frameType == other.frameType() - && super.equals(other); - } - - @Override - public int hashCode() { - int hash = super.hashCode(); - hash = hash * 31 + frameType; - hash = hash * 31 + flags.hashCode(); - if (stream != null) { - hash = hash * 31 + stream.hashCode(); - } - - return hash; - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2WindowUpdateFrame.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2WindowUpdateFrame.java deleted file mode 100644 index 1c98dd471c..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2WindowUpdateFrame.java +++ /dev/null @@ -1,54 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.UnstableApi; - -/** - * The default {@link Http2WindowUpdateFrame} implementation. - */ -@UnstableApi -public class DefaultHttp2WindowUpdateFrame extends AbstractHttp2StreamFrame implements Http2WindowUpdateFrame { - - private final int windowUpdateIncrement; - - public DefaultHttp2WindowUpdateFrame(int windowUpdateIncrement) { - this.windowUpdateIncrement = windowUpdateIncrement; - } - - @Override - public DefaultHttp2WindowUpdateFrame stream(Http2FrameStream stream) { - super.stream(stream); - return this; - } - - @Override - public String name() { - return "WINDOW_UPDATE"; - } - - @Override - public int windowSizeIncrement() { - return windowUpdateIncrement; - } - - @Override - public String toString() { - return StringUtil.simpleClassName(this) + - "(stream=" + stream() + ", windowUpdateIncrement=" + windowUpdateIncrement + ')'; - } -} 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 deleted file mode 100644 index e38e30195a..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DelegatingDecompressorFrameListener.java +++ /dev/null @@ -1,429 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.ByteToMessageDecoder; -import io.netty.handler.codec.compression.Brotli; -import io.netty.handler.codec.compression.BrotliDecoder; -import io.netty.handler.codec.compression.ZlibCodecFactory; -import io.netty.handler.codec.compression.ZlibWrapper; -import io.netty.util.internal.UnstableApi; - -import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_ENCODING; -import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH; -import static io.netty.handler.codec.http.HttpHeaderValues.BR; -import static io.netty.handler.codec.http.HttpHeaderValues.DEFLATE; -import static io.netty.handler.codec.http.HttpHeaderValues.GZIP; -import static io.netty.handler.codec.http.HttpHeaderValues.IDENTITY; -import static io.netty.handler.codec.http.HttpHeaderValues.X_DEFLATE; -import static io.netty.handler.codec.http.HttpHeaderValues.X_GZIP; -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.checkPositiveOrZero; -import static java.util.Objects.requireNonNull; - -/** - * An HTTP2 frame listener that will decompress data frames according to the {@code content-encoding} header for each - * stream. The decompression provided by this class will be applied to the data for the entire stream. - */ -@UnstableApi -public class DelegatingDecompressorFrameListener extends Http2FrameListenerDecorator { - - private final Http2Connection connection; - private final boolean strict; - private boolean flowControllerInitialized; - private final Http2Connection.PropertyKey propertyKey; - - public DelegatingDecompressorFrameListener(Http2Connection connection, Http2FrameListener listener) { - this(connection, listener, true); - } - - public DelegatingDecompressorFrameListener(Http2Connection connection, Http2FrameListener listener, - boolean strict) { - super(listener); - this.connection = connection; - this.strict = strict; - - propertyKey = connection.newKey(); - connection.addListener(new Http2ConnectionAdapter() { - @Override - public void onStreamRemoved(Http2Stream stream) { - final Http2Decompressor decompressor = decompressor(stream); - if (decompressor != null) { - cleanup(decompressor); - } - } - }); - } - - @Override - public int onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding, boolean endOfStream) - throws Http2Exception { - final Http2Stream stream = connection.stream(streamId); - final Http2Decompressor decompressor = decompressor(stream); - if (decompressor == null) { - // The decompressor may be null if no compatible encoding type was found in this stream's headers - return listener.onDataRead(ctx, streamId, data, padding, endOfStream); - } - - final EmbeddedChannel channel = decompressor.decompressor(); - final int compressedBytes = data.readableBytes() + padding; - decompressor.incrementCompressedBytes(compressedBytes); - try { - // call retain here as it will call release after its written to the channel - channel.writeInbound(data.retain()); - ByteBuf buf = nextReadableBuf(channel); - if (buf == null && endOfStream && channel.finish()) { - buf = nextReadableBuf(channel); - } - if (buf == null) { - if (endOfStream) { - listener.onDataRead(ctx, streamId, Unpooled.EMPTY_BUFFER, padding, true); - } - // No new decompressed data was extracted from the compressed data. This means the application could - // not be provided with data and thus could not return how many bytes were processed. We will assume - // there is more data coming which will complete the decompression block. To allow for more data we - // return all bytes to the flow control window (so the peer can send more data). - decompressor.incrementDecompressedBytes(compressedBytes); - return compressedBytes; - } - try { - Http2LocalFlowController flowController = connection.local().flowController(); - decompressor.incrementDecompressedBytes(padding); - for (;;) { - ByteBuf nextBuf = nextReadableBuf(channel); - boolean decompressedEndOfStream = nextBuf == null && endOfStream; - if (decompressedEndOfStream && channel.finish()) { - nextBuf = nextReadableBuf(channel); - decompressedEndOfStream = nextBuf == null; - } - - decompressor.incrementDecompressedBytes(buf.readableBytes()); - // Immediately return the bytes back to the flow controller. ConsumedBytesConverter will convert - // from the decompressed amount which the user knows about to the compressed amount which flow - // control knows about. - flowController.consumeBytes(stream, - listener.onDataRead(ctx, streamId, buf, padding, decompressedEndOfStream)); - if (nextBuf == null) { - break; - } - - padding = 0; // Padding is only communicated once on the first iteration. - buf.release(); - buf = nextBuf; - } - // We consume bytes each time we call the listener to ensure if multiple frames are decompressed - // that the bytes are accounted for immediately. Otherwise the user may see an inconsistent state of - // flow control. - return 0; - } finally { - buf.release(); - } - } catch (Http2Exception e) { - throw e; - } catch (Throwable t) { - throw streamError(stream.id(), INTERNAL_ERROR, t, - "Decompressor error detected while delegating data read on streamId %d", stream.id()); - } - } - - @Override - public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding, - boolean endStream) throws Http2Exception { - initDecompressor(ctx, streamId, headers, endStream); - listener.onHeadersRead(ctx, streamId, headers, padding, endStream); - } - - @Override - public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int streamDependency, - short weight, boolean exclusive, int padding, boolean endStream) throws Http2Exception { - initDecompressor(ctx, streamId, headers, endStream); - listener.onHeadersRead(ctx, streamId, headers, streamDependency, weight, exclusive, padding, endStream); - } - - /** - * Returns a new {@link EmbeddedChannel} that decodes the HTTP2 message content encoded in the specified - * {@code contentEncoding}. - * - * @param contentEncoding the value of the {@code content-encoding} header - * @return a new {@link ByteToMessageDecoder} if the specified encoding is supported. {@code null} otherwise - * (alternatively, you can throw a {@link Http2Exception} to block unknown encoding). - * @throws Http2Exception If the specified encoding is not not supported and warrants an exception - */ - protected EmbeddedChannel newContentDecompressor(final ChannelHandlerContext ctx, CharSequence contentEncoding) - throws Http2Exception { - if (GZIP.contentEqualsIgnoreCase(contentEncoding) || X_GZIP.contentEqualsIgnoreCase(contentEncoding)) { - return new EmbeddedChannel(ctx.channel().id(), ctx.channel().metadata().hasDisconnect(), - ctx.channel().config(), ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP)); - } - if (DEFLATE.contentEqualsIgnoreCase(contentEncoding) || X_DEFLATE.contentEqualsIgnoreCase(contentEncoding)) { - final ZlibWrapper wrapper = strict ? ZlibWrapper.ZLIB : ZlibWrapper.ZLIB_OR_NONE; - // To be strict, 'deflate' means ZLIB, but some servers were not implemented correctly. - return new EmbeddedChannel(ctx.channel().id(), ctx.channel().metadata().hasDisconnect(), - ctx.channel().config(), ZlibCodecFactory.newZlibDecoder(wrapper)); - } - if (Brotli.isAvailable() && BR.contentEqualsIgnoreCase(contentEncoding)) { - return new EmbeddedChannel(ctx.channel().id(), ctx.channel().metadata().hasDisconnect(), - ctx.channel().config(), new BrotliDecoder()); - } - // 'identity' or unsupported - return null; - } - - /** - * Returns the expected content encoding of the decoded content. This getMethod returns {@code "identity"} by - * default, which is the case for most decompressors. - * - * @param contentEncoding the value of the {@code content-encoding} header - * @return the expected content encoding of the new content. - * @throws Http2Exception if the {@code contentEncoding} is not supported and warrants an exception - */ - protected CharSequence getTargetContentEncoding(@SuppressWarnings("UnusedParameters") CharSequence contentEncoding) - throws Http2Exception { - return IDENTITY; - } - - /** - * Checks if a new decompressor object is needed for the stream identified by {@code streamId}. - * This method will modify the {@code content-encoding} header contained in {@code headers}. - * - * @param ctx The context - * @param streamId The identifier for the headers inside {@code headers} - * @param headers Object representing headers which have been read - * @param endOfStream Indicates if the stream has ended - * @throws Http2Exception If the {@code content-encoding} is not supported - */ - private void initDecompressor(ChannelHandlerContext ctx, int streamId, Http2Headers headers, boolean endOfStream) - throws Http2Exception { - final Http2Stream stream = connection.stream(streamId); - if (stream == null) { - return; - } - - Http2Decompressor decompressor = decompressor(stream); - if (decompressor == null && !endOfStream) { - // Determine the content encoding. - CharSequence contentEncoding = headers.get(CONTENT_ENCODING); - if (contentEncoding == null) { - contentEncoding = IDENTITY; - } - final EmbeddedChannel channel = newContentDecompressor(ctx, contentEncoding); - if (channel != null) { - decompressor = new Http2Decompressor(channel); - stream.setProperty(propertyKey, decompressor); - // Decode the content and remove or replace the existing headers - // so that the message looks like a decoded message. - CharSequence targetContentEncoding = getTargetContentEncoding(contentEncoding); - if (IDENTITY.contentEqualsIgnoreCase(targetContentEncoding)) { - headers.remove(CONTENT_ENCODING); - } else { - headers.set(CONTENT_ENCODING, targetContentEncoding); - } - } - } - - if (decompressor != null) { - // The content length will be for the compressed data. Since we will decompress the data - // this content-length will not be correct. Instead of queuing messages or delaying sending - // header frames just remove the content-length header. - headers.remove(CONTENT_LENGTH); - - // The first time that we initialize a decompressor, decorate the local flow controller to - // properly convert consumed bytes. - if (!flowControllerInitialized) { - flowControllerInitialized = true; - connection.local().flowController(new ConsumedBytesConverter(connection.local().flowController())); - } - } - } - - Http2Decompressor decompressor(Http2Stream stream) { - return stream == null ? null : (Http2Decompressor) stream.getProperty(propertyKey); - } - - /** - * Release remaining content from the {@link EmbeddedChannel}. - * - * @param decompressor The decompressor for {@code stream} - */ - private static void cleanup(Http2Decompressor decompressor) { - decompressor.decompressor().finishAndReleaseAll(); - } - - /** - * Read the next decompressed {@link ByteBuf} from the {@link EmbeddedChannel} - * or {@code null} if one does not exist. - * - * @param decompressor The channel to read from - * @return The next decoded {@link ByteBuf} from the {@link EmbeddedChannel} or {@code null} if one does not exist - */ - private static ByteBuf nextReadableBuf(EmbeddedChannel decompressor) { - for (;;) { - final ByteBuf buf = decompressor.readInbound(); - if (buf == null) { - return null; - } - if (!buf.isReadable()) { - buf.release(); - continue; - } - return buf; - } - } - - /** - * A decorator around the local flow controller that converts consumed bytes from uncompressed to compressed. - */ - private final class ConsumedBytesConverter implements Http2LocalFlowController { - private final Http2LocalFlowController flowController; - - ConsumedBytesConverter(Http2LocalFlowController flowController) { - this.flowController = requireNonNull(flowController, "flowController"); - } - - @Override - public Http2LocalFlowController frameWriter(Http2FrameWriter frameWriter) { - return flowController.frameWriter(frameWriter); - } - - @Override - public void channelHandlerContext(ChannelHandlerContext ctx) throws Http2Exception { - flowController.channelHandlerContext(ctx); - } - - @Override - public void initialWindowSize(int newWindowSize) throws Http2Exception { - flowController.initialWindowSize(newWindowSize); - } - - @Override - public int initialWindowSize() { - return flowController.initialWindowSize(); - } - - @Override - public int windowSize(Http2Stream stream) { - return flowController.windowSize(stream); - } - - @Override - public void incrementWindowSize(Http2Stream stream, int delta) throws Http2Exception { - flowController.incrementWindowSize(stream, delta); - } - - @Override - public void receiveFlowControlledFrame(Http2Stream stream, ByteBuf data, int padding, - boolean endOfStream) throws Http2Exception { - flowController.receiveFlowControlledFrame(stream, data, padding, endOfStream); - } - - @Override - public boolean consumeBytes(Http2Stream stream, int numBytes) throws Http2Exception { - Http2Decompressor decompressor = decompressor(stream); - if (decompressor != null) { - // Convert the decompressed bytes to compressed (on the wire) bytes. - numBytes = decompressor.consumeBytes(stream.id(), numBytes); - } - try { - return flowController.consumeBytes(stream, numBytes); - } catch (Http2Exception e) { - throw e; - } catch (Throwable t) { - // The stream should be closed at this point. We have already changed our state tracking the compressed - // bytes, and there is no guarantee we can recover if the underlying flow controller throws. - throw streamError(stream.id(), INTERNAL_ERROR, t, "Error while returning bytes to flow control window"); - } - } - - @Override - public int unconsumedBytes(Http2Stream stream) { - return flowController.unconsumedBytes(stream); - } - - @Override - public int initialWindowSize(Http2Stream stream) { - return flowController.initialWindowSize(stream); - } - } - - /** - * Provides the state for stream {@code DATA} frame decompression. - */ - private static final class Http2Decompressor { - private final EmbeddedChannel decompressor; - private int compressed; - private int decompressed; - - Http2Decompressor(EmbeddedChannel decompressor) { - this.decompressor = decompressor; - } - - /** - * Responsible for taking compressed bytes in and producing decompressed bytes. - */ - EmbeddedChannel decompressor() { - return decompressor; - } - - /** - * Increment the number of bytes received prior to doing any decompression. - */ - void incrementCompressedBytes(int delta) { - assert delta >= 0; - compressed += delta; - } - - /** - * Increment the number of bytes after the decompression process. - */ - void incrementDecompressedBytes(int delta) { - assert delta >= 0; - decompressed += delta; - } - - /** - * Determines the ratio between {@code numBytes} and {@link Http2Decompressor#decompressed}. - * This ratio is used to decrement {@link Http2Decompressor#decompressed} and - * {@link Http2Decompressor#compressed}. - * @param streamId the stream ID - * @param decompressedBytes The number of post-decompressed bytes to return to flow control - * @return The number of pre-decompressed bytes that have been consumed. - */ - int consumeBytes(int streamId, int decompressedBytes) throws Http2Exception { - checkPositiveOrZero(decompressedBytes, "decompressedBytes"); - if (decompressed - decompressedBytes < 0) { - throw streamError(streamId, INTERNAL_ERROR, - "Attempting to return too many bytes for stream %d. decompressed: %d " + - "decompressedBytes: %d", streamId, decompressed, decompressedBytes); - } - double consumedRatio = decompressedBytes / (double) decompressed; - int consumedCompressed = Math.min(compressed, (int) Math.ceil(compressed * consumedRatio)); - if (compressed - consumedCompressed < 0) { - throw streamError(streamId, INTERNAL_ERROR, - "overflow when converting decompressed bytes to compressed bytes for stream %d." + - "decompressedBytes: %d decompressed: %d compressed: %d consumedCompressed: %d", - streamId, decompressedBytes, decompressed, compressed, consumedCompressed); - } - decompressed -= decompressedBytes; - compressed -= consumedCompressed; - - return consumedCompressed; - } - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/EmptyHttp2Headers.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/EmptyHttp2Headers.java deleted file mode 100644 index f096337de2..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/EmptyHttp2Headers.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.handler.codec.EmptyHeaders; -import io.netty.util.internal.UnstableApi; - -@UnstableApi -public final class EmptyHttp2Headers - extends EmptyHeaders implements Http2Headers { - public static final EmptyHttp2Headers INSTANCE = new EmptyHttp2Headers(); - - private EmptyHttp2Headers() { - } - - @Override - public EmptyHttp2Headers method(CharSequence method) { - throw new UnsupportedOperationException(); - } - - @Override - public EmptyHttp2Headers scheme(CharSequence status) { - throw new UnsupportedOperationException(); - } - - @Override - public EmptyHttp2Headers authority(CharSequence authority) { - throw new UnsupportedOperationException(); - } - - @Override - public EmptyHttp2Headers path(CharSequence path) { - throw new UnsupportedOperationException(); - } - - @Override - public EmptyHttp2Headers status(CharSequence status) { - throw new UnsupportedOperationException(); - } - - @Override - public CharSequence method() { - return get(PseudoHeaderName.METHOD.value()); - } - - @Override - public CharSequence scheme() { - return get(PseudoHeaderName.SCHEME.value()); - } - - @Override - public CharSequence authority() { - return get(PseudoHeaderName.AUTHORITY.value()); - } - - @Override - public CharSequence path() { - return get(PseudoHeaderName.PATH.value()); - } - - @Override - public CharSequence status() { - return get(PseudoHeaderName.STATUS.value()); - } - - @Override - public boolean contains(CharSequence name, CharSequence value, boolean caseInsensitive) { - return false; - } -} 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 deleted file mode 100644 index 497ee0d6fe..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/HpackDecoder.java +++ /dev/null @@ -1,573 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/* - * Copyright 2014 Twitter, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.http2.HpackUtil.IndexType; -import io.netty.util.AsciiString; - -import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_HEADER_TABLE_SIZE; -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.handler.codec.http2.Http2CodecUtil.MIN_HEADER_LIST_SIZE; -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.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; -import static io.netty.util.internal.ObjectUtil.checkPositive; - -final class HpackDecoder { - private static final Http2Exception DECODE_ULE_128_DECOMPRESSION_EXCEPTION = - Http2Exception.newStatic(COMPRESSION_ERROR, "HPACK - decompression failure", - Http2Exception.ShutdownHint.HARD_SHUTDOWN, HpackDecoder.class, - "decodeULE128(..)"); - private static final Http2Exception DECODE_ULE_128_TO_LONG_DECOMPRESSION_EXCEPTION = - Http2Exception.newStatic(COMPRESSION_ERROR, "HPACK - long overflow", - Http2Exception.ShutdownHint.HARD_SHUTDOWN, HpackDecoder.class, "decodeULE128(..)"); - private static final Http2Exception DECODE_ULE_128_TO_INT_DECOMPRESSION_EXCEPTION = - Http2Exception.newStatic(COMPRESSION_ERROR, "HPACK - int overflow", - Http2Exception.ShutdownHint.HARD_SHUTDOWN, HpackDecoder.class, "decodeULE128ToInt(..)"); - private static final Http2Exception DECODE_ILLEGAL_INDEX_VALUE = - Http2Exception.newStatic(COMPRESSION_ERROR, "HPACK - illegal index value", - Http2Exception.ShutdownHint.HARD_SHUTDOWN, HpackDecoder.class, "decode(..)"); - private static final Http2Exception INDEX_HEADER_ILLEGAL_INDEX_VALUE = - Http2Exception.newStatic(COMPRESSION_ERROR, "HPACK - illegal index value", - Http2Exception.ShutdownHint.HARD_SHUTDOWN, HpackDecoder.class, "indexHeader(..)"); - private static final Http2Exception READ_NAME_ILLEGAL_INDEX_VALUE = - Http2Exception.newStatic(COMPRESSION_ERROR, "HPACK - illegal index value", - Http2Exception.ShutdownHint.HARD_SHUTDOWN, HpackDecoder.class, "readName(..)"); - private static final Http2Exception INVALID_MAX_DYNAMIC_TABLE_SIZE = - Http2Exception.newStatic(COMPRESSION_ERROR, "HPACK - invalid max dynamic table size", - Http2Exception.ShutdownHint.HARD_SHUTDOWN, HpackDecoder.class, - "setDynamicTableSize(..)"); - private static final Http2Exception MAX_DYNAMIC_TABLE_SIZE_CHANGE_REQUIRED = - Http2Exception.newStatic(COMPRESSION_ERROR, "HPACK - max dynamic table size change required", - Http2Exception.ShutdownHint.HARD_SHUTDOWN, HpackDecoder.class, "decode(..)"); - private static final byte READ_HEADER_REPRESENTATION = 0; - private static final byte READ_MAX_DYNAMIC_TABLE_SIZE = 1; - private static final byte READ_INDEXED_HEADER = 2; - private static final byte READ_INDEXED_HEADER_NAME = 3; - private static final byte READ_LITERAL_HEADER_NAME_LENGTH_PREFIX = 4; - private static final byte READ_LITERAL_HEADER_NAME_LENGTH = 5; - private static final byte READ_LITERAL_HEADER_NAME = 6; - private static final byte READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX = 7; - private static final byte READ_LITERAL_HEADER_VALUE_LENGTH = 8; - private static final byte READ_LITERAL_HEADER_VALUE = 9; - - private final HpackHuffmanDecoder huffmanDecoder = new HpackHuffmanDecoder(); - private final HpackDynamicTable hpackDynamicTable; - private long maxHeaderListSize; - private long maxDynamicTableSize; - private long encoderMaxDynamicTableSize; - private boolean maxDynamicTableSizeChangeRequired; - - /** - * Create a new instance. - * @param maxHeaderListSize This is the only setting that can be configured before notifying the peer. - * This is because SETTINGS_MAX_HEADER_LIST_SIZE - * allows a lower than advertised limit from being enforced, and the default limit is unlimited - * (which is dangerous). - */ - HpackDecoder(long maxHeaderListSize) { - this(maxHeaderListSize, DEFAULT_HEADER_TABLE_SIZE); - } - - /** - * Exposed Used for testing only! Default values used in the initial settings frame are overridden intentionally - * for testing but violate the RFC if used outside the scope of testing. - */ - HpackDecoder(long maxHeaderListSize, int maxHeaderTableSize) { - this.maxHeaderListSize = checkPositive(maxHeaderListSize, "maxHeaderListSize"); - - maxDynamicTableSize = encoderMaxDynamicTableSize = maxHeaderTableSize; - maxDynamicTableSizeChangeRequired = false; - hpackDynamicTable = new HpackDynamicTable(maxHeaderTableSize); - } - - /** - * Decode the header block into header fields. - *

- * 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(streamId, headers, maxHeaderListSize, validateHeaders); - decode(in, sink); - - // 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) throws Http2Exception { - int index = 0; - int nameLength = 0; - int valueLength = 0; - byte state = READ_HEADER_REPRESENTATION; - boolean huffmanEncoded = false; - CharSequence name = null; - IndexType indexType = IndexType.NONE; - while (in.isReadable()) { - switch (state) { - case READ_HEADER_REPRESENTATION: - byte b = in.readByte(); - if (maxDynamicTableSizeChangeRequired && (b & 0xE0) != 0x20) { - // HpackEncoder MUST signal maximum dynamic table size change - throw MAX_DYNAMIC_TABLE_SIZE_CHANGE_REQUIRED; - } - if (b < 0) { - // Indexed Header Field - index = b & 0x7F; - switch (index) { - case 0: - throw DECODE_ILLEGAL_INDEX_VALUE; - case 0x7F: - state = READ_INDEXED_HEADER; - break; - default: - HpackHeaderField indexedHeader = getIndexedHeader(index); - sink.appendToHeaderList(indexedHeader.name, indexedHeader.value); - } - } else if ((b & 0x40) == 0x40) { - // Literal Header Field with Incremental Indexing - indexType = IndexType.INCREMENTAL; - index = b & 0x3F; - switch (index) { - case 0: - state = READ_LITERAL_HEADER_NAME_LENGTH_PREFIX; - break; - case 0x3F: - state = READ_INDEXED_HEADER_NAME; - break; - default: - // Index was stored as the prefix - name = readName(index); - nameLength = name.length(); - state = READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX; - } - } else if ((b & 0x20) == 0x20) { - // Dynamic Table Size Update - index = b & 0x1F; - if (index == 0x1F) { - state = READ_MAX_DYNAMIC_TABLE_SIZE; - } else { - setDynamicTableSize(index); - state = READ_HEADER_REPRESENTATION; - } - } else { - // Literal Header Field without Indexing / never Indexed - indexType = (b & 0x10) == 0x10 ? IndexType.NEVER : IndexType.NONE; - index = b & 0x0F; - switch (index) { - case 0: - state = READ_LITERAL_HEADER_NAME_LENGTH_PREFIX; - break; - case 0x0F: - state = READ_INDEXED_HEADER_NAME; - break; - default: - // Index was stored as the prefix - name = readName(index); - nameLength = name.length(); - state = READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX; - } - } - break; - - case READ_MAX_DYNAMIC_TABLE_SIZE: - setDynamicTableSize(decodeULE128(in, (long) index)); - state = READ_HEADER_REPRESENTATION; - break; - - case READ_INDEXED_HEADER: - HpackHeaderField indexedHeader = getIndexedHeader(decodeULE128(in, index)); - sink.appendToHeaderList(indexedHeader.name, indexedHeader.value); - state = READ_HEADER_REPRESENTATION; - break; - - case READ_INDEXED_HEADER_NAME: - // Header Name matches an entry in the Header Table - name = readName(decodeULE128(in, index)); - nameLength = name.length(); - state = READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX; - break; - - case READ_LITERAL_HEADER_NAME_LENGTH_PREFIX: - b = in.readByte(); - huffmanEncoded = (b & 0x80) == 0x80; - index = b & 0x7F; - if (index == 0x7f) { - state = READ_LITERAL_HEADER_NAME_LENGTH; - } else { - nameLength = index; - state = READ_LITERAL_HEADER_NAME; - } - break; - - case READ_LITERAL_HEADER_NAME_LENGTH: - // Header Name is a Literal String - nameLength = decodeULE128(in, index); - - state = READ_LITERAL_HEADER_NAME; - break; - - case READ_LITERAL_HEADER_NAME: - // Wait until entire name is readable - if (in.readableBytes() < nameLength) { - throw notEnoughDataException(in); - } - - name = readStringLiteral(in, nameLength, huffmanEncoded); - - state = READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX; - break; - - case READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX: - b = in.readByte(); - huffmanEncoded = (b & 0x80) == 0x80; - index = b & 0x7F; - switch (index) { - case 0x7f: - state = READ_LITERAL_HEADER_VALUE_LENGTH; - break; - case 0: - insertHeader(sink, name, EMPTY_STRING, indexType); - state = READ_HEADER_REPRESENTATION; - break; - default: - valueLength = index; - state = READ_LITERAL_HEADER_VALUE; - } - - break; - - case READ_LITERAL_HEADER_VALUE_LENGTH: - // Header Value is a Literal String - valueLength = decodeULE128(in, index); - - state = READ_LITERAL_HEADER_VALUE; - break; - - case READ_LITERAL_HEADER_VALUE: - // Wait until entire value is readable - if (in.readableBytes() < valueLength) { - throw notEnoughDataException(in); - } - - CharSequence value = readStringLiteral(in, valueLength, huffmanEncoded); - insertHeader(sink, name, value, indexType); - state = READ_HEADER_REPRESENTATION; - break; - - default: - throw new Error("should not reach here state: " + state); - } - } - - if (state != READ_HEADER_REPRESENTATION) { - throw connectionError(COMPRESSION_ERROR, "Incomplete header block fragment."); - } - } - - /** - * Set the maximum table size. If this is below the maximum size of the dynamic table used by - * the encoder, the beginning of the next header block MUST signal this change. - */ - public void setMaxHeaderTableSize(long maxHeaderTableSize) throws Http2Exception { - if (maxHeaderTableSize < MIN_HEADER_TABLE_SIZE || maxHeaderTableSize > MAX_HEADER_TABLE_SIZE) { - throw connectionError(PROTOCOL_ERROR, "Header Table Size must be >= %d and <= %d but was %d", - MIN_HEADER_TABLE_SIZE, MAX_HEADER_TABLE_SIZE, maxHeaderTableSize); - } - maxDynamicTableSize = maxHeaderTableSize; - if (maxDynamicTableSize < encoderMaxDynamicTableSize) { - // decoder requires less space than encoder - // encoder MUST signal this change - maxDynamicTableSizeChangeRequired = true; - hpackDynamicTable.setCapacity(maxDynamicTableSize); - } - } - - /** - * @deprecated use {@link #setMaxHeaderListSize(long)}; {@code maxHeaderListSizeGoAway} is - * ignored - */ - @Deprecated - public void setMaxHeaderListSize(long maxHeaderListSize, long maxHeaderListSizeGoAway) throws Http2Exception { - 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; - } - - public long getMaxHeaderListSize() { - return maxHeaderListSize; - } - - /** - * Return the maximum table size. This is the maximum size allowed by both the encoder and the - * decoder. - */ - public long getMaxHeaderTableSize() { - return hpackDynamicTable.capacity(); - } - - /** - * Return the number of header fields in the dynamic table. Exposed for testing. - */ - int length() { - return hpackDynamicTable.length(); - } - - /** - * Return the size of the dynamic table. Exposed for testing. - */ - long size() { - return hpackDynamicTable.size(); - } - - /** - * Return the header field at the given index. Exposed for testing. - */ - HpackHeaderField getHeaderField(int index) { - return hpackDynamicTable.getEntry(index + 1); - } - - private void setDynamicTableSize(long dynamicTableSize) throws Http2Exception { - if (dynamicTableSize > maxDynamicTableSize) { - throw INVALID_MAX_DYNAMIC_TABLE_SIZE; - } - encoderMaxDynamicTableSize = dynamicTableSize; - maxDynamicTableSizeChangeRequired = false; - hpackDynamicTable.setCapacity(dynamicTableSize); - } - - private static HeaderType validate(int streamId, CharSequence name, - HeaderType previousHeaderType) throws Http2Exception { - if (hasPseudoHeaderFormat(name)) { - if (previousHeaderType == HeaderType.REGULAR_HEADER) { - throw streamError(streamId, PROTOCOL_ERROR, - "Pseudo-header field '%s' found after regular header.", name); - } - - final Http2Headers.PseudoHeaderName pseudoHeader = getPseudoHeader(name); - if (pseudoHeader == null) { - 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 streamError(streamId, PROTOCOL_ERROR, "Mix of request and response pseudo-headers."); - } - - return currentHeaderType; - } - - return HeaderType.REGULAR_HEADER; - } - - private CharSequence readName(int index) throws Http2Exception { - if (index <= HpackStaticTable.length) { - HpackHeaderField hpackHeaderField = HpackStaticTable.getEntry(index); - return hpackHeaderField.name; - } - if (index - HpackStaticTable.length <= hpackDynamicTable.length()) { - HpackHeaderField hpackHeaderField = hpackDynamicTable.getEntry(index - HpackStaticTable.length); - return hpackHeaderField.name; - } - throw READ_NAME_ILLEGAL_INDEX_VALUE; - } - - private HpackHeaderField getIndexedHeader(int index) throws Http2Exception { - if (index <= HpackStaticTable.length) { - return HpackStaticTable.getEntry(index); - } - if (index - HpackStaticTable.length <= hpackDynamicTable.length()) { - return hpackDynamicTable.getEntry(index - HpackStaticTable.length); - } - throw INDEX_HEADER_ILLEGAL_INDEX_VALUE; - } - - private void insertHeader(Sink sink, CharSequence name, CharSequence value, IndexType indexType) { - sink.appendToHeaderList(name, value); - - switch (indexType) { - case NONE: - case NEVER: - break; - - case INCREMENTAL: - hpackDynamicTable.add(new HpackHeaderField(name, value)); - break; - - default: - throw new Error("should not reach here"); - } - } - - private CharSequence readStringLiteral(ByteBuf in, int length, boolean huffmanEncoded) throws Http2Exception { - if (huffmanEncoded) { - return huffmanDecoder.decode(in, length); - } - byte[] buf = new byte[length]; - in.readBytes(buf); - return new AsciiString(buf, false); - } - - private static IllegalArgumentException notEnoughDataException(ByteBuf in) { - return new IllegalArgumentException("decode only works with an entire header block! " + in); - } - - /** - * Unsigned Little Endian Base 128 Variable-Length Integer Encoding - *

- * Visible for testing only! - */ - static int decodeULE128(ByteBuf in, int result) throws Http2Exception { - final int readerIndex = in.readerIndex(); - final long v = decodeULE128(in, (long) result); - if (v > Integer.MAX_VALUE) { - // the maximum value that can be represented by a signed 32 bit number is: - // [0x1,0x7f] + 0x7f + (0x7f << 7) + (0x7f << 14) + (0x7f << 21) + (0x6 << 28) - // OR - // 0x0 + 0x7f + (0x7f << 7) + (0x7f << 14) + (0x7f << 21) + (0x7 << 28) - // we should reset the readerIndex if we overflowed the int type. - in.readerIndex(readerIndex); - throw DECODE_ULE_128_TO_INT_DECOMPRESSION_EXCEPTION; - } - return (int) v; - } - - /** - * Unsigned Little Endian Base 128 Variable-Length Integer Encoding - *

- * Visible for testing only! - */ - static long decodeULE128(ByteBuf in, long result) throws Http2Exception { - assert result <= 0x7f && result >= 0; - final boolean resultStartedAtZero = result == 0; - final int writerIndex = in.writerIndex(); - for (int readerIndex = in.readerIndex(), shift = 0; readerIndex < writerIndex; ++readerIndex, shift += 7) { - byte b = in.getByte(readerIndex); - if (shift == 56 && ((b & 0x80) != 0 || b == 0x7F && !resultStartedAtZero)) { - // the maximum value that can be represented by a signed 64 bit number is: - // [0x01L, 0x7fL] + 0x7fL + (0x7fL << 7) + (0x7fL << 14) + (0x7fL << 21) + (0x7fL << 28) + (0x7fL << 35) - // + (0x7fL << 42) + (0x7fL << 49) + (0x7eL << 56) - // OR - // 0x0L + 0x7fL + (0x7fL << 7) + (0x7fL << 14) + (0x7fL << 21) + (0x7fL << 28) + (0x7fL << 35) + - // (0x7fL << 42) + (0x7fL << 49) + (0x7fL << 56) - // this means any more shifts will result in overflow so we should break out and throw an error. - throw DECODE_ULE_128_TO_LONG_DECOMPRESSION_EXCEPTION; - } - - if ((b & 0x80) == 0) { - in.readerIndex(readerIndex + 1); - return result + ((b & 0x7FL) << shift); - } - result += (b & 0x7FL) << shift; - } - - throw DECODE_ULE_128_DECOMPRESSION_EXCEPTION; - } - - /** - * HTTP/2 header types. - */ - private enum HeaderType { - REGULAR_HEADER, - REQUEST_PSEUDO_HEADER, - RESPONSE_PSEUDO_HEADER - } - - 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; - - 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); - exceededMaxLength |= headersLength > maxHeaderListSize; - - if (exceededMaxLength || validationException != null) { - // We don't store the header since we've already failed validation requirements. - return; - } - - if (validate) { - try { - previousType = validate(streamId, name, previousType); - } catch (Http2Exception ex) { - validationException = ex; - return; - } - } - - headers.add(name, value); - } - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/HpackDynamicTable.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/HpackDynamicTable.java deleted file mode 100644 index 4a712eed25..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/HpackDynamicTable.java +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/* - * Copyright 2014 Twitter, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.netty.handler.codec.http2; - -import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_HEADER_TABLE_SIZE; -import static io.netty.handler.codec.http2.Http2CodecUtil.MIN_HEADER_TABLE_SIZE; - -final class HpackDynamicTable { - - // a circular queue of header fields - HpackHeaderField[] hpackHeaderFields; - int head; - int tail; - private long size; - private long capacity = -1; // ensure setCapacity creates the array - - /** - * Creates a new dynamic table with the specified initial capacity. - */ - HpackDynamicTable(long initialCapacity) { - setCapacity(initialCapacity); - } - - /** - * Return the number of header fields in the dynamic table. - */ - public int length() { - int length; - if (head < tail) { - length = hpackHeaderFields.length - tail + head; - } else { - length = head - tail; - } - return length; - } - - /** - * Return the current size of the dynamic table. This is the sum of the size of the entries. - */ - public long size() { - return size; - } - - /** - * Return the maximum allowable size of the dynamic table. - */ - public long capacity() { - return capacity; - } - - /** - * Return the header field at the given index. The first and newest entry is always at index 1, - * and the oldest entry is at the index length(). - */ - public HpackHeaderField getEntry(int index) { - if (index <= 0 || index > length()) { - throw new IndexOutOfBoundsException("Index " + index + " out of bounds for length " + length()); - } - int i = head - index; - if (i < 0) { - return hpackHeaderFields[i + hpackHeaderFields.length]; - } else { - return hpackHeaderFields[i]; - } - } - - /** - * Add the header field to the dynamic table. Entries are evicted from the dynamic table until - * the size of the table and the new header field is less than or equal to the table's capacity. - * If the size of the new entry is larger than the table's capacity, the dynamic table will be - * cleared. - */ - public void add(HpackHeaderField header) { - int headerSize = header.size(); - if (headerSize > capacity) { - clear(); - return; - } - while (capacity - size < headerSize) { - remove(); - } - hpackHeaderFields[head++] = header; - size += headerSize; - if (head == hpackHeaderFields.length) { - head = 0; - } - } - - /** - * Remove and return the oldest header field from the dynamic table. - */ - public HpackHeaderField remove() { - HpackHeaderField removed = hpackHeaderFields[tail]; - if (removed == null) { - return null; - } - size -= removed.size(); - hpackHeaderFields[tail++] = null; - if (tail == hpackHeaderFields.length) { - tail = 0; - } - return removed; - } - - /** - * Remove all entries from the dynamic table. - */ - public void clear() { - while (tail != head) { - hpackHeaderFields[tail++] = null; - if (tail == hpackHeaderFields.length) { - tail = 0; - } - } - head = 0; - tail = 0; - size = 0; - } - - /** - * Set the maximum size of the dynamic table. Entries are evicted from the dynamic table until - * the size of the table is less than or equal to the maximum size. - */ - public void setCapacity(long capacity) { - if (capacity < MIN_HEADER_TABLE_SIZE || capacity > MAX_HEADER_TABLE_SIZE) { - throw new IllegalArgumentException("capacity is invalid: " + capacity); - } - // initially capacity will be -1 so init won't return here - if (this.capacity == capacity) { - return; - } - this.capacity = capacity; - - if (capacity == 0) { - clear(); - } else { - // initially size will be 0 so remove won't be called - while (size > capacity) { - remove(); - } - } - - int maxEntries = (int) (capacity / HpackHeaderField.HEADER_ENTRY_OVERHEAD); - if (capacity % HpackHeaderField.HEADER_ENTRY_OVERHEAD != 0) { - maxEntries++; - } - - // check if capacity change requires us to reallocate the array - if (hpackHeaderFields != null && hpackHeaderFields.length == maxEntries) { - return; - } - - HpackHeaderField[] tmp = new HpackHeaderField[maxEntries]; - - // initially length will be 0 so there will be no copy - int len = length(); - if (hpackHeaderFields != null) { - int cursor = tail; - for (int i = 0; i < len; i++) { - HpackHeaderField entry = hpackHeaderFields[cursor++]; - tmp[i] = entry; - if (cursor == hpackHeaderFields.length) { - cursor = 0; - } - } - } - - tail = 0; - head = tail + len; - hpackHeaderFields = tmp; - } -} 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 deleted file mode 100644 index 6df151b1e2..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/HpackEncoder.java +++ /dev/null @@ -1,521 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/* - * Copyright 2014 Twitter, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.http2.HpackUtil.IndexType; -import io.netty.handler.codec.http2.Http2HeadersEncoder.SensitivityDetector; -import io.netty.util.AsciiString; -import io.netty.util.CharsetUtil; - -import java.util.Arrays; -import java.util.Map; - -import static io.netty.handler.codec.http2.HpackUtil.equalsConstantTime; -import static io.netty.handler.codec.http2.HpackUtil.equalsVariableTime; -import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_HEADER_TABLE_SIZE; -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.handler.codec.http2.Http2CodecUtil.MIN_HEADER_LIST_SIZE; -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.PROTOCOL_ERROR; -import static io.netty.handler.codec.http2.Http2Exception.connectionError; -import static io.netty.util.internal.MathUtil.findNextPositivePowerOfTwo; -import static java.lang.Math.max; -import static java.lang.Math.min; - -/** - * An HPACK encoder. - * - *

Implementation note: This class is security sensitive, and depends on users correctly identifying their headers - * as security sensitive or not. If a header is considered not sensitive, methods names "insensitive" are used which - * are fast, but don't provide any security guarantees. - */ -final class HpackEncoder { - static final int NOT_FOUND = -1; - static final int HUFF_CODE_THRESHOLD = 512; - // a linked hash map of header fields - private final HeaderEntry[] headerFields; - private final HeaderEntry head = new HeaderEntry(-1, AsciiString.EMPTY_STRING, - AsciiString.EMPTY_STRING, Integer.MAX_VALUE, null); - private final HpackHuffmanEncoder hpackHuffmanEncoder = new HpackHuffmanEncoder(); - private final byte hashMask; - private final boolean ignoreMaxHeaderListSize; - private final int huffCodeThreshold; - private long size; - private long maxHeaderTableSize; - private long maxHeaderListSize; - - /** - * Creates a new encoder. - */ - HpackEncoder() { - this(false); - } - - /** - * Creates a new encoder. - */ - HpackEncoder(boolean ignoreMaxHeaderListSize) { - this(ignoreMaxHeaderListSize, 16, HUFF_CODE_THRESHOLD); - } - - /** - * Creates a new encoder. - */ - HpackEncoder(boolean ignoreMaxHeaderListSize, int arraySizeHint, int huffCodeThreshold) { - this.ignoreMaxHeaderListSize = ignoreMaxHeaderListSize; - maxHeaderTableSize = DEFAULT_HEADER_TABLE_SIZE; - maxHeaderListSize = MAX_HEADER_LIST_SIZE; - // Enforce a bound of [2, 128] because hashMask is a byte. The max possible value of hashMask is one less - // than the length of this array, and we want the mask to be > 0. - headerFields = new HeaderEntry[findNextPositivePowerOfTwo(max(2, min(arraySizeHint, 128)))]; - hashMask = (byte) (headerFields.length - 1); - head.before = head.after = head; - this.huffCodeThreshold = huffCodeThreshold; - } - - /** - * Encode the header field into the header block. - * - * The given {@link CharSequence}s must be immutable! - */ - public void encodeHeaders(int streamId, ByteBuf out, Http2Headers headers, SensitivityDetector sensitivityDetector) - throws Http2Exception { - if (ignoreMaxHeaderListSize) { - encodeHeadersIgnoreMaxHeaderListSize(out, headers, sensitivityDetector); - } else { - encodeHeadersEnforceMaxHeaderListSize(streamId, out, headers, sensitivityDetector); - } - } - - private void encodeHeadersEnforceMaxHeaderListSize(int streamId, ByteBuf out, Http2Headers headers, - SensitivityDetector sensitivityDetector) - throws Http2Exception { - long headerSize = 0; - // To ensure we stay consistent with our peer check the size is valid before we potentially modify HPACK state. - for (Map.Entry header : headers) { - CharSequence name = header.getKey(); - CharSequence value = header.getValue(); - // OK to increment now and check for bounds after because this value is limited to unsigned int and will not - // overflow. - headerSize += HpackHeaderField.sizeOf(name, value); - if (headerSize > maxHeaderListSize) { - headerListSizeExceeded(streamId, maxHeaderListSize, false); - } - } - encodeHeadersIgnoreMaxHeaderListSize(out, headers, sensitivityDetector); - } - - private void encodeHeadersIgnoreMaxHeaderListSize(ByteBuf out, Http2Headers headers, - SensitivityDetector sensitivityDetector) throws Http2Exception { - for (Map.Entry header : headers) { - CharSequence name = header.getKey(); - CharSequence value = header.getValue(); - encodeHeader(out, name, value, sensitivityDetector.isSensitive(name, value), - HpackHeaderField.sizeOf(name, value)); - } - } - - /** - * Encode the header field into the header block. - * - * The given {@link CharSequence}s must be immutable! - */ - private void encodeHeader(ByteBuf out, CharSequence name, CharSequence value, boolean sensitive, long headerSize) { - // If the header value is sensitive then it must never be indexed - if (sensitive) { - int nameIndex = getNameIndex(name); - encodeLiteral(out, name, value, IndexType.NEVER, nameIndex); - return; - } - - // If the peer will only use the static table - if (maxHeaderTableSize == 0) { - int staticTableIndex = HpackStaticTable.getIndexInsensitive(name, value); - if (staticTableIndex == HpackStaticTable.NOT_FOUND) { - int nameIndex = HpackStaticTable.getIndex(name); - encodeLiteral(out, name, value, IndexType.NONE, nameIndex); - } else { - encodeInteger(out, 0x80, 7, staticTableIndex); - } - return; - } - - // If the headerSize is greater than the max table size then it must be encoded literally - if (headerSize > maxHeaderTableSize) { - int nameIndex = getNameIndex(name); - encodeLiteral(out, name, value, IndexType.NONE, nameIndex); - return; - } - - HeaderEntry headerField = getEntryInsensitive(name, value); - if (headerField != null) { - int index = getIndex(headerField.index) + HpackStaticTable.length; - // Section 6.1. Indexed Header Field Representation - encodeInteger(out, 0x80, 7, index); - } else { - int staticTableIndex = HpackStaticTable.getIndexInsensitive(name, value); - if (staticTableIndex != HpackStaticTable.NOT_FOUND) { - // Section 6.1. Indexed Header Field Representation - encodeInteger(out, 0x80, 7, staticTableIndex); - } else { - ensureCapacity(headerSize); - encodeLiteral(out, name, value, IndexType.INCREMENTAL, getNameIndex(name)); - add(name, value, headerSize); - } - } - } - - /** - * Set the maximum table size. - */ - public void setMaxHeaderTableSize(ByteBuf out, long maxHeaderTableSize) throws Http2Exception { - if (maxHeaderTableSize < MIN_HEADER_TABLE_SIZE || maxHeaderTableSize > MAX_HEADER_TABLE_SIZE) { - throw connectionError(PROTOCOL_ERROR, "Header Table Size must be >= %d and <= %d but was %d", - MIN_HEADER_TABLE_SIZE, MAX_HEADER_TABLE_SIZE, maxHeaderTableSize); - } - if (this.maxHeaderTableSize == maxHeaderTableSize) { - return; - } - this.maxHeaderTableSize = maxHeaderTableSize; - ensureCapacity(0); - // Casting to integer is safe as we verified the maxHeaderTableSize is a valid unsigned int. - encodeInteger(out, 0x20, 5, maxHeaderTableSize); - } - - /** - * Return the maximum table size. - */ - public long getMaxHeaderTableSize() { - return maxHeaderTableSize; - } - - 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_LIST_SIZE, MAX_HEADER_LIST_SIZE, maxHeaderListSize); - } - this.maxHeaderListSize = maxHeaderListSize; - } - - public long getMaxHeaderListSize() { - return maxHeaderListSize; - } - - /** - * Encode integer according to Section 5.1. - */ - private static void encodeInteger(ByteBuf out, int mask, int n, int i) { - encodeInteger(out, mask, n, (long) i); - } - - /** - * Encode integer according to Section 5.1. - */ - private static void encodeInteger(ByteBuf out, int mask, int n, long i) { - assert n >= 0 && n <= 8 : "N: " + n; - int nbits = 0xFF >>> 8 - n; - if (i < nbits) { - out.writeByte((int) (mask | i)); - } else { - out.writeByte(mask | nbits); - long length = i - nbits; - for (; (length & ~0x7F) != 0; length >>>= 7) { - out.writeByte((int) (length & 0x7F | 0x80)); - } - out.writeByte((int) length); - } - } - - /** - * Encode string literal according to Section 5.2. - */ - private void encodeStringLiteral(ByteBuf out, CharSequence string) { - int huffmanLength; - if (string.length() >= huffCodeThreshold - && (huffmanLength = hpackHuffmanEncoder.getEncodedLength(string)) < string.length()) { - encodeInteger(out, 0x80, 7, huffmanLength); - hpackHuffmanEncoder.encode(out, string); - } else { - encodeInteger(out, 0x00, 7, string.length()); - if (string instanceof AsciiString) { - // Fast-path - AsciiString asciiString = (AsciiString) string; - out.writeBytes(asciiString.array(), asciiString.arrayOffset(), asciiString.length()); - } else { - // Only ASCII is allowed in http2 headers, so its fine to use this. - // https://tools.ietf.org/html/rfc7540#section-8.1.2 - out.writeCharSequence(string, CharsetUtil.ISO_8859_1); - } - } - } - - /** - * Encode literal header field according to Section 6.2. - */ - private void encodeLiteral(ByteBuf out, CharSequence name, CharSequence value, IndexType indexType, - int nameIndex) { - boolean nameIndexValid = nameIndex != NOT_FOUND; - switch (indexType) { - case INCREMENTAL: - encodeInteger(out, 0x40, 6, nameIndexValid ? nameIndex : 0); - break; - case NONE: - encodeInteger(out, 0x00, 4, nameIndexValid ? nameIndex : 0); - break; - case NEVER: - encodeInteger(out, 0x10, 4, nameIndexValid ? nameIndex : 0); - break; - default: - throw new Error("should not reach here"); - } - if (!nameIndexValid) { - encodeStringLiteral(out, name); - } - encodeStringLiteral(out, value); - } - - private int getNameIndex(CharSequence name) { - int index = HpackStaticTable.getIndex(name); - if (index == HpackStaticTable.NOT_FOUND) { - index = getIndex(name); - if (index >= 0) { - index += HpackStaticTable.length; - } - } - return index; - } - - /** - * Ensure that the dynamic table has enough room to hold 'headerSize' more bytes. Removes the - * oldest entry from the dynamic table until sufficient space is available. - */ - private void ensureCapacity(long headerSize) { - while (maxHeaderTableSize - size < headerSize) { - int index = length(); - if (index == 0) { - break; - } - remove(); - } - } - - /** - * Return the number of header fields in the dynamic table. Exposed for testing. - */ - int length() { - return size == 0 ? 0 : head.after.index - head.before.index + 1; - } - - /** - * Return the size of the dynamic table. Exposed for testing. - */ - long size() { - return size; - } - - /** - * Return the header field at the given index. Exposed for testing. - */ - HpackHeaderField getHeaderField(int index) { - HeaderEntry entry = head; - while (index-- >= 0) { - entry = entry.before; - } - return entry; - } - - /** - * Returns the header entry with the lowest index value for the header field. Returns null if - * header field is not in the dynamic table. - */ - private HeaderEntry getEntryInsensitive(CharSequence name, CharSequence value) { - if (length() == 0 || name == null || value == null) { - return null; - } - int h = AsciiString.hashCode(name); - int i = index(h); - for (HeaderEntry e = headerFields[i]; e != null; e = e.next) { - // Check the value before then name, as it is more likely the value will be different incase there is no - // match. - if (e.hash == h && equalsVariableTime(value, e.value) && equalsVariableTime(name, e.name)) { - return e; - } - } - return null; - } - - /** - * Returns the lowest index value for the header field name in the dynamic table. Returns -1 if - * the header field name is not in the dynamic table. - */ - private int getIndex(CharSequence name) { - if (length() == 0 || name == null) { - return NOT_FOUND; - } - int h = AsciiString.hashCode(name); - int i = index(h); - for (HeaderEntry e = headerFields[i]; e != null; e = e.next) { - if (e.hash == h && equalsConstantTime(name, e.name) != 0) { - return getIndex(e.index); - } - } - return NOT_FOUND; - } - - /** - * Compute the index into the dynamic table given the index in the header entry. - */ - private int getIndex(int index) { - return index == NOT_FOUND ? NOT_FOUND : index - head.before.index + 1; - } - - /** - * Add the header field to the dynamic table. Entries are evicted from the dynamic table until - * the size of the table and the new header field is less than the table's maxHeaderTableSize. If the size - * of the new entry is larger than the table's maxHeaderTableSize, the dynamic table will be cleared. - */ - private void add(CharSequence name, CharSequence value, long headerSize) { - // Clear the table if the header field size is larger than the maxHeaderTableSize. - if (headerSize > maxHeaderTableSize) { - clear(); - return; - } - - // Evict oldest entries until we have enough maxHeaderTableSize. - while (maxHeaderTableSize - size < headerSize) { - remove(); - } - - int h = AsciiString.hashCode(name); - int i = index(h); - HeaderEntry old = headerFields[i]; - HeaderEntry e = new HeaderEntry(h, name, value, head.before.index - 1, old); - headerFields[i] = e; - e.addBefore(head); - size += headerSize; - } - - /** - * Remove and return the oldest header field from the dynamic table. - */ - private HpackHeaderField remove() { - if (size == 0) { - return null; - } - HeaderEntry eldest = head.after; - int h = eldest.hash; - int i = index(h); - HeaderEntry prev = headerFields[i]; - HeaderEntry e = prev; - while (e != null) { - HeaderEntry next = e.next; - if (e == eldest) { - if (prev == eldest) { - headerFields[i] = next; - } else { - prev.next = next; - } - eldest.remove(); - size -= eldest.size(); - return eldest; - } - prev = e; - e = next; - } - return null; - } - - /** - * Remove all entries from the dynamic table. - */ - private void clear() { - Arrays.fill(headerFields, null); - head.before = head.after = head; - size = 0; - } - - /** - * Returns the index into the hash table for the hash code h. - */ - private int index(int h) { - return h & hashMask; - } - - /** - * A linked hash map HpackHeaderField entry. - */ - private static final class HeaderEntry extends HpackHeaderField { - // These fields comprise the doubly linked list used for iteration. - HeaderEntry before, after; - - // These fields comprise the chained list for header fields with the same hash. - HeaderEntry next; - int hash; - - // This is used to compute the index in the dynamic table. - int index; - - /** - * Creates new entry. - */ - HeaderEntry(int hash, CharSequence name, CharSequence value, int index, HeaderEntry next) { - super(name, value); - this.index = index; - this.hash = hash; - this.next = next; - } - - /** - * Removes this entry from the linked list. - */ - private void remove() { - before.after = after; - after.before = before; - before = null; // null references to prevent nepotism in generational GC. - after = null; - next = null; - } - - /** - * Inserts this entry before the specified existing entry in the list. - */ - private void addBefore(HeaderEntry existingEntry) { - after = existingEntry; - before = existingEntry.before; - before.after = this; - after.before = this; - } - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/HpackHeaderField.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/HpackHeaderField.java deleted file mode 100644 index 2e030078a2..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/HpackHeaderField.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/* - * Copyright 2014 Twitter, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.netty.handler.codec.http2; - -import static io.netty.handler.codec.http2.HpackUtil.equalsVariableTime; -import static java.util.Objects.requireNonNull; - -class HpackHeaderField { - - // Section 4.1. Calculating Table Size - // The additional 32 octets account for an estimated - // overhead associated with the structure. - static final int HEADER_ENTRY_OVERHEAD = 32; - - static long sizeOf(CharSequence name, CharSequence value) { - return name.length() + value.length() + HEADER_ENTRY_OVERHEAD; - } - - final CharSequence name; - final CharSequence value; - - // This constructor can only be used if name and value are ISO-8859-1 encoded. - HpackHeaderField(CharSequence name, CharSequence value) { - this.name = requireNonNull(name, "name"); - this.value = requireNonNull(value, "value"); - } - - final int size() { - return name.length() + value.length() + HEADER_ENTRY_OVERHEAD; - } - - public final boolean equalsForTest(HpackHeaderField other) { - return equalsVariableTime(name, other.name) && equalsVariableTime(value, other.value); - } - - @Override - public String toString() { - return name + ": " + value; - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/HpackHuffmanDecoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/HpackHuffmanDecoder.java deleted file mode 100644 index eac86e2282..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/HpackHuffmanDecoder.java +++ /dev/null @@ -1,4736 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/* - * Copyright 2014 Twitter, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.util.AsciiString; -import io.netty.util.ByteProcessor; - -import static io.netty.handler.codec.http2.Http2Error.COMPRESSION_ERROR; - -final class HpackHuffmanDecoder implements ByteProcessor { - - /* Scroll to the bottom! */ - - private static final byte HUFFMAN_COMPLETE = 1; - private static final byte HUFFMAN_EMIT_SYMBOL = 1 << 1; - private static final byte HUFFMAN_FAIL = 1 << 2; - - private static final int HUFFMAN_COMPLETE_SHIFT = HUFFMAN_COMPLETE << 8; - private static final int HUFFMAN_EMIT_SYMBOL_SHIFT = HUFFMAN_EMIT_SYMBOL << 8; - private static final int HUFFMAN_FAIL_SHIFT = HUFFMAN_FAIL << 8; - - /** - * A table of byte tuples (state, flags, output). They are packed together as: - *

- * state<<16 + flags<<8 + output - */ - private static final int[] HUFFS = new int[] { - // Node 0 (Root Node, never emits symbols.) - 4 << 16, - 5 << 16, - 7 << 16, - 8 << 16, - 11 << 16, - 12 << 16, - 16 << 16, - 19 << 16, - 25 << 16, - 28 << 16, - 32 << 16, - 35 << 16, - 42 << 16, - 49 << 16, - 57 << 16, - (64 << 16) + (HUFFMAN_COMPLETE << 8), - - // Node 1 - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 48, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 49, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 50, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 97, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 99, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 101, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 105, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 111, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 115, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 116, - 13 << 16, - 14 << 16, - 17 << 16, - 18 << 16, - 20 << 16, - 21 << 16, - - // Node 2 - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 48, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 48, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 49, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 49, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 50, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 50, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 97, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 97, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 99, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 99, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 101, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 101, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 105, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 105, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 111, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 111, - - // Node 3 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 48, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 48, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 48, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 48, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 49, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 49, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 49, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 49, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 50, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 50, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 50, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 50, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 97, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 97, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 97, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 97, - - // Node 4 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 48, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 48, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 48, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 48, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 48, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 48, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 48, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 48, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 49, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 49, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 49, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 49, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 49, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 49, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 49, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 49, - - // Node 5 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 50, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 50, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 50, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 50, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 50, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 50, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 50, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 50, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 97, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 97, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 97, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 97, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 97, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 97, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 97, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 97, - - // Node 6 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 99, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 99, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 99, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 99, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 101, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 101, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 101, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 101, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 105, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 105, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 105, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 105, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 111, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 111, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 111, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 111, - - // Node 7 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 99, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 99, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 99, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 99, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 99, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 99, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 99, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 99, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 101, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 101, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 101, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 101, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 101, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 101, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 101, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 101, - - // Node 8 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 105, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 105, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 105, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 105, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 105, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 105, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 105, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 105, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 111, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 111, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 111, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 111, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 111, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 111, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 111, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 111, - - // Node 9 - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 115, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 115, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 116, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 116, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 32, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 37, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 45, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 46, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 47, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 51, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 52, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 53, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 54, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 55, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 56, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 57, - - // Node 10 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 115, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 115, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 115, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 115, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 116, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 116, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 116, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 116, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 32, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 32, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 37, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 37, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 45, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 45, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 46, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 46, - - // Node 11 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 115, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 115, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 115, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 115, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 115, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 115, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 115, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 115, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 116, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 116, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 116, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 116, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 116, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 116, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 116, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 116, - - // Node 12 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 32, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 32, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 32, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 32, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 37, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 37, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 37, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 37, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 45, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 45, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 45, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 45, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 46, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 46, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 46, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 46, - - // Node 13 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 32, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 32, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 32, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 32, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 32, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 32, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 32, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 32, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 37, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 37, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 37, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 37, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 37, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 37, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 37, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 37, - - // Node 14 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 45, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 45, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 45, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 45, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 45, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 45, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 45, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 45, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 46, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 46, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 46, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 46, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 46, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 46, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 46, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 46, - - // Node 15 - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 47, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 47, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 51, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 51, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 52, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 52, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 53, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 53, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 54, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 54, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 55, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 55, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 56, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 56, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 57, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 57, - - // Node 16 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 47, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 47, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 47, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 47, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 51, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 51, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 51, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 51, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 52, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 52, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 52, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 52, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 53, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 53, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 53, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 53, - - // Node 17 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 47, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 47, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 47, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 47, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 47, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 47, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 47, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 47, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 51, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 51, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 51, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 51, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 51, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 51, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 51, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 51, - - // Node 18 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 52, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 52, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 52, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 52, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 52, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 52, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 52, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 52, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 53, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 53, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 53, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 53, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 53, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 53, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 53, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 53, - - // Node 19 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 54, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 54, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 54, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 54, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 55, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 55, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 55, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 55, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 56, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 56, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 56, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 56, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 57, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 57, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 57, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 57, - - // Node 20 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 54, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 54, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 54, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 54, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 54, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 54, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 54, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 54, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 55, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 55, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 55, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 55, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 55, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 55, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 55, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 55, - - // Node 21 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 56, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 56, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 56, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 56, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 56, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 56, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 56, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 56, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 57, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 57, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 57, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 57, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 57, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 57, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 57, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 57, - - // Node 22 - 26 << 16, - 27 << 16, - 29 << 16, - 30 << 16, - 33 << 16, - 34 << 16, - 36 << 16, - 37 << 16, - 43 << 16, - 46 << 16, - 50 << 16, - 53 << 16, - 58 << 16, - 61 << 16, - 65 << 16, - (68 << 16) + (HUFFMAN_COMPLETE << 8), - - // Node 23 - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 61, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 65, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 95, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 98, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 100, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 102, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 103, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 104, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 108, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 109, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 110, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 112, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 114, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 117, - 38 << 16, - 39 << 16, - - // Node 24 - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 61, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 61, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 65, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 65, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 95, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 95, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 98, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 98, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 100, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 100, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 102, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 102, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 103, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 103, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 104, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 104, - - // Node 25 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 61, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 61, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 61, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 61, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 65, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 65, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 65, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 65, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 95, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 95, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 95, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 95, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 98, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 98, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 98, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 98, - - // Node 26 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 61, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 61, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 61, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 61, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 61, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 61, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 61, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 61, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 65, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 65, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 65, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 65, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 65, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 65, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 65, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 65, - - // Node 27 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 95, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 95, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 95, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 95, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 95, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 95, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 95, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 95, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 98, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 98, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 98, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 98, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 98, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 98, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 98, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 98, - - // Node 28 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 100, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 100, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 100, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 100, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 102, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 102, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 102, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 102, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 103, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 103, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 103, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 103, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 104, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 104, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 104, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 104, - - // Node 29 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 100, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 100, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 100, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 100, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 100, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 100, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 100, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 100, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 102, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 102, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 102, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 102, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 102, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 102, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 102, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 102, - - // Node 30 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 103, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 103, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 103, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 103, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 103, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 103, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 103, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 103, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 104, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 104, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 104, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 104, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 104, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 104, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 104, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 104, - - // Node 31 - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 108, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 108, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 109, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 109, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 110, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 110, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 112, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 112, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 114, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 114, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 117, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 117, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 58, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 66, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 67, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 68, - - // Node 32 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 108, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 108, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 108, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 108, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 109, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 109, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 109, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 109, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 110, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 110, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 110, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 110, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 112, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 112, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 112, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 112, - - // Node 33 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 108, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 108, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 108, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 108, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 108, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 108, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 108, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 108, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 109, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 109, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 109, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 109, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 109, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 109, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 109, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 109, - - // Node 34 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 110, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 110, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 110, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 110, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 110, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 110, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 110, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 110, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 112, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 112, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 112, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 112, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 112, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 112, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 112, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 112, - - // Node 35 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 114, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 114, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 114, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 114, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 117, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 117, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 117, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 117, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 58, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 58, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 66, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 66, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 67, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 67, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 68, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 68, - - // Node 36 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 114, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 114, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 114, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 114, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 114, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 114, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 114, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 114, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 117, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 117, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 117, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 117, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 117, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 117, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 117, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 117, - - // Node 37 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 58, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 58, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 58, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 58, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 66, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 66, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 66, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 66, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 67, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 67, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 67, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 67, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 68, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 68, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 68, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 68, - - // Node 38 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 58, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 58, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 58, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 58, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 58, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 58, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 58, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 58, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 66, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 66, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 66, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 66, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 66, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 66, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 66, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 66, - - // Node 39 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 67, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 67, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 67, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 67, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 67, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 67, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 67, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 67, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 68, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 68, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 68, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 68, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 68, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 68, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 68, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 68, - - // Node 40 - 44 << 16, - 45 << 16, - 47 << 16, - 48 << 16, - 51 << 16, - 52 << 16, - 54 << 16, - 55 << 16, - 59 << 16, - 60 << 16, - 62 << 16, - 63 << 16, - 66 << 16, - 67 << 16, - 69 << 16, - (72 << 16) + (HUFFMAN_COMPLETE << 8), - - // Node 41 - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 69, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 70, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 71, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 72, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 73, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 74, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 75, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 76, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 77, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 78, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 79, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 80, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 81, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 82, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 83, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 84, - - // Node 42 - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 69, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 69, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 70, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 70, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 71, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 71, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 72, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 72, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 73, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 73, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 74, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 74, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 75, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 75, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 76, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 76, - - // Node 43 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 69, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 69, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 69, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 69, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 70, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 70, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 70, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 70, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 71, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 71, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 71, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 71, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 72, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 72, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 72, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 72, - - // Node 44 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 69, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 69, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 69, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 69, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 69, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 69, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 69, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 69, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 70, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 70, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 70, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 70, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 70, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 70, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 70, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 70, - - // Node 45 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 71, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 71, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 71, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 71, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 71, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 71, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 71, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 71, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 72, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 72, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 72, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 72, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 72, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 72, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 72, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 72, - - // Node 46 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 73, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 73, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 73, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 73, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 74, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 74, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 74, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 74, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 75, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 75, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 75, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 75, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 76, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 76, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 76, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 76, - - // Node 47 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 73, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 73, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 73, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 73, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 73, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 73, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 73, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 73, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 74, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 74, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 74, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 74, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 74, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 74, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 74, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 74, - - // Node 48 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 75, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 75, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 75, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 75, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 75, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 75, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 75, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 75, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 76, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 76, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 76, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 76, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 76, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 76, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 76, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 76, - - // Node 49 - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 77, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 77, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 78, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 78, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 79, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 79, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 80, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 80, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 81, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 81, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 82, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 82, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 83, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 83, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 84, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 84, - - // Node 50 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 77, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 77, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 77, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 77, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 78, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 78, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 78, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 78, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 79, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 79, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 79, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 79, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 80, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 80, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 80, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 80, - - // Node 51 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 77, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 77, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 77, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 77, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 77, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 77, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 77, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 77, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 78, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 78, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 78, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 78, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 78, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 78, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 78, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 78, - - // Node 52 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 79, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 79, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 79, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 79, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 79, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 79, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 79, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 79, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 80, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 80, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 80, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 80, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 80, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 80, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 80, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 80, - - // Node 53 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 81, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 81, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 81, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 81, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 82, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 82, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 82, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 82, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 83, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 83, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 83, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 83, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 84, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 84, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 84, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 84, - - // Node 54 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 81, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 81, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 81, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 81, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 81, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 81, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 81, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 81, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 82, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 82, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 82, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 82, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 82, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 82, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 82, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 82, - - // Node 55 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 83, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 83, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 83, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 83, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 83, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 83, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 83, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 83, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 84, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 84, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 84, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 84, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 84, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 84, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 84, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 84, - - // Node 56 - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 85, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 86, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 87, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 89, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 106, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 107, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 113, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 118, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 119, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 120, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 121, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 122, - 70 << 16, - 71 << 16, - 73 << 16, - (74 << 16) + (HUFFMAN_COMPLETE << 8), - - // Node 57 - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 85, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 85, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 86, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 86, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 87, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 87, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 89, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 89, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 106, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 106, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 107, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 107, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 113, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 113, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 118, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 118, - - // Node 58 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 85, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 85, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 85, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 85, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 86, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 86, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 86, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 86, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 87, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 87, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 87, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 87, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 89, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 89, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 89, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 89, - - // Node 59 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 85, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 85, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 85, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 85, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 85, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 85, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 85, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 85, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 86, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 86, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 86, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 86, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 86, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 86, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 86, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 86, - - // Node 60 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 87, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 87, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 87, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 87, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 87, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 87, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 87, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 87, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 89, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 89, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 89, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 89, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 89, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 89, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 89, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 89, - - // Node 61 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 106, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 106, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 106, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 106, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 107, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 107, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 107, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 107, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 113, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 113, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 113, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 113, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 118, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 118, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 118, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 118, - - // Node 62 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 106, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 106, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 106, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 106, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 106, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 106, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 106, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 106, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 107, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 107, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 107, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 107, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 107, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 107, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 107, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 107, - - // Node 63 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 113, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 113, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 113, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 113, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 113, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 113, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 113, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 113, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 118, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 118, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 118, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 118, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 118, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 118, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 118, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 118, - - // Node 64 - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 119, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 119, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 120, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 120, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 121, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 121, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 122, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 122, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 38, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 42, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 44, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 59, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 88, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 90, - 75 << 16, - 78 << 16, - - // Node 65 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 119, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 119, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 119, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 119, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 120, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 120, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 120, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 120, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 121, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 121, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 121, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 121, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 122, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 122, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 122, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 122, - - // Node 66 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 119, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 119, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 119, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 119, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 119, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 119, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 119, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 119, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 120, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 120, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 120, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 120, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 120, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 120, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 120, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 120, - - // Node 67 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 121, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 121, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 121, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 121, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 121, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 121, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 121, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 121, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 122, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 122, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 122, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 122, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 122, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 122, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 122, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 122, - - // Node 68 - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 38, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 38, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 42, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 42, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 44, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 44, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 59, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 59, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 88, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 88, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 90, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 90, - 76 << 16, - 77 << 16, - 79 << 16, - 81 << 16, - - // Node 69 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 38, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 38, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 38, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 38, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 42, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 42, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 42, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 42, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 44, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 44, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 44, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 44, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 59, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 59, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 59, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 59, - - // Node 70 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 38, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 38, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 38, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 38, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 38, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 38, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 38, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 38, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 42, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 42, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 42, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 42, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 42, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 42, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 42, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 42, - - // Node 71 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 44, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 44, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 44, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 44, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 44, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 44, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 44, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 44, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 59, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 59, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 59, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 59, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 59, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 59, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 59, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 59, - - // Node 72 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 88, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 88, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 88, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 88, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 90, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 90, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 90, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 90, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 33, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 34, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 40, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 41, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 63, - 80 << 16, - 82 << 16, - 84 << 16, - - // Node 73 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 88, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 88, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 88, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 88, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 88, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 88, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 88, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 88, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 90, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 90, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 90, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 90, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 90, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 90, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 90, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 90, - - // Node 74 - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 33, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 33, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 34, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 34, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 40, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 40, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 41, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 41, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 63, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 63, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 39, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 43, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 124, - 83 << 16, - 85 << 16, - 88 << 16, - - // Node 75 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 33, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 33, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 33, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 33, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 34, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 34, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 34, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 34, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 40, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 40, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 40, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 40, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 41, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 41, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 41, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 41, - - // Node 76 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 33, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 33, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 33, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 33, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 33, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 33, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 33, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 33, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 34, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 34, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 34, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 34, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 34, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 34, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 34, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 34, - - // Node 77 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 40, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 40, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 40, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 40, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 40, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 40, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 40, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 40, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 41, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 41, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 41, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 41, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 41, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 41, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 41, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 41, - - // Node 78 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 63, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 63, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 63, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 63, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 39, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 39, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 43, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 43, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 124, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 124, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 35, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 62, - 86 << 16, - 87 << 16, - 89 << 16, - 90 << 16, - - // Node 79 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 63, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 63, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 63, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 63, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 63, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 63, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 63, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 63, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 39, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 39, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 39, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 39, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 43, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 43, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 43, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 43, - - // Node 80 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 39, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 39, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 39, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 39, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 39, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 39, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 39, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 39, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 43, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 43, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 43, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 43, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 43, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 43, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 43, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 43, - - // Node 81 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 124, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 124, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 124, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 124, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 35, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 35, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 62, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 62, - (HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 36, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 64, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 91, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 93, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 126, - 91 << 16, - 92 << 16, - - // Node 82 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 124, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 124, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 124, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 124, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 124, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 124, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 124, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 124, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 35, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 35, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 35, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 35, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 62, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 62, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 62, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 62, - - // Node 83 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 35, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 35, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 35, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 35, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 35, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 35, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 35, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 35, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 62, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 62, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 62, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 62, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 62, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 62, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 62, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 62, - - // Node 84 - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8), - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8), - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 36, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 36, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 64, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 64, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 91, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 91, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 93, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 93, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 126, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 126, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 94, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 125, - 93 << 16, - 94 << 16, - - // Node 85 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8), - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8), - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8), - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8), - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 36, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 36, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 36, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 36, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 64, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 64, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 64, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 64, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 91, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 91, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 91, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 91, - - // Node 86 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8), - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8), - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8), - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8), - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8), - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8), - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8), - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8), - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 36, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 36, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 36, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 36, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 36, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 36, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 36, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 36, - - // Node 87 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 64, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 64, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 64, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 64, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 64, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 64, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 64, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 64, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 91, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 91, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 91, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 91, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 91, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 91, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 91, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 91, - - // Node 88 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 93, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 93, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 93, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 93, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 126, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 126, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 126, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 126, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 94, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 94, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 125, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 125, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 60, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 96, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 123, - 95 << 16, - - // Node 89 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 93, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 93, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 93, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 93, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 93, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 93, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 93, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 93, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 126, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 126, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 126, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 126, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 126, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 126, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 126, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 126, - - // Node 90 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 94, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 94, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 94, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 94, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 125, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 125, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 125, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 125, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 60, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 60, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 96, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 96, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 123, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 123, - 96 << 16, - 110 << 16, - - // Node 91 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 94, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 94, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 94, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 94, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 94, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 94, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 94, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 94, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 125, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 125, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 125, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 125, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 125, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 125, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 125, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 125, - - // Node 92 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 60, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 60, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 60, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 60, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 96, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 96, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 96, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 96, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 123, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 123, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 123, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 123, - 97 << 16, - 101 << 16, - 111 << 16, - 133 << 16, - - // Node 93 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 60, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 60, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 60, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 60, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 60, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 60, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 60, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 60, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 96, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 96, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 96, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 96, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 96, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 96, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 96, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 96, - - // Node 94 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 123, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 123, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 123, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 123, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 123, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 123, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 123, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 123, - 98 << 16, - 99 << 16, - 102 << 16, - 105 << 16, - 112 << 16, - 119 << 16, - 134 << 16, - 153 << 16, - - // Node 95 - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 92, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 195, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 208, - 100 << 16, - 103 << 16, - 104 << 16, - 106 << 16, - 107 << 16, - 113 << 16, - 116 << 16, - 120 << 16, - 126 << 16, - 135 << 16, - 142 << 16, - 154 << 16, - 169 << 16, - - // Node 96 - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 92, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 92, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 195, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 195, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 208, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 208, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 128, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 130, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 131, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 162, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 184, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 194, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 224, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 226, - 108 << 16, - 109 << 16, - - // Node 97 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 92, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 92, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 92, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 92, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 195, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 195, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 195, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 195, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 208, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 208, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 208, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 208, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 128, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 128, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 130, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 130, - - // Node 98 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 92, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 92, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 92, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 92, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 92, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 92, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 92, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 92, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 195, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 195, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 195, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 195, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 195, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 195, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 195, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 195, - - // Node 99 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 208, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 208, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 208, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 208, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 208, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 208, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 208, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 208, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 128, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 128, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 128, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 128, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 130, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 130, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 130, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 130, - - // Node 100 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 128, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 128, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 128, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 128, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 128, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 128, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 128, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 128, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 130, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 130, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 130, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 130, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 130, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 130, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 130, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 130, - - // Node 101 - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 131, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 131, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 162, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 162, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 184, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 184, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 194, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 194, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 224, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 224, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 226, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 226, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 153, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 161, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 167, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 172, - - // Node 102 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 131, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 131, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 131, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 131, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 162, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 162, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 162, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 162, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 184, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 184, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 184, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 184, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 194, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 194, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 194, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 194, - - // Node 103 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 131, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 131, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 131, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 131, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 131, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 131, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 131, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 131, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 162, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 162, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 162, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 162, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 162, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 162, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 162, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 162, - - // Node 104 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 184, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 184, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 184, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 184, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 184, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 184, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 184, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 184, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 194, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 194, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 194, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 194, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 194, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 194, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 194, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 194, - - // Node 105 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 224, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 224, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 224, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 224, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 226, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 226, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 226, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 226, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 153, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 153, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 161, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 161, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 167, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 167, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 172, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 172, - - // Node 106 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 224, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 224, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 224, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 224, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 224, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 224, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 224, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 224, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 226, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 226, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 226, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 226, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 226, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 226, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 226, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 226, - - // Node 107 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 153, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 153, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 153, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 153, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 161, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 161, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 161, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 161, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 167, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 167, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 167, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 167, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 172, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 172, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 172, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 172, - - // Node 108 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 153, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 153, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 153, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 153, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 153, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 153, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 153, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 153, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 161, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 161, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 161, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 161, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 161, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 161, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 161, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 161, - - // Node 109 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 167, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 167, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 167, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 167, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 167, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 167, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 167, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 167, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 172, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 172, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 172, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 172, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 172, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 172, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 172, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 172, - - // Node 110 - 114 << 16, - 115 << 16, - 117 << 16, - 118 << 16, - 121 << 16, - 123 << 16, - 127 << 16, - 130 << 16, - 136 << 16, - 139 << 16, - 143 << 16, - 146 << 16, - 155 << 16, - 162 << 16, - 170 << 16, - 180 << 16, - - // Node 111 - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 176, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 177, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 179, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 209, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 216, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 217, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 227, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 229, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 230, - 122 << 16, - 124 << 16, - 125 << 16, - 128 << 16, - 129 << 16, - 131 << 16, - 132 << 16, - - // Node 112 - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 176, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 176, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 177, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 177, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 179, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 179, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 209, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 209, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 216, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 216, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 217, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 217, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 227, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 227, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 229, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 229, - - // Node 113 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 176, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 176, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 176, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 176, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 177, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 177, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 177, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 177, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 179, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 179, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 179, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 179, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 209, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 209, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 209, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 209, - - // Node 114 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 176, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 176, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 176, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 176, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 176, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 176, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 176, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 176, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 177, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 177, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 177, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 177, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 177, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 177, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 177, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 177, - - // Node 115 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 179, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 179, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 179, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 179, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 179, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 179, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 179, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 179, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 209, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 209, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 209, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 209, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 209, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 209, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 209, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 209, - - // Node 116 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 216, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 216, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 216, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 216, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 217, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 217, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 217, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 217, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 227, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 227, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 227, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 227, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 229, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 229, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 229, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 229, - - // Node 117 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 216, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 216, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 216, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 216, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 216, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 216, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 216, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 216, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 217, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 217, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 217, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 217, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 217, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 217, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 217, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 217, - - // Node 118 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 227, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 227, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 227, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 227, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 227, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 227, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 227, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 227, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 229, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 229, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 229, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 229, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 229, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 229, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 229, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 229, - - // Node 119 - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 230, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 230, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 129, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 132, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 133, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 134, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 136, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 146, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 154, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 156, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 160, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 163, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 164, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 169, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 170, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 173, - - // Node 120 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 230, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 230, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 230, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 230, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 129, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 129, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 132, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 132, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 133, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 133, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 134, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 134, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 136, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 136, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 146, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 146, - - // Node 121 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 230, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 230, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 230, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 230, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 230, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 230, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 230, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 230, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 129, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 129, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 129, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 129, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 132, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 132, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 132, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 132, - - // Node 122 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 129, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 129, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 129, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 129, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 129, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 129, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 129, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 129, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 132, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 132, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 132, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 132, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 132, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 132, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 132, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 132, - - // Node 123 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 133, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 133, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 133, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 133, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 134, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 134, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 134, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 134, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 136, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 136, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 136, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 136, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 146, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 146, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 146, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 146, - - // Node 124 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 133, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 133, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 133, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 133, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 133, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 133, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 133, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 133, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 134, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 134, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 134, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 134, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 134, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 134, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 134, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 134, - - // Node 125 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 136, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 136, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 136, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 136, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 136, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 136, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 136, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 136, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 146, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 146, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 146, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 146, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 146, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 146, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 146, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 146, - - // Node 126 - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 154, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 154, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 156, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 156, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 160, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 160, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 163, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 163, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 164, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 164, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 169, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 169, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 170, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 170, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 173, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 173, - - // Node 127 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 154, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 154, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 154, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 154, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 156, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 156, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 156, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 156, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 160, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 160, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 160, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 160, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 163, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 163, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 163, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 163, - - // Node 128 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 154, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 154, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 154, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 154, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 154, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 154, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 154, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 154, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 156, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 156, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 156, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 156, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 156, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 156, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 156, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 156, - - // Node 129 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 160, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 160, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 160, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 160, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 160, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 160, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 160, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 160, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 163, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 163, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 163, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 163, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 163, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 163, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 163, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 163, - - // Node 130 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 164, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 164, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 164, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 164, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 169, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 169, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 169, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 169, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 170, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 170, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 170, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 170, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 173, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 173, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 173, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 173, - - // Node 131 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 164, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 164, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 164, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 164, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 164, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 164, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 164, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 164, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 169, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 169, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 169, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 169, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 169, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 169, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 169, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 169, - - // Node 132 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 170, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 170, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 170, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 170, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 170, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 170, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 170, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 170, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 173, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 173, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 173, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 173, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 173, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 173, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 173, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 173, - - // Node 133 - 137 << 16, - 138 << 16, - 140 << 16, - 141 << 16, - 144 << 16, - 145 << 16, - 147 << 16, - 150 << 16, - 156 << 16, - 159 << 16, - 163 << 16, - 166 << 16, - 171 << 16, - 174 << 16, - 181 << 16, - 190 << 16, - - // Node 134 - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 178, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 181, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 185, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 186, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 187, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 189, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 190, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 196, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 198, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 228, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 232, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 233, - 148 << 16, - 149 << 16, - 151 << 16, - 152 << 16, - - // Node 135 - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 178, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 178, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 181, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 181, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 185, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 185, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 186, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 186, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 187, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 187, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 189, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 189, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 190, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 190, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 196, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 196, - - // Node 136 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 178, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 178, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 178, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 178, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 181, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 181, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 181, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 181, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 185, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 185, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 185, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 185, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 186, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 186, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 186, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 186, - - // Node 137 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 178, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 178, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 178, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 178, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 178, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 178, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 178, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 178, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 181, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 181, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 181, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 181, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 181, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 181, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 181, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 181, - - // Node 138 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 185, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 185, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 185, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 185, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 185, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 185, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 185, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 185, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 186, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 186, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 186, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 186, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 186, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 186, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 186, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 186, - - // Node 139 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 187, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 187, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 187, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 187, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 189, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 189, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 189, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 189, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 190, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 190, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 190, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 190, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 196, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 196, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 196, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 196, - - // Node 140 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 187, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 187, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 187, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 187, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 187, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 187, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 187, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 187, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 189, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 189, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 189, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 189, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 189, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 189, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 189, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 189, - - // Node 141 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 190, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 190, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 190, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 190, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 190, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 190, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 190, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 190, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 196, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 196, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 196, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 196, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 196, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 196, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 196, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 196, - - // Node 142 - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 198, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 198, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 228, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 228, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 232, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 232, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 233, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 233, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 1, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 135, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 137, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 138, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 139, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 140, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 141, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 143, - - // Node 143 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 198, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 198, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 198, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 198, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 228, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 228, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 228, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 228, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 232, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 232, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 232, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 232, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 233, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 233, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 233, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 233, - - // Node 144 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 198, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 198, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 198, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 198, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 198, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 198, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 198, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 198, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 228, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 228, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 228, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 228, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 228, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 228, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 228, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 228, - - // Node 145 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 232, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 232, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 232, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 232, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 232, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 232, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 232, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 232, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 233, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 233, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 233, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 233, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 233, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 233, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 233, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 233, - - // Node 146 - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 1, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 1, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 135, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 135, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 137, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 137, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 138, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 138, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 139, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 139, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 140, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 140, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 141, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 141, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 143, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 143, - - // Node 147 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 1, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 1, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 1, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 1, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 135, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 135, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 135, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 135, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 137, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 137, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 137, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 137, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 138, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 138, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 138, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 138, - - // Node 148 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 1, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 1, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 1, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 1, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 1, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 1, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 1, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 1, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 135, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 135, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 135, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 135, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 135, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 135, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 135, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 135, - - // Node 149 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 137, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 137, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 137, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 137, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 137, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 137, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 137, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 137, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 138, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 138, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 138, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 138, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 138, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 138, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 138, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 138, - - // Node 150 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 139, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 139, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 139, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 139, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 140, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 140, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 140, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 140, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 141, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 141, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 141, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 141, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 143, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 143, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 143, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 143, - - // Node 151 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 139, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 139, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 139, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 139, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 139, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 139, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 139, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 139, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 140, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 140, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 140, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 140, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 140, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 140, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 140, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 140, - - // Node 152 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 141, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 141, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 141, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 141, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 141, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 141, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 141, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 141, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 143, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 143, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 143, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 143, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 143, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 143, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 143, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 143, - - // Node 153 - 157 << 16, - 158 << 16, - 160 << 16, - 161 << 16, - 164 << 16, - 165 << 16, - 167 << 16, - 168 << 16, - 172 << 16, - 173 << 16, - 175 << 16, - 177 << 16, - 182 << 16, - 185 << 16, - 191 << 16, - 207 << 16, - - // Node 154 - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 147, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 149, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 150, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 151, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 152, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 155, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 157, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 158, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 165, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 166, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 168, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 174, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 175, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 180, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 182, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 183, - - // Node 155 - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 147, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 147, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 149, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 149, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 150, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 150, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 151, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 151, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 152, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 152, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 155, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 155, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 157, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 157, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 158, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 158, - - // Node 156 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 147, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 147, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 147, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 147, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 149, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 149, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 149, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 149, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 150, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 150, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 150, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 150, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 151, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 151, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 151, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 151, - - // Node 157 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 147, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 147, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 147, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 147, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 147, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 147, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 147, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 147, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 149, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 149, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 149, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 149, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 149, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 149, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 149, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 149, - - // Node 158 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 150, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 150, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 150, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 150, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 150, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 150, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 150, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 150, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 151, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 151, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 151, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 151, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 151, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 151, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 151, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 151, - - // Node 159 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 152, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 152, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 152, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 152, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 155, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 155, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 155, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 155, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 157, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 157, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 157, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 157, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 158, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 158, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 158, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 158, - - // Node 160 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 152, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 152, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 152, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 152, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 152, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 152, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 152, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 152, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 155, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 155, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 155, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 155, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 155, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 155, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 155, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 155, - - // Node 161 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 157, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 157, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 157, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 157, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 157, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 157, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 157, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 157, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 158, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 158, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 158, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 158, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 158, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 158, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 158, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 158, - - // Node 162 - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 165, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 165, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 166, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 166, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 168, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 168, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 174, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 174, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 175, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 175, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 180, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 180, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 182, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 182, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 183, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 183, - - // Node 163 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 165, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 165, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 165, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 165, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 166, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 166, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 166, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 166, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 168, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 168, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 168, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 168, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 174, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 174, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 174, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 174, - - // Node 164 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 165, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 165, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 165, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 165, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 165, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 165, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 165, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 165, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 166, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 166, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 166, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 166, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 166, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 166, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 166, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 166, - - // Node 165 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 168, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 168, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 168, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 168, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 168, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 168, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 168, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 168, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 174, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 174, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 174, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 174, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 174, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 174, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 174, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 174, - - // Node 166 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 175, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 175, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 175, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 175, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 180, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 180, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 180, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 180, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 182, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 182, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 182, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 182, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 183, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 183, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 183, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 183, - - // Node 167 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 175, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 175, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 175, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 175, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 175, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 175, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 175, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 175, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 180, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 180, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 180, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 180, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 180, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 180, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 180, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 180, - - // Node 168 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 182, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 182, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 182, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 182, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 182, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 182, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 182, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 182, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 183, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 183, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 183, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 183, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 183, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 183, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 183, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 183, - - // Node 169 - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 188, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 191, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 197, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 231, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 239, - 176 << 16, - 178 << 16, - 179 << 16, - 183 << 16, - 184 << 16, - 186 << 16, - 187 << 16, - 192 << 16, - 199 << 16, - 208 << 16, - 223 << 16, - - // Node 170 - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 188, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 188, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 191, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 191, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 197, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 197, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 231, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 231, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 239, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 239, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 9, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 142, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 144, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 145, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 148, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 159, - - // Node 171 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 188, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 188, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 188, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 188, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 191, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 191, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 191, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 191, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 197, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 197, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 197, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 197, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 231, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 231, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 231, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 231, - - // Node 172 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 188, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 188, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 188, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 188, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 188, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 188, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 188, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 188, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 191, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 191, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 191, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 191, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 191, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 191, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 191, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 191, - - // Node 173 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 197, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 197, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 197, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 197, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 197, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 197, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 197, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 197, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 231, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 231, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 231, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 231, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 231, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 231, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 231, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 231, - - // Node 174 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 239, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 239, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 239, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 239, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 9, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 9, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 142, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 142, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 144, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 144, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 145, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 145, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 148, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 148, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 159, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 159, - - // Node 175 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 239, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 239, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 239, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 239, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 239, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 239, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 239, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 239, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 9, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 9, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 9, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 9, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 142, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 142, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 142, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 142, - - // Node 176 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 9, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 9, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 9, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 9, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 9, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 9, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 9, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 9, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 142, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 142, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 142, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 142, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 142, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 142, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 142, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 142, - - // Node 177 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 144, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 144, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 144, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 144, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 145, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 145, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 145, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 145, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 148, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 148, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 148, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 148, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 159, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 159, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 159, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 159, - - // Node 178 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 144, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 144, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 144, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 144, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 144, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 144, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 144, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 144, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 145, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 145, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 145, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 145, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 145, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 145, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 145, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 145, - - // Node 179 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 148, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 148, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 148, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 148, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 148, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 148, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 148, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 148, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 159, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 159, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 159, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 159, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 159, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 159, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 159, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 159, - - // Node 180 - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 171, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 206, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 215, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 225, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 236, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 237, - 188 << 16, - 189 << 16, - 193 << 16, - 196 << 16, - 200 << 16, - 203 << 16, - 209 << 16, - 216 << 16, - 224 << 16, - 238 << 16, - - // Node 181 - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 171, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 171, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 206, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 206, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 215, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 215, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 225, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 225, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 236, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 236, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 237, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 237, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 199, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 207, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 234, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 235, - - // Node 182 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 171, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 171, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 171, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 171, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 206, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 206, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 206, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 206, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 215, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 215, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 215, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 215, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 225, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 225, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 225, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 225, - - // Node 183 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 171, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 171, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 171, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 171, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 171, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 171, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 171, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 171, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 206, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 206, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 206, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 206, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 206, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 206, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 206, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 206, - - // Node 184 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 215, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 215, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 215, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 215, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 215, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 215, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 215, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 215, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 225, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 225, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 225, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 225, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 225, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 225, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 225, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 225, - - // Node 185 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 236, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 236, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 236, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 236, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 237, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 237, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 237, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 237, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 199, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 199, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 207, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 207, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 234, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 234, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 235, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 235, - - // Node 186 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 236, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 236, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 236, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 236, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 236, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 236, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 236, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 236, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 237, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 237, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 237, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 237, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 237, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 237, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 237, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 237, - - // Node 187 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 199, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 199, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 199, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 199, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 207, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 207, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 207, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 207, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 234, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 234, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 234, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 234, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 235, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 235, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 235, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 235, - - // Node 188 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 199, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 199, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 199, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 199, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 199, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 199, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 199, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 199, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 207, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 207, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 207, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 207, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 207, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 207, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 207, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 207, - - // Node 189 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 234, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 234, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 234, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 234, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 234, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 234, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 234, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 234, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 235, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 235, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 235, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 235, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 235, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 235, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 235, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 235, - - // Node 190 - 194 << 16, - 195 << 16, - 197 << 16, - 198 << 16, - 201 << 16, - 202 << 16, - 204 << 16, - 205 << 16, - 210 << 16, - 213 << 16, - 217 << 16, - 220 << 16, - 225 << 16, - 231 << 16, - 239 << 16, - 246 << 16, - - // Node 191 - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 192, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 193, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 200, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 201, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 202, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 205, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 210, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 213, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 218, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 219, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 238, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 240, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 242, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 243, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 255, - 206 << 16, - - // Node 192 - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 192, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 192, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 193, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 193, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 200, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 200, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 201, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 201, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 202, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 202, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 205, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 205, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 210, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 210, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 213, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 213, - - // Node 193 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 192, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 192, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 192, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 192, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 193, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 193, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 193, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 193, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 200, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 200, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 200, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 200, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 201, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 201, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 201, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 201, - - // Node 194 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 192, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 192, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 192, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 192, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 192, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 192, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 192, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 192, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 193, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 193, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 193, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 193, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 193, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 193, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 193, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 193, - - // Node 195 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 200, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 200, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 200, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 200, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 200, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 200, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 200, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 200, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 201, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 201, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 201, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 201, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 201, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 201, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 201, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 201, - - // Node 196 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 202, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 202, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 202, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 202, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 205, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 205, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 205, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 205, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 210, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 210, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 210, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 210, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 213, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 213, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 213, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 213, - - // Node 197 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 202, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 202, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 202, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 202, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 202, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 202, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 202, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 202, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 205, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 205, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 205, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 205, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 205, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 205, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 205, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 205, - - // Node 198 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 210, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 210, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 210, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 210, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 210, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 210, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 210, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 210, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 213, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 213, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 213, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 213, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 213, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 213, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 213, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 213, - - // Node 199 - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 218, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 218, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 219, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 219, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 238, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 238, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 240, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 240, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 242, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 242, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 243, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 243, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 255, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 255, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 203, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 204, - - // Node 200 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 218, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 218, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 218, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 218, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 219, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 219, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 219, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 219, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 238, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 238, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 238, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 238, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 240, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 240, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 240, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 240, - - // Node 201 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 218, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 218, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 218, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 218, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 218, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 218, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 218, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 218, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 219, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 219, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 219, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 219, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 219, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 219, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 219, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 219, - - // Node 202 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 238, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 238, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 238, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 238, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 238, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 238, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 238, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 238, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 240, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 240, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 240, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 240, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 240, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 240, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 240, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 240, - - // Node 203 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 242, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 242, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 242, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 242, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 243, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 243, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 243, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 243, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 255, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 255, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 255, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 255, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 203, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 203, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 204, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 204, - - // Node 204 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 242, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 242, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 242, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 242, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 242, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 242, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 242, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 242, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 243, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 243, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 243, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 243, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 243, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 243, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 243, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 243, - - // Node 205 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 255, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 255, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 255, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 255, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 255, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 255, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 255, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 255, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 203, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 203, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 203, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 203, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 204, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 204, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 204, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 204, - - // Node 206 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 203, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 203, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 203, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 203, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 203, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 203, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 203, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 203, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 204, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 204, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 204, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 204, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 204, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 204, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 204, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 204, - - // Node 207 - 211 << 16, - 212 << 16, - 214 << 16, - 215 << 16, - 218 << 16, - 219 << 16, - 221 << 16, - 222 << 16, - 226 << 16, - 228 << 16, - 232 << 16, - 235 << 16, - 240 << 16, - 243 << 16, - 247 << 16, - 250 << 16, - - // Node 208 - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 211, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 212, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 214, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 221, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 222, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 223, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 241, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 244, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 245, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 246, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 247, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 248, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 250, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 251, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 252, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 253, - - // Node 209 - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 211, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 211, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 212, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 212, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 214, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 214, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 221, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 221, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 222, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 222, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 223, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 223, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 241, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 241, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 244, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 244, - - // Node 210 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 211, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 211, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 211, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 211, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 212, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 212, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 212, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 212, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 214, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 214, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 214, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 214, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 221, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 221, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 221, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 221, - - // Node 211 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 211, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 211, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 211, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 211, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 211, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 211, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 211, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 211, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 212, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 212, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 212, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 212, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 212, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 212, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 212, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 212, - - // Node 212 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 214, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 214, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 214, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 214, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 214, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 214, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 214, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 214, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 221, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 221, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 221, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 221, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 221, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 221, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 221, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 221, - - // Node 213 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 222, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 222, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 222, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 222, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 223, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 223, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 223, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 223, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 241, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 241, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 241, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 241, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 244, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 244, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 244, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 244, - - // Node 214 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 222, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 222, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 222, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 222, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 222, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 222, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 222, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 222, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 223, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 223, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 223, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 223, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 223, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 223, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 223, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 223, - - // Node 215 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 241, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 241, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 241, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 241, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 241, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 241, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 241, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 241, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 244, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 244, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 244, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 244, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 244, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 244, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 244, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 244, - - // Node 216 - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 245, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 245, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 246, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 246, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 247, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 247, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 248, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 248, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 250, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 250, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 251, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 251, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 252, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 252, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 253, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 253, - - // Node 217 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 245, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 245, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 245, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 245, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 246, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 246, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 246, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 246, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 247, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 247, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 247, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 247, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 248, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 248, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 248, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 248, - - // Node 218 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 245, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 245, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 245, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 245, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 245, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 245, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 245, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 245, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 246, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 246, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 246, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 246, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 246, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 246, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 246, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 246, - - // Node 219 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 247, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 247, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 247, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 247, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 247, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 247, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 247, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 247, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 248, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 248, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 248, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 248, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 248, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 248, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 248, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 248, - - // Node 220 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 250, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 250, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 250, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 250, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 251, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 251, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 251, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 251, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 252, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 252, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 252, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 252, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 253, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 253, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 253, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 253, - - // Node 221 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 250, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 250, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 250, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 250, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 250, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 250, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 250, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 250, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 251, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 251, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 251, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 251, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 251, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 251, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 251, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 251, - - // Node 222 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 252, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 252, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 252, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 252, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 252, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 252, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 252, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 252, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 253, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 253, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 253, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 253, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 253, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 253, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 253, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 253, - - // Node 223 - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 254, - 227 << 16, - 229 << 16, - 230 << 16, - 233 << 16, - 234 << 16, - 236 << 16, - 237 << 16, - 241 << 16, - 242 << 16, - 244 << 16, - 245 << 16, - 248 << 16, - 249 << 16, - 251 << 16, - 252 << 16, - - // Node 224 - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 254, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 254, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 2, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 3, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 4, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 5, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 6, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 7, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 8, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 11, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 12, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 14, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 15, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 16, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 17, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 18, - - // Node 225 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 254, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 254, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 254, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 254, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 2, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 2, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 3, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 3, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 4, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 4, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 5, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 5, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 6, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 6, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 7, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 7, - - // Node 226 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 254, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 254, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 254, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 254, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 254, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 254, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 254, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 254, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 2, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 2, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 2, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 2, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 3, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 3, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 3, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 3, - - // Node 227 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 2, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 2, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 2, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 2, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 2, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 2, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 2, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 2, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 3, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 3, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 3, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 3, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 3, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 3, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 3, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 3, - - // Node 228 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 4, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 4, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 4, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 4, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 5, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 5, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 5, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 5, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 6, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 6, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 6, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 6, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 7, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 7, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 7, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 7, - - // Node 229 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 4, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 4, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 4, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 4, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 4, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 4, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 4, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 4, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 5, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 5, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 5, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 5, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 5, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 5, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 5, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 5, - - // Node 230 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 6, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 6, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 6, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 6, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 6, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 6, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 6, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 6, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 7, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 7, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 7, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 7, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 7, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 7, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 7, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 7, - - // Node 231 - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 8, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 8, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 11, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 11, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 12, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 12, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 14, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 14, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 15, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 15, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 16, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 16, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 17, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 17, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 18, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 18, - - // Node 232 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 8, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 8, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 8, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 8, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 11, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 11, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 11, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 11, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 12, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 12, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 12, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 12, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 14, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 14, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 14, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 14, - - // Node 233 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 8, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 8, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 8, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 8, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 8, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 8, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 8, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 8, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 11, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 11, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 11, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 11, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 11, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 11, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 11, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 11, - - // Node 234 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 12, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 12, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 12, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 12, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 12, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 12, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 12, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 12, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 14, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 14, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 14, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 14, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 14, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 14, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 14, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 14, - - // Node 235 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 15, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 15, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 15, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 15, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 16, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 16, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 16, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 16, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 17, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 17, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 17, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 17, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 18, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 18, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 18, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 18, - - // Node 236 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 15, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 15, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 15, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 15, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 15, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 15, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 15, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 15, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 16, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 16, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 16, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 16, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 16, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 16, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 16, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 16, - - // Node 237 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 17, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 17, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 17, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 17, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 17, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 17, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 17, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 17, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 18, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 18, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 18, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 18, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 18, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 18, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 18, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 18, - - // Node 238 - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 19, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 20, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 21, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 23, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 24, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 25, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 26, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 27, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 28, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 29, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 30, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 31, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 127, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 220, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 249, - 253 << 16, - - // Node 239 - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 19, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 19, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 20, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 20, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 21, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 21, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 23, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 23, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 24, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 24, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 25, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 25, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 26, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 26, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 27, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 27, - - // Node 240 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 19, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 19, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 19, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 19, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 20, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 20, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 20, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 20, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 21, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 21, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 21, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 21, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 23, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 23, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 23, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 23, - - // Node 241 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 19, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 19, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 19, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 19, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 19, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 19, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 19, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 19, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 20, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 20, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 20, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 20, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 20, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 20, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 20, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 20, - - // Node 242 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 21, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 21, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 21, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 21, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 21, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 21, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 21, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 21, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 23, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 23, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 23, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 23, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 23, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 23, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 23, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 23, - - // Node 243 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 24, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 24, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 24, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 24, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 25, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 25, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 25, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 25, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 26, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 26, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 26, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 26, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 27, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 27, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 27, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 27, - - // Node 244 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 24, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 24, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 24, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 24, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 24, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 24, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 24, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 24, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 25, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 25, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 25, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 25, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 25, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 25, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 25, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 25, - - // Node 245 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 26, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 26, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 26, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 26, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 26, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 26, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 26, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 26, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 27, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 27, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 27, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 27, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 27, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 27, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 27, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 27, - - // Node 246 - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 28, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 28, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 29, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 29, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 30, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 30, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 31, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 31, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 127, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 127, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 220, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 220, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 249, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 249, - 254 << 16, - 255 << 16, - - // Node 247 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 28, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 28, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 28, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 28, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 29, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 29, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 29, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 29, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 30, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 30, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 30, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 30, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 31, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 31, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 31, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 31, - - // Node 248 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 28, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 28, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 28, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 28, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 28, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 28, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 28, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 28, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 29, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 29, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 29, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 29, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 29, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 29, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 29, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 29, - - // Node 249 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 30, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 30, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 30, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 30, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 30, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 30, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 30, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 30, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 31, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 31, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 31, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 31, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 31, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 31, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 31, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 31, - - // Node 250 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 127, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 127, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 127, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 127, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 220, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 220, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 220, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 220, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 249, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 249, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 249, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 249, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 10, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 13, - ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 22, - HUFFMAN_FAIL << 8, - - // Node 251 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 127, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 127, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 127, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 127, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 127, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 127, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 127, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 127, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 220, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 220, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 220, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 220, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 220, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 220, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 220, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 220, - - // Node 252 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 249, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 249, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 249, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 249, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 249, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 249, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 249, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 249, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 10, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 10, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 13, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 13, - (1 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 22, - (22 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 22, - HUFFMAN_FAIL << 8, - HUFFMAN_FAIL << 8, - - // Node 253 - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 10, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 10, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 10, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 10, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 13, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 13, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 13, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 13, - (2 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 22, - (9 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 22, - (23 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 22, - (40 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 22, - HUFFMAN_FAIL << 8, - HUFFMAN_FAIL << 8, - HUFFMAN_FAIL << 8, - HUFFMAN_FAIL << 8, - - // Node 254 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 10, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 10, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 10, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 10, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 10, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 10, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 10, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 10, - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 13, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 13, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 13, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 13, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 13, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 13, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 13, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 13, - - // Node 255 - (3 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 22, - (6 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 22, - (10 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 22, - (15 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 22, - (24 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 22, - (31 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 22, - (41 << 16) + (HUFFMAN_EMIT_SYMBOL << 8) + 22, - (56 << 16) + ((HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL) << 8) + 22, - HUFFMAN_FAIL << 8, - HUFFMAN_FAIL << 8, - HUFFMAN_FAIL << 8, - HUFFMAN_FAIL << 8, - HUFFMAN_FAIL << 8, - HUFFMAN_FAIL << 8, - HUFFMAN_FAIL << 8, - HUFFMAN_FAIL << 8, - }; - - private static final Http2Exception BAD_ENCODING = - Http2Exception.newStatic(COMPRESSION_ERROR, "HPACK - Bad Encoding", - Http2Exception.ShutdownHint.HARD_SHUTDOWN, HpackHuffmanDecoder.class, "decode(..)"); - - private byte[] dest; - private int k; - private int state; - - HpackHuffmanDecoder() { } - - /** - * Decompresses the given Huffman coded string literal. - * - * @param buf the string literal to be decoded - * @return the output stream for the compressed data - * @throws Http2Exception EOS Decoded - */ - public AsciiString decode(ByteBuf buf, int length) throws Http2Exception { - if (length == 0) { - return AsciiString.EMPTY_STRING; - } - dest = new byte[length * 8 / 5]; - try { - int readerIndex = buf.readerIndex(); - // Using ByteProcessor to reduce bounds-checking and reference-count checking during byte-by-byte - // processing of the ByteBuf. - int endIndex = buf.forEachByte(readerIndex, length, this); - if (endIndex == -1) { - // We did consume the requested length - buf.readerIndex(readerIndex + length); - if ((state & HUFFMAN_COMPLETE_SHIFT) != HUFFMAN_COMPLETE_SHIFT) { - throw BAD_ENCODING; - } - return new AsciiString(dest, 0, k, false); - } - - // The process(...) method returned before the requested length was requested. This means there - // was a bad encoding detected. - buf.readerIndex(endIndex); - throw BAD_ENCODING; - } finally { - dest = null; - k = 0; - state = 0; - } - } - - /** - * This should never be called from anything but this class itself! - */ - @Override - public boolean process(byte input) { - return processNibble(input >> 4) && processNibble(input); - } - - private boolean processNibble(int input) { - // The high nibble of the flags byte of each row is always zero - // (low nibble after shifting row by 12), since there are only 3 flag bits - int index = state >> 12 | (input & 0x0F); - state = HUFFS[index]; - if ((state & HUFFMAN_FAIL_SHIFT) != 0) { - return false; - } - if ((state & HUFFMAN_EMIT_SYMBOL_SHIFT) != 0) { - // state is always positive so can cast without mask here - dest[k++] = (byte) state; - } - return true; - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/HpackHuffmanEncoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/HpackHuffmanEncoder.java deleted file mode 100644 index 9a6a9a7512..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/HpackHuffmanEncoder.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/* - * Copyright 2014 Twitter, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.util.AsciiString; -import io.netty.util.ByteProcessor; - -import static java.util.Objects.requireNonNull; - -final class HpackHuffmanEncoder { - - private final int[] codes; - private final byte[] lengths; - private final EncodedLengthProcessor encodedLengthProcessor = new EncodedLengthProcessor(); - private final EncodeProcessor encodeProcessor = new EncodeProcessor(); - - HpackHuffmanEncoder() { - this(HpackUtil.HUFFMAN_CODES, HpackUtil.HUFFMAN_CODE_LENGTHS); - } - - /** - * Creates a new Huffman encoder with the specified Huffman coding. - * - * @param codes the Huffman codes indexed by symbol - * @param lengths the length of each Huffman code - */ - private HpackHuffmanEncoder(int[] codes, byte[] lengths) { - this.codes = codes; - this.lengths = lengths; - } - - /** - * Compresses the input string literal using the Huffman coding. - * - * @param out the output stream for the compressed data - * @param data the string literal to be Huffman encoded - */ - public void encode(ByteBuf out, CharSequence data) { - requireNonNull(out, "out"); - if (data instanceof AsciiString) { - AsciiString string = (AsciiString) data; - encodeProcessor.out = out; - try { - string.forEachByte(encodeProcessor); - } finally { - encodeProcessor.end(); - } - } else { - encodeSlowPath(out, data); - } - } - - private void encodeSlowPath(ByteBuf out, CharSequence data) { - long current = 0; - int n = 0; - - for (int i = 0; i < data.length(); i++) { - int b = data.charAt(i) & 0xFF; - int code = codes[b]; - int nbits = lengths[b]; - - current <<= nbits; - current |= code; - n += nbits; - - while (n >= 8) { - n -= 8; - out.writeByte((int) (current >> n)); - } - } - - if (n > 0) { - current <<= 8 - n; - current |= 0xFF >>> n; // this should be EOS symbol - out.writeByte((int) current); - } - } - - /** - * Returns the number of bytes required to Huffman encode the input string literal. - * - * @param data the string literal to be Huffman encoded - * @return the number of bytes required to Huffman encode {@code data} - */ - int getEncodedLength(CharSequence data) { - if (data instanceof AsciiString) { - AsciiString string = (AsciiString) data; - encodedLengthProcessor.reset(); - string.forEachByte(encodedLengthProcessor); - return encodedLengthProcessor.length(); - } else { - return getEncodedLengthSlowPath(data); - } - } - - private int getEncodedLengthSlowPath(CharSequence data) { - long len = 0; - for (int i = 0; i < data.length(); i++) { - len += lengths[data.charAt(i) & 0xFF]; - } - return (int) (len + 7 >> 3); - } - - private final class EncodeProcessor implements ByteProcessor { - ByteBuf out; - private long current; - private int n; - - @Override - public boolean process(byte value) { - int b = value & 0xFF; - int nbits = lengths[b]; - - current <<= nbits; - current |= codes[b]; - n += nbits; - - while (n >= 8) { - n -= 8; - out.writeByte((int) (current >> n)); - } - return true; - } - - void end() { - try { - if (n > 0) { - current <<= 8 - n; - current |= 0xFF >>> n; // this should be EOS symbol - out.writeByte((int) current); - } - } finally { - out = null; - current = 0; - n = 0; - } - } - } - - private final class EncodedLengthProcessor implements ByteProcessor { - private long len; - - @Override - public boolean process(byte value) { - len += lengths[value & 0xFF]; - return true; - } - - void reset() { - len = 0; - } - - int length() { - return (int) (len + 7 >> 3); - } - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/HpackStaticTable.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/HpackStaticTable.java deleted file mode 100644 index 8ab65ce4bf..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/HpackStaticTable.java +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/* - * Copyright 2014 Twitter, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.handler.codec.UnsupportedValueConverter; -import io.netty.util.AsciiString; - -import java.util.Arrays; -import java.util.List; - -import static io.netty.handler.codec.http2.HpackUtil.equalsVariableTime; - -final class HpackStaticTable { - - static final int NOT_FOUND = -1; - - // Appendix A: Static Table - // https://tools.ietf.org/html/rfc7541#appendix-A - private static final List STATIC_TABLE = Arrays.asList( - /* 1 */ newEmptyHeaderField(":authority"), - /* 2 */ newHeaderField(":method", "GET"), - /* 3 */ newHeaderField(":method", "POST"), - /* 4 */ newHeaderField(":path", "/"), - /* 5 */ newHeaderField(":path", "/index.html"), - /* 6 */ newHeaderField(":scheme", "http"), - /* 7 */ newHeaderField(":scheme", "https"), - /* 8 */ newHeaderField(":status", "200"), - /* 9 */ newHeaderField(":status", "204"), - /* 10 */ newHeaderField(":status", "206"), - /* 11 */ newHeaderField(":status", "304"), - /* 12 */ newHeaderField(":status", "400"), - /* 13 */ newHeaderField(":status", "404"), - /* 14 */ newHeaderField(":status", "500"), - /* 15 */ newEmptyHeaderField("accept-charset"), - /* 16 */ newHeaderField("accept-encoding", "gzip, deflate"), - /* 17 */ newEmptyHeaderField("accept-language"), - /* 18 */ newEmptyHeaderField("accept-ranges"), - /* 19 */ newEmptyHeaderField("accept"), - /* 20 */ newEmptyHeaderField("access-control-allow-origin"), - /* 21 */ newEmptyHeaderField("age"), - /* 22 */ newEmptyHeaderField("allow"), - /* 23 */ newEmptyHeaderField("authorization"), - /* 24 */ newEmptyHeaderField("cache-control"), - /* 25 */ newEmptyHeaderField("content-disposition"), - /* 26 */ newEmptyHeaderField("content-encoding"), - /* 27 */ newEmptyHeaderField("content-language"), - /* 28 */ newEmptyHeaderField("content-length"), - /* 29 */ newEmptyHeaderField("content-location"), - /* 30 */ newEmptyHeaderField("content-range"), - /* 31 */ newEmptyHeaderField("content-type"), - /* 32 */ newEmptyHeaderField("cookie"), - /* 33 */ newEmptyHeaderField("date"), - /* 34 */ newEmptyHeaderField("etag"), - /* 35 */ newEmptyHeaderField("expect"), - /* 36 */ newEmptyHeaderField("expires"), - /* 37 */ newEmptyHeaderField("from"), - /* 38 */ newEmptyHeaderField("host"), - /* 39 */ newEmptyHeaderField("if-match"), - /* 40 */ newEmptyHeaderField("if-modified-since"), - /* 41 */ newEmptyHeaderField("if-none-match"), - /* 42 */ newEmptyHeaderField("if-range"), - /* 43 */ newEmptyHeaderField("if-unmodified-since"), - /* 44 */ newEmptyHeaderField("last-modified"), - /* 45 */ newEmptyHeaderField("link"), - /* 46 */ newEmptyHeaderField("location"), - /* 47 */ newEmptyHeaderField("max-forwards"), - /* 48 */ newEmptyHeaderField("proxy-authenticate"), - /* 49 */ newEmptyHeaderField("proxy-authorization"), - /* 50 */ newEmptyHeaderField("range"), - /* 51 */ newEmptyHeaderField("referer"), - /* 52 */ newEmptyHeaderField("refresh"), - /* 53 */ newEmptyHeaderField("retry-after"), - /* 54 */ newEmptyHeaderField("server"), - /* 55 */ newEmptyHeaderField("set-cookie"), - /* 56 */ newEmptyHeaderField("strict-transport-security"), - /* 57 */ newEmptyHeaderField("transfer-encoding"), - /* 58 */ newEmptyHeaderField("user-agent"), - /* 59 */ newEmptyHeaderField("vary"), - /* 60 */ newEmptyHeaderField("via"), - /* 61 */ newEmptyHeaderField("www-authenticate") - ); - - private static HpackHeaderField newEmptyHeaderField(String name) { - return new HpackHeaderField(AsciiString.cached(name), AsciiString.EMPTY_STRING); - } - - private static HpackHeaderField newHeaderField(String name, String value) { - return new HpackHeaderField(AsciiString.cached(name), AsciiString.cached(value)); - } - - private static final CharSequenceMap STATIC_INDEX_BY_NAME = createMap(); - - private static final int MAX_SAME_NAME_FIELD_INDEX = maxSameNameFieldIndex(); - - /** - * The number of header fields in the static table. - */ - static final int length = STATIC_TABLE.size(); - - /** - * Return the header field at the given index value. - */ - static HpackHeaderField getEntry(int index) { - return STATIC_TABLE.get(index - 1); - } - - /** - * Returns the lowest index value for the given header field name in the static table. Returns - * -1 if the header field name is not in the static table. - */ - static int getIndex(CharSequence name) { - Integer index = STATIC_INDEX_BY_NAME.get(name); - if (index == null) { - return NOT_FOUND; - } - return index; - } - - /** - * Returns the index value for the given header field in the static table. Returns -1 if the - * header field is not in the static table. - */ - static int getIndexInsensitive(CharSequence name, CharSequence value) { - int index = getIndex(name); - if (index == NOT_FOUND) { - return NOT_FOUND; - } - - // Compare values for the first name match - HpackHeaderField entry = getEntry(index); - if (equalsVariableTime(value, entry.value)) { - return index; - } - - // Note this assumes all entries for a given header field are sequential. - index++; - while (index <= MAX_SAME_NAME_FIELD_INDEX) { - entry = getEntry(index); - if (!equalsVariableTime(name, entry.name)) { - // As far as fields with the same name are placed in the table sequentialy - // and INDEX_BY_NAME returns index of the fist position, - it's safe to - // exit immediatly. - return NOT_FOUND; - } - if (equalsVariableTime(value, entry.value)) { - return index; - } - index++; - } - - return NOT_FOUND; - } - - // create a map CharSequenceMap header name to index value to allow quick lookup - private static CharSequenceMap createMap() { - int length = STATIC_TABLE.size(); - @SuppressWarnings("unchecked") - CharSequenceMap ret = new CharSequenceMap<>(true, - UnsupportedValueConverter.instance(), length); - // Iterate through the static table in reverse order to - // save the smallest index for a given name in the map. - for (int index = length; index > 0; index--) { - HpackHeaderField entry = getEntry(index); - CharSequence name = entry.name; - ret.set(name, index); - } - return ret; - } - - /** - * Returns the last position in the array that contains multiple - * fields with the same name. Starting from this position, all - * names are unique. Similary to {@link #getIndexInsensitive(CharSequence, CharSequence)} method - * assumes all entries for a given header field are sequential - */ - private static int maxSameNameFieldIndex() { - final int length = STATIC_TABLE.size(); - HpackHeaderField cursor = getEntry(length); - for (int index = length - 1; index > 0; index--) { - HpackHeaderField entry = getEntry(index); - if (equalsVariableTime(entry.name, cursor.name)) { - return index + 1; - } else { - cursor = entry; - } - } - return length; - } - - // singleton - private HpackStaticTable() { - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/HpackUtil.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/HpackUtil.java deleted file mode 100644 index 5c51c52e8d..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/HpackUtil.java +++ /dev/null @@ -1,372 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/* - * Copyright 2014 Twitter, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.util.AsciiString; -import io.netty.util.internal.ConstantTimeUtils; -import io.netty.util.internal.PlatformDependent; - -final class HpackUtil { - /** - * Compare two {@link CharSequence} objects without leaking timing information. - *

- * The {@code int} return type is intentional and is designed to allow cascading of constant time operations: - *

-     *     String s1 = "foo";
-     *     String s2 = "foo";
-     *     String s3 = "foo";
-     *     String s4 = "goo";
-     *     boolean equals = (equalsConstantTime(s1, s2) & equalsConstantTime(s3, s4)) != 0;
-     * 
- * @param s1 the first value. - * @param s2 the second value. - * @return {@code 0} if not equal. {@code 1} if equal. - */ - static int equalsConstantTime(CharSequence s1, CharSequence s2) { - if (s1 instanceof AsciiString && s2 instanceof AsciiString) { - if (s1.length() != s2.length()) { - return 0; - } - AsciiString s1Ascii = (AsciiString) s1; - AsciiString s2Ascii = (AsciiString) s2; - return PlatformDependent.equalsConstantTime(s1Ascii.array(), s1Ascii.arrayOffset(), - s2Ascii.array(), s2Ascii.arrayOffset(), s1.length()); - } - - return ConstantTimeUtils.equalsConstantTime(s1, s2); - } - - /** - * Compare two {@link CharSequence}s. - * @param s1 the first value. - * @param s2 the second value. - * @return {@code false} if not equal. {@code true} if equal. - */ - static boolean equalsVariableTime(CharSequence s1, CharSequence s2) { - return AsciiString.contentEquals(s1, s2); - } - - // Section 6.2. Literal Header Field Representation - enum IndexType { - INCREMENTAL, // Section 6.2.1. Literal Header Field with Incremental Indexing - NONE, // Section 6.2.2. Literal Header Field without Indexing - NEVER // Section 6.2.3. Literal Header Field never Indexed - } - - // Appendix B: Huffman Codes - // https://tools.ietf.org/html/rfc7541#appendix-B - static final int[] HUFFMAN_CODES = { - 0x1ff8, - 0x7fffd8, - 0xfffffe2, - 0xfffffe3, - 0xfffffe4, - 0xfffffe5, - 0xfffffe6, - 0xfffffe7, - 0xfffffe8, - 0xffffea, - 0x3ffffffc, - 0xfffffe9, - 0xfffffea, - 0x3ffffffd, - 0xfffffeb, - 0xfffffec, - 0xfffffed, - 0xfffffee, - 0xfffffef, - 0xffffff0, - 0xffffff1, - 0xffffff2, - 0x3ffffffe, - 0xffffff3, - 0xffffff4, - 0xffffff5, - 0xffffff6, - 0xffffff7, - 0xffffff8, - 0xffffff9, - 0xffffffa, - 0xffffffb, - 0x14, - 0x3f8, - 0x3f9, - 0xffa, - 0x1ff9, - 0x15, - 0xf8, - 0x7fa, - 0x3fa, - 0x3fb, - 0xf9, - 0x7fb, - 0xfa, - 0x16, - 0x17, - 0x18, - 0x0, - 0x1, - 0x2, - 0x19, - 0x1a, - 0x1b, - 0x1c, - 0x1d, - 0x1e, - 0x1f, - 0x5c, - 0xfb, - 0x7ffc, - 0x20, - 0xffb, - 0x3fc, - 0x1ffa, - 0x21, - 0x5d, - 0x5e, - 0x5f, - 0x60, - 0x61, - 0x62, - 0x63, - 0x64, - 0x65, - 0x66, - 0x67, - 0x68, - 0x69, - 0x6a, - 0x6b, - 0x6c, - 0x6d, - 0x6e, - 0x6f, - 0x70, - 0x71, - 0x72, - 0xfc, - 0x73, - 0xfd, - 0x1ffb, - 0x7fff0, - 0x1ffc, - 0x3ffc, - 0x22, - 0x7ffd, - 0x3, - 0x23, - 0x4, - 0x24, - 0x5, - 0x25, - 0x26, - 0x27, - 0x6, - 0x74, - 0x75, - 0x28, - 0x29, - 0x2a, - 0x7, - 0x2b, - 0x76, - 0x2c, - 0x8, - 0x9, - 0x2d, - 0x77, - 0x78, - 0x79, - 0x7a, - 0x7b, - 0x7ffe, - 0x7fc, - 0x3ffd, - 0x1ffd, - 0xffffffc, - 0xfffe6, - 0x3fffd2, - 0xfffe7, - 0xfffe8, - 0x3fffd3, - 0x3fffd4, - 0x3fffd5, - 0x7fffd9, - 0x3fffd6, - 0x7fffda, - 0x7fffdb, - 0x7fffdc, - 0x7fffdd, - 0x7fffde, - 0xffffeb, - 0x7fffdf, - 0xffffec, - 0xffffed, - 0x3fffd7, - 0x7fffe0, - 0xffffee, - 0x7fffe1, - 0x7fffe2, - 0x7fffe3, - 0x7fffe4, - 0x1fffdc, - 0x3fffd8, - 0x7fffe5, - 0x3fffd9, - 0x7fffe6, - 0x7fffe7, - 0xffffef, - 0x3fffda, - 0x1fffdd, - 0xfffe9, - 0x3fffdb, - 0x3fffdc, - 0x7fffe8, - 0x7fffe9, - 0x1fffde, - 0x7fffea, - 0x3fffdd, - 0x3fffde, - 0xfffff0, - 0x1fffdf, - 0x3fffdf, - 0x7fffeb, - 0x7fffec, - 0x1fffe0, - 0x1fffe1, - 0x3fffe0, - 0x1fffe2, - 0x7fffed, - 0x3fffe1, - 0x7fffee, - 0x7fffef, - 0xfffea, - 0x3fffe2, - 0x3fffe3, - 0x3fffe4, - 0x7ffff0, - 0x3fffe5, - 0x3fffe6, - 0x7ffff1, - 0x3ffffe0, - 0x3ffffe1, - 0xfffeb, - 0x7fff1, - 0x3fffe7, - 0x7ffff2, - 0x3fffe8, - 0x1ffffec, - 0x3ffffe2, - 0x3ffffe3, - 0x3ffffe4, - 0x7ffffde, - 0x7ffffdf, - 0x3ffffe5, - 0xfffff1, - 0x1ffffed, - 0x7fff2, - 0x1fffe3, - 0x3ffffe6, - 0x7ffffe0, - 0x7ffffe1, - 0x3ffffe7, - 0x7ffffe2, - 0xfffff2, - 0x1fffe4, - 0x1fffe5, - 0x3ffffe8, - 0x3ffffe9, - 0xffffffd, - 0x7ffffe3, - 0x7ffffe4, - 0x7ffffe5, - 0xfffec, - 0xfffff3, - 0xfffed, - 0x1fffe6, - 0x3fffe9, - 0x1fffe7, - 0x1fffe8, - 0x7ffff3, - 0x3fffea, - 0x3fffeb, - 0x1ffffee, - 0x1ffffef, - 0xfffff4, - 0xfffff5, - 0x3ffffea, - 0x7ffff4, - 0x3ffffeb, - 0x7ffffe6, - 0x3ffffec, - 0x3ffffed, - 0x7ffffe7, - 0x7ffffe8, - 0x7ffffe9, - 0x7ffffea, - 0x7ffffeb, - 0xffffffe, - 0x7ffffec, - 0x7ffffed, - 0x7ffffee, - 0x7ffffef, - 0x7fffff0, - 0x3ffffee, - 0x3fffffff // EOS - }; - - static final byte[] HUFFMAN_CODE_LENGTHS = { - 13, 23, 28, 28, 28, 28, 28, 28, 28, 24, 30, 28, 28, 30, 28, 28, - 28, 28, 28, 28, 28, 28, 30, 28, 28, 28, 28, 28, 28, 28, 28, 28, - 6, 10, 10, 12, 13, 6, 8, 11, 10, 10, 8, 11, 8, 6, 6, 6, - 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 8, 15, 6, 12, 10, - 13, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 8, 7, 8, 13, 19, 13, 14, 6, - 15, 5, 6, 5, 6, 5, 6, 6, 6, 5, 7, 7, 6, 6, 6, 5, - 6, 7, 6, 5, 5, 6, 7, 7, 7, 7, 7, 15, 11, 14, 13, 28, - 20, 22, 20, 20, 22, 22, 22, 23, 22, 23, 23, 23, 23, 23, 24, 23, - 24, 24, 22, 23, 24, 23, 23, 23, 23, 21, 22, 23, 22, 23, 23, 24, - 22, 21, 20, 22, 22, 23, 23, 21, 23, 22, 22, 24, 21, 22, 23, 23, - 21, 21, 22, 21, 23, 22, 23, 23, 20, 22, 22, 22, 23, 22, 22, 23, - 26, 26, 20, 19, 22, 23, 22, 25, 26, 26, 26, 27, 27, 26, 24, 25, - 19, 21, 26, 27, 27, 26, 27, 24, 21, 21, 26, 26, 28, 27, 27, 27, - 20, 24, 20, 21, 22, 21, 21, 23, 22, 22, 25, 25, 24, 24, 26, 23, - 26, 27, 26, 26, 27, 27, 27, 27, 27, 28, 27, 27, 27, 27, 27, 26, - 30 // EOS - }; - - static final int HUFFMAN_EOS = 256; - - private HpackUtil() { - // utility class - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ChannelDuplexHandler.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ChannelDuplexHandler.java deleted file mode 100644 index abd902c951..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ChannelDuplexHandler.java +++ /dev/null @@ -1,94 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.UnstableApi; - -/** - * A {@link ChannelHandler} providing additional functionality for HTTP/2. Specifically it allows to: - *
    - *
  • Create new outbound streams using {@link #newStream()}.
  • - *
  • Iterate over all active streams using {@link #forEachActiveStream(Http2FrameStreamVisitor)}.
  • - *
- * - *

The {@link Http2FrameCodec} is required to be part of the {@link ChannelPipeline} before this handler is added, - * or else an {@link IllegalStateException} will be thrown. - */ -@UnstableApi -public abstract class Http2ChannelDuplexHandler implements ChannelHandler { - - private volatile Http2FrameCodec frameCodec; - - @Override - public final void handlerAdded(ChannelHandlerContext ctx) throws Exception { - frameCodec = requireHttp2FrameCodec(ctx); - handlerAdded0(ctx); - } - - protected void handlerAdded0(@SuppressWarnings("unused") ChannelHandlerContext ctx) throws Exception { - // NOOP - } - - @Override - public final void handlerRemoved(ChannelHandlerContext ctx) throws Exception { - try { - handlerRemoved0(ctx); - } finally { - frameCodec = null; - } - } - - protected void handlerRemoved0(@SuppressWarnings("unused") ChannelHandlerContext ctx) throws Exception { - // NOOP - } - - /** - * Creates a new {@link Http2FrameStream} object. - * - *

This method is thread-safe. - */ - public final Http2FrameStream newStream() { - Http2FrameCodec codec = frameCodec; - if (codec == null) { - throw new IllegalStateException(StringUtil.simpleClassName(Http2FrameCodec.class) + " not found." + - " Has the handler been added to a pipeline?"); - } - return codec.newStream(); - } - - /** - * Allows to iterate over all currently active streams. - * - *

This method may only be called from the eventloop thread. - */ - protected final void forEachActiveStream(Http2FrameStreamVisitor streamVisitor) throws Http2Exception { - frameCodec.forEachActiveStream(streamVisitor); - } - - private static Http2FrameCodec requireHttp2FrameCodec(ChannelHandlerContext ctx) { - ChannelHandlerContext frameCodecCtx = ctx.pipeline().context(Http2FrameCodec.class); - if (frameCodecCtx == null) { - throw new IllegalArgumentException(Http2FrameCodec.class.getSimpleName() - + " was not found in the channel pipeline."); - } - return (Http2FrameCodec) frameCodecCtx.handler(); - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ClientUpgradeCodec.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ClientUpgradeCodec.java deleted file mode 100644 index 3ae449840f..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ClientUpgradeCodec.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.base64.Base64; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpClientUpgradeHandler; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.util.collection.CharObjectMap; -import io.netty.util.internal.UnstableApi; - -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -import static io.netty.handler.codec.base64.Base64Dialect.URL_SAFE; -import static io.netty.handler.codec.http2.Http2CodecUtil.HTTP_UPGRADE_PROTOCOL_NAME; -import static io.netty.handler.codec.http2.Http2CodecUtil.HTTP_UPGRADE_SETTINGS_HEADER; -import static io.netty.handler.codec.http2.Http2CodecUtil.SETTING_ENTRY_LENGTH; -import static io.netty.util.CharsetUtil.UTF_8; -import static io.netty.util.ReferenceCountUtil.release; -import static java.util.Objects.requireNonNull; - -/** - * Client-side cleartext upgrade codec from HTTP to HTTP/2. - */ -@UnstableApi -public class Http2ClientUpgradeCodec implements HttpClientUpgradeHandler.UpgradeCodec { - - private static final List UPGRADE_HEADERS = Collections.singletonList(HTTP_UPGRADE_SETTINGS_HEADER); - - private final String handlerName; - private final Http2ConnectionHandler connectionHandler; - private final ChannelHandler upgradeToHandler; - private final ChannelHandler http2MultiplexHandler; - - public Http2ClientUpgradeCodec(Http2FrameCodec frameCodec, ChannelHandler upgradeToHandler) { - this(null, frameCodec, upgradeToHandler); - } - - public Http2ClientUpgradeCodec(String handlerName, Http2FrameCodec frameCodec, ChannelHandler upgradeToHandler) { - this(handlerName, (Http2ConnectionHandler) frameCodec, upgradeToHandler, null); - } - - /** - * Creates the codec using a default name for the connection handler when adding to the - * pipeline. - * - * @param connectionHandler the HTTP/2 connection handler - */ - public Http2ClientUpgradeCodec(Http2ConnectionHandler connectionHandler) { - this((String) null, connectionHandler); - } - - /** - * Creates the codec using a default name for the connection handler when adding to the - * pipeline. - * - * @param connectionHandler the HTTP/2 connection handler - * @param http2MultiplexHandler the Http2 Multiplexer handler to work with Http2FrameCodec - */ - public Http2ClientUpgradeCodec(Http2ConnectionHandler connectionHandler, - Http2MultiplexHandler http2MultiplexHandler) { - this((String) null, connectionHandler, http2MultiplexHandler); - } - - /** - * Creates the codec providing an upgrade to the given handler for HTTP/2. - * - * @param handlerName the name of the HTTP/2 connection handler to be used in the pipeline, - * or {@code null} to auto-generate the name - * @param connectionHandler the HTTP/2 connection handler - */ - public Http2ClientUpgradeCodec(String handlerName, Http2ConnectionHandler connectionHandler) { - this(handlerName, connectionHandler, connectionHandler, null); - } - - /** - * Creates the codec providing an upgrade to the given handler for HTTP/2. - * - * @param handlerName the name of the HTTP/2 connection handler to be used in the pipeline, - * or {@code null} to auto-generate the name - * @param connectionHandler the HTTP/2 connection handler - */ - public Http2ClientUpgradeCodec(String handlerName, Http2ConnectionHandler connectionHandler, - Http2MultiplexHandler http2MultiplexHandler) { - this(handlerName, connectionHandler, connectionHandler, http2MultiplexHandler); - } - - private Http2ClientUpgradeCodec(String handlerName, Http2ConnectionHandler connectionHandler, ChannelHandler - upgradeToHandler, Http2MultiplexHandler http2MultiplexHandler) { - this.handlerName = handlerName; - this.connectionHandler = requireNonNull(connectionHandler, "connectionHandler"); - this.upgradeToHandler = requireNonNull(upgradeToHandler, "upgradeToHandler"); - this.http2MultiplexHandler = http2MultiplexHandler; - } - - @Override - public CharSequence protocol() { - return HTTP_UPGRADE_PROTOCOL_NAME; - } - - @Override - public Collection setUpgradeHeaders(ChannelHandlerContext ctx, - HttpRequest upgradeRequest) { - CharSequence settingsValue = getSettingsHeaderValue(ctx); - upgradeRequest.headers().set(HTTP_UPGRADE_SETTINGS_HEADER, settingsValue); - return UPGRADE_HEADERS; - } - - @Override - public void upgradeTo(ChannelHandlerContext ctx, FullHttpResponse upgradeResponse) - throws Exception { - try { - // Add the handler to the pipeline. - ctx.pipeline().addAfter(ctx.name(), handlerName, upgradeToHandler); - - // Add the Http2 Multiplex handler as this handler handle events produced by the connectionHandler. - // See https://github.com/netty/netty/issues/9495 - if (http2MultiplexHandler != null) { - final String name = ctx.pipeline().context(connectionHandler).name(); - ctx.pipeline().addAfter(name, null, http2MultiplexHandler); - } - - // Reserve local stream 1 for the response. - connectionHandler.onHttpClientUpgrade(); - } catch (Http2Exception e) { - ctx.fireExceptionCaught(e); - ctx.close(); - } - } - - /** - * Converts the current settings for the handler to the Base64-encoded representation used in - * the HTTP2-Settings upgrade header. - */ - private CharSequence getSettingsHeaderValue(ChannelHandlerContext ctx) { - ByteBuf buf = null; - ByteBuf encodedBuf = null; - try { - // Get the local settings for the handler. - Http2Settings settings = connectionHandler.decoder().localSettings(); - - // Serialize the payload of the SETTINGS frame. - int payloadLength = SETTING_ENTRY_LENGTH * settings.size(); - buf = ctx.alloc().buffer(payloadLength); - for (CharObjectMap.PrimitiveEntry entry : settings.entries()) { - buf.writeChar(entry.key()); - buf.writeInt(entry.value().intValue()); - } - - // Base64 encode the payload and then convert to a string for the header. - encodedBuf = Base64.encode(buf, URL_SAFE); - return encodedBuf.toString(UTF_8); - } finally { - release(buf); - release(encodedBuf); - } - } -} 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 deleted file mode 100644 index 43e5fec1f4..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2CodecUtil.java +++ /dev/null @@ -1,405 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.ssl.ApplicationProtocolNames; -import io.netty.util.AsciiString; -import io.netty.util.concurrent.DefaultPromise; -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; -import io.netty.util.internal.UnstableApi; - -import static io.netty.buffer.Unpooled.directBuffer; -import static io.netty.buffer.Unpooled.unreleasableBuffer; -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.headerListSizeError; -import static io.netty.util.CharsetUtil.UTF_8; -import static java.lang.Math.max; -import static java.lang.Math.min; -import static java.util.concurrent.TimeUnit.MILLISECONDS; -import static java.util.concurrent.TimeUnit.SECONDS; - -/** - * Constants and utility method used for encoding/decoding HTTP2 frames. - */ -@UnstableApi -public final class Http2CodecUtil { - public static final int CONNECTION_STREAM_ID = 0; - public static final int HTTP_UPGRADE_STREAM_ID = 1; - public static final CharSequence HTTP_UPGRADE_SETTINGS_HEADER = AsciiString.cached("HTTP2-Settings"); - public static final CharSequence HTTP_UPGRADE_PROTOCOL_NAME = "h2c"; - public static final CharSequence TLS_UPGRADE_PROTOCOL_NAME = ApplicationProtocolNames.HTTP_2; - - public static final int PING_FRAME_PAYLOAD_LENGTH = 8; - public static final short MAX_UNSIGNED_BYTE = 0xff; - /** - * The maximum number of padding bytes. That is the 255 padding bytes appended to the end of a frame and the 1 byte - * pad length field. - */ - public static final int MAX_PADDING = 256; - public static final long MAX_UNSIGNED_INT = 0xffffffffL; - public static final int FRAME_HEADER_LENGTH = 9; - public static final int SETTING_ENTRY_LENGTH = 6; - public static final int PRIORITY_ENTRY_LENGTH = 5; - public static final int INT_FIELD_LENGTH = 4; - public static final short MAX_WEIGHT = 256; - public static final short MIN_WEIGHT = 1; - - 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 int MAX_PADDING_LENGTH_LENGTH = 1; - public static final int DATA_FRAME_HEADER_LENGTH = FRAME_HEADER_LENGTH + MAX_PADDING_LENGTH_LENGTH; - public static final int HEADERS_FRAME_HEADER_LENGTH = - FRAME_HEADER_LENGTH + MAX_PADDING_LENGTH_LENGTH + INT_FIELD_LENGTH + 1; - public static final int PRIORITY_FRAME_LENGTH = FRAME_HEADER_LENGTH + PRIORITY_ENTRY_LENGTH; - public static final int RST_STREAM_FRAME_LENGTH = FRAME_HEADER_LENGTH + INT_FIELD_LENGTH; - public static final int PUSH_PROMISE_FRAME_HEADER_LENGTH = - FRAME_HEADER_LENGTH + MAX_PADDING_LENGTH_LENGTH + INT_FIELD_LENGTH; - public static final int GO_AWAY_FRAME_HEADER_LENGTH = FRAME_HEADER_LENGTH + 2 * INT_FIELD_LENGTH; - public static final int WINDOW_UPDATE_FRAME_LENGTH = FRAME_HEADER_LENGTH + INT_FIELD_LENGTH; - public static final int CONTINUATION_FRAME_HEADER_LENGTH = FRAME_HEADER_LENGTH + MAX_PADDING_LENGTH_LENGTH; - - public static final char SETTINGS_HEADER_TABLE_SIZE = 1; - public static final char SETTINGS_ENABLE_PUSH = 2; - public static final char SETTINGS_MAX_CONCURRENT_STREAMS = 3; - public static final char SETTINGS_INITIAL_WINDOW_SIZE = 4; - public static final char SETTINGS_MAX_FRAME_SIZE = 5; - public static final char SETTINGS_MAX_HEADER_LIST_SIZE = 6; - public static final int NUM_STANDARD_SETTINGS = 6; - - public static final long MAX_HEADER_TABLE_SIZE = MAX_UNSIGNED_INT; - public static final long MAX_CONCURRENT_STREAMS = MAX_UNSIGNED_INT; - public static final int MAX_INITIAL_WINDOW_SIZE = Integer.MAX_VALUE; - public static final int MAX_FRAME_SIZE_LOWER_BOUND = 0x4000; - public static final int MAX_FRAME_SIZE_UPPER_BOUND = 0xffffff; - public static final long MAX_HEADER_LIST_SIZE = MAX_UNSIGNED_INT; - - public static final long MIN_HEADER_TABLE_SIZE = 0; - public static final long MIN_CONCURRENT_STREAMS = 0; - public static final int MIN_INITIAL_WINDOW_SIZE = 0; - public static final long MIN_HEADER_LIST_SIZE = 0; - - public static final int DEFAULT_WINDOW_SIZE = 65535; - public static final short DEFAULT_PRIORITY_WEIGHT = 16; - public static final int DEFAULT_HEADER_TABLE_SIZE = 4096; - /** - * The initial value of this setting is unlimited. - * However in practice we don't want to allow our peers to use unlimited memory by default. So we take advantage - * of the For any given request, a lower limit than what is advertised MAY be enforced. loophole. - */ - public static final long DEFAULT_HEADER_LIST_SIZE = 8192; - public static final int DEFAULT_MAX_FRAME_SIZE = MAX_FRAME_SIZE_LOWER_BOUND; - /** - * The assumed minimum value for {@code SETTINGS_MAX_CONCURRENT_STREAMS} as - * recommended by the HTTP/2 spec. - */ - public static final int SMALLEST_MAX_CONCURRENT_STREAMS = 100; - static final int DEFAULT_MAX_RESERVED_STREAMS = SMALLEST_MAX_CONCURRENT_STREAMS; - static final int DEFAULT_MIN_ALLOCATION_CHUNK = 1024; - - /** - * Calculate the threshold in bytes which should trigger a {@code GO_AWAY} if a set of headers exceeds this amount. - * @param maxHeaderListSize - * SETTINGS_MAX_HEADER_LIST_SIZE for the local - * endpoint. - * @return the threshold in bytes which should trigger a {@code GO_AWAY} if a set of headers exceeds this amount. - */ - public static long calculateMaxHeaderListSizeGoAway(long maxHeaderListSize) { - // This is equivalent to `maxHeaderListSize * 1.25` but we avoid floating point multiplication. - return maxHeaderListSize + (maxHeaderListSize >>> 2); - } - - public static final long DEFAULT_GRACEFUL_SHUTDOWN_TIMEOUT_MILLIS = MILLISECONDS.convert(30, SECONDS); - - public static final int DEFAULT_MAX_QUEUED_CONTROL_FRAMES = 10000; - - /** - * Returns {@code true} if the stream is an outbound stream. - * - * @param server {@code true} if the endpoint is a server, {@code false} otherwise. - * @param streamId the stream identifier - */ - public static boolean isOutboundStream(boolean server, int streamId) { - boolean even = (streamId & 1) == 0; - return streamId > 0 && server == even; - } - - /** - * Returns true if the {@code streamId} is a valid HTTP/2 stream identifier. - */ - public static boolean isStreamIdValid(int streamId) { - return streamId >= 0; - } - - static boolean isStreamIdValid(int streamId, boolean server) { - return isStreamIdValid(streamId) && server == ((streamId & 1) == 0); - } - - /** - * Indicates whether or not the given value for max frame size falls within the valid range. - */ - public static boolean isMaxFrameSizeValid(int maxFrameSize) { - return maxFrameSize >= MAX_FRAME_SIZE_LOWER_BOUND && maxFrameSize <= MAX_FRAME_SIZE_UPPER_BOUND; - } - - /** - * Returns a buffer containing the {@link #CONNECTION_PREFACE}. - */ - public static ByteBuf connectionPrefaceBuf() { - // Return a duplicate so that modifications to the reader index will not affect the original buffer. - return CONNECTION_PREFACE.retainedDuplicate(); - } - - /** - * Iteratively looks through the causality chain for the given exception and returns the first - * {@link Http2Exception} or {@code null} if none. - */ - public static Http2Exception getEmbeddedHttp2Exception(Throwable cause) { - while (cause != null) { - if (cause instanceof Http2Exception) { - return (Http2Exception) cause; - } - cause = cause.getCause(); - } - return null; - } - - /** - * Creates a buffer containing the error message from the given exception. If the cause is - * {@code null} returns an empty buffer. - */ - public static ByteBuf toByteBuf(ChannelHandlerContext ctx, Throwable cause) { - if (cause == null || cause.getMessage() == null) { - return Unpooled.EMPTY_BUFFER; - } - - return ByteBufUtil.writeUtf8(ctx.alloc(), cause.getMessage()); - } - - /** - * Reads a big-endian (31-bit) integer from the buffer. - */ - public static int readUnsignedInt(ByteBuf buf) { - return buf.readInt() & 0x7fffffff; - } - - /** - * Writes an HTTP/2 frame header to the output buffer. - */ - public static void writeFrameHeader(ByteBuf out, int payloadLength, byte type, - Http2Flags flags, int streamId) { - out.ensureWritable(FRAME_HEADER_LENGTH + payloadLength); - writeFrameHeaderInternal(out, payloadLength, type, flags, streamId); - } - - /** - * Calculate the amount of bytes that can be sent by {@code state}. The lower bound is {@code 0}. - */ - public static int streamableBytes(StreamByteDistributor.StreamState state) { - return max(0, (int) min(state.pendingBytes(), state.windowSize())); - } - - /** - * Results in a RST_STREAM being sent for {@code streamId} due to violating - * SETTINGS_MAX_HEADER_LIST_SIZE. - * @param streamId The stream ID that was being processed when the exceptional condition occurred. - * @param maxHeaderListSize The max allowed size for a list of headers in bytes which was exceeded. - * @param onDecode {@code true} if the exception was encountered during decoder. {@code false} for encode. - * @throws Http2Exception a stream error. - */ - public static void headerListSizeExceeded(int streamId, long maxHeaderListSize, - boolean onDecode) throws Http2Exception { - throw headerListSizeError(streamId, PROTOCOL_ERROR, onDecode, "Header size exceeded max " + - "allowed size (%d)", maxHeaderListSize); - } - - /** - * Results in a GO_AWAY being sent due to violating - * SETTINGS_MAX_HEADER_LIST_SIZE in an unrecoverable - * manner. - * @param maxHeaderListSize The max allowed size for a list of headers in bytes which was exceeded. - * @throws Http2Exception a connection error. - */ - public static void headerListSizeExceeded(long maxHeaderListSize) throws Http2Exception { - throw connectionError(PROTOCOL_ERROR, "Header size exceeded max " + - "allowed size (%d)", maxHeaderListSize); - } - - static void writeFrameHeaderInternal(ByteBuf out, int payloadLength, byte type, - Http2Flags flags, int streamId) { - out.writeMedium(payloadLength); - out.writeByte(type); - out.writeByte(flags.value()); - out.writeInt(streamId); - } - - /** - * Provides the ability to associate the outcome of multiple {@link Promise} - * objects into a single {@link Promise} object. - */ - static final class SimpleChannelPromiseAggregator extends DefaultPromise { - private final Promise promise; - private int expectedCount; - private int doneCount; - private Throwable aggregateFailure; - private boolean doneAllocating; - - SimpleChannelPromiseAggregator(Promise promise, EventExecutor e) { - super(e); - assert promise != null && !promise.isDone(); - this.promise = promise; - } - - /** - * Allocate a new promise which will be used to aggregate the overall success of this promise aggregator. - * @return A new promise which will be aggregated. - * {@code null} if {@link #doneAllocatingPromises()} was previously called. - */ - public Promise newPromise() { - assert !doneAllocating : "Done allocating. No more promises can be allocated."; - ++expectedCount; - return this; - } - - /** - * Signify that no more {@link #newPromise()} allocations will be made. - * The aggregation can not be successful until this method is called. - * @return The {@link Future} that is the aggregation of all promises allocated with {@link #newPromise()}. - */ - public Future doneAllocatingPromises() { - if (!doneAllocating) { - doneAllocating = true; - if (doneCount == expectedCount || expectedCount == 0) { - return setPromise().asFuture(); - } - } - return this; - } - - @Override - public boolean tryFailure(Throwable cause) { - if (allowFailure()) { - ++doneCount; - setAggregateFailure(cause); - if (allPromisesDone()) { - return tryPromise(); - } - // TODO: We break the interface a bit here. - // Multiple failure events can be processed without issue because this is an aggregation. - return true; - } - return false; - } - - /** - * Fail this object if it has not already been failed. - *

- * This method will NOT throw an {@link IllegalStateException} if called multiple times - * because that may be expected. - */ - @Override - public Promise setFailure(Throwable cause) { - if (allowFailure()) { - ++doneCount; - setAggregateFailure(cause); - if (allPromisesDone()) { - setPromise(); - } - } - return this; - } - - @Override - public Promise setSuccess(Void result) { - if (awaitingPromises()) { - ++doneCount; - if (allPromisesDone()) { - setPromise(); - } - } - return this; - } - - @Override - public boolean trySuccess(Void result) { - if (awaitingPromises()) { - ++doneCount; - if (allPromisesDone()) { - return tryPromise(); - } - // TODO: We break the interface a bit here. - // Multiple success events can be processed without issue because this is an aggregation. - return true; - } - return false; - } - - private boolean allowFailure() { - return awaitingPromises() || expectedCount == 0; - } - - private boolean awaitingPromises() { - return doneCount < expectedCount; - } - - private boolean allPromisesDone() { - return doneCount == expectedCount && doneAllocating; - } - - private Promise setPromise() { - if (aggregateFailure == null) { - promise.setSuccess(null); - super.setSuccess(null); - } else { - promise.setFailure(aggregateFailure); - super.setFailure(aggregateFailure); - } - return this; - } - - private boolean tryPromise() { - if (aggregateFailure == null) { - promise.trySuccess(null); - return super.trySuccess(null); - } else { - promise.tryFailure(aggregateFailure); - return super.tryFailure(aggregateFailure); - } - } - - private void setAggregateFailure(Throwable cause) { - if (aggregateFailure == null) { - aggregateFailure = cause; - } - } - } - - public static void verifyPadding(int padding) { - if (padding < 0 || padding > MAX_PADDING) { - throw new IllegalArgumentException(String.format("Invalid padding '%d'. Padding must be between 0 and " + - "%d (inclusive).", padding, MAX_PADDING)); - } - } - private Http2CodecUtil() { } -} 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 deleted file mode 100644 index cc4e874b48..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Connection.java +++ /dev/null @@ -1,356 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; -import io.netty.util.internal.UnstableApi; - -/** - * Manager for the state of an HTTP/2 connection with the remote end-point. - */ -@UnstableApi -public interface Http2Connection { - /** - * Listener for life-cycle events for streams in this connection. - */ - interface Listener { - /** - * Notifies the listener that the given stream was added to the connection. This stream may - * not yet be active (i.e. {@code OPEN} or {@code HALF CLOSED}). - *

- * If a {@link RuntimeException} is thrown it will be logged and not propagated. - * Throwing from this method is not supported and is considered a programming error. - */ - void onStreamAdded(Http2Stream stream); - - /** - * Notifies the listener that the given stream was made active (i.e. {@code OPEN} or {@code HALF CLOSED}). - *

- * If a {@link RuntimeException} is thrown it will be logged and not propagated. - * Throwing from this method is not supported and is considered a programming error. - */ - void onStreamActive(Http2Stream stream); - - /** - * Notifies the listener that the given stream has transitioned from {@code OPEN} to {@code HALF CLOSED}. - * This method will not be called until a state transition occurs from when - * {@link #onStreamActive(Http2Stream)} was called. - * The stream can be inspected to determine which side is {@code HALF CLOSED}. - *

- * If a {@link RuntimeException} is thrown it will be logged and not propagated. - * Throwing from this method is not supported and is considered a programming error. - */ - void onStreamHalfClosed(Http2Stream stream); - - /** - * Notifies the listener that the given stream is now {@code CLOSED} in both directions and will no longer - * be accessible via {@link #forEachActiveStream(Http2StreamVisitor)}. - *

- * If a {@link RuntimeException} is thrown it will be logged and not propagated. - * Throwing from this method is not supported and is considered a programming error. - */ - void onStreamClosed(Http2Stream stream); - - /** - * Notifies the listener that the given stream has now been removed from the connection and - * will no longer be returned via {@link Http2Connection#stream(int)}. The connection may - * maintain inactive streams for some time before removing them. - *

- * If a {@link RuntimeException} is thrown it will be logged and not propagated. - * Throwing from this method is not supported and is considered a programming error. - */ - void onStreamRemoved(Http2Stream stream); - - /** - * Called when a {@code GOAWAY} frame was sent for the connection. - *

- * If a {@link RuntimeException} is thrown it will be logged and not propagated. - * Throwing from this method is not supported and is considered a programming error. - * @param lastStreamId the last known stream of the remote endpoint. - * @param errorCode the error code, if abnormal closure. - * @param debugData application-defined debug data. - */ - void onGoAwaySent(int lastStreamId, long errorCode, ByteBuf debugData); - - /** - * Called when a {@code GOAWAY} was received from the remote endpoint. This event handler duplicates {@link - * Http2FrameListener#onGoAwayRead(io.netty.channel.ChannelHandlerContext, int, long, ByteBuf)} - * but is added here in order to simplify application logic for handling {@code GOAWAY} in a uniform way. An - * application should generally not handle both events, but if it does this method is called second, after - * notifying the {@link Http2FrameListener}. - *

- * If a {@link RuntimeException} is thrown it will be logged and not propagated. - * Throwing from this method is not supported and is considered a programming error. - * @param lastStreamId the last known stream of the remote endpoint. - * @param errorCode the error code, if abnormal closure. - * @param debugData application-defined debug data. - */ - void onGoAwayReceived(int lastStreamId, long errorCode, ByteBuf debugData); - } - - /** - * A view of the connection from one endpoint (local or remote). - */ - interface Endpoint { - /** - * Increment and get the next generated stream id this endpoint. If negative, the stream IDs are - * exhausted for this endpoint an no further streams may be created. - */ - int incrementAndGetNextStreamId(); - - /** - * Indicates whether the given streamId is from the set of IDs used by this endpoint to - * create new streams. - */ - boolean isValidStreamId(int streamId); - - /** - * Indicates whether or not this endpoint may have created the given stream. This is {@code true} if - * {@link #isValidStreamId(int)} and {@code streamId} <= {@link #lastStreamCreated()}. - */ - boolean mayHaveCreatedStream(int streamId); - - /** - * Indicates whether or not this endpoint created the given stream. - */ - boolean created(Http2Stream stream); - - /** - * Indicates whether or a stream created by this endpoint can be opened without violating - * {@link #maxActiveStreams()}. - */ - boolean canOpenStream(); - - /** - * Creates a stream initiated by this endpoint. This could fail for the following reasons: - *

    - *
  • The requested stream ID is not the next sequential ID for this endpoint.
  • - *
  • The stream already exists.
  • - *
  • {@link #canOpenStream()} is {@code false}.
  • - *
  • The connection is marked as going away.
  • - *
- *

- * The initial state of the stream will be immediately set before notifying {@link Listener}s. The state - * transition is sensitive to {@code halfClosed} and is defined by {@link Http2Stream#open(boolean)}. - * @param streamId The ID of the stream - * @param halfClosed see {@link Http2Stream#open(boolean)}. - * @see Http2Stream#open(boolean) - */ - Http2Stream createStream(int streamId, boolean halfClosed) throws Http2Exception; - - /** - * Creates a push stream in the reserved state for this endpoint and notifies all listeners. - * This could fail for the following reasons: - *

    - *
  • Server push is not allowed to the opposite endpoint.
  • - *
  • The requested stream ID is not the next sequential stream ID for this endpoint.
  • - *
  • The number of concurrent streams is above the allowed threshold for this endpoint.
  • - *
  • The connection is marked as going away.
  • - *
  • The parent stream ID does not exist or is not {@code OPEN} from the side sending the push - * promise.
  • - *
  • Could not set a valid priority for the new stream.
  • - *
- * - * @param streamId the ID of the push stream - * @param parent the parent stream used to initiate the push stream. - */ - Http2Stream reservePushStream(int streamId, Http2Stream parent) throws Http2Exception; - - /** - * Indicates whether or not this endpoint is the server-side of the connection. - */ - boolean isServer(); - - /** - * This is the SETTINGS_ENABLE_PUSH value sent - * from the opposite endpoint. This method should only be called by Netty (not users) as a result of a - * receiving a {@code SETTINGS} frame. - */ - void allowPushTo(boolean allow); - - /** - * This is the SETTINGS_ENABLE_PUSH value sent - * from the opposite endpoint. The initial value must be {@code true} for the client endpoint and always false - * for a server endpoint. - */ - boolean allowPushTo(); - - /** - * Gets the number of active streams (i.e. {@code OPEN} or {@code HALF CLOSED}) that were created by this - * endpoint. - */ - int numActiveStreams(); - - /** - * Gets the maximum number of streams (created by this endpoint) that are allowed to be active at - * the same time. This is the - * SETTINGS_MAX_CONCURRENT_STREAMS - * value sent from the opposite endpoint to restrict stream creation by this endpoint. - *

- * The default value returned by this method must be "unlimited". - */ - int maxActiveStreams(); - - /** - * Sets the limit for {@code SETTINGS_MAX_CONCURRENT_STREAMS}. - * @param maxActiveStreams The maximum number of streams (created by this endpoint) that are allowed to be - * active at once. This is the - * SETTINGS_MAX_CONCURRENT_STREAMS value sent - * from the opposite endpoint to restrict stream creation by this endpoint. - */ - void maxActiveStreams(int maxActiveStreams); - - /** - * Gets the ID of the stream last successfully created by this endpoint. - */ - int lastStreamCreated(); - - /** - * If a GOAWAY was received for this endpoint, this will be the last stream ID from the - * GOAWAY frame. Otherwise, this will be {@code -1}. - */ - int lastStreamKnownByPeer(); - - /** - * Gets the flow controller for this endpoint. - */ - F flowController(); - - /** - * Sets the flow controller for this endpoint. - */ - void flowController(F flowController); - - /** - * Gets the {@link Endpoint} opposite this one. - */ - Endpoint opposite(); - } - - /** - * A key to be used for associating application-defined properties with streams within this connection. - */ - interface PropertyKey { - } - - /** - * Close this connection. No more new streams can be created after this point and - * all streams that exists (active or otherwise) will be closed and removed. - *

Note if iterating active streams via {@link #forEachActiveStream(Http2StreamVisitor)} and an exception is - * thrown it is necessary to call this method again to ensure the close completes. - * - * @param promise Will be completed when all streams have been removed, and listeners have been notified. - */ - void close(Promise promise); - - /** - * Creates a new key that is unique within this {@link Http2Connection}. - */ - PropertyKey newKey(); - - /** - * Adds a listener of stream life-cycle events. - */ - void addListener(Listener listener); - - /** - * Removes a listener of stream life-cycle events. If the same listener was added multiple times - * then only the first occurrence gets removed. - */ - void removeListener(Listener listener); - - /** - * Gets the stream if it exists. If not, returns {@code null}. - */ - Http2Stream stream(int streamId); - - /** - * Indicates whether or not the given stream may have existed within this connection. This is a short form - * for calling {@link Endpoint#mayHaveCreatedStream(int)} on both endpoints. - */ - boolean streamMayHaveExisted(int streamId); - - /** - * Gets the stream object representing the connection, itself (i.e. stream zero). This object - * always exists. - */ - Http2Stream connectionStream(); - - /** - * Gets the number of streams that are actively in use (i.e. {@code OPEN} or {@code HALF CLOSED}). - */ - int numActiveStreams(); - - /** - * Provide a means of iterating over the collection of active streams. - * - * @param visitor The visitor which will visit each active stream. - * @return The stream before iteration stopped or {@code null} if iteration went past the end. - */ - Http2Stream forEachActiveStream(Http2StreamVisitor visitor) throws Http2Exception; - - /** - * Indicates whether or not the local endpoint for this connection is the server. - */ - boolean isServer(); - - /** - * Gets a view of this connection from the local {@link Endpoint}. - */ - Endpoint local(); - - /** - * Gets a view of this connection from the remote {@link Endpoint}. - */ - Endpoint remote(); - - /** - * Indicates whether or not a {@code GOAWAY} was received from the remote endpoint. - */ - boolean goAwayReceived(); - - /** - * 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) throws Http2Exception; - - /** - * Indicates whether or not a {@code GOAWAY} was sent to the remote endpoint. - */ - boolean goAwaySent(); - - /** - * 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. - */ - boolean goAwaySent(int lastKnownStream, long errorCode, ByteBuf message) throws Http2Exception; -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionAdapter.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionAdapter.java deleted file mode 100644 index 0ba4d947d6..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionAdapter.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.util.internal.UnstableApi; - -/** - * Provides empty implementations of all {@link Http2Connection.Listener} methods. - */ -@UnstableApi -public class Http2ConnectionAdapter implements Http2Connection.Listener { - @Override - public void onStreamAdded(Http2Stream stream) { - } - - @Override - public void onStreamActive(Http2Stream stream) { - } - - @Override - public void onStreamHalfClosed(Http2Stream stream) { - } - - @Override - public void onStreamClosed(Http2Stream stream) { - } - - @Override - public void onStreamRemoved(Http2Stream stream) { - } - - @Override - public void onGoAwaySent(int lastStreamId, long errorCode, ByteBuf debugData) { - } - - @Override - public void onGoAwayReceived(int lastStreamId, long errorCode, ByteBuf debugData) { - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionDecoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionDecoder.java deleted file mode 100644 index 5cfa52d57a..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionDecoder.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.internal.UnstableApi; - -import java.io.Closeable; - -/** - * Handler for inbound traffic on behalf of {@link Http2ConnectionHandler}. Performs basic protocol - * conformance on inbound frames before calling the delegate {@link Http2FrameListener} for - * application-specific processing. Note that frames of an unknown type (i.e. HTTP/2 extensions) - * will skip all protocol checks and be given directly to the listener for processing. - */ -@UnstableApi -public interface Http2ConnectionDecoder extends Closeable { - - /** - * Sets the lifecycle manager. Must be called as part of initialization before the decoder is used. - */ - void lifecycleManager(Http2LifecycleManager lifecycleManager); - - /** - * Provides direct access to the underlying connection. - */ - Http2Connection connection(); - - /** - * Provides the local flow controller for managing inbound traffic. - */ - Http2LocalFlowController flowController(); - - /** - * Set the {@link Http2FrameListener} which will be notified when frames are decoded. - *

- * This must be set before frames are decoded. - */ - void frameListener(Http2FrameListener listener); - - /** - * Get the {@link Http2FrameListener} which will be notified when frames are decoded. - */ - Http2FrameListener frameListener(); - - /** - * Called by the {@link Http2ConnectionHandler} to decode the next frame from the input buffer. - */ - void decodeFrame(ChannelHandlerContext ctx, ByteBuf in) throws Http2Exception; - - /** - * Gets the local settings for this endpoint of the HTTP/2 connection. - */ - Http2Settings localSettings(); - - /** - * Indicates whether or not the first initial {@code SETTINGS} frame was received from the remote endpoint. - */ - boolean prefaceReceived(); - - @Override - void close(); -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionEncoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionEncoder.java deleted file mode 100644 index a81a1d34d8..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionEncoder.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; -import io.netty.util.internal.UnstableApi; - - -/** - * Handler for outbound HTTP/2 traffic. - */ -@UnstableApi -public interface Http2ConnectionEncoder extends Http2FrameWriter { - - /** - * Sets the lifecycle manager. Must be called as part of initialization before the encoder is used. - */ - void lifecycleManager(Http2LifecycleManager lifecycleManager); - - /** - * Provides direct access to the underlying connection. - */ - Http2Connection connection(); - - /** - * Provides the remote flow controller for managing outbound traffic. - */ - Http2RemoteFlowController flowController(); - - /** - * Provides direct access to the underlying frame writer object. - */ - Http2FrameWriter frameWriter(); - - /** - * Gets the local settings on the top of the queue that has been sent but not ACKed. This may - * return {@code null}. - */ - Http2Settings pollSentSettings(); - - /** - * Sets the settings for the remote endpoint of the HTTP/2 connection. - */ - void remoteSettings(Http2Settings settings) throws Http2Exception; - - /** - * Writes the given data to the internal {@link Http2FrameWriter} without performing any - * state checks on the connection/stream. - */ - @Override - Future writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId, - Http2Flags flags, ByteBuf payload); -} 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 deleted file mode 100644 index 12bb1339e8..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionHandler.java +++ /dev/null @@ -1,929 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelFutureListeners; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.ByteToMessageDecoder; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http2.Http2Exception.CompositeStreamException; -import io.netty.handler.codec.http2.Http2Exception.StreamException; -import io.netty.util.CharsetUtil; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.FutureListener; -import io.netty.util.concurrent.Promise; -import io.netty.util.internal.UnstableApi; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.util.concurrent.TimeUnit; - -import static io.netty.buffer.ByteBufUtil.hexDump; -import static io.netty.buffer.Unpooled.EMPTY_BUFFER; -import static io.netty.handler.codec.http2.Http2CodecUtil.HTTP_UPGRADE_STREAM_ID; -import static io.netty.handler.codec.http2.Http2CodecUtil.connectionPrefaceBuf; -import static io.netty.handler.codec.http2.Http2CodecUtil.getEmbeddedHttp2Exception; -import static io.netty.handler.codec.http2.Http2Error.INTERNAL_ERROR; -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.Http2Exception.connectionError; -import static io.netty.handler.codec.http2.Http2Exception.isStreamError; -import static io.netty.handler.codec.http2.Http2FrameTypes.SETTINGS; -import static io.netty.handler.codec.http2.Http2Stream.State.IDLE; -import static io.netty.util.CharsetUtil.UTF_8; -import static java.lang.Math.min; -import static java.util.Objects.requireNonNull; -import static java.util.concurrent.TimeUnit.MILLISECONDS; - -/** - * Provides the default implementation for processing inbound frame events and delegates to a - * {@link Http2FrameListener} - *

- * This class will read HTTP/2 frames and delegate the events to a {@link Http2FrameListener} - *

- * This interface enforces inbound flow control functionality through - * {@link Http2LocalFlowController} - */ -@UnstableApi -public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http2LifecycleManager { - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(Http2ConnectionHandler.class); - - private static final Http2Headers HEADERS_TOO_LARGE_HEADERS = ReadOnlyHttp2Headers.serverHeaders(false, - HttpResponseStatus.REQUEST_HEADER_FIELDS_TOO_LARGE.codeAsText()); - private static final ByteBuf HTTP_1_X_BUF = Unpooled.unreleasableBuffer( - Unpooled.wrappedBuffer(new byte[] {'H', 'T', 'T', 'P', '/', '1', '.'})).asReadOnly(); - - private final Http2ConnectionDecoder decoder; - private final Http2ConnectionEncoder encoder; - private final Http2Settings initialSettings; - private final boolean decoupleCloseAndGoAway; - private FutureListener closeListener; - private BaseDecoder byteDecoder; - private long gracefulShutdownTimeoutMillis; - - protected Http2ConnectionHandler(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder, - Http2Settings initialSettings) { - this(decoder, encoder, initialSettings, false); - } - - protected Http2ConnectionHandler(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder, - Http2Settings initialSettings, boolean decoupleCloseAndGoAway) { - this.initialSettings = requireNonNull(initialSettings, "initialSettings"); - this.decoder = requireNonNull(decoder, "decoder"); - this.encoder = requireNonNull(encoder, "encoder"); - this.decoupleCloseAndGoAway = decoupleCloseAndGoAway; - if (encoder.connection() != decoder.connection()) { - throw new IllegalArgumentException("Encoder and Decoder do not share the same connection object"); - } - } - - /** - * Get the amount of time (in milliseconds) this endpoint will wait for all streams to be closed before closing - * the connection during the graceful shutdown process. Returns -1 if this connection is configured to wait - * indefinitely for all streams to close. - */ - public long gracefulShutdownTimeoutMillis() { - return gracefulShutdownTimeoutMillis; - } - - /** - * Set the amount of time (in milliseconds) this endpoint will wait for all streams to be closed before closing - * the connection during the graceful shutdown process. - * @param gracefulShutdownTimeoutMillis the amount of time (in milliseconds) this endpoint will wait for all - * streams to be closed before closing the connection during the graceful shutdown process. - */ - public void gracefulShutdownTimeoutMillis(long gracefulShutdownTimeoutMillis) { - if (gracefulShutdownTimeoutMillis < -1) { - throw new IllegalArgumentException("gracefulShutdownTimeoutMillis: " + gracefulShutdownTimeoutMillis + - " (expected: -1 for indefinite or >= 0)"); - } - this.gracefulShutdownTimeoutMillis = gracefulShutdownTimeoutMillis; - } - - public Http2Connection connection() { - return encoder.connection(); - } - - public Http2ConnectionDecoder decoder() { - return decoder; - } - - public Http2ConnectionEncoder encoder() { - return encoder; - } - - private boolean prefaceSent() { - return byteDecoder != null && byteDecoder.prefaceSent(); - } - - /** - * Handles the client-side (cleartext) upgrade from HTTP to HTTP/2. - * Reserves local stream 1 for the HTTP/2 response. - */ - public void onHttpClientUpgrade() throws Http2Exception { - if (connection().isServer()) { - throw connectionError(PROTOCOL_ERROR, "Client-side HTTP upgrade requested for a server"); - } - if (!prefaceSent()) { - // If the preface was not sent yet it most likely means the handler was not added to the pipeline before - // calling this method. - throw connectionError(INTERNAL_ERROR, "HTTP upgrade must occur after preface was sent"); - } - if (decoder.prefaceReceived()) { - throw connectionError(PROTOCOL_ERROR, "HTTP upgrade must occur before HTTP/2 preface is received"); - } - - // Create a local stream used for the HTTP cleartext upgrade. - connection().local().createStream(HTTP_UPGRADE_STREAM_ID, true); - } - - /** - * Handles the server-side (cleartext) upgrade from HTTP to HTTP/2. - * @param settings the settings for the remote endpoint. - */ - public void onHttpServerUpgrade(Http2Settings settings) throws Http2Exception { - if (!connection().isServer()) { - throw connectionError(PROTOCOL_ERROR, "Server-side HTTP upgrade requested for a client"); - } - if (!prefaceSent()) { - // If the preface was not sent yet it most likely means the handler was not added to the pipeline before - // calling this method. - throw connectionError(INTERNAL_ERROR, "HTTP upgrade must occur after preface was sent"); - } - if (decoder.prefaceReceived()) { - throw connectionError(PROTOCOL_ERROR, "HTTP upgrade must occur before HTTP/2 preface is received"); - } - - // Apply the settings but no ACK is necessary. - encoder.remoteSettings(settings); - - // Create a stream in the half-closed state. - connection().remote().createStream(HTTP_UPGRADE_STREAM_ID, true); - } - - @Override - public void flush(ChannelHandlerContext ctx) { - try { - // Trigger pending writes in the remote flow controller. - encoder.flowController().writePendingBytes(); - ctx.flush(); - } catch (Http2Exception e) { - onError(ctx, true, e); - } catch (Throwable cause) { - onError(ctx, true, connectionError(INTERNAL_ERROR, cause, "Error flushing")); - } - } - - private abstract class BaseDecoder { - public abstract void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception; - public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { } - public void channelActive(ChannelHandlerContext ctx) throws Exception { } - - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - // Connection has terminated, close the encoder and decoder. - encoder().close(); - decoder().close(); - - // We need to remove all streams (not just the active ones). - // See https://github.com/netty/netty/issues/4838. - connection().close(ctx.newPromise()); - } - - /** - * Determine if the HTTP/2 connection preface been sent. - */ - public boolean prefaceSent() { - return true; - } - } - - private final class PrefaceDecoder extends BaseDecoder { - private ByteBuf clientPrefaceString; - private boolean prefaceSent; - - 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. - sendPreface(ctx); - } - - @Override - public boolean prefaceSent() { - return prefaceSent; - } - - @Override - public void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - try { - if (ctx.channel().isActive() && readClientPrefaceString(in) && verifyFirstFrameIsSettings(in)) { - // After the preface is read, it is time to hand over control to the post initialized decoder. - byteDecoder = new FrameDecoder(); - byteDecoder.decode(ctx, in); - } - } catch (Throwable e) { - onError(ctx, false, e); - } - } - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - // The channel just became active - send the connection preface to the remote endpoint. - sendPreface(ctx); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - cleanup(); - super.channelInactive(ctx); - } - - /** - * Releases the {@code clientPrefaceString}. Any active streams will be left in the open. - */ - @Override - public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { - cleanup(); - } - - /** - * Releases the {@code clientPrefaceString}. Any active streams will be left in the open. - */ - private void cleanup() { - if (clientPrefaceString != null) { - clientPrefaceString.release(); - clientPrefaceString = null; - } - } - - /** - * Decodes the client connection preface string from the input buffer. - * - * @return {@code true} if processing of the client preface string is complete. Since client preface strings can - * only be received by servers, returns true immediately for client endpoints. - */ - private boolean readClientPrefaceString(ByteBuf in) throws Http2Exception { - if (clientPrefaceString == null) { - return true; - } - - int prefaceRemaining = clientPrefaceString.readableBytes(); - int bytesRead = min(in.readableBytes(), prefaceRemaining); - - // If the input so far doesn't match the preface, break the connection. - if (bytesRead == 0 || !ByteBufUtil.equals(in, in.readerIndex(), - clientPrefaceString, clientPrefaceString.readerIndex(), - bytesRead)) { - int maxSearch = 1024; // picked because 512 is too little, and 2048 too much - int http1Index = - ByteBufUtil.indexOf(HTTP_1_X_BUF, in.slice(in.readerIndex(), min(in.readableBytes(), maxSearch))); - if (http1Index != -1) { - String chunk = in.toString(in.readerIndex(), http1Index - in.readerIndex(), CharsetUtil.US_ASCII); - throw connectionError(PROTOCOL_ERROR, "Unexpected HTTP/1.x request: %s", chunk); - } - String receivedBytes = hexDump(in, in.readerIndex(), - min(in.readableBytes(), clientPrefaceString.readableBytes())); - throw connectionError(PROTOCOL_ERROR, "HTTP/2 client preface string missing or corrupt. " + - "Hex dump for received bytes: %s", receivedBytes); - } - in.skipBytes(bytesRead); - clientPrefaceString.skipBytes(bytesRead); - - if (!clientPrefaceString.isReadable()) { - // Entire preface has been read. - clientPrefaceString.release(); - clientPrefaceString = null; - return true; - } - return false; - } - - /** - * Peeks at that the next frame in the buffer and verifies that it is a non-ack {@code SETTINGS} frame. - * - * @param in the inbound buffer. - * @return {@code true} if the next frame is a non-ack {@code SETTINGS} frame, {@code false} if more - * data is required before we can determine the next frame type. - * @throws Http2Exception thrown if the next frame is NOT a non-ack {@code SETTINGS} frame. - */ - private boolean verifyFirstFrameIsSettings(ByteBuf in) throws Http2Exception { - if (in.readableBytes() < 5) { - // Need more data before we can see the frame type for the first frame. - return false; - } - - short frameType = in.getUnsignedByte(in.readerIndex() + 3); - short flags = in.getUnsignedByte(in.readerIndex() + 4); - if (frameType != SETTINGS || (flags & Http2Flags.ACK) != 0) { - throw connectionError(PROTOCOL_ERROR, "First received frame was not SETTINGS. " + - "Hex dump for first 5 bytes: %s", - hexDump(in, in.readerIndex(), 5)); - } - return true; - } - - /** - * Sends the HTTP/2 connection preface upon establishment of the connection, if not already sent. - */ - private void sendPreface(ChannelHandlerContext ctx) throws Exception { - if (prefaceSent || !ctx.channel().isActive()) { - return; - } - - prefaceSent = true; - - final boolean isClient = !connection().isServer(); - if (isClient) { - // Clients must send the preface string as the first bytes on the connection. - ctx.write(connectionPrefaceBuf()).addListener(ctx.channel(), ChannelFutureListeners.CLOSE_ON_FAILURE); - } - - // Both client and server must send their initial settings. - encoder.writeSettings(ctx, initialSettings) - .addListener(ctx.channel(), ChannelFutureListeners.CLOSE_ON_FAILURE); - - if (isClient) { - // If this handler is extended by the user and we directly fire the userEvent from this context then - // the user will not see the event. We should fire the event starting with this handler so this class - // (and extending classes) have a chance to process the event. - userEventTriggered(ctx, Http2ConnectionPrefaceAndSettingsFrameWrittenEvent.INSTANCE); - } - } - } - - private final class FrameDecoder extends BaseDecoder { - @Override - public void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - try { - decoder.decodeFrame(ctx, in); - } catch (Throwable e) { - onError(ctx, false, e); - } - } - } - - @Override - public void handlerAdded0(ChannelHandlerContext ctx) throws Exception { - // Initialize the encoder, decoder, flow controllers, and internal state. - encoder.lifecycleManager(this); - decoder.lifecycleManager(this); - encoder.flowController().channelHandlerContext(ctx); - decoder.flowController().channelHandlerContext(ctx); - byteDecoder = new PrefaceDecoder(ctx); - } - - @Override - protected void handlerRemoved0(ChannelHandlerContext ctx) throws Exception { - if (byteDecoder != null) { - byteDecoder.handlerRemoved(ctx); - byteDecoder = null; - } - } - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - if (byteDecoder == null) { - byteDecoder = new PrefaceDecoder(ctx); - } - byteDecoder.channelActive(ctx); - super.channelActive(ctx); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - // Call super class first, as this may result in decode being called. - super.channelInactive(ctx); - if (byteDecoder != null) { - byteDecoder.channelInactive(ctx); - byteDecoder = null; - } - } - - @Override - public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception { - // Writability is expected to change while we are writing. We cannot allow this event to trigger reentering - // the allocation and write loop. Reentering the event loop will lead to over or illegal allocation. - try { - if (ctx.channel().isWritable()) { - flush(ctx); - } - encoder.flowController().channelWritabilityChanged(); - } finally { - super.channelWritabilityChanged(ctx); - } - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - byteDecoder.decode(ctx, in); - } - - @Override - public Future close(ChannelHandlerContext ctx) { - if (decoupleCloseAndGoAway) { - return ctx.close(); - } - // Avoid NotYetConnectedException and avoid sending before connection preface - if (!ctx.channel().isActive() || !prefaceSent()) { - return ctx.close(); - } - - // If the user has already sent a GO_AWAY frame they may be attempting to do a graceful shutdown which requires - // sending multiple GO_AWAY frames. We should only send a GO_AWAY here if one has not already been sent. If - // a GO_AWAY has been sent we send a empty buffer just so we can wait to close until all other data has been - // flushed to the OS. - // https://github.com/netty/netty/issues/5307 - Future f = connection().goAwaySent() ? ctx.write(EMPTY_BUFFER) : goAway(ctx, null); - ctx.flush(); - Promise promise = ctx.newPromise(); - doGracefulShutdown(ctx, f, promise); - return promise.asFuture(); - } - - private FutureListener newClosingChannelFutureListener( - ChannelHandlerContext ctx, Promise promise) { - long gracefulShutdownTimeoutMillis = this.gracefulShutdownTimeoutMillis; - return gracefulShutdownTimeoutMillis < 0 ? - new ClosingChannelFutureListener(ctx, promise) : - new ClosingChannelFutureListener(ctx, promise, gracefulShutdownTimeoutMillis, MILLISECONDS); - } - - private void doGracefulShutdown(ChannelHandlerContext ctx, Future future, final Promise promise) { - FutureListener listener = newClosingChannelFutureListener(ctx, promise); - if (isGracefulShutdownComplete()) { - // If there are no active streams, close immediately after the GO_AWAY write completes or the timeout - // elapsed. - future.addListener(listener); - } else { - // If there are active streams we should wait until they are all closed before closing the connection. - - // The ClosingChannelFutureListener will cascade promise completion. We need to always notify the - // new ClosingChannelFutureListener when the graceful close completes if the promise is not null. - if (closeListener == null) { - closeListener = listener; - } else if (promise != null) { - FutureListener oldCloseListener = closeListener; - closeListener = future1 -> { - try { - oldCloseListener.operationComplete(future1); - } finally { - listener.operationComplete(future1); - } - }; - } - } - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { - // Trigger flush after read on the assumption that flush is cheap if there is nothing to write and that - // for flow-control the read may release window that causes data to be written that can now be flushed. - try { - // First call channelReadComplete0(...) as this may produce more data that we want to flush - channelReadComplete0(ctx); - } finally { - flush(ctx); - } - } - - final void channelReadComplete0(ChannelHandlerContext ctx) { - // Discard bytes of the cumulation buffer if needed. - discardSomeReadBytes(); - - // Ensure we never stall 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(); - } - - /** - * Handles {@link Http2Exception} objects that were thrown from other handlers. Ignores all other exceptions. - */ - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - if (getEmbeddedHttp2Exception(cause) != null) { - // Some exception in the causality chain is an Http2Exception - handle it. - onError(ctx, false, cause); - } else { - super.exceptionCaught(ctx, cause); - } - } - - /** - * Closes the local side of the given stream. If this causes the stream to be closed, adds a - * hook to close the channel after the given future completes. - * - * @param stream the stream to be half closed. - * @param future If closing, the future after which to close the channel. - */ - @Override - public void closeStreamLocal(Http2Stream stream, Future future) { - switch (stream.state()) { - case HALF_CLOSED_LOCAL: - case OPEN: - stream.closeLocalSide(); - break; - default: - closeStream(stream, future); - break; - } - } - - /** - * Closes the remote side of the given stream. If this causes the stream to be closed, adds a - * hook to close the channel after the given future completes. - * - * @param stream the stream to be half closed. - * @param future If closing, the future after which to close the channel. - */ - @Override - public void closeStreamRemote(Http2Stream stream, Future future) { - switch (stream.state()) { - case HALF_CLOSED_REMOTE: - case OPEN: - stream.closeRemoteSide(); - break; - default: - closeStream(stream, future); - break; - } - } - - @Override - public void closeStream(final Http2Stream stream, Future future) { - stream.close(); - - if (future.isDone()) { - checkCloseConnection(future); - } else { - future.addListener(this::checkCloseConnection); - } - } - - /** - * Central handler for all exceptions caught during HTTP/2 processing. - */ - @Override - public void onError(ChannelHandlerContext ctx, boolean outbound, Throwable cause) { - Http2Exception embedded = getEmbeddedHttp2Exception(cause); - if (isStreamError(embedded)) { - onStreamError(ctx, outbound, cause, (StreamException) embedded); - } else if (embedded instanceof CompositeStreamException) { - CompositeStreamException compositException = (CompositeStreamException) embedded; - for (StreamException streamException : compositException) { - onStreamError(ctx, outbound, cause, streamException); - } - } else { - onConnectionError(ctx, outbound, cause, embedded); - } - ctx.flush(); - } - - /** - * Called by the graceful shutdown logic to determine when it is safe to close the connection. Returns {@code true} - * if the graceful shutdown has completed and the connection can be safely closed. This implementation just - * guarantees that there are no active streams. Subclasses may override to provide additional checks. - */ - protected boolean isGracefulShutdownComplete() { - return connection().numActiveStreams() == 0; - } - - /** - * Handler for a connection error. Sends a GO_AWAY frame to the remote endpoint. Once all - * streams are closed, the connection is shut down. - * - * @param ctx the channel context - * @param outbound {@code true} if the error was caused by an outbound operation. - * @param cause the exception that was caught - * @param http2Ex the {@link Http2Exception} that is embedded in the causality chain. This may - * be {@code null} if it's an unknown exception. - */ - protected void onConnectionError(ChannelHandlerContext ctx, boolean outbound, - Throwable cause, Http2Exception http2Ex) { - if (http2Ex == null) { - http2Ex = new Http2Exception(INTERNAL_ERROR, cause.getMessage(), cause); - } - - Promise promise = ctx.newPromise(); - Future future = goAway(ctx, http2Ex); - if (http2Ex.shutdownHint() == Http2Exception.ShutdownHint.GRACEFUL_SHUTDOWN) { - doGracefulShutdown(ctx, future, promise); - } else { - future.addListener(newClosingChannelFutureListener(ctx, promise)); - } - } - - /** - * Handler for a stream error. Sends a {@code RST_STREAM} frame to the remote endpoint and closes the - * stream. - * - * @param ctx the channel context - * @param outbound {@code true} if the error was caused by an outbound operation. - * @param cause the exception that was caught - * @param http2Ex the {@link StreamException} that is embedded in the causality chain. - */ - protected void onStreamError(ChannelHandlerContext ctx, boolean outbound, - @SuppressWarnings("unused") Throwable cause, StreamException http2Ex) { - final int streamId = http2Ex.streamId(); - Http2Stream stream = connection().stream(streamId); - - //if this is caused by reading headers that are too large, send a header with status 431 - if (http2Ex instanceof Http2Exception.HeaderListSizeException && - ((Http2Exception.HeaderListSizeException) http2Ex).duringDecode() && - connection().isServer()) { - - // NOTE We have to check to make sure that a stream exists before we send our reply. - // We likely always create the stream below as the stream isn't created until the - // header block is completely processed. - - // The case of a streamId referring to a stream which was already closed is handled - // by createStream and will land us in the catch block below - if (stream == null) { - try { - stream = encoder.connection().remote().createStream(streamId, true); - } catch (Http2Exception e) { - resetUnknownStream(ctx, streamId, http2Ex.error().code()); - return; - } - } - - // ensure that we have not already sent headers on this stream - if (stream != null && !stream.isHeadersSent()) { - try { - handleServerHeaderDecodeSizeError(ctx, stream); - } catch (Throwable cause2) { - onError(ctx, outbound, connectionError(INTERNAL_ERROR, cause2, "Error DecodeSizeError")); - } - } - } - - if (stream == null) { - if (!outbound || connection().local().mayHaveCreatedStream(streamId)) { - resetUnknownStream(ctx, streamId, http2Ex.error().code()); - } - } else { - resetStream(ctx, stream, http2Ex.error().code()); - } - } - - /** - * Notifies client that this server has received headers that are larger than what it is - * willing to accept. Override to change behavior. - * - * @param ctx the channel context - * @param stream the Http2Stream on which the header was received - */ - protected void handleServerHeaderDecodeSizeError(ChannelHandlerContext ctx, Http2Stream stream) { - encoder().writeHeaders(ctx, stream.id(), HEADERS_TOO_LARGE_HEADERS, 0, true); - } - - protected Http2FrameWriter frameWriter() { - return encoder().frameWriter(); - } - - /** - * Sends a {@code RST_STREAM} frame even if we don't know about the stream. This error condition is most likely - * triggered by the first frame of a stream being invalid. That is, there was an error reading the frame before - * we could create a new stream. - */ - private Future resetUnknownStream(final ChannelHandlerContext ctx, int streamId, long errorCode) { - Future future = frameWriter().writeRstStream(ctx, streamId, errorCode); - if (future.isDone()) { - closeConnectionOnError(ctx, future); - } else { - future.addListener(ctx, this::closeConnectionOnError); - } - return future; - } - - @Override - public Future resetStream(final ChannelHandlerContext ctx, int streamId, long errorCode) { - final Http2Stream stream = connection().stream(streamId); - if (stream == null) { - return resetUnknownStream(ctx, streamId, errorCode); - } - - return resetStream(ctx, stream, errorCode); - } - - private Future resetStream(final ChannelHandlerContext ctx, final Http2Stream stream, - long errorCode) { - if (stream.isResetSent()) { - // Don't write a RST_STREAM frame if we have already written one. - return ctx.newSucceededFuture(); - } - // Synchronously set the resetSent flag to prevent any subsequent calls - // from resulting in multiple reset frames being sent. - // - // This needs to be done before we notify the promise as the promise may have a listener attached that - // call resetStream(...) again. - stream.resetSent(); - - final Future future; - // If the remote peer is not aware of the steam, then we are not allowed to send a RST_STREAM - // https://tools.ietf.org/html/rfc7540#section-6.4. - if (stream.state() == IDLE || - connection().local().created(stream) && !stream.isHeadersSent() && !stream.isPushPromiseSent()) { - future = ctx.newSucceededFuture(); - } else { - future = frameWriter().writeRstStream(ctx, stream.id(), errorCode); - } - if (future.isDone()) { - processRstStreamWriteResult(ctx, stream, future); - } else { - future.addListener(future1 -> processRstStreamWriteResult(ctx, stream, future1)); - } - - return future; - } - - @Override - public Future goAway(final ChannelHandlerContext ctx, final int lastStreamId, final long errorCode, - final ByteBuf debugData) { - final Http2Connection connection = connection(); - try { - if (!connection.goAwaySent(lastStreamId, errorCode, debugData)) { - debugData.release(); - return ctx.newSucceededFuture(); - } - } catch (Throwable cause) { - debugData.release(); - return ctx.newFailedFuture(cause); - } - - // 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(); - Future future = frameWriter().writeGoAway(ctx, lastStreamId, errorCode, debugData); - - if (future.isDone()) { - processGoAwayWriteResult(ctx, lastStreamId, errorCode, debugData, future); - } else { - future.addListener(future1 -> - processGoAwayWriteResult(ctx, lastStreamId, errorCode, debugData, future1)); - } - - return future; - } - - /** - * Closes the connection if the graceful shutdown process has completed. - * @param future Represents the status that will be passed to the {@link #closeListener}. - */ - private void checkCloseConnection(Future future) { - // If this connection is closing and the graceful shutdown has completed, close the connection - // once this operation completes. - if (closeListener != null && isGracefulShutdownComplete()) { - FutureListener closeListener = this.closeListener; - // This method could be called multiple times - // and we don't want to notify the closeListener multiple times. - this.closeListener = null; - try { - closeListener.operationComplete(future); - } catch (Exception e) { - throw new IllegalStateException("Close listener threw an unexpected exception", e); - } - } - } - - /** - * Close the remote endpoint with with a {@code GO_AWAY} frame. Does not flush - * immediately, this is the responsibility of the caller. - */ - private Future goAway(ChannelHandlerContext ctx, Http2Exception cause) { - long errorCode = cause != null ? cause.error().code() : NO_ERROR.code(); - int lastKnownStream; - if (cause != null && cause.shutdownHint() == Http2Exception.ShutdownHint.HARD_SHUTDOWN) { - // The hard shutdown could have been triggered during header processing, before updating - // lastStreamCreated(). Specifically, any connection errors encountered by Http2FrameReader or HPACK - // decoding will fail to update the last known stream. So we must be pessimistic. - // https://github.com/netty/netty/issues/10670 - lastKnownStream = Integer.MAX_VALUE; - } else { - lastKnownStream = connection().remote().lastStreamCreated(); - } - return goAway(ctx, lastKnownStream, errorCode, Http2CodecUtil.toByteBuf(ctx, cause)); - } - - @SuppressWarnings("unchecked") - private void processRstStreamWriteResult(ChannelHandlerContext ctx, Http2Stream stream, Future future) { - if (future.isSuccess()) { - closeStream(stream, (Future) future); - } else { - // The connection will be closed and so no need to change the resetSent flag to false. - onConnectionError(ctx, true, future.cause(), null); - } - } - - private void closeConnectionOnError(ChannelHandlerContext ctx, Future future) { - if (future.isFailed()) { - onConnectionError(ctx, true, future.cause(), null); - } - } - - /** - * Returns the client preface string if this is a client connection, otherwise returns {@code null}. - */ - private static ByteBuf clientPrefaceString(Http2Connection connection) { - return connection.isServer() ? connectionPrefaceBuf() : null; - } - - private static void processGoAwayWriteResult(final ChannelHandlerContext ctx, final int lastStreamId, - final long errorCode, final ByteBuf debugData, Future future) { - try { - if (future.isSuccess()) { - if (errorCode != NO_ERROR.code()) { - if (logger.isDebugEnabled()) { - logger.debug("{} Sent GOAWAY: lastStreamId '{}', errorCode '{}', " + - "debugData '{}'. Forcing shutdown of the connection.", - ctx.channel(), lastStreamId, errorCode, debugData.toString(UTF_8), future.cause()); - } - ctx.close(); - } - } else { - if (logger.isDebugEnabled()) { - logger.debug("{} Sending GOAWAY failed: lastStreamId '{}', errorCode '{}', " + - "debugData '{}'. Forcing shutdown of the connection.", - ctx.channel(), lastStreamId, errorCode, debugData.toString(UTF_8), future.cause()); - } - ctx.close(); - } - } finally { - // We're done with the debug data now. - debugData.release(); - } - } - - /** - * Closes the channel when the future completes. - */ - private static final class ClosingChannelFutureListener implements FutureListener { - private final ChannelHandlerContext ctx; - private final Promise promise; - private final Future timeoutTask; - private boolean closed; - - ClosingChannelFutureListener(ChannelHandlerContext ctx, Promise promise) { - this.ctx = ctx; - this.promise = promise; - timeoutTask = null; - } - - ClosingChannelFutureListener(final ChannelHandlerContext ctx, final Promise promise, - long timeout, TimeUnit unit) { - this.ctx = ctx; - this.promise = promise; - timeoutTask = ctx.executor().schedule(this::doClose, timeout, unit); - } - - @Override - public void operationComplete(Future sentGoAwayFuture) { - if (timeoutTask != null) { - timeoutTask.cancel(); - } - doClose(); - } - - private void doClose() { - // We need to guard against multiple calls as the timeout may trigger close() first and then it will be - // triggered again because of operationComplete(...) is called. - if (closed) { - // This only happens if we also scheduled a timeout task. - assert timeoutTask != null; - return; - } - closed = true; - // Before trying to close we need to check if the handler still exists in the pipeline as it may - // have been removed already. - if (!ctx.isRemoved()) { - if (promise == null) { - ctx.close(); - } else { - ctx.close().cascadeTo(promise); - } - } else if (promise != null) { - promise.setSuccess(null); - } - } - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionHandlerBuilder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionHandlerBuilder.java deleted file mode 100644 index c9ddfdda7f..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionHandlerBuilder.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.handler.codec.http2.Http2HeadersEncoder.SensitivityDetector; -import io.netty.util.internal.UnstableApi; - -/** - * Builder which builds {@link Http2ConnectionHandler} objects. - */ -@UnstableApi -public final class Http2ConnectionHandlerBuilder - extends AbstractHttp2ConnectionHandlerBuilder { - - @Override - public Http2ConnectionHandlerBuilder validateHeaders(boolean validateHeaders) { - return super.validateHeaders(validateHeaders); - } - - @Override - public Http2ConnectionHandlerBuilder initialSettings(Http2Settings settings) { - return super.initialSettings(settings); - } - - @Override - public Http2Settings initialSettings() { - return super.initialSettings(); - } - - @Override - public Http2ConnectionHandlerBuilder frameListener(Http2FrameListener frameListener) { - return super.frameListener(frameListener); - } - - @Override - public Http2ConnectionHandlerBuilder gracefulShutdownTimeoutMillis(long gracefulShutdownTimeoutMillis) { - return super.gracefulShutdownTimeoutMillis(gracefulShutdownTimeoutMillis); - } - - @Override - public Http2ConnectionHandlerBuilder server(boolean isServer) { - return super.server(isServer); - } - - @Override - public Http2ConnectionHandlerBuilder connection(Http2Connection connection) { - return super.connection(connection); - } - - @Override - public Http2ConnectionHandlerBuilder maxReservedStreams(int maxReservedStreams) { - return super.maxReservedStreams(maxReservedStreams); - } - - @Override - public Http2ConnectionHandlerBuilder codec(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder) { - return super.codec(decoder, encoder); - } - - @Override - public Http2ConnectionHandlerBuilder frameLogger(Http2FrameLogger frameLogger) { - return super.frameLogger(frameLogger); - } - - @Override - public Http2ConnectionHandlerBuilder encoderEnforceMaxConcurrentStreams( - boolean encoderEnforceMaxConcurrentStreams) { - return super.encoderEnforceMaxConcurrentStreams(encoderEnforceMaxConcurrentStreams); - } - - @Override - public Http2ConnectionHandlerBuilder encoderIgnoreMaxHeaderListSize(boolean encoderIgnoreMaxHeaderListSize) { - return super.encoderIgnoreMaxHeaderListSize(encoderIgnoreMaxHeaderListSize); - } - - @Override - public Http2ConnectionHandlerBuilder headerSensitivityDetector(SensitivityDetector headerSensitivityDetector) { - return super.headerSensitivityDetector(headerSensitivityDetector); - } - - @Override - @Deprecated - public Http2ConnectionHandlerBuilder initialHuffmanDecodeCapacity(int initialHuffmanDecodeCapacity) { - return super.initialHuffmanDecodeCapacity(initialHuffmanDecodeCapacity); - } - - @Override - public Http2ConnectionHandlerBuilder decoupleCloseAndGoAway(boolean decoupleCloseAndGoAway) { - return super.decoupleCloseAndGoAway(decoupleCloseAndGoAway); - } - - @Override - public Http2ConnectionHandler build() { - return super.build(); - } - - @Override - protected Http2ConnectionHandler build(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder, - Http2Settings initialSettings) { - return new Http2ConnectionHandler(decoder, encoder, initialSettings, decoupleCloseAndGoAway()); - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionPrefaceAndSettingsFrameWrittenEvent.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionPrefaceAndSettingsFrameWrittenEvent.java deleted file mode 100644 index 6e975a037b..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionPrefaceAndSettingsFrameWrittenEvent.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.util.internal.UnstableApi; - -/** - * Signifies that the connection preface and - * the initial SETTINGS frame have been sent. The client sends the preface, and the server receives the preface. - * The client shouldn't write any data until this event has been processed. - */ -@UnstableApi -public final class Http2ConnectionPrefaceAndSettingsFrameWrittenEvent { - static final Http2ConnectionPrefaceAndSettingsFrameWrittenEvent INSTANCE = - new Http2ConnectionPrefaceAndSettingsFrameWrittenEvent(); - - private Http2ConnectionPrefaceAndSettingsFrameWrittenEvent() { - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ControlFrameLimitEncoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ControlFrameLimitEncoder.java deleted file mode 100644 index c68f2a74b2..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ControlFrameLimitEncoder.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.FutureListener; -import io.netty.util.internal.ObjectUtil; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -/** - * {@link DecoratingHttp2ConnectionEncoder} which guards against a remote peer that will trigger a massive amount - * of control frames but will not consume our responses to these. - * This encoder will tear-down the connection once we reached the configured limit to reduce the risk of DDOS. - */ -final class Http2ControlFrameLimitEncoder extends DecoratingHttp2ConnectionEncoder { - private static final InternalLogger logger = InternalLoggerFactory.getInstance(Http2ControlFrameLimitEncoder.class); - - private final int maxOutstandingControlFrames; - private Http2LifecycleManager lifecycleManager; - private int outstandingControlFrames; - private final FutureListener outstandingControlFramesListener = future -> outstandingControlFrames--; - private boolean limitReached; - - Http2ControlFrameLimitEncoder(Http2ConnectionEncoder delegate, int maxOutstandingControlFrames) { - super(delegate); - this.maxOutstandingControlFrames = ObjectUtil.checkPositive(maxOutstandingControlFrames, - "maxOutstandingControlFrames"); - } - - @Override - public void lifecycleManager(Http2LifecycleManager lifecycleManager) { - this.lifecycleManager = lifecycleManager; - super.lifecycleManager(lifecycleManager); - } - - @Override - public Future writeSettingsAck(ChannelHandlerContext ctx) { - FutureListener listener = handleOutstandingControlFrames(ctx); - Future f = super.writeSettingsAck(ctx); - if (listener != null) { - f.addListener(listener); - } - return f; - } - - @Override - public Future writePing(ChannelHandlerContext ctx, boolean ack, long data) { - // Only apply the limit to ping acks. - if (ack) { - FutureListener listener = handleOutstandingControlFrames(ctx); - Future f = super.writePing(ctx, ack, data); - if (listener != null) { - f.addListener(listener); - } - return f; - } - return super.writePing(ctx, ack, data); - } - - @Override - public Future writeRstStream( - ChannelHandlerContext ctx, int streamId, long errorCode) { - FutureListener listener = handleOutstandingControlFrames(ctx); - Future f = super.writeRstStream(ctx, streamId, errorCode); - if (listener != null) { - f.addListener(listener); - } - return f; - } - - private FutureListener handleOutstandingControlFrames(ChannelHandlerContext ctx) { - if (!limitReached) { - if (outstandingControlFrames == maxOutstandingControlFrames) { - // Let's try to flush once as we may be able to flush some of the control frames. - ctx.flush(); - } - if (outstandingControlFrames == maxOutstandingControlFrames) { - limitReached = true; - Http2Exception exception = Http2Exception.connectionError(Http2Error.ENHANCE_YOUR_CALM, - "Maximum number %d of outstanding control frames reached", maxOutstandingControlFrames); - logger.info("Maximum number {} of outstanding control frames reached. Closing channel {}", - maxOutstandingControlFrames, ctx.channel(), exception); - - // First notify the Http2LifecycleManager and then close the connection. - lifecycleManager.onError(ctx, true, exception); - ctx.close(); - return null; - } - outstandingControlFrames++; - - // We did not reach the limit yet, add the listener to decrement the number of outstanding control frames - // once the promise was completed - return outstandingControlFramesListener; - } - return null; - } -} 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 deleted file mode 100644 index 15097b0243..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2DataFrame.java +++ /dev/null @@ -1,73 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufHolder; -import io.netty.util.internal.UnstableApi; - -/** - * HTTP/2 DATA frame. - */ -@UnstableApi -public interface Http2DataFrame extends Http2StreamFrame, ByteBufHolder { - - /** - * Frame padding to use. Will be non-negative and less than 256. - */ - int padding(); - - /** - * Payload of DATA frame. Will not be {@code null}. - */ - @Override - ByteBuf content(); - - /** - * Returns the number of bytes that are flow-controlled initially, so even if the {@link #content()} is consumed - * this will not change. - */ - int initialFlowControlledBytes(); - - /** - * Returns {@code true} if the END_STREAM flag is set. - */ - boolean isEndStream(); - - @Override - Http2DataFrame copy(); - - @Override - Http2DataFrame duplicate(); - - @Override - Http2DataFrame retainedDuplicate(); - - @Override - Http2DataFrame replace(ByteBuf content); - - @Override - Http2DataFrame retain(); - - @Override - Http2DataFrame retain(int increment); - - @Override - Http2DataFrame touch(); - - @Override - Http2DataFrame touch(Object hint); -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2DataWriter.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2DataWriter.java deleted file mode 100644 index 5da9a9ed6f..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2DataWriter.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; -import io.netty.util.internal.UnstableApi; - -/** - * Interface that defines an object capable of producing HTTP/2 data frames. - */ -@UnstableApi -public interface Http2DataWriter { - /** - * Writes a {@code DATA} frame to the remote endpoint. This will result in one or more - * frames being written to the context. - * - * @param ctx the context to use for writing. - * @param streamId the stream for which to send the frame. - * @param data the payload of the frame. This will be released by this method. - * @param padding additional bytes that should be added to obscure the true content size. Must be between 0 and - * 256 (inclusive). A 1 byte padding is encoded as just the pad length field with value 0. - * A 256 byte padding is encoded as the pad length field with value 255 and 255 padding bytes - * appended to the end of the frame. - * @param endStream indicates if this is the last frame to be sent for the stream. - * @return the future for the write. - */ - Future writeData(ChannelHandlerContext ctx, int streamId, - ByteBuf data, int padding, boolean endStream); -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2EmptyDataFrameConnectionDecoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2EmptyDataFrameConnectionDecoder.java deleted file mode 100644 index 69f2f2f231..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2EmptyDataFrameConnectionDecoder.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.util.internal.ObjectUtil; - -/** - * Enforce a limit on the maximum number of consecutive empty DATA frames (without end_of_stream flag) that are allowed - * before the connection will be closed. - */ -final class Http2EmptyDataFrameConnectionDecoder extends DecoratingHttp2ConnectionDecoder { - - private final int maxConsecutiveEmptyFrames; - - Http2EmptyDataFrameConnectionDecoder(Http2ConnectionDecoder delegate, int maxConsecutiveEmptyFrames) { - super(delegate); - this.maxConsecutiveEmptyFrames = ObjectUtil.checkPositive( - maxConsecutiveEmptyFrames, "maxConsecutiveEmptyFrames"); - } - - @Override - public void frameListener(Http2FrameListener listener) { - if (listener != null) { - super.frameListener(new Http2EmptyDataFrameListener(listener, maxConsecutiveEmptyFrames)); - } else { - super.frameListener(null); - } - } - - @Override - public Http2FrameListener frameListener() { - Http2FrameListener frameListener = frameListener0(); - // Unwrap the original Http2FrameListener as we add this decoder under the hood. - if (frameListener instanceof Http2EmptyDataFrameListener) { - return ((Http2EmptyDataFrameListener) frameListener).listener; - } - return frameListener; - } - - // Package-private for testing - Http2FrameListener frameListener0() { - return super.frameListener(); - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2EmptyDataFrameListener.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2EmptyDataFrameListener.java deleted file mode 100644 index 7f194bf6bb..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2EmptyDataFrameListener.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.internal.ObjectUtil; - -/** - * Enforce a limit on the maximum number of consecutive empty DATA frames (without end_of_stream flag) that are allowed - * before the connection will be closed. - */ -final class Http2EmptyDataFrameListener extends Http2FrameListenerDecorator { - private final int maxConsecutiveEmptyFrames; - - private boolean violationDetected; - private int emptyDataFrames; - - Http2EmptyDataFrameListener(Http2FrameListener listener, int maxConsecutiveEmptyFrames) { - super(listener); - this.maxConsecutiveEmptyFrames = ObjectUtil.checkPositive( - maxConsecutiveEmptyFrames, "maxConsecutiveEmptyFrames"); - } - - @Override - public int onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding, boolean endOfStream) - throws Http2Exception { - if (endOfStream || data.isReadable()) { - emptyDataFrames = 0; - } else if (emptyDataFrames++ == maxConsecutiveEmptyFrames && !violationDetected) { - violationDetected = true; - throw Http2Exception.connectionError(Http2Error.ENHANCE_YOUR_CALM, - "Maximum number %d of empty data frames without end_of_stream flag received", - maxConsecutiveEmptyFrames); - } - - return super.onDataRead(ctx, streamId, data, padding, endOfStream); - } - - @Override - public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, - int padding, boolean endStream) throws Http2Exception { - emptyDataFrames = 0; - super.onHeadersRead(ctx, streamId, headers, padding, endStream); - } - - @Override - public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int streamDependency, - short weight, boolean exclusive, int padding, boolean endStream) throws Http2Exception { - emptyDataFrames = 0; - super.onHeadersRead(ctx, streamId, headers, streamDependency, weight, exclusive, padding, endStream); - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Error.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Error.java deleted file mode 100644 index 96187b7a53..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Error.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.util.internal.UnstableApi; - -/** - * All error codes identified by the HTTP/2 spec. - */ -@UnstableApi -public enum Http2Error { - NO_ERROR(0x0), - PROTOCOL_ERROR(0x1), - INTERNAL_ERROR(0x2), - FLOW_CONTROL_ERROR(0x3), - SETTINGS_TIMEOUT(0x4), - STREAM_CLOSED(0x5), - FRAME_SIZE_ERROR(0x6), - REFUSED_STREAM(0x7), - CANCEL(0x8), - COMPRESSION_ERROR(0x9), - CONNECT_ERROR(0xA), - ENHANCE_YOUR_CALM(0xB), - INADEQUATE_SECURITY(0xC), - HTTP_1_1_REQUIRED(0xD); - - private final long code; - private static final Http2Error[] INT_TO_ENUM_MAP; - static { - Http2Error[] errors = values(); - Http2Error[] map = new Http2Error[errors.length]; - for (Http2Error error : errors) { - map[(int) error.code()] = error; - } - INT_TO_ENUM_MAP = map; - } - - Http2Error(long code) { - this.code = code; - } - - /** - * Gets the code for this error used on the wire. - */ - public long code() { - return code; - } - - public static Http2Error valueOf(long value) { - return value >= INT_TO_ENUM_MAP.length || value < 0 ? null : INT_TO_ENUM_MAP[(int) value]; - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2EventAdapter.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2EventAdapter.java deleted file mode 100644 index 328b8239a4..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2EventAdapter.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.internal.UnstableApi; - -/** - * This class brings {@link Http2Connection.Listener} and {@link Http2FrameListener} together to provide - * NOOP implementation so inheriting classes can selectively choose which methods to override. - */ -@UnstableApi -public class Http2EventAdapter implements Http2Connection.Listener, Http2FrameListener { - @Override - public int onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding, boolean endOfStream) - throws Http2Exception { - return data.readableBytes() + padding; - } - - @Override - public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding, - boolean endStream) throws Http2Exception { - } - - @Override - public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int streamDependency, - short weight, boolean exclusive, int padding, boolean endStream) throws Http2Exception { - } - - @Override - public void onPriorityRead(ChannelHandlerContext ctx, int streamId, int streamDependency, short weight, - boolean exclusive) throws Http2Exception { - } - - @Override - public void onRstStreamRead(ChannelHandlerContext ctx, int streamId, long errorCode) throws Http2Exception { - } - - @Override - public void onSettingsAckRead(ChannelHandlerContext ctx) throws Http2Exception { - } - - @Override - public void onSettingsRead(ChannelHandlerContext ctx, Http2Settings settings) throws Http2Exception { - } - - @Override - public void onPingRead(ChannelHandlerContext ctx, long data) throws Http2Exception { - } - - @Override - public void onPingAckRead(ChannelHandlerContext ctx, long data) throws Http2Exception { - } - - @Override - public void onPushPromiseRead(ChannelHandlerContext ctx, int streamId, int promisedStreamId, - Http2Headers headers, int padding) throws Http2Exception { - } - - @Override - public void onGoAwayRead(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData) - throws Http2Exception { - } - - @Override - public void onWindowUpdateRead(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement) - throws Http2Exception { - } - - @Override - public void onUnknownFrame(ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags, - ByteBuf payload) throws Http2Exception { - } - - @Override - public void onStreamAdded(Http2Stream stream) { - } - - @Override - public void onStreamActive(Http2Stream stream) { - } - - @Override - public void onStreamHalfClosed(Http2Stream stream) { - } - - @Override - public void onStreamClosed(Http2Stream stream) { - } - - @Override - public void onStreamRemoved(Http2Stream stream) { - } - - @Override - public void onGoAwaySent(int lastStreamId, long errorCode, ByteBuf debugData) { - } - - @Override - public void onGoAwayReceived(int lastStreamId, long errorCode, ByteBuf debugData) { - } -} 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 deleted file mode 100644 index 6c3922e537..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Exception.java +++ /dev/null @@ -1,320 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.util.internal.ThrowableUtil; -import io.netty.util.internal.UnstableApi; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import static io.netty.handler.codec.http2.Http2CodecUtil.CONNECTION_STREAM_ID; -import static java.util.Objects.requireNonNull; - -/** - * Exception thrown when an HTTP/2 error was encountered. - */ -@UnstableApi -public class Http2Exception extends Exception { - private static final long serialVersionUID = -6941186345430164209L; - private final Http2Error error; - private final ShutdownHint shutdownHint; - - public Http2Exception(Http2Error error) { - this(error, ShutdownHint.HARD_SHUTDOWN); - } - - public Http2Exception(Http2Error error, ShutdownHint shutdownHint) { - this.error = requireNonNull(error, "error"); - this.shutdownHint = requireNonNull(shutdownHint, "shutdownHint"); - } - - public Http2Exception(Http2Error error, String message) { - this(error, message, ShutdownHint.HARD_SHUTDOWN); - } - - public Http2Exception(Http2Error error, String message, ShutdownHint shutdownHint) { - super(message); - this.error = requireNonNull(error, "error"); - this.shutdownHint = requireNonNull(shutdownHint, "shutdownHint"); - } - - public Http2Exception(Http2Error error, String message, Throwable cause) { - this(error, message, cause, ShutdownHint.HARD_SHUTDOWN); - } - - public Http2Exception(Http2Error error, String message, Throwable cause, ShutdownHint shutdownHint) { - super(message, cause); - this.error = requireNonNull(error, "error"); - this.shutdownHint = requireNonNull(shutdownHint, "shutdownHint"); - } - - static Http2Exception newStatic(Http2Error error, String message, ShutdownHint shutdownHint, - Class clazz, String method) { - return ThrowableUtil.unknownStackTrace( - new StacklessHttp2Exception(error, message, shutdownHint), clazz, method); - } - - private Http2Exception(Http2Error error, String message, ShutdownHint shutdownHint, boolean shared) { - super(message, null, false, true); - assert shared; - this.error = requireNonNull(error, "error"); - this.shutdownHint = requireNonNull(shutdownHint, "shutdownHint"); - } - - public Http2Error error() { - return error; - } - - /** - * Provide a hint as to what type of shutdown should be executed. Note this hint may be ignored. - */ - public ShutdownHint shutdownHint() { - return shutdownHint; - } - - /** - * Use if an error has occurred which can not be isolated to a single stream, but instead applies - * to the entire connection. - * @param error The type of error as defined by the HTTP/2 specification. - * @param fmt String with the content and format for the additional debug data. - * @param args Objects which fit into the format defined by {@code fmt}. - * @return An exception which can be translated into an HTTP/2 error. - */ - public static Http2Exception connectionError(Http2Error error, String fmt, Object... args) { - return new Http2Exception(error, String.format(fmt, args)); - } - - /** - * Use if an error has occurred which can not be isolated to a single stream, but instead applies - * to the entire connection. - * @param error The type of error as defined by the HTTP/2 specification. - * @param cause The object which caused the error. - * @param fmt String with the content and format for the additional debug data. - * @param args Objects which fit into the format defined by {@code fmt}. - * @return An exception which can be translated into an HTTP/2 error. - */ - public static Http2Exception connectionError(Http2Error error, Throwable cause, - String fmt, Object... args) { - return new Http2Exception(error, String.format(fmt, args), cause); - } - - /** - * Use if an error has occurred which can not be isolated to a single stream, but instead applies - * to the entire connection. - * @param error The type of error as defined by the HTTP/2 specification. - * @param fmt String with the content and format for the additional debug data. - * @param args Objects which fit into the format defined by {@code fmt}. - * @return An exception which can be translated into an HTTP/2 error. - */ - public static Http2Exception closedStreamError(Http2Error error, String fmt, Object... args) { - return new ClosedStreamCreationException(error, String.format(fmt, args)); - } - - /** - * Use if an error which can be isolated to a single stream has occurred. If the {@code id} is not - * {@link Http2CodecUtil#CONNECTION_STREAM_ID} then a {@link StreamException} will be returned. - * Otherwise the error is considered a connection error and a {@link Http2Exception} is returned. - * @param id The stream id for which the error is isolated to. - * @param error The type of error as defined by the HTTP/2 specification. - * @param fmt String with the content and format for the additional debug data. - * @param args Objects which fit into the format defined by {@code fmt}. - * @return If the {@code id} is not - * {@link Http2CodecUtil#CONNECTION_STREAM_ID} then a {@link StreamException} will be returned. - * Otherwise the error is considered a connection error and a {@link Http2Exception} is returned. - */ - public static Http2Exception streamError(int id, Http2Error error, String fmt, Object... args) { - return CONNECTION_STREAM_ID == id ? - connectionError(error, fmt, args) : - new StreamException(id, error, String.format(fmt, args)); - } - - /** - * Use if an error which can be isolated to a single stream has occurred. If the {@code id} is not - * {@link Http2CodecUtil#CONNECTION_STREAM_ID} then a {@link StreamException} will be returned. - * Otherwise the error is considered a connection error and a {@link Http2Exception} is returned. - * @param id The stream id for which the error is isolated to. - * @param error The type of error as defined by the HTTP/2 specification. - * @param cause The object which caused the error. - * @param fmt String with the content and format for the additional debug data. - * @param args Objects which fit into the format defined by {@code fmt}. - * @return If the {@code id} is not - * {@link Http2CodecUtil#CONNECTION_STREAM_ID} then a {@link StreamException} will be returned. - * Otherwise the error is considered a connection error and a {@link Http2Exception} is returned. - */ - public static Http2Exception streamError(int id, Http2Error error, Throwable cause, - String fmt, Object... args) { - return CONNECTION_STREAM_ID == id ? - connectionError(error, cause, fmt, args) : - new StreamException(id, error, String.format(fmt, args), cause); - } - - /** - * A specific stream error resulting from failing to decode headers that exceeds the max header size list. - * If the {@code id} is not {@link Http2CodecUtil#CONNECTION_STREAM_ID} then a - * {@link StreamException} will be returned. Otherwise the error is considered a - * connection error and a {@link Http2Exception} is returned. - * @param id The stream id for which the error is isolated to. - * @param error The type of error as defined by the HTTP/2 specification. - * @param onDecode Whether this error was caught while decoding headers - * @param fmt String with the content and format for the additional debug data. - * @param args Objects which fit into the format defined by {@code fmt}. - * @return If the {@code id} is not - * {@link Http2CodecUtil#CONNECTION_STREAM_ID} then a {@link HeaderListSizeException} - * will be returned. Otherwise the error is considered a connection error and a {@link Http2Exception} is - * returned. - */ - public static Http2Exception headerListSizeError(int id, Http2Error error, boolean onDecode, - String fmt, Object... args) { - return CONNECTION_STREAM_ID == id ? - connectionError(error, fmt, args) : - new HeaderListSizeException(id, error, String.format(fmt, args), onDecode); - } - - /** - * Check if an exception is isolated to a single stream or the entire connection. - * @param e The exception to check. - * @return {@code true} if {@code e} is an instance of {@link StreamException}. - * {@code false} otherwise. - */ - public static boolean isStreamError(Http2Exception e) { - return e instanceof StreamException; - } - - /** - * Get the stream id associated with an exception. - * @param e The exception to get the stream id for. - * @return {@link Http2CodecUtil#CONNECTION_STREAM_ID} if {@code e} is a connection error. - * Otherwise the stream id associated with the stream error. - */ - public static int streamId(Http2Exception e) { - return isStreamError(e) ? ((StreamException) e).streamId() : CONNECTION_STREAM_ID; - } - - /** - * Provides a hint as to if shutdown is justified, what type of shutdown should be executed. - */ - public enum ShutdownHint { - /** - * Do not shutdown the underlying channel. - */ - NO_SHUTDOWN, - /** - * Attempt to execute a "graceful" shutdown. The definition of "graceful" is left to the implementation. - * An example of "graceful" would be wait for some amount of time until all active streams are closed. - */ - GRACEFUL_SHUTDOWN, - /** - * Close the channel immediately after a {@code GOAWAY} is sent. - */ - HARD_SHUTDOWN - } - - /** - * Used when a stream creation attempt fails but may be because the stream was previously closed. - */ - public static final class ClosedStreamCreationException extends Http2Exception { - private static final long serialVersionUID = -6746542974372246206L; - - public ClosedStreamCreationException(Http2Error error) { - super(error); - } - - public ClosedStreamCreationException(Http2Error error, String message) { - super(error, message); - } - - public ClosedStreamCreationException(Http2Error error, String message, Throwable cause) { - super(error, message, cause); - } - } - - /** - * Represents an exception that can be isolated to a single stream (as opposed to the entire connection). - */ - public static class StreamException extends Http2Exception { - private static final long serialVersionUID = 602472544416984384L; - private final int streamId; - - StreamException(int streamId, Http2Error error, String message) { - super(error, message, ShutdownHint.NO_SHUTDOWN); - this.streamId = streamId; - } - - StreamException(int streamId, Http2Error error, String message, Throwable cause) { - super(error, message, cause, ShutdownHint.NO_SHUTDOWN); - this.streamId = streamId; - } - - public int streamId() { - return streamId; - } - } - - public static final class HeaderListSizeException extends StreamException { - private static final long serialVersionUID = -8807603212183882637L; - - private final boolean decode; - - HeaderListSizeException(int streamId, Http2Error error, String message, boolean decode) { - super(streamId, error, message); - this.decode = decode; - } - - public boolean duringDecode() { - return decode; - } - } - - /** - * Provides the ability to handle multiple stream exceptions with one throw statement. - */ - public static final class CompositeStreamException extends Http2Exception implements Iterable { - private static final long serialVersionUID = 7091134858213711015L; - private final List exceptions; - - public CompositeStreamException(Http2Error error, int initialCapacity) { - super(error, ShutdownHint.NO_SHUTDOWN); - exceptions = new ArrayList<>(initialCapacity); - } - - public void add(StreamException e) { - exceptions.add(e); - } - - @Override - public Iterator iterator() { - return exceptions.iterator(); - } - } - - private static final class StacklessHttp2Exception extends Http2Exception { - - private static final long serialVersionUID = 1077888485687219443L; - - StacklessHttp2Exception(Http2Error error, String message, ShutdownHint shutdownHint) { - super(error, message, shutdownHint, true); - } - - // Override fillInStackTrace() so we not populate the backtrace via a native call and so leak the - // Classloader. - @Override - public Throwable fillInStackTrace() { - return this; - } - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Flags.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Flags.java deleted file mode 100644 index 7885d20173..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Flags.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.util.internal.UnstableApi; - -/** - * Provides utility methods for accessing specific flags as defined by the HTTP/2 spec. - */ -@UnstableApi -public final class Http2Flags { - public static final short END_STREAM = 0x1; - public static final short END_HEADERS = 0x4; - public static final short ACK = 0x1; - public static final short PADDED = 0x8; - public static final short PRIORITY = 0x20; - - private short value; - - public Http2Flags() { - } - - public Http2Flags(short value) { - this.value = value; - } - - /** - * Gets the underlying flags value. - */ - public short value() { - return value; - } - - /** - * Determines whether the {@link #END_STREAM} flag is set. Only applies to DATA and HEADERS - * frames. - */ - public boolean endOfStream() { - return isFlagSet(END_STREAM); - } - - /** - * Determines whether the {@link #END_HEADERS} flag is set. Only applies for HEADERS, - * PUSH_PROMISE, and CONTINUATION frames. - */ - public boolean endOfHeaders() { - return isFlagSet(END_HEADERS); - } - - /** - * Determines whether the flag is set indicating the presence of the exclusive, stream - * dependency, and weight fields in a HEADERS frame. - */ - public boolean priorityPresent() { - return isFlagSet(PRIORITY); - } - - /** - * Determines whether the flag is set indicating that this frame is an ACK. Only applies for - * SETTINGS and PING frames. - */ - public boolean ack() { - return isFlagSet(ACK); - } - - /** - * For frames that include padding, indicates if the {@link #PADDED} field is present. Only - * applies to DATA, HEADERS, PUSH_PROMISE and CONTINUATION frames. - */ - public boolean paddingPresent() { - return isFlagSet(PADDED); - } - - /** - * Gets the number of bytes expected for the priority fields of the payload. This is determined - * by the {@link #priorityPresent()} flag. - */ - public int getNumPriorityBytes() { - return priorityPresent() ? 5 : 0; - } - - /** - * Gets the length in bytes of the padding presence field expected in the payload. This is - * determined by the {@link #paddingPresent()} flag. - */ - public int getPaddingPresenceFieldLength() { - return paddingPresent() ? 1 : 0; - } - - /** - * Sets the {@link #END_STREAM} flag. - */ - public Http2Flags endOfStream(boolean endOfStream) { - return setFlag(endOfStream, END_STREAM); - } - - /** - * Sets the {@link #END_HEADERS} flag. - */ - public Http2Flags endOfHeaders(boolean endOfHeaders) { - return setFlag(endOfHeaders, END_HEADERS); - } - - /** - * Sets the {@link #PRIORITY} flag. - */ - public Http2Flags priorityPresent(boolean priorityPresent) { - return setFlag(priorityPresent, PRIORITY); - } - - /** - * Sets the {@link #PADDED} flag. - */ - public Http2Flags paddingPresent(boolean paddingPresent) { - return setFlag(paddingPresent, PADDED); - } - - /** - * Sets the {@link #ACK} flag. - */ - public Http2Flags ack(boolean ack) { - return setFlag(ack, ACK); - } - - /** - * Generic method to set any flag. - * @param on if the flag should be enabled or disabled. - * @param mask the mask that identifies the bit for the flag. - * @return this instance. - */ - public Http2Flags setFlag(boolean on, short mask) { - if (on) { - value |= mask; - } else { - value &= ~mask; - } - return this; - } - - /** - * Indicates whether or not a particular flag is set. - * @param mask the mask identifying the bit for the particular flag being tested - * @return {@code true} if the flag is set - */ - public boolean isFlagSet(short mask) { - return (value & mask) != 0; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + value; - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - - return value == ((Http2Flags) obj).value; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("value = ").append(value).append(" ("); - if (ack()) { - builder.append("ACK,"); - } - if (endOfHeaders()) { - builder.append("END_OF_HEADERS,"); - } - if (endOfStream()) { - builder.append("END_OF_STREAM,"); - } - if (priorityPresent()) { - builder.append("PRIORITY_PRESENT,"); - } - if (paddingPresent()) { - builder.append("PADDING_PRESENT,"); - } - builder.append(')'); - return builder.toString(); - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FlowController.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FlowController.java deleted file mode 100644 index 2846b3d84c..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FlowController.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.internal.UnstableApi; - -/** - * Base interface for all HTTP/2 flow controllers. - */ -@UnstableApi -public interface Http2FlowController { - /** - * Set the {@link ChannelHandlerContext} for which to apply flow control on. - *

- * This must be called to properly initialize the {@link Http2FlowController}. - * Not calling this is considered a programming error. - * @param ctx The {@link ChannelHandlerContext} for which to apply flow control on. - * @throws Http2Exception if any protocol-related error occurred. - */ - void channelHandlerContext(ChannelHandlerContext ctx) throws Http2Exception; - - /** - * Sets the connection-wide initial flow control window and updates all stream windows (but not the connection - * stream window) by the delta. - *

- * Represents the value for - * SETTINGS_INITIAL_WINDOW_SIZE. This method should - * only be called by Netty (not users) as a result of a receiving a {@code SETTINGS} frame. - * - * @param newWindowSize the new initial window size. - * @throws Http2Exception thrown if any protocol-related error occurred. - */ - void initialWindowSize(int newWindowSize) throws Http2Exception; - - /** - * Gets the connection-wide initial flow control window size that is used as the basis for new stream flow - * control windows. - *

- * Represents the value for - * SETTINGS_INITIAL_WINDOW_SIZE. The initial value - * returned by this method must be {@link Http2CodecUtil#DEFAULT_WINDOW_SIZE}. - */ - int initialWindowSize(); - - /** - * Get the portion of the flow control window for the given stream that is currently available for sending/receiving - * frames which are subject to flow control. This quantity is measured in number of bytes. - */ - int windowSize(Http2Stream stream); - - /** - * Increments the size of the stream's flow control window by the given delta. - *

- * In the case of a {@link Http2RemoteFlowController} this is called upon receipt of a - * {@code WINDOW_UPDATE} frame from the remote endpoint to mirror the changes to the window - * size. - *

- * For a {@link Http2LocalFlowController} this can be called to request the expansion of the - * window size published by this endpoint. It is up to the implementation, however, as to when a - * {@code WINDOW_UPDATE} is actually sent. - * - * @param stream The subject stream. Use {@link Http2Connection#connectionStream()} for - * requesting the size of the connection window. - * @param delta the change in size of the flow control window. - * @throws Http2Exception thrown if a protocol-related error occurred. - */ - void incrementWindowSize(Http2Stream stream, int delta) throws Http2Exception; -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Frame.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Frame.java deleted file mode 100644 index 799edaf7b7..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Frame.java +++ /dev/null @@ -1,28 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.util.internal.UnstableApi; - -/** An HTTP/2 frame. */ -@UnstableApi -public interface Http2Frame { - - /** - * Returns the name of the HTTP/2 frame e.g. DATA, GOAWAY, etc. - */ - String name(); -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameAdapter.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameAdapter.java deleted file mode 100644 index e491e6cdc7..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameAdapter.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.internal.UnstableApi; - -/** - * Convenience class that provides no-op implementations for all methods of {@link Http2FrameListener}. - */ -@UnstableApi -public class Http2FrameAdapter implements Http2FrameListener { - - @Override - public int onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding, - boolean endOfStream) throws Http2Exception { - return data.readableBytes() + padding; - } - - @Override - public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, - int padding, boolean endStream) throws Http2Exception { - } - - @Override - public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, - int streamDependency, short weight, boolean exclusive, int padding, boolean endStream) - throws Http2Exception { - } - - @Override - public void onPriorityRead(ChannelHandlerContext ctx, int streamId, int streamDependency, - short weight, boolean exclusive) throws Http2Exception { - } - - @Override - public void onRstStreamRead(ChannelHandlerContext ctx, int streamId, long errorCode) - throws Http2Exception { - } - - @Override - public void onSettingsAckRead(ChannelHandlerContext ctx) throws Http2Exception { - } - - @Override - public void onSettingsRead(ChannelHandlerContext ctx, Http2Settings settings) - throws Http2Exception { - } - - @Override - public void onPingRead(ChannelHandlerContext ctx, long data) throws Http2Exception { - } - - @Override - public void onPingAckRead(ChannelHandlerContext ctx, long data) throws Http2Exception { - } - - @Override - public void onPushPromiseRead(ChannelHandlerContext ctx, int streamId, int promisedStreamId, - Http2Headers headers, int padding) throws Http2Exception { - } - - @Override - public void onGoAwayRead(ChannelHandlerContext ctx, int lastStreamId, long errorCode, - ByteBuf debugData) throws Http2Exception { - } - - @Override - public void onWindowUpdateRead(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement) - throws Http2Exception { - } - - @Override - public void onUnknownFrame(ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags, - ByteBuf payload) { - } -} 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 deleted file mode 100644 index a1010c2c20..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameCodec.java +++ /dev/null @@ -1,752 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.UnsupportedMessageTypeException; -import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeEvent; -import io.netty.handler.codec.http2.Http2Connection.PropertyKey; -import io.netty.handler.codec.http2.Http2Stream.State; -import io.netty.handler.codec.http2.StreamBufferingEncoder.Http2ChannelClosedException; -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.concurrent.Future; -import io.netty.util.concurrent.Promise; -import io.netty.util.internal.UnstableApi; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import static io.netty.buffer.ByteBufUtil.writeAscii; -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.NO_ERROR; -import static io.netty.util.internal.logging.InternalLogLevel.DEBUG; - -/** - *

This API is very immature. The Http2Connection-based API is currently preferred over this API. - * This API is targeted to eventually replace or reduce the need for the {@link Http2ConnectionHandler} API. - * - *

An HTTP/2 handler that maps HTTP/2 frames to {@link Http2Frame} objects and vice versa. For every incoming HTTP/2 - * frame, an {@link Http2Frame} object is created and propagated via {@link #channelRead}. Outbound {@link Http2Frame} - * objects received via {@link #write} are converted to the HTTP/2 wire format. HTTP/2 frames specific to a stream - * implement the {@link Http2StreamFrame} interface. The {@link Http2FrameCodec} is instantiated using the - * {@link Http2FrameCodecBuilder}. It's recommended for channel handlers to inherit from the - * {@link Http2ChannelDuplexHandler}, as it provides additional functionality like iterating over all active streams or - * creating outbound streams. - * - *

Stream Lifecycle

- *

- * The frame codec delivers and writes frames for active streams. An active stream is closed when either side sends a - * {@code RST_STREAM} frame or both sides send a frame with the {@code END_STREAM} flag set. Each - * {@link Http2StreamFrame} has a {@link Http2FrameStream} object attached that uniquely identifies a particular stream. - * - *

{@link Http2StreamFrame}s read from the channel always a {@link Http2FrameStream} object set, while when writing a - * {@link Http2StreamFrame} the application code needs to set a {@link Http2FrameStream} object using - * {@link Http2StreamFrame#stream(Http2FrameStream)}. - * - *

Flow control

- *

- * The frame codec automatically increments stream and connection flow control windows. - * - *

Incoming flow controlled frames need to be consumed by writing a {@link Http2WindowUpdateFrame} with the consumed - * number of bytes and the corresponding stream identifier set to the frame codec. - * - *

The local stream-level flow control window can be changed by writing a {@link Http2SettingsFrame} with the - * {@link Http2Settings#initialWindowSize()} set to the targeted value. - * - *

The connection-level flow control window can be changed by writing a {@link Http2WindowUpdateFrame} with the - * desired window size increment in bytes and the stream identifier set to {@code 0}. By default the initial - * connection-level flow control window is the same as initial stream-level flow control window. - * - *

New inbound Streams

- *

- * The first frame of an HTTP/2 stream must be an {@link Http2HeadersFrame}, which will have an {@link Http2FrameStream} - * object attached. - * - *

New outbound Streams

- *

- * A outbound HTTP/2 stream can be created by first instantiating a new {@link Http2FrameStream} object via - * {@link Http2ChannelDuplexHandler#newStream()}, and then writing a {@link Http2HeadersFrame} object with the stream - * attached. - * - *

{@code
- *     final Http2Stream2 stream = handler.newStream();
- *     ctx.write(headersFrame.stream(stream)).addListener(new FutureListener() {
- *
- *         @Override
- *         public void operationComplete(Future f) {
- *             if (f.isSuccess()) {
- *                 // Stream is active and stream.id() returns a valid stream identifier.
- *                 System.out.println("New stream with id " + stream.id() + " created.");
- *             } else {
- *                 // Stream failed to become active. Handle error.
- *                 if (f.cause() instanceof Http2NoMoreStreamIdsException) {
- *
- *                 } else if (f.cause() instanceof Http2GoAwayException) {
- *
- *                 } else {
- *
- *                 }
- *             }
- *         }
- *     }
- * }
- * - *

If a new stream cannot be created due to stream id exhaustion of the endpoint, the {@link Promise} of the - * HEADERS frame will fail with a {@link Http2NoMoreStreamIdsException}. - * - *

The HTTP/2 standard allows for an endpoint to limit the maximum number of concurrently active streams via the - * {@code SETTINGS_MAX_CONCURRENT_STREAMS} setting. When this limit is reached, no new streams can be created. However, - * the {@link Http2FrameCodec} can be build with - * {@link Http2FrameCodecBuilder#encoderEnforceMaxConcurrentStreams(boolean)} enabled, in which case a new stream and - * its associated frames will be buffered until either the limit is increased or an active stream is closed. It's, - * however, possible that a buffered stream will never become active. That is, the channel might - * get closed or a GO_AWAY frame might be received. In the first case, all writes of buffered streams will fail with a - * {@link Http2ChannelClosedException}. In the second case, all writes of buffered streams with an identifier less than - * the last stream identifier of the GO_AWAY frame will fail with a {@link Http2GoAwayException}. - * - *

Error Handling

- *

- * Exceptions and errors are propagated via {@link ChannelHandler#exceptionCaught}. Exceptions that apply to - * a specific HTTP/2 stream are wrapped in a {@link Http2FrameStreamException} and have the corresponding - * {@link Http2FrameStream} object attached. - * - *

Reference Counting

- *

- * Some {@link Http2StreamFrame}s implement the {@link ReferenceCounted} interface, as they carry - * reference counted objects (e.g. {@link ByteBuf}s). The frame codec will call {@link ReferenceCounted#retain()} before - * propagating a reference counted object through the pipeline, and thus an application handler needs to release such - * an object after having consumed it. For more information on reference counting take a look at - * https://netty.io/wiki/reference-counted-objects.html - * - *

HTTP Upgrade

- *

- * Server-side HTTP to HTTP/2 upgrade is supported in conjunction with {@link Http2ServerUpgradeCodec}; the necessary - * HTTP-to-HTTP/2 conversion is performed automatically. - */ -@UnstableApi -public class Http2FrameCodec extends Http2ConnectionHandler { - - private static final InternalLogger LOG = InternalLoggerFactory.getInstance(Http2FrameCodec.class); - - protected final PropertyKey streamKey; - private final PropertyKey upgradeKey; - - private final Integer initialFlowControlWindowSize; - - ChannelHandlerContext ctx; - - /** - * Number of buffered streams if the {@link StreamBufferingEncoder} is used. - **/ - private int numBufferedStreams; - private final IntObjectMap frameStreamToInitializeMap = - new IntObjectHashMap<>(8); - - Http2FrameCodec(Http2ConnectionEncoder encoder, Http2ConnectionDecoder decoder, Http2Settings initialSettings, - boolean decoupleCloseAndGoAway) { - super(decoder, encoder, initialSettings, decoupleCloseAndGoAway); - - decoder.frameListener(new FrameListener()); - connection().addListener(new ConnectionListener()); - connection().remote().flowController().listener(new Http2RemoteFlowControllerListener()); - streamKey = connection().newKey(); - upgradeKey = connection().newKey(); - initialFlowControlWindowSize = initialSettings.initialWindowSize(); - } - - /** - * Creates a new outbound/local stream. - */ - DefaultHttp2FrameStream newStream() { - return new DefaultHttp2FrameStream(); - } - - /** - * Iterates over all active HTTP/2 streams. - * - *

This method must not be called outside of the event loop. - */ - final void forEachActiveStream(final Http2FrameStreamVisitor streamVisitor) throws Http2Exception { - assert ctx.executor().inEventLoop(); - - if (connection().numActiveStreams() > 0) { - connection().forEachActiveStream(stream -> { - try { - return streamVisitor.visit(stream.getProperty(streamKey)); - } catch (Throwable cause) { - onError(ctx, false, cause); - return false; - } - }); - } - } - - /** - * Retrieve the number of streams currently in the process of being initialized. - *

- * This is package-private for testing only. - */ - int numInitializingStreams() { - return frameStreamToInitializeMap.size(); - } - - @Override - public void handlerAdded0(ChannelHandlerContext ctx) throws Exception { - super.handlerAdded0(ctx); - this.ctx = ctx; - // Must be after Http2ConnectionHandler does its initialization in handlerAdded above. - // The server will not send a connection preface so we are good to send a window update. - Http2Connection connection = connection(); - if (connection.isServer()) { - tryExpandConnectionFlowControlWindow(connection); - } - } - - private void tryExpandConnectionFlowControlWindow(Http2Connection connection) throws Http2Exception { - if (initialFlowControlWindowSize != null) { - // The window size in the settings explicitly excludes the connection window. So we manually manipulate the - // connection window to accommodate more concurrent data per connection. - Http2Stream connectionStream = connection.connectionStream(); - Http2LocalFlowController localFlowController = connection.local().flowController(); - final int delta = initialFlowControlWindowSize - localFlowController.initialWindowSize(connectionStream); - // Only increase the connection window, don't decrease it. - if (delta > 0) { - // Double the delta just so a single stream can't exhaust the connection window. - localFlowController.incrementWindowSize(connectionStream, Math.max(delta << 1, delta)); - flush(ctx); - } - } - } - - /** - * Handles the cleartext HTTP upgrade event. If an upgrade occurred, sends a simple response via - * HTTP/2 on stream 1 (the stream specifically reserved for cleartext HTTP upgrade). - */ - @Override - public final void userEventTriggered(final ChannelHandlerContext ctx, final Object evt) throws Exception { - if (evt == Http2ConnectionPrefaceAndSettingsFrameWrittenEvent.INSTANCE) { - // The user event implies that we are on the client. - tryExpandConnectionFlowControlWindow(connection()); - - // We schedule this on the EventExecutor to allow to have any extra handlers added to the pipeline - // before we pass the event to the next handler. This is needed as the event may be called from within - // handlerAdded(...) which will be run before other handlers will be added to the pipeline. - ctx.executor().execute(() -> ctx.fireUserEventTriggered(evt)); - } else if (evt instanceof UpgradeEvent) { - UpgradeEvent upgrade = (UpgradeEvent) evt; - try { - onUpgradeEvent(ctx, upgrade.retain()); - Http2Stream stream = connection().stream(HTTP_UPGRADE_STREAM_ID); - if (stream.getProperty(streamKey) == null) { - // TODO: improve handler/stream lifecycle so that stream isn't active before handler added. - // The stream was already made active, but ctx may have been null so it wasn't initialized. - // https://github.com/netty/netty/issues/4942 - onStreamActive0(stream); - } - upgrade.upgradeRequest().headers().setInt( - HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), HTTP_UPGRADE_STREAM_ID); - stream.setProperty(upgradeKey, true); - InboundHttpToHttp2Adapter.handle( - ctx, connection(), decoder().frameListener(), upgrade.upgradeRequest().retain()); - } finally { - upgrade.release(); - } - } else { - ctx.fireUserEventTriggered(evt); - } - } - - /** - * Processes all {@link Http2Frame}s. {@link Http2StreamFrame}s may only originate in child - * streams. - */ - @Override - public Future write(ChannelHandlerContext ctx, Object msg) { - if (msg instanceof Http2DataFrame) { - Http2DataFrame dataFrame = (Http2DataFrame) msg; - return encoder().writeData(ctx, dataFrame.stream().id(), dataFrame.content(), - dataFrame.padding(), dataFrame.isEndStream()); - } else if (msg instanceof Http2HeadersFrame) { - return writeHeadersFrame(ctx, (Http2HeadersFrame) msg); - } else if (msg instanceof Http2WindowUpdateFrame) { - Http2WindowUpdateFrame frame = (Http2WindowUpdateFrame) msg; - Http2FrameStream frameStream = frame.stream(); - // It is legit to send a WINDOW_UPDATE frame for the connection stream. The parent channel doesn't attempt - // to set the Http2FrameStream so we assume if it is null the WINDOW_UPDATE is for the connection stream. - try { - if (frameStream == null) { - increaseInitialConnectionWindow(frame.windowSizeIncrement()); - } else { - consumeBytes(frameStream.id(), frame.windowSizeIncrement()); - } - return ctx.newSucceededFuture(); - } catch (Throwable t) { - return ctx.newFailedFuture(t); - } - } else if (msg instanceof Http2ResetFrame) { - Http2ResetFrame rstFrame = (Http2ResetFrame) msg; - int id = rstFrame.stream().id(); - // Only ever send a reset frame if stream may have existed before as otherwise we may send a RST on a - // stream in an invalid state and cause a connection error. - if (connection().streamMayHaveExisted(id)) { - return encoder().writeRstStream(ctx, rstFrame.stream().id(), rstFrame.errorCode()); - } else { - ReferenceCountUtil.release(rstFrame); - return ctx.newFailedFuture(Http2Exception.streamError( - rstFrame.stream().id(), Http2Error.PROTOCOL_ERROR, "Stream never existed")); - } - } else if (msg instanceof Http2PingFrame) { - Http2PingFrame frame = (Http2PingFrame) msg; - return encoder().writePing(ctx, frame.ack(), frame.content()); - } else if (msg instanceof Http2SettingsFrame) { - return encoder().writeSettings(ctx, ((Http2SettingsFrame) msg).settings()); - } else if (msg instanceof Http2SettingsAckFrame) { - // In the event of manual SETTINGS ACK, it is assumed the encoder will apply the earliest received but not - // yet ACKed settings. - return encoder().writeSettingsAck(ctx); - } else if (msg instanceof Http2GoAwayFrame) { - return writeGoAwayFrame(ctx, (Http2GoAwayFrame) msg); - } else if (msg instanceof Http2PushPromiseFrame) { - Http2PushPromiseFrame pushPromiseFrame = (Http2PushPromiseFrame) msg; - return writePushPromise(ctx, pushPromiseFrame); - } else if (msg instanceof Http2PriorityFrame) { - Http2PriorityFrame priorityFrame = (Http2PriorityFrame) msg; - return encoder().writePriority(ctx, priorityFrame.stream().id(), priorityFrame.streamDependency(), - priorityFrame.weight(), priorityFrame.exclusive()); - } else if (msg instanceof Http2UnknownFrame) { - Http2UnknownFrame unknownFrame = (Http2UnknownFrame) msg; - return encoder().writeFrame(ctx, unknownFrame.frameType(), unknownFrame.stream().id(), - unknownFrame.flags(), unknownFrame.content()); - } else if (!(msg instanceof Http2Frame)) { - return ctx.write(msg); - } else { - ReferenceCountUtil.release(msg); - return ctx.newFailedFuture(new UnsupportedMessageTypeException(msg)); - } - } - - private void increaseInitialConnectionWindow(int deltaBytes) throws Http2Exception { - // The LocalFlowController is responsible for detecting over/under flow. - connection().local().flowController().incrementWindowSize(connection().connectionStream(), deltaBytes); - } - - final boolean consumeBytes(int streamId, int bytes) throws Http2Exception { - Http2Stream stream = connection().stream(streamId); - // Upgraded requests are ineligible for stream control. We add the null check - // in case the stream has been deregistered. - if (stream != null && streamId == HTTP_UPGRADE_STREAM_ID) { - Boolean upgraded = stream.getProperty(upgradeKey); - if (Boolean.TRUE.equals(upgraded)) { - return false; - } - } - - return connection().local().flowController().consumeBytes(stream, bytes); - } - - private Future writeGoAwayFrame(ChannelHandlerContext ctx, Http2GoAwayFrame frame) { - if (frame.lastStreamId() > -1) { - frame.release(); - return ctx.newFailedFuture(new IllegalArgumentException("Last stream id must not be set on GOAWAY frame")); - } - - int lastStreamCreated = connection().remote().lastStreamCreated(); - long lastStreamId = lastStreamCreated + ((long) frame.extraStreamIds()) * 2; - // Check if the computation overflowed. - if (lastStreamId > Integer.MAX_VALUE) { - lastStreamId = Integer.MAX_VALUE; - } - return goAway(ctx, (int) lastStreamId, frame.errorCode(), frame.content()); - } - - private Future writeHeadersFrame(final ChannelHandlerContext ctx, Http2HeadersFrame headersFrame) { - - if (isStreamIdValid(headersFrame.stream().id())) { - return encoder().writeHeaders(ctx, headersFrame.stream().id(), headersFrame.headers(), - headersFrame.padding(), headersFrame.isEndStream()); - } else { - Future future = initializeNewStream(ctx, (DefaultHttp2FrameStream) headersFrame.stream()); - if (future == null) { - final int streamId = headersFrame.stream().id(); - - future = encoder().writeHeaders(ctx, streamId, headersFrame.headers(), headersFrame.padding(), - headersFrame.isEndStream()); - - if (!future.isDone()) { - numBufferedStreams++; - // Clean up the stream being initialized if writing the headers fails and also - // decrement the number of buffered streams. - future.addListener(channelFuture -> { - numBufferedStreams--; - - handleHeaderFuture(channelFuture, streamId); - }); - } else { - handleHeaderFuture(future, streamId); - } - } - return future; - } - } - - private Future writePushPromise(final ChannelHandlerContext ctx, Http2PushPromiseFrame pushPromiseFrame) { - if (isStreamIdValid(pushPromiseFrame.pushStream().id())) { - return encoder().writePushPromise(ctx, pushPromiseFrame.stream().id(), pushPromiseFrame.pushStream().id(), - pushPromiseFrame.http2Headers(), pushPromiseFrame.padding()); - } else { - Future future = initializeNewStream(ctx, (DefaultHttp2FrameStream) pushPromiseFrame.pushStream()); - if (future == null) { - final int streamId = pushPromiseFrame.stream().id(); - future = encoder().writePushPromise(ctx, streamId, pushPromiseFrame.pushStream().id(), - pushPromiseFrame.http2Headers(), pushPromiseFrame.padding()); - - if (future.isDone()) { - handleHeaderFuture(future, streamId); - } else { - numBufferedStreams++; - // Clean up the stream being initialized if writing the headers fails and also - // decrement the number of buffered streams. - future.addListener(f -> { - numBufferedStreams--; - handleHeaderFuture(f, streamId); - }); - } - return future; - } - return future; - } - } - - private Future initializeNewStream(ChannelHandlerContext ctx, DefaultHttp2FrameStream http2FrameStream) { - final Http2Connection connection = connection(); - final int streamId = connection.local().incrementAndGetNextStreamId(); - if (streamId < 0) { - Future f = ctx.newFailedFuture(new Http2NoMoreStreamIdsException()); - - // Simulate a GOAWAY being received due to stream exhaustion on this connection. We use the maximum - // valid stream ID for the current peer. - onHttp2Frame(ctx, new DefaultHttp2GoAwayFrame(connection.isServer() ? Integer.MAX_VALUE : - Integer.MAX_VALUE - 1, NO_ERROR.code(), - writeAscii(ctx.alloc(), "Stream IDs exhausted on local stream creation"))); - - return f; - } - http2FrameStream.id = streamId; - - // 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, http2FrameStream); - - // We should not re-use ids. - assert old == null; - return null; - } - - private void handleHeaderFuture(Future channelFuture, int streamId) { - if (channelFuture.isFailed()) { - frameStreamToInitializeMap.remove(streamId); - } - } - - private void onStreamActive0(Http2Stream stream) { - if (stream.id() != HTTP_UPGRADE_STREAM_ID && - connection().local().isValidStreamId(stream.id())) { - return; - } - - DefaultHttp2FrameStream stream2 = newStream().setStreamAndProperty(streamKey, stream); - onHttp2StreamStateChanged(ctx, stream2); - } - - private final class ConnectionListener extends Http2ConnectionAdapter { - @Override - public void onStreamAdded(Http2Stream stream) { - DefaultHttp2FrameStream frameStream = frameStreamToInitializeMap.remove(stream.id()); - - if (frameStream != null) { - frameStream.setStreamAndProperty(streamKey, stream); - } - } - - @Override - public void onStreamActive(Http2Stream stream) { - onStreamActive0(stream); - } - - @Override - public void onStreamClosed(Http2Stream stream) { - onHttp2StreamStateChanged0(stream); - } - - @Override - public void onStreamHalfClosed(Http2Stream stream) { - onHttp2StreamStateChanged0(stream); - } - - private void onHttp2StreamStateChanged0(Http2Stream stream) { - DefaultHttp2FrameStream stream2 = stream.getProperty(streamKey); - if (stream2 != null) { - onHttp2StreamStateChanged(ctx, stream2); - } - } - } - - @Override - protected void onConnectionError( - ChannelHandlerContext ctx, boolean outbound, Throwable cause, Http2Exception http2Ex) { - if (!outbound) { - // allow the user to handle it first in the pipeline, and then automatically clean up. - // If this is not desired behavior the user can override this method. - // - // We only forward non outbound errors as outbound errors will already be reflected by failing the promise. - ctx.fireExceptionCaught(cause); - } - super.onConnectionError(ctx, outbound, cause, http2Ex); - } - - /** - * Exceptions for unknown streams, that is streams that have no {@link Http2FrameStream} object attached - * are simply logged and replied to by sending a RST_STREAM frame. - */ - @Override - protected final void onStreamError(ChannelHandlerContext ctx, boolean outbound, Throwable cause, - Http2Exception.StreamException streamException) { - int streamId = streamException.streamId(); - Http2Stream connectionStream = connection().stream(streamId); - if (connectionStream == null) { - onHttp2UnknownStreamError(ctx, cause, streamException); - // Write a RST_STREAM - super.onStreamError(ctx, outbound, cause, streamException); - return; - } - - Http2FrameStream stream = connectionStream.getProperty(streamKey); - if (stream == null) { - LOG.warn("Stream exception thrown without stream object attached.", cause); - // Write a RST_STREAM - super.onStreamError(ctx, outbound, cause, streamException); - return; - } - - if (!outbound) { - // We only forward non outbound errors as outbound errors will already be reflected by failing the promise. - onHttp2FrameStreamException(ctx, new Http2FrameStreamException(stream, streamException.error(), cause)); - } - } - - private static void onHttp2UnknownStreamError(@SuppressWarnings("unused") ChannelHandlerContext ctx, - Throwable cause, Http2Exception.StreamException streamException) { - // We log here for debugging purposes. This exception will be propagated to the upper layers through other ways: - // - fireExceptionCaught - // - fireUserEventTriggered(Http2ResetFrame), see Http2MultiplexHandler#channelRead(...) - // - by failing write promise - // Receiver of the error is responsible for correct handling of this exception. - LOG.log(DEBUG, "Stream exception thrown for unknown stream {}.", streamException.streamId(), cause); - } - - @Override - protected final boolean isGracefulShutdownComplete() { - return super.isGracefulShutdownComplete() && numBufferedStreams == 0; - } - - private final class FrameListener implements Http2FrameListener { - - @Override - public void onUnknownFrame( - ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags, ByteBuf payload) { - if (streamId == 0) { - // Ignore unknown frames on connection stream, for example: HTTP/2 GREASE testing - return; - } - onHttp2Frame(ctx, new DefaultHttp2UnknownFrame(frameType, flags, payload) - .stream(requireStream(streamId)).retain()); - } - - @Override - public void onSettingsRead(ChannelHandlerContext ctx, Http2Settings settings) { - onHttp2Frame(ctx, new DefaultHttp2SettingsFrame(settings)); - } - - @Override - public void onPingRead(ChannelHandlerContext ctx, long data) { - onHttp2Frame(ctx, new DefaultHttp2PingFrame(data, false)); - } - - @Override - public void onPingAckRead(ChannelHandlerContext ctx, long data) { - onHttp2Frame(ctx, new DefaultHttp2PingFrame(data, true)); - } - - @Override - public void onRstStreamRead(ChannelHandlerContext ctx, int streamId, long errorCode) { - onHttp2Frame(ctx, new DefaultHttp2ResetFrame(errorCode).stream(requireStream(streamId))); - } - - @Override - public void onWindowUpdateRead(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement) { - if (streamId == 0) { - // Ignore connection window updates. - return; - } - onHttp2Frame(ctx, new DefaultHttp2WindowUpdateFrame(windowSizeIncrement).stream(requireStream(streamId))); - } - - @Override - public void onHeadersRead(ChannelHandlerContext ctx, int streamId, - Http2Headers headers, int streamDependency, short weight, boolean - exclusive, int padding, boolean endStream) { - onHeadersRead(ctx, streamId, headers, padding, endStream); - } - - @Override - public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, - int padding, boolean endOfStream) { - onHttp2Frame(ctx, new DefaultHttp2HeadersFrame(headers, endOfStream, padding) - .stream(requireStream(streamId))); - } - - @Override - public int onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding, - boolean endOfStream) { - onHttp2Frame(ctx, new DefaultHttp2DataFrame(data, endOfStream, padding) - .stream(requireStream(streamId)).retain()); - // We return the bytes in consumeBytes() once the stream channel consumed the bytes. - return 0; - } - - @Override - public void onGoAwayRead(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData) { - onHttp2Frame(ctx, new DefaultHttp2GoAwayFrame(lastStreamId, errorCode, debugData).retain()); - } - - @Override - public void onPriorityRead(ChannelHandlerContext ctx, int streamId, int streamDependency, - short weight, boolean exclusive) { - - Http2Stream stream = connection().stream(streamId); - if (stream == null) { - // The stream was not opened yet, let's just ignore this for now. - return; - } - onHttp2Frame(ctx, new DefaultHttp2PriorityFrame(streamDependency, weight, exclusive) - .stream(requireStream(streamId))); - } - - @Override - public void onSettingsAckRead(ChannelHandlerContext ctx) { - onHttp2Frame(ctx, Http2SettingsAckFrame.INSTANCE); - } - - @Override - public void onPushPromiseRead(ChannelHandlerContext ctx, int streamId, int promisedStreamId, - Http2Headers headers, int padding) { - onHttp2Frame(ctx, new DefaultHttp2PushPromiseFrame(headers, padding, promisedStreamId) - .pushStream(new DefaultHttp2FrameStream() - .setStreamAndProperty(streamKey, connection().stream(promisedStreamId))) - .stream(requireStream(streamId))); - } - - private Http2FrameStream requireStream(int streamId) { - Http2FrameStream stream = connection().stream(streamId).getProperty(streamKey); - if (stream == null) { - throw new IllegalStateException("Stream object required for identifier: " + streamId); - } - return stream; - } - } - - private void onUpgradeEvent(ChannelHandlerContext ctx, UpgradeEvent evt) { - ctx.fireUserEventTriggered(evt); - } - - private void onHttp2StreamWritabilityChanged(ChannelHandlerContext ctx, DefaultHttp2FrameStream stream, - @SuppressWarnings("unused") boolean writable) { - ctx.fireUserEventTriggered(stream.writabilityChanged); - } - - void onHttp2StreamStateChanged(ChannelHandlerContext ctx, DefaultHttp2FrameStream stream) { - ctx.fireUserEventTriggered(stream.stateChanged); - } - - void onHttp2Frame(ChannelHandlerContext ctx, Http2Frame frame) { - ctx.fireChannelRead(frame); - } - - void onHttp2FrameStreamException(ChannelHandlerContext ctx, Http2FrameStreamException cause) { - ctx.fireExceptionCaught(cause); - } - - private final class Http2RemoteFlowControllerListener implements Http2RemoteFlowController.Listener { - @Override - public void writabilityChanged(Http2Stream stream) { - DefaultHttp2FrameStream frameStream = stream.getProperty(streamKey); - if (frameStream == null) { - return; - } - onHttp2StreamWritabilityChanged( - ctx, frameStream, connection().remote().flowController().isWritable(stream)); - } - } - - /** - * {@link Http2FrameStream} implementation. - */ - // TODO(buchgr): Merge Http2FrameStream and Http2Stream. - static class DefaultHttp2FrameStream implements Http2FrameStream { - - private volatile int id = -1; - private volatile Http2Stream stream; - - final Http2FrameStreamEvent stateChanged = Http2FrameStreamEvent.stateChanged(this); - final Http2FrameStreamEvent writabilityChanged = Http2FrameStreamEvent.writabilityChanged(this); - - Channel attachment; - - DefaultHttp2FrameStream setStreamAndProperty(PropertyKey streamKey, Http2Stream stream) { - assert id == -1 || stream.id() == id; - this.stream = stream; - stream.setProperty(streamKey, this); - return this; - } - - @Override - public int id() { - Http2Stream stream = this.stream; - return stream == null ? id : stream.id(); - } - - @Override - public State state() { - Http2Stream stream = this.stream; - return stream == null ? State.IDLE : stream.state(); - } - - @Override - public String toString() { - return String.valueOf(id()); - } - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameCodecBuilder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameCodecBuilder.java deleted file mode 100644 index 418e252659..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameCodecBuilder.java +++ /dev/null @@ -1,233 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.util.internal.UnstableApi; - -import static java.util.Objects.requireNonNull; - -/** - * Builder for the {@link Http2FrameCodec}. - */ -@UnstableApi -public class Http2FrameCodecBuilder extends - AbstractHttp2ConnectionHandlerBuilder { - - private Http2FrameWriter frameWriter; - - /** - * Allows overriding behavior of existing builder. - *

- * Users of this constructor are responsible for invoking {@link #server(boolean)} method or overriding - * {@link #isServer()} method to give the builder information if the {@link Http2Connection}(s) it creates are in - * server or client mode. - * - * @see AbstractHttp2ConnectionHandlerBuilder - */ - protected Http2FrameCodecBuilder() { - } - - Http2FrameCodecBuilder(boolean server) { - server(server); - // For backwards compatibility we should disable to timeout by default at this layer. - gracefulShutdownTimeoutMillis(0); - } - - /** - * Creates a builder for an HTTP/2 client. - */ - public static Http2FrameCodecBuilder forClient() { - return new Http2FrameCodecBuilder(false); - } - - /** - * Creates a builder for an HTTP/2 server. - */ - public static Http2FrameCodecBuilder forServer() { - return new Http2FrameCodecBuilder(true); - } - - // For testing only. - Http2FrameCodecBuilder frameWriter(Http2FrameWriter frameWriter) { - this.frameWriter = requireNonNull(frameWriter, "frameWriter"); - return this; - } - - @Override - public Http2Settings initialSettings() { - return super.initialSettings(); - } - - @Override - public Http2FrameCodecBuilder initialSettings(Http2Settings settings) { - return super.initialSettings(settings); - } - - @Override - public long gracefulShutdownTimeoutMillis() { - return super.gracefulShutdownTimeoutMillis(); - } - - @Override - public Http2FrameCodecBuilder gracefulShutdownTimeoutMillis(long gracefulShutdownTimeoutMillis) { - return super.gracefulShutdownTimeoutMillis(gracefulShutdownTimeoutMillis); - } - - @Override - public boolean isServer() { - return super.isServer(); - } - - @Override - public int maxReservedStreams() { - return super.maxReservedStreams(); - } - - @Override - public Http2FrameCodecBuilder maxReservedStreams(int maxReservedStreams) { - return super.maxReservedStreams(maxReservedStreams); - } - - @Override - public boolean isValidateHeaders() { - return super.isValidateHeaders(); - } - - @Override - public Http2FrameCodecBuilder validateHeaders(boolean validateHeaders) { - return super.validateHeaders(validateHeaders); - } - - @Override - public Http2FrameLogger frameLogger() { - return super.frameLogger(); - } - - @Override - public Http2FrameCodecBuilder frameLogger(Http2FrameLogger frameLogger) { - return super.frameLogger(frameLogger); - } - - @Override - public boolean encoderEnforceMaxConcurrentStreams() { - return super.encoderEnforceMaxConcurrentStreams(); - } - - @Override - public Http2FrameCodecBuilder encoderEnforceMaxConcurrentStreams(boolean encoderEnforceMaxConcurrentStreams) { - return super.encoderEnforceMaxConcurrentStreams(encoderEnforceMaxConcurrentStreams); - } - - @Override - public int encoderEnforceMaxQueuedControlFrames() { - return super.encoderEnforceMaxQueuedControlFrames(); - } - - @Override - public Http2FrameCodecBuilder encoderEnforceMaxQueuedControlFrames(int maxQueuedControlFrames) { - return super.encoderEnforceMaxQueuedControlFrames(maxQueuedControlFrames); - } - - @Override - public Http2HeadersEncoder.SensitivityDetector headerSensitivityDetector() { - return super.headerSensitivityDetector(); - } - - @Override - public Http2FrameCodecBuilder headerSensitivityDetector( - Http2HeadersEncoder.SensitivityDetector headerSensitivityDetector) { - return super.headerSensitivityDetector(headerSensitivityDetector); - } - - @Override - public Http2FrameCodecBuilder encoderIgnoreMaxHeaderListSize(boolean ignoreMaxHeaderListSize) { - return super.encoderIgnoreMaxHeaderListSize(ignoreMaxHeaderListSize); - } - - @Override - @Deprecated - public Http2FrameCodecBuilder initialHuffmanDecodeCapacity(int initialHuffmanDecodeCapacity) { - return super.initialHuffmanDecodeCapacity(initialHuffmanDecodeCapacity); - } - - @Override - public Http2FrameCodecBuilder autoAckSettingsFrame(boolean autoAckSettings) { - return super.autoAckSettingsFrame(autoAckSettings); - } - - @Override - public Http2FrameCodecBuilder autoAckPingFrame(boolean autoAckPingFrame) { - return super.autoAckPingFrame(autoAckPingFrame); - } - - @Override - public Http2FrameCodecBuilder decoupleCloseAndGoAway(boolean decoupleCloseAndGoAway) { - return super.decoupleCloseAndGoAway(decoupleCloseAndGoAway); - } - - @Override - public int decoderEnforceMaxConsecutiveEmptyDataFrames() { - return super.decoderEnforceMaxConsecutiveEmptyDataFrames(); - } - - @Override - public Http2FrameCodecBuilder decoderEnforceMaxConsecutiveEmptyDataFrames(int maxConsecutiveEmptyFrames) { - return super.decoderEnforceMaxConsecutiveEmptyDataFrames(maxConsecutiveEmptyFrames); - } - - /** - * Build a {@link Http2FrameCodec} object. - */ - @Override - public Http2FrameCodec 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(isValidateHeaders()) : - new DefaultHttp2HeadersDecoder(isValidateHeaders(), 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, - promisedRequestVerifier(), isAutoAckSettingsFrame(), isAutoAckPingFrame()); - int maxConsecutiveEmptyDataFrames = decoderEnforceMaxConsecutiveEmptyDataFrames(); - if (maxConsecutiveEmptyDataFrames > 0) { - decoder = new Http2EmptyDataFrameConnectionDecoder(decoder, maxConsecutiveEmptyDataFrames); - } - return build(decoder, encoder, initialSettings()); - } - return super.build(); - } - - @Override - protected Http2FrameCodec build( - Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder, Http2Settings initialSettings) { - Http2FrameCodec codec = new Http2FrameCodec(encoder, decoder, initialSettings, decoupleCloseAndGoAway()); - codec.gracefulShutdownTimeoutMillis(gracefulShutdownTimeoutMillis()); - return codec; - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameListener.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameListener.java deleted file mode 100644 index eb75307612..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameListener.java +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.internal.UnstableApi; - -/** - * An listener of HTTP/2 frames. - */ -@UnstableApi -public interface Http2FrameListener { - /** - * Handles an inbound {@code DATA} frame. - * - * @param ctx the context from the handler where the frame was read. - * @param streamId the subject stream for the frame. - * @param data payload buffer for the frame. This buffer will be released by the codec. - * @param padding additional bytes that should be added to obscure the true content size. Must be between 0 and - * 256 (inclusive). - * @param endOfStream Indicates whether this is the last frame to be sent from the remote endpoint for this stream. - * @return the number of bytes that have been processed by the application. The returned bytes are used by the - * inbound flow controller to determine the appropriate time to expand the inbound flow control window (i.e. send - * {@code WINDOW_UPDATE}). Returning a value equal to the length of {@code data} + {@code padding} will effectively - * opt-out of application-level flow control for this frame. Returning a value less than the length of {@code data} - * + {@code padding} will defer the returning of the processed bytes, which the application must later return via - * {@link Http2LocalFlowController#consumeBytes(Http2Stream, int)}. The returned value must - * be >= {@code 0} and <= {@code data.readableBytes()} + {@code padding}. - */ - int onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding, - boolean endOfStream) throws Http2Exception; - - /** - * Handles an inbound {@code HEADERS} frame. - *

- * Only one of the following methods will be called for each {@code HEADERS} frame sequence. - * One will be called when the {@code END_HEADERS} flag has been received. - *

    - *
  • {@link #onHeadersRead(ChannelHandlerContext, int, Http2Headers, int, boolean)}
  • - *
  • {@link #onHeadersRead(ChannelHandlerContext, int, Http2Headers, int, short, boolean, int, boolean)}
  • - *
  • {@link #onPushPromiseRead(ChannelHandlerContext, int, int, Http2Headers, int)}
  • - *
- *

- * To say it another way; the {@link Http2Headers} will contain all of the headers - * for the current message exchange step (additional queuing is not necessary). - * - * @param ctx the context from the handler where the frame was read. - * @param streamId the subject stream for the frame. - * @param headers the received headers. - * @param padding additional bytes that should be added to obscure the true content size. Must be between 0 and - * 256 (inclusive). - * @param endOfStream Indicates whether this is the last frame to be sent from the remote endpoint - * for this stream. - */ - void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding, - boolean endOfStream) throws Http2Exception; - - /** - * Handles an inbound {@code HEADERS} frame with priority information specified. - * Only called if {@code END_HEADERS} encountered. - *

- * Only one of the following methods will be called for each {@code HEADERS} frame sequence. - * One will be called when the {@code END_HEADERS} flag has been received. - *

    - *
  • {@link #onHeadersRead(ChannelHandlerContext, int, Http2Headers, int, boolean)}
  • - *
  • {@link #onHeadersRead(ChannelHandlerContext, int, Http2Headers, int, short, boolean, int, boolean)}
  • - *
  • {@link #onPushPromiseRead(ChannelHandlerContext, int, int, Http2Headers, int)}
  • - *
- *

- * To say it another way; the {@link Http2Headers} will contain all of the headers - * for the current message exchange step (additional queuing is not necessary). - * - * @param ctx the context from the handler where the frame was read. - * @param streamId the subject stream for the frame. - * @param headers the received headers. - * @param streamDependency the stream on which this stream depends, or 0 if dependent on the - * connection. - * @param weight the new weight for the stream. - * @param exclusive whether or not the stream should be the exclusive dependent of its parent. - * @param padding additional bytes that should be added to obscure the true content size. Must be between 0 and - * 256 (inclusive). - * @param endOfStream Indicates whether this is the last frame to be sent from the remote endpoint - * for this stream. - */ - void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, - int streamDependency, short weight, boolean exclusive, int padding, boolean endOfStream) - throws Http2Exception; - - /** - * Handles an inbound {@code PRIORITY} frame. - *

- * Note that is it possible to have this method called and no stream object exist for either - * {@code streamId}, {@code streamDependency}, or both. This is because the {@code PRIORITY} frame can be - * sent/received when streams are in the {@code CLOSED} state. - * - * @param ctx the context from the handler where the frame was read. - * @param streamId the subject stream for the frame. - * @param streamDependency the stream on which this stream depends, or 0 if dependent on the - * connection. - * @param weight the new weight for the stream. - * @param exclusive whether or not the stream should be the exclusive dependent of its parent. - */ - void onPriorityRead(ChannelHandlerContext ctx, int streamId, int streamDependency, - short weight, boolean exclusive) throws Http2Exception; - - /** - * Handles an inbound {@code RST_STREAM} frame. - * - * @param ctx the context from the handler where the frame was read. - * @param streamId the stream that is terminating. - * @param errorCode the error code identifying the type of failure. - */ - void onRstStreamRead(ChannelHandlerContext ctx, int streamId, long errorCode) throws Http2Exception; - - /** - * Handles an inbound {@code SETTINGS} acknowledgment frame. - * @param ctx the context from the handler where the frame was read. - */ - void onSettingsAckRead(ChannelHandlerContext ctx) throws Http2Exception; - - /** - * Handles an inbound {@code SETTINGS} frame. - * - * @param ctx the context from the handler where the frame was read. - * @param settings the settings received from the remote endpoint. - */ - void onSettingsRead(ChannelHandlerContext ctx, Http2Settings settings) throws Http2Exception; - - /** - * Handles an inbound {@code PING} frame. - * - * @param ctx the context from the handler where the frame was read. - * @param data the payload of the frame. - */ - void onPingRead(ChannelHandlerContext ctx, long data) throws Http2Exception; - - /** - * Handles an inbound {@code PING} acknowledgment. - * - * @param ctx the context from the handler where the frame was read. - * @param data the payload of the frame. - */ - void onPingAckRead(ChannelHandlerContext ctx, long data) throws Http2Exception; - - /** - * Handles an inbound {@code PUSH_PROMISE} frame. Only called if {@code END_HEADERS} encountered. - *

- * Promised requests MUST be authoritative, cacheable, and safe. - * See [RFC 7540], Section 8.2. - *

- * Only one of the following methods will be called for each {@code HEADERS} frame sequence. - * One will be called when the {@code END_HEADERS} flag has been received. - *

    - *
  • {@link #onHeadersRead(ChannelHandlerContext, int, Http2Headers, int, boolean)}
  • - *
  • {@link #onHeadersRead(ChannelHandlerContext, int, Http2Headers, int, short, boolean, int, boolean)}
  • - *
  • {@link #onPushPromiseRead(ChannelHandlerContext, int, int, Http2Headers, int)}
  • - *
- *

- * To say it another way; the {@link Http2Headers} will contain all of the headers - * for the current message exchange step (additional queuing is not necessary). - * - * @param ctx the context from the handler where the frame was read. - * @param streamId the stream the frame was sent on. - * @param promisedStreamId the ID of the promised stream. - * @param headers the received headers. - * @param padding additional bytes that should be added to obscure the true content size. Must be between 0 and - * 256 (inclusive). - */ - void onPushPromiseRead(ChannelHandlerContext ctx, int streamId, int promisedStreamId, - Http2Headers headers, int padding) throws Http2Exception; - - /** - * Handles an inbound {@code GO_AWAY} frame. - * - * @param ctx the context from the handler where the frame was read. - * @param lastStreamId the last known stream of the remote endpoint. - * @param errorCode the error code, if abnormal closure. - * @param debugData application-defined debug data. If this buffer needs to be retained by the - * listener they must make a copy. - */ - void onGoAwayRead(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData) - throws Http2Exception; - - /** - * Handles an inbound {@code WINDOW_UPDATE} frame. - * - * @param ctx the context from the handler where the frame was read. - * @param streamId the stream the frame was sent on. - * @param windowSizeIncrement the increased number of bytes of the remote endpoint's flow - * control window. - */ - void onWindowUpdateRead(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement) - throws Http2Exception; - - /** - * Handler for a frame not defined by the HTTP/2 spec. - * - * @param ctx the context from the handler where the frame was read. - * @param frameType the frame type from the HTTP/2 header. - * @param streamId the stream the frame was sent on. - * @param flags the flags in the frame header. - * @param payload the payload of the frame. - */ - void onUnknownFrame(ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags, ByteBuf payload) - throws Http2Exception; -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameListenerDecorator.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameListenerDecorator.java deleted file mode 100644 index 3726363713..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameListenerDecorator.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.internal.UnstableApi; - -/** - * Provides a decorator around a {@link Http2FrameListener} and delegates all method calls - */ -@UnstableApi -public class Http2FrameListenerDecorator implements Http2FrameListener { - protected final Http2FrameListener listener; - - public Http2FrameListenerDecorator(Http2FrameListener listener) { - this.listener = requireNonNull(listener, "listener"); - } - - @Override - public int onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding, boolean endOfStream) - throws Http2Exception { - return listener.onDataRead(ctx, streamId, data, padding, endOfStream); - } - - @Override - public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding, - boolean endStream) throws Http2Exception { - listener.onHeadersRead(ctx, streamId, headers, padding, endStream); - } - - @Override - public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int streamDependency, - short weight, boolean exclusive, int padding, boolean endStream) throws Http2Exception { - listener.onHeadersRead(ctx, streamId, headers, streamDependency, weight, exclusive, padding, endStream); - } - - @Override - public void onPriorityRead(ChannelHandlerContext ctx, int streamId, int streamDependency, short weight, - boolean exclusive) throws Http2Exception { - listener.onPriorityRead(ctx, streamId, streamDependency, weight, exclusive); - } - - @Override - public void onRstStreamRead(ChannelHandlerContext ctx, int streamId, long errorCode) throws Http2Exception { - listener.onRstStreamRead(ctx, streamId, errorCode); - } - - @Override - public void onSettingsAckRead(ChannelHandlerContext ctx) throws Http2Exception { - listener.onSettingsAckRead(ctx); - } - - @Override - public void onSettingsRead(ChannelHandlerContext ctx, Http2Settings settings) throws Http2Exception { - listener.onSettingsRead(ctx, settings); - } - - @Override - public void onPingRead(ChannelHandlerContext ctx, long data) throws Http2Exception { - listener.onPingRead(ctx, data); - } - - @Override - public void onPingAckRead(ChannelHandlerContext ctx, long data) throws Http2Exception { - listener.onPingAckRead(ctx, data); - } - - @Override - public void onPushPromiseRead(ChannelHandlerContext ctx, int streamId, int promisedStreamId, Http2Headers headers, - int padding) throws Http2Exception { - listener.onPushPromiseRead(ctx, streamId, promisedStreamId, headers, padding); - } - - @Override - public void onGoAwayRead(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData) - throws Http2Exception { - listener.onGoAwayRead(ctx, lastStreamId, errorCode, debugData); - } - - @Override - public void onWindowUpdateRead(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement) - throws Http2Exception { - listener.onWindowUpdateRead(ctx, streamId, windowSizeIncrement); - } - - @Override - public void onUnknownFrame(ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags, - ByteBuf payload) throws Http2Exception { - listener.onUnknownFrame(ctx, frameType, streamId, flags, payload); - } -} 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 deleted file mode 100644 index a562a2e2bc..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameLogger.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.logging.LogLevel; -import io.netty.util.internal.UnstableApi; -import io.netty.util.internal.logging.InternalLogLevel; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import static java.util.Objects.requireNonNull; - -/** - * Logs HTTP2 frames for debugging purposes. - */ -@UnstableApi -public class Http2FrameLogger { - - public enum Direction { - INBOUND, - OUTBOUND - } - - private static final int BUFFER_LENGTH_THRESHOLD = 64; - private final InternalLogger logger; - private final InternalLogLevel level; - - public Http2FrameLogger(LogLevel level) { - this(checkAndConvertLevel(level), InternalLoggerFactory.getInstance(Http2FrameLogger.class)); - } - - public Http2FrameLogger(LogLevel level, String name) { - this(checkAndConvertLevel(level), InternalLoggerFactory.getInstance(requireNonNull(name, "name"))); - } - - public Http2FrameLogger(LogLevel level, Class clazz) { - this(checkAndConvertLevel(level), InternalLoggerFactory.getInstance(requireNonNull(clazz, "clazz"))); - } - - private Http2FrameLogger(InternalLogLevel level, InternalLogger logger) { - this.level = level; - this.logger = logger; - } - - private static InternalLogLevel checkAndConvertLevel(LogLevel level) { - return requireNonNull(level, "level").toInternalLevel(); - } - - public boolean isEnabled() { - return logger.isEnabled(level); - } - - public void logData(Direction direction, ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding, - boolean endStream) { - 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) { - 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) { - 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) { - 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) { - if (isEnabled()) { - logger.log(level, "{} {} RST_STREAM: streamId={} errorCode={}", ctx.channel(), - direction.name(), streamId, errorCode); - } - } - - public void logSettingsAck(Direction direction, ChannelHandlerContext ctx) { - logger.log(level, "{} {} SETTINGS: ack=true", ctx.channel(), direction.name()); - } - - public void logSettings(Direction direction, ChannelHandlerContext ctx, Http2Settings settings) { - if (isEnabled()) { - logger.log(level, "{} {} SETTINGS: ack=false settings={}", ctx.channel(), direction.name(), settings); - } - } - - public void logPing(Direction direction, ChannelHandlerContext ctx, long data) { - if (isEnabled()) { - logger.log(level, "{} {} PING: ack=false bytes={}", ctx.channel(), - direction.name(), data); - } - } - - public void logPingAck(Direction direction, ChannelHandlerContext ctx, long 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) { - 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) { - 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) { - 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) { - 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 (level == InternalLogLevel.TRACE || buf.readableBytes() <= BUFFER_LENGTH_THRESHOLD) { - // Log the entire buffer. - return ByteBufUtil.hexDump(buf); - } - - // Otherwise just log the first 64 bytes. - int length = Math.min(buf.readableBytes(), BUFFER_LENGTH_THRESHOLD); - return ByteBufUtil.hexDump(buf, buf.readerIndex(), length) + "..."; - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameReader.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameReader.java deleted file mode 100644 index 7d5ac3f565..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameReader.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.internal.UnstableApi; - -import java.io.Closeable; - -/** - * Reads HTTP/2 frames from an input {@link ByteBuf} and notifies the specified - * {@link Http2FrameListener} when frames are complete. - */ -@UnstableApi -public interface Http2FrameReader extends Closeable { - /** - * Configuration specific to {@link Http2FrameReader} - */ - interface Configuration { - /** - * Get the {@link Http2HeadersDecoder.Configuration} for this {@link Http2FrameReader} - */ - Http2HeadersDecoder.Configuration headersConfiguration(); - - /** - * Get the {@link Http2FrameSizePolicy} for this {@link Http2FrameReader} - */ - Http2FrameSizePolicy frameSizePolicy(); - } - - /** - * Attempts to read the next frame from the input buffer. If enough data is available to fully - * read the frame, notifies the listener of the read frame. - */ - void readFrame(ChannelHandlerContext ctx, ByteBuf input, Http2FrameListener listener) - throws Http2Exception; - - /** - * Get the configuration related elements for this {@link Http2FrameReader} - */ - Configuration configuration(); - - /** - * Closes this reader and frees any allocated resources. - */ - @Override - void close(); -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameSizePolicy.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameSizePolicy.java deleted file mode 100644 index 23abd3a9cb..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameSizePolicy.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.util.internal.UnstableApi; - -@UnstableApi -public interface Http2FrameSizePolicy { - /** - * Sets the maximum allowed frame size. Attempts to write frames longer than this maximum will fail. - *

- * This value is used to represent - * SETTINGS_MAX_FRAME_SIZE. This method should - * only be called by Netty (not users) as a result of a receiving a {@code SETTINGS} frame. - */ - void maxFrameSize(int max) throws Http2Exception; - - /** - * Gets the maximum allowed frame size. - *

- * This value is used to represent - * SETTINGS_MAX_FRAME_SIZE. The initial value - * defined by the RFC is unlimited but enforcing a lower limit is generally permitted. - * {@link Http2CodecUtil#DEFAULT_MAX_FRAME_SIZE} can be used as a more conservative default. - */ - int maxFrameSize(); -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameStream.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameStream.java deleted file mode 100644 index 5c3e98075a..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameStream.java +++ /dev/null @@ -1,39 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.handler.codec.http2.Http2Stream.State; -import io.netty.util.internal.UnstableApi; - -/** - * A single stream within an HTTP/2 connection. To be used with the {@link Http2FrameCodec}. - */ -@UnstableApi -public interface Http2FrameStream { - /** - * Returns the stream identifier. - * - *

Use {@link Http2CodecUtil#isStreamIdValid(int)} to check if the stream has already been assigned an - * identifier. - */ - int id(); - - /** - * Returns the state of this stream. - */ - State state(); -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameStreamEvent.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameStreamEvent.java deleted file mode 100644 index 7218675070..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameStreamEvent.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.util.internal.UnstableApi; - -@UnstableApi -public final class Http2FrameStreamEvent { - - private final Http2FrameStream stream; - private final Type type; - - enum Type { - State, - Writability - } - - private Http2FrameStreamEvent(Http2FrameStream stream, Type type) { - this.stream = stream; - this.type = type; - } - - public Http2FrameStream stream() { - return stream; - } - - public Type type() { - return type; - } - - static Http2FrameStreamEvent stateChanged(Http2FrameStream stream) { - return new Http2FrameStreamEvent(stream, Type.State); - } - - static Http2FrameStreamEvent writabilityChanged(Http2FrameStream stream) { - return new Http2FrameStreamEvent(stream, Type.Writability); - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameStreamException.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameStreamException.java deleted file mode 100644 index d1f1806769..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameStreamException.java +++ /dev/null @@ -1,47 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.util.internal.UnstableApi; - -import static java.util.Objects.requireNonNull; - -/** - * An HTTP/2 exception for a specific {@link Http2FrameStream}. - */ -@UnstableApi -public final class Http2FrameStreamException extends Exception { - - private static final long serialVersionUID = -4407186173493887044L; - - private final Http2Error error; - private final Http2FrameStream stream; - - public Http2FrameStreamException(Http2FrameStream stream, Http2Error error, Throwable cause) { - super(cause.getMessage(), cause); - this.stream = requireNonNull(stream, "stream"); - this.error = requireNonNull(error, "error"); - } - - public Http2Error error() { - return error; - } - - public Http2FrameStream stream() { - return stream; - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameStreamVisitor.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameStreamVisitor.java deleted file mode 100644 index 8c75d9e7bb..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameStreamVisitor.java +++ /dev/null @@ -1,38 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.util.internal.UnstableApi; - -/** - * A visitor that allows to iterate over a collection of {@link Http2FrameStream}s. - */ -@UnstableApi -public interface Http2FrameStreamVisitor { - - /** - * This method is called once for each stream of the collection. - * - *

If an {@link Exception} is thrown, the loop is stopped. - * - * @return

    - *
  • {@code true} if the visitor wants to continue the loop and handle the stream.
  • - *
  • {@code false} if the visitor wants to stop handling the stream and abort the loop.
  • - *
- */ - boolean visit(Http2FrameStream stream); -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameTypes.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameTypes.java deleted file mode 100644 index 54bcc3b510..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameTypes.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.util.internal.UnstableApi; - -/** - * Registry of all standard frame types defined by the HTTP/2 specification. - */ -@UnstableApi -public final class Http2FrameTypes { - public static final byte DATA = 0x0; - public static final byte HEADERS = 0x1; - public static final byte PRIORITY = 0x2; - public static final byte RST_STREAM = 0x3; - public static final byte SETTINGS = 0x4; - public static final byte PUSH_PROMISE = 0x5; - public static final byte PING = 0x6; - public static final byte GO_AWAY = 0x7; - public static final byte WINDOW_UPDATE = 0x8; - public static final byte CONTINUATION = 0x9; - - private Http2FrameTypes() { - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameWriter.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameWriter.java deleted file mode 100644 index e708279e61..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameWriter.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.concurrent.Future; -import io.netty.util.internal.UnstableApi; - -import java.io.Closeable; - -/** - * A writer responsible for marshaling HTTP/2 frames to the channel. All of the write methods in - * this interface write to the context, but DO NOT FLUSH. To perform a flush, you must separately - * call {@link ChannelHandlerContext#flush()}. - */ -@UnstableApi -public interface Http2FrameWriter extends Http2DataWriter, Closeable { - /** - * Configuration specific to {@link Http2FrameWriter} - */ - interface Configuration { - /** - * Get the {@link Http2HeadersEncoder.Configuration} for this {@link Http2FrameWriter} - */ - Http2HeadersEncoder.Configuration headersConfiguration(); - - /** - * Get the {@link Http2FrameSizePolicy} for this {@link Http2FrameWriter} - */ - Http2FrameSizePolicy frameSizePolicy(); - } - - /** - * Writes a HEADERS frame to the remote endpoint. - * - * @param ctx the context to use for writing. - * @param streamId the stream for which to send the frame. - * @param headers the headers to be sent. - * @param padding additional bytes that should be added to obscure the true content size. Must be between 0 and - * 256 (inclusive). - * @param endStream indicates if this is the last frame to be sent for the stream. - * @return the future for the write. - * Section 10.5.1 states the following: - *
-     * The header block MUST be processed to ensure a consistent connection state, unless the connection is closed.
-     * 
- * If this call has modified the HPACK header state you MUST throw a connection error. - *

- * If this call has NOT modified the HPACK header state you are free to throw a stream error. - */ - Future writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, - int padding, boolean endStream); - - /** - * Writes a HEADERS frame with priority specified to the remote endpoint. - * - * @param ctx the context to use for writing. - * @param streamId the stream for which to send the frame. - * @param headers the headers to be sent. - * @param streamDependency the stream on which this stream should depend, or 0 if it should - * depend on the connection. - * @param weight the weight for this stream. - * @param exclusive whether this stream should be the exclusive dependant of its parent. - * @param padding additional bytes that should be added to obscure the true content size. Must be between 0 and - * 256 (inclusive). - * @param endStream indicates if this is the last frame to be sent for the stream. - * @return the future for the write. - * Section 10.5.1 states the following: - *

-     * The header block MUST be processed to ensure a consistent connection state, unless the connection is closed.
-     * 
- * If this call has modified the HPACK header state you MUST throw a connection error. - *

- * If this call has NOT modified the HPACK header state you are free to throw a stream error. - */ - Future writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, - int streamDependency, short weight, boolean exclusive, int padding, boolean endStream); - - /** - * Writes a PRIORITY frame to the remote endpoint. - * - * @param ctx the context to use for writing. - * @param streamId the stream for which to send the frame. - * @param streamDependency the stream on which this stream should depend, or 0 if it should - * depend on the connection. - * @param weight the weight for this stream. - * @param exclusive whether this stream should be the exclusive dependant of its parent. - * @return the future for the write. - */ - Future writePriority(ChannelHandlerContext ctx, int streamId, int streamDependency, - short weight, boolean exclusive); - - /** - * Writes a RST_STREAM frame to the remote endpoint. - * - * @param ctx the context to use for writing. - * @param streamId the stream for which to send the frame. - * @param errorCode the error code indicating the nature of the failure. - * @return the future for the write. - */ - Future writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode); - - /** - * Writes a SETTINGS frame to the remote endpoint. - * - * @param ctx the context to use for writing. - * @param settings the settings to be sent. - * @return the future for the write. - */ - Future writeSettings(ChannelHandlerContext ctx, Http2Settings settings); - - /** - * Writes a SETTINGS acknowledgment to the remote endpoint. - * - * @param ctx the context to use for writing. - * @return the future for the write. - */ - Future writeSettingsAck(ChannelHandlerContext ctx); - - /** - * Writes a PING frame to the remote endpoint. - * - * @param ctx the context to use for writing. - * @param ack indicates whether this is an ack of a PING frame previously received from the - * remote endpoint. - * @param data the payload of the frame. - * @return the future for the write. - */ - Future writePing(ChannelHandlerContext ctx, boolean ack, long data); - - /** - * Writes a PUSH_PROMISE frame to the remote endpoint. - * - * @param ctx the context to use for writing. - * @param streamId the stream for which to send the frame. - * @param promisedStreamId the ID of the promised stream. - * @param headers the headers to be sent. - * @param padding additional bytes that should be added to obscure the true content size. Must be between 0 and - * 256 (inclusive). - * @return the future for the write. - * Section 10.5.1 states the following: - *

-     * The header block MUST be processed to ensure a consistent connection state, unless the connection is closed.
-     * 
- * If this call has modified the HPACK header state you MUST throw a connection error. - *

- * If this call has NOT modified the HPACK header state you are free to throw a stream error. - */ - Future writePushPromise(ChannelHandlerContext ctx, int streamId, int promisedStreamId, - Http2Headers headers, int padding); - - /** - * Writes a GO_AWAY frame to the remote endpoint. - * - * @param ctx the context to use for writing. - * @param lastStreamId the last known stream of this endpoint. - * @param errorCode the error code, if the connection was abnormally terminated. - * @param debugData application-defined debug data. This will be released by this method. - * @return the future for the write. - */ - Future writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode, - ByteBuf debugData); - - /** - * Writes a WINDOW_UPDATE frame to the remote endpoint. - * - * @param ctx the context to use for writing. - * @param streamId the stream for which to send the frame. - * @param windowSizeIncrement the number of bytes by which the local inbound flow control window - * is increasing. - * @return the future for the write. - */ - Future writeWindowUpdate(ChannelHandlerContext ctx, int streamId, - int windowSizeIncrement); - - /** - * Generic write method for any HTTP/2 frame. This allows writing of non-standard frames. - * - * @param ctx the context to use for writing. - * @param frameType the frame type identifier. - * @param streamId the stream for which to send the frame. - * @param flags the flags to write for this frame. - * @param payload the payload to write for this frame. This will be released by this method. - * @return the future for the write. - */ - Future writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId, - Http2Flags flags, ByteBuf payload); - - /** - * Get the configuration related elements for this {@link Http2FrameWriter} - */ - Configuration configuration(); - - /** - * Closes this writer and frees any allocated resources. - */ - @Override - void close(); -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2GoAwayFrame.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2GoAwayFrame.java deleted file mode 100644 index 70f22d3b55..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2GoAwayFrame.java +++ /dev/null @@ -1,89 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufHolder; -import io.netty.util.internal.UnstableApi; - -/** - * HTTP/2 GOAWAY frame. - * - *

The last stream identifier must not be set by the application, but instead the - * relative {@link #extraStreamIds()} should be used. The {@link #lastStreamId()} will only be - * set for incoming GOAWAY frames by the HTTP/2 codec. - * - *

Graceful shutdown as described in the HTTP/2 spec can be accomplished by calling - * {@code #setExtraStreamIds(Integer.MAX_VALUE)}. - */ -@UnstableApi -public interface Http2GoAwayFrame extends Http2Frame, ByteBufHolder { - /** - * The reason for beginning closure of the connection. Represented as an HTTP/2 error code. - */ - long errorCode(); - - /** - * The number of IDs to reserve for the receiver to use while GOAWAY is in transit. This allows - * for new streams currently en route to still be created, up to a point, which allows for very - * graceful shutdown of both sides. - */ - int extraStreamIds(); - - /** - * Sets the number of IDs to reserve for the receiver to use while GOAWAY is in transit. - * - * @see #extraStreamIds - * @return {@code this} - */ - Http2GoAwayFrame setExtraStreamIds(int extraStreamIds); - - /** - * Returns the last stream identifier if set, or {@code -1} else. - */ - int lastStreamId(); - - /** - * Optional debugging information describing cause the GOAWAY. Will not be {@code null}, but may - * be empty. - */ - @Override - ByteBuf content(); - - @Override - Http2GoAwayFrame copy(); - - @Override - Http2GoAwayFrame duplicate(); - - @Override - Http2GoAwayFrame retainedDuplicate(); - - @Override - Http2GoAwayFrame replace(ByteBuf content); - - @Override - Http2GoAwayFrame retain(); - - @Override - Http2GoAwayFrame retain(int increment); - - @Override - Http2GoAwayFrame touch(); - - @Override - Http2GoAwayFrame touch(Object hint); -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Headers.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Headers.java deleted file mode 100644 index f3b03dad67..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Headers.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.handler.codec.Headers; -import io.netty.util.AsciiString; -import io.netty.util.internal.UnstableApi; - -import java.util.Iterator; -import java.util.Map.Entry; - -/** - * A collection of headers sent or received via HTTP/2. - */ -@UnstableApi -public interface Http2Headers extends Headers { - - /** - * HTTP/2 pseudo-headers names. - */ - enum PseudoHeaderName { - /** - * {@code :method}. - */ - METHOD(":method", true), - - /** - * {@code :scheme}. - */ - SCHEME(":scheme", true), - - /** - * {@code :authority}. - */ - AUTHORITY(":authority", true), - - /** - * {@code :path}. - */ - PATH(":path", true), - - /** - * {@code :status}. - */ - STATUS(":status", false), - - /** - * {@code :protocol}, as defined in RFC 8441, - * Bootstrapping WebSockets with HTTP/2. - */ - PROTOCOL(":protocol", true); - - private static final char PSEUDO_HEADER_PREFIX = ':'; - private static final byte PSEUDO_HEADER_PREFIX_BYTE = (byte) PSEUDO_HEADER_PREFIX; - - private final AsciiString value; - private final boolean requestOnly; - private static final CharSequenceMap PSEUDO_HEADERS = new CharSequenceMap<>(); - - static { - for (PseudoHeaderName pseudoHeader : values()) { - PSEUDO_HEADERS.add(pseudoHeader.value(), pseudoHeader); - } - } - - PseudoHeaderName(String value, boolean requestOnly) { - this.value = AsciiString.cached(value); - this.requestOnly = requestOnly; - } - - public AsciiString value() { - // Return a slice so that the buffer gets its own reader index. - return value; - } - - /** - * Indicates whether the specified header follows the pseudo-header format (begins with ':' character) - * - * @return {@code true} if the header follow the pseudo-header format - */ - public static boolean hasPseudoHeaderFormat(CharSequence headerName) { - if (headerName instanceof AsciiString) { - final AsciiString asciiHeaderName = (AsciiString) headerName; - return asciiHeaderName.length() > 0 && asciiHeaderName.byteAt(0) == PSEUDO_HEADER_PREFIX_BYTE; - } else { - return headerName.length() > 0 && headerName.charAt(0) == PSEUDO_HEADER_PREFIX; - } - } - - /** - * Indicates whether the given header name is a valid HTTP/2 pseudo header. - */ - public static boolean isPseudoHeader(CharSequence header) { - return PSEUDO_HEADERS.contains(header); - } - - /** - * Returns the {@link PseudoHeaderName} corresponding to the specified header name. - * - * @return corresponding {@link PseudoHeaderName} if any, {@code null} otherwise. - */ - public static PseudoHeaderName getPseudoHeader(CharSequence header) { - return PSEUDO_HEADERS.get(header); - } - - /** - * Indicates whether the pseudo-header is to be used in a request context. - * - * @return {@code true} if the pseudo-header is to be used in a request context - */ - public boolean isRequestOnly() { - return requestOnly; - } - } - - /** - * Returns an iterator over all HTTP/2 headers. The iteration order is as follows: - * 1. All pseudo headers (order not specified). - * 2. All non-pseudo headers (in insertion order). - */ - @Override - Iterator> iterator(); - - /** - * Equivalent to {@link #getAll(Object)} but no intermediate list is generated. - * @param name the name of the header to retrieve - * @return an {@link Iterator} of header values corresponding to {@code name}. - */ - Iterator valueIterator(CharSequence name); - - /** - * Sets the {@link PseudoHeaderName#METHOD} header - */ - Http2Headers method(CharSequence value); - - /** - * Sets the {@link PseudoHeaderName#SCHEME} header - */ - Http2Headers scheme(CharSequence value); - - /** - * Sets the {@link PseudoHeaderName#AUTHORITY} header - */ - Http2Headers authority(CharSequence value); - - /** - * Sets the {@link PseudoHeaderName#PATH} header - */ - Http2Headers path(CharSequence value); - - /** - * Sets the {@link PseudoHeaderName#STATUS} header - */ - Http2Headers status(CharSequence value); - - /** - * Gets the {@link PseudoHeaderName#METHOD} header or {@code null} if there is no such header - */ - CharSequence method(); - - /** - * Gets the {@link PseudoHeaderName#SCHEME} header or {@code null} if there is no such header - */ - CharSequence scheme(); - - /** - * Gets the {@link PseudoHeaderName#AUTHORITY} header or {@code null} if there is no such header - */ - CharSequence authority(); - - /** - * Gets the {@link PseudoHeaderName#PATH} header or {@code null} if there is no such header - */ - CharSequence path(); - - /** - * Gets the {@link PseudoHeaderName#STATUS} header or {@code null} if there is no such header - */ - CharSequence status(); - - /** - * Returns {@code true} if a header with the {@code name} and {@code value} exists, {@code false} otherwise. - *

- * If {@code caseInsensitive} is {@code true} then a case insensitive compare is done on the value. - * - * @param name the name of the header to find - * @param value the value of the header to find - * @param caseInsensitive {@code true} then a case insensitive compare is run to compare values. - * otherwise a case sensitive compare is run to compare values. - */ - boolean contains(CharSequence name, CharSequence value, boolean caseInsensitive); -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2HeadersDecoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2HeadersDecoder.java deleted file mode 100644 index b519c33d29..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2HeadersDecoder.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.util.internal.UnstableApi; - -/** - * Decodes HPACK-encoded headers blocks into {@link Http2Headers}. - */ -@UnstableApi -public interface Http2HeadersDecoder { - /** - * Configuration related elements for the {@link Http2HeadersDecoder} interface - */ - interface Configuration { - /** - * Represents the value for - * SETTINGS_HEADER_TABLE_SIZE. - * This method should only be called by Netty (not users) as a result of a receiving a {@code SETTINGS} frame. - */ - void maxHeaderTableSize(long max) throws Http2Exception; - - /** - * Represents the value for - * SETTINGS_HEADER_TABLE_SIZE. The initial value - * returned by this method must be {@link Http2CodecUtil#DEFAULT_HEADER_TABLE_SIZE}. - */ - long maxHeaderTableSize(); - - /** - * Configure the maximum allowed size in bytes of each set of headers. - *

- * This method should only be called by Netty (not users) as a result of a receiving a {@code SETTINGS} frame. - * @param max SETTINGS_MAX_HEADER_LIST_SIZE. - * If this limit is exceeded the implementation should attempt to keep the HPACK header tables up to date - * by processing data from the peer, but a {@code RST_STREAM} frame will be sent for the offending stream. - * @param goAwayMax Must be {@code >= max}. A {@code GO_AWAY} frame will be generated if this limit is exceeded - * for any particular stream. - * @throws Http2Exception if limits exceed the RFC's boundaries or {@code max > goAwayMax}. - */ - void maxHeaderListSize(long max, long goAwayMax) throws Http2Exception; - - /** - * Represents the value for - * SETTINGS_MAX_HEADER_LIST_SIZE. - */ - long maxHeaderListSize(); - - /** - * Represents the upper bound in bytes for a set of headers before a {@code GO_AWAY} should be sent. - * This will be {@code <=} - * SETTINGS_MAX_HEADER_LIST_SIZE. - */ - long maxHeaderListSizeGoAway(); - } - - /** - * Decodes the given headers block and returns the headers. - */ - Http2Headers decodeHeaders(int streamId, ByteBuf headerBlock) throws Http2Exception; - - /** - * Get the {@link Configuration} for this {@link Http2HeadersDecoder} - */ - Configuration configuration(); -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2HeadersEncoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2HeadersEncoder.java deleted file mode 100644 index 845de83dfa..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2HeadersEncoder.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.util.internal.UnstableApi; - -/** - * Encodes {@link Http2Headers} into HPACK-encoded headers blocks. - */ -@UnstableApi -public interface Http2HeadersEncoder { - /** - * Configuration related elements for the {@link Http2HeadersEncoder} interface - */ - interface Configuration { - /** - * Represents the value for - * SETTINGS_HEADER_TABLE_SIZE. - * This method should only be called by Netty (not users) as a result of a receiving a {@code SETTINGS} frame. - */ - void maxHeaderTableSize(long max) throws Http2Exception; - - /** - * Represents the value for - * SETTINGS_HEADER_TABLE_SIZE. - * The initial value returned by this method must be {@link Http2CodecUtil#DEFAULT_HEADER_TABLE_SIZE}. - */ - long maxHeaderTableSize(); - - /** - * Represents the value for - * SETTINGS_MAX_HEADER_LIST_SIZE. - * This method should only be called by Netty (not users) as a result of a receiving a {@code SETTINGS} frame. - */ - void maxHeaderListSize(long max) throws Http2Exception; - - /** - * Represents the value for - * SETTINGS_MAX_HEADER_LIST_SIZE. - */ - long maxHeaderListSize(); - } - - /** - * Determine if a header name/value pair is treated as - * sensitive. - * If the object can be dynamically modified and shared across multiple connections it may need to be thread safe. - */ - interface SensitivityDetector { - /** - * Determine if a header {@code name}/{@code value} pair should be treated as - * sensitive. - * - * @param name The name for the header. - * @param value The value of the header. - * @return {@code true} if a header {@code name}/{@code value} pair should be treated as - * sensitive. - * {@code false} otherwise. - */ - boolean isSensitive(CharSequence name, CharSequence value); - } - - /** - * Encodes the given headers and writes the output headers block to the given output buffer. - * - * @param streamId the identifier of the stream for which the headers are encoded. - * @param headers the headers to be encoded. - * @param buffer the buffer to receive the encoded headers. - */ - void encodeHeaders(int streamId, Http2Headers headers, ByteBuf buffer) throws Http2Exception; - - /** - * Get the {@link Configuration} for this {@link Http2HeadersEncoder} - */ - Configuration configuration(); - - /** - * Always return {@code false} for {@link SensitivityDetector#isSensitive(CharSequence, CharSequence)}. - */ - SensitivityDetector NEVER_SENSITIVE = (name, value) -> false; - - /** - * Always return {@code true} for {@link SensitivityDetector#isSensitive(CharSequence, CharSequence)}. - */ - SensitivityDetector ALWAYS_SENSITIVE = (name, value) -> true; -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2HeadersFrame.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2HeadersFrame.java deleted file mode 100644 index 2a24f6341f..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2HeadersFrame.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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.util.internal.UnstableApi; - -/** - * HTTP/2 HEADERS frame. - */ -@UnstableApi -public interface Http2HeadersFrame extends Http2StreamFrame { - - /** - * A complete header list. CONTINUATION frames are automatically handled. - */ - Http2Headers headers(); - - /** - * Frame padding to use. Must be non-negative and less than 256. - */ - int padding(); - - /** - * Returns {@code true} if the END_STREAM flag is set. - */ - boolean isEndStream(); -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2InboundFrameLogger.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2InboundFrameLogger.java deleted file mode 100644 index 852920609a..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2InboundFrameLogger.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import static io.netty.handler.codec.http2.Http2FrameLogger.Direction.INBOUND; -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.internal.UnstableApi; - -/** - * Decorator around a {@link Http2FrameReader} that logs all inbound frames before calling - * back the listener. - */ -@UnstableApi -public class Http2InboundFrameLogger implements Http2FrameReader { - private final Http2FrameReader reader; - private final Http2FrameLogger logger; - - public Http2InboundFrameLogger(Http2FrameReader reader, Http2FrameLogger logger) { - this.reader = requireNonNull(reader, "reader"); - this.logger = requireNonNull(logger, "logger"); - } - - @Override - public void readFrame(ChannelHandlerContext ctx, ByteBuf input, final Http2FrameListener listener) - throws Http2Exception { - reader.readFrame(ctx, input, new Http2FrameListener() { - - @Override - public int onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, - int padding, boolean endOfStream) - throws Http2Exception { - logger.logData(INBOUND, ctx, streamId, data, padding, endOfStream); - return listener.onDataRead(ctx, streamId, data, padding, endOfStream); - } - - @Override - public void onHeadersRead(ChannelHandlerContext ctx, int streamId, - Http2Headers headers, int padding, boolean endStream) - throws Http2Exception { - logger.logHeaders(INBOUND, ctx, streamId, headers, padding, endStream); - listener.onHeadersRead(ctx, streamId, headers, padding, endStream); - } - - @Override - public void onHeadersRead(ChannelHandlerContext ctx, int streamId, - Http2Headers headers, int streamDependency, short weight, boolean exclusive, - int padding, boolean endStream) throws Http2Exception { - logger.logHeaders(INBOUND, ctx, streamId, headers, streamDependency, weight, exclusive, - padding, endStream); - listener.onHeadersRead(ctx, streamId, headers, streamDependency, weight, exclusive, - padding, endStream); - } - - @Override - public void onPriorityRead(ChannelHandlerContext ctx, int streamId, - int streamDependency, short weight, boolean exclusive) throws Http2Exception { - logger.logPriority(INBOUND, ctx, streamId, streamDependency, weight, exclusive); - listener.onPriorityRead(ctx, streamId, streamDependency, weight, exclusive); - } - - @Override - public void onRstStreamRead(ChannelHandlerContext ctx, int streamId, long errorCode) - throws Http2Exception { - logger.logRstStream(INBOUND, ctx, streamId, errorCode); - listener.onRstStreamRead(ctx, streamId, errorCode); - } - - @Override - public void onSettingsAckRead(ChannelHandlerContext ctx) throws Http2Exception { - logger.logSettingsAck(INBOUND, ctx); - listener.onSettingsAckRead(ctx); - } - - @Override - public void onSettingsRead(ChannelHandlerContext ctx, Http2Settings settings) - throws Http2Exception { - logger.logSettings(INBOUND, ctx, settings); - listener.onSettingsRead(ctx, settings); - } - - @Override - public void onPingRead(ChannelHandlerContext ctx, long data) throws Http2Exception { - logger.logPing(INBOUND, ctx, data); - listener.onPingRead(ctx, data); - } - - @Override - public void onPingAckRead(ChannelHandlerContext ctx, long data) throws Http2Exception { - logger.logPingAck(INBOUND, ctx, data); - listener.onPingAckRead(ctx, data); - } - - @Override - public void onPushPromiseRead(ChannelHandlerContext ctx, int streamId, - int promisedStreamId, Http2Headers headers, int padding) throws Http2Exception { - logger.logPushPromise(INBOUND, ctx, streamId, promisedStreamId, headers, padding); - listener.onPushPromiseRead(ctx, streamId, promisedStreamId, headers, padding); - } - - @Override - public void onGoAwayRead(ChannelHandlerContext ctx, int lastStreamId, long errorCode, - ByteBuf debugData) throws Http2Exception { - logger.logGoAway(INBOUND, ctx, lastStreamId, errorCode, debugData); - listener.onGoAwayRead(ctx, lastStreamId, errorCode, debugData); - } - - @Override - public void onWindowUpdateRead(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement) - throws Http2Exception { - logger.logWindowsUpdate(INBOUND, ctx, streamId, windowSizeIncrement); - listener.onWindowUpdateRead(ctx, streamId, windowSizeIncrement); - } - - @Override - public void onUnknownFrame(ChannelHandlerContext ctx, byte frameType, int streamId, - Http2Flags flags, ByteBuf payload) throws Http2Exception { - logger.logUnknownFrame(INBOUND, ctx, frameType, streamId, flags, payload); - listener.onUnknownFrame(ctx, frameType, streamId, flags, payload); - } - }); - } - - @Override - public void close() { - reader.close(); - } - - @Override - public Configuration configuration() { - return reader.configuration(); - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2LifecycleManager.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2LifecycleManager.java deleted file mode 100644 index 60e91ddd24..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2LifecycleManager.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; -import io.netty.util.internal.UnstableApi; - -/** - * Manager for the life cycle of the HTTP/2 connection. Handles graceful shutdown of the channel, - * closing only after all of the streams have closed. - */ -@UnstableApi -public interface Http2LifecycleManager { - - /** - * Closes the local side of the {@code stream}. Depending on the {@code stream} state this may result in - * {@code stream} being closed. See {@link #closeStream(Http2Stream, Future)}. - * @param stream the stream to be half closed. - * @param future See {@link #closeStream(Http2Stream, Future)}. - */ - void closeStreamLocal(Http2Stream stream, Future future); - - /** - * Closes the remote side of the {@code stream}. Depending on the {@code stream} state this may result in - * {@code stream} being closed. See {@link #closeStream(Http2Stream, Future)}. - * @param stream the stream to be half closed. - * @param future See {@link #closeStream(Http2Stream, Future)}. - */ - void closeStreamRemote(Http2Stream stream, Future future); - - /** - * Closes and deactivates the given {@code stream}. A listener is also attached to {@code future} and upon - * completion the underlying channel will be closed if {@link Http2Connection#numActiveStreams()} is 0. - * @param stream the stream to be closed and deactivated. - * @param future when completed if {@link Http2Connection#numActiveStreams()} is 0 then the underlying channel - * will be closed. - */ - void closeStream(Http2Stream stream, Future future); - - /** - * Ensure the stream identified by {@code streamId} is reset. If our local state does not indicate the stream has - * been reset yet then a {@code RST_STREAM} will be sent to the peer. If our local state indicates the stream - * has already been reset then the return status will indicate success without sending anything to the peer. - * @param ctx The context used for communication and buffer allocation if necessary. - * @param streamId The identifier of the stream to reset. - * @param errorCode Justification as to why this stream is being reset. See {@link Http2Error}. - * @return Will be considered successful when the connection and stream state has been updated, and a - * {@code RST_STREAM} frame has been sent to the peer. If the stream state has already been updated and a - * {@code RST_STREAM} frame has been sent then the return status may indicate success immediately. - */ - Future resetStream(ChannelHandlerContext ctx, int streamId, long errorCode); - - /** - * Prevents the peer from creating streams and close the connection if {@code errorCode} is not - * {@link Http2Error#NO_ERROR}. After this call the peer is not allowed to create any new streams and the local - * endpoint will be limited to creating streams with {@code stream identifier <= lastStreamId}. This may result in - * sending a {@code GO_AWAY} frame (assuming we have not already sent one with - * {@code Last-Stream-ID <= lastStreamId}), or may just return success if a {@code GO_AWAY} has previously been - * sent. - * @param ctx The context used for communication and buffer allocation if necessary. - * @param lastStreamId The last stream that the local endpoint is claiming it will accept. - * @param errorCode The rational as to why the connection is being closed. See {@link Http2Error}. - * @param debugData For diagnostic purposes (carries no semantic value). - * @return Will be considered successful when the connection and stream state has been updated, and a - * {@code GO_AWAY} frame has been sent to the peer. If the stream state has already been updated and a - * {@code GO_AWAY} frame has been sent then the return status may indicate success immediately. - */ - Future goAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode, - ByteBuf debugData); - - /** - * Processes the given error. - * - * @param ctx The context used for communication and buffer allocation if necessary. - * @param outbound {@code true} if the error was caused by an outbound operation and so the corresponding - * {@link Promise} was failed as well. - * @param cause the error. - */ - void onError(ChannelHandlerContext ctx, boolean outbound, Throwable cause); -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2LocalFlowController.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2LocalFlowController.java deleted file mode 100644 index 544b2e315d..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2LocalFlowController.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.util.internal.UnstableApi; - -/** - * A {@link Http2FlowController} for controlling the inbound flow of {@code DATA} frames from the remote endpoint. - */ -@UnstableApi -public interface Http2LocalFlowController extends Http2FlowController { - /** - * Sets the writer to be use for sending {@code WINDOW_UPDATE} frames. This must be called before any flow - * controlled data is received. - * - * @param frameWriter the HTTP/2 frame writer. - */ - Http2LocalFlowController frameWriter(Http2FrameWriter frameWriter); - - /** - * Receives an inbound {@code DATA} frame from the remote endpoint and applies flow control policies to it for both - * the {@code stream} as well as the connection. If any flow control policies have been violated, an exception is - * raised immediately, otherwise the frame is considered to have "passed" flow control. - *

- * If {@code stream} is {@code null} or closed, flow control should only be applied to the connection window and the - * bytes are immediately consumed. - * - * @param stream the subject stream for the received frame. The connection stream object must not be used. If {@code - * stream} is {@code null} or closed, flow control should only be applied to the connection window and the bytes are - * immediately consumed. - * @param data payload buffer for the frame. - * @param padding additional bytes that should be added to obscure the true content size. Must be between 0 and - * 256 (inclusive). - * @param endOfStream Indicates whether this is the last frame to be sent from the remote endpoint for this stream. - * @throws Http2Exception if any flow control errors are encountered. - */ - void receiveFlowControlledFrame(Http2Stream stream, ByteBuf data, int padding, - boolean endOfStream) throws Http2Exception; - - /** - * Indicates that the application has consumed a number of bytes for the given stream and is therefore ready to - * receive more data from the remote endpoint. The application must consume any bytes that it receives or the flow - * control window will collapse. Consuming bytes enables the flow controller to send {@code WINDOW_UPDATE} to - * restore a portion of the flow control window for the stream. - *

- * If {@code stream} is {@code null} or closed (i.e. {@link Http2Stream#state()} method returns {@link - * Http2Stream.State#CLOSED}), calling this method has no effect. - * - * @param stream the stream for which window space should be freed. The connection stream object must not be used. - * If {@code stream} is {@code null} or closed (i.e. {@link Http2Stream#state()} method returns {@link - * Http2Stream.State#CLOSED}), calling this method has no effect. - * @param numBytes the number of bytes to be returned to the flow control window. - * @return true if a {@code WINDOW_UPDATE} was sent, false otherwise. - * @throws Http2Exception if the number of bytes returned exceeds the {@link #unconsumedBytes(Http2Stream)} for the - * stream. - */ - boolean consumeBytes(Http2Stream stream, int numBytes) throws Http2Exception; - - /** - * The number of bytes for the given stream that have been received but not yet consumed by the - * application. - * - * @param stream the stream for which window space should be freed. - * @return the number of unconsumed bytes for the stream. - */ - int unconsumedBytes(Http2Stream stream); - - /** - * Get the initial flow control window size for the given stream. This quantity is measured in number of bytes. Note - * the unavailable window portion can be calculated by {@link #initialWindowSize()} - {@link - * #windowSize(Http2Stream)}. - */ - int initialWindowSize(Http2Stream stream); -} 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 deleted file mode 100644 index c9c82068a4..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodec.java +++ /dev/null @@ -1,322 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.Channel; -import io.netty.channel.ChannelConfig; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.EventLoop; -import io.netty.util.ReferenceCounted; -import io.netty.util.concurrent.Future; -import io.netty.util.internal.UnstableApi; - -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.Http2Error.INTERNAL_ERROR; -import static io.netty.handler.codec.http2.Http2Exception.connectionError; - -/** - * An HTTP/2 handler that creates child channels for each stream. - * - *

When a new stream is created, a new {@link Channel} is created for it. Applications send and - * receive {@link Http2StreamFrame}s on the created channel. {@link ByteBuf}s cannot be processed by the channel; - * all writes that reach the head of the pipeline must be an instance of {@link Http2StreamFrame}. Writes that reach - * the head of the pipeline are processed directly by this handler and cannot be intercepted. - * - *

The child channel will be notified of user events that impact the stream, such as {@link - * Http2GoAwayFrame} and {@link Http2ResetFrame}, as soon as they occur. Although {@code - * Http2GoAwayFrame} and {@code Http2ResetFrame} signify that the remote is ignoring further - * communication, closing of the channel is delayed until any inbound queue is drained with {@link - * Channel#read()}, which follows the default behavior of channels in Netty. Applications are - * free to close the channel in response to such events if they don't have use for any queued - * messages. Any connection level events like {@link Http2SettingsFrame} and {@link Http2GoAwayFrame} - * will be processed internally and also propagated down the pipeline for other handlers to act on. - * - *

Outbound streams are supported via the {@link Http2StreamChannelBootstrap}. - * - *

{@link ChannelConfig#setMaxMessagesPerRead(int)} and {@link ChannelConfig#setAutoRead(boolean)} are supported. - * - *

Reference Counting

- * - * Some {@link Http2StreamFrame}s implement the {@link ReferenceCounted} interface, as they carry - * reference counted objects (e.g. {@link ByteBuf}s). The multiplex codec will call {@link ReferenceCounted#retain()} - * before propagating a reference counted object through the pipeline, and thus an application handler needs to release - * such an object after having consumed it. For more information on reference counting take a look at - * https://netty.io/wiki/reference-counted-objects.html - * - *

Channel Events

- * - * A child channel becomes active as soon as it is registered to an {@link EventLoop}. Therefore, an active channel - * does not map to an active HTTP/2 stream immediately. Only once a {@link Http2HeadersFrame} has been successfully sent - * or received, does the channel map to an active HTTP/2 stream. In case it is not possible to open a new HTTP/2 stream - * (i.e. due to the maximum number of active streams being exceeded), the child channel receives an exception - * indicating the cause and is closed immediately thereafter. - * - *

Writability and Flow Control

- * - * A child channel observes outbound/remote flow control via the channel's writability. A channel only becomes writable - * when it maps to an active HTTP/2 stream and the stream's flow control window is greater than zero. A child channel - * does not know about the connection-level flow control window. {@link ChannelHandler}s are free to ignore the - * channel's writability, in which case the excessive writes will be buffered by the parent channel. It's important to - * note that only {@link Http2DataFrame}s are subject to HTTP/2 flow control. - * - * @deprecated use {@link Http2FrameCodecBuilder} together with {@link Http2MultiplexHandler}. - */ -@Deprecated -@UnstableApi -public class Http2MultiplexCodec extends Http2FrameCodec { - - private final ChannelHandler inboundStreamHandler; - private final ChannelHandler upgradeStreamHandler; - private final Queue readCompletePendingQueue = - new MaxCapacityQueue(new ArrayDeque(8), - // Choose 100 which is what is used most of the times as default. - Http2CodecUtil.SMALLEST_MAX_CONCURRENT_STREAMS); - - private boolean parentReadInProgress; - private int idCount; - - // Need to be volatile as accessed from within the Http2MultiplexCodecStreamChannel in a multi-threaded fashion. - volatile ChannelHandlerContext ctx; - - Http2MultiplexCodec(Http2ConnectionEncoder encoder, - Http2ConnectionDecoder decoder, - Http2Settings initialSettings, - ChannelHandler inboundStreamHandler, - ChannelHandler upgradeStreamHandler, boolean decoupleCloseAndGoAway) { - super(encoder, decoder, initialSettings, decoupleCloseAndGoAway); - 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(); - } - - @Override - public final void handlerAdded0(ChannelHandlerContext ctx) throws Exception { - if (ctx.executor() != ctx.channel().executor()) { - throw new IllegalStateException("EventExecutor must be EventLoop of Channel"); - } - this.ctx = ctx; - super.handlerAdded0(ctx); - } - - @Override - public final void handlerRemoved0(ChannelHandlerContext ctx) throws Exception { - super.handlerRemoved0(ctx); - - readCompletePendingQueue.clear(); - } - - @Override - final void onHttp2Frame(ChannelHandlerContext ctx, Http2Frame frame) { - if (frame instanceof Http2StreamFrame) { - Http2StreamFrame streamFrame = (Http2StreamFrame) frame; - AbstractHttp2StreamChannel channel = (AbstractHttp2StreamChannel) - ((DefaultHttp2FrameStream) streamFrame.stream()).attachment; - channel.fireChildRead(streamFrame); - return; - } - if (frame instanceof Http2GoAwayFrame) { - onHttp2GoAwayFrame(ctx, (Http2GoAwayFrame) frame); - } - // Send frames down the pipeline - ctx.fireChannelRead(frame); - } - - @Override - final void onHttp2StreamStateChanged(ChannelHandlerContext ctx, DefaultHttp2FrameStream stream) { - switch (stream.state()) { - case HALF_CLOSED_LOCAL: - if (stream.id() != HTTP_UPGRADE_STREAM_ID) { - // Ignore everything which was not caused by an upgrade - break; - } - // fall-through - case HALF_CLOSED_REMOTE: - // fall-through - case OPEN: - if (stream.attachment != null) { - // ignore if child channel was already created. - break; - } - final Http2MultiplexCodecStreamChannel streamChannel; - // We need to handle upgrades special when on the client side. - if (stream.id() == HTTP_UPGRADE_STREAM_ID && !connection().isServer()) { - // Add our upgrade handler to the channel and then register the channel. - // The register call fires the channelActive, etc. - assert upgradeStreamHandler != null; - streamChannel = new Http2MultiplexCodecStreamChannel(stream, upgradeStreamHandler); - streamChannel.closeOutbound(); - } else { - streamChannel = new Http2MultiplexCodecStreamChannel(stream, inboundStreamHandler); - } - - Future future = streamChannel.register(); - if (future.isDone()) { - Http2MultiplexHandler.registerDone(streamChannel, future); - } else { - future.addListener(streamChannel, Http2MultiplexHandler.CHILD_CHANNEL_REGISTRATION_LISTENER); - } - break; - case CLOSED: - AbstractHttp2StreamChannel channel = (AbstractHttp2StreamChannel) stream.attachment; - if (channel != null) { - channel.streamClosed(); - } - break; - default: - // ignore for now - break; - } - } - - // TODO: This is most likely not the best way to expose this, need to think more about it. - final Http2StreamChannel newOutboundStream() { - return new Http2MultiplexCodecStreamChannel(newStream(), null); - } - - @Override - final void onHttp2FrameStreamException(ChannelHandlerContext ctx, Http2FrameStreamException cause) { - Http2FrameStream stream = cause.stream(); - AbstractHttp2StreamChannel channel = (AbstractHttp2StreamChannel) ((DefaultHttp2FrameStream) stream).attachment; - - try { - channel.pipeline().fireExceptionCaught(cause.getCause()); - } finally { - channel.unsafe().closeForcibly(); - } - } - - private void onHttp2GoAwayFrame(ChannelHandlerContext ctx, final Http2GoAwayFrame goAwayFrame) { - if (goAwayFrame.lastStreamId() == Integer.MAX_VALUE) { - // None of the streams can have an id greater than Integer.MAX_VALUE - return; - } - // Notify which streams were not processed by the remote peer and are safe to retry on another connection: - try { - forEachActiveStream(stream -> { - final int streamId = stream.id(); - AbstractHttp2StreamChannel channel = (AbstractHttp2StreamChannel) - ((DefaultHttp2FrameStream) stream).attachment; - if (streamId > goAwayFrame.lastStreamId() && connection().local().isValidStreamId(streamId)) { - channel.pipeline().fireUserEventTriggered(goAwayFrame.retainedDuplicate()); - } - return true; - }); - } catch (Http2Exception e) { - ctx.fireExceptionCaught(e); - ctx.close(); - } - } - - /** - * Notifies any child streams of the read completion. - */ - @Override - public final void channelReadComplete(ChannelHandlerContext ctx) throws Exception { - processPendingReadCompleteQueue(); - channelReadComplete0(ctx); - } - - private void processPendingReadCompleteQueue() { - parentReadInProgress = true; - try { - // 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. - for (;;) { - AbstractHttp2StreamChannel childChannel = readCompletePendingQueue.poll(); - if (childChannel == null) { - break; - } - childChannel.fireChildReadComplete(); - } - } finally { - parentReadInProgress = false; - readCompletePendingQueue.clear(); - // We always flush as this is what Http2ConnectionHandler does for now. - flush0(ctx); - } - } - @Override - public final void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - parentReadInProgress = true; - super.channelRead(ctx, msg); - } - - @Override - public final void channelWritabilityChanged(final ChannelHandlerContext ctx) throws Exception { - if (ctx.channel().isWritable()) { - // While the writability state may change during iterating of the streams we just set all of the streams - // to writable to not affect fairness. These will be "limited" by their own watermarks in any case. - forEachActiveStream(AbstractHttp2StreamChannel.WRITABLE_VISITOR); - } - - super.channelWritabilityChanged(ctx); - } - - final void flush0(ChannelHandlerContext ctx) { - flush(ctx); - } - - private final class Http2MultiplexCodecStreamChannel extends AbstractHttp2StreamChannel { - - Http2MultiplexCodecStreamChannel(DefaultHttp2FrameStream stream, ChannelHandler inboundHandler) { - super(stream, ++idCount, inboundHandler); - } - - @Override - protected boolean isParentReadInProgress() { - return parentReadInProgress; - } - - @Override - protected void addChannelToReadCompletePendingQueue() { - // If there is no space left in the queue, just keep on processing everything that is already - // stored there and try again. - while (!readCompletePendingQueue.offer(this)) { - processPendingReadCompleteQueue(); - } - } - - @Override - protected ChannelHandlerContext parentContext() { - return ctx; - } - - @Override - protected Future write0(ChannelHandlerContext ctx, Object msg) { - return Http2MultiplexCodec.this.write(ctx, msg); - } - - @Override - protected void flush0(ChannelHandlerContext ctx) { - Http2MultiplexCodec.this.flush0(ctx); - } - } -} 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 deleted file mode 100644 index 8df7e14c59..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexCodecBuilder.java +++ /dev/null @@ -1,250 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerAdapter; -import io.netty.util.internal.UnstableApi; - -import static java.util.Objects.requireNonNull; - -/** - * A builder for {@link Http2MultiplexCodec}. - * - * @deprecated use {@link Http2FrameCodecBuilder} together with {@link Http2MultiplexHandler}. - */ -@Deprecated -@UnstableApi -public class Http2MultiplexCodecBuilder - extends AbstractHttp2ConnectionHandlerBuilder { - private Http2FrameWriter frameWriter; - - final ChannelHandler childHandler; - private ChannelHandler upgradeStreamHandler; - - Http2MultiplexCodecBuilder(boolean server, ChannelHandler childHandler) { - server(server); - this.childHandler = checkSharable(requireNonNull(childHandler, "childHandler")); - // For backwards compatibility we should disable to timeout by default at this layer. - gracefulShutdownTimeoutMillis(0); - } - - private static ChannelHandler checkSharable(ChannelHandler handler) { - if (handler instanceof ChannelHandlerAdapter && !((ChannelHandlerAdapter) handler).isSharable() && - !handler.getClass().isAnnotationPresent(ChannelHandler.Sharable.class)) { - throw new IllegalArgumentException("The handler must be Sharable"); - } - return handler; - } - - // For testing only. - Http2MultiplexCodecBuilder frameWriter(Http2FrameWriter frameWriter) { - this.frameWriter = requireNonNull(frameWriter, "frameWriter"); - return this; - } - - /** - * Creates a builder for an HTTP/2 client. - * - * @param childHandler the handler added to channels for remotely-created streams. It must be - * {@link ChannelHandler.Sharable}. - */ - public static Http2MultiplexCodecBuilder forClient(ChannelHandler childHandler) { - return new Http2MultiplexCodecBuilder(false, childHandler); - } - - /** - * Creates a builder for an HTTP/2 server. - * - * @param childHandler the handler added to channels for remotely-created streams. It must be - * {@link ChannelHandler.Sharable}. - */ - public static Http2MultiplexCodecBuilder forServer(ChannelHandler childHandler) { - return new Http2MultiplexCodecBuilder(true, childHandler); - } - - public Http2MultiplexCodecBuilder withUpgradeStreamHandler(ChannelHandler upgradeStreamHandler) { - if (isServer()) { - throw new IllegalArgumentException("Server codecs don't use an extra handler for the upgrade stream"); - } - this.upgradeStreamHandler = upgradeStreamHandler; - return this; - } - - @Override - public Http2Settings initialSettings() { - return super.initialSettings(); - } - - @Override - public Http2MultiplexCodecBuilder initialSettings(Http2Settings settings) { - return super.initialSettings(settings); - } - - @Override - public long gracefulShutdownTimeoutMillis() { - return super.gracefulShutdownTimeoutMillis(); - } - - @Override - public Http2MultiplexCodecBuilder gracefulShutdownTimeoutMillis(long gracefulShutdownTimeoutMillis) { - return super.gracefulShutdownTimeoutMillis(gracefulShutdownTimeoutMillis); - } - - @Override - public boolean isServer() { - return super.isServer(); - } - - @Override - public int maxReservedStreams() { - return super.maxReservedStreams(); - } - - @Override - public Http2MultiplexCodecBuilder maxReservedStreams(int maxReservedStreams) { - return super.maxReservedStreams(maxReservedStreams); - } - - @Override - public boolean isValidateHeaders() { - return super.isValidateHeaders(); - } - - @Override - public Http2MultiplexCodecBuilder validateHeaders(boolean validateHeaders) { - return super.validateHeaders(validateHeaders); - } - - @Override - public Http2FrameLogger frameLogger() { - return super.frameLogger(); - } - - @Override - public Http2MultiplexCodecBuilder frameLogger(Http2FrameLogger frameLogger) { - return super.frameLogger(frameLogger); - } - - @Override - public boolean encoderEnforceMaxConcurrentStreams() { - return super.encoderEnforceMaxConcurrentStreams(); - } - - @Override - public Http2MultiplexCodecBuilder encoderEnforceMaxConcurrentStreams(boolean encoderEnforceMaxConcurrentStreams) { - return super.encoderEnforceMaxConcurrentStreams(encoderEnforceMaxConcurrentStreams); - } - - @Override - public int encoderEnforceMaxQueuedControlFrames() { - return super.encoderEnforceMaxQueuedControlFrames(); - } - - @Override - public Http2MultiplexCodecBuilder encoderEnforceMaxQueuedControlFrames(int maxQueuedControlFrames) { - return super.encoderEnforceMaxQueuedControlFrames(maxQueuedControlFrames); - } - - @Override - public Http2HeadersEncoder.SensitivityDetector headerSensitivityDetector() { - return super.headerSensitivityDetector(); - } - - @Override - public Http2MultiplexCodecBuilder headerSensitivityDetector( - Http2HeadersEncoder.SensitivityDetector headerSensitivityDetector) { - return super.headerSensitivityDetector(headerSensitivityDetector); - } - - @Override - public Http2MultiplexCodecBuilder encoderIgnoreMaxHeaderListSize(boolean ignoreMaxHeaderListSize) { - return super.encoderIgnoreMaxHeaderListSize(ignoreMaxHeaderListSize); - } - - @Override - @Deprecated - public Http2MultiplexCodecBuilder initialHuffmanDecodeCapacity(int initialHuffmanDecodeCapacity) { - return super.initialHuffmanDecodeCapacity(initialHuffmanDecodeCapacity); - } - - @Override - public Http2MultiplexCodecBuilder autoAckSettingsFrame(boolean autoAckSettings) { - return super.autoAckSettingsFrame(autoAckSettings); - } - - @Override - public Http2MultiplexCodecBuilder autoAckPingFrame(boolean autoAckPingFrame) { - return super.autoAckPingFrame(autoAckPingFrame); - } - - @Override - public Http2MultiplexCodecBuilder decoupleCloseAndGoAway(boolean decoupleCloseAndGoAway) { - return super.decoupleCloseAndGoAway(decoupleCloseAndGoAway); - } - - @Override - public int decoderEnforceMaxConsecutiveEmptyDataFrames() { - return super.decoderEnforceMaxConsecutiveEmptyDataFrames(); - } - - @Override - public Http2MultiplexCodecBuilder decoderEnforceMaxConsecutiveEmptyDataFrames(int maxConsecutiveEmptyFrames) { - return super.decoderEnforceMaxConsecutiveEmptyDataFrames(maxConsecutiveEmptyFrames); - } - - @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(isValidateHeaders()) : - new DefaultHttp2HeadersDecoder(isValidateHeaders(), 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, - promisedRequestVerifier(), isAutoAckSettingsFrame(), isAutoAckPingFrame()); - - int maxConsecutiveEmptyDataFrames = decoderEnforceMaxConsecutiveEmptyDataFrames(); - if (maxConsecutiveEmptyDataFrames > 0) { - decoder = new Http2EmptyDataFrameConnectionDecoder(decoder, maxConsecutiveEmptyDataFrames); - } - - return build(decoder, encoder, initialSettings()); - } - return super.build(); - } - - @Override - protected Http2MultiplexCodec build( - Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder, Http2Settings initialSettings) { - Http2MultiplexCodec codec = new Http2MultiplexCodec(encoder, decoder, initialSettings, childHandler, - upgradeStreamHandler, decoupleCloseAndGoAway()); - codec.gracefulShutdownTimeoutMillis(gracefulShutdownTimeoutMillis()); - return codec; - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexHandler.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexHandler.java deleted file mode 100644 index cbafce863c..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2MultiplexHandler.java +++ /dev/null @@ -1,373 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.Channel; -import io.netty.channel.ChannelConfig; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoop; -import io.netty.channel.ServerChannel; -import io.netty.handler.codec.http2.Http2FrameCodec.DefaultHttp2FrameStream; -import io.netty.util.ReferenceCounted; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.FutureContextListener; -import io.netty.util.internal.UnstableApi; - -import javax.net.ssl.SSLException; -import java.util.ArrayDeque; -import java.util.Objects; -import java.util.Queue; - -import static io.netty.handler.codec.http2.Http2Error.INTERNAL_ERROR; -import static io.netty.handler.codec.http2.Http2Exception.connectionError; - -/** - * An HTTP/2 handler that creates child channels for each stream. This handler must be used in combination - * with {@link Http2FrameCodec}. - * - *

When a new stream is created, a new {@link Channel} is created for it. Applications send and - * receive {@link Http2StreamFrame}s on the created channel. {@link ByteBuf}s cannot be processed by the channel; - * all writes that reach the head of the pipeline must be an instance of {@link Http2StreamFrame}. Writes that reach - * the head of the pipeline are processed directly by this handler and cannot be intercepted. - * - *

The child channel will be notified of user events that impact the stream, such as {@link - * Http2GoAwayFrame} and {@link Http2ResetFrame}, as soon as they occur. Although {@code - * Http2GoAwayFrame} and {@code Http2ResetFrame} signify that the remote is ignoring further - * communication, closing of the channel is delayed until any inbound queue is drained with {@link - * Channel#read()}, which follows the default behavior of channels in Netty. Applications are - * free to close the channel in response to such events if they don't have use for any queued - * messages. Any connection level events like {@link Http2SettingsFrame} and {@link Http2GoAwayFrame} - * will be processed internally and also propagated down the pipeline for other handlers to act on. - * - *

Outbound streams are supported via the {@link Http2StreamChannelBootstrap}. - * - *

{@link ChannelConfig#setMaxMessagesPerRead(int)} and {@link ChannelConfig#setAutoRead(boolean)} are supported. - * - *

Reference Counting

- * - * Some {@link Http2StreamFrame}s implement the {@link ReferenceCounted} interface, as they carry - * reference counted objects (e.g. {@link ByteBuf}s). The multiplex codec will call {@link ReferenceCounted#retain()} - * before propagating a reference counted object through the pipeline, and thus an application handler needs to release - * such an object after having consumed it. For more information on reference counting take a look at - * https://netty.io/wiki/reference-counted-objects.html - * - *

Channel Events

- * - * A child channel becomes active as soon as it is registered to an {@link EventLoop}. Therefore, an active channel - * does not map to an active HTTP/2 stream immediately. Only once a {@link Http2HeadersFrame} has been successfully sent - * or received, does the channel map to an active HTTP/2 stream. In case it is not possible to open a new HTTP/2 stream - * (i.e. due to the maximum number of active streams being exceeded), the child channel receives an exception - * indicating the cause and is closed immediately thereafter. - * - *

Writability and Flow Control

- * - * A child channel observes outbound/remote flow control via the channel's writability. A channel only becomes writable - * when it maps to an active HTTP/2 stream . A child channel does not know about the connection-level flow control - * window. {@link ChannelHandler}s are free to ignore the channel's writability, in which case the excessive writes will - * be buffered by the parent channel. It's important to note that only {@link Http2DataFrame}s are subject to - * HTTP/2 flow control. - */ -@UnstableApi -public final class Http2MultiplexHandler extends Http2ChannelDuplexHandler { - - static final FutureContextListener CHILD_CHANNEL_REGISTRATION_LISTENER = - Http2MultiplexHandler::registerDone; - - private final ChannelHandler inboundStreamHandler; - private final ChannelHandler upgradeStreamHandler; - private final Queue readCompletePendingQueue = - new MaxCapacityQueue(new ArrayDeque(8), - // Choose 100 which is what is used most of the times as default. - Http2CodecUtil.SMALLEST_MAX_CONCURRENT_STREAMS); - - private boolean parentReadInProgress; - private int idCount; - - // Need to be volatile as accessed from within the Http2MultiplexHandlerStreamChannel in a multi-threaded fashion. - private volatile ChannelHandlerContext ctx; - - /** - * Creates a new instance - * - * @param inboundStreamHandler the {@link ChannelHandler} that will be added to the {@link ChannelPipeline} of - * the {@link Channel}s created for new inbound streams. - */ - public Http2MultiplexHandler(ChannelHandler inboundStreamHandler) { - this(inboundStreamHandler, null); - } - - /** - * Creates a new instance - * - * @param inboundStreamHandler the {@link ChannelHandler} that will be added to the {@link ChannelPipeline} of - * the {@link Channel}s created for new inbound streams. - * @param upgradeStreamHandler the {@link ChannelHandler} that will be added to the {@link ChannelPipeline} of the - * upgraded {@link Channel}. - */ - public Http2MultiplexHandler(ChannelHandler inboundStreamHandler, ChannelHandler upgradeStreamHandler) { - this.inboundStreamHandler = Objects.requireNonNull(inboundStreamHandler, "inboundStreamHandler"); - this.upgradeStreamHandler = upgradeStreamHandler; - } - - static void registerDone(Channel childChannel, Future future) { - // Handle any errors that occurred on the local thread while registering. Even though - // failures can happen after this point, they will be handled by the channel by closing the - // childChannel. - if (future.isFailed()) { - if (childChannel.isRegistered()) { - childChannel.close(); - } else { - childChannel.unsafe().closeForcibly(); - } - } - } - - @Override - protected void handlerAdded0(ChannelHandlerContext ctx) { - if (ctx.executor() != ctx.channel().executor()) { - throw new IllegalStateException("EventExecutor must be EventLoop of Channel"); - } - this.ctx = ctx; - } - - @Override - protected void handlerRemoved0(ChannelHandlerContext ctx) { - readCompletePendingQueue.clear(); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - parentReadInProgress = true; - if (msg instanceof Http2StreamFrame) { - if (msg instanceof Http2WindowUpdateFrame) { - // We dont want to propagate update frames to the user - return; - } - Http2StreamFrame streamFrame = (Http2StreamFrame) msg; - DefaultHttp2FrameStream s = - (DefaultHttp2FrameStream) streamFrame.stream(); - - AbstractHttp2StreamChannel channel = (AbstractHttp2StreamChannel) s.attachment; - if (msg instanceof Http2ResetFrame) { - // Reset frames needs to be propagated via user events as these are not flow-controlled and so - // must not be controlled by suppressing channel.read() on the child channel. - channel.pipeline().fireUserEventTriggered(msg); - - // RST frames will also trigger closing of the streams which then will call - // AbstractHttp2StreamChannel.streamClosed() - } else { - channel.fireChildRead(streamFrame); - } - return; - } - - if (msg instanceof Http2GoAwayFrame) { - // goaway frames will also trigger closing of the streams which then will call - // AbstractHttp2StreamChannel.streamClosed() - onHttp2GoAwayFrame(ctx, (Http2GoAwayFrame) msg); - } - - // Send everything down the pipeline - ctx.fireChannelRead(msg); - } - - @Override - public void channelWritabilityChanged(final ChannelHandlerContext ctx) throws Exception { - if (ctx.channel().isWritable()) { - // While the writability state may change during iterating of the streams we just set all of the streams - // to writable to not affect fairness. These will be "limited" by their own watermarks in any case. - forEachActiveStream(AbstractHttp2StreamChannel.WRITABLE_VISITOR); - } - - ctx.fireChannelWritabilityChanged(); - } - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (evt instanceof Http2FrameStreamEvent) { - Http2FrameStreamEvent event = (Http2FrameStreamEvent) evt; - DefaultHttp2FrameStream stream = (DefaultHttp2FrameStream) event.stream(); - if (event.type() == Http2FrameStreamEvent.Type.State) { - switch (stream.state()) { - case HALF_CLOSED_LOCAL: - if (stream.id() != Http2CodecUtil.HTTP_UPGRADE_STREAM_ID) { - // Ignore everything which was not caused by an upgrade - break; - } - // fall-through - case HALF_CLOSED_REMOTE: - // fall-through - case OPEN: - if (stream.attachment != null) { - // ignore if child channel was already created. - break; - } - final AbstractHttp2StreamChannel ch; - // We need to handle upgrades special when on the client side. - if (stream.id() == Http2CodecUtil.HTTP_UPGRADE_STREAM_ID && !isServer(ctx)) { - // 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"); - } - ch = new Http2MultiplexHandlerStreamChannel(stream, upgradeStreamHandler); - ch.closeOutbound(); - } else { - ch = new Http2MultiplexHandlerStreamChannel(stream, inboundStreamHandler); - } - Future future = ch.register(); - if (future.isDone()) { - registerDone(ch, future); - } else { - future.addListener(ch, CHILD_CHANNEL_REGISTRATION_LISTENER); - } - break; - case CLOSED: - AbstractHttp2StreamChannel channel = (AbstractHttp2StreamChannel) stream.attachment; - if (channel != null) { - channel.streamClosed(); - } - break; - default: - // ignore for now - break; - } - } - return; - } - ctx.fireUserEventTriggered(evt); - } - - // TODO: This is most likely not the best way to expose this, need to think more about it. - Http2StreamChannel newOutboundStream() { - return new Http2MultiplexHandlerStreamChannel((DefaultHttp2FrameStream) newStream(), null); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, final Throwable cause) throws Exception { - if (cause instanceof Http2FrameStreamException) { - Http2FrameStreamException exception = (Http2FrameStreamException) cause; - Http2FrameStream stream = exception.stream(); - AbstractHttp2StreamChannel childChannel = (AbstractHttp2StreamChannel) - ((DefaultHttp2FrameStream) stream).attachment; - try { - childChannel.pipeline().fireExceptionCaught(cause.getCause()); - } finally { - childChannel.unsafe().closeForcibly(); - } - return; - } - if (cause.getCause() instanceof SSLException) { - forEachActiveStream(new Http2FrameStreamVisitor() { - @Override - public boolean visit(Http2FrameStream stream) { - AbstractHttp2StreamChannel childChannel = (AbstractHttp2StreamChannel) - ((DefaultHttp2FrameStream) stream).attachment; - childChannel.pipeline().fireExceptionCaught(cause); - return true; - } - }); - } - ctx.fireExceptionCaught(cause); - } - - private static boolean isServer(ChannelHandlerContext ctx) { - return ctx.channel().parent() instanceof ServerChannel; - } - - private void onHttp2GoAwayFrame(ChannelHandlerContext ctx, final Http2GoAwayFrame goAwayFrame) { - if (goAwayFrame.lastStreamId() == Integer.MAX_VALUE) { - // None of the streams can have an id greater than Integer.MAX_VALUE - return; - } - // Notify which streams were not processed by the remote peer and are safe to retry on another connection: - try { - final boolean server = isServer(ctx); - forEachActiveStream(stream -> { - final int streamId = stream.id(); - if (streamId > goAwayFrame.lastStreamId() && Http2CodecUtil.isStreamIdValid(streamId, server)) { - final AbstractHttp2StreamChannel childChannel = (AbstractHttp2StreamChannel) - ((DefaultHttp2FrameStream) stream).attachment; - childChannel.pipeline().fireUserEventTriggered(goAwayFrame.retainedDuplicate()); - } - return true; - }); - } catch (Http2Exception e) { - ctx.fireExceptionCaught(e); - ctx.close(); - } - } - - /** - * Notifies any child streams of the read completion. - */ - @Override - public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { - processPendingReadCompleteQueue(); - ctx.fireChannelReadComplete(); - } - - private void processPendingReadCompleteQueue() { - parentReadInProgress = true; - // 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. - AbstractHttp2StreamChannel childChannel = readCompletePendingQueue.poll(); - if (childChannel != null) { - try { - do { - childChannel.fireChildReadComplete(); - childChannel = readCompletePendingQueue.poll(); - } while (childChannel != null); - } finally { - parentReadInProgress = false; - readCompletePendingQueue.clear(); - ctx.flush(); - } - } else { - parentReadInProgress = false; - } - } - - private final class Http2MultiplexHandlerStreamChannel extends AbstractHttp2StreamChannel { - - Http2MultiplexHandlerStreamChannel(DefaultHttp2FrameStream stream, ChannelHandler inboundHandler) { - super(stream, ++idCount, inboundHandler); - } - - @Override - protected boolean isParentReadInProgress() { - return parentReadInProgress; - } - - @Override - protected void addChannelToReadCompletePendingQueue() { - // If there is no space left in the queue, just keep on processing everything that is already - // stored there and try again. - while (!readCompletePendingQueue.offer(this)) { - processPendingReadCompleteQueue(); - } - } - - @Override - protected ChannelHandlerContext parentContext() { - return ctx; - } - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2NoMoreStreamIdsException.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2NoMoreStreamIdsException.java deleted file mode 100644 index 121ae9622a..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2NoMoreStreamIdsException.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.util.internal.UnstableApi; - -import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR; - -/** - * This exception is thrown when there are no more stream IDs available for the current connection - */ -@UnstableApi -public class Http2NoMoreStreamIdsException extends Http2Exception { - private static final long serialVersionUID = -7756236161274851110L; - private static final String ERROR_MESSAGE = "No more streams can be created on this connection"; - - public Http2NoMoreStreamIdsException() { - super(PROTOCOL_ERROR, ERROR_MESSAGE, ShutdownHint.GRACEFUL_SHUTDOWN); - } - - public Http2NoMoreStreamIdsException(Throwable cause) { - super(PROTOCOL_ERROR, ERROR_MESSAGE, cause, ShutdownHint.GRACEFUL_SHUTDOWN); - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2OutboundFrameLogger.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2OutboundFrameLogger.java deleted file mode 100644 index 3ba4be22b3..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2OutboundFrameLogger.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.concurrent.Future; -import io.netty.util.internal.UnstableApi; - -import static io.netty.handler.codec.http2.Http2FrameLogger.Direction.OUTBOUND; -import static java.util.Objects.requireNonNull; - -/** - * Decorator around a {@link Http2FrameWriter} that logs all outbound frames before calling the - * writer. - */ -@UnstableApi -public class Http2OutboundFrameLogger implements Http2FrameWriter { - private final Http2FrameWriter writer; - private final Http2FrameLogger logger; - - public Http2OutboundFrameLogger(Http2FrameWriter writer, Http2FrameLogger logger) { - this.writer = requireNonNull(writer, "writer"); - this.logger = requireNonNull(logger, "logger"); - } - - @Override - public Future writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data, - int padding, boolean endStream) { - logger.logData(OUTBOUND, ctx, streamId, data, padding, endStream); - return writer.writeData(ctx, streamId, data, padding, endStream); - } - - @Override - public Future writeHeaders(ChannelHandlerContext ctx, int streamId, - Http2Headers headers, int padding, boolean endStream) { - logger.logHeaders(OUTBOUND, ctx, streamId, headers, padding, endStream); - return writer.writeHeaders(ctx, streamId, headers, padding, endStream); - } - - @Override - public Future writeHeaders(ChannelHandlerContext ctx, int streamId, - Http2Headers headers, int streamDependency, short weight, boolean exclusive, - int padding, boolean endStream) { - logger.logHeaders(OUTBOUND, ctx, streamId, headers, streamDependency, weight, exclusive, - padding, endStream); - return writer.writeHeaders(ctx, streamId, headers, streamDependency, weight, - exclusive, padding, endStream); - } - - @Override - public Future writePriority(ChannelHandlerContext ctx, int streamId, - int streamDependency, short weight, boolean exclusive) { - logger.logPriority(OUTBOUND, ctx, streamId, streamDependency, weight, exclusive); - return writer.writePriority(ctx, streamId, streamDependency, weight, exclusive); - } - - @Override - public Future writeRstStream(ChannelHandlerContext ctx, - int streamId, long errorCode) { - logger.logRstStream(OUTBOUND, ctx, streamId, errorCode); - return writer.writeRstStream(ctx, streamId, errorCode); - } - - @Override - public Future writeSettings(ChannelHandlerContext ctx, - Http2Settings settings) { - logger.logSettings(OUTBOUND, ctx, settings); - return writer.writeSettings(ctx, settings); - } - - @Override - public Future writeSettingsAck(ChannelHandlerContext ctx) { - logger.logSettingsAck(OUTBOUND, ctx); - return writer.writeSettingsAck(ctx); - } - - @Override - public Future writePing(ChannelHandlerContext ctx, boolean ack, - long data) { - if (ack) { - logger.logPingAck(OUTBOUND, ctx, data); - } else { - logger.logPing(OUTBOUND, ctx, data); - } - return writer.writePing(ctx, ack, data); - } - - @Override - public Future writePushPromise(ChannelHandlerContext ctx, int streamId, - int promisedStreamId, Http2Headers headers, int padding) { - logger.logPushPromise(OUTBOUND, ctx, streamId, promisedStreamId, headers, padding); - return writer.writePushPromise(ctx, streamId, promisedStreamId, headers, padding); - } - - @Override - public Future writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode, - ByteBuf debugData) { - logger.logGoAway(OUTBOUND, ctx, lastStreamId, errorCode, debugData); - return writer.writeGoAway(ctx, lastStreamId, errorCode, debugData); - } - - @Override - public Future writeWindowUpdate(ChannelHandlerContext ctx, - int streamId, int windowSizeIncrement) { - logger.logWindowsUpdate(OUTBOUND, ctx, streamId, windowSizeIncrement); - return writer.writeWindowUpdate(ctx, streamId, windowSizeIncrement); - } - - @Override - public Future writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId, - Http2Flags flags, ByteBuf payload) { - logger.logUnknownFrame(OUTBOUND, ctx, frameType, streamId, flags, payload); - return writer.writeFrame(ctx, frameType, streamId, flags, payload); - } - - @Override - public void close() { - writer.close(); - } - - @Override - public Configuration configuration() { - return writer.configuration(); - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2PingFrame.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2PingFrame.java deleted file mode 100644 index 16b1807807..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2PingFrame.java +++ /dev/null @@ -1,36 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.util.internal.UnstableApi; - -/** - * HTTP/2 PING Frame. - */ -@UnstableApi -public interface Http2PingFrame extends Http2Frame { - - /** - * When {@code true}, indicates that this ping is a ping response. - */ - boolean ack(); - - /** - * Returns the eight byte opaque data. - */ - long content(); -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2PriorityFrame.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2PriorityFrame.java deleted file mode 100644 index 403028fa39..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2PriorityFrame.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.util.internal.UnstableApi; - -/** - * HTTP/2 Priority Frame - */ -@UnstableApi -public interface Http2PriorityFrame extends Http2StreamFrame { - - /** - * Parent Stream Id of this Priority request - */ - int streamDependency(); - - /** - * Stream weight - */ - short weight(); - - /** - * Set to {@code true} if this stream is exclusive else set to {@code false} - */ - boolean exclusive(); - - @Override - Http2PriorityFrame stream(Http2FrameStream stream); - -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2PromisedRequestVerifier.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2PromisedRequestVerifier.java deleted file mode 100644 index 0642350b59..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2PromisedRequestVerifier.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.internal.UnstableApi; - -/** - * Provides an extensibility point for users to define the validity of push requests. - * @see [RFC 7540], Section 8.2. - */ -@UnstableApi -public interface Http2PromisedRequestVerifier { - /** - * Determine if a {@link Http2Headers} are authoritative for a particular {@link ChannelHandlerContext}. - * @param ctx The context on which the {@code headers} where received on. - * @param headers The headers to be verified. - * @return {@code true} if the {@code ctx} is authoritative for the {@code headers}, {@code false} otherwise. - * @see - * [RFC 7540], Section 10.1. - */ - boolean isAuthoritative(ChannelHandlerContext ctx, Http2Headers headers); - - /** - * Determine if a request is cacheable. - * @param headers The headers for a push request. - * @return {@code true} if the request associated with {@code headers} is known to be cacheable, - * {@code false} otherwise. - * @see [RFC 7231], Section 4.2.3. - */ - boolean isCacheable(Http2Headers headers); - - /** - * Determine if a request is safe. - * @param headers The headers for a push request. - * @return {@code true} if the request associated with {@code headers} is known to be safe, - * {@code false} otherwise. - * @see [RFC 7231], Section 4.2.1. - */ - boolean isSafe(Http2Headers headers); - - /** - * A default implementation of {@link Http2PromisedRequestVerifier} which always returns positive responses for - * all verification challenges. - */ - Http2PromisedRequestVerifier ALWAYS_VERIFY = new Http2PromisedRequestVerifier() { - @Override - public boolean isAuthoritative(ChannelHandlerContext ctx, Http2Headers headers) { - return true; - } - - @Override - public boolean isCacheable(Http2Headers headers) { - return true; - } - - @Override - public boolean isSafe(Http2Headers headers) { - return true; - } - }; -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2PushPromiseFrame.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2PushPromiseFrame.java deleted file mode 100644 index dc5d7cb42e..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2PushPromiseFrame.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.util.internal.UnstableApi; - -/** - * HTTP/2 Push Promise Frame - */ -@UnstableApi -public interface Http2PushPromiseFrame extends Http2StreamFrame { - - /** - * Set the Promise {@link Http2FrameStream} object for this frame. - */ - Http2StreamFrame pushStream(Http2FrameStream stream); - - /** - * Returns the Promise {@link Http2FrameStream} object for this frame, or {@code null} if the - * frame has yet to be associated with a stream. - */ - Http2FrameStream pushStream(); - - /** - * {@link Http2Headers} sent in Push Promise - */ - Http2Headers http2Headers(); - - /** - * Frame padding to use. Will be non-negative and less than 256. - */ - int padding(); - - /** - * Promised Stream ID - */ - int promisedStreamId(); - - @Override - Http2PushPromiseFrame stream(Http2FrameStream stream); - -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2RemoteFlowController.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2RemoteFlowController.java deleted file mode 100644 index acec99c0df..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2RemoteFlowController.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.internal.UnstableApi; - -/** - * A {@link Http2FlowController} for controlling the flow of outbound {@code DATA} frames to the remote - * endpoint. - */ -@UnstableApi -public interface Http2RemoteFlowController extends Http2FlowController { - /** - * Get the {@link ChannelHandlerContext} for which to apply flow control on. - *

- * This is intended for us by {@link FlowControlled} implementations only. Use with caution. - * @return The {@link ChannelHandlerContext} for which to apply flow control on. - */ - ChannelHandlerContext channelHandlerContext(); - - /** - * Queues a payload for transmission to the remote endpoint. There is no guarantee as to when the data - * will be written or how it will be assigned to frames. - * before sending. - *

- * Writes do not actually occur until {@link #writePendingBytes()} is called. - * - * @param stream the subject stream. Must not be the connection stream object. - * @param payload payload to write subject to flow-control accounting and ordering rules. - */ - void addFlowControlled(Http2Stream stream, FlowControlled payload); - - /** - * Determine if {@code stream} has any {@link FlowControlled} frames currently queued. - * @param stream the stream to check if it has flow controlled frames. - * @return {@code true} if {@code stream} has any {@link FlowControlled} frames currently queued. - */ - boolean hasFlowControlled(Http2Stream stream); - - /** - * Write all data pending in the flow controller up to the flow-control limits. - * - * @throws Http2Exception throws if a protocol-related error occurred. - */ - void writePendingBytes() throws Http2Exception; - - /** - * Set the active listener on the flow-controller. - * - * @param listener to notify when the a write occurs, can be {@code null}. - */ - void listener(Listener listener); - - /** - * Determine if the {@code stream} has bytes remaining for use in the flow control window. - *

- * Note that this method respects channel writability. The channel must be writable for this method to - * return {@code true}. - * - * @param stream The stream to test. - * @return {@code true} if the {@code stream} has bytes remaining for use in the flow control window and the - * channel is writable, {@code false} otherwise. - */ - boolean isWritable(Http2Stream stream); - - /** - * Notification that the writability of {@link #channelHandlerContext()} has changed. - * @throws Http2Exception If any writes occur as a result of this call and encounter errors. - */ - void channelWritabilityChanged() throws Http2Exception; - - /** - * Explicitly update the dependency tree. This method is called independently of stream state changes. - * @param childStreamId The stream identifier associated with the child stream. - * @param parentStreamId The stream identifier associated with the parent stream. May be {@code 0}, - * to make {@code childStreamId} and immediate child of the connection. - * @param weight The weight which is used relative to other child streams for {@code parentStreamId}. This value - * must be between 1 and 256 (inclusive). - * @param exclusive If {@code childStreamId} should be the exclusive dependency of {@code parentStreamId}. - */ - void updateDependencyTree(int childStreamId, int parentStreamId, short weight, boolean exclusive); - - /** - * Implementations of this interface are used to progressively write chunks of the underlying - * payload to the stream. A payload is considered to be fully written if {@link #write} has - * been called at least once and it's {@link #size} is now zero. - */ - interface FlowControlled { - /** - * The size of the payload in terms of bytes applied to the flow-control window. - * Some payloads like {@code HEADER} frames have no cost against flow control and would - * return 0 for this value even though they produce a non-zero number of bytes on - * the wire. Other frames like {@code DATA} frames have both their payload and padding count - * against flow-control. - */ - int size(); - - /** - * Called to indicate that an error occurred before this object could be completely written. - *

- * The {@link Http2RemoteFlowController} will make exactly one call to either - * this method or {@link #writeComplete()}. - *

- * - * @param ctx The context to use if any communication needs to occur as a result of the error. - * This may be {@code null} if an exception occurs when the connection has not been established yet. - * @param cause of the error. - */ - void error(ChannelHandlerContext ctx, Throwable cause); - - /** - * Called after this object has been successfully written. - *

- * The {@link Http2RemoteFlowController} will make exactly one call to either - * this method or {@link #error(ChannelHandlerContext, Throwable)}. - *

- */ - void writeComplete(); - - /** - * Writes up to {@code allowedBytes} of the encapsulated payload to the stream. Note that - * a value of 0 may be passed which will allow payloads with flow-control size == 0 to be - * written. The flow-controller may call this method multiple times with different values until - * the payload is fully written, i.e it's size after the write is 0. - *

- * When an exception is thrown the {@link Http2RemoteFlowController} will make a call to - * {@link #error(ChannelHandlerContext, Throwable)}. - *

- * - * @param ctx The context to use for writing. - * @param allowedBytes an upper bound on the number of bytes the payload can write at this time. - */ - void write(ChannelHandlerContext ctx, int allowedBytes); - - /** - * Merge the contents of the {@code next} message into this message so they can be written out as one unit. - * This allows many small messages to be written as a single DATA frame. - * - * @return {@code true} if {@code next} was successfully merged and does not need to be enqueued, - * {@code false} otherwise. - */ - boolean merge(ChannelHandlerContext ctx, FlowControlled next); - } - - /** - * Listener to the number of flow-controlled bytes written per stream. - */ - interface Listener { - /** - * Notification that {@link Http2RemoteFlowController#isWritable(Http2Stream)} has changed for {@code stream}. - *

- * This method should not throw. Any thrown exceptions are considered a programming error and are ignored. - * @param stream The stream which writability has changed for. - */ - void writabilityChanged(Http2Stream stream); - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ResetFrame.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ResetFrame.java deleted file mode 100644 index 431a572868..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ResetFrame.java +++ /dev/null @@ -1,28 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.util.internal.UnstableApi; - -/** HTTP/2 RST_STREAM frame. */ -@UnstableApi -public interface Http2ResetFrame extends Http2StreamFrame { - - /** - * The reason for resetting the stream. Represented as an HTTP/2 error code. - */ - long errorCode(); -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2SecurityUtil.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2SecurityUtil.java deleted file mode 100644 index c36a0571b3..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2SecurityUtil.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.util.internal.UnstableApi; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -/** - * Provides utilities related to security requirements specific to HTTP/2. - */ -@UnstableApi -public final class Http2SecurityUtil { - /** - * The following list is derived from SunJSSE Supported - * Ciphers and Mozilla Modern Cipher - * Suites in accordance with the HTTP/2 Specification. - * - * According to the - * JSSE documentation "the names mentioned in the TLS RFCs prefixed with TLS_ are functionally equivalent - * to the JSSE cipher suites prefixed with SSL_". - * Both variants are used to support JVMs supporting the one or the other. - */ - public static final List CIPHERS; - - /** - * Mozilla Modern Cipher Suites Intermediate compatibility minus the following cipher suites that are black - * listed by the HTTP/2 RFC. - */ - private static final List CIPHERS_JAVA_MOZILLA_MODERN_SECURITY = Collections.unmodifiableList(Arrays - .asList( - /* openssl = ECDHE-ECDSA-AES128-GCM-SHA256 */ - "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", - - /* REQUIRED BY HTTP/2 SPEC */ - /* openssl = ECDHE-RSA-AES128-GCM-SHA256 */ - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", - /* REQUIRED BY HTTP/2 SPEC */ - - /* openssl = ECDHE-ECDSA-AES256-GCM-SHA384 */ - "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", - /* openssl = ECDHE-RSA-AES256-GCM-SHA384 */ - "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", - /* openssl = ECDHE-ECDSA-CHACHA20-POLY1305 */ - "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", - /* openssl = ECDHE-RSA-CHACHA20-POLY1305 */ - "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", - - /* TLS 1.3 ciphers */ - "TLS_AES_128_GCM_SHA256", - "TLS_AES_256_GCM_SHA384", - "TLS_CHACHA20_POLY1305_SHA256" - )); - - static { - CIPHERS = Collections.unmodifiableList(new ArrayList<>(CIPHERS_JAVA_MOZILLA_MODERN_SECURITY)); - } - - private Http2SecurityUtil() { } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ServerUpgradeCodec.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ServerUpgradeCodec.java deleted file mode 100644 index e5689ef1fb..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ServerUpgradeCodec.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.base64.Base64; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpServerUpgradeHandler; -import io.netty.util.CharsetUtil; -import io.netty.util.internal.UnstableApi; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.nio.CharBuffer; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -import static io.netty.handler.codec.base64.Base64Dialect.URL_SAFE; -import static io.netty.handler.codec.http2.Http2CodecUtil.FRAME_HEADER_LENGTH; -import static io.netty.handler.codec.http2.Http2CodecUtil.HTTP_UPGRADE_SETTINGS_HEADER; -import static io.netty.handler.codec.http2.Http2CodecUtil.writeFrameHeader; -import static io.netty.handler.codec.http2.Http2FrameTypes.SETTINGS; - -/** - * Server-side codec for performing a cleartext upgrade from HTTP/1.x to HTTP/2. - */ -@UnstableApi -public class Http2ServerUpgradeCodec implements HttpServerUpgradeHandler.UpgradeCodec { - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(Http2ServerUpgradeCodec.class); - private static final List REQUIRED_UPGRADE_HEADERS = - Collections.singletonList(HTTP_UPGRADE_SETTINGS_HEADER); - private static final ChannelHandler[] EMPTY_HANDLERS = new ChannelHandler[0]; - - private final String handlerName; - private final Http2ConnectionHandler connectionHandler; - private final ChannelHandler[] handlers; - private final Http2FrameReader frameReader; - - private Http2Settings settings; - - /** - * Creates the codec using a default name for the connection handler when adding to the - * pipeline. - * - * @param connectionHandler the HTTP/2 connection handler - */ - public Http2ServerUpgradeCodec(Http2ConnectionHandler connectionHandler) { - this(null, connectionHandler, EMPTY_HANDLERS); - } - - /** - * Creates the codec using a default name for the connection handler when adding to the - * pipeline. - * - * @param http2Codec the HTTP/2 multiplexing handler. - */ - public Http2ServerUpgradeCodec(Http2MultiplexCodec http2Codec) { - this(null, http2Codec, EMPTY_HANDLERS); - } - - /** - * Creates the codec providing an upgrade to the given handler for HTTP/2. - * - * @param handlerName the name of the HTTP/2 connection handler to be used in the pipeline, - * or {@code null} to auto-generate the name - * @param connectionHandler the HTTP/2 connection handler - */ - public Http2ServerUpgradeCodec(String handlerName, Http2ConnectionHandler connectionHandler) { - this(handlerName, connectionHandler, EMPTY_HANDLERS); - } - - /** - * Creates the codec providing an upgrade to the given handler for HTTP/2. - * - * @param handlerName the name of the HTTP/2 connection handler to be used in the pipeline. - * @param http2Codec the HTTP/2 multiplexing handler. - */ - public Http2ServerUpgradeCodec(String handlerName, Http2MultiplexCodec http2Codec) { - this(handlerName, http2Codec, EMPTY_HANDLERS); - } - - /** - * Creates the codec using a default name for the connection handler when adding to the - * pipeline. - * - * @param http2Codec the HTTP/2 frame handler. - * @param handlers the handlers that will handle the {@link Http2Frame}s. - */ - public Http2ServerUpgradeCodec(Http2FrameCodec http2Codec, ChannelHandler... handlers) { - this(null, http2Codec, handlers); - } - - private Http2ServerUpgradeCodec(String handlerName, Http2ConnectionHandler connectionHandler, - ChannelHandler... handlers) { - this.handlerName = handlerName; - this.connectionHandler = connectionHandler; - this.handlers = handlers; - frameReader = new DefaultHttp2FrameReader(); - } - - @Override - public Collection requiredUpgradeHeaders() { - return REQUIRED_UPGRADE_HEADERS; - } - - @Override - public boolean prepareUpgradeResponse(ChannelHandlerContext ctx, FullHttpRequest upgradeRequest, - HttpHeaders headers) { - try { - // Decode the HTTP2-Settings header and set the settings on the handler to make - // sure everything is fine with the request. - List upgradeHeaders = upgradeRequest.headers().getAll(HTTP_UPGRADE_SETTINGS_HEADER); - if (upgradeHeaders.size() != 1) { - throw new IllegalArgumentException("There must be 1 and only 1 " - + HTTP_UPGRADE_SETTINGS_HEADER + " header."); - } - settings = decodeSettingsHeader(ctx, upgradeHeaders.get(0)); - // Everything looks good. - return true; - } catch (Throwable cause) { - logger.info("Error during upgrade to HTTP/2", cause); - return false; - } - } - - @Override - public void upgradeTo(final ChannelHandlerContext ctx, FullHttpRequest upgradeRequest) { - try { - // Add the HTTP/2 connection handler to the pipeline immediately following the current handler. - ctx.pipeline().addAfter(ctx.name(), handlerName, connectionHandler); - - // Add also all extra handlers as these may handle events / messages produced by the connectionHandler. - // See https://github.com/netty/netty/issues/9314 - if (handlers != null) { - final String name = ctx.pipeline().context(connectionHandler).name(); - for (int i = handlers.length - 1; i >= 0; i--) { - ctx.pipeline().addAfter(name, null, handlers[i]); - } - } - connectionHandler.onHttpServerUpgrade(settings); - } catch (Http2Exception e) { - ctx.fireExceptionCaught(e); - ctx.close(); - } - } - - /** - * Decodes the settings header and returns a {@link Http2Settings} object. - */ - private Http2Settings decodeSettingsHeader(ChannelHandlerContext ctx, CharSequence settingsHeader) - throws Http2Exception { - ByteBuf header = ByteBufUtil.encodeString(ctx.alloc(), CharBuffer.wrap(settingsHeader), CharsetUtil.UTF_8); - try { - // Decode the SETTINGS payload. - ByteBuf payload = Base64.decode(header, URL_SAFE); - - // Create an HTTP/2 frame for the settings. - ByteBuf frame = createSettingsFrame(ctx, payload); - - // Decode the SETTINGS frame and return the settings object. - return decodeSettings(ctx, frame); - } finally { - header.release(); - } - } - - /** - * Decodes the settings frame and returns the settings. - */ - private Http2Settings decodeSettings(ChannelHandlerContext ctx, ByteBuf frame) throws Http2Exception { - try { - final Http2Settings decodedSettings = new Http2Settings(); - frameReader.readFrame(ctx, frame, new Http2FrameAdapter() { - @Override - public void onSettingsRead(ChannelHandlerContext ctx, Http2Settings settings) { - decodedSettings.copyFrom(settings); - } - }); - return decodedSettings; - } finally { - frame.release(); - } - } - - /** - * Creates an HTTP2-Settings header with the given payload. The payload buffer is released. - */ - private static ByteBuf createSettingsFrame(ChannelHandlerContext ctx, ByteBuf payload) { - ByteBuf frame = ctx.alloc().buffer(FRAME_HEADER_LENGTH + payload.readableBytes()); - writeFrameHeader(frame, payload.readableBytes(), SETTINGS, new Http2Flags(), 0); - frame.writeBytes(payload); - payload.release(); - return frame; - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Settings.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Settings.java deleted file mode 100644 index 6dbd6fa8a4..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Settings.java +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.util.collection.CharObjectHashMap; -import io.netty.util.internal.UnstableApi; - -import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_HEADER_LIST_SIZE; -import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_CONCURRENT_STREAMS; -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.handler.codec.http2.Http2CodecUtil.MAX_INITIAL_WINDOW_SIZE; -import static io.netty.handler.codec.http2.Http2CodecUtil.MIN_CONCURRENT_STREAMS; -import static io.netty.handler.codec.http2.Http2CodecUtil.MIN_HEADER_LIST_SIZE; -import static io.netty.handler.codec.http2.Http2CodecUtil.MIN_HEADER_TABLE_SIZE; -import static io.netty.handler.codec.http2.Http2CodecUtil.MIN_INITIAL_WINDOW_SIZE; -import static io.netty.handler.codec.http2.Http2CodecUtil.NUM_STANDARD_SETTINGS; -import static io.netty.handler.codec.http2.Http2CodecUtil.SETTINGS_ENABLE_PUSH; -import static io.netty.handler.codec.http2.Http2CodecUtil.SETTINGS_HEADER_TABLE_SIZE; -import static io.netty.handler.codec.http2.Http2CodecUtil.SETTINGS_INITIAL_WINDOW_SIZE; -import static io.netty.handler.codec.http2.Http2CodecUtil.SETTINGS_MAX_CONCURRENT_STREAMS; -import static io.netty.handler.codec.http2.Http2CodecUtil.SETTINGS_MAX_FRAME_SIZE; -import static io.netty.handler.codec.http2.Http2CodecUtil.SETTINGS_MAX_HEADER_LIST_SIZE; -import static io.netty.handler.codec.http2.Http2CodecUtil.isMaxFrameSizeValid; -import static java.util.Objects.requireNonNull; - -/** - * Settings for one endpoint in an HTTP/2 connection. Each of the values are optional as defined in - * the spec for the SETTINGS frame. Permits storage of arbitrary key/value pairs but provides helper - * methods for standard settings. - */ -@UnstableApi -public final class Http2Settings extends CharObjectHashMap { - /** - * Default capacity based on the number of standard settings from the HTTP/2 spec, adjusted so that adding all of - * the standard settings will not cause the map capacity to change. - */ - private static final int DEFAULT_CAPACITY = (int) (NUM_STANDARD_SETTINGS / DEFAULT_LOAD_FACTOR) + 1; - private static final Long FALSE = 0L; - private static final Long TRUE = 1L; - - public Http2Settings() { - this(DEFAULT_CAPACITY); - } - - public Http2Settings(int initialCapacity, float loadFactor) { - super(initialCapacity, loadFactor); - } - - public Http2Settings(int initialCapacity) { - super(initialCapacity); - } - - /** - * Adds the given setting key/value pair. For standard settings defined by the HTTP/2 spec, performs - * validation on the values. - * - * @throws IllegalArgumentException if verification for a standard HTTP/2 setting fails. - */ - @Override - public Long put(char key, Long value) { - verifyStandardSetting(key, value); - return super.put(key, value); - } - - /** - * Gets the {@code SETTINGS_HEADER_TABLE_SIZE} value. If unavailable, returns {@code null}. - */ - public Long headerTableSize() { - return get(SETTINGS_HEADER_TABLE_SIZE); - } - - /** - * Sets the {@code SETTINGS_HEADER_TABLE_SIZE} value. - * - * @throws IllegalArgumentException if verification of the setting fails. - */ - public Http2Settings headerTableSize(long value) { - put(SETTINGS_HEADER_TABLE_SIZE, Long.valueOf(value)); - return this; - } - - /** - * Gets the {@code SETTINGS_ENABLE_PUSH} value. If unavailable, returns {@code null}. - */ - public Boolean pushEnabled() { - Long value = get(SETTINGS_ENABLE_PUSH); - if (value == null) { - return null; - } - return TRUE.equals(value); - } - - /** - * Sets the {@code SETTINGS_ENABLE_PUSH} value. - */ - public Http2Settings pushEnabled(boolean enabled) { - put(SETTINGS_ENABLE_PUSH, enabled ? TRUE : FALSE); - return this; - } - - /** - * Gets the {@code SETTINGS_MAX_CONCURRENT_STREAMS} value. If unavailable, returns {@code null}. - */ - public Long maxConcurrentStreams() { - return get(SETTINGS_MAX_CONCURRENT_STREAMS); - } - - /** - * Sets the {@code SETTINGS_MAX_CONCURRENT_STREAMS} value. - * - * @throws IllegalArgumentException if verification of the setting fails. - */ - public Http2Settings maxConcurrentStreams(long value) { - put(SETTINGS_MAX_CONCURRENT_STREAMS, Long.valueOf(value)); - return this; - } - - /** - * Gets the {@code SETTINGS_INITIAL_WINDOW_SIZE} value. If unavailable, returns {@code null}. - */ - public Integer initialWindowSize() { - return getIntValue(SETTINGS_INITIAL_WINDOW_SIZE); - } - - /** - * Sets the {@code SETTINGS_INITIAL_WINDOW_SIZE} value. - * - * @throws IllegalArgumentException if verification of the setting fails. - */ - public Http2Settings initialWindowSize(int value) { - put(SETTINGS_INITIAL_WINDOW_SIZE, Long.valueOf(value)); - return this; - } - - /** - * Gets the {@code SETTINGS_MAX_FRAME_SIZE} value. If unavailable, returns {@code null}. - */ - public Integer maxFrameSize() { - return getIntValue(SETTINGS_MAX_FRAME_SIZE); - } - - /** - * Sets the {@code SETTINGS_MAX_FRAME_SIZE} value. - * - * @throws IllegalArgumentException if verification of the setting fails. - */ - public Http2Settings maxFrameSize(int value) { - put(SETTINGS_MAX_FRAME_SIZE, Long.valueOf(value)); - return this; - } - - /** - * Gets the {@code SETTINGS_MAX_HEADER_LIST_SIZE} value. If unavailable, returns {@code null}. - */ - public Long maxHeaderListSize() { - return get(SETTINGS_MAX_HEADER_LIST_SIZE); - } - - /** - * Sets the {@code SETTINGS_MAX_HEADER_LIST_SIZE} value. - * - * @throws IllegalArgumentException if verification of the setting fails. - */ - public Http2Settings maxHeaderListSize(long value) { - put(SETTINGS_MAX_HEADER_LIST_SIZE, Long.valueOf(value)); - return this; - } - - /** - * Clears and then copies the given settings into this object. - */ - public Http2Settings copyFrom(Http2Settings settings) { - clear(); - putAll(settings); - return this; - } - - /** - * A helper method that returns {@link Long#intValue()} on the return of {@link #get(char)}, if present. Note that - * if the range of the value exceeds {@link Integer#MAX_VALUE}, the {@link #get(char)} method should - * be used instead to avoid truncation of the value. - */ - public Integer getIntValue(char key) { - Long value = get(key); - if (value == null) { - return null; - } - return value.intValue(); - } - - private static void verifyStandardSetting(int key, Long value) { - requireNonNull(value, "value"); - switch (key) { - case SETTINGS_HEADER_TABLE_SIZE: - if (value < MIN_HEADER_TABLE_SIZE || value > MAX_HEADER_TABLE_SIZE) { - throw new IllegalArgumentException("Setting HEADER_TABLE_SIZE is invalid: " + value); - } - break; - case SETTINGS_ENABLE_PUSH: - if (value != 0L && value != 1L) { - throw new IllegalArgumentException("Setting ENABLE_PUSH is invalid: " + value); - } - break; - case SETTINGS_MAX_CONCURRENT_STREAMS: - if (value < MIN_CONCURRENT_STREAMS || value > MAX_CONCURRENT_STREAMS) { - throw new IllegalArgumentException( - "Setting MAX_CONCURRENT_STREAMS is invalid: " + value); - } - break; - case SETTINGS_INITIAL_WINDOW_SIZE: - if (value < MIN_INITIAL_WINDOW_SIZE || value > MAX_INITIAL_WINDOW_SIZE) { - throw new IllegalArgumentException("Setting INITIAL_WINDOW_SIZE is invalid: " - + value); - } - break; - case SETTINGS_MAX_FRAME_SIZE: - if (!isMaxFrameSizeValid(value.intValue())) { - throw new IllegalArgumentException("Setting MAX_FRAME_SIZE is invalid: " + value); - } - break; - case SETTINGS_MAX_HEADER_LIST_SIZE: - if (value < MIN_HEADER_LIST_SIZE || value > MAX_HEADER_LIST_SIZE) { - throw new IllegalArgumentException("Setting MAX_HEADER_LIST_SIZE is invalid: " + value); - } - break; - default: - // Non-standard HTTP/2 setting - don't do validation. - break; - } - } - - @Override - protected String keyToString(char key) { - switch (key) { - case SETTINGS_HEADER_TABLE_SIZE: - return "HEADER_TABLE_SIZE"; - case SETTINGS_ENABLE_PUSH: - return "ENABLE_PUSH"; - case SETTINGS_MAX_CONCURRENT_STREAMS: - return "MAX_CONCURRENT_STREAMS"; - case SETTINGS_INITIAL_WINDOW_SIZE: - return "INITIAL_WINDOW_SIZE"; - case SETTINGS_MAX_FRAME_SIZE: - return "MAX_FRAME_SIZE"; - case SETTINGS_MAX_HEADER_LIST_SIZE: - return "MAX_HEADER_LIST_SIZE"; - default: - // Unknown keys. - return super.keyToString(key); - } - } - - public static Http2Settings defaultSettings() { - return new Http2Settings().maxHeaderListSize(DEFAULT_HEADER_LIST_SIZE); - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2SettingsAckFrame.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2SettingsAckFrame.java deleted file mode 100644 index a497e87fc6..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2SettingsAckFrame.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -/** - * An ack for a previously received {@link Http2SettingsFrame}. - *

- * The HTTP/2 protocol enforces that ACKs are applied in - * order, so this ACK will apply to the earliest received and not yet ACKed {@link Http2SettingsFrame} frame. - */ -public interface Http2SettingsAckFrame extends Http2Frame { - Http2SettingsAckFrame INSTANCE = new DefaultHttp2SettingsAckFrame(); - - @Override - String name(); -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2SettingsFrame.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2SettingsFrame.java deleted file mode 100644 index f809062f5b..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2SettingsFrame.java +++ /dev/null @@ -1,28 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.http2; - -/** - * HTTP/2 SETTINGS frame. - */ -public interface Http2SettingsFrame extends Http2Frame { - - Http2Settings settings(); - - @Override - String name(); -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2SettingsReceivedConsumer.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2SettingsReceivedConsumer.java deleted file mode 100644 index 69ba71857b..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2SettingsReceivedConsumer.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -/** - * Provides a Consumer like interface to consume remote settings received but not yet ACKed. - */ -public interface Http2SettingsReceivedConsumer { - /** - * Consume the most recently received but not yet ACKed settings. - */ - void consumeReceivedSettings(Http2Settings settings); -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Stream.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Stream.java deleted file mode 100644 index 202f6ba1cf..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Stream.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.util.internal.UnstableApi; - -/** - * A single stream within an HTTP2 connection. Streams are compared to each other by priority. - */ -@UnstableApi -public interface Http2Stream { - - /** - * The allowed states of an HTTP2 stream. - */ - enum State { - IDLE(false, false), - RESERVED_LOCAL(false, false), - RESERVED_REMOTE(false, false), - OPEN(true, true), - HALF_CLOSED_LOCAL(false, true), - HALF_CLOSED_REMOTE(true, false), - CLOSED(false, false); - - private final boolean localSideOpen; - private final boolean remoteSideOpen; - - State(boolean localSideOpen, boolean remoteSideOpen) { - this.localSideOpen = localSideOpen; - this.remoteSideOpen = remoteSideOpen; - } - - /** - * Indicates whether the local side of this stream is open (i.e. the state is either - * {@link State#OPEN} or {@link State#HALF_CLOSED_REMOTE}). - */ - public boolean localSideOpen() { - return localSideOpen; - } - - /** - * Indicates whether the remote side of this stream is open (i.e. the state is either - * {@link State#OPEN} or {@link State#HALF_CLOSED_LOCAL}). - */ - public boolean remoteSideOpen() { - return remoteSideOpen; - } - } - - /** - * Gets the unique identifier for this stream within the connection. - */ - int id(); - - /** - * Gets the state of this stream. - */ - State state(); - - /** - * Opens this stream, making it available via {@link Http2Connection#forEachActiveStream(Http2StreamVisitor)} and - * transition state to: - *

    - *
  • {@link State#OPEN} if {@link #state()} is {@link State#IDLE} and {@code halfClosed} is {@code false}.
  • - *
  • {@link State#HALF_CLOSED_LOCAL} if {@link #state()} is {@link State#IDLE} and {@code halfClosed} - * is {@code true} and the stream is local. In this state, {@link #isHeadersSent()} is {@code true}
  • - *
  • {@link State#HALF_CLOSED_REMOTE} if {@link #state()} is {@link State#IDLE} and {@code halfClosed} - * is {@code true} and the stream is remote. In this state, {@link #isHeadersReceived()} is {@code true}
  • - *
  • {@link State#RESERVED_LOCAL} if {@link #state()} is {@link State#HALF_CLOSED_REMOTE}.
  • - *
  • {@link State#RESERVED_REMOTE} if {@link #state()} is {@link State#HALF_CLOSED_LOCAL}.
  • - *
- */ - Http2Stream open(boolean halfClosed) throws Http2Exception; - - /** - * Closes the stream. - */ - Http2Stream close(); - - /** - * Closes the local side of this stream. If this makes the stream closed, the child is closed as - * well. - */ - Http2Stream closeLocalSide(); - - /** - * Closes the remote side of this stream. If this makes the stream closed, the child is closed - * as well. - */ - Http2Stream closeRemoteSide(); - - /** - * Indicates whether a {@code RST_STREAM} frame has been sent from the local endpoint for this stream. - */ - boolean isResetSent(); - - /** - * Sets the flag indicating that a {@code RST_STREAM} frame has been sent from the local endpoint - * for this stream. This does not affect the stream state. - */ - Http2Stream resetSent(); - - /** - * Associates the application-defined data with this stream. - * @return The value that was previously associated with {@code key}, or {@code null} if there was none. - */ - V setProperty(Http2Connection.PropertyKey key, V value); - - /** - * Returns application-defined data if any was associated with this stream. - */ - V getProperty(Http2Connection.PropertyKey key); - - /** - * Returns and removes application-defined data if any was associated with this stream. - */ - V removeProperty(Http2Connection.PropertyKey key); - - /** - * Indicates that headers have been sent to the remote endpoint on this stream. The first call to this method would - * be for the initial headers (see {@link #isHeadersSent()}} and the second call would indicate the trailers - * (see {@link #isTrailersReceived()}). - * @param isInformational {@code true} if the headers contain an informational status code (for responses only). - */ - Http2Stream headersSent(boolean isInformational); - - /** - * Indicates whether or not headers were sent to the remote endpoint. - */ - boolean isHeadersSent(); - - /** - * Indicates whether or not trailers were sent to the remote endpoint. - */ - boolean isTrailersSent(); - - /** - * Indicates that headers have been received. The first call to this method would be for the initial headers - * (see {@link #isHeadersReceived()}} and the second call would indicate the trailers - * (see {@link #isTrailersReceived()}). - * @param isInformational {@code true} if the headers contain an informational status code (for responses only). - */ - Http2Stream headersReceived(boolean isInformational); - - /** - * Indicates whether or not the initial headers have been received. - */ - boolean isHeadersReceived(); - - /** - * Indicates whether or not the trailers have been received. - */ - boolean isTrailersReceived(); - - /** - * Indicates that a push promise was sent to the remote endpoint. - */ - Http2Stream pushPromiseSent(); - - /** - * Indicates whether or not a push promise was sent to the remote endpoint. - */ - boolean isPushPromiseSent(); -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2StreamChannel.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2StreamChannel.java deleted file mode 100644 index 819acaccd8..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2StreamChannel.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.channel.Channel; -import io.netty.util.internal.UnstableApi; - -// TODO: Should we have an extra method to "open" the stream and so Channel and take care of sending the -// Http2HeadersFrame under the hood ? -// TODO: Should we extend SocketChannel and map input and output state to the stream state ? -// -@UnstableApi -public interface Http2StreamChannel extends Channel { - - /** - * Returns the {@link Http2FrameStream} that belongs to this channel. - */ - Http2FrameStream stream(); -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2StreamChannelBootstrap.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2StreamChannelBootstrap.java deleted file mode 100644 index f59146ac61..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2StreamChannelBootstrap.java +++ /dev/null @@ -1,249 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelOption; -import io.netty.channel.ChannelPipeline; -import io.netty.util.AttributeKey; -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.UnstableApi; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.nio.channels.ClosedChannelException; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import static java.util.Objects.requireNonNull; - -@UnstableApi -public final class Http2StreamChannelBootstrap { - private static final InternalLogger logger = InternalLoggerFactory.getInstance(Http2StreamChannelBootstrap.class); - @SuppressWarnings("unchecked") - private static final Map.Entry, Object>[] EMPTY_OPTION_ARRAY = new Map.Entry[0]; - @SuppressWarnings("unchecked") - private static final Map.Entry, Object>[] EMPTY_ATTRIBUTE_ARRAY = new Map.Entry[0]; - - // The order in which ChannelOptions are applied is important they may depend on each other for validation - // purposes. - private final Map, Object> options = new LinkedHashMap<>(); - private final Map, Object> attrs = new ConcurrentHashMap<>(); - private final Channel channel; - private volatile ChannelHandler handler; - - // Cache the ChannelHandlerContext to speed up open(...) operations. - private volatile ChannelHandlerContext multiplexCtx; - - public Http2StreamChannelBootstrap(Channel channel) { - this.channel = requireNonNull(channel, "channel"); - } - - /** - * Allow to specify a {@link ChannelOption} which is used for the {@link Http2StreamChannel} instances once they got - * created. Use a value of {@code null} to remove a previous set {@link ChannelOption}. - */ - public Http2StreamChannelBootstrap option(ChannelOption option, T value) { - requireNonNull(option, "option"); - - synchronized (options) { - if (value == null) { - options.remove(option); - } else { - options.put(option, value); - } - } - return this; - } - - /** - * Allow to specify an initial attribute of the newly created {@link Http2StreamChannel}. If the {@code value} is - * {@code null}, the attribute of the specified {@code key} is removed. - */ - public Http2StreamChannelBootstrap attr(AttributeKey key, T value) { - requireNonNull(key, "key"); - if (value == null) { - attrs.remove(key); - } else { - attrs.put(key, value); - } - return this; - } - - /** - * the {@link ChannelHandler} to use for serving the requests. - */ - public Http2StreamChannelBootstrap handler(ChannelHandler handler) { - this.handler = requireNonNull(handler, "handler"); - return this; - } - - /** - * Open a new {@link Http2StreamChannel} to use. - * @return the {@link Future} that will be notified once the channel was opened successfully or it failed. - */ - public Future open() { - return open(channel.executor().newPromise()); - } - - /** - * Open a new {@link Http2StreamChannel} to use and notifies the given {@link Promise}. - * @return the {@link Future} that will be notified once the channel was opened successfully or it failed. - */ - public Future open(final Promise promise) { - try { - ChannelHandlerContext ctx = findCtx(); - EventExecutor executor = ctx.executor(); - if (executor.inEventLoop()) { - open0(ctx, promise); - } else { - final ChannelHandlerContext finalCtx = ctx; - executor.execute(() -> { - if (channel.isActive()) { - open0(finalCtx, promise); - } else { - promise.setFailure(new ClosedChannelException()); - } - }); - } - } catch (Throwable cause) { - promise.setFailure(cause); - } - return promise.asFuture(); - } - - @SuppressWarnings("deprecation") - private ChannelHandlerContext findCtx() throws ClosedChannelException { - // First try to use cached context and if this not work lets try to lookup the context. - ChannelHandlerContext ctx = multiplexCtx; - if (ctx != null && !ctx.isRemoved()) { - return ctx; - } - ChannelPipeline pipeline = channel.pipeline(); - ctx = pipeline.context(Http2MultiplexCodec.class); - if (ctx == null) { - ctx = pipeline.context(Http2MultiplexHandler.class); - } - if (ctx == null) { - if (channel.isActive()) { - throw new IllegalStateException(StringUtil.simpleClassName(Http2MultiplexCodec.class) + " or " - + StringUtil.simpleClassName(Http2MultiplexHandler.class) - + " must be in the ChannelPipeline of Channel " + channel); - } else { - throw new ClosedChannelException(); - } - } - multiplexCtx = ctx; - return ctx; - } - - /** - * @deprecated should not be used directly. Use {@link #open()} or {@link #open(Promise)} - */ - @Deprecated - public void open0(ChannelHandlerContext ctx, final Promise promise) { - assert ctx.executor().inEventLoop(); - if (!promise.setUncancellable()) { - return; - } - final Http2StreamChannel streamChannel; - try { - if (ctx.handler() instanceof Http2MultiplexCodec) { - streamChannel = ((Http2MultiplexCodec) ctx.handler()).newOutboundStream(); - } else { - streamChannel = ((Http2MultiplexHandler) ctx.handler()).newOutboundStream(); - } - } catch (Exception e) { - promise.setFailure(e); - return; - } - try { - init(streamChannel); - } catch (Exception e) { - streamChannel.unsafe().closeForcibly(); - promise.setFailure(e); - return; - } - - Future future = streamChannel.register(); - future.addListener(future1 -> { - if (future1.isSuccess()) { - promise.setSuccess(streamChannel); - } else if (future1.isCancelled()) { - promise.cancel(); - } else { - if (streamChannel.isRegistered()) { - streamChannel.close(); - } else { - streamChannel.unsafe().closeForcibly(); - } - - promise.setFailure(future1.cause()); - } - }); - } - - private void init(Channel channel) { - ChannelPipeline p = channel.pipeline(); - ChannelHandler handler = this.handler; - if (handler != null) { - p.addLast(handler); - } - final Map.Entry, Object> [] optionArray; - synchronized (options) { - optionArray = options.entrySet().toArray(EMPTY_OPTION_ARRAY); - } - - setChannelOptions(channel, optionArray); - setAttributes(channel, attrs.entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY)); - } - - private static void setChannelOptions( - Channel channel, Map.Entry, Object>[] options) { - for (Map.Entry, Object> e: options) { - setChannelOption(channel, e.getKey(), e.getValue()); - } - } - - private static void setChannelOption( - Channel channel, ChannelOption option, Object value) { - try { - @SuppressWarnings("unchecked") - ChannelOption opt = (ChannelOption) option; - if (!channel.config().setOption(opt, value)) { - logger.warn("Unknown channel option '{}' for channel '{}'", option, channel); - } - } catch (Throwable t) { - logger.warn( - "Failed to set channel option '{}' with value '{}' for channel '{}'", option, value, channel, t); - } - } - - private static void setAttributes( - Channel channel, Map.Entry, Object>[] options) { - for (Map.Entry, Object> e: options) { - @SuppressWarnings("unchecked") - AttributeKey key = (AttributeKey) e.getKey(); - channel.attr(key).set(e.getValue()); - } - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2StreamChannelId.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2StreamChannelId.java deleted file mode 100644 index e50038a051..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2StreamChannelId.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.channel.ChannelId; - -/** - * ChannelId implementation which is used by our {@link Http2StreamChannel} implementation. - */ -final class Http2StreamChannelId implements ChannelId { - private static final long serialVersionUID = -6642338822166867585L; - - private final int id; - private final ChannelId parentId; - - Http2StreamChannelId(ChannelId parentId, int id) { - this.parentId = parentId; - this.id = id; - } - - @Override - public String asShortText() { - return parentId.asShortText() + '/' + id; - } - - @Override - public String asLongText() { - return parentId.asLongText() + '/' + id; - } - - @Override - public int compareTo(ChannelId o) { - if (o instanceof Http2StreamChannelId) { - Http2StreamChannelId otherId = (Http2StreamChannelId) o; - int res = parentId.compareTo(otherId.parentId); - if (res == 0) { - return id - otherId.id; - } else { - return res; - } - } - return parentId.compareTo(o); - } - - @Override - public int hashCode() { - return id * 31 + parentId.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof Http2StreamChannelId)) { - return false; - } - Http2StreamChannelId otherId = (Http2StreamChannelId) obj; - return id == otherId.id && parentId.equals(otherId.parentId); - } - - @Override - public String toString() { - return asShortText(); - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2StreamFrame.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2StreamFrame.java deleted file mode 100644 index 9ad448beaf..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2StreamFrame.java +++ /dev/null @@ -1,38 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.util.internal.UnstableApi; - -/** - * A frame whose meaning may apply to a particular stream, instead of the entire connection. It is still - * possible for this frame type to apply to the entire connection. In such cases, the {@link #stream()} must return - * {@code null}. If the frame applies to a stream, the {@link Http2FrameStream#id()} must be greater than zero. - */ -@UnstableApi -public interface Http2StreamFrame extends Http2Frame { - - /** - * Set the {@link Http2FrameStream} object for this frame. - */ - Http2StreamFrame stream(Http2FrameStream stream); - - /** - * Returns the {@link Http2FrameStream} object for this frame, or {@code null} if the frame has yet to be associated - * with a stream. - */ - Http2FrameStream stream(); -} 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 deleted file mode 100644 index 41d434670e..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2StreamFrameToHttpObjectCodec.java +++ /dev/null @@ -1,250 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.EncoderException; -import io.netty.handler.codec.MessageToMessageCodec; -import io.netty.handler.codec.http.DefaultHttpContent; -import io.netty.handler.codec.http.DefaultLastHttpContent; -import io.netty.handler.codec.http.FullHttpMessage; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpMessage; -import io.netty.handler.codec.http.HttpObject; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpScheme; -import io.netty.handler.codec.http.HttpUtil; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http.LastHttpContent; -import io.netty.handler.ssl.SslHandler; -import io.netty.util.Attribute; -import io.netty.util.AttributeKey; -import io.netty.util.internal.UnstableApi; - -import java.util.List; - -/** - * This handler converts from {@link Http2StreamFrame} to {@link HttpObject}, - * and back. It can be used as an adapter in conjunction with {@link - * Http2MultiplexCodec} to make http/2 connections backward-compatible with - * {@link ChannelHandler}s expecting {@link HttpObject} - * - * For simplicity, it converts to chunked encoding unless the entire stream - * is a single header. - */ -@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; - - public Http2StreamFrameToHttpObjectCodec(final boolean isServer, - final boolean validateHeaders) { - this.isServer = isServer; - this.validateHeaders = validateHeaders; - } - - public Http2StreamFrameToHttpObjectCodec(final boolean isServer) { - this(isServer, true); - } - - @Override - public boolean acceptInboundMessage(Object msg) throws Exception { - return msg instanceof Http2HeadersFrame || msg instanceof Http2DataFrame; - } - - @Override - protected void decode(ChannelHandlerContext ctx, Http2StreamFrame frame) throws Exception { - if (frame instanceof Http2HeadersFrame) { - Http2HeadersFrame headersFrame = (Http2HeadersFrame) frame; - Http2Headers headers = headersFrame.headers(); - Http2FrameStream stream = headersFrame.stream(); - int id = stream == null ? 0 : stream.id(); - - final CharSequence status = headers.status(); - - // 100-continue response is a special case where Http2HeadersFrame#isEndStream=false - // but we need to decode it as a FullHttpResponse to play nice with HttpObjectAggregator. - if (null != status && HttpResponseStatus.CONTINUE.codeAsText().contentEquals(status)) { - final FullHttpMessage fullMsg = newFullMessage(id, headers, ctx.alloc()); - ctx.fireChannelRead(fullMsg); - return; - } - - if (headersFrame.isEndStream()) { - if (headers.method() == null && status == null) { - LastHttpContent last = new DefaultLastHttpContent(Unpooled.EMPTY_BUFFER, validateHeaders); - HttpConversionUtil.addHttp2ToHttpHeaders(id, headers, last.trailingHeaders(), - HttpVersion.HTTP_1_1, true, true); - ctx.fireChannelRead(last); - } else { - FullHttpMessage full = newFullMessage(id, headers, ctx.alloc()); - ctx.fireChannelRead(full); - } - } else { - HttpMessage req = newMessage(id, headers); - if (!HttpUtil.isContentLengthSet(req)) { - req.headers().add(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED); - } - ctx.fireChannelRead(req); - } - } else if (frame instanceof Http2DataFrame) { - Http2DataFrame dataFrame = (Http2DataFrame) frame; - if (dataFrame.isEndStream()) { - ctx.fireChannelRead(new DefaultLastHttpContent(dataFrame.content().retain(), validateHeaders)); - } else { - ctx.fireChannelRead(new DefaultHttpContent(dataFrame.content().retain())); - } - } - } - - private void encodeLastContent(LastHttpContent last, List out) { - boolean needFiller = !(last instanceof FullHttpMessage) && last.trailingHeaders().isEmpty(); - if (last.content().isReadable() || needFiller) { - out.add(new DefaultHttp2DataFrame(last.content().retain(), last.trailingHeaders().isEmpty())); - } - if (!last.trailingHeaders().isEmpty()) { - Http2Headers headers = HttpConversionUtil.toHttp2Headers(last.trailingHeaders(), validateHeaders); - out.add(new DefaultHttp2HeadersFrame(headers, true)); - } - } - - /** - * Encode from an {@link HttpObject} to an {@link Http2StreamFrame}. This method will - * be called for each written message that can be handled by this encoder. - * - * NOTE: 100-Continue responses that are NOT {@link FullHttpResponse} will be rejected. - * - * @param ctx the {@link ChannelHandlerContext} which this handler belongs to - * @param obj the {@link HttpObject} message to encode - * @param out the {@link List} into which the encoded msg should be added - * needs to do some kind of aggregation - * @throws Exception is thrown if an error occurs - */ - @Override - protected void encode(ChannelHandlerContext ctx, HttpObject obj, List out) throws Exception { - // 100-continue is typically a FullHttpResponse, but the decoded - // Http2HeadersFrame should not be marked as endStream=true - if (obj instanceof HttpResponse) { - final HttpResponse res = (HttpResponse) obj; - if (res.status().equals(HttpResponseStatus.CONTINUE)) { - if (res instanceof FullHttpResponse) { - final Http2Headers headers = toHttp2Headers(ctx, res); - out.add(new DefaultHttp2HeadersFrame(headers, false)); - return; - } else { - throw new EncoderException( - HttpResponseStatus.CONTINUE + " must be a FullHttpResponse"); - } - } - } - - if (obj instanceof HttpMessage) { - Http2Headers headers = toHttp2Headers(ctx, (HttpMessage) obj); - boolean noMoreFrames = false; - if (obj instanceof FullHttpMessage) { - FullHttpMessage full = (FullHttpMessage) obj; - noMoreFrames = !full.content().isReadable() && full.trailingHeaders().isEmpty(); - } - - out.add(new DefaultHttp2HeadersFrame(headers, noMoreFrames)); - } - - if (obj instanceof LastHttpContent) { - LastHttpContent last = (LastHttpContent) obj; - encodeLastContent(last, out); - } else if (obj instanceof HttpContent) { - HttpContent cont = (HttpContent) obj; - out.add(new DefaultHttp2DataFrame(cont.content().retain(), false)); - } - } - - private Http2Headers toHttp2Headers(final ChannelHandlerContext ctx, final HttpMessage msg) { - if (msg instanceof HttpRequest) { - msg.headers().set( - HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), - connectionScheme(ctx)); - } - - return HttpConversionUtil.toHttp2Headers(msg, validateHeaders); - } - - private HttpMessage newMessage(final int id, - final Http2Headers headers) throws Http2Exception { - return isServer ? - HttpConversionUtil.toHttpRequest(id, headers, validateHeaders) : - HttpConversionUtil.toHttpResponse(id, headers, validateHeaders); - } - - private FullHttpMessage newFullMessage(final int id, - final Http2Headers headers, - final ByteBufAllocator alloc) throws Http2Exception { - return isServer ? - HttpConversionUtil.toFullHttpRequest(id, headers, alloc, validateHeaders) : - HttpConversionUtil.toFullHttpResponse(id, headers, alloc, validateHeaders); - } - - @Override - public void handlerAdded(final ChannelHandlerContext ctx) throws Exception { - super.handlerAdded(ctx); - - // 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. - 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 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/main/java/io/netty/handler/codec/http2/Http2StreamVisitor.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2StreamVisitor.java deleted file mode 100644 index 229cd1b00f..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2StreamVisitor.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.util.internal.UnstableApi; - -/** - * A visitor that allows iteration over a collection of streams. - */ -@UnstableApi -public interface Http2StreamVisitor { - /** - * @return
    - *
  • {@code true} if the visitor wants to continue the loop and handle the entry.
  • - *
  • {@code false} if the visitor wants to stop handling headers and abort the loop.
  • - *
- */ - boolean visit(Http2Stream stream) throws Http2Exception; -} 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 deleted file mode 100644 index 9ce1ecdd63..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2UnknownFrame.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufHolder; -import io.netty.util.internal.UnstableApi; - -@UnstableApi -public interface Http2UnknownFrame extends Http2StreamFrame, ByteBufHolder { - - @Override - Http2FrameStream stream(); - - @Override - Http2UnknownFrame stream(Http2FrameStream stream); - - byte frameType(); - - Http2Flags flags(); - - @Override - Http2UnknownFrame copy(); - - @Override - Http2UnknownFrame duplicate(); - - @Override - Http2UnknownFrame retainedDuplicate(); - - @Override - Http2UnknownFrame replace(ByteBuf content); - - @Override - Http2UnknownFrame retain(); - - @Override - Http2UnknownFrame retain(int increment); - - @Override - Http2UnknownFrame touch(); - - @Override - Http2UnknownFrame touch(Object hint); -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2WindowUpdateFrame.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2WindowUpdateFrame.java deleted file mode 100644 index cdd015eff6..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2WindowUpdateFrame.java +++ /dev/null @@ -1,30 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.util.internal.UnstableApi; - -/** - * HTTP/2 WINDOW_UPDATE frame. - */ -@UnstableApi -public interface Http2WindowUpdateFrame extends Http2StreamFrame { - - /** - * Number of bytes to increment the HTTP/2 stream's or connection's flow control window. - */ - int windowSizeIncrement(); -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/HttpConversionUtil.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/HttpConversionUtil.java deleted file mode 100644 index 620832d1d2..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/HttpConversionUtil.java +++ /dev/null @@ -1,691 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.handler.codec.UnsupportedValueConverter; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.DefaultFullHttpResponse; -import io.netty.handler.codec.http.DefaultHttpRequest; -import io.netty.handler.codec.http.DefaultHttpResponse; -import io.netty.handler.codec.http.FullHttpMessage; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpMessage; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpUtil; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.util.AsciiString; -import io.netty.util.internal.InternalThreadLocalMap; -import io.netty.util.internal.UnstableApi; - -import java.net.URI; -import java.util.Iterator; -import java.util.List; -import java.util.Map.Entry; - -import static io.netty.handler.codec.http.HttpHeaderNames.CONNECTION; -import static io.netty.handler.codec.http.HttpHeaderNames.COOKIE; -import static io.netty.handler.codec.http.HttpHeaderNames.TE; -import static io.netty.handler.codec.http.HttpHeaderValues.TRAILERS; -import static io.netty.handler.codec.http.HttpResponseStatus.parseLine; -import static io.netty.handler.codec.http.HttpScheme.HTTP; -import static io.netty.handler.codec.http.HttpScheme.HTTPS; -import static io.netty.handler.codec.http.HttpUtil.isAsteriskForm; -import static io.netty.handler.codec.http.HttpUtil.isOriginForm; -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.util.AsciiString.EMPTY_STRING; -import static io.netty.util.AsciiString.contentEqualsIgnoreCase; -import static io.netty.util.AsciiString.indexOf; -import static io.netty.util.AsciiString.trim; -import static io.netty.util.ByteProcessor.FIND_COMMA; -import static io.netty.util.ByteProcessor.FIND_SEMI_COLON; -import static io.netty.util.internal.StringUtil.isNullOrEmpty; -import static io.netty.util.internal.StringUtil.length; -import static io.netty.util.internal.StringUtil.unescapeCsvFields; -import static java.util.Objects.requireNonNull; - -/** - * Provides utility methods and constants for the HTTP/2 to HTTP conversion - */ -@UnstableApi -public final class HttpConversionUtil { - /** - * The set of headers that should not be directly copied when converting headers from HTTP to HTTP/2. - */ - private static final CharSequenceMap HTTP_TO_HTTP2_HEADER_BLACKLIST = - new CharSequenceMap<>(); - static { - HTTP_TO_HTTP2_HEADER_BLACKLIST.add(CONNECTION, EMPTY_STRING); - @SuppressWarnings("deprecation") - AsciiString keepAlive = HttpHeaderNames.KEEP_ALIVE; - HTTP_TO_HTTP2_HEADER_BLACKLIST.add(keepAlive, EMPTY_STRING); - @SuppressWarnings("deprecation") - AsciiString proxyConnection = HttpHeaderNames.PROXY_CONNECTION; - HTTP_TO_HTTP2_HEADER_BLACKLIST.add(proxyConnection, EMPTY_STRING); - HTTP_TO_HTTP2_HEADER_BLACKLIST.add(HttpHeaderNames.TRANSFER_ENCODING, EMPTY_STRING); - HTTP_TO_HTTP2_HEADER_BLACKLIST.add(HttpHeaderNames.HOST, EMPTY_STRING); - HTTP_TO_HTTP2_HEADER_BLACKLIST.add(HttpHeaderNames.UPGRADE, EMPTY_STRING); - HTTP_TO_HTTP2_HEADER_BLACKLIST.add(ExtensionHeaderNames.STREAM_ID.text(), EMPTY_STRING); - HTTP_TO_HTTP2_HEADER_BLACKLIST.add(ExtensionHeaderNames.SCHEME.text(), EMPTY_STRING); - HTTP_TO_HTTP2_HEADER_BLACKLIST.add(ExtensionHeaderNames.PATH.text(), EMPTY_STRING); - } - - /** - * This will be the method used for {@link HttpRequest} objects generated out of the HTTP message flow defined in [RFC 7540], Section 8.1 - */ - public static final HttpMethod OUT_OF_MESSAGE_SEQUENCE_METHOD = HttpMethod.OPTIONS; - - /** - * This will be the path used for {@link HttpRequest} objects generated out of the HTTP message flow defined in [RFC 7540], Section 8.1 - */ - public static final String OUT_OF_MESSAGE_SEQUENCE_PATH = ""; - - /** - * This will be the status code used for {@link HttpResponse} objects generated out of the HTTP message flow defined - * in [RFC 7540], Section 8.1 - */ - public static final HttpResponseStatus OUT_OF_MESSAGE_SEQUENCE_RETURN_CODE = HttpResponseStatus.OK; - - /** - * [RFC 7540], 8.1.2.3 states the path must not - * be empty, and instead should be {@code /}. - */ - private static final AsciiString EMPTY_REQUEST_PATH = AsciiString.cached("/"); - - private HttpConversionUtil() { - } - - /** - * Provides the HTTP header extensions used to carry HTTP/2 information in HTTP objects - */ - public enum ExtensionHeaderNames { - /** - * HTTP extension header which will identify the stream id from the HTTP/2 event(s) responsible for - * generating an {@code HttpObject} - *

- * {@code "x-http2-stream-id"} - */ - STREAM_ID("x-http2-stream-id"), - /** - * HTTP extension header which will identify the scheme pseudo header from the HTTP/2 event(s) responsible for - * generating an {@code HttpObject} - *

- * {@code "x-http2-scheme"} - */ - SCHEME("x-http2-scheme"), - /** - * HTTP extension header which will identify the path pseudo header from the HTTP/2 event(s) responsible for - * generating an {@code HttpObject} - *

- * {@code "x-http2-path"} - */ - PATH("x-http2-path"), - /** - * HTTP extension header which will identify the stream id used to create this stream in an HTTP/2 push promise - * frame - *

- * {@code "x-http2-stream-promise-id"} - */ - STREAM_PROMISE_ID("x-http2-stream-promise-id"), - /** - * HTTP extension header which will identify the stream id which this stream is dependent on. This stream will - * be a child node of the stream id associated with this header value. - *

- * {@code "x-http2-stream-dependency-id"} - */ - STREAM_DEPENDENCY_ID("x-http2-stream-dependency-id"), - /** - * HTTP extension header which will identify the weight (if non-default and the priority is not on the default - * stream) of the associated HTTP/2 stream responsible responsible for generating an {@code HttpObject} - *

- * {@code "x-http2-stream-weight"} - */ - STREAM_WEIGHT("x-http2-stream-weight"); - - private final AsciiString text; - - ExtensionHeaderNames(String text) { - this.text = AsciiString.cached(text); - } - - public AsciiString text() { - return text; - } - } - - /** - * Apply HTTP/2 rules while translating status code to {@link HttpResponseStatus} - * - * @param status The status from an HTTP/2 frame - * @return The HTTP/1.x status - * @throws Http2Exception If there is a problem translating from HTTP/2 to HTTP/1.x - */ - public static HttpResponseStatus parseStatus(CharSequence status) throws Http2Exception { - HttpResponseStatus result; - try { - result = parseLine(status); - if (result == HttpResponseStatus.SWITCHING_PROTOCOLS) { - throw connectionError(PROTOCOL_ERROR, "Invalid HTTP/2 status code '%d'", result.code()); - } - } catch (Http2Exception e) { - throw e; - } catch (Throwable t) { - throw connectionError(PROTOCOL_ERROR, t, - "Unrecognized HTTP status code '%s' encountered in translation to HTTP/1.x", status); - } - return result; - } - - /** - * Create a new object to contain the response data - * - * @param streamId The stream associated with the response - * @param http2Headers The initial set of HTTP/2 headers to create the response with - * @param alloc The {@link ByteBufAllocator} to use to generate the content of the message - * @param validateHttpHeaders

    - *
  • {@code true} to validate HTTP headers in the http-codec
  • - *
  • {@code false} not to validate HTTP headers in the http-codec
  • - *
- * @return A new response object which represents headers/data - * @throws Http2Exception see {@link #addHttp2ToHttpHeaders(int, Http2Headers, FullHttpMessage, boolean)} - */ - public static FullHttpResponse toFullHttpResponse(int streamId, Http2Headers http2Headers, ByteBufAllocator alloc, - boolean validateHttpHeaders) throws Http2Exception { - return toFullHttpResponse(streamId, http2Headers, alloc.buffer(), validateHttpHeaders); - } - - /** - * Create a new object to contain the response data - * - * @param streamId The stream associated with the response - * @param http2Headers The initial set of HTTP/2 headers to create the response with - * @param content {@link ByteBuf} content to put in {@link FullHttpResponse} - * @param validateHttpHeaders
    - *
  • {@code true} to validate HTTP headers in the http-codec
  • - *
  • {@code false} not to validate HTTP headers in the http-codec
  • - *
- * @return A new response object which represents headers/data - * @throws Http2Exception see {@link #addHttp2ToHttpHeaders(int, Http2Headers, FullHttpMessage, boolean)} - */ - public static FullHttpResponse toFullHttpResponse(int streamId, Http2Headers http2Headers, ByteBuf content, - boolean validateHttpHeaders) - throws Http2Exception { - HttpResponseStatus status = parseStatus(http2Headers.status()); - // HTTP/2 does not define a way to carry the version or reason phrase that is included in an - // HTTP/1.1 status line. - FullHttpResponse msg = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status, content, - validateHttpHeaders); - try { - addHttp2ToHttpHeaders(streamId, http2Headers, msg, false); - } catch (Http2Exception e) { - msg.release(); - throw e; - } catch (Throwable t) { - msg.release(); - throw streamError(streamId, PROTOCOL_ERROR, t, "HTTP/2 to HTTP/1.x headers conversion error"); - } - return msg; - } - - /** - * Create a new object to contain the request data - * - * @param streamId The stream associated with the request - * @param http2Headers The initial set of HTTP/2 headers to create the request with - * @param alloc The {@link ByteBufAllocator} to use to generate the content of the message - * @param validateHttpHeaders
    - *
  • {@code true} to validate HTTP headers in the http-codec
  • - *
  • {@code false} not to validate HTTP headers in the http-codec
  • - *
- * @return A new request object which represents headers/data - * @throws Http2Exception see {@link #addHttp2ToHttpHeaders(int, Http2Headers, FullHttpMessage, boolean)} - */ - public static FullHttpRequest toFullHttpRequest(int streamId, Http2Headers http2Headers, ByteBufAllocator alloc, - boolean validateHttpHeaders) throws Http2Exception { - return toFullHttpRequest(streamId, http2Headers, alloc.buffer(), validateHttpHeaders); - } - - private static CharSequence extractPath(CharSequence method, Http2Headers headers) { - if (HttpMethod.CONNECT.asciiName().contentEqualsIgnoreCase(method)) { - // See https://tools.ietf.org/html/rfc7231#section-4.3.6 - return requireNonNull(headers.authority(), - "authority header cannot be null in the conversion to HTTP/1.x"); - } else { - return requireNonNull(headers.path(), - "path header cannot be null in conversion to HTTP/1.x"); - } - } - - /** - * Create a new object to contain the request data - * - * @param streamId The stream associated with the request - * @param http2Headers The initial set of HTTP/2 headers to create the request with - * @param content {@link ByteBuf} content to put in {@link FullHttpRequest} - * @param validateHttpHeaders
    - *
  • {@code true} to validate HTTP headers in the http-codec
  • - *
  • {@code false} not to validate HTTP headers in the http-codec
  • - *
- * @return A new request object which represents headers/data - * @throws Http2Exception see {@link #addHttp2ToHttpHeaders(int, Http2Headers, FullHttpMessage, boolean)} - */ - public static FullHttpRequest toFullHttpRequest(int streamId, Http2Headers http2Headers, ByteBuf content, - boolean validateHttpHeaders) throws Http2Exception { - // HTTP/2 does not define a way to carry the version identifier that is included in the HTTP/1.1 request line. - final CharSequence method = requireNonNull(http2Headers.method(), - "method header cannot be null in conversion to HTTP/1.x"); - final CharSequence path = extractPath(method, http2Headers); - FullHttpRequest msg = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.valueOf(method - .toString()), path.toString(), content, validateHttpHeaders); - try { - addHttp2ToHttpHeaders(streamId, http2Headers, msg, false); - } catch (Http2Exception e) { - msg.release(); - throw e; - } catch (Throwable t) { - msg.release(); - throw streamError(streamId, PROTOCOL_ERROR, t, "HTTP/2 to HTTP/1.x headers conversion error"); - } - return msg; - } - - /** - * Create a new object to contain the request data. - * - * @param streamId The stream associated with the request - * @param http2Headers The initial set of HTTP/2 headers to create the request with - * @param validateHttpHeaders
    - *
  • {@code true} to validate HTTP headers in the http-codec
  • - *
  • {@code false} not to validate HTTP headers in the http-codec
  • - *
- * @return A new request object which represents headers for a chunked request - * @throws Http2Exception see {@link #addHttp2ToHttpHeaders(int, Http2Headers, FullHttpMessage, boolean)} - */ - public static HttpRequest toHttpRequest(int streamId, Http2Headers http2Headers, boolean validateHttpHeaders) - throws Http2Exception { - // HTTP/2 does not define a way to carry the version identifier that is included in the HTTP/1.1 request line. - final CharSequence method = requireNonNull(http2Headers.method(), - "method header cannot be null in conversion to HTTP/1.x"); - final CharSequence path = extractPath(method, http2Headers); - HttpRequest msg = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.valueOf(method.toString()), - path.toString(), validateHttpHeaders); - try { - addHttp2ToHttpHeaders(streamId, http2Headers, msg.headers(), msg.protocolVersion(), false, true); - } catch (Http2Exception e) { - throw e; - } catch (Throwable t) { - throw streamError(streamId, PROTOCOL_ERROR, t, "HTTP/2 to HTTP/1.x headers conversion error"); - } - return msg; - } - - /** - * Create a new object to contain the response data. - * - * @param streamId The stream associated with the response - * @param http2Headers The initial set of HTTP/2 headers to create the response with - * @param validateHttpHeaders
    - *
  • {@code true} to validate HTTP headers in the http-codec
  • - *
  • {@code false} not to validate HTTP headers in the http-codec
  • - *
- * @return A new response object which represents headers for a chunked response - * @throws Http2Exception see {@link #addHttp2ToHttpHeaders(int, Http2Headers, - * HttpHeaders, HttpVersion, boolean, boolean)} - */ - public static HttpResponse toHttpResponse(final int streamId, - final Http2Headers http2Headers, - final boolean validateHttpHeaders) throws Http2Exception { - final HttpResponseStatus status = parseStatus(http2Headers.status()); - // HTTP/2 does not define a way to carry the version or reason phrase that is included in an - // HTTP/1.1 status line. - final HttpResponse msg = new DefaultHttpResponse(HttpVersion.HTTP_1_1, status, validateHttpHeaders); - try { - addHttp2ToHttpHeaders(streamId, http2Headers, msg.headers(), msg.protocolVersion(), false, false); - } catch (final Http2Exception e) { - throw e; - } catch (final Throwable t) { - throw streamError(streamId, PROTOCOL_ERROR, t, "HTTP/2 to HTTP/1.x headers conversion error"); - } - return msg; - } - - /** - * Translate and add HTTP/2 headers to HTTP/1.x headers. - * - * @param streamId The stream associated with {@code sourceHeaders}. - * @param inputHeaders The HTTP/2 headers to convert. - * @param destinationMessage The object which will contain the resulting HTTP/1.x headers. - * @param addToTrailer {@code true} to add to trailing headers. {@code false} to add to initial headers. - * @throws Http2Exception If not all HTTP/2 headers can be translated to HTTP/1.x. - * @see #addHttp2ToHttpHeaders(int, Http2Headers, HttpHeaders, HttpVersion, boolean, boolean) - */ - public static void addHttp2ToHttpHeaders(int streamId, Http2Headers inputHeaders, - FullHttpMessage destinationMessage, boolean addToTrailer) throws Http2Exception { - addHttp2ToHttpHeaders(streamId, inputHeaders, - addToTrailer ? destinationMessage.trailingHeaders() : destinationMessage.headers(), - destinationMessage.protocolVersion(), addToTrailer, destinationMessage instanceof HttpRequest); - } - - /** - * Translate and add HTTP/2 headers to HTTP/1.x headers. - * - * @param streamId The stream associated with {@code sourceHeaders}. - * @param inputHeaders The HTTP/2 headers to convert. - * @param outputHeaders The object which will contain the resulting HTTP/1.x headers.. - * @param httpVersion What HTTP/1.x version {@code outputHeaders} should be treated as when doing the conversion. - * @param isTrailer {@code true} if {@code outputHeaders} should be treated as trailing headers. - * {@code false} otherwise. - * @param isRequest {@code true} if the {@code outputHeaders} will be used in a request message. - * {@code false} for response message. - * @throws Http2Exception If not all HTTP/2 headers can be translated to HTTP/1.x. - */ - public static void addHttp2ToHttpHeaders(int streamId, Http2Headers inputHeaders, HttpHeaders outputHeaders, - HttpVersion httpVersion, boolean isTrailer, boolean isRequest) throws Http2Exception { - Http2ToHttpHeaderTranslator translator = new Http2ToHttpHeaderTranslator(streamId, outputHeaders, isRequest); - try { - translator.translateHeaders(inputHeaders); - } catch (Http2Exception ex) { - throw ex; - } catch (Throwable t) { - throw streamError(streamId, PROTOCOL_ERROR, t, "HTTP/2 to HTTP/1.x headers conversion error"); - } - - outputHeaders.remove(HttpHeaderNames.TRANSFER_ENCODING); - outputHeaders.remove(HttpHeaderNames.TRAILER); - if (!isTrailer) { - outputHeaders.setInt(ExtensionHeaderNames.STREAM_ID.text(), streamId); - HttpUtil.setKeepAlive(outputHeaders, httpVersion, true); - } - } - - /** - * Converts the given HTTP/1.x headers into HTTP/2 headers. - * The following headers are only used if they can not be found in from the {@code HOST} header or the - * {@code Request-Line} as defined by rfc7230 - *
    - *
  • {@link ExtensionHeaderNames#SCHEME}
  • - *
- * {@link ExtensionHeaderNames#PATH} is ignored and instead extracted from the {@code Request-Line}. - */ - public static Http2Headers toHttp2Headers(HttpMessage in, boolean validateHeaders) { - HttpHeaders inHeaders = in.headers(); - final Http2Headers out = new DefaultHttp2Headers(validateHeaders, inHeaders.size()); - if (in instanceof HttpRequest) { - HttpRequest request = (HttpRequest) in; - URI requestTargetUri = URI.create(request.uri()); - out.path(toHttp2Path(requestTargetUri)); - out.method(request.method().asciiName()); - setHttp2Scheme(inHeaders, requestTargetUri, out); - - if (!isOriginForm(requestTargetUri) && !isAsteriskForm(requestTargetUri)) { - // Attempt to take from HOST header before taking from the request-line - String host = inHeaders.getAsString(HttpHeaderNames.HOST); - setHttp2Authority(host == null || host.isEmpty() ? requestTargetUri.getAuthority() : host, out); - } - } else if (in instanceof HttpResponse) { - HttpResponse response = (HttpResponse) in; - out.status(response.status().codeAsText()); - } - - // Add the HTTP headers which have not been consumed above - toHttp2Headers(inHeaders, out); - return out; - } - - public static Http2Headers toHttp2Headers(HttpHeaders inHeaders, boolean validateHeaders) { - if (inHeaders.isEmpty()) { - return EmptyHttp2Headers.INSTANCE; - } - - final Http2Headers out = new DefaultHttp2Headers(validateHeaders, inHeaders.size()); - toHttp2Headers(inHeaders, out); - return out; - } - - private static CharSequenceMap toLowercaseMap(Iterator valuesIter, - int arraySizeHint) { - UnsupportedValueConverter valueConverter = UnsupportedValueConverter.instance(); - CharSequenceMap result = new CharSequenceMap<>(true, valueConverter, arraySizeHint); - - while (valuesIter.hasNext()) { - AsciiString lowerCased = AsciiString.of(valuesIter.next()).toLowerCase(); - int index = lowerCased.forEachByte(FIND_COMMA); - if (index != -1) { - int start = 0; - do { - result.add(lowerCased.subSequence(start, index, false).trim(), EMPTY_STRING); - start = index + 1; - } while (start < lowerCased.length() && - (index = lowerCased.forEachByte(start, lowerCased.length() - start, FIND_COMMA)) != -1); - result.add(lowerCased.subSequence(start, lowerCased.length(), false).trim(), EMPTY_STRING); - } else { - result.add(lowerCased.trim(), EMPTY_STRING); - } - } - return result; - } - - /** - * Filter the {@link HttpHeaderNames#TE} header according to the - * special rules in the HTTP/2 RFC. - * @param entry An entry whose name is {@link HttpHeaderNames#TE}. - * @param out the resulting HTTP/2 headers. - */ - private static void toHttp2HeadersFilterTE(Entry entry, - Http2Headers out) { - if (indexOf(entry.getValue(), ',', 0) == -1) { - if (contentEqualsIgnoreCase(trim(entry.getValue()), TRAILERS)) { - out.add(TE, TRAILERS); - } - } else { - List teValues = unescapeCsvFields(entry.getValue()); - for (CharSequence teValue : teValues) { - if (contentEqualsIgnoreCase(trim(teValue), TRAILERS)) { - out.add(TE, TRAILERS); - break; - } - } - } - } - - public static void toHttp2Headers(HttpHeaders inHeaders, Http2Headers out) { - Iterator> iter = inHeaders.iteratorCharSequence(); - // Choose 8 as a default size because it is unlikely we will see more than 4 Connection headers values, but - // still allowing for "enough" space in the map to reduce the chance of hash code collision. - CharSequenceMap connectionBlacklist = - toLowercaseMap(inHeaders.valueCharSequenceIterator(CONNECTION), 8); - while (iter.hasNext()) { - Entry entry = iter.next(); - final AsciiString aName = AsciiString.of(entry.getKey()).toLowerCase(); - if (!HTTP_TO_HTTP2_HEADER_BLACKLIST.contains(aName) && !connectionBlacklist.contains(aName)) { - // https://tools.ietf.org/html/rfc7540#section-8.1.2.2 makes a special exception for TE - if (aName.contentEqualsIgnoreCase(TE)) { - toHttp2HeadersFilterTE(entry, out); - } else if (aName.contentEqualsIgnoreCase(COOKIE)) { - AsciiString value = AsciiString.of(entry.getValue()); - // split up cookies to allow for better compression - // https://tools.ietf.org/html/rfc7540#section-8.1.2.5 - int index = value.forEachByte(FIND_SEMI_COLON); - if (index != -1) { - int start = 0; - do { - out.add(COOKIE, value.subSequence(start, index, false)); - // skip 2 characters "; " (see https://tools.ietf.org/html/rfc6265#section-4.2.1) - start = index + 2; - } while (start < value.length() && - (index = value.forEachByte(start, value.length() - start, FIND_SEMI_COLON)) != -1); - if (start >= value.length()) { - throw new IllegalArgumentException("cookie value is of unexpected format: " + value); - } - out.add(COOKIE, value.subSequence(start, value.length(), false)); - } else { - out.add(COOKIE, value); - } - } else { - out.add(aName, entry.getValue()); - } - } - } - } - - /** - * Generate an HTTP/2 {code :path} from a URI in accordance with - * rfc7230, 5.3. - */ - private static AsciiString toHttp2Path(URI uri) { - StringBuilder pathBuilder = new StringBuilder(length(uri.getRawPath()) + - length(uri.getRawQuery()) + length(uri.getRawFragment()) + 2); - if (!isNullOrEmpty(uri.getRawPath())) { - pathBuilder.append(uri.getRawPath()); - } - if (!isNullOrEmpty(uri.getRawQuery())) { - pathBuilder.append('?'); - pathBuilder.append(uri.getRawQuery()); - } - if (!isNullOrEmpty(uri.getRawFragment())) { - pathBuilder.append('#'); - pathBuilder.append(uri.getRawFragment()); - } - String path = pathBuilder.toString(); - return path.isEmpty() ? EMPTY_REQUEST_PATH : new AsciiString(path); - } - - // package-private for testing only - static void setHttp2Authority(String authority, Http2Headers out) { - // The authority MUST NOT include the deprecated "userinfo" subcomponent - if (authority != null) { - if (authority.isEmpty()) { - out.authority(EMPTY_STRING); - } else { - int start = authority.indexOf('@') + 1; - int length = authority.length() - start; - if (length == 0) { - throw new IllegalArgumentException("authority: " + authority); - } - out.authority(new AsciiString(authority, start, length)); - } - } - } - - private static void setHttp2Scheme(HttpHeaders in, URI uri, Http2Headers out) { - String value = uri.getScheme(); - if (value != null) { - out.scheme(new AsciiString(value)); - return; - } - - // Consume the Scheme extension header if present - CharSequence cValue = in.get(ExtensionHeaderNames.SCHEME.text()); - if (cValue != null) { - out.scheme(AsciiString.of(cValue)); - return; - } - - if (uri.getPort() == HTTPS.port()) { - out.scheme(HTTPS.name()); - } else if (uri.getPort() == HTTP.port()) { - out.scheme(HTTP.name()); - } else { - throw new IllegalArgumentException(":scheme must be specified. " + - "see https://tools.ietf.org/html/rfc7540#section-8.1.2.3"); - } - } - - /** - * Utility which translates HTTP/2 headers to HTTP/1 headers. - */ - private static final class Http2ToHttpHeaderTranslator { - /** - * Translations from HTTP/2 header name to the HTTP/1.x equivalent. - */ - private static final CharSequenceMap - REQUEST_HEADER_TRANSLATIONS = new CharSequenceMap<>(); - private static final CharSequenceMap - RESPONSE_HEADER_TRANSLATIONS = new CharSequenceMap<>(); - static { - RESPONSE_HEADER_TRANSLATIONS.add(Http2Headers.PseudoHeaderName.AUTHORITY.value(), - HttpHeaderNames.HOST); - RESPONSE_HEADER_TRANSLATIONS.add(Http2Headers.PseudoHeaderName.SCHEME.value(), - ExtensionHeaderNames.SCHEME.text()); - REQUEST_HEADER_TRANSLATIONS.add(RESPONSE_HEADER_TRANSLATIONS); - RESPONSE_HEADER_TRANSLATIONS.add(Http2Headers.PseudoHeaderName.PATH.value(), - ExtensionHeaderNames.PATH.text()); - } - - private final int streamId; - private final HttpHeaders output; - private final CharSequenceMap translations; - - /** - * Create a new instance - * - * @param output The HTTP/1.x headers object to store the results of the translation - * @param request if {@code true}, translates headers using the request translation map. Otherwise uses the - * response translation map. - */ - Http2ToHttpHeaderTranslator(int streamId, HttpHeaders output, boolean request) { - this.streamId = streamId; - this.output = output; - translations = request ? REQUEST_HEADER_TRANSLATIONS : RESPONSE_HEADER_TRANSLATIONS; - } - - void translateHeaders(Iterable> inputHeaders) throws Http2Exception { - // lazily created as needed - StringBuilder cookies = null; - - for (Entry entry : inputHeaders) { - final CharSequence name = entry.getKey(); - final CharSequence value = entry.getValue(); - AsciiString translatedName = translations.get(name); - if (translatedName != null) { - output.add(translatedName, AsciiString.of(value)); - } else if (!Http2Headers.PseudoHeaderName.isPseudoHeader(name)) { - // https://tools.ietf.org/html/rfc7540#section-8.1.2.3 - // All headers that start with ':' are only valid in HTTP/2 context - if (name.length() == 0 || name.charAt(0) == ':') { - throw streamError(streamId, PROTOCOL_ERROR, - "Invalid HTTP/2 header '%s' encountered in translation to HTTP/1.x", name); - } - if (COOKIE.equals(name)) { - // combine the cookie values into 1 header entry. - // https://tools.ietf.org/html/rfc7540#section-8.1.2.5 - if (cookies == null) { - cookies = InternalThreadLocalMap.get().stringBuilder(); - } else if (cookies.length() > 0) { - cookies.append("; "); - } - cookies.append(value); - } else { - output.add(name, value); - } - } - } - if (cookies != null) { - output.add(COOKIE, cookies.toString()); - } - } - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/HttpToHttp2ConnectionHandler.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/HttpToHttp2ConnectionHandler.java deleted file mode 100644 index 4ffa0389ac..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/HttpToHttp2ConnectionHandler.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http.EmptyHttpHeaders; -import io.netty.handler.codec.http.FullHttpMessage; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpMessage; -import io.netty.handler.codec.http.HttpScheme; -import io.netty.handler.codec.http.LastHttpContent; -import io.netty.handler.codec.http2.Http2CodecUtil.SimpleChannelPromiseAggregator; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; -import io.netty.util.internal.UnstableApi; - -/** - * Translates HTTP/1.x object writes into HTTP/2 frames. - *

- * See {@link InboundHttp2ToHttpAdapter} to get translation from HTTP/2 frames to HTTP/1.x objects. - */ -@UnstableApi -public class HttpToHttp2ConnectionHandler extends Http2ConnectionHandler { - - private final boolean validateHeaders; - private int currentStreamId; - private HttpScheme httpScheme; - - protected HttpToHttp2ConnectionHandler(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder, - Http2Settings initialSettings, boolean validateHeaders) { - super(decoder, encoder, initialSettings); - this.validateHeaders = validateHeaders; - } - - protected HttpToHttp2ConnectionHandler(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder, - Http2Settings initialSettings, boolean validateHeaders, - boolean decoupleCloseAndGoAway) { - this(decoder, encoder, initialSettings, validateHeaders, decoupleCloseAndGoAway, null); - } - - protected HttpToHttp2ConnectionHandler(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder, - Http2Settings initialSettings, boolean validateHeaders, - boolean decoupleCloseAndGoAway, HttpScheme httpScheme) { - super(decoder, encoder, initialSettings, decoupleCloseAndGoAway); - this.validateHeaders = validateHeaders; - this.httpScheme = httpScheme; - } - - /** - * Get the next stream id either from the {@link HttpHeaders} object or HTTP/2 codec - * - * @param httpHeaders The HTTP/1.x headers object to look for the stream id - * @return The stream id to use with this {@link HttpHeaders} object - * @throws Exception If the {@code httpHeaders} object specifies an invalid stream id - */ - private int getStreamId(HttpHeaders httpHeaders) throws Exception { - return httpHeaders.getInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), - connection().local().incrementAndGetNextStreamId()); - } - - /** - * Handles conversion of {@link HttpMessage} and {@link HttpContent} to HTTP/2 frames. - */ - @Override - public Future write(ChannelHandlerContext ctx, Object msg) { - if (!(msg instanceof HttpMessage || msg instanceof HttpContent)) { - return ctx.write(msg); - } - - boolean release = true; - Promise promise = ctx.newPromise(); - SimpleChannelPromiseAggregator promiseAggregator = - new SimpleChannelPromiseAggregator(promise, ctx.executor()); - try { - Http2ConnectionEncoder encoder = encoder(); - boolean endStream = false; - if (msg instanceof HttpMessage) { - final HttpMessage httpMsg = (HttpMessage) msg; - - // Provide the user the opportunity to specify the streamId - currentStreamId = getStreamId(httpMsg.headers()); - - // Add HttpScheme if it's defined in constructor and header does not contain it. - if (httpScheme != null && - !httpMsg.headers().contains(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text())) { - httpMsg.headers().set(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), httpScheme.name()); - } - - // Convert and write the headers. - Http2Headers http2Headers = HttpConversionUtil.toHttp2Headers(httpMsg, validateHeaders); - endStream = msg instanceof FullHttpMessage && !((FullHttpMessage) msg).content().isReadable(); - writeHeaders(ctx, encoder, currentStreamId, httpMsg.headers(), http2Headers, - endStream).cascadeTo(promiseAggregator.newPromise()); - } - - if (!endStream && msg instanceof HttpContent) { - boolean isLastContent = false; - HttpHeaders trailers = EmptyHttpHeaders.INSTANCE; - Http2Headers http2Trailers = EmptyHttp2Headers.INSTANCE; - if (msg instanceof LastHttpContent) { - isLastContent = true; - - // Convert any trailing headers. - final LastHttpContent lastContent = (LastHttpContent) msg; - trailers = lastContent.trailingHeaders(); - http2Trailers = HttpConversionUtil.toHttp2Headers(trailers, validateHeaders); - } - - // Write the data - final ByteBuf content = ((HttpContent) msg).content(); - endStream = isLastContent && trailers.isEmpty(); - encoder.writeData(ctx, currentStreamId, content, 0, endStream) - .cascadeTo(promiseAggregator.newPromise()); - release = false; - - if (!trailers.isEmpty()) { - // Write trailing headers. - writeHeaders(ctx, encoder, currentStreamId, trailers, http2Trailers, true) - .cascadeTo(promiseAggregator.newPromise()); - } - } - } catch (Throwable t) { - onError(ctx, true, t); - promiseAggregator.setFailure(t); - } finally { - if (release) { - ReferenceCountUtil.release(msg); - } - promiseAggregator.doneAllocatingPromises(); - } - return promise.asFuture(); - } - - private static Future writeHeaders(ChannelHandlerContext ctx, Http2ConnectionEncoder encoder, int streamId, - HttpHeaders headers, Http2Headers http2Headers, boolean endStream) { - int dependencyId = headers.getInt( - HttpConversionUtil.ExtensionHeaderNames.STREAM_DEPENDENCY_ID.text(), 0); - short weight = headers.getShort( - HttpConversionUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), Http2CodecUtil.DEFAULT_PRIORITY_WEIGHT); - return encoder.writeHeaders(ctx, streamId, http2Headers, dependencyId, weight, false, - 0, endStream); - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/HttpToHttp2ConnectionHandlerBuilder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/HttpToHttp2ConnectionHandlerBuilder.java deleted file mode 100644 index 52a71d6d42..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/HttpToHttp2ConnectionHandlerBuilder.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.handler.codec.http.HttpScheme; -import io.netty.handler.codec.http2.Http2HeadersEncoder.SensitivityDetector; -import io.netty.util.internal.UnstableApi; - -/** - * Builder which builds {@link HttpToHttp2ConnectionHandler} objects. - */ -@UnstableApi -public final class HttpToHttp2ConnectionHandlerBuilder extends - AbstractHttp2ConnectionHandlerBuilder { - - private HttpScheme httpScheme; - - @Override - public HttpToHttp2ConnectionHandlerBuilder validateHeaders(boolean validateHeaders) { - return super.validateHeaders(validateHeaders); - } - - @Override - public HttpToHttp2ConnectionHandlerBuilder initialSettings(Http2Settings settings) { - return super.initialSettings(settings); - } - - @Override - public HttpToHttp2ConnectionHandlerBuilder frameListener(Http2FrameListener frameListener) { - return super.frameListener(frameListener); - } - - @Override - public HttpToHttp2ConnectionHandlerBuilder gracefulShutdownTimeoutMillis(long gracefulShutdownTimeoutMillis) { - return super.gracefulShutdownTimeoutMillis(gracefulShutdownTimeoutMillis); - } - - @Override - public HttpToHttp2ConnectionHandlerBuilder server(boolean isServer) { - return super.server(isServer); - } - - @Override - public HttpToHttp2ConnectionHandlerBuilder connection(Http2Connection connection) { - return super.connection(connection); - } - - @Override - public HttpToHttp2ConnectionHandlerBuilder codec(Http2ConnectionDecoder decoder, - Http2ConnectionEncoder encoder) { - return super.codec(decoder, encoder); - } - - @Override - public HttpToHttp2ConnectionHandlerBuilder frameLogger(Http2FrameLogger frameLogger) { - return super.frameLogger(frameLogger); - } - - @Override - public HttpToHttp2ConnectionHandlerBuilder encoderEnforceMaxConcurrentStreams( - boolean encoderEnforceMaxConcurrentStreams) { - return super.encoderEnforceMaxConcurrentStreams(encoderEnforceMaxConcurrentStreams); - } - - @Override - public HttpToHttp2ConnectionHandlerBuilder headerSensitivityDetector( - SensitivityDetector headerSensitivityDetector) { - return super.headerSensitivityDetector(headerSensitivityDetector); - } - - @Override - @Deprecated - public HttpToHttp2ConnectionHandlerBuilder initialHuffmanDecodeCapacity(int initialHuffmanDecodeCapacity) { - return super.initialHuffmanDecodeCapacity(initialHuffmanDecodeCapacity); - } - - @Override - public HttpToHttp2ConnectionHandlerBuilder decoupleCloseAndGoAway(boolean decoupleCloseAndGoAway) { - return super.decoupleCloseAndGoAway(decoupleCloseAndGoAway); - } - - /** - * Add {@code scheme} in {@link Http2Headers} if not already present. - * - * @param httpScheme {@link HttpScheme} type - * @return {@code this}. - */ - public HttpToHttp2ConnectionHandlerBuilder httpScheme(HttpScheme httpScheme) { - this.httpScheme = httpScheme; - return self(); - } - - @Override - public HttpToHttp2ConnectionHandler build() { - return super.build(); - } - - @Override - protected HttpToHttp2ConnectionHandler build(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder, - Http2Settings initialSettings) { - return new HttpToHttp2ConnectionHandler(decoder, encoder, initialSettings, isValidateHeaders(), - decoupleCloseAndGoAway(), httpScheme); - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapter.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapter.java deleted file mode 100644 index 5978dda963..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapter.java +++ /dev/null @@ -1,362 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http.FullHttpMessage; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpStatusClass; -import io.netty.handler.codec.http.HttpUtil; -import io.netty.util.internal.UnstableApi; - -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.http.HttpResponseStatus.OK; -import static java.util.Objects.requireNonNull; -import static io.netty.util.internal.ObjectUtil.checkPositive; - -/** - * This adapter provides just header/data events from the HTTP message flow defined - * in [RFC 7540], Section 8.1. - *

- * See {@link HttpToHttp2ConnectionHandler} to get translation from HTTP/1.x objects to HTTP/2 frames for writes. - */ -@UnstableApi -public class InboundHttp2ToHttpAdapter extends Http2EventAdapter { - private static final ImmediateSendDetector DEFAULT_SEND_DETECTOR = new ImmediateSendDetector() { - @Override - public boolean mustSendImmediately(FullHttpMessage msg) { - if (msg instanceof FullHttpResponse) { - return ((FullHttpResponse) msg).status().codeClass() == HttpStatusClass.INFORMATIONAL; - } - if (msg instanceof FullHttpRequest) { - return msg.headers().contains(HttpHeaderNames.EXPECT); - } - return false; - } - - @Override - public FullHttpMessage copyIfNeeded(ByteBufAllocator allocator, FullHttpMessage msg) { - if (msg instanceof FullHttpRequest) { - FullHttpRequest copy = ((FullHttpRequest) msg).replace(allocator.buffer(0)); - copy.headers().remove(HttpHeaderNames.EXPECT); - return copy; - } - return null; - } - }; - - private final int maxContentLength; - private final ImmediateSendDetector sendDetector; - private final Http2Connection.PropertyKey messageKey; - private final boolean propagateSettings; - protected final Http2Connection connection; - protected final boolean validateHttpHeaders; - - protected InboundHttp2ToHttpAdapter(Http2Connection connection, int maxContentLength, - boolean validateHttpHeaders, boolean propagateSettings) { - - requireNonNull(connection, "connection"); - this.connection = connection; - this.maxContentLength = checkPositive(maxContentLength, "maxContentLength"); - this.validateHttpHeaders = validateHttpHeaders; - this.propagateSettings = propagateSettings; - sendDetector = DEFAULT_SEND_DETECTOR; - messageKey = connection.newKey(); - } - - /** - * The stream is out of scope for the HTTP message flow and will no longer be tracked - * @param stream The stream to remove associated state with - * @param release {@code true} to call release on the value if it is present. {@code false} to not call release. - */ - protected final void removeMessage(Http2Stream stream, boolean release) { - FullHttpMessage msg = stream.removeProperty(messageKey); - if (release && msg != null) { - msg.release(); - } - } - - /** - * Get the {@link FullHttpMessage} associated with {@code stream}. - * @param stream The stream to get the associated state from - * @return The {@link FullHttpMessage} associated with {@code stream}. - */ - protected final FullHttpMessage getMessage(Http2Stream stream) { - return (FullHttpMessage) stream.getProperty(messageKey); - } - - /** - * Make {@code message} be the state associated with {@code stream}. - * @param stream The stream which {@code message} is associated with. - * @param message The message which contains the HTTP semantics. - */ - protected final void putMessage(Http2Stream stream, FullHttpMessage message) { - FullHttpMessage previous = stream.setProperty(messageKey, message); - if (previous != message && previous != null) { - previous.release(); - } - } - - @Override - public void onStreamRemoved(Http2Stream stream) { - removeMessage(stream, true); - } - - /** - * Set final headers and fire a channel read event - * - * @param ctx The context to fire the event on - * @param msg The message to send - * @param release {@code true} to call release on the value if it is present. {@code false} to not call release. - * @param stream the stream of the message which is being fired - */ - protected void fireChannelRead(ChannelHandlerContext ctx, FullHttpMessage msg, boolean release, - Http2Stream stream) { - removeMessage(stream, release); - HttpUtil.setContentLength(msg, msg.content().readableBytes()); - ctx.fireChannelRead(msg); - } - - /** - * Create a new {@link FullHttpMessage} based upon the current connection parameters - * - * @param stream The stream to create a message for - * @param headers The headers associated with {@code stream} - * @param validateHttpHeaders - *

    - *
  • {@code true} to validate HTTP headers in the http-codec
  • - *
  • {@code false} not to validate HTTP headers in the http-codec
  • - *
- * @param alloc The {@link ByteBufAllocator} to use to generate the content of the message - * @throws Http2Exception If there is an error when creating {@link FullHttpMessage} from - * {@link Http2Stream} and {@link Http2Headers} - */ - protected FullHttpMessage newMessage(Http2Stream stream, Http2Headers headers, boolean validateHttpHeaders, - ByteBufAllocator alloc) throws Http2Exception { - return connection.isServer() ? HttpConversionUtil.toFullHttpRequest(stream.id(), headers, alloc, - validateHttpHeaders) : HttpConversionUtil.toFullHttpResponse(stream.id(), headers, alloc, - validateHttpHeaders); - } - - /** - * Provides translation between HTTP/2 and HTTP header objects while ensuring the stream - * is in a valid state for additional headers. - * - * @param ctx The context for which this message has been received. - * Used to send informational header if detected. - * @param stream The stream the {@code headers} apply to - * @param headers The headers to process - * @param endOfStream {@code true} if the {@code stream} has received the end of stream flag - * @param allowAppend - *
    - *
  • {@code true} if headers will be appended if the stream already exists.
  • - *
  • if {@code false} and the stream already exists this method returns {@code null}.
  • - *
- * @param appendToTrailer - *
    - *
  • {@code true} if a message {@code stream} already exists then the headers - * should be added to the trailing headers.
  • - *
  • {@code false} then appends will be done to the initial headers.
  • - *
- * @return The object used to track the stream corresponding to {@code stream}. {@code null} if - * {@code allowAppend} is {@code false} and the stream already exists. - * @throws Http2Exception If the stream id is not in the correct state to process the headers request - */ - protected FullHttpMessage processHeadersBegin(ChannelHandlerContext ctx, Http2Stream stream, Http2Headers headers, - boolean endOfStream, boolean allowAppend, boolean appendToTrailer) - throws Http2Exception { - FullHttpMessage msg = getMessage(stream); - boolean release = true; - if (msg == null) { - msg = newMessage(stream, headers, validateHttpHeaders, ctx.alloc()); - } else if (allowAppend) { - release = false; - HttpConversionUtil.addHttp2ToHttpHeaders(stream.id(), headers, msg, appendToTrailer); - } else { - release = false; - msg = null; - } - - if (sendDetector.mustSendImmediately(msg)) { - // Copy the message (if necessary) before sending. The content is not expected to be copied (or used) in - // this operation but just in case it is used do the copy before sending and the resource may be released - final FullHttpMessage copy = endOfStream ? null : sendDetector.copyIfNeeded(ctx.alloc(), msg); - fireChannelRead(ctx, msg, release, stream); - return copy; - } - - return msg; - } - - /** - * After HTTP/2 headers have been processed by {@link #processHeadersBegin} this method either - * sends the result up the pipeline or retains the message for future processing. - * - * @param ctx The context for which this message has been received - * @param stream The stream the {@code objAccumulator} corresponds to - * @param msg The object which represents all headers/data for corresponding to {@code stream} - * @param endOfStream {@code true} if this is the last event for the stream - */ - private void processHeadersEnd(ChannelHandlerContext ctx, Http2Stream stream, FullHttpMessage msg, - boolean endOfStream) { - if (endOfStream) { - // Release if the msg from the map is different from the object being forwarded up the pipeline. - fireChannelRead(ctx, msg, getMessage(stream) != msg, stream); - } else { - putMessage(stream, msg); - } - } - - @Override - public int onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding, boolean endOfStream) - throws Http2Exception { - Http2Stream stream = connection.stream(streamId); - FullHttpMessage msg = getMessage(stream); - if (msg == null) { - throw connectionError(PROTOCOL_ERROR, "Data Frame received for unknown stream id %d", streamId); - } - - ByteBuf content = msg.content(); - final int dataReadableBytes = data.readableBytes(); - if (content.readableBytes() > maxContentLength - dataReadableBytes) { - throw connectionError(INTERNAL_ERROR, - "Content length exceeded max of %d for stream id %d", maxContentLength, streamId); - } - - content.writeBytes(data, data.readerIndex(), dataReadableBytes); - - if (endOfStream) { - fireChannelRead(ctx, msg, false, stream); - } - - // All bytes have been processed. - return dataReadableBytes + padding; - } - - @Override - public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding, - boolean endOfStream) throws Http2Exception { - Http2Stream stream = connection.stream(streamId); - FullHttpMessage msg = processHeadersBegin(ctx, stream, headers, endOfStream, true, true); - if (msg != null) { - processHeadersEnd(ctx, stream, msg, endOfStream); - } - } - - @Override - public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int streamDependency, - short weight, boolean exclusive, int padding, boolean endOfStream) - throws Http2Exception { - Http2Stream stream = connection.stream(streamId); - FullHttpMessage msg = processHeadersBegin(ctx, stream, headers, endOfStream, true, true); - if (msg != null) { - // Add headers for dependency and weight. - // See https://github.com/netty/netty/issues/5866 - if (streamDependency != Http2CodecUtil.CONNECTION_STREAM_ID) { - msg.headers().setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_DEPENDENCY_ID.text(), - streamDependency); - } - msg.headers().setShort(HttpConversionUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), weight); - - processHeadersEnd(ctx, stream, msg, endOfStream); - } - } - - @Override - public void onRstStreamRead(ChannelHandlerContext ctx, int streamId, long errorCode) throws Http2Exception { - Http2Stream stream = connection.stream(streamId); - FullHttpMessage msg = getMessage(stream); - if (msg != null) { - onRstStreamRead(stream, msg); - } - ctx.fireExceptionCaught(Http2Exception.streamError(streamId, Http2Error.valueOf(errorCode), - "HTTP/2 to HTTP layer caught stream reset")); - } - - @Override - public void onPushPromiseRead(ChannelHandlerContext ctx, int streamId, int promisedStreamId, - Http2Headers headers, int padding) throws Http2Exception { - // A push promise should not be allowed to add headers to an existing stream - Http2Stream promisedStream = connection.stream(promisedStreamId); - if (headers.status() == null) { - // A PUSH_PROMISE frame has no Http response status. - // https://tools.ietf.org/html/rfc7540#section-8.2.1 - // Server push is semantically equivalent to a server responding to a - // request; however, in this case, that request is also sent by the - // server, as a PUSH_PROMISE frame. - headers.status(OK.codeAsText()); - } - FullHttpMessage msg = processHeadersBegin(ctx, promisedStream, headers, false, false, false); - if (msg == null) { - throw connectionError(PROTOCOL_ERROR, "Push Promise Frame received for pre-existing stream id %d", - promisedStreamId); - } - - msg.headers().setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_PROMISE_ID.text(), streamId); - msg.headers().setShort(HttpConversionUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), - Http2CodecUtil.DEFAULT_PRIORITY_WEIGHT); - - processHeadersEnd(ctx, promisedStream, msg, false); - } - - @Override - public void onSettingsRead(ChannelHandlerContext ctx, Http2Settings settings) throws Http2Exception { - if (propagateSettings) { - // Provide an interface for non-listeners to capture settings - ctx.fireChannelRead(settings); - } - } - - /** - * Called if a {@code RST_STREAM} is received but we have some data for that stream. - */ - protected void onRstStreamRead(Http2Stream stream, FullHttpMessage msg) { - removeMessage(stream, true); - } - - /** - * Allows messages to be sent up the pipeline before the next phase in the - * HTTP message flow is detected. - */ - private interface ImmediateSendDetector { - /** - * Determine if the response should be sent immediately, or wait for the end of the stream - * - * @param msg The response to test - * @return {@code true} if the message should be sent immediately - * {@code false) if we should wait for the end of the stream - */ - boolean mustSendImmediately(FullHttpMessage msg); - - /** - * Determine if a copy must be made after an immediate send happens. - *

- * An example of this use case is if a request is received - * with a 'Expect: 100-continue' header. The message will be sent immediately, - * and the data will be queued and sent at the end of the stream. - * - * @param allocator The {@link ByteBufAllocator} that can be used to allocate - * @param msg The message which has just been sent due to {@link #mustSendImmediately(FullHttpMessage)} - * @return A modified copy of the {@code msg} or {@code null} if a copy is not needed. - */ - FullHttpMessage copyIfNeeded(ByteBufAllocator allocator, FullHttpMessage msg); - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapterBuilder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapterBuilder.java deleted file mode 100644 index 4e3691b96f..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapterBuilder.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.util.internal.UnstableApi; - -/** - * Builds an {@link InboundHttp2ToHttpAdapter}. - */ -@UnstableApi -public final class InboundHttp2ToHttpAdapterBuilder - extends AbstractInboundHttp2ToHttpAdapterBuilder { - - /** - * Creates a new {@link InboundHttp2ToHttpAdapter} builder for the specified {@link Http2Connection}. - * - * @param connection the object which will provide connection notification events - * for the current connection - */ - public InboundHttp2ToHttpAdapterBuilder(Http2Connection connection) { - super(connection); - } - - @Override - public InboundHttp2ToHttpAdapterBuilder maxContentLength(int maxContentLength) { - return super.maxContentLength(maxContentLength); - } - - @Override - public InboundHttp2ToHttpAdapterBuilder validateHttpHeaders(boolean validate) { - return super.validateHttpHeaders(validate); - } - - @Override - public InboundHttp2ToHttpAdapterBuilder propagateSettings(boolean propagate) { - return super.propagateSettings(propagate); - } - - @Override - public InboundHttp2ToHttpAdapter build() { - return super.build(); - } - - @Override - protected InboundHttp2ToHttpAdapter build(Http2Connection connection, - int maxContentLength, - boolean validateHttpHeaders, - boolean propagateSettings) throws Exception { - - return new InboundHttp2ToHttpAdapter(connection, maxContentLength, - validateHttpHeaders, propagateSettings); - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/InboundHttpToHttp2Adapter.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/InboundHttpToHttp2Adapter.java deleted file mode 100644 index 81926e4701..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/InboundHttpToHttp2Adapter.java +++ /dev/null @@ -1,81 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http.FullHttpMessage; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpScheme; -import io.netty.util.internal.UnstableApi; - -/** - * Translates HTTP/1.x object reads into HTTP/2 frames. - */ -@UnstableApi -public class InboundHttpToHttp2Adapter implements ChannelHandler { - private final Http2Connection connection; - private final Http2FrameListener listener; - - public InboundHttpToHttp2Adapter(Http2Connection connection, Http2FrameListener listener) { - this.connection = connection; - this.listener = listener; - } - - private static int getStreamId(Http2Connection connection, HttpHeaders httpHeaders) { - return httpHeaders.getInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), - connection.remote().incrementAndGetNextStreamId()); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - if (msg instanceof FullHttpMessage) { - handle(ctx, connection, listener, (FullHttpMessage) msg); - } else { - ctx.fireChannelRead(msg); - } - } - - // note that this may behave strangely when used for the initial upgrade - // message when using h2c, since that message is ineligible for flow - // control, but there is not yet an API for signaling that. - static void handle(ChannelHandlerContext ctx, Http2Connection connection, - Http2FrameListener listener, FullHttpMessage message) throws Http2Exception { - try { - int streamId = getStreamId(connection, message.headers()); - Http2Stream stream = connection.stream(streamId); - if (stream == null) { - stream = connection.remote().createStream(streamId, false); - } - message.headers().set(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), HttpScheme.HTTP.name()); - Http2Headers messageHeaders = HttpConversionUtil.toHttp2Headers(message, true); - boolean hasContent = message.content().isReadable(); - boolean hasTrailers = !message.trailingHeaders().isEmpty(); - listener.onHeadersRead( - ctx, streamId, messageHeaders, 0, !(hasContent || hasTrailers)); - if (hasContent) { - listener.onDataRead(ctx, streamId, message.content(), 0, !hasTrailers); - } - if (hasTrailers) { - Http2Headers headers = HttpConversionUtil.toHttp2Headers(message.trailingHeaders(), true); - listener.onHeadersRead(ctx, streamId, headers, 0, true); - } - stream.closeRemoteSide(); - } finally { - message.release(); - } - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/MaxCapacityQueue.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/MaxCapacityQueue.java deleted file mode 100644 index 90b1d0a3c7..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/MaxCapacityQueue.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import java.util.Collection; -import java.util.Iterator; -import java.util.Queue; - -final class MaxCapacityQueue implements Queue { - private final Queue queue; - private final int maxCapacity; - - MaxCapacityQueue(Queue queue, int maxCapacity) { - this.queue = queue; - this.maxCapacity = maxCapacity; - } - - @Override - public boolean add(E element) { - if (offer(element)) { - return true; - } - throw new IllegalStateException(); - } - - @Override - public boolean offer(E element) { - if (maxCapacity <= queue.size()) { - return false; - } - return queue.offer(element); - } - - @Override - public E remove() { - return queue.remove(); - } - - @Override - public E poll() { - return queue.poll(); - } - - @Override - public E element() { - return queue.element(); - } - - @Override - public E peek() { - return queue.peek(); - } - - @Override - public int size() { - return queue.size(); - } - - @Override - public boolean isEmpty() { - return queue.isEmpty(); - } - - @Override - public boolean contains(Object o) { - return queue.contains(o); - } - - @Override - public Iterator iterator() { - return queue.iterator(); - } - - @Override - public Object[] toArray() { - return queue.toArray(); - } - - @Override - public T[] toArray(T[] a) { - return queue.toArray(a); - } - - @Override - public boolean remove(Object o) { - return queue.remove(o); - } - - @Override - public boolean containsAll(Collection c) { - return queue.containsAll(c); - } - - @Override - public boolean addAll(Collection c) { - if (maxCapacity >= size() + c.size()) { - return queue.addAll(c); - } - throw new IllegalStateException(); - } - - @Override - public boolean removeAll(Collection c) { - return queue.removeAll(c); - } - - @Override - public boolean retainAll(Collection c) { - return queue.retainAll(c); - } - - @Override - public void clear() { - queue.clear(); - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/ReadOnlyHttp2Headers.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/ReadOnlyHttp2Headers.java deleted file mode 100644 index 798b18529b..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/ReadOnlyHttp2Headers.java +++ /dev/null @@ -1,892 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.handler.codec.Headers; -import io.netty.util.AsciiString; -import io.netty.util.HashingStrategy; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Set; - -import static io.netty.handler.codec.CharSequenceValueConverter.*; -import static io.netty.handler.codec.http2.DefaultHttp2Headers.*; -import static io.netty.util.AsciiString.*; -import static io.netty.util.internal.EmptyArrays.*; -import static io.netty.util.internal.ObjectUtil.checkNotNullArrayParam; - -/** - * A variant of {@link Http2Headers} which only supports read-only methods. - *

- * Any array passed to this class may be used directly in the underlying data structures of this class. If these - * arrays may be modified it is the caller's responsibility to supply this class with a copy of the array. - *

- * This may be a good alternative to {@link DefaultHttp2Headers} if your have a fixed set of headers which will not - * change. - */ -public final class ReadOnlyHttp2Headers implements Http2Headers { - private static final byte PSEUDO_HEADER_TOKEN = (byte) ':'; - private final AsciiString[] pseudoHeaders; - private final AsciiString[] otherHeaders; - - /** - * Used to create read only object designed to represent trailers. - *

- * If this is used for a purpose other than trailers you may violate the header serialization ordering defined by - * RFC 7540, 8.1.2.1. - * @param validateHeaders {@code true} will run validation on each header name/value pair to ensure protocol - * compliance. - * @param otherHeaders A an array of key:value pairs. Must not contain any - * pseudo headers - * or {@code null} names/values. - * A copy will NOT be made of this array. If the contents of this array - * may be modified externally you are responsible for passing in a copy. - * @return A read only representation of the headers. - */ - public static ReadOnlyHttp2Headers trailers(boolean validateHeaders, AsciiString... otherHeaders) { - return new ReadOnlyHttp2Headers(validateHeaders, EMPTY_ASCII_STRINGS, otherHeaders); - } - - /** - * Create a new read only representation of headers used by clients. - * @param validateHeaders {@code true} will run validation on each header name/value pair to ensure protocol - * compliance. - * @param method The value for {@link PseudoHeaderName#METHOD}. - * @param path The value for {@link PseudoHeaderName#PATH}. - * @param scheme The value for {@link PseudoHeaderName#SCHEME}. - * @param authority The value for {@link PseudoHeaderName#AUTHORITY}. - * @param otherHeaders A an array of key:value pairs. Must not contain any - * pseudo headers - * or {@code null} names/values. - * A copy will NOT be made of this array. If the contents of this array - * may be modified externally you are responsible for passing in a copy. - * @return a new read only representation of headers used by clients. - */ - public static ReadOnlyHttp2Headers clientHeaders(boolean validateHeaders, - AsciiString method, AsciiString path, - AsciiString scheme, AsciiString authority, - AsciiString... otherHeaders) { - return new ReadOnlyHttp2Headers(validateHeaders, - new AsciiString[] { - PseudoHeaderName.METHOD.value(), method, PseudoHeaderName.PATH.value(), path, - PseudoHeaderName.SCHEME.value(), scheme, PseudoHeaderName.AUTHORITY.value(), authority - }, - otherHeaders); - } - - /** - * Create a new read only representation of headers used by servers. - * @param validateHeaders {@code true} will run validation on each header name/value pair to ensure protocol - * compliance. - * @param status The value for {@link PseudoHeaderName#STATUS}. - * @param otherHeaders A an array of key:value pairs. Must not contain any - * pseudo headers - * or {@code null} names/values. - * A copy will NOT be made of this array. If the contents of this array - * may be modified externally you are responsible for passing in a copy. - * @return a new read only representation of headers used by servers. - */ - public static ReadOnlyHttp2Headers serverHeaders(boolean validateHeaders, - AsciiString status, - AsciiString... otherHeaders) { - return new ReadOnlyHttp2Headers(validateHeaders, - new AsciiString[] { PseudoHeaderName.STATUS.value(), status }, - otherHeaders); - } - - private ReadOnlyHttp2Headers(boolean validateHeaders, AsciiString[] pseudoHeaders, AsciiString... otherHeaders) { - assert (pseudoHeaders.length & 1) == 0; // pseudoHeaders are only set internally so assert should be enough. - if ((otherHeaders.length & 1) != 0) { - throw newInvalidArraySizeException(); - } - if (validateHeaders) { - validateHeaders(pseudoHeaders, otherHeaders); - } - this.pseudoHeaders = pseudoHeaders; - this.otherHeaders = otherHeaders; - } - - private static IllegalArgumentException newInvalidArraySizeException() { - return new IllegalArgumentException("pseudoHeaders and otherHeaders must be arrays of [name, value] pairs"); - } - - private static void validateHeaders(AsciiString[] pseudoHeaders, AsciiString... otherHeaders) { - // We are only validating values... so start at 1 and go until end. - for (int i = 1; i < pseudoHeaders.length; i += 2) { - // pseudoHeaders names are only set internally so they are assumed to be valid. - checkNotNullArrayParam(pseudoHeaders[i], i, "pseudoHeaders"); - } - - boolean seenNonPseudoHeader = false; - final int otherHeadersEnd = otherHeaders.length - 1; - for (int i = 0; i < otherHeadersEnd; i += 2) { - AsciiString name = otherHeaders[i]; - HTTP2_NAME_VALIDATOR.validateName(name); - if (!seenNonPseudoHeader && !name.isEmpty() && name.byteAt(0) != PSEUDO_HEADER_TOKEN) { - seenNonPseudoHeader = true; - } else if (seenNonPseudoHeader && !name.isEmpty() && name.byteAt(0) == PSEUDO_HEADER_TOKEN) { - throw new IllegalArgumentException( - "otherHeaders name at index " + i + " is a pseudo header that appears after non-pseudo headers."); - } - checkNotNullArrayParam(otherHeaders[i + 1], i + 1, "otherHeaders"); - } - } - - private AsciiString get0(CharSequence name) { - final int nameHash = AsciiString.hashCode(name); - - final int pseudoHeadersEnd = pseudoHeaders.length - 1; - for (int i = 0; i < pseudoHeadersEnd; i += 2) { - AsciiString roName = pseudoHeaders[i]; - if (roName.hashCode() == nameHash && roName.contentEqualsIgnoreCase(name)) { - return pseudoHeaders[i + 1]; - } - } - - final int otherHeadersEnd = otherHeaders.length - 1; - for (int i = 0; i < otherHeadersEnd; i += 2) { - AsciiString roName = otherHeaders[i]; - if (roName.hashCode() == nameHash && roName.contentEqualsIgnoreCase(name)) { - return otherHeaders[i + 1]; - } - } - return null; - } - - @Override - public CharSequence get(CharSequence name) { - return get0(name); - } - - @Override - public CharSequence get(CharSequence name, CharSequence defaultValue) { - CharSequence value = get(name); - return value != null ? value : defaultValue; - } - - @Override - public CharSequence getAndRemove(CharSequence name) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public CharSequence getAndRemove(CharSequence name, CharSequence defaultValue) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public List getAll(CharSequence name) { - final int nameHash = AsciiString.hashCode(name); - List values = new ArrayList<>(); - - final int pseudoHeadersEnd = pseudoHeaders.length - 1; - for (int i = 0; i < pseudoHeadersEnd; i += 2) { - AsciiString roName = pseudoHeaders[i]; - if (roName.hashCode() == nameHash && roName.contentEqualsIgnoreCase(name)) { - values.add(pseudoHeaders[i + 1]); - } - } - - final int otherHeadersEnd = otherHeaders.length - 1; - for (int i = 0; i < otherHeadersEnd; i += 2) { - AsciiString roName = otherHeaders[i]; - if (roName.hashCode() == nameHash && roName.contentEqualsIgnoreCase(name)) { - values.add(otherHeaders[i + 1]); - } - } - - return values; - } - - @Override - public List getAllAndRemove(CharSequence name) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Boolean getBoolean(CharSequence name) { - AsciiString value = get0(name); - return value != null ? INSTANCE.convertToBoolean(value) : null; - } - - @Override - public boolean getBoolean(CharSequence name, boolean defaultValue) { - Boolean value = getBoolean(name); - return value != null ? value : defaultValue; - } - - @Override - public Byte getByte(CharSequence name) { - AsciiString value = get0(name); - return value != null ? INSTANCE.convertToByte(value) : null; - } - - @Override - public byte getByte(CharSequence name, byte defaultValue) { - Byte value = getByte(name); - return value != null ? value : defaultValue; - } - - @Override - public Character getChar(CharSequence name) { - AsciiString value = get0(name); - return value != null ? INSTANCE.convertToChar(value) : null; - } - - @Override - public char getChar(CharSequence name, char defaultValue) { - Character value = getChar(name); - return value != null ? value : defaultValue; - } - - @Override - public Short getShort(CharSequence name) { - AsciiString value = get0(name); - return value != null ? INSTANCE.convertToShort(value) : null; - } - - @Override - public short getShort(CharSequence name, short defaultValue) { - Short value = getShort(name); - return value != null ? value : defaultValue; - } - - @Override - public Integer getInt(CharSequence name) { - AsciiString value = get0(name); - return value != null ? INSTANCE.convertToInt(value) : null; - } - - @Override - public int getInt(CharSequence name, int defaultValue) { - Integer value = getInt(name); - return value != null ? value : defaultValue; - } - - @Override - public Long getLong(CharSequence name) { - AsciiString value = get0(name); - return value != null ? INSTANCE.convertToLong(value) : null; - } - - @Override - public long getLong(CharSequence name, long defaultValue) { - Long value = getLong(name); - return value != null ? value : defaultValue; - } - - @Override - public Float getFloat(CharSequence name) { - AsciiString value = get0(name); - return value != null ? INSTANCE.convertToFloat(value) : null; - } - - @Override - public float getFloat(CharSequence name, float defaultValue) { - Float value = getFloat(name); - return value != null ? value : defaultValue; - } - - @Override - public Double getDouble(CharSequence name) { - AsciiString value = get0(name); - return value != null ? INSTANCE.convertToDouble(value) : null; - } - - @Override - public double getDouble(CharSequence name, double defaultValue) { - Double value = getDouble(name); - return value != null ? value : defaultValue; - } - - @Override - public Long getTimeMillis(CharSequence name) { - AsciiString value = get0(name); - return value != null ? INSTANCE.convertToTimeMillis(value) : null; - } - - @Override - public long getTimeMillis(CharSequence name, long defaultValue) { - Long value = getTimeMillis(name); - return value != null ? value : defaultValue; - } - - @Override - public Boolean getBooleanAndRemove(CharSequence name) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public boolean getBooleanAndRemove(CharSequence name, boolean defaultValue) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Byte getByteAndRemove(CharSequence name) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public byte getByteAndRemove(CharSequence name, byte defaultValue) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Character getCharAndRemove(CharSequence name) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public char getCharAndRemove(CharSequence name, char defaultValue) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Short getShortAndRemove(CharSequence name) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public short getShortAndRemove(CharSequence name, short defaultValue) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Integer getIntAndRemove(CharSequence name) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public int getIntAndRemove(CharSequence name, int defaultValue) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Long getLongAndRemove(CharSequence name) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public long getLongAndRemove(CharSequence name, long defaultValue) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Float getFloatAndRemove(CharSequence name) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public float getFloatAndRemove(CharSequence name, float defaultValue) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Double getDoubleAndRemove(CharSequence name) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public double getDoubleAndRemove(CharSequence name, double defaultValue) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Long getTimeMillisAndRemove(CharSequence name) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public long getTimeMillisAndRemove(CharSequence name, long defaultValue) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public boolean contains(CharSequence name) { - return get(name) != null; - } - - @Override - public boolean contains(CharSequence name, CharSequence value) { - return contains(name, value, false); - } - - @Override - public boolean containsObject(CharSequence name, Object value) { - if (value instanceof CharSequence) { - return contains(name, (CharSequence) value); - } - return contains(name, value.toString()); - } - - @Override - public boolean containsBoolean(CharSequence name, boolean value) { - return contains(name, String.valueOf(value)); - } - - @Override - public boolean containsByte(CharSequence name, byte value) { - return contains(name, String.valueOf(value)); - } - - @Override - public boolean containsChar(CharSequence name, char value) { - return contains(name, String.valueOf(value)); - } - - @Override - public boolean containsShort(CharSequence name, short value) { - return contains(name, String.valueOf(value)); - } - - @Override - public boolean containsInt(CharSequence name, int value) { - return contains(name, String.valueOf(value)); - } - - @Override - public boolean containsLong(CharSequence name, long value) { - return contains(name, String.valueOf(value)); - } - - @Override - public boolean containsFloat(CharSequence name, float value) { - return false; - } - - @Override - public boolean containsDouble(CharSequence name, double value) { - return contains(name, String.valueOf(value)); - } - - @Override - public boolean containsTimeMillis(CharSequence name, long value) { - return contains(name, String.valueOf(value)); - } - - @Override - public int size() { - return pseudoHeaders.length + otherHeaders.length >>> 1; - } - - @Override - public boolean isEmpty() { - return pseudoHeaders.length == 0 && otherHeaders.length == 0; - } - - @Override - public Set names() { - if (isEmpty()) { - return Collections.emptySet(); - } - Set names = new LinkedHashSet<>(size()); - final int pseudoHeadersEnd = pseudoHeaders.length - 1; - for (int i = 0; i < pseudoHeadersEnd; i += 2) { - names.add(pseudoHeaders[i]); - } - - final int otherHeadersEnd = otherHeaders.length - 1; - for (int i = 0; i < otherHeadersEnd; i += 2) { - names.add(otherHeaders[i]); - } - return names; - } - - @Override - public Http2Headers add(CharSequence name, CharSequence value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Http2Headers add(CharSequence name, Iterable values) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Http2Headers add(CharSequence name, CharSequence... values) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Http2Headers addObject(CharSequence name, Object value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Http2Headers addObject(CharSequence name, Iterable values) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Http2Headers addObject(CharSequence name, Object... values) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Http2Headers addBoolean(CharSequence name, boolean value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Http2Headers addByte(CharSequence name, byte value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Http2Headers addChar(CharSequence name, char value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Http2Headers addShort(CharSequence name, short value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Http2Headers addInt(CharSequence name, int value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Http2Headers addLong(CharSequence name, long value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Http2Headers addFloat(CharSequence name, float value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Http2Headers addDouble(CharSequence name, double value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Http2Headers addTimeMillis(CharSequence name, long value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Http2Headers add(Headers headers) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Http2Headers set(CharSequence name, CharSequence value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Http2Headers set(CharSequence name, Iterable values) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Http2Headers set(CharSequence name, CharSequence... values) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Http2Headers setObject(CharSequence name, Object value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Http2Headers setObject(CharSequence name, Iterable values) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Http2Headers setObject(CharSequence name, Object... values) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Http2Headers setBoolean(CharSequence name, boolean value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Http2Headers setByte(CharSequence name, byte value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Http2Headers setChar(CharSequence name, char value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Http2Headers setShort(CharSequence name, short value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Http2Headers setInt(CharSequence name, int value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Http2Headers setLong(CharSequence name, long value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Http2Headers setFloat(CharSequence name, float value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Http2Headers setDouble(CharSequence name, double value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Http2Headers setTimeMillis(CharSequence name, long value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Http2Headers set(Headers headers) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Http2Headers setAll(Headers headers) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public boolean remove(CharSequence name) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Http2Headers clear() { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Iterator> iterator() { - return new ReadOnlyIterator(); - } - - @Override - public Iterator valueIterator(CharSequence name) { - return new ReadOnlyValueIterator(name); - } - - @Override - public Http2Headers method(CharSequence value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Http2Headers scheme(CharSequence value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Http2Headers authority(CharSequence value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Http2Headers path(CharSequence value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public Http2Headers status(CharSequence value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public CharSequence method() { - return get(PseudoHeaderName.METHOD.value()); - } - - @Override - public CharSequence scheme() { - return get(PseudoHeaderName.SCHEME.value()); - } - - @Override - public CharSequence authority() { - return get(PseudoHeaderName.AUTHORITY.value()); - } - - @Override - public CharSequence path() { - return get(PseudoHeaderName.PATH.value()); - } - - @Override - public CharSequence status() { - return get(PseudoHeaderName.STATUS.value()); - } - - @Override - public boolean contains(CharSequence name, CharSequence value, boolean caseInsensitive) { - final int nameHash = AsciiString.hashCode(name); - final HashingStrategy strategy = - caseInsensitive ? CASE_INSENSITIVE_HASHER : CASE_SENSITIVE_HASHER; - final int valueHash = strategy.hashCode(value); - - return contains(name, nameHash, value, valueHash, strategy, otherHeaders) - || contains(name, nameHash, value, valueHash, strategy, pseudoHeaders); - } - - private static boolean contains(CharSequence name, int nameHash, CharSequence value, int valueHash, - HashingStrategy hashingStrategy, AsciiString[] headers) { - final int headersEnd = headers.length - 1; - for (int i = 0; i < headersEnd; i += 2) { - AsciiString roName = headers[i]; - AsciiString roValue = headers[i + 1]; - if (roName.hashCode() == nameHash && roValue.hashCode() == valueHash && - roName.contentEqualsIgnoreCase(name) && hashingStrategy.equals(roValue, value)) { - return true; - } - } - return false; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(getClass().getSimpleName()).append('['); - String separator = ""; - for (Map.Entry entry : this) { - builder.append(separator); - builder.append(entry.getKey()).append(": ").append(entry.getValue()); - separator = ", "; - } - return builder.append(']').toString(); - } - - private final class ReadOnlyValueIterator implements Iterator { - private int i; - private final int nameHash; - private final CharSequence name; - private AsciiString[] current = pseudoHeaders.length != 0 ? pseudoHeaders : otherHeaders; - private AsciiString next; - - ReadOnlyValueIterator(CharSequence name) { - nameHash = AsciiString.hashCode(name); - this.name = name; - calculateNext(); - } - - @Override - public boolean hasNext() { - return next != null; - } - - @Override - public CharSequence next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - CharSequence current = next; - calculateNext(); - return current; - } - - @Override - public void remove() { - throw new UnsupportedOperationException("read only"); - } - - private void calculateNext() { - for (; i < current.length; i += 2) { - AsciiString roName = current[i]; - if (roName.hashCode() == nameHash && roName.contentEqualsIgnoreCase(name)) { - if (i + 1 < current.length) { - next = current[i + 1]; - i += 2; - } - return; - } - } - if (current == pseudoHeaders) { - i = 0; - current = otherHeaders; - calculateNext(); - } else { - next = null; - } - } - } - - private final class ReadOnlyIterator implements Map.Entry, - Iterator> { - private int i; - private AsciiString[] current = pseudoHeaders.length != 0 ? pseudoHeaders : otherHeaders; - private AsciiString key; - private AsciiString value; - - @Override - public boolean hasNext() { - return i != current.length; - } - - @Override - public Map.Entry next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - key = current[i]; - value = current[i + 1]; - i += 2; - if (i == current.length && current == pseudoHeaders) { - current = otherHeaders; - i = 0; - } - return this; - } - - @Override - public CharSequence getKey() { - return key; - } - - @Override - public CharSequence getValue() { - return value; - } - - @Override - public CharSequence setValue(CharSequence value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public void remove() { - throw new UnsupportedOperationException("read only"); - } - - @Override - public String toString() { - return key.toString() + '=' + value.toString(); - } - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/StreamBufferingEncoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/StreamBufferingEncoder.java deleted file mode 100644 index 43feb27a75..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/StreamBufferingEncoder.java +++ /dev/null @@ -1,384 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; -import io.netty.util.internal.UnstableApi; - -import java.util.ArrayDeque; -import java.util.Iterator; -import java.util.Map; -import java.util.Queue; -import java.util.TreeMap; - -import static io.netty.handler.codec.http2.Http2CodecUtil.SMALLEST_MAX_CONCURRENT_STREAMS; -import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR; -import static io.netty.handler.codec.http2.Http2Exception.connectionError; - -/** - * Implementation of a {@link Http2ConnectionEncoder} that dispatches all method call to another - * {@link Http2ConnectionEncoder}, until {@code SETTINGS_MAX_CONCURRENT_STREAMS} is reached. - *

- *

When this limit is hit, instead of rejecting any new streams this implementation buffers newly - * created streams and their corresponding frames. Once an active stream gets closed or the maximum - * number of concurrent streams is increased, this encoder will automatically try to empty its - * buffer and create as many new streams as possible. - *

- *

- * If a {@code GOAWAY} frame is received from the remote endpoint, all buffered writes for streams - * with an ID less than the specified {@code lastStreamId} will immediately fail with a - * {@link Http2GoAwayException}. - *

- *

- * If the channel/encoder gets closed, all new and buffered writes will immediately fail with a - * {@link Http2ChannelClosedException}. - *

- *

This implementation makes the buffering mostly transparent and is expected to be used as a - * drop-in decorator of {@link DefaultHttp2ConnectionEncoder}. - *

- */ -@UnstableApi -public class StreamBufferingEncoder extends DecoratingHttp2ConnectionEncoder { - - /** - * Thrown if buffered streams are terminated due to this encoder being closed. - */ - public static final class Http2ChannelClosedException extends Http2Exception { - private static final long serialVersionUID = 4768543442094476971L; - - public Http2ChannelClosedException() { - super(Http2Error.REFUSED_STREAM, "Connection closed"); - } - } - - private static final class GoAwayDetail { - private final int lastStreamId; - private final long errorCode; - private final byte[] debugData; - - GoAwayDetail(int lastStreamId, long errorCode, byte[] debugData) { - this.lastStreamId = lastStreamId; - this.errorCode = errorCode; - this.debugData = debugData.clone(); - } - } - - /** - * Thrown by {@link StreamBufferingEncoder} if buffered streams are terminated due to - * receipt of a {@code GOAWAY}. - */ - public static final class Http2GoAwayException extends Http2Exception { - private static final long serialVersionUID = 1326785622777291198L; - private final GoAwayDetail goAwayDetail; - - public Http2GoAwayException(int lastStreamId, long errorCode, byte[] debugData) { - this(new GoAwayDetail(lastStreamId, errorCode, debugData)); - } - - Http2GoAwayException(GoAwayDetail goAwayDetail) { - super(Http2Error.STREAM_CLOSED); - this.goAwayDetail = goAwayDetail; - } - - public int lastStreamId() { - return goAwayDetail.lastStreamId; - } - - public long errorCode() { - return goAwayDetail.errorCode; - } - - public byte[] debugData() { - return goAwayDetail.debugData.clone(); - } - } - - /** - * Buffer for any streams and corresponding frames that could not be created due to the maximum - * concurrent stream limit being hit. - */ - private final TreeMap pendingStreams = new TreeMap<>(); - private int maxConcurrentStreams; - private boolean closed; - private GoAwayDetail goAwayDetail; - - public StreamBufferingEncoder(Http2ConnectionEncoder delegate) { - this(delegate, SMALLEST_MAX_CONCURRENT_STREAMS); - } - - public StreamBufferingEncoder(Http2ConnectionEncoder delegate, int initialMaxConcurrentStreams) { - super(delegate); - maxConcurrentStreams = initialMaxConcurrentStreams; - connection().addListener(new Http2ConnectionAdapter() { - - @Override - public void onGoAwayReceived(int lastStreamId, long errorCode, ByteBuf debugData) { - goAwayDetail = new GoAwayDetail( - // Using getBytes(..., false) is safe here as GoAwayDetail(...) will clone the byte[]. - lastStreamId, errorCode, - ByteBufUtil.getBytes(debugData, debugData.readerIndex(), debugData.readableBytes(), false)); - cancelGoAwayStreams(goAwayDetail); - } - - @Override - public void onStreamClosed(Http2Stream stream) { - tryCreatePendingStreams(); - } - }); - } - - /** - * Indicates the number of streams that are currently buffered, awaiting creation. - */ - public int numBufferedStreams() { - return pendingStreams.size(); - } - - @Override - public Future writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, - int padding, boolean endStream) { - return writeHeaders(ctx, streamId, headers, 0, Http2CodecUtil.DEFAULT_PRIORITY_WEIGHT, - false, padding, endStream); - } - - @Override - public Future writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, - int streamDependency, short weight, boolean exclusive, - int padding, boolean endOfStream) { - if (closed) { - return ctx.newFailedFuture(new Http2ChannelClosedException()); - } - if (isExistingStream(streamId) || canCreateStream()) { - return super.writeHeaders(ctx, streamId, headers, streamDependency, weight, - exclusive, padding, endOfStream); - } - if (goAwayDetail != null) { - return ctx.newFailedFuture(new Http2GoAwayException(goAwayDetail)); - } - PendingStream pendingStream = pendingStreams.get(streamId); - if (pendingStream == null) { - pendingStream = new PendingStream(ctx, streamId); - pendingStreams.put(streamId, pendingStream); - } - Promise promise = ctx.newPromise(); - - pendingStream.frames.add(new HeadersFrame(headers, streamDependency, weight, exclusive, - padding, endOfStream, promise)); - return promise.asFuture(); - } - - @Override - public Future writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode) { - if (isExistingStream(streamId)) { - return super.writeRstStream(ctx, streamId, errorCode); - } - // Since the delegate doesn't know about any buffered streams we have to handle cancellation - // of the promises and releasing of the ByteBufs here. - PendingStream stream = pendingStreams.remove(streamId); - if (stream != null) { - // Sending a RST_STREAM to a buffered stream will succeed the promise of all frames - // associated with the stream, as sending a RST_STREAM means that someone "doesn't care" - // about the stream anymore and thus there is not point in failing the promises and invoking - // error handling routines. - stream.close(null); - return ctx.newSucceededFuture(); - } else { - return ctx.newFailedFuture(connectionError(PROTOCOL_ERROR, "Stream does not exist %d", streamId)); - } - } - - @Override - public Future writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data, - int padding, boolean endOfStream) { - if (isExistingStream(streamId)) { - return super.writeData(ctx, streamId, data, padding, endOfStream); - } - PendingStream pendingStream = pendingStreams.get(streamId); - if (pendingStream != null) { - Promise promise = ctx.newPromise(); - pendingStream.frames.add(new DataFrame(data, padding, endOfStream, promise)); - return promise.asFuture(); - } else { - ReferenceCountUtil.safeRelease(data); - return ctx.newFailedFuture(connectionError(PROTOCOL_ERROR, "Stream does not exist %d", streamId)); - } - } - - @Override - public void remoteSettings(Http2Settings settings) throws Http2Exception { - // Need to let the delegate decoder handle the settings first, so that it sees the - // new setting before we attempt to create any new streams. - super.remoteSettings(settings); - - // Get the updated value for SETTINGS_MAX_CONCURRENT_STREAMS. - maxConcurrentStreams = connection().local().maxActiveStreams(); - - // Try to create new streams up to the new threshold. - tryCreatePendingStreams(); - } - - @Override - public void close() { - try { - if (!closed) { - closed = true; - - // Fail all buffered streams. - Http2ChannelClosedException e = new Http2ChannelClosedException(); - while (!pendingStreams.isEmpty()) { - PendingStream stream = pendingStreams.pollFirstEntry().getValue(); - stream.close(e); - } - } - } finally { - super.close(); - } - } - - private void tryCreatePendingStreams() { - while (!pendingStreams.isEmpty() && canCreateStream()) { - Map.Entry entry = pendingStreams.pollFirstEntry(); - PendingStream pendingStream = entry.getValue(); - try { - pendingStream.sendFrames(); - } catch (Throwable t) { - pendingStream.close(t); - } - } - } - - private void cancelGoAwayStreams(GoAwayDetail goAwayDetail) { - Iterator iter = pendingStreams.values().iterator(); - Exception e = new Http2GoAwayException(goAwayDetail); - while (iter.hasNext()) { - PendingStream stream = iter.next(); - if (stream.streamId > goAwayDetail.lastStreamId) { - iter.remove(); - stream.close(e); - } - } - } - - /** - * Determines whether or not we're allowed to create a new stream right now. - */ - private boolean canCreateStream() { - return connection().local().numActiveStreams() < maxConcurrentStreams; - } - - private boolean isExistingStream(int streamId) { - return streamId <= connection().local().lastStreamCreated(); - } - - private static final class PendingStream { - final ChannelHandlerContext ctx; - final int streamId; - final Queue frames = new ArrayDeque<>(2); - - PendingStream(ChannelHandlerContext ctx, int streamId) { - this.ctx = ctx; - this.streamId = streamId; - } - - void sendFrames() { - for (Frame frame : frames) { - frame.send(ctx, streamId); - } - } - - void close(Throwable t) { - for (Frame frame : frames) { - frame.release(t); - } - } - } - - private abstract static class Frame { - final Promise promise; - - Frame(Promise promise) { - this.promise = promise; - } - - /** - * Release any resources (features, buffers, ...) associated with the frame. - */ - void release(Throwable t) { - if (t == null) { - promise.setSuccess(null); - } else { - promise.setFailure(t); - } - } - - abstract void send(ChannelHandlerContext ctx, int streamId); - } - - private final class HeadersFrame extends Frame { - final Http2Headers headers; - final int streamDependency; - final short weight; - final boolean exclusive; - final int padding; - final boolean endOfStream; - - HeadersFrame(Http2Headers headers, int streamDependency, short weight, boolean exclusive, - int padding, boolean endOfStream, Promise promise) { - super(promise); - this.headers = headers; - this.streamDependency = streamDependency; - this.weight = weight; - this.exclusive = exclusive; - this.padding = padding; - this.endOfStream = endOfStream; - } - - @Override - void send(ChannelHandlerContext ctx, int streamId) { - writeHeaders(ctx, streamId, headers, streamDependency, weight, exclusive, padding, endOfStream) - .cascadeTo(promise); - } - } - - private final class DataFrame extends Frame { - final ByteBuf data; - final int padding; - final boolean endOfStream; - - DataFrame(ByteBuf data, int padding, boolean endOfStream, Promise promise) { - super(promise); - this.data = data; - this.padding = padding; - this.endOfStream = endOfStream; - } - - @Override - void release(Throwable t) { - super.release(t); - ReferenceCountUtil.safeRelease(data); - } - - @Override - void send(ChannelHandlerContext ctx, int streamId) { - writeData(ctx, streamId, data, padding, endOfStream).cascadeTo(promise); - } - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/StreamByteDistributor.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/StreamByteDistributor.java deleted file mode 100644 index 2fe840d843..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/StreamByteDistributor.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.util.internal.UnstableApi; - -/** - * An object (used by remote flow control) that is responsible for distributing the bytes to be - * written across the streams in the connection. - */ -@UnstableApi -public interface StreamByteDistributor { - - /** - * State information for the stream, indicating the number of bytes that are currently - * streamable. This is provided to the {@link #updateStreamableBytes(StreamState)} method. - */ - interface StreamState { - /** - * Gets the stream this state is associated with. - */ - Http2Stream stream(); - - /** - * Get the amount of bytes this stream has pending to send. The actual amount written must not exceed - * {@link #windowSize()}! - * @return The amount of bytes this stream has pending to send. - * @see Http2CodecUtil#streamableBytes(StreamState) - */ - long pendingBytes(); - - /** - * Indicates whether or not there are frames pending for this stream. - */ - boolean hasFrame(); - - /** - * The size (in bytes) of the stream's flow control window. The amount written must not exceed this amount! - *

A {@link StreamByteDistributor} needs to know the stream's window size in order to avoid allocating bytes - * if the window size is negative. The window size being {@code 0} may also be significant to determine when if - * an stream has been given a chance to write an empty frame, and also enables optimizations like not writing - * empty frames in some situations (don't write headers until data can also be written). - * @return the size of the stream's flow control window. - * @see Http2CodecUtil#streamableBytes(StreamState) - */ - int windowSize(); - } - - /** - * Object that performs the writing of the bytes that have been allocated for a stream. - */ - interface Writer { - /** - * Writes the allocated bytes for this stream. - *

- * Any {@link Throwable} thrown from this method is considered a programming error. - * A {@code GOAWAY} frame will be sent and the will be connection closed. - * @param stream the stream for which to perform the write. - * @param numBytes the number of bytes to write. - */ - void write(Http2Stream stream, int numBytes); - } - - /** - * Called when the streamable bytes for a stream has changed. Until this - * method is called for the first time for a give stream, the stream is assumed to have no - * streamable bytes. - */ - void updateStreamableBytes(StreamState state); - - /** - * Explicitly update the dependency tree. This method is called independently of stream state changes. - * @param childStreamId The stream identifier associated with the child stream. - * @param parentStreamId The stream identifier associated with the parent stream. May be {@code 0}, - * to make {@code childStreamId} and immediate child of the connection. - * @param weight The weight which is used relative to other child streams for {@code parentStreamId}. This value - * must be between 1 and 256 (inclusive). - * @param exclusive If {@code childStreamId} should be the exclusive dependency of {@code parentStreamId}. - */ - void updateDependencyTree(int childStreamId, int parentStreamId, short weight, boolean exclusive); - - /** - * Distributes up to {@code maxBytes} to those streams containing streamable bytes and - * iterates across those streams to write the appropriate bytes. Criteria for - * traversing streams is undefined and it is up to the implementation to determine when to stop - * at a given stream. - * - *

The streamable bytes are not automatically updated by calling this method. It is up to the - * caller to indicate the number of bytes streamable after the write by calling - * {@link #updateStreamableBytes(StreamState)}. - * - * @param maxBytes the maximum number of bytes to write. - * @return {@code true} if there are still streamable bytes that have not yet been written, - * otherwise {@code false}. - * @throws Http2Exception If an internal exception occurs and internal connection state would otherwise be - * corrupted. - */ - boolean distribute(int maxBytes, Writer writer) throws Http2Exception; -} 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 deleted file mode 100644 index 6f0ec40f78..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/UniformStreamByteDistributor.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.util.internal.UnstableApi; - -import java.util.ArrayDeque; -import java.util.Deque; - -import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_MIN_ALLOCATION_CHUNK; -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 java.lang.Math.max; -import static java.lang.Math.min; -import static java.util.Objects.requireNonNull; - -/** - * A {@link StreamByteDistributor} that ignores stream priority and uniformly allocates bytes to all - * streams. This class uses a minimum chunk size that will be allocated to each stream. While - * fewer streams may be written to in each call to {@link #distribute(int, Writer)}, doing this - * should improve the goodput on each written stream. - */ -@UnstableApi -public final class UniformStreamByteDistributor implements StreamByteDistributor { - private final Http2Connection.PropertyKey stateKey; - private final Deque queue = new ArrayDeque<>(4); - - /** - * The minimum number of bytes that we will attempt to allocate to a stream. This is to - * help improve goodput on a per-stream basis. - */ - private int minAllocationChunk = DEFAULT_MIN_ALLOCATION_CHUNK; - private long totalStreamableBytes; - - public UniformStreamByteDistributor(Http2Connection connection) { - // Add a state for the connection. - stateKey = connection.newKey(); - Http2Stream connectionStream = connection.connectionStream(); - connectionStream.setProperty(stateKey, new State(connectionStream)); - - // Register for notification of new streams. - connection.addListener(new Http2ConnectionAdapter() { - @Override - public void onStreamAdded(Http2Stream stream) { - stream.setProperty(stateKey, new State(stream)); - } - - @Override - public void onStreamClosed(Http2Stream stream) { - state(stream).close(); - } - }); - } - - /** - * Sets the minimum allocation chunk that will be allocated to each stream. Defaults to 1KiB. - * - * @param minAllocationChunk the minimum number of bytes that will be allocated to each stream. - * Must be > 0. - */ - public void minAllocationChunk(int minAllocationChunk) { - checkPositive(minAllocationChunk, "minAllocationChunk"); - this.minAllocationChunk = minAllocationChunk; - } - - @Override - public void updateStreamableBytes(StreamState streamState) { - state(streamState.stream()).updateStreamableBytes(streamableBytes(streamState), - streamState.hasFrame(), - streamState.windowSize()); - } - - @Override - public void updateDependencyTree(int childStreamId, int parentStreamId, short weight, boolean exclusive) { - // This class ignores priority and dependency! - } - - @Override - public boolean distribute(int maxBytes, Writer writer) throws Http2Exception { - final int size = queue.size(); - if (size == 0) { - return totalStreamableBytes > 0; - } - - final int chunkSize = max(minAllocationChunk, maxBytes / size); - - State state = queue.pollFirst(); - do { - state.enqueued = false; - if (state.windowNegative) { - continue; - } - if (maxBytes == 0 && state.streamableBytes > 0) { - // Stop at the first state that can't send. Add this state back to the head of the queue. Note - // that empty frames at the head of the queue will always be written, assuming the stream window - // is not negative. - queue.addFirst(state); - state.enqueued = true; - break; - } - - // Allocate as much data as we can for this stream. - int chunk = min(chunkSize, min(maxBytes, state.streamableBytes)); - maxBytes -= chunk; - - // Write the allocated bytes and enqueue as necessary. - state.write(chunk, writer); - } while ((state = queue.pollFirst()) != null); - - return totalStreamableBytes > 0; - } - - private State state(Http2Stream stream) { - return requireNonNull(stream, "stream").getProperty(stateKey); - } - - /** - * The remote flow control state for a single stream. - */ - private final class State { - final Http2Stream stream; - int streamableBytes; - boolean windowNegative; - boolean enqueued; - boolean writing; - - State(Http2Stream stream) { - this.stream = stream; - } - - void updateStreamableBytes(int newStreamableBytes, boolean hasFrame, int windowSize) { - assert hasFrame || newStreamableBytes == 0 : - "hasFrame: " + hasFrame + " newStreamableBytes: " + newStreamableBytes; - - int delta = newStreamableBytes - streamableBytes; - if (delta != 0) { - streamableBytes = newStreamableBytes; - totalStreamableBytes += delta; - } - // In addition to only enqueuing state when they have frames we enforce the following restrictions: - // 1. If the window has gone negative. We never want to queue a state. However we also don't want to - // Immediately remove the item if it is already queued because removal from deque is O(n). So - // we allow it to stay queued and rely on the distribution loop to remove this state. - // 2. If the window is zero we only want to queue if we are not writing. If we are writing that means - // we gave the state a chance to write zero length frames. We wait until updateStreamableBytes is - // called again before this state is allowed to write. - windowNegative = windowSize < 0; - if (hasFrame && (windowSize > 0 || windowSize == 0 && !writing)) { - addToQueue(); - } - } - - /** - * Write any allocated bytes for the given stream and updates the streamable bytes, - * assuming all of the bytes will be written. - */ - void write(int numBytes, Writer writer) throws Http2Exception { - writing = true; - try { - // Write the allocated bytes. - writer.write(stream, numBytes); - } catch (Throwable t) { - throw connectionError(INTERNAL_ERROR, t, "byte distribution write error"); - } finally { - writing = false; - } - } - - void addToQueue() { - if (!enqueued) { - enqueued = true; - queue.addLast(this); - } - } - - void removeFromQueue() { - if (enqueued) { - enqueued = false; - queue.remove(this); - } - } - - void close() { - // Remove this state from the queue. - removeFromQueue(); - - // Clear the streamable bytes. - updateStreamableBytes(0, false, 0); - } - } -} 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 deleted file mode 100644 index 5e974c49a4..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/WeightedFairQueueByteDistributor.java +++ /dev/null @@ -1,802 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.util.collection.IntCollections; -import io.netty.util.collection.IntObjectHashMap; -import io.netty.util.collection.IntObjectMap; -import io.netty.util.internal.DefaultPriorityQueue; -import io.netty.util.internal.EmptyPriorityQueue; -import io.netty.util.internal.PriorityQueue; -import io.netty.util.internal.PriorityQueueNode; -import io.netty.util.internal.SystemPropertyUtil; -import io.netty.util.internal.UnstableApi; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.Iterator; -import java.util.List; - -import static io.netty.handler.codec.http2.Http2CodecUtil.CONNECTION_STREAM_ID; -import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_MIN_ALLOCATION_CHUNK; -import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_PRIORITY_WEIGHT; -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; - -/** - * A {@link StreamByteDistributor} that is sensitive to stream priority and uses - * Weighted Fair Queueing approach for distributing - * bytes. - *

- * Inspiration for this distributor was taken from Linux's - * Completely Fair Scheduler - * to model the distribution of bytes to simulate an "ideal multi-tasking CPU", but in this case we are simulating - * an "ideal multi-tasking NIC". - *

- * Each write operation will use the {@link #allocationQuantum(int)} to know how many more bytes should be allocated - * relative to the next stream which wants to write. This is to balance fairness while also considering goodput. - */ -@UnstableApi -public final class WeightedFairQueueByteDistributor implements StreamByteDistributor { - /** - * The initial size of the children map is chosen to be conservative on initial memory allocations under - * the assumption that most streams will have a small number of children. This choice may be - * sub-optimal if when children are present there are many children (i.e. a web page which has many - * dependencies to load). - * - * Visible only for testing! - */ - static final int INITIAL_CHILDREN_MAP_SIZE = - max(1, SystemPropertyUtil.getInt("io.netty.http2.childrenMapSize", 2)); - /** - * FireFox currently uses 5 streams to establish QoS classes. - */ - private static final int DEFAULT_MAX_STATE_ONLY_SIZE = 5; - - private final Http2Connection.PropertyKey stateKey; - /** - * If there is no Http2Stream object, but we still persist priority information then this is where the state will - * reside. - */ - private final IntObjectMap stateOnlyMap; - /** - * This queue will hold streams that are not active and provides the capability to retain priority for streams which - * have no {@link Http2Stream} object. See {@link StateOnlyComparator} for the priority comparator. - */ - private final PriorityQueue stateOnlyRemovalQueue; - private final Http2Connection connection; - private final State connectionState; - /** - * The minimum number of bytes that we will attempt to allocate to a stream. This is to - * help improve goodput on a per-stream basis. - */ - private int allocationQuantum = DEFAULT_MIN_ALLOCATION_CHUNK; - private final int maxStateOnlySize; - - public WeightedFairQueueByteDistributor(Http2Connection connection) { - this(connection, DEFAULT_MAX_STATE_ONLY_SIZE); - } - - public WeightedFairQueueByteDistributor(Http2Connection connection, int maxStateOnlySize) { - checkPositiveOrZero(maxStateOnlySize, "maxStateOnlySize"); - if (maxStateOnlySize == 0) { - stateOnlyMap = IntCollections.emptyMap(); - stateOnlyRemovalQueue = EmptyPriorityQueue.instance(); - } else { - stateOnlyMap = new IntObjectHashMap(maxStateOnlySize); - // +2 because we may exceed the limit by 2 if a new dependency has no associated Http2Stream object. We need - // to create the State objects to put them into the dependency tree, which then impacts priority. - stateOnlyRemovalQueue = new DefaultPriorityQueue<>(StateOnlyComparator.INSTANCE, maxStateOnlySize + 2); - } - this.maxStateOnlySize = maxStateOnlySize; - - this.connection = connection; - stateKey = connection.newKey(); - final Http2Stream connectionStream = connection.connectionStream(); - connectionStream.setProperty(stateKey, connectionState = new State(connectionStream, 16)); - - // Register for notification of new streams. - connection.addListener(new Http2ConnectionAdapter() { - @Override - public void onStreamAdded(Http2Stream stream) { - State state = stateOnlyMap.remove(stream.id()); - if (state == null) { - state = new State(stream); - // Only the stream which was just added will change parents. So we only need an array of size 1. - List events = new ArrayList<>(1); - connectionState.takeChild(state, false, events); - notifyParentChanged(events); - } else { - stateOnlyRemovalQueue.removeTyped(state); - state.stream = stream; - } - switch (stream.state()) { - case RESERVED_REMOTE: - case RESERVED_LOCAL: - state.setStreamReservedOrActivated(); - // wasStreamReservedOrActivated is part of the comparator for stateOnlyRemovalQueue there is no - // need to reprioritize here because it will not be in stateOnlyRemovalQueue. - break; - default: - break; - } - stream.setProperty(stateKey, state); - } - - @Override - public void onStreamActive(Http2Stream stream) { - state(stream).setStreamReservedOrActivated(); - // wasStreamReservedOrActivated is part of the comparator for stateOnlyRemovalQueue there is no need to - // reprioritize here because it will not be in stateOnlyRemovalQueue. - } - - @Override - public void onStreamClosed(Http2Stream stream) { - state(stream).close(); - } - - @Override - public void onStreamRemoved(Http2Stream stream) { - // The stream has been removed from the connection. We can no longer rely on the stream's property - // storage to track the State. If we have room, and the precedence of the stream is sufficient, we - // should retain the State in the stateOnlyMap. - State state = state(stream); - - // Typically the stream is set to null when the stream is closed because it is no longer needed to write - // data. However if the stream was not activated it may not be closed (reserved streams) so we ensure - // the stream reference is set to null to avoid retaining a reference longer than necessary. - state.stream = null; - - if (WeightedFairQueueByteDistributor.this.maxStateOnlySize == 0) { - state.parent.removeChild(state); - return; - } - if (stateOnlyRemovalQueue.size() == WeightedFairQueueByteDistributor.this.maxStateOnlySize) { - State stateToRemove = stateOnlyRemovalQueue.peek(); - if (StateOnlyComparator.INSTANCE.compare(stateToRemove, state) >= 0) { - // The "lowest priority" stream is a "higher priority" than the stream being removed, so we - // just discard the state. - state.parent.removeChild(state); - return; - } - stateOnlyRemovalQueue.poll(); - stateToRemove.parent.removeChild(stateToRemove); - stateOnlyMap.remove(stateToRemove.streamId); - } - stateOnlyRemovalQueue.add(state); - stateOnlyMap.put(state.streamId, state); - } - }); - } - - @Override - public void updateStreamableBytes(StreamState state) { - state(state.stream()).updateStreamableBytes(streamableBytes(state), - state.hasFrame() && state.windowSize() >= 0); - } - - @Override - public void updateDependencyTree(int childStreamId, int parentStreamId, short weight, boolean exclusive) { - State state = state(childStreamId); - if (state == null) { - // If there is no State object that means there is no Http2Stream object and we would have to keep the - // State object in the stateOnlyMap and stateOnlyRemovalQueue. However if maxStateOnlySize is 0 this means - // stateOnlyMap and stateOnlyRemovalQueue are empty collections and cannot be modified so we drop the State. - if (maxStateOnlySize == 0) { - return; - } - state = new State(childStreamId); - stateOnlyRemovalQueue.add(state); - stateOnlyMap.put(childStreamId, state); - } - - State newParent = state(parentStreamId); - if (newParent == null) { - // If there is no State object that means there is no Http2Stream object and we would have to keep the - // State object in the stateOnlyMap and stateOnlyRemovalQueue. However if maxStateOnlySize is 0 this means - // stateOnlyMap and stateOnlyRemovalQueue are empty collections and cannot be modified so we drop the State. - if (maxStateOnlySize == 0) { - return; - } - newParent = new State(parentStreamId); - stateOnlyRemovalQueue.add(newParent); - stateOnlyMap.put(parentStreamId, newParent); - // Only the stream which was just added will change parents. So we only need an array of size 1. - List events = new ArrayList<>(1); - connectionState.takeChild(newParent, false, events); - notifyParentChanged(events); - } - - // if activeCountForTree == 0 then it will not be in its parent's pseudoTimeQueue and thus should not be counted - // toward parent.totalQueuedWeights. - if (state.activeCountForTree != 0 && state.parent != null) { - state.parent.totalQueuedWeights += weight - state.weight; - } - state.weight = weight; - - if (newParent != state.parent || exclusive && newParent.children.size() != 1) { - final List events; - if (newParent.isDescendantOf(state)) { - events = new ArrayList<>(2 + (exclusive ? newParent.children.size() : 0)); - state.parent.takeChild(newParent, false, events); - } else { - events = new ArrayList<>(1 + (exclusive ? newParent.children.size() : 0)); - } - newParent.takeChild(state, exclusive, events); - notifyParentChanged(events); - } - - // The location in the dependency tree impacts the priority in the stateOnlyRemovalQueue map. If we created new - // State objects we must check if we exceeded the limit after we insert into the dependency tree to ensure the - // stateOnlyRemovalQueue has been updated. - while (stateOnlyRemovalQueue.size() > maxStateOnlySize) { - State stateToRemove = stateOnlyRemovalQueue.poll(); - stateToRemove.parent.removeChild(stateToRemove); - stateOnlyMap.remove(stateToRemove.streamId); - } - } - - @Override - public boolean distribute(int maxBytes, Writer writer) throws Http2Exception { - // As long as there is some active frame we should write at least 1 time. - if (connectionState.activeCountForTree == 0) { - return false; - } - - // The goal is to write until we write all the allocated bytes or are no longer making progress. - // We still attempt to write even after the number of allocated bytes has been exhausted to allow empty frames - // to be sent. Making progress means the active streams rooted at the connection stream has changed. - int oldIsActiveCountForTree; - do { - oldIsActiveCountForTree = connectionState.activeCountForTree; - // connectionState will never be active, so go right to its children. - maxBytes -= distributeToChildren(maxBytes, writer, connectionState); - } while (connectionState.activeCountForTree != 0 && - (maxBytes > 0 || oldIsActiveCountForTree != connectionState.activeCountForTree)); - - return connectionState.activeCountForTree != 0; - } - - /** - * Sets the amount of bytes that will be allocated to each stream. Defaults to 1KiB. - * @param allocationQuantum the amount of bytes that will be allocated to each stream. Must be > 0. - */ - public void allocationQuantum(int allocationQuantum) { - checkPositive(allocationQuantum, "allocationQuantum"); - this.allocationQuantum = allocationQuantum; - } - - private int distribute(int maxBytes, Writer writer, State state) throws Http2Exception { - if (state.isActive()) { - int nsent = min(maxBytes, state.streamableBytes); - state.write(nsent, writer); - if (nsent == 0 && maxBytes != 0) { - // If a stream sends zero bytes, then we gave it a chance to write empty frames and it is now - // considered inactive until the next call to updateStreamableBytes. This allows descendant streams to - // be allocated bytes when the parent stream can't utilize them. This may be as a result of the - // stream's flow control window being 0. - state.updateStreamableBytes(state.streamableBytes, false); - } - return nsent; - } - - return distributeToChildren(maxBytes, writer, state); - } - - /** - * It is a pre-condition that {@code state.poll()} returns a non-{@code null} value. This is a result of the way - * the allocation algorithm is structured and can be explained in the following cases: - *

For the recursive case

- * If a stream has no children (in the allocation tree) than that node must be active or it will not be in the - * allocation tree. If a node is active then it will not delegate to children and recursion ends. - *

For the initial case

- * We check connectionState.activeCountForTree == 0 before any allocation is done. So if the connection stream - * has no active children we don't get into this method. - */ - private int distributeToChildren(int maxBytes, Writer writer, State state) throws Http2Exception { - long oldTotalQueuedWeights = state.totalQueuedWeights; - State childState = state.pollPseudoTimeQueue(); - State nextChildState = state.peekPseudoTimeQueue(); - childState.setDistributing(); - try { - assert nextChildState == null || nextChildState.pseudoTimeToWrite >= childState.pseudoTimeToWrite : - "nextChildState[" + nextChildState.streamId + "].pseudoTime(" + nextChildState.pseudoTimeToWrite + - ") < " + " childState[" + childState.streamId + "].pseudoTime(" + childState.pseudoTimeToWrite + ')'; - int nsent = distribute(nextChildState == null ? maxBytes : - min(maxBytes, (int) min((nextChildState.pseudoTimeToWrite - childState.pseudoTimeToWrite) * - childState.weight / oldTotalQueuedWeights + allocationQuantum, MAX_VALUE) - ), - writer, - childState); - state.pseudoTime += nsent; - childState.updatePseudoTime(state, nsent, oldTotalQueuedWeights); - return nsent; - } finally { - childState.unsetDistributing(); - // Do in finally to ensure the internal flags is not corrupted if an exception is thrown. - // The offer operation is delayed until we unroll up the recursive stack, so we don't have to remove from - // the priority pseudoTimeQueue due to a write operation. - if (childState.activeCountForTree != 0) { - state.offerPseudoTimeQueue(childState); - } - } - } - - private State state(Http2Stream stream) { - return stream.getProperty(stateKey); - } - - private State state(int streamId) { - Http2Stream stream = connection.stream(streamId); - return stream != null ? state(stream) : stateOnlyMap.get(streamId); - } - - /** - * For testing only! - */ - boolean isChild(int childId, int parentId, short weight) { - State parent = state(parentId); - State child; - return parent.children.containsKey(childId) && - (child = state(childId)).parent == parent && child.weight == weight; - } - - /** - * For testing only! - */ - int numChildren(int streamId) { - State state = state(streamId); - return state == null ? 0 : state.children.size(); - } - - /** - * Notify all listeners of the priority tree change events (in ascending order) - * @param events The events (top down order) which have changed - */ - void notifyParentChanged(List events) { - for (int i = 0; i < events.size(); ++i) { - ParentChangedEvent event = events.get(i); - stateOnlyRemovalQueue.priorityChanged(event.state); - if (event.state.parent != null && event.state.activeCountForTree != 0) { - event.state.parent.offerAndInitializePseudoTime(event.state); - event.state.parent.activeCountChangeForTree(event.state.activeCountForTree); - } - } - } - - /** - * A comparator for {@link State} which has no associated {@link Http2Stream} object. The general precedence is: - *
    - *
  • Was a stream activated or reserved (streams only used for priority are higher priority)
  • - *
  • Depth in the priority tree (closer to root is higher priority>
  • - *
  • Stream ID (higher stream ID is higher priority - used for tie breaker)
  • - *
- */ - private static final class StateOnlyComparator implements Comparator, Serializable { - private static final long serialVersionUID = -4806936913002105966L; - - static final StateOnlyComparator INSTANCE = new StateOnlyComparator(); - - @Override - public int compare(State o1, State o2) { - // "priority only streams" (which have not been activated) are higher priority than streams used for data. - boolean o1Actived = o1.wasStreamReservedOrActivated(); - if (o1Actived != o2.wasStreamReservedOrActivated()) { - return o1Actived ? -1 : 1; - } - // Numerically greater depth is higher priority. - int x = o2.dependencyTreeDepth - o1.dependencyTreeDepth; - - // I also considered tracking the number of streams which are "activated" (eligible transfer data) at each - // subtree. This would require a traversal from each node to the root on dependency tree structural changes, - // and then it would require a re-prioritization at each of these nodes (instead of just the nodes where the - // direct parent changed). The costs of this are judged to be relatively high compared to the nominal - // benefit it provides to the heuristic. Instead folks should just increase maxStateOnlySize. - - // Last resort is to give larger stream ids more priority. - return x != 0 ? x : o1.streamId - o2.streamId; - } - } - - private static final class StatePseudoTimeComparator implements Comparator, Serializable { - private static final long serialVersionUID = -1437548640227161828L; - - static final StatePseudoTimeComparator INSTANCE = new StatePseudoTimeComparator(); - - @Override - public int compare(State o1, State o2) { - return Long.compare(o1.pseudoTimeToWrite, o2.pseudoTimeToWrite); - } - } - - /** - * The remote flow control state for a single stream. - */ - private final class State implements PriorityQueueNode { - private static final byte STATE_IS_ACTIVE = 0x1; - private static final byte STATE_IS_DISTRIBUTING = 0x2; - private static final byte STATE_STREAM_ACTIVATED = 0x4; - - /** - * Maybe {@code null} if the stream if the stream is not active. - */ - Http2Stream stream; - State parent; - IntObjectMap children = IntCollections.emptyMap(); - private final PriorityQueue pseudoTimeQueue; - final int streamId; - int streamableBytes; - int dependencyTreeDepth; - /** - * Count of nodes rooted at this sub tree with {@link #isActive()} equal to {@code true}. - */ - int activeCountForTree; - private int pseudoTimeQueueIndex = INDEX_NOT_IN_QUEUE; - private int stateOnlyQueueIndex = INDEX_NOT_IN_QUEUE; - /** - * An estimate of when this node should be given the opportunity to write data. - */ - long pseudoTimeToWrite; - /** - * A pseudo time maintained for immediate children to base their {@link #pseudoTimeToWrite} off of. - */ - long pseudoTime; - long totalQueuedWeights; - private byte flags; - short weight = DEFAULT_PRIORITY_WEIGHT; - - State(int streamId) { - this(streamId, null, 0); - } - - State(Http2Stream stream) { - this(stream, 0); - } - - State(Http2Stream stream, int initialSize) { - this(stream.id(), stream, initialSize); - } - - State(int streamId, Http2Stream stream, int initialSize) { - this.stream = stream; - this.streamId = streamId; - pseudoTimeQueue = new DefaultPriorityQueue<>(StatePseudoTimeComparator.INSTANCE, initialSize); - } - - boolean isDescendantOf(State state) { - State next = parent; - while (next != null) { - if (next == state) { - return true; - } - next = next.parent; - } - return false; - } - - void takeChild(State child, boolean exclusive, List events) { - takeChild(null, child, exclusive, events); - } - - /** - * Adds a child to this priority. If exclusive is set, any children of this node are moved to being dependent on - * the child. - */ - void takeChild(Iterator> childItr, State child, boolean exclusive, - List events) { - State oldParent = child.parent; - - if (oldParent != this) { - events.add(new ParentChangedEvent(child, oldParent)); - child.setParent(this); - // If the childItr is not null we are iterating over the oldParent.children collection and should - // use the iterator to remove from the collection to avoid concurrent modification. Otherwise it is - // assumed we are not iterating over this collection and it is safe to call remove directly. - if (childItr != null) { - childItr.remove(); - } else if (oldParent != null) { - oldParent.children.remove(child.streamId); - } - - // Lazily initialize the children to save object allocations. - initChildrenIfEmpty(); - - final State oldChild = children.put(child.streamId, child); - assert oldChild == null : "A stream with the same stream ID was already in the child map."; - } - - if (exclusive && !children.isEmpty()) { - // If it was requested that this child be the exclusive dependency of this node, - // move any previous children to the child node, becoming grand children of this node. - Iterator> itr = removeAllChildrenExcept(child).entries().iterator(); - while (itr.hasNext()) { - child.takeChild(itr, itr.next().value(), false, events); - } - } - } - - /** - * Removes the child priority and moves any of its dependencies to being direct dependencies on this node. - */ - void removeChild(State child) { - if (children.remove(child.streamId) != null) { - List events = new ArrayList<>(1 + child.children.size()); - events.add(new ParentChangedEvent(child, child.parent)); - child.setParent(null); - - if (!child.children.isEmpty()) { - // Move up any grand children to be directly dependent on this node. - Iterator> itr = child.children.entries().iterator(); - long totalWeight = child.getTotalWeight(); - do { - // Redistribute the weight of child to its dependency proportionally. - State dependency = itr.next().value(); - dependency.weight = (short) max(1, dependency.weight * child.weight / totalWeight); - takeChild(itr, dependency, false, events); - } while (itr.hasNext()); - } - - notifyParentChanged(events); - } - } - - private long getTotalWeight() { - long totalWeight = 0L; - for (State state : children.values()) { - totalWeight += state.weight; - } - return totalWeight; - } - - /** - * Remove all children with the exception of {@code streamToRetain}. - * This method is intended to be used to support an exclusive priority dependency operation. - * @return The map of children prior to this operation, excluding {@code streamToRetain} if present. - */ - private IntObjectMap removeAllChildrenExcept(State stateToRetain) { - stateToRetain = children.remove(stateToRetain.streamId); - IntObjectMap prevChildren = children; - // This map should be re-initialized in anticipation for the 1 exclusive child which will be added. - // It will either be added directly in this method, or after this method is called...but it will be added. - initChildren(); - if (stateToRetain != null) { - children.put(stateToRetain.streamId, stateToRetain); - } - return prevChildren; - } - - private void setParent(State newParent) { - // if activeCountForTree == 0 then it will not be in its parent's pseudoTimeQueue. - if (activeCountForTree != 0 && parent != null) { - parent.removePseudoTimeQueue(this); - parent.activeCountChangeForTree(-activeCountForTree); - } - parent = newParent; - // Use MAX_VALUE if no parent because lower depth is considered higher priority by StateOnlyComparator. - dependencyTreeDepth = newParent == null ? MAX_VALUE : newParent.dependencyTreeDepth + 1; - } - - private void initChildrenIfEmpty() { - if (children == IntCollections.emptyMap()) { - initChildren(); - } - } - - private void initChildren() { - children = new IntObjectHashMap(INITIAL_CHILDREN_MAP_SIZE); - } - - void write(int numBytes, Writer writer) throws Http2Exception { - assert stream != null; - try { - writer.write(stream, numBytes); - } catch (Throwable t) { - throw connectionError(INTERNAL_ERROR, t, "byte distribution write error"); - } - } - - void activeCountChangeForTree(int increment) { - assert activeCountForTree + increment >= 0; - activeCountForTree += increment; - if (parent != null) { - assert activeCountForTree != increment || - pseudoTimeQueueIndex == INDEX_NOT_IN_QUEUE || - parent.pseudoTimeQueue.containsTyped(this) : - "State[" + streamId + "].activeCountForTree changed from 0 to " + increment + " is in a " + - "pseudoTimeQueue, but not in parent[ " + parent.streamId + "]'s pseudoTimeQueue"; - if (activeCountForTree == 0) { - parent.removePseudoTimeQueue(this); - } else if (activeCountForTree == increment && !isDistributing()) { - // If frame count was 0 but is now not, and this node is not already in a pseudoTimeQueue (assumed - // to be pState's pseudoTimeQueue) then enqueue it. If this State object is being processed the - // pseudoTime for this node should not be adjusted, and the node will be added back to the - // pseudoTimeQueue/tree structure after it is done being processed. This may happen if the - // activeCountForTree == 0 (a node which can't stream anything and is blocked) is at/near root of - // the tree, and is popped off the pseudoTimeQueue during processing, and then put back on the - // pseudoTimeQueue because a child changes position in the priority tree (or is closed because it is - // not blocked and finished writing all data). - parent.offerAndInitializePseudoTime(this); - } - parent.activeCountChangeForTree(increment); - } - } - - void updateStreamableBytes(int newStreamableBytes, boolean isActive) { - if (isActive() != isActive) { - if (isActive) { - activeCountChangeForTree(1); - setActive(); - } else { - activeCountChangeForTree(-1); - unsetActive(); - } - } - - streamableBytes = newStreamableBytes; - } - - /** - * Assumes the parents {@link #totalQueuedWeights} includes this node's weight. - */ - void updatePseudoTime(State parentState, int nsent, long totalQueuedWeights) { - assert streamId != CONNECTION_STREAM_ID && nsent >= 0; - // If the current pseudoTimeToSend is greater than parentState.pseudoTime then we previously over accounted - // and should use parentState.pseudoTime. - pseudoTimeToWrite = min(pseudoTimeToWrite, parentState.pseudoTime) + nsent * totalQueuedWeights / weight; - } - - /** - * The concept of pseudoTime can be influenced by priority tree manipulations or if a stream goes from "active" - * to "non-active". This method accounts for that by initializing the {@link #pseudoTimeToWrite} for - * {@code state} to {@link #pseudoTime} of this node and then calls {@link #offerPseudoTimeQueue(State)}. - */ - void offerAndInitializePseudoTime(State state) { - state.pseudoTimeToWrite = pseudoTime; - offerPseudoTimeQueue(state); - } - - void offerPseudoTimeQueue(State state) { - pseudoTimeQueue.offer(state); - totalQueuedWeights += state.weight; - } - - /** - * Must only be called if the pseudoTimeQueue is non-empty! - */ - State pollPseudoTimeQueue() { - State state = pseudoTimeQueue.poll(); - // This method is only ever called if the pseudoTimeQueue is non-empty. - totalQueuedWeights -= state.weight; - return state; - } - - void removePseudoTimeQueue(State state) { - if (pseudoTimeQueue.removeTyped(state)) { - totalQueuedWeights -= state.weight; - } - } - - State peekPseudoTimeQueue() { - return pseudoTimeQueue.peek(); - } - - void close() { - updateStreamableBytes(0, false); - stream = null; - } - - boolean wasStreamReservedOrActivated() { - return (flags & STATE_STREAM_ACTIVATED) != 0; - } - - void setStreamReservedOrActivated() { - flags |= STATE_STREAM_ACTIVATED; - } - - boolean isActive() { - return (flags & STATE_IS_ACTIVE) != 0; - } - - private void setActive() { - flags |= STATE_IS_ACTIVE; - } - - private void unsetActive() { - flags &= ~STATE_IS_ACTIVE; - } - - boolean isDistributing() { - return (flags & STATE_IS_DISTRIBUTING) != 0; - } - - void setDistributing() { - flags |= STATE_IS_DISTRIBUTING; - } - - void unsetDistributing() { - flags &= ~STATE_IS_DISTRIBUTING; - } - - @Override - public int priorityQueueIndex(DefaultPriorityQueue queue) { - return queue == stateOnlyRemovalQueue ? stateOnlyQueueIndex : pseudoTimeQueueIndex; - } - - @Override - public void priorityQueueIndex(DefaultPriorityQueue queue, int i) { - if (queue == stateOnlyRemovalQueue) { - stateOnlyQueueIndex = i; - } else { - pseudoTimeQueueIndex = i; - } - } - - @Override - public String toString() { - // Use activeCountForTree as a rough estimate for how many nodes are in this subtree. - StringBuilder sb = new StringBuilder(256 * (activeCountForTree > 0 ? activeCountForTree : 1)); - toString(sb); - return sb.toString(); - } - - private void toString(StringBuilder sb) { - sb.append("{streamId ").append(streamId) - .append(" streamableBytes ").append(streamableBytes) - .append(" activeCountForTree ").append(activeCountForTree) - .append(" pseudoTimeQueueIndex ").append(pseudoTimeQueueIndex) - .append(" pseudoTimeToWrite ").append(pseudoTimeToWrite) - .append(" pseudoTime ").append(pseudoTime) - .append(" flags ").append(flags) - .append(" pseudoTimeQueue.size() ").append(pseudoTimeQueue.size()) - .append(" stateOnlyQueueIndex ").append(stateOnlyQueueIndex) - .append(" parent.streamId ").append(parent == null ? -1 : parent.streamId).append("} ["); - - if (!pseudoTimeQueue.isEmpty()) { - for (State s : pseudoTimeQueue) { - s.toString(sb); - sb.append(", "); - } - // Remove the last ", " - sb.setLength(sb.length() - 2); - } - sb.append(']'); - } - } - - /** - * Allows a correlation to be made between a stream and its old parent before a parent change occurs. - */ - private static final class ParentChangedEvent { - final State state; - final State oldParent; - - /** - * Create a new instance. - * @param state The state who has had a parent change. - * @param oldParent The previous parent. - */ - ParentChangedEvent(State state, State oldParent) { - this.state = state; - this.oldParent = oldParent; - } - } -} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/package-info.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/package-info.java deleted file mode 100644 index c8c7cbcaed..0000000000 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/package-info.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -/** - * Handlers for sending and receiving HTTP/2 frames. - */ -@UnstableApi -package io.netty.handler.codec.http2; - -import io.netty.util.internal.UnstableApi; diff --git a/codec-http2/src/main/resources/META-INF/native-image/io.netty/codec-http2/native-image.properties b/codec-http2/src/main/resources/META-INF/native-image/io.netty/codec-http2/native-image.properties deleted file mode 100644 index c5218957bf..0000000000 --- a/codec-http2/src/main/resources/META-INF/native-image/io.netty/codec-http2/native-image.properties +++ /dev/null @@ -1,16 +0,0 @@ -# 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: -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -Args = --initialize-at-build-time=io.netty \ - --initialize-at-run-time=io.netty.handler.codec.http2.Http2CodecUtil,io.netty.handler.codec.http2.Http2ClientUpgradeCodec,io.netty.handler.codec.http2.Http2ConnectionHandler,io.netty.handler.codec.http2.DefaultHttp2FrameWriter diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/AbstractWeightedFairQueueByteDistributorDependencyTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/AbstractWeightedFairQueueByteDistributorDependencyTest.java deleted file mode 100644 index c796204dee..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/AbstractWeightedFairQueueByteDistributorDependencyTest.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.handler.codec.http2.Http2TestUtil.TestStreamByteDistributorStreamState; -import io.netty.util.collection.IntObjectHashMap; -import io.netty.util.collection.IntObjectMap; -import org.mockito.Mock; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; - -abstract class AbstractWeightedFairQueueByteDistributorDependencyTest { - Http2Connection connection; - WeightedFairQueueByteDistributor distributor; - private final IntObjectMap stateMap = - new IntObjectHashMap(); - - @Mock - StreamByteDistributor.Writer writer; - - Http2Stream stream(int streamId) { - return connection.stream(streamId); - } - - Answer writeAnswer(final boolean closeIfNoFrame) { - return in -> { - Http2Stream stream = in.getArgument(0); - int numBytes = in.getArgument(1); - TestStreamByteDistributorStreamState state = stateMap.get(stream.id()); - state.pendingBytes -= numBytes; - state.hasFrame = state.pendingBytes > 0; - state.isWriteAllowed = state.hasFrame; - if (closeIfNoFrame && !state.hasFrame) { - stream.close(); - } - distributor.updateStreamableBytes(state); - return null; - }; - } - - void initState(final int streamId, final long streamableBytes, final boolean hasFrame) { - initState(streamId, streamableBytes, hasFrame, hasFrame); - } - - void initState(final int streamId, final long pendingBytes, final boolean hasFrame, - final boolean isWriteAllowed) { - final Http2Stream stream = stream(streamId); - TestStreamByteDistributorStreamState state = new TestStreamByteDistributorStreamState(stream, pendingBytes, - hasFrame, isWriteAllowed); - stateMap.put(streamId, state); - distributor.updateStreamableBytes(state); - } - - void setPriority(int streamId, int parent, int weight, boolean exclusive) throws Http2Exception { - distributor.updateDependencyTree(streamId, parent, (short) weight, exclusive); - } -} 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 deleted file mode 100644 index d3c037837d..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/CleartextHttp2ServerUpgradeHandlerTest.java +++ /dev/null @@ -1,280 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -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.ChannelInitializer; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.http.DefaultHttpHeaders; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpServerCodec; -import io.netty.handler.codec.http.HttpServerUpgradeHandler; -import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeCodecFactory; -import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeEvent; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http.LastHttpContent; -import io.netty.handler.codec.http2.CleartextHttp2ServerUpgradeHandler.PriorKnowledgeUpgradeEvent; -import io.netty.handler.codec.http2.Http2Stream.State; -import io.netty.util.CharsetUtil; -import io.netty.util.ReferenceCountUtil; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.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} - */ -public class CleartextHttp2ServerUpgradeHandlerTest { - private EmbeddedChannel channel; - - private Http2FrameListener frameListener; - - private Http2ConnectionHandler http2ConnectionHandler; - - private List userEvents; - - private void setUpServerChannel() { - frameListener = mock(Http2FrameListener.class); - - http2ConnectionHandler = new Http2ConnectionHandlerBuilder() - .frameListener(frameListener).build(); - - UpgradeCodecFactory upgradeCodecFactory = protocol -> new Http2ServerUpgradeCodec(http2ConnectionHandler); - - userEvents = new ArrayList<>(); - - HttpServerCodec httpServerCodec = new HttpServerCodec(); - HttpServerUpgradeHandler upgradeHandler = new HttpServerUpgradeHandler(httpServerCodec, upgradeCodecFactory); - - CleartextHttp2ServerUpgradeHandler handler = new CleartextHttp2ServerUpgradeHandler( - httpServerCodec, upgradeHandler, http2ConnectionHandler); - channel = new EmbeddedChannel(handler, new ChannelHandler() { - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - userEvents.add(evt); - } - }); - } - - @AfterEach - public void tearDown() throws Exception { - channel.finishAndReleaseAll(); - } - - @Test - public void priorKnowledge() throws Exception { - setUpServerChannel(); - - channel.writeInbound(Http2CodecUtil.connectionPrefaceBuf()); - - ByteBuf settingsFrame = settingsFrameBuf(); - - assertFalse(channel.writeInbound(settingsFrame)); - - assertEquals(1, userEvents.size()); - assertTrue(userEvents.get(0) instanceof PriorKnowledgeUpgradeEvent); - - assertEquals(100, http2ConnectionHandler.connection().local().maxActiveStreams()); - assertEquals(65535, http2ConnectionHandler.connection().local().flowController().initialWindowSize()); - - verify(frameListener).onSettingsRead( - any(ChannelHandlerContext.class), eq(expectedSettings())); - } - - @Test - public void upgrade() throws Exception { - 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"; - validateClearTextUpgrade(upgradeString); - } - - @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); - } - - @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 - public void priorKnowledgeInFragments() throws Exception { - setUpServerChannel(); - - ByteBuf connectionPreface = Http2CodecUtil.connectionPrefaceBuf(); - assertFalse(channel.writeInbound(connectionPreface.readBytes(5), connectionPreface)); - - ByteBuf settingsFrame = settingsFrameBuf(); - assertFalse(channel.writeInbound(settingsFrame)); - - assertEquals(1, userEvents.size()); - assertTrue(userEvents.get(0) instanceof PriorKnowledgeUpgradeEvent); - - assertEquals(100, http2ConnectionHandler.connection().local().maxActiveStreams()); - assertEquals(65535, http2ConnectionHandler.connection().local().flowController().initialWindowSize()); - - verify(frameListener).onSettingsRead( - any(ChannelHandlerContext.class), eq(expectedSettings())); - } - - @Test - public void downgrade() throws Exception { - setUpServerChannel(); - - String requestString = "GET / HTTP/1.1\r\n" + - "Host: example.com\r\n\r\n"; - ByteBuf inbound = Unpooled.buffer().writeBytes(requestString.getBytes(CharsetUtil.US_ASCII)); - - assertTrue(channel.writeInbound(inbound)); - - Object firstInbound = channel.readInbound(); - assertTrue(firstInbound instanceof HttpRequest); - HttpRequest request = (HttpRequest) firstInbound; - assertEquals(HttpMethod.GET, request.method()); - assertEquals("/", request.uri()); - assertEquals(HttpVersion.HTTP_1_1, request.protocolVersion()); - assertEquals(new DefaultHttpHeaders().add("Host", "example.com"), request.headers()); - - ((LastHttpContent) channel.readInbound()).release(); - - assertNull(channel.readInbound()); - } - - @Test - public void usedHttp2MultiplexCodec() throws Exception { - final Http2MultiplexCodec http2Codec = new Http2MultiplexCodecBuilder(true, new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - } - }).build(); - UpgradeCodecFactory upgradeCodecFactory = protocol -> new Http2ServerUpgradeCodec(http2Codec); - http2ConnectionHandler = http2Codec; - - userEvents = new ArrayList<>(); - - HttpServerCodec httpServerCodec = new HttpServerCodec(); - HttpServerUpgradeHandler upgradeHandler = new HttpServerUpgradeHandler(httpServerCodec, upgradeCodecFactory); - - CleartextHttp2ServerUpgradeHandler handler = new CleartextHttp2ServerUpgradeHandler( - httpServerCodec, upgradeHandler, http2Codec); - channel = new EmbeddedChannel(handler, new ChannelHandler() { - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - userEvents.add(evt); - } - }); - - assertFalse(channel.writeInbound(Http2CodecUtil.connectionPrefaceBuf())); - - ByteBuf settingsFrame = settingsFrameBuf(); - - assertTrue(channel.writeInbound(settingsFrame)); - - assertEquals(1, userEvents.size()); - assertTrue(userEvents.get(0) instanceof PriorKnowledgeUpgradeEvent); - } - - private static ByteBuf settingsFrameBuf() { - ByteBuf settingsFrame = Unpooled.buffer(); - settingsFrame.writeMedium(12); // Payload length - settingsFrame.writeByte(0x4); // Frame type - settingsFrame.writeByte(0x0); // Flags - settingsFrame.writeInt(0x0); // StreamId - settingsFrame.writeShort(0x3); - settingsFrame.writeInt(100); - settingsFrame.writeShort(0x4); - settingsFrame.writeInt(65535); - - return settingsFrame; - } - - 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()); - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/DataCompressionHttp2Test.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/DataCompressionHttp2Test.java deleted file mode 100644 index e6de11691a..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/DataCompressionHttp2Test.java +++ /dev/null @@ -1,438 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -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.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.util.AsciiString; -import io.netty.util.CharsetUtil; -import io.netty.util.NetUtil; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.net.InetSocketAddress; -import java.util.Random; -import java.util.concurrent.CountDownLatch; - -import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_PRIORITY_WEIGHT; -import static io.netty.handler.codec.http2.Http2TestUtil.runInChannel; -import static java.util.concurrent.TimeUnit.MILLISECONDS; -import static java.util.concurrent.TimeUnit.SECONDS; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyBoolean; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.anyShort; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.verify; - -/** - * Test for data decompression in the HTTP/2 codec. - */ -public class DataCompressionHttp2Test { - private static final AsciiString GET = new AsciiString("GET"); - private static final AsciiString POST = new AsciiString("POST"); - private static final AsciiString PATH = new AsciiString("/some/path"); - - @Mock - private Http2FrameListener serverListener; - @Mock - private Http2FrameListener clientListener; - - private Http2ConnectionEncoder clientEncoder; - private ServerBootstrap sb; - private Bootstrap cb; - private Channel serverChannel; - private Channel clientChannel; - private volatile Channel serverConnectedChannel; - private CountDownLatch serverLatch; - private Http2Connection serverConnection; - private Http2Connection clientConnection; - private Http2ConnectionHandler clientHandler; - private ByteArrayOutputStream serverOut; - - @BeforeEach - public void setup() throws InterruptedException, Http2Exception { - MockitoAnnotations.initMocks(this); - doAnswer(invocation -> { - if (invocation.getArgument(4)) { - serverConnection.stream((Integer) invocation.getArgument(1)).close(); - } - return null; - }).when(serverListener).onHeadersRead(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class), - anyInt(), anyBoolean()); - doAnswer(invocation -> { - if (invocation.getArgument(7)) { - serverConnection.stream((Integer) invocation.getArgument(1)).close(); - } - return null; - }).when(serverListener).onHeadersRead(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class), - anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean()); - } - - @AfterEach - public void cleanup() throws IOException { - serverOut.close(); - } - - @AfterEach - public void teardown() throws InterruptedException { - if (clientChannel != null) { - clientChannel.close().sync(); - clientChannel = null; - } - if (serverChannel != null) { - serverChannel.close().sync(); - serverChannel = null; - } - final Channel serverConnectedChannel = this.serverConnectedChannel; - if (serverConnectedChannel != null) { - serverConnectedChannel.close().sync(); - this.serverConnectedChannel = null; - } - Future serverGroup = sb.config().group().shutdownGracefully(0, 0, MILLISECONDS); - Future serverChildGroup = sb.config().childGroup().shutdownGracefully(0, 0, MILLISECONDS); - Future clientGroup = cb.config().group().shutdownGracefully(0, 0, MILLISECONDS); - serverGroup.sync(); - serverChildGroup.sync(); - clientGroup.sync(); - } - - @Test - public void justHeadersNoData() throws Exception { - bootstrapEnv(0); - final Http2Headers headers = new DefaultHttp2Headers().method(GET).path(PATH) - .set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.GZIP); - - runInChannel(clientChannel, () -> { - clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, true); - clientHandler.flush(ctxClient()); - }); - awaitServer(); - verify(serverListener).onHeadersRead(any(ChannelHandlerContext.class), eq(3), eq(headers), eq(0), - eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), eq(true)); - } - - @Test - public void gzipEncodingSingleEmptyMessage() throws Exception { - final String text = ""; - final ByteBuf data = Unpooled.copiedBuffer(text.getBytes()); - bootstrapEnv(data.readableBytes()); - try { - final Http2Headers headers = new DefaultHttp2Headers().method(POST).path(PATH) - .set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.GZIP); - - runInChannel(clientChannel, () -> { - clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false); - clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true); - clientHandler.flush(ctxClient()); - }); - awaitServer(); - assertEquals(text, serverOut.toString(CharsetUtil.UTF_8.name())); - } finally { - data.release(); - } - } - - @Test - public void gzipEncodingSingleMessage() throws Exception { - final String text = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccc"; - final ByteBuf data = Unpooled.copiedBuffer(text.getBytes()); - bootstrapEnv(data.readableBytes()); - try { - final Http2Headers headers = new DefaultHttp2Headers().method(POST).path(PATH) - .set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.GZIP); - - runInChannel(clientChannel, () -> { - clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false); - clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true); - clientHandler.flush(ctxClient()); - }); - awaitServer(); - assertEquals(text, serverOut.toString(CharsetUtil.UTF_8.name())); - } finally { - data.release(); - } - } - - @Test - public void gzipEncodingMultipleMessages() throws Exception { - final String text1 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccc"; - final String text2 = "dddddddddddddddddddeeeeeeeeeeeeeeeeeeeffffffffffffffffffff"; - final ByteBuf data1 = Unpooled.copiedBuffer(text1.getBytes()); - final ByteBuf data2 = Unpooled.copiedBuffer(text2.getBytes()); - bootstrapEnv(data1.readableBytes() + data2.readableBytes()); - try { - final Http2Headers headers = new DefaultHttp2Headers().method(POST).path(PATH) - .set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.GZIP); - - runInChannel(clientChannel, () -> { - clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false); - clientEncoder.writeData(ctxClient(), 3, data1.retain(), 0, false); - clientEncoder.writeData(ctxClient(), 3, data2.retain(), 0, true); - clientHandler.flush(ctxClient()); - }); - awaitServer(); - assertEquals(text1 + text2, serverOut.toString(CharsetUtil.UTF_8.name())); - } finally { - data1.release(); - data2.release(); - } - } - - @Test - public void brotliEncodingSingleEmptyMessage() throws Exception { - final String text = ""; - final ByteBuf data = Unpooled.copiedBuffer(text.getBytes()); - bootstrapEnv(data.readableBytes()); - try { - final Http2Headers headers = new DefaultHttp2Headers().method(POST).path(PATH) - .set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.BR); - - runInChannel(clientChannel, () -> { - clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false); - clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true); - clientHandler.flush(ctxClient()); - }); - awaitServer(); - assertEquals(text, serverOut.toString(CharsetUtil.UTF_8.name())); - } finally { - data.release(); - } - } - - @Test - public void brotliEncodingSingleMessage() throws Exception { - final String text = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccc"; - final ByteBuf data = Unpooled.copiedBuffer(text.getBytes(CharsetUtil.UTF_8.name())); - bootstrapEnv(data.readableBytes()); - try { - final Http2Headers headers = new DefaultHttp2Headers().method(POST).path(PATH) - .set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.BR); - - runInChannel(clientChannel, () -> { - clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false); - clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true); - clientHandler.flush(ctxClient()); - }); - awaitServer(); - assertEquals(text, serverOut.toString(CharsetUtil.UTF_8.name())); - } finally { - data.release(); - } - } - - @Test - public void zstdEncodingSingleEmptyMessage() throws Exception { - final String text = ""; - final ByteBuf data = Unpooled.copiedBuffer(text.getBytes()); - bootstrapEnv(data.readableBytes()); - try { - final Http2Headers headers = new DefaultHttp2Headers().method(POST).path(PATH) - .set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.ZSTD); - - runInChannel(clientChannel, () -> { - clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false); - clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true); - clientHandler.flush(ctxClient()); - }); - awaitServer(); - assertEquals(text, serverOut.toString(CharsetUtil.UTF_8.name())); - } finally { - data.release(); - } - } - - @Test - public void zstdEncodingSingleMessage() throws Exception { - final String text = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccc"; - final ByteBuf data = Unpooled.copiedBuffer(text.getBytes(CharsetUtil.UTF_8.name())); - bootstrapEnv(data.readableBytes()); - try { - final Http2Headers headers = new DefaultHttp2Headers().method(POST).path(PATH) - .set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.ZSTD); - - runInChannel(clientChannel, () -> { - clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false); - clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true); - clientHandler.flush(ctxClient()); - }); - awaitServer(); - assertEquals(text, serverOut.toString(CharsetUtil.UTF_8.name())); - } finally { - data.release(); - } - } - - @Test - public void deflateEncodingWriteLargeMessage() throws Exception { - final int BUFFER_SIZE = 1 << 12; - final byte[] bytes = new byte[BUFFER_SIZE]; - new Random().nextBytes(bytes); - bootstrapEnv(BUFFER_SIZE); - final ByteBuf data = Unpooled.wrappedBuffer(bytes); - try { - final Http2Headers headers = new DefaultHttp2Headers().method(POST).path(PATH) - .set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.DEFLATE); - - runInChannel(clientChannel, () -> { - clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false); - clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true); - clientHandler.flush(ctxClient()); - }); - awaitServer(); - assertEquals(data.readerIndex(0).toString(CharsetUtil.UTF_8), - serverOut.toString(CharsetUtil.UTF_8.name())); - } finally { - data.release(); - } - } - - private void bootstrapEnv(int serverOutSize) throws Exception { - final CountDownLatch prefaceWrittenLatch = new CountDownLatch(1); - serverOut = new ByteArrayOutputStream(serverOutSize); - serverLatch = new CountDownLatch(1); - sb = new ServerBootstrap(); - cb = new Bootstrap(); - - // Streams are created before the normal flow for this test, so these connection must be initialized up front. - serverConnection = new DefaultHttp2Connection(true); - clientConnection = new DefaultHttp2Connection(false); - - serverConnection.addListener(new Http2ConnectionAdapter() { - @Override - public void onStreamClosed(Http2Stream stream) { - serverLatch.countDown(); - } - }); - - doAnswer(in -> { - ByteBuf buf = (ByteBuf) in.getArguments()[2]; - int padding = (Integer) in.getArguments()[3]; - int processedBytes = buf.readableBytes() + padding; - - buf.readBytes(serverOut, buf.readableBytes()); - - if (in.getArgument(4)) { - serverConnection.stream((Integer) in.getArgument(1)).close(); - } - return processedBytes; - }).when(serverListener).onDataRead(any(ChannelHandlerContext.class), anyInt(), - any(ByteBuf.class), anyInt(), anyBoolean()); - - final CountDownLatch serverChannelLatch = new CountDownLatch(1); - sb.group(new MultithreadEventLoopGroup(NioHandler.newFactory()), - new MultithreadEventLoopGroup(NioHandler.newFactory())); - sb.channel(NioServerSocketChannel.class); - sb.childHandler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - serverConnectedChannel = ch; - ChannelPipeline p = ch.pipeline(); - Http2FrameWriter frameWriter = new DefaultHttp2FrameWriter(); - serverConnection.remote().flowController( - new DefaultHttp2RemoteFlowController(serverConnection)); - serverConnection.local().flowController( - new DefaultHttp2LocalFlowController(serverConnection).frameWriter(frameWriter)); - Http2ConnectionEncoder encoder = new CompressorHttp2ConnectionEncoder( - new DefaultHttp2ConnectionEncoder(serverConnection, frameWriter)); - Http2ConnectionDecoder decoder = - new DefaultHttp2ConnectionDecoder(serverConnection, encoder, new DefaultHttp2FrameReader()); - Http2ConnectionHandler connectionHandler = new Http2ConnectionHandlerBuilder() - .frameListener(new DelegatingDecompressorFrameListener(serverConnection, serverListener)) - .codec(decoder, encoder).build(); - p.addLast(connectionHandler); - serverChannelLatch.countDown(); - } - }); - - cb.group(new MultithreadEventLoopGroup(NioHandler.newFactory())); - cb.channel(NioSocketChannel.class); - cb.handler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - ChannelPipeline p = ch.pipeline(); - Http2FrameWriter frameWriter = new DefaultHttp2FrameWriter(); - clientConnection.remote().flowController( - new DefaultHttp2RemoteFlowController(clientConnection)); - clientConnection.local().flowController( - new DefaultHttp2LocalFlowController(clientConnection).frameWriter(frameWriter)); - clientEncoder = new CompressorHttp2ConnectionEncoder( - new DefaultHttp2ConnectionEncoder(clientConnection, frameWriter)); - - Http2ConnectionDecoder decoder = - new DefaultHttp2ConnectionDecoder(clientConnection, clientEncoder, - new DefaultHttp2FrameReader()); - clientHandler = new Http2ConnectionHandlerBuilder() - .frameListener(new DelegatingDecompressorFrameListener(clientConnection, clientListener)) - // By default tests don't wait for server to gracefully shutdown streams - .gracefulShutdownTimeoutMillis(0) - .codec(decoder, clientEncoder).build(); - p.addLast(clientHandler); - p.addLast(new ChannelHandler() { - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (evt == Http2ConnectionPrefaceAndSettingsFrameWrittenEvent.INSTANCE) { - prefaceWrittenLatch.countDown(); - ctx.pipeline().remove(this); - } - } - }); - } - }); - - serverChannel = sb.bind(new InetSocketAddress(0)).get(); - int port = ((InetSocketAddress) serverChannel.localAddress()).getPort(); - - clientChannel = cb.connect(new InetSocketAddress(NetUtil.LOCALHOST, port)).get(); - assertTrue(prefaceWrittenLatch.await(5, SECONDS)); - assertTrue(serverChannelLatch.await(5, SECONDS)); - } - - private void awaitServer() throws Exception { - assertTrue(serverLatch.await(5, SECONDS)); - serverOut.flush(); - } - - private ChannelHandlerContext ctxClient() { - return clientChannel.pipeline().firstContext(); - } - - private Promise newPromiseClient() { - return ctxClient().newPromise(); - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/DecoratingHttp2ConnectionEncoderTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/DecoratingHttp2ConnectionEncoderTest.java deleted file mode 100644 index fd72b36708..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/DecoratingHttp2ConnectionEncoderTest.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; - -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.times; - -import static org.junit.jupiter.api.Assertions.assertThrows; - -public class DecoratingHttp2ConnectionEncoderTest { - - @Test - public void testConsumeReceivedSettingsThrows() { - Http2ConnectionEncoder encoder = mock(Http2ConnectionEncoder.class); - final DecoratingHttp2ConnectionEncoder decoratingHttp2ConnectionEncoder = - new DecoratingHttp2ConnectionEncoder(encoder); - assertThrows(IllegalStateException.class, new Executable() { - @Override - public void execute() { - decoratingHttp2ConnectionEncoder.consumeReceivedSettings(Http2Settings.defaultSettings()); - } - }); - } - - @Test - public void testConsumeReceivedSettingsDelegate() { - TestHttp2ConnectionEncoder encoder = mock(TestHttp2ConnectionEncoder.class); - DecoratingHttp2ConnectionEncoder decoratingHttp2ConnectionEncoder = - new DecoratingHttp2ConnectionEncoder(encoder); - - Http2Settings settings = Http2Settings.defaultSettings(); - decoratingHttp2ConnectionEncoder.consumeReceivedSettings(Http2Settings.defaultSettings()); - verify(encoder, times(1)).consumeReceivedSettings(eq(settings)); - } - - private interface TestHttp2ConnectionEncoder extends Http2ConnectionEncoder, Http2SettingsReceivedConsumer { } -} 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 deleted file mode 100644 index e69758ec80..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoderTest.java +++ /dev/null @@ -1,997 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.ImmediateEventExecutor; -import io.netty.util.concurrent.Promise; -import junit.framework.AssertionFailedError; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; -import org.mockito.ArgumentCaptor; -import org.mockito.ArgumentMatchers; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; - -import java.util.IdentityHashMap; -import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; - -import static io.netty.buffer.Unpooled.EMPTY_BUFFER; -import static io.netty.buffer.Unpooled.wrappedBuffer; -import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_PRIORITY_WEIGHT; -import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR; -import static io.netty.handler.codec.http2.Http2Stream.State.IDLE; -import static io.netty.handler.codec.http2.Http2Stream.State.OPEN; -import static io.netty.handler.codec.http2.Http2Stream.State.RESERVED_REMOTE; -import static io.netty.util.CharsetUtil.UTF_8; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.not; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyBoolean; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.anyLong; -import static org.mockito.Mockito.anyShort; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.isNull; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -/** - * Tests for {@link DefaultHttp2ConnectionDecoder}. - */ -public class DefaultHttp2ConnectionDecoderTest { - private static final int STREAM_ID = 3; - private static final int PUSH_STREAM_ID = 2; - private static final int STREAM_DEPENDENCY_ID = 5; - private static final int STATE_RECV_HEADERS = 1; - private static final int STATE_RECV_TRAILERS = 1 << 1; - - private Http2ConnectionDecoder decoder; - - @Mock - private Http2Connection connection; - - @Mock - private Http2Connection.Endpoint remote; - - @Mock - private Http2Connection.Endpoint local; - - @Mock - private Http2LocalFlowController localFlow; - - @Mock - private Http2RemoteFlowController remoteFlow; - - @Mock - private ChannelHandlerContext ctx; - - @Mock - private Channel channel; - - @Mock - private Future future; - - @Mock - private Http2Stream stream; - - @Mock - private Http2Stream pushStream; - - @Mock - private Http2FrameListener listener; - - @Mock - private Http2FrameReader reader; - - @Mock - private Http2FrameWriter writer; - - @Mock - private Http2ConnectionEncoder encoder; - - @Mock - private Http2LifecycleManager lifecycleManager; - - @BeforeEach - public void setup() throws Exception { - MockitoAnnotations.initMocks(this); - - Promise promise = ImmediateEventExecutor.INSTANCE.newPromise(); - - final AtomicInteger headersReceivedState = new AtomicInteger(); - when(channel.isActive()).thenReturn(true); - when(stream.id()).thenReturn(STREAM_ID); - when(stream.state()).thenReturn(OPEN); - when(stream.open(anyBoolean())).thenReturn(stream); - - final Map properties = new IdentityHashMap(); - when(stream.getProperty(ArgumentMatchers.any())).thenAnswer(new Answer() { - @Override - public Object answer(InvocationOnMock invocationOnMock) { - return properties.get(invocationOnMock.getArgument(0)); - } - }); - when(stream.setProperty(ArgumentMatchers.any(), any())).then(new Answer() { - @Override - public Object answer(InvocationOnMock invocationOnMock) { - return properties.put(invocationOnMock.getArgument(0), invocationOnMock.getArgument(1)); - } - }); - - when(pushStream.id()).thenReturn(PUSH_STREAM_ID); - doAnswer((Answer) in -> - (headersReceivedState.get() & STATE_RECV_HEADERS) != 0).when(stream).isHeadersReceived(); - doAnswer((Answer) in -> - (headersReceivedState.get() & STATE_RECV_TRAILERS) != 0).when(stream).isTrailersReceived(); - doAnswer((Answer) in -> { - boolean isInformational = in.getArgument(0); - if (isInformational) { - return stream; - } - for (;;) { - int current = headersReceivedState.get(); - int next = current; - if ((current & STATE_RECV_HEADERS) != 0) { - if ((current & STATE_RECV_TRAILERS) != 0) { - throw new IllegalStateException("already sent headers!"); - } - next |= STATE_RECV_TRAILERS; - } else { - next |= STATE_RECV_HEADERS; - } - if (headersReceivedState.compareAndSet(current, next)) { - break; - } - } - return stream; - }).when(stream).headersReceived(anyBoolean()); - doAnswer((Answer) in -> { - Http2StreamVisitor visitor = in.getArgument(0); - if (!visitor.visit(stream)) { - return stream; - } - return null; - }).when(connection).forEachActiveStream(any(Http2StreamVisitor.class)); - when(connection.stream(STREAM_ID)).thenReturn(stream); - when(connection.streamMayHaveExisted(STREAM_ID)).thenReturn(true); - when(connection.local()).thenReturn(local); - when(local.flowController()).thenReturn(localFlow); - when(encoder.flowController()).thenReturn(remoteFlow); - when(encoder.frameWriter()).thenReturn(writer); - when(connection.remote()).thenReturn(remote); - when(local.reservePushStream(eq(PUSH_STREAM_ID), eq(stream))).thenReturn(pushStream); - when(remote.reservePushStream(eq(PUSH_STREAM_ID), eq(stream))).thenReturn(pushStream); - when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT); - when(ctx.channel()).thenReturn(channel); - when(ctx.newSucceededFuture()).thenReturn(future); - when(ctx.newPromise()).thenReturn(promise); - when(ctx.write(any())).thenReturn(future); - - decoder = new DefaultHttp2ConnectionDecoder(connection, encoder, reader); - decoder.lifecycleManager(lifecycleManager); - decoder.frameListener(listener); - - // Simulate receiving the initial settings from the remote endpoint. - decode().onSettingsRead(ctx, new Http2Settings()); - verify(listener).onSettingsRead(eq(ctx), eq(new Http2Settings())); - assertTrue(decoder.prefaceReceived()); - verify(encoder).writeSettingsAck(eq(ctx)); - - // Simulate receiving the SETTINGS ACK for the initial settings. - decode().onSettingsAckRead(ctx); - - // Disallow any further flushes now that settings ACK has been sent - when(ctx.flush()).thenThrow(new AssertionFailedError("forbidden")); - } - - @Test - public void dataReadAfterGoAwaySentShouldApplyFlowControl() throws Exception { - mockGoAwaySent(); - - final ByteBuf data = dummyData(); - int padding = 10; - int processedBytes = data.readableBytes() + padding; - mockFlowControl(processedBytes); - try { - decode().onDataRead(ctx, STREAM_ID, data, padding, true); - verify(localFlow).receiveFlowControlledFrame(eq(stream), eq(data), eq(padding), eq(true)); - verify(localFlow).consumeBytes(eq(stream), eq(processedBytes)); - - // Verify that the event was absorbed and not propagated to the observer. - verify(listener, never()).onDataRead(eq(ctx), anyInt(), any(ByteBuf.class), anyInt(), anyBoolean()); - } finally { - data.release(); - } - } - - @Test - public void dataReadAfterGoAwaySentShouldAllowFramesForStreamCreatedByLocalEndpoint() throws Exception { - mockGoAwaySentShouldAllowFramesForStreamCreatedByLocalEndpoint(); - - final ByteBuf data = dummyData(); - int padding = 10; - int processedBytes = data.readableBytes() + padding; - mockFlowControl(processedBytes); - try { - decode().onDataRead(ctx, STREAM_ID, data, padding, true); - verify(localFlow).receiveFlowControlledFrame(eq(stream), eq(data), eq(padding), eq(true)); - verify(localFlow).consumeBytes(eq(stream), eq(processedBytes)); - - // Verify that the event was absorbed and not propagated to the observer. - verify(listener).onDataRead(eq(ctx), anyInt(), any(ByteBuf.class), anyInt(), anyBoolean()); - } finally { - data.release(); - } - } - - @Test - public void dataReadForUnknownStreamShouldApplyFlowControlAndFail() throws Exception { - when(connection.streamMayHaveExisted(STREAM_ID)).thenReturn(true); - when(connection.stream(STREAM_ID)).thenReturn(null); - final ByteBuf data = dummyData(); - final int padding = 10; - int processedBytes = data.readableBytes() + padding; - assertThrows(Http2Exception.StreamException.class, new Executable() { - @Override - public void execute() throws Throwable { - decode().onDataRead(ctx, STREAM_ID, data, padding, true); - } - }); - try { - verify(localFlow) - .receiveFlowControlledFrame(eq((Http2Stream) null), eq(data), eq(padding), eq(true)); - verify(localFlow).consumeBytes(eq((Http2Stream) null), eq(processedBytes)); - verify(localFlow).frameWriter(any(Http2FrameWriter.class)); - verifyNoMoreInteractions(localFlow); - verify(listener, never()).onDataRead(eq(ctx), anyInt(), any(ByteBuf.class), anyInt(), anyBoolean()); - } finally { - data.release(); - } - } - - @Test - public void dataReadForUnknownStreamThatCouldntExistFail() throws Exception { - when(connection.streamMayHaveExisted(STREAM_ID)).thenReturn(false); - when(connection.stream(STREAM_ID)).thenReturn(null); - final ByteBuf data = dummyData(); - final int padding = 10; - int processedBytes = data.readableBytes() + padding; - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - try { - decode().onDataRead(ctx, STREAM_ID, data, padding, true); - } catch (Http2Exception ex) { - assertThat(ex, not(instanceOf(Http2Exception.StreamException.class))); - throw ex; - } - } - }); - try { - verify(localFlow) - .receiveFlowControlledFrame(eq((Http2Stream) null), eq(data), eq(padding), eq(true)); - verify(localFlow).consumeBytes(eq((Http2Stream) null), eq(processedBytes)); - verify(localFlow).frameWriter(any(Http2FrameWriter.class)); - verifyNoMoreInteractions(localFlow); - verify(listener, never()).onDataRead(eq(ctx), anyInt(), any(ByteBuf.class), anyInt(), anyBoolean()); - } finally { - data.release(); - } - } - - @Test - public void dataReadForUnknownStreamShouldApplyFlowControl() throws Exception { - when(connection.stream(STREAM_ID)).thenReturn(null); - final ByteBuf data = dummyData(); - final int padding = 10; - int processedBytes = data.readableBytes() + padding; - try { - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - decode().onDataRead(ctx, STREAM_ID, data, padding, true); - } - }); - verify(localFlow) - .receiveFlowControlledFrame(eq((Http2Stream) null), eq(data), eq(padding), eq(true)); - verify(localFlow).consumeBytes(eq((Http2Stream) null), eq(processedBytes)); - verify(localFlow).frameWriter(any(Http2FrameWriter.class)); - verifyNoMoreInteractions(localFlow); - - // Verify that the event was absorbed and not propagated to the observer. - verify(listener, never()).onDataRead(eq(ctx), anyInt(), any(ByteBuf.class), anyInt(), anyBoolean()); - } finally { - data.release(); - } - } - - @Test - public void emptyDataFrameShouldApplyFlowControl() throws Exception { - final ByteBuf data = EMPTY_BUFFER; - int padding = 0; - mockFlowControl(0); - try { - decode().onDataRead(ctx, STREAM_ID, data, padding, true); - verify(localFlow).receiveFlowControlledFrame(eq(stream), eq(data), eq(padding), eq(true)); - - // Now we ignore the empty bytes inside consumeBytes method, so it will be called once. - verify(localFlow).consumeBytes(eq(stream), eq(0)); - - // Verify that the empty data event was propagated to the observer. - verify(listener).onDataRead(eq(ctx), eq(STREAM_ID), eq(data), eq(padding), eq(true)); - } finally { - data.release(); - } - } - - @Test - public void dataReadForStreamInInvalidStateShouldThrow() throws Exception { - // Throw an exception when checking stream state. - when(stream.state()).thenReturn(Http2Stream.State.CLOSED); - final ByteBuf data = dummyData(); - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - decode().onDataRead(ctx, STREAM_ID, data, 10, true); - } - }); - data.release(); - } - - @Test - public void dataReadAfterGoAwaySentForStreamInInvalidStateShouldIgnore() throws Exception { - // Throw an exception when checking stream state. - when(stream.state()).thenReturn(Http2Stream.State.CLOSED); - mockGoAwaySent(); - final ByteBuf data = dummyData(); - try { - decode().onDataRead(ctx, STREAM_ID, data, 10, true); - verify(localFlow).receiveFlowControlledFrame(eq(stream), eq(data), eq(10), eq(true)); - verify(listener, never()).onDataRead(eq(ctx), anyInt(), any(ByteBuf.class), anyInt(), anyBoolean()); - } finally { - data.release(); - } - } - - @Test - public void dataReadAfterGoAwaySentOnUnknownStreamShouldIgnore() throws Exception { - // Throw an exception when checking stream state. - when(connection.stream(STREAM_ID)).thenReturn(null); - mockGoAwaySent(); - final ByteBuf data = dummyData(); - try { - decode().onDataRead(ctx, STREAM_ID, data, 10, true); - verify(localFlow).receiveFlowControlledFrame((Http2Stream) isNull(), eq(data), eq(10), eq(true)); - verify(listener, never()).onDataRead(eq(ctx), anyInt(), any(ByteBuf.class), anyInt(), anyBoolean()); - } finally { - data.release(); - } - } - - @Test - public void dataReadAfterRstStreamForStreamInInvalidStateShouldIgnore() throws Exception { - // Throw an exception when checking stream state. - when(stream.state()).thenReturn(Http2Stream.State.CLOSED); - when(stream.isResetSent()).thenReturn(true); - final ByteBuf data = dummyData(); - try { - decode().onDataRead(ctx, STREAM_ID, data, 10, true); - verify(localFlow).receiveFlowControlledFrame(eq(stream), eq(data), eq(10), eq(true)); - verify(listener, never()).onDataRead(eq(ctx), anyInt(), any(ByteBuf.class), anyInt(), anyBoolean()); - } finally { - data.release(); - } - } - - @Test - public void dataReadWithEndOfStreamShouldcloseStreamRemote() throws Exception { - final ByteBuf data = dummyData(); - try { - decode().onDataRead(ctx, STREAM_ID, data, 10, true); - verify(localFlow).receiveFlowControlledFrame(eq(stream), eq(data), eq(10), eq(true)); - verify(lifecycleManager).closeStreamRemote(eq(stream), eq(future)); - verify(listener).onDataRead(eq(ctx), eq(STREAM_ID), eq(data), eq(10), eq(true)); - } finally { - data.release(); - } - } - - @Test - public void errorDuringDeliveryShouldReturnCorrectNumberOfBytes() throws Exception { - final ByteBuf data = dummyData(); - final int padding = 10; - final AtomicInteger unprocessed = new AtomicInteger(data.readableBytes() + padding); - doAnswer((Answer) in -> unprocessed.get()).when(localFlow).unconsumedBytes(eq(stream)); - doAnswer((Answer) in -> { - int delta = (Integer) in.getArguments()[1]; - int newValue = unprocessed.addAndGet(-delta); - if (newValue < 0) { - throw new RuntimeException("Returned too many bytes"); - } - return null; - }).when(localFlow).consumeBytes(eq(stream), anyInt()); - // When the listener callback is called, process a few bytes and then throw. - doAnswer((Answer) in -> { - localFlow.consumeBytes(stream, 4); - throw new RuntimeException("Fake Exception"); - }).when(listener).onDataRead(eq(ctx), eq(STREAM_ID), any(ByteBuf.class), eq(10), eq(true)); - try { - assertThrows(RuntimeException.class, new Executable() { - @Override - public void execute() throws Throwable { - decode().onDataRead(ctx, STREAM_ID, data, padding, true); - } - }); - verify(localFlow) - .receiveFlowControlledFrame(eq(stream), eq(data), eq(padding), eq(true)); - verify(listener).onDataRead(eq(ctx), eq(STREAM_ID), eq(data), eq(padding), eq(true)); - assertEquals(0, localFlow.unconsumedBytes(stream)); - } finally { - data.release(); - } - } - - @Test - public void headersReadForUnknownStreamShouldThrow() throws Exception { - when(connection.stream(STREAM_ID)).thenReturn(null); - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - decode().onHeadersRead(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false); - } - }); - } - - @Test - public void headersReadForStreamThatAlreadySentResetShouldBeIgnored() throws Exception { - when(stream.isResetSent()).thenReturn(true); - decode().onHeadersRead(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false); - verify(remote, never()).createStream(anyInt(), anyBoolean()); - verify(stream, never()).open(anyBoolean()); - - // Verify that the event was absorbed and not propagated to the observer. - verify(listener, never()).onHeadersRead(eq(ctx), anyInt(), any(Http2Headers.class), anyInt(), anyBoolean()); - verify(remote, never()).createStream(anyInt(), anyBoolean()); - verify(stream, never()).open(anyBoolean()); - } - - @Test - public void headersReadForUnknownStreamAfterGoAwayShouldBeIgnored() throws Exception { - mockGoAwaySent(); - when(connection.stream(STREAM_ID)).thenReturn(null); - decode().onHeadersRead(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false); - verify(remote, never()).createStream(anyInt(), anyBoolean()); - verify(stream, never()).open(anyBoolean()); - - // Verify that the event was absorbed and not propagated to the observer. - verify(listener, never()).onHeadersRead(eq(ctx), anyInt(), any(Http2Headers.class), anyInt(), anyBoolean()); - verify(remote, never()).createStream(anyInt(), anyBoolean()); - verify(stream, never()).open(anyBoolean()); - } - - @Test - public void headersReadForUnknownStreamShouldCreateStream() throws Exception { - final int streamId = 5; - when(remote.createStream(eq(streamId), anyBoolean())).thenReturn(stream); - decode().onHeadersRead(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false); - verify(remote).createStream(eq(streamId), eq(false)); - verify(listener).onHeadersRead(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), eq(0), - eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), eq(false)); - } - - @Test - public void headersReadForUnknownStreamShouldCreateHalfClosedStream() throws Exception { - final int streamId = 5; - when(remote.createStream(eq(streamId), anyBoolean())).thenReturn(stream); - decode().onHeadersRead(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, true); - verify(remote).createStream(eq(streamId), eq(true)); - verify(listener).onHeadersRead(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), eq(0), - eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), eq(true)); - } - - @Test - public void headersReadForPromisedStreamShouldHalfOpenStream() throws Exception { - when(stream.state()).thenReturn(RESERVED_REMOTE); - decode().onHeadersRead(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false); - verify(stream).open(false); - verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE), eq(0), - eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), eq(false)); - } - - @Test - public void trailersDoNotEndStreamThrows() throws Exception { - decode().onHeadersRead(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false); - // Trailers must end the stream! - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - decode().onHeadersRead(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false); - } - }); - } - - @Test - public void tooManyHeadersEOSThrows() throws Exception { - tooManyHeaderThrows(true); - } - - @Test - public void tooManyHeadersNoEOSThrows() throws Exception { - tooManyHeaderThrows(false); - } - - private void tooManyHeaderThrows(final boolean eos) throws Exception { - decode().onHeadersRead(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false); - decode().onHeadersRead(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true); - // We already received the trailers! - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - decode().onHeadersRead(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, eos); - } - }); - } - - private static Http2Headers informationalHeaders() { - Http2Headers headers = new DefaultHttp2Headers(); - headers.status(HttpResponseStatus.CONTINUE.codeAsText()); - return headers; - } - - @Test - public void infoHeadersAndTrailersAllowed() throws Exception { - infoHeadersAndTrailersAllowed(true, 1); - } - - @Test - public void multipleInfoHeadersAndTrailersAllowed() throws Exception { - infoHeadersAndTrailersAllowed(true, 10); - } - - @Test - public void infoHeadersAndTrailersNoEOSThrows() throws Exception { - infoHeadersAndTrailersAllowed(false, 1); - } - - @Test - public void multipleInfoHeadersAndTrailersNoEOSThrows() throws Exception { - infoHeadersAndTrailersAllowed(false, 10); - } - - private void infoHeadersAndTrailersAllowed(final boolean eos, int infoHeaderCount) - throws Exception { - for (int i = 0; i < infoHeaderCount; ++i) { - decode().onHeadersRead(ctx, STREAM_ID, informationalHeaders(), 0, false); - } - decode().onHeadersRead(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false); - if (eos) { - decode().onHeadersRead(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, eos); - } else { - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - decode().onHeadersRead(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, eos); - } - }); - } - } - - @Test - public void headersReadForPromisedStreamShouldCloseStream() throws Exception { - when(stream.state()).thenReturn(RESERVED_REMOTE); - decode().onHeadersRead(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true); - verify(stream).open(true); - verify(lifecycleManager).closeStreamRemote(eq(stream), eq(future)); - verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE), eq(0), - eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), eq(true)); - } - - @SuppressWarnings("unchecked") - @Test - public void headersDependencyNotCreatedShouldCreateAndSucceed() throws Exception { - final short weight = 1; - decode().onHeadersRead(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, STREAM_DEPENDENCY_ID, - weight, true, 0, true); - verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE), eq(STREAM_DEPENDENCY_ID), - eq(weight), eq(true), eq(0), eq(true)); - verify(remoteFlow).updateDependencyTree(eq(STREAM_ID), eq(STREAM_DEPENDENCY_ID), eq(weight), eq(true)); - verify(lifecycleManager).closeStreamRemote(eq(stream), any(Future.class)); - } - - @Test - public void pushPromiseReadAfterGoAwaySentShouldBeIgnored() throws Exception { - mockGoAwaySent(); - decode().onPushPromiseRead(ctx, STREAM_ID, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0); - verify(remote, never()).reservePushStream(anyInt(), any(Http2Stream.class)); - verify(listener, never()).onPushPromiseRead(eq(ctx), anyInt(), anyInt(), any(Http2Headers.class), anyInt()); - } - - @Test - public void pushPromiseReadAfterGoAwayShouldAllowFramesForStreamCreatedByLocalEndpoint() throws Exception { - mockGoAwaySentShouldAllowFramesForStreamCreatedByLocalEndpoint(); - decode().onPushPromiseRead(ctx, STREAM_ID, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0); - verify(remote).reservePushStream(anyInt(), any(Http2Stream.class)); - verify(listener).onPushPromiseRead(eq(ctx), anyInt(), anyInt(), any(Http2Headers.class), anyInt()); - } - - @Test - public void pushPromiseReadForUnknownStreamShouldThrow() throws Exception { - when(connection.stream(STREAM_ID)).thenReturn(null); - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - decode().onPushPromiseRead(ctx, STREAM_ID, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0); - } - }); - } - - @Test - public void pushPromiseReadShouldSucceed() throws Exception { - decode().onPushPromiseRead(ctx, STREAM_ID, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0); - verify(remote).reservePushStream(eq(PUSH_STREAM_ID), eq(stream)); - verify(listener).onPushPromiseRead(eq(ctx), eq(STREAM_ID), eq(PUSH_STREAM_ID), - eq(EmptyHttp2Headers.INSTANCE), eq(0)); - } - - @Test - public void priorityReadAfterGoAwaySentShouldAllowFramesForStreamCreatedByLocalEndpoint() throws Exception { - mockGoAwaySentShouldAllowFramesForStreamCreatedByLocalEndpoint(); - decode().onPriorityRead(ctx, STREAM_ID, 0, (short) 255, true); - verify(remoteFlow).updateDependencyTree(eq(STREAM_ID), eq(0), eq((short) 255), eq(true)); - verify(listener).onPriorityRead(eq(ctx), anyInt(), anyInt(), anyShort(), anyBoolean()); - } - - @Test - public void priorityReadForUnknownStreamShouldNotBeIgnored() throws Exception { - when(connection.stream(STREAM_ID)).thenReturn(null); - decode().onPriorityRead(ctx, STREAM_ID, 0, (short) 255, true); - verify(remoteFlow).updateDependencyTree(eq(STREAM_ID), eq(0), eq((short) 255), eq(true)); - verify(listener).onPriorityRead(eq(ctx), eq(STREAM_ID), eq(0), eq((short) 255), eq(true)); - } - - @Test - public void priorityReadShouldNotCreateNewStream() throws Exception { - when(connection.streamMayHaveExisted(STREAM_ID)).thenReturn(false); - when(connection.stream(STREAM_ID)).thenReturn(null); - decode().onPriorityRead(ctx, STREAM_ID, STREAM_DEPENDENCY_ID, (short) 255, true); - verify(remoteFlow).updateDependencyTree(eq(STREAM_ID), eq(STREAM_DEPENDENCY_ID), eq((short) 255), eq(true)); - verify(listener).onPriorityRead(eq(ctx), eq(STREAM_ID), eq(STREAM_DEPENDENCY_ID), eq((short) 255), eq(true)); - verify(remote, never()).createStream(eq(STREAM_ID), anyBoolean()); - verify(stream, never()).open(anyBoolean()); - } - - @Test - public void windowUpdateReadAfterGoAwaySentShouldBeIgnored() throws Exception { - mockGoAwaySent(); - decode().onWindowUpdateRead(ctx, STREAM_ID, 10); - verify(remoteFlow, never()).incrementWindowSize(any(Http2Stream.class), anyInt()); - verify(listener, never()).onWindowUpdateRead(eq(ctx), anyInt(), anyInt()); - } - - @Test - public void windowUpdateReadAfterGoAwaySentShouldAllowFramesForStreamCreatedByLocalEndpoint() throws Exception { - mockGoAwaySentShouldAllowFramesForStreamCreatedByLocalEndpoint(); - decode().onWindowUpdateRead(ctx, STREAM_ID, 10); - verify(remoteFlow).incrementWindowSize(any(Http2Stream.class), anyInt()); - verify(listener).onWindowUpdateRead(eq(ctx), anyInt(), anyInt()); - } - - @Test - public void windowUpdateReadForUnknownStreamShouldThrow() throws Exception { - when(connection.streamMayHaveExisted(STREAM_ID)).thenReturn(false); - when(connection.stream(STREAM_ID)).thenReturn(null); - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - decode().onWindowUpdateRead(ctx, STREAM_ID, 10); - } - }); - } - - @Test - public void windowUpdateReadForUnknownStreamShouldBeIgnored() throws Exception { - when(connection.stream(STREAM_ID)).thenReturn(null); - decode().onWindowUpdateRead(ctx, STREAM_ID, 10); - verify(remoteFlow, never()).incrementWindowSize(any(Http2Stream.class), anyInt()); - verify(listener, never()).onWindowUpdateRead(eq(ctx), anyInt(), anyInt()); - } - - @Test - public void windowUpdateReadShouldSucceed() throws Exception { - decode().onWindowUpdateRead(ctx, STREAM_ID, 10); - verify(remoteFlow).incrementWindowSize(eq(stream), eq(10)); - verify(listener).onWindowUpdateRead(eq(ctx), eq(STREAM_ID), eq(10)); - } - - @Test - public void rstStreamReadAfterGoAwayShouldSucceed() throws Exception { - when(connection.goAwaySent()).thenReturn(true); - decode().onRstStreamRead(ctx, STREAM_ID, PROTOCOL_ERROR.code()); - verify(lifecycleManager).closeStream(eq(stream), eq(future)); - verify(listener).onRstStreamRead(eq(ctx), anyInt(), anyLong()); - } - - @Test - public void rstStreamReadForUnknownStreamShouldThrow() throws Exception { - when(connection.streamMayHaveExisted(STREAM_ID)).thenReturn(false); - when(connection.stream(STREAM_ID)).thenReturn(null); - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - decode().onRstStreamRead(ctx, STREAM_ID, PROTOCOL_ERROR.code()); - } - }); - } - - @Test - public void rstStreamReadForUnknownStreamShouldBeIgnored() throws Exception { - when(connection.stream(STREAM_ID)).thenReturn(null); - decode().onRstStreamRead(ctx, STREAM_ID, PROTOCOL_ERROR.code()); - verify(lifecycleManager, never()).closeStream(eq(stream), eq(future)); - verify(listener, never()).onRstStreamRead(eq(ctx), anyInt(), anyLong()); - } - - @Test - public void rstStreamReadShouldCloseStream() throws Exception { - decode().onRstStreamRead(ctx, STREAM_ID, PROTOCOL_ERROR.code()); - verify(lifecycleManager).closeStream(eq(stream), eq(future)); - verify(listener).onRstStreamRead(eq(ctx), eq(STREAM_ID), eq(PROTOCOL_ERROR.code())); - } - - @Test - public void rstStreamOnIdleStreamShouldThrow() throws Exception { - when(stream.state()).thenReturn(IDLE); - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - decode().onRstStreamRead(ctx, STREAM_ID, PROTOCOL_ERROR.code()); - } - }); - verify(listener, never()).onRstStreamRead(any(ChannelHandlerContext.class), anyInt(), anyLong()); - } - - @Test - public void pingReadWithAckShouldNotifyListener() throws Exception { - decode().onPingAckRead(ctx, 0L); - verify(listener).onPingAckRead(eq(ctx), eq(0L)); - } - - @Test - public void pingReadShouldReplyWithAck() throws Exception { - decode().onPingRead(ctx, 0L); - verify(encoder).writePing(eq(ctx), eq(true), eq(0L)); - verify(listener, never()).onPingAckRead(eq(ctx), any(long.class)); - } - - @Test - public void settingsReadWithAckShouldNotifyListener() throws Exception { - decode().onSettingsAckRead(ctx); - // Take into account the time this was called during setup(). - verify(listener, times(2)).onSettingsAckRead(eq(ctx)); - } - - @Test - public void settingsReadShouldSetValues() throws Exception { - Http2Settings settings = new Http2Settings(); - settings.pushEnabled(true); - settings.initialWindowSize(123); - settings.maxConcurrentStreams(456); - settings.headerTableSize(789); - decode().onSettingsRead(ctx, settings); - verify(encoder).remoteSettings(settings); - verify(listener).onSettingsRead(eq(ctx), eq(settings)); - } - - @Test - public void goAwayShouldReadShouldUpdateConnectionState() throws Exception { - decode().onGoAwayRead(ctx, 1, 2L, EMPTY_BUFFER); - verify(connection).goAwayReceived(eq(1), eq(2L), eq(EMPTY_BUFFER)); - verify(listener).onGoAwayRead(eq(ctx), eq(1), eq(2L), eq(EMPTY_BUFFER)); - } - - @Test - public void dataContentLengthMissmatch() throws Exception { - dataContentLengthInvalid(false); - } - - @Test - public void dataContentLengthInvalid() throws Exception { - dataContentLengthInvalid(true); - } - - private void dataContentLengthInvalid(boolean negative) throws Exception { - final ByteBuf data = dummyData(); - final int padding = 10; - int processedBytes = data.readableBytes() + padding; - mockFlowControl(processedBytes); - try { - if (negative) { - assertThrows(Http2Exception.StreamException.class, new Executable() { - @Override - public void execute() throws Throwable { - decode().onHeadersRead(ctx, STREAM_ID, new DefaultHttp2Headers() - .setLong(HttpHeaderNames.CONTENT_LENGTH, -1L), padding, false); - } - }); - } else { - decode().onHeadersRead(ctx, STREAM_ID, new DefaultHttp2Headers() - .setLong(HttpHeaderNames.CONTENT_LENGTH, 1L), padding, false); - assertThrows(Http2Exception.StreamException.class, new Executable() { - @Override - public void execute() throws Throwable { - decode().onDataRead(ctx, STREAM_ID, data, padding, true); - } - }); - verify(localFlow).receiveFlowControlledFrame(eq(stream), eq(data), eq(padding), eq(true)); - verify(localFlow).consumeBytes(eq(stream), eq(processedBytes)); - - verify(listener, times(1)).onHeadersRead(eq(ctx), anyInt(), - any(Http2Headers.class), eq(0), eq(DEFAULT_PRIORITY_WEIGHT), eq(false), - eq(padding), eq(false)); - } - // Verify that the event was absorbed and not propagated to the observer. - verify(listener, never()).onDataRead(eq(ctx), anyInt(), any(ByteBuf.class), anyInt(), anyBoolean()); - } finally { - data.release(); - } - } - - @Test - public void headersContentLengthPositiveSign() throws Exception { - headersContentLengthSign("+1"); - } - - @Test - public void headersContentLengthNegativeSign() throws Exception { - headersContentLengthSign("-1"); - } - - private void headersContentLengthSign(final String length) throws Exception { - final int padding = 10; - when(connection.isServer()).thenReturn(true); - - assertThrows(Http2Exception.StreamException.class, new Executable() { - @Override - public void execute() throws Throwable { - decode().onHeadersRead(ctx, STREAM_ID, new DefaultHttp2Headers() - .set(HttpHeaderNames.CONTENT_LENGTH, length), padding, false); - } - }); - - // Verify that the event was absorbed and not propagated to the observer. - verify(listener, never()).onHeadersRead(eq(ctx), anyInt(), - any(Http2Headers.class), anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean()); - } - - @Test - public void headersContentLengthMissmatch() throws Exception { - headersContentLength(false); - } - - @Test - public void headersContentLengthInvalid() throws Exception { - headersContentLength(true); - } - - private void headersContentLength(final boolean negative) throws Exception { - final int padding = 10; - when(connection.isServer()).thenReturn(true); - assertThrows(Http2Exception.StreamException.class, new Executable() { - @Override - public void execute() throws Throwable { - decode().onHeadersRead(ctx, STREAM_ID, new DefaultHttp2Headers() - .setLong(HttpHeaderNames.CONTENT_LENGTH, negative ? -1L : 1L), padding, true); - } - }); - - // Verify that the event was absorbed and not propagated to the observer. - verify(listener, never()).onHeadersRead(eq(ctx), anyInt(), - any(Http2Headers.class), anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean()); - } - - @Test - public void multipleHeadersContentLengthSame() throws Exception { - multipleHeadersContentLength(true); - } - - @Test - public void multipleHeadersContentLengthDifferent() throws Exception { - multipleHeadersContentLength(false); - } - - private void multipleHeadersContentLength(boolean same) throws Exception { - final int padding = 10; - when(connection.isServer()).thenReturn(true); - final Http2Headers headers = new DefaultHttp2Headers(); - if (same) { - headers.addLong(HttpHeaderNames.CONTENT_LENGTH, 0); - headers.addLong(HttpHeaderNames.CONTENT_LENGTH, 0); - } else { - headers.addLong(HttpHeaderNames.CONTENT_LENGTH, 0); - headers.addLong(HttpHeaderNames.CONTENT_LENGTH, 1); - } - - if (same) { - decode().onHeadersRead(ctx, STREAM_ID, headers, padding, true); - verify(listener, times(1)).onHeadersRead(eq(ctx), anyInt(), - any(Http2Headers.class), anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean()); - assertEquals(1, headers.getAll(HttpHeaderNames.CONTENT_LENGTH).size()); - } else { - assertThrows(Http2Exception.StreamException.class, new Executable() { - @Override - public void execute() throws Throwable { - decode().onHeadersRead(ctx, STREAM_ID, headers, padding, true); - } - }); - - // Verify that the event was absorbed and not propagated to the observer. - verify(listener, never()).onHeadersRead(eq(ctx), anyInt(), - any(Http2Headers.class), anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean()); - } - } - - private static ByteBuf dummyData() { - // The buffer is purposely 8 bytes so it will even work for a ping frame. - return wrappedBuffer("abcdefgh".getBytes(UTF_8)); - } - - /** - * Calls the decode method on the handler and gets back the captured internal listener - */ - private Http2FrameListener decode() throws Exception { - ArgumentCaptor internalListener = ArgumentCaptor.forClass(Http2FrameListener.class); - doNothing().when(reader).readFrame(eq(ctx), any(ByteBuf.class), internalListener.capture()); - decoder.decodeFrame(ctx, EMPTY_BUFFER); - return internalListener.getValue(); - } - - private void mockFlowControl(final int processedBytes) throws Http2Exception { - doAnswer((Answer) invocation -> - processedBytes).when(listener).onDataRead(any(ChannelHandlerContext.class), anyInt(), - any(ByteBuf.class), anyInt(), anyBoolean()); - } - - private void mockGoAwaySent() { - when(connection.goAwaySent()).thenReturn(true); - when(remote.isValidStreamId(STREAM_ID)).thenReturn(true); - when(remote.lastStreamKnownByPeer()).thenReturn(0); - } - - private void mockGoAwaySentShouldAllowFramesForStreamCreatedByLocalEndpoint() { - when(connection.goAwaySent()).thenReturn(true); - when(remote.isValidStreamId(STREAM_ID)).thenReturn(false); - when(remote.lastStreamKnownByPeer()).thenReturn(0); - } -} 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 deleted file mode 100644 index 1667a92755..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionEncoderTest.java +++ /dev/null @@ -1,800 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.PooledByteBufAllocator; -import io.netty.buffer.Unpooled; -import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty.channel.Channel; -import io.netty.channel.ChannelConfig; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelMetadata; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.DefaultChannelConfig; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http2.Http2RemoteFlowController.FlowControlled; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.ImmediateEventExecutor; -import io.netty.util.concurrent.Promise; -import junit.framework.AssertionFailedError; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.InOrder; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.stubbing.Answer; - -import java.util.ArrayList; -import java.util.List; - -import static io.netty.buffer.Unpooled.EMPTY_BUFFER; -import static io.netty.buffer.Unpooled.wrappedBuffer; -import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_PRIORITY_WEIGHT; -import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR; -import static io.netty.handler.codec.http2.Http2Stream.State.HALF_CLOSED_REMOTE; -import static io.netty.handler.codec.http2.Http2Stream.State.RESERVED_LOCAL; -import static io.netty.util.CharsetUtil.UTF_8; -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyBoolean; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.anyLong; -import static org.mockito.Mockito.anyShort; -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; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -/** - * Tests for {@link DefaultHttp2ConnectionEncoder} - */ -@SuppressWarnings("unchecked") -public class DefaultHttp2ConnectionEncoderTest { - private static final int STREAM_ID = 2; - private static final int PUSH_STREAM_ID = 4; - - @Mock - private Http2RemoteFlowController remoteFlow; - - @Mock - private ChannelHandlerContext ctx; - - @Mock - private Channel channel; - - @Mock - private Channel.Unsafe unsafe; - - @Mock - private ChannelPipeline pipeline; - - @Mock - private Http2FrameWriter writer; - - @Mock - private Http2FrameWriter.Configuration writerConfig; - - @Mock - private Http2FrameSizePolicy frameSizePolicy; - - @Mock - private Http2LifecycleManager lifecycleManager; - - private DefaultHttp2ConnectionEncoder encoder; - private Http2Connection connection; - private ArgumentCaptor payloadCaptor; - private List writtenData; - private List writtenPadding; - private boolean streamClosed; - - @BeforeEach - public void setup() throws Exception { - MockitoAnnotations.initMocks(this); - - ChannelMetadata metadata = new ChannelMetadata(false, 16); - when(channel.isActive()).thenReturn(true); - when(channel.pipeline()).thenReturn(pipeline); - when(channel.metadata()).thenReturn(metadata); - when(channel.unsafe()).thenReturn(unsafe); - ChannelConfig config = new DefaultChannelConfig(channel); - when(channel.config()).thenReturn(config); - - when(writer.configuration()).thenReturn(writerConfig); - when(writerConfig.frameSizePolicy()).thenReturn(frameSizePolicy); - when(frameSizePolicy.maxFrameSize()).thenReturn(64); - doAnswer((Answer>) in -> ImmediateEventExecutor.INSTANCE.newSucceededFuture(null)) - .when(writer).writeSettings(eq(ctx), any(Http2Settings.class)); - doAnswer((Answer>) in -> { - ((ByteBuf) in.getArguments()[3]).release(); - return ImmediateEventExecutor.INSTANCE.newSucceededFuture(null); - }).when(writer).writeGoAway(eq(ctx), anyInt(), anyInt(), any(ByteBuf.class)); - - writtenData = new ArrayList<>(); - writtenPadding = new ArrayList<>(); - when(writer.writeData(eq(ctx), anyInt(), any(ByteBuf.class), anyInt(), anyBoolean())) - .then((Answer>) in -> { - // Make sure we only receive stream closure on the last frame and that void promises - // are used for all writes except the last one. - if (streamClosed) { - fail("Stream already closed"); - } else { - streamClosed = (Boolean) in.getArguments()[4]; - } - writtenPadding.add((Integer) in.getArguments()[3]); - ByteBuf data = (ByteBuf) in.getArguments()[2]; - writtenData.add(data.toString(UTF_8)); - // Release the buffer just as DefaultHttp2FrameWriter does - data.release(); - // Let the promise succeed to trigger listeners. - return ImmediateEventExecutor.INSTANCE.newSucceededFuture(null); - }); - when(writer.writeHeaders(eq(ctx), anyInt(), any(Http2Headers.class), anyInt(), anyShort(), anyBoolean(), - anyInt(), anyBoolean())) - .then((Answer>) invocationOnMock -> { - if (streamClosed) { - fail("Stream already closed"); - } else { - streamClosed = (Boolean) invocationOnMock.getArguments()[5]; - } - return ImmediateEventExecutor.INSTANCE.newSucceededFuture(null); - }); - when(writer.writeHeaders(eq(ctx), anyInt(), any(Http2Headers.class), - anyInt(), anyBoolean())) - .then((Answer>) invocationOnMock -> { - if (streamClosed) { - fail("Stream already closed"); - } else { - streamClosed = invocationOnMock.getArgument(4); - } - return ImmediateEventExecutor.INSTANCE.newSucceededFuture(null); - }); - payloadCaptor = ArgumentCaptor.forClass(Http2RemoteFlowController.FlowControlled.class); - doNothing().when(remoteFlow).addFlowControlled(any(Http2Stream.class), payloadCaptor.capture()); - when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT); - when(ctx.channel()).thenReturn(channel); - doAnswer((Answer>) in -> ImmediateEventExecutor.INSTANCE.newPromise()).when(ctx).newPromise(); - doAnswer((Answer>) in -> ImmediateEventExecutor.INSTANCE.newSucceededFuture(null)) - .when(ctx).newSucceededFuture(); - doAnswer((Answer>) in -> ImmediateEventExecutor.INSTANCE.newFailedFuture(in.getArgument(0))) - .when(ctx).newFailedFuture(any(Throwable.class)); - when(ctx.flush()).thenThrow(new AssertionFailedError("forbidden")); - when(channel.alloc()).thenReturn(PooledByteBufAllocator.DEFAULT); - doAnswer((Answer>) in -> ImmediateEventExecutor.INSTANCE.newFailedFuture(in.getArgument(0))) - .when(channel).newFailedFuture(any(Throwable.class)); - // Use a server-side connection so we can test server push. - connection = new DefaultHttp2Connection(true); - connection.remote().flowController(remoteFlow); - - encoder = new DefaultHttp2ConnectionEncoder(connection, writer); - 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(); - Future f = encoder.writeData(ctx, STREAM_ID, data, 0, endOfStream); - - 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, f.cause()); - } - - @Test - public void dataWriteShouldSucceed() throws Exception { - createStream(STREAM_ID, false); - final ByteBuf data = dummyData(); - Future f = encoder.writeData(ctx, STREAM_ID, data, 0, true); - assertEquals(8, payloadCaptor.getValue().size()); - payloadCaptor.getValue().write(ctx, 8); - assertEquals(0, payloadCaptor.getValue().size()); - assertEquals("abcdefgh", writtenData.get(0)); - assertEquals(0, data.refCnt()); - assertTrue(f.isSuccess()); - } - - @Test - public void dataFramesShouldMerge() throws Exception { - createStream(STREAM_ID, false); - final ByteBuf data = dummyData().retain(); - - Future future1 = encoder.writeData(ctx, STREAM_ID, data, 0, true); - Future future2 = encoder.writeData(ctx, STREAM_ID, data, 0, true); - - // Now merge the two payloads. - List capturedWrites = payloadCaptor.getAllValues(); - FlowControlled mergedPayload = capturedWrites.get(0); - mergedPayload.merge(ctx, capturedWrites.get(1)); - assertEquals(16, mergedPayload.size()); - assertFalse(future1.isDone()); - assertFalse(future2.isDone()); - - // Write the merged payloads and verify it was written correctly. - mergedPayload.write(ctx, 16); - assertEquals(0, mergedPayload.size()); - assertEquals("abcdefghabcdefgh", writtenData.get(0)); - assertEquals(0, data.refCnt()); - assertTrue(future1.isSuccess()); - assertTrue(future2.isSuccess()); - } - - @Test - public void dataFramesDontMergeWithHeaders() throws Exception { - createStream(STREAM_ID, false); - final ByteBuf data = dummyData().retain(); - encoder.writeData(ctx, STREAM_ID, data, 0, false); - when(remoteFlow.hasFlowControlled(any(Http2Stream.class))).thenReturn(true); - encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true); - List capturedWrites = payloadCaptor.getAllValues(); - assertFalse(capturedWrites.get(0).merge(ctx, capturedWrites.get(1))); - } - - @Test - public void emptyFrameShouldSplitPadding() throws Exception { - ByteBuf data = Unpooled.buffer(0); - assertSplitPaddingOnEmptyBuffer(data); - assertEquals(0, data.refCnt()); - } - - private void assertSplitPaddingOnEmptyBuffer(ByteBuf data) throws Exception { - createStream(STREAM_ID, false); - when(frameSizePolicy.maxFrameSize()).thenReturn(5); - Future f = encoder.writeData(ctx, STREAM_ID, data, 10, true); - assertEquals(10, payloadCaptor.getValue().size()); - payloadCaptor.getValue().write(ctx, 10); - // writer was called 2 times - assertEquals(1, writtenData.size()); - assertEquals("", writtenData.get(0)); - assertEquals(10, (int) writtenPadding.get(0)); - assertEquals(0, data.refCnt()); - assertTrue(f.isSuccess()); - } - - @Test - public void headersWriteForUnknownStreamShouldCreateStream() throws Exception { - writeAllFlowControlledFrames(); - final int streamId = 6; - Future f = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false); - verify(writer).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), eq(0), - eq(false)); - assertTrue(f.isSuccess()); - } - - @Test - public void headersWriteShouldOpenStreamForPush() throws Exception { - writeAllFlowControlledFrames(); - Http2Stream parent = createStream(STREAM_ID, false); - reservePushStream(PUSH_STREAM_ID, parent); - - encoder.writeHeaders(ctx, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false); - assertEquals(HALF_CLOSED_REMOTE, stream(PUSH_STREAM_ID).state()); - verify(writer).writeHeaders(eq(ctx), eq(PUSH_STREAM_ID), eq(EmptyHttp2Headers.INSTANCE), - eq(0), eq(false)); - } - - @Test - public void trailersDoNotEndStreamThrows() { - writeAllFlowControlledFrames(); - final int streamId = 6; - encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false); - - Future future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false); - assertTrue(future.isDone()); - assertFalse(future.isSuccess()); - - verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), - eq(0), eq(false)); - } - - @Test - public void trailersDoNotEndStreamWithDataThrows() { - writeAllFlowControlledFrames(); - final int streamId = 6; - encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false); - - Http2Stream stream = connection.stream(streamId); - when(remoteFlow.hasFlowControlled(eq(stream))).thenReturn(true); - - Future future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false); - assertTrue(future.isDone()); - assertFalse(future.isSuccess()); - - verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), - eq(0), eq(false)); - } - - @Test - public void tooManyHeadersNoEOSThrows() { - tooManyHeadersThrows(false); - } - - @Test - public void tooManyHeadersEOSThrows() { - tooManyHeadersThrows(true); - } - - private void tooManyHeadersThrows(boolean eos) { - writeAllFlowControlledFrames(); - final int streamId = 6; - encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false); - encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, true); - - Future future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos); - assertTrue(future.isDone()); - assertFalse(future.isSuccess()); - - verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), - eq(0), eq(false)); - verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), - eq(0), eq(true)); - } - - @Test - public void infoHeadersAndTrailersAllowed() throws Exception { - infoHeadersAndTrailers(true, 1); - } - - @Test - public void multipleInfoHeadersAndTrailersAllowed() throws Exception { - infoHeadersAndTrailers(true, 10); - } - - @Test - public void infoHeadersAndTrailersNoEOSThrows() throws Exception { - infoHeadersAndTrailers(false, 1); - } - - @Test - public void multipleInfoHeadersAndTrailersNoEOSThrows() throws Exception { - infoHeadersAndTrailers(false, 10); - } - - private void infoHeadersAndTrailers(boolean eos, int infoHeaderCount) { - writeAllFlowControlledFrames(); - final int streamId = 6; - Http2Headers infoHeaders = informationalHeaders(); - for (int i = 0; i < infoHeaderCount; ++i) { - encoder.writeHeaders(ctx, streamId, infoHeaders, 0, false); - } - encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false); - - Future future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos); - assertTrue(future.isDone()); - assertEquals(eos, future.isSuccess()); - - verify(writer, times(infoHeaderCount)).writeHeaders(eq(ctx), eq(streamId), eq(infoHeaders), - eq(0), eq(false)); - verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), - eq(0), eq(false)); - if (eos) { - verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), - eq(0), eq(true)); - } - } - - private static Http2Headers informationalHeaders() { - Http2Headers headers = new DefaultHttp2Headers(); - headers.status(HttpResponseStatus.CONTINUE.codeAsText()); - return headers; - } - - @Test - public void tooManyHeadersWithDataNoEOSThrows() { - tooManyHeadersWithDataThrows(false); - } - - @Test - public void tooManyHeadersWithDataEOSThrows() { - tooManyHeadersWithDataThrows(true); - } - - private void tooManyHeadersWithDataThrows(boolean eos) { - writeAllFlowControlledFrames(); - final int streamId = 6; - encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false); - - Http2Stream stream = connection.stream(streamId); - when(remoteFlow.hasFlowControlled(eq(stream))).thenReturn(true); - - encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, true); - - Future future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos); - assertTrue(future.isDone()); - assertFalse(future.isSuccess()); - - verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), - eq(0), eq(false)); - verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), - eq(0), eq(true)); - } - - @Test - public void infoHeadersAndTrailersWithDataAllowed() { - infoHeadersAndTrailersWithData(true, 1); - } - - @Test - public void multipleInfoHeadersAndTrailersWithDataAllowed() { - infoHeadersAndTrailersWithData(true, 10); - } - - @Test - public void infoHeadersAndTrailersWithDataNoEOSThrows() { - infoHeadersAndTrailersWithData(false, 1); - } - - @Test - public void multipleInfoHeadersAndTrailersWithDataNoEOSThrows() { - infoHeadersAndTrailersWithData(false, 10); - } - - private void infoHeadersAndTrailersWithData(boolean eos, int infoHeaderCount) { - writeAllFlowControlledFrames(); - final int streamId = 6; - Http2Headers infoHeaders = informationalHeaders(); - for (int i = 0; i < infoHeaderCount; ++i) { - encoder.writeHeaders(ctx, streamId, infoHeaders, 0, false); - } - - Http2Stream stream = connection.stream(streamId); - when(remoteFlow.hasFlowControlled(eq(stream))).thenReturn(true); - - encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false); - - Future future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos); - assertTrue(future.isDone()); - assertEquals(eos, future.isSuccess()); - - verify(writer, times(infoHeaderCount)).writeHeaders(eq(ctx), eq(streamId), eq(infoHeaders), - eq(0), eq(false)); - verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), - eq(0), eq(false)); - if (eos) { - verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), - eq(0), eq(true)); - } - } - - @Test - public void pushPromiseWriteAfterGoAwayReceivedShouldFail() throws Exception { - createStream(STREAM_ID, false); - goAwayReceived(0); - Future future = encoder.writePushPromise(ctx, STREAM_ID, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0); - assertTrue(future.isDone()); - assertFalse(future.isSuccess()); - } - - @Test - public void pushPromiseWriteShouldReserveStream() throws Exception { - createStream(STREAM_ID, false); - encoder.writePushPromise(ctx, STREAM_ID, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0); - assertEquals(RESERVED_LOCAL, stream(PUSH_STREAM_ID).state()); - verify(writer).writePushPromise(eq(ctx), eq(STREAM_ID), eq(PUSH_STREAM_ID), - eq(EmptyHttp2Headers.INSTANCE), eq(0)); - } - - @Test - public void priorityWriteAfterGoAwayShouldSucceed() throws Exception { - createStream(STREAM_ID, false); - goAwayReceived(Integer.MAX_VALUE); - encoder.writePriority(ctx, STREAM_ID, 0, (short) 255, true); - verify(writer).writePriority(eq(ctx), eq(STREAM_ID), eq(0), eq((short) 255), eq(true)); - } - - @Test - public void priorityWriteShouldSetPriorityForStream() throws Exception { - short weight = 255; - encoder.writePriority(ctx, STREAM_ID, 0, weight, true); - - // Verify that this did NOT create a stream object. - Http2Stream stream = stream(STREAM_ID); - assertNull(stream); - - verify(writer).writePriority(eq(ctx), eq(STREAM_ID), eq(0), eq((short) 255), eq(true)); - } - - @Test - public void priorityWriteOnPreviouslyExistingStreamShouldSucceed() throws Exception { - createStream(STREAM_ID, false).close(); - short weight = 255; - encoder.writePriority(ctx, STREAM_ID, 0, weight, true); - verify(writer).writePriority(eq(ctx), eq(STREAM_ID), eq(0), eq(weight), eq(true)); - } - - @Test - public void priorityWriteOnPreviouslyExistingParentStreamShouldSucceed() throws Exception { - final int parentStreamId = STREAM_ID + 2; - createStream(STREAM_ID, false); - createStream(parentStreamId, false).close(); - - short weight = 255; - encoder.writePriority(ctx, STREAM_ID, parentStreamId, weight, true); - verify(writer).writePriority(eq(ctx), eq(STREAM_ID), eq(parentStreamId), eq(weight), eq(true)); - } - - @Test - public void rstStreamWriteForUnknownStreamShouldIgnore() throws Exception { - encoder.writeRstStream(ctx, 5, PROTOCOL_ERROR.code()); - verify(writer, never()).writeRstStream(eq(ctx), anyInt(), anyLong()); - } - - @Test - public void rstStreamShouldCloseStream() throws Exception { - // Create the stream and send headers. - writeAllFlowControlledFrames(); - encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true); - - // Now verify that a stream reset is performed. - stream(STREAM_ID); - encoder.writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code()); - verify(lifecycleManager).resetStream(eq(ctx), eq(STREAM_ID), anyLong()); - } - - @Test - public void pingWriteAfterGoAwayShouldSucceed() throws Exception { - goAwayReceived(0); - encoder.writePing(ctx, false, 0L); - verify(writer).writePing(eq(ctx), eq(false), eq(0L)); - } - - @Test - public void pingWriteShouldSucceed() throws Exception { - encoder.writePing(ctx, false, 0L); - verify(writer).writePing(eq(ctx), eq(false), eq(0L)); - } - - @Test - public void settingsWriteAfterGoAwayShouldSucceed() throws Exception { - goAwayReceived(0); - encoder.writeSettings(ctx, new Http2Settings()); - verify(writer).writeSettings(eq(ctx), any(Http2Settings.class)); - } - - @Test - public void settingsWriteShouldNotUpdateSettings() throws Exception { - Http2Settings settings = new Http2Settings(); - settings.initialWindowSize(100); - settings.maxConcurrentStreams(1000); - settings.headerTableSize(2000); - - encoder.writeSettings(ctx, settings); - verify(writer).writeSettings(eq(ctx), eq(settings)); - } - - @Test - public void dataWriteShouldCreateHalfClosedStream() throws Exception { - writeAllFlowControlledFrames(); - - Http2Stream stream = createStream(STREAM_ID, false); - ByteBuf data = dummyData(); - Future f = encoder.writeData(ctx, STREAM_ID, data.retain(), 0, true); - assertTrue(f.isSuccess()); - verify(remoteFlow).addFlowControlled(eq(stream), any(FlowControlled.class)); - verify(lifecycleManager).closeStreamLocal(eq(stream), eq(f)); - assertEquals(data.toString(UTF_8), writtenData.get(0)); - data.release(); - } - - @Test - public void headersWriteShouldHalfCloseStream() throws Exception { - writeAllFlowControlledFrames(); - createStream(STREAM_ID, false); - Future f = encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true); - - assertTrue(f.isSuccess()); - verify(lifecycleManager).closeStreamLocal(eq(stream(STREAM_ID)), eq(f)); - } - - @Test - public void headersWriteShouldHalfClosePushStream() throws Exception { - writeAllFlowControlledFrames(); - Http2Stream parent = createStream(STREAM_ID, false); - Http2Stream stream = reservePushStream(PUSH_STREAM_ID, parent); - Future f = encoder.writeHeaders(ctx, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true); - assertEquals(HALF_CLOSED_REMOTE, stream.state()); - assertTrue(f.isSuccess()); - verify(lifecycleManager).closeStreamLocal(eq(stream), eq(f)); - } - - @Test - public void headersWriteShouldHalfCloseAfterOnErrorForPreCreatedStream() throws Exception { - 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(true))) - .thenReturn(ImmediateEventExecutor.INSTANCE.newFailedFuture(ex)); - - writeAllFlowControlledFrames(); - Http2Stream stream = createStream(STREAM_ID, false); - Future f = encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true); - - assertTrue(f.isDone()); - assertFalse(f.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(f)); - } - - @Test - public void headersWriteShouldHalfCloseAfterOnErrorForImplicitlyCreatedStream() throws Exception { - 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(true))) - .thenReturn(ImmediateEventExecutor.INSTANCE.newFailedFuture(ex)); - - writeAllFlowControlledFrames(); - Future f = encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true); - - assertTrue(f.isDone()); - assertFalse(f.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(f)); - } - - @Test - public void encoderDelegatesGoAwayToLifeCycleManager() { - encoder.writeGoAway(ctx, STREAM_ID, Http2Error.INTERNAL_ERROR.code(), null); - verify(lifecycleManager).goAway(eq(ctx), eq(STREAM_ID), eq(Http2Error.INTERNAL_ERROR.code()), - eq((ByteBuf) null)); - verifyNoMoreInteractions(writer); - } - - @Test - public void dataWriteToClosedStreamShouldFail() throws Exception { - createStream(STREAM_ID, false).close(); - ByteBuf data = mock(ByteBuf.class); - Future f = encoder.writeData(ctx, STREAM_ID, data, 0, false); - assertTrue(f.isDone()); - assertFalse(f.isSuccess()); - assertThat(f.cause(), instanceOf(IllegalArgumentException.class)); - verify(data).release(); - } - - @Test - public void dataWriteToHalfClosedLocalStreamShouldFail() throws Exception { - createStream(STREAM_ID, true); - ByteBuf data = mock(ByteBuf.class); - Future f = encoder.writeData(ctx, STREAM_ID, data, 0, false); - assertTrue(f.isDone()); - assertFalse(f.isSuccess()); - assertThat(f.cause(), instanceOf(IllegalStateException.class)); - verify(data).release(); - } - - @Test - public void canWriteDataFrameAfterGoAwaySent() throws Exception { - Http2Stream stream = createStream(STREAM_ID, false); - connection.goAwaySent(0, 0, EMPTY_BUFFER); - ByteBuf data = mock(ByteBuf.class); - encoder.writeData(ctx, STREAM_ID, data, 0, false); - verify(remoteFlow).addFlowControlled(eq(stream), any(FlowControlled.class)); - } - - @Test - public void canWriteHeaderFrameAfterGoAwaySent() throws Exception { - writeAllFlowControlledFrames(); - createStream(STREAM_ID, false); - goAwaySent(0); - encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false); - verify(writer).writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE), - eq(0), eq(false)); - } - - @Test - public void canWriteDataFrameAfterGoAwayReceived() throws Exception { - Http2Stream stream = createStream(STREAM_ID, false); - goAwayReceived(STREAM_ID); - ByteBuf data = mock(ByteBuf.class); - encoder.writeData(ctx, STREAM_ID, data, 0, false); - verify(remoteFlow).addFlowControlled(eq(stream), any(FlowControlled.class)); - } - - @Test - public void canWriteHeaderFrameAfterGoAwayReceived() throws Http2Exception { - writeAllFlowControlledFrames(); - goAwayReceived(STREAM_ID); - encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false); - verify(writer).writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE), - eq(0), eq(false)); - } - - @Test - public void headersWithNoPriority() { - writeAllFlowControlledFrames(); - final int streamId = 6; - encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false); - verify(writer).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), - eq(0), eq(false)); - } - - @Test - public void headersWithPriority() { - writeAllFlowControlledFrames(); - final int streamId = 6; - encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 10, DEFAULT_PRIORITY_WEIGHT, - true, 1, false); - verify(writer).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), eq(10), - eq(DEFAULT_PRIORITY_WEIGHT), eq(true), eq(1), eq(false)); - } - - private void writeAllFlowControlledFrames() { - doAnswer((Answer) invocationOnMock -> { - FlowControlled flowControlled = (FlowControlled) invocationOnMock.getArguments()[1]; - flowControlled.write(ctx, Integer.MAX_VALUE); - flowControlled.writeComplete(); - return null; - }).when(remoteFlow).addFlowControlled(any(Http2Stream.class), payloadCaptor.capture()); - } - - private Http2Stream createStream(int streamId, boolean halfClosed) throws Http2Exception { - return connection.local().createStream(streamId, halfClosed); - } - - private Http2Stream reservePushStream(int pushStreamId, Http2Stream parent) throws Http2Exception { - return connection.local().reservePushStream(pushStreamId, parent); - } - - private Http2Stream stream(int streamId) { - return connection.stream(streamId); - } - - private void goAwayReceived(int lastStreamId) throws Http2Exception { - connection.goAwayReceived(lastStreamId, 0, EMPTY_BUFFER); - } - - private void goAwaySent(int lastStreamId) throws Http2Exception { - connection.goAwaySent(lastStreamId, 0, EMPTY_BUFFER); - } - - private static ByteBuf dummyData() { - // The buffer is purposely 8 bytes so it will even work for a ping frame. - return wrappedBuffer("abcdefgh".getBytes(UTF_8)); - } -} 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 deleted file mode 100644 index e40bdf7f71..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionTest.java +++ /dev/null @@ -1,707 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.local.LocalHandler; -import io.netty.handler.codec.http2.Http2Connection.Endpoint; -import io.netty.handler.codec.http2.Http2Stream.State; -import io.netty.util.concurrent.Promise; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; -import org.mockito.ArgumentMatchers; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -import static java.lang.Integer.MAX_VALUE; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.anyLong; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; - -/** - * Tests for {@link DefaultHttp2Connection}. - */ -public class DefaultHttp2ConnectionTest { - - private DefaultHttp2Connection server; - private DefaultHttp2Connection client; - private static EventLoopGroup group; - - @Mock - private Http2Connection.Listener clientListener; - - @Mock - private Http2Connection.Listener clientListener2; - - @BeforeAll - public static void beforeClass() { - group = new MultithreadEventLoopGroup(2, LocalHandler.newFactory()); - } - - @AfterAll - public static void afterClass() { - group.shutdownGracefully(); - } - - @BeforeEach - public void setup() { - MockitoAnnotations.initMocks(this); - - server = new DefaultHttp2Connection(true); - client = new DefaultHttp2Connection(false); - client.addListener(clientListener); - doAnswer((Answer) invocation -> { - assertNotNull(client.stream(((Http2Stream) invocation.getArgument(0)).id())); - return null; - }).when(clientListener).onStreamClosed(any(Http2Stream.class)); - doAnswer((Answer) invocation -> { - assertNull(client.stream(((Http2Stream) invocation.getArgument(0)).id())); - return null; - }).when(clientListener).onStreamRemoved(any(Http2Stream.class)); - } - - @Test - public void getStreamWithoutStreamShouldReturnNull() { - assertNull(server.stream(100)); - } - - @Test - public void removeAllStreamsWithEmptyStreams() throws InterruptedException { - testRemoveAllStreams(); - } - - @Test - public void removeAllStreamsWithJustOneLocalStream() throws Exception { - client.local().createStream(3, false); - testRemoveAllStreams(); - } - - @Test - public void removeAllStreamsWithJustOneRemoveStream() throws Exception { - client.remote().createStream(2, false); - testRemoveAllStreams(); - } - - @Test - public void removeAllStreamsWithManyActiveStreams() throws Exception { - Endpoint remote = client.remote(); - Endpoint local = client.local(); - for (int c = 3, s = 2; c < 5000; c += 2, s += 2) { - local.createStream(c, false); - remote.createStream(s, false); - } - testRemoveAllStreams(); - } - - @Test - public void removeIndividualStreamsWhileCloseDoesNotNPE() throws Exception { - final Http2Stream streamA = client.local().createStream(3, false); - final Http2Stream streamB = client.remote().createStream(2, false); - doAnswer((Answer) invocation -> { - streamA.close(); - streamB.close(); - return null; - }).when(clientListener2).onStreamClosed(any(Http2Stream.class)); - try { - client.addListener(clientListener2); - testRemoveAllStreams(); - } finally { - client.removeListener(clientListener2); - } - } - - @Test - public void removeAllStreamsWhileIteratingActiveStreams() throws Exception { - final Endpoint remote = client.remote(); - final Endpoint local = client.local(); - for (int c = 3, s = 2; c < 5000; c += 2, s += 2) { - local.createStream(c, false); - remote.createStream(s, false); - } - final Promise promise = group.next().newPromise(); - final CountDownLatch latch = new CountDownLatch(client.numActiveStreams()); - client.forEachActiveStream(stream -> { - promise.asFuture().addListener(future -> { - latch.countDown(); - }); - client.close(promise); - return true; - }); - assertTrue(latch.await(5, TimeUnit.SECONDS)); - } - - @Test - public void removeAllStreamsWhileIteratingActiveStreamsAndExceptionOccurs() - throws Exception { - final Endpoint remote = client.remote(); - final Endpoint local = client.local(); - for (int c = 3, s = 2; c < 5000; c += 2, s += 2) { - local.createStream(c, false); - remote.createStream(s, false); - } - final Promise promise = group.next().newPromise(); - final CountDownLatch latch = new CountDownLatch(1); - try { - client.forEachActiveStream(stream -> { - // This close call is basically a noop, because the following statement will throw an exception. - client.close(promise); - // Do an invalid operation while iterating. - remote.createStream(3, false); - return true; - }); - } catch (Http2Exception ignored) { - promise.asFuture().addListener(future -> { - latch.countDown(); - }); - client.close(promise); - } - assertTrue(latch.await(5, TimeUnit.SECONDS)); - } - - @Test - public void goAwayReceivedShouldCloseStreamsGreaterThanLastStream() throws Exception { - Http2Stream stream1 = client.local().createStream(3, false); - Http2Stream stream2 = client.local().createStream(5, false); - Http2Stream remoteStream = client.remote().createStream(4, false); - - assertEquals(State.OPEN, stream1.state()); - assertEquals(State.OPEN, stream2.state()); - - client.goAwayReceived(3, 8, null); - - assertEquals(State.OPEN, stream1.state()); - assertEquals(State.CLOSED, stream2.state()); - assertEquals(State.OPEN, remoteStream.state()); - assertEquals(3, client.local().lastStreamKnownByPeer()); - assertEquals(5, client.local().lastStreamCreated()); - // The remote endpoint must not be affected by a received GOAWAY frame. - assertEquals(-1, client.remote().lastStreamKnownByPeer()); - assertEquals(State.OPEN, remoteStream.state()); - } - - @Test - public void goAwaySentShouldCloseStreamsGreaterThanLastStream() throws Exception { - Http2Stream stream1 = server.remote().createStream(3, false); - Http2Stream stream2 = server.remote().createStream(5, false); - Http2Stream localStream = server.local().createStream(4, false); - - server.goAwaySent(3, 8, null); - - assertEquals(State.OPEN, stream1.state()); - assertEquals(State.CLOSED, stream2.state()); - - assertEquals(3, server.remote().lastStreamKnownByPeer()); - assertEquals(5, server.remote().lastStreamCreated()); - // The local endpoint must not be affected by a sent GOAWAY frame. - assertEquals(-1, server.local().lastStreamKnownByPeer()); - assertEquals(State.OPEN, localStream.state()); - } - - @Test - public void serverCreateStreamShouldSucceed() throws Http2Exception { - Http2Stream stream = server.local().createStream(2, false); - assertEquals(2, stream.id()); - assertEquals(State.OPEN, stream.state()); - assertEquals(1, server.numActiveStreams()); - assertEquals(2, server.local().lastStreamCreated()); - - stream = server.local().createStream(4, true); - assertEquals(4, stream.id()); - assertEquals(State.HALF_CLOSED_LOCAL, stream.state()); - assertEquals(2, server.numActiveStreams()); - assertEquals(4, server.local().lastStreamCreated()); - - stream = server.remote().createStream(3, true); - assertEquals(3, stream.id()); - assertEquals(State.HALF_CLOSED_REMOTE, stream.state()); - assertEquals(3, server.numActiveStreams()); - assertEquals(3, server.remote().lastStreamCreated()); - - stream = server.remote().createStream(5, false); - assertEquals(5, stream.id()); - assertEquals(State.OPEN, stream.state()); - assertEquals(4, server.numActiveStreams()); - assertEquals(5, server.remote().lastStreamCreated()); - } - - @Test - public void clientCreateStreamShouldSucceed() throws Http2Exception { - Http2Stream stream = client.remote().createStream(2, false); - assertEquals(2, stream.id()); - assertEquals(State.OPEN, stream.state()); - assertEquals(1, client.numActiveStreams()); - assertEquals(2, client.remote().lastStreamCreated()); - - stream = client.remote().createStream(4, true); - assertEquals(4, stream.id()); - assertEquals(State.HALF_CLOSED_REMOTE, stream.state()); - assertEquals(2, client.numActiveStreams()); - assertEquals(4, client.remote().lastStreamCreated()); - assertTrue(stream.isHeadersReceived()); - - stream = client.local().createStream(3, true); - assertEquals(3, stream.id()); - assertEquals(State.HALF_CLOSED_LOCAL, stream.state()); - assertEquals(3, client.numActiveStreams()); - assertEquals(3, client.local().lastStreamCreated()); - assertTrue(stream.isHeadersSent()); - - stream = client.local().createStream(5, false); - assertEquals(5, stream.id()); - assertEquals(State.OPEN, stream.state()); - assertEquals(4, client.numActiveStreams()); - assertEquals(5, client.local().lastStreamCreated()); - } - - @Test - public void serverReservePushStreamShouldSucceed() throws Http2Exception { - Http2Stream stream = server.remote().createStream(3, true); - Http2Stream pushStream = server.local().reservePushStream(2, stream); - assertEquals(2, pushStream.id()); - assertEquals(State.RESERVED_LOCAL, pushStream.state()); - assertEquals(1, server.numActiveStreams()); - assertEquals(2, server.local().lastStreamCreated()); - } - - @Test - public void clientReservePushStreamShouldSucceed() throws Http2Exception { - Http2Stream stream = server.remote().createStream(3, true); - Http2Stream pushStream = server.local().reservePushStream(4, stream); - assertEquals(4, pushStream.id()); - assertEquals(State.RESERVED_LOCAL, pushStream.state()); - assertEquals(1, server.numActiveStreams()); - assertEquals(4, server.local().lastStreamCreated()); - } - - @Test - public void serverRemoteIncrementAndGetStreamShouldSucceed() throws Http2Exception { - incrementAndGetStreamShouldSucceed(server.remote()); - } - - @Test - public void serverLocalIncrementAndGetStreamShouldSucceed() throws Http2Exception { - incrementAndGetStreamShouldSucceed(server.local()); - } - - @Test - public void clientRemoteIncrementAndGetStreamShouldSucceed() throws Http2Exception { - incrementAndGetStreamShouldSucceed(client.remote()); - } - - @Test - public void clientLocalIncrementAndGetStreamShouldSucceed() throws Http2Exception { - incrementAndGetStreamShouldSucceed(client.local()); - } - - @Test - public void serverRemoteIncrementAndGetStreamShouldRespectOverflow() throws Http2Exception { - incrementAndGetStreamShouldRespectOverflow(server.remote(), MAX_VALUE); - } - - @Test - public void serverLocalIncrementAndGetStreamShouldRespectOverflow() throws Http2Exception { - incrementAndGetStreamShouldRespectOverflow(server.local(), MAX_VALUE - 1); - } - - @Test - public void clientRemoteIncrementAndGetStreamShouldRespectOverflow() throws Http2Exception { - incrementAndGetStreamShouldRespectOverflow(client.remote(), MAX_VALUE - 1); - } - - @Test - public void clientLocalIncrementAndGetStreamShouldRespectOverflow() throws Http2Exception { - incrementAndGetStreamShouldRespectOverflow(client.local(), MAX_VALUE); - } - - @Test - public void clientLocalCreateStreamExhaustedSpace() throws Http2Exception { - client.local().createStream(MAX_VALUE, true); - Http2Exception expected = assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - client.local().createStream(MAX_VALUE, true); - } - }); - assertEquals(Http2Error.REFUSED_STREAM, expected.error()); - assertEquals(Http2Exception.ShutdownHint.GRACEFUL_SHUTDOWN, expected.shutdownHint()); - } - - @Test - public void newStreamBehindExpectedShouldThrow() throws Http2Exception { - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - server.local().createStream(0, true); - } - }); - } - - @Test - public void newStreamNotForServerShouldThrow() throws Http2Exception { - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - server.local().createStream(11, true); - } - }); - } - - @Test - public void newStreamNotForClientShouldThrow() throws Http2Exception { - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - client.local().createStream(10, true); - } - }); - } - - @Test - public void createShouldThrowWhenMaxAllowedStreamsOpenExceeded() throws Http2Exception { - server.local().maxActiveStreams(0); - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - server.local().createStream(2, true); - } - }); - } - - @Test - public void serverCreatePushShouldFailOnRemoteEndpointWhenMaxAllowedStreamsExceeded() throws Http2Exception { - server = new DefaultHttp2Connection(true, 0); - server.remote().maxActiveStreams(1); - final Http2Stream requestStream = server.remote().createStream(3, false); - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - server.remote().reservePushStream(2, requestStream); - } - }); - } - - @Test - public void clientCreatePushShouldFailOnRemoteEndpointWhenMaxAllowedStreamsExceeded() throws Http2Exception { - client = new DefaultHttp2Connection(false, 0); - client.remote().maxActiveStreams(1); - final Http2Stream requestStream = client.remote().createStream(2, false); - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - client.remote().reservePushStream(4, requestStream); - } - }); - } - - @Test - public void serverCreatePushShouldSucceedOnLocalEndpointWhenMaxAllowedStreamsExceeded() throws Http2Exception { - server = new DefaultHttp2Connection(true, 0); - server.local().maxActiveStreams(1); - Http2Stream requestStream = server.remote().createStream(3, false); - assertNotNull(server.local().reservePushStream(2, requestStream)); - } - - @Test - public void reserveWithPushDisallowedShouldThrow() throws Http2Exception { - final Http2Stream stream = server.remote().createStream(3, true); - server.remote().allowPushTo(false); - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - server.local().reservePushStream(2, stream); - } - }); - } - - @Test - public void goAwayReceivedShouldDisallowLocalCreation() throws Http2Exception { - server.goAwayReceived(0, 1L, Unpooled.EMPTY_BUFFER); - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - server.local().createStream(3, true); - } - }); - } - - @Test - public void goAwayReceivedShouldAllowRemoteCreation() throws Http2Exception { - server.goAwayReceived(0, 1L, Unpooled.EMPTY_BUFFER); - server.remote().createStream(3, true); - } - - @Test - public void goAwaySentShouldDisallowRemoteCreation() throws Http2Exception { - server.goAwaySent(0, 1L, Unpooled.EMPTY_BUFFER); - - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - 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); - stream.close(); - assertEquals(State.CLOSED, stream.state()); - assertEquals(0, server.numActiveStreams()); - } - - @Test - public void closeLocalWhenOpenShouldSucceed() throws Http2Exception { - Http2Stream stream = server.remote().createStream(3, false); - stream.closeLocalSide(); - assertEquals(State.HALF_CLOSED_LOCAL, stream.state()); - assertEquals(1, server.numActiveStreams()); - } - - @Test - public void closeRemoteWhenOpenShouldSucceed() throws Http2Exception { - Http2Stream stream = server.remote().createStream(3, false); - stream.closeRemoteSide(); - assertEquals(State.HALF_CLOSED_REMOTE, stream.state()); - assertEquals(1, server.numActiveStreams()); - } - - @Test - public void closeOnlyOpenSideShouldClose() throws Http2Exception { - Http2Stream stream = server.remote().createStream(3, true); - stream.closeLocalSide(); - assertEquals(State.CLOSED, stream.state()); - assertEquals(0, server.numActiveStreams()); - } - - @SuppressWarnings("NumericOverflow") - @Test - public void localStreamInvalidStreamIdShouldThrow() { - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - client.local().createStream(MAX_VALUE + 2, false); - } - }); - } - - @SuppressWarnings("NumericOverflow") - @Test - public void remoteStreamInvalidStreamIdShouldThrow() { - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - client.remote().createStream(MAX_VALUE + 1, false); - } - }); - } - - /** - * We force {@link #clientListener} methods to all throw a {@link RuntimeException} and verify the following: - *
    - *
  1. all listener methods are called for both {@link #clientListener} and {@link #clientListener2}
  2. - *
  3. {@link #clientListener2} is notified after {@link #clientListener}
  4. - *
  5. {@link #clientListener2} methods are all still called despite {@link #clientListener}'s - * method throwing a {@link RuntimeException}
  6. - *
- */ - @Test - public void listenerThrowShouldNotPreventOtherListenersFromBeingNotified() throws Http2Exception { - final boolean[] calledArray = new boolean[128]; - // The following setup will ensure that clientListener throws exceptions, and marks a value in an array - // such that clientListener2 will verify that is is set or fail the test. - int methodIndex = 0; - doAnswer(new ListenerExceptionThrower(calledArray, methodIndex)) - .when(clientListener).onStreamAdded(any(Http2Stream.class)); - doAnswer(new ListenerVerifyCallAnswer(calledArray, methodIndex++)) - .when(clientListener2).onStreamAdded(any(Http2Stream.class)); - - doAnswer(new ListenerExceptionThrower(calledArray, methodIndex)) - .when(clientListener).onStreamActive(any(Http2Stream.class)); - doAnswer(new ListenerVerifyCallAnswer(calledArray, methodIndex++)) - .when(clientListener2).onStreamActive(any(Http2Stream.class)); - - doAnswer(new ListenerExceptionThrower(calledArray, methodIndex)) - .when(clientListener).onStreamHalfClosed(any(Http2Stream.class)); - doAnswer(new ListenerVerifyCallAnswer(calledArray, methodIndex++)) - .when(clientListener2).onStreamHalfClosed(any(Http2Stream.class)); - - doAnswer(new ListenerExceptionThrower(calledArray, methodIndex)) - .when(clientListener).onStreamClosed(any(Http2Stream.class)); - doAnswer(new ListenerVerifyCallAnswer(calledArray, methodIndex++)) - .when(clientListener2).onStreamClosed(any(Http2Stream.class)); - - doAnswer(new ListenerExceptionThrower(calledArray, methodIndex)) - .when(clientListener).onStreamRemoved(any(Http2Stream.class)); - doAnswer(new ListenerVerifyCallAnswer(calledArray, methodIndex++)) - .when(clientListener2).onStreamRemoved(any(Http2Stream.class)); - - doAnswer(new ListenerExceptionThrower(calledArray, methodIndex)) - .when(clientListener).onGoAwaySent(anyInt(), anyLong(), any(ByteBuf.class)); - doAnswer(new ListenerVerifyCallAnswer(calledArray, methodIndex++)) - .when(clientListener2).onGoAwaySent(anyInt(), anyLong(), any(ByteBuf.class)); - - doAnswer(new ListenerExceptionThrower(calledArray, methodIndex)) - .when(clientListener).onGoAwayReceived(anyInt(), anyLong(), any(ByteBuf.class)); - doAnswer(new ListenerVerifyCallAnswer(calledArray, methodIndex++)) - .when(clientListener2).onGoAwayReceived(anyInt(), anyLong(), any(ByteBuf.class)); - - doAnswer(new ListenerExceptionThrower(calledArray, methodIndex)) - .when(clientListener).onStreamAdded(any(Http2Stream.class)); - doAnswer(new ListenerVerifyCallAnswer(calledArray, methodIndex++)) - .when(clientListener2).onStreamAdded(any(Http2Stream.class)); - - // Now we add clientListener2 and exercise all listener functionality - try { - client.addListener(clientListener2); - Http2Stream stream = client.local().createStream(3, false); - verify(clientListener).onStreamAdded(any(Http2Stream.class)); - verify(clientListener2).onStreamAdded(any(Http2Stream.class)); - verify(clientListener).onStreamActive(any(Http2Stream.class)); - verify(clientListener2).onStreamActive(any(Http2Stream.class)); - - Http2Stream reservedStream = client.remote().reservePushStream(2, stream); - verify(clientListener, never()).onStreamActive(streamEq(reservedStream)); - verify(clientListener2, never()).onStreamActive(streamEq(reservedStream)); - - reservedStream.open(false); - verify(clientListener).onStreamActive(streamEq(reservedStream)); - verify(clientListener2).onStreamActive(streamEq(reservedStream)); - - stream.closeLocalSide(); - verify(clientListener).onStreamHalfClosed(any(Http2Stream.class)); - verify(clientListener2).onStreamHalfClosed(any(Http2Stream.class)); - - stream.close(); - verify(clientListener).onStreamClosed(any(Http2Stream.class)); - verify(clientListener2).onStreamClosed(any(Http2Stream.class)); - verify(clientListener).onStreamRemoved(any(Http2Stream.class)); - verify(clientListener2).onStreamRemoved(any(Http2Stream.class)); - - client.goAwaySent(client.connectionStream().id(), Http2Error.INTERNAL_ERROR.code(), Unpooled.EMPTY_BUFFER); - verify(clientListener).onGoAwaySent(anyInt(), anyLong(), any(ByteBuf.class)); - verify(clientListener2).onGoAwaySent(anyInt(), anyLong(), any(ByteBuf.class)); - - client.goAwayReceived(client.connectionStream().id(), - Http2Error.INTERNAL_ERROR.code(), Unpooled.EMPTY_BUFFER); - verify(clientListener).onGoAwayReceived(anyInt(), anyLong(), any(ByteBuf.class)); - verify(clientListener2).onGoAwayReceived(anyInt(), anyLong(), any(ByteBuf.class)); - } finally { - client.removeListener(clientListener2); - } - } - - private void testRemoveAllStreams() throws InterruptedException { - final CountDownLatch latch = new CountDownLatch(1); - final Promise promise = group.next().newPromise(); - promise.asFuture().addListener(future -> { - latch.countDown(); - }); - client.close(promise); - assertTrue(latch.await(5, TimeUnit.SECONDS)); - } - - private static void incrementAndGetStreamShouldRespectOverflow(final Endpoint endpoint, int streamId) { - assertTrue(streamId > 0); - try { - endpoint.createStream(streamId, true); - streamId = endpoint.incrementAndGetNextStreamId(); - } catch (Throwable t) { - fail(t); - } - assertTrue(streamId < 0); - final int finalStreamId = streamId; - assertThrows(Http2NoMoreStreamIdsException.class, new Executable() { - @Override - public void execute() throws Throwable { - endpoint.createStream(finalStreamId, true); - } - }); - } - - private static void incrementAndGetStreamShouldSucceed(Endpoint endpoint) throws Http2Exception { - Http2Stream streamA = endpoint.createStream(endpoint.incrementAndGetNextStreamId(), true); - Http2Stream streamB = endpoint.createStream(streamA.id() + 2, true); - Http2Stream streamC = endpoint.createStream(endpoint.incrementAndGetNextStreamId(), true); - assertEquals(streamB.id() + 2, streamC.id()); - endpoint.createStream(streamC.id() + 2, true); - } - - private static final class ListenerExceptionThrower implements Answer { - private static final RuntimeException FAKE_EXCEPTION = new RuntimeException("Fake Exception"); - private final boolean[] array; - private final int index; - - ListenerExceptionThrower(boolean[] array, int index) { - this.array = array; - this.index = index; - } - - @Override - public Void answer(InvocationOnMock invocation) throws Throwable { - array[index] = true; - throw FAKE_EXCEPTION; - } - } - - private static final class ListenerVerifyCallAnswer implements Answer { - private final boolean[] array; - private final int index; - - ListenerVerifyCallAnswer(boolean[] array, int index) { - this.array = array; - this.index = index; - } - - @Override - public Void answer(InvocationOnMock invocation) throws Throwable { - assertTrue(array[index]); - return null; - } - } - - @SuppressWarnings("unchecked") - private static T streamEq(T stream) { - return (T) (stream == null ? ArgumentMatchers.isNull() : eq(stream)); - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2FrameReaderTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2FrameReaderTest.java deleted file mode 100644 index bfedf0ac61..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2FrameReaderTest.java +++ /dev/null @@ -1,453 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty.channel.ChannelHandlerContext; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import static io.netty.handler.codec.http2.Http2CodecUtil.*; -import static io.netty.handler.codec.http2.Http2FrameTypes.*; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.*; - - -/** - * Tests for {@link DefaultHttp2FrameReader}. - */ -public class DefaultHttp2FrameReaderTest { - @Mock - private Http2FrameListener listener; - - @Mock - private ChannelHandlerContext ctx; - - private DefaultHttp2FrameReader frameReader; - - // Used to generate frame - private HpackEncoder hpackEncoder; - - @BeforeEach - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT); - - frameReader = new DefaultHttp2FrameReader(); - hpackEncoder = new HpackEncoder(); - } - - @AfterEach - public void tearDown() { - frameReader.close(); - } - - @Test - public void readHeaderFrame() throws Http2Exception { - final int streamId = 1; - - ByteBuf input = Unpooled.buffer(); - try { - Http2Headers headers = new DefaultHttp2Headers() - .authority("foo") - .method("get") - .path("/") - .scheme("https"); - Http2Flags flags = new Http2Flags().endOfHeaders(true).endOfStream(true); - writeHeaderFrame(input, streamId, headers, flags); - frameReader.readFrame(ctx, input, listener); - - verify(listener).onHeadersRead(ctx, 1, headers, 0, true); - } finally { - input.release(); - } - } - - @Test - public void readHeaderFrameAndContinuationFrame() throws Http2Exception { - final int streamId = 1; - - ByteBuf input = Unpooled.buffer(); - try { - Http2Headers headers = new DefaultHttp2Headers() - .authority("foo") - .method("get") - .path("/") - .scheme("https"); - writeHeaderFrame(input, streamId, headers, - new Http2Flags().endOfHeaders(false).endOfStream(true)); - writeContinuationFrame(input, streamId, new DefaultHttp2Headers().add("foo", "bar"), - new Http2Flags().endOfHeaders(true)); - - frameReader.readFrame(ctx, input, listener); - - verify(listener).onHeadersRead(ctx, 1, headers.add("foo", "bar"), 0, true); - } finally { - input.release(); - } - } - - @Test - public void readUnknownFrame() throws Http2Exception { - ByteBuf input = Unpooled.buffer(); - ByteBuf payload = Unpooled.buffer(); - try { - payload.writeByte(1); - - writeFrameHeader(input, payload.readableBytes(), (byte) 0xff, new Http2Flags(), 0); - input.writeBytes(payload); - frameReader.readFrame(ctx, input, listener); - - verify(listener).onUnknownFrame( - ctx, (byte) 0xff, 0, new Http2Flags(), payload.slice(0, 1)); - } finally { - payload.release(); - input.release(); - } - } - - @Test - public void failedWhenUnknownFrameInMiddleOfHeaderBlock() throws Http2Exception { - final int streamId = 1; - - final ByteBuf input = Unpooled.buffer(); - try { - Http2Headers headers = new DefaultHttp2Headers() - .authority("foo") - .method("get") - .path("/") - .scheme("https"); - Http2Flags flags = new Http2Flags().endOfHeaders(false).endOfStream(true); - writeHeaderFrame(input, streamId, headers, flags); - writeFrameHeader(input, 0, (byte) 0xff, new Http2Flags(), streamId); - - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - frameReader.readFrame(ctx, input, listener); - } - }); - } finally { - input.release(); - } - } - - @Test - public void failedWhenContinuationFrameStreamIdMismatch() throws Http2Exception { - final ByteBuf input = Unpooled.buffer(); - try { - Http2Headers headers = new DefaultHttp2Headers() - .authority("foo") - .method("get") - .path("/") - .scheme("https"); - writeHeaderFrame(input, 1, headers, - new Http2Flags().endOfHeaders(false).endOfStream(true)); - writeContinuationFrame(input, 3, new DefaultHttp2Headers().add("foo", "bar"), - new Http2Flags().endOfHeaders(true)); - - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - frameReader.readFrame(ctx, input, listener); - } - }); - } finally { - input.release(); - } - } - - @Test - public void failedWhenContinuationFrameNotFollowHeaderFrame() throws Http2Exception { - final ByteBuf input = Unpooled.buffer(); - try { - writeContinuationFrame(input, 1, new DefaultHttp2Headers().add("foo", "bar"), - new Http2Flags().endOfHeaders(true)); - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - frameReader.readFrame(ctx, input, listener); - } - }); - } finally { - input.release(); - } - } - - @Test - public void failedWhenHeaderFrameDependsOnItself() throws Http2Exception { - final ByteBuf input = Unpooled.buffer(); - try { - Http2Headers headers = new DefaultHttp2Headers() - .authority("foo") - .method("get") - .path("/") - .scheme("https"); - writeHeaderFramePriorityPresent( - input, 1, headers, - new Http2Flags().endOfHeaders(true).endOfStream(true).priorityPresent(true), - 1, 10); - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - frameReader.readFrame(ctx, input, listener); - } - }); - } finally { - input.release(); - } - } - - @Test - public void readHeaderAndData() throws Http2Exception { - ByteBuf input = Unpooled.buffer(); - ByteBuf dataPayload = Unpooled.buffer(); - try { - Http2Headers headers = new DefaultHttp2Headers() - .authority("foo") - .method("get") - .path("/") - .scheme("https"); - dataPayload.writeByte(1); - writeHeaderFrameWithData(input, 1, headers, dataPayload); - - frameReader.readFrame(ctx, input, listener); - - verify(listener).onHeadersRead(ctx, 1, headers, 0, false); - verify(listener).onDataRead(ctx, 1, dataPayload.slice(0, 1), 0, true); - } finally { - input.release(); - dataPayload.release(); - } - } - - @Test - public void failedWhenDataFrameNotAssociateWithStream() throws Http2Exception { - final ByteBuf input = Unpooled.buffer(); - ByteBuf payload = Unpooled.buffer(); - try { - payload.writeByte(1); - - writeFrameHeader(input, payload.readableBytes(), DATA, new Http2Flags().endOfStream(true), 0); - input.writeBytes(payload); - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - frameReader.readFrame(ctx, input, listener); - } - }); - } finally { - payload.release(); - input.release(); - } - } - - @Test - public void readPriorityFrame() throws Http2Exception { - ByteBuf input = Unpooled.buffer(); - try { - writePriorityFrame(input, 1, 0, 10); - frameReader.readFrame(ctx, input, listener); - } finally { - input.release(); - } - } - - @Test - public void failedWhenPriorityFrameDependsOnItself() throws Http2Exception { - final ByteBuf input = Unpooled.buffer(); - try { - writePriorityFrame(input, 1, 1, 10); - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - frameReader.readFrame(ctx, input, listener); - } - }); - } finally { - input.release(); - } - } - - @Test - public void failedWhenWindowUpdateFrameWithZeroDelta() throws Http2Exception { - final ByteBuf input = Unpooled.buffer(); - try { - writeFrameHeader(input, 4, WINDOW_UPDATE, new Http2Flags(), 0); - input.writeInt(0); - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - frameReader.readFrame(ctx, input, listener); - } - }); - } finally { - input.release(); - } - } - - @Test - public void readSettingsFrame() throws Http2Exception { - ByteBuf input = Unpooled.buffer(); - try { - writeFrameHeader(input, 6, SETTINGS, new Http2Flags(), 0); - input.writeShort(SETTINGS_MAX_HEADER_LIST_SIZE); - input.writeInt(1024); - frameReader.readFrame(ctx, input, listener); - - listener.onSettingsRead(ctx, new Http2Settings().maxHeaderListSize(1024)); - } finally { - input.release(); - } - } - - @Test - public void readAckSettingsFrame() throws Http2Exception { - ByteBuf input = Unpooled.buffer(); - try { - writeFrameHeader(input, 0, SETTINGS, new Http2Flags().ack(true), 0); - frameReader.readFrame(ctx, input, listener); - - listener.onSettingsAckRead(ctx); - } finally { - input.release(); - } - } - - @Test - public void failedWhenSettingsFrameOnNonZeroStream() throws Http2Exception { - final ByteBuf input = Unpooled.buffer(); - try { - writeFrameHeader(input, 6, SETTINGS, new Http2Flags(), 1); - input.writeShort(SETTINGS_MAX_HEADER_LIST_SIZE); - input.writeInt(1024); - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - frameReader.readFrame(ctx, input, listener); - } - }); - } finally { - input.release(); - } - } - - @Test - public void failedWhenAckSettingsFrameWithPayload() throws Http2Exception { - final ByteBuf input = Unpooled.buffer(); - try { - writeFrameHeader(input, 1, SETTINGS, new Http2Flags().ack(true), 0); - input.writeByte(1); - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - frameReader.readFrame(ctx, input, listener); - } - }); - } finally { - input.release(); - } - } - - @Test - public void failedWhenSettingsFrameWithWrongPayloadLength() throws Http2Exception { - final ByteBuf input = Unpooled.buffer(); - try { - writeFrameHeader(input, 8, SETTINGS, new Http2Flags(), 0); - input.writeInt(SETTINGS_MAX_HEADER_LIST_SIZE); - input.writeInt(1024); - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - frameReader.readFrame(ctx, input, listener); - } - }); - } finally { - input.release(); - } - } - - private void writeHeaderFrame( - ByteBuf output, int streamId, Http2Headers headers, - Http2Flags flags) throws Http2Exception { - ByteBuf headerBlock = Unpooled.buffer(); - try { - hpackEncoder.encodeHeaders(streamId, headerBlock, headers, Http2HeadersEncoder.NEVER_SENSITIVE); - writeFrameHeader(output, headerBlock.readableBytes(), HEADERS, flags, streamId); - output.writeBytes(headerBlock, headerBlock.readableBytes()); - } finally { - headerBlock.release(); - } - } - - private void writeHeaderFrameWithData( - ByteBuf output, int streamId, Http2Headers headers, - ByteBuf dataPayload) throws Http2Exception { - ByteBuf headerBlock = Unpooled.buffer(); - try { - hpackEncoder.encodeHeaders(streamId, headerBlock, headers, Http2HeadersEncoder.NEVER_SENSITIVE); - writeFrameHeader(output, headerBlock.readableBytes(), HEADERS, - new Http2Flags().endOfHeaders(true), streamId); - output.writeBytes(headerBlock, headerBlock.readableBytes()); - - writeFrameHeader(output, dataPayload.readableBytes(), DATA, new Http2Flags().endOfStream(true), streamId); - output.writeBytes(dataPayload); - } finally { - headerBlock.release(); - } - } - - private void writeHeaderFramePriorityPresent( - ByteBuf output, int streamId, Http2Headers headers, - Http2Flags flags, int streamDependency, int weight) throws Http2Exception { - ByteBuf headerBlock = Unpooled.buffer(); - try { - headerBlock.writeInt(streamDependency); - headerBlock.writeByte(weight - 1); - hpackEncoder.encodeHeaders(streamId, headerBlock, headers, Http2HeadersEncoder.NEVER_SENSITIVE); - writeFrameHeader(output, headerBlock.readableBytes(), HEADERS, flags, streamId); - output.writeBytes(headerBlock, headerBlock.readableBytes()); - } finally { - headerBlock.release(); - } - } - - private void writeContinuationFrame( - ByteBuf output, int streamId, Http2Headers headers, - Http2Flags flags) throws Http2Exception { - ByteBuf headerBlock = Unpooled.buffer(); - try { - hpackEncoder.encodeHeaders(streamId, headerBlock, headers, Http2HeadersEncoder.NEVER_SENSITIVE); - writeFrameHeader(output, headerBlock.readableBytes(), CONTINUATION, flags, streamId); - output.writeBytes(headerBlock, headerBlock.readableBytes()); - } finally { - headerBlock.release(); - } - } - - private static void writePriorityFrame( - ByteBuf output, int streamId, int streamDependency, int weight) { - writeFrameHeader(output, 5, PRIORITY, new Http2Flags(), streamId); - output.writeInt(streamDependency); - output.writeByte(weight - 1); - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2FrameWriterTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2FrameWriterTest.java deleted file mode 100644 index eef3c0425d..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2FrameWriterTest.java +++ /dev/null @@ -1,381 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.ImmediateEventExecutor; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.stubbing.Answer; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.Arrays; - -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.when; - -/** - * Tests for {@link DefaultHttp2FrameWriter}. - */ -public class DefaultHttp2FrameWriterTest { - private DefaultHttp2FrameWriter frameWriter; - - private ByteBuf outbound; - - private ByteBuf expectedOutbound; - - private Http2HeadersEncoder http2HeadersEncoder; - - @Mock - private Channel channel; - - @Mock - private Future future; - - @Mock - private ChannelHandlerContext ctx; - - @BeforeEach - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - http2HeadersEncoder = new DefaultHttp2HeadersEncoder( - Http2HeadersEncoder.NEVER_SENSITIVE, new HpackEncoder(false, 16, 0)); - - frameWriter = new DefaultHttp2FrameWriter(new DefaultHttp2HeadersEncoder( - Http2HeadersEncoder.NEVER_SENSITIVE, new HpackEncoder(false, 16, 0))); - - outbound = Unpooled.buffer(); - - expectedOutbound = Unpooled.EMPTY_BUFFER; - - Answer answer = var1 -> { - Object msg = var1.getArgument(0); - if (msg instanceof ByteBuf) { - outbound.writeBytes((ByteBuf) msg); - } - ReferenceCountUtil.release(msg); - return future; - }; - when(ctx.write(any())).then(answer); - when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT); - when(ctx.channel()).thenReturn(channel); - when(ctx.executor()).thenReturn(ImmediateEventExecutor.INSTANCE); - when(ctx.newPromise()).thenReturn(ImmediateEventExecutor.INSTANCE.newPromise()); - } - - @AfterEach - public void tearDown() throws Exception { - outbound.release(); - expectedOutbound.release(); - frameWriter.close(); - } - - @Test - public void writeHeaders() throws Exception { - int streamId = 1; - Http2Headers headers = new DefaultHttp2Headers() - .method("GET").path("/").authority("foo.com").scheme("https"); - - frameWriter.writeHeaders(ctx, streamId, headers, 0, true); - - byte[] expectedPayload = headerPayload(streamId, headers); - byte[] expectedFrameBytes = { - (byte) 0x00, (byte) 0x00, (byte) 0x0a, // payload length = 10 - (byte) 0x01, // payload type = 1 - (byte) 0x05, // flags = (0x01 | 0x04) - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01 // stream id = 1 - }; - expectedOutbound = Unpooled.copiedBuffer(expectedFrameBytes, expectedPayload); - assertEquals(expectedOutbound, outbound); - } - - @Test - public void writeHeadersWithPadding() throws Exception { - int streamId = 1; - Http2Headers headers = new DefaultHttp2Headers() - .method("GET").path("/").authority("foo.com").scheme("https"); - - frameWriter.writeHeaders(ctx, streamId, headers, 5, true); - - byte[] expectedPayload = headerPayload(streamId, headers, (byte) 4); - byte[] expectedFrameBytes = { - (byte) 0x00, (byte) 0x00, (byte) 0x0f, // payload length = 16 - (byte) 0x01, // payload type = 1 - (byte) 0x0d, // flags = (0x01 | 0x04 | 0x08) - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01 // stream id = 1 - }; - expectedOutbound = Unpooled.copiedBuffer(expectedFrameBytes, expectedPayload); - assertEquals(expectedOutbound, outbound); - } - - @Test - public void writeHeadersNotEndStream() throws Exception { - int streamId = 1; - Http2Headers headers = new DefaultHttp2Headers() - .method("GET").path("/").authority("foo.com").scheme("https"); - - frameWriter.writeHeaders(ctx, streamId, headers, 0, false); - - byte[] expectedPayload = headerPayload(streamId, headers); - byte[] expectedFrameBytes = { - (byte) 0x00, (byte) 0x00, (byte) 0x0a, // payload length = 10 - (byte) 0x01, // payload type = 1 - (byte) 0x04, // flags = 0x04 - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01 // stream id = 1 - }; - ByteBuf expectedOutbound = Unpooled.copiedBuffer(expectedFrameBytes, expectedPayload); - assertEquals(expectedOutbound, outbound); - } - - @Test - public void writeEmptyDataWithPadding() { - int streamId = 1; - - ByteBuf payloadByteBuf = Unpooled.buffer(); - frameWriter.writeData(ctx, streamId, payloadByteBuf, 2, true); - - assertEquals(0, payloadByteBuf.refCnt()); - - byte[] expectedFrameBytes = { - (byte) 0x00, (byte) 0x00, (byte) 0x02, // payload length - (byte) 0x00, // payload type - (byte) 0x09, // flags - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, // stream id - (byte) 0x01, (byte) 0x00, // padding - }; - expectedOutbound = Unpooled.copiedBuffer(expectedFrameBytes); - assertEquals(expectedOutbound, outbound); - } - - /** - * Test large headers that exceed {@link DefaultHttp2FrameWriter#maxFrameSize()} - * the remaining headers will be sent in a CONTINUATION frame - */ - @Test - public void writeLargeHeaders() throws Exception { - int streamId = 1; - Http2Headers headers = new DefaultHttp2Headers() - .method("GET").path("/").authority("foo.com").scheme("https"); - headers = dummyHeaders(headers, 20); - - http2HeadersEncoder.configuration().maxHeaderListSize(Integer.MAX_VALUE); - frameWriter.headersConfiguration().maxHeaderListSize(Integer.MAX_VALUE); - frameWriter.maxFrameSize(Http2CodecUtil.MAX_FRAME_SIZE_LOWER_BOUND); - frameWriter.writeHeaders(ctx, streamId, headers, 0, true); - - byte[] expectedPayload = headerPayload(streamId, headers); - - // First frame: HEADER(length=0x4000, flags=0x01) - assertEquals(Http2CodecUtil.MAX_FRAME_SIZE_LOWER_BOUND, - outbound.readUnsignedMedium()); - assertEquals(0x01, outbound.readByte()); - assertEquals(0x01, outbound.readByte()); - assertEquals(streamId, outbound.readInt()); - - byte[] firstPayload = new byte[Http2CodecUtil.MAX_FRAME_SIZE_LOWER_BOUND]; - outbound.readBytes(firstPayload); - - int remainPayloadLength = expectedPayload.length - Http2CodecUtil.MAX_FRAME_SIZE_LOWER_BOUND; - // Second frame: CONTINUATION(length=remainPayloadLength, flags=0x04) - assertEquals(remainPayloadLength, outbound.readUnsignedMedium()); - assertEquals(0x09, outbound.readByte()); - assertEquals(0x04, outbound.readByte()); - assertEquals(streamId, outbound.readInt()); - - byte[] secondPayload = new byte[remainPayloadLength]; - outbound.readBytes(secondPayload); - - assertArrayEquals(Arrays.copyOfRange(expectedPayload, 0, firstPayload.length), - firstPayload); - assertArrayEquals(Arrays.copyOfRange(expectedPayload, firstPayload.length, - expectedPayload.length), - secondPayload); - } - - @Test - public void writeLargeHeaderWithPadding() throws Exception { - int streamId = 1; - Http2Headers headers = new DefaultHttp2Headers() - .method("GET").path("/").authority("foo.com").scheme("https"); - headers = dummyHeaders(headers, 20); - - http2HeadersEncoder.configuration().maxHeaderListSize(Integer.MAX_VALUE); - frameWriter.headersConfiguration().maxHeaderListSize(Integer.MAX_VALUE); - frameWriter.maxFrameSize(Http2CodecUtil.MAX_FRAME_SIZE_LOWER_BOUND); - frameWriter.writeHeaders(ctx, streamId, headers, 5, true); - - byte[] expectedPayload = buildLargeHeaderPayload(streamId, headers, (byte) 4, - Http2CodecUtil.MAX_FRAME_SIZE_LOWER_BOUND); - - // First frame: HEADER(length=0x4000, flags=0x09) - assertEquals(Http2CodecUtil.MAX_FRAME_SIZE_LOWER_BOUND, - outbound.readUnsignedMedium()); - assertEquals(0x01, outbound.readByte()); - assertEquals(0x09, outbound.readByte()); // 0x01 + 0x08 - assertEquals(streamId, outbound.readInt()); - - byte[] firstPayload = new byte[Http2CodecUtil.MAX_FRAME_SIZE_LOWER_BOUND]; - outbound.readBytes(firstPayload); - - int remainPayloadLength = expectedPayload.length - Http2CodecUtil.MAX_FRAME_SIZE_LOWER_BOUND; - // Second frame: CONTINUATION(length=remainPayloadLength, flags=0x04) - assertEquals(remainPayloadLength, outbound.readUnsignedMedium()); - assertEquals(0x09, outbound.readByte()); - assertEquals(0x04, outbound.readByte()); - assertEquals(streamId, outbound.readInt()); - - byte[] secondPayload = new byte[remainPayloadLength]; - outbound.readBytes(secondPayload); - - assertArrayEquals(Arrays.copyOfRange(expectedPayload, 0, firstPayload.length), - firstPayload); - assertArrayEquals(Arrays.copyOfRange(expectedPayload, firstPayload.length, - expectedPayload.length), - secondPayload); - } - - @Test - public void writeFrameZeroPayload() throws Exception { - frameWriter.writeFrame(ctx, (byte) 0xf, 0, new Http2Flags(), Unpooled.EMPTY_BUFFER); - - byte[] expectedFrameBytes = { - (byte) 0x00, (byte) 0x00, (byte) 0x00, // payload length - (byte) 0x0f, // payload type - (byte) 0x00, // flags - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 // stream id - }; - - expectedOutbound = Unpooled.wrappedBuffer(expectedFrameBytes); - assertEquals(expectedOutbound, outbound); - } - - @Test - public void writeFrameHasPayload() throws Exception { - byte[] payload = {(byte) 0x01, (byte) 0x03, (byte) 0x05, (byte) 0x07, (byte) 0x09}; - - // will auto release after frameWriter.writeFrame succeed - ByteBuf payloadByteBuf = Unpooled.wrappedBuffer(payload); - frameWriter.writeFrame(ctx, (byte) 0xf, 0, new Http2Flags(), payloadByteBuf); - - byte[] expectedFrameHeaderBytes = { - (byte) 0x00, (byte) 0x00, (byte) 0x05, // payload length - (byte) 0x0f, // payload type - (byte) 0x00, // flags - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 // stream id - }; - expectedOutbound = Unpooled.copiedBuffer(expectedFrameHeaderBytes, payload); - assertEquals(expectedOutbound, outbound); - } - - @Test - public void writePriority() { - frameWriter.writePriority( - ctx, /* streamId= */ 1, /* dependencyId= */ 2, /* weight= */ (short) 256, /* exclusive= */ true); - - expectedOutbound = Unpooled.copiedBuffer(new byte[] { - (byte) 0x00, (byte) 0x00, (byte) 0x05, // payload length = 5 - (byte) 0x02, // payload type = 2 - (byte) 0x00, // flags = 0x00 - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, // stream id = 1 - (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x02, // dependency id = 2 | exclusive = 1 << 63 - (byte) 0xFF, // weight = 255 (implicit +1) - }); - assertEquals(expectedOutbound, outbound); - } - - @Test - public void writePriorityDefaults() { - frameWriter.writePriority( - ctx, /* streamId= */ 1, /* dependencyId= */ 0, /* weight= */ (short) 16, /* exclusive= */ false); - - expectedOutbound = Unpooled.copiedBuffer(new byte[] { - (byte) 0x00, (byte) 0x00, (byte) 0x05, // payload length = 5 - (byte) 0x02, // payload type = 2 - (byte) 0x00, // flags = 0x00 - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, // stream id = 1 - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, // dependency id = 0 | exclusive = 0 << 63 - (byte) 0x0F, // weight = 15 (implicit +1) - }); - assertEquals(expectedOutbound, outbound); - } - - private byte[] headerPayload(int streamId, Http2Headers headers, byte padding) throws Http2Exception, IOException { - if (padding == 0) { - return headerPayload(streamId, headers); - } - - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - try { - outputStream.write(padding); - outputStream.write(headerPayload(streamId, headers)); - outputStream.write(new byte[padding]); - return outputStream.toByteArray(); - } finally { - outputStream.close(); - } - } - - private byte[] headerPayload(int streamId, Http2Headers headers) throws Http2Exception { - ByteBuf byteBuf = Unpooled.buffer(); - try { - http2HeadersEncoder.encodeHeaders(streamId, headers, byteBuf); - byte[] bytes = new byte[byteBuf.readableBytes()]; - byteBuf.readBytes(bytes); - return bytes; - } finally { - byteBuf.release(); - } - } - - private byte[] buildLargeHeaderPayload(int streamId, Http2Headers headers, byte padding, int maxFrameSize) - throws Http2Exception, IOException { - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - try { - outputStream.write(padding); - byte[] payload = headerPayload(streamId, headers); - int firstPayloadSize = maxFrameSize - (padding + 1); //1 for padding length - outputStream.write(payload, 0, firstPayloadSize); - outputStream.write(new byte[padding]); - outputStream.write(payload, firstPayloadSize, payload.length - firstPayloadSize); - return outputStream.toByteArray(); - } finally { - outputStream.close(); - } - } - - private static Http2Headers dummyHeaders(Http2Headers headers, int times) { - final String largeValue = repeat("dummy-value", 100); - for (int i = 0; i < times; i++) { - headers.add(String.format("dummy-%d", i), largeValue); - } - return headers; - } - - private static String repeat(String str, int count) { - return String.format(String.format("%%%ds", count), " ").replace(" ", str); - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2HeadersDecoderTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2HeadersDecoderTest.java deleted file mode 100644 index 72f08e0048..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2HeadersDecoderTest.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.AsciiString; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; - -import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_HEADER_LIST_SIZE; -import static io.netty.handler.codec.http2.Http2CodecUtil.MIN_HEADER_LIST_SIZE; -import static io.netty.handler.codec.http2.Http2HeadersEncoder.NEVER_SENSITIVE; -import static io.netty.handler.codec.http2.Http2TestUtil.newTestEncoder; -import static io.netty.handler.codec.http2.Http2TestUtil.randomBytes; -import static io.netty.util.CharsetUtil.UTF_8; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - -/** - * Tests for {@link DefaultHttp2HeadersDecoder}. - */ -public class DefaultHttp2HeadersDecoderTest { - - private DefaultHttp2HeadersDecoder decoder; - - @BeforeEach - public void setup() { - decoder = new DefaultHttp2HeadersDecoder(false); - } - - @Test - public void decodeShouldSucceed() throws Exception { - ByteBuf buf = encode(b(":method"), b("GET"), b("akey"), b("avalue"), randomBytes(), randomBytes()); - try { - Http2Headers headers = decoder.decodeHeaders(0, buf); - assertEquals(3, headers.size()); - assertEquals("GET", headers.method().toString()); - assertEquals("avalue", headers.get(new AsciiString("akey")).toString()); - } finally { - buf.release(); - } - } - - @Test - public void testExceedHeaderSize() throws Exception { - final int maxListSize = 100; - decoder.configuration().maxHeaderListSize(maxListSize, maxListSize); - final ByteBuf buf = encode(randomBytes(maxListSize), randomBytes(1)); - - try { - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - decoder.decodeHeaders(0, buf); - } - }); - } finally { - buf.release(); - } - } - - @Test - public void decodeLargerThanHeaderListSizeButLessThanGoAway() throws Exception { - decoder.maxHeaderListSize(MIN_HEADER_LIST_SIZE, MAX_HEADER_LIST_SIZE); - final ByteBuf buf = encode(b(":method"), b("GET")); - final int streamId = 1; - Http2Exception.HeaderListSizeException e = - assertThrows(Http2Exception.HeaderListSizeException.class, new Executable() { - @Override - public void execute() throws Throwable { - decoder.decodeHeaders(streamId, buf); - } - }); - assertEquals(streamId, e.streamId()); - buf.release(); - } - - @Test - public void decodeLargerThanHeaderListSizeButLessThanGoAwayWithInitialDecoderSettings() throws Exception { - final ByteBuf buf = encode(b(":method"), b("GET"), b("test_header"), - b(String.format("%09000d", 0).replace('0', 'A'))); - final int streamId = 1; - try { - Http2Exception.HeaderListSizeException e = assertThrows(Http2Exception.HeaderListSizeException.class, - new Executable() { - @Override - public void execute() throws Throwable { - decoder.decodeHeaders(streamId, buf); - } - }); - assertEquals(streamId, e.streamId()); - } finally { - buf.release(); - } - } - - @Test - public void decodeLargerThanHeaderListSizeGoAway() throws Exception { - decoder.maxHeaderListSize(MIN_HEADER_LIST_SIZE, MIN_HEADER_LIST_SIZE); - final ByteBuf buf = encode(b(":method"), b("GET")); - final int streamId = 1; - try { - Http2Exception e = assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - decoder.decodeHeaders(streamId, buf); - } - }); - assertEquals(Http2Error.PROTOCOL_ERROR, e.error()); - } finally { - buf.release(); - } - } - - private static byte[] b(String string) { - return string.getBytes(UTF_8); - } - - private static ByteBuf encode(byte[]... entries) throws Exception { - HpackEncoder hpackEncoder = newTestEncoder(); - ByteBuf out = Unpooled.buffer(); - Http2Headers http2Headers = new DefaultHttp2Headers(false); - for (int ix = 0; ix < entries.length;) { - http2Headers.add(new AsciiString(entries[ix++], false), new AsciiString(entries[ix++], false)); - } - hpackEncoder.encodeHeaders(3 /* randomly chosen */, out, http2Headers, NEVER_SENSITIVE); - return out; - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2HeadersEncoderTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2HeadersEncoderTest.java deleted file mode 100644 index 3091c86841..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2HeadersEncoderTest.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.handler.codec.http2.Http2Exception.StreamException; -import io.netty.util.AsciiString; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; - -import static io.netty.handler.codec.http2.Http2TestUtil.newTestEncoder; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** - * Tests for {@link DefaultHttp2HeadersEncoder}. - */ -public class DefaultHttp2HeadersEncoderTest { - - private DefaultHttp2HeadersEncoder encoder; - - @BeforeEach - public void setup() { - encoder = new DefaultHttp2HeadersEncoder(Http2HeadersEncoder.NEVER_SENSITIVE, newTestEncoder()); - } - - @Test - public void encodeShouldSucceed() throws Http2Exception { - Http2Headers headers = headers(); - ByteBuf buf = Unpooled.buffer(); - try { - encoder.encodeHeaders(3 /* randomly chosen */, headers, buf); - assertTrue(buf.writerIndex() > 0); - } finally { - buf.release(); - } - } - - @Test - public void headersExceedMaxSetSizeShouldFail() throws Http2Exception { - final Http2Headers headers = headers(); - encoder.maxHeaderListSize(2); - assertThrows(StreamException.class, new Executable() { - @Override - public void execute() throws Throwable { - encoder.encodeHeaders(3 /* randomly chosen */, headers, Unpooled.buffer()); - } - }); - } - - private static Http2Headers headers() { - return new DefaultHttp2Headers().method(new AsciiString("GET")).add(new AsciiString("a"), new AsciiString("1")) - .add(new AsciiString("a"), new AsciiString("2")); - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2HeadersTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2HeadersTest.java deleted file mode 100644 index 1bb204883d..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2HeadersTest.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.handler.codec.http2.Http2Headers.PseudoHeaderName; -import io.netty.util.internal.StringUtil; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; - -import java.util.Map.Entry; - -import static io.netty.util.AsciiString.*; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - -public class DefaultHttp2HeadersTest { - - @Test - public void nullHeaderNameNotAllowed() { - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - new DefaultHttp2Headers().add(null, "foo"); - } - }); - } - - @Test - public void emptyHeaderNameNotAllowed() { - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - new DefaultHttp2Headers().add(StringUtil.EMPTY_STRING, "foo"); - } - }); - } - - @Test - public void testPseudoHeadersMustComeFirstWhenIterating() { - Http2Headers headers = newHeaders(); - - verifyPseudoHeadersFirst(headers); - verifyAllPseudoHeadersPresent(headers); - } - - @Test - public void testPseudoHeadersWithRemovePreservesPseudoIterationOrder() { - Http2Headers headers = newHeaders(); - - Http2Headers nonPseudoHeaders = new DefaultHttp2Headers(); - for (Entry entry : headers) { - if (entry.getKey().length() == 0 || entry.getKey().charAt(0) != ':' && - !nonPseudoHeaders.contains(entry.getKey())) { - nonPseudoHeaders.add(entry.getKey(), entry.getValue()); - } - } - - assertFalse(nonPseudoHeaders.isEmpty()); - - // Remove all the non-pseudo headers and verify - for (Entry nonPseudoHeaderEntry : nonPseudoHeaders) { - assertTrue(headers.remove(nonPseudoHeaderEntry.getKey())); - verifyPseudoHeadersFirst(headers); - verifyAllPseudoHeadersPresent(headers); - } - - // Add back all non-pseudo headers - for (Entry nonPseudoHeaderEntry : nonPseudoHeaders) { - headers.add(nonPseudoHeaderEntry.getKey(), of("goo")); - verifyPseudoHeadersFirst(headers); - verifyAllPseudoHeadersPresent(headers); - } - } - - @Test - public void testPseudoHeadersWithClearDoesNotLeak() { - Http2Headers headers = newHeaders(); - - assertFalse(headers.isEmpty()); - headers.clear(); - assertTrue(headers.isEmpty()); - - // Combine 2 headers together, make sure pseudo headers stay up front. - headers.add("name1", "value1").scheme("nothing"); - verifyPseudoHeadersFirst(headers); - - Http2Headers other = new DefaultHttp2Headers().add("name2", "value2").authority("foo"); - verifyPseudoHeadersFirst(other); - - headers.add(other); - verifyPseudoHeadersFirst(headers); - - // Make sure the headers are what we expect them to be, and no leaking behind the scenes. - assertEquals(4, headers.size()); - assertEquals("value1", headers.get("name1")); - assertEquals("value2", headers.get("name2")); - assertEquals("nothing", headers.scheme()); - assertEquals("foo", headers.authority()); - } - - @Test - public void testSetHeadersOrdersPseudoHeadersCorrectly() { - Http2Headers headers = newHeaders(); - Http2Headers other = new DefaultHttp2Headers().add("name2", "value2").authority("foo"); - - headers.set(other); - verifyPseudoHeadersFirst(headers); - assertEquals(other.size(), headers.size()); - assertEquals("foo", headers.authority()); - assertEquals("value2", headers.get("name2")); - } - - @Test - public void testSetAllOrdersPseudoHeadersCorrectly() { - Http2Headers headers = newHeaders(); - Http2Headers other = new DefaultHttp2Headers().add("name2", "value2").authority("foo"); - - int headersSizeBefore = headers.size(); - headers.setAll(other); - verifyPseudoHeadersFirst(headers); - verifyAllPseudoHeadersPresent(headers); - assertEquals(headersSizeBefore + 1, headers.size()); - assertEquals("foo", headers.authority()); - assertEquals("value2", headers.get("name2")); - } - - @Test - public void testHeaderNameValidation() { - final Http2Headers headers = newHeaders(); - - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - headers.add(of("Foo"), of("foo")); - } - }); - } - - @Test - public void testClearResetsPseudoHeaderDivision() { - DefaultHttp2Headers http2Headers = new DefaultHttp2Headers(); - http2Headers.method("POST"); - http2Headers.set("some", "value"); - http2Headers.clear(); - http2Headers.method("GET"); - assertEquals(1, http2Headers.names().size()); - } - - @Test - public void testContainsNameAndValue() { - Http2Headers headers = newHeaders(); - assertTrue(headers.contains("name1", "value2")); - assertFalse(headers.contains("name1", "Value2")); - assertTrue(headers.contains("2name", "Value3", true)); - assertFalse(headers.contains("2name", "Value3", false)); - } - - private static void verifyAllPseudoHeadersPresent(Http2Headers headers) { - for (PseudoHeaderName pseudoName : PseudoHeaderName.values()) { - assertNotNull(headers.get(pseudoName.value())); - } - } - - static void verifyPseudoHeadersFirst(Http2Headers headers) { - CharSequence lastNonPseudoName = null; - for (Entry entry: headers) { - if (entry.getKey().length() == 0 || entry.getKey().charAt(0) != ':') { - lastNonPseudoName = entry.getKey(); - } else if (lastNonPseudoName != null) { - fail("All pseudo headers must be first in iteration. Pseudo header " + entry.getKey() + - " is after a non pseudo header " + lastNonPseudoName); - } - } - } - - private static Http2Headers newHeaders() { - Http2Headers headers = new DefaultHttp2Headers(); - headers.add(of("name1"), of("value1"), of("value2")); - headers.method(of("POST")); - headers.add(of("2name"), of("value3")); - headers.path(of("/index.html")); - headers.status(of("200")); - headers.authority(of("netty.io")); - headers.add(of("name3"), of("value4")); - headers.scheme(of("https")); - headers.add(of(":protocol"), of("websocket")); - return headers; - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2LocalFlowControllerTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2LocalFlowControllerTest.java deleted file mode 100644 index 0573aab8fa..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2LocalFlowControllerTest.java +++ /dev/null @@ -1,448 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http2.Http2Stream.State; -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.Promise; -import junit.framework.AssertionFailedError; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.stubbing.Answer; - -import static io.netty.handler.codec.http2.DefaultHttp2LocalFlowController.DEFAULT_WINDOW_UPDATE_RATIO; -import static io.netty.handler.codec.http2.Http2CodecUtil.CONNECTION_STREAM_ID; -import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_WINDOW_SIZE; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -/** - * Tests for {@link DefaultHttp2LocalFlowController}. - */ -public class DefaultHttp2LocalFlowControllerTest { - private static final int STREAM_ID = 1; - - private DefaultHttp2LocalFlowController controller; - - @Mock - private Http2FrameWriter frameWriter; - - @Mock - private ChannelHandlerContext ctx; - - @Mock - private EventExecutor executor; - - @Mock - private Promise promise; - - private DefaultHttp2Connection connection; - - @BeforeEach - public void setup() throws Http2Exception { - MockitoAnnotations.initMocks(this); - setupChannelHandlerContext(false); - when(executor.inEventLoop()).thenReturn(true); - - initController(false); - } - - private void setupChannelHandlerContext(boolean allowFlush) { - reset(ctx); - when(ctx.newPromise()).thenReturn(promise); - if (allowFlush) { - when(ctx.flush()).then((Answer) invocationOnMock -> ctx); - } else { - when(ctx.flush()).thenThrow(new AssertionFailedError("forbidden")); - } - when(ctx.executor()).thenReturn(executor); - } - - @Test - public void dataFrameShouldBeAccepted() throws Http2Exception { - receiveFlowControlledFrame(STREAM_ID, 10, 0, false); - verifyWindowUpdateNotSent(); - } - - @Test - public void windowUpdateShouldSendOnceBytesReturned() throws Http2Exception { - int dataSize = (int) (DEFAULT_WINDOW_SIZE * DEFAULT_WINDOW_UPDATE_RATIO) + 1; - receiveFlowControlledFrame(STREAM_ID, dataSize, 0, false); - - // Return only a few bytes and verify that the WINDOW_UPDATE hasn't been sent. - assertFalse(consumeBytes(STREAM_ID, 10)); - verifyWindowUpdateNotSent(STREAM_ID); - verifyWindowUpdateNotSent(CONNECTION_STREAM_ID); - - // Return the rest and verify the WINDOW_UPDATE is sent. - assertTrue(consumeBytes(STREAM_ID, dataSize - 10)); - verifyWindowUpdateSent(STREAM_ID, dataSize); - verifyWindowUpdateSent(CONNECTION_STREAM_ID, dataSize); - verifyNoMoreInteractions(frameWriter); - } - - @Test - public void connectionWindowShouldAutoRefillWhenDataReceived() throws Http2Exception { - // Reconfigure controller to auto-refill the connection window. - initController(true); - - int dataSize = (int) (DEFAULT_WINDOW_SIZE * DEFAULT_WINDOW_UPDATE_RATIO) + 1; - receiveFlowControlledFrame(STREAM_ID, dataSize, 0, false); - // Verify that we immediately refill the connection window. - verifyWindowUpdateSent(CONNECTION_STREAM_ID, dataSize); - - // Return only a few bytes and verify that the WINDOW_UPDATE hasn't been sent for the stream. - assertFalse(consumeBytes(STREAM_ID, 10)); - verifyWindowUpdateNotSent(STREAM_ID); - - // Return the rest and verify the WINDOW_UPDATE is sent for the stream. - assertTrue(consumeBytes(STREAM_ID, dataSize - 10)); - verifyWindowUpdateSent(STREAM_ID, dataSize); - verifyNoMoreInteractions(frameWriter); - } - - @Test - public void connectionFlowControlExceededShouldThrow() throws Http2Exception { - // Window exceeded because of the padding. - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - receiveFlowControlledFrame(STREAM_ID, DEFAULT_WINDOW_SIZE, 1, true); - } - }); - } - - @Test - public void windowUpdateShouldNotBeSentAfterEndOfStream() throws Http2Exception { - int dataSize = (int) (DEFAULT_WINDOW_SIZE * DEFAULT_WINDOW_UPDATE_RATIO) + 1; - - // Set end-of-stream on the frame, so no window update will be sent for the stream. - receiveFlowControlledFrame(STREAM_ID, dataSize, 0, true); - verifyWindowUpdateNotSent(CONNECTION_STREAM_ID); - verifyWindowUpdateNotSent(STREAM_ID); - - assertTrue(consumeBytes(STREAM_ID, dataSize)); - verifyWindowUpdateSent(CONNECTION_STREAM_ID, dataSize); - verifyWindowUpdateNotSent(STREAM_ID); - } - - @Test - public void windowUpdateShouldNotBeSentAfterStreamIsClosedForUnconsumedBytes() throws Http2Exception { - int dataSize = (int) (DEFAULT_WINDOW_SIZE * DEFAULT_WINDOW_UPDATE_RATIO) + 1; - - // Don't set end-of-stream on the frame as we want to verify that we not return the unconsumed bytes in this - // case once the stream was closed, - receiveFlowControlledFrame(STREAM_ID, dataSize, 0, false); - verifyWindowUpdateNotSent(CONNECTION_STREAM_ID); - verifyWindowUpdateNotSent(STREAM_ID); - - // Close the stream - Http2Stream stream = connection.stream(STREAM_ID); - stream.close(); - assertEquals(State.CLOSED, stream.state()); - assertNull(connection.stream(STREAM_ID)); - - // The window update for the connection should made it through but not the update for the already closed - // stream - verifyWindowUpdateSent(CONNECTION_STREAM_ID, dataSize); - verifyWindowUpdateNotSent(STREAM_ID); - } - - @Test - public void windowUpdateShouldBeWrittenWhenStreamIsClosedAndFlushed() throws Http2Exception { - int dataSize = (int) (DEFAULT_WINDOW_SIZE * DEFAULT_WINDOW_UPDATE_RATIO) + 1; - - setupChannelHandlerContext(true); - - receiveFlowControlledFrame(STREAM_ID, dataSize, 0, false); - verifyWindowUpdateNotSent(CONNECTION_STREAM_ID); - verifyWindowUpdateNotSent(STREAM_ID); - - connection.stream(STREAM_ID).close(); - - verifyWindowUpdateSent(CONNECTION_STREAM_ID, dataSize); - - // Verify we saw one flush. - verify(ctx).flush(); - } - - @Test - public void halfWindowRemainingShouldUpdateAllWindows() throws Http2Exception { - int dataSize = (int) (DEFAULT_WINDOW_SIZE * DEFAULT_WINDOW_UPDATE_RATIO) + 1; - int initialWindowSize = DEFAULT_WINDOW_SIZE; - int windowDelta = getWindowDelta(initialWindowSize, initialWindowSize, dataSize); - - // Don't set end-of-stream so we'll get a window update for the stream as well. - receiveFlowControlledFrame(STREAM_ID, dataSize, 0, false); - assertTrue(consumeBytes(STREAM_ID, dataSize)); - verifyWindowUpdateSent(CONNECTION_STREAM_ID, windowDelta); - verifyWindowUpdateSent(STREAM_ID, windowDelta); - } - - @Test - public void initialWindowUpdateShouldAllowMoreFrames() throws Http2Exception { - // Send a frame that takes up the entire window. - int initialWindowSize = DEFAULT_WINDOW_SIZE; - receiveFlowControlledFrame(STREAM_ID, initialWindowSize, 0, false); - assertEquals(0, window(STREAM_ID)); - assertEquals(0, window(CONNECTION_STREAM_ID)); - consumeBytes(STREAM_ID, initialWindowSize); - assertEquals(initialWindowSize, window(STREAM_ID)); - assertEquals(DEFAULT_WINDOW_SIZE, window(CONNECTION_STREAM_ID)); - - // Update the initial window size to allow another frame. - int newInitialWindowSize = 2 * initialWindowSize; - controller.initialWindowSize(newInitialWindowSize); - assertEquals(newInitialWindowSize, window(STREAM_ID)); - assertEquals(DEFAULT_WINDOW_SIZE, window(CONNECTION_STREAM_ID)); - - // Clear any previous calls to the writer. - reset(frameWriter); - - // Send the next frame and verify that the expected window updates were sent. - receiveFlowControlledFrame(STREAM_ID, initialWindowSize, 0, false); - assertTrue(consumeBytes(STREAM_ID, initialWindowSize)); - int delta = newInitialWindowSize - initialWindowSize; - verifyWindowUpdateSent(STREAM_ID, delta); - verifyWindowUpdateSent(CONNECTION_STREAM_ID, delta); - } - - @Test - public void connectionWindowShouldAdjustWithMultipleStreams() throws Http2Exception { - int newStreamId = 3; - connection.local().createStream(newStreamId, false); - - try { - assertEquals(DEFAULT_WINDOW_SIZE, window(STREAM_ID)); - assertEquals(DEFAULT_WINDOW_SIZE, window(CONNECTION_STREAM_ID)); - - // Test that both stream and connection window are updated (or not updated) together - int data1 = (int) (DEFAULT_WINDOW_SIZE * DEFAULT_WINDOW_UPDATE_RATIO) + 1; - receiveFlowControlledFrame(STREAM_ID, data1, 0, false); - verifyWindowUpdateNotSent(STREAM_ID); - verifyWindowUpdateNotSent(CONNECTION_STREAM_ID); - assertEquals(DEFAULT_WINDOW_SIZE - data1, window(STREAM_ID)); - assertEquals(DEFAULT_WINDOW_SIZE - data1, window(CONNECTION_STREAM_ID)); - assertTrue(consumeBytes(STREAM_ID, data1)); - verifyWindowUpdateSent(STREAM_ID, data1); - verifyWindowUpdateSent(CONNECTION_STREAM_ID, data1); - - reset(frameWriter); - - // Create a scenario where data is depleted from multiple streams, but not enough data - // to generate a window update on those streams. The amount will be enough to generate - // a window update for the connection stream. - --data1; - int data2 = data1 >> 1; - receiveFlowControlledFrame(STREAM_ID, data1, 0, false); - receiveFlowControlledFrame(newStreamId, data1, 0, false); - verifyWindowUpdateNotSent(STREAM_ID); - verifyWindowUpdateNotSent(newStreamId); - verifyWindowUpdateNotSent(CONNECTION_STREAM_ID); - assertEquals(DEFAULT_WINDOW_SIZE - data1, window(STREAM_ID)); - assertEquals(DEFAULT_WINDOW_SIZE - data1, window(newStreamId)); - assertEquals(DEFAULT_WINDOW_SIZE - (data1 << 1), window(CONNECTION_STREAM_ID)); - assertFalse(consumeBytes(STREAM_ID, data1)); - assertTrue(consumeBytes(newStreamId, data2)); - verifyWindowUpdateNotSent(STREAM_ID); - verifyWindowUpdateNotSent(newStreamId); - verifyWindowUpdateSent(CONNECTION_STREAM_ID, data1 + data2); - assertEquals(DEFAULT_WINDOW_SIZE - data1, window(STREAM_ID)); - assertEquals(DEFAULT_WINDOW_SIZE - data1, window(newStreamId)); - assertEquals(DEFAULT_WINDOW_SIZE - (data1 - data2), window(CONNECTION_STREAM_ID)); - } finally { - connection.stream(newStreamId).close(); - } - } - - @Test - public void closeShouldConsumeBytes() throws Http2Exception { - receiveFlowControlledFrame(STREAM_ID, 10, 0, false); - assertEquals(10, controller.unconsumedBytes(connection.connectionStream())); - stream(STREAM_ID).close(); - assertEquals(0, controller.unconsumedBytes(connection.connectionStream())); - } - - @Test - public void closeShouldNotConsumeConnectionWindowWhenAutoRefilled() throws Http2Exception { - // Reconfigure controller to auto-refill the connection window. - initController(true); - - receiveFlowControlledFrame(STREAM_ID, 10, 0, false); - assertEquals(0, controller.unconsumedBytes(connection.connectionStream())); - stream(STREAM_ID).close(); - assertEquals(0, controller.unconsumedBytes(connection.connectionStream())); - } - - @Test - public void dataReceivedForClosedStreamShouldImmediatelyConsumeBytes() throws Http2Exception { - Http2Stream stream = stream(STREAM_ID); - stream.close(); - receiveFlowControlledFrame(stream, 10, 0, false); - assertEquals(0, controller.unconsumedBytes(connection.connectionStream())); - } - - @Test - public void dataReceivedForNullStreamShouldImmediatelyConsumeBytes() throws Http2Exception { - receiveFlowControlledFrame(null, 10, 0, false); - assertEquals(0, controller.unconsumedBytes(connection.connectionStream())); - } - - @Test - public void consumeBytesForNullStreamShouldIgnore() throws Http2Exception { - controller.consumeBytes(null, 10); - assertEquals(0, controller.unconsumedBytes(connection.connectionStream())); - } - - @Test - public void globalRatioShouldImpactStreams() throws Http2Exception { - float ratio = 0.6f; - controller.windowUpdateRatio(ratio); - testRatio(ratio, DEFAULT_WINDOW_SIZE << 1, 3, false); - } - - @Test - public void streamlRatioShouldImpactStreams() throws Http2Exception { - float ratio = 0.6f; - testRatio(ratio, DEFAULT_WINDOW_SIZE << 1, 3, true); - } - - @Test - public void consumeBytesForZeroNumBytesShouldIgnore() throws Http2Exception { - assertFalse(controller.consumeBytes(connection.stream(STREAM_ID), 0)); - } - - @Test - public void consumeBytesForNegativeNumBytesShouldFail() throws Http2Exception { - assertThrows(IllegalArgumentException.class, new Executable() { - @Override - public void execute() throws Throwable { - controller.consumeBytes(connection.stream(STREAM_ID), -1); - } - }); - } - - private void testRatio(float ratio, int newDefaultWindowSize, int newStreamId, boolean setStreamRatio) - throws Http2Exception { - int delta = newDefaultWindowSize - DEFAULT_WINDOW_SIZE; - controller.incrementWindowSize(stream(0), delta); - Http2Stream stream = connection.local().createStream(newStreamId, false); - if (setStreamRatio) { - controller.windowUpdateRatio(stream, ratio); - } - controller.incrementWindowSize(stream, delta); - reset(frameWriter); - try { - int data1 = (int) (newDefaultWindowSize * ratio) + 1; - int data2 = (int) (DEFAULT_WINDOW_SIZE * DEFAULT_WINDOW_UPDATE_RATIO) >> 1; - receiveFlowControlledFrame(STREAM_ID, data2, 0, false); - receiveFlowControlledFrame(newStreamId, data1, 0, false); - verifyWindowUpdateNotSent(STREAM_ID); - verifyWindowUpdateNotSent(newStreamId); - verifyWindowUpdateNotSent(CONNECTION_STREAM_ID); - assertEquals(DEFAULT_WINDOW_SIZE - data2, window(STREAM_ID)); - assertEquals(newDefaultWindowSize - data1, window(newStreamId)); - assertEquals(newDefaultWindowSize - data2 - data1, window(CONNECTION_STREAM_ID)); - assertFalse(consumeBytes(STREAM_ID, data2)); - assertTrue(consumeBytes(newStreamId, data1)); - verifyWindowUpdateNotSent(STREAM_ID); - verifyWindowUpdateSent(newStreamId, data1); - verifyWindowUpdateSent(CONNECTION_STREAM_ID, data1 + data2); - assertEquals(DEFAULT_WINDOW_SIZE - data2, window(STREAM_ID)); - assertEquals(newDefaultWindowSize, window(newStreamId)); - assertEquals(newDefaultWindowSize, window(CONNECTION_STREAM_ID)); - } finally { - connection.stream(newStreamId).close(); - } - } - - private static int getWindowDelta(int initialSize, int windowSize, int dataSize) { - int newWindowSize = windowSize - dataSize; - return initialSize - newWindowSize; - } - - private void receiveFlowControlledFrame(int streamId, int dataSize, int padding, - boolean endOfStream) throws Http2Exception { - receiveFlowControlledFrame(stream(streamId), dataSize, padding, endOfStream); - } - - private void receiveFlowControlledFrame(Http2Stream stream, int dataSize, int padding, - boolean endOfStream) throws Http2Exception { - final ByteBuf buf = dummyData(dataSize); - try { - controller.receiveFlowControlledFrame(stream, buf, padding, endOfStream); - } finally { - buf.release(); - } - } - - private static ByteBuf dummyData(int size) { - final ByteBuf buffer = Unpooled.buffer(size); - buffer.writerIndex(size); - return buffer; - } - - private boolean consumeBytes(int streamId, int numBytes) throws Http2Exception { - return controller.consumeBytes(stream(streamId), numBytes); - } - - private void verifyWindowUpdateSent(int streamId, int windowSizeIncrement) { - verify(frameWriter).writeWindowUpdate(eq(ctx), eq(streamId), eq(windowSizeIncrement)); - } - - private void verifyWindowUpdateNotSent(int streamId) { - verify(frameWriter, never()).writeWindowUpdate(eq(ctx), eq(streamId), anyInt()); - } - - @SuppressWarnings("unchecked") - private void verifyWindowUpdateNotSent() { - verify(frameWriter, never()).writeWindowUpdate(any(ChannelHandlerContext.class), anyInt(), anyInt()); - } - - private int window(int streamId) { - return controller.windowSize(stream(streamId)); - } - - private Http2Stream stream(int streamId) { - return connection.stream(streamId); - } - - private void initController(boolean autoRefillConnectionWindow) throws Http2Exception { - connection = new DefaultHttp2Connection(false); - controller = new DefaultHttp2LocalFlowController(connection, - DEFAULT_WINDOW_UPDATE_RATIO, autoRefillConnectionWindow).frameWriter(frameWriter); - connection.local().flowController(controller); - connection.local().createStream(STREAM_ID, false); - controller.channelHandlerContext(ctx); - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2PushPromiseFrameTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2PushPromiseFrameTest.java deleted file mode 100644 index 453d31a138..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2PushPromiseFrameTest.java +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.bootstrap.Bootstrap; -import io.netty.bootstrap.ServerBootstrap; -import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -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.ReferenceCountUtil; -import io.netty.util.concurrent.Future; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class DefaultHttp2PushPromiseFrameTest { - - private final EventLoopGroup eventLoopGroup = new MultithreadEventLoopGroup(2, NioHandler.newFactory()); - private final ClientHandler clientHandler = new ClientHandler(); - private final Map contentMap = new ConcurrentHashMap(); - - private Future connectionFuture; - - @BeforeEach - public void setup() throws Exception { - ServerBootstrap serverBootstrap = new ServerBootstrap() - .group(eventLoopGroup) - .channel(NioServerSocketChannel.class) - .childHandler(new ChannelInitializer() { - @Override - protected void initChannel(SocketChannel ch) { - ChannelPipeline pipeline = ch.pipeline(); - - Http2FrameCodec frameCodec = Http2FrameCodecBuilder.forServer() - .autoAckSettingsFrame(true) - .autoAckPingFrame(true) - .build(); - - pipeline.addLast(frameCodec); - pipeline.addLast(new ServerHandler()); - } - }); - - Channel channel = serverBootstrap.bind(0).get(); - - final Bootstrap bootstrap = new Bootstrap() - .group(eventLoopGroup) - .channel(NioSocketChannel.class) - .handler(new ChannelInitializer() { - @Override - protected void initChannel(SocketChannel ch) { - ChannelPipeline pipeline = ch.pipeline(); - - Http2FrameCodec frameCodec = Http2FrameCodecBuilder.forClient() - .autoAckSettingsFrame(true) - .autoAckPingFrame(true) - .initialSettings(Http2Settings.defaultSettings().pushEnabled(true)) - .build(); - - pipeline.addLast(frameCodec); - pipeline.addLast(clientHandler); - } - }); - - connectionFuture = bootstrap.connect(channel.localAddress()); - } - - @Test - public void send() { - connectionFuture.addListener(future -> clientHandler.write()); - } - - @AfterEach - public void shutdown() { - eventLoopGroup.shutdownGracefully(); - } - - private final class ServerHandler extends Http2ChannelDuplexHandler { - - @Override - public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception { - - if (msg instanceof Http2HeadersFrame) { - final Http2HeadersFrame receivedFrame = (Http2HeadersFrame) msg; - - Http2Headers pushRequestHeaders = new DefaultHttp2Headers(); - pushRequestHeaders.path("/meow") - .method("GET") - .scheme("https") - .authority("localhost:5555"); - - // Write PUSH_PROMISE request headers - final Http2FrameStream newPushFrameStream = newStream(); - Http2PushPromiseFrame pushPromiseFrame = new DefaultHttp2PushPromiseFrame(pushRequestHeaders); - pushPromiseFrame.stream(receivedFrame.stream()); - pushPromiseFrame.pushStream(newPushFrameStream); - ctx.writeAndFlush(pushPromiseFrame).addListener(future -> { - contentMap.put(newPushFrameStream.id(), "Meow, I am Pushed via HTTP/2"); - - // Write headers for actual request - Http2Headers http2Headers = new DefaultHttp2Headers(); - http2Headers.status("200"); - http2Headers.add("push", "false"); - Http2HeadersFrame headersFrame = new DefaultHttp2HeadersFrame(http2Headers, false); - headersFrame.stream(receivedFrame.stream()); - Future channelFuture = ctx.writeAndFlush(headersFrame); - - // Write Data of actual request - channelFuture.addListener(fut -> { - Http2DataFrame dataFrame = new DefaultHttp2DataFrame( - Unpooled.wrappedBuffer("Meow".getBytes()), true); - dataFrame.stream(receivedFrame.stream()); - ctx.writeAndFlush(dataFrame); - }); - }); - } else if (msg instanceof Http2PriorityFrame) { - Http2PriorityFrame priorityFrame = (Http2PriorityFrame) msg; - String content = contentMap.get(priorityFrame.stream().id()); - if (content == null) { - ctx.writeAndFlush(new DefaultHttp2GoAwayFrame(Http2Error.REFUSED_STREAM)); - return; - } - - // Write headers for Priority request - Http2Headers http2Headers = new DefaultHttp2Headers(); - http2Headers.status("200"); - http2Headers.add("push", "true"); - Http2HeadersFrame headersFrame = new DefaultHttp2HeadersFrame(http2Headers, false); - headersFrame.stream(priorityFrame.stream()); - ctx.writeAndFlush(headersFrame); - - // Write Data of Priority request - Http2DataFrame dataFrame = new DefaultHttp2DataFrame(Unpooled.wrappedBuffer(content.getBytes()), true); - dataFrame.stream(priorityFrame.stream()); - ctx.writeAndFlush(dataFrame); - } - } - } - - private static final class ClientHandler extends Http2ChannelDuplexHandler { - - private volatile ChannelHandlerContext ctx; - - @Override - public void channelActive(ChannelHandlerContext ctx) throws InterruptedException { - this.ctx = ctx; - } - - void write() { - Http2Headers http2Headers = new DefaultHttp2Headers(); - http2Headers.path("/") - .authority("localhost") - .method("GET") - .scheme("https"); - - Http2HeadersFrame headersFrame = new DefaultHttp2HeadersFrame(http2Headers, true); - headersFrame.stream(newStream()); - ctx.writeAndFlush(headersFrame); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - - if (msg instanceof Http2PushPromiseFrame) { - Http2PushPromiseFrame pushPromiseFrame = (Http2PushPromiseFrame) msg; - - assertEquals("/meow", pushPromiseFrame.http2Headers().path().toString()); - assertEquals("GET", pushPromiseFrame.http2Headers().method().toString()); - assertEquals("https", pushPromiseFrame.http2Headers().scheme().toString()); - assertEquals("localhost:5555", pushPromiseFrame.http2Headers().authority().toString()); - - Http2PriorityFrame priorityFrame = new DefaultHttp2PriorityFrame(pushPromiseFrame.stream().id(), - Http2CodecUtil.DEFAULT_PRIORITY_WEIGHT, true); - priorityFrame.stream(pushPromiseFrame.pushStream()); - ctx.writeAndFlush(priorityFrame); - } else if (msg instanceof Http2HeadersFrame) { - Http2HeadersFrame headersFrame = (Http2HeadersFrame) msg; - - if (headersFrame.stream().id() == 3) { - assertEquals("200", headersFrame.headers().status().toString()); - assertEquals("false", headersFrame.headers().get("push").toString()); - } else if (headersFrame.stream().id() == 2) { - assertEquals("200", headersFrame.headers().status().toString()); - assertEquals("true", headersFrame.headers().get("push").toString()); - } else { - ctx.writeAndFlush(new DefaultHttp2GoAwayFrame(Http2Error.REFUSED_STREAM)); - } - } else if (msg instanceof Http2DataFrame) { - Http2DataFrame dataFrame = (Http2DataFrame) msg; - - try { - if (dataFrame.stream().id() == 3) { - assertEquals("Meow", dataFrame.content().toString(CharsetUtil.UTF_8)); - } else if (dataFrame.stream().id() == 2) { - assertEquals("Meow, I am Pushed via HTTP/2", dataFrame.content().toString(CharsetUtil.UTF_8)); - } else { - ctx.writeAndFlush(new DefaultHttp2GoAwayFrame(Http2Error.REFUSED_STREAM)); - } - } finally { - ReferenceCountUtil.release(dataFrame); - } - } - } - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2RemoteFlowControllerTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2RemoteFlowControllerTest.java deleted file mode 100644 index a48f388768..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2RemoteFlowControllerTest.java +++ /dev/null @@ -1,1122 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelConfig; -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.Promise; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.stubbing.Answer; -import org.opentest4j.AssertionFailedError; - -import java.util.concurrent.atomic.AtomicInteger; - -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.Http2CodecUtil.DEFAULT_WINDOW_SIZE; -import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_WEIGHT; -import static io.netty.handler.codec.http2.Http2CodecUtil.MIN_WEIGHT; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.verifyZeroInteractions; -import static org.mockito.Mockito.when; - -/** - * Tests for {@link DefaultHttp2RemoteFlowController}. - */ -public abstract class DefaultHttp2RemoteFlowControllerTest { - private static final int STREAM_A = 1; - private static final int STREAM_B = 3; - private static final int STREAM_C = 5; - private static final int STREAM_D = 7; - - private DefaultHttp2RemoteFlowController controller; - - @Mock - private ChannelHandlerContext ctx; - - @Mock - private Channel channel; - - @Mock - private ChannelConfig config; - - @Mock - private EventExecutor executor; - - @Mock - private Promise promise; - - @Mock - private Http2RemoteFlowController.Listener listener; - - private DefaultHttp2Connection connection; - - @BeforeEach - public void setup() throws Http2Exception { - MockitoAnnotations.initMocks(this); - - when(ctx.newPromise()).thenReturn(promise); - when(ctx.flush()).thenThrow(new AssertionFailedError("forbidden")); - setChannelWritability(true); - when(channel.config()).thenReturn(config); - when(executor.inEventLoop()).thenReturn(true); - - initConnectionAndController(); - - resetCtx(); - // This is intentionally left out of initConnectionAndController so it can be tested below. - controller.channelHandlerContext(ctx); - assertWritabilityChanged(1, true); - reset(listener); - } - - protected abstract StreamByteDistributor newDistributor(Http2Connection connection); - - private void initConnectionAndController() throws Http2Exception { - connection = new DefaultHttp2Connection(false); - controller = new DefaultHttp2RemoteFlowController(connection, newDistributor(connection), listener); - connection.remote().flowController(controller); - - connection.local().createStream(STREAM_A, false); - connection.local().createStream(STREAM_B, false); - Http2Stream streamC = connection.local().createStream(STREAM_C, false); - Http2Stream streamD = connection.local().createStream(STREAM_D, false); - controller.updateDependencyTree(streamC.id(), STREAM_A, DEFAULT_PRIORITY_WEIGHT, false); - controller.updateDependencyTree(streamD.id(), STREAM_A, DEFAULT_PRIORITY_WEIGHT, false); - } - - @Test - public void initialWindowSizeShouldOnlyChangeStreams() throws Http2Exception { - controller.initialWindowSize(0); - assertEquals(DEFAULT_WINDOW_SIZE, window(CONNECTION_STREAM_ID)); - assertEquals(0, window(STREAM_A)); - assertEquals(0, window(STREAM_B)); - assertEquals(0, window(STREAM_C)); - assertEquals(0, window(STREAM_D)); - assertWritabilityChanged(1, false); - } - - @Test - public void windowUpdateShouldChangeConnectionWindow() throws Http2Exception { - incrementWindowSize(CONNECTION_STREAM_ID, 100); - assertEquals(DEFAULT_WINDOW_SIZE + 100, window(CONNECTION_STREAM_ID)); - assertEquals(DEFAULT_WINDOW_SIZE, window(STREAM_A)); - assertEquals(DEFAULT_WINDOW_SIZE, window(STREAM_B)); - assertEquals(DEFAULT_WINDOW_SIZE, window(STREAM_C)); - assertEquals(DEFAULT_WINDOW_SIZE, window(STREAM_D)); - verifyZeroInteractions(listener); - } - - @Test - public void windowUpdateShouldChangeStreamWindow() throws Http2Exception { - incrementWindowSize(STREAM_A, 100); - assertEquals(DEFAULT_WINDOW_SIZE, window(CONNECTION_STREAM_ID)); - assertEquals(DEFAULT_WINDOW_SIZE + 100, window(STREAM_A)); - assertEquals(DEFAULT_WINDOW_SIZE, window(STREAM_B)); - assertEquals(DEFAULT_WINDOW_SIZE, window(STREAM_C)); - assertEquals(DEFAULT_WINDOW_SIZE, window(STREAM_D)); - verifyZeroInteractions(listener); - } - - @Test - public void payloadSmallerThanWindowShouldBeWrittenImmediately() throws Http2Exception { - FakeFlowControlled data = new FakeFlowControlled(5); - sendData(STREAM_A, data); - data.assertNotWritten(); - verifyZeroInteractions(listener); - controller.writePendingBytes(); - data.assertFullyWritten(); - verifyZeroInteractions(listener); - } - - @Test - public void emptyPayloadShouldBeWrittenImmediately() throws Http2Exception { - FakeFlowControlled data = new FakeFlowControlled(0); - sendData(STREAM_A, data); - data.assertNotWritten(); - controller.writePendingBytes(); - data.assertFullyWritten(); - verifyZeroInteractions(listener); - } - - @Test - public void unflushedPayloadsShouldBeDroppedOnCancel() throws Http2Exception { - FakeFlowControlled data = new FakeFlowControlled(5); - Http2Stream streamA = stream(STREAM_A); - sendData(STREAM_A, data); - streamA.close(); - controller.writePendingBytes(); - data.assertNotWritten(); - controller.writePendingBytes(); - data.assertNotWritten(); - verify(listener, times(1)).writabilityChanged(streamA); - assertFalse(controller.isWritable(streamA)); - } - - @Test - public void payloadsShouldMerge() throws Http2Exception { - controller.initialWindowSize(15); - FakeFlowControlled data1 = new FakeFlowControlled(5, true); - FakeFlowControlled data2 = new FakeFlowControlled(10, true); - sendData(STREAM_A, data1); - sendData(STREAM_A, data2); - data1.assertNotWritten(); - data1.assertNotWritten(); - data2.assertMerged(); - controller.writePendingBytes(); - data1.assertFullyWritten(); - data2.assertNotWritten(); - verify(listener, times(1)).writabilityChanged(stream(STREAM_A)); - assertFalse(controller.isWritable(stream(STREAM_A))); - } - - @Test - public void flowControllerCorrectlyAccountsForBytesWithMerge() throws Http2Exception { - controller.initialWindowSize(112); // This must be more than the total merged frame size 110 - FakeFlowControlled data1 = new FakeFlowControlled(5, 2, true); - FakeFlowControlled data2 = new FakeFlowControlled(5, 100, true); - sendData(STREAM_A, data1); - sendData(STREAM_A, data2); - data1.assertNotWritten(); - data1.assertNotWritten(); - data2.assertMerged(); - controller.writePendingBytes(); - data1.assertFullyWritten(); - data2.assertNotWritten(); - verify(listener, never()).writabilityChanged(stream(STREAM_A)); - assertTrue(controller.isWritable(stream(STREAM_A))); - } - - @Test - public void stalledStreamShouldQueuePayloads() throws Http2Exception { - controller.initialWindowSize(0); - verify(listener, times(1)).writabilityChanged(stream(STREAM_A)); - assertFalse(controller.isWritable(stream(STREAM_A))); - reset(listener); - - FakeFlowControlled data = new FakeFlowControlled(15); - FakeFlowControlled moreData = new FakeFlowControlled(0); - sendData(STREAM_A, data); - controller.writePendingBytes(); - data.assertNotWritten(); - sendData(STREAM_A, moreData); - controller.writePendingBytes(); - moreData.assertNotWritten(); - verifyZeroInteractions(listener); - } - - @Test - public void queuedPayloadsReceiveErrorOnStreamClose() throws Http2Exception { - controller.initialWindowSize(0); - verify(listener, times(1)).writabilityChanged(stream(STREAM_A)); - assertFalse(controller.isWritable(stream(STREAM_A))); - reset(listener); - - FakeFlowControlled data = new FakeFlowControlled(15); - FakeFlowControlled moreData = new FakeFlowControlled(0); - sendData(STREAM_A, data); - controller.writePendingBytes(); - data.assertNotWritten(); - sendData(STREAM_A, moreData); - controller.writePendingBytes(); - moreData.assertNotWritten(); - - connection.stream(STREAM_A).close(); - data.assertError(Http2Error.STREAM_CLOSED); - moreData.assertError(Http2Error.STREAM_CLOSED); - verifyZeroInteractions(listener); - } - - @Test - public void payloadLargerThanWindowShouldWritePartial() throws Http2Exception { - controller.initialWindowSize(5); - verify(listener, never()).writabilityChanged(stream(STREAM_A)); - assertTrue(controller.isWritable(stream(STREAM_A))); - reset(listener); - - final FakeFlowControlled data = new FakeFlowControlled(10); - sendData(STREAM_A, data); - controller.writePendingBytes(); - // Verify that a partial frame of 5 remains to be sent - data.assertPartiallyWritten(5); - verify(listener, times(1)).writabilityChanged(stream(STREAM_A)); - assertFalse(controller.isWritable(stream(STREAM_A))); - verifyNoMoreInteractions(listener); - } - - @Test - public void windowUpdateAndFlushShouldTriggerWrite() throws Http2Exception { - controller.initialWindowSize(10); - verify(listener, never()).writabilityChanged(stream(STREAM_A)); - assertTrue(controller.isWritable(stream(STREAM_A))); - - FakeFlowControlled data = new FakeFlowControlled(20); - FakeFlowControlled moreData = new FakeFlowControlled(10); - sendData(STREAM_A, data); - sendData(STREAM_A, moreData); - controller.writePendingBytes(); - data.assertPartiallyWritten(10); - moreData.assertNotWritten(); - verify(listener, times(1)).writabilityChanged(stream(STREAM_A)); - assertFalse(controller.isWritable(stream(STREAM_A))); - reset(listener); - resetCtx(); - - // Update the window and verify that the rest of data and some of moreData are written - incrementWindowSize(STREAM_A, 15); - verify(listener, never()).writabilityChanged(stream(STREAM_A)); - assertFalse(controller.isWritable(stream(STREAM_A))); - reset(listener); - - controller.writePendingBytes(); - - data.assertFullyWritten(); - moreData.assertPartiallyWritten(5); - verify(listener, never()).writabilityChanged(stream(STREAM_A)); - assertFalse(controller.isWritable(stream(STREAM_A))); - - assertEquals(DEFAULT_WINDOW_SIZE - 25, window(CONNECTION_STREAM_ID)); - assertEquals(0, window(STREAM_A)); - assertEquals(10, window(STREAM_B)); - assertEquals(10, window(STREAM_C)); - assertEquals(10, window(STREAM_D)); - } - - @Test - public void initialWindowUpdateShouldSendPayload() throws Http2Exception { - incrementWindowSize(CONNECTION_STREAM_ID, -window(CONNECTION_STREAM_ID) + 10); - assertWritabilityChanged(0, true); - reset(listener); - - controller.initialWindowSize(0); - assertWritabilityChanged(1, false); - reset(listener); - - FakeFlowControlled data = new FakeFlowControlled(10); - sendData(STREAM_A, data); - controller.writePendingBytes(); - data.assertNotWritten(); - - // Verify that the entire frame was sent. - controller.initialWindowSize(10); - data.assertFullyWritten(); - assertWritabilityChanged(0, false); - } - - @Test - public void successiveSendsShouldNotInteract() throws Http2Exception { - // Collapse the connection window to force queueing. - incrementWindowSize(CONNECTION_STREAM_ID, -window(CONNECTION_STREAM_ID)); - assertEquals(0, window(CONNECTION_STREAM_ID)); - assertWritabilityChanged(1, false); - reset(listener); - - FakeFlowControlled dataA = new FakeFlowControlled(10); - // Queue data for stream A and allow most of it to be written. - sendData(STREAM_A, dataA); - controller.writePendingBytes(); - dataA.assertNotWritten(); - incrementWindowSize(CONNECTION_STREAM_ID, 8); - assertWritabilityChanged(0, false); - reset(listener); - - controller.writePendingBytes(); - dataA.assertPartiallyWritten(8); - assertEquals(65527, window(STREAM_A)); - assertEquals(0, window(CONNECTION_STREAM_ID)); - assertWritabilityChanged(0, false); - reset(listener); - - // Queue data for stream B and allow the rest of A and all of B to be written. - FakeFlowControlled dataB = new FakeFlowControlled(10); - sendData(STREAM_B, dataB); - controller.writePendingBytes(); - dataB.assertNotWritten(); - incrementWindowSize(CONNECTION_STREAM_ID, 12); - assertWritabilityChanged(0, false); - reset(listener); - - controller.writePendingBytes(); - assertEquals(0, window(CONNECTION_STREAM_ID)); - assertWritabilityChanged(0, false); - - // Verify the rest of A is written. - dataA.assertFullyWritten(); - assertEquals(65525, window(STREAM_A)); - - dataB.assertFullyWritten(); - assertEquals(65525, window(STREAM_B)); - verifyNoMoreInteractions(listener); - } - - @Test - public void negativeWindowShouldNotThrowException() throws Http2Exception { - final int initWindow = 20; - final int secondWindowSize = 10; - controller.initialWindowSize(initWindow); - assertWritabilityChanged(0, true); - reset(listener); - - FakeFlowControlled data1 = new FakeFlowControlled(initWindow); - FakeFlowControlled data2 = new FakeFlowControlled(5); - - // Deplete the stream A window to 0 - sendData(STREAM_A, data1); - controller.writePendingBytes(); - data1.assertFullyWritten(); - assertTrue(window(CONNECTION_STREAM_ID) > 0); - verify(listener, times(1)).writabilityChanged(stream(STREAM_A)); - verify(listener, never()).writabilityChanged(stream(STREAM_B)); - verify(listener, never()).writabilityChanged(stream(STREAM_C)); - verify(listener, never()).writabilityChanged(stream(STREAM_D)); - assertFalse(controller.isWritable(stream(STREAM_A))); - assertTrue(controller.isWritable(stream(STREAM_B))); - assertTrue(controller.isWritable(stream(STREAM_C))); - assertTrue(controller.isWritable(stream(STREAM_D))); - reset(listener); - - // Make the window size for stream A negative - controller.initialWindowSize(initWindow - secondWindowSize); - assertEquals(-secondWindowSize, window(STREAM_A)); - verify(listener, never()).writabilityChanged(stream(STREAM_A)); - verify(listener, never()).writabilityChanged(stream(STREAM_B)); - verify(listener, never()).writabilityChanged(stream(STREAM_C)); - verify(listener, never()).writabilityChanged(stream(STREAM_D)); - assertFalse(controller.isWritable(stream(STREAM_A))); - assertTrue(controller.isWritable(stream(STREAM_B))); - assertTrue(controller.isWritable(stream(STREAM_C))); - assertTrue(controller.isWritable(stream(STREAM_D))); - reset(listener); - - // Queue up a write. It should not be written now because the window is negative - sendData(STREAM_A, data2); - controller.writePendingBytes(); - data2.assertNotWritten(); - verify(listener, never()).writabilityChanged(stream(STREAM_A)); - verify(listener, never()).writabilityChanged(stream(STREAM_B)); - verify(listener, never()).writabilityChanged(stream(STREAM_C)); - verify(listener, never()).writabilityChanged(stream(STREAM_D)); - assertFalse(controller.isWritable(stream(STREAM_A))); - assertTrue(controller.isWritable(stream(STREAM_B))); - assertTrue(controller.isWritable(stream(STREAM_C))); - assertTrue(controller.isWritable(stream(STREAM_D))); - reset(listener); - - // Open the window size back up a bit (no send should happen) - incrementWindowSize(STREAM_A, 5); - controller.writePendingBytes(); - assertEquals(-5, window(STREAM_A)); - data2.assertNotWritten(); - verify(listener, never()).writabilityChanged(stream(STREAM_A)); - verify(listener, never()).writabilityChanged(stream(STREAM_B)); - verify(listener, never()).writabilityChanged(stream(STREAM_C)); - verify(listener, never()).writabilityChanged(stream(STREAM_D)); - assertFalse(controller.isWritable(stream(STREAM_A))); - assertTrue(controller.isWritable(stream(STREAM_B))); - assertTrue(controller.isWritable(stream(STREAM_C))); - assertTrue(controller.isWritable(stream(STREAM_D))); - reset(listener); - - // Open the window size back up a bit (no send should happen) - incrementWindowSize(STREAM_A, 5); - controller.writePendingBytes(); - assertEquals(0, window(STREAM_A)); - data2.assertNotWritten(); - verify(listener, never()).writabilityChanged(stream(STREAM_A)); - verify(listener, never()).writabilityChanged(stream(STREAM_B)); - verify(listener, never()).writabilityChanged(stream(STREAM_C)); - verify(listener, never()).writabilityChanged(stream(STREAM_D)); - assertFalse(controller.isWritable(stream(STREAM_A))); - assertTrue(controller.isWritable(stream(STREAM_B))); - assertTrue(controller.isWritable(stream(STREAM_C))); - assertTrue(controller.isWritable(stream(STREAM_D))); - reset(listener); - - // Open the window size back up and allow the write to happen - incrementWindowSize(STREAM_A, 5); - controller.writePendingBytes(); - data2.assertFullyWritten(); - verify(listener, never()).writabilityChanged(stream(STREAM_A)); - verify(listener, never()).writabilityChanged(stream(STREAM_B)); - verify(listener, never()).writabilityChanged(stream(STREAM_C)); - verify(listener, never()).writabilityChanged(stream(STREAM_D)); - assertFalse(controller.isWritable(stream(STREAM_A))); - assertTrue(controller.isWritable(stream(STREAM_B))); - assertTrue(controller.isWritable(stream(STREAM_C))); - assertTrue(controller.isWritable(stream(STREAM_D))); - } - - @Test - public void initialWindowUpdateShouldSendEmptyFrame() throws Http2Exception { - controller.initialWindowSize(0); - assertWritabilityChanged(1, false); - reset(listener); - - // First send a frame that will get buffered. - FakeFlowControlled data = new FakeFlowControlled(10, false); - sendData(STREAM_A, data); - controller.writePendingBytes(); - data.assertNotWritten(); - - // Now send an empty frame on the same stream and verify that it's also buffered. - FakeFlowControlled data2 = new FakeFlowControlled(0, false); - sendData(STREAM_A, data2); - controller.writePendingBytes(); - data2.assertNotWritten(); - - // Re-expand the window and verify that both frames were sent. - controller.initialWindowSize(10); - verify(listener, never()).writabilityChanged(stream(STREAM_A)); - verify(listener, times(1)).writabilityChanged(stream(STREAM_B)); - verify(listener, times(1)).writabilityChanged(stream(STREAM_C)); - verify(listener, times(1)).writabilityChanged(stream(STREAM_D)); - assertFalse(controller.isWritable(stream(STREAM_A))); - assertTrue(controller.isWritable(stream(STREAM_B))); - assertTrue(controller.isWritable(stream(STREAM_C))); - assertTrue(controller.isWritable(stream(STREAM_D))); - - data.assertFullyWritten(); - data2.assertFullyWritten(); - } - - @Test - public void initialWindowUpdateShouldSendPartialFrame() throws Http2Exception { - controller.initialWindowSize(0); - assertWritabilityChanged(1, false); - reset(listener); - - FakeFlowControlled data = new FakeFlowControlled(10); - sendData(STREAM_A, data); - controller.writePendingBytes(); - data.assertNotWritten(); - - // Verify that a partial frame of 5 was sent. - controller.initialWindowSize(5); - verify(listener, never()).writabilityChanged(stream(STREAM_A)); - verify(listener, times(1)).writabilityChanged(stream(STREAM_B)); - verify(listener, times(1)).writabilityChanged(stream(STREAM_C)); - verify(listener, times(1)).writabilityChanged(stream(STREAM_D)); - assertFalse(controller.isWritable(stream(STREAM_A))); - assertTrue(controller.isWritable(stream(STREAM_B))); - assertTrue(controller.isWritable(stream(STREAM_C))); - assertTrue(controller.isWritable(stream(STREAM_D))); - - data.assertPartiallyWritten(5); - } - - @Test - public void connectionWindowUpdateShouldSendFrame() throws Http2Exception { - // Set the connection window size to zero. - exhaustStreamWindow(CONNECTION_STREAM_ID); - assertWritabilityChanged(1, false); - reset(listener); - - FakeFlowControlled data = new FakeFlowControlled(10); - sendData(STREAM_A, data); - controller.writePendingBytes(); - data.assertNotWritten(); - assertWritabilityChanged(0, false); - reset(listener); - - // Verify that the entire frame was sent. - incrementWindowSize(CONNECTION_STREAM_ID, 10); - assertWritabilityChanged(0, false); - reset(listener); - data.assertNotWritten(); - - controller.writePendingBytes(); - data.assertFullyWritten(); - assertWritabilityChanged(0, false); - assertEquals(0, window(CONNECTION_STREAM_ID)); - assertEquals(DEFAULT_WINDOW_SIZE - 10, window(STREAM_A)); - assertEquals(DEFAULT_WINDOW_SIZE, window(STREAM_B)); - assertEquals(DEFAULT_WINDOW_SIZE, window(STREAM_C)); - assertEquals(DEFAULT_WINDOW_SIZE, window(STREAM_D)); - } - - @Test - public void connectionWindowUpdateShouldSendPartialFrame() throws Http2Exception { - // Set the connection window size to zero. - exhaustStreamWindow(CONNECTION_STREAM_ID); - assertWritabilityChanged(1, false); - reset(listener); - - FakeFlowControlled data = new FakeFlowControlled(10); - sendData(STREAM_A, data); - controller.writePendingBytes(); - data.assertNotWritten(); - - // Verify that a partial frame of 5 was sent. - incrementWindowSize(CONNECTION_STREAM_ID, 5); - data.assertNotWritten(); - assertWritabilityChanged(0, false); - reset(listener); - - controller.writePendingBytes(); - data.assertPartiallyWritten(5); - assertWritabilityChanged(0, false); - assertEquals(0, window(CONNECTION_STREAM_ID)); - assertEquals(DEFAULT_WINDOW_SIZE - 5, window(STREAM_A)); - assertEquals(DEFAULT_WINDOW_SIZE, window(STREAM_B)); - assertEquals(DEFAULT_WINDOW_SIZE, window(STREAM_C)); - assertEquals(DEFAULT_WINDOW_SIZE, window(STREAM_D)); - } - - @Test - public void streamWindowUpdateShouldSendFrame() throws Http2Exception { - // Set the stream window size to zero. - exhaustStreamWindow(STREAM_A); - verify(listener, times(1)).writabilityChanged(stream(STREAM_A)); - verify(listener, never()).writabilityChanged(stream(STREAM_B)); - verify(listener, never()).writabilityChanged(stream(STREAM_C)); - verify(listener, never()).writabilityChanged(stream(STREAM_D)); - assertFalse(controller.isWritable(stream(STREAM_A))); - assertTrue(controller.isWritable(stream(STREAM_B))); - assertTrue(controller.isWritable(stream(STREAM_C))); - assertTrue(controller.isWritable(stream(STREAM_D))); - reset(listener); - - FakeFlowControlled data = new FakeFlowControlled(10); - sendData(STREAM_A, data); - controller.writePendingBytes(); - data.assertNotWritten(); - - // Verify that the entire frame was sent. - incrementWindowSize(STREAM_A, 10); - verify(listener, never()).writabilityChanged(stream(STREAM_A)); - verify(listener, never()).writabilityChanged(stream(STREAM_B)); - verify(listener, never()).writabilityChanged(stream(STREAM_C)); - verify(listener, never()).writabilityChanged(stream(STREAM_D)); - assertFalse(controller.isWritable(stream(STREAM_A))); - assertTrue(controller.isWritable(stream(STREAM_B))); - assertTrue(controller.isWritable(stream(STREAM_C))); - assertTrue(controller.isWritable(stream(STREAM_D))); - reset(listener); - - data.assertNotWritten(); - controller.writePendingBytes(); - data.assertFullyWritten(); - verify(listener, never()).writabilityChanged(stream(STREAM_A)); - verify(listener, never()).writabilityChanged(stream(STREAM_B)); - verify(listener, never()).writabilityChanged(stream(STREAM_C)); - verify(listener, never()).writabilityChanged(stream(STREAM_D)); - assertFalse(controller.isWritable(stream(STREAM_A))); - assertTrue(controller.isWritable(stream(STREAM_B))); - assertTrue(controller.isWritable(stream(STREAM_C))); - assertTrue(controller.isWritable(stream(STREAM_D))); - assertEquals(DEFAULT_WINDOW_SIZE - 10, window(CONNECTION_STREAM_ID)); - assertEquals(0, window(STREAM_A)); - assertEquals(DEFAULT_WINDOW_SIZE, window(STREAM_B)); - assertEquals(DEFAULT_WINDOW_SIZE, window(STREAM_C)); - assertEquals(DEFAULT_WINDOW_SIZE, window(STREAM_D)); - } - - @Test - public void streamWindowUpdateShouldSendPartialFrame() throws Http2Exception { - // Set the stream window size to zero. - exhaustStreamWindow(STREAM_A); - verify(listener, times(1)).writabilityChanged(stream(STREAM_A)); - verify(listener, never()).writabilityChanged(stream(STREAM_B)); - verify(listener, never()).writabilityChanged(stream(STREAM_C)); - verify(listener, never()).writabilityChanged(stream(STREAM_D)); - assertFalse(controller.isWritable(stream(STREAM_A))); - assertTrue(controller.isWritable(stream(STREAM_B))); - assertTrue(controller.isWritable(stream(STREAM_C))); - assertTrue(controller.isWritable(stream(STREAM_D))); - reset(listener); - - FakeFlowControlled data = new FakeFlowControlled(10); - sendData(STREAM_A, data); - controller.writePendingBytes(); - data.assertNotWritten(); - - // Verify that a partial frame of 5 was sent. - incrementWindowSize(STREAM_A, 5); - verify(listener, never()).writabilityChanged(stream(STREAM_A)); - verify(listener, never()).writabilityChanged(stream(STREAM_B)); - verify(listener, never()).writabilityChanged(stream(STREAM_C)); - verify(listener, never()).writabilityChanged(stream(STREAM_D)); - assertFalse(controller.isWritable(stream(STREAM_A))); - assertTrue(controller.isWritable(stream(STREAM_B))); - assertTrue(controller.isWritable(stream(STREAM_C))); - assertTrue(controller.isWritable(stream(STREAM_D))); - reset(listener); - - data.assertNotWritten(); - controller.writePendingBytes(); - data.assertPartiallyWritten(5); - assertEquals(DEFAULT_WINDOW_SIZE - 5, window(CONNECTION_STREAM_ID)); - assertEquals(0, window(STREAM_A)); - assertEquals(DEFAULT_WINDOW_SIZE, window(STREAM_B)); - assertEquals(DEFAULT_WINDOW_SIZE, window(STREAM_C)); - assertEquals(DEFAULT_WINDOW_SIZE, window(STREAM_D)); - } - - @Test - public void flowControlledWriteThrowsAnException() throws Exception { - final Http2RemoteFlowController.FlowControlled flowControlled = mockedFlowControlledThatThrowsOnWrite(); - final Http2Stream stream = stream(STREAM_A); - doAnswer((Answer) invocationOnMock -> { - stream.closeLocalSide(); - return null; - }).when(flowControlled).error(any(ChannelHandlerContext.class), any(Throwable.class)); - - int windowBefore = window(STREAM_A); - - controller.addFlowControlled(stream, flowControlled); - controller.writePendingBytes(); - - verify(flowControlled, atLeastOnce()).write(any(ChannelHandlerContext.class), anyInt()); - verify(flowControlled).error(any(ChannelHandlerContext.class), any(Throwable.class)); - verify(flowControlled, never()).writeComplete(); - - assertEquals(90, windowBefore - window(STREAM_A)); - verify(listener, times(1)).writabilityChanged(stream(STREAM_A)); - verify(listener, never()).writabilityChanged(stream(STREAM_B)); - verify(listener, never()).writabilityChanged(stream(STREAM_C)); - verify(listener, never()).writabilityChanged(stream(STREAM_D)); - assertFalse(controller.isWritable(stream(STREAM_A))); - assertTrue(controller.isWritable(stream(STREAM_B))); - assertTrue(controller.isWritable(stream(STREAM_C))); - assertTrue(controller.isWritable(stream(STREAM_D))); - } - - @Test - public void flowControlledWriteAndErrorThrowAnException() throws Exception { - final Http2RemoteFlowController.FlowControlled flowControlled = mockedFlowControlledThatThrowsOnWrite(); - final Http2Stream stream = stream(STREAM_A); - final RuntimeException fakeException = new RuntimeException("error failed"); - doAnswer((Answer) invocationOnMock -> { - throw fakeException; - }).when(flowControlled).error(any(ChannelHandlerContext.class), any(Throwable.class)); - - int windowBefore = window(STREAM_A); - - Http2Exception e = assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - controller.addFlowControlled(stream, flowControlled); - controller.writePendingBytes(); - } - }); - assertSame(fakeException, e.getCause()); - - verify(flowControlled, atLeastOnce()).write(any(ChannelHandlerContext.class), anyInt()); - verify(flowControlled).error(any(ChannelHandlerContext.class), any(Throwable.class)); - verify(flowControlled, never()).writeComplete(); - - assertEquals(90, windowBefore - window(STREAM_A)); - verifyZeroInteractions(listener); - } - - @Test - public void flowControlledWriteCompleteThrowsAnException() throws Exception { - final Http2RemoteFlowController.FlowControlled flowControlled = - mock(Http2RemoteFlowController.FlowControlled.class); - Http2Stream streamA = stream(STREAM_A); - final AtomicInteger size = new AtomicInteger(150); - doAnswer((Answer) invocationOnMock -> size.get()).when(flowControlled).size(); - doAnswer((Answer) invocationOnMock -> { - size.addAndGet(-50); - return null; - }).when(flowControlled).write(any(ChannelHandlerContext.class), anyInt()); - - final Http2Stream stream = stream(STREAM_A); - doAnswer((Answer) invocationOnMock -> { - throw new RuntimeException("writeComplete failed"); - }).when(flowControlled).writeComplete(); - - int windowBefore = window(STREAM_A); - - controller.addFlowControlled(stream, flowControlled); - controller.writePendingBytes(); - - verify(flowControlled, times(3)).write(any(ChannelHandlerContext.class), anyInt()); - verify(flowControlled, never()).error(any(ChannelHandlerContext.class), any(Throwable.class)); - verify(flowControlled).writeComplete(); - - assertEquals(150, windowBefore - window(STREAM_A)); - verify(listener, times(1)).writabilityChanged(streamA); - verify(listener, never()).writabilityChanged(stream(STREAM_B)); - verify(listener, never()).writabilityChanged(stream(STREAM_C)); - verify(listener, never()).writabilityChanged(stream(STREAM_D)); - assertFalse(controller.isWritable(streamA)); - assertTrue(controller.isWritable(stream(STREAM_B))); - assertTrue(controller.isWritable(stream(STREAM_C))); - assertTrue(controller.isWritable(stream(STREAM_D))); - } - - @Test - public void closeStreamInFlowControlledError() throws Exception { - final Http2RemoteFlowController.FlowControlled flowControlled = - mock(Http2RemoteFlowController.FlowControlled.class); - final Http2Stream stream = stream(STREAM_A); - when(flowControlled.size()).thenReturn(100); - doThrow(new RuntimeException("write failed")) - .when(flowControlled).write(any(ChannelHandlerContext.class), anyInt()); - doAnswer((Answer) invocationOnMock -> { - stream.close(); - return null; - }).when(flowControlled).error(any(ChannelHandlerContext.class), any(Throwable.class)); - - controller.addFlowControlled(stream, flowControlled); - controller.writePendingBytes(); - - verify(flowControlled).write(any(ChannelHandlerContext.class), anyInt()); - verify(flowControlled).error(any(ChannelHandlerContext.class), any(Throwable.class)); - verify(flowControlled, never()).writeComplete(); - verify(listener, times(1)).writabilityChanged(stream); - verify(listener, never()).writabilityChanged(stream(STREAM_B)); - verify(listener, never()).writabilityChanged(stream(STREAM_C)); - verify(listener, never()).writabilityChanged(stream(STREAM_D)); - assertFalse(controller.isWritable(stream)); - assertTrue(controller.isWritable(stream(STREAM_B))); - assertTrue(controller.isWritable(stream(STREAM_C))); - assertTrue(controller.isWritable(stream(STREAM_D))); - } - - @Test - public void nonWritableChannelDoesNotAttemptToWrite() throws Exception { - // Start the channel as not writable and exercise the public methods of the flow controller - // making sure no frames are written. - setChannelWritability(false); - assertWritabilityChanged(1, false); - reset(listener); - FakeFlowControlled dataA = new FakeFlowControlled(1); - FakeFlowControlled dataB = new FakeFlowControlled(1); - final Http2Stream stream = stream(STREAM_A); - - controller.addFlowControlled(stream, dataA); - controller.writePendingBytes(); - dataA.assertNotWritten(); - - controller.incrementWindowSize(stream, 100); - controller.writePendingBytes(); - dataA.assertNotWritten(); - - controller.addFlowControlled(stream, dataB); - controller.writePendingBytes(); - dataA.assertNotWritten(); - dataB.assertNotWritten(); - assertWritabilityChanged(0, false); - - // Now change the channel to writable and make sure frames are written. - setChannelWritability(true); - assertWritabilityChanged(1, true); - controller.writePendingBytes(); - dataA.assertFullyWritten(); - dataB.assertFullyWritten(); - } - - @Test - public void contextShouldSendQueuedFramesWhenSet() throws Exception { - // Re-initialize the controller so we can ensure the context hasn't been set yet. - initConnectionAndController(); - - FakeFlowControlled dataA = new FakeFlowControlled(1); - final Http2Stream stream = stream(STREAM_A); - - // Queue some frames - controller.addFlowControlled(stream, dataA); - dataA.assertNotWritten(); - - controller.incrementWindowSize(stream, 100); - dataA.assertNotWritten(); - - assertWritabilityChanged(0, false); - - // Set the controller - controller.channelHandlerContext(ctx); - dataA.assertFullyWritten(); - - assertWritabilityChanged(1, true); - } - - @Test - public void initialWindowSizeWithNoContextShouldNotThrow() throws Exception { - // Re-initialize the controller so we can ensure the context hasn't been set yet. - initConnectionAndController(); - - // This should not throw. - controller.initialWindowSize(1024 * 100); - - FakeFlowControlled dataA = new FakeFlowControlled(1); - final Http2Stream stream = stream(STREAM_A); - - // Queue some frames - controller.addFlowControlled(stream, dataA); - dataA.assertNotWritten(); - - // Set the controller - controller.channelHandlerContext(ctx); - dataA.assertFullyWritten(); - } - - @Test - public void invalidParentStreamIdThrows() { - assertThrows(AssertionError.class, new Executable() { - @Override - public void execute() throws Throwable { - controller.updateDependencyTree(STREAM_D, -1, DEFAULT_PRIORITY_WEIGHT, true); - } - }); - } - - @Test - public void invalidChildStreamIdThrows() { - assertThrows(AssertionError.class, new Executable() { - @Override - public void execute() throws Throwable { - controller.updateDependencyTree(-1, STREAM_D, DEFAULT_PRIORITY_WEIGHT, true); - } - }); - } - - @Test - public void connectionChildStreamIdThrows() { - assertThrows(AssertionError.class, new Executable() { - @Override - public void execute() throws Throwable { - controller.updateDependencyTree(0, STREAM_D, DEFAULT_PRIORITY_WEIGHT, true); - } - }); - } - - @Test - public void invalidWeightTooSmallThrows() { - assertThrows(AssertionError.class, new Executable() { - @Override - public void execute() throws Throwable { - controller.updateDependencyTree(STREAM_A, STREAM_D, (short) (MIN_WEIGHT - 1), true); - } - }); - } - - @Test - public void invalidWeightTooBigThrows() { - assertThrows(AssertionError.class, new Executable() { - @Override - public void execute() throws Throwable { - controller.updateDependencyTree(STREAM_A, STREAM_D, (short) (MAX_WEIGHT + 1), true); - } - }); - } - - @Test - public void dependencyOnSelfThrows() { - assertThrows(AssertionError.class, new Executable() { - @Override - public void execute() throws Throwable { - controller.updateDependencyTree(STREAM_A, STREAM_A, DEFAULT_PRIORITY_WEIGHT, true); - } - }); - } - - private void assertWritabilityChanged(int amt, boolean writable) { - verify(listener, times(amt)).writabilityChanged(stream(STREAM_A)); - verify(listener, times(amt)).writabilityChanged(stream(STREAM_B)); - verify(listener, times(amt)).writabilityChanged(stream(STREAM_C)); - verify(listener, times(amt)).writabilityChanged(stream(STREAM_D)); - if (writable) { - assertTrue(controller.isWritable(stream(STREAM_A))); - assertTrue(controller.isWritable(stream(STREAM_B))); - assertTrue(controller.isWritable(stream(STREAM_C))); - assertTrue(controller.isWritable(stream(STREAM_D))); - } else { - assertFalse(controller.isWritable(stream(STREAM_A))); - assertFalse(controller.isWritable(stream(STREAM_B))); - assertFalse(controller.isWritable(stream(STREAM_C))); - assertFalse(controller.isWritable(stream(STREAM_D))); - } - } - - private static Http2RemoteFlowController.FlowControlled mockedFlowControlledThatThrowsOnWrite() throws Exception { - final Http2RemoteFlowController.FlowControlled flowControlled = - mock(Http2RemoteFlowController.FlowControlled.class); - when(flowControlled.size()).thenReturn(100); - doAnswer((Answer) in -> { - // Write most of the bytes and then fail - when(flowControlled.size()).thenReturn(10); - throw new RuntimeException("Write failed"); - }).when(flowControlled).write(any(ChannelHandlerContext.class), anyInt()); - return flowControlled; - } - - private void sendData(int streamId, FakeFlowControlled data) { - Http2Stream stream = stream(streamId); - controller.addFlowControlled(stream, data); - } - - private void exhaustStreamWindow(int streamId) throws Http2Exception { - incrementWindowSize(streamId, -window(streamId)); - } - - private int window(int streamId) { - return controller.windowSize(stream(streamId)); - } - - private void incrementWindowSize(int streamId, int delta) throws Http2Exception { - controller.incrementWindowSize(stream(streamId), delta); - } - - private Http2Stream stream(int streamId) { - return connection.stream(streamId); - } - - private void resetCtx() { - reset(ctx); - when(ctx.channel()).thenReturn(channel); - when(ctx.executor()).thenReturn(executor); - } - - private void setChannelWritability(boolean isWritable) throws Http2Exception { - when(channel.bytesBeforeUnwritable()).thenReturn(isWritable ? Long.MAX_VALUE : 0); - when(channel.isWritable()).thenReturn(isWritable); - if (controller != null) { - controller.channelWritabilityChanged(); - } - } - - private static final class FakeFlowControlled implements Http2RemoteFlowController.FlowControlled { - private int currentPadding; - private int currentPayloadSize; - private int originalPayloadSize; - private int originalPadding; - private boolean writeCalled; - private final boolean mergeable; - private boolean merged; - - private Throwable t; - - private FakeFlowControlled(int size) { - this(size, false); - } - - private FakeFlowControlled(int size, boolean mergeable) { - this(size, 0, mergeable); - } - - private FakeFlowControlled(int payloadSize, int padding, boolean mergeable) { - currentPayloadSize = originalPayloadSize = payloadSize; - currentPadding = originalPadding = padding; - this.mergeable = mergeable; - } - - @Override - public int size() { - return currentPayloadSize + currentPadding; - } - - private int originalSize() { - return originalPayloadSize + originalPadding; - } - - @Override - public void error(ChannelHandlerContext ctx, Throwable t) { - this.t = t; - } - - @Override - public void writeComplete() { - } - - @Override - public void write(ChannelHandlerContext ctx, int allowedBytes) { - if (allowedBytes <= 0 && size() != 0) { - // Write has been called but no data can be written - return; - } - writeCalled = true; - int written = Math.min(size(), allowedBytes); - if (written > currentPayloadSize) { - written -= currentPayloadSize; - currentPayloadSize = 0; - currentPadding -= written; - } else { - currentPayloadSize -= written; - } - } - - @Override - public boolean merge(ChannelHandlerContext ctx, Http2RemoteFlowController.FlowControlled next) { - if (mergeable && next instanceof FakeFlowControlled) { - FakeFlowControlled ffcNext = (FakeFlowControlled) next; - originalPayloadSize += ffcNext.originalPayloadSize; - currentPayloadSize += ffcNext.originalPayloadSize; - currentPadding = originalPadding = Math.max(originalPadding, ffcNext.originalPadding); - ffcNext.merged = true; - return true; - } - return false; - } - - public int written() { - return originalSize() - size(); - } - - public void assertNotWritten() { - assertFalse(writeCalled); - } - - public void assertPartiallyWritten(int expectedWritten) { - assertPartiallyWritten(expectedWritten, 0); - } - - public void assertPartiallyWritten(int expectedWritten, int delta) { - assertTrue(writeCalled); - assertEquals(expectedWritten, written(), delta); - } - - public void assertFullyWritten() { - assertTrue(writeCalled); - assertEquals(0, currentPayloadSize); - assertEquals(0, currentPadding); - } - - public boolean assertMerged() { - return merged; - } - - public void assertError(Http2Error error) { - assertNotNull(t); - if (error != null) { - assertSame(error, ((Http2Exception) t).error()); - } - } - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/HashCollisionTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/HashCollisionTest.java deleted file mode 100644 index 21771e29db..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/HashCollisionTest.java +++ /dev/null @@ -1,169 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.util.AsciiString; -import io.netty.util.internal.PlatformDependent; -import org.junit.jupiter.api.Disabled; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.io.PrintStream; -import java.lang.reflect.Field; -import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -@Disabled -public final class HashCollisionTest { - private HashCollisionTest() { } - - public static void main(String[] args) throws IllegalAccessException, IOException, URISyntaxException { - // Big initial size for when all name sources are pulled in. - List strings = new ArrayList<>(350000); - addHttpHeaderNames(strings); - addHttpHeaderValues(strings); - addHttp2HeaderNames(strings); - addWordsFromFile(new File("/usr/share/dict/words"), strings); - // More "english words" can be found here: - // https://gist.github.com/Scottmitch/de2f03912778016ecee3c140478f07e0#file-englishwords-txt - - Map> dups = calculateDuplicates(strings, string -> { - int h = 0; - for (int i = 0; i < string.length(); ++i) { - // masking with 0x1F reduces the number of overall bits that impact the hash code but makes the hash - // code the same regardless of character case (upper case or lower case hash is the same). - h = h * 31 + (string.charAt(i) & 0x1F); - } - return h; - }); - PrintStream writer = System.out; - writer.println("==Old Duplicates=="); - printResults(writer, dups); - - dups = calculateDuplicates(strings, PlatformDependent::hashCodeAscii); - writer.println(); - writer.println("==New Duplicates=="); - printResults(writer, dups); - } - - private static void addHttpHeaderNames(List values) throws IllegalAccessException { - for (Field f : HttpHeaderNames.class.getFields()) { - if (f.getType() == AsciiString.class) { - values.add((AsciiString) f.get(null)); - } - } - } - - private static void addHttpHeaderValues(List values) throws IllegalAccessException { - for (Field f : HttpHeaderValues.class.getFields()) { - if (f.getType() == AsciiString.class) { - values.add((AsciiString) f.get(null)); - } - } - } - - private static void addHttp2HeaderNames(List values) throws IllegalAccessException { - for (Http2Headers.PseudoHeaderName name : Http2Headers.PseudoHeaderName.values()) { - values.add(name.value()); - } - } - - private static void addWordsFromFile(File file, List values) - throws IllegalAccessException, IOException { - BufferedReader br = new BufferedReader(new FileReader(file)); - try { - String line; - while ((line = br.readLine()) != null) { - // Make a "best effort" to prune input which contains characters that are not valid in HTTP header names - if (line.indexOf('\'') < 0) { - values.add(line); - } - } - } finally { - br.close(); - } - } - - private static Map> calculateDuplicates(List strings, - Function hasher) { - Map> hashResults = new HashMap<>(); - Set duplicateHashCodes = new HashSet<>(); - - for (CharSequence str : strings) { - Integer hash = hasher.apply(str); - List results = hashResults.get(hash); - if (results == null) { - results = new ArrayList<>(1); - hashResults.put(hash, results); - } else { - duplicateHashCodes.add(hash); - } - results.add(str); - } - - if (duplicateHashCodes.isEmpty()) { - return Collections.emptyMap(); - } - Map> duplicates = - new HashMap<>(duplicateHashCodes.size()); - for (Integer duplicateHashCode : duplicateHashCodes) { - List realDups = new ArrayList<>(2); - Iterator itr = hashResults.get(duplicateHashCode).iterator(); - // there should be at least 2 elements in the list ... bcz there may be duplicates - realDups.add(itr.next()); - checknext: do { - CharSequence next = itr.next(); - for (CharSequence potentialDup : realDups) { - if (!AsciiString.contentEqualsIgnoreCase(next, potentialDup)) { - realDups.add(next); - break checknext; - } - } - } while (itr.hasNext()); - - if (realDups.size() > 1) { - duplicates.put(duplicateHashCode, realDups); - } - } - return duplicates; - } - - private static void printResults(PrintStream stream, Map> dups) { - stream.println("Number duplicates: " + dups.size()); - for (Entry> entry : dups.entrySet()) { - stream.print(entry.getValue().size() + " duplicates for hash: " + entry.getKey() + " values: "); - for (CharSequence str : entry.getValue()) { - stream.print("[" + str + "] "); - } - stream.println(); - } - } - - private interface Function { - R apply(P param); - } -} 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 deleted file mode 100644 index 48a2f669f4..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/HpackDecoderTest.java +++ /dev/null @@ -1,800 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/* - * Copyright 2014 Twitter, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.internal.StringUtil; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; - -import static io.netty.handler.codec.http2.HpackDecoder.decodeULE128; -import static io.netty.handler.codec.http2.Http2HeadersEncoder.NEVER_SENSITIVE; -import static io.netty.util.AsciiString.EMPTY_STRING; -import static io.netty.util.AsciiString.of; -import static java.lang.Integer.MAX_VALUE; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.greaterThanOrEqualTo; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; - -public class HpackDecoderTest { - - private HpackDecoder hpackDecoder; - private Http2Headers mockHeaders; - - private static String hex(String s) { - return StringUtil.toHexString(s.getBytes()); - } - - private void decode(String encoded) throws Http2Exception { - byte[] b = StringUtil.decodeHexDump(encoded); - ByteBuf in = Unpooled.wrappedBuffer(b); - try { - hpackDecoder.decode(0, in, mockHeaders, true); - } finally { - in.release(); - } - } - - @BeforeEach - public void setUp() { - hpackDecoder = new HpackDecoder(8192); - mockHeaders = mock(Http2Headers.class); - } - - @Test - public void testDecodeULE128IntMax() throws Http2Exception { - byte[] input = {(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x07}; - ByteBuf in = Unpooled.wrappedBuffer(input); - try { - assertEquals(MAX_VALUE, decodeULE128(in, 0)); - } finally { - in.release(); - } - } - - @Test - public void testDecodeULE128IntOverflow1() throws Http2Exception { - byte[] input = {(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x07}; - final ByteBuf in = Unpooled.wrappedBuffer(input); - final int readerIndex = in.readerIndex(); - try { - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - decodeULE128(in, 1); - } - }); - } finally { - assertEquals(readerIndex, in.readerIndex()); - in.release(); - } - } - - @Test - public void testDecodeULE128IntOverflow2() throws Http2Exception { - byte[] input = {(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x08}; - final ByteBuf in = Unpooled.wrappedBuffer(input); - final int readerIndex = in.readerIndex(); - try { - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - decodeULE128(in, 0); - } - }); - } finally { - assertEquals(readerIndex, in.readerIndex()); - in.release(); - } - } - - @Test - public void testDecodeULE128LongMax() throws Http2Exception { - byte[] input = {(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, - (byte) 0xFF, (byte) 0x7F}; - ByteBuf in = Unpooled.wrappedBuffer(input); - try { - assertEquals(Long.MAX_VALUE, decodeULE128(in, 0L)); - } finally { - in.release(); - } - } - - @Test - public void testDecodeULE128LongOverflow1() throws Http2Exception { - byte[] input = {(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, - (byte) 0xFF, (byte) 0xFF}; - final ByteBuf in = Unpooled.wrappedBuffer(input); - final int readerIndex = in.readerIndex(); - try { - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - decodeULE128(in, 0L); - } - }); - } finally { - assertEquals(readerIndex, in.readerIndex()); - in.release(); - } - } - - @Test - public void testDecodeULE128LongOverflow2() throws Http2Exception { - byte[] input = {(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, - (byte) 0xFF, (byte) 0x7F}; - final ByteBuf in = Unpooled.wrappedBuffer(input); - final int readerIndex = in.readerIndex(); - try { - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - decodeULE128(in, 1L); - } - }); - } finally { - assertEquals(readerIndex, in.readerIndex()); - in.release(); - } - } - - @Test - public void testSetTableSizeWithMaxUnsigned32BitValueSucceeds() throws Http2Exception { - byte[] input = {(byte) 0x3F, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x0E}; - ByteBuf in = Unpooled.wrappedBuffer(input); - try { - final long expectedHeaderSize = 4026531870L; // based on the input above - hpackDecoder.setMaxHeaderTableSize(expectedHeaderSize); - hpackDecoder.decode(0, in, mockHeaders, true); - assertEquals(expectedHeaderSize, hpackDecoder.getMaxHeaderTableSize()); - } finally { - in.release(); - } - } - - @Test - public void testSetTableSizeOverLimitFails() throws Http2Exception { - byte[] input = {(byte) 0x3F, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x0E}; - final ByteBuf in = Unpooled.wrappedBuffer(input); - try { - hpackDecoder.setMaxHeaderTableSize(4026531870L - 1); // based on the input above ... 1 less than is above. - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - hpackDecoder.decode(0, in, mockHeaders, true); - } - }); - } finally { - in.release(); - } - } - - @Test - public void testLiteralHuffmanEncodedWithEmptyNameAndValue() throws Http2Exception { - byte[] input = {0, (byte) 0x80, 0}; - ByteBuf in = Unpooled.wrappedBuffer(input); - try { - hpackDecoder.decode(0, in, mockHeaders, true); - verify(mockHeaders, times(1)).add(EMPTY_STRING, EMPTY_STRING); - } finally { - in.release(); - } - } - - @Test - public void testLiteralHuffmanEncodedWithPaddingGreaterThan7Throws() throws Http2Exception { - byte[] input = {0, (byte) 0x81, -1}; - final ByteBuf in = Unpooled.wrappedBuffer(input); - try { - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - hpackDecoder.decode(0, in, mockHeaders, true); - } - }); - } finally { - in.release(); - } - } - - @Test - public void testLiteralHuffmanEncodedWithDecodingEOSThrows() throws Http2Exception { - byte[] input = {0, (byte) 0x84, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF}; - final ByteBuf in = Unpooled.wrappedBuffer(input); - try { - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - hpackDecoder.decode(0, in, mockHeaders, true); - } - }); - } finally { - in.release(); - } - } - - @Test - public void testLiteralHuffmanEncodedWithPaddingNotCorrespondingToMSBThrows() throws Http2Exception { - byte[] input = {0, (byte) 0x81, 0}; - final ByteBuf in = Unpooled.wrappedBuffer(input); - try { - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - hpackDecoder.decode(0, in, mockHeaders, true); - } - }); - } finally { - in.release(); - } - } - - @Test - public void testIncompleteIndex() throws Http2Exception { - byte[] compressed = StringUtil.decodeHexDump("FFF0"); - final ByteBuf in = Unpooled.wrappedBuffer(compressed); - try { - assertEquals(2, in.readableBytes()); - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - hpackDecoder.decode(0, in, mockHeaders, true); - } - }); - } finally { - in.release(); - } - } - - @Test - public void testUnusedIndex() throws Http2Exception { - // Index 0 is not used - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - decode("80"); - } - }); - } - - @Test - public void testIllegalIndex() throws Http2Exception { - // Index larger than the header table - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - decode("FF00"); - } - }); - } - - @Test - public void testInsidiousIndex() throws Http2Exception { - // Insidious index so the last shift causes sign overflow - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - decode("FF8080808007"); - } - }); - } - - @Test - public void testDynamicTableSizeUpdate() throws Http2Exception { - decode("20"); - assertEquals(0, hpackDecoder.getMaxHeaderTableSize()); - decode("3FE11F"); - assertEquals(4096, hpackDecoder.getMaxHeaderTableSize()); - } - - @Test - public void testDynamicTableSizeUpdateRequired() throws Http2Exception { - hpackDecoder.setMaxHeaderTableSize(32); - decode("3F00"); - assertEquals(31, hpackDecoder.getMaxHeaderTableSize()); - } - - @Test - public void testIllegalDynamicTableSizeUpdate() throws Http2Exception { - // max header table size = MAX_HEADER_TABLE_SIZE + 1 - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - decode("3FE21F"); - } - }); - } - - @Test - public void testInsidiousMaxDynamicTableSize() throws Http2Exception { - hpackDecoder.setMaxHeaderTableSize(MAX_VALUE); - // max header table size sign overflow - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - decode("3FE1FFFFFF07"); - } - }); - } - - @Test - public void testMaxValidDynamicTableSize() throws Http2Exception { - hpackDecoder.setMaxHeaderTableSize(MAX_VALUE); - String baseValue = "3FE1FFFFFF0"; - for (int i = 0; i < 7; ++i) { - decode(baseValue + i); - } - } - - @Test - public void testReduceMaxDynamicTableSize() throws Http2Exception { - hpackDecoder.setMaxHeaderTableSize(0); - assertEquals(0, hpackDecoder.getMaxHeaderTableSize()); - decode("2081"); - } - - @Test - public void testTooLargeDynamicTableSizeUpdate() throws Http2Exception { - hpackDecoder.setMaxHeaderTableSize(0); - assertEquals(0, hpackDecoder.getMaxHeaderTableSize()); - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - decode("21"); // encoder max header table size not small enough - } - }); - } - - @Test - public void testMissingDynamicTableSizeUpdate() throws Http2Exception { - hpackDecoder.setMaxHeaderTableSize(0); - assertEquals(0, hpackDecoder.getMaxHeaderTableSize()); - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - decode("81"); - } - }); - } - - @Test - public void testLiteralWithIncrementalIndexingWithEmptyName() throws Http2Exception { - decode("400005" + hex("value")); - verify(mockHeaders, times(1)).add(EMPTY_STRING, of("value")); - } - - @Test - public void testLiteralWithIncrementalIndexingCompleteEviction() throws Http2Exception { - // Verify indexed host header - decode("4004" + hex("name") + "05" + hex("value")); - verify(mockHeaders).add(of("name"), of("value")); - verifyNoMoreInteractions(mockHeaders); - - reset(mockHeaders); - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < 4096; i++) { - sb.append('a'); - } - String value = sb.toString(); - sb = new StringBuilder(); - sb.append("417F811F"); - for (int i = 0; i < 4096; i++) { - sb.append("61"); // 'a' - } - decode(sb.toString()); - verify(mockHeaders).add(of(":authority"), of(value)); - verifyNoMoreInteractions(mockHeaders); - reset(mockHeaders); - - // Verify next header is inserted at index 62 - decode("4004" + hex("name") + "05" + hex("value") + "BE"); - verify(mockHeaders, times(2)).add(of("name"), of("value")); - verifyNoMoreInteractions(mockHeaders); - } - - @Test - public void testLiteralWithIncrementalIndexingWithLargeValue() throws Http2Exception { - // Ignore header that exceeds max header size - final StringBuilder sb = new StringBuilder(); - sb.append("4004"); - sb.append(hex("name")); - sb.append("7F813F"); - for (int i = 0; i < 8192; i++) { - sb.append("61"); // 'a' - } - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - decode(sb.toString()); - } - }); - } - - @Test - public void testLiteralWithoutIndexingWithEmptyName() throws Http2Exception { - decode("000005" + hex("value")); - verify(mockHeaders, times(1)).add(EMPTY_STRING, of("value")); - } - - @Test - public void testLiteralWithoutIndexingWithLargeName() throws Http2Exception { - // Ignore header name that exceeds max header size - final StringBuilder sb = new StringBuilder(); - sb.append("007F817F"); - for (int i = 0; i < 16384; i++) { - sb.append("61"); // 'a' - } - sb.append("00"); - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - decode(sb.toString()); - } - }); - } - - @Test - public void testLiteralWithoutIndexingWithLargeValue() throws Http2Exception { - // Ignore header that exceeds max header size - final StringBuilder sb = new StringBuilder(); - sb.append("0004"); - sb.append(hex("name")); - sb.append("7F813F"); - for (int i = 0; i < 8192; i++) { - sb.append("61"); // 'a' - } - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - decode(sb.toString()); - } - }); - } - - @Test - public void testLiteralNeverIndexedWithEmptyName() throws Http2Exception { - decode("100005" + hex("value")); - verify(mockHeaders, times(1)).add(EMPTY_STRING, of("value")); - } - - @Test - public void testLiteralNeverIndexedWithLargeName() throws Http2Exception { - // Ignore header name that exceeds max header size - final StringBuilder sb = new StringBuilder(); - sb.append("107F817F"); - for (int i = 0; i < 16384; i++) { - sb.append("61"); // 'a' - } - sb.append("00"); - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - decode(sb.toString()); - } - }); - } - - @Test - public void testLiteralNeverIndexedWithLargeValue() throws Http2Exception { - // Ignore header that exceeds max header size - final StringBuilder sb = new StringBuilder(); - sb.append("1004"); - sb.append(hex("name")); - sb.append("7F813F"); - for (int i = 0; i < 8192; i++) { - sb.append("61"); // 'a' - } - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - decode(sb.toString()); - } - }); - } - - @Test - public void testDecodeLargerThanMaxHeaderListSizeUpdatesDynamicTable() throws Http2Exception { - final ByteBuf in = Unpooled.buffer(300); - try { - hpackDecoder.setMaxHeaderListSize(200); - HpackEncoder hpackEncoder = new HpackEncoder(true); - - // encode headers that are slightly larger than maxHeaderListSize - Http2Headers toEncode = new DefaultHttp2Headers(); - toEncode.add("test_1", "1"); - toEncode.add("test_2", "2"); - toEncode.add("long", String.format("%0100d", 0).replace('0', 'A')); - toEncode.add("test_3", "3"); - hpackEncoder.encodeHeaders(1, in, toEncode, NEVER_SENSITIVE); - - // decode the headers, we should get an exception - final Http2Headers decoded = new DefaultHttp2Headers(); - assertThrows(Http2Exception.HeaderListSizeException.class, new Executable() { - @Override - public void execute() throws Throwable { - hpackDecoder.decode(1, in, decoded, true); - } - }); - - // 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(); - } - } - - @Test - public void testDecodeCountsNamesOnlyOnce() throws Http2Exception { - ByteBuf in = Unpooled.buffer(200); - try { - hpackDecoder.setMaxHeaderListSize(3500); - HpackEncoder hpackEncoder = new HpackEncoder(true); - - // encode headers that are slightly larger than maxHeaderListSize - Http2Headers toEncode = new DefaultHttp2Headers(); - toEncode.add(String.format("%03000d", 0).replace('0', 'f'), "value"); - toEncode.add("accept", "value"); - hpackEncoder.encodeHeaders(1, in, toEncode, NEVER_SENSITIVE); - - Http2Headers decoded = new DefaultHttp2Headers(); - hpackDecoder.decode(1, in, decoded, true); - assertEquals(2, decoded.size()); - } finally { - in.release(); - } - } - - @Test - public void testAccountForHeaderOverhead() throws Exception { - final ByteBuf in = Unpooled.buffer(100); - try { - String headerName = "12345"; - String headerValue = "56789"; - long headerSize = headerName.length() + headerValue.length(); - hpackDecoder.setMaxHeaderListSize(headerSize); - HpackEncoder hpackEncoder = new HpackEncoder(true); - - Http2Headers toEncode = new DefaultHttp2Headers(); - toEncode.add(headerName, headerValue); - hpackEncoder.encodeHeaders(1, in, toEncode, NEVER_SENSITIVE); - - final Http2Headers decoded = new DefaultHttp2Headers(); - - // SETTINGS_MAX_HEADER_LIST_SIZE is big enough for the header to fit... - assertThat(hpackDecoder.getMaxHeaderListSize(), is(greaterThanOrEqualTo(headerSize))); - - // ... but decode should fail because we add some overhead for each header entry - assertThrows(Http2Exception.HeaderListSizeException.class, new Executable() { - @Override - public void execute() throws Throwable { - hpackDecoder.decode(1, in, decoded, true); - } - }); - } finally { - in.release(); - } - } - - @Test - public void testIncompleteHeaderFieldRepresentation() throws Http2Exception { - // Incomplete Literal Header Field with Incremental Indexing - byte[] input = {(byte) 0x40}; - final ByteBuf in = Unpooled.wrappedBuffer(input); - try { - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - hpackDecoder.decode(0, in, mockHeaders, true); - } - }); - } finally { - in.release(); - } - } - - @Test - public void unknownPseudoHeader() throws Exception { - final ByteBuf in = Unpooled.buffer(200); - try { - HpackEncoder hpackEncoder = new HpackEncoder(true); - - Http2Headers toEncode = new DefaultHttp2Headers(); - toEncode.add(":test", "1"); - hpackEncoder.encodeHeaders(1, in, toEncode, NEVER_SENSITIVE); - - final Http2Headers decoded = new DefaultHttp2Headers(); - - assertThrows(Http2Exception.StreamException.class, new Executable() { - @Override - public void execute() throws Throwable { - hpackDecoder.decode(1, in, decoded, true); - } - }); - } finally { - in.release(); - } - } - - @Test - public void disableHeaderValidation() throws Exception { - ByteBuf in = Unpooled.buffer(200); - try { - HpackEncoder hpackEncoder = new HpackEncoder(true); - - Http2Headers toEncode = new DefaultHttp2Headers(); - toEncode.add(":test", "1"); - toEncode.add(":status", "200"); - toEncode.add(":method", "GET"); - hpackEncoder.encodeHeaders(1, in, toEncode, NEVER_SENSITIVE); - - Http2Headers decoded = new DefaultHttp2Headers(); - - hpackDecoder.decode(1, in, decoded, false); - - assertThat(decoded.valueIterator(":test").next().toString(), is("1")); - assertThat(decoded.status().toString(), is("200")); - assertThat(decoded.method().toString(), is("GET")); - } finally { - in.release(); - } - } - - @Test - public void requestPseudoHeaderInResponse() throws Exception { - final ByteBuf in = Unpooled.buffer(200); - try { - HpackEncoder hpackEncoder = new HpackEncoder(true); - - Http2Headers toEncode = new DefaultHttp2Headers(); - toEncode.add(":status", "200"); - toEncode.add(":method", "GET"); - hpackEncoder.encodeHeaders(1, in, toEncode, NEVER_SENSITIVE); - - final Http2Headers decoded = new DefaultHttp2Headers(); - - assertThrows(Http2Exception.StreamException.class, new Executable() { - @Override - public void execute() throws Throwable { - hpackDecoder.decode(1, in, decoded, true); - } - }); - } finally { - in.release(); - } - } - - @Test - public void responsePseudoHeaderInRequest() throws Exception { - final ByteBuf in = Unpooled.buffer(200); - try { - HpackEncoder hpackEncoder = new HpackEncoder(true); - - Http2Headers toEncode = new DefaultHttp2Headers(); - toEncode.add(":method", "GET"); - toEncode.add(":status", "200"); - hpackEncoder.encodeHeaders(1, in, toEncode, NEVER_SENSITIVE); - - final Http2Headers decoded = new DefaultHttp2Headers(); - - assertThrows(Http2Exception.StreamException.class, new Executable() { - @Override - public void execute() throws Throwable { - hpackDecoder.decode(1, in, decoded, true); - } - }); - } finally { - in.release(); - } - } - - @Test - public void pseudoHeaderAfterRegularHeader() throws Exception { - final ByteBuf in = Unpooled.buffer(200); - try { - HpackEncoder hpackEncoder = new HpackEncoder(true); - - Http2Headers toEncode = new InOrderHttp2Headers(); - toEncode.add("test", "1"); - toEncode.add(":method", "GET"); - hpackEncoder.encodeHeaders(1, in, toEncode, NEVER_SENSITIVE); - - final Http2Headers decoded = new DefaultHttp2Headers(); - - assertThrows(Http2Exception.StreamException.class, new Executable() { - @Override - public void execute() throws Throwable { - hpackDecoder.decode(1, in, decoded, true); - } - }); - } finally { - in.release(); - } - } - - @Test - public void failedValidationDoesntCorruptHpack() throws Exception { - final 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); - - final Http2Headers decoded = new DefaultHttp2Headers(); - - Http2Exception.StreamException expected = - assertThrows(Http2Exception.StreamException.class, new Executable() { - @Override - public void execute() throws Throwable { - hpackDecoder.decode(1, in1, decoded, true); - } - }); - 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(); - } - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/HpackDynamicTableTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/HpackDynamicTableTest.java deleted file mode 100644 index dde31eefa3..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/HpackDynamicTableTest.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.http2; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; - -public class HpackDynamicTableTest { - - @Test - public void testLength() { - HpackDynamicTable table = new HpackDynamicTable(100); - assertEquals(0, table.length()); - HpackHeaderField entry = new HpackHeaderField("foo", "bar"); - table.add(entry); - assertEquals(1, table.length()); - table.clear(); - assertEquals(0, table.length()); - } - - @Test - public void testSize() { - HpackDynamicTable table = new HpackDynamicTable(100); - assertEquals(0, table.size()); - HpackHeaderField entry = new HpackHeaderField("foo", "bar"); - table.add(entry); - assertEquals(entry.size(), table.size()); - table.clear(); - assertEquals(0, table.size()); - } - - @Test - public void testGetEntry() { - final HpackDynamicTable table = new HpackDynamicTable(100); - HpackHeaderField entry = new HpackHeaderField("foo", "bar"); - table.add(entry); - assertEquals(entry, table.getEntry(1)); - table.clear(); - - assertThrows(IndexOutOfBoundsException.class, new Executable() { - @Override - public void execute() throws Throwable { - table.getEntry(1); - } - }); - } - - @Test - public void testGetEntryExceptionally() { - final HpackDynamicTable table = new HpackDynamicTable(1); - assertThrows(IndexOutOfBoundsException.class, new Executable() { - @Override - public void execute() throws Throwable { - table.getEntry(1); - } - }); - } - - @Test - public void testRemove() { - HpackDynamicTable table = new HpackDynamicTable(100); - assertNull(table.remove()); - HpackHeaderField entry1 = new HpackHeaderField("foo", "bar"); - HpackHeaderField entry2 = new HpackHeaderField("hello", "world"); - table.add(entry1); - table.add(entry2); - assertEquals(entry1, table.remove()); - assertEquals(entry2, table.getEntry(1)); - assertEquals(1, table.length()); - assertEquals(entry2.size(), table.size()); - } - - @Test - public void testSetCapacity() { - HpackHeaderField entry1 = new HpackHeaderField("foo", "bar"); - HpackHeaderField entry2 = new HpackHeaderField("hello", "world"); - final int size1 = entry1.size(); - final int size2 = entry2.size(); - HpackDynamicTable table = new HpackDynamicTable(size1 + size2); - table.add(entry1); - table.add(entry2); - assertEquals(2, table.length()); - assertEquals(size1 + size2, table.size()); - table.setCapacity(((long) size1 + size2) * 2); //larger capacity - assertEquals(2, table.length()); - assertEquals(size1 + size2, table.size()); - table.setCapacity(size2); //smaller capacity - //entry1 will be removed - assertEquals(1, table.length()); - assertEquals(size2, table.size()); - assertEquals(entry2, table.getEntry(1)); - table.setCapacity(0); //clear all - assertEquals(0, table.length()); - assertEquals(0, table.size()); - } - - @Test - public void testAdd() { - HpackDynamicTable table = new HpackDynamicTable(100); - assertEquals(0, table.size()); - HpackHeaderField entry1 = new HpackHeaderField("foo", "bar"); //size:3+3+32=38 - HpackHeaderField entry2 = new HpackHeaderField("hello", "world"); - table.add(entry1); //success - assertEquals(entry1.size(), table.size()); - table.setCapacity(32); //entry1 is removed from table - assertEquals(0, table.size()); - assertEquals(0, table.length()); - table.add(entry1); //fail quietly - assertEquals(0, table.size()); - assertEquals(0, table.length()); - table.setCapacity(64); - table.add(entry1); //success - assertEquals(entry1.size(), table.size()); - assertEquals(1, table.length()); - table.add(entry2); //entry2 is added, but entry1 is removed from table - assertEquals(entry2.size(), table.size()); - assertEquals(1, table.length()); - assertEquals(entry2, table.getEntry(1)); - } -} 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 deleted file mode 100644 index 39baf6f44d..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/HpackEncoderTest.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; - -import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_HEADER_LIST_SIZE; -import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_HEADER_TABLE_SIZE; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.mock; - -public class HpackEncoderTest { - private HpackDecoder hpackDecoder; - private HpackEncoder hpackEncoder; - private Http2Headers mockHeaders; - - @BeforeEach - public void setUp() { - hpackEncoder = new HpackEncoder(); - hpackDecoder = new HpackDecoder(DEFAULT_HEADER_LIST_SIZE); - mockHeaders = mock(Http2Headers.class); - } - - @Test - public void testSetMaxHeaderTableSizeToMaxValue() throws Http2Exception { - ByteBuf buf = Unpooled.buffer(); - hpackEncoder.setMaxHeaderTableSize(buf, MAX_HEADER_TABLE_SIZE); - hpackDecoder.setMaxHeaderTableSize(MAX_HEADER_TABLE_SIZE); - hpackDecoder.decode(0, buf, mockHeaders, true); - assertEquals(MAX_HEADER_TABLE_SIZE, hpackDecoder.getMaxHeaderTableSize()); - buf.release(); - } - - @Test - public void testSetMaxHeaderTableSizeOverflow() throws Http2Exception { - final ByteBuf buf = Unpooled.buffer(); - try { - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - hpackEncoder.setMaxHeaderTableSize(buf, MAX_HEADER_TABLE_SIZE + 1); - } - }); - } finally { - buf.release(); - } - } - - /** - * The encoder should not impose an arbitrary limit on the header size if - * the server has not specified any limit. - */ - @Test - public void testWillEncode16MBHeaderByDefault() throws Http2Exception { - ByteBuf buf = Unpooled.buffer(); - String bigHeaderName = "x-big-header"; - int bigHeaderSize = 1024 * 1024 * 16; - String bigHeaderVal = new String(new char[bigHeaderSize]).replace('\0', 'X'); - Http2Headers headersIn = new DefaultHttp2Headers().add( - "x-big-header", bigHeaderVal); - Http2Headers headersOut = new DefaultHttp2Headers(); - - try { - hpackEncoder.encodeHeaders(0, buf, headersIn, Http2HeadersEncoder.NEVER_SENSITIVE); - hpackDecoder.setMaxHeaderListSize(bigHeaderSize + 1024); - hpackDecoder.decode(0, buf, headersOut, false); - } finally { - buf.release(); - } - assertEquals(headersOut.get(bigHeaderName).toString(), bigHeaderVal); - } - - @Test - public void testSetMaxHeaderListSizeEnforcedAfterSet() throws Http2Exception { - final ByteBuf buf = Unpooled.buffer(); - final Http2Headers headers = new DefaultHttp2Headers().add( - "x-big-header", - new String(new char[1024 * 16]).replace('\0', 'X') - ); - - hpackEncoder.setMaxHeaderListSize(1000); - - try { - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - hpackEncoder.encodeHeaders(0, buf, headers, Http2HeadersEncoder.NEVER_SENSITIVE); - } - }); - } finally { - buf.release(); - } - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/HpackHuffmanTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/HpackHuffmanTest.java deleted file mode 100644 index 2a6ea038aa..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/HpackHuffmanTest.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/* - * Copyright 2014 Twitter, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.AsciiString; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; - -import java.util.Random; - -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; - -public class HpackHuffmanTest { - - @Test - public void testHuffman() throws Http2Exception { - String s = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - for (int i = 0; i < s.length(); i++) { - roundTrip(s.substring(0, i)); - } - - Random random = new Random(123456789L); - byte[] buf = new byte[4096]; - random.nextBytes(buf); - roundTrip(buf); - } - - @Test - public void testDecodeEOS() throws Http2Exception { - final byte[] buf = new byte[4]; - for (int i = 0; i < 4; i++) { - buf[i] = (byte) 0xFF; - } - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - decode(buf); - } - }); - } - - @Test - public void testDecodeIllegalPadding() throws Http2Exception { - final byte[] buf = new byte[1]; - buf[0] = 0x00; // '0', invalid padding - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - decode(buf); - } - }); - } - - @Test - public void testDecodeExtraPadding() throws Http2Exception { - final byte[] buf = makeBuf(0x0f, 0xFF); // '1', 'EOS' - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - decode(buf); - } - }); - } - - @Test - public void testDecodeExtraPadding1byte() throws Http2Exception { - final byte[] buf = makeBuf(0xFF); - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - decode(buf); - } - }); - } - - @Test - public void testDecodeExtraPadding2byte() throws Http2Exception { - final byte[] buf = makeBuf(0x1F, 0xFF); // 'a' - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - decode(buf); - } - }); - } - - @Test - public void testDecodeExtraPadding3byte() throws Http2Exception { - final byte[] buf = makeBuf(0x1F, 0xFF, 0xFF); // 'a' - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - decode(buf); - } - }); - } - - @Test - public void testDecodeExtraPadding4byte() throws Http2Exception { - final byte[] buf = makeBuf(0x1F, 0xFF, 0xFF, 0xFF); // 'a' - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - decode(buf); - } - }); - } - - @Test - public void testDecodeExtraPadding29bit() throws Http2Exception { - final byte[] buf = makeBuf(0xFF, 0x9F, 0xFF, 0xFF, 0xFF); // '|' - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - decode(buf); - } - }); - } - - @Test - public void testDecodePartialSymbol() throws Http2Exception { - final byte[] buf = - makeBuf(0x52, 0xBC, 0x30, 0xFF, 0xFF, 0xFF, 0xFF); // " pFA\x00", 31 bits of padding, a.k.a. EOS - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - decode(buf); - } - }); - } - - private static byte[] makeBuf(int ... bytes) { - byte[] buf = new byte[bytes.length]; - for (int i = 0; i < buf.length; i++) { - buf[i] = (byte) bytes[i]; - } - return buf; - } - - private static void roundTrip(String s) throws Http2Exception { - roundTrip(new HpackHuffmanEncoder(), s); - } - - private static void roundTrip(HpackHuffmanEncoder encoder, String s) - throws Http2Exception { - roundTrip(encoder, s.getBytes()); - } - - private static void roundTrip(byte[] buf) throws Http2Exception { - roundTrip(new HpackHuffmanEncoder(), buf); - } - - private static void roundTrip(HpackHuffmanEncoder encoder, byte[] buf) - throws Http2Exception { - ByteBuf buffer = Unpooled.buffer(); - try { - encoder.encode(buffer, new AsciiString(buf, false)); - byte[] bytes = new byte[buffer.readableBytes()]; - buffer.readBytes(bytes); - - byte[] actualBytes = decode(bytes); - - assertArrayEquals(buf, actualBytes); - } finally { - buffer.release(); - } - } - - private static byte[] decode(byte[] bytes) throws Http2Exception { - ByteBuf buffer = Unpooled.wrappedBuffer(bytes); - try { - AsciiString decoded = new HpackHuffmanDecoder().decode(buffer, buffer.readableBytes()); - assertFalse(buffer.isReadable()); - return decoded.toByteArray(); - } finally { - buffer.release(); - } - } -} 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 deleted file mode 100644 index 7655bc212e..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/HpackTest.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/* - * Copyright 2014 Twitter, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.util.internal.ResourcesUtil; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -import java.io.File; -import java.io.InputStream; -import java.util.Objects; - -public class HpackTest { - - private static final String TEST_DIR = '/' + HpackTest.class.getPackage().getName().replaceAll("\\.", "/") - + "/testdata/"; - - public static File[] files() { - File[] files = ResourcesUtil.getFile(HpackTest.class, TEST_DIR).listFiles(); - Objects.requireNonNull(files, "files"); - return files; - } - - @ParameterizedTest(name = "file = {0}") - @MethodSource("files") - public void test(File file) throws Exception { - InputStream is = HpackTest.class.getResourceAsStream(TEST_DIR + file.getName()); - HpackTestCase hpackTestCase = HpackTestCase.load(is); - hpackTestCase.testCompress(); - hpackTestCase.testDecompress(); - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/HpackTestCase.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/HpackTestCase.java deleted file mode 100644 index 66f3da42ee..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/HpackTestCase.java +++ /dev/null @@ -1,285 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/* - * Copyright 2014 Twitter, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.netty.handler.codec.http2; - -import com.google.gson.FieldNamingPolicy; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.internal.StringUtil; - -import java.io.InputStream; -import java.io.InputStreamReader; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_HEADER_LIST_SIZE; -import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_HEADER_LIST_SIZE; -import static io.netty.handler.codec.http2.Http2TestUtil.newTestEncoder; - -final class HpackTestCase { - - private static final Gson GSON = new GsonBuilder() - .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) - .registerTypeAdapter(HpackHeaderField.class, new HeaderFieldDeserializer()) - .create(); - - int maxHeaderTableSize = -1; - boolean sensitiveHeaders; - - List headerBlocks; - - private HpackTestCase() { - } - - static HpackTestCase load(InputStream is) { - InputStreamReader r = new InputStreamReader(is); - HpackTestCase hpackTestCase = GSON.fromJson(r, HpackTestCase.class); - for (HeaderBlock headerBlock : hpackTestCase.headerBlocks) { - headerBlock.encodedBytes = StringUtil.decodeHexDump(headerBlock.getEncodedStr()); - } - return hpackTestCase; - } - - void testCompress() throws Exception { - HpackEncoder hpackEncoder = createEncoder(); - - for (HeaderBlock headerBlock : headerBlocks) { - - byte[] actual = - encode(hpackEncoder, headerBlock.getHeaders(), headerBlock.getMaxHeaderTableSize(), - sensitiveHeaders); - - if (!Arrays.equals(actual, headerBlock.encodedBytes)) { - throw new AssertionError( - "\nEXPECTED:\n" + headerBlock.getEncodedStr() + - "\nACTUAL:\n" + StringUtil.toHexString(actual)); - } - - List actualDynamicTable = new ArrayList<>(); - for (int index = 0; index < hpackEncoder.length(); index++) { - actualDynamicTable.add(hpackEncoder.getHeaderField(index)); - } - - List expectedDynamicTable = headerBlock.getDynamicTable(); - - if (!headersEqual(expectedDynamicTable, actualDynamicTable)) { - throw new AssertionError( - "\nEXPECTED DYNAMIC TABLE:\n" + expectedDynamicTable + - "\nACTUAL DYNAMIC TABLE:\n" + actualDynamicTable); - } - - if (headerBlock.getTableSize() != hpackEncoder.size()) { - throw new AssertionError( - "\nEXPECTED TABLE SIZE: " + headerBlock.getTableSize() + - "\n ACTUAL TABLE SIZE : " + hpackEncoder.size()); - } - } - } - - void testDecompress() throws Exception { - HpackDecoder hpackDecoder = createDecoder(); - - for (HeaderBlock headerBlock : headerBlocks) { - - List actualHeaders = decode(hpackDecoder, headerBlock.encodedBytes); - - List expectedHeaders = new ArrayList<>(); - for (HpackHeaderField h : headerBlock.getHeaders()) { - expectedHeaders.add(new HpackHeaderField(h.name, h.value)); - } - - if (!headersEqual(expectedHeaders, actualHeaders)) { - throw new AssertionError( - "\nEXPECTED:\n" + expectedHeaders + - "\nACTUAL:\n" + actualHeaders); - } - - List actualDynamicTable = new ArrayList<>(); - for (int index = 0; index < hpackDecoder.length(); index++) { - actualDynamicTable.add(hpackDecoder.getHeaderField(index)); - } - - List expectedDynamicTable = headerBlock.getDynamicTable(); - - if (!headersEqual(expectedDynamicTable, actualDynamicTable)) { - throw new AssertionError( - "\nEXPECTED DYNAMIC TABLE:\n" + expectedDynamicTable + - "\nACTUAL DYNAMIC TABLE:\n" + actualDynamicTable); - } - - if (headerBlock.getTableSize() != hpackDecoder.size()) { - throw new AssertionError( - "\nEXPECTED TABLE SIZE: " + headerBlock.getTableSize() + - "\n ACTUAL TABLE SIZE : " + hpackDecoder.size()); - } - } - } - - private HpackEncoder createEncoder() { - int maxHeaderTableSize = this.maxHeaderTableSize; - if (maxHeaderTableSize == -1) { - maxHeaderTableSize = Integer.MAX_VALUE; - } - - try { - return newTestEncoder(true, MAX_HEADER_LIST_SIZE, maxHeaderTableSize); - } catch (Http2Exception e) { - throw new Error("invalid initial values!", e); - } - } - - private HpackDecoder createDecoder() { - int maxHeaderTableSize = this.maxHeaderTableSize; - if (maxHeaderTableSize == -1) { - maxHeaderTableSize = Integer.MAX_VALUE; - } - - return new HpackDecoder(DEFAULT_HEADER_LIST_SIZE, maxHeaderTableSize); - } - - private static byte[] encode(HpackEncoder hpackEncoder, List headers, int maxHeaderTableSize, - final boolean sensitive) throws Http2Exception { - Http2Headers http2Headers = toHttp2Headers(headers); - Http2HeadersEncoder.SensitivityDetector sensitivityDetector = (name, value) -> sensitive; - ByteBuf buffer = Unpooled.buffer(); - try { - if (maxHeaderTableSize != -1) { - hpackEncoder.setMaxHeaderTableSize(buffer, maxHeaderTableSize); - } - - hpackEncoder.encodeHeaders(3 /* randomly chosen */, buffer, http2Headers, sensitivityDetector); - byte[] bytes = new byte[buffer.readableBytes()]; - buffer.readBytes(bytes); - return bytes; - } finally { - buffer.release(); - } - } - - private static Http2Headers toHttp2Headers(List inHeaders) { - Http2Headers headers = new DefaultHttp2Headers(false); - for (HpackHeaderField e : inHeaders) { - headers.add(e.name, e.value); - } - return headers; - } - - private static List decode(HpackDecoder hpackDecoder, byte[] expected) throws Exception { - ByteBuf in = Unpooled.wrappedBuffer(expected); - try { - List headers = new ArrayList<>(); - TestHeaderListener listener = new TestHeaderListener(headers); - hpackDecoder.decode(0, in, listener, true); - return headers; - } finally { - in.release(); - } - } - - private static String concat(List l) { - StringBuilder ret = new StringBuilder(); - for (String s : l) { - ret.append(s); - } - return ret.toString(); - } - - private static boolean headersEqual(List expected, List actual) { - if (expected.size() != actual.size()) { - return false; - } - for (int i = 0; i < expected.size(); i++) { - if (!expected.get(i).equalsForTest(actual.get(i))) { - return false; - } - } - return true; - } - - static class HeaderBlock { - @SuppressWarnings("FieldMayBeFinal") - private int maxHeaderTableSize = -1; - private byte[] encodedBytes; - private List encoded; - private List headers; - private List dynamicTable; - private int tableSize; - - private int getMaxHeaderTableSize() { - return maxHeaderTableSize; - } - - public String getEncodedStr() { - return concat(encoded).replaceAll(" ", ""); - } - - public List getHeaders() { - return headers; - } - - public List getDynamicTable() { - return dynamicTable; - } - - public int getTableSize() { - return tableSize; - } - } - - static class HeaderFieldDeserializer implements JsonDeserializer { - - @Override - public HpackHeaderField deserialize(JsonElement json, Type typeOfT, - JsonDeserializationContext context) { - JsonObject jsonObject = json.getAsJsonObject(); - Set> entrySet = jsonObject.entrySet(); - if (entrySet.size() != 1) { - throw new JsonParseException("JSON Object has multiple entries: " + entrySet); - } - Map.Entry entry = entrySet.iterator().next(); - String name = entry.getKey(); - String value = entry.getValue().getAsString(); - return new HpackHeaderField(name, value); - } - } -} 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 deleted file mode 100644 index 7ea7ed79e1..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ClientUpgradeCodecTest.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpVersion; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - - -public class Http2ClientUpgradeCodecTest { - - @Test - public void testUpgradeToHttp2ConnectionHandler() throws Exception { - testUpgrade(new Http2ConnectionHandlerBuilder().server(false).frameListener( - new Http2FrameAdapter()).build(), null); - } - - @Test - public void testUpgradeToHttp2FrameCodec() throws Exception { - testUpgrade(Http2FrameCodecBuilder.forClient().build(), null); - } - - @Test - public void testUpgradeToHttp2MultiplexCodec() throws Exception { - testUpgrade(Http2MultiplexCodecBuilder.forClient(new HttpInboundHandler()) - .withUpgradeStreamHandler(new ChannelHandler() { }).build(), null); - } - - @Test - public void testUpgradeToHttp2FrameCodecWithMultiplexer() throws Exception { - testUpgrade(Http2FrameCodecBuilder.forClient().build(), - new Http2MultiplexHandler(new HttpInboundHandler(), new HttpInboundHandler())); - } - - private static void testUpgrade(Http2ConnectionHandler handler, Http2MultiplexHandler multiplexer) - throws Exception { - FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.OPTIONS, "*"); - - EmbeddedChannel channel = new EmbeddedChannel(new ChannelHandler() { }); - ChannelHandlerContext ctx = channel.pipeline().firstContext(); - - Http2ClientUpgradeCodec codec; - - if (multiplexer == null) { - codec = new Http2ClientUpgradeCodec("connectionHandler", handler); - } else { - codec = new Http2ClientUpgradeCodec("connectionHandler", handler, multiplexer); - } - - codec.setUpgradeHeaders(ctx, request); - // Flush the channel to ensure we write out all buffered data - channel.flush(); - - channel.executor().submit(() -> { - codec.upgradeTo(ctx, null); - return null; - }).sync(); - assertNotNull(channel.pipeline().get("connectionHandler")); - - if (multiplexer != null) { - assertNotNull(channel.pipeline().get(Http2MultiplexHandler.class)); - } - - assertTrue(channel.finishAndReleaseAll()); - } - - @ChannelHandler.Sharable - private static final class HttpInboundHandler implements ChannelHandler { } -} 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 deleted file mode 100644 index 66a9c7494e..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ConnectionHandlerTest.java +++ /dev/null @@ -1,758 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelMetadata; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.DefaultChannelConfig; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http2.Http2Exception.ShutdownHint; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.FutureListener; -import io.netty.util.concurrent.ImmediateEventExecutor; -import io.netty.util.concurrent.Promise; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.stubbing.Answer; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - -import static io.netty.buffer.Unpooled.copiedBuffer; -import static io.netty.handler.codec.http2.Http2CodecUtil.connectionPrefaceBuf; -import static io.netty.handler.codec.http2.Http2Error.CANCEL; -import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR; -import static io.netty.handler.codec.http2.Http2Error.STREAM_CLOSED; -import static io.netty.handler.codec.http2.Http2Stream.State.CLOSED; -import static io.netty.handler.codec.http2.Http2Stream.State.IDLE; -import static io.netty.util.CharsetUtil.US_ASCII; -import static io.netty.util.CharsetUtil.UTF_8; -import static java.util.concurrent.TimeUnit.SECONDS; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyBoolean; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.anyLong; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.verifyZeroInteractions; -import static org.mockito.Mockito.when; - -/** - * Tests for {@link Http2ConnectionHandler} - */ -@SuppressWarnings("unchecked") -public class Http2ConnectionHandlerTest { - private static final int STREAM_ID = 1; - private static final int NON_EXISTANT_STREAM_ID = 13; - - private Http2ConnectionHandler handler; - private Promise promise; - - @Mock - private Http2Connection connection; - - @Mock - private Http2RemoteFlowController remoteFlow; - - @Mock - private Http2LocalFlowController localFlow; - - @Mock - private Http2Connection.Endpoint remote; - - @Mock - private Http2RemoteFlowController remoteFlowController; - - @Mock - private Http2Connection.Endpoint local; - - @Mock - private Http2LocalFlowController localFlowController; - - @Mock - private ChannelHandlerContext ctx; - - @Mock - private EventExecutor executor; - - @Mock - private Channel channel; - - @Mock - private ChannelPipeline pipeline; - - @Mock - private Future future; - - @Mock - private Http2Stream stream; - - @Mock - private Http2ConnectionDecoder decoder; - - @Mock - private Http2ConnectionEncoder encoder; - - @Mock - private Http2FrameWriter frameWriter; - - private String goAwayDebugCap; - - @SuppressWarnings("unchecked") - @BeforeEach - public void setup() throws Exception { - MockitoAnnotations.initMocks(this); - - promise = ImmediateEventExecutor.INSTANCE.newPromise(); - - 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); - when(encoder.frameWriter()).thenReturn(frameWriter); - when(encoder.flowController()).thenReturn(remoteFlow); - when(decoder.flowController()).thenReturn(localFlow); - doAnswer((Answer>) invocation -> { - ByteBuf buf = invocation.getArgument(3); - goAwayDebugCap = buf.toString(UTF_8); - buf.release(); - return future; - }).when(frameWriter).writeGoAway( - any(ChannelHandlerContext.class), anyInt(), anyLong(), any(ByteBuf.class)); - doAnswer((Answer>) invocation -> { - Object o = invocation.getArguments()[0]; - if (o instanceof FutureListener) { - ((FutureListener) o).operationComplete(future); - } - return future; - }).when(future).addListener(any(FutureListener.class)); - when(future.cause()).thenReturn(fakeException); - when(channel.isActive()).thenReturn(true); - when(future.isFailed()).thenReturn(true); - when(channel.pipeline()).thenReturn(pipeline); - when(connection.remote()).thenReturn(remote); - when(remote.flowController()).thenReturn(remoteFlowController); - when(connection.local()).thenReturn(local); - when(local.flowController()).thenReturn(localFlowController); - doAnswer((Answer) in -> { - Http2StreamVisitor visitor = in.getArgument(0); - if (!visitor.visit(stream)) { - return stream; - } - return null; - }).when(connection).forEachActiveStream(any(Http2StreamVisitor.class)); - 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(any(ChannelHandlerContext.class), - any(Http2Settings.class))).thenReturn(future); - when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT); - when(ctx.channel()).thenReturn(channel); - when(ctx.newFailedFuture(any(Throwable.class))) - .thenAnswer(invocationOnMock -> - ImmediateEventExecutor.INSTANCE.newFailedFuture(invocationOnMock.getArgument(0))); - when(ctx.newSucceededFuture()).thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null)); - when(ctx.newPromise()).thenReturn(promise); - when(ctx.write(any())).thenReturn(future); - when(ctx.executor()).thenReturn(executor); - doAnswer(in -> { - Object msg = in.getArgument(0); - ReferenceCountUtil.release(msg); - return null; - }).when(ctx).fireChannelRead(any()); - doAnswer((Answer>) in -> - ImmediateEventExecutor.INSTANCE.newSucceededFuture(null)).when(ctx).write(any()); - doAnswer((Answer>) in -> - ImmediateEventExecutor.INSTANCE.newSucceededFuture(null)).when(ctx).close(); - } - - private Http2ConnectionHandler newHandler() throws Exception { - Http2ConnectionHandler handler = new Http2ConnectionHandlerBuilder().codec(decoder, encoder).build(); - handler.handlerAdded(ctx); - return handler; - } - - @AfterEach - public void tearDown() throws Exception { - if (handler != null) { - handler.handlerRemoved(ctx); - } - } - - @Test - public void onHttpServerUpgradeWithoutHandlerAdded() throws Exception { - handler = new Http2ConnectionHandlerBuilder().frameListener(new Http2FrameAdapter()).server(true).build(); - Http2Exception e = assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - handler.onHttpServerUpgrade(new Http2Settings()); - } - }); - assertEquals(Http2Error.INTERNAL_ERROR, e.error()); - } - - @Test - public void onHttpClientUpgradeWithoutHandlerAdded() throws Exception { - handler = new Http2ConnectionHandlerBuilder().frameListener(new Http2FrameAdapter()).server(false).build(); - Http2Exception e = assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - handler.onHttpClientUpgrade(); - } - }); - assertEquals(Http2Error.INTERNAL_ERROR, e.error()); - } - - @Test - public void clientShouldveSentPrefaceAndSettingsFrameWhenUserEventIsTriggered() throws Exception { - when(connection.isServer()).thenReturn(false); - when(channel.isActive()).thenReturn(false); - handler = newHandler(); - when(channel.isActive()).thenReturn(true); - - final Http2ConnectionPrefaceAndSettingsFrameWrittenEvent evt = - Http2ConnectionPrefaceAndSettingsFrameWrittenEvent.INSTANCE; - - final AtomicBoolean verified = new AtomicBoolean(false); - final Answer verifier = in -> { - assertEquals(in.getArgument(0), evt); // sanity check... - verify(ctx).write(eq(connectionPrefaceBuf())); - verify(encoder).writeSettings(eq(ctx), any(Http2Settings.class)); - verified.set(true); - return null; - }; - - doAnswer(verifier).when(ctx).fireUserEventTriggered(evt); - - handler.channelActive(ctx); - assertTrue(verified.get()); - } - - @Test - public void clientShouldSendClientPrefaceStringWhenActive() throws Exception { - when(connection.isServer()).thenReturn(false); - when(channel.isActive()).thenReturn(false); - handler = newHandler(); - when(channel.isActive()).thenReturn(true); - handler.channelActive(ctx); - verify(ctx).write(eq(connectionPrefaceBuf())); - } - - @Test - public void serverShouldNotSendClientPrefaceStringWhenActive() throws Exception { - when(connection.isServer()).thenReturn(true); - when(channel.isActive()).thenReturn(false); - handler = newHandler(); - when(channel.isActive()).thenReturn(true); - handler.channelActive(ctx); - verify(ctx, never()).write(eq(connectionPrefaceBuf())); - } - - @Test - public void serverReceivingInvalidClientPrefaceStringShouldHandleException() throws Exception { - when(connection.isServer()).thenReturn(true); - handler = newHandler(); - handler.channelRead(ctx, copiedBuffer("BAD_PREFACE", UTF_8)); - ArgumentCaptor captor = ArgumentCaptor.forClass(ByteBuf.class); - verify(frameWriter).writeGoAway(any(ChannelHandlerContext.class), - eq(Integer.MAX_VALUE), eq(PROTOCOL_ERROR.code()), captor.capture()); - assertEquals(0, captor.getValue().refCnt()); - } - - @Test - public void serverReceivingHttp1ClientPrefaceStringShouldIncludePreface() throws Exception { - when(connection.isServer()).thenReturn(true); - handler = newHandler(); - handler.channelRead(ctx, copiedBuffer("GET /path HTTP/1.1", US_ASCII)); - ArgumentCaptor captor = ArgumentCaptor.forClass(ByteBuf.class); - verify(frameWriter).writeGoAway(any(ChannelHandlerContext.class), eq(Integer.MAX_VALUE), - eq(PROTOCOL_ERROR.code()), captor.capture()); - assertEquals(0, captor.getValue().refCnt()); - assertTrue(goAwayDebugCap.contains("/path")); - } - - @Test - public void serverReceivingClientPrefaceStringFollowedByNonSettingsShouldHandleException() - throws Exception { - when(connection.isServer()).thenReturn(true); - handler = newHandler(); - - // Create a connection preface followed by a bunch of zeros (i.e. not a settings frame). - ByteBuf buf = Unpooled.buffer().writeBytes(connectionPrefaceBuf()).writeZero(10); - handler.channelRead(ctx, buf); - ArgumentCaptor captor = ArgumentCaptor.forClass(ByteBuf.class); - verify(frameWriter, atLeastOnce()).writeGoAway(any(ChannelHandlerContext.class), - eq(Integer.MAX_VALUE), eq(PROTOCOL_ERROR.code()), captor.capture()); - assertEquals(0, captor.getValue().refCnt()); - } - - @Test - public void serverReceivingValidClientPrefaceStringShouldContinueReadingFrames() throws Exception { - when(connection.isServer()).thenReturn(true); - handler = newHandler(); - ByteBuf prefacePlusSome = addSettingsHeader(Unpooled.buffer().writeBytes(connectionPrefaceBuf())); - handler.channelRead(ctx, prefacePlusSome); - verify(decoder, atLeastOnce()).decodeFrame(any(ChannelHandlerContext.class), - any(ByteBuf.class)); - } - - @Test - public void verifyChannelHandlerCanBeReusedInPipeline() throws Exception { - when(connection.isServer()).thenReturn(true); - handler = newHandler(); - // Only read the connection preface...after preface is read internal state of Http2ConnectionHandler - // is expected to change relative to the pipeline. - ByteBuf preface = connectionPrefaceBuf(); - handler.channelRead(ctx, preface); - verify(decoder, never()).decodeFrame(any(ChannelHandlerContext.class), - any(ByteBuf.class)); - - // Now remove and add the handler...this is setting up the test condition. - handler.handlerRemoved(ctx); - handler.handlerAdded(ctx); - - // Now verify we can continue as normal, reading connection preface plus more. - ByteBuf prefacePlusSome = addSettingsHeader(Unpooled.buffer().writeBytes(connectionPrefaceBuf())); - handler.channelRead(ctx, prefacePlusSome); - verify(decoder, atLeastOnce()).decodeFrame(any(ChannelHandlerContext.class), any(ByteBuf.class)); - } - - @SuppressWarnings("unchecked") - @Test - public void channelInactiveShouldCloseStreams() throws Exception { - handler = newHandler(); - handler.channelInactive(ctx); - verify(connection).close(any(Promise.class)); - } - - @Test - public void connectionErrorShouldStartShutdown() throws Exception { - handler = newHandler(); - Http2Exception e = new Http2Exception(PROTOCOL_ERROR); - // There's no guarantee that lastStreamCreated in correct, as the error could have occurred during header - // processing before it was updated. Thus, it should _not_ be used for the GOAWAY. - // https://github.com/netty/netty/issues/10670 - when(remote.lastStreamCreated()).thenReturn(STREAM_ID); - handler.exceptionCaught(ctx, e); - ArgumentCaptor captor = ArgumentCaptor.forClass(ByteBuf.class); - verify(frameWriter).writeGoAway(eq(ctx), eq(Integer.MAX_VALUE), eq(PROTOCOL_ERROR.code()), - captor.capture()); - captor.getValue().release(); - } - - @Test - public void serverShouldSend431OnHeaderSizeErrorWhenDecodingInitialHeaders() throws Exception { - int padding = 0; - handler = newHandler(); - Http2Exception e = new Http2Exception.HeaderListSizeException(STREAM_ID, PROTOCOL_ERROR, - "Header size exceeded max allowed size 8196", true); - - when(stream.id()).thenReturn(STREAM_ID); - when(connection.isServer()).thenReturn(true); - when(stream.isHeadersSent()).thenReturn(false); - when(remote.lastStreamCreated()).thenReturn(STREAM_ID); - when(frameWriter.writeRstStream(eq(ctx), eq(STREAM_ID), - eq(PROTOCOL_ERROR.code()))).thenReturn(future); - - handler.exceptionCaught(ctx, e); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Http2Headers.class); - verify(encoder).writeHeaders(eq(ctx), eq(STREAM_ID), - captor.capture(), eq(padding), eq(true)); - Http2Headers headers = captor.getValue(); - assertEquals(HttpResponseStatus.REQUEST_HEADER_FIELDS_TOO_LARGE.codeAsText(), headers.status()); - verify(frameWriter).writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code()); - } - - @Test - public void serverShouldNeverSend431HeaderSizeErrorWhenEncoding() throws Exception { - int padding = 0; - handler = newHandler(); - Http2Exception e = new Http2Exception.HeaderListSizeException(STREAM_ID, PROTOCOL_ERROR, - "Header size exceeded max allowed size 8196", false); - - when(stream.id()).thenReturn(STREAM_ID); - when(connection.isServer()).thenReturn(true); - when(stream.isHeadersSent()).thenReturn(false); - when(remote.lastStreamCreated()).thenReturn(STREAM_ID); - when(frameWriter.writeRstStream(eq(ctx), eq(STREAM_ID), - eq(PROTOCOL_ERROR.code()))).thenReturn(future); - - handler.exceptionCaught(ctx, e); - - verify(encoder, never()).writeHeaders(eq(ctx), eq(STREAM_ID), - any(Http2Headers.class), eq(padding), eq(true)); - verify(frameWriter).writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code()); - } - - @Test - public void clientShouldNeverSend431WhenHeadersAreTooLarge() throws Exception { - int padding = 0; - handler = newHandler(); - Http2Exception e = new Http2Exception.HeaderListSizeException(STREAM_ID, PROTOCOL_ERROR, - "Header size exceeded max allowed size 8196", true); - - when(stream.id()).thenReturn(STREAM_ID); - when(connection.isServer()).thenReturn(false); - when(stream.isHeadersSent()).thenReturn(false); - when(remote.lastStreamCreated()).thenReturn(STREAM_ID); - when(frameWriter.writeRstStream(eq(ctx), eq(STREAM_ID), - eq(PROTOCOL_ERROR.code()))).thenReturn(future); - - handler.exceptionCaught(ctx, e); - - verify(encoder, never()).writeHeaders(eq(ctx), eq(STREAM_ID), - any(Http2Headers.class), eq(padding), eq(true)); - verify(frameWriter).writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code()); - } - - @Test - public void prefaceUserEventProcessed() throws Exception { - final CountDownLatch latch = new CountDownLatch(1); - handler = new Http2ConnectionHandler(decoder, encoder, new Http2Settings()) { - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { - if (evt == Http2ConnectionPrefaceAndSettingsFrameWrittenEvent.INSTANCE) { - latch.countDown(); - } - } - }; - handler.handlerAdded(ctx); - assertTrue(latch.await(5, SECONDS)); - } - - @Test - public void serverShouldNeverSend431IfHeadersAlreadySent() throws Exception { - int padding = 0; - handler = newHandler(); - Http2Exception e = new Http2Exception.HeaderListSizeException(STREAM_ID, PROTOCOL_ERROR, - "Header size exceeded max allowed size 8196", true); - - when(stream.id()).thenReturn(STREAM_ID); - when(connection.isServer()).thenReturn(true); - when(stream.isHeadersSent()).thenReturn(true); - when(remote.lastStreamCreated()).thenReturn(STREAM_ID); - when(frameWriter.writeRstStream(eq(ctx), eq(STREAM_ID), - eq(PROTOCOL_ERROR.code()))).thenReturn(future); - handler.exceptionCaught(ctx, e); - - verify(encoder, never()).writeHeaders(eq(ctx), eq(STREAM_ID), - any(Http2Headers.class), eq(padding), eq(true)); - - verify(frameWriter).writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code()); - } - - @Test - public void serverShouldCreateStreamIfNeededBeforeSending431() throws Exception { - int padding = 0; - handler = newHandler(); - Http2Exception e = new Http2Exception.HeaderListSizeException(STREAM_ID, PROTOCOL_ERROR, - "Header size exceeded max allowed size 8196", true); - - when(connection.stream(STREAM_ID)).thenReturn(null); - when(remote.createStream(STREAM_ID, true)).thenReturn(stream); - when(stream.id()).thenReturn(STREAM_ID); - - when(connection.isServer()).thenReturn(true); - when(stream.isHeadersSent()).thenReturn(false); - when(remote.lastStreamCreated()).thenReturn(STREAM_ID); - when(frameWriter.writeRstStream(eq(ctx), eq(STREAM_ID), - eq(PROTOCOL_ERROR.code()))).thenReturn(future); - handler.exceptionCaught(ctx, e); - - verify(remote).createStream(STREAM_ID, true); - verify(encoder).writeHeaders(eq(ctx), eq(STREAM_ID), - any(Http2Headers.class), eq(padding), eq(true)); - - verify(frameWriter).writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code()); - } - - @Test - public void encoderAndDecoderAreClosedOnChannelInactive() throws Exception { - handler = newHandler(); - handler.channelActive(ctx); - when(channel.isActive()).thenReturn(false); - handler.channelInactive(ctx); - verify(encoder).close(); - verify(decoder).close(); - } - - @Test - public void writeRstOnNonExistantStreamShouldSucceed() throws Exception { - handler = newHandler(); - when(frameWriter.writeRstStream(eq(ctx), eq(NON_EXISTANT_STREAM_ID), - eq(STREAM_CLOSED.code()))).thenReturn(future); - handler.resetStream(ctx, NON_EXISTANT_STREAM_ID, STREAM_CLOSED.code()); - verify(frameWriter).writeRstStream(eq(ctx), eq(NON_EXISTANT_STREAM_ID), eq(STREAM_CLOSED.code())); - } - - @Test - public void writeRstOnClosedStreamShouldSucceed() throws Exception { - handler = newHandler(); - when(stream.id()).thenReturn(STREAM_ID); - when(frameWriter.writeRstStream(eq(ctx), eq(STREAM_ID), - anyLong())).thenReturn(future); - when(stream.state()).thenReturn(CLOSED); - when(stream.isHeadersSent()).thenReturn(true); - // The stream is "closed" but is still known about by the connection (connection().stream(..) - // will return the stream). We should still write a RST_STREAM frame in this scenario. - handler.resetStream(ctx, STREAM_ID, STREAM_CLOSED.code()); - verify(frameWriter).writeRstStream(eq(ctx), eq(STREAM_ID), anyLong()); - } - - @Test - public void writeRstOnIdleStreamShouldNotWriteButStillSucceed() throws Exception { - handler = newHandler(); - when(stream.state()).thenReturn(IDLE); - handler.resetStream(ctx, STREAM_ID, STREAM_CLOSED.code()); - verify(frameWriter, never()).writeRstStream(eq(ctx), eq(STREAM_ID), anyLong()); - verify(stream).close(); - } - - @SuppressWarnings("unchecked") - @Test - public void closeListenerShouldBeNotifiedOnlyOneTime() throws Exception { - handler = newHandler(); - when(future.isDone()).thenReturn(true); - when(future.isSuccess()).thenReturn(true); - doAnswer((Answer>) invocation -> { - Object[] args = invocation.getArguments(); - FutureListener listener = (FutureListener) args[0]; - // Simulate that all streams have become inactive by the time the future completes. - doAnswer((Answer) in -> null).when(connection).forEachActiveStream( - any(Http2StreamVisitor.class)); - when(connection.numActiveStreams()).thenReturn(0); - // Simulate the future being completed. - listener.operationComplete(future); - return future; - }).when(future).addListener(any(FutureListener.class)); - handler.close(ctx); - if (future.isDone()) { - when(connection.numActiveStreams()).thenReturn(0); - } - handler.closeStream(stream, future); - // Simulate another stream close call being made after the context should already be closed. - handler.closeStream(stream, future); - verify(ctx, times(1)).close(); - } - - @SuppressWarnings("unchecked") - @Test - public void canSendGoAwayFrame() throws Exception { - ByteBuf data = dummyData(); - long errorCode = Http2Error.INTERNAL_ERROR.code(); - when(future.isDone()).thenReturn(true); - when(future.isSuccess()).thenReturn(true); - doAnswer((Answer) invocation -> { - ((FutureListener) invocation.getArgument(0)).operationComplete(future); - return null; - }).when(future).addListener(any(FutureListener.class)); - handler = newHandler(); - handler.goAway(ctx, STREAM_ID, errorCode, data); - - verify(connection).goAwaySent(eq(STREAM_ID), eq(errorCode), eq(data)); - verify(frameWriter).writeGoAway(eq(ctx), eq(STREAM_ID), eq(errorCode), eq(data)); - verify(ctx).close(); - assertEquals(0, data.refCnt()); - } - - @Test - public void canSendGoAwayFramesWithDecreasingLastStreamIds() throws Exception { - handler = newHandler(); - ByteBuf data = dummyData(); - long errorCode = Http2Error.INTERNAL_ERROR.code(); - - handler.goAway(ctx, STREAM_ID + 2, errorCode, data.retain()); - verify(frameWriter).writeGoAway(eq(ctx), eq(STREAM_ID + 2), eq(errorCode), eq(data)); - verify(connection).goAwaySent(eq(STREAM_ID + 2), eq(errorCode), eq(data)); - promise = ImmediateEventExecutor.INSTANCE.newPromise(); - handler.goAway(ctx, STREAM_ID, errorCode, data); - verify(frameWriter).writeGoAway(eq(ctx), eq(STREAM_ID), eq(errorCode), eq(data)); - verify(connection).goAwaySent(eq(STREAM_ID), eq(errorCode), eq(data)); - assertEquals(0, data.refCnt()); - } - - @Test - public void cannotSendGoAwayFrameWithIncreasingLastStreamIds() throws Exception { - handler = newHandler(); - ByteBuf data = dummyData(); - long errorCode = Http2Error.INTERNAL_ERROR.code(); - - Future future = handler.goAway(ctx, STREAM_ID, errorCode, data.retain()); - verify(connection).goAwaySent(eq(STREAM_ID), eq(errorCode), eq(data)); - verify(frameWriter).writeGoAway(eq(ctx), eq(STREAM_ID), eq(errorCode), eq(data)); - // The frameWriter is only mocked, so it should not have interacted with the promise. - assertFalse(future.isDone()); - - when(connection.goAwaySent()).thenReturn(true); - when(remote.lastStreamKnownByPeer()).thenReturn(STREAM_ID); - - Exception ex = new IllegalStateException(); - doAnswer((Answer) invocationOnMock -> { - throw ex; - }).when(connection).goAwaySent(anyInt(), anyLong(), any(ByteBuf.class)); - Future future2 = handler.goAway(ctx, STREAM_ID + 2, errorCode, data); - assertTrue(future2.isDone()); - assertFalse(future2.isSuccess()); - assertSame(ex, future2.cause()); - - assertEquals(0, data.refCnt()); - verifyNoMoreInteractions(frameWriter); - } - - @Test - public void channelReadCompleteTriggersFlush() throws Exception { - handler = newHandler(); - handler.channelReadComplete(ctx); - 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); - handler = newHandler(); - when(channel.isActive()).thenReturn(false); - handler.channelInactive(ctx); - verify(frameWriter, never()).writeGoAway(any(ChannelHandlerContext.class), anyInt(), anyLong(), - any(ByteBuf.class)); - verify(frameWriter, never()).writeRstStream(any(ChannelHandlerContext.class), anyInt(), anyLong()); - } - - @Test - public void clientChannelClosedDoesNotSendGoAwayBeforePreface() throws Exception { - when(connection.isServer()).thenReturn(false); - when(channel.isActive()).thenReturn(false); - handler = newHandler(); - when(channel.isActive()).thenReturn(true); - handler.close(ctx); - verifyZeroInteractions(frameWriter); - } - - @Test - public void gracefulShutdownTimeoutWhenConnectionErrorHardShutdownTest() throws Exception { - gracefulShutdownTimeoutWhenConnectionErrorTest0(ShutdownHint.HARD_SHUTDOWN); - } - - @Test - public void gracefulShutdownTimeoutWhenConnectionErrorGracefulShutdownTest() throws Exception { - gracefulShutdownTimeoutWhenConnectionErrorTest0(ShutdownHint.GRACEFUL_SHUTDOWN); - } - - private void gracefulShutdownTimeoutWhenConnectionErrorTest0(ShutdownHint hint) throws Exception { - handler = newHandler(); - final long expectedMillis = 1234; - handler.gracefulShutdownTimeoutMillis(expectedMillis); - Http2Exception exception = new Http2Exception(PROTOCOL_ERROR, "Test error", hint); - handler.onConnectionError(ctx, false, exception, exception); - verify(executor, atLeastOnce()).schedule(any(Runnable.class), eq(expectedMillis), eq(TimeUnit.MILLISECONDS)); - } - - @Test - public void gracefulShutdownTimeoutTest() throws Exception { - handler = newHandler(); - final long expectedMillis = 1234; - handler.gracefulShutdownTimeoutMillis(expectedMillis); - handler.close(ctx); - verify(executor, atLeastOnce()).schedule(any(Runnable.class), eq(expectedMillis), eq(TimeUnit.MILLISECONDS)); - } - - @Test - public void gracefulShutdownTimeoutNoActiveStreams() throws Exception { - handler = newHandler(); - when(connection.numActiveStreams()).thenReturn(0); - final long expectedMillis = 1234; - handler.gracefulShutdownTimeoutMillis(expectedMillis); - handler.close(ctx); - verify(executor, atLeastOnce()).schedule(any(Runnable.class), eq(expectedMillis), eq(TimeUnit.MILLISECONDS)); - } - - @Test - public void gracefulShutdownIndefiniteTimeoutTest() throws Exception { - handler = newHandler(); - handler.gracefulShutdownTimeoutMillis(-1); - handler.close(ctx); - verify(executor, never()).schedule(any(Runnable.class), anyLong(), any(TimeUnit.class)); - } - - @Test - public void writeMultipleRstFramesForSameStream() throws Exception { - handler = newHandler(); - when(stream.id()).thenReturn(STREAM_ID); - - final AtomicBoolean resetSent = new AtomicBoolean(); - when(stream.resetSent()).then((Answer) invocationOnMock -> { - resetSent.set(true); - return stream; - }); - when(stream.isResetSent()).then((Answer) invocationOnMock -> resetSent.get()); - when(frameWriter.writeRstStream(eq(ctx), eq(STREAM_ID), anyLong())) - .thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null)); - - Future f1 = handler.resetStream(ctx, STREAM_ID, STREAM_CLOSED.code()); - Future f2 = handler.resetStream(ctx, STREAM_ID, CANCEL.code()); - verify(frameWriter).writeRstStream(eq(ctx), eq(STREAM_ID), anyLong()); - assertTrue(f1.isSuccess()); - assertTrue(f2.isSuccess()); - } - - private static ByteBuf dummyData() { - return Unpooled.buffer().writeBytes("abcdefgh".getBytes(UTF_8)); - } - - private static ByteBuf addSettingsHeader(ByteBuf buf) { - buf.writeMedium(Http2CodecUtil.SETTING_ENTRY_LENGTH); - buf.writeByte(Http2FrameTypes.SETTINGS); - buf.writeByte(0); - buf.writeInt(0); - return buf; - } -} 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 deleted file mode 100644 index 9f405c427c..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ConnectionRoundtripTest.java +++ /dev/null @@ -1,1139 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.bootstrap.Bootstrap; -import io.netty.bootstrap.ServerBootstrap; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.local.LocalAddress; -import io.netty.channel.local.LocalChannel; -import io.netty.channel.local.LocalHandler; -import io.netty.channel.local.LocalServerChannel; -import io.netty.handler.codec.http2.Http2TestUtil.FrameCountDown; -import io.netty.util.AsciiString; -import io.netty.util.IllegalReferenceCountException; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.ImmediateEventExecutor; -import io.netty.util.concurrent.Promise; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; - -import java.io.ByteArrayOutputStream; -import java.util.Random; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -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; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyBoolean; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.anyLong; -import static org.mockito.Mockito.anyShort; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -/** - * Tests the full HTTP/2 framing stack including the connection and preface handlers. - */ -public class Http2ConnectionRoundtripTest { - - private static final long DEFAULT_AWAIT_TIMEOUT_SECONDS = 15; - - @Mock - private Http2FrameListener clientListener; - - @Mock - private Http2FrameListener serverListener; - - private Http2ConnectionHandler http2Client; - private Http2ConnectionHandler http2Server; - private ServerBootstrap sb; - private Bootstrap cb; - private Channel serverChannel; - private volatile Channel serverConnectedChannel; - private Channel clientChannel; - private FrameCountDown serverFrameCountDown; - private CountDownLatch requestLatch; - private CountDownLatch serverSettingsAckLatch; - private CountDownLatch dataLatch; - private CountDownLatch trailersLatch; - private CountDownLatch goAwayLatch; - - @BeforeEach - public void setup() throws Exception { - MockitoAnnotations.initMocks(this); - mockFlowControl(clientListener); - mockFlowControl(serverListener); - } - - @AfterEach - public void teardown() throws Exception { - if (clientChannel != null) { - clientChannel.close().await(); - clientChannel = null; - } - if (serverChannel != null) { - serverChannel.close().syncUninterruptibly(); - serverChannel = null; - } - final Channel serverConnectedChannel = this.serverConnectedChannel; - if (serverConnectedChannel != null) { - serverConnectedChannel.close().syncUninterruptibly(); - this.serverConnectedChannel = null; - } - Future serverGroup = sb.config().group().shutdownGracefully(0, 5, SECONDS); - Future serverChildGroup = sb.config().childGroup().shutdownGracefully(0, 5, SECONDS); - Future clientGroup = cb.config().group().shutdownGracefully(0, 5, SECONDS); - serverGroup.syncUninterruptibly(); - serverChildGroup.syncUninterruptibly(); - clientGroup.syncUninterruptibly(); - } - - @Test - public void inflightFrameAfterStreamResetShouldNotMakeConnectionUnusable() throws Exception { - bootstrapEnv(1, 1, 2, 1); - final CountDownLatch latch = new CountDownLatch(1); - doAnswer(invocationOnMock -> { - ChannelHandlerContext ctx = invocationOnMock.getArgument(0); - http2Server.encoder().writeHeaders(ctx, - invocationOnMock.getArgument(1), - invocationOnMock.getArgument(2), - 0, - false); - http2Server.flush(ctx); - return null; - }).when(serverListener).onHeadersRead(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class), - anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean()); - - doAnswer((Answer) invocationOnMock -> { - latch.countDown(); - return null; - }).when(clientListener).onHeadersRead(any(ChannelHandlerContext.class), eq(5), any(Http2Headers.class), - anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean()); - - // Create a single stream by sending a HEADERS frame to the server. - final short weight = 16; - final Http2Headers headers = dummyHeaders(); - runInChannel(clientChannel, () -> { - http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, weight, false, 0, false); - http2Client.flush(ctx()); - http2Client.encoder().writeRstStream(ctx(), 3, Http2Error.INTERNAL_ERROR.code()); - http2Client.flush(ctx()); - }); - - runInChannel(clientChannel, () -> { - http2Client.encoder().writeHeaders(ctx(), 5, headers, 0, weight, false, 0, false); - http2Client.flush(ctx()); - }); - - assertTrue(latch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); - } - - @Test - public void headersWithEndStreamShouldNotSendError() throws Exception { - bootstrapEnv(1, 1, 2, 1); - - // Create a single stream by sending a HEADERS frame to the server. - final short weight = 16; - final Http2Headers headers = dummyHeaders(); - runInChannel(clientChannel, () -> { - http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, weight, false, 0, true); - http2Client.flush(ctx()); - }); - - assertTrue(requestLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); - verify(serverListener).onHeadersRead(any(ChannelHandlerContext.class), eq(3), eq(headers), - eq(0), eq(weight), eq(false), eq(0), eq(true)); - // Wait for some time to see if a go_away or reset frame will be received. - Thread.sleep(1000); - - // Verify that no errors have been received. - verify(serverListener, never()).onGoAwayRead(any(ChannelHandlerContext.class), anyInt(), - anyLong(), any(ByteBuf.class)); - verify(serverListener, never()).onRstStreamRead(any(ChannelHandlerContext.class), anyInt(), - anyLong()); - - // The server will not respond, and so don't wait for graceful shutdown - setClientGracefulShutdownTime(0); - } - - @Test - public void encodeViolatesMaxHeaderListSizeCanStillUseConnection() throws Exception { - bootstrapEnv(1, 2, 1, 0, 0); - - final CountDownLatch serverSettingsAckLatch1 = new CountDownLatch(2); - final CountDownLatch serverSettingsAckLatch2 = new CountDownLatch(3); - final CountDownLatch clientSettingsLatch1 = new CountDownLatch(3); - final CountDownLatch serverRevHeadersLatch = new CountDownLatch(1); - final CountDownLatch clientHeadersLatch = new CountDownLatch(1); - final CountDownLatch clientDataWrite = new CountDownLatch(1); - final AtomicReference clientHeadersWriteException = new AtomicReference<>(); - final AtomicReference clientHeadersWriteException2 = new AtomicReference<>(); - final AtomicReference clientDataWriteException = new AtomicReference<>(); - - final Http2Headers headers = dummyHeaders(); - - doAnswer((Answer) invocationOnMock -> { - serverSettingsAckLatch1.countDown(); - serverSettingsAckLatch2.countDown(); - return null; - }).when(serverListener).onSettingsAckRead(any(ChannelHandlerContext.class)); - doAnswer((Answer) invocationOnMock -> { - clientSettingsLatch1.countDown(); - return null; - }).when(clientListener).onSettingsRead(any(ChannelHandlerContext.class), any(Http2Settings.class)); - - // Manually add a listener for when we receive the expected headers on the server. - doAnswer((Answer) invocationOnMock -> { - serverRevHeadersLatch.countDown(); - return null; - }).when(serverListener).onHeadersRead(any(ChannelHandlerContext.class), eq(5), eq(headers), - anyInt(), anyShort(), anyBoolean(), eq(0), eq(true)); - - // Set the maxHeaderListSize to 100 so we may be able to write some headers, but not all. We want to verify - // that we don't corrupt state if some can be written but not all. - runInChannel(serverConnectedChannel, () -> { - http2Server.encoder().writeSettings(serverCtx(), - new Http2Settings().copyFrom(http2Server.decoder().localSettings()) - .maxHeaderListSize(100)); - http2Server.flush(serverCtx()); - }); - - assertTrue(serverSettingsAckLatch1.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); - - runInChannel(clientChannel, () -> { - http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, false) - .addListener(future -> clientHeadersWriteException.set(future.cause())); - // It is expected that this write should fail locally and the remote peer will never see this. - http2Client.encoder().writeData(ctx(), 3, Unpooled.buffer(), 0, true) - .addListener(future -> { - clientDataWriteException.set(future.cause()); - clientDataWrite.countDown(); - }); - http2Client.flush(ctx()); - }); - - assertTrue(clientDataWrite.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); - assertNotNull(clientHeadersWriteException.get(), "Header encode should have exceeded maxHeaderListSize!"); - assertNotNull(clientDataWriteException.get(), "Data on closed stream should fail!"); - - // Set the maxHeaderListSize to the max value so we can send the headers. - runInChannel(serverConnectedChannel, () -> { - http2Server.encoder().writeSettings(serverCtx(), - new Http2Settings().copyFrom(http2Server.decoder().localSettings()) - .maxHeaderListSize(Http2CodecUtil.MAX_HEADER_LIST_SIZE)); - http2Server.flush(serverCtx()); - }); - - assertTrue(clientSettingsLatch1.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); - assertTrue(serverSettingsAckLatch2.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); - - runInChannel(clientChannel, () -> { - http2Client.encoder().writeHeaders(ctx(), 5, headers, 0, true).addListener(future -> { - clientHeadersWriteException2.set(future.cause()); - clientHeadersLatch.countDown(); - }); - http2Client.flush(ctx()); - }); - - assertTrue(clientHeadersLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); - assertNull(clientHeadersWriteException2.get(), - "Client write of headers should succeed with increased header list size!"); - assertTrue(serverRevHeadersLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); - - verify(serverListener, never()).onDataRead(any(ChannelHandlerContext.class), anyInt(), any(ByteBuf.class), - anyInt(), anyBoolean()); - - // Verify that no errors have been received. - verify(serverListener, never()).onGoAwayRead(any(ChannelHandlerContext.class), anyInt(), anyLong(), - any(ByteBuf.class)); - verify(serverListener, never()).onRstStreamRead(any(ChannelHandlerContext.class), anyInt(), anyLong()); - verify(clientListener, never()).onGoAwayRead(any(ChannelHandlerContext.class), anyInt(), anyLong(), - any(ByteBuf.class)); - verify(clientListener, never()).onRstStreamRead(any(ChannelHandlerContext.class), anyInt(), anyLong()); - } - - @Test - public void testSettingsAckIsSentBeforeUsingFlowControl() throws Exception { - bootstrapEnv(1, 1, 1, 1); - - final CountDownLatch serverSettingsAckLatch1 = new CountDownLatch(1); - final CountDownLatch serverSettingsAckLatch2 = new CountDownLatch(2); - final CountDownLatch serverDataLatch = new CountDownLatch(1); - final CountDownLatch clientWriteDataLatch = new CountDownLatch(1); - final byte[] data = {1, 2, 3, 4, 5}; - final ByteArrayOutputStream out = new ByteArrayOutputStream(data.length); - - doAnswer((Answer) invocationOnMock -> { - serverSettingsAckLatch1.countDown(); - serverSettingsAckLatch2.countDown(); - return null; - }).when(serverListener).onSettingsAckRead(any(ChannelHandlerContext.class)); - doAnswer((Answer) in -> { - ByteBuf buf = (ByteBuf) in.getArguments()[2]; - int padding = (Integer) in.getArguments()[3]; - int processedBytes = buf.readableBytes() + padding; - - buf.readBytes(out, buf.readableBytes()); - serverDataLatch.countDown(); - return processedBytes; - }).when(serverListener).onDataRead(any(ChannelHandlerContext.class), eq(3), - any(ByteBuf.class), eq(0), anyBoolean()); - - final Http2Headers headers = dummyHeaders(); - - // The server initially reduces the connection flow control window to 0. - runInChannel(serverConnectedChannel, () -> { - http2Server.encoder().writeSettings(serverCtx(), - new Http2Settings().copyFrom(http2Server.decoder().localSettings()) - .initialWindowSize(0)); - http2Server.flush(serverCtx()); - }); - - assertTrue(serverSettingsAckLatch1.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); - - // The client should now attempt to send data, but the window size is 0 so it will be queued in the flow - // controller. - runInChannel(clientChannel, () -> { - http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false); - http2Client.encoder().writeData(ctx(), 3, Unpooled.wrappedBuffer(data), 0, true); - http2Client.flush(ctx()); - clientWriteDataLatch.countDown(); - }); - - assertTrue(clientWriteDataLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); - - // Now the server opens up the connection window to allow the client to send the pending data. - runInChannel(serverConnectedChannel, () -> { - http2Server.encoder().writeSettings(serverCtx(), - new Http2Settings().copyFrom(http2Server.decoder().localSettings()) - .initialWindowSize(data.length)); - http2Server.flush(serverCtx()); - }); - - assertTrue(serverSettingsAckLatch2.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); - assertTrue(serverDataLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); - assertArrayEquals(data, out.toByteArray()); - - // Verify that no errors have been received. - verify(serverListener, never()).onGoAwayRead(any(ChannelHandlerContext.class), anyInt(), anyLong(), - any(ByteBuf.class)); - verify(serverListener, never()).onRstStreamRead(any(ChannelHandlerContext.class), anyInt(), anyLong()); - verify(clientListener, never()).onGoAwayRead(any(ChannelHandlerContext.class), anyInt(), anyLong(), - any(ByteBuf.class)); - verify(clientListener, never()).onRstStreamRead(any(ChannelHandlerContext.class), anyInt(), anyLong()); - } - - @Test - public void priorityUsingHigherValuedStreamIdDoesNotPreventUsingLowerStreamId() throws Exception { - bootstrapEnv(1, 1, 2, 0); - - final Http2Headers headers = dummyHeaders(); - runInChannel(clientChannel, () -> { - http2Client.encoder().writePriority(ctx(), 5, 3, (short) 14, false); - http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false); - http2Client.flush(ctx()); - }); - - assertTrue(serverSettingsAckLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); - assertTrue(requestLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); - - verify(serverListener).onPriorityRead(any(ChannelHandlerContext.class), eq(5), eq(3), eq((short) 14), - eq(false)); - verify(serverListener).onHeadersRead(any(ChannelHandlerContext.class), eq(3), eq(headers), eq(0), - eq((short) 16), eq(false), eq(0), eq(false)); - - // Verify that no errors have been received. - verify(serverListener, never()).onGoAwayRead(any(ChannelHandlerContext.class), anyInt(), anyLong(), - any(ByteBuf.class)); - verify(serverListener, never()).onRstStreamRead(any(ChannelHandlerContext.class), anyInt(), anyLong()); - verify(clientListener, never()).onGoAwayRead(any(ChannelHandlerContext.class), anyInt(), anyLong(), - any(ByteBuf.class)); - verify(clientListener, never()).onRstStreamRead(any(ChannelHandlerContext.class), anyInt(), anyLong()); - } - - @Test - public void headersUsingHigherValuedStreamIdPreventsUsingLowerStreamId() throws Exception { - bootstrapEnv(1, 1, 1, 0); - - final Http2Headers headers = dummyHeaders(); - runInChannel(clientChannel, () -> { - http2Client.encoder().writeHeaders(ctx(), 5, headers, 0, (short) 16, false, 0, false); - http2Client.encoder().frameWriter().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false); - http2Client.flush(ctx()); - }); - - assertTrue(serverSettingsAckLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); - assertTrue(requestLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); - - verify(serverListener).onHeadersRead(any(ChannelHandlerContext.class), eq(5), eq(headers), eq(0), - eq((short) 16), eq(false), eq(0), eq(false)); - verify(serverListener, never()).onHeadersRead(any(ChannelHandlerContext.class), eq(3), any(Http2Headers.class), - anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean()); - - // Client should receive a RST_STREAM for stream 3, but there is not Http2Stream object so the listener is never - // notified. - verify(serverListener, never()).onGoAwayRead(any(ChannelHandlerContext.class), anyInt(), anyLong(), - any(ByteBuf.class)); - verify(serverListener, never()).onRstStreamRead(any(ChannelHandlerContext.class), anyInt(), anyLong()); - verify(clientListener, never()).onGoAwayRead(any(ChannelHandlerContext.class), anyInt(), anyLong(), - any(ByteBuf.class)); - verify(clientListener, never()).onRstStreamRead(any(ChannelHandlerContext.class), anyInt(), anyLong()); - } - - @Test - public void headersWriteForPeerStreamWhichWasResetShouldNotGoAway() throws Exception { - bootstrapEnv(1, 1, 1, 0); - - final CountDownLatch serverGotRstLatch = new CountDownLatch(1); - final CountDownLatch serverWriteHeadersLatch = new CountDownLatch(1); - final AtomicReference serverWriteHeadersCauseRef = new AtomicReference<>(); - - final int streamId = 3; - - doAnswer((Answer) invocationOnMock -> { - if (streamId == (Integer) invocationOnMock.getArgument(1)) { - serverGotRstLatch.countDown(); - } - return null; - }).when(serverListener).onRstStreamRead(any(ChannelHandlerContext.class), eq(streamId), anyLong()); - - final Http2Headers headers = dummyHeaders(); - runInChannel(clientChannel, () -> { - http2Client.encoder().writeHeaders(ctx(), streamId, headers, CONNECTION_STREAM_ID, - DEFAULT_PRIORITY_WEIGHT, false, 0, false); - http2Client.encoder().writeRstStream(ctx(), streamId, Http2Error.CANCEL.code()); - http2Client.flush(ctx()); - }); - - assertTrue(serverSettingsAckLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); - assertTrue(serverGotRstLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); - - verify(serverListener).onHeadersRead(any(ChannelHandlerContext.class), eq(streamId), eq(headers), anyInt(), - anyShort(), anyBoolean(), anyInt(), eq(false)); - - // Now have the server attempt to send a headers frame simulating some asynchronous work. - runInChannel(serverConnectedChannel, () -> { - http2Server.encoder().writeHeaders(serverCtx(), streamId, headers, 0, true) - .addListener(future -> { - serverWriteHeadersCauseRef.set(future.cause()); - serverWriteHeadersLatch.countDown(); - }); - http2Server.flush(serverCtx()); - }); - - assertTrue(serverWriteHeadersLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); - Throwable serverWriteHeadersCause = serverWriteHeadersCauseRef.get(); - assertNotNull(serverWriteHeadersCause); - assertThat(serverWriteHeadersCauseRef.get(), not(instanceOf(Http2Exception.class))); - - // Server should receive a RST_STREAM for stream 3. - verify(serverListener, never()).onGoAwayRead(any(ChannelHandlerContext.class), anyInt(), anyLong(), - any(ByteBuf.class)); - verify(clientListener, never()).onGoAwayRead(any(ChannelHandlerContext.class), anyInt(), anyLong(), - any(ByteBuf.class)); - verify(clientListener, never()).onRstStreamRead(any(ChannelHandlerContext.class), anyInt(), anyLong()); - } - - @Test - public void http2ExceptionInPipelineShouldCloseConnection() throws Exception { - bootstrapEnv(1, 1, 2, 1); - - // Create a latch to track when the close occurs. - final CountDownLatch closeLatch = new CountDownLatch(1); - clientChannel.closeFuture().addListener(future -> closeLatch.countDown()); - - // Create a single stream by sending a HEADERS frame to the server. - final Http2Headers headers = dummyHeaders(); - runInChannel(clientChannel, () -> { - http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false); - http2Client.flush(ctx()); - }); - - // Wait for the server to create the stream. - assertTrue(serverSettingsAckLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); - assertTrue(requestLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); - - // Add a handler that will immediately throw an exception. - clientChannel.pipeline().addFirst(new ChannelHandler() { - @Override - public void handlerAdded(ChannelHandlerContext ctx) throws Exception { - throw Http2Exception.connectionError(PROTOCOL_ERROR, "Fake Exception"); - } - }); - - // Wait for the close to occur. - assertTrue(closeLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); - assertFalse(clientChannel.isOpen()); - } - - @Test - public void listenerExceptionShouldCloseConnection() throws Exception { - final Http2Headers headers = dummyHeaders(); - doThrow(new RuntimeException("Fake Exception")).when(serverListener).onHeadersRead( - any(ChannelHandlerContext.class), eq(3), eq(headers), eq(0), eq((short) 16), - eq(false), eq(0), eq(false)); - - bootstrapEnv(1, 0, 1, 1); - - // Create a latch to track when the close occurs. - final CountDownLatch closeLatch = new CountDownLatch(1); - clientChannel.closeFuture().addListener(future -> closeLatch.countDown()); - - // Create a single stream by sending a HEADERS frame to the server. - runInChannel(clientChannel, () -> { - http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false); - http2Client.flush(ctx()); - }); - - // Wait for the server to create the stream. - assertTrue(serverSettingsAckLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); - assertTrue(requestLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); - - // Wait for the close to occur. - assertTrue(closeLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); - assertFalse(clientChannel.isOpen()); - } - - private enum WriteEmptyBufferMode { - SINGLE_END_OF_STREAM, - SECOND_END_OF_STREAM, - SINGLE_WITH_TRAILERS, - SECOND_WITH_TRAILERS - } - - @Test - public void writeOfEmptyReleasedBufferSingleBufferQueuedInFlowControllerShouldFail() throws Exception { - writeOfEmptyReleasedBufferQueuedInFlowControllerShouldFail(WriteEmptyBufferMode.SINGLE_END_OF_STREAM); - } - - @Test - public void writeOfEmptyReleasedBufferSingleBufferTrailersQueuedInFlowControllerShouldFail() throws Exception { - writeOfEmptyReleasedBufferQueuedInFlowControllerShouldFail(WriteEmptyBufferMode.SINGLE_WITH_TRAILERS); - } - - @Test - public void writeOfEmptyReleasedBufferMultipleBuffersQueuedInFlowControllerShouldFail() throws Exception { - writeOfEmptyReleasedBufferQueuedInFlowControllerShouldFail(WriteEmptyBufferMode.SECOND_END_OF_STREAM); - } - - @Test - public void writeOfEmptyReleasedBufferMultipleBuffersTrailersQueuedInFlowControllerShouldFail() throws Exception { - writeOfEmptyReleasedBufferQueuedInFlowControllerShouldFail(WriteEmptyBufferMode.SECOND_WITH_TRAILERS); - } - - private void writeOfEmptyReleasedBufferQueuedInFlowControllerShouldFail(final WriteEmptyBufferMode mode) - throws Exception { - bootstrapEnv(1, 1, 2, 1); - - Promise promise = ImmediateEventExecutor.INSTANCE.newPromise(); - runInChannel(clientChannel, () -> { - http2Client.encoder().writeHeaders(ctx(), 3, EmptyHttp2Headers.INSTANCE, 0, (short) 16, false, 0, false); - ByteBuf emptyBuf = Unpooled.buffer(); - emptyBuf.release(); - final Future future; - switch (mode) { - case SINGLE_END_OF_STREAM: - future = http2Client.encoder().writeData(ctx(), 3, emptyBuf, 0, true); - break; - case SECOND_END_OF_STREAM: - future = http2Client.encoder().writeData(ctx(), 3, emptyBuf, 0, false); - http2Client.encoder().writeData(ctx(), 3, randomBytes(8), 0, true); - break; - case SINGLE_WITH_TRAILERS: - future = http2Client.encoder().writeData(ctx(), 3, emptyBuf, 0, false); - http2Client.encoder().writeHeaders(ctx(), 3, EmptyHttp2Headers.INSTANCE, 0, - (short) 16, false, 0, true); - break; - case SECOND_WITH_TRAILERS: - future = http2Client.encoder().writeData(ctx(), 3, emptyBuf, 0, false); - http2Client.encoder().writeData(ctx(), 3, randomBytes(8), 0, false); - http2Client.encoder().writeHeaders(ctx(), 3, EmptyHttp2Headers.INSTANCE, 0, - (short) 16, false, 0, true); - break; - default: - throw new Error(); - } - http2Client.flush(ctx()); - future.cascadeTo(promise); - }); - - ExecutionException e = assertThrows(ExecutionException.class, new Executable() { - @Override - public void execute() throws Throwable { - promise.asFuture().get(); - } - }); - assertThat(e.getCause(), is(instanceOf(IllegalReferenceCountException.class))); - } - - @Test - public void writeFailureFlowControllerRemoveFrame() - throws Exception { - bootstrapEnv(1, 1, 2, 1); - - final Promise dataPromise = newPromise(); - final Promise assertPromise = newPromise(); - - runInChannel(clientChannel, () -> { - http2Client.encoder().writeHeaders(ctx(), 3, EmptyHttp2Headers.INSTANCE, 0, (short) 16, false, 0, false); - clientChannel.pipeline().addFirst(new ChannelHandler() { - @Override - public Future write(ChannelHandlerContext ctx, Object msg) { - ReferenceCountUtil.release(msg); - - try { - // Ensure we update the window size so we will try to write the rest of the frame while - // processing the flush. - http2Client.encoder().flowController().initialWindowSize(8); - return ctx.newFailedFuture(new IllegalStateException()); - } catch (Http2Exception e) { - return ctx.newFailedFuture(e); - } - } - }); - - http2Client.encoder().flowController().initialWindowSize(4); - http2Client.encoder().writeData(ctx(), 3, randomBytes(8), 0, false) - .cascadeTo(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(null); - } catch (Throwable error) { - assertPromise.setFailure(error); - } - }); - - ExecutionException e = assertThrows(ExecutionException.class, new Executable() { - @Override - public void execute() throws Throwable { - dataPromise.asFuture().get(); - } - }); - assertThat(e.getCause(), is(instanceOf(IllegalStateException.class))); - assertPromise.asFuture().sync(); - } - - @Test - public void nonHttp2ExceptionInPipelineShouldNotCloseConnection() throws Exception { - bootstrapEnv(1, 1, 2, 1); - - // Create a latch to track when the close occurs. - final CountDownLatch closeLatch = new CountDownLatch(1); - clientChannel.closeFuture().addListener(future -> closeLatch.countDown()); - - // Create a single stream by sending a HEADERS frame to the server. - final Http2Headers headers = dummyHeaders(); - runInChannel(clientChannel, () -> { - http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false); - http2Client.flush(ctx()); - }); - - // Wait for the server to create the stream. - assertTrue(serverSettingsAckLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); - assertTrue(requestLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); - - // Add a handler that will immediately throw an exception. - clientChannel.pipeline().addFirst(new ChannelHandler() { - @Override - public void handlerAdded(ChannelHandlerContext ctx) throws Exception { - throw new RuntimeException("Fake Exception"); - } - }); - - // The close should NOT occur. - assertFalse(closeLatch.await(2, SECONDS)); - assertTrue(clientChannel.isOpen()); - - // Set the timeout very low because we know graceful shutdown won't complete - setClientGracefulShutdownTime(0); - } - - @Test - public void noMoreStreamIdsShouldSendGoAway() throws Exception { - bootstrapEnv(1, 1, 3, 1, 1); - - // Don't wait for the server to close streams - setClientGracefulShutdownTime(0); - - // Create a single stream by sending a HEADERS frame to the server. - final Http2Headers headers = dummyHeaders(); - runInChannel(clientChannel, () -> { - http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, - true); - http2Client.flush(ctx()); - }); - - assertTrue(serverSettingsAckLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); - - runInChannel(clientChannel, () -> { - http2Client.encoder().writeHeaders(ctx(), MAX_VALUE + 1, headers, 0, (short) 16, false, 0, - true); - http2Client.flush(ctx()); - }); - - assertTrue(goAwayLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); - verify(serverListener).onGoAwayRead(any(ChannelHandlerContext.class), eq(0), - 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((Answer) invocationOnMock -> { - 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, () -> { - http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, - false); - 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, () -> { - http2Server.encoder().writeGoAway(serverCtx(), 3, NO_ERROR.code(), EMPTY_BUFFER); - 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, () -> { - Future f = http2Client.encoder().writeHeaders(ctx(), 5, headers, 0, (short) 16, false, 0, - true); - clientWriteAfterGoAwayFutureRef.set(f); - http2Client.flush(ctx()); - f.addListener(future -> clientWriteAfterGoAwayLatch.countDown()); - }); - - // Wait for the client's write operation to complete. - assertTrue(clientWriteAfterGoAwayLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); - - Future 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 listenerIsNotifiedOfGoawayBeforeStreamsAreRemovedFromTheConnection() throws Exception { - bootstrapEnv(1, 1, 2, 1, 1); - - // We want both sides to do graceful shutdown during the test. - setClientGracefulShutdownTime(10000); - setServerGracefulShutdownTime(10000); - - final AtomicReference clientStream3State = new AtomicReference(); - final CountDownLatch clientGoAwayLatch = new CountDownLatch(1); - doAnswer(new Answer() { - @Override - public Void answer(InvocationOnMock invocationOnMock) throws Throwable { - clientStream3State.set(http2Client.connection().stream(3).state()); - 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, () -> { - http2Client.encoder().writeHeaders(ctx(), 1, headers, 0, (short) 16, false, 0, - false); - http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, - false); - 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, () -> { - http2Server.encoder().writeGoAway(serverCtx(), 1, NO_ERROR.code(), EMPTY_BUFFER); - 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(1), eq(NO_ERROR.code()), - any(ByteBuf.class)); - assertEquals(Http2Stream.State.OPEN, clientStream3State.get()); - - // Make sure that stream 3 has been closed which is true if it's gone. - final CountDownLatch probeStreamCount = new CountDownLatch(1); - final AtomicBoolean stream3Exists = new AtomicBoolean(); - final AtomicInteger streamCount = new AtomicInteger(); - runInChannel(clientChannel, () -> { - stream3Exists.set(http2Client.connection().stream(3) != null); - streamCount.set(http2Client.connection().numActiveStreams()); - probeStreamCount.countDown(); - }); - // The stream should be closed right after - assertTrue(probeStreamCount.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); - assertEquals(1, streamCount.get()); - assertFalse(stream3Exists.get()); - - // 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(); - - // Create a large message to send. - final int length = 10485760; // 10MB - - // Create a buffer filled with random bytes. - final ByteBuf data = randomBytes(length); - final ByteArrayOutputStream out = new ByteArrayOutputStream(length); - doAnswer((Answer) in -> { - ByteBuf buf = (ByteBuf) in.getArguments()[2]; - int padding = (Integer) in.getArguments()[3]; - int processedBytes = buf.readableBytes() + padding; - - buf.readBytes(out, buf.readableBytes()); - return processedBytes; - }).when(serverListener).onDataRead(any(ChannelHandlerContext.class), eq(3), - any(ByteBuf.class), eq(0), anyBoolean()); - try { - // Initialize the data latch based on the number of bytes expected. - bootstrapEnv(length, 1, 2, 1); - - // Create the stream and send all of the data at once. - runInChannel(clientChannel, () -> { - http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, - false); - http2Client.encoder().writeData(ctx(), 3, data.retainedDuplicate(), 0, false); - - // Write trailers. - http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, - true); - http2Client.flush(ctx()); - }); - - // Wait for the trailers to be received. - assertTrue(serverSettingsAckLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); - assertTrue(trailersLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); - - // Verify that headers and trailers were received. - verify(serverListener).onHeadersRead(any(ChannelHandlerContext.class), eq(3), eq(headers), eq(0), - eq((short) 16), eq(false), eq(0), eq(false)); - verify(serverListener).onHeadersRead(any(ChannelHandlerContext.class), eq(3), eq(headers), eq(0), - eq((short) 16), eq(false), eq(0), eq(true)); - - // Verify we received all the bytes. - assertEquals(0, dataLatch.getCount()); - out.flush(); - byte[] received = out.toByteArray(); - assertArrayEquals(data.array(), received); - } finally { - // Don't wait for server to close streams - setClientGracefulShutdownTime(0); - data.release(); - out.close(); - } - } - - @Test - public void stressTest() throws Exception { - final Http2Headers headers = dummyHeaders(); - int length = 10; - final ByteBuf data = randomBytes(length); - final String dataAsHex = ByteBufUtil.hexDump(data); - final long pingData = 8; - final int numStreams = 2000; - - // Collect all the ping buffers as we receive them at the server. - final long[] receivedPings = new long[numStreams]; - doAnswer(new Answer() { - int nextIndex; - - @Override - public Void answer(InvocationOnMock in) throws Throwable { - receivedPings[nextIndex++] = (Long) in.getArguments()[1]; - return null; - } - }).when(serverListener).onPingRead(any(ChannelHandlerContext.class), any(Long.class)); - - // Collect all the data buffers as we receive them at the server. - final StringBuilder[] receivedData = new StringBuilder[numStreams]; - doAnswer((Answer) in -> { - int streamId = (Integer) in.getArguments()[1]; - ByteBuf buf = (ByteBuf) in.getArguments()[2]; - int padding = (Integer) in.getArguments()[3]; - int processedBytes = buf.readableBytes() + padding; - - int streamIndex = (streamId - 3) / 2; - StringBuilder builder = receivedData[streamIndex]; - if (builder == null) { - builder = new StringBuilder(dataAsHex.length()); - receivedData[streamIndex] = builder; - } - builder.append(ByteBufUtil.hexDump(buf)); - return processedBytes; - }).when(serverListener).onDataRead(any(ChannelHandlerContext.class), anyInt(), - any(ByteBuf.class), anyInt(), anyBoolean()); - try { - bootstrapEnv(numStreams * length, 1, numStreams * 4, numStreams); - runInChannel(clientChannel, () -> { - int upperLimit = 3 + 2 * numStreams; - for (int streamId = 3; streamId < upperLimit; streamId += 2) { - // Send a bunch of data on each stream. - http2Client.encoder().writeHeaders(ctx(), streamId, headers, 0, (short) 16, - false, 0, false); - http2Client.encoder().writePing(ctx(), false, pingData); - http2Client.encoder().writeData(ctx(), streamId, data.retainedSlice(), 0, - false); - // Write trailers. - http2Client.encoder().writeHeaders(ctx(), streamId, headers, 0, (short) 16, - false, 0, true); - http2Client.flush(ctx()); - } - }); - // Wait for all frames to be received. - assertTrue(serverSettingsAckLatch.await(60, SECONDS)); - assertTrue(trailersLatch.await(60, SECONDS)); - verify(serverListener, times(numStreams)).onHeadersRead(any(ChannelHandlerContext.class), anyInt(), - eq(headers), eq(0), eq((short) 16), eq(false), eq(0), eq(false)); - verify(serverListener, times(numStreams)).onHeadersRead(any(ChannelHandlerContext.class), anyInt(), - eq(headers), eq(0), eq((short) 16), eq(false), eq(0), eq(true)); - verify(serverListener, times(numStreams)).onPingRead(any(ChannelHandlerContext.class), - any(long.class)); - verify(serverListener, never()).onDataRead(any(ChannelHandlerContext.class), - anyInt(), any(ByteBuf.class), eq(0), eq(true)); - for (StringBuilder builder : receivedData) { - assertEquals(dataAsHex, builder.toString()); - } - for (long receivedPing : receivedPings) { - assertEquals(pingData, receivedPing); - } - } finally { - // Don't wait for server to close streams - setClientGracefulShutdownTime(0); - data.release(); - } - } - - private void bootstrapEnv(int dataCountDown, int settingsAckCount, - int requestCountDown, int trailersCountDown) throws Exception { - bootstrapEnv(dataCountDown, settingsAckCount, requestCountDown, trailersCountDown, -1); - } - - private void bootstrapEnv(int dataCountDown, int settingsAckCount, - int requestCountDown, int trailersCountDown, int goAwayCountDown) throws Exception { - final CountDownLatch prefaceWrittenLatch = new CountDownLatch(1); - requestLatch = new CountDownLatch(requestCountDown); - serverSettingsAckLatch = new CountDownLatch(settingsAckCount); - dataLatch = new CountDownLatch(dataCountDown); - trailersLatch = new CountDownLatch(trailersCountDown); - goAwayLatch = goAwayCountDown > 0 ? new CountDownLatch(goAwayCountDown) : requestLatch; - sb = new ServerBootstrap(); - cb = new Bootstrap(); - - final AtomicReference serverHandlerRef = new AtomicReference<>(); - final CountDownLatch serverInitLatch = new CountDownLatch(1); - sb.group(new MultithreadEventLoopGroup(LocalHandler.newFactory())); - sb.channel(LocalServerChannel.class); - sb.childHandler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - serverConnectedChannel = ch; - ChannelPipeline p = ch.pipeline(); - serverFrameCountDown = - new FrameCountDown(serverListener, serverSettingsAckLatch, - requestLatch, dataLatch, trailersLatch, goAwayLatch); - serverHandlerRef.set(new Http2ConnectionHandlerBuilder() - .server(true) - .frameListener(serverFrameCountDown) - .validateHeaders(false) - .build()); - p.addLast(serverHandlerRef.get()); - serverInitLatch.countDown(); - } - }); - - cb.group(new MultithreadEventLoopGroup(LocalHandler.newFactory())); - cb.channel(LocalChannel.class); - cb.handler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - ChannelPipeline p = ch.pipeline(); - p.addLast(new Http2ConnectionHandlerBuilder() - .server(false) - .frameListener(clientListener) - .validateHeaders(false) - .gracefulShutdownTimeoutMillis(0) - .build()); - p.addLast(new ChannelHandler() { - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (evt == Http2ConnectionPrefaceAndSettingsFrameWrittenEvent.INSTANCE) { - prefaceWrittenLatch.countDown(); - ctx.pipeline().remove(this); - } - } - }); - } - }); - - serverChannel = sb.bind(new LocalAddress("Http2ConnectionRoundtripTest")).get(); - - clientChannel = cb.connect(serverChannel.localAddress()).get(); - assertTrue(prefaceWrittenLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); - http2Client = clientChannel.pipeline().get(Http2ConnectionHandler.class); - assertTrue(serverInitLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); - http2Server = serverHandlerRef.get(); - } - - private ChannelHandlerContext ctx() { - return clientChannel.pipeline().firstContext(); - } - - private ChannelHandlerContext serverCtx() { - return serverConnectedChannel.pipeline().firstContext(); - } - - private Promise newPromise() { - return ctx().newPromise(); - } - - private static Http2Headers dummyHeaders() { - return new DefaultHttp2Headers(false).method(new AsciiString("GET")).scheme(new AsciiString("https")) - .authority(new AsciiString("example.org")).path(new AsciiString("/some/path/resource2")) - .add(randomString(), randomString()); - } - - private static void mockFlowControl(Http2FrameListener listener) throws Http2Exception { - doAnswer((Answer) invocation -> { - ByteBuf buf = (ByteBuf) invocation.getArguments()[2]; - int padding = (Integer) invocation.getArguments()[3]; - return buf.readableBytes() + padding; - }).when(listener).onDataRead(any(ChannelHandlerContext.class), anyInt(), - 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, () -> { - 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. - */ - private static ByteBuf randomBytes(int length) { - final byte[] bytes = new byte[length]; - new Random().nextBytes(bytes); - return Unpooled.wrappedBuffer(bytes); - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ControlFrameLimitEncoderTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ControlFrameLimitEncoderTest.java deleted file mode 100644 index 512652c796..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ControlFrameLimitEncoderTest.java +++ /dev/null @@ -1,268 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty.channel.Channel; -import io.netty.channel.ChannelConfig; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelMetadata; -import io.netty.channel.DefaultMessageSizeEstimator; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.ImmediateEventExecutor; -import io.netty.util.concurrent.Promise; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.stubbing.Answer; - -import java.util.ArrayDeque; -import java.util.Queue; - -import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_MAX_FRAME_SIZE; -import static io.netty.handler.codec.http2.Http2Error.CANCEL; -import static io.netty.handler.codec.http2.Http2Error.ENHANCE_YOUR_CALM; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyBoolean; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.anyLong; -import static org.mockito.Mockito.atLeast; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -/** - * Tests for {@link Http2ControlFrameLimitEncoder}. - */ -public class Http2ControlFrameLimitEncoderTest { - - private Http2ControlFrameLimitEncoder encoder; - - @Mock - private Http2FrameWriter writer; - - @Mock - private ChannelHandlerContext ctx; - - @Mock - private Channel channel; - - @Mock - private Channel.Unsafe unsafe; - - @Mock - private ChannelConfig config; - - @Mock - private EventExecutor executor; - - private int numWrites; - - private final Queue> goAwayPromises = new ArrayDeque>(); - - /** - * Init fields and do mocking. - */ - @BeforeEach - public void setup() throws Exception { - MockitoAnnotations.initMocks(this); - - numWrites = 0; - - Http2FrameWriter.Configuration configuration = mock(Http2FrameWriter.Configuration.class); - Http2FrameSizePolicy frameSizePolicy = mock(Http2FrameSizePolicy.class); - when(writer.configuration()).thenReturn(configuration); - when(configuration.frameSizePolicy()).thenReturn(frameSizePolicy); - when(frameSizePolicy.maxFrameSize()).thenReturn(DEFAULT_MAX_FRAME_SIZE); - - when(writer.writeRstStream(eq(ctx), anyInt(), anyLong())) - .thenAnswer((Answer>) invocationOnMock -> handlePromise().asFuture()); - when(writer.writeSettingsAck(any(ChannelHandlerContext.class))) - .thenAnswer((Answer>) invocationOnMock -> handlePromise().asFuture()); - when(writer.writePing(any(ChannelHandlerContext.class), anyBoolean(), anyLong())) - .thenAnswer((Answer>) invocationOnMock -> { - Promise promise = handlePromise(); - if (invocationOnMock.getArgument(1) == Boolean.FALSE) { - promise.trySuccess(null); - } - return promise.asFuture(); - }); - when(writer.writeGoAway(any(ChannelHandlerContext.class), anyInt(), anyLong(), any(ByteBuf.class))) - .thenAnswer((Answer>) invocationOnMock -> { - ReferenceCountUtil.release(invocationOnMock.getArgument(3)); - Promise promise = ImmediateEventExecutor.INSTANCE.newPromise(); - goAwayPromises.offer(promise); - return promise.asFuture(); - }); - Http2Connection connection = new DefaultHttp2Connection(false); - connection.remote().flowController(new DefaultHttp2RemoteFlowController(connection)); - connection.local().flowController(new DefaultHttp2LocalFlowController(connection).frameWriter(writer)); - - DefaultHttp2ConnectionEncoder defaultEncoder = - new DefaultHttp2ConnectionEncoder(connection, writer); - encoder = new Http2ControlFrameLimitEncoder(defaultEncoder, 2); - DefaultHttp2ConnectionDecoder decoder = - new DefaultHttp2ConnectionDecoder(connection, encoder, mock(Http2FrameReader.class)); - Http2ConnectionHandler handler = new Http2ConnectionHandlerBuilder() - .frameListener(mock(Http2FrameListener.class)) - .codec(decoder, encoder).build(); - - // Set LifeCycleManager on encoder and decoder - when(ctx.channel()).thenReturn(channel); - when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT); - when(channel.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT); - when(executor.inEventLoop()).thenReturn(true); - doAnswer((Answer>) invocation -> newPromise()).when(ctx).newPromise(); - doAnswer((Answer>) invocation -> - ImmediateEventExecutor.INSTANCE.newFailedFuture(invocation.getArgument(0))) - .when(ctx).newFailedFuture(any(Throwable.class)); - - when(ctx.executor()).thenReturn(executor); - when(ctx.close()).thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null)); - when(channel.isActive()).thenReturn(false); - when(channel.config()).thenReturn(config); - when(channel.isWritable()).thenReturn(true); - when(channel.bytesBeforeUnwritable()).thenReturn(Long.MAX_VALUE); - when(config.getWriteBufferHighWaterMark()).thenReturn(Integer.MAX_VALUE); - when(config.getMessageSizeEstimator()).thenReturn(DefaultMessageSizeEstimator.DEFAULT); - ChannelMetadata metadata = new ChannelMetadata(false, 16); - when(channel.metadata()).thenReturn(metadata); - when(channel.unsafe()).thenReturn(unsafe); - handler.handlerAdded(ctx); - } - - private Promise handlePromise() { - Promise p = ImmediateEventExecutor.INSTANCE.newPromise(); - if (++numWrites == 2) { - p.setSuccess(null); - } - return p; - } - - @AfterEach - public void tearDown() { - // Close and release any buffered frames. - encoder.close(); - - // Notify all goAway Promise instances now as these will also release the retained ByteBuf for the - // debugData. - for (;;) { - Promise promise = goAwayPromises.poll(); - if (promise == null) { - break; - } - promise.setSuccess(null); - } - } - - @Test - public void testLimitSettingsAck() { - assertFalse(encoder.writeSettingsAck(ctx).isDone()); - // The second write is always marked as success by our mock, which means it will also not be queued and so - // not count to the number of queued frames. - assertTrue(encoder.writeSettingsAck(ctx).isSuccess()); - assertFalse(encoder.writeSettingsAck(ctx).isDone()); - - verifyFlushAndClose(0, false); - - assertFalse(encoder.writeSettingsAck(ctx).isDone()); - assertFalse(encoder.writeSettingsAck(ctx).isDone()); - - verifyFlushAndClose(1, true); - } - - @Test - public void testLimitPingAck() { - assertFalse(encoder.writePing(ctx, true, 8).isDone()); - // The second write is always marked as success by our mock, which means it will also not be queued and so - // not count to the number of queued frames. - assertTrue(encoder.writePing(ctx, true, 8).isSuccess()); - assertFalse(encoder.writePing(ctx, true, 8).isDone()); - - verifyFlushAndClose(0, false); - - assertFalse(encoder.writePing(ctx, true, 8).isDone()); - assertFalse(encoder.writePing(ctx, true, 8).isDone()); - - verifyFlushAndClose(1, true); - } - - @Test - public void testNotLimitPing() { - assertTrue(encoder.writePing(ctx, false, 8).isSuccess()); - assertTrue(encoder.writePing(ctx, false, 8).isSuccess()); - assertTrue(encoder.writePing(ctx, false, 8).isSuccess()); - assertTrue(encoder.writePing(ctx, false, 8).isSuccess()); - - verifyFlushAndClose(0, false); - } - - @Test - public void testLimitRst() { - assertFalse(encoder.writeRstStream(ctx, 1, CANCEL.code()).isDone()); - // The second write is always marked as success by our mock, which means it will also not be queued and so - // not count to the number of queued frames. - assertTrue(encoder.writeRstStream(ctx, 1, CANCEL.code()).isSuccess()); - assertFalse(encoder.writeRstStream(ctx, 1, CANCEL.code()).isDone()); - - verifyFlushAndClose(0, false); - - assertFalse(encoder.writeRstStream(ctx, 1, CANCEL.code()).isDone()); - assertFalse(encoder.writeRstStream(ctx, 1, CANCEL.code()).isDone()); - - verifyFlushAndClose(1, true); - } - - @Test - public void testLimit() { - assertFalse(encoder.writeRstStream(ctx, 1, CANCEL.code()).isDone()); - // The second write is always marked as success by our mock, which means it will also not be queued and so - // not count to the number of queued frames. - assertTrue(encoder.writePing(ctx, false, 8).isSuccess()); - assertFalse(encoder.writePing(ctx, true, 8).isSuccess()); - - verifyFlushAndClose(0, false); - - assertFalse(encoder.writeSettingsAck(ctx).isDone()); - assertFalse(encoder.writeRstStream(ctx, 1, CANCEL.code()).isDone()); - assertFalse(encoder.writePing(ctx, true, 8).isSuccess()); - - verifyFlushAndClose(1, true); - } - - private void verifyFlushAndClose(int invocations, boolean failed) { - verify(ctx, atLeast(invocations)).flush(); - verify(ctx, times(invocations)).close(); - if (failed) { - verify(writer, times(1)).writeGoAway(eq(ctx), eq(Integer.MAX_VALUE), eq(ENHANCE_YOUR_CALM.code()), - any(ByteBuf.class)); - } - } - - private static Promise newPromise() { - return ImmediateEventExecutor.INSTANCE.newPromise(); - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2DefaultFramesTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2DefaultFramesTest.java deleted file mode 100644 index aeb3c24337..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2DefaultFramesTest.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.buffer.DefaultByteBufHolder; -import io.netty.buffer.Unpooled; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertFalse; - -public class Http2DefaultFramesTest { - - @SuppressWarnings("SimplifiableJUnitAssertion") - @Test - public void testEqualOperation() { - // in this case, 'goAwayFrame' and 'unknownFrame' will also have an EMPTY_BUFFER data - // so we want to check that 'dflt' will not consider them equal. - DefaultHttp2GoAwayFrame goAwayFrame = new DefaultHttp2GoAwayFrame(1); - DefaultHttp2UnknownFrame unknownFrame = new DefaultHttp2UnknownFrame((byte) 1, new Http2Flags((short) 1)); - DefaultByteBufHolder dflt = new DefaultByteBufHolder(Unpooled.EMPTY_BUFFER); - try { - // not using 'assertNotEquals' to be explicit about which object we are calling .equals() on - assertFalse(dflt.equals(goAwayFrame)); - assertFalse(dflt.equals(unknownFrame)); - } finally { - goAwayFrame.release(); - unknownFrame.release(); - dflt.release(); - } - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2EmptyDataFrameConnectionDecoderTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2EmptyDataFrameConnectionDecoderTest.java deleted file mode 100644 index 7da9f8927e..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2EmptyDataFrameConnectionDecoderTest.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import org.hamcrest.CoreMatchers; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.stubbing.Answer; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -public class Http2EmptyDataFrameConnectionDecoderTest { - - @Test - public void testDecoration() { - Http2ConnectionDecoder delegate = mock(Http2ConnectionDecoder.class); - final ArgumentCaptor listenerArgumentCaptor = - ArgumentCaptor.forClass(Http2FrameListener.class); - when(delegate.frameListener()).then( - (Answer) invocationOnMock -> listenerArgumentCaptor.getValue()); - Http2FrameListener listener = mock(Http2FrameListener.class); - Http2EmptyDataFrameConnectionDecoder decoder = new Http2EmptyDataFrameConnectionDecoder(delegate, 2); - decoder.frameListener(listener); - verify(delegate).frameListener(listenerArgumentCaptor.capture()); - - assertThat(decoder.frameListener(), - CoreMatchers.not(CoreMatchers.instanceOf(Http2EmptyDataFrameListener.class))); - assertThat(decoder.frameListener0(), CoreMatchers.instanceOf(Http2EmptyDataFrameListener.class)); - } - - @Test - public void testDecorationWithNull() { - Http2ConnectionDecoder delegate = mock(Http2ConnectionDecoder.class); - - Http2EmptyDataFrameConnectionDecoder decoder = new Http2EmptyDataFrameConnectionDecoder(delegate, 2); - decoder.frameListener(null); - assertNull(decoder.frameListener()); - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2EmptyDataFrameListenerTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2EmptyDataFrameListenerTest.java deleted file mode 100644 index 9d1adf599d..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2EmptyDataFrameListenerTest.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandlerContext; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; -import org.mockito.Mock; - -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.initMocks; - -public class Http2EmptyDataFrameListenerTest { - - @Mock - private Http2FrameListener frameListener; - @Mock - private ChannelHandlerContext ctx; - - @Mock - private ByteBuf nonEmpty; - - private Http2EmptyDataFrameListener listener; - - @BeforeEach - public void setUp() { - initMocks(this); - when(nonEmpty.isReadable()).thenReturn(true); - listener = new Http2EmptyDataFrameListener(frameListener, 2); - } - - @Test - public void testEmptyDataFrames() throws Http2Exception { - listener.onDataRead(ctx, 1, Unpooled.EMPTY_BUFFER, 0, false); - listener.onDataRead(ctx, 1, Unpooled.EMPTY_BUFFER, 0, false); - - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - listener.onDataRead(ctx, 1, Unpooled.EMPTY_BUFFER, 0, false); - } - }); - verify(frameListener, times(2)).onDataRead(eq(ctx), eq(1), any(ByteBuf.class), eq(0), eq(false)); - } - - @Test - public void testEmptyDataFramesWithNonEmptyInBetween() throws Http2Exception { - final Http2EmptyDataFrameListener listener = new Http2EmptyDataFrameListener(frameListener, 2); - listener.onDataRead(ctx, 1, Unpooled.EMPTY_BUFFER, 0, false); - listener.onDataRead(ctx, 1, nonEmpty, 0, false); - - listener.onDataRead(ctx, 1, Unpooled.EMPTY_BUFFER, 0, false); - listener.onDataRead(ctx, 1, Unpooled.EMPTY_BUFFER, 0, false); - - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - listener.onDataRead(ctx, 1, Unpooled.EMPTY_BUFFER, 0, false); - } - }); - verify(frameListener, times(4)).onDataRead(eq(ctx), eq(1), any(ByteBuf.class), eq(0), eq(false)); - } - - @Test - public void testEmptyDataFramesWithEndOfStreamInBetween() throws Http2Exception { - final Http2EmptyDataFrameListener listener = new Http2EmptyDataFrameListener(frameListener, 2); - listener.onDataRead(ctx, 1, Unpooled.EMPTY_BUFFER, 0, false); - listener.onDataRead(ctx, 1, Unpooled.EMPTY_BUFFER, 0, true); - - listener.onDataRead(ctx, 1, Unpooled.EMPTY_BUFFER, 0, false); - listener.onDataRead(ctx, 1, Unpooled.EMPTY_BUFFER, 0, false); - - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - listener.onDataRead(ctx, 1, Unpooled.EMPTY_BUFFER, 0, false); - } - }); - - verify(frameListener, times(1)).onDataRead(eq(ctx), eq(1), any(ByteBuf.class), eq(0), eq(true)); - verify(frameListener, times(3)).onDataRead(eq(ctx), eq(1), any(ByteBuf.class), eq(0), eq(false)); - } - - @Test - public void testEmptyDataFramesWithHeaderFrameInBetween() throws Http2Exception { - final Http2EmptyDataFrameListener listener = new Http2EmptyDataFrameListener(frameListener, 2); - listener.onDataRead(ctx, 1, Unpooled.EMPTY_BUFFER, 0, false); - listener.onHeadersRead(ctx, 1, EmptyHttp2Headers.INSTANCE, 0, true); - - listener.onDataRead(ctx, 1, Unpooled.EMPTY_BUFFER, 0, false); - listener.onDataRead(ctx, 1, Unpooled.EMPTY_BUFFER, 0, false); - - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - listener.onDataRead(ctx, 1, Unpooled.EMPTY_BUFFER, 0, false); - } - }); - - verify(frameListener, times(1)).onHeadersRead(eq(ctx), eq(1), eq(EmptyHttp2Headers.INSTANCE), eq(0), eq(true)); - verify(frameListener, times(3)).onDataRead(eq(ctx), eq(1), any(ByteBuf.class), eq(0), eq(false)); - } - - @Test - public void testEmptyDataFramesWithHeaderFrameInBetween2() throws Http2Exception { - final Http2EmptyDataFrameListener listener = new Http2EmptyDataFrameListener(frameListener, 2); - listener.onDataRead(ctx, 1, Unpooled.EMPTY_BUFFER, 0, false); - listener.onHeadersRead(ctx, 1, EmptyHttp2Headers.INSTANCE, 0, (short) 0, false, 0, true); - - listener.onDataRead(ctx, 1, Unpooled.EMPTY_BUFFER, 0, false); - listener.onDataRead(ctx, 1, Unpooled.EMPTY_BUFFER, 0, false); - - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - listener.onDataRead(ctx, 1, Unpooled.EMPTY_BUFFER, 0, false); - } - }); - - verify(frameListener, times(1)).onHeadersRead(eq(ctx), eq(1), - eq(EmptyHttp2Headers.INSTANCE), eq(0), eq((short) 0), eq(false), eq(0), eq(true)); - verify(frameListener, times(3)).onDataRead(eq(ctx), eq(1), any(ByteBuf.class), eq(0), eq(false)); - } -} 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 deleted file mode 100644 index 31b729a789..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2FrameCodecTest.java +++ /dev/null @@ -1,957 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.UnsupportedMessageTypeException; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpScheme; -import io.netty.handler.codec.http.HttpServerUpgradeHandler; -import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeEvent; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http2.Http2Exception.StreamException; -import io.netty.handler.codec.http2.Http2Stream.State; -import io.netty.handler.logging.LogLevel; -import io.netty.util.AbstractReferenceCounted; -import io.netty.util.AsciiString; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.ReferenceCounted; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.GlobalEventExecutor; -import io.netty.util.concurrent.Promise; -import io.netty.util.internal.ReflectionUtil; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assumptions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; -import org.junit.jupiter.api.function.Executable; -import org.mockito.ArgumentCaptor; - -import java.lang.reflect.Constructor; -import java.net.InetSocketAddress; -import java.util.HashSet; -import java.util.Set; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; - -import static io.netty.handler.codec.http2.Http2CodecUtil.isStreamIdValid; -import static io.netty.handler.codec.http2.Http2Error.NO_ERROR; -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.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.instanceOf; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -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.never; -import static org.mockito.Mockito.same; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; - -/** - * Unit tests for {@link Http2FrameCodec}. - */ -public class Http2FrameCodecTest { - - // For verifying outbound frames - private Http2FrameWriter frameWriter; - private Http2FrameCodec frameCodec; - private EmbeddedChannel channel; - - // For injecting inbound frames - private Http2FrameInboundWriter frameInboundWriter; - - private LastInboundHandler inboundHandler; - - private final Http2Headers request = new DefaultHttp2Headers() - .method(HttpMethod.GET.asciiName()).scheme(HttpScheme.HTTPS.name()) - .authority(new AsciiString("example.org")).path(new AsciiString("/foo")); - private final Http2Headers response = new DefaultHttp2Headers() - .status(HttpResponseStatus.OK.codeAsText()); - - @BeforeEach - public void setUp() throws Exception { - setUp(Http2FrameCodecBuilder.forServer(), new Http2Settings()); - } - - @AfterEach - public void tearDown() throws Exception { - if (inboundHandler != null) { - inboundHandler.finishAndReleaseAll(); - inboundHandler = null; - } - if (channel != null) { - channel.finishAndReleaseAll(); - channel.close(); - channel = null; - } - } - - private void setUp(Http2FrameCodecBuilder frameCodecBuilder, Http2Settings initialRemoteSettings) throws Exception { - /* - Some tests call this method twice. Once with JUnit's @Before and once directly to pass special settings. - This call ensures that in case of two consecutive calls to setUp(), the previous channel is shutdown and - ByteBufs are released correctly. - */ - tearDown(); - - frameWriter = Http2TestUtil.mockedFrameWriter(); - - frameCodec = frameCodecBuilder.frameWriter(frameWriter).frameLogger(new Http2FrameLogger(LogLevel.TRACE)) - .initialSettings(initialRemoteSettings).build(); - 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(); - - // Handshake - verify(frameWriter).writeSettings(any(ChannelHandlerContext.class), anyHttp2Settings()); - verifyNoMoreInteractions(frameWriter); - channel.writeInbound(Http2CodecUtil.connectionPrefaceBuf()); - - frameInboundWriter.writeInboundSettings(initialRemoteSettings); - - verify(frameWriter).writeSettingsAck(any(ChannelHandlerContext.class)); - - frameInboundWriter.writeInboundSettingsAck(); - - Http2SettingsFrame settingsFrame = inboundHandler.readInbound(); - assertNotNull(settingsFrame); - Http2SettingsAckFrame settingsAckFrame = inboundHandler.readInbound(); - assertNotNull(settingsAckFrame); - } - - @Test - public void stateChanges() throws Exception { - frameInboundWriter.writeInboundHeaders(1, request, 31, true); - - Http2Stream stream = frameCodec.connection().stream(1); - assertNotNull(stream); - assertEquals(State.HALF_CLOSED_REMOTE, stream.state()); - - Http2FrameStreamEvent event = inboundHandler.readInboundMessageOrUserEvent(); - assertEquals(State.HALF_CLOSED_REMOTE, event.stream().state()); - - Http2StreamFrame inboundFrame = inboundHandler.readInbound(); - Http2FrameStream stream2 = inboundFrame.stream(); - assertNotNull(stream2); - assertEquals(1, stream2.id()); - assertEquals(inboundFrame, new DefaultHttp2HeadersFrame(request, true, 31).stream(stream2)); - assertNull(inboundHandler.readInbound()); - - channel.writeOutbound(new DefaultHttp2HeadersFrame(response, true, 27).stream(stream2)); - verify(frameWriter).writeHeaders( - any(ChannelHandlerContext.class), eq(1), eq(response), - eq(27), eq(true)); - verify(frameWriter, never()).writeRstStream( - any(ChannelHandlerContext.class), anyInt(), anyLong()); - - assertEquals(State.CLOSED, stream.state()); - event = inboundHandler.readInboundMessageOrUserEvent(); - assertEquals(State.CLOSED, event.stream().state()); - - assertTrue(channel.isActive()); - } - - @Test - public void headerRequestHeaderResponse() throws Exception { - frameInboundWriter.writeInboundHeaders(1, request, 31, true); - - Http2Stream stream = frameCodec.connection().stream(1); - assertNotNull(stream); - assertEquals(State.HALF_CLOSED_REMOTE, stream.state()); - - Http2StreamFrame inboundFrame = inboundHandler.readInbound(); - Http2FrameStream stream2 = inboundFrame.stream(); - assertNotNull(stream2); - assertEquals(1, stream2.id()); - assertEquals(inboundFrame, new DefaultHttp2HeadersFrame(request, true, 31).stream(stream2)); - assertNull(inboundHandler.readInbound()); - - channel.writeOutbound(new DefaultHttp2HeadersFrame(response, true, 27).stream(stream2)); - verify(frameWriter).writeHeaders( - any(ChannelHandlerContext.class), eq(1), eq(response), - eq(27), eq(true)); - verify(frameWriter, never()).writeRstStream( - any(ChannelHandlerContext.class), anyInt(), anyLong()); - - 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(), false); - EmbeddedChannel em = new EmbeddedChannel(codec); - - AtomicReference errorRef = new AtomicReference<>(); - em.executor().execute(() -> { - try { - // 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)); - } catch (Http2Exception e) { - errorRef.set(e); - } - }); - - assertTrue(em.finishAndReleaseAll()); - Http2Exception exception = errorRef.get(); - if (exception != null) { - throw exception; - } - } - - @Test - public void entityRequestEntityResponse() throws Exception { - frameInboundWriter.writeInboundHeaders(1, request, 0, false); - - Http2Stream stream = frameCodec.connection().stream(1); - assertNotNull(stream); - assertEquals(State.OPEN, stream.state()); - - Http2HeadersFrame inboundHeaders = inboundHandler.readInbound(); - Http2FrameStream stream2 = inboundHeaders.stream(); - assertNotNull(stream2); - assertEquals(1, stream2.id()); - assertEquals(new DefaultHttp2HeadersFrame(request, false).stream(stream2), inboundHeaders); - assertNull(inboundHandler.readInbound()); - - ByteBuf hello = bb("hello"); - frameInboundWriter.writeInboundData(1, hello, 31, true); - Http2DataFrame inboundData = inboundHandler.readInbound(); - Http2DataFrame expected = new DefaultHttp2DataFrame(bb("hello"), true, 31).stream(stream2); - assertEqualsAndRelease(expected, inboundData); - - assertNull(inboundHandler.readInbound()); - - inboundHandler.writeOutbound(new DefaultHttp2HeadersFrame(response, false).stream(stream2)); - verify(frameWriter).writeHeaders(any(ChannelHandlerContext.class), eq(1), eq(response), eq(0), - eq(false)); - - channel.writeOutbound(new DefaultHttp2DataFrame(bb("world"), true, 27).stream(stream2)); - ArgumentCaptor outboundData = ArgumentCaptor.forClass(ByteBuf.class); - verify(frameWriter).writeData(any(ChannelHandlerContext.class), eq(1), outboundData.capture(), eq(27), - eq(true)); - - ByteBuf bb = bb("world"); - assertEquals(bb, outboundData.getValue()); - assertEquals(1, outboundData.getValue().refCnt()); - bb.release(); - outboundData.getValue().release(); - - verify(frameWriter, never()).writeRstStream(any(ChannelHandlerContext.class), - anyInt(), anyLong()); - assertTrue(channel.isActive()); - } - - @Test - public void sendRstStream() throws Exception { - frameInboundWriter.writeInboundHeaders(3, request, 31, true); - - Http2Stream stream = frameCodec.connection().stream(3); - assertNotNull(stream); - assertEquals(State.HALF_CLOSED_REMOTE, stream.state()); - - Http2HeadersFrame inboundHeaders = inboundHandler.readInbound(); - assertNotNull(inboundHeaders); - assertTrue(inboundHeaders.isEndStream()); - - Http2FrameStream stream2 = inboundHeaders.stream(); - assertNotNull(stream2); - assertEquals(3, stream2.id()); - - channel.writeOutbound(new DefaultHttp2ResetFrame(314 /* non-standard error */).stream(stream2)); - verify(frameWriter).writeRstStream(any(ChannelHandlerContext.class), eq(3), eq(314L)); - assertEquals(State.CLOSED, stream.state()); - assertTrue(channel.isActive()); - } - - @Test - public void receiveRstStream() throws Exception { - frameInboundWriter.writeInboundHeaders(3, request, 31, false); - - Http2Stream stream = frameCodec.connection().stream(3); - assertNotNull(stream); - assertEquals(State.OPEN, stream.state()); - - Http2HeadersFrame expectedHeaders = new DefaultHttp2HeadersFrame(request, false, 31); - Http2HeadersFrame actualHeaders = inboundHandler.readInbound(); - assertEquals(expectedHeaders.stream(actualHeaders.stream()), actualHeaders); - - frameInboundWriter.writeInboundRstStream(3, NO_ERROR.code()); - - Http2ResetFrame expectedRst = new DefaultHttp2ResetFrame(NO_ERROR).stream(actualHeaders.stream()); - Http2ResetFrame actualRst = inboundHandler.readInbound(); - assertEquals(expectedRst, actualRst); - - assertNull(inboundHandler.readInbound()); - } - - @Test - public void sendGoAway() throws Exception { - frameInboundWriter.writeInboundHeaders(3, request, 31, false); - Http2Stream stream = frameCodec.connection().stream(3); - assertNotNull(stream); - assertEquals(State.OPEN, stream.state()); - - ByteBuf debugData = bb("debug"); - ByteBuf expected = debugData.copy(); - - Http2GoAwayFrame goAwayFrame = new DefaultHttp2GoAwayFrame(NO_ERROR.code(), - debugData.retainedDuplicate()); - goAwayFrame.setExtraStreamIds(2); - - channel.writeOutbound(goAwayFrame); - verify(frameWriter).writeGoAway(any(ChannelHandlerContext.class), eq(7), - eq(NO_ERROR.code()), eq(expected)); - assertEquals(State.OPEN, stream.state()); - assertTrue(channel.isActive()); - expected.release(); - debugData.release(); - } - - @Test - public void receiveGoaway() throws Exception { - ByteBuf debugData = bb("foo"); - frameInboundWriter.writeInboundGoAway(2, NO_ERROR.code(), debugData); - Http2GoAwayFrame expectedFrame = new DefaultHttp2GoAwayFrame(2, NO_ERROR.code(), bb("foo")); - Http2GoAwayFrame actualFrame = inboundHandler.readInbound(); - - assertEqualsAndRelease(expectedFrame, actualFrame); - - assertNull(inboundHandler.readInbound()); - } - - @Test - public void unknownFrameTypeShouldThrowAndBeReleased() throws Exception { - class UnknownHttp2Frame extends AbstractReferenceCounted implements Http2Frame { - @Override - public String name() { - return "UNKNOWN"; - } - - @Override - protected void deallocate() { - } - - @Override - public ReferenceCounted touch(Object hint) { - return this; - } - } - - UnknownHttp2Frame frame = new UnknownHttp2Frame(); - assertEquals(1, frame.refCnt()); - - Future f = channel.write(frame); - f.await(); - assertTrue(f.isDone()); - assertFalse(f.isSuccess()); - assertThat(f.cause(), instanceOf(UnsupportedMessageTypeException.class)); - assertEquals(0, frame.refCnt()); - } - - @Test - public void unknownFrameTypeOnConnectionStream() throws Exception { - // handle the case where unknown frames are sent before a stream is created, - // for example: HTTP/2 GREASE testing - ByteBuf debugData = bb("debug"); - frameInboundWriter.writeInboundFrame((byte) 0xb, 0, new Http2Flags(), debugData); - channel.flush(); - - assertEquals(0, debugData.refCnt()); - assertTrue(channel.isActive()); - } - - @Test - public void goAwayLastStreamIdOverflowed() throws Exception { - frameInboundWriter.writeInboundHeaders(5, request, 31, false); - - Http2Stream stream = frameCodec.connection().stream(5); - assertNotNull(stream); - assertEquals(State.OPEN, stream.state()); - - ByteBuf debugData = bb("debug"); - Http2GoAwayFrame goAwayFrame = new DefaultHttp2GoAwayFrame(NO_ERROR.code(), - debugData.retainedDuplicate()); - goAwayFrame.setExtraStreamIds(Integer.MAX_VALUE); - - 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(any(ChannelHandlerContext.class), eq(Integer.MAX_VALUE), - eq(NO_ERROR.code()), eq(debugData)); - debugData.release(); - assertEquals(State.OPEN, stream.state()); - assertTrue(channel.isActive()); - } - - @Test - public void streamErrorShouldFireExceptionForInbound() throws Exception { - frameInboundWriter.writeInboundHeaders(3, request, 31, false); - - Http2Stream stream = frameCodec.connection().stream(3); - assertNotNull(stream); - - StreamException streamEx = new StreamException(3, Http2Error.INTERNAL_ERROR, "foo"); - channel.pipeline().fireExceptionCaught(streamEx); - - Http2FrameStreamEvent event = inboundHandler.readInboundMessageOrUserEvent(); - assertEquals(Http2FrameStreamEvent.Type.State, event.type()); - assertEquals(State.OPEN, event.stream().state()); - Http2HeadersFrame headersFrame = inboundHandler.readInboundMessageOrUserEvent(); - assertNotNull(headersFrame); - - Http2FrameStreamException e = assertThrows(Http2FrameStreamException.class, new Executable() { - @Override - public void execute() throws Throwable { - inboundHandler.checkException(); - } - }); - assertEquals(streamEx, e.getCause()); - - assertNull(inboundHandler.readInboundMessageOrUserEvent()); - } - - @Test - public void streamErrorShouldNotFireExceptionForOutbound() throws Exception { - 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(frameCodec.ctx, true, streamEx); - - Http2FrameStreamEvent event = inboundHandler.readInboundMessageOrUserEvent(); - assertEquals(Http2FrameStreamEvent.Type.State, event.type()); - assertEquals(State.OPEN, event.stream().state()); - Http2HeadersFrame headersFrame = inboundHandler.readInboundMessageOrUserEvent(); - assertNotNull(headersFrame); - - // No exception expected - inboundHandler.checkException(); - - assertNull(inboundHandler.readInboundMessageOrUserEvent()); - } - - @Test - public void windowUpdateFrameDecrementsConsumedBytes() throws Exception { - frameInboundWriter.writeInboundHeaders(3, request, 31, false); - - Http2Connection connection = frameCodec.connection(); - Http2Stream stream = connection.stream(3); - assertNotNull(stream); - - ByteBuf data = Unpooled.buffer(100).writeZero(100); - frameInboundWriter.writeInboundData(3, data, 0, false); - - Http2HeadersFrame inboundHeaders = inboundHandler.readInbound(); - assertNotNull(inboundHeaders); - assertNotNull(inboundHeaders.stream()); - - Http2FrameStream stream2 = inboundHeaders.stream(); - - int before = connection.local().flowController().unconsumedBytes(stream); - Future f = channel.write(new DefaultHttp2WindowUpdateFrame(100).stream(stream2)); - int after = connection.local().flowController().unconsumedBytes(stream); - assertEquals(100, before - after); - assertTrue(f.isSuccess()); - } - - @Test - public void windowUpdateMayFail() throws Exception { - frameInboundWriter.writeInboundHeaders(3, request, 31, false); - Http2Connection connection = frameCodec.connection(); - Http2Stream stream = connection.stream(3); - assertNotNull(stream); - - Http2HeadersFrame inboundHeaders = inboundHandler.readInbound(); - assertNotNull(inboundHeaders); - - Http2FrameStream stream2 = inboundHeaders.stream(); - - // Fails, cause trying to return too many bytes to the flow controller - Future f = channel.write(new DefaultHttp2WindowUpdateFrame(100).stream(stream2)); - assertTrue(f.isDone()); - assertFalse(f.isSuccess()); - assertThat(f.cause(), instanceOf(Http2Exception.class)); - } - - @Test - public void inboundWindowUpdateShouldBeForwarded() throws Exception { - frameInboundWriter.writeInboundHeaders(3, request, 31, false); - frameInboundWriter.writeInboundWindowUpdate(3, 100); - // Connection-level window update - frameInboundWriter.writeInboundWindowUpdate(0, 100); - - Http2HeadersFrame headersFrame = inboundHandler.readInbound(); - assertNotNull(headersFrame); - - Http2WindowUpdateFrame windowUpdateFrame = inboundHandler.readInbound(); - assertNotNull(windowUpdateFrame); - assertEquals(3, windowUpdateFrame.stream().id()); - assertEquals(100, windowUpdateFrame.windowSizeIncrement()); - - // Window update for the connection should not be forwarded. - assertNull(inboundHandler.readInbound()); - } - - @Test - public void streamZeroWindowUpdateIncrementsConnectionWindow() throws Http2Exception { - Http2Connection connection = frameCodec.connection(); - Http2LocalFlowController localFlow = connection.local().flowController(); - int initialWindowSizeBefore = localFlow.initialWindowSize(); - Http2Stream connectionStream = connection.connectionStream(); - int connectionWindowSizeBefore = localFlow.windowSize(connectionStream); - - AtomicReference errorRef = new AtomicReference<>(); - channel.executor().execute(() -> { - try { - // We only replenish the flow control window after the amount consumed drops below the following - // threshold. We make the threshold very "high" so that window updates will be sent when the delta is - // relatively small. - ((DefaultHttp2LocalFlowController) localFlow).windowUpdateRatio(connectionStream, .999f); - } catch (Http2Exception e) { - errorRef.set(e); - } - }); - - Http2Exception exception = errorRef.get(); - if (exception != null) { - throw exception; - } - - int windowUpdate = 1024; - - channel.write(new DefaultHttp2WindowUpdateFrame(windowUpdate)); - - // The initial window size is only changed by Http2Settings, so it shouldn't change. - assertEquals(initialWindowSizeBefore, localFlow.initialWindowSize()); - // The connection window should be increased by the delta amount. - assertEquals(connectionWindowSizeBefore + windowUpdate, localFlow.windowSize(connectionStream)); - } - - @Test - public void windowUpdateDoesNotOverflowConnectionWindow() { - Http2Connection connection = frameCodec.connection(); - Http2LocalFlowController localFlow = connection.local().flowController(); - int initialWindowSizeBefore = localFlow.initialWindowSize(); - - channel.write(new DefaultHttp2WindowUpdateFrame(Integer.MAX_VALUE)); - - // The initial window size is only changed by Http2Settings, so it shouldn't change. - assertEquals(initialWindowSizeBefore, localFlow.initialWindowSize()); - // The connection window should be increased by the delta amount. - assertEquals(Integer.MAX_VALUE, localFlow.windowSize(connection.connectionStream())); - } - - @Test - public void writeUnknownFrame() { - final Http2FrameStream stream = frameCodec.newStream(); - - ByteBuf buffer = Unpooled.buffer().writeByte(1); - DefaultHttp2UnknownFrame unknownFrame = new DefaultHttp2UnknownFrame( - (byte) 20, new Http2Flags().ack(true), buffer); - unknownFrame.stream(stream); - channel.write(unknownFrame); - - verify(frameWriter).writeFrame(any(ChannelHandlerContext.class), eq(unknownFrame.frameType()), - eq(unknownFrame.stream().id()), eq(unknownFrame.flags()), eq(buffer)); - } - - @Test - public void sendSettingsFrame() { - Http2Settings settings = new Http2Settings(); - channel.write(new DefaultHttp2SettingsFrame(settings)); - - verify(frameWriter).writeSettings(any(ChannelHandlerContext.class), same(settings)); - } - - @Test - @Timeout(value = 5000, unit = TimeUnit.MILLISECONDS) - public void newOutboundStream() { - final Http2FrameStream stream = frameCodec.newStream(); - - assertNotNull(stream); - assertFalse(isStreamIdValid(stream.id())); - - final Promise listenerExecuted = GlobalEventExecutor.INSTANCE.newPromise(); - - channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers(), false).stream(stream)) - .addListener(future -> { - assertTrue(future.isSuccess()); - assertTrue(isStreamIdValid(stream.id())); - listenerExecuted.setSuccess(null); - } - ); - ByteBuf data = Unpooled.buffer().writeZero(100); - Future f = channel.writeAndFlush(new DefaultHttp2DataFrame(data).stream(stream)); - assertTrue(f.isSuccess()); - - listenerExecuted.asFuture().syncUninterruptibly(); - assertTrue(listenerExecuted.isSuccess()); - } - - @Test - public void newOutboundStreamsShouldBeBuffered() throws Exception { - setUp(Http2FrameCodecBuilder.forServer().encoderEnforceMaxConcurrentStreams(true), - new Http2Settings().maxConcurrentStreams(1)); - - Http2FrameStream stream1 = frameCodec.newStream(); - Http2FrameStream stream2 = frameCodec.newStream(); - - Future future1 = channel.writeAndFlush( - new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream1)); - Future future2 = channel.writeAndFlush( - new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream2)); - - assertTrue(isStreamIdValid(stream1.id())); - channel.runPendingTasks(); - assertTrue(isStreamIdValid(stream2.id())); - - assertTrue(future1.syncUninterruptibly().isSuccess()); - assertFalse(future2.isDone()); - - // Increase concurrent streams limit to 2 - frameInboundWriter.writeInboundSettings(new Http2Settings().maxConcurrentStreams(2)); - - channel.flush(); - - assertTrue(future2.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(); - - Future future1 = channel.writeAndFlush( - new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream1)); - Future future2 = channel.writeAndFlush( - new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream2)); - Future future3 = channel.writeAndFlush( - new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream3)); - - assertTrue(isStreamIdValid(stream1.id())); - channel.runPendingTasks(); - assertTrue(isStreamIdValid(stream2.id())); - - assertTrue(future1.syncUninterruptibly().isSuccess()); - assertFalse(future2.isDone()); - assertFalse(future3.isDone()); - - // Increase concurrent streams limit to 2 - frameInboundWriter.writeInboundSettings(new Http2Settings().maxConcurrentStreams(2)); - channel.flush(); - - // As we increased the limit to 2 we should have also succeed the second frame. - assertTrue(future2.syncUninterruptibly().isSuccess()); - assertFalse(future3.isDone()); - - frameInboundWriter.writeInboundSettings(new Http2Settings().maxConcurrentStreams(3)); - channel.flush(); - - // With the max streams of 3 all streams should be succeed now. - assertTrue(future3.syncUninterruptibly().isSuccess()); - - assertFalse(channel.finishAndReleaseAll()); - } - - @Test - public void doNotLeakOnFailedInitializationForChannels() throws Exception { - setUp(Http2FrameCodecBuilder.forServer(), new Http2Settings().maxConcurrentStreams(2)); - Http2FrameStream stream1 = frameCodec.newStream(); - Http2FrameStream stream2 = frameCodec.newStream(); - Future stream1HeaderFuture = channel.writeAndFlush( - new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream1)); - channel.runPendingTasks(); - - frameInboundWriter.writeInboundGoAway(stream1.id(), 0L, Unpooled.EMPTY_BUFFER); - - Future stream2HeaderFuture = channel.writeAndFlush( - new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream2)); - channel.runPendingTasks(); - - assertTrue(stream1HeaderFuture.syncUninterruptibly().isSuccess()); - assertTrue(stream2HeaderFuture.isDone()); - - assertEquals(0, frameCodec.numInitializingStreams()); - assertFalse(channel.finishAndReleaseAll()); - } - - @Test - public void streamIdentifiersExhausted() throws Exception { - int maxServerStreamId = Integer.MAX_VALUE - 1; - - channel.executor().submit(() -> { - assertNotNull(frameCodec.connection().local().createStream(maxServerStreamId, false)); - return null; - }).sync(); - - Http2FrameStream stream = frameCodec.newStream(); - assertNotNull(stream); - - Future writeFuture = channel.writeAndFlush( - new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream)); - - Http2GoAwayFrame goAwayFrame = inboundHandler.readInbound(); - assertNotNull(goAwayFrame); - assertEquals(NO_ERROR.code(), goAwayFrame.errorCode()); - assertEquals(Integer.MAX_VALUE, goAwayFrame.lastStreamId()); - goAwayFrame.release(); - assertThat(writeFuture.cause(), instanceOf(Http2NoMoreStreamIdsException.class)); - } - - @Test - public void receivePing() throws Http2Exception { - frameInboundWriter.writeInboundPing(false, 12345L); - - Http2PingFrame pingFrame = inboundHandler.readInbound(); - assertNotNull(pingFrame); - - assertEquals(12345, pingFrame.content()); - assertFalse(pingFrame.ack()); - } - - @Test - public void sendPing() { - channel.writeAndFlush(new DefaultHttp2PingFrame(12345)); - - verify(frameWriter).writePing(any(ChannelHandlerContext.class), eq(false), - eq(12345L)); - } - - @Test - public void receiveSettings() throws Http2Exception { - Http2Settings settings = new Http2Settings().maxConcurrentStreams(1); - frameInboundWriter.writeInboundSettings(settings); - - Http2SettingsFrame settingsFrame = inboundHandler.readInbound(); - assertNotNull(settingsFrame); - assertEquals(settings, settingsFrame.settings()); - } - - @Test - public void sendSettings() { - Http2Settings settings = new Http2Settings().maxConcurrentStreams(1); - channel.writeAndFlush(new DefaultHttp2SettingsFrame(settings)); - - verify(frameWriter).writeSettings(any(ChannelHandlerContext.class), eq(settings)); - } - - @Test - public void iterateActiveStreams() throws Exception { - setUp(Http2FrameCodecBuilder.forServer().encoderEnforceMaxConcurrentStreams(true), - new Http2Settings().maxConcurrentStreams(1)); - - frameInboundWriter.writeInboundHeaders(3, request, 0, false); - - Http2HeadersFrame headersFrame = inboundHandler.readInbound(); - assertNotNull(headersFrame); - - Http2FrameStream activeInbond = headersFrame.stream(); - - Http2FrameStream activeOutbound = frameCodec.newStream(); - channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(activeOutbound)); - - Http2FrameStream bufferedOutbound = frameCodec.newStream(); - channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(bufferedOutbound)); - - @SuppressWarnings("unused") - Http2FrameStream idleStream = frameCodec.newStream(); - - final Set activeStreams = new HashSet<>(); - final AtomicReference errorRef = new AtomicReference<>(); - channel.executor().execute(() -> { - try { - frameCodec.forEachActiveStream(stream -> { - activeStreams.add(stream); - return true; - }); - } catch (Http2Exception e) { - errorRef.set(e); - } - }); - Http2Exception exception = errorRef.get(); - if (exception != null) { - throw exception; - } - - assertEquals(2, activeStreams.size()); - - Set expectedStreams = new HashSet<>(); - expectedStreams.add(activeInbond); - expectedStreams.add(activeOutbound); - assertEquals(expectedStreams, activeStreams); - } - - @Test - public void autoAckPingTrue() throws Exception { - setUp(Http2FrameCodecBuilder.forServer().autoAckPingFrame(true), new Http2Settings()); - frameInboundWriter.writeInboundPing(false, 8); - Http2PingFrame frame = inboundHandler.readInbound(); - assertFalse(frame.ack()); - assertEquals(8, frame.content()); - verify(frameWriter).writePing(any(ChannelHandlerContext.class), eq(true), eq(8L)); - } - - @Test - public void autoAckPingFalse() throws Exception { - setUp(Http2FrameCodecBuilder.forServer().autoAckPingFrame(false), new Http2Settings()); - frameInboundWriter.writeInboundPing(false, 8); - verify(frameWriter, never()).writePing(any(ChannelHandlerContext.class), eq(true), eq(8L)); - Http2PingFrame frame = inboundHandler.readInbound(); - assertFalse(frame.ack()); - assertEquals(8, frame.content()); - - // Now ack the frame manually. - channel.writeAndFlush(new DefaultHttp2PingFrame(8, true)); - verify(frameWriter).writePing(any(ChannelHandlerContext.class), eq(true), eq(8L)); - } - - @Test - public void streamShouldBeOpenInListener() { - final Http2FrameStream stream2 = frameCodec.newStream(); - assertEquals(State.IDLE, stream2.state()); - - final AtomicBoolean listenerExecuted = new AtomicBoolean(); - channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream2)) - .addListener(future -> { - assertTrue(future.isSuccess()); - assertEquals(State.OPEN, stream2.state()); - listenerExecuted.set(true); - }); - - assertTrue(listenerExecuted.get()); - } - - @Test - public void upgradeEventNoRefCntError() throws Exception { - 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); - - // Check if we could make it accessible which may fail on java9. - Assumptions.assumeTrue(ReflectionUtil.trySetAccessible(constructor, true) == null); - - HttpServerUpgradeHandler.UpgradeEvent upgradeEvent = constructor.newInstance( - "HTTP/2", new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/")); - channel.pipeline().fireUserEventTriggered(upgradeEvent); - assertEquals(1, upgradeEvent.refCnt()); - } - - @Test - public void upgradeWithoutFlowControlling() throws Exception { - channel.pipeline().addAfter(frameCodec.ctx.name(), null, new ChannelHandler() { - @Override - public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception { - if (msg instanceof Http2DataFrame) { - // Simulate consuming the frame and update the flow-controller. - Http2DataFrame data = (Http2DataFrame) msg; - ctx.writeAndFlush(new DefaultHttp2WindowUpdateFrame(data.initialFlowControlledBytes()) - .stream(data.stream())).addListener(future -> { - Throwable cause = future.cause(); - if (cause != null) { - ctx.fireExceptionCaught(cause); - } - }); - } - ReferenceCountUtil.release(msg); - } - }); - - 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); - - // Check if we could make it accessible which may fail on java9. - Assumptions.assumeTrue(ReflectionUtil.trySetAccessible(constructor, true) == null); - - String longString = new String(new char[70000]).replace("\0", "*"); - DefaultFullHttpRequest request = - new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/", bb(longString)); - - HttpServerUpgradeHandler.UpgradeEvent upgradeEvent = constructor.newInstance( - "HTTP/2", request); - channel.pipeline().fireUserEventTriggered(upgradeEvent); - } - - @Test - public void priorityForNonExistingStream() { - writeHeaderAndAssert(1); - - frameInboundWriter.writeInboundPriority(3, 1, (short) 31, true); - } - - @Test - public void priorityForExistingStream() { - writeHeaderAndAssert(1); - writeHeaderAndAssert(3); - frameInboundWriter.writeInboundPriority(3, 1, (short) 31, true); - - assertInboundStreamFrame(3, new DefaultHttp2PriorityFrame(1, (short) 31, true)); - } - - private void writeHeaderAndAssert(int streamId) { - frameInboundWriter.writeInboundHeaders(streamId, request, 31, false); - - Http2Stream stream = frameCodec.connection().stream(streamId); - assertNotNull(stream); - assertEquals(State.OPEN, stream.state()); - - assertInboundStreamFrame(streamId, new DefaultHttp2HeadersFrame(request, false, 31)); - } - - private void assertInboundStreamFrame(int expectedId, Http2StreamFrame streamFrame) { - Http2StreamFrame inboundFrame = inboundHandler.readInbound(); - Http2FrameStream stream2 = inboundFrame.stream(); - assertNotNull(stream2); - assertEquals(expectedId, stream2.id()); - assertEquals(inboundFrame, streamFrame.stream(stream2)); - } -} 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 deleted file mode 100644 index 2b5db4fa9a..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2FrameInboundWriter.java +++ /dev/null @@ -1,298 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.api.BufferAllocator; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.Attribute; -import io.netty.util.AttributeKey; -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; - -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) { - ctx = new WriteInboundChannelHandlerContext(channel); - this.writer = writer; - } - - void writeInboundData(int streamId, ByteBuf data, int padding, boolean endStream) { - writer.writeData(ctx, streamId, data, padding, endStream).syncUninterruptibly(); - } - - void writeInboundHeaders(int streamId, Http2Headers headers, - int padding, boolean endStream) { - writer.writeHeaders(ctx, streamId, headers, padding, endStream).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).syncUninterruptibly(); - } - - void writeInboundPriority(int streamId, int streamDependency, - short weight, boolean exclusive) { - writer.writePriority(ctx, streamId, streamDependency, weight, - exclusive).syncUninterruptibly(); - } - - void writeInboundRstStream(int streamId, long errorCode) { - writer.writeRstStream(ctx, streamId, errorCode).syncUninterruptibly(); - } - - void writeInboundSettings(Http2Settings settings) { - writer.writeSettings(ctx, settings).syncUninterruptibly(); - } - - void writeInboundSettingsAck() { - writer.writeSettingsAck(ctx).syncUninterruptibly(); - } - - void writeInboundPing(boolean ack, long data) { - writer.writePing(ctx, ack, data).syncUninterruptibly(); - } - - void writePushPromise(int streamId, int promisedStreamId, - Http2Headers headers, int padding) { - writer.writePushPromise(ctx, streamId, promisedStreamId, - headers, padding).syncUninterruptibly(); - } - - void writeInboundGoAway(int lastStreamId, long errorCode, ByteBuf debugData) { - writer.writeGoAway(ctx, lastStreamId, errorCode, debugData).syncUninterruptibly(); - } - - void writeInboundWindowUpdate(int streamId, int windowSizeIncrement) { - writer.writeWindowUpdate(ctx, streamId, windowSizeIncrement).syncUninterruptibly(); - } - - void writeInboundFrame(byte frameType, int streamId, - Http2Flags flags, ByteBuf payload) { - writer.writeFrame(ctx, frameType, streamId, flags, payload).syncUninterruptibly(); - } - - private static final class WriteInboundChannelHandlerContext - implements ChannelHandlerContext, ChannelHandler { - private final EmbeddedChannel channel; - - WriteInboundChannelHandlerContext(EmbeddedChannel channel) { - this.channel = channel; - } - - @Override - public Channel channel() { - return channel; - } - - @Override - public EventExecutor executor() { - return channel.executor(); - } - - @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 BufferAllocator bufferAllocator() { - return channel.bufferAllocator(); - } - - @Override - public Attribute attr(AttributeKey key) { - return channel.attr(key); - } - - @Override - public boolean hasAttr(AttributeKey key) { - return channel.hasAttr(key); - } - - @Override - public Future bind(SocketAddress localAddress) { - return channel.bind(localAddress); - } - - @Override - public Future connect(SocketAddress remoteAddress) { - return channel.connect(remoteAddress); - } - - @Override - public Future connect(SocketAddress remoteAddress, SocketAddress localAddress) { - return channel.connect(remoteAddress, localAddress); - } - - @Override - public Future disconnect() { - return channel.disconnect(); - } - - @Override - public Future close() { - return channel.close(); - } - - @Override - public Future register() { - return channel.register(); - } - - @Override - public Future deregister() { - return channel.deregister(); - } - - @Override - public Future write(Object msg) { - return writeAndFlush(msg); - } - - @Override - public Future writeAndFlush(Object msg) { - try { - channel.writeInbound(msg); - channel.runPendingTasks(); - } catch (Throwable cause) { - return newFailedFuture(cause); - } - return newSucceededFuture(); - } - - @Override - public Promise newPromise() { - return channel.newPromise(); - } - - @Override - public Future newSucceededFuture() { - return channel.newSucceededFuture(); - } - - @Override - public Future newFailedFuture(Throwable cause) { - return channel.newFailedFuture(cause); - } - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2FrameRoundtripTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2FrameRoundtripTest.java deleted file mode 100644 index 487aaf0ce5..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2FrameRoundtripTest.java +++ /dev/null @@ -1,479 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.CompositeByteBuf; -import io.netty.buffer.EmptyByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.AsciiString; -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.GlobalEventExecutor; -import io.netty.util.concurrent.ImmediateEventExecutor; -import io.netty.util.concurrent.Promise; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.stubbing.Answer; - -import java.util.LinkedList; -import java.util.List; - -import static io.netty.buffer.Unpooled.EMPTY_BUFFER; -import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_PADDING; -import static io.netty.handler.codec.http2.Http2HeadersEncoder.NEVER_SENSITIVE; -import static io.netty.handler.codec.http2.Http2TestUtil.newTestDecoder; -import static io.netty.handler.codec.http2.Http2TestUtil.newTestEncoder; -import static io.netty.handler.codec.http2.Http2TestUtil.randomString; -import static io.netty.util.CharsetUtil.UTF_8; -import static java.lang.Math.min; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyBoolean; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.anyShort; - -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -/** - * Tests encoding/decoding each HTTP2 frame type. - */ -public class Http2FrameRoundtripTest { - private static final byte[] MESSAGE = "hello world".getBytes(UTF_8); - private static final int STREAM_ID = 0x7FFFFFFF; - private static final int WINDOW_UPDATE = 0x7FFFFFFF; - private static final long ERROR_CODE = 0xFFFFFFFFL; - - @Mock - private Http2FrameListener listener; - - @Mock - private ChannelHandlerContext ctx; - - @Mock - private EventExecutor executor; - - @Mock - private Channel channel; - - @Mock - private ByteBufAllocator alloc; - - private Http2FrameWriter writer; - private Http2FrameReader reader; - private final List needReleasing = new LinkedList<>(); - - @BeforeEach - public void setup() throws Exception { - MockitoAnnotations.initMocks(this); - - when(ctx.alloc()).thenReturn(alloc); - when(ctx.executor()).thenReturn(executor); - when(ctx.channel()).thenReturn(channel); - doAnswer((Answer>) in -> - ImmediateEventExecutor.INSTANCE.newSucceededFuture(null)).when(ctx).write(any()); - doAnswer((Answer) in -> Unpooled.buffer()).when(alloc).buffer(); - doAnswer((Answer) in -> Unpooled.buffer((Integer) in.getArguments()[0])).when(alloc).buffer(anyInt()); - doAnswer((Answer>) invocation -> - GlobalEventExecutor.INSTANCE.newPromise()).when(ctx).newPromise(); - - writer = new DefaultHttp2FrameWriter(new DefaultHttp2HeadersEncoder(NEVER_SENSITIVE, newTestEncoder())); - reader = new DefaultHttp2FrameReader(new DefaultHttp2HeadersDecoder(false, newTestDecoder())); - } - - @AfterEach - public void tearDown() { - try { - // Release all of the buffers. - for (ByteBuf buf : needReleasing) { - buf.release(); - } - // Now verify that all of the reference counts are zero. - for (ByteBuf buf : needReleasing) { - int expectedFinalRefCount = 0; - if (buf.isReadOnly() || buf instanceof EmptyByteBuf) { - // Special case for when we're writing slices of the padding buffer. - expectedFinalRefCount = 1; - } - assertEquals(expectedFinalRefCount, buf.refCnt()); - } - } finally { - needReleasing.clear(); - } - } - - @Test - public void emptyDataShouldMatch() throws Exception { - final ByteBuf data = EMPTY_BUFFER; - writer.writeData(ctx, STREAM_ID, data.slice(), 0, false); - readFrames(); - verify(listener).onDataRead(eq(ctx), eq(STREAM_ID), eq(data), eq(0), eq(false)); - } - - @Test - public void dataShouldMatch() throws Exception { - final ByteBuf data = data(10); - writer.writeData(ctx, STREAM_ID, data.slice(), 1, false); - readFrames(); - verify(listener).onDataRead(eq(ctx), eq(STREAM_ID), eq(data), eq(1), eq(false)); - } - - @Test - public void dataWithPaddingShouldMatch() throws Exception { - final ByteBuf data = data(10); - writer.writeData(ctx, STREAM_ID, data.slice(), MAX_PADDING, true); - readFrames(); - verify(listener).onDataRead(eq(ctx), eq(STREAM_ID), eq(data), eq(MAX_PADDING), eq(true)); - } - - @Test - public void largeDataFrameShouldMatch() throws Exception { - // Create a large message to force chunking. - final ByteBuf originalData = data(1024 * 1024); - final int originalPadding = 100; - final boolean endOfStream = true; - - writer.writeData(ctx, STREAM_ID, originalData.slice(), originalPadding, - endOfStream); - readFrames(); - - // Verify that at least one frame was sent with eos=false and exactly one with eos=true. - verify(listener, atLeastOnce()).onDataRead(eq(ctx), eq(STREAM_ID), any(ByteBuf.class), - anyInt(), eq(false)); - verify(listener).onDataRead(eq(ctx), eq(STREAM_ID), any(ByteBuf.class), - anyInt(), eq(true)); - - // Capture the read data and padding. - ArgumentCaptor dataCaptor = ArgumentCaptor.forClass(ByteBuf.class); - ArgumentCaptor paddingCaptor = ArgumentCaptor.forClass(Integer.class); - verify(listener, atLeastOnce()).onDataRead(eq(ctx), eq(STREAM_ID), dataCaptor.capture(), - paddingCaptor.capture(), anyBoolean()); - - // Make sure the data matches the original. - for (ByteBuf chunk : dataCaptor.getAllValues()) { - ByteBuf originalChunk = originalData.readSlice(chunk.readableBytes()); - assertEquals(originalChunk, chunk); - } - assertFalse(originalData.isReadable()); - - // Make sure the padding matches the original. - int totalReadPadding = 0; - for (int framePadding : paddingCaptor.getAllValues()) { - totalReadPadding += framePadding; - } - assertEquals(originalPadding, totalReadPadding); - } - - @Test - public void emptyHeadersShouldMatch() throws Exception { - final Http2Headers headers = EmptyHttp2Headers.INSTANCE; - writer.writeHeaders(ctx, STREAM_ID, headers, 0, true); - readFrames(); - verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(0), eq(true)); - } - - @Test - public void emptyHeadersWithPaddingShouldMatch() throws Exception { - final Http2Headers headers = EmptyHttp2Headers.INSTANCE; - writer.writeHeaders(ctx, STREAM_ID, headers, MAX_PADDING, true); - readFrames(); - verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(MAX_PADDING), eq(true)); - } - - @Test - public void binaryHeadersWithoutPriorityShouldMatch() throws Exception { - final Http2Headers headers = binaryHeaders(); - writer.writeHeaders(ctx, STREAM_ID, headers, 0, true); - readFrames(); - verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(0), eq(true)); - } - - @Test - public void headersFrameWithoutPriorityShouldMatch() throws Exception { - final Http2Headers headers = headers(); - writer.writeHeaders(ctx, STREAM_ID, headers, 0, true); - readFrames(); - verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(0), eq(true)); - } - - @Test - public void headersFrameWithPriorityShouldMatch() throws Exception { - final Http2Headers headers = headers(); - writer.writeHeaders(ctx, STREAM_ID, headers, 4, (short) 255, true, 0, true); - readFrames(); - verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(4), eq((short) 255), - eq(true), eq(0), eq(true)); - } - - @Test - public void headersWithPaddingWithoutPriorityShouldMatch() throws Exception { - final Http2Headers headers = headers(); - writer.writeHeaders(ctx, STREAM_ID, headers, MAX_PADDING, true); - readFrames(); - verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(MAX_PADDING), eq(true)); - } - - @Test - public void headersWithPaddingWithPriorityShouldMatch() throws Exception { - final Http2Headers headers = headers(); - writer.writeHeaders(ctx, STREAM_ID, headers, 2, (short) 3, true, 1, true); - readFrames(); - verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(2), eq((short) 3), eq(true), - eq(1), eq(true)); - } - - @Test - public void continuedHeadersShouldMatch() throws Exception { - final Http2Headers headers = largeHeaders(); - writer.writeHeaders(ctx, STREAM_ID, headers, 2, (short) 3, true, 0, true); - readFrames(); - verify(listener) - .onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(2), eq((short) 3), eq(true), eq(0), eq(true)); - } - - @Test - public void continuedHeadersWithPaddingShouldMatch() throws Exception { - final Http2Headers headers = largeHeaders(); - writer.writeHeaders(ctx, STREAM_ID, headers, 2, (short) 3, true, MAX_PADDING, true); - readFrames(); - verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(2), eq((short) 3), eq(true), - eq(MAX_PADDING), eq(true)); - } - - @Test - public void headersThatAreTooBigShouldFail() throws Exception { - reader = new DefaultHttp2FrameReader(false); - final int maxListSize = 100; - reader.configuration().headersConfiguration().maxHeaderListSize(maxListSize, maxListSize); - final Http2Headers headers = headersOfSize(maxListSize + 1); - writer.writeHeaders(ctx, STREAM_ID, headers, 2, (short) 3, true, MAX_PADDING, true); - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - readFrames(); - } - }); - verify(listener, never()).onHeadersRead(any(ChannelHandlerContext.class), anyInt(), - any(Http2Headers.class), anyInt(), anyShort(), anyBoolean(), anyInt(), - anyBoolean()); - } - - @Test - public void emptyPushPromiseShouldMatch() throws Exception { - final Http2Headers headers = EmptyHttp2Headers.INSTANCE; - writer.writePushPromise(ctx, STREAM_ID, 2, headers, 0); - readFrames(); - verify(listener).onPushPromiseRead(eq(ctx), eq(STREAM_ID), eq(2), eq(headers), eq(0)); - } - - @Test - public void pushPromiseFrameShouldMatch() throws Exception { - final Http2Headers headers = headers(); - writer.writePushPromise(ctx, STREAM_ID, 1, headers, 5); - readFrames(); - verify(listener).onPushPromiseRead(eq(ctx), eq(STREAM_ID), eq(1), eq(headers), eq(5)); - } - - @Test - public void pushPromiseWithPaddingShouldMatch() throws Exception { - final Http2Headers headers = headers(); - writer.writePushPromise(ctx, STREAM_ID, 2, headers, MAX_PADDING); - readFrames(); - verify(listener).onPushPromiseRead(eq(ctx), eq(STREAM_ID), eq(2), eq(headers), eq(MAX_PADDING)); - } - - @Test - public void continuedPushPromiseShouldMatch() throws Exception { - final Http2Headers headers = largeHeaders(); - writer.writePushPromise(ctx, STREAM_ID, 2, headers, 0); - readFrames(); - verify(listener).onPushPromiseRead(eq(ctx), eq(STREAM_ID), eq(2), eq(headers), eq(0)); - } - - @Test - public void continuedPushPromiseWithPaddingShouldMatch() throws Exception { - final Http2Headers headers = largeHeaders(); - writer.writePushPromise(ctx, STREAM_ID, 2, headers, 0xFF); - readFrames(); - verify(listener).onPushPromiseRead(eq(ctx), eq(STREAM_ID), eq(2), eq(headers), eq(0xFF)); - } - - @Test - public void goAwayFrameShouldMatch() throws Exception { - final String text = "test"; - final ByteBuf data = buf(text.getBytes()); - - writer.writeGoAway(ctx, STREAM_ID, ERROR_CODE, data.slice()); - readFrames(); - - ArgumentCaptor captor = ArgumentCaptor.forClass(ByteBuf.class); - verify(listener).onGoAwayRead(eq(ctx), eq(STREAM_ID), eq(ERROR_CODE), captor.capture()); - assertEquals(data, captor.getValue()); - } - - @Test - public void pingFrameShouldMatch() throws Exception { - writer.writePing(ctx, false, 1234567); - readFrames(); - - ArgumentCaptor captor = ArgumentCaptor.forClass(long.class); - verify(listener).onPingRead(eq(ctx), captor.capture()); - assertEquals(1234567, (long) captor.getValue()); - } - - @Test - public void pingAckFrameShouldMatch() throws Exception { - writer.writePing(ctx, true, 1234567); - readFrames(); - - ArgumentCaptor captor = ArgumentCaptor.forClass(long.class); - verify(listener).onPingAckRead(eq(ctx), captor.capture()); - assertEquals(1234567, (long) captor.getValue()); - } - - @Test - public void priorityFrameShouldMatch() throws Exception { - writer.writePriority(ctx, STREAM_ID, 1, (short) 1, true); - readFrames(); - verify(listener).onPriorityRead(eq(ctx), eq(STREAM_ID), eq(1), eq((short) 1), eq(true)); - } - - @Test - public void rstStreamFrameShouldMatch() throws Exception { - writer.writeRstStream(ctx, STREAM_ID, ERROR_CODE); - readFrames(); - verify(listener).onRstStreamRead(eq(ctx), eq(STREAM_ID), eq(ERROR_CODE)); - } - - @Test - public void emptySettingsFrameShouldMatch() throws Exception { - final Http2Settings settings = new Http2Settings(); - writer.writeSettings(ctx, settings); - readFrames(); - verify(listener).onSettingsRead(eq(ctx), eq(settings)); - } - - @Test - public void settingsShouldStripShouldMatch() throws Exception { - final Http2Settings settings = new Http2Settings(); - settings.pushEnabled(true); - settings.headerTableSize(4096); - settings.initialWindowSize(123); - settings.maxConcurrentStreams(456); - - writer.writeSettings(ctx, settings); - readFrames(); - verify(listener).onSettingsRead(eq(ctx), eq(settings)); - } - - @Test - public void settingsAckShouldMatch() throws Exception { - writer.writeSettingsAck(ctx); - readFrames(); - verify(listener).onSettingsAckRead(eq(ctx)); - } - - @Test - public void windowUpdateFrameShouldMatch() throws Exception { - writer.writeWindowUpdate(ctx, STREAM_ID, WINDOW_UPDATE); - readFrames(); - verify(listener).onWindowUpdateRead(eq(ctx), eq(STREAM_ID), eq(WINDOW_UPDATE)); - } - - private void readFrames() throws Http2Exception { - // Now read all of the written frames. - ByteBuf write = captureWrites(); - reader.readFrame(ctx, write, listener); - } - - private static ByteBuf data(int size) { - byte[] data = new byte[size]; - for (int ix = 0; ix < data.length;) { - int length = min(MESSAGE.length, data.length - ix); - System.arraycopy(MESSAGE, 0, data, ix, length); - ix += length; - } - return buf(data); - } - - private static ByteBuf buf(byte[] bytes) { - return Unpooled.wrappedBuffer(bytes); - } - - private T releaseLater(T buf) { - needReleasing.add(buf); - return buf; - } - - private ByteBuf captureWrites() { - ArgumentCaptor captor = ArgumentCaptor.forClass(ByteBuf.class); - verify(ctx, atLeastOnce()).write(captor.capture()); - CompositeByteBuf composite = releaseLater(Unpooled.compositeBuffer()); - for (ByteBuf buf : captor.getAllValues()) { - buf = releaseLater(buf.retain()); - composite.addComponent(true, buf); - } - return composite; - } - - private static Http2Headers headers() { - return new DefaultHttp2Headers(false).method(AsciiString.of("GET")).scheme(AsciiString.of("https")) - .authority(AsciiString.of("example.org")).path(AsciiString.of("/some/path/resource2")) - .add(randomString(), randomString()); - } - - private static Http2Headers largeHeaders() { - DefaultHttp2Headers headers = new DefaultHttp2Headers(false); - for (int i = 0; i < 100; ++i) { - String key = "this-is-a-test-header-key-" + i; - String value = "this-is-a-test-header-value-" + i; - headers.add(AsciiString.of(key), AsciiString.of(value)); - } - return headers; - } - - private static Http2Headers headersOfSize(final int minSize) { - final AsciiString singleByte = new AsciiString(new byte[]{0}, false); - DefaultHttp2Headers headers = new DefaultHttp2Headers(false); - for (int size = 0; size < minSize; size += 2) { - headers.add(singleByte, singleByte); - } - return headers; - } - - private static Http2Headers binaryHeaders() { - DefaultHttp2Headers headers = new DefaultHttp2Headers(false); - for (int ix = 0; ix < 10; ++ix) { - headers.add(randomString(), randomString()); - } - return headers; - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2HeaderBlockIOTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2HeaderBlockIOTest.java deleted file mode 100644 index 927f4c1392..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2HeaderBlockIOTest.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.AsciiString; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static io.netty.handler.codec.http2.Http2TestUtil.randomString; -import static org.junit.jupiter.api.Assertions.assertEquals; - -/** - * Tests for encoding/decoding HTTP2 header blocks. - */ -public class Http2HeaderBlockIOTest { - - private DefaultHttp2HeadersDecoder decoder; - private DefaultHttp2HeadersEncoder encoder; - private ByteBuf buffer; - - @BeforeEach - public void setup() { - encoder = new DefaultHttp2HeadersEncoder(); - decoder = new DefaultHttp2HeadersDecoder(false); - buffer = Unpooled.buffer(); - } - - @AfterEach - public void teardown() { - buffer.release(); - } - - @Test - public void roundtripShouldBeSuccessful() throws Http2Exception { - Http2Headers in = headers(); - assertRoundtripSuccessful(in); - } - - @Test - public void successiveCallsShouldSucceed() throws Http2Exception { - Http2Headers in = new DefaultHttp2Headers().method(new AsciiString("GET")).scheme(new AsciiString("https")) - .authority(new AsciiString("example.org")).path(new AsciiString("/some/path")) - .add(new AsciiString("accept"), new AsciiString("*/*")); - assertRoundtripSuccessful(in); - - in = new DefaultHttp2Headers().method(new AsciiString("GET")).scheme(new AsciiString("https")) - .authority(new AsciiString("example.org")).path(new AsciiString("/some/path/resource1")) - .add(new AsciiString("accept"), new AsciiString("image/jpeg")) - .add(new AsciiString("cache-control"), new AsciiString("no-cache")); - assertRoundtripSuccessful(in); - - in = new DefaultHttp2Headers().method(new AsciiString("GET")).scheme(new AsciiString("https")) - .authority(new AsciiString("example.org")).path(new AsciiString("/some/path/resource2")) - .add(new AsciiString("accept"), new AsciiString("image/png")) - .add(new AsciiString("cache-control"), new AsciiString("no-cache")); - assertRoundtripSuccessful(in); - } - - @Test - public void setMaxHeaderSizeShouldBeSuccessful() throws Http2Exception { - encoder.maxHeaderTableSize(10); - Http2Headers in = headers(); - assertRoundtripSuccessful(in); - assertEquals(10, decoder.maxHeaderTableSize()); - } - - private void assertRoundtripSuccessful(Http2Headers in) throws Http2Exception { - encoder.encodeHeaders(3 /* randomly chosen */, in, buffer); - - Http2Headers out = decoder.decodeHeaders(0, buffer); - assertEquals(in, out); - } - - private static Http2Headers headers() { - return new DefaultHttp2Headers(false).method(new AsciiString("GET")).scheme(new AsciiString("https")) - .authority(new AsciiString("example.org")).path(new AsciiString("/some/path/resource2")) - .add(new AsciiString("accept"), new AsciiString("image/png")) - .add(new AsciiString("cache-control"), new AsciiString("no-cache")) - .add(new AsciiString("custom"), new AsciiString("value1")) - .add(new AsciiString("custom"), new AsciiString("value2")) - .add(new AsciiString("custom"), new AsciiString("value3")) - .add(new AsciiString("custom"), new AsciiString("custom4")) - .add(randomString(), randomString()); - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexClientUpgradeTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexClientUpgradeTest.java deleted file mode 100644 index 8d13e6065f..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexClientUpgradeTest.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.embedded.EmbeddedChannel; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public abstract class Http2MultiplexClientUpgradeTest { - - @ChannelHandler.Sharable - static final class NoopHandler implements ChannelHandler { - @Override - public void channelActive(ChannelHandlerContext ctx) { - ctx.channel().close(); - } - } - - private static final class UpgradeHandler implements ChannelHandler { - Http2Stream.State stateOnActive; - int streamId; - boolean channelInactiveCalled; - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - Http2StreamChannel ch = (Http2StreamChannel) ctx.channel(); - stateOnActive = ch.stream().state(); - streamId = ch.stream().id(); - ctx.fireChannelActive(); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - channelInactiveCalled = true; - ctx.fireChannelInactive(); - } - } - - protected abstract C newCodec(ChannelHandler upgradeHandler); - - protected abstract ChannelHandler newMultiplexer(ChannelHandler upgradeHandler); - - @Test - public void upgradeHandlerGetsActivated() throws Exception { - UpgradeHandler upgradeHandler = new UpgradeHandler(); - C codec = newCodec(upgradeHandler); - EmbeddedChannel ch = new EmbeddedChannel(codec, newMultiplexer(upgradeHandler)); - - ch.executor().submit(() -> { - codec.onHttpClientUpgrade(); - return null; - }).sync(); - - assertFalse(upgradeHandler.stateOnActive.localSideOpen()); - assertTrue(upgradeHandler.stateOnActive.remoteSideOpen()); - assertNotNull(codec.connection().stream(Http2CodecUtil.HTTP_UPGRADE_STREAM_ID).getProperty(codec.streamKey)); - assertEquals(Http2CodecUtil.HTTP_UPGRADE_STREAM_ID, upgradeHandler.streamId); - assertTrue(ch.finishAndReleaseAll()); - assertTrue(upgradeHandler.channelInactiveCalled); - } - - @Test - public void clientUpgradeWithoutUpgradeHandlerThrowsHttp2Exception() throws Http2Exception { - final C codec = newCodec(null); - final EmbeddedChannel ch = new EmbeddedChannel(codec, newMultiplexer(null)); - - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Http2Exception { - try { - codec.onHttpClientUpgrade(); - } finally { - ch.finishAndReleaseAll(); - } - } - }); - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecBuilderTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecBuilderTest.java deleted file mode 100644 index d5d5a63341..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecBuilderTest.java +++ /dev/null @@ -1,265 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.http2; - -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.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerAdapter; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.local.LocalAddress; -import io.netty.channel.local.LocalChannel; -import io.netty.channel.local.LocalHandler; -import io.netty.channel.local.LocalServerChannel; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; - -import java.util.concurrent.CountDownLatch; - -import static io.netty.handler.codec.http2.Http2CodecUtil.isStreamIdValid; -import static java.util.concurrent.TimeUnit.SECONDS; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - -/** - * Unit tests for {@link Http2MultiplexCodec}. - */ -public class Http2MultiplexCodecBuilderTest { - - private static EventLoopGroup group; - private Channel serverChannel; - private volatile Channel serverConnectedChannel; - private Channel clientChannel; - private LastInboundHandler serverLastInboundHandler; - - @BeforeAll - public static void init() { - group = new MultithreadEventLoopGroup(1, LocalHandler.newFactory()); - } - - @BeforeEach - public void setUp() throws Exception { - final CountDownLatch serverChannelLatch = new CountDownLatch(1); - LocalAddress serverAddress = new LocalAddress(getClass().getName()); - serverLastInboundHandler = new SharableLastInboundHandler(); - ServerBootstrap sb = new ServerBootstrap() - .channel(LocalServerChannel.class) - .group(group) - .childHandler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - serverConnectedChannel = ch; - ch.pipeline().addLast(new Http2MultiplexCodecBuilder(true, new ChannelInitializer() { - - @Override - protected void initChannel(Channel ch) throws Exception { - ch.pipeline().addLast(new ChannelHandler() { - private boolean writable; - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - writable |= ctx.channel().isWritable(); - ctx.fireChannelActive(); - } - - @Override - public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception { - writable |= ctx.channel().isWritable(); - ctx.fireChannelWritabilityChanged(); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - assertTrue(writable); - ctx.fireChannelInactive(); - } - }); - ch.pipeline().addLast(serverLastInboundHandler); - } - }).build()); - serverChannelLatch.countDown(); - } - }); - serverChannel = sb.bind(serverAddress).get(); - - Bootstrap cb = new Bootstrap() - .channel(LocalChannel.class) - .group(group) - .handler(new Http2MultiplexCodecBuilder(false, new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - fail("Should not be called for outbound streams"); - } - }).build()); - clientChannel = cb.connect(serverAddress).get(); - assertTrue(serverChannelLatch.await(5, SECONDS)); - } - - @AfterAll - public static void shutdown() { - group.shutdownGracefully(0, 5, SECONDS); - } - - @AfterEach - public void tearDown() throws Exception { - if (clientChannel != null) { - clientChannel.close().syncUninterruptibly(); - clientChannel = null; - } - if (serverChannel != null) { - serverChannel.close().syncUninterruptibly(); - serverChannel = null; - } - final Channel serverConnectedChannel = this.serverConnectedChannel; - if (serverConnectedChannel != null) { - serverConnectedChannel.close().syncUninterruptibly(); - this.serverConnectedChannel = null; - } - } - - private Http2StreamChannel newOutboundStream(ChannelHandler handler) { - return new Http2StreamChannelBootstrap(clientChannel).handler(handler).open().syncUninterruptibly().getNow(); - } - - @Test - public void multipleOutboundStreams() throws Exception { - Http2StreamChannel childChannel1 = newOutboundStream(new TestChannelInitializer()); - assertTrue(childChannel1.isActive()); - assertFalse(isStreamIdValid(childChannel1.stream().id())); - Http2StreamChannel childChannel2 = newOutboundStream(new TestChannelInitializer()); - assertTrue(childChannel2.isActive()); - assertFalse(isStreamIdValid(childChannel2.stream().id())); - - Http2Headers headers1 = new DefaultHttp2Headers(); - Http2Headers headers2 = new DefaultHttp2Headers(); - // Test that streams can be made active (headers sent) in different order than the corresponding channels - // have been created. - childChannel2.writeAndFlush(new DefaultHttp2HeadersFrame(headers2)); - childChannel1.writeAndFlush(new DefaultHttp2HeadersFrame(headers1)); - - Http2HeadersFrame headersFrame2 = serverLastInboundHandler.blockingReadInbound(); - assertNotNull(headersFrame2); - assertEquals(3, headersFrame2.stream().id()); - - Http2HeadersFrame headersFrame1 = serverLastInboundHandler.blockingReadInbound(); - assertNotNull(headersFrame1); - assertEquals(5, headersFrame1.stream().id()); - - assertEquals(3, childChannel2.stream().id()); - assertEquals(5, childChannel1.stream().id()); - - childChannel1.close(); - childChannel2.close(); - - serverLastInboundHandler.checkException(); - } - - @Test - public void createOutboundStream() throws Exception { - Channel childChannel = newOutboundStream(new TestChannelInitializer()); - assertTrue(childChannel.isRegistered()); - assertTrue(childChannel.isActive()); - - Http2Headers headers = new DefaultHttp2Headers(); - childChannel.writeAndFlush(new DefaultHttp2HeadersFrame(headers)); - ByteBuf data = Unpooled.buffer(100).writeZero(100); - childChannel.writeAndFlush(new DefaultHttp2DataFrame(data, true)); - - Http2HeadersFrame headersFrame = serverLastInboundHandler.blockingReadInbound(); - assertNotNull(headersFrame); - assertEquals(3, headersFrame.stream().id()); - assertEquals(headers, headersFrame.headers()); - - Http2DataFrame dataFrame = serverLastInboundHandler.blockingReadInbound(); - assertNotNull(dataFrame); - assertEquals(3, dataFrame.stream().id()); - assertEquals(data.readerIndex(0), dataFrame.content()); - assertTrue(dataFrame.isEndStream()); - dataFrame.release(); - - childChannel.close(); - - Http2ResetFrame rstFrame = serverLastInboundHandler.blockingReadInbound(); - assertNotNull(rstFrame); - assertEquals(3, rstFrame.stream().id()); - - serverLastInboundHandler.checkException(); - } - - @Sharable - private static class SharableLastInboundHandler extends LastInboundHandler { - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - ctx.fireChannelActive(); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - ctx.fireChannelInactive(); - } - } - - private static class SharableChannelHandler1 extends ChannelHandlerAdapter { - @Override - public boolean isSharable() { - return true; - } - } - - @Sharable - private static class SharableChannelHandler2 extends ChannelHandlerAdapter { - } - - private static class UnsharableChannelHandler extends ChannelHandlerAdapter { - @Override - public boolean isSharable() { - return false; - } - } - - @Test - public void testSharableCheck() { - assertNotNull(Http2MultiplexCodecBuilder.forServer(new SharableChannelHandler1())); - assertNotNull(Http2MultiplexCodecBuilder.forServer(new SharableChannelHandler2())); - } - - @Test - public void testUnsharableHandler() { - assertThrows(IllegalArgumentException.class, new Executable() { - @Override - public void execute() throws Throwable { - Http2MultiplexCodecBuilder.forServer(new UnsharableChannelHandler()); - } - }); - } -} 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 deleted file mode 100644 index 144638569d..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecClientUpgradeTest.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.channel.ChannelHandler; - -public class Http2MultiplexCodecClientUpgradeTest extends Http2MultiplexClientUpgradeTest { - - @Override - protected Http2MultiplexCodec newCodec(ChannelHandler upgradeHandler) { - Http2MultiplexCodecBuilder builder = Http2MultiplexCodecBuilder.forClient(new NoopHandler()); - if (upgradeHandler != null) { - builder.withUpgradeStreamHandler(upgradeHandler); - } - return builder.build(); - } - - @Override - protected ChannelHandler newMultiplexer(ChannelHandler upgradeHandler) { - return null; - } -} 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 deleted file mode 100644 index 2abed4dc46..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexCodecTest.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.channel.ChannelHandler; - -public class Http2MultiplexCodecTest extends Http2MultiplexTest { - - @Override - protected Http2FrameCodec newCodec(TestChannelInitializer childChannelInitializer, Http2FrameWriter frameWriter) { - return new Http2MultiplexCodecBuilder(true, childChannelInitializer).frameWriter(frameWriter).build(); - } - - @Override - protected ChannelHandler newMultiplexer(TestChannelInitializer childChannelInitializer) { - return null; - } - - @Override - protected boolean useUserEventForResetFrame() { - return false; - } - - @Override - protected boolean ignoreWindowUpdateFrames() { - return false; - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexHandlerClientUpgradeTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexHandlerClientUpgradeTest.java deleted file mode 100644 index 68e55a6f63..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexHandlerClientUpgradeTest.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.channel.ChannelHandler; - -public class Http2MultiplexHandlerClientUpgradeTest extends Http2MultiplexClientUpgradeTest { - - @Override - protected Http2FrameCodec newCodec(ChannelHandler upgradeHandler) { - return Http2FrameCodecBuilder.forClient().build(); - } - - @Override - protected ChannelHandler newMultiplexer(ChannelHandler upgradeHandler) { - return new Http2MultiplexHandler(new NoopHandler(), upgradeHandler); - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexHandlerTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexHandlerTest.java deleted file mode 100644 index 6bf33bd108..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexHandlerTest.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.channel.ChannelHandler; - -/** - * Unit tests for {@link Http2MultiplexHandler}. - */ -public class Http2MultiplexHandlerTest extends Http2MultiplexTest { - - @Override - protected Http2FrameCodec newCodec(TestChannelInitializer childChannelInitializer, Http2FrameWriter frameWriter) { - return new Http2FrameCodecBuilder(true).frameWriter(frameWriter).build(); - } - - @Override - protected ChannelHandler newMultiplexer(TestChannelInitializer childChannelInitializer) { - return new Http2MultiplexHandler(childChannelInitializer, null); - } - - @Override - protected boolean useUserEventForResetFrame() { - return true; - } - - @Override - protected boolean ignoreWindowUpdateFrames() { - return true; - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexTest.java deleted file mode 100644 index 9085b4ba19..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexTest.java +++ /dev/null @@ -1,1340 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -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.WriteBufferWaterMark; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.http.HttpHeaderNames; -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.util.AsciiString; -import io.netty.util.AttributeKey; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.ImmediateEventExecutor; -import io.netty.util.concurrent.Promise; -import org.hamcrest.CoreMatchers; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; -import org.mockito.Mockito; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; - -import java.net.InetSocketAddress; -import java.nio.channels.ClosedChannelException; -import java.util.ArrayDeque; -import java.util.Queue; -import java.util.UUID; -import java.util.concurrent.CompletionException; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Consumer; - -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 io.netty.util.ReferenceCountUtil.release; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.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.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; - -public abstract class Http2MultiplexTest { - private final Http2Headers request = new DefaultHttp2Headers() - .method(HttpMethod.GET.asciiName()).scheme(HttpScheme.HTTPS.name()) - .authority(new AsciiString("example.org")).path(new AsciiString("/foo")); - - private EmbeddedChannel parentChannel; - private Http2FrameWriter frameWriter; - private Http2FrameInboundWriter frameInboundWriter; - private TestChannelInitializer childChannelInitializer; - private C codec; - - private static final int initialRemoteStreamWindow = 1024; - - protected abstract C newCodec(TestChannelInitializer childChannelInitializer, Http2FrameWriter frameWriter); - protected abstract ChannelHandler newMultiplexer(TestChannelInitializer childChannelInitializer); - - @BeforeEach - public void setUp() { - childChannelInitializer = new TestChannelInitializer(); - parentChannel = new EmbeddedChannel(); - frameInboundWriter = new Http2FrameInboundWriter(parentChannel); - parentChannel.connect(new InetSocketAddress(0)); - frameWriter = Http2TestUtil.mockedFrameWriter(); - codec = newCodec(childChannelInitializer, frameWriter); - parentChannel.pipeline().addLast(codec); - ChannelHandler multiplexer = newMultiplexer(childChannelInitializer); - if (multiplexer != null) { - parentChannel.pipeline().addLast(multiplexer); - } - - parentChannel.pipeline().fireChannelActive(); - - parentChannel.writeInbound(Http2CodecUtil.connectionPrefaceBuf()); - - Http2Settings settings = new Http2Settings().initialWindowSize(initialRemoteStreamWindow); - frameInboundWriter.writeInboundSettings(settings); - - verify(frameWriter).writeSettingsAck(any(ChannelHandlerContext.class)); - - frameInboundWriter.writeInboundSettingsAck(); - - Http2SettingsFrame settingsFrame = parentChannel.readInbound(); - assertNotNull(settingsFrame); - Http2SettingsAckFrame settingsAckFrame = parentChannel.readInbound(); - assertNotNull(settingsAckFrame); - - // Handshake - verify(frameWriter).writeSettings(any(ChannelHandlerContext.class), - anyHttp2Settings()); - } - - @AfterEach - public void tearDown() throws Exception { - if (childChannelInitializer.handler instanceof LastInboundHandler) { - ((LastInboundHandler) childChannelInitializer.handler).finishAndReleaseAll(); - } - parentChannel.finishAndReleaseAll(); - codec = null; - } - - // TODO(buchgr): Flush from child channel - // TODO(buchgr): ChildChannel.childReadComplete() - // TODO(buchgr): GOAWAY Logic - // TODO(buchgr): Test ChannelConfig.setMaxMessagesPerRead - - @Test - public void writeUnknownFrame() { - Http2StreamChannel childChannel = newOutboundStream(new ChannelHandler() { - @Override - public void channelActive(ChannelHandlerContext ctx) { - ctx.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers())); - ctx.writeAndFlush(new DefaultHttp2UnknownFrame((byte) 99, new Http2Flags())); - ctx.fireChannelActive(); - } - }); - assertTrue(childChannel.isActive()); - - verify(frameWriter).writeFrame(any(ChannelHandlerContext.class), eq((byte) 99), eqStreamId(childChannel), - any(Http2Flags.class), any(ByteBuf.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 ChannelHandler() { - @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); - Http2StreamChannel channel = streamChannelRef.get(); - assertEquals(streamId, channel.stream().id()); - return channel; - } - - @Test - public void readUnkownFrame() { - LastInboundHandler handler = new LastInboundHandler(); - - Http2StreamChannel channel = newInboundStream(3, true, handler); - frameInboundWriter.writeInboundFrame((byte) 99, channel.stream().id(), new Http2Flags(), Unpooled.EMPTY_BUFFER); - - // header frame and unknown frame - verifyFramesMultiplexedToCorrectChannel(channel, handler, 2); - - Channel childChannel = newOutboundStream(new ChannelHandler() { }); - assertTrue(childChannel.isActive()); - } - - @Test - public void headerAndDataFramesShouldBeDelivered() { - LastInboundHandler inboundHandler = new LastInboundHandler(); - - 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()); - - assertTrue(inboundHandler.isChannelActive()); - frameInboundWriter.writeInboundData(channel.stream().id(), bb("hello"), 0, false); - frameInboundWriter.writeInboundData(channel.stream().id(), bb("world"), 0, false); - - assertEquals(headersFrame, inboundHandler.readInbound()); - - assertEqualsAndRelease(dataFrame1, inboundHandler.readInbound()); - assertEqualsAndRelease(dataFrame2, inboundHandler.readInbound()); - - assertNull(inboundHandler.readInbound()); - } - - @Test - public void headerMultipleContentLengthValidationShouldPropagate() { - headerMultipleContentLengthValidationShouldPropagate(false); - } - - @Test - public void headerMultipleContentLengthValidationShouldPropagateWithEndStream() { - headerMultipleContentLengthValidationShouldPropagate(true); - } - - private void headerMultipleContentLengthValidationShouldPropagate(boolean endStream) { - final LastInboundHandler inboundHandler = new LastInboundHandler(); - request.addLong(HttpHeaderNames.CONTENT_LENGTH, 0); - request.addLong(HttpHeaderNames.CONTENT_LENGTH, 1); - Http2StreamChannel channel = newInboundStream(3, endStream, inboundHandler); - - assertThrows(StreamException.class, new Executable() { - @Override - public void execute() throws Throwable { - inboundHandler.checkException(); - } - }); - assertNull(inboundHandler.readInbound()); - assertFalse(channel.isActive()); - } - - @Test - public void headerPlusSignContentLengthValidationShouldPropagate() { - headerSignContentLengthValidationShouldPropagateWithEndStream(false, false); - } - - @Test - public void headerPlusSignContentLengthValidationShouldPropagateWithEndStream() { - headerSignContentLengthValidationShouldPropagateWithEndStream(false, true); - } - - @Test - public void headerMinusSignContentLengthValidationShouldPropagate() { - headerSignContentLengthValidationShouldPropagateWithEndStream(true, false); - } - - @Test - public void headerMinusSignContentLengthValidationShouldPropagateWithEndStream() { - headerSignContentLengthValidationShouldPropagateWithEndStream(true, true); - } - - private void headerSignContentLengthValidationShouldPropagateWithEndStream(boolean minus, boolean endStream) { - final LastInboundHandler inboundHandler = new LastInboundHandler(); - request.add(HttpHeaderNames.CONTENT_LENGTH, (minus ? "-" : "+") + 1); - Http2StreamChannel channel = newInboundStream(3, endStream, inboundHandler); - assertThrows(StreamException.class, new Executable() { - @Override - public void execute() throws Throwable { - inboundHandler.checkException(); - } - }); - - assertNull(inboundHandler.readInbound()); - assertFalse(channel.isActive()); - } - - @Test - public void headerContentLengthNotMatchValidationShouldPropagate() { - headerContentLengthNotMatchValidationShouldPropagate(false, false, false); - } - - @Test - public void headerContentLengthNotMatchValidationShouldPropagateWithEndStream() { - headerContentLengthNotMatchValidationShouldPropagate(false, true, false); - } - - @Test - public void headerContentLengthNotMatchValidationShouldPropagateCloseLocal() { - headerContentLengthNotMatchValidationShouldPropagate(true, false, false); - } - - @Test - public void headerContentLengthNotMatchValidationShouldPropagateWithEndStreamCloseLocal() { - headerContentLengthNotMatchValidationShouldPropagate(true, true, false); - } - - @Test - public void headerContentLengthNotMatchValidationShouldPropagateTrailers() { - headerContentLengthNotMatchValidationShouldPropagate(false, false, true); - } - - @Test - public void headerContentLengthNotMatchValidationShouldPropagateWithEndStreamTrailers() { - headerContentLengthNotMatchValidationShouldPropagate(false, true, true); - } - - @Test - public void headerContentLengthNotMatchValidationShouldPropagateCloseLocalTrailers() { - headerContentLengthNotMatchValidationShouldPropagate(true, false, true); - } - - @Test - public void headerContentLengthNotMatchValidationShouldPropagateWithEndStreamCloseLocalTrailers() { - headerContentLengthNotMatchValidationShouldPropagate(true, true, true); - } - - private void headerContentLengthNotMatchValidationShouldPropagate( - boolean closeLocal, boolean endStream, boolean trailer) { - final LastInboundHandler inboundHandler = new LastInboundHandler(); - request.addLong(HttpHeaderNames.CONTENT_LENGTH, 1); - Http2StreamChannel channel = newInboundStream(3, false, inboundHandler); - assertTrue(channel.isActive()); - - if (closeLocal) { - channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers(), true)) - .syncUninterruptibly(); - assertEquals(Http2Stream.State.HALF_CLOSED_LOCAL, channel.stream().state()); - } else { - assertEquals(Http2Stream.State.OPEN, channel.stream().state()); - } - - if (trailer) { - frameInboundWriter.writeInboundHeaders(channel.stream().id(), new DefaultHttp2Headers(), 0, endStream); - } else { - frameInboundWriter.writeInboundData(channel.stream().id(), bb("foo"), 0, endStream); - } - - assertThrows(StreamException.class, new Executable() { - @Override - public void execute() throws Throwable { - inboundHandler.checkException(); - } - }); - - Http2HeadersFrame headersFrame = new DefaultHttp2HeadersFrame(request).stream(channel.stream()); - assertEquals(headersFrame, inboundHandler.readInbound()); - assertNull(inboundHandler.readInbound()); - assertFalse(channel.isActive()); - } - - @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); - - verifyFramesMultiplexedToCorrectChannel(channel1, handler1, 1); - verifyFramesMultiplexedToCorrectChannel(channel2, handler2, 2); - verifyFramesMultiplexedToCorrectChannel(channel3, handler3, 1); - } - - @Test - 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"); - - frameInboundWriter.writeInboundData(channel.stream().id(), tenBytes, 0, true); - - // Verify we marked the bytes as consumed - verify(flowController).consumeBytes(argThat(http2Stream -> http2Stream.id() == channel.stream().id()), eq(10)); - - // headers and data frame - verifyFramesMultiplexedToCorrectChannel(channel, handler, 2); - } - - @Test - public void unhandledHttp2FramesShouldBePropagated() { - Http2PingFrame pingFrame = new DefaultHttp2PingFrame(0); - frameInboundWriter.writeInboundPing(false, 0); - assertEquals(parentChannel.readInbound(), pingFrame); - - DefaultHttp2GoAwayFrame goAwayFrame = new DefaultHttp2GoAwayFrame(1, - parentChannel.alloc().buffer().writeLong(8)); - frameInboundWriter.writeInboundGoAway(0, goAwayFrame.errorCode(), goAwayFrame.content().retainedDuplicate()); - - Http2GoAwayFrame frame = parentChannel.readInbound(); - assertEqualsAndRelease(frame, goAwayFrame); - } - - @Test - public void channelReadShouldRespectAutoRead() { - LastInboundHandler inboundHandler = new LastInboundHandler(); - Http2StreamChannel childChannel = newInboundStream(3, false, inboundHandler); - assertTrue(childChannel.config().isAutoRead()); - Http2HeadersFrame headersFrame = inboundHandler.readInbound(); - assertNotNull(headersFrame); - - childChannel.config().setAutoRead(false); - - frameInboundWriter.writeInboundData(childChannel.stream().id(), bb("hello world"), 0, false); - Http2DataFrame dataFrame0 = inboundHandler.readInbound(); - assertNotNull(dataFrame0); - release(dataFrame0); - - frameInboundWriter.writeInboundData(childChannel.stream().id(), bb("foo"), 0, false); - frameInboundWriter.writeInboundData(childChannel.stream().id(), bb("bar"), 0, false); - - assertNull(inboundHandler.readInbound()); - - childChannel.config().setAutoRead(true); - verifyFramesMultiplexedToCorrectChannel(childChannel, inboundHandler, 2); - } - - @Test - public void channelReadShouldRespectAutoReadAndNotProduceNPE() throws Exception { - LastInboundHandler inboundHandler = new LastInboundHandler(); - Http2StreamChannel childChannel = newInboundStream(3, false, inboundHandler); - assertTrue(childChannel.config().isAutoRead()); - Http2HeadersFrame headersFrame = inboundHandler.readInbound(); - assertNotNull(headersFrame); - - childChannel.config().setAutoRead(false); - childChannel.pipeline().addFirst(new ChannelHandler() { - private int count; - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - ctx.fireChannelRead(msg); - // Close channel after 2 reads so there is still something in the inboundBuffer when the close happens. - if (++count == 2) { - ctx.close(); - } - } - }); - frameInboundWriter.writeInboundData(childChannel.stream().id(), bb("hello world"), 0, false); - Http2DataFrame dataFrame0 = inboundHandler.readInbound(); - assertNotNull(dataFrame0); - release(dataFrame0); - - frameInboundWriter.writeInboundData(childChannel.stream().id(), bb("foo"), 0, false); - frameInboundWriter.writeInboundData(childChannel.stream().id(), bb("bar"), 0, false); - frameInboundWriter.writeInboundData(childChannel.stream().id(), bb("bar"), 0, false); - - assertNull(inboundHandler.readInbound()); - - childChannel.config().setAutoRead(true); - verifyFramesMultiplexedToCorrectChannel(childChannel, inboundHandler, 3); - inboundHandler.checkException(); - } - - @Test - public void readInChannelReadWithoutAutoRead() { - useReadWithoutAutoRead(false); - } - - @Test - public void readInChannelReadCompleteWithoutAutoRead() { - useReadWithoutAutoRead(true); - } - - private void useReadWithoutAutoRead(final boolean readComplete) { - LastInboundHandler inboundHandler = new LastInboundHandler(); - Http2StreamChannel childChannel = newInboundStream(3, false, inboundHandler); - 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 ChannelHandler() { - @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(); - } - } - }); - - 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); - - verifyFramesMultiplexedToCorrectChannel(childChannel, inboundHandler, 6); - } - - private Http2StreamChannel newOutboundStream(ChannelHandler handler) { - Future future = new Http2StreamChannelBootstrap(parentChannel).handler(handler) - .open(); - return future.syncUninterruptibly().getNow(); - } - - /** - * A child channel for an 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() { - LastInboundHandler handler = new LastInboundHandler(); - - Channel childChannel = newOutboundStream(handler); - assertTrue(childChannel.isActive()); - - childChannel.close(); - - assertFalse(childChannel.isOpen()); - assertFalse(childChannel.isActive()); - assertNull(parentChannel.readOutbound()); - } - - @Test - public void outboundStreamShouldWriteResetFrameOnClose_headersSent() { - ChannelHandler handler = new ChannelHandler() { - @Override - public void channelActive(ChannelHandlerContext ctx) { - ctx.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers())); - ctx.fireChannelActive(); - } - }; - - Http2StreamChannel childChannel = newOutboundStream(handler); - assertTrue(childChannel.isActive()); - - childChannel.close(); - verify(frameWriter).writeRstStream(any(ChannelHandlerContext.class), - eqStreamId(childChannel), eq(Http2Error.CANCEL.code())); - } - - @Test - public void outboundStreamShouldNotWriteResetFrameOnClose_IfStreamDidntExist() { - when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(), - any(Http2Headers.class), anyInt(), anyBoolean())).thenAnswer(new Answer>() { - - private boolean headersWritten; - @Override - public Future 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) { - headersWritten = true; - return ImmediateEventExecutor.INSTANCE.newFailedFuture(new Exception("boom")); - } - return ImmediateEventExecutor.INSTANCE.newSucceededFuture(null); - } - }); - - Http2StreamChannel childChannel = newOutboundStream(new ChannelHandler() { - @Override - public void channelActive(ChannelHandlerContext ctx) { - ctx.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers())); - ctx.fireChannelActive(); - } - }); - - assertFalse(childChannel.isActive()); - - childChannel.close(); - // The channel was never active so we should not generate a RST frame. - verify(frameWriter, never()).writeRstStream(any(ChannelHandlerContext.class), - eqStreamId(childChannel), anyLong()); - - assertTrue(parentChannel.outboundMessages().isEmpty()); - } - - @Test - public void inboundRstStreamFireChannelInactive() { - LastInboundHandler inboundHandler = new LastInboundHandler(); - Http2StreamChannel channel = newInboundStream(3, false, inboundHandler); - assertTrue(inboundHandler.isChannelActive()); - 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. - verify(frameWriter, never()).writeRstStream(any(ChannelHandlerContext.class), eqStreamId(channel), - anyLong()); - } - - @Test - public void streamExceptionTriggersChildChannelExceptionAndClose() throws Exception { - final 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(channel.isActive()); - - assertThrows(StreamException.class, new Executable() { - @Override - public void execute() throws Throwable { - inboundHandler.checkException(); - } - }); - } - - @Test - public void streamClosedErrorTranslatedToClosedChannelExceptionOnWrites() throws Exception { - LastInboundHandler inboundHandler = new LastInboundHandler(); - - final Http2StreamChannel childChannel = newOutboundStream(inboundHandler); - assertTrue(childChannel.isActive()); - - Http2Headers headers = new DefaultHttp2Headers(); - when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(), - eq(headers), anyInt(), anyBoolean())).thenAnswer(invocationOnMock -> - ImmediateEventExecutor.INSTANCE.newFailedFuture( - new StreamException(childChannel.stream().id(), Http2Error.STREAM_CLOSED, "Stream Closed"))); - final Future future = childChannel.writeAndFlush( - new DefaultHttp2HeadersFrame(new DefaultHttp2Headers())); - - parentChannel.flush(); - - assertFalse(childChannel.isActive()); - assertFalse(childChannel.isOpen()); - - inboundHandler.checkException(); - - CompletionException e = assertThrows(CompletionException.class, new Executable() { - @Override - public void execute() { - future.syncUninterruptibly(); - } - }); - assertThat(e.getCause(), CoreMatchers.instanceOf(ClosedChannelException.class)); - } - - @Test - public void creatingWritingReadingAndClosingOutboundStreamShouldWork() { - LastInboundHandler inboundHandler = new LastInboundHandler(); - Http2StreamChannel childChannel = newOutboundStream(inboundHandler); - assertTrue(childChannel.isActive()); - assertTrue(inboundHandler.isChannelActive()); - - // Write to the child channel - Http2Headers headers = new DefaultHttp2Headers().scheme("https").method("GET").path("/foo.txt"); - childChannel.writeAndFlush(new DefaultHttp2HeadersFrame(headers)); - - // Read from the child channel - frameInboundWriter.writeInboundHeaders(childChannel.stream().id(), headers, 0, false); - - Http2HeadersFrame headersFrame = inboundHandler.readInbound(); - assertNotNull(headersFrame); - assertEquals(headers, headersFrame.headers()); - - // Close the child channel. - childChannel.close(); - - // An active outbound stream should emit a RST_STREAM frame. - verify(frameWriter).writeRstStream(any(ChannelHandlerContext.class), eqStreamId(childChannel), - anyLong()); - - assertFalse(childChannel.isOpen()); - assertFalse(childChannel.isActive()); - assertFalse(inboundHandler.isChannelActive()); - } - - // Test failing the promise of the first headers frame of an outbound stream. In practice this error case would most - // likely happen due to the max concurrent streams limit being hit or the channel running out of stream identifiers. - // - @Test - public void failedOutboundStreamCreationThrowsAndClosesChannel() throws Exception { - LastInboundHandler handler = new LastInboundHandler(); - Http2StreamChannel childChannel = newOutboundStream(handler); - assertTrue(childChannel.isActive()); - - Http2Headers headers = new DefaultHttp2Headers(); - when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(), - eq(headers), anyInt(), anyBoolean())).thenAnswer(invocationOnMock -> - ImmediateEventExecutor.INSTANCE.newFailedFuture(new Http2NoMoreStreamIdsException())); - - final Future future = childChannel.writeAndFlush(new DefaultHttp2HeadersFrame(headers)); - parentChannel.flush(); - - assertFalse(childChannel.isActive()); - assertFalse(childChannel.isOpen()); - - handler.checkException(); - - CompletionException e = assertThrows(CompletionException.class, new Executable() { - @Override - public void execute() { - future.syncUninterruptibly(); - } - }); - assertThat(e.getCause(), CoreMatchers.instanceOf(Http2NoMoreStreamIdsException.class)); - } - - @Test - public void channelClosedWhenCloseListenerCompletes() { - LastInboundHandler inboundHandler = new LastInboundHandler(); - Http2StreamChannel childChannel = newInboundStream(3, false, inboundHandler); - - assertTrue(childChannel.isOpen()); - assertTrue(childChannel.isActive()); - - final AtomicBoolean channelOpen = new AtomicBoolean(true); - final AtomicBoolean channelActive = new AtomicBoolean(true); - - // Create a promise before actually doing the close, because otherwise we would be adding a listener to a future - // that is already completed because we are using EmbeddedChannel which executes code in the JUnit thread. - Promise p = childChannel.newPromise(); - p.asFuture().addListener(childChannel, (channel, future) -> { - channelOpen.set(channel.isOpen()); - channelActive.set(channel.isActive()); - }); - childChannel.close().cascadeTo(p).syncUninterruptibly(); - - assertFalse(channelOpen.get()); - assertFalse(channelActive.get()); - assertFalse(childChannel.isActive()); - } - - @Test - public void channelClosedWhenChannelClosePromiseCompletes() { - LastInboundHandler inboundHandler = new LastInboundHandler(); - Http2StreamChannel childChannel = newInboundStream(3, false, inboundHandler); - - assertTrue(childChannel.isOpen()); - assertTrue(childChannel.isActive()); - - final AtomicBoolean channelOpen = new AtomicBoolean(true); - final AtomicBoolean channelActive = new AtomicBoolean(true); - - childChannel.closeFuture().addListener(childChannel, (channel, future) -> { - channelOpen.set(channel.isOpen()); - channelActive.set(channel.isActive()); - }); - childChannel.close().syncUninterruptibly(); - - assertFalse(channelOpen.get()); - assertFalse(channelActive.get()); - assertFalse(childChannel.isActive()); - } - - @Test - public void channelClosedWhenWriteFutureFails() { - final Queue> writePromises = new ArrayDeque<>(); - - LastInboundHandler inboundHandler = new LastInboundHandler(); - Http2StreamChannel childChannel = newInboundStream(3, false, inboundHandler); - - assertTrue(childChannel.isOpen()); - assertTrue(childChannel.isActive()); - - final AtomicBoolean channelOpen = new AtomicBoolean(true); - final AtomicBoolean channelActive = new AtomicBoolean(true); - - Http2Headers headers = new DefaultHttp2Headers(); - when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(), - eq(headers), anyInt(), anyBoolean())).thenAnswer(invocationOnMock -> { - Promise promise = ImmediateEventExecutor.INSTANCE.newPromise(); - writePromises.offer(promise); - return promise; - }); - - Future f = childChannel.writeAndFlush(new DefaultHttp2HeadersFrame(headers)); - assertFalse(f.isDone()); - f.addListener(childChannel, (channel, future)-> { - channelOpen.set(channel.isOpen()); - channelActive.set(channel.isActive()); - }); - - Promise first = writePromises.poll(); - first.setFailure(new ClosedChannelException()); - f.awaitUninterruptibly(); - - assertFalse(channelOpen.get()); - assertFalse(channelActive.get()); - assertFalse(childChannel.isActive()); - } - - @Test - public void channelClosedTwiceMarksPromiseAsSuccessful() { - LastInboundHandler inboundHandler = new LastInboundHandler(); - Http2StreamChannel childChannel = newInboundStream(3, false, inboundHandler); - - assertTrue(childChannel.isOpen()); - assertTrue(childChannel.isActive()); - childChannel.close().syncUninterruptibly(); - childChannel.close().syncUninterruptibly(); - - assertFalse(childChannel.isOpen()); - assertFalse(childChannel.isActive()); - } - - @Test - public void settingChannelOptsAndAttrs() { - AttributeKey key = AttributeKey.newInstance(UUID.randomUUID().toString()); - - Channel childChannel = newOutboundStream(new ChannelHandler() { }); - childChannel.config().setAutoRead(false).setWriteSpinCount(1000); - childChannel.attr(key).set("bar"); - assertFalse(childChannel.config().isAutoRead()); - assertEquals(1000, childChannel.config().getWriteSpinCount()); - assertEquals("bar", childChannel.attr(key).get()); - } - - @Test - public void outboundFlowControlWritability() { - Http2StreamChannel childChannel = newOutboundStream(new ChannelHandler() { }); - assertTrue(childChannel.isActive()); - - assertTrue(childChannel.isWritable()); - childChannel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers())); - parentChannel.flush(); - - // Test for initial window size - assertTrue(initialRemoteStreamWindow < childChannel.config().getWriteBufferHighWaterMark()); - - assertTrue(childChannel.isWritable()); - childChannel.write(new DefaultHttp2DataFrame(Unpooled.buffer().writeZero(16 * 1024 * 1024))); - assertEquals(0, childChannel.bytesBeforeUnwritable()); - assertFalse(childChannel.isWritable()); - } - - @Test - public void writabilityOfParentIsRespected() { - Http2StreamChannel childChannel = newOutboundStream(new ChannelHandler() { }); - childChannel.config().setWriteBufferWaterMark(new WriteBufferWaterMark(2048, 4096)); - parentChannel.config().setWriteBufferWaterMark(new WriteBufferWaterMark(256, 512)); - assertTrue(childChannel.isWritable()); - assertTrue(parentChannel.isActive()); - - childChannel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers())); - parentChannel.flush(); - - assertTrue(childChannel.isWritable()); - childChannel.write(new DefaultHttp2DataFrame(Unpooled.buffer().writeZero(256))); - assertTrue(childChannel.isWritable()); - childChannel.writeAndFlush(new DefaultHttp2DataFrame(Unpooled.buffer().writeZero(512))); - - long bytesBeforeUnwritable = childChannel.bytesBeforeUnwritable(); - assertNotEquals(0, bytesBeforeUnwritable); - // Add something to the ChannelOutboundBuffer of the parent to simulate queuing in the parents channel buffer - // and verify that this only affect the writability of the parent channel while the child stays writable - // until it used all of its credits. - parentChannel.unsafe().outboundBuffer().addMessage( - Unpooled.buffer().writeZero(800), 800, parentChannel.newPromise()); - assertFalse(parentChannel.isWritable()); - - assertTrue(childChannel.isWritable()); - assertEquals(4096, childChannel.bytesBeforeUnwritable()); - - // Flush everything which simulate writing everything to the socket. - parentChannel.flush(); - assertTrue(parentChannel.isWritable()); - assertTrue(childChannel.isWritable()); - assertEquals(bytesBeforeUnwritable, childChannel.bytesBeforeUnwritable()); - - Future future = childChannel.writeAndFlush(new DefaultHttp2DataFrame( - Unpooled.buffer().writeZero((int) bytesBeforeUnwritable))); - assertFalse(childChannel.isWritable()); - assertTrue(parentChannel.isWritable()); - - parentChannel.flush(); - assertFalse(future.isDone()); - assertTrue(parentChannel.isWritable()); - assertFalse(childChannel.isWritable()); - - // Now write an window update frame for the stream which then should ensure we will flush the bytes that were - // queued in the RemoteFlowController before for the stream. - frameInboundWriter.writeInboundWindowUpdate(childChannel.stream().id(), (int) bytesBeforeUnwritable); - assertTrue(childChannel.isWritable()); - assertTrue(future.isDone()); - } - - @Test - public void channelClosedWhenInactiveFired() { - LastInboundHandler inboundHandler = new LastInboundHandler(); - Http2StreamChannel childChannel = newInboundStream(3, false, inboundHandler); - - final AtomicBoolean channelOpen = new AtomicBoolean(false); - final AtomicBoolean channelActive = new AtomicBoolean(false); - assertTrue(childChannel.isOpen()); - assertTrue(childChannel.isActive()); - - childChannel.pipeline().addLast(new ChannelHandler() { - @Override - public void channelInactive(ChannelHandlerContext ctx) { - channelOpen.set(ctx.channel().isOpen()); - channelActive.set(ctx.channel().isActive()); - - ctx.fireChannelInactive(); - } - }); - - childChannel.close().syncUninterruptibly(); - assertFalse(channelOpen.get()); - assertFalse(channelActive.get()); - } - - @Test - public void channelInactiveHappensAfterExceptionCaughtEvents() { - 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(new ChannelHandler() { - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - ctx.close(); - throw new Exception("exception"); - } - }); - - childChannel.pipeline().addLast(new ChannelHandler() { - - @Override - public void channelInactive(ChannelHandlerContext ctx) { - channelInactive.set(count.getAndIncrement()); - ctx.fireChannelInactive(); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - exceptionCaught.set(count.getAndIncrement()); - ctx.fireExceptionCaught(cause); - } - - @Override - public void channelUnregistered(ChannelHandlerContext ctx) { - channelUnregistered.set(count.getAndIncrement()); - ctx.fireChannelUnregistered(); - } - }); - - childChannel.pipeline().fireUserEventTriggered(new Object()); - - // 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()); - } - - @Test - public void callUnsafeCloseMultipleTimes() { - LastInboundHandler inboundHandler = new LastInboundHandler(); - Http2StreamChannel childChannel = newInboundStream(3, false, inboundHandler); - childChannel.unsafe().close(childChannel.newPromise()); - - Promise promise = childChannel.newPromise(); - childChannel.unsafe().close(promise); - promise.asFuture().syncUninterruptibly(); - childChannel.closeFuture().syncUninterruptibly(); - } - - @Test - public void endOfStreamDoesNotDiscardData() { - AtomicInteger numReads = new AtomicInteger(1); - final AtomicBoolean shouldDisableAutoRead = new AtomicBoolean(); - Consumer ctxConsumer = obj -> { - if (shouldDisableAutoRead.get()) { - obj.channel().config().setAutoRead(false); - } - }; - LastInboundHandler inboundHandler = new LastInboundHandler(ctxConsumer); - Http2StreamChannel childChannel = newInboundStream(3, false, numReads, inboundHandler); - childChannel.config().setAutoRead(false); - - 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 ChannelHandler() { - @Override - public void channelReadComplete(ChannelHandlerContext ctx) { - // 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); - - assertEqualsAndRelease(dataFrame1, inboundHandler.readInbound()); - - // Deliver frames, and then a stream closed while read is inactive. - 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); - - frameInboundWriter.writeInboundRstStream(childChannel.stream().id(), Http2Error.NO_ERROR.code()); - - // Detecting EOS should flush all pending data regardless of read calls. - assertEqualsAndRelease(dataFrame2, inboundHandler.readInbound()); - assertNull(inboundHandler.readInbound()); - - // As we limited the number to 1 we also need to call read() again. - childChannel.read(); - - assertEqualsAndRelease(dataFrame3, inboundHandler.readInbound()); - assertEqualsAndRelease(dataFrame4, inboundHandler.readInbound()); - - Http2ResetFrame resetFrame = useUserEventForResetFrame() ? inboundHandler.readUserEvent() : - 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(); - } - - protected abstract boolean useUserEventForResetFrame(); - - protected abstract boolean ignoreWindowUpdateFrames(); - - @Test - public void windowUpdateFrames() { - AtomicInteger numReads = new AtomicInteger(1); - LastInboundHandler inboundHandler = new LastInboundHandler(); - Http2StreamChannel childChannel = newInboundStream(3, false, numReads, inboundHandler); - - assertEquals(new DefaultHttp2HeadersFrame(request).stream(childChannel.stream()), inboundHandler.readInbound()); - - frameInboundWriter.writeInboundWindowUpdate(childChannel.stream().id(), 4); - - Http2WindowUpdateFrame updateFrame = inboundHandler.readInbound(); - if (ignoreWindowUpdateFrames()) { - assertNull(updateFrame); - } else { - assertEquals(new DefaultHttp2WindowUpdateFrame(4).stream(childChannel.stream()), updateFrame); - } - - frameInboundWriter.writeInboundWindowUpdate(Http2CodecUtil.CONNECTION_STREAM_ID, 6); - - assertNull(parentChannel.readInbound()); - childChannel.close().syncUninterruptibly(); - } - - @Test - public void childQueueIsDrainedAndNewDataIsDispatchedInParentReadLoopAutoRead() { - AtomicInteger numReads = new AtomicInteger(1); - final AtomicInteger channelReadCompleteCount = new AtomicInteger(0); - final AtomicBoolean shouldDisableAutoRead = new AtomicBoolean(); - Consumer ctxConsumer = obj -> { - channelReadCompleteCount.incrementAndGet(); - if (shouldDisableAutoRead.get()) { - obj.channel().config().setAutoRead(false); - } - }; - LastInboundHandler inboundHandler = new LastInboundHandler(ctxConsumer); - Http2StreamChannel childChannel = newInboundStream(3, false, numReads, inboundHandler); - childChannel.config().setAutoRead(false); - - 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 ChannelHandler() { - @Override - public void channelReadComplete(ChannelHandlerContext ctx) { - // 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); - - 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. - frameInboundWriter.writeInboundData(childChannel.stream().id(), bb("2"), 0, false); - - numReads.set(10); - shouldDisableAutoRead.set(true); - childChannel.config().setAutoRead(true); - - 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. - 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()); - } - - @Test - public void childQueueIsDrainedAndNewDataIsDispatchedInParentReadLoopNoAutoRead() { - final AtomicInteger numReads = new AtomicInteger(1); - final AtomicInteger channelReadCompleteCount = new AtomicInteger(0); - final AtomicBoolean shouldDisableAutoRead = new AtomicBoolean(); - Consumer ctxConsumer = obj -> { - channelReadCompleteCount.incrementAndGet(); - if (shouldDisableAutoRead.get()) { - obj.channel().config().setAutoRead(false); - } - }; - final LastInboundHandler inboundHandler = new LastInboundHandler(ctxConsumer); - Http2StreamChannel childChannel = newInboundStream(3, false, numReads, inboundHandler); - childChannel.config().setAutoRead(false); - - 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 ChannelHandler() { - @Override - public void channelReadComplete(ChannelHandlerContext ctx) { - // 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); - - 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. - frameInboundWriter.writeInboundData(childChannel.stream().id(), bb("2"), 0, false); - - numReads.set(2); - childChannel.read(); - - 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(). - frameInboundWriter.writeInboundData(childChannel.stream().id(), bb("3"), 0, false); - - assertEqualsAndRelease(dataFrame3, inboundHandler.readInbound()); - - frameInboundWriter.writeInboundData(childChannel.stream().id(), bb("4"), 0, false); - assertNull(inboundHandler.readInbound()); - - childChannel.read(); - - 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()); - } - - @Test - public void useReadWithoutAutoReadInRead() { - useReadWithoutAutoReadBuffered(false); - } - - @Test - public void useReadWithoutAutoReadInReadComplete() { - useReadWithoutAutoReadBuffered(true); - } - - private void useReadWithoutAutoReadBuffered(final boolean triggerOnReadComplete) { - LastInboundHandler inboundHandler = new LastInboundHandler(); - Http2StreamChannel childChannel = newInboundStream(3, false, inboundHandler); - assertTrue(childChannel.config().isAutoRead()); - childChannel.config().setAutoRead(false); - assertFalse(childChannel.config().isAutoRead()); - - Http2HeadersFrame headersFrame = inboundHandler.readInbound(); - assertNotNull(headersFrame); - - // Write some bytes to get the channel into the idle state with buffered data and also verify we - // do not dispatch it until we receive a read() call. - 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); - - // Add a handler which will request reads. - childChannel.pipeline().addFirst(new ChannelHandler() { - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) { - ctx.fireChannelReadComplete(); - if (triggerOnReadComplete) { - ctx.read(); - ctx.read(); - } - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - ctx.fireChannelRead(msg); - if (!triggerOnReadComplete) { - ctx.read(); - ctx.read(); - } - } - }); - - inboundHandler.channel().read(); - - verifyFramesMultiplexedToCorrectChannel(childChannel, inboundHandler, 3); - - frameInboundWriter.writeInboundData(childChannel.stream().id(), bb("hello world2"), 0, false); - frameInboundWriter.writeInboundData(childChannel.stream().id(), bb("foo2"), 0, false); - frameInboundWriter.writeInboundData(childChannel.stream().id(), bb("bar2"), 0, true); - - verifyFramesMultiplexedToCorrectChannel(childChannel, inboundHandler, 3); - } - - private static final class FlushSniffer implements ChannelHandler { - - private boolean didFlush; - - public boolean checkFlush() { - boolean r = didFlush; - didFlush = false; - return r; - } - - @Override - public void flush(ChannelHandlerContext ctx) { - didFlush = true; - ctx.flush(); - } - } - - @Test - public void windowUpdatesAreFlushed() { - LastInboundHandler inboundHandler = new LastInboundHandler(); - FlushSniffer flushSniffer = new FlushSniffer(); - parentChannel.pipeline().addFirst(flushSniffer); - - Http2StreamChannel childChannel = newInboundStream(3, false, inboundHandler); - assertTrue(childChannel.config().isAutoRead()); - childChannel.config().setAutoRead(false); - assertFalse(childChannel.config().isAutoRead()); - - Http2HeadersFrame headersFrame = inboundHandler.readInbound(); - assertNotNull(headersFrame); - - assertTrue(flushSniffer.checkFlush()); - - // Write some bytes to get the channel into the idle state with buffered data and also verify we - // do not dispatch it until we receive a read() call. - frameInboundWriter.writeInboundData(childChannel.stream().id(), bb(16 * 1024), 0, false); - frameInboundWriter.writeInboundData(childChannel.stream().id(), bb(16 * 1024), 0, false); - assertTrue(flushSniffer.checkFlush()); - - verify(frameWriter, never()) - .writeWindowUpdate(any(ChannelHandlerContext.class), anyInt(), anyInt()); - // only the first one was read because it was legacy auto-read behavior. - verifyFramesMultiplexedToCorrectChannel(childChannel, inboundHandler, 1); - assertFalse(flushSniffer.checkFlush()); - - // Trigger a read of the second frame. - childChannel.read(); - verifyFramesMultiplexedToCorrectChannel(childChannel, inboundHandler, 1); - // We expect a flush here because the StreamChannel will flush the smaller increment but the - // connection will collect the bytes and decide not to send a wire level frame until more are consumed. - assertTrue(flushSniffer.checkFlush()); - verify(frameWriter, never()) - .writeWindowUpdate(any(ChannelHandlerContext.class), anyInt(), anyInt()); - - // Call read one more time which should trigger the writing of the flow control update. - childChannel.read(); - verify(frameWriter) - .writeWindowUpdate(any(ChannelHandlerContext.class), eq(0), eq(32 * 1024)); - verify(frameWriter) - .writeWindowUpdate(any(ChannelHandlerContext.class), eq(childChannel.stream().id()), - eq(32 * 1024)); - assertTrue(flushSniffer.checkFlush()); - } - - private static void verifyFramesMultiplexedToCorrectChannel(Http2StreamChannel streamChannel, - LastInboundHandler inboundHandler, - int numFrames) { - for (int i = 0; i < numFrames; i++) { - Http2StreamFrame frame = inboundHandler.readInbound(); - assertNotNull(frame, i + " out of " + numFrames + " received"); - assertEquals(streamChannel.stream(), frame.stream()); - release(frame); - } - assertNull(inboundHandler.readInbound()); - } - - private static int eqStreamId(Http2StreamChannel channel) { - return eq(channel.stream().id()); - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexTransportTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexTransportTest.java deleted file mode 100644 index 8da8ca31b5..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexTransportTest.java +++ /dev/null @@ -1,565 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.bootstrap.Bootstrap; -import io.netty.bootstrap.ServerBootstrap; -import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerAdapter; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.ssl.ApplicationProtocolConfig; -import io.netty.handler.ssl.ApplicationProtocolNames; -import io.netty.handler.ssl.ApplicationProtocolNegotiationHandler; -import io.netty.handler.ssl.ClientAuth; -import io.netty.handler.ssl.OpenSsl; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.SslHandshakeCompletionEvent; -import io.netty.handler.ssl.SslProvider; -import io.netty.handler.ssl.SupportedCipherSuiteFilter; -import io.netty.handler.ssl.util.InsecureTrustManagerFactory; -import io.netty.handler.ssl.util.SelfSignedCertificate; -import io.netty.util.CharsetUtil; -import io.netty.util.NetUtil; -import io.netty.util.ReferenceCountUtil; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; -import org.junit.jupiter.api.condition.DisabledOnOs; -import org.junit.jupiter.api.condition.OS; - -import javax.net.ssl.SSLException; -import javax.net.ssl.X509TrustManager; -import java.net.InetSocketAddress; -import java.security.cert.CertificateException; -import java.security.cert.CertificateExpiredException; -import java.security.cert.X509Certificate; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.atomic.AtomicReference; - -import static java.util.concurrent.TimeUnit.MILLISECONDS; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assumptions.assumeTrue; - -public class Http2MultiplexTransportTest { - private static final ChannelHandler DISCARD_HANDLER = new ChannelHandlerAdapter() { - - @Override - public boolean isSharable() { - return true; - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - ReferenceCountUtil.release(msg); - } - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { - ReferenceCountUtil.release(evt); - } - }; - - private EventLoopGroup eventLoopGroup; - private Channel clientChannel; - private Channel serverChannel; - private Channel serverConnectedChannel; - - @BeforeEach - public void setup() { - eventLoopGroup = new MultithreadEventLoopGroup(NioHandler.newFactory()); - } - - @AfterEach - public void teardown() { - if (clientChannel != null) { - clientChannel.close(); - } - if (serverChannel != null) { - serverChannel.close(); - } - if (serverConnectedChannel != null) { - serverConnectedChannel.close(); - } - eventLoopGroup.shutdownGracefully(0, 0, MILLISECONDS); - } - - @Test - @Timeout(value = 10000, unit = MILLISECONDS) - public void asyncSettingsAckWithMultiplexCodec() throws Exception { - asyncSettingsAck0(new Http2MultiplexCodecBuilder(true, DISCARD_HANDLER).build(), null); - } - - @Test - @Timeout(value = 10000, unit = MILLISECONDS) - public void asyncSettingsAckWithMultiplexHandler() throws Exception { - asyncSettingsAck0(new Http2FrameCodecBuilder(true).build(), - new Http2MultiplexHandler(DISCARD_HANDLER)); - } - - private void asyncSettingsAck0(final Http2FrameCodec codec, final ChannelHandler multiplexer) - throws Exception { - // The client expects 2 settings frames. One from the connection setup and one from this test. - final CountDownLatch serverAckOneLatch = new CountDownLatch(1); - final CountDownLatch serverAckAllLatch = new CountDownLatch(2); - final CountDownLatch clientSettingsLatch = new CountDownLatch(2); - final CountDownLatch serverConnectedChannelLatch = new CountDownLatch(1); - final AtomicReference serverConnectedChannelRef = new AtomicReference(); - ServerBootstrap sb = new ServerBootstrap(); - sb.group(eventLoopGroup); - sb.channel(NioServerSocketChannel.class); - sb.childHandler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) { - ch.pipeline().addLast(codec); - if (multiplexer != null) { - ch.pipeline().addLast(multiplexer); - } - ch.pipeline().addLast(new ChannelHandler() { - @Override - public void channelActive(ChannelHandlerContext ctx) { - serverConnectedChannelRef.set(ctx.channel()); - serverConnectedChannelLatch.countDown(); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - if (msg instanceof Http2SettingsAckFrame) { - serverAckOneLatch.countDown(); - serverAckAllLatch.countDown(); - } - ReferenceCountUtil.release(msg); - } - }); - } - }); - serverChannel = sb.bind(new InetSocketAddress(NetUtil.LOCALHOST, 0)).get(); - - Bootstrap bs = new Bootstrap(); - bs.group(eventLoopGroup); - bs.channel(NioSocketChannel.class); - bs.handler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) { - ch.pipeline().addLast(Http2MultiplexCodecBuilder - .forClient(DISCARD_HANDLER).autoAckSettingsFrame(false).build()); - ch.pipeline().addLast(new ChannelHandler() { - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - if (msg instanceof Http2SettingsFrame) { - clientSettingsLatch.countDown(); - } - ReferenceCountUtil.release(msg); - } - }); - } - }); - clientChannel = bs.connect(serverChannel.localAddress()).get(); - serverConnectedChannelLatch.await(); - serverConnectedChannel = serverConnectedChannelRef.get(); - - serverConnectedChannel.writeAndFlush(new DefaultHttp2SettingsFrame(new Http2Settings() - .maxConcurrentStreams(10))).sync(); - - clientSettingsLatch.await(); - - // We expect a timeout here because we want to asynchronously generate the SETTINGS ACK below. - assertFalse(serverAckOneLatch.await(300, MILLISECONDS)); - - // We expect 2 settings frames, the initial settings frame during connection establishment and the setting frame - // written in this test. We should ack both of these settings frames. - clientChannel.writeAndFlush(Http2SettingsAckFrame.INSTANCE).sync(); - clientChannel.writeAndFlush(Http2SettingsAckFrame.INSTANCE).sync(); - - serverAckAllLatch.await(); - } - - @Test - @Timeout(value = 5000L, unit = MILLISECONDS) - public void testFlushNotDiscarded() throws Exception { - final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); - - try { - ServerBootstrap sb = new ServerBootstrap(); - sb.group(eventLoopGroup); - sb.channel(NioServerSocketChannel.class); - sb.childHandler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) { - ch.pipeline().addLast(new Http2FrameCodecBuilder(true).build()); - ch.pipeline().addLast(new Http2MultiplexHandler(new ChannelHandler() { - @Override - public void channelRead(final ChannelHandlerContext ctx, Object msg) { - if (msg instanceof Http2HeadersFrame && ((Http2HeadersFrame) msg).isEndStream()) { - executorService.schedule(() -> { - ctx.writeAndFlush(new DefaultHttp2HeadersFrame( - new DefaultHttp2Headers(), false)).addListener(future -> { - ctx.write(new DefaultHttp2DataFrame( - Unpooled.copiedBuffer("Hello World", - CharsetUtil.US_ASCII), true)); - ctx.channel().executor().execute(ctx::flush); - }); - }, 500, MILLISECONDS); - } - ReferenceCountUtil.release(msg); - } - })); - } - }); - serverChannel = sb.bind(new InetSocketAddress(NetUtil.LOCALHOST, 0)).get(); - - final CountDownLatch latch = new CountDownLatch(1); - Bootstrap bs = new Bootstrap(); - bs.group(eventLoopGroup); - bs.channel(NioSocketChannel.class); - bs.handler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) { - ch.pipeline().addLast(new Http2FrameCodecBuilder(false).build()); - ch.pipeline().addLast(new Http2MultiplexHandler(DISCARD_HANDLER)); - } - }); - clientChannel = bs.connect(serverChannel.localAddress()).get(); - Http2StreamChannelBootstrap h2Bootstrap = new Http2StreamChannelBootstrap(clientChannel); - h2Bootstrap.handler(new ChannelHandler() { - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - if (msg instanceof Http2DataFrame && ((Http2DataFrame) msg).isEndStream()) { - latch.countDown(); - } - ReferenceCountUtil.release(msg); - } - }); - Http2StreamChannel streamChannel = h2Bootstrap.open().syncUninterruptibly().getNow(); - streamChannel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers(), true)) - .syncUninterruptibly(); - - latch.await(); - } finally { - executorService.shutdown(); - } - } - - @Test - @Timeout(value = 5000L, unit = MILLISECONDS) - public void testSSLExceptionOpenSslTLSv12() throws Exception { - testSslException(SslProvider.OPENSSL, false); - } - - @Test - @Timeout(value = 5000L, unit = MILLISECONDS) - public void testSSLExceptionOpenSslTLSv13() throws Exception { - testSslException(SslProvider.OPENSSL, true); - } - - @Disabled("JDK SSLEngine does not produce an alert") - @Test - @Timeout(value = 5000L, unit = MILLISECONDS) - public void testSSLExceptionJDKTLSv12() throws Exception { - testSslException(SslProvider.JDK, false); - } - - @Disabled("JDK SSLEngine does not produce an alert") - @Test - @Timeout(value = 5000L, unit = MILLISECONDS) - public void testSSLExceptionJDKTLSv13() throws Exception { - testSslException(SslProvider.JDK, true); - } - - private void testSslException(SslProvider provider, final boolean tlsv13) throws Exception { - assumeTrue(SslProvider.isAlpnSupported(provider)); - if (tlsv13) { - assumeTrue(SslProvider.isTlsv13Supported(provider)); - } - final String protocol = tlsv13 ? "TLSv1.3" : "TLSv1.2"; - SelfSignedCertificate ssc = null; - try { - ssc = new SelfSignedCertificate(); - final SslContext sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .trustManager(new X509TrustManager() { - @Override - public void checkClientTrusted(X509Certificate[] chain, String authType) - throws CertificateException { - throw new CertificateExpiredException(); - } - - @Override - public void checkServerTrusted(X509Certificate[] chain, String authType) - throws CertificateException { - throw new CertificateExpiredException(); - } - - @Override - public X509Certificate[] getAcceptedIssuers() { - return new X509Certificate[0]; - } - }).sslProvider(provider) - .ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE) - .protocols(protocol) - .applicationProtocolConfig(new ApplicationProtocolConfig( - ApplicationProtocolConfig.Protocol.ALPN, - // NO_ADVERTISE is currently the only mode supported by both OpenSsl and JDK providers. - ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE, - // ACCEPT is currently the only mode supported by both OpenSsl and JDK providers. - ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT, - ApplicationProtocolNames.HTTP_2, - ApplicationProtocolNames.HTTP_1_1)).clientAuth(ClientAuth.REQUIRE) - .build(); - - ServerBootstrap sb = new ServerBootstrap(); - sb.group(eventLoopGroup); - sb.channel(NioServerSocketChannel.class); - sb.childHandler(new ChannelInitializer() { - - @Override - protected void initChannel(Channel ch) { - ch.pipeline().addLast(sslCtx.newHandler(ch.alloc())); - ch.pipeline().addLast(new Http2FrameCodecBuilder(true).build()); - ch.pipeline().addLast(new Http2MultiplexHandler(DISCARD_HANDLER)); - } - }); - serverChannel = sb.bind(new InetSocketAddress(NetUtil.LOCALHOST, 0)).get(); - - final SslContext clientCtx = SslContextBuilder.forClient() - .keyManager(ssc.key(), ssc.cert()) - .sslProvider(provider) - /* NOTE: the cipher filter may not include all ciphers required by the HTTP/2 specification. - * Please refer to the HTTP/2 specification for cipher requirements. */ - .ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE) - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .protocols(protocol) - .applicationProtocolConfig(new ApplicationProtocolConfig( - ApplicationProtocolConfig.Protocol.ALPN, - // NO_ADVERTISE is currently the only mode supported by both OpenSsl and JDK providers. - ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE, - // ACCEPT is currently the only mode supported by both OpenSsl and JDK providers. - ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT, - ApplicationProtocolNames.HTTP_2, - ApplicationProtocolNames.HTTP_1_1)) - .build(); - - final CountDownLatch latch = new CountDownLatch(2); - final AtomicReference errorRef = new AtomicReference(); - Bootstrap bs = new Bootstrap(); - bs.group(eventLoopGroup); - bs.channel(NioSocketChannel.class); - bs.handler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) { - ch.pipeline().addLast(clientCtx.newHandler(ch.alloc())); - ch.pipeline().addLast(new Http2FrameCodecBuilder(false).build()); - ch.pipeline().addLast(new Http2MultiplexHandler(DISCARD_HANDLER)); - ch.pipeline().addLast(new ChannelHandler() { - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { - if (evt instanceof SslHandshakeCompletionEvent) { - SslHandshakeCompletionEvent handshakeCompletionEvent = - (SslHandshakeCompletionEvent) evt; - if (handshakeCompletionEvent.isSuccess()) { - // In case of TLSv1.3 we should succeed the handshake. The alert for - // the mTLS failure will be send in the next round-trip. - if (!tlsv13) { - errorRef.set(new AssertionError("TLSv1.3 expected")); - } - - Http2StreamChannelBootstrap h2Bootstrap = - new Http2StreamChannelBootstrap(ctx.channel()); - h2Bootstrap.handler(new ChannelHandler() { - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - if (cause.getCause() instanceof SSLException) { - latch.countDown(); - } - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) { - latch.countDown(); - } - }); - h2Bootstrap.open().addListener(future -> { - if (future.isSuccess()) { - future.getNow().writeAndFlush(new DefaultHttp2HeadersFrame( - new DefaultHttp2Headers(), false)); - } - }); - - } else if (handshakeCompletionEvent.cause() instanceof SSLException) { - // In case of TLSv1.2 we should never see the handshake succeed as the alert for - // the mTLS failure will be send in the same round-trip. - if (tlsv13) { - errorRef.set(new AssertionError("TLSv1.2 expected")); - } - latch.countDown(); - latch.countDown(); - } - } - } - }); - } - }); - clientChannel = bs.connect(serverChannel.localAddress()).get(); - latch.await(); - AssertionError error = errorRef.get(); - if (error != null) { - throw error; - } - } finally { - if (ssc != null) { - ssc.delete(); - } - } - } - - @Test - @DisabledOnOs(value = OS.WINDOWS, disabledReason = "See: https://github.com/netty/netty/issues/11542") - @Timeout(value = 5000L, unit = MILLISECONDS) - public void testFireChannelReadAfterHandshakeSuccess_JDK() throws Exception { - assumeTrue(SslProvider.isAlpnSupported(SslProvider.JDK)); - testFireChannelReadAfterHandshakeSuccess(SslProvider.JDK); - } - - @Disabled("This fails atm... needs investigation") - @Test - @DisabledOnOs(value = OS.WINDOWS, disabledReason = "See: https://github.com/netty/netty/issues/11542") - @Timeout(value = 5000L, unit = MILLISECONDS) - public void testFireChannelReadAfterHandshakeSuccess_OPENSSL() throws Exception { - assumeTrue(OpenSsl.isAvailable()); - assumeTrue(SslProvider.isAlpnSupported(SslProvider.OPENSSL)); - testFireChannelReadAfterHandshakeSuccess(SslProvider.OPENSSL); - } - - private void testFireChannelReadAfterHandshakeSuccess(SslProvider provider) throws Exception { - SelfSignedCertificate ssc = null; - try { - ssc = new SelfSignedCertificate(); - final SslContext serverCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(provider) - .ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE) - .applicationProtocolConfig(new ApplicationProtocolConfig( - ApplicationProtocolConfig.Protocol.ALPN, - ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE, - ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT, - ApplicationProtocolNames.HTTP_2, - ApplicationProtocolNames.HTTP_1_1)) - .build(); - - ServerBootstrap sb = new ServerBootstrap(); - sb.group(eventLoopGroup); - sb.channel(NioServerSocketChannel.class); - sb.childHandler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) { - ch.pipeline().addLast(serverCtx.newHandler(ch.alloc())); - ch.pipeline().addLast(new ApplicationProtocolNegotiationHandler(ApplicationProtocolNames.HTTP_1_1) { - @Override - protected void configurePipeline(ChannelHandlerContext ctx, String protocol) { - ctx.pipeline().addLast(new Http2FrameCodecBuilder(true).build()); - ctx.pipeline().addLast(new Http2MultiplexHandler(new ChannelHandler() { - @Override - public void channelRead(final ChannelHandlerContext ctx, Object msg) { - if (msg instanceof Http2HeadersFrame && ((Http2HeadersFrame) msg).isEndStream()) { - ctx.writeAndFlush(new DefaultHttp2HeadersFrame( - new DefaultHttp2Headers(), false)) - .addListener(future -> { - ctx.writeAndFlush(new DefaultHttp2DataFrame( - Unpooled.copiedBuffer("Hello World", CharsetUtil.US_ASCII), - true)); - }); - } - ReferenceCountUtil.release(msg); - } - })); - } - }); - } - }); - serverChannel = sb.bind(new InetSocketAddress(NetUtil.LOCALHOST, 0)).get(); - - final SslContext clientCtx = SslContextBuilder.forClient() - .sslProvider(provider) - .ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE) - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .applicationProtocolConfig(new ApplicationProtocolConfig( - ApplicationProtocolConfig.Protocol.ALPN, - ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE, - ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT, - ApplicationProtocolNames.HTTP_2, - ApplicationProtocolNames.HTTP_1_1)) - .build(); - - final CountDownLatch latch = new CountDownLatch(1); - Bootstrap bs = new Bootstrap(); - bs.group(eventLoopGroup); - bs.channel(NioSocketChannel.class); - bs.handler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) { - ch.pipeline().addLast(clientCtx.newHandler(ch.alloc())); - ch.pipeline().addLast(new Http2FrameCodecBuilder(false).build()); - ch.pipeline().addLast(new Http2MultiplexHandler(DISCARD_HANDLER)); - ch.pipeline().addLast(new ChannelHandler() { - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { - if (evt instanceof SslHandshakeCompletionEvent) { - SslHandshakeCompletionEvent handshakeCompletionEvent = - (SslHandshakeCompletionEvent) evt; - if (handshakeCompletionEvent.isSuccess()) { - Http2StreamChannelBootstrap h2Bootstrap = - new Http2StreamChannelBootstrap(clientChannel); - h2Bootstrap.handler(new ChannelHandler() { - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - if (msg instanceof Http2DataFrame && ((Http2DataFrame) msg).isEndStream()) { - latch.countDown(); - } - ReferenceCountUtil.release(msg); - } - }); - h2Bootstrap.open().addListener(future -> { - if (future.isSuccess()) { - future.getNow().writeAndFlush(new DefaultHttp2HeadersFrame( - new DefaultHttp2Headers(), true)); - } - }); - } - } - } - }); - } - }); - clientChannel = bs.connect(serverChannel.localAddress()).get(); - - latch.await(); - } finally { - if (ssc != null) { - ssc.delete(); - } - } - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2SecurityUtilTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2SecurityUtilTest.java deleted file mode 100644 index 81ec236a58..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2SecurityUtilTest.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.SslProvider; -import io.netty.handler.ssl.SupportedCipherSuiteFilter; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Assumptions; -import org.junit.jupiter.api.Test; - -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLException; - -public class Http2SecurityUtilTest { - - @Test - public void testTLSv13CiphersIncluded() throws SSLException { - Assumptions.assumeTrue(SslProvider.isTlsv13Supported(SslProvider.JDK)); - testCiphersIncluded("TLSv1.3"); - } - - @Test - public void testTLSv12CiphersIncluded() throws SSLException { - testCiphersIncluded("TLSv1.2"); - } - - private static void testCiphersIncluded(String protocol) throws SSLException { - SslContext context = SslContextBuilder.forClient().sslProvider(SslProvider.JDK).protocols(protocol) - .ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE).build(); - SSLEngine engine = context.newEngine(UnpooledByteBufAllocator.DEFAULT); - Assertions.assertTrue(engine.getEnabledCipherSuites().length > 0, "No " + protocol + " ciphers found"); - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ServerUpgradeCodecTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ServerUpgradeCodecTest.java deleted file mode 100644 index ae2cd02682..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ServerUpgradeCodecTest.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.DefaultChannelId; -import io.netty.channel.ServerChannel; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -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.HttpMethod; -import io.netty.handler.codec.http.HttpVersion; - -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -public class Http2ServerUpgradeCodecTest { - - @Test - public void testUpgradeToHttp2ConnectionHandler() { - testUpgrade(new Http2ConnectionHandlerBuilder().frameListener(new Http2FrameAdapter()).build(), null); - } - - @Test - public void testUpgradeToHttp2FrameCodec() { - testUpgrade(new Http2FrameCodecBuilder(true).build(), null); - } - - @Test - public void testUpgradeToHttp2MultiplexCodec() { - testUpgrade(new Http2MultiplexCodecBuilder(true, new HttpInboundHandler()).build(), null); - } - - @Test - public void testUpgradeToHttp2FrameCodecWithMultiplexer() { - testUpgrade(new Http2FrameCodecBuilder(true).build(), - new Http2MultiplexHandler(new HttpInboundHandler())); - } - - private static void testUpgrade(Http2ConnectionHandler handler, ChannelHandler multiplexer) { - FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.OPTIONS, "*"); - request.headers().set(HttpHeaderNames.HOST, "netty.io"); - request.headers().set(HttpHeaderNames.CONNECTION, "Upgrade, HTTP2-Settings"); - request.headers().set(HttpHeaderNames.UPGRADE, "h2c"); - request.headers().set("HTTP2-Settings", "AAMAAABkAAQAAP__"); - - ServerChannel parent = Mockito.mock(ServerChannel.class); - EmbeddedChannel channel = new EmbeddedChannel(parent, DefaultChannelId.newInstance(), true, false, - new ChannelHandler() { }); - ChannelHandlerContext ctx = channel.pipeline().firstContext(); - Http2ServerUpgradeCodec codec; - if (multiplexer == null) { - codec = new Http2ServerUpgradeCodec(handler); - } else { - codec = new Http2ServerUpgradeCodec((Http2FrameCodec) handler, multiplexer); - } - channel.executor().execute(() -> { - assertTrue(codec.prepareUpgradeResponse(ctx, request, new DefaultHttpHeaders())); - codec.upgradeTo(ctx, request); - }); - - // Flush the channel to ensure we write out all buffered data - channel.flush(); - - channel.writeInbound(Http2CodecUtil.connectionPrefaceBuf()); - Http2FrameInboundWriter writer = new Http2FrameInboundWriter(channel); - writer.writeInboundSettings(new Http2Settings()); - writer.writeInboundRstStream(Http2CodecUtil.HTTP_UPGRADE_STREAM_ID, Http2Error.CANCEL.code()); - - assertSame(handler, channel.pipeline().remove(handler.getClass())); - assertNull(channel.pipeline().get(handler.getClass())); - assertTrue(channel.finish()); - - // Check that the preface was send (a.k.a the settings frame) - ByteBuf settingsBuffer = channel.readOutbound(); - assertNotNull(settingsBuffer); - settingsBuffer.release(); - - ByteBuf buf = channel.readOutbound(); - assertNotNull(buf); - buf.release(); - - assertNull(channel.readOutbound()); - } - - @ChannelHandler.Sharable - private static final class HttpInboundHandler implements ChannelHandler { } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2SettingsTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2SettingsTest.java deleted file mode 100644 index 48685d0c1f..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2SettingsTest.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.http2; - - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; - -import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_FRAME_SIZE_UPPER_BOUND; -import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_UNSIGNED_INT; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** - * Tests for {@link Http2Settings}. - */ -public class Http2SettingsTest { - - private Http2Settings settings; - - @BeforeEach - public void setup() { - settings = new Http2Settings(); - } - - @Test - public void standardSettingsShouldBeNotSet() { - assertEquals(0, settings.size()); - assertNull(settings.headerTableSize()); - assertNull(settings.initialWindowSize()); - assertNull(settings.maxConcurrentStreams()); - assertNull(settings.pushEnabled()); - assertNull(settings.maxFrameSize()); - assertNull(settings.maxHeaderListSize()); - } - - @Test - public void standardSettingsShouldBeSet() { - settings.initialWindowSize(1); - settings.maxConcurrentStreams(2); - settings.pushEnabled(true); - settings.headerTableSize(3); - settings.maxFrameSize(MAX_FRAME_SIZE_UPPER_BOUND); - settings.maxHeaderListSize(4); - assertEquals(1, (int) settings.initialWindowSize()); - assertEquals(2L, (long) settings.maxConcurrentStreams()); - assertTrue(settings.pushEnabled()); - assertEquals(3L, (long) settings.headerTableSize()); - assertEquals(MAX_FRAME_SIZE_UPPER_BOUND, (int) settings.maxFrameSize()); - assertEquals(4L, (long) settings.maxHeaderListSize()); - } - - @Test - public void nonStandardSettingsShouldBeSet() { - char key = 0; - settings.put(key, (Long) 123L); - assertEquals(123L, (long) settings.get(key)); - } - - @Test - public void settingsShouldSupportUnsignedShort() { - char key = (char) (Short.MAX_VALUE + 1); - settings.put(key, (Long) 123L); - assertEquals(123L, (long) settings.get(key)); - } - - @Test - public void headerListSizeUnsignedInt() { - settings.maxHeaderListSize(MAX_UNSIGNED_INT); - assertEquals(MAX_UNSIGNED_INT, (long) settings.maxHeaderListSize()); - } - - @Test - public void headerListSizeBoundCheck() { - assertThrows(IllegalArgumentException.class, new Executable() { - @Override - public void execute() { - settings.maxHeaderListSize(Long.MAX_VALUE); - } - }); - } - - @Test - public void headerTableSizeUnsignedInt() { - settings.put(Http2CodecUtil.SETTINGS_HEADER_TABLE_SIZE, (Long) MAX_UNSIGNED_INT); - assertEquals(MAX_UNSIGNED_INT, (long) settings.get(Http2CodecUtil.SETTINGS_HEADER_TABLE_SIZE)); - } - - @Test - public void headerTableSizeBoundCheck() { - assertThrows(IllegalArgumentException.class, new Executable() { - @Override - public void execute() { - settings.put(Http2CodecUtil.SETTINGS_HEADER_TABLE_SIZE, (Long) Long.MAX_VALUE); - } - }); - } - - @Test - public void headerTableSizeBoundCheck2() { - assertThrows(IllegalArgumentException.class, new Executable() { - @Override - public void execute() { - settings.put(Http2CodecUtil.SETTINGS_HEADER_TABLE_SIZE, Long.valueOf(-1L)); - } - }); - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2StreamChannelBootstrapTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2StreamChannelBootstrapTest.java deleted file mode 100644 index 6b684c4d9b..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2StreamChannelBootstrapTest.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.bootstrap.Bootstrap; -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.local.LocalAddress; -import io.netty.channel.local.LocalChannel; -import io.netty.channel.local.LocalHandler; -import io.netty.channel.local.LocalServerChannel; -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.ImmediateEventExecutor; -import io.netty.util.concurrent.Promise; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; -import org.hamcrest.core.IsInstanceOf; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; - -import java.nio.channels.ClosedChannelException; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutionException; - -import static io.netty.handler.codec.http2.Http2FrameCodecBuilder.forClient; -import static io.netty.handler.codec.http2.Http2FrameCodecBuilder.forServer; -import static java.util.concurrent.TimeUnit.SECONDS; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.is; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class Http2StreamChannelBootstrapTest { - - private static final InternalLogger logger = - InternalLoggerFactory.getInstance(Http2StreamChannelBootstrapTest.class); - - private volatile Channel serverConnectedChannel; - - @Test - public void testStreamIsNotCreatedIfParentConnectionIsClosedConcurrently() throws Exception { - EventLoopGroup group = null; - Channel serverChannel = null; - Channel clientChannel = null; - try { - final CountDownLatch serverChannelLatch = new CountDownLatch(1); - group = new MultithreadEventLoopGroup(LocalHandler.newFactory()); - LocalAddress serverAddress = new LocalAddress(getClass().getName()); - ServerBootstrap sb = new ServerBootstrap() - .channel(LocalServerChannel.class) - .group(group) - .childHandler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) { - serverConnectedChannel = ch; - ch.pipeline().addLast(forServer().build(), newMultiplexedHandler()); - serverChannelLatch.countDown(); - } - }); - serverChannel = sb.bind(serverAddress).get(); - - Bootstrap cb = new Bootstrap() - .channel(LocalChannel.class) - .group(group) - .handler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) { - ch.pipeline().addLast(forClient().build(), newMultiplexedHandler()); - } - }); - clientChannel = cb.connect(serverAddress).get(); - assertTrue(serverChannelLatch.await(3, SECONDS)); - - Http2StreamChannelBootstrap bootstrap = new Http2StreamChannelBootstrap(clientChannel); - final Promise promise = clientChannel.executor().newPromise(); - clientChannel.close().sync(); - - bootstrap.open(promise); - - ExecutionException exception = assertThrows(ExecutionException.class, new Executable() { - @Override - public void execute() throws Throwable { - promise.asFuture().get(3, SECONDS); - } - }); - assertThat(exception.getCause(), IsInstanceOf.instanceOf(ClosedChannelException.class)); - } finally { - safeClose(clientChannel); - safeClose(serverConnectedChannel); - safeClose(serverChannel); - if (group != null) { - group.shutdownGracefully(0, 3, SECONDS); - } - } - } - - private static Http2MultiplexHandler newMultiplexedHandler() { - return new Http2MultiplexHandler(new ChannelInitializer() { - @Override - protected void initChannel(Http2StreamChannel ch) { - // noop - } - }); - } - - private static void safeClose(Channel channel) { - if (channel != null) { - try { - channel.close().syncUninterruptibly(); - } catch (Exception e) { - logger.error(e); - } - } - } - - @Test - public void open0FailsPromiseOnHttp2MultiplexHandlerError() { - Http2StreamChannelBootstrap bootstrap = new Http2StreamChannelBootstrap(mock(Channel.class)); - - Http2MultiplexHandler handler = new Http2MultiplexHandler(mock(ChannelHandler.class)); - EventExecutor executor = mock(EventExecutor.class); - when(executor.inEventLoop()).thenReturn(true); - ChannelHandlerContext ctx = mock(ChannelHandlerContext.class); - when(ctx.executor()).thenReturn(executor); - when(ctx.handler()).thenReturn(handler); - - Promise promise = ImmediateEventExecutor.INSTANCE.newPromise(); - bootstrap.open0(ctx, promise); - assertThat(promise.isDone(), is(true)); - assertThat(promise.cause(), is(instanceOf(IllegalStateException.class))); - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2StreamChannelIdTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2StreamChannelIdTest.java deleted file mode 100644 index 7482230625..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2StreamChannelIdTest.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufInputStream; -import io.netty.buffer.ByteBufOutputStream; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelId; -import io.netty.channel.DefaultChannelId; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class Http2StreamChannelIdTest { - - @Test - public void testSerialization() throws Exception { - ChannelId normalInstance = new Http2StreamChannelId(DefaultChannelId.newInstance(), 0); - - ByteBuf buf = Unpooled.buffer(); - ObjectOutputStream outStream = new ObjectOutputStream(new ByteBufOutputStream(buf)); - try { - outStream.writeObject(normalInstance); - } finally { - outStream.close(); - } - - ObjectInputStream inStream = new ObjectInputStream(new ByteBufInputStream(buf, true)); - final ChannelId deserializedInstance; - try { - deserializedInstance = (ChannelId) inStream.readObject(); - } finally { - inStream.close(); - } - - assertEquals(normalInstance, deserializedInstance); - assertEquals(normalInstance.hashCode(), deserializedInstance.hashCode()); - assertEquals(0, normalInstance.compareTo(deserializedInstance)); - assertEquals(normalInstance.asLongText(), deserializedInstance.asLongText()); - assertEquals(normalInstance.asShortText(), deserializedInstance.asShortText()); - } -} 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 deleted file mode 100644 index e86d3e74f1..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2StreamFrameToHttpObjectCodecTest.java +++ /dev/null @@ -1,936 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.http2; - -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.embedded.EmbeddedChannel; -import io.netty.handler.codec.EncoderException; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.DefaultFullHttpResponse; -import io.netty.handler.codec.http.DefaultHttpContent; -import io.netty.handler.codec.http.DefaultHttpRequest; -import io.netty.handler.codec.http.DefaultHttpResponse; -import io.netty.handler.codec.http.DefaultLastHttpContent; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpScheme; -import io.netty.handler.codec.http.HttpUtil; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http.LastHttpContent; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.SslProvider; -import io.netty.util.CharsetUtil; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; - -import java.util.Queue; -import java.util.concurrent.ConcurrentLinkedQueue; - -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.nullValue; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class Http2StreamFrameToHttpObjectCodecTest { - - @Test - public void testUpgradeEmptyFullResponse() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(true)); - assertTrue(ch.writeOutbound(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK))); - - Http2HeadersFrame headersFrame = ch.readOutbound(); - assertThat(headersFrame.headers().status().toString(), is("200")); - assertTrue(headersFrame.isEndStream()); - - assertThat(ch.readOutbound(), is(nullValue())); - assertFalse(ch.finish()); - } - - @Test - public void encode100ContinueAsHttp2HeadersFrameThatIsNotEndStream() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(true)); - assertTrue(ch.writeOutbound(new DefaultFullHttpResponse( - HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE))); - - Http2HeadersFrame headersFrame = ch.readOutbound(); - assertThat(headersFrame.headers().status().toString(), is("100")); - assertFalse(headersFrame.isEndStream()); - - assertThat(ch.readOutbound(), is(nullValue())); - assertFalse(ch.finish()); - } - - @Test - public void encodeNonFullHttpResponse100ContinueIsRejected() throws Exception { - final EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(true)); - assertThrows(EncoderException.class, new Executable() { - @Override - public void execute() { - ch.writeOutbound(new DefaultHttpResponse( - HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE)); - } - }); - ch.finishAndReleaseAll(); - } - - @Test - public void testUpgradeNonEmptyFullResponse() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(true)); - ByteBuf hello = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8); - assertTrue(ch.writeOutbound(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, hello))); - - Http2HeadersFrame headersFrame = ch.readOutbound(); - assertThat(headersFrame.headers().status().toString(), is("200")); - assertFalse(headersFrame.isEndStream()); - - Http2DataFrame dataFrame = ch.readOutbound(); - try { - assertThat(dataFrame.content().toString(CharsetUtil.UTF_8), is("hello world")); - assertTrue(dataFrame.isEndStream()); - } finally { - dataFrame.release(); - } - - assertThat(ch.readOutbound(), is(nullValue())); - assertFalse(ch.finish()); - } - - @Test - public void testUpgradeEmptyFullResponseWithTrailers() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(true)); - FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - HttpHeaders trailers = response.trailingHeaders(); - trailers.set("key", "value"); - assertTrue(ch.writeOutbound(response)); - - Http2HeadersFrame headersFrame = ch.readOutbound(); - assertThat(headersFrame.headers().status().toString(), is("200")); - assertFalse(headersFrame.isEndStream()); - - Http2HeadersFrame trailersFrame = ch.readOutbound(); - assertThat(trailersFrame.headers().get("key").toString(), is("value")); - assertTrue(trailersFrame.isEndStream()); - - assertThat(ch.readOutbound(), is(nullValue())); - assertFalse(ch.finish()); - } - - @Test - public void testUpgradeNonEmptyFullResponseWithTrailers() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(true)); - ByteBuf hello = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8); - FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, hello); - HttpHeaders trailers = response.trailingHeaders(); - trailers.set("key", "value"); - assertTrue(ch.writeOutbound(response)); - - Http2HeadersFrame headersFrame = ch.readOutbound(); - assertThat(headersFrame.headers().status().toString(), is("200")); - assertFalse(headersFrame.isEndStream()); - - Http2DataFrame dataFrame = ch.readOutbound(); - try { - assertThat(dataFrame.content().toString(CharsetUtil.UTF_8), is("hello world")); - assertFalse(dataFrame.isEndStream()); - } finally { - dataFrame.release(); - } - - Http2HeadersFrame trailersFrame = ch.readOutbound(); - assertThat(trailersFrame.headers().get("key").toString(), is("value")); - assertTrue(trailersFrame.isEndStream()); - - assertThat(ch.readOutbound(), is(nullValue())); - assertFalse(ch.finish()); - } - - @Test - public void testUpgradeHeaders() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(true)); - HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - assertTrue(ch.writeOutbound(response)); - - Http2HeadersFrame headersFrame = ch.readOutbound(); - assertThat(headersFrame.headers().status().toString(), is("200")); - assertFalse(headersFrame.isEndStream()); - - assertThat(ch.readOutbound(), is(nullValue())); - assertFalse(ch.finish()); - } - - @Test - public void testUpgradeChunk() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(true)); - ByteBuf hello = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8); - HttpContent content = new DefaultHttpContent(hello); - assertTrue(ch.writeOutbound(content)); - - Http2DataFrame dataFrame = ch.readOutbound(); - try { - assertThat(dataFrame.content().toString(CharsetUtil.UTF_8), is("hello world")); - assertFalse(dataFrame.isEndStream()); - } finally { - dataFrame.release(); - } - - assertThat(ch.readOutbound(), is(nullValue())); - assertFalse(ch.finish()); - } - - @Test - public void testUpgradeEmptyEnd() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(true)); - LastHttpContent end = LastHttpContent.EMPTY_LAST_CONTENT; - assertTrue(ch.writeOutbound(end)); - - Http2DataFrame emptyFrame = ch.readOutbound(); - try { - assertThat(emptyFrame.content().readableBytes(), is(0)); - assertTrue(emptyFrame.isEndStream()); - } finally { - emptyFrame.release(); - } - - assertThat(ch.readOutbound(), is(nullValue())); - assertFalse(ch.finish()); - } - - @Test - public void testUpgradeDataEnd() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(true)); - ByteBuf hello = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8); - LastHttpContent end = new DefaultLastHttpContent(hello, true); - assertTrue(ch.writeOutbound(end)); - - Http2DataFrame dataFrame = ch.readOutbound(); - try { - assertThat(dataFrame.content().toString(CharsetUtil.UTF_8), is("hello world")); - assertTrue(dataFrame.isEndStream()); - } finally { - dataFrame.release(); - } - - assertThat(ch.readOutbound(), is(nullValue())); - assertFalse(ch.finish()); - } - - @Test - public void testUpgradeTrailers() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(true)); - LastHttpContent trailers = new DefaultLastHttpContent(Unpooled.EMPTY_BUFFER, true); - HttpHeaders headers = trailers.trailingHeaders(); - headers.set("key", "value"); - assertTrue(ch.writeOutbound(trailers)); - - Http2HeadersFrame headerFrame = ch.readOutbound(); - assertThat(headerFrame.headers().get("key").toString(), is("value")); - assertTrue(headerFrame.isEndStream()); - - assertThat(ch.readOutbound(), is(nullValue())); - assertFalse(ch.finish()); - } - - @Test - public void testUpgradeDataEndWithTrailers() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(true)); - ByteBuf hello = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8); - LastHttpContent trailers = new DefaultLastHttpContent(hello, true); - HttpHeaders headers = trailers.trailingHeaders(); - headers.set("key", "value"); - assertTrue(ch.writeOutbound(trailers)); - - Http2DataFrame dataFrame = ch.readOutbound(); - try { - assertThat(dataFrame.content().toString(CharsetUtil.UTF_8), is("hello world")); - assertFalse(dataFrame.isEndStream()); - } finally { - dataFrame.release(); - } - - Http2HeadersFrame headerFrame = ch.readOutbound(); - assertThat(headerFrame.headers().get("key").toString(), is("value")); - assertTrue(headerFrame.isEndStream()); - - assertThat(ch.readOutbound(), is(nullValue())); - assertFalse(ch.finish()); - } - - @Test - public void testDowngradeHeaders() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(true)); - Http2Headers headers = new DefaultHttp2Headers(); - headers.path("/"); - headers.method("GET"); - - assertTrue(ch.writeInbound(new DefaultHttp2HeadersFrame(headers))); - - HttpRequest request = ch.readInbound(); - assertThat(request.uri(), is("/")); - assertThat(request.method(), is(HttpMethod.GET)); - assertThat(request.protocolVersion(), is(HttpVersion.HTTP_1_1)); - assertFalse(request instanceof FullHttpRequest); - assertTrue(HttpUtil.isTransferEncodingChunked(request)); - - assertThat(ch.readInbound(), is(nullValue())); - assertFalse(ch.finish()); - } - - @Test - public void testDowngradeHeadersWithContentLength() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(true)); - Http2Headers headers = new DefaultHttp2Headers(); - headers.path("/"); - headers.method("GET"); - headers.setInt("content-length", 0); - - assertTrue(ch.writeInbound(new DefaultHttp2HeadersFrame(headers))); - - HttpRequest request = ch.readInbound(); - assertThat(request.uri(), is("/")); - assertThat(request.method(), is(HttpMethod.GET)); - assertThat(request.protocolVersion(), is(HttpVersion.HTTP_1_1)); - assertFalse(request instanceof FullHttpRequest); - assertFalse(HttpUtil.isTransferEncodingChunked(request)); - - assertThat(ch.readInbound(), is(nullValue())); - assertFalse(ch.finish()); - } - - @Test - public void testDowngradeFullHeaders() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(true)); - Http2Headers headers = new DefaultHttp2Headers(); - headers.path("/"); - headers.method("GET"); - - assertTrue(ch.writeInbound(new DefaultHttp2HeadersFrame(headers, true))); - - FullHttpRequest request = ch.readInbound(); - try { - assertThat(request.uri(), is("/")); - assertThat(request.method(), is(HttpMethod.GET)); - assertThat(request.protocolVersion(), is(HttpVersion.HTTP_1_1)); - assertThat(request.content().readableBytes(), is(0)); - assertTrue(request.trailingHeaders().isEmpty()); - assertFalse(HttpUtil.isTransferEncodingChunked(request)); - } finally { - request.release(); - } - - assertThat(ch.readInbound(), is(nullValue())); - assertFalse(ch.finish()); - } - - @Test - public void testDowngradeTrailers() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(true)); - Http2Headers headers = new DefaultHttp2Headers(); - headers.set("key", "value"); - assertTrue(ch.writeInbound(new DefaultHttp2HeadersFrame(headers, true))); - - LastHttpContent trailers = ch.readInbound(); - try { - assertThat(trailers.content().readableBytes(), is(0)); - assertThat(trailers.trailingHeaders().get("key"), is("value")); - assertFalse(trailers instanceof FullHttpRequest); - } finally { - trailers.release(); - } - - assertThat(ch.readInbound(), is(nullValue())); - assertFalse(ch.finish()); - } - - @Test - public void testDowngradeData() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(true)); - ByteBuf hello = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8); - assertTrue(ch.writeInbound(new DefaultHttp2DataFrame(hello))); - - HttpContent content = ch.readInbound(); - try { - assertThat(content.content().toString(CharsetUtil.UTF_8), is("hello world")); - assertFalse(content instanceof LastHttpContent); - } finally { - content.release(); - } - - assertThat(ch.readInbound(), is(nullValue())); - assertFalse(ch.finish()); - } - - @Test - public void testDowngradeEndData() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(true)); - ByteBuf hello = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8); - assertTrue(ch.writeInbound(new DefaultHttp2DataFrame(hello, true))); - - LastHttpContent content = ch.readInbound(); - try { - assertThat(content.content().toString(CharsetUtil.UTF_8), is("hello world")); - assertTrue(content.trailingHeaders().isEmpty()); - } finally { - content.release(); - } - - assertThat(ch.readInbound(), is(nullValue())); - assertFalse(ch.finish()); - } - - @Test - public void testPassThroughOther() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(true)); - Http2ResetFrame reset = new DefaultHttp2ResetFrame(0); - Http2GoAwayFrame goaway = new DefaultHttp2GoAwayFrame(0); - assertTrue(ch.writeInbound(reset)); - assertTrue(ch.writeInbound(goaway.retain())); - - assertEquals(reset, ch.readInbound()); - - Http2GoAwayFrame frame = ch.readInbound(); - try { - assertEquals(goaway, frame); - assertThat(ch.readInbound(), is(nullValue())); - assertFalse(ch.finish()); - } finally { - goaway.release(); - frame.release(); - } - } - - // client-specific tests - @Test - public void testEncodeEmptyFullRequest() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(false)); - assertTrue(ch.writeOutbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/hello/world"))); - - Http2HeadersFrame headersFrame = ch.readOutbound(); - Http2Headers 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()); - - assertThat(ch.readOutbound(), is(nullValue())); - assertFalse(ch.finish()); - } - - @Test - public void testEncodeHttpsSchemeWhenSslHandlerExists() throws Exception { - final Queue frames = new ConcurrentLinkedQueue<>(); - - final SslContext ctx = SslContextBuilder.forClient().sslProvider(SslProvider.JDK).build(); - EmbeddedChannel ch = new EmbeddedChannel(ctx.newHandler(ByteBufAllocator.DEFAULT), - new ChannelHandler() { - @Override - public Future write(ChannelHandlerContext ctx, Object msg) { - if (msg instanceof Http2StreamFrame) { - frames.add((Http2StreamFrame) msg); - return ctx.write(Unpooled.EMPTY_BUFFER); - } - return ctx.write(msg); - } - }, new Http2StreamFrameToHttpObjectCodec(false)); - - try { - FullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/hello/world"); - assertTrue(ch.writeOutbound(req)); - - ch.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()); - } finally { - ch.finishAndReleaseAll(); - } - } - - @Test - public void testEncodeNonEmptyFullRequest() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(false)); - ByteBuf hello = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8); - assertTrue(ch.writeOutbound(new DefaultFullHttpRequest( - HttpVersion.HTTP_1_1, HttpMethod.PUT, "/hello/world", hello))); - - Http2HeadersFrame headersFrame = ch.readOutbound(); - Http2Headers headers = headersFrame.headers(); - - assertThat(headers.scheme().toString(), is("http")); - assertThat(headers.method().toString(), is("PUT")); - assertThat(headers.path().toString(), is("/hello/world")); - assertFalse(headersFrame.isEndStream()); - - Http2DataFrame dataFrame = ch.readOutbound(); - try { - assertThat(dataFrame.content().toString(CharsetUtil.UTF_8), is("hello world")); - assertTrue(dataFrame.isEndStream()); - } finally { - dataFrame.release(); - } - - assertThat(ch.readOutbound(), is(nullValue())); - assertFalse(ch.finish()); - } - - @Test - public void testEncodeEmptyFullRequestWithTrailers() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(false)); - FullHttpRequest request = new DefaultFullHttpRequest( - HttpVersion.HTTP_1_1, HttpMethod.PUT, "/hello/world"); - - HttpHeaders trailers = request.trailingHeaders(); - trailers.set("key", "value"); - assertTrue(ch.writeOutbound(request)); - - Http2HeadersFrame headersFrame = ch.readOutbound(); - Http2Headers headers = headersFrame.headers(); - - assertThat(headers.scheme().toString(), is("http")); - assertThat(headers.method().toString(), is("PUT")); - assertThat(headers.path().toString(), is("/hello/world")); - assertFalse(headersFrame.isEndStream()); - - Http2HeadersFrame trailersFrame = ch.readOutbound(); - assertThat(trailersFrame.headers().get("key").toString(), is("value")); - assertTrue(trailersFrame.isEndStream()); - - assertThat(ch.readOutbound(), is(nullValue())); - assertFalse(ch.finish()); - } - - @Test - public void testEncodeNonEmptyFullRequestWithTrailers() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(false)); - ByteBuf hello = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8); - FullHttpRequest request = new DefaultFullHttpRequest( - HttpVersion.HTTP_1_1, HttpMethod.PUT, "/hello/world", hello); - - HttpHeaders trailers = request.trailingHeaders(); - trailers.set("key", "value"); - assertTrue(ch.writeOutbound(request)); - - Http2HeadersFrame headersFrame = ch.readOutbound(); - Http2Headers headers = headersFrame.headers(); - - assertThat(headers.scheme().toString(), is("http")); - assertThat(headers.method().toString(), is("PUT")); - assertThat(headers.path().toString(), is("/hello/world")); - assertFalse(headersFrame.isEndStream()); - - Http2DataFrame dataFrame = ch.readOutbound(); - try { - assertThat(dataFrame.content().toString(CharsetUtil.UTF_8), is("hello world")); - assertFalse(dataFrame.isEndStream()); - } finally { - dataFrame.release(); - } - - Http2HeadersFrame trailersFrame = ch.readOutbound(); - assertThat(trailersFrame.headers().get("key").toString(), is("value")); - assertTrue(trailersFrame.isEndStream()); - - assertThat(ch.readOutbound(), is(nullValue())); - assertFalse(ch.finish()); - } - - @Test - public void testEncodeRequestHeaders() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(false)); - HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/hello/world"); - assertTrue(ch.writeOutbound(request)); - - Http2HeadersFrame headersFrame = ch.readOutbound(); - Http2Headers headers = headersFrame.headers(); - - assertThat(headers.scheme().toString(), is("http")); - assertThat(headers.method().toString(), is("GET")); - assertThat(headers.path().toString(), is("/hello/world")); - assertFalse(headersFrame.isEndStream()); - - assertThat(ch.readOutbound(), is(nullValue())); - assertFalse(ch.finish()); - } - - @Test - public void testEncodeChunkAsClient() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(false)); - ByteBuf hello = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8); - HttpContent content = new DefaultHttpContent(hello); - assertTrue(ch.writeOutbound(content)); - - Http2DataFrame dataFrame = ch.readOutbound(); - try { - assertThat(dataFrame.content().toString(CharsetUtil.UTF_8), is("hello world")); - assertFalse(dataFrame.isEndStream()); - } finally { - dataFrame.release(); - } - - assertThat(ch.readOutbound(), is(nullValue())); - assertFalse(ch.finish()); - } - - @Test - public void testEncodeEmptyEndAsClient() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(false)); - LastHttpContent end = LastHttpContent.EMPTY_LAST_CONTENT; - assertTrue(ch.writeOutbound(end)); - - Http2DataFrame emptyFrame = ch.readOutbound(); - try { - assertThat(emptyFrame.content().readableBytes(), is(0)); - assertTrue(emptyFrame.isEndStream()); - } finally { - emptyFrame.release(); - } - - assertThat(ch.readOutbound(), is(nullValue())); - assertFalse(ch.finish()); - } - - @Test - public void testEncodeDataEndAsClient() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(false)); - ByteBuf hello = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8); - LastHttpContent end = new DefaultLastHttpContent(hello, true); - assertTrue(ch.writeOutbound(end)); - - Http2DataFrame dataFrame = ch.readOutbound(); - try { - assertThat(dataFrame.content().toString(CharsetUtil.UTF_8), is("hello world")); - assertTrue(dataFrame.isEndStream()); - } finally { - dataFrame.release(); - } - - assertThat(ch.readOutbound(), is(nullValue())); - assertFalse(ch.finish()); - } - - @Test - public void testEncodeTrailersAsClient() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(false)); - LastHttpContent trailers = new DefaultLastHttpContent(Unpooled.EMPTY_BUFFER, true); - HttpHeaders headers = trailers.trailingHeaders(); - headers.set("key", "value"); - assertTrue(ch.writeOutbound(trailers)); - - Http2HeadersFrame headerFrame = ch.readOutbound(); - assertThat(headerFrame.headers().get("key").toString(), is("value")); - assertTrue(headerFrame.isEndStream()); - - assertThat(ch.readOutbound(), is(nullValue())); - assertFalse(ch.finish()); - } - - @Test - public void testEncodeDataEndWithTrailersAsClient() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(false)); - ByteBuf hello = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8); - LastHttpContent trailers = new DefaultLastHttpContent(hello, true); - HttpHeaders headers = trailers.trailingHeaders(); - headers.set("key", "value"); - assertTrue(ch.writeOutbound(trailers)); - - Http2DataFrame dataFrame = ch.readOutbound(); - try { - assertThat(dataFrame.content().toString(CharsetUtil.UTF_8), is("hello world")); - assertFalse(dataFrame.isEndStream()); - } finally { - dataFrame.release(); - } - - Http2HeadersFrame headerFrame = ch.readOutbound(); - assertThat(headerFrame.headers().get("key").toString(), is("value")); - assertTrue(headerFrame.isEndStream()); - - assertThat(ch.readOutbound(), is(nullValue())); - assertFalse(ch.finish()); - } - - @Test - public void decode100ContinueHttp2HeadersAsFullHttpResponse() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(false)); - Http2Headers headers = new DefaultHttp2Headers(); - headers.scheme(HttpScheme.HTTP.name()); - headers.status(HttpResponseStatus.CONTINUE.codeAsText()); - - assertTrue(ch.writeInbound(new DefaultHttp2HeadersFrame(headers, false))); - - final FullHttpResponse response = ch.readInbound(); - try { - assertThat(response.status(), is(HttpResponseStatus.CONTINUE)); - assertThat(response.protocolVersion(), is(HttpVersion.HTTP_1_1)); - } finally { - response.release(); - } - - assertThat(ch.readInbound(), is(nullValue())); - assertFalse(ch.finish()); - } - - @Test - public void testDecodeResponseHeaders() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(false)); - Http2Headers headers = new DefaultHttp2Headers(); - headers.scheme(HttpScheme.HTTP.name()); - headers.status(HttpResponseStatus.OK.codeAsText()); - - assertTrue(ch.writeInbound(new DefaultHttp2HeadersFrame(headers))); - - HttpResponse response = ch.readInbound(); - assertThat(response.status(), is(HttpResponseStatus.OK)); - assertThat(response.protocolVersion(), is(HttpVersion.HTTP_1_1)); - assertFalse(response instanceof FullHttpResponse); - assertTrue(HttpUtil.isTransferEncodingChunked(response)); - - assertThat(ch.readInbound(), is(nullValue())); - assertFalse(ch.finish()); - } - - @Test - public void testDecodeResponseHeadersWithContentLength() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(false)); - Http2Headers headers = new DefaultHttp2Headers(); - headers.scheme(HttpScheme.HTTP.name()); - headers.status(HttpResponseStatus.OK.codeAsText()); - headers.setInt("content-length", 0); - - assertTrue(ch.writeInbound(new DefaultHttp2HeadersFrame(headers))); - - HttpResponse response = ch.readInbound(); - assertThat(response.status(), is(HttpResponseStatus.OK)); - assertThat(response.protocolVersion(), is(HttpVersion.HTTP_1_1)); - assertFalse(response instanceof FullHttpResponse); - assertFalse(HttpUtil.isTransferEncodingChunked(response)); - - assertThat(ch.readInbound(), is(nullValue())); - assertFalse(ch.finish()); - } - - @Test - public void testDecodeFullResponseHeaders() throws Exception { - testDecodeFullResponseHeaders(false); - } - - @Test - public void testDecodeFullResponseHeadersWithStreamID() throws Exception { - testDecodeFullResponseHeaders(true); - } - - private void testDecodeFullResponseHeaders(boolean withStreamId) throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(false)); - Http2Headers headers = new DefaultHttp2Headers(); - headers.scheme(HttpScheme.HTTP.name()); - headers.status(HttpResponseStatus.OK.codeAsText()); - - Http2HeadersFrame frame = new DefaultHttp2HeadersFrame(headers, true); - if (withStreamId) { - frame.stream(new Http2FrameStream() { - @Override - public int id() { - return 1; - } - - @Override - public Http2Stream.State state() { - return Http2Stream.State.OPEN; - } - }); - } - - assertTrue(ch.writeInbound(frame)); - - FullHttpResponse response = ch.readInbound(); - try { - assertThat(response.status(), is(HttpResponseStatus.OK)); - assertThat(response.protocolVersion(), is(HttpVersion.HTTP_1_1)); - assertThat(response.content().readableBytes(), is(0)); - assertTrue(response.trailingHeaders().isEmpty()); - assertFalse(HttpUtil.isTransferEncodingChunked(response)); - if (withStreamId) { - assertEquals(1, - (int) response.headers().getInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text())); - } - } finally { - response.release(); - } - - assertThat(ch.readInbound(), is(nullValue())); - assertFalse(ch.finish()); - } - - @Test - public void testDecodeResponseTrailersAsClient() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(false)); - Http2Headers headers = new DefaultHttp2Headers(); - headers.set("key", "value"); - assertTrue(ch.writeInbound(new DefaultHttp2HeadersFrame(headers, true))); - - LastHttpContent trailers = ch.readInbound(); - try { - assertThat(trailers.content().readableBytes(), is(0)); - assertThat(trailers.trailingHeaders().get("key"), is("value")); - assertFalse(trailers instanceof FullHttpRequest); - } finally { - trailers.release(); - } - - assertThat(ch.readInbound(), is(nullValue())); - assertFalse(ch.finish()); - } - - @Test - public void testDecodeDataAsClient() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(false)); - ByteBuf hello = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8); - assertTrue(ch.writeInbound(new DefaultHttp2DataFrame(hello))); - - HttpContent content = ch.readInbound(); - try { - assertThat(content.content().toString(CharsetUtil.UTF_8), is("hello world")); - assertFalse(content instanceof LastHttpContent); - } finally { - content.release(); - } - - assertThat(ch.readInbound(), is(nullValue())); - assertFalse(ch.finish()); - } - - @Test - public void testDecodeEndDataAsClient() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(false)); - ByteBuf hello = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8); - assertTrue(ch.writeInbound(new DefaultHttp2DataFrame(hello, true))); - - LastHttpContent content = ch.readInbound(); - try { - assertThat(content.content().toString(CharsetUtil.UTF_8), is("hello world")); - assertTrue(content.trailingHeaders().isEmpty()); - } finally { - content.release(); - } - - assertThat(ch.readInbound(), is(nullValue())); - assertFalse(ch.finish()); - } - - @Test - public void testPassThroughOtherAsClient() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(false)); - Http2ResetFrame reset = new DefaultHttp2ResetFrame(0); - Http2GoAwayFrame goaway = new DefaultHttp2GoAwayFrame(0); - assertTrue(ch.writeInbound(reset)); - assertTrue(ch.writeInbound(goaway.retain())); - - assertEquals(reset, ch.readInbound()); - - Http2GoAwayFrame frame = ch.readInbound(); - try { - assertEquals(goaway, frame); - assertThat(ch.readInbound(), is(nullValue())); - assertFalse(ch.finish()); - } finally { - goaway.release(); - 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 ChannelHandler() { - @Override - public Future write(ChannelHandlerContext ctx, Object msg) { - if (msg instanceof Http2StreamFrame) { - frames.add((Http2StreamFrame) msg); - return ctx.newSucceededFuture(); - } - return ctx.write(msg); - } - }, sharedHandler); - - EmbeddedChannel plaintextCh = new EmbeddedChannel( - new ChannelHandler() { - @Override - public Future write(ChannelHandlerContext ctx, Object msg) { - if (msg instanceof Http2StreamFrame) { - frames.add((Http2StreamFrame) msg); - return ctx.newSucceededFuture(); - } - return ctx.write(msg); - } - }, 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()); - } -} 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 deleted file mode 100644 index 3d5f5e171a..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2TestUtil.java +++ /dev/null @@ -1,433 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.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.ChannelHandlerContext; -import io.netty.util.AsciiString; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.concurrent.ImmediateEventExecutor; -import io.netty.util.concurrent.Promise; -import org.mockito.Mockito; - -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.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.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. - */ -@SuppressWarnings("unchecked") -public final class Http2TestUtil { - /** - * Interface that allows for running a operation that throws a {@link Http2Exception}. - */ - interface Http2Runnable { - void run() throws Http2Exception; - } - - /** - * Runs the given operation within the event loop thread of the given {@link Channel}. - */ - static void runInChannel(Channel channel, final Http2Runnable runnable) { - channel.executor().execute(() -> { - try { - runnable.run(); - } catch (Http2Exception e) { - throw new RuntimeException(e); - } - }); - } - - /** - * Returns a byte array filled with random data. - */ - public static byte[] randomBytes() { - return randomBytes(100); - } - - /** - * Returns a byte array filled with random data. - */ - public static byte[] randomBytes(int size) { - byte[] data = new byte[size]; - new Random().nextBytes(data); - return data; - } - - /** - * Returns an {@link AsciiString} that wraps a randomly-filled byte array. - */ - public static AsciiString randomString() { - return new AsciiString(randomBytes()); - } - - public static CharSequence of(String s) { - return s; - } - - public static HpackEncoder newTestEncoder() { - try { - return newTestEncoder(true, MAX_HEADER_LIST_SIZE, MAX_HEADER_TABLE_SIZE); - } catch (Http2Exception e) { - throw new Error("max size not allowed?", e); - } - } - - public static HpackEncoder newTestEncoder(boolean ignoreMaxHeaderListSize, - long maxHeaderListSize, long maxHeaderTableSize) throws Http2Exception { - HpackEncoder hpackEncoder = new HpackEncoder(false, 16, 0); - ByteBuf buf = Unpooled.buffer(); - try { - hpackEncoder.setMaxHeaderTableSize(buf, maxHeaderTableSize); - hpackEncoder.setMaxHeaderListSize(maxHeaderListSize); - } finally { - buf.release(); - } - return hpackEncoder; - } - - public static HpackDecoder newTestDecoder() { - try { - return newTestDecoder(MAX_HEADER_LIST_SIZE, MAX_HEADER_TABLE_SIZE); - } catch (Http2Exception e) { - throw new Error("max size not allowed?", e); - } - } - - public static HpackDecoder newTestDecoder(long maxHeaderListSize, long maxHeaderTableSize) throws Http2Exception { - HpackDecoder hpackDecoder = new HpackDecoder(maxHeaderListSize); - hpackDecoder.setMaxHeaderTableSize(maxHeaderTableSize); - return hpackDecoder; - } - - private Http2TestUtil() { - } - - /** - * A decorator around a {@link Http2FrameListener} that counts down the latch so that we can await the completion of - * the request. - */ - static class FrameCountDown implements Http2FrameListener { - private final Http2FrameListener listener; - private final CountDownLatch messageLatch; - private final CountDownLatch settingsAckLatch; - private final CountDownLatch dataLatch; - private final CountDownLatch trailersLatch; - private final CountDownLatch goAwayLatch; - - FrameCountDown(Http2FrameListener listener, CountDownLatch settingsAckLatch, CountDownLatch messageLatch, - CountDownLatch dataLatch, CountDownLatch trailersLatch) { - this(listener, settingsAckLatch, messageLatch, dataLatch, trailersLatch, messageLatch); - } - - FrameCountDown(Http2FrameListener listener, CountDownLatch settingsAckLatch, CountDownLatch messageLatch, - CountDownLatch dataLatch, CountDownLatch trailersLatch, CountDownLatch goAwayLatch) { - this.listener = listener; - this.messageLatch = messageLatch; - this.settingsAckLatch = settingsAckLatch; - this.dataLatch = dataLatch; - this.trailersLatch = trailersLatch; - this.goAwayLatch = goAwayLatch; - } - - @Override - public int onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding, boolean endOfStream) - throws Http2Exception { - int numBytes = data.readableBytes(); - int processed = listener.onDataRead(ctx, streamId, data, padding, endOfStream); - messageLatch.countDown(); - if (dataLatch != null) { - for (int i = 0; i < numBytes; ++i) { - dataLatch.countDown(); - } - } - return processed; - } - - @Override - public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding, - boolean endStream) throws Http2Exception { - listener.onHeadersRead(ctx, streamId, headers, padding, endStream); - messageLatch.countDown(); - if (trailersLatch != null && endStream) { - trailersLatch.countDown(); - } - } - - @Override - public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int streamDependency, - short weight, boolean exclusive, int padding, boolean endStream) throws Http2Exception { - listener.onHeadersRead(ctx, streamId, headers, streamDependency, weight, exclusive, padding, endStream); - messageLatch.countDown(); - if (trailersLatch != null && endStream) { - trailersLatch.countDown(); - } - } - - @Override - public void onPriorityRead(ChannelHandlerContext ctx, int streamId, int streamDependency, short weight, - boolean exclusive) throws Http2Exception { - listener.onPriorityRead(ctx, streamId, streamDependency, weight, exclusive); - messageLatch.countDown(); - } - - @Override - public void onRstStreamRead(ChannelHandlerContext ctx, int streamId, long errorCode) throws Http2Exception { - listener.onRstStreamRead(ctx, streamId, errorCode); - messageLatch.countDown(); - } - - @Override - public void onSettingsAckRead(ChannelHandlerContext ctx) throws Http2Exception { - listener.onSettingsAckRead(ctx); - settingsAckLatch.countDown(); - } - - @Override - public void onSettingsRead(ChannelHandlerContext ctx, Http2Settings settings) throws Http2Exception { - listener.onSettingsRead(ctx, settings); - messageLatch.countDown(); - } - - @Override - public void onPingRead(ChannelHandlerContext ctx, long data) throws Http2Exception { - listener.onPingRead(ctx, data); - messageLatch.countDown(); - } - - @Override - public void onPingAckRead(ChannelHandlerContext ctx, long data) throws Http2Exception { - listener.onPingAckRead(ctx, data); - messageLatch.countDown(); - } - - @Override - public void onPushPromiseRead(ChannelHandlerContext ctx, int streamId, int promisedStreamId, - Http2Headers headers, int padding) throws Http2Exception { - listener.onPushPromiseRead(ctx, streamId, promisedStreamId, headers, padding); - messageLatch.countDown(); - } - - @Override - public void onGoAwayRead(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData) - throws Http2Exception { - listener.onGoAwayRead(ctx, lastStreamId, errorCode, debugData); - goAwayLatch.countDown(); - } - - @Override - public void onWindowUpdateRead(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement) - throws Http2Exception { - listener.onWindowUpdateRead(ctx, streamId, windowSizeIncrement); - messageLatch.countDown(); - } - - @Override - public void onUnknownFrame(ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags, - ByteBuf payload) throws Http2Exception { - listener.onUnknownFrame(ctx, frameType, streamId, flags, payload); - messageLatch.countDown(); - } - } - - static final class TestStreamByteDistributorStreamState implements StreamByteDistributor.StreamState { - private final Http2Stream stream; - boolean isWriteAllowed; - long pendingBytes; - boolean hasFrame; - - TestStreamByteDistributorStreamState(Http2Stream stream, long pendingBytes, boolean hasFrame, - boolean isWriteAllowed) { - this.stream = stream; - this.isWriteAllowed = isWriteAllowed; - this.pendingBytes = pendingBytes; - this.hasFrame = hasFrame; - } - - @Override - public Http2Stream stream() { - return stream; - } - - @Override - public long pendingBytes() { - return pendingBytes; - } - - @Override - public boolean hasFrame() { - return hasFrame; - } - - @Override - 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(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))) - .thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null)); - - when(frameWriter.writeSettingsAck(any(ChannelHandlerContext.class))) - .thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null)); - - when(frameWriter.writePing(any(ChannelHandlerContext.class), anyBoolean(), anyLong())) - .thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null)); - when(frameWriter.writeGoAway(any(ChannelHandlerContext.class), anyInt(), - anyLong(), any(ByteBuf.class))).thenAnswer(invocationOnMock -> { - buffers.offer((ByteBuf) invocationOnMock.getArgument(3)); - return ImmediateEventExecutor.INSTANCE.newSucceededFuture(null); - }); - when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class), anyInt(), - anyBoolean())).thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null)); - - when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(), - any(Http2Headers.class), anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean())) - .thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null)); - - when(frameWriter.writeData(any(ChannelHandlerContext.class), anyInt(), any(ByteBuf.class), anyInt(), - anyBoolean())).thenAnswer(invocationOnMock -> { - buffers.offer((ByteBuf) invocationOnMock.getArgument(2)); - return ImmediateEventExecutor.INSTANCE.newSucceededFuture(null); - }); - - when(frameWriter.writeRstStream(any(ChannelHandlerContext.class), anyInt(), - anyLong())).thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null)); - - when(frameWriter.writeWindowUpdate(any(ChannelHandlerContext.class), anyInt(), anyInt())) - .thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null)); - - when(frameWriter.writePushPromise(any(ChannelHandlerContext.class), anyInt(), anyInt(), any(Http2Headers.class), - anyInt())).thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null)); - - when(frameWriter.writeFrame(any(ChannelHandlerContext.class), anyByte(), anyInt(), any(Http2Flags.class), - any(ByteBuf.class))).thenAnswer(invocationOnMock -> { - buffers.offer((ByteBuf) invocationOnMock.getArgument(4)); - return ImmediateEventExecutor.INSTANCE.newSucceededFuture(null); - }); - return frameWriter; - } - - static Promise anyChannelPromise() { - return any(Promise.class); - } - - static Http2Settings anyHttp2Settings() { - return any(Http2Settings.class); - } - - static ByteBuf bb(String s) { - return ByteBufUtil.writeUtf8(UnpooledByteBufAllocator.DEFAULT, s); - } - - static ByteBuf bb(int size) { - return UnpooledByteBufAllocator.DEFAULT.buffer().writeZero(size); - } - - 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/HttpConversionUtilTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/HttpConversionUtilTest.java deleted file mode 100644 index debfc565a7..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/HttpConversionUtilTest.java +++ /dev/null @@ -1,233 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.handler.codec.http.DefaultHttpHeaders; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.util.AsciiString; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; - -import static io.netty.handler.codec.http.HttpHeaderNames.CONNECTION; -import static io.netty.handler.codec.http.HttpHeaderNames.COOKIE; -import static io.netty.handler.codec.http.HttpHeaderNames.HOST; -import static io.netty.handler.codec.http.HttpHeaderNames.KEEP_ALIVE; -import static io.netty.handler.codec.http.HttpHeaderNames.PROXY_CONNECTION; -import static io.netty.handler.codec.http.HttpHeaderNames.TE; -import static io.netty.handler.codec.http.HttpHeaderNames.TRANSFER_ENCODING; -import static io.netty.handler.codec.http.HttpHeaderNames.UPGRADE; -import static io.netty.handler.codec.http.HttpHeaderValues.GZIP; -import static io.netty.handler.codec.http.HttpHeaderValues.TRAILERS; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class HttpConversionUtilTest { - - @Test - public void connectNoPath() throws Exception { - String authority = "netty.io:80"; - Http2Headers headers = new DefaultHttp2Headers(); - headers.authority(authority); - headers.method(HttpMethod.CONNECT.asciiName()); - HttpRequest request = HttpConversionUtil.toHttpRequest(0, headers, true); - assertNotNull(request); - assertEquals(authority, request.uri()); - assertEquals(authority, request.headers().get(HOST)); - } - - @Test - public void setHttp2AuthorityWithoutUserInfo() { - Http2Headers headers = new DefaultHttp2Headers(); - - HttpConversionUtil.setHttp2Authority("foo", headers); - assertEquals(new AsciiString("foo"), headers.authority()); - } - - @Test - public void setHttp2AuthorityWithUserInfo() { - Http2Headers headers = new DefaultHttp2Headers(); - - HttpConversionUtil.setHttp2Authority("info@foo", headers); - assertEquals(new AsciiString("foo"), headers.authority()); - - HttpConversionUtil.setHttp2Authority("@foo.bar", headers); - assertEquals(new AsciiString("foo.bar"), headers.authority()); - } - - @Test - public void setHttp2AuthorityNullOrEmpty() { - Http2Headers headers = new DefaultHttp2Headers(); - - HttpConversionUtil.setHttp2Authority(null, headers); - assertNull(headers.authority()); - - HttpConversionUtil.setHttp2Authority("", headers); - assertSame(AsciiString.EMPTY_STRING, headers.authority()); - } - - @Test - public void setHttp2AuthorityWithEmptyAuthority() { - assertThrows(IllegalArgumentException.class, new Executable() { - @Override - public void execute() { - HttpConversionUtil.setHttp2Authority("info@", new DefaultHttp2Headers()); - } - }); - } - - @Test - public void stripTEHeaders() { - HttpHeaders inHeaders = new DefaultHttpHeaders(); - inHeaders.add(TE, GZIP); - Http2Headers out = new DefaultHttp2Headers(); - HttpConversionUtil.toHttp2Headers(inHeaders, out); - assertTrue(out.isEmpty()); - } - - @Test - public void stripTEHeadersExcludingTrailers() { - HttpHeaders inHeaders = new DefaultHttpHeaders(); - inHeaders.add(TE, GZIP); - inHeaders.add(TE, TRAILERS); - Http2Headers out = new DefaultHttp2Headers(); - HttpConversionUtil.toHttp2Headers(inHeaders, out); - assertSame(TRAILERS, out.get(TE)); - } - - @Test - public void stripTEHeadersCsvSeparatedExcludingTrailers() { - HttpHeaders inHeaders = new DefaultHttpHeaders(); - inHeaders.add(TE, GZIP + "," + TRAILERS); - Http2Headers out = new DefaultHttp2Headers(); - HttpConversionUtil.toHttp2Headers(inHeaders, out); - assertSame(TRAILERS, out.get(TE)); - } - - @Test - public void stripTEHeadersCsvSeparatedAccountsForValueSimilarToTrailers() { - HttpHeaders inHeaders = new DefaultHttpHeaders(); - inHeaders.add(TE, GZIP + "," + TRAILERS + "foo"); - Http2Headers out = new DefaultHttp2Headers(); - HttpConversionUtil.toHttp2Headers(inHeaders, out); - assertFalse(out.contains(TE)); - } - - @Test - public void stripTEHeadersAccountsForValueSimilarToTrailers() { - HttpHeaders inHeaders = new DefaultHttpHeaders(); - inHeaders.add(TE, TRAILERS + "foo"); - Http2Headers out = new DefaultHttp2Headers(); - HttpConversionUtil.toHttp2Headers(inHeaders, out); - assertFalse(out.contains(TE)); - } - - @Test - public void stripTEHeadersAccountsForOWS() { - HttpHeaders inHeaders = new DefaultHttpHeaders(); - inHeaders.add(TE, " " + TRAILERS + ' '); - Http2Headers out = new DefaultHttp2Headers(); - HttpConversionUtil.toHttp2Headers(inHeaders, out); - assertSame(TRAILERS, out.get(TE)); - } - - @Test - public void stripConnectionHeadersAndNominees() { - HttpHeaders inHeaders = new DefaultHttpHeaders(); - inHeaders.add(CONNECTION, "foo"); - inHeaders.add("foo", "bar"); - Http2Headers out = new DefaultHttp2Headers(); - HttpConversionUtil.toHttp2Headers(inHeaders, out); - assertTrue(out.isEmpty()); - } - - @Test - public void stripConnectionNomineesWithCsv() { - HttpHeaders inHeaders = new DefaultHttpHeaders(); - inHeaders.add(CONNECTION, "foo, bar"); - inHeaders.add("foo", "baz"); - inHeaders.add("bar", "qux"); - inHeaders.add("hello", "world"); - Http2Headers out = new DefaultHttp2Headers(); - HttpConversionUtil.toHttp2Headers(inHeaders, out); - assertEquals(1, out.size()); - assertSame("world", out.get("hello")); - } - - @Test - public void addHttp2ToHttpHeadersCombinesCookies() throws Http2Exception { - Http2Headers inHeaders = new DefaultHttp2Headers(); - inHeaders.add("yes", "no"); - inHeaders.add(COOKIE, "foo=bar"); - inHeaders.add(COOKIE, "bax=baz"); - - HttpHeaders outHeaders = new DefaultHttpHeaders(); - - HttpConversionUtil.addHttp2ToHttpHeaders(5, inHeaders, outHeaders, HttpVersion.HTTP_1_1, false, false); - assertEquals("no", outHeaders.get("yes")); - assertEquals("foo=bar; bax=baz", outHeaders.get(COOKIE.toString())); - } - - @Test - public void connectionSpecificHeadersShouldBeRemoved() { - HttpHeaders inHeaders = new DefaultHttpHeaders(); - inHeaders.add(CONNECTION, "keep-alive"); - inHeaders.add(HOST, "example.com"); - @SuppressWarnings("deprecation") - AsciiString keepAlive = KEEP_ALIVE; - inHeaders.add(keepAlive, "timeout=5, max=1000"); - @SuppressWarnings("deprecation") - AsciiString proxyConnection = PROXY_CONNECTION; - inHeaders.add(proxyConnection, "timeout=5, max=1000"); - inHeaders.add(TRANSFER_ENCODING, "chunked"); - inHeaders.add(UPGRADE, "h2c"); - - Http2Headers outHeaders = new DefaultHttp2Headers(); - HttpConversionUtil.toHttp2Headers(inHeaders, outHeaders); - - assertFalse(outHeaders.contains(CONNECTION)); - assertFalse(outHeaders.contains(HOST)); - assertFalse(outHeaders.contains(keepAlive)); - assertFalse(outHeaders.contains(proxyConnection)); - assertFalse(outHeaders.contains(TRANSFER_ENCODING)); - assertFalse(outHeaders.contains(UPGRADE)); - } - - @Test - public void http2ToHttpHeaderTest() throws Exception { - Http2Headers http2Headers = new DefaultHttp2Headers(); - http2Headers.status("200"); - http2Headers.path("/meow"); // HTTP/2 Header response should not contain 'path' in response. - http2Headers.set("cat", "meow"); - - HttpHeaders httpHeaders = new DefaultHttpHeaders(); - HttpConversionUtil.addHttp2ToHttpHeaders(3, http2Headers, httpHeaders, HttpVersion.HTTP_1_1, false, true); - assertFalse(httpHeaders.contains(HttpConversionUtil.ExtensionHeaderNames.PATH.text())); - assertEquals("meow", httpHeaders.get("cat")); - - httpHeaders.clear(); - HttpConversionUtil.addHttp2ToHttpHeaders(3, http2Headers, httpHeaders, HttpVersion.HTTP_1_1, false, false); - assertTrue(httpHeaders.contains(HttpConversionUtil.ExtensionHeaderNames.PATH.text())); - assertEquals("meow", httpHeaders.get("cat")); - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/HttpToHttp2ConnectionHandlerTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/HttpToHttp2ConnectionHandlerTest.java deleted file mode 100644 index b30136469d..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/HttpToHttp2ConnectionHandlerTest.java +++ /dev/null @@ -1,592 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -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.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.local.LocalAddress; -import io.netty.channel.local.LocalChannel; -import io.netty.channel.local.LocalHandler; -import io.netty.channel.local.LocalServerChannel; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.DefaultHttpContent; -import io.netty.handler.codec.http.DefaultHttpRequest; -import io.netty.handler.codec.http.DefaultLastHttpContent; -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.HttpRequest; -import io.netty.handler.codec.http.HttpScheme; -import io.netty.handler.codec.http.LastHttpContent; -import io.netty.handler.codec.http2.Http2TestUtil.FrameCountDown; -import io.netty.util.AsciiString; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.stubbing.Answer; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.CountDownLatch; - -import static io.netty.handler.codec.http.HttpMethod.CONNECT; -import static io.netty.handler.codec.http.HttpMethod.GET; -import static io.netty.handler.codec.http.HttpMethod.OPTIONS; -import static io.netty.handler.codec.http.HttpMethod.POST; -import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; -import static io.netty.handler.codec.http2.Http2TestUtil.of; -import static io.netty.util.CharsetUtil.UTF_8; -import static java.util.concurrent.TimeUnit.SECONDS; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.instanceOf; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyBoolean; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.anyShort; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; - -/** - * Testing the {@link HttpToHttp2ConnectionHandler} for {@link FullHttpRequest} objects into HTTP/2 frames - */ -public class HttpToHttp2ConnectionHandlerTest { - private static final int WAIT_TIME_SECONDS = 5; - - @Mock - private Http2FrameListener clientListener; - - @Mock - private Http2FrameListener serverListener; - - private ServerBootstrap sb; - private Bootstrap cb; - private Channel serverChannel; - private volatile Channel serverConnectedChannel; - private Channel clientChannel; - private CountDownLatch requestLatch; - private CountDownLatch serverSettingsAckLatch; - private CountDownLatch trailersLatch; - private FrameCountDown serverFrameCountDown; - - @BeforeEach - public void setup() throws Exception { - MockitoAnnotations.initMocks(this); - } - - @AfterEach - public void tearDown() throws Exception { - if (clientChannel != null) { - clientChannel.close().syncUninterruptibly(); - clientChannel = null; - } - if (serverChannel != null) { - serverChannel.close().syncUninterruptibly(); - serverChannel = null; - } - final Channel serverConnectedChannel = this.serverConnectedChannel; - if (serverConnectedChannel != null) { - serverConnectedChannel.close().syncUninterruptibly(); - this.serverConnectedChannel = null; - } - Future serverGroup = sb.config().group().shutdownGracefully(0, 5, SECONDS); - Future serverChildGroup = sb.config().childGroup().shutdownGracefully(0, 5, SECONDS); - Future clientGroup = cb.config().group().shutdownGracefully(0, 5, SECONDS); - serverGroup.syncUninterruptibly(); - serverChildGroup.syncUninterruptibly(); - clientGroup.syncUninterruptibly(); - } - - @Test - public void testHeadersOnlyRequest() throws Exception { - bootstrapEnv(2, 1, 0); - final FullHttpRequest request = new DefaultFullHttpRequest(HTTP_1_1, GET, - "http://my-user_name@www.example.org:5555/example"); - final HttpHeaders httpHeaders = request.headers(); - httpHeaders.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), 5); - httpHeaders.set(HttpHeaderNames.HOST, "my-user_name@www.example.org:5555"); - httpHeaders.set(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), "http"); - httpHeaders.add(of("foo"), of("goo")); - httpHeaders.add(of("foo"), of("goo2")); - httpHeaders.add(of("foo2"), of("goo2")); - final Http2Headers http2Headers = - new DefaultHttp2Headers().method(new AsciiString("GET")).path(new AsciiString("/example")) - .authority(new AsciiString("www.example.org:5555")).scheme(new AsciiString("http")) - .add(new AsciiString("foo"), new AsciiString("goo")) - .add(new AsciiString("foo"), new AsciiString("goo2")) - .add(new AsciiString("foo2"), new AsciiString("goo2")); - - verifyHeadersOnly(http2Headers, clientChannel.writeAndFlush(request)); - } - - @Test - public void testHttpScheme() throws Exception { - bootstrapEnv(2, 1, 0); - final FullHttpRequest request = new DefaultFullHttpRequest(HTTP_1_1, GET, - "http://my-user_name@www.example.org:5555/example"); - final HttpHeaders httpHeaders = request.headers(); - httpHeaders.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), 5); - httpHeaders.set(HttpHeaderNames.HOST, "my-user_name@www.example.org:5555"); - httpHeaders.add(of("foo"), of("goo")); - httpHeaders.add(of("foo"), of("goo2")); - httpHeaders.add(of("foo2"), of("goo2")); - final Http2Headers http2Headers = - new DefaultHttp2Headers().method(new AsciiString("GET")).path(new AsciiString("/example")) - .authority(new AsciiString("www.example.org:5555")).scheme(new AsciiString("http")) - .scheme(new AsciiString("http")) - .add(new AsciiString("foo"), new AsciiString("goo")) - .add(new AsciiString("foo"), new AsciiString("goo2")) - .add(new AsciiString("foo2"), new AsciiString("goo2")); - - verifyHeadersOnly(http2Headers, clientChannel.writeAndFlush(request)); - } - - @Test - public void testMultipleCookieEntriesAreCombined() throws Exception { - bootstrapEnv(2, 1, 0); - final FullHttpRequest request = new DefaultFullHttpRequest(HTTP_1_1, GET, - "http://my-user_name@www.example.org:5555/example"); - final HttpHeaders httpHeaders = request.headers(); - httpHeaders.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), 5); - httpHeaders.set(HttpHeaderNames.HOST, "my-user_name@www.example.org:5555"); - httpHeaders.set(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), "http"); - httpHeaders.set(HttpHeaderNames.COOKIE, "a=b; c=d; e=f"); - final Http2Headers http2Headers = - new DefaultHttp2Headers().method(new AsciiString("GET")).path(new AsciiString("/example")) - .authority(new AsciiString("www.example.org:5555")).scheme(new AsciiString("http")) - .add(HttpHeaderNames.COOKIE, "a=b") - .add(HttpHeaderNames.COOKIE, "c=d") - .add(HttpHeaderNames.COOKIE, "e=f"); - - verifyHeadersOnly(http2Headers, clientChannel.writeAndFlush(request)); - } - - @Test - public void testOriginFormRequestTargetHandled() throws Exception { - bootstrapEnv(2, 1, 0); - final FullHttpRequest request = new DefaultFullHttpRequest(HTTP_1_1, GET, "/where?q=now&f=then#section1"); - final HttpHeaders httpHeaders = request.headers(); - httpHeaders.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), 5); - httpHeaders.set(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), "http"); - final Http2Headers http2Headers = - new DefaultHttp2Headers().method(new AsciiString("GET")) - .path(new AsciiString("/where?q=now&f=then#section1")) - .scheme(new AsciiString("http")); - - verifyHeadersOnly(http2Headers, clientChannel.writeAndFlush(request)); - } - - @Test - public void testOriginFormRequestTargetHandledFromUrlencodedUri() throws Exception { - bootstrapEnv(2, 1, 0); - final FullHttpRequest request = new DefaultFullHttpRequest( - HTTP_1_1, GET, "/where%2B0?q=now%2B0&f=then%2B0#section1%2B0"); - final HttpHeaders httpHeaders = request.headers(); - httpHeaders.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), 5); - httpHeaders.set(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), "http"); - final Http2Headers http2Headers = - new DefaultHttp2Headers().method(new AsciiString("GET")) - .path(new AsciiString("/where%2B0?q=now%2B0&f=then%2B0#section1%2B0")) - .scheme(new AsciiString("http")); - - verifyHeadersOnly(http2Headers, clientChannel.writeAndFlush(request)); - } - - @Test - public void testAbsoluteFormRequestTargetHandledFromHeaders() throws Exception { - bootstrapEnv(2, 1, 0); - final FullHttpRequest request = new DefaultFullHttpRequest(HTTP_1_1, GET, "/pub/WWW/TheProject.html"); - final HttpHeaders httpHeaders = request.headers(); - httpHeaders.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), 5); - httpHeaders.set(HttpHeaderNames.HOST, "foouser@www.example.org:5555"); - httpHeaders.set(HttpConversionUtil.ExtensionHeaderNames.PATH.text(), "ignored_path"); - httpHeaders.set(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), "https"); - final Http2Headers http2Headers = - new DefaultHttp2Headers().method(new AsciiString("GET")) - .path(new AsciiString("/pub/WWW/TheProject.html")) - .authority(new AsciiString("www.example.org:5555")).scheme(new AsciiString("https")); - - verifyHeadersOnly(http2Headers, clientChannel.writeAndFlush(request)); - } - - @Test - public void testAbsoluteFormRequestTargetHandledFromRequestTargetUri() throws Exception { - bootstrapEnv(2, 1, 0); - final FullHttpRequest request = new DefaultFullHttpRequest(HTTP_1_1, GET, - "http://foouser@www.example.org:5555/pub/WWW/TheProject.html"); - final HttpHeaders httpHeaders = request.headers(); - httpHeaders.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), 5); - final Http2Headers http2Headers = - new DefaultHttp2Headers().method(new AsciiString("GET")) - .path(new AsciiString("/pub/WWW/TheProject.html")) - .authority(new AsciiString("www.example.org:5555")).scheme(new AsciiString("http")); - - verifyHeadersOnly(http2Headers, clientChannel.writeAndFlush(request)); - } - - @Test - public void testAuthorityFormRequestTargetHandled() throws Exception { - bootstrapEnv(2, 1, 0); - final FullHttpRequest request = new DefaultFullHttpRequest(HTTP_1_1, CONNECT, "http://www.example.com:80"); - final HttpHeaders httpHeaders = request.headers(); - httpHeaders.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), 5); - final Http2Headers http2Headers = - new DefaultHttp2Headers().method(new AsciiString("CONNECT")).path(new AsciiString("/")) - .scheme(new AsciiString("http")).authority(new AsciiString("www.example.com:80")); - - verifyHeadersOnly(http2Headers, clientChannel.writeAndFlush(request)); - } - - @Test - public void testAsterikFormRequestTargetHandled() throws Exception { - bootstrapEnv(2, 1, 0); - final FullHttpRequest request = new DefaultFullHttpRequest(HTTP_1_1, OPTIONS, "*"); - final HttpHeaders httpHeaders = request.headers(); - httpHeaders.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), 5); - httpHeaders.set(HttpHeaderNames.HOST, "www.example.com:80"); - httpHeaders.set(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), "http"); - final Http2Headers http2Headers = - new DefaultHttp2Headers().method(new AsciiString("OPTIONS")).path(new AsciiString("*")) - .scheme(new AsciiString("http")).authority(new AsciiString("www.example.com:80")); - - verifyHeadersOnly(http2Headers, clientChannel.writeAndFlush(request)); - } - - @Test - public void testHostIPv6FormRequestTargetHandled() throws Exception { - // Valid according to - // https://tools.ietf.org/html/rfc7230#section-2.7.1 -> https://tools.ietf.org/html/rfc3986#section-3.2.2 - bootstrapEnv(2, 1, 0); - final FullHttpRequest request = new DefaultFullHttpRequest(HTTP_1_1, GET, "/"); - final HttpHeaders httpHeaders = request.headers(); - httpHeaders.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), 5); - httpHeaders.set(HttpHeaderNames.HOST, "[::1]:80"); - httpHeaders.set(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), "http"); - final Http2Headers http2Headers = - new DefaultHttp2Headers().method(new AsciiString("GET")).path(new AsciiString("/")) - .scheme(new AsciiString("http")).authority(new AsciiString("[::1]:80")); - - verifyHeadersOnly(http2Headers, clientChannel.writeAndFlush(request)); - } - - @Test - public void testHostFormRequestTargetHandled() throws Exception { - bootstrapEnv(2, 1, 0); - final FullHttpRequest request = new DefaultFullHttpRequest(HTTP_1_1, GET, "/"); - final HttpHeaders httpHeaders = request.headers(); - httpHeaders.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), 5); - httpHeaders.set(HttpHeaderNames.HOST, "localhost:80"); - httpHeaders.set(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), "http"); - final Http2Headers http2Headers = - new DefaultHttp2Headers().method(new AsciiString("GET")).path(new AsciiString("/")) - .scheme(new AsciiString("http")).authority(new AsciiString("localhost:80")); - - verifyHeadersOnly(http2Headers, clientChannel.writeAndFlush(request)); - } - - @Test - public void testHostIPv4FormRequestTargetHandled() throws Exception { - bootstrapEnv(2, 1, 0); - final FullHttpRequest request = new DefaultFullHttpRequest(HTTP_1_1, GET, "/"); - final HttpHeaders httpHeaders = request.headers(); - httpHeaders.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), 5); - httpHeaders.set(HttpHeaderNames.HOST, "1.2.3.4:80"); - httpHeaders.set(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), "http"); - final Http2Headers http2Headers = - new DefaultHttp2Headers().method(new AsciiString("GET")).path(new AsciiString("/")) - .scheme(new AsciiString("http")).authority(new AsciiString("1.2.3.4:80")); - - verifyHeadersOnly(http2Headers, clientChannel.writeAndFlush(request)); - } - - @Test - public void testNoSchemeRequestTargetHandled() throws Exception { - bootstrapEnv(2, 1, 0); - final FullHttpRequest request = new DefaultFullHttpRequest(HTTP_1_1, GET, "/"); - final HttpHeaders httpHeaders = request.headers(); - httpHeaders.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), 5); - httpHeaders.set(HttpHeaderNames.HOST, "localhost"); - - Future writeFuture = clientChannel.writeAndFlush(request); - - assertTrue(writeFuture.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS)); - assertTrue(writeFuture.isDone()); - assertFalse(writeFuture.isSuccess()); - } - - @Test - public void testInvalidStreamId() throws Exception { - bootstrapEnv(2, 1, 0); - final FullHttpRequest request = new DefaultFullHttpRequest(HTTP_1_1, POST, "/foo", - Unpooled.copiedBuffer("foobar", UTF_8)); - final HttpHeaders httpHeaders = request.headers(); - httpHeaders.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), -1); - httpHeaders.set(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), "http"); - httpHeaders.set(HttpHeaderNames.HOST, "localhost"); - - Future writeFuture = clientChannel.writeAndFlush(request); - - assertTrue(writeFuture.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS)); - assertTrue(writeFuture.isDone()); - assertFalse(writeFuture.isSuccess()); - Throwable cause = writeFuture.cause(); - assertThat(cause, instanceOf(Http2NoMoreStreamIdsException.class)); - } - - @Test - public void testRequestWithBody() throws Exception { - final String text = "foooooogoooo"; - final List receivedBuffers = Collections.synchronizedList(new ArrayList<>()); - doAnswer((Answer) in -> { - receivedBuffers.add(((ByteBuf) in.getArguments()[2]).toString(UTF_8)); - return null; - }).when(serverListener).onDataRead(any(ChannelHandlerContext.class), eq(3), - any(ByteBuf.class), eq(0), eq(true)); - bootstrapEnv(3, 1, 0); - final FullHttpRequest request = new DefaultFullHttpRequest(HTTP_1_1, POST, - "http://your_user-name123@www.example.org:5555/example", - Unpooled.copiedBuffer(text, UTF_8)); - final HttpHeaders httpHeaders = request.headers(); - httpHeaders.set(HttpHeaderNames.HOST, "www.example-origin.org:5555"); - httpHeaders.add(of("foo"), of("goo")); - httpHeaders.add(of("foo"), of("goo2")); - httpHeaders.add(of("foo2"), of("goo2")); - final Http2Headers http2Headers = - new DefaultHttp2Headers().method(new AsciiString("POST")).path(new AsciiString("/example")) - .authority(new AsciiString("www.example-origin.org:5555")).scheme(new AsciiString("http")) - .add(new AsciiString("foo"), new AsciiString("goo")) - .add(new AsciiString("foo"), new AsciiString("goo2")) - .add(new AsciiString("foo2"), new AsciiString("goo2")); - - Future writeFuture = clientChannel.writeAndFlush(request); - - assertTrue(writeFuture.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS)); - assertTrue(writeFuture.isSuccess()); - awaitRequests(); - verify(serverListener).onHeadersRead(any(ChannelHandlerContext.class), eq(3), eq(http2Headers), eq(0), - anyShort(), anyBoolean(), eq(0), eq(false)); - verify(serverListener).onDataRead(any(ChannelHandlerContext.class), eq(3), any(ByteBuf.class), eq(0), - eq(true)); - assertEquals(1, receivedBuffers.size()); - assertEquals(text, receivedBuffers.get(0)); - } - - @Test - public void testRequestWithBodyAndTrailingHeaders() throws Exception { - final String text = "foooooogoooo"; - final List receivedBuffers = Collections.synchronizedList(new ArrayList<>()); - doAnswer((Answer) in -> { - receivedBuffers.add(((ByteBuf) in.getArguments()[2]).toString(UTF_8)); - return null; - }).when(serverListener).onDataRead(any(ChannelHandlerContext.class), eq(3), - any(ByteBuf.class), eq(0), eq(false)); - bootstrapEnv(4, 1, 1); - final FullHttpRequest request = new DefaultFullHttpRequest(HTTP_1_1, POST, - "http://your_user-name123@www.example.org:5555/example", - Unpooled.copiedBuffer(text, UTF_8)); - final HttpHeaders httpHeaders = request.headers(); - httpHeaders.set(HttpHeaderNames.HOST, "www.example.org:5555"); - httpHeaders.add(of("foo"), of("goo")); - httpHeaders.add(of("foo"), of("goo2")); - httpHeaders.add(of("foo2"), of("goo2")); - final Http2Headers http2Headers = - new DefaultHttp2Headers().method(new AsciiString("POST")).path(new AsciiString("/example")) - .authority(new AsciiString("www.example.org:5555")).scheme(new AsciiString("http")) - .add(new AsciiString("foo"), new AsciiString("goo")) - .add(new AsciiString("foo"), new AsciiString("goo2")) - .add(new AsciiString("foo2"), new AsciiString("goo2")); - - request.trailingHeaders().add(of("trailing"), of("bar")); - - final Http2Headers http2TrailingHeaders = new DefaultHttp2Headers() - .add(new AsciiString("trailing"), new AsciiString("bar")); - - Future writeFuture = clientChannel.writeAndFlush(request); - - assertTrue(writeFuture.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS)); - assertTrue(writeFuture.isSuccess()); - awaitRequests(); - verify(serverListener).onHeadersRead(any(ChannelHandlerContext.class), eq(3), eq(http2Headers), eq(0), - anyShort(), anyBoolean(), eq(0), eq(false)); - verify(serverListener).onDataRead(any(ChannelHandlerContext.class), eq(3), any(ByteBuf.class), eq(0), - eq(false)); - verify(serverListener).onHeadersRead(any(ChannelHandlerContext.class), eq(3), eq(http2TrailingHeaders), eq(0), - anyShort(), anyBoolean(), eq(0), eq(true)); - assertEquals(1, receivedBuffers.size()); - assertEquals(text, receivedBuffers.get(0)); - } - - @Test - public void testChunkedRequestWithBodyAndTrailingHeaders() throws Exception { - final String text = "foooooo"; - final String text2 = "goooo"; - final List receivedBuffers = Collections.synchronizedList(new ArrayList<>()); - doAnswer((Answer) in -> { - receivedBuffers.add(((ByteBuf) in.getArguments()[2]).toString(UTF_8)); - return null; - }).when(serverListener).onDataRead(any(ChannelHandlerContext.class), eq(3), - any(ByteBuf.class), eq(0), eq(false)); - bootstrapEnv(4, 1, 1); - final HttpRequest request = new DefaultHttpRequest(HTTP_1_1, POST, - "http://your_user-name123@www.example.org:5555/example"); - final HttpHeaders httpHeaders = request.headers(); - httpHeaders.set(HttpHeaderNames.HOST, "www.example.org:5555"); - httpHeaders.add(HttpHeaderNames.TRANSFER_ENCODING, "chunked"); - httpHeaders.add(of("foo"), of("goo")); - httpHeaders.add(of("foo"), of("goo2")); - httpHeaders.add(of("foo2"), of("goo2")); - final Http2Headers http2Headers = - new DefaultHttp2Headers().method(new AsciiString("POST")).path(new AsciiString("/example")) - .authority(new AsciiString("www.example.org:5555")).scheme(new AsciiString("http")) - .add(new AsciiString("foo"), new AsciiString("goo")) - .add(new AsciiString("foo"), new AsciiString("goo2")) - .add(new AsciiString("foo2"), new AsciiString("goo2")); - - final DefaultHttpContent httpContent = new DefaultHttpContent(Unpooled.copiedBuffer(text, UTF_8)); - final LastHttpContent lastHttpContent = new DefaultLastHttpContent(Unpooled.copiedBuffer(text2, UTF_8)); - - lastHttpContent.trailingHeaders().add(of("trailing"), of("bar")); - - final Http2Headers http2TrailingHeaders = new DefaultHttp2Headers() - .add(new AsciiString("trailing"), new AsciiString("bar")); - - Future writeFuture = clientChannel.write(request); - Future contentFuture = clientChannel.write(httpContent); - Future lastContentFuture = clientChannel.write(lastHttpContent); - - clientChannel.flush(); - - assertTrue(writeFuture.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS)); - assertTrue(writeFuture.isSuccess()); - - assertTrue(contentFuture.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS)); - assertTrue(contentFuture.isSuccess()); - - assertTrue(lastContentFuture.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS)); - assertTrue(lastContentFuture.isSuccess()); - - awaitRequests(); - verify(serverListener).onHeadersRead(any(ChannelHandlerContext.class), eq(3), eq(http2Headers), eq(0), - anyShort(), anyBoolean(), eq(0), eq(false)); - verify(serverListener).onDataRead(any(ChannelHandlerContext.class), eq(3), any(ByteBuf.class), eq(0), - eq(false)); - verify(serverListener).onHeadersRead(any(ChannelHandlerContext.class), eq(3), eq(http2TrailingHeaders), eq(0), - anyShort(), anyBoolean(), eq(0), eq(true)); - assertEquals(1, receivedBuffers.size()); - assertEquals(text + text2, receivedBuffers.get(0)); - } - - private void bootstrapEnv(int requestCountDown, int serverSettingsAckCount, int trailersCount) throws Exception { - final CountDownLatch prefaceWrittenLatch = new CountDownLatch(1); - final CountDownLatch serverChannelLatch = new CountDownLatch(1); - requestLatch = new CountDownLatch(requestCountDown); - serverSettingsAckLatch = new CountDownLatch(serverSettingsAckCount); - trailersLatch = trailersCount == 0 ? null : new CountDownLatch(trailersCount); - - sb = new ServerBootstrap(); - cb = new Bootstrap(); - - sb.group(new MultithreadEventLoopGroup(LocalHandler.newFactory())); - sb.channel(LocalServerChannel.class); - sb.childHandler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - serverConnectedChannel = ch; - ChannelPipeline p = ch.pipeline(); - serverFrameCountDown = - new FrameCountDown(serverListener, serverSettingsAckLatch, requestLatch, null, trailersLatch); - p.addLast(new HttpToHttp2ConnectionHandlerBuilder() - .server(true) - .frameListener(serverFrameCountDown) - .httpScheme(HttpScheme.HTTP) - .build()); - serverChannelLatch.countDown(); - } - }); - - cb.group(new MultithreadEventLoopGroup(LocalHandler.newFactory())); - cb.channel(LocalChannel.class); - cb.handler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - ChannelPipeline p = ch.pipeline(); - HttpToHttp2ConnectionHandler handler = new HttpToHttp2ConnectionHandlerBuilder() - .server(false) - .frameListener(clientListener) - .gracefulShutdownTimeoutMillis(0) - .build(); - p.addLast(handler); - p.addLast(new ChannelHandler() { - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (evt == Http2ConnectionPrefaceAndSettingsFrameWrittenEvent.INSTANCE) { - prefaceWrittenLatch.countDown(); - ctx.pipeline().remove(this); - } - } - }); - } - }); - - serverChannel = sb.bind(new LocalAddress("HttpToHttp2ConnectionHandlerTest")).get(); - - clientChannel = cb.connect(serverChannel.localAddress()).get(); - assertTrue(prefaceWrittenLatch.await(5, SECONDS)); - assertTrue(serverChannelLatch.await(WAIT_TIME_SECONDS, SECONDS)); - } - private void verifyHeadersOnly(Http2Headers expected, Future writeFuture) - throws Exception { - assertTrue(writeFuture.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS)); - assertTrue(writeFuture.isSuccess()); - awaitRequests(); - verify(serverListener).onHeadersRead(any(ChannelHandlerContext.class), eq(5), - eq(expected), eq(0), anyShort(), anyBoolean(), eq(0), eq(true)); - verify(serverListener, never()).onDataRead(any(ChannelHandlerContext.class), anyInt(), - any(ByteBuf.class), anyInt(), anyBoolean()); - } - - private void awaitRequests() throws Exception { - assertTrue(requestLatch.await(WAIT_TIME_SECONDS, SECONDS)); - if (trailersLatch != null) { - assertTrue(trailersLatch.await(WAIT_TIME_SECONDS, SECONDS)); - } - assertTrue(serverSettingsAckLatch.await(WAIT_TIME_SECONDS, SECONDS)); - } - - private ChannelHandlerContext ctx() { - return clientChannel.pipeline().firstContext(); - } - - private Promise newPromise() { - return ctx().newPromise(); - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/InOrderHttp2Headers.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/InOrderHttp2Headers.java deleted file mode 100644 index e566e94863..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/InOrderHttp2Headers.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.handler.codec.CharSequenceValueConverter; -import io.netty.handler.codec.DefaultHeaders; - -import static io.netty.util.AsciiString.CASE_INSENSITIVE_HASHER; -import static io.netty.util.AsciiString.CASE_SENSITIVE_HASHER; - -/** - * Http2Headers implementation that preserves headers insertion order. - */ -public class InOrderHttp2Headers - extends DefaultHeaders implements Http2Headers { - - InOrderHttp2Headers() { - super(CharSequenceValueConverter.INSTANCE); - } - - @Override - public boolean equals(Object o) { - return o instanceof Http2Headers && equals((Http2Headers) o, CASE_SENSITIVE_HASHER); - } - - @Override - public int hashCode() { - return hashCode(CASE_SENSITIVE_HASHER); - } - - @Override - public Http2Headers method(CharSequence value) { - set(PseudoHeaderName.METHOD.value(), value); - return this; - } - - @Override - public Http2Headers scheme(CharSequence value) { - set(PseudoHeaderName.SCHEME.value(), value); - return this; - } - - @Override - public Http2Headers authority(CharSequence value) { - set(PseudoHeaderName.AUTHORITY.value(), value); - return this; - } - - @Override - public Http2Headers path(CharSequence value) { - set(PseudoHeaderName.PATH.value(), value); - return this; - } - - @Override - public Http2Headers status(CharSequence value) { - set(PseudoHeaderName.STATUS.value(), value); - return this; - } - - @Override - public CharSequence method() { - return get(PseudoHeaderName.METHOD.value()); - } - - @Override - public CharSequence scheme() { - return get(PseudoHeaderName.SCHEME.value()); - } - - @Override - public CharSequence authority() { - return get(PseudoHeaderName.AUTHORITY.value()); - } - - @Override - public CharSequence path() { - return get(PseudoHeaderName.PATH.value()); - } - - @Override - public CharSequence status() { - return get(PseudoHeaderName.STATUS.value()); - } - - @Override - public boolean contains(CharSequence name, CharSequence value, boolean caseInsensitive) { - return contains(name, value, caseInsensitive ? CASE_INSENSITIVE_HASHER : CASE_SENSITIVE_HASHER); - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapterTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapterTest.java deleted file mode 100644 index b5787221f7..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapterTest.java +++ /dev/null @@ -1,782 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -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.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.channel.local.LocalAddress; -import io.netty.channel.local.LocalChannel; -import io.netty.channel.local.LocalHandler; -import io.netty.channel.local.LocalServerChannel; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.DefaultFullHttpResponse; -import io.netty.handler.codec.http.FullHttpMessage; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpObject; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.util.AsciiString; -import io.netty.util.CharsetUtil; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.List; -import java.util.concurrent.CountDownLatch; - -import static io.netty.handler.codec.http2.Http2CodecUtil.getEmbeddedHttp2Exception; -import static io.netty.handler.codec.http2.Http2Exception.isStreamError; -import static io.netty.handler.codec.http2.Http2TestUtil.of; -import static io.netty.handler.codec.http2.Http2TestUtil.runInChannel; -import static java.util.concurrent.TimeUnit.SECONDS; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -/** - * Testing the {@link InboundHttp2ToHttpAdapter} and base class {@link InboundHttp2ToHttpAdapter} for HTTP/2 - * frames into {@link HttpObject}s - */ -public class InboundHttp2ToHttpAdapterTest { - private List capturedRequests; - private List capturedResponses; - - @Mock - private HttpResponseListener serverListener; - - @Mock - private HttpResponseListener clientListener; - - @Mock - private HttpSettingsListener settingsListener; - - private Http2ConnectionHandler serverHandler; - private Http2ConnectionHandler clientHandler; - private ServerBootstrap sb; - private Bootstrap cb; - private Channel serverChannel; - private volatile Channel serverConnectedChannel; - private Channel clientChannel; - private CountDownLatch serverLatch; - private CountDownLatch clientLatch; - private CountDownLatch serverLatch2; - private CountDownLatch clientLatch2; - private CountDownLatch settingsLatch; - private int maxContentLength; - private HttpResponseDelegator serverDelegator; - private HttpResponseDelegator clientDelegator; - private HttpSettingsDelegator settingsDelegator; - private Http2Exception clientException; - - @BeforeEach - public void setup() throws Exception { - MockitoAnnotations.initMocks(this); - } - - @AfterEach - public void tearDown() throws Exception { - cleanupCapturedRequests(); - cleanupCapturedResponses(); - if (clientChannel != null) { - clientChannel.close().syncUninterruptibly(); - clientChannel = null; - } - if (serverChannel != null) { - serverChannel.close().syncUninterruptibly(); - serverChannel = null; - } - final Channel serverConnectedChannel = this.serverConnectedChannel; - if (serverConnectedChannel != null) { - serverConnectedChannel.close().syncUninterruptibly(); - this.serverConnectedChannel = null; - } - Future serverGroup = sb.config().group().shutdownGracefully(0, 5, SECONDS); - Future serverChildGroup = sb.config().childGroup().shutdownGracefully(0, 5, SECONDS); - Future clientGroup = cb.config().group().shutdownGracefully(0, 5, SECONDS); - serverGroup.syncUninterruptibly(); - serverChildGroup.syncUninterruptibly(); - clientGroup.syncUninterruptibly(); - } - - @Test - public void clientRequestSingleHeaderNoDataFrames() throws Exception { - boostrapEnv(1, 1, 1); - final FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, - "/some/path/resource2", true); - try { - HttpHeaders httpHeaders = request.headers(); - httpHeaders.set(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), "https"); - httpHeaders.set(HttpHeaderNames.HOST, "example.org"); - httpHeaders.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); - httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, 0); - httpHeaders.setShort(HttpConversionUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), (short) 16); - final Http2Headers http2Headers = new DefaultHttp2Headers().method(new AsciiString("GET")). - scheme(new AsciiString("https")).authority(new AsciiString("example.org")) - .path(new AsciiString("/some/path/resource2")); - runInChannel(clientChannel, () -> { - clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, true); - clientChannel.flush(); - }); - awaitRequests(); - ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(FullHttpMessage.class); - verify(serverListener).messageReceived(requestCaptor.capture()); - capturedRequests = requestCaptor.getAllValues(); - assertEquals(request, capturedRequests.get(0)); - } finally { - request.release(); - } - } - - @Test - public void clientRequestSingleHeaderCookieSplitIntoMultipleEntries() throws Exception { - boostrapEnv(1, 1, 1); - final FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, - "/some/path/resource2", true); - try { - HttpHeaders httpHeaders = request.headers(); - httpHeaders.set(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), "https"); - httpHeaders.set(HttpHeaderNames.HOST, "example.org"); - httpHeaders.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); - httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, 0); - httpHeaders.set(HttpHeaderNames.COOKIE, "a=b; c=d; e=f"); - httpHeaders.setShort(HttpConversionUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), (short) 16); - final Http2Headers http2Headers = new DefaultHttp2Headers().method(new AsciiString("GET")). - scheme(new AsciiString("https")).authority(new AsciiString("example.org")) - .path(new AsciiString("/some/path/resource2")) - .add(HttpHeaderNames.COOKIE, "a=b") - .add(HttpHeaderNames.COOKIE, "c=d") - .add(HttpHeaderNames.COOKIE, "e=f"); - runInChannel(clientChannel, () -> { - clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, true); - clientChannel.flush(); - }); - awaitRequests(); - ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(FullHttpMessage.class); - verify(serverListener).messageReceived(requestCaptor.capture()); - capturedRequests = requestCaptor.getAllValues(); - assertEquals(request, capturedRequests.get(0)); - } finally { - request.release(); - } - } - - @Test - public void clientRequestSingleHeaderCookieSplitIntoMultipleEntries2() throws Exception { - boostrapEnv(1, 1, 1); - final FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, - "/some/path/resource2", true); - try { - HttpHeaders httpHeaders = request.headers(); - httpHeaders.set(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), "https"); - httpHeaders.set(HttpHeaderNames.HOST, "example.org"); - httpHeaders.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); - httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, 0); - httpHeaders.set(HttpHeaderNames.COOKIE, "a=b; c=d; e=f"); - httpHeaders.setShort(HttpConversionUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), (short) 16); - final Http2Headers http2Headers = new DefaultHttp2Headers().method(new AsciiString("GET")). - scheme(new AsciiString("https")).authority(new AsciiString("example.org")) - .path(new AsciiString("/some/path/resource2")) - .add(HttpHeaderNames.COOKIE, "a=b; c=d") - .add(HttpHeaderNames.COOKIE, "e=f"); - runInChannel(clientChannel, () -> { - clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, true); - clientChannel.flush(); - }); - awaitRequests(); - ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(FullHttpMessage.class); - verify(serverListener).messageReceived(requestCaptor.capture()); - capturedRequests = requestCaptor.getAllValues(); - assertEquals(request, capturedRequests.get(0)); - } finally { - request.release(); - } - } - - @Test - public void clientRequestSingleHeaderNonAsciiShouldThrow() throws Exception { - boostrapEnv(1, 1, 1); - final Http2Headers http2Headers = new DefaultHttp2Headers() - .method(new AsciiString("GET")) - .scheme(new AsciiString("https")) - .authority(new AsciiString("example.org")) - .path(new AsciiString("/some/path/resource2")) - .add(new AsciiString("çÃŖ".getBytes(CharsetUtil.UTF_8)), - new AsciiString("ÃÃŖ".getBytes(CharsetUtil.UTF_8))); - runInChannel(clientChannel, () -> { - clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, true); - clientChannel.flush(); - }); - awaitResponses(); - assertTrue(isStreamError(clientException)); - } - - @Test - public void clientRequestOneDataFrame() throws Exception { - boostrapEnv(1, 1, 1); - final String text = "hello world"; - final ByteBuf content = Unpooled.copiedBuffer(text.getBytes()); - final FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, - "/some/path/resource2", content, true); - try { - HttpHeaders httpHeaders = request.headers(); - httpHeaders.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); - httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, text.length()); - httpHeaders.setShort(HttpConversionUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), (short) 16); - final Http2Headers http2Headers = new DefaultHttp2Headers().method(new AsciiString("GET")).path( - new AsciiString("/some/path/resource2")); - runInChannel(clientChannel, () -> { - clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, false); - clientHandler.encoder().writeData(ctxClient(), 3, content.retainedDuplicate(), 0, true); - clientChannel.flush(); - }); - awaitRequests(); - ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(FullHttpMessage.class); - verify(serverListener).messageReceived(requestCaptor.capture()); - capturedRequests = requestCaptor.getAllValues(); - assertEquals(request, capturedRequests.get(0)); - } finally { - request.release(); - } - } - - @Test - public void clientRequestMultipleDataFrames() throws Exception { - boostrapEnv(1, 1, 1); - final String text = "hello world big time data!"; - final ByteBuf content = Unpooled.copiedBuffer(text.getBytes()); - final FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, - "/some/path/resource2", content, true); - try { - HttpHeaders httpHeaders = request.headers(); - httpHeaders.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); - httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, text.length()); - httpHeaders.setShort(HttpConversionUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), (short) 16); - final Http2Headers http2Headers = new DefaultHttp2Headers().method(new AsciiString("GET")).path( - new AsciiString("/some/path/resource2")); - final int midPoint = text.length() / 2; - runInChannel(clientChannel, () -> { - clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, false); - clientHandler.encoder().writeData( - ctxClient(), 3, content.retainedSlice(0, midPoint), 0, false); - clientHandler.encoder().writeData( - ctxClient(), 3, content.retainedSlice(midPoint, text.length() - midPoint), - 0, true); - clientChannel.flush(); - }); - awaitRequests(); - ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(FullHttpMessage.class); - verify(serverListener).messageReceived(requestCaptor.capture()); - capturedRequests = requestCaptor.getAllValues(); - assertEquals(request, capturedRequests.get(0)); - } finally { - request.release(); - } - } - - @Test - public void clientRequestMultipleEmptyDataFrames() throws Exception { - boostrapEnv(1, 1, 1); - final String text = ""; - final ByteBuf content = Unpooled.copiedBuffer(text.getBytes()); - final FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, - "/some/path/resource2", content, true); - try { - HttpHeaders httpHeaders = request.headers(); - httpHeaders.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); - httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, text.length()); - httpHeaders.setShort(HttpConversionUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), (short) 16); - final Http2Headers http2Headers = new DefaultHttp2Headers().method(new AsciiString("GET")).path( - new AsciiString("/some/path/resource2")); - runInChannel(clientChannel, () -> { - clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, false); - clientHandler.encoder().writeData(ctxClient(), 3, content.retain(), 0, false); - clientHandler.encoder().writeData(ctxClient(), 3, content.retain(), 0, false); - clientHandler.encoder().writeData(ctxClient(), 3, content.retain(), 0, true); - clientChannel.flush(); - }); - awaitRequests(); - ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(FullHttpMessage.class); - verify(serverListener).messageReceived(requestCaptor.capture()); - capturedRequests = requestCaptor.getAllValues(); - assertEquals(request, capturedRequests.get(0)); - } finally { - request.release(); - } - } - - @Test - public void clientRequestTrailingHeaders() throws Exception { - boostrapEnv(1, 1, 1); - final String text = "some data"; - final ByteBuf content = Unpooled.copiedBuffer(text.getBytes()); - final FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, - "/some/path/resource2", content, true); - try { - HttpHeaders httpHeaders = request.headers(); - httpHeaders.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); - httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, text.length()); - httpHeaders.setShort(HttpConversionUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), (short) 16); - HttpHeaders trailingHeaders = request.trailingHeaders(); - trailingHeaders.set(of("Foo"), of("goo")); - trailingHeaders.set(of("fOo2"), of("goo2")); - trailingHeaders.add(of("foO2"), of("goo3")); - final Http2Headers http2Headers = new DefaultHttp2Headers().method(new AsciiString("GET")).path( - new AsciiString("/some/path/resource2")); - final Http2Headers http2Headers2 = new DefaultHttp2Headers() - .set(new AsciiString("foo"), new AsciiString("goo")) - .set(new AsciiString("foo2"), new AsciiString("goo2")) - .add(new AsciiString("foo2"), new AsciiString("goo3")); - runInChannel(clientChannel, () -> { - clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, false); - clientHandler.encoder().writeData(ctxClient(), 3, content.retainedDuplicate(), 0, false); - clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers2, 0, true); - clientChannel.flush(); - }); - awaitRequests(); - ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(FullHttpMessage.class); - verify(serverListener).messageReceived(requestCaptor.capture()); - capturedRequests = requestCaptor.getAllValues(); - assertEquals(request, capturedRequests.get(0)); - } finally { - request.release(); - } - } - - @Test - public void clientRequestStreamDependencyInHttpMessageFlow() throws Exception { - boostrapEnv(1, 2, 1); - final String text = "hello world big time data!"; - final ByteBuf content = Unpooled.copiedBuffer(text.getBytes()); - final String text2 = "hello world big time data...number 2!!"; - final ByteBuf content2 = Unpooled.copiedBuffer(text2.getBytes()); - final FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.PUT, - "/some/path/resource", content, true); - final FullHttpMessage request2 = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.PUT, - "/some/path/resource2", content2, true); - try { - HttpHeaders httpHeaders = request.headers(); - httpHeaders.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); - httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, text.length()); - httpHeaders.setShort(HttpConversionUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), (short) 16); - HttpHeaders httpHeaders2 = request2.headers(); - httpHeaders2.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), 5); - httpHeaders2.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_DEPENDENCY_ID.text(), 3); - httpHeaders2.setShort(HttpConversionUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), (short) 123); - httpHeaders2.setInt(HttpHeaderNames.CONTENT_LENGTH, text2.length()); - final Http2Headers http2Headers = new DefaultHttp2Headers().method(new AsciiString("PUT")).path( - new AsciiString("/some/path/resource")); - final Http2Headers http2Headers2 = new DefaultHttp2Headers().method(new AsciiString("PUT")).path( - new AsciiString("/some/path/resource2")); - runInChannel(clientChannel, () -> { - clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, false); - clientHandler.encoder().writeHeaders(ctxClient(), 5, http2Headers2, 3, (short) 123, true, 0, - false); - clientChannel.flush(); // Headers are queued in the flow controller and so flush them. - clientHandler.encoder().writeData(ctxClient(), 3, content.retainedDuplicate(), 0, true); - clientHandler.encoder().writeData(ctxClient(), 5, content2.retainedDuplicate(), 0, true); - clientChannel.flush(); - }); - awaitRequests(); - ArgumentCaptor httpObjectCaptor = ArgumentCaptor.forClass(FullHttpMessage.class); - verify(serverListener, times(2)).messageReceived(httpObjectCaptor.capture()); - capturedRequests = httpObjectCaptor.getAllValues(); - assertEquals(request, capturedRequests.get(0)); - assertEquals(request2, capturedRequests.get(1)); - } finally { - request.release(); - request2.release(); - } - } - - @Test - public void serverRequestPushPromise() throws Exception { - boostrapEnv(1, 1, 1); - final String text = "hello world big time data!"; - final ByteBuf content = Unpooled.copiedBuffer(text.getBytes()); - final String text2 = "hello world smaller data?"; - final ByteBuf content2 = Unpooled.copiedBuffer(text2.getBytes()); - final FullHttpMessage response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, - content, true); - final FullHttpMessage response2 = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CREATED, - content2, true); - final FullHttpMessage request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/push/test", - true); - try { - HttpHeaders httpHeaders = response.headers(); - httpHeaders.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); - httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, text.length()); - httpHeaders.setShort(HttpConversionUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), (short) 16); - HttpHeaders httpHeaders2 = response2.headers(); - httpHeaders2.set(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), "https"); - httpHeaders2.set(HttpHeaderNames.HOST, "example.org"); - httpHeaders2.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), 5); - httpHeaders2.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_PROMISE_ID.text(), 3); - httpHeaders2.setInt(HttpHeaderNames.CONTENT_LENGTH, text2.length()); - - httpHeaders = request.headers(); - httpHeaders.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); - httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, 0); - httpHeaders.setShort(HttpConversionUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), (short) 16); - final Http2Headers http2Headers3 = new DefaultHttp2Headers().method(new AsciiString("GET")) - .path(new AsciiString("/push/test")); - runInChannel(clientChannel, () -> { - clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers3, 0, true); - clientChannel.flush(); - }); - awaitRequests(); - ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(FullHttpMessage.class); - verify(serverListener).messageReceived(requestCaptor.capture()); - capturedRequests = requestCaptor.getAllValues(); - assertEquals(request, capturedRequests.get(0)); - - final Http2Headers http2Headers = new DefaultHttp2Headers().status(new AsciiString("200")); - // The PUSH_PROMISE frame includes a header block that contains a - // complete set of request header fields that the server attributes to - // the request. - // https://tools.ietf.org/html/rfc7540#section-8.2.1 - // Therefore, we should consider the case where there is no Http response status. - final Http2Headers http2Headers2 = new DefaultHttp2Headers() - .scheme(new AsciiString("https")) - .authority(new AsciiString("example.org")); - runInChannel(serverConnectedChannel, () -> { - serverHandler.encoder().writeHeaders(ctxServer(), 3, http2Headers, 0, false); - serverHandler.encoder().writePushPromise(ctxServer(), 3, 2, http2Headers2, 0); - serverHandler.encoder().writeData(ctxServer(), 3, content.retainedDuplicate(), 0, true); - serverHandler.encoder().writeData(ctxServer(), 5, content2.retainedDuplicate(), 0, true); - serverConnectedChannel.flush(); - }); - awaitResponses(); - ArgumentCaptor responseCaptor = ArgumentCaptor.forClass(FullHttpMessage.class); - verify(clientListener).messageReceived(responseCaptor.capture()); - capturedResponses = responseCaptor.getAllValues(); - assertEquals(response, capturedResponses.get(0)); - } finally { - request.release(); - response.release(); - response2.release(); - } - } - - @Test - public void serverResponseHeaderInformational() throws Exception { - boostrapEnv(1, 2, 1, 2, 1); - final FullHttpMessage request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.PUT, "/info/test", - true); - HttpHeaders httpHeaders = request.headers(); - httpHeaders.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); - httpHeaders.set(HttpHeaderNames.EXPECT, HttpHeaderValues.CONTINUE); - httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, 0); - httpHeaders.setShort(HttpConversionUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), (short) 16); - - final Http2Headers http2Headers = new DefaultHttp2Headers().method(new AsciiString("PUT")) - .path(new AsciiString("/info/test")) - .set(new AsciiString(HttpHeaderNames.EXPECT.toString()), - new AsciiString(HttpHeaderValues.CONTINUE.toString())); - final FullHttpMessage response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE); - final String text = "a big payload"; - final ByteBuf payload = Unpooled.copiedBuffer(text.getBytes()); - final FullHttpMessage request2 = request.replace(payload); - final FullHttpMessage response2 = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - - try { - runInChannel(clientChannel, () -> { - clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, false); - clientChannel.flush(); - }); - - awaitRequests(); - httpHeaders = response.headers(); - httpHeaders.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); - httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, 0); - final Http2Headers http2HeadersResponse = new DefaultHttp2Headers().status(new AsciiString("100")); - runInChannel(serverConnectedChannel, () -> { - serverHandler.encoder().writeHeaders(ctxServer(), 3, http2HeadersResponse, 0, false); - serverConnectedChannel.flush(); - }); - - awaitResponses(); - httpHeaders = request2.headers(); - httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, text.length()); - httpHeaders.remove(HttpHeaderNames.EXPECT); - runInChannel(clientChannel, () -> { - clientHandler.encoder().writeData(ctxClient(), 3, payload.retainedDuplicate(), 0, true); - clientChannel.flush(); - }); - - awaitRequests2(); - httpHeaders = response2.headers(); - httpHeaders.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); - httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, 0); - httpHeaders.setShort(HttpConversionUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), (short) 16); - - final Http2Headers http2HeadersResponse2 = new DefaultHttp2Headers().status(new AsciiString("200")); - runInChannel(serverConnectedChannel, () -> { - serverHandler.encoder().writeHeaders(ctxServer(), 3, http2HeadersResponse2, 0, true); - serverConnectedChannel.flush(); - }); - - awaitResponses2(); - ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(FullHttpMessage.class); - verify(serverListener, times(2)).messageReceived(requestCaptor.capture()); - capturedRequests = requestCaptor.getAllValues(); - assertEquals(2, capturedRequests.size()); - // We do not expect to have this header in the captured request so remove it now. - assertNotNull(request.headers().remove("x-http2-stream-weight")); - - assertEquals(request, capturedRequests.get(0)); - assertEquals(request2, capturedRequests.get(1)); - - ArgumentCaptor responseCaptor = ArgumentCaptor.forClass(FullHttpMessage.class); - verify(clientListener, times(2)).messageReceived(responseCaptor.capture()); - capturedResponses = responseCaptor.getAllValues(); - assertEquals(2, capturedResponses.size()); - assertEquals(response, capturedResponses.get(0)); - assertEquals(response2, capturedResponses.get(1)); - } finally { - request.release(); - request2.release(); - response.release(); - response2.release(); - } - } - - @Test - public void propagateSettings() throws Exception { - boostrapEnv(1, 1, 2); - final Http2Settings settings = new Http2Settings().pushEnabled(true); - runInChannel(clientChannel, () -> { - clientHandler.encoder().writeSettings(ctxClient(), settings); - clientChannel.flush(); - }); - assertTrue(settingsLatch.await(5, SECONDS)); - ArgumentCaptor settingsCaptor = ArgumentCaptor.forClass(Http2Settings.class); - verify(settingsListener, times(2)).messageReceived(settingsCaptor.capture()); - assertEquals(settings, settingsCaptor.getValue()); - } - - private void boostrapEnv(int clientLatchCount, int serverLatchCount, int settingsLatchCount) throws Exception { - boostrapEnv(clientLatchCount, clientLatchCount, serverLatchCount, serverLatchCount, settingsLatchCount); - } - - private void boostrapEnv(int clientLatchCount, int clientLatchCount2, int serverLatchCount, int serverLatchCount2, - int settingsLatchCount) throws Exception { - final CountDownLatch prefaceWrittenLatch = new CountDownLatch(1); - clientDelegator = null; - serverDelegator = null; - serverConnectedChannel = null; - maxContentLength = 1024; - final CountDownLatch serverChannelLatch = new CountDownLatch(1); - serverLatch = new CountDownLatch(serverLatchCount); - clientLatch = new CountDownLatch(clientLatchCount); - serverLatch2 = new CountDownLatch(serverLatchCount2); - clientLatch2 = new CountDownLatch(clientLatchCount2); - settingsLatch = new CountDownLatch(settingsLatchCount); - - sb = new ServerBootstrap(); - cb = new Bootstrap(); - - sb.group(new MultithreadEventLoopGroup(LocalHandler.newFactory())); - sb.channel(LocalServerChannel.class); - sb.childHandler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - serverConnectedChannel = ch; - ChannelPipeline p = ch.pipeline(); - Http2Connection connection = new DefaultHttp2Connection(true); - - serverHandler = new Http2ConnectionHandlerBuilder().frameListener( - new InboundHttp2ToHttpAdapterBuilder(connection) - .maxContentLength(maxContentLength) - .validateHttpHeaders(true) - .propagateSettings(true) - .build()) - .connection(connection) - .gracefulShutdownTimeoutMillis(0) - .build(); - p.addLast(serverHandler); - - serverDelegator = new HttpResponseDelegator(serverListener, serverLatch, serverLatch2); - p.addLast(serverDelegator); - settingsDelegator = new HttpSettingsDelegator(settingsListener, settingsLatch); - p.addLast(settingsDelegator); - serverChannelLatch.countDown(); - } - }); - - cb.group(new MultithreadEventLoopGroup(LocalHandler.newFactory())); - cb.channel(LocalChannel.class); - cb.handler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - ChannelPipeline p = ch.pipeline(); - Http2Connection connection = new DefaultHttp2Connection(false); - - clientHandler = new Http2ConnectionHandlerBuilder().frameListener( - new InboundHttp2ToHttpAdapterBuilder(connection) - .maxContentLength(maxContentLength) - .build()) - .connection(connection) - .gracefulShutdownTimeoutMillis(0) - .build(); - p.addLast(clientHandler); - - clientDelegator = new HttpResponseDelegator(clientListener, clientLatch, clientLatch2); - p.addLast(clientDelegator); - p.addLast(new ChannelHandler() { - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - Http2Exception e = getEmbeddedHttp2Exception(cause); - if (e != null) { - clientException = e; - clientLatch.countDown(); - } else { - ctx.fireExceptionCaught(cause); - } - } - }); - p.addLast(new ChannelHandler() { - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (evt == Http2ConnectionPrefaceAndSettingsFrameWrittenEvent.INSTANCE) { - prefaceWrittenLatch.countDown(); - ctx.pipeline().remove(this); - } - } - }); - } - }); - - serverChannel = sb.bind(new LocalAddress("InboundHttp2ToHttpAdapterTest")).get(); - - clientChannel = cb.connect(serverChannel.localAddress()).get(); - assertTrue(prefaceWrittenLatch.await(5, SECONDS)); - assertTrue(serverChannelLatch.await(5, SECONDS)); - } - - private void cleanupCapturedRequests() { - if (capturedRequests != null) { - for (FullHttpMessage capturedRequest : capturedRequests) { - capturedRequest.release(); - } - capturedRequests = null; - } - } - - private void cleanupCapturedResponses() { - if (capturedResponses != null) { - for (FullHttpMessage capturedResponse : capturedResponses) { - capturedResponse.release(); - } - capturedResponses = null; - } - } - - private void awaitRequests() throws Exception { - assertTrue(serverLatch.await(5, SECONDS)); - } - - private void awaitResponses() throws Exception { - assertTrue(clientLatch.await(5, SECONDS)); - } - - private void awaitRequests2() throws Exception { - assertTrue(serverLatch2.await(5, SECONDS)); - } - - private void awaitResponses2() throws Exception { - assertTrue(clientLatch2.await(5, SECONDS)); - } - - private ChannelHandlerContext ctxClient() { - return clientChannel.pipeline().firstContext(); - } - - private Promise newPromiseClient() { - return ctxClient().newPromise(); - } - - private ChannelHandlerContext ctxServer() { - return serverConnectedChannel.pipeline().firstContext(); - } - - private Promise newPromiseServer() { - return ctxServer().newPromise(); - } - - private interface HttpResponseListener { - void messageReceived(HttpObject obj); - } - - private interface HttpSettingsListener { - void messageReceived(Http2Settings settings); - } - - private static final class HttpResponseDelegator extends SimpleChannelInboundHandler { - private final HttpResponseListener listener; - private final CountDownLatch latch; - private final CountDownLatch latch2; - - HttpResponseDelegator(HttpResponseListener listener, CountDownLatch latch, CountDownLatch latch2) { - super(false); - this.listener = listener; - this.latch = latch; - this.latch2 = latch2; - } - - @Override - protected void messageReceived(ChannelHandlerContext ctx, HttpObject msg) throws Exception { - listener.messageReceived(msg); - latch.countDown(); - latch2.countDown(); - } - } - - private static final class HttpSettingsDelegator extends SimpleChannelInboundHandler { - private final HttpSettingsListener listener; - private final CountDownLatch latch; - - HttpSettingsDelegator(HttpSettingsListener listener, CountDownLatch latch) { - super(false); - this.listener = listener; - this.latch = latch; - } - - @Override - protected void messageReceived(ChannelHandlerContext ctx, Http2Settings settings) throws Exception { - listener.messageReceived(settings); - latch.countDown(); - } - } -} 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 deleted file mode 100644 index f29171c06c..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/LastInboundHandler.java +++ /dev/null @@ -1,213 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.internal.PlatformDependent; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.locks.LockSupport; -import java.util.function.Consumer; - -import static java.util.Objects.requireNonNull; -import static java.util.concurrent.TimeUnit.MILLISECONDS; - -/** - * Channel handler that allows to easily access inbound messages. - */ -public class LastInboundHandler implements ChannelHandler { - private final List queue = new ArrayList<>(); - private final Consumer channelReadCompleteConsumer; - private Throwable lastException; - private ChannelHandlerContext ctx; - private boolean channelActive; - private String writabilityStates = ""; - - private static final Consumer NOOP_CONSUMER = obj -> { - }; - - @SuppressWarnings("unchecked") - public static Consumer noopConsumer() { - return (Consumer) NOOP_CONSUMER; - } - - public LastInboundHandler() { - this(noopConsumer()); - } - - public LastInboundHandler(Consumer channelReadCompleteConsumer) { - this.channelReadCompleteConsumer = requireNonNull(channelReadCompleteConsumer, "channelReadCompleteConsumer"); - } - - @Override - public void handlerAdded(ChannelHandlerContext ctx) throws Exception { - this.ctx = ctx; - } - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - if (channelActive) { - throw new IllegalStateException("channelActive may only be fired once."); - } - channelActive = true; - } - - public boolean isChannelActive() { - return channelActive; - } - - public String writabilityStates() { - return writabilityStates; - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - if (!channelActive) { - throw new IllegalStateException("channelInactive may only be fired once after channelActive."); - } - channelActive = false; - ctx.fireChannelInactive(); - } - - @Override - public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception { - if ("".equals(writabilityStates)) { - writabilityStates = String.valueOf(ctx.channel().isWritable()); - } else { - writabilityStates += "," + ctx.channel().isWritable(); - } - ctx.fireChannelWritabilityChanged(); - } - - @Override - 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)); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - if (lastException != null) { - cause.printStackTrace(); - } else { - lastException = cause; - } - } - - public void checkException() throws Exception { - if (lastException == null) { - return; - } - Throwable t = lastException; - lastException = null; - PlatformDependent.throwException(t); - } - - @SuppressWarnings("unchecked") - public T readInbound() { - for (int i = 0; i < queue.size(); i++) { - Object o = queue.get(i); - if (!(o instanceof UserEvent)) { - queue.remove(i); - return (T) o; - } - } - - return null; - } - - public T blockingReadInbound() { - T msg; - while ((msg = readInbound()) == null) { - LockSupport.parkNanos(MILLISECONDS.toNanos(10)); - } - return msg; - } - - @SuppressWarnings("unchecked") - public T readUserEvent() { - for (int i = 0; i < queue.size(); i++) { - Object o = queue.get(i); - if (o instanceof UserEvent) { - queue.remove(i); - return (T) ((UserEvent) o).evt; - } - } - - return null; - } - - /** - * Useful to test order of events and messages. - */ - @SuppressWarnings("unchecked") - public T readInboundMessageOrUserEvent() { - if (queue.isEmpty()) { - return null; - } - Object o = queue.remove(0); - if (o instanceof UserEvent) { - return (T) ((UserEvent) o).evt; - } - return (T) o; - } - - public void writeOutbound(Object... msgs) throws Exception { - for (Object msg : msgs) { - ctx.write(msg); - } - ctx.flush(); - EmbeddedChannel ch = (EmbeddedChannel) ctx.channel(); - ch.runPendingTasks(); - ch.checkException(); - checkException(); - } - - public void finishAndReleaseAll() throws Exception { - checkException(); - Object o; - while ((o = readInboundMessageOrUserEvent()) != null) { - ReferenceCountUtil.release(o); - } - } - - public Channel channel() { - return ctx.channel(); - } - - private static final class UserEvent { - private final Object evt; - - UserEvent(Object evt) { - this.evt = evt; - } - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/ReadOnlyHttp2HeadersTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/ReadOnlyHttp2HeadersTest.java deleted file mode 100644 index 7125eae44e..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/ReadOnlyHttp2HeadersTest.java +++ /dev/null @@ -1,298 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.util.AsciiString; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; - -import java.util.Iterator; -import java.util.Map; -import java.util.NoSuchElementException; - -import static io.netty.handler.codec.http2.DefaultHttp2HeadersTest.*; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class ReadOnlyHttp2HeadersTest { - @Test - public void notKeyValuePairThrows() { - assertThrows(IllegalArgumentException.class, new Executable() { - @Override - public void execute() { - ReadOnlyHttp2Headers.trailers(false, new AsciiString[]{ null }); - } - }); - } - - @Test - public void nullTrailersNotAllowed() { - assertThrows(NullPointerException.class, new Executable() { - @Override - public void execute() { - ReadOnlyHttp2Headers.trailers(false, (AsciiString[]) null); - } - }); - } - - @Test - public void nullHeaderNameNotChecked() { - ReadOnlyHttp2Headers.trailers(false, null, null); - } - - @Test - public void nullHeaderNameValidated() { - assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() { - ReadOnlyHttp2Headers.trailers(true, null, new AsciiString("foo")); - } - }); - } - - @Test - public void pseudoHeaderNotAllowedAfterNonPseudoHeaders() { - assertThrows(IllegalArgumentException.class, new Executable() { - @Override - public void execute() { - ReadOnlyHttp2Headers.trailers(true, new AsciiString(":name"), new AsciiString("foo"), - new AsciiString("othername"), new AsciiString("goo"), - new AsciiString(":pseudo"), new AsciiString("val")); - } - }); - } - - @Test - public void nullValuesAreNotAllowed() { - assertThrows(IllegalArgumentException.class, new Executable() { - @Override - public void execute() { - ReadOnlyHttp2Headers.trailers(true, new AsciiString("foo"), null); - } - }); - } - - @Test - public void emptyHeaderNameAllowed() { - ReadOnlyHttp2Headers.trailers(false, AsciiString.EMPTY_STRING, new AsciiString("foo")); - } - - @Test - public void testPseudoHeadersMustComeFirstWhenIteratingServer() { - Http2Headers headers = newServerHeaders(); - verifyPseudoHeadersFirst(headers); - } - - @Test - public void testPseudoHeadersMustComeFirstWhenIteratingClient() { - Http2Headers headers = newClientHeaders(); - verifyPseudoHeadersFirst(headers); - } - - @Test - public void testIteratorReadOnlyClient() { - assertThrows(UnsupportedOperationException.class, new Executable() { - @Override - public void execute() { - testIteratorReadOnly(newClientHeaders()); - } - }); - } - - @Test - public void testIteratorReadOnlyServer() { - assertThrows(UnsupportedOperationException.class, new Executable() { - @Override - public void execute() { - testIteratorReadOnly(newServerHeaders()); - } - }); - } - - @Test - public void testIteratorReadOnlyTrailers() { - assertThrows(UnsupportedOperationException.class, new Executable() { - @Override - public void execute() { - testIteratorReadOnly(newTrailers()); - } - }); - } - - @Test - public void testIteratorEntryReadOnlyClient() { - assertThrows(UnsupportedOperationException.class, new Executable() { - @Override - public void execute() { - testIteratorEntryReadOnly(newClientHeaders()); - } - }); - } - - @Test - public void testIteratorEntryReadOnlyServer() { - assertThrows(UnsupportedOperationException.class, new Executable() { - @Override - public void execute() { - testIteratorEntryReadOnly(newServerHeaders()); - } - }); - } - - @Test - public void testIteratorEntryReadOnlyTrailers() { - assertThrows(UnsupportedOperationException.class, new Executable() { - @Override - public void execute() { - testIteratorEntryReadOnly(newTrailers()); - } - }); - } - - @Test - public void testSize() { - Http2Headers headers = newTrailers(); - assertEquals(otherHeaders().length / 2, headers.size()); - } - - @Test - public void testIsNotEmpty() { - Http2Headers headers = newTrailers(); - assertFalse(headers.isEmpty()); - } - - @Test - public void testIsEmpty() { - Http2Headers headers = ReadOnlyHttp2Headers.trailers(false); - assertTrue(headers.isEmpty()); - } - - @Test - public void testContainsName() { - Http2Headers headers = newClientHeaders(); - assertTrue(headers.contains("Name1")); - assertTrue(headers.contains(Http2Headers.PseudoHeaderName.PATH.value())); - assertFalse(headers.contains(Http2Headers.PseudoHeaderName.STATUS.value())); - assertFalse(headers.contains("a missing header")); - } - - @Test - public void testContainsNameAndValue() { - Http2Headers headers = newClientHeaders(); - assertTrue(headers.contains("Name1", "value1")); - assertFalse(headers.contains("Name1", "Value1")); - assertTrue(headers.contains("name2", "Value2", true)); - assertFalse(headers.contains("name2", "Value2", false)); - assertTrue(headers.contains(Http2Headers.PseudoHeaderName.PATH.value(), "/foo")); - assertFalse(headers.contains(Http2Headers.PseudoHeaderName.STATUS.value(), "200")); - assertFalse(headers.contains("a missing header", "a missing value")); - } - - @Test - public void testGet() { - Http2Headers headers = newClientHeaders(); - assertTrue(AsciiString.contentEqualsIgnoreCase("value1", headers.get("Name1"))); - assertTrue(AsciiString.contentEqualsIgnoreCase("/foo", - headers.get(Http2Headers.PseudoHeaderName.PATH.value()))); - assertNull(headers.get(Http2Headers.PseudoHeaderName.STATUS.value())); - assertNull(headers.get("a missing header")); - } - - @Test - public void testClientOtherValueIterator() { - testValueIteratorSingleValue(newClientHeaders(), "name2", "value2"); - } - - @Test - public void testClientPsuedoValueIterator() { - testValueIteratorSingleValue(newClientHeaders(), ":path", "/foo"); - } - - @Test - public void testServerPsuedoValueIterator() { - testValueIteratorSingleValue(newServerHeaders(), ":status", "200"); - } - - @Test - public void testEmptyValueIterator() { - Http2Headers headers = newServerHeaders(); - final Iterator itr = headers.valueIterator("foo"); - assertFalse(itr.hasNext()); - assertThrows(NoSuchElementException.class, new Executable() { - @Override - public void execute() { - itr.next(); - } - }); - } - - @Test - public void testIteratorMultipleValues() { - Http2Headers headers = ReadOnlyHttp2Headers.serverHeaders(false, new AsciiString("200"), - new AsciiString("name2"), new AsciiString("value1"), - new AsciiString("name1"), new AsciiString("value2"), - new AsciiString("name2"), new AsciiString("value3")); - Iterator itr = headers.valueIterator("name2"); - assertTrue(itr.hasNext()); - assertTrue(AsciiString.contentEqualsIgnoreCase("value1", itr.next())); - assertTrue(itr.hasNext()); - assertTrue(AsciiString.contentEqualsIgnoreCase("value3", itr.next())); - assertFalse(itr.hasNext()); - } - - private static void testValueIteratorSingleValue(Http2Headers headers, CharSequence name, CharSequence value) { - Iterator itr = headers.valueIterator(name); - assertTrue(itr.hasNext()); - assertTrue(AsciiString.contentEqualsIgnoreCase(value, itr.next())); - assertFalse(itr.hasNext()); - } - - private static void testIteratorReadOnly(Http2Headers headers) { - Iterator> itr = headers.iterator(); - assertTrue(itr.hasNext()); - itr.remove(); - } - - private static void testIteratorEntryReadOnly(Http2Headers headers) { - Iterator> itr = headers.iterator(); - assertTrue(itr.hasNext()); - itr.next().setValue("foo"); - } - - private static ReadOnlyHttp2Headers newServerHeaders() { - return ReadOnlyHttp2Headers.serverHeaders(false, new AsciiString("200"), otherHeaders()); - } - - private static ReadOnlyHttp2Headers newClientHeaders() { - return ReadOnlyHttp2Headers.clientHeaders(false, new AsciiString("meth"), new AsciiString("/foo"), - new AsciiString("schemer"), new AsciiString("respect_my_authority"), otherHeaders()); - } - - private static ReadOnlyHttp2Headers newTrailers() { - return ReadOnlyHttp2Headers.trailers(false, otherHeaders()); - } - - private static AsciiString[] otherHeaders() { - return new AsciiString[] { - new AsciiString("name1"), new AsciiString("value1"), - new AsciiString("name2"), new AsciiString("value2"), - new AsciiString("name3"), new AsciiString("value3") - }; - } -} 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 deleted file mode 100644 index 46439be6ca..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/StreamBufferingEncoderTest.java +++ /dev/null @@ -1,576 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty.channel.Channel; -import io.netty.channel.ChannelConfig; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelMetadata; -import io.netty.channel.DefaultMessageSizeEstimator; -import io.netty.handler.codec.http2.StreamBufferingEncoder.Http2GoAwayException; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.ImmediateEventExecutor; -import io.netty.util.concurrent.Promise; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; -import org.mockito.verification.VerificationMode; - -import java.util.ArrayList; -import java.util.List; - -import static io.netty.buffer.Unpooled.EMPTY_BUFFER; -import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_MAX_FRAME_SIZE; -import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_PRIORITY_WEIGHT; -import static io.netty.handler.codec.http2.Http2CodecUtil.SMALLEST_MAX_CONCURRENT_STREAMS; -import static io.netty.handler.codec.http2.Http2Error.CANCEL; -import static io.netty.handler.codec.http2.Http2Stream.State.HALF_CLOSED_LOCAL; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyBoolean; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.anyLong; -import static org.mockito.Mockito.anyShort; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -/** - * Tests for {@link StreamBufferingEncoder}. - */ -@SuppressWarnings("unchecked") -public class StreamBufferingEncoderTest { - - private StreamBufferingEncoder encoder; - - private Http2Connection connection; - - @Mock - private Http2FrameWriter writer; - - @Mock - private ChannelHandlerContext ctx; - - @Mock - private Channel channel; - - @Mock - private Channel.Unsafe unsafe; - - @Mock - private ChannelConfig config; - - @Mock - private EventExecutor executor; - - /** - * Init fields and do mocking. - */ - @BeforeEach - public void setup() throws Exception { - MockitoAnnotations.initMocks(this); - - Http2FrameWriter.Configuration configuration = mock(Http2FrameWriter.Configuration.class); - Http2FrameSizePolicy frameSizePolicy = mock(Http2FrameSizePolicy.class); - when(writer.configuration()).thenReturn(configuration); - when(configuration.frameSizePolicy()).thenReturn(frameSizePolicy); - when(frameSizePolicy.maxFrameSize()).thenReturn(DEFAULT_MAX_FRAME_SIZE); - when(writer.writeData(any(ChannelHandlerContext.class), anyInt(), any(ByteBuf.class), anyInt(), anyBoolean())) - .thenAnswer(successAnswer()); - when(writer.writeRstStream(eq(ctx), anyInt(), anyLong())).thenAnswer( - successAnswer()); - when(writer.writeGoAway(any(ChannelHandlerContext.class), anyInt(), anyLong(), any(ByteBuf.class))) - .thenAnswer(successAnswer()); - when(writer.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class), - anyInt(), anyBoolean())).thenAnswer(noopAnswer()); - when(writer.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class), - anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean())) - .thenAnswer(noopAnswer()); - - connection = new DefaultHttp2Connection(false); - connection.remote().flowController(new DefaultHttp2RemoteFlowController(connection)); - connection.local().flowController(new DefaultHttp2LocalFlowController(connection).frameWriter(writer)); - - DefaultHttp2ConnectionEncoder defaultEncoder = - new DefaultHttp2ConnectionEncoder(connection, writer); - encoder = new StreamBufferingEncoder(defaultEncoder); - DefaultHttp2ConnectionDecoder decoder = - new DefaultHttp2ConnectionDecoder(connection, encoder, mock(Http2FrameReader.class)); - Http2ConnectionHandler handler = new Http2ConnectionHandlerBuilder() - .frameListener(mock(Http2FrameListener.class)) - .codec(decoder, encoder).build(); - - // Set LifeCycleManager on encoder and decoder - when(ctx.channel()).thenReturn(channel); - when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT); - when(channel.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT); - when(executor.inEventLoop()).thenReturn(true); - doAnswer((Answer>) invocation -> newPromise()).when(ctx).newPromise(); - doAnswer((Answer>) invocation -> ImmediateEventExecutor.INSTANCE.newSucceededFuture(null)) - .when(ctx).newSucceededFuture(); - - doAnswer((Answer>) invocation -> - ImmediateEventExecutor.INSTANCE.newFailedFuture(invocation.getArgument(0))) - .when(ctx).newFailedFuture(any(Throwable.class)); - - when(ctx.executor()).thenReturn(executor); - when(channel.isActive()).thenReturn(false); - when(channel.config()).thenReturn(config); - when(channel.isWritable()).thenReturn(true); - when(channel.bytesBeforeUnwritable()).thenReturn(Long.MAX_VALUE); - when(config.getWriteBufferHighWaterMark()).thenReturn(Integer.MAX_VALUE); - when(config.getMessageSizeEstimator()).thenReturn(DefaultMessageSizeEstimator.DEFAULT); - ChannelMetadata metadata = new ChannelMetadata(false, 16); - when(channel.metadata()).thenReturn(metadata); - when(channel.unsafe()).thenReturn(unsafe); - handler.handlerAdded(ctx); - } - - @AfterEach - public void teardown() { - // Close and release any buffered frames. - encoder.close(); - } - - @Test - public void multipleWritesToActiveStream() { - encoder.writeSettingsAck(ctx); - encoderWriteHeaders(3); - assertEquals(0, encoder.numBufferedStreams()); - ByteBuf data = data(); - final int expectedBytes = data.readableBytes() * 3; - encoder.writeData(ctx, 3, data, 0, false); - encoder.writeData(ctx, 3, data(), 0, false); - encoder.writeData(ctx, 3, data(), 0, false); - encoderWriteHeaders(3); - - writeVerifyWriteHeaders(times(1), 3); - // Contiguous data writes are coalesced - ArgumentCaptor bufCaptor = ArgumentCaptor.forClass(ByteBuf.class); - verify(writer, times(1)).writeData(any(ChannelHandlerContext.class), eq(3), - bufCaptor.capture(), eq(0), eq(false)); - assertEquals(expectedBytes, bufCaptor.getValue().readableBytes()); - } - - @Test - public void ensureCanCreateNextStreamWhenStreamCloses() { - encoder.writeSettingsAck(ctx); - setMaxConcurrentStreams(1); - - encoderWriteHeaders(3); - assertEquals(0, encoder.numBufferedStreams()); - - // This one gets buffered. - encoderWriteHeaders(5); - assertEquals(1, connection.numActiveStreams()); - assertEquals(1, encoder.numBufferedStreams()); - - // Now prevent us from creating another stream. - setMaxConcurrentStreams(0); - - // Close the previous stream. - connection.stream(3).close(); - - // Ensure that no streams are currently active and that only the HEADERS from the first - // stream were written. - writeVerifyWriteHeaders(times(1), 3); - writeVerifyWriteHeaders(never(), 5); - assertEquals(0, connection.numActiveStreams()); - assertEquals(1, encoder.numBufferedStreams()); - } - - @Test - public void alternatingWritesToActiveAndBufferedStreams() { - encoder.writeSettingsAck(ctx); - setMaxConcurrentStreams(1); - - encoderWriteHeaders(3); - assertEquals(0, encoder.numBufferedStreams()); - - encoderWriteHeaders(5); - assertEquals(1, connection.numActiveStreams()); - assertEquals(1, encoder.numBufferedStreams()); - - encoder.writeData(ctx, 3, EMPTY_BUFFER, 0, false); - writeVerifyWriteHeaders(times(1), 3); - encoder.writeData(ctx, 5, EMPTY_BUFFER, 0, false); - verify(writer, never()) - .writeData(eq(ctx), eq(5), any(ByteBuf.class), eq(0), eq(false)); - } - - @Test - public void bufferingNewStreamFailsAfterGoAwayReceived() throws Http2Exception { - encoder.writeSettingsAck(ctx); - setMaxConcurrentStreams(0); - connection.goAwayReceived(1, 8, EMPTY_BUFFER); - - Future future = encoderWriteHeaders(3); - assertEquals(0, encoder.numBufferedStreams()); - assertTrue(future.isDone()); - assertFalse(future.isSuccess()); - } - - @Test - public void receivingGoAwayFailsBufferedStreams() throws Http2Exception { - encoder.writeSettingsAck(ctx); - setMaxConcurrentStreams(5); - - int streamId = 3; - List> futures = new ArrayList<>(); - for (int i = 0; i < 9; i++) { - futures.add(encoderWriteHeaders(streamId)); - streamId += 2; - } - assertEquals(5, connection.numActiveStreams()); - assertEquals(4, encoder.numBufferedStreams()); - - connection.goAwayReceived(11, 8, EMPTY_BUFFER); - - assertEquals(5, connection.numActiveStreams()); - assertEquals(0, encoder.numBufferedStreams()); - int failCount = 0; - for (Future f : futures) { - if (f.isFailed()) { - assertTrue(f.cause() instanceof Http2GoAwayException); - failCount++; - } - } - assertEquals(4, failCount); - } - - @Test - public void receivingGoAwayFailsNewStreamIfMaxConcurrentStreamsReached() throws Http2Exception { - encoder.writeSettingsAck(ctx); - setMaxConcurrentStreams(1); - encoderWriteHeaders(3); - connection.goAwayReceived(11, 8, EMPTY_BUFFER); - Future f = encoderWriteHeaders(5); - - assertTrue(f.awaitUninterruptibly().cause() instanceof Http2GoAwayException); - assertEquals(0, encoder.numBufferedStreams()); - } - - @Test - public void sendingGoAwayShouldNotFailStreams() { - encoder.writeSettingsAck(ctx); - setMaxConcurrentStreams(1); - - when(writer.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class), anyInt(), - anyBoolean())).thenAnswer(successAnswer()); - when(writer.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class), anyInt(), - anyShort(), anyBoolean(), anyInt(), anyBoolean())).thenAnswer(successAnswer()); - - Future f1 = encoderWriteHeaders(3); - assertEquals(0, encoder.numBufferedStreams()); - Future f2 = encoderWriteHeaders(5); - assertEquals(1, encoder.numBufferedStreams()); - Future f3 = encoderWriteHeaders(7); - assertEquals(2, encoder.numBufferedStreams()); - - ByteBuf empty = Unpooled.buffer(0); - encoder.writeGoAway(ctx, 3, CANCEL.code(), empty); - - assertEquals(1, connection.numActiveStreams()); - assertEquals(2, encoder.numBufferedStreams()); - // As the first stream did exists the first future should be done. - assertTrue(f1.isDone()); - assertFalse(f2.isDone()); - assertFalse(f3.isDone()); - } - - @Test - public void endStreamDoesNotFailBufferedStream() { - encoder.writeSettingsAck(ctx); - setMaxConcurrentStreams(0); - - encoderWriteHeaders(3); - assertEquals(1, encoder.numBufferedStreams()); - - encoder.writeData(ctx, 3, EMPTY_BUFFER, 0, true); - - assertEquals(0, connection.numActiveStreams()); - assertEquals(1, encoder.numBufferedStreams()); - - // Simulate that we received a SETTINGS frame which - // increased MAX_CONCURRENT_STREAMS to 1. - setMaxConcurrentStreams(1); - encoder.writeSettingsAck(ctx); - - assertEquals(1, connection.numActiveStreams()); - assertEquals(0, encoder.numBufferedStreams()); - assertEquals(HALF_CLOSED_LOCAL, connection.stream(3).state()); - } - - @Test - public void rstStreamClosesBufferedStream() { - encoder.writeSettingsAck(ctx); - setMaxConcurrentStreams(0); - - encoderWriteHeaders(3); - assertEquals(1, encoder.numBufferedStreams()); - - Future rstStreamFuture = encoder.writeRstStream(ctx, 3, CANCEL.code()); - assertTrue(rstStreamFuture.isSuccess()); - assertEquals(0, encoder.numBufferedStreams()); - } - - @Test - public void bufferUntilActiveStreamsAreReset() throws Exception { - encoder.writeSettingsAck(ctx); - setMaxConcurrentStreams(1); - - encoderWriteHeaders(3); - assertEquals(0, encoder.numBufferedStreams()); - encoderWriteHeaders(5); - assertEquals(1, encoder.numBufferedStreams()); - encoderWriteHeaders(7); - assertEquals(2, encoder.numBufferedStreams()); - - writeVerifyWriteHeaders(times(1), 3); - writeVerifyWriteHeaders(never(), 5); - writeVerifyWriteHeaders(never(), 7); - - encoder.writeRstStream(ctx, 3, CANCEL.code()); - connection.remote().flowController().writePendingBytes(); - writeVerifyWriteHeaders(times(1), 5); - writeVerifyWriteHeaders(never(), 7); - assertEquals(1, connection.numActiveStreams()); - assertEquals(1, encoder.numBufferedStreams()); - - encoder.writeRstStream(ctx, 5, CANCEL.code()); - connection.remote().flowController().writePendingBytes(); - writeVerifyWriteHeaders(times(1), 7); - assertEquals(1, connection.numActiveStreams()); - assertEquals(0, encoder.numBufferedStreams()); - - encoder.writeRstStream(ctx, 7, CANCEL.code()); - assertEquals(0, connection.numActiveStreams()); - assertEquals(0, encoder.numBufferedStreams()); - } - - @Test - public void bufferUntilMaxStreamsIncreased() { - encoder.writeSettingsAck(ctx); - setMaxConcurrentStreams(2); - - encoderWriteHeaders(3); - encoderWriteHeaders(5); - encoderWriteHeaders(7); - encoderWriteHeaders(9); - assertEquals(2, encoder.numBufferedStreams()); - - writeVerifyWriteHeaders(times(1), 3); - writeVerifyWriteHeaders(times(1), 5); - writeVerifyWriteHeaders(never(), 7); - writeVerifyWriteHeaders(never(), 9); - - // Simulate that we received a SETTINGS frame which - // increased MAX_CONCURRENT_STREAMS to 5. - setMaxConcurrentStreams(5); - encoder.writeSettingsAck(ctx); - - assertEquals(0, encoder.numBufferedStreams()); - writeVerifyWriteHeaders(times(1), 7); - writeVerifyWriteHeaders(times(1), 9); - - encoderWriteHeaders(11); - - writeVerifyWriteHeaders(times(1), 11); - - assertEquals(5, connection.local().numActiveStreams()); - } - - @Test - public void bufferUntilSettingsReceived() throws Http2Exception { - int initialLimit = SMALLEST_MAX_CONCURRENT_STREAMS; - int numStreams = initialLimit * 2; - for (int ix = 0, nextStreamId = 3; ix < numStreams; ++ix, nextStreamId += 2) { - encoderWriteHeaders(nextStreamId); - if (ix < initialLimit) { - writeVerifyWriteHeaders(times(1), nextStreamId); - } else { - writeVerifyWriteHeaders(never(), nextStreamId); - } - } - assertEquals(numStreams / 2, encoder.numBufferedStreams()); - - // Simulate that we received a SETTINGS frame. - setMaxConcurrentStreams(initialLimit * 2); - - assertEquals(0, encoder.numBufferedStreams()); - assertEquals(numStreams, connection.local().numActiveStreams()); - } - - @Test - public void bufferUntilSettingsReceivedWithNoMaxConcurrentStreamValue() throws Http2Exception { - int initialLimit = SMALLEST_MAX_CONCURRENT_STREAMS; - int numStreams = initialLimit * 2; - for (int ix = 0, nextStreamId = 3; ix < numStreams; ++ix, nextStreamId += 2) { - encoderWriteHeaders(nextStreamId); - if (ix < initialLimit) { - writeVerifyWriteHeaders(times(1), nextStreamId); - } else { - writeVerifyWriteHeaders(never(), nextStreamId); - } - } - assertEquals(numStreams / 2, encoder.numBufferedStreams()); - - // Simulate that we received an empty SETTINGS frame. - encoder.remoteSettings(new Http2Settings()); - - assertEquals(0, encoder.numBufferedStreams()); - assertEquals(numStreams, connection.local().numActiveStreams()); - } - - @Test - public void exhaustedStreamsDoNotBuffer() throws Http2Exception { - // Write the highest possible stream ID for the client. - // This will cause the next stream ID to be negative. - encoderWriteHeaders(Integer.MAX_VALUE); - - // Disallow any further streams. - setMaxConcurrentStreams(0); - - // Simulate numeric overflow for the next stream ID. - Future f = encoderWriteHeaders(-1); - - // Verify that the write fails. - assertNotNull(f.awaitUninterruptibly().cause()); - } - - @Test - public void closedBufferedStreamReleasesByteBuf() { - encoder.writeSettingsAck(ctx); - setMaxConcurrentStreams(0); - ByteBuf data = mock(ByteBuf.class); - Future f1 = encoderWriteHeaders(3); - assertEquals(1, encoder.numBufferedStreams()); - Future f2 = encoder.writeData(ctx, 3, data, 0, false); - - Future rstFuture = encoder.writeRstStream(ctx, 3, CANCEL.code()); - - assertEquals(0, encoder.numBufferedStreams()); - assertTrue(rstFuture.isSuccess()); - assertTrue(f1.isSuccess()); - assertTrue(f2.isSuccess()); - verify(data).release(); - } - - @Test - public void closeShouldCancelAllBufferedStreams() throws Http2Exception { - encoder.writeSettingsAck(ctx); - connection.local().maxActiveStreams(0); - - Future f1 = encoderWriteHeaders(3); - Future f2 = encoderWriteHeaders(5); - Future f3 = encoderWriteHeaders(7); - - encoder.close(); - assertNotNull(f1.awaitUninterruptibly().cause()); - assertNotNull(f2.awaitUninterruptibly().cause()); - assertNotNull(f3.awaitUninterruptibly().cause()); - } - - @Test - public void headersAfterCloseShouldImmediatelyFail() { - encoder.writeSettingsAck(ctx); - encoder.close(); - - Future f = encoderWriteHeaders(3); - assertNotNull(f.cause()); - } - - private void setMaxConcurrentStreams(int newValue) { - try { - encoder.remoteSettings(new Http2Settings().maxConcurrentStreams(newValue)); - // Flush the remote flow controller to write data - encoder.flowController().writePendingBytes(); - } catch (Http2Exception e) { - throw new RuntimeException(e); - } - } - - private Future encoderWriteHeaders(int streamId) { - Future future = - encoder.writeHeaders(ctx, streamId, new DefaultHttp2Headers(), 0, DEFAULT_PRIORITY_WEIGHT, - false, 0, false); - try { - encoder.flowController().writePendingBytes(); - return future; - } catch (Http2Exception e) { - throw new RuntimeException(e); - } - } - - private void writeVerifyWriteHeaders(VerificationMode mode, int streamId) { - verify(writer, mode).writeHeaders(eq(ctx), eq(streamId), any(Http2Headers.class), eq(0), - eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), - eq(false)); - } - - private static Answer> successAnswer() { - return invocation -> { - for (Object a : invocation.getArguments()) { - ReferenceCountUtil.safeRelease(a); - } - - return ImmediateEventExecutor.INSTANCE.newSucceededFuture(null); - }; - } - - private static Answer> noopAnswer() { - return new Answer>() { - @Override - public Future answer(InvocationOnMock invocation) throws Throwable { - for (Object a : invocation.getArguments()) { - if (a instanceof Promise) { - return ((Promise) a).asFuture(); - } - } - return newPromise().asFuture(); - } - }; - } - - private static Promise newPromise() { - return ImmediateEventExecutor.INSTANCE.newPromise(); - } - - private static ByteBuf data() { - ByteBuf buf = Unpooled.buffer(10); - for (int i = 0; i < buf.writableBytes(); i++) { - buf.writeByte(i); - } - return buf; - } -} 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 deleted file mode 100644 index c85d98912c..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/TestChannelInitializer.java +++ /dev/null @@ -1,122 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.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. - */ -@Sharable -public class TestChannelInitializer extends ChannelInitializer { - ChannelHandler handler; - AtomicInteger maxReads; - - @Override - public void initChannel(Channel channel) { - if (handler != null) { - 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. - */ - static final class TestNumReadsRecvByteBufAllocator implements RecvByteBufAllocator { - private final AtomicInteger numReads; - private 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. - } - }; - } - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/TestHeaderListener.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/TestHeaderListener.java deleted file mode 100644 index 1240561ac0..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/TestHeaderListener.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/* - * Copyright 2014 Twitter, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.netty.handler.codec.http2; - -import java.util.List; - -final class TestHeaderListener extends DefaultHttp2Headers { - - private final List headers; - - TestHeaderListener(List headers) { - this.headers = headers; - } - - @Override - public TestHeaderListener add(CharSequence name, CharSequence value) { - headers.add(new HpackHeaderField(name, value)); - return this; - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/UniformStreamByteDistributorFlowControllerTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/UniformStreamByteDistributorFlowControllerTest.java deleted file mode 100644 index d7b039da9e..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/UniformStreamByteDistributorFlowControllerTest.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -public class UniformStreamByteDistributorFlowControllerTest extends DefaultHttp2RemoteFlowControllerTest { - @Override - protected StreamByteDistributor newDistributor(Http2Connection connection) { - return new UniformStreamByteDistributor(connection); - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/UniformStreamByteDistributorTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/UniformStreamByteDistributorTest.java deleted file mode 100644 index f7b4c73d71..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/UniformStreamByteDistributorTest.java +++ /dev/null @@ -1,280 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.handler.codec.http2.Http2TestUtil.TestStreamByteDistributorStreamState; -import io.netty.util.collection.IntObjectHashMap; -import io.netty.util.collection.IntObjectMap; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; -import org.mockito.verification.VerificationMode; - -import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_MIN_ALLOCATION_CHUNK; -import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_PRIORITY_WEIGHT; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.atMost; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.same; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; - -/** - * Tests for {@link UniformStreamByteDistributor}. - */ -public class UniformStreamByteDistributorTest { - private static final int CHUNK_SIZE = DEFAULT_MIN_ALLOCATION_CHUNK; - - private static final int STREAM_A = 1; - private static final int STREAM_B = 3; - private static final int STREAM_C = 5; - private static final int STREAM_D = 7; - - private Http2Connection connection; - private UniformStreamByteDistributor distributor; - private IntObjectMap stateMap; - - @Mock - private StreamByteDistributor.Writer writer; - - @BeforeEach - public void setup() throws Http2Exception { - MockitoAnnotations.initMocks(this); - - stateMap = new IntObjectHashMap(); - connection = new DefaultHttp2Connection(false); - distributor = new UniformStreamByteDistributor(connection); - - // Assume we always write all the allocated bytes. - resetWriter(); - - connection.local().createStream(STREAM_A, false); - connection.local().createStream(STREAM_B, false); - Http2Stream streamC = connection.local().createStream(STREAM_C, false); - Http2Stream streamD = connection.local().createStream(STREAM_D, false); - setPriority(streamC.id(), STREAM_A, DEFAULT_PRIORITY_WEIGHT, false); - setPriority(streamD.id(), STREAM_A, DEFAULT_PRIORITY_WEIGHT, false); - } - - private Answer writeAnswer() { - return in -> { - Http2Stream stream = in.getArgument(0); - int numBytes = in.getArgument(1); - TestStreamByteDistributorStreamState state = stateMap.get(stream.id()); - state.pendingBytes -= numBytes; - state.hasFrame = state.pendingBytes > 0; - distributor.updateStreamableBytes(state); - return null; - }; - } - - private void resetWriter() { - reset(writer); - doAnswer(writeAnswer()).when(writer).write(any(Http2Stream.class), anyInt()); - } - - @Test - public void bytesUnassignedAfterProcessing() throws Http2Exception { - initState(STREAM_A, 1, true); - initState(STREAM_B, 2, true); - initState(STREAM_C, 3, true); - initState(STREAM_D, 4, true); - - assertFalse(write(10)); - verifyWrite(STREAM_A, 1); - verifyWrite(STREAM_B, 2); - verifyWrite(STREAM_C, 3); - verifyWrite(STREAM_D, 4); - verifyNoMoreInteractions(writer); - - assertFalse(write(10)); - verifyNoMoreInteractions(writer); - } - - @Test - public void connectionErrorForWriterException() throws Http2Exception { - initState(STREAM_A, 1, true); - initState(STREAM_B, 2, true); - initState(STREAM_C, 3, true); - initState(STREAM_D, 4, true); - - Exception fakeException = new RuntimeException("Fake exception"); - doThrow(fakeException).when(writer).write(same(stream(STREAM_C)), eq(3)); - - Http2Exception e = assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - write(10); - } - }); - assertFalse(Http2Exception.isStreamError(e)); - assertEquals(Http2Error.INTERNAL_ERROR, e.error()); - assertSame(fakeException, e.getCause()); - - verifyWrite(atMost(1), STREAM_A, 1); - verifyWrite(atMost(1), STREAM_B, 2); - verifyWrite(STREAM_C, 3); - verifyWrite(atMost(1), STREAM_D, 4); - - doNothing().when(writer).write(same(stream(STREAM_C)), eq(3)); - write(10); - verifyWrite(STREAM_A, 1); - verifyWrite(STREAM_B, 2); - verifyWrite(STREAM_C, 3); - verifyWrite(STREAM_D, 4); - } - - /** - * In this test, we verify that each stream is allocated a minimum chunk size. When bytes - * run out, the remaining streams will be next in line for the next iteration. - */ - @Test - public void minChunkShouldBeAllocatedPerStream() throws Http2Exception { - // Re-assign weights. - setPriority(STREAM_A, 0, (short) 50, false); - setPriority(STREAM_B, 0, (short) 200, false); - setPriority(STREAM_C, STREAM_A, (short) 100, false); - setPriority(STREAM_D, STREAM_A, (short) 100, false); - - // Update the streams. - initState(STREAM_A, CHUNK_SIZE, true); - initState(STREAM_B, CHUNK_SIZE, true); - initState(STREAM_C, CHUNK_SIZE, true); - initState(STREAM_D, CHUNK_SIZE, true); - - // Only write 3 * chunkSize, so that we'll only write to the first 3 streams. - int written = 3 * CHUNK_SIZE; - assertTrue(write(written)); - assertEquals(CHUNK_SIZE, captureWrite(STREAM_A)); - assertEquals(CHUNK_SIZE, captureWrite(STREAM_B)); - assertEquals(CHUNK_SIZE, captureWrite(STREAM_C)); - verifyNoMoreInteractions(writer); - - resetWriter(); - - // Now write again and verify that the last stream is written to. - assertFalse(write(CHUNK_SIZE)); - assertEquals(CHUNK_SIZE, captureWrite(STREAM_D)); - verifyNoMoreInteractions(writer); - } - - @Test - public void streamWithMoreDataShouldBeEnqueuedAfterWrite() throws Http2Exception { - // Give the stream a bunch of data. - initState(STREAM_A, 2 * CHUNK_SIZE, true); - - // Write only part of the data. - assertTrue(write(CHUNK_SIZE)); - assertEquals(CHUNK_SIZE, captureWrite(STREAM_A)); - verifyNoMoreInteractions(writer); - - resetWriter(); - - // Now write the rest of the data. - assertFalse(write(CHUNK_SIZE)); - assertEquals(CHUNK_SIZE, captureWrite(STREAM_A)); - verifyNoMoreInteractions(writer); - } - - @Test - public void emptyFrameAtHeadIsWritten() throws Http2Exception { - initState(STREAM_A, 10, true); - initState(STREAM_B, 0, true); - initState(STREAM_C, 0, true); - initState(STREAM_D, 10, true); - - assertTrue(write(10)); - verifyWrite(STREAM_A, 10); - verifyWrite(STREAM_B, 0); - verifyWrite(STREAM_C, 0); - verifyNoMoreInteractions(writer); - } - - @Test - public void streamWindowExhaustedDoesNotWrite() throws Http2Exception { - initState(STREAM_A, 0, true, false); - initState(STREAM_B, 0, true); - initState(STREAM_C, 0, true); - initState(STREAM_D, 0, true, false); - - assertFalse(write(10)); - verifyWrite(STREAM_B, 0); - verifyWrite(STREAM_C, 0); - verifyNoMoreInteractions(writer); - } - - @Test - public void streamWindowLargerThanIntDoesNotInfiniteLoop() throws Http2Exception { - initState(STREAM_A, Integer.MAX_VALUE + 1L, true, true); - assertTrue(write(Integer.MAX_VALUE)); - verifyWrite(STREAM_A, Integer.MAX_VALUE); - assertFalse(write(1)); - verifyWrite(STREAM_A, 1); - } - - private Http2Stream stream(int streamId) { - return connection.stream(streamId); - } - - private void initState(final int streamId, final long streamableBytes, final boolean hasFrame) { - initState(streamId, streamableBytes, hasFrame, hasFrame); - } - - private void initState(final int streamId, final long pendingBytes, final boolean hasFrame, - final boolean isWriteAllowed) { - final Http2Stream stream = stream(streamId); - TestStreamByteDistributorStreamState state = new TestStreamByteDistributorStreamState(stream, pendingBytes, - hasFrame, isWriteAllowed); - stateMap.put(streamId, state); - distributor.updateStreamableBytes(state); - } - - private void setPriority(int streamId, int parent, int weight, boolean exclusive) { - distributor.updateDependencyTree(streamId, parent, (short) weight, exclusive); - } - - private boolean write(int numBytes) throws Http2Exception { - return distributor.distribute(numBytes, writer); - } - - private void verifyWrite(int streamId, int numBytes) { - verify(writer).write(same(stream(streamId)), eq(numBytes)); - } - - private void verifyWrite(VerificationMode mode, int streamId, int numBytes) { - verify(writer, mode).write(same(stream(streamId)), eq(numBytes)); - } - - private int captureWrite(int streamId) { - ArgumentCaptor captor = ArgumentCaptor.forClass(Integer.class); - verify(writer).write(same(stream(streamId)), captor.capture()); - return captor.getValue(); - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/WeightedFairQueueByteDistributorDependencyTreeTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/WeightedFairQueueByteDistributorDependencyTreeTest.java deleted file mode 100644 index 8786a03d14..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/WeightedFairQueueByteDistributorDependencyTreeTest.java +++ /dev/null @@ -1,977 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.MockitoAnnotations; - -import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_PRIORITY_WEIGHT; -import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_WEIGHT; -import static io.netty.handler.codec.http2.Http2CodecUtil.MIN_WEIGHT; -import static io.netty.handler.codec.http2.WeightedFairQueueByteDistributor.INITIAL_CHILDREN_MAP_SIZE; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.doAnswer; - -public class WeightedFairQueueByteDistributorDependencyTreeTest extends - AbstractWeightedFairQueueByteDistributorDependencyTest { - private static final int leadersId = 3; // js, css - private static final int unblockedId = 5; - private static final int backgroundId = 7; - private static final int speculativeId = 9; - private static final int followersId = 11; // images - private static final short leadersWeight = 201; - private static final short unblockedWeight = 101; - private static final short backgroundWeight = 1; - private static final short speculativeWeight = 1; - private static final short followersWeight = 1; - - @BeforeEach - public void setup() throws Http2Exception { - MockitoAnnotations.initMocks(this); - - setup(0); - } - - private void setup(int maxStateOnlySize) { - connection = new DefaultHttp2Connection(false); - distributor = new WeightedFairQueueByteDistributor(connection, maxStateOnlySize); - - // Assume we always write all the allocated bytes. - doAnswer(writeAnswer(false)).when(writer).write(any(Http2Stream.class), anyInt()); - } - - @Test - public void closingStreamWithChildrenDoesNotCauseConcurrentModification() throws Http2Exception { - // We create enough streams to wrap around the child array. We carefully craft the stream ids so that they hash - // codes overlap with respect to the child collection. If the implementation is not careful this may lead to a - // concurrent modification exception while promoting all children to the connection stream. - final Http2Stream streamA = connection.local().createStream(1, false); - final int numStreams = INITIAL_CHILDREN_MAP_SIZE - 1; - for (int i = 0, streamId = 3; i < numStreams; ++i, streamId += INITIAL_CHILDREN_MAP_SIZE) { - final Http2Stream stream = connection.local().createStream(streamId, false); - setPriority(stream.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); - } - assertEquals(INITIAL_CHILDREN_MAP_SIZE, connection.numActiveStreams()); - streamA.close(); - assertEquals(numStreams, connection.numActiveStreams()); - } - - @Test - public void closeWhileIteratingDoesNotNPE() throws Http2Exception { - final Http2Stream streamA = connection.local().createStream(3, false); - final Http2Stream streamB = connection.local().createStream(5, false); - final Http2Stream streamC = connection.local().createStream(7, false); - setPriority(streamB.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); - connection.forEachActiveStream(stream -> { - streamA.close(); - setPriority(streamB.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT, false); - return true; - }); - } - - @Test - public void localStreamCanDependUponIdleStream() throws Http2Exception { - setup(1); - - Http2Stream streamA = connection.local().createStream(1, false); - setPriority(3, streamA.id(), MIN_WEIGHT, true); - assertTrue(distributor.isChild(3, streamA.id(), MIN_WEIGHT)); - } - - @Test - public void remoteStreamCanDependUponIdleStream() throws Http2Exception { - setup(1); - - Http2Stream streamA = connection.remote().createStream(2, false); - setPriority(4, streamA.id(), MIN_WEIGHT, true); - assertTrue(distributor.isChild(4, streamA.id(), MIN_WEIGHT)); - } - - @Test - public void prioritizeShouldUseDefaults() throws Exception { - Http2Stream stream = connection.local().createStream(1, false); - assertTrue(distributor.isChild(stream.id(), connection.connectionStream().id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(1, distributor.numChildren(connection.connectionStream().id())); - assertEquals(0, distributor.numChildren(stream.id())); - } - - @Test - public void reprioritizeWithNoChangeShouldDoNothing() throws Exception { - Http2Stream stream = connection.local().createStream(1, false); - setPriority(stream.id(), connection.connectionStream().id(), DEFAULT_PRIORITY_WEIGHT, false); - assertTrue(distributor.isChild(stream.id(), connection.connectionStream().id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(1, distributor.numChildren(connection.connectionStream().id())); - assertEquals(0, distributor.numChildren(stream.id())); - } - - @Test - public void stateOnlyPriorityShouldBePreservedWhenStreamsAreCreatedAndClosed() throws Http2Exception { - setup(3); - - short weight3 = MIN_WEIGHT + 1; - short weight5 = (short) (weight3 + 1); - short weight7 = (short) (weight5 + 1); - setPriority(3, connection.connectionStream().id(), weight3, true); - setPriority(5, connection.connectionStream().id(), weight5, true); - setPriority(7, connection.connectionStream().id(), weight7, true); - - assertEquals(0, connection.numActiveStreams()); - verifyStateOnlyPriorityShouldBePreservedWhenStreamsAreCreated(weight3, weight5, weight7); - - // Now create stream objects and ensure the state and dependency tree is preserved. - Http2Stream streamA = connection.local().createStream(3, false); - Http2Stream streamB = connection.local().createStream(5, false); - Http2Stream streamC = connection.local().createStream(7, false); - - assertEquals(3, connection.numActiveStreams()); - verifyStateOnlyPriorityShouldBePreservedWhenStreamsAreCreated(weight3, weight5, weight7); - - // Close all the streams and ensure the state and dependency tree is preserved. - streamA.close(); - streamB.close(); - streamC.close(); - - assertEquals(0, connection.numActiveStreams()); - verifyStateOnlyPriorityShouldBePreservedWhenStreamsAreCreated(weight3, weight5, weight7); - } - - private void verifyStateOnlyPriorityShouldBePreservedWhenStreamsAreCreated(short weight3, short weight5, - short weight7) { - // Level 0 - assertEquals(1, distributor.numChildren(connection.connectionStream().id())); - - // Level 1 - assertTrue(distributor.isChild(7, connection.connectionStream().id(), weight7)); - assertEquals(1, distributor.numChildren(7)); - - // Level 2 - assertTrue(distributor.isChild(5, 7, weight5)); - assertEquals(1, distributor.numChildren(5)); - - // Level 3 - assertTrue(distributor.isChild(3, 5, weight3)); - assertEquals(0, distributor.numChildren(3)); - } - - @Test - public void fireFoxQoSStreamsRemainAfterDataStreamsAreClosed() throws Http2Exception { - // https://bitsup.blogspot.com/2015/01/http2-dependency-priorities-in-firefox.html - setup(5); - - setPriority(leadersId, connection.connectionStream().id(), leadersWeight, false); - setPriority(unblockedId, connection.connectionStream().id(), unblockedWeight, false); - setPriority(backgroundId, connection.connectionStream().id(), backgroundWeight, false); - setPriority(speculativeId, backgroundId, speculativeWeight, false); - setPriority(followersId, leadersId, followersWeight, false); - - verifyFireFoxQoSStreams(); - - // Simulate a HTML request - short htmlGetStreamWeight = 2; - Http2Stream htmlGetStream = connection.local().createStream(13, false); - setPriority(htmlGetStream.id(), followersId, htmlGetStreamWeight, false); - Http2Stream favIconStream = connection.local().createStream(15, false); - setPriority(favIconStream.id(), connection.connectionStream().id(), DEFAULT_PRIORITY_WEIGHT, false); - Http2Stream cssStream = connection.local().createStream(17, false); - setPriority(cssStream.id(), leadersId, DEFAULT_PRIORITY_WEIGHT, false); - Http2Stream jsStream = connection.local().createStream(19, false); - setPriority(jsStream.id(), leadersId, DEFAULT_PRIORITY_WEIGHT, false); - Http2Stream imageStream = connection.local().createStream(21, false); - setPriority(imageStream.id(), followersId, 1, false); - - // Level 0 - assertEquals(4, distributor.numChildren(connection.connectionStream().id())); - - // Level 1 - assertTrue(distributor.isChild(leadersId, connection.connectionStream().id(), leadersWeight)); - assertEquals(3, distributor.numChildren(leadersId)); - - assertTrue(distributor.isChild(unblockedId, connection.connectionStream().id(), unblockedWeight)); - assertEquals(0, distributor.numChildren(unblockedId)); - - assertTrue(distributor.isChild(backgroundId, connection.connectionStream().id(), backgroundWeight)); - assertEquals(1, distributor.numChildren(backgroundId)); - - assertTrue(distributor.isChild(favIconStream.id(), connection.connectionStream().id(), - DEFAULT_PRIORITY_WEIGHT)); - assertEquals(0, distributor.numChildren(favIconStream.id())); - - // Level 2 - assertTrue(distributor.isChild(followersId, leadersId, followersWeight)); - assertEquals(2, distributor.numChildren(followersId)); - - assertTrue(distributor.isChild(speculativeId, backgroundId, speculativeWeight)); - assertEquals(0, distributor.numChildren(speculativeId)); - - assertTrue(distributor.isChild(cssStream.id(), leadersId, DEFAULT_PRIORITY_WEIGHT)); - assertEquals(0, distributor.numChildren(cssStream.id())); - - assertTrue(distributor.isChild(jsStream.id(), leadersId, DEFAULT_PRIORITY_WEIGHT)); - assertEquals(0, distributor.numChildren(jsStream.id())); - - // Level 3 - assertTrue(distributor.isChild(htmlGetStream.id(), followersId, htmlGetStreamWeight)); - assertEquals(0, distributor.numChildren(htmlGetStream.id())); - - assertTrue(distributor.isChild(imageStream.id(), followersId, followersWeight)); - assertEquals(0, distributor.numChildren(imageStream.id())); - - // Close all the data streams and ensure the "priority only streams" are retained in the dependency tree. - htmlGetStream.close(); - favIconStream.close(); - cssStream.close(); - jsStream.close(); - imageStream.close(); - - verifyFireFoxQoSStreams(); - } - - private void verifyFireFoxQoSStreams() { - // Level 0 - assertEquals(3, distributor.numChildren(connection.connectionStream().id())); - - // Level 1 - assertTrue(distributor.isChild(leadersId, connection.connectionStream().id(), leadersWeight)); - assertEquals(1, distributor.numChildren(leadersId)); - - assertTrue(distributor.isChild(unblockedId, connection.connectionStream().id(), unblockedWeight)); - assertEquals(0, distributor.numChildren(unblockedId)); - - assertTrue(distributor.isChild(backgroundId, connection.connectionStream().id(), backgroundWeight)); - assertEquals(1, distributor.numChildren(backgroundId)); - - // Level 2 - assertTrue(distributor.isChild(followersId, leadersId, followersWeight)); - assertEquals(0, distributor.numChildren(followersId)); - - assertTrue(distributor.isChild(speculativeId, backgroundId, speculativeWeight)); - assertEquals(0, distributor.numChildren(speculativeId)); - } - - @Test - public void lowestPrecedenceStateShouldBeDropped() throws Http2Exception { - setup(3); - - short weight3 = MAX_WEIGHT; - short weight5 = (short) (weight3 - 1); - short weight7 = (short) (weight5 - 1); - short weight9 = (short) (weight7 - 1); - setPriority(3, connection.connectionStream().id(), weight3, true); - setPriority(5, connection.connectionStream().id(), weight5, true); - setPriority(7, connection.connectionStream().id(), weight7, false); - assertEquals(0, connection.numActiveStreams()); - verifyLowestPrecedenceStateShouldBeDropped1(weight3, weight5, weight7); - - // Attempt to create a new item in the dependency tree but the maximum amount of "state only" streams is meet - // so a stream will have to be dropped. Currently the new stream is the lowest "precedence" so it is dropped. - setPriority(9, 3, weight9, false); - assertEquals(0, connection.numActiveStreams()); - verifyLowestPrecedenceStateShouldBeDropped1(weight3, weight5, weight7); - - // Set the priority for stream 9 such that its depth in the dependency tree is numerically lower than stream 3, - // and therefore the dependency state associated with stream 3 will be dropped. - setPriority(9, 5, weight9, true); - verifyLowestPrecedenceStateShouldBeDropped2(weight9, weight5, weight7); - - // Test that stream which has been activated is lower priority than other streams that have not been activated. - Http2Stream streamA = connection.local().createStream(5, false); - streamA.close(); - verifyLowestPrecedenceStateShouldBeDropped2(weight9, weight5, weight7); - - // Stream 3 (hasn't been opened) should result in stream 5 being dropped. - // dropping stream 5 will distribute its weight to children (only 9) - setPriority(3, 9, weight3, false); - verifyLowestPrecedenceStateShouldBeDropped3(weight3, weight7, weight5); - - // Stream 5's state has been discarded so we should be able to re-insert this state. - setPriority(5, 0, weight5, false); - verifyLowestPrecedenceStateShouldBeDropped4(weight5, weight7, weight5); - - // All streams are at the same level, so stream ID should be used to drop the numeric lowest valued stream. - short weight11 = (short) (weight9 - 1); - setPriority(11, 0, weight11, false); - verifyLowestPrecedenceStateShouldBeDropped5(weight7, weight5, weight11); - } - - private void verifyLowestPrecedenceStateShouldBeDropped1(short weight3, short weight5, short weight7) { - // Level 0 - assertEquals(2, distributor.numChildren(connection.connectionStream().id())); - - // Level 1 - assertTrue(distributor.isChild(7, connection.connectionStream().id(), weight7)); - assertEquals(0, distributor.numChildren(7)); - - assertTrue(distributor.isChild(5, connection.connectionStream().id(), weight5)); - assertEquals(1, distributor.numChildren(5)); - - // Level 2 - assertTrue(distributor.isChild(3, 5, weight3)); - assertEquals(0, distributor.numChildren(3)); - } - - private void verifyLowestPrecedenceStateShouldBeDropped2(short weight9, short weight5, short weight7) { - // Level 0 - assertEquals(2, distributor.numChildren(connection.connectionStream().id())); - - // Level 1 - assertTrue(distributor.isChild(7, connection.connectionStream().id(), weight7)); - assertEquals(0, distributor.numChildren(7)); - - assertTrue(distributor.isChild(5, connection.connectionStream().id(), weight5)); - assertEquals(1, distributor.numChildren(5)); - - // Level 2 - assertTrue(distributor.isChild(9, 5, weight9)); - assertEquals(0, distributor.numChildren(9)); - } - - private void verifyLowestPrecedenceStateShouldBeDropped3(short weight3, short weight7, short weight9) { - // Level 0 - assertEquals(2, distributor.numChildren(connection.connectionStream().id())); - - // Level 1 - assertTrue(distributor.isChild(7, connection.connectionStream().id(), weight7)); - assertEquals(0, distributor.numChildren(7)); - - assertTrue(distributor.isChild(9, connection.connectionStream().id(), weight9)); - assertEquals(1, distributor.numChildren(9)); - - // Level 2 - assertTrue(distributor.isChild(3, 9, weight3)); - assertEquals(0, distributor.numChildren(3)); - } - - private void verifyLowestPrecedenceStateShouldBeDropped4(short weight5, short weight7, short weight9) { - // Level 0 - assertEquals(3, distributor.numChildren(connection.connectionStream().id())); - - // Level 1 - assertTrue(distributor.isChild(5, connection.connectionStream().id(), weight5)); - assertEquals(0, distributor.numChildren(5)); - - assertTrue(distributor.isChild(7, connection.connectionStream().id(), weight7)); - assertEquals(0, distributor.numChildren(7)); - - assertTrue(distributor.isChild(9, connection.connectionStream().id(), weight9)); - assertEquals(0, distributor.numChildren(9)); - } - - private void verifyLowestPrecedenceStateShouldBeDropped5(short weight7, short weight9, short weight11) { - // Level 0 - assertEquals(3, distributor.numChildren(connection.connectionStream().id())); - - // Level 1 - assertTrue(distributor.isChild(11, connection.connectionStream().id(), weight11)); - assertEquals(0, distributor.numChildren(11)); - - assertTrue(distributor.isChild(7, connection.connectionStream().id(), weight7)); - assertEquals(0, distributor.numChildren(7)); - - assertTrue(distributor.isChild(9, connection.connectionStream().id(), weight9)); - assertEquals(0, distributor.numChildren(9)); - } - - @Test - public void priorityOnlyStreamsArePreservedWhenReservedStreamsAreClosed() throws Http2Exception { - setup(1); - - short weight3 = MIN_WEIGHT; - setPriority(3, connection.connectionStream().id(), weight3, true); - - Http2Stream streamA = connection.local().createStream(5, false); - Http2Stream streamB = connection.remote().reservePushStream(4, streamA); - - // Level 0 - assertEquals(3, distributor.numChildren(connection.connectionStream().id())); - - // Level 1 - assertTrue(distributor.isChild(3, connection.connectionStream().id(), weight3)); - assertEquals(0, distributor.numChildren(3)); - - assertTrue(distributor.isChild(streamA.id(), connection.connectionStream().id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(0, distributor.numChildren(streamA.id())); - - assertTrue(distributor.isChild(streamB.id(), connection.connectionStream().id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(0, distributor.numChildren(streamB.id())); - - // Close both streams. - streamB.close(); - streamA.close(); - - // Level 0 - assertEquals(1, distributor.numChildren(connection.connectionStream().id())); - - // Level 1 - assertTrue(distributor.isChild(3, connection.connectionStream().id(), weight3)); - assertEquals(0, distributor.numChildren(3)); - } - - @Test - public void insertExclusiveShouldAddNewLevel() throws Exception { - Http2Stream streamA = connection.local().createStream(1, false); - Http2Stream streamB = connection.local().createStream(3, false); - Http2Stream streamC = connection.local().createStream(5, false); - Http2Stream streamD = connection.local().createStream(7, false); - - setPriority(streamB.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); - setPriority(streamC.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); - setPriority(streamD.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, true); - - assertEquals(4, connection.numActiveStreams()); - - // Level 0 - assertEquals(1, distributor.numChildren(connection.connectionStream().id())); - - // Level 1 - assertTrue(distributor.isChild(streamA.id(), connection.connectionStream().id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(1, distributor.numChildren(streamA.id())); - - // Level 2 - assertTrue(distributor.isChild(streamD.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(2, distributor.numChildren(streamD.id())); - - // Level 3 - assertTrue(distributor.isChild(streamB.id(), streamD.id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(0, distributor.numChildren(streamB.id())); - - assertTrue(distributor.isChild(streamC.id(), streamD.id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(0, distributor.numChildren(streamC.id())); - } - - @Test - public void existingChildMadeExclusiveShouldNotCreateTreeCycle() throws Http2Exception { - Http2Stream streamA = connection.local().createStream(1, false); - Http2Stream streamB = connection.local().createStream(3, false); - Http2Stream streamC = connection.local().createStream(5, false); - Http2Stream streamD = connection.local().createStream(7, false); - - setPriority(streamB.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); - setPriority(streamC.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); - setPriority(streamD.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT, false); - - // Stream C is already dependent on Stream A, but now make that an exclusive dependency - setPriority(streamC.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, true); - - assertEquals(4, connection.numActiveStreams()); - - // Level 0 - assertEquals(1, distributor.numChildren(connection.connectionStream().id())); - - // Level 1 - assertTrue(distributor.isChild(streamA.id(), connection.connectionStream().id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(1, distributor.numChildren(streamA.id())); - - // Level 2 - assertTrue(distributor.isChild(streamC.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(2, distributor.numChildren(streamC.id())); - - // Level 3 - assertTrue(distributor.isChild(streamB.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(0, distributor.numChildren(streamB.id())); - - assertTrue(distributor.isChild(streamD.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(0, distributor.numChildren(streamD.id())); - } - - @Test - public void newExclusiveChildShouldUpdateOldParentCorrectly() throws Http2Exception { - Http2Stream streamA = connection.local().createStream(1, false); - Http2Stream streamB = connection.local().createStream(3, false); - Http2Stream streamC = connection.local().createStream(5, false); - Http2Stream streamD = connection.local().createStream(7, false); - Http2Stream streamE = connection.local().createStream(9, false); - Http2Stream streamF = connection.local().createStream(11, false); - - setPriority(streamB.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); - setPriority(streamC.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); - setPriority(streamD.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT, false); - setPriority(streamF.id(), streamE.id(), DEFAULT_PRIORITY_WEIGHT, false); - - // F is now going to be exclusively dependent on A, after this we should check that stream E - // prioritizableForTree is not over decremented. - setPriority(streamF.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, true); - - assertEquals(6, connection.numActiveStreams()); - - // Level 0 - assertEquals(2, distributor.numChildren(connection.connectionStream().id())); - - // Level 1 - assertTrue(distributor.isChild(streamE.id(), connection.connectionStream().id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(0, distributor.numChildren(streamE.id())); - - assertTrue(distributor.isChild(streamA.id(), connection.connectionStream().id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(1, distributor.numChildren(streamA.id())); - - // Level 2 - assertTrue(distributor.isChild(streamF.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(2, distributor.numChildren(streamF.id())); - - // Level 3 - assertTrue(distributor.isChild(streamB.id(), streamF.id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(0, distributor.numChildren(streamB.id())); - - assertTrue(distributor.isChild(streamC.id(), streamF.id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(1, distributor.numChildren(streamC.id())); - - // Level 4 - assertTrue(distributor.isChild(streamD.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(0, distributor.numChildren(streamD.id())); - } - - @Test - public void weightChangeWithNoTreeChangeShouldBeRespected() throws Http2Exception { - Http2Stream streamA = connection.local().createStream(1, false); - Http2Stream streamB = connection.local().createStream(3, false); - Http2Stream streamC = connection.local().createStream(5, false); - Http2Stream streamD = connection.local().createStream(7, false); - - setPriority(streamB.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); - setPriority(streamC.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); - setPriority(streamD.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, true); - - assertEquals(4, connection.numActiveStreams()); - - short newWeight = (short) (DEFAULT_PRIORITY_WEIGHT + 1); - setPriority(streamD.id(), streamA.id(), newWeight, false); - - // Level 0 - assertEquals(1, distributor.numChildren(connection.connectionStream().id())); - - // Level 1 - assertTrue(distributor.isChild(streamA.id(), connection.connectionStream().id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(1, distributor.numChildren(streamA.id())); - - // Level 2 - assertTrue(distributor.isChild(streamD.id(), streamA.id(), newWeight)); - assertEquals(2, distributor.numChildren(streamD.id())); - - // Level 3 - assertTrue(distributor.isChild(streamB.id(), streamD.id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(0, distributor.numChildren(streamB.id())); - - assertTrue(distributor.isChild(streamC.id(), streamD.id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(0, distributor.numChildren(streamC.id())); - } - - @Test - public void sameNodeDependentShouldNotStackOverflowNorChangePrioritizableForTree() throws Http2Exception { - Http2Stream streamA = connection.local().createStream(1, false); - Http2Stream streamB = connection.local().createStream(3, false); - Http2Stream streamC = connection.local().createStream(5, false); - Http2Stream streamD = connection.local().createStream(7, false); - - setPriority(streamB.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); - setPriority(streamC.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); - setPriority(streamD.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, true); - - boolean[] exclusives = { true, false }; - short[] weights = { DEFAULT_PRIORITY_WEIGHT, 100, 200, DEFAULT_PRIORITY_WEIGHT }; - - assertEquals(4, connection.numActiveStreams()); - - // The goal is to call setPriority with the same parent and vary the parameters - // we were at one point adding a circular depends to the tree and then throwing - // a StackOverflow due to infinite recursive operation. - for (short weight : weights) { - for (boolean exclusive : exclusives) { - setPriority(streamD.id(), streamA.id(), weight, exclusive); - - assertEquals(0, distributor.numChildren(streamB.id())); - assertEquals(0, distributor.numChildren(streamC.id())); - assertEquals(1, distributor.numChildren(streamA.id())); - assertEquals(2, distributor.numChildren(streamD.id())); - assertFalse(distributor.isChild(streamB.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT)); - assertFalse(distributor.isChild(streamC.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT)); - assertTrue(distributor.isChild(streamB.id(), streamD.id(), DEFAULT_PRIORITY_WEIGHT)); - assertTrue(distributor.isChild(streamC.id(), streamD.id(), DEFAULT_PRIORITY_WEIGHT)); - assertTrue(distributor.isChild(streamD.id(), streamA.id(), weight)); - } - } - } - - @Test - public void multipleCircularDependencyShouldUpdatePrioritizable() throws Http2Exception { - Http2Stream streamA = connection.local().createStream(1, false); - Http2Stream streamB = connection.local().createStream(3, false); - Http2Stream streamC = connection.local().createStream(5, false); - Http2Stream streamD = connection.local().createStream(7, false); - - setPriority(streamB.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); - setPriority(streamC.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); - setPriority(streamD.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, true); - - assertEquals(4, connection.numActiveStreams()); - - // Bring B to the root - setPriority(streamA.id(), streamB.id(), DEFAULT_PRIORITY_WEIGHT, true); - - // Move all streams to be children of B - setPriority(streamC.id(), streamB.id(), DEFAULT_PRIORITY_WEIGHT, false); - setPriority(streamD.id(), streamB.id(), DEFAULT_PRIORITY_WEIGHT, false); - - // Move A back to the root - setPriority(streamB.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, true); - - // Move all streams to be children of A - setPriority(streamC.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); - setPriority(streamD.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); - - // Level 0 - assertEquals(1, distributor.numChildren(connection.connectionStream().id())); - - // Level 1 - assertTrue(distributor.isChild(streamA.id(), connection.connectionStream().id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(3, distributor.numChildren(streamA.id())); - - // Level 2 - assertTrue(distributor.isChild(streamB.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(0, distributor.numChildren(streamB.id())); - - assertTrue(distributor.isChild(streamC.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(0, distributor.numChildren(streamC.id())); - - assertTrue(distributor.isChild(streamD.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(0, distributor.numChildren(streamD.id())); - } - - @Test - public void removeWithPrioritizableDependentsShouldNotRestructureTree() throws Exception { - Http2Stream streamA = connection.local().createStream(1, false); - Http2Stream streamB = connection.local().createStream(3, false); - Http2Stream streamC = connection.local().createStream(5, false); - Http2Stream streamD = connection.local().createStream(7, false); - - setPriority(streamB.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); - setPriority(streamC.id(), streamB.id(), DEFAULT_PRIORITY_WEIGHT, false); - setPriority(streamD.id(), streamB.id(), DEFAULT_PRIORITY_WEIGHT, false); - - // Default removal policy will cause it to be removed immediately. - // Closing streamB will distribute its weight to the children (C & D) equally. - streamB.close(); - - // Level 0 - assertEquals(1, distributor.numChildren(connection.connectionStream().id())); - - // Level 1 - assertTrue(distributor.isChild(streamA.id(), connection.connectionStream().id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(2, distributor.numChildren(streamA.id())); - - // Level 2 - short halfWeight = DEFAULT_PRIORITY_WEIGHT / 2; - assertTrue(distributor.isChild(streamC.id(), streamA.id(), halfWeight)); - assertEquals(0, distributor.numChildren(streamC.id())); - - assertTrue(distributor.isChild(streamD.id(), streamA.id(), halfWeight)); - assertEquals(0, distributor.numChildren(streamD.id())); - } - - @Test - public void closeWithNoPrioritizableDependentsShouldRestructureTree() throws Exception { - Http2Stream streamA = connection.local().createStream(1, false); - Http2Stream streamB = connection.local().createStream(3, false); - Http2Stream streamC = connection.local().createStream(5, false); - Http2Stream streamD = connection.local().createStream(7, false); - Http2Stream streamE = connection.local().createStream(9, false); - Http2Stream streamF = connection.local().createStream(11, false); - - setPriority(streamB.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); - setPriority(streamC.id(), streamB.id(), DEFAULT_PRIORITY_WEIGHT, false); - setPriority(streamD.id(), streamB.id(), DEFAULT_PRIORITY_WEIGHT, false); - setPriority(streamE.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT, false); - setPriority(streamF.id(), streamD.id(), DEFAULT_PRIORITY_WEIGHT, false); - - // Close internal nodes, leave 1 leaf node open, the only remaining stream is the one that is not closed (E). - streamA.close(); - // Closing streamB will distribute its weight to the children (C & D) equally. - streamB.close(); - streamC.close(); - streamD.close(); - streamF.close(); - - // Level 0 - assertEquals(1, distributor.numChildren(connection.connectionStream().id())); - - // Level 1 - short halfWeight = DEFAULT_PRIORITY_WEIGHT / 2; - assertTrue(distributor.isChild(streamE.id(), connection.connectionStream().id(), halfWeight)); - assertEquals(0, distributor.numChildren(streamE.id())); - } - - @Test - public void closeStreamWithChildrenShouldRedistributeWeightToChildren() throws Exception { - Http2Stream streamA = connection.local().createStream(1, false); - Http2Stream streamB = connection.local().createStream(3, false); - Http2Stream streamC = connection.local().createStream(5, false); - Http2Stream streamD = connection.local().createStream(7, false); - Http2Stream streamE = connection.local().createStream(9, false); - Http2Stream streamF = connection.local().createStream(11, false); - Http2Stream streamG = connection.local().createStream(13, false); - Http2Stream streamH = connection.local().createStream(15, false); - - setPriority(streamC.id(), streamA.id(), MAX_WEIGHT, false); - setPriority(streamD.id(), streamA.id(), MAX_WEIGHT, false); - setPriority(streamE.id(), streamA.id(), MAX_WEIGHT, false); - - setPriority(streamF.id(), streamB.id(), DEFAULT_PRIORITY_WEIGHT, false); - setPriority(streamG.id(), streamB.id(), DEFAULT_PRIORITY_WEIGHT, false); - setPriority(streamH.id(), streamB.id(), 2 * DEFAULT_PRIORITY_WEIGHT, false); - - streamE.close(); - // closing stream A will distribute its weight to the children (C & D) equally - streamA.close(); - // closing stream B will distribute its weight to the children (F & G & H) proportionally - streamB.close(); - // Level 0 - assertEquals(5, distributor.numChildren(connection.connectionStream().id())); - // Level 1 - short halfWeight = DEFAULT_PRIORITY_WEIGHT / 2; - assertTrue(distributor.isChild(streamC.id(), connection.connectionStream().id(), halfWeight)); - assertTrue(distributor.isChild(streamD.id(), connection.connectionStream().id(), halfWeight)); - - short quarterWeight = DEFAULT_PRIORITY_WEIGHT / 4; - assertTrue(distributor.isChild(streamF.id(), connection.connectionStream().id(), quarterWeight)); - assertTrue(distributor.isChild(streamG.id(), connection.connectionStream().id(), quarterWeight)); - assertTrue(distributor.isChild(streamH.id(), connection.connectionStream().id(), (short) (2 * quarterWeight))); - } - - @Test - public void priorityChangeWithNoPrioritizableDependentsShouldRestructureTree() throws Exception { - Http2Stream streamA = connection.local().createStream(1, false); - Http2Stream streamB = connection.local().createStream(3, false); - Http2Stream streamC = connection.local().createStream(5, false); - Http2Stream streamD = connection.local().createStream(7, false); - Http2Stream streamE = connection.local().createStream(9, false); - Http2Stream streamF = connection.local().createStream(11, false); - - setPriority(streamB.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); - setPriority(streamC.id(), streamB.id(), DEFAULT_PRIORITY_WEIGHT, false); - setPriority(streamD.id(), streamB.id(), DEFAULT_PRIORITY_WEIGHT, false); - setPriority(streamF.id(), streamD.id(), DEFAULT_PRIORITY_WEIGHT, false); - setPriority(streamE.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT, false); - - // Leave leaf nodes open (E & F) - streamA.close(); - // Closing streamB will distribute its weight to the children (C & D) equally. - streamB.close(); - streamC.close(); - streamD.close(); - - // Move F to depend on C, even though C is closed. - setPriority(streamF.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT, false); - - // Level 0 - assertEquals(2, distributor.numChildren(connection.connectionStream().id())); - - // Level 1 - short halfWeight = DEFAULT_PRIORITY_WEIGHT / 2; - assertTrue(distributor.isChild(streamE.id(), connection.connectionStream().id(), halfWeight)); - assertEquals(0, distributor.numChildren(streamE.id())); - - assertTrue(distributor.isChild(streamF.id(), connection.connectionStream().id(), halfWeight)); - assertEquals(0, distributor.numChildren(streamF.id())); - } - - @Test - public void circularDependencyShouldRestructureTree() throws Exception { - // Using example from https://tools.ietf.org/html/rfc7540#section-5.3.3 - // Initialize all the nodes - Http2Stream streamA = connection.local().createStream(1, false); - Http2Stream streamB = connection.local().createStream(3, false); - Http2Stream streamC = connection.local().createStream(5, false); - Http2Stream streamD = connection.local().createStream(7, false); - Http2Stream streamE = connection.local().createStream(9, false); - Http2Stream streamF = connection.local().createStream(11, false); - - assertEquals(6, distributor.numChildren(connection.connectionStream().id())); - assertEquals(0, distributor.numChildren(streamA.id())); - assertEquals(0, distributor.numChildren(streamB.id())); - assertEquals(0, distributor.numChildren(streamC.id())); - assertEquals(0, distributor.numChildren(streamD.id())); - assertEquals(0, distributor.numChildren(streamE.id())); - assertEquals(0, distributor.numChildren(streamF.id())); - - // Build the tree - setPriority(streamB.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); - assertEquals(5, distributor.numChildren(connection.connectionStream().id())); - assertTrue(distributor.isChild(streamB.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(1, distributor.numChildren(streamA.id())); - - setPriority(streamC.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); - assertEquals(4, distributor.numChildren(connection.connectionStream().id())); - assertTrue(distributor.isChild(streamC.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(2, distributor.numChildren(streamA.id())); - - setPriority(streamD.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT, false); - assertEquals(3, distributor.numChildren(connection.connectionStream().id())); - assertTrue(distributor.isChild(streamD.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(1, distributor.numChildren(streamC.id())); - - setPriority(streamE.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT, false); - assertEquals(2, distributor.numChildren(connection.connectionStream().id())); - assertTrue(distributor.isChild(streamE.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(2, distributor.numChildren(streamC.id())); - - setPriority(streamF.id(), streamD.id(), DEFAULT_PRIORITY_WEIGHT, false); - assertEquals(1, distributor.numChildren(connection.connectionStream().id())); - assertTrue(distributor.isChild(streamF.id(), streamD.id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(1, distributor.numChildren(streamD.id())); - - assertEquals(6, connection.numActiveStreams()); - - // Non-exclusive re-prioritization of a->d. - setPriority(streamA.id(), streamD.id(), DEFAULT_PRIORITY_WEIGHT, false); - - // Level 0 - assertEquals(1, distributor.numChildren(connection.connectionStream().id())); - - // Level 1 - assertTrue(distributor.isChild(streamD.id(), connection.connectionStream().id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(2, distributor.numChildren(streamD.id())); - - // Level 2 - assertTrue(distributor.isChild(streamF.id(), streamD.id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(0, distributor.numChildren(streamF.id())); - - assertTrue(distributor.isChild(streamA.id(), streamD.id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(2, distributor.numChildren(streamA.id())); - - // Level 3 - assertTrue(distributor.isChild(streamB.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(0, distributor.numChildren(streamB.id())); - - assertTrue(distributor.isChild(streamC.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(1, distributor.numChildren(streamC.id())); - - // Level 4 - assertTrue(distributor.isChild(streamE.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(0, distributor.numChildren(streamE.id())); - } - - @Test - public void circularDependencyWithExclusiveShouldRestructureTree() throws Exception { - // Using example from https://tools.ietf.org/html/rfc7540#section-5.3.3 - // Initialize all the nodes - Http2Stream streamA = connection.local().createStream(1, false); - Http2Stream streamB = connection.local().createStream(3, false); - Http2Stream streamC = connection.local().createStream(5, false); - Http2Stream streamD = connection.local().createStream(7, false); - Http2Stream streamE = connection.local().createStream(9, false); - Http2Stream streamF = connection.local().createStream(11, false); - - assertEquals(6, distributor.numChildren(connection.connectionStream().id())); - assertEquals(0, distributor.numChildren(streamA.id())); - assertEquals(0, distributor.numChildren(streamB.id())); - assertEquals(0, distributor.numChildren(streamC.id())); - assertEquals(0, distributor.numChildren(streamD.id())); - assertEquals(0, distributor.numChildren(streamE.id())); - assertEquals(0, distributor.numChildren(streamF.id())); - - // Build the tree - setPriority(streamB.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); - assertEquals(5, distributor.numChildren(connection.connectionStream().id())); - assertTrue(distributor.isChild(streamB.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(1, distributor.numChildren(streamA.id())); - - setPriority(streamC.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); - assertEquals(4, distributor.numChildren(connection.connectionStream().id())); - assertTrue(distributor.isChild(streamC.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(2, distributor.numChildren(streamA.id())); - - setPriority(streamD.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT, false); - assertEquals(3, distributor.numChildren(connection.connectionStream().id())); - assertTrue(distributor.isChild(streamD.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(1, distributor.numChildren(streamC.id())); - - setPriority(streamE.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT, false); - assertEquals(2, distributor.numChildren(connection.connectionStream().id())); - assertTrue(distributor.isChild(streamE.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(2, distributor.numChildren(streamC.id())); - - setPriority(streamF.id(), streamD.id(), DEFAULT_PRIORITY_WEIGHT, false); - assertEquals(1, distributor.numChildren(connection.connectionStream().id())); - assertTrue(distributor.isChild(streamF.id(), streamD.id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(1, distributor.numChildren(streamD.id())); - - assertEquals(6, connection.numActiveStreams()); - - // Exclusive re-prioritization of a->d. - setPriority(streamA.id(), streamD.id(), DEFAULT_PRIORITY_WEIGHT, true); - - // Level 0 - assertEquals(1, distributor.numChildren(connection.connectionStream().id())); - - // Level 1 - assertTrue(distributor.isChild(streamD.id(), connection.connectionStream().id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(1, distributor.numChildren(streamD.id())); - - // Level 2 - assertTrue(distributor.isChild(streamA.id(), streamD.id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(3, distributor.numChildren(streamA.id())); - - // Level 3 - assertTrue(distributor.isChild(streamB.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(0, distributor.numChildren(streamB.id())); - - assertTrue(distributor.isChild(streamF.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(0, distributor.numChildren(streamF.id())); - - assertTrue(distributor.isChild(streamC.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(1, distributor.numChildren(streamC.id())); - - // Level 4; - assertTrue(distributor.isChild(streamE.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(0, distributor.numChildren(streamE.id())); - } - - // Unknown parent streams can come about in two ways: - // 1. Because the stream is old and its state was purged - // 2. This is the first reference to the stream, as implied at least by RFC7540§5.3.1: - // > A dependency on a stream that is not currently in the tree — such as a stream in the - // > "idle" state — results in that stream being given a default priority - @Test - public void unknownParentShouldBeCreatedUnderConnection() throws Exception { - setup(5); - - // Purposefully avoid creating streamA's Http2Stream so that is it completely unknown. - // It shouldn't matter whether the ID is before or after streamB.id() - int streamAId = 1; - Http2Stream streamB = connection.local().createStream(3, false); - - assertEquals(1, distributor.numChildren(connection.connectionStream().id())); - assertEquals(0, distributor.numChildren(streamB.id())); - - // Build the tree - setPriority(streamB.id(), streamAId, DEFAULT_PRIORITY_WEIGHT, false); - - assertEquals(1, connection.numActiveStreams()); - - // Level 0 - assertEquals(1, distributor.numChildren(connection.connectionStream().id())); - - // Level 1 - assertTrue(distributor.isChild(streamAId, connection.connectionStream().id(), DEFAULT_PRIORITY_WEIGHT)); - assertEquals(1, distributor.numChildren(streamAId)); - - // Level 2 - assertTrue(distributor.isChild(streamB.id(), streamAId, DEFAULT_PRIORITY_WEIGHT)); - assertEquals(0, distributor.numChildren(streamB.id())); - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/WeightedFairQueueByteDistributorTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/WeightedFairQueueByteDistributorTest.java deleted file mode 100644 index 714fb2ccef..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/WeightedFairQueueByteDistributorTest.java +++ /dev/null @@ -1,964 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; -import org.mockito.ArgumentCaptor; -import org.mockito.MockitoAnnotations; -import org.mockito.verification.VerificationMode; - -import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_PRIORITY_WEIGHT; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.atMost; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.same; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -public class WeightedFairQueueByteDistributorTest extends AbstractWeightedFairQueueByteDistributorDependencyTest { - private static final int STREAM_A = 1; - private static final int STREAM_B = 3; - private static final int STREAM_C = 5; - private static final int STREAM_D = 7; - private static final int STREAM_E = 9; - private static final int ALLOCATION_QUANTUM = 100; - - @BeforeEach - public void setup() throws Http2Exception { - MockitoAnnotations.initMocks(this); - - // Assume we always write all the allocated bytes. - doAnswer(writeAnswer(false)).when(writer).write(any(Http2Stream.class), anyInt()); - - setup(-1); - } - - private void setup(int maxStateOnlySize) throws Http2Exception { - connection = new DefaultHttp2Connection(false); - distributor = maxStateOnlySize >= 0 ? new WeightedFairQueueByteDistributor(connection, maxStateOnlySize) - : new WeightedFairQueueByteDistributor(connection); - distributor.allocationQuantum(ALLOCATION_QUANTUM); - - connection.local().createStream(STREAM_A, false); - connection.local().createStream(STREAM_B, false); - Http2Stream streamC = connection.local().createStream(STREAM_C, false); - Http2Stream streamD = connection.local().createStream(STREAM_D, false); - setPriority(streamC.id(), STREAM_A, DEFAULT_PRIORITY_WEIGHT, false); - setPriority(streamD.id(), STREAM_A, DEFAULT_PRIORITY_WEIGHT, false); - } - - /** - * In this test, we block B such that it has no frames. We distribute enough bytes for all streams and stream B - * should be preserved in the priority queue structure until it has no "active" children, but it should not be - * doubly added to stream 0. - * - *
-     *         0
-     *         |
-     *         A
-     *         |
-     *        [B]
-     *         |
-     *         C
-     *         |
-     *         D
-     * 
- * - * After the write: - *
-     *         0
-     * 
- */ - @Test - public void writeWithNonActiveStreamShouldNotDobuleAddToPriorityQueue() throws Http2Exception { - initState(STREAM_A, 400, true); - initState(STREAM_B, 500, true); - initState(STREAM_C, 600, true); - initState(STREAM_D, 700, true); - - setPriority(STREAM_B, STREAM_A, DEFAULT_PRIORITY_WEIGHT, true); - setPriority(STREAM_D, STREAM_C, DEFAULT_PRIORITY_WEIGHT, true); - - // Block B, but it should still remain in the queue/tree structure. - initState(STREAM_B, 0, false); - - // Get the streams before the write, because they may be be closed. - Http2Stream streamA = stream(STREAM_A); - Http2Stream streamB = stream(STREAM_B); - Http2Stream streamC = stream(STREAM_C); - Http2Stream streamD = stream(STREAM_D); - - reset(writer); - doAnswer(writeAnswer(true)).when(writer).write(any(Http2Stream.class), anyInt()); - - assertFalse(write(400 + 600 + 700)); - assertEquals(400, captureWrites(streamA)); - verifyNeverWrite(streamB); - assertEquals(600, captureWrites(streamC)); - assertEquals(700, captureWrites(streamD)); - } - - @Test - public void bytesUnassignedAfterProcessing() throws Http2Exception { - initState(STREAM_A, 1, true); - initState(STREAM_B, 2, true); - initState(STREAM_C, 3, true); - initState(STREAM_D, 4, true); - - assertFalse(write(10)); - verifyWrite(STREAM_A, 1); - verifyWrite(STREAM_B, 2); - verifyWrite(STREAM_C, 3); - verifyWrite(STREAM_D, 4); - - assertFalse(write(10)); - verifyAnyWrite(STREAM_A, 1); - verifyAnyWrite(STREAM_B, 1); - verifyAnyWrite(STREAM_C, 1); - verifyAnyWrite(STREAM_D, 1); - } - - @Test - public void connectionErrorForWriterException() throws Http2Exception { - initState(STREAM_A, 1, true); - initState(STREAM_B, 2, true); - initState(STREAM_C, 3, true); - initState(STREAM_D, 4, true); - - Exception fakeException = new RuntimeException("Fake exception"); - doThrow(fakeException).when(writer).write(same(stream(STREAM_C)), eq(3)); - - Http2Exception e = assertThrows(Http2Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - write(10); - } - }); - assertFalse(Http2Exception.isStreamError(e)); - assertEquals(Http2Error.INTERNAL_ERROR, e.error()); - assertSame(fakeException, e.getCause()); - - verifyWrite(atMost(1), STREAM_A, 1); - verifyWrite(atMost(1), STREAM_B, 2); - verifyWrite(STREAM_C, 3); - verifyWrite(atMost(1), STREAM_D, 4); - - doAnswer(writeAnswer(false)).when(writer).write(same(stream(STREAM_C)), eq(3)); - assertFalse(write(10)); - verifyWrite(STREAM_A, 1); - verifyWrite(STREAM_B, 2); - verifyWrite(times(2), STREAM_C, 3); - verifyWrite(STREAM_D, 4); - } - - /** - * In this test, we verify that each stream is allocated a minimum chunk size. When bytes - * run out, the remaining streams will be next in line for the next iteration. - */ - @Test - public void minChunkShouldBeAllocatedPerStream() throws Http2Exception { - // Re-assign weights. - setPriority(STREAM_A, 0, (short) 50, false); - setPriority(STREAM_B, 0, (short) 200, false); - setPriority(STREAM_C, STREAM_A, (short) 100, false); - setPriority(STREAM_D, STREAM_A, (short) 100, false); - - // Update the streams. - initState(STREAM_A, ALLOCATION_QUANTUM, true); - initState(STREAM_B, ALLOCATION_QUANTUM, true); - initState(STREAM_C, ALLOCATION_QUANTUM, true); - initState(STREAM_D, ALLOCATION_QUANTUM, true); - - // Only write 3 * chunkSize, so that we'll only write to the first 3 streams. - int written = 3 * ALLOCATION_QUANTUM; - assertTrue(write(written)); - assertEquals(ALLOCATION_QUANTUM, captureWrites(STREAM_A)); - assertEquals(ALLOCATION_QUANTUM, captureWrites(STREAM_B)); - assertEquals(ALLOCATION_QUANTUM, captureWrites(STREAM_C)); - verifyWrite(atMost(1), STREAM_D, 0); - - // Now write again and verify that the last stream is written to. - assertFalse(write(ALLOCATION_QUANTUM)); - assertEquals(ALLOCATION_QUANTUM, captureWrites(STREAM_A)); - assertEquals(ALLOCATION_QUANTUM, captureWrites(STREAM_B)); - assertEquals(ALLOCATION_QUANTUM, captureWrites(STREAM_C)); - assertEquals(ALLOCATION_QUANTUM, captureWrites(STREAM_D)); - } - - /** - * In this test, we verify that the highest priority frame which has 0 bytes to send, but an empty frame is able - * to send that empty frame. - * - *
-     *         0
-     *        / \
-     *       A   B
-     *      / \
-     *     C   D
-     * 
- * - * After the tree shift: - * - *
-     *         0
-     *         |
-     *         A
-     *         |
-     *         B
-     *        / \
-     *       C   D
-     * 
- */ - @Test - public void emptyFrameAtHeadIsWritten() throws Http2Exception { - initState(STREAM_A, 0, true); - initState(STREAM_B, 0, true); - initState(STREAM_C, 0, true); - initState(STREAM_D, 10, true); - - setPriority(STREAM_B, STREAM_A, DEFAULT_PRIORITY_WEIGHT, true); - - assertFalse(write(10)); - verifyWrite(STREAM_A, 0); - verifyWrite(STREAM_B, 0); - verifyWrite(STREAM_C, 0); - verifyWrite(STREAM_D, 10); - } - - /** - * In this test, we block A which allows bytes to be written by C and D. Here's a view of the tree (stream A is - * blocked). - * - *
-     *         0
-     *        / \
-     *      [A]  B
-     *      / \
-     *     C   D
-     * 
- */ - @Test - public void blockedStreamNoDataShouldSpreadDataToChildren() throws Http2Exception { - blockedStreamShouldSpreadDataToChildren(false); - } - - /** - * In this test, we block A and also give it an empty data frame to send. - * All bytes should be delegated to by C and D. Here's a view of the tree (stream A is blocked). - * - *
-     *           0
-     *         /   \
-     *      [A](0)  B
-     *      / \
-     *     C   D
-     * 
- */ - @Test - public void blockedStreamWithDataAndNotAllowedToSendShouldSpreadDataToChildren() throws Http2Exception { - // A cannot stream. - initState(STREAM_A, 0, true, false); - blockedStreamShouldSpreadDataToChildren(false); - } - - /** - * In this test, we allow A to send, but expect the flow controller will only write to the stream 1 time. - * This is because we give the stream a chance to write its empty frame 1 time, and the stream will not - * be written to again until a update stream is called. - * - *
-     *         0
-     *        / \
-     *       A   B
-     *      / \
-     *     C   D
-     * 
- */ - @Test - public void streamWithZeroFlowControlWindowAndDataShouldWriteOnlyOnce() throws Http2Exception { - initState(STREAM_A, 0, true, true); - blockedStreamShouldSpreadDataToChildren(true); - - // Make sure if we call update stream again, A should write 1 more time. - initState(STREAM_A, 0, true, true); - assertFalse(write(1)); - verifyWrite(times(2), STREAM_A, 0); - - // Try to write again, but since no initState A should not write again - assertFalse(write(1)); - verifyWrite(times(2), STREAM_A, 0); - } - - private void blockedStreamShouldSpreadDataToChildren(boolean streamAShouldWriteZero) throws Http2Exception { - initState(STREAM_B, 10, true); - initState(STREAM_C, 10, true); - initState(STREAM_D, 10, true); - - // Write up to 10 bytes. - assertTrue(write(10)); - - if (streamAShouldWriteZero) { - verifyWrite(STREAM_A, 0); - } else { - verifyNeverWrite(STREAM_A); - } - verifyWrite(atMost(1), STREAM_C, 0); - verifyWrite(atMost(1), STREAM_D, 0); - - // B is entirely written - verifyWrite(STREAM_B, 10); - - // Now test that writes get delegated from A (which is blocked) to its children - assertTrue(write(5)); - if (streamAShouldWriteZero) { - verifyWrite(times(1), STREAM_A, 0); - } else { - verifyNeverWrite(STREAM_A); - } - verifyWrite(STREAM_D, 5); - verifyWrite(atMost(1), STREAM_C, 0); - - assertTrue(write(5)); - if (streamAShouldWriteZero) { - verifyWrite(times(1), STREAM_A, 0); - } else { - verifyNeverWrite(STREAM_A); - } - assertEquals(10, captureWrites(STREAM_C) + captureWrites(STREAM_D)); - - assertTrue(write(5)); - assertFalse(write(5)); - if (streamAShouldWriteZero) { - verifyWrite(times(1), STREAM_A, 0); - } else { - verifyNeverWrite(STREAM_A); - } - verifyWrite(times(2), STREAM_C, 5); - verifyWrite(times(2), STREAM_D, 5); - } - - /** - * In this test, we block B which allows all bytes to be written by A. A should not share the data with its children - * since it's not blocked. - * - *
-     *         0
-     *        / \
-     *       A  [B]
-     *      / \
-     *     C   D
-     * 
- */ - @Test - public void childrenShouldNotSendDataUntilParentBlocked() throws Http2Exception { - // B cannot stream. - initState(STREAM_A, 10, true); - initState(STREAM_C, 10, true); - initState(STREAM_D, 10, true); - - // Write up to 10 bytes. - assertTrue(write(10)); - - // A is assigned all of the bytes. - verifyWrite(STREAM_A, 10); - verifyNeverWrite(STREAM_B); - verifyWrite(atMost(1), STREAM_C, 0); - verifyWrite(atMost(1), STREAM_D, 0); - } - - /** - * In this test, we block B which allows all bytes to be written by A. Once A is complete, it will spill over the - * remaining of its portion to its children. - * - *
-     *         0
-     *        / \
-     *       A  [B]
-     *      / \
-     *     C   D
-     * 
- */ - @Test - public void parentShouldWaterFallDataToChildren() throws Http2Exception { - // B cannot stream. - initState(STREAM_A, 5, true); - initState(STREAM_C, 10, true); - initState(STREAM_D, 10, true); - - // Write up to 10 bytes. - assertTrue(write(10)); - - verifyWrite(STREAM_A, 5); - verifyNeverWrite(STREAM_B); - verifyWrite(STREAM_C, 5); - verifyNeverWrite(STREAM_D); - - assertFalse(write(15)); - verifyAnyWrite(STREAM_A, 1); - verifyNeverWrite(STREAM_B); - verifyWrite(times(2), STREAM_C, 5); - verifyWrite(STREAM_D, 10); - } - - /** - * In this test, we verify re-prioritizing a stream. We start out with B blocked: - * - *
-     *         0
-     *        / \
-     *       A  [B]
-     *      / \
-     *     C   D
-     * 
- * - * We then re-prioritize D so that it's directly off of the connection and verify that A and D split the written - * bytes between them. - * - *
-     *           0
-     *          /|\
-     *        /  |  \
-     *       A  [B]  D
-     *      /
-     *     C
-     * 
- */ - @Test - public void reprioritizeShouldAdjustOutboundFlow() throws Http2Exception { - // B cannot stream. - initState(STREAM_A, 10, true); - initState(STREAM_C, 10, true); - initState(STREAM_D, 10, true); - - // Re-prioritize D as a direct child of the connection. - setPriority(STREAM_D, 0, DEFAULT_PRIORITY_WEIGHT, false); - - assertTrue(write(10)); - - verifyWrite(STREAM_A, 10); - verifyNeverWrite(STREAM_B); - verifyNeverWrite(STREAM_C); - verifyWrite(atMost(1), STREAM_D, 0); - - assertFalse(write(20)); - verifyAnyWrite(STREAM_A, 1); - verifyNeverWrite(STREAM_B); - verifyWrite(STREAM_C, 10); - verifyWrite(STREAM_D, 10); - } - - /** - * Test that the maximum allowed amount the flow controller allows to be sent is always fully allocated if - * the streams have at least this much data to send. See https://github.com/netty/netty/issues/4266. - *
-     *            0
-     *          / | \
-     *        /   |   \
-     *      A(0) B(0) C(0)
-     *     /
-     *    D(> allowed to send in 1 allocation attempt)
-     * 
- */ - @Test - public void unstreamableParentsShouldFeedHungryChildren() throws Http2Exception { - // Setup the priority tree. - setPriority(STREAM_A, 0, (short) 32, false); - setPriority(STREAM_B, 0, (short) 16, false); - setPriority(STREAM_C, 0, (short) 16, false); - setPriority(STREAM_D, STREAM_A, (short) 16, false); - - final int writableBytes = 100; - - // Send enough so it can not be completely written out - final int expectedUnsentAmount = 1; - initState(STREAM_D, writableBytes + expectedUnsentAmount, true); - - assertTrue(write(writableBytes)); - verifyWrite(STREAM_D, writableBytes); - - assertFalse(write(expectedUnsentAmount)); - verifyWrite(STREAM_D, expectedUnsentAmount); - } - - /** - * In this test, we root all streams at the connection, and then verify that data is split appropriately based on - * weight (all available data is the same). - * - *
-     *           0
-     *        / / \ \
-     *       A B   C D
-     * 
- */ - @Test - public void writeShouldPreferHighestWeight() throws Http2Exception { - // Root the streams at the connection and assign weights. - setPriority(STREAM_A, 0, (short) 50, false); - setPriority(STREAM_B, 0, (short) 200, false); - setPriority(STREAM_C, 0, (short) 100, false); - setPriority(STREAM_D, 0, (short) 100, false); - - initState(STREAM_A, 1000, true); - initState(STREAM_B, 1000, true); - initState(STREAM_C, 1000, true); - initState(STREAM_D, 1000, true); - - // Set allocation quantum to 1 so it is easier to see the ratio of total bytes written between each stream. - distributor.allocationQuantum(1); - assertTrue(write(1000)); - - assertEquals(100, captureWrites(STREAM_A)); - assertEquals(450, captureWrites(STREAM_B)); - assertEquals(225, captureWrites(STREAM_C)); - assertEquals(225, captureWrites(STREAM_D)); - } - - /** - * In this test, we root all streams at the connection, block streams C and D, and then verify that data is - * prioritized toward stream B which has a higher weight than stream A. - *

- * We also verify that the amount that is written is not uniform, and not always the allocation quantum. - * - *

-     *            0
-     *        / /  \  \
-     *       A B   [C] [D]
-     * 
- */ - @Test - public void writeShouldFavorPriority() throws Http2Exception { - // Root the streams at the connection and assign weights. - setPriority(STREAM_A, 0, (short) 50, false); - setPriority(STREAM_B, 0, (short) 200, false); - setPriority(STREAM_C, 0, (short) 100, false); - setPriority(STREAM_D, 0, (short) 100, false); - - initState(STREAM_A, 1000, true); - initState(STREAM_B, 1000, true); - initState(STREAM_C, 1000, false); - initState(STREAM_D, 1000, false); - - // Set allocation quantum to 1 so it is easier to see the ratio of total bytes written between each stream. - distributor.allocationQuantum(1); - - assertTrue(write(100)); - assertEquals(20, captureWrites(STREAM_A)); - verifyWrite(times(20), STREAM_A, 1); - assertEquals(80, captureWrites(STREAM_B)); - verifyWrite(times(0), STREAM_B, 1); - verifyNeverWrite(STREAM_C); - verifyNeverWrite(STREAM_D); - - assertTrue(write(100)); - assertEquals(40, captureWrites(STREAM_A)); - verifyWrite(times(40), STREAM_A, 1); - assertEquals(160, captureWrites(STREAM_B)); - verifyWrite(atMost(1), STREAM_B, 1); - verifyNeverWrite(STREAM_C); - verifyNeverWrite(STREAM_D); - - assertTrue(write(1050)); - assertEquals(250, captureWrites(STREAM_A)); - verifyWrite(times(250), STREAM_A, 1); - assertEquals(1000, captureWrites(STREAM_B)); - verifyWrite(atMost(2), STREAM_B, 1); - verifyNeverWrite(STREAM_C); - verifyNeverWrite(STREAM_D); - - assertFalse(write(750)); - assertEquals(1000, captureWrites(STREAM_A)); - verifyWrite(times(1), STREAM_A, 750); - assertEquals(1000, captureWrites(STREAM_B)); - verifyWrite(times(0), STREAM_B, 0); - verifyNeverWrite(STREAM_C); - verifyNeverWrite(STREAM_D); - } - - /** - * In this test, we root all streams at the connection, and then verify that data is split equally among the stream, - * since they all have the same weight. - * - *
-     *           0
-     *        / / \ \
-     *       A B   C D
-     * 
- */ - @Test - public void samePriorityShouldDistributeBasedOnData() throws Http2Exception { - // Root the streams at the connection with the same weights. - setPriority(STREAM_A, 0, DEFAULT_PRIORITY_WEIGHT, false); - setPriority(STREAM_B, 0, DEFAULT_PRIORITY_WEIGHT, false); - setPriority(STREAM_C, 0, DEFAULT_PRIORITY_WEIGHT, false); - setPriority(STREAM_D, 0, DEFAULT_PRIORITY_WEIGHT, false); - - initState(STREAM_A, 400, true); - initState(STREAM_B, 500, true); - initState(STREAM_C, 0, true); - initState(STREAM_D, 700, true); - - // Set allocation quantum to 1 so it is easier to see the ratio of total bytes written between each stream. - distributor.allocationQuantum(1); - assertTrue(write(999)); - - assertEquals(333, captureWrites(STREAM_A)); - assertEquals(333, captureWrites(STREAM_B)); - verifyWrite(times(1), STREAM_C, 0); - assertEquals(333, captureWrites(STREAM_D)); - } - - /** - * In this test, we call distribute with 0 bytes and verify that all streams with 0 bytes are written. - * - *
-     *         0
-     *        / \
-     *       A   B
-     *      / \
-     *     C   D
-     * 
- * - * After the tree shift: - * - *
-     *         0
-     *         |
-     *        [A]
-     *         |
-     *         B
-     *        / \
-     *       C   D
-     * 
- */ - @Test - public void zeroDistributeShouldWriteAllZeroFrames() throws Http2Exception { - initState(STREAM_A, 400, false); - initState(STREAM_B, 0, true); - initState(STREAM_C, 0, true); - initState(STREAM_D, 0, true); - - setPriority(STREAM_B, STREAM_A, DEFAULT_PRIORITY_WEIGHT, true); - - assertFalse(write(0)); - verifyNeverWrite(STREAM_A); - verifyWrite(STREAM_B, 0); - verifyAnyWrite(STREAM_B, 1); - verifyWrite(STREAM_C, 0); - verifyAnyWrite(STREAM_C, 1); - verifyWrite(STREAM_D, 0); - verifyAnyWrite(STREAM_D, 1); - } - - /** - * In this test, we call distribute with 100 bytes which is the total amount eligible to be written, and also have - * streams with 0 bytes to write. All of these streams should be written with a single call to distribute. - * - *
-     *         0
-     *        / \
-     *       A   B
-     *      / \
-     *     C   D
-     * 
- * - * After the tree shift: - * - *
-     *         0
-     *         |
-     *        [A]
-     *         |
-     *         B
-     *        / \
-     *       C   D
-     * 
- */ - @Test - public void nonZeroDistributeShouldWriteAllZeroFramesIfAllEligibleDataIsWritten() throws Http2Exception { - initState(STREAM_A, 400, false); - initState(STREAM_B, 100, true); - initState(STREAM_C, 0, true); - initState(STREAM_D, 0, true); - - setPriority(STREAM_B, STREAM_A, DEFAULT_PRIORITY_WEIGHT, true); - - assertFalse(write(100)); - verifyNeverWrite(STREAM_A); - verifyWrite(STREAM_B, 100); - verifyAnyWrite(STREAM_B, 1); - verifyWrite(STREAM_C, 0); - verifyAnyWrite(STREAM_C, 1); - verifyWrite(STREAM_D, 0); - verifyAnyWrite(STREAM_D, 1); - } - - /** - * In this test, we shift the priority tree and verify priority bytes for each subtree are correct - * - *
-     *         0
-     *        / \
-     *       A   B
-     *      / \
-     *     C   D
-     * 
- * - * After the tree shift: - * - *
-     *         0
-     *         |
-     *         A
-     *         |
-     *         B
-     *        / \
-     *       C   D
-     * 
- */ - @Test - public void bytesDistributedWithRestructureShouldBeCorrect() throws Http2Exception { - initState(STREAM_A, 400, true); - initState(STREAM_B, 500, true); - initState(STREAM_C, 600, true); - initState(STREAM_D, 700, true); - - setPriority(STREAM_B, STREAM_A, DEFAULT_PRIORITY_WEIGHT, true); - - assertTrue(write(500)); - assertEquals(400, captureWrites(STREAM_A)); - verifyWrite(STREAM_B, 100); - verifyNeverWrite(STREAM_C); - verifyNeverWrite(STREAM_D); - - assertTrue(write(400)); - assertEquals(400, captureWrites(STREAM_A)); - assertEquals(500, captureWrites(STREAM_B)); - verifyWrite(atMost(1), STREAM_C, 0); - verifyWrite(atMost(1), STREAM_D, 0); - - assertFalse(write(1300)); - assertEquals(400, captureWrites(STREAM_A)); - assertEquals(500, captureWrites(STREAM_B)); - assertEquals(600, captureWrites(STREAM_C)); - assertEquals(700, captureWrites(STREAM_D)); - } - - /** - * In this test, we add a node to the priority tree and verify - * - *
-     *         0
-     *        / \
-     *       A   B
-     *      / \
-     *     C   D
-     * 
- * - * After the tree shift: - * - *
-     *         0
-     *        / \
-     *       A   B
-     *       |
-     *       E
-     *      / \
-     *     C   D
-     * 
- */ - @Test - public void bytesDistributedWithAdditionShouldBeCorrect() throws Http2Exception { - Http2Stream streamE = connection.local().createStream(STREAM_E, false); - setPriority(streamE.id(), STREAM_A, DEFAULT_PRIORITY_WEIGHT, true); - - // Send a bunch of data on each stream. - initState(STREAM_A, 400, true); - initState(STREAM_B, 500, true); - initState(STREAM_C, 600, true); - initState(STREAM_D, 700, true); - initState(STREAM_E, 900, true); - - assertTrue(write(900)); - assertEquals(400, captureWrites(STREAM_A)); - assertEquals(500, captureWrites(STREAM_B)); - verifyNeverWrite(STREAM_C); - verifyNeverWrite(STREAM_D); - verifyWrite(atMost(1), STREAM_E, 0); - - assertTrue(write(900)); - assertEquals(400, captureWrites(STREAM_A)); - assertEquals(500, captureWrites(STREAM_B)); - verifyWrite(atMost(1), STREAM_C, 0); - verifyWrite(atMost(1), STREAM_D, 0); - assertEquals(900, captureWrites(STREAM_E)); - - assertFalse(write(1301)); - assertEquals(400, captureWrites(STREAM_A)); - assertEquals(500, captureWrites(STREAM_B)); - assertEquals(600, captureWrites(STREAM_C)); - assertEquals(700, captureWrites(STREAM_D)); - assertEquals(900, captureWrites(STREAM_E)); - } - - /** - * In this test, we close an internal stream in the priority tree. - * - *
-     *         0
-     *        / \
-     *       A   B
-     *      / \
-     *     C   D
-     * 
- * - * After the close: - *
-     *          0
-     *        / | \
-     *       C  D  B
-     * 
- */ - @Test - public void bytesDistributedShouldBeCorrectWithInternalStreamClose() throws Http2Exception { - initState(STREAM_A, 400, true); - initState(STREAM_B, 500, true); - initState(STREAM_C, 600, true); - initState(STREAM_D, 700, true); - - stream(STREAM_A).close(); - - assertTrue(write(500)); - verifyNeverWrite(STREAM_A); - assertEquals(500, captureWrites(STREAM_B) + captureWrites(STREAM_C) + captureWrites(STREAM_D)); - - assertFalse(write(1300)); - verifyNeverWrite(STREAM_A); - assertEquals(500, captureWrites(STREAM_B)); - assertEquals(600, captureWrites(STREAM_C)); - assertEquals(700, captureWrites(STREAM_D)); - } - - /** - * In this test, we close a leaf stream in the priority tree and verify distribution. - * - *
-     *         0
-     *        / \
-     *       A   B
-     *      / \
-     *     C   D
-     * 
- * - * After the close: - *
-     *         0
-     *        / \
-     *       A   B
-     *       |
-     *       D
-     * 
- */ - @Test - public void bytesDistributedShouldBeCorrectWithLeafStreamClose() throws Http2Exception { - initState(STREAM_A, 400, true); - initState(STREAM_B, 500, true); - initState(STREAM_C, 600, true); - initState(STREAM_D, 700, true); - - stream(STREAM_C).close(); - - assertTrue(write(900)); - assertEquals(400, captureWrites(STREAM_A)); - assertEquals(500, captureWrites(STREAM_B)); - verifyNeverWrite(STREAM_C); - verifyWrite(atMost(1), STREAM_D, 0); - - assertFalse(write(700)); - assertEquals(400, captureWrites(STREAM_A)); - assertEquals(500, captureWrites(STREAM_B)); - verifyNeverWrite(STREAM_C); - assertEquals(700, captureWrites(STREAM_D)); - } - - @Test - public void activeStreamDependentOnNewNonActiveStreamGetsQuantum() throws Http2Exception { - setup(0); - initState(STREAM_D, 700, true); - setPriority(STREAM_D, STREAM_E, DEFAULT_PRIORITY_WEIGHT, true); - - assertFalse(write(700)); - assertEquals(700, captureWrites(STREAM_D)); - } - - @Test - public void streamWindowLargerThanIntDoesNotInfiniteLoop() throws Http2Exception { - initState(STREAM_A, Integer.MAX_VALUE + 1L, true, true); - assertTrue(write(Integer.MAX_VALUE)); - verifyWrite(STREAM_A, Integer.MAX_VALUE); - assertFalse(write(1)); - verifyWrite(STREAM_A, 1); - } - - private boolean write(int numBytes) throws Http2Exception { - return distributor.distribute(numBytes, writer); - } - - private void verifyWrite(int streamId, int numBytes) { - verify(writer).write(same(stream(streamId)), eq(numBytes)); - } - - private void verifyWrite(VerificationMode mode, int streamId, int numBytes) { - verify(writer, mode).write(same(stream(streamId)), eq(numBytes)); - } - - private void verifyAnyWrite(int streamId, int times) { - verify(writer, times(times)).write(same(stream(streamId)), anyInt()); - } - - private void verifyNeverWrite(int streamId) { - verifyNeverWrite(stream(streamId)); - } - - private void verifyNeverWrite(Http2Stream stream) { - verify(writer, never()).write(same(stream), anyInt()); - } - - private int captureWrites(int streamId) { - return captureWrites(stream(streamId)); - } - - private int captureWrites(Http2Stream stream) { - ArgumentCaptor captor = ArgumentCaptor.forClass(Integer.class); - verify(writer, atLeastOnce()).write(same(stream), captor.capture()); - int total = 0; - for (Integer x : captor.getAllValues()) { - total += x; - } - return total; - } -} diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/WeightedFairQueueRemoteFlowControllerTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/WeightedFairQueueRemoteFlowControllerTest.java deleted file mode 100644 index f65bd5717a..0000000000 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/WeightedFairQueueRemoteFlowControllerTest.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -public class WeightedFairQueueRemoteFlowControllerTest extends DefaultHttp2RemoteFlowControllerTest { - @Override - protected StreamByteDistributor newDistributor(Http2Connection connection) { - return new WeightedFairQueueByteDistributor(connection); - } -} diff --git a/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testDuplicateHeaders.json b/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testDuplicateHeaders.json deleted file mode 100644 index 8e07effb24..0000000000 --- a/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testDuplicateHeaders.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "header_blocks": - [ - { - "headers": [ - { ":path": "/somepath" }, - { "x-custom": "val" } - ], - "encoded": [ - "4487 6107 a4b5 8d33 ff40 86f2 b12d 424f", - "4f03 7661 6c" - ], - "dynamic_table": [ - { "x-custom": "val" }, - { ":path": "/somepath" } - ], - "table_size": 89 - }, - { - "headers": [ - { ":path": "/somepath" }, - { "x-custom": "val" }, - { "x-custom": "val" } - ], - "encoded": [ - "bfbe be" - ], - "dynamic_table": [ - { "x-custom": "val" }, - { ":path": "/somepath" } - ], - "table_size": 89 - }, - { - "headers": [ - { ":path": "/somepath" }, - { "x-custom": "val" }, - { "foo": "bar" }, - { "x-custom": "val" } - ], - "encoded": [ - "bfbe 4082 94e7 0362 6172 bf" - ], - "dynamic_table": [ - { "foo": "bar" }, - { "x-custom": "val" }, - { ":path": "/somepath" } - ], - "table_size": 127 - }, - { - "headers": [ - { ":path": "/somepath" } - ], - "encoded": [ - "c0" - ], - "dynamic_table": [ - { "foo": "bar" }, - { "x-custom": "val" }, - { ":path": "/somepath" } - ], - "table_size": 127 - } - ] -} diff --git a/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testEmpty.json b/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testEmpty.json deleted file mode 100644 index 19b46dd617..0000000000 --- a/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testEmpty.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "header_blocks": - [ - { - "headers": [ - ], - "encoded": [ - ], - "dynamic_table": [ - ], - "table_size": 0 - } - ] -} diff --git a/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testEviction.json b/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testEviction.json deleted file mode 100644 index fb5ddcf416..0000000000 --- a/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testEviction.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "max_header_table_size": 128, - "header_blocks": - [ - { - "headers": [ - { ":path": "/somepath" }, - { "x-custom": "val1" }, - { "x-custom": "val2" }, - { "x-custom": "val3" } - ], - "encoded": [ - "4487 6107 a4b5 8d33 ff40 86f2 b12d 424f", - "4f83 ee3a 037e 83ee 3a05 7e83 ee3a 19" - ], - "dynamic_table": [ - { "x-custom": "val3" }, - { "x-custom": "val2" } - ], - "table_size": 88 - }, - { - "headers": [ - { ":path": "/somepath" }, - { "x-custom": "val4" }, - { "x-custom": "val5" }, - { "x-custom": "val6" } - ], - "encoded": [ - "4487 6107 a4b5 8d33 ff40 86f2 b12d 424f", - "4f83 ee3a 1a7e 83ee 3a1b 7e83 ee3a 1c" - ], - "dynamic_table": [ - { "x-custom": "val6" }, - { "x-custom": "val5" } - ], - "table_size": 88 - }, - { - "headers": [ - { ":path": "/somepath" }, - { "x-custom": "val1" }, - { "x-custom": "val2" }, - { "x-custom": "val3" } - ], - "encoded": [ - "4487 6107 a4b5 8d33 ff40 86f2 b12d 424f", - "4f83 ee3a 037e 83ee 3a05 7e83 ee3a 19" - ], - "dynamic_table": [ - { "x-custom": "val3" }, - { "x-custom": "val2" } - ], - "table_size": 88 - } - ] -} diff --git a/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testMaxHeaderTableSize.json b/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testMaxHeaderTableSize.json deleted file mode 100644 index 69d3b8c183..0000000000 --- a/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testMaxHeaderTableSize.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "max_header_table_size": 128, - "header_blocks": - [ - { - "headers": [ - { "name1": "val1" }, - { "name2": "val2" }, - { "name3": "val3" } - ], - "encoded": [ - "4084 a874 943f 83ee 3a03 4084 a874 945f", - "83ee 3a05 4084 a874 959f 83ee 3a19" - ], - "dynamic_table": [ - { "name3": "val3" }, - { "name2": "val2" }, - { "name1": "val1" } - ], - "table_size": 123 - }, - { - "max_header_table_size": 81, - "headers": [ - { "name3": "val3" }, - { "name2": "val2" } - ], - "encoded": [ - "3f32 be40 84a8 7494 5f83 ee3a 05" - ], - "dynamic_table": [ - { "name2": "val2" } - ], - "table_size": 41 - }, - { - "max_header_table_size": 128, - "headers": [ - { "name1": "val1" }, - { "name2": "val2" }, - { "name3": "val3" } - ], - "encoded": [ - "3f61 4084 a874 943f 83ee 3a03 bf40 84a8", - "7495 9f83 ee3a 19" - ], - "dynamic_table": [ - { "name3": "val3" }, - { "name1": "val1" }, - { "name2": "val2" } - ], - "table_size": 123 - } - ] -} diff --git a/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testSpecExampleC2_1.json b/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testSpecExampleC2_1.json deleted file mode 100644 index 0838aba003..0000000000 --- a/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testSpecExampleC2_1.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "header_blocks": - [ - { - "headers": [ - { "custom-key": "custom-header" } - ], - "encoded": [ - "4088 25a8 49e9 5ba9 7d7f 8925 a849 e95a", - "728e 42d9" - ], - "dynamic_table": [ - { "custom-key": "custom-header" } - ], - "table_size": 55 - } - ] -} diff --git a/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testSpecExampleC2_2.json b/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testSpecExampleC2_2.json deleted file mode 100644 index 01fb8fe5c0..0000000000 --- a/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testSpecExampleC2_2.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "header_blocks": - [ - { - "headers": [ - { ":path": "/sample/path" } - ], - "encoded": [ - "4489 6103 a6ba 0ac5 634c ff" - ], - "dynamic_table": [ - { ":path": "/sample/path" } - ], - "table_size": 49 - } - ] -} diff --git a/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testSpecExampleC2_3.json b/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testSpecExampleC2_3.json deleted file mode 100644 index 50892d647e..0000000000 --- a/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testSpecExampleC2_3.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "sensitive_headers": true, - "header_blocks": - [ - { - "headers": [ - { "password": "secret" } - ], - "encoded": [ - "1086 ac68 4783 d927 8441 4961 53" - ], - "dynamic_table": [ - ], - "table_size": 0 - } - ] -} diff --git a/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testSpecExampleC2_4.json b/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testSpecExampleC2_4.json deleted file mode 100644 index 4e8c483fdc..0000000000 --- a/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testSpecExampleC2_4.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "force_huffman_off": true, - "header_blocks": - [ - { - "headers": [ - { ":method": "GET" } - ], - "encoded": [ - "82" - ], - "dynamic_table": [ - ], - "table_size": 0 - } - ] -} diff --git a/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testSpecExampleC3.json b/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testSpecExampleC3.json deleted file mode 100644 index 3e8f658b43..0000000000 --- a/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testSpecExampleC3.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "header_blocks": - [ - { - "headers": [ - { ":method": "GET" }, - { ":scheme": "http" }, - { ":path": "/" }, - { ":authority": "www.example.com" } - ], - "encoded": [ - "8286 8441 8cf1 e3c2 e5f2 3a6b a0ab 90f4", - "ff" - ], - "dynamic_table": [ - { ":authority": "www.example.com" } - ], - "table_size": 57 - }, - { - "headers": [ - { ":method": "GET" }, - { ":scheme": "http" }, - { ":path": "/" }, - { ":authority": "www.example.com" }, - { "cache-control": "no-cache" } - ], - "encoded": [ - "8286 84be 5886 a8eb 1064 9cbf" - ], - "dynamic_table": [ - { "cache-control": "no-cache" }, - { ":authority": "www.example.com" } - ], - "table_size": 110 - }, - { - "headers": [ - { ":method": "GET" }, - { ":scheme": "https" }, - { ":path": "/index.html" }, - { ":authority": "www.example.com" }, - { "custom-key": "custom-value" } - ], - "encoded": [ - "8287 85bf 4088 25a8 49e9 5ba9 7d7f 8925", - "a849 e95b b8e8 b4bf" - ], - "dynamic_table": [ - { "custom-key": "custom-value" }, - { "cache-control": "no-cache" }, - { ":authority": "www.example.com" } - ], - "table_size": 164 - } - ] -} diff --git a/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testSpecExampleC4.json b/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testSpecExampleC4.json deleted file mode 100644 index 0543f53af9..0000000000 --- a/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testSpecExampleC4.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "force_huffman_on": true, - "header_blocks": - [ - { - "headers": [ - { ":method": "GET" }, - { ":scheme": "http" }, - { ":path": "/" }, - { ":authority": "www.example.com" } - ], - "encoded": [ - "8286 8441 8cf1 e3c2 e5f2 3a6b a0ab 90f4", - "ff" - ], - "dynamic_table": [ - { ":authority": "www.example.com" } - ], - "table_size": 57 - }, - { - "headers": [ - { ":method": "GET" }, - { ":scheme": "http" }, - { ":path": "/" }, - { ":authority": "www.example.com" }, - { "cache-control": "no-cache" } - ], - "encoded": [ - "8286 84be 5886 a8eb 1064 9cbf" - ], - "dynamic_table": [ - { "cache-control": "no-cache" }, - { ":authority": "www.example.com" } - ], - "table_size": 110 - }, - { - "headers": [ - { ":method": "GET" }, - { ":scheme": "https" }, - { ":path": "/index.html" }, - { ":authority": "www.example.com" }, - { "custom-key": "custom-value" } - ], - "encoded": [ - "8287 85bf 4088 25a8 49e9 5ba9 7d7f 8925", - "a849 e95b b8e8 b4bf" - ], - "dynamic_table": [ - { "custom-key": "custom-value" }, - { "cache-control": "no-cache" }, - { ":authority": "www.example.com" } - ], - "table_size": 164 - } - ] -} diff --git a/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testSpecExampleC5.json b/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testSpecExampleC5.json deleted file mode 100644 index 28ec5036c2..0000000000 --- a/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testSpecExampleC5.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "max_header_table_size": 256, - "header_blocks": - [ - { - "headers": [ - { ":status": "302" }, - { "cache-control": "private" }, - { "date": "Mon, 21 Oct 2013 20:13:21 GMT" }, - { "location": "https://www.example.com" } - ], - "encoded": [ - "4882 6402 5885 aec3 771a 4b61 96d0 7abe", - "9410 54d4 44a8 2005 9504 0b81 66e0 82a6", - "2d1b ff6e 919d 29ad 1718 63c7 8f0b 97c8", - "e9ae 82ae 43d3" - ], - "dynamic_table": [ - { "location": "https://www.example.com" }, - { "date": "Mon, 21 Oct 2013 20:13:21 GMT" }, - { "cache-control": "private" }, - { ":status": "302" } - ], - "table_size": 222 - }, - { - "headers": [ - { ":status": "307" }, - { "cache-control": "private" }, - { "date": "Mon, 21 Oct 2013 20:13:21 GMT" }, - { "location": "https://www.example.com" } - ], - "encoded": [ - "4803 3330 37c1 c0bf" - ], - "dynamic_table": [ - { ":status": "307" }, - { "location": "https://www.example.com" }, - { "date": "Mon, 21 Oct 2013 20:13:21 GMT" }, - { "cache-control": "private" } - ], - "table_size": 222 - }, - { - "headers": [ - { ":status": "200" }, - { "cache-control": "private" }, - { "date": "Mon, 21 Oct 2013 20:13:22 GMT" }, - { "location": "https://www.example.com" }, - { "content-encoding": "gzip" }, - { "set-cookie": "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1" } - ], - "encoded": [ - "88c1 6196 d07a be94 1054 d444 a820 0595", - "040b 8166 e084 a62d 1bff c05a 839b d9ab", - "77ad 94e7 821d d7f2 e6c7 b335 dfdf cd5b", - "3960 d5af 2708 7f36 72c1 ab27 0fb5 291f", - "9587 3160 65c0 03ed 4ee5 b106 3d50 07" - ], - "dynamic_table": [ - { "set-cookie": "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1" }, - { "content-encoding": "gzip" }, - { "date": "Mon, 21 Oct 2013 20:13:22 GMT" } - ], - "table_size": 215 - } - ] -} diff --git a/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testSpecExampleC6.json b/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testSpecExampleC6.json deleted file mode 100644 index 28ec5036c2..0000000000 --- a/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testSpecExampleC6.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "max_header_table_size": 256, - "header_blocks": - [ - { - "headers": [ - { ":status": "302" }, - { "cache-control": "private" }, - { "date": "Mon, 21 Oct 2013 20:13:21 GMT" }, - { "location": "https://www.example.com" } - ], - "encoded": [ - "4882 6402 5885 aec3 771a 4b61 96d0 7abe", - "9410 54d4 44a8 2005 9504 0b81 66e0 82a6", - "2d1b ff6e 919d 29ad 1718 63c7 8f0b 97c8", - "e9ae 82ae 43d3" - ], - "dynamic_table": [ - { "location": "https://www.example.com" }, - { "date": "Mon, 21 Oct 2013 20:13:21 GMT" }, - { "cache-control": "private" }, - { ":status": "302" } - ], - "table_size": 222 - }, - { - "headers": [ - { ":status": "307" }, - { "cache-control": "private" }, - { "date": "Mon, 21 Oct 2013 20:13:21 GMT" }, - { "location": "https://www.example.com" } - ], - "encoded": [ - "4803 3330 37c1 c0bf" - ], - "dynamic_table": [ - { ":status": "307" }, - { "location": "https://www.example.com" }, - { "date": "Mon, 21 Oct 2013 20:13:21 GMT" }, - { "cache-control": "private" } - ], - "table_size": 222 - }, - { - "headers": [ - { ":status": "200" }, - { "cache-control": "private" }, - { "date": "Mon, 21 Oct 2013 20:13:22 GMT" }, - { "location": "https://www.example.com" }, - { "content-encoding": "gzip" }, - { "set-cookie": "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1" } - ], - "encoded": [ - "88c1 6196 d07a be94 1054 d444 a820 0595", - "040b 8166 e084 a62d 1bff c05a 839b d9ab", - "77ad 94e7 821d d7f2 e6c7 b335 dfdf cd5b", - "3960 d5af 2708 7f36 72c1 ab27 0fb5 291f", - "9587 3160 65c0 03ed 4ee5 b106 3d50 07" - ], - "dynamic_table": [ - { "set-cookie": "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1" }, - { "content-encoding": "gzip" }, - { "date": "Mon, 21 Oct 2013 20:13:22 GMT" } - ], - "table_size": 215 - } - ] -} diff --git a/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testStaticTableEntries.json b/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testStaticTableEntries.json deleted file mode 100644 index 9f5ccc2631..0000000000 --- a/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testStaticTableEntries.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "header_blocks": - [ - { - "headers": [ - { ":authority": "" }, - { ":method": "GET" }, - { ":method": "POST" }, - { ":path": "/" }, - { ":path": "/index.html" }, - { ":scheme": "http" }, - { ":scheme": "https" }, - { "accept-charset": "" }, - { "accept-encoding": "gzip, deflate" }, - { "accept-language": "" }, - { "accept-ranges": "" }, - { "accept": "" }, - { "access-control-allow-origin": "" }, - { "age": "" }, - { "allow": "" }, - { "authorization": "" }, - { "cache-control": "" }, - { "content-disposition": "" }, - { "content-encoding": "" }, - { "content-language": "" }, - { "content-length": "" }, - { "content-location": "" }, - { "content-range": "" }, - { "content-type": "" }, - { "cookie": "" }, - { "date": "" }, - { "etag": "" }, - { "expect": "" }, - { "expires": "" }, - { "from": "" }, - { "host": "" }, - { "if-match": "" }, - { "if-modified-since": "" }, - { "if-none-match": "" }, - { "if-range": "" }, - { "if-unmodified-since": "" }, - { "last-modified": "" }, - { "link": "" }, - { "location": "" }, - { "max-forwards": "" }, - { "proxy-authenticate": "" }, - { "proxy-authorization": "" }, - { "range": "" }, - { "referer": "" }, - { "refresh": "" }, - { "retry-after": "" }, - { "server": "" }, - { "set-cookie": "" }, - { "strict-transport-security": "" }, - { "transfer-encoding": "" }, - { "user-agent": "" }, - { "vary": "" }, - { "via": "" }, - { "www-authenticate": "" } - ], - "encoded": [ - "8182 8384 8586 87 8f90", - "9192 9394 9596 9798 999a 9b9c 9d9e 9fa0", - "a1a2 a3a4 a5a6 a7a8 a9aa abac adae afb0", - "b1b2 b3b4 b5b6 b7b8 b9ba bbbc bd" - ], - "dynamic_table": [ - ], - "table_size": 0 - } - ] -} - diff --git a/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testStaticTableResponseEntries.json b/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testStaticTableResponseEntries.json deleted file mode 100644 index 91ec07b8ea..0000000000 --- a/codec-http2/src/test/resources/io/netty/handler/codec/http2/testdata/testStaticTableResponseEntries.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "header_blocks": - [ - { - "headers": [ - { ":status": "200" }, - { ":status": "204" }, - { ":status": "206" }, - { ":status": "304" }, - { ":status": "400" }, - { ":status": "404" }, - { ":status": "500" } - ], - "encoded": [ - "8889 8a8b 8c8d 8e" - ], - "dynamic_table": [ - ], - "table_size": 0 - } - ] -} - diff --git a/codec-memcache/pom.xml b/codec-memcache/pom.xml deleted file mode 100644 index 2383724433..0000000000 --- a/codec-memcache/pom.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - - - 4.0.0 - - io.netty - netty-parent - 5.0.0.Final-SNAPSHOT - - - netty-codec-memcache - jar - - Netty/Codec/Memcache - - - io.netty.codec.memcache - - - - - ${project.groupId} - netty-common - ${project.version} - - - ${project.groupId} - netty-buffer - ${project.version} - - - ${project.groupId} - netty-transport - ${project.version} - - - ${project.groupId} - netty-codec - ${project.version} - - - - - diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/AbstractMemcacheObject.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/AbstractMemcacheObject.java deleted file mode 100644 index 395c5edfb1..0000000000 --- a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/AbstractMemcacheObject.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.memcache; - -import static java.util.Objects.requireNonNull; - -import io.netty.handler.codec.DecoderResult; -import io.netty.util.AbstractReferenceCounted; -import io.netty.util.internal.UnstableApi; - -/** - * The default {@link MemcacheObject} implementation. - */ -@UnstableApi -public abstract class AbstractMemcacheObject extends AbstractReferenceCounted implements MemcacheObject { - - private DecoderResult decoderResult = DecoderResult.SUCCESS; - - protected AbstractMemcacheObject() { - // Disallow direct instantiation - } - - @Override - public DecoderResult decoderResult() { - return decoderResult; - } - - @Override - public void setDecoderResult(DecoderResult result) { - requireNonNull(result, "result"); - - decoderResult = result; - } -} diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/AbstractMemcacheObjectAggregator.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/AbstractMemcacheObjectAggregator.java deleted file mode 100644 index 15878fe4de..0000000000 --- a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/AbstractMemcacheObjectAggregator.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.memcache; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelPipeline; -import io.netty.handler.codec.MessageAggregator; -import io.netty.handler.codec.memcache.binary.BinaryMemcacheRequestDecoder; -import io.netty.handler.codec.memcache.binary.BinaryMemcacheResponseEncoder; -import io.netty.util.internal.UnstableApi; - -/** - * A {@link ChannelHandler} that aggregates an {@link MemcacheMessage} - * and its following {@link MemcacheContent}s into a single {@link MemcacheMessage} with - * no following {@link MemcacheContent}s. It is useful when you don't want to take - * care of memcache messages where the content comes along in chunks. Insert this - * handler after a AbstractMemcacheObjectDecoder in the {@link ChannelPipeline}. - *

- * For example, here for the binary protocol: - *

- *

- * {@link ChannelPipeline} p = ...;
- * ...
- * p.addLast("decoder", new {@link BinaryMemcacheRequestDecoder}());
- * p.addLast("aggregator", new {@link io.netty.handler.codec.memcache.binary.BinaryMemcacheObjectAggregator}(1048576)
- * );
- * ...
- * p.addLast("encoder", new {@link BinaryMemcacheResponseEncoder}());
- * p.addLast("handler", new YourMemcacheRequestHandler());
- * 
- */ -@UnstableApi -public abstract class AbstractMemcacheObjectAggregator extends - MessageAggregator { - - protected AbstractMemcacheObjectAggregator(int maxContentLength) { - super(maxContentLength); - } - - @Override - protected boolean isContentMessage(MemcacheObject msg) throws Exception { - return msg instanceof MemcacheContent; - } - - @Override - protected boolean isLastContentMessage(MemcacheContent msg) throws Exception { - return msg instanceof LastMemcacheContent; - } - - @Override - protected boolean isAggregated(MemcacheObject msg) throws Exception { - return msg instanceof FullMemcacheMessage; - } - - @Override - protected boolean isContentLengthInvalid(H start, int maxContentLength) { - return false; - } - - @Override - protected Object newContinueResponse(H start, int maxContentLength, ChannelPipeline pipeline) { - return null; - } - - @Override - protected boolean closeAfterContinueResponse(Object msg) throws Exception { - throw new UnsupportedOperationException(); - } - - @Override - protected boolean ignoreContentAfterContinueResponse(Object msg) throws Exception { - throw new UnsupportedOperationException(); - } -} diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/AbstractMemcacheObjectDecoder.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/AbstractMemcacheObjectDecoder.java deleted file mode 100644 index 20ac1f6d8c..0000000000 --- a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/AbstractMemcacheObjectDecoder.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.memcache; - -import io.netty.handler.codec.ByteToMessageDecoder; -import io.netty.util.internal.UnstableApi; - -/** - * Abstract super class for both ascii and binary decoders. - *

- * Currently it just acts as a common denominator, but will certainly include methods once the ascii protocol - * is implemented. - */ -@UnstableApi -public abstract class AbstractMemcacheObjectDecoder extends ByteToMessageDecoder { -} diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/AbstractMemcacheObjectEncoder.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/AbstractMemcacheObjectEncoder.java deleted file mode 100644 index 5f7659790a..0000000000 --- a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/AbstractMemcacheObjectEncoder.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.memcache; - -import io.netty.buffer.ByteBufConvertible; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.FileRegion; -import io.netty.handler.codec.MessageToMessageEncoder; -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.UnstableApi; - -import java.util.List; - -/** - * A general purpose {@link AbstractMemcacheObjectEncoder} that encodes {@link MemcacheMessage}s. - *

- *

Note that this class is designed to be extended, especially because both the binary and ascii protocol - * require different treatment of their messages. Since the content chunk writing is the same for both, the encoder - * abstracts this right away.

- */ -@UnstableApi -public abstract class AbstractMemcacheObjectEncoder extends MessageToMessageEncoder { - - private boolean expectingMoreContent; - - @Override - protected void encode(ChannelHandlerContext ctx, Object msg, List out) throws Exception { - if (msg instanceof MemcacheMessage) { - if (expectingMoreContent) { - throw new IllegalStateException("unexpected message type: " + StringUtil.simpleClassName(msg)); - } - - @SuppressWarnings({ "unchecked", "CastConflictsWithInstanceof" }) - final M m = (M) msg; - out.add(encodeMessage(ctx, m)); - } - - if (msg instanceof MemcacheContent || msg instanceof ByteBufConvertible || msg instanceof FileRegion) { - int contentLength = contentLength(msg); - if (contentLength > 0) { - out.add(encodeAndRetain(msg)); - } else { - out.add(Unpooled.EMPTY_BUFFER); - } - - expectingMoreContent = !(msg instanceof LastMemcacheContent); - } - } - - @Override - public boolean acceptOutboundMessage(Object msg) throws Exception { - return msg instanceof MemcacheObject || msg instanceof ByteBufConvertible || msg instanceof FileRegion; - } - - /** - * Take the given {@link MemcacheMessage} and encode it into a writable {@link ByteBuf}. - * - * @param ctx the channel handler context. - * @param msg the message to encode. - * @return the {@link ByteBuf} representation of the message. - */ - protected abstract ByteBuf encodeMessage(ChannelHandlerContext ctx, M msg); - - /** - * Determine the content length of the given object. - * - * @param msg the object to determine the length of. - * @return the determined content length. - */ - private static int contentLength(Object msg) { - if (msg instanceof MemcacheContent) { - return ((MemcacheContent) msg).content().readableBytes(); - } - if (msg instanceof ByteBufConvertible) { - return ((ByteBufConvertible) msg).asByteBuf().readableBytes(); - } - if (msg instanceof FileRegion) { - return (int) ((FileRegion) msg).count(); - } - throw new IllegalStateException("unexpected message type: " + StringUtil.simpleClassName(msg)); - } - - /** - * Encode the content, depending on the object type. - * - * @param msg the object to encode. - * @return the encoded object. - */ - private static Object encodeAndRetain(Object msg) { - if (msg instanceof ByteBufConvertible) { - return ((ByteBufConvertible) msg).asByteBuf().retain(); - } - if (msg instanceof MemcacheContent) { - return ((MemcacheContent) msg).content().retain(); - } - if (msg instanceof FileRegion) { - return ((FileRegion) msg).retain(); - } - throw new IllegalStateException("unexpected message type: " + StringUtil.simpleClassName(msg)); - } - -} diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/DefaultLastMemcacheContent.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/DefaultLastMemcacheContent.java deleted file mode 100644 index f63001cf0b..0000000000 --- a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/DefaultLastMemcacheContent.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.memcache; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.internal.UnstableApi; - -/** - * The default implementation for the {@link LastMemcacheContent}. - */ -@UnstableApi -public class DefaultLastMemcacheContent extends DefaultMemcacheContent implements LastMemcacheContent { - - public DefaultLastMemcacheContent() { - super(Unpooled.buffer()); - } - - public DefaultLastMemcacheContent(ByteBuf content) { - super(content); - } - - @Override - public LastMemcacheContent retain() { - super.retain(); - return this; - } - - @Override - public LastMemcacheContent retain(int increment) { - super.retain(increment); - return this; - } - - @Override - public LastMemcacheContent touch() { - super.touch(); - return this; - } - - @Override - public LastMemcacheContent touch(Object hint) { - super.touch(hint); - return this; - } - - @Override - public LastMemcacheContent copy() { - return replace(content().copy()); - } - - @Override - public LastMemcacheContent duplicate() { - return replace(content().duplicate()); - } - - @Override - public LastMemcacheContent retainedDuplicate() { - return replace(content().retainedDuplicate()); - } - - @Override - public LastMemcacheContent replace(ByteBuf content) { - return new DefaultLastMemcacheContent(content); - } -} diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/DefaultMemcacheContent.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/DefaultMemcacheContent.java deleted file mode 100644 index beeb92a09e..0000000000 --- a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/DefaultMemcacheContent.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.memcache; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.UnstableApi; - -/** - * The default {@link MemcacheContent} implementation. - */ -@UnstableApi -public class DefaultMemcacheContent extends AbstractMemcacheObject implements MemcacheContent { - - private final ByteBuf content; - - /** - * Creates a new instance with the specified content. - */ - public DefaultMemcacheContent(ByteBuf content) { - requireNonNull(content, "content"); - this.content = content; - } - - @Override - public ByteBuf content() { - return content; - } - - @Override - public MemcacheContent copy() { - return replace(content.copy()); - } - - @Override - public MemcacheContent duplicate() { - return replace(content.duplicate()); - } - - @Override - public MemcacheContent retainedDuplicate() { - return replace(content.retainedDuplicate()); - } - - @Override - public MemcacheContent replace(ByteBuf content) { - return new DefaultMemcacheContent(content); - } - - @Override - public MemcacheContent retain() { - super.retain(); - return this; - } - - @Override - public MemcacheContent retain(int increment) { - super.retain(increment); - return this; - } - - @Override - public MemcacheContent touch() { - super.touch(); - return this; - } - - @Override - public MemcacheContent touch(Object hint) { - content.touch(hint); - return this; - } - - @Override - protected void deallocate() { - content.release(); - } - - @Override - public String toString() { - return StringUtil.simpleClassName(this) + - "(data: " + content() + ", decoderResult: " + decoderResult() + ')'; - } -} diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/FullMemcacheMessage.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/FullMemcacheMessage.java deleted file mode 100644 index b2fd8c5e58..0000000000 --- a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/FullMemcacheMessage.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.memcache; - -import io.netty.buffer.ByteBuf; -import io.netty.util.internal.UnstableApi; - -/** - * Combines {@link MemcacheMessage} and {@link LastMemcacheContent} into one - * message. So it represent a complete memcache message. - */ -@UnstableApi -public interface FullMemcacheMessage extends MemcacheMessage, LastMemcacheContent { - - @Override - FullMemcacheMessage copy(); - - @Override - FullMemcacheMessage duplicate(); - - @Override - FullMemcacheMessage retainedDuplicate(); - - @Override - FullMemcacheMessage replace(ByteBuf content); - - @Override - FullMemcacheMessage retain(int increment); - - @Override - FullMemcacheMessage retain(); - - @Override - FullMemcacheMessage touch(); - - @Override - FullMemcacheMessage touch(Object hint); -} diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/LastMemcacheContent.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/LastMemcacheContent.java deleted file mode 100644 index e89e5fd884..0000000000 --- a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/LastMemcacheContent.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.memcache; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.handler.codec.DecoderResult; -import io.netty.util.internal.UnstableApi; - -/** - * The {@link MemcacheContent} which signals the end of the content batch. - *

- * Note that by design, even when no content is emitted by the protocol, an - * empty {@link LastMemcacheContent} is issued to make the upstream parsing - * easier. - */ -@UnstableApi -public interface LastMemcacheContent extends MemcacheContent { - - LastMemcacheContent EMPTY_LAST_CONTENT = new LastMemcacheContent() { - - @Override - public LastMemcacheContent copy() { - return EMPTY_LAST_CONTENT; - } - - @Override - public LastMemcacheContent duplicate() { - return this; - } - - @Override - public LastMemcacheContent retainedDuplicate() { - return this; - } - - @Override - public LastMemcacheContent replace(ByteBuf content) { - return new DefaultLastMemcacheContent(content); - } - - @Override - public LastMemcacheContent retain(int increment) { - return this; - } - - @Override - public LastMemcacheContent retain() { - return this; - } - - @Override - public LastMemcacheContent touch() { - return this; - } - - @Override - public LastMemcacheContent touch(Object hint) { - return this; - } - - @Override - public ByteBuf content() { - return Unpooled.EMPTY_BUFFER; - } - - @Override - public DecoderResult decoderResult() { - return DecoderResult.SUCCESS; - } - - @Override - public void setDecoderResult(DecoderResult result) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public int refCnt() { - return 1; - } - - @Override - public boolean release() { - return false; - } - - @Override - public boolean release(int decrement) { - return false; - } - }; - - @Override - LastMemcacheContent copy(); - - @Override - LastMemcacheContent duplicate(); - - @Override - LastMemcacheContent retainedDuplicate(); - - @Override - LastMemcacheContent replace(ByteBuf content); - - @Override - LastMemcacheContent retain(int increment); - - @Override - LastMemcacheContent retain(); - - @Override - LastMemcacheContent touch(); - - @Override - LastMemcacheContent touch(Object hint); -} diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/MemcacheContent.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/MemcacheContent.java deleted file mode 100644 index 4339fb8eb7..0000000000 --- a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/MemcacheContent.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.memcache; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufHolder; -import io.netty.channel.ChannelPipeline; -import io.netty.util.internal.UnstableApi; - -/** - * An Memcache content chunk. - *

- * A implementation of a {@link AbstractMemcacheObjectDecoder} generates {@link MemcacheContent} after - * {@link MemcacheMessage} when the content is large. If you prefer not to receive {@link MemcacheContent} - * in your handler, place a aggregator after an implementation of the {@link AbstractMemcacheObjectDecoder} - * in the {@link ChannelPipeline}. - */ -@UnstableApi -public interface MemcacheContent extends MemcacheObject, ByteBufHolder { - - @Override - MemcacheContent copy(); - - @Override - MemcacheContent duplicate(); - - @Override - MemcacheContent retainedDuplicate(); - - @Override - MemcacheContent replace(ByteBuf content); - - @Override - MemcacheContent retain(); - - @Override - MemcacheContent retain(int increment); - - @Override - MemcacheContent touch(); - - @Override - MemcacheContent touch(Object hint); -} diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/MemcacheMessage.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/MemcacheMessage.java deleted file mode 100644 index e658cab81f..0000000000 --- a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/MemcacheMessage.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.memcache; - -import io.netty.util.ReferenceCounted; -import io.netty.util.internal.UnstableApi; - -/** - * Marker interface for both ascii and binary messages. - */ -@UnstableApi -public interface MemcacheMessage extends MemcacheObject, ReferenceCounted { - - /** - * Increases the reference count by {@code 1}. - */ - @Override - MemcacheMessage retain(); - - /** - * Increases the reference count by the specified {@code increment}. - */ - @Override - MemcacheMessage retain(int increment); - - @Override - MemcacheMessage touch(); - - @Override - MemcacheMessage touch(Object hint); -} diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/MemcacheObject.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/MemcacheObject.java deleted file mode 100644 index a7bbd94fe5..0000000000 --- a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/MemcacheObject.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.memcache; - -import io.netty.handler.codec.DecoderResultProvider; -import io.netty.util.internal.UnstableApi; - -/** - * Defines a common interface for all {@link MemcacheObject} implementations. - */ -@UnstableApi -public interface MemcacheObject extends DecoderResultProvider { } 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 deleted file mode 100644 index 1cd4962055..0000000000 --- a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/AbstractBinaryMemcacheDecoder.java +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.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; -import io.netty.handler.codec.DecoderResult; -import io.netty.handler.codec.memcache.AbstractMemcacheObjectDecoder; -import io.netty.handler.codec.memcache.DefaultLastMemcacheContent; -import io.netty.handler.codec.memcache.DefaultMemcacheContent; -import io.netty.handler.codec.memcache.LastMemcacheContent; -import io.netty.handler.codec.memcache.MemcacheContent; -import io.netty.util.internal.UnstableApi; - -/** - * Decoder for both {@link BinaryMemcacheRequest} and {@link BinaryMemcacheResponse}. - *

- * The difference in the protocols (header) is implemented by the subclasses. - */ -@UnstableApi -public abstract class AbstractBinaryMemcacheDecoder - extends AbstractMemcacheObjectDecoder { - - public static final int DEFAULT_MAX_CHUNK_SIZE = 8192; - - private final int chunkSize; - - private M currentMessage; - private int alreadyReadChunkSize; - - private State state = State.READ_HEADER; - - /** - * Create a new {@link AbstractBinaryMemcacheDecoder} with default settings. - */ - protected AbstractBinaryMemcacheDecoder() { - this(DEFAULT_MAX_CHUNK_SIZE); - } - - /** - * Create a new {@link AbstractBinaryMemcacheDecoder} with custom settings. - * - * @param chunkSize the maximum chunk size of the payload. - */ - protected AbstractBinaryMemcacheDecoder(int chunkSize) { - checkPositiveOrZero(chunkSize, "chunkSize"); - - this.chunkSize = chunkSize; - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - switch (state) { - case READ_HEADER: try { - if (in.readableBytes() < 24) { - return; - } - resetDecoder(); - - currentMessage = decodeHeader(in); - state = State.READ_EXTRAS; - } catch (Exception e) { - resetDecoder(); - ctx.fireChannelRead(invalidMessage(e)); - return; - } - case READ_EXTRAS: try { - byte extrasLength = currentMessage.extrasLength(); - if (extrasLength > 0) { - if (in.readableBytes() < extrasLength) { - return; - } - - currentMessage.setExtras(in.readRetainedSlice(extrasLength)); - } - - state = State.READ_KEY; - } catch (Exception e) { - resetDecoder(); - ctx.fireChannelRead(invalidMessage(e)); - return; - } - case READ_KEY: try { - short keyLength = currentMessage.keyLength(); - if (keyLength > 0) { - if (in.readableBytes() < keyLength) { - return; - } - - currentMessage.setKey(in.readRetainedSlice(keyLength)); - } - ctx.fireChannelRead(currentMessage.retain()); - state = State.READ_CONTENT; - } catch (Exception e) { - resetDecoder(); - ctx.fireChannelRead(invalidMessage(e)); - return; - } - case READ_CONTENT: try { - int valueLength = currentMessage.totalBodyLength() - - currentMessage.keyLength() - - currentMessage.extrasLength(); - int toRead = in.readableBytes(); - if (valueLength > 0) { - if (toRead == 0) { - return; - } - - if (toRead > chunkSize) { - toRead = chunkSize; - } - - int remainingLength = valueLength - alreadyReadChunkSize; - if (toRead > remainingLength) { - toRead = remainingLength; - } - - ByteBuf chunkBuffer = in.readRetainedSlice(toRead); - - MemcacheContent chunk; - if ((alreadyReadChunkSize += toRead) >= valueLength) { - chunk = new DefaultLastMemcacheContent(chunkBuffer); - } else { - chunk = new DefaultMemcacheContent(chunkBuffer); - } - - ctx.fireChannelRead(chunk); - if (alreadyReadChunkSize < valueLength) { - return; - } - } else { - ctx.fireChannelRead(LastMemcacheContent.EMPTY_LAST_CONTENT); - } - - resetDecoder(); - state = State.READ_HEADER; - return; - } catch (Exception e) { - resetDecoder(); - ctx.fireChannelRead(invalidChunk(e)); - return; - } - case BAD_MESSAGE: - in.skipBytes(actualReadableBytes()); - return; - default: - throw new Error("Unknown state reached: " + state); - } - } - - /** - * Helper method to create a message indicating a invalid decoding result. - * - * @param cause the cause of the decoding failure. - * @return a valid message indicating failure. - */ - private M invalidMessage(Exception cause) { - state = State.BAD_MESSAGE; - M message = buildInvalidMessage(); - message.setDecoderResult(DecoderResult.failure(cause)); - return message; - } - - /** - * Helper method to create a content chunk indicating a invalid decoding result. - * - * @param cause the cause of the decoding failure. - * @return a valid content chunk indicating failure. - */ - private MemcacheContent invalidChunk(Exception cause) { - state = State.BAD_MESSAGE; - MemcacheContent chunk = new DefaultLastMemcacheContent(Unpooled.EMPTY_BUFFER); - chunk.setDecoderResult(DecoderResult.failure(cause)); - return chunk; - } - - /** - * When the channel goes inactive, release all frames to prevent data leaks. - * - * @param ctx handler context - */ - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - super.channelInactive(ctx); - - resetDecoder(); - } - - /** - * Prepare for next decoding iteration. - */ - protected void resetDecoder() { - if (currentMessage != null) { - currentMessage.release(); - currentMessage = null; - } - alreadyReadChunkSize = 0; - } - - /** - * Decode and return the parsed {@link BinaryMemcacheMessage}. - * - * @param in the incoming buffer. - * @return the decoded header. - */ - protected abstract M decodeHeader(ByteBuf in); - - /** - * Helper method to create a upstream message when the incoming parsing did fail. - * - * @return a message indicating a decoding failure. - */ - protected abstract M buildInvalidMessage(); - - /** - * Contains all states this decoder can possibly be in. - *

- * Note that most of the states can be optional, the only one required is reading - * the header ({@link #READ_HEADER}. All other steps depend on the length fields - * in the header and will be executed conditionally. - */ - enum State { - /** - * Currently reading the header portion. - */ - READ_HEADER, - - /** - * Currently reading the extras portion (optional). - */ - READ_EXTRAS, - - /** - * Currently reading the key portion (optional). - */ - READ_KEY, - - /** - * Currently reading the value chunks (optional). - */ - READ_CONTENT, - - /** - * Something went wrong while decoding the message or chunks. - */ - BAD_MESSAGE - } - -} diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/AbstractBinaryMemcacheEncoder.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/AbstractBinaryMemcacheEncoder.java deleted file mode 100644 index b8b5fac29a..0000000000 --- a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/AbstractBinaryMemcacheEncoder.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.memcache.binary; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToByteEncoder; -import io.netty.handler.codec.memcache.AbstractMemcacheObjectEncoder; -import io.netty.util.internal.UnstableApi; - -/** - * A {@link MessageToByteEncoder} that encodes binary memcache messages into bytes. - */ -@UnstableApi -public abstract class AbstractBinaryMemcacheEncoder - extends AbstractMemcacheObjectEncoder { - - /** - * Every binary memcache message has at least a 24 bytes header. - */ - private static final int MINIMUM_HEADER_SIZE = 24; - - @Override - protected ByteBuf encodeMessage(ChannelHandlerContext ctx, M msg) { - ByteBuf buf = ctx.alloc().buffer(MINIMUM_HEADER_SIZE + msg.extrasLength() - + msg.keyLength()); - - encodeHeader(buf, msg); - encodeExtras(buf, msg.extras()); - encodeKey(buf, msg.key()); - - return buf; - } - - /** - * Encode the extras. - * - * @param buf the {@link ByteBuf} to write into. - * @param extras the extras to encode. - */ - private static void encodeExtras(ByteBuf buf, ByteBuf extras) { - if (extras == null || !extras.isReadable()) { - return; - } - - buf.writeBytes(extras); - } - - /** - * Encode the key. - * - * @param buf the {@link ByteBuf} to write into. - * @param key the key to encode. - */ - private static void encodeKey(ByteBuf buf, ByteBuf key) { - if (key == null || !key.isReadable()) { - return; - } - - buf.writeBytes(key); - } - - /** - * Encode the header. - *

- * This methods needs to be implemented by a sub class because the header is different - * for both requests and responses. - * - * @param buf the {@link ByteBuf} to write into. - * @param msg the message to encode. - */ - protected abstract void encodeHeader(ByteBuf buf, M msg); - -} diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/AbstractBinaryMemcacheMessage.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/AbstractBinaryMemcacheMessage.java deleted file mode 100644 index cb5fdd0c75..0000000000 --- a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/AbstractBinaryMemcacheMessage.java +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.memcache.binary; - -import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.memcache.AbstractMemcacheObject; -import io.netty.util.internal.UnstableApi; - -/** - * Default implementation of a {@link BinaryMemcacheMessage}. - */ -@UnstableApi -public abstract class AbstractBinaryMemcacheMessage - extends AbstractMemcacheObject - implements BinaryMemcacheMessage { - - /** - * Contains the optional key. - */ - private ByteBuf key; - - /** - * Contains the optional extras. - */ - private ByteBuf extras; - - private byte magic; - private byte opcode; - private short keyLength; - private byte extrasLength; - private byte dataType; - private int totalBodyLength; - private int opaque; - private long cas; - - /** - * Create a new instance with all properties set. - * - * @param key the message key. - * @param extras the message extras. - */ - protected AbstractBinaryMemcacheMessage(ByteBuf key, ByteBuf extras) { - this.key = key; - keyLength = key == null ? 0 : (short) key.readableBytes(); - this.extras = extras; - extrasLength = extras == null ? 0 : (byte) extras.readableBytes(); - totalBodyLength = keyLength + extrasLength; - } - - @Override - public ByteBuf key() { - return key; - } - - @Override - public ByteBuf extras() { - return extras; - } - - @Override - public BinaryMemcacheMessage setKey(ByteBuf key) { - if (this.key != null) { - this.key.release(); - } - this.key = key; - short oldKeyLength = keyLength; - keyLength = key == null ? 0 : (short) key.readableBytes(); - totalBodyLength = totalBodyLength + keyLength - oldKeyLength; - return this; - } - - @Override - public BinaryMemcacheMessage setExtras(ByteBuf extras) { - if (this.extras != null) { - this.extras.release(); - } - this.extras = extras; - short oldExtrasLength = extrasLength; - extrasLength = extras == null ? 0 : (byte) extras.readableBytes(); - totalBodyLength = totalBodyLength + extrasLength - oldExtrasLength; - return this; - } - - @Override - public byte magic() { - return magic; - } - - @Override - public BinaryMemcacheMessage setMagic(byte magic) { - this.magic = magic; - return this; - } - - @Override - public long cas() { - return cas; - } - - @Override - public BinaryMemcacheMessage setCas(long cas) { - this.cas = cas; - return this; - } - - @Override - public int opaque() { - return opaque; - } - - @Override - public BinaryMemcacheMessage setOpaque(int opaque) { - this.opaque = opaque; - return this; - } - - @Override - public int totalBodyLength() { - return totalBodyLength; - } - - @Override - public BinaryMemcacheMessage setTotalBodyLength(int totalBodyLength) { - this.totalBodyLength = totalBodyLength; - return this; - } - - @Override - public byte dataType() { - return dataType; - } - - @Override - public BinaryMemcacheMessage setDataType(byte dataType) { - this.dataType = dataType; - return this; - } - - @Override - public byte extrasLength() { - return extrasLength; - } - - /** - * Set the extras length of the message. - *

- * This may be 0, since the extras content is optional. - * - * @param extrasLength the extras length. - */ - BinaryMemcacheMessage setExtrasLength(byte extrasLength) { - this.extrasLength = extrasLength; - return this; - } - - @Override - public short keyLength() { - return keyLength; - } - - /** - * Set the key length of the message. - *

- * This may be 0, since the key is optional. - * - * @param keyLength the key length to use. - */ - BinaryMemcacheMessage setKeyLength(short keyLength) { - this.keyLength = keyLength; - return this; - } - - @Override - public byte opcode() { - return opcode; - } - - @Override - public BinaryMemcacheMessage setOpcode(byte opcode) { - this.opcode = opcode; - return this; - } - - @Override - public BinaryMemcacheMessage retain() { - super.retain(); - return this; - } - - @Override - public BinaryMemcacheMessage retain(int increment) { - super.retain(increment); - return this; - } - - @Override - protected void deallocate() { - if (key != null) { - key.release(); - } - if (extras != null) { - extras.release(); - } - } - - @Override - public BinaryMemcacheMessage touch() { - super.touch(); - return this; - } - - @Override - public BinaryMemcacheMessage touch(Object hint) { - if (key != null) { - key.touch(hint); - } - if (extras != null) { - extras.touch(hint); - } - return this; - } - - /** - * Copies special metadata hold by this instance to the provided instance - * - * @param dst The instance where to copy the metadata of this instance to - */ - void copyMeta(AbstractBinaryMemcacheMessage dst) { - dst.magic = magic; - dst.opcode = opcode; - dst.keyLength = keyLength; - dst.extrasLength = extrasLength; - dst.dataType = dataType; - dst.totalBodyLength = totalBodyLength; - dst.opaque = opaque; - dst.cas = cas; - } -} diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheClientCodec.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheClientCodec.java deleted file mode 100644 index 7c9d82b8b3..0000000000 --- a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheClientCodec.java +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.memcache.binary; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.api.BufferAllocator; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.CombinedChannelDuplexHandler; -import io.netty.handler.codec.PrematureChannelClosureException; -import io.netty.handler.codec.memcache.LastMemcacheContent; -import io.netty.util.Attribute; -import io.netty.util.AttributeKey; -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; -import io.netty.util.internal.UnstableApi; - -import java.net.SocketAddress; -import java.util.List; -import java.util.concurrent.atomic.AtomicLong; - -/** - * The client codec that combines the proper encoder and decoder. - *

- * Use this codec if you want to implement a memcache client that speaks the binary protocol. It - * combines both the {@link BinaryMemcacheResponseDecoder} and the {@link BinaryMemcacheRequestEncoder}. - *

- * Optionally, it counts the number of outstanding responses and raises an exception if - on connection - * close - the list is not 0 (this is turned off by default). You can also define a chunk size for the - * content, which defaults to 8192. This chunk size is the maximum, so if smaller chunks arrive they - * will be passed up the pipeline and not queued up to the chunk size. - */ -@UnstableApi -public final class BinaryMemcacheClientCodec extends - CombinedChannelDuplexHandler { - - private final boolean failOnMissingResponse; - private final AtomicLong requestResponseCounter = new AtomicLong(); - - /** - * Create a new {@link BinaryMemcacheClientCodec} with the default settings applied. - */ - public BinaryMemcacheClientCodec() { - this(AbstractBinaryMemcacheDecoder.DEFAULT_MAX_CHUNK_SIZE); - } - - /** - * Create a new {@link BinaryMemcacheClientCodec} and set a custom chunk size. - * - * @param decodeChunkSize the maximum chunk size. - */ - public BinaryMemcacheClientCodec(int decodeChunkSize) { - this(decodeChunkSize, false); - } - - /** - * Create a new {@link BinaryMemcacheClientCodec} with custom settings. - * - * @param decodeChunkSize the maximum chunk size. - * @param failOnMissingResponse report if after close there are outstanding requests. - */ - public BinaryMemcacheClientCodec(int decodeChunkSize, boolean failOnMissingResponse) { - this.failOnMissingResponse = failOnMissingResponse; - init(new Decoder(decodeChunkSize), new Encoder()); - } - - private final class Encoder extends BinaryMemcacheRequestEncoder { - - @Override - protected void encode(ChannelHandlerContext ctx, Object msg, List out) throws Exception { - super.encode(ctx, msg, out); - - if (failOnMissingResponse && msg instanceof LastMemcacheContent) { - requestResponseCounter.incrementAndGet(); - } - } - } - - private final class Decoder extends BinaryMemcacheResponseDecoder { - - private ChannelHandlerContext context; - - Decoder(int chunkSize) { - super(chunkSize); - } - - @Override - protected void handlerAdded0(final ChannelHandlerContext ctx) { - context = new ChannelHandlerContext() { - @Override - public Channel channel() { - return ctx.channel(); - } - - @Override - public EventExecutor executor() { - return ctx.executor(); - } - - @Override - public String name() { - return ctx.name(); - } - - @Override - public ChannelHandler handler() { - return ctx.handler(); - } - - @Override - public boolean isRemoved() { - return ctx.isRemoved(); - } - - @Override - public ChannelHandlerContext fireChannelRegistered() { - ctx.fireChannelRegistered(); - return this; - } - - @Override - public ChannelHandlerContext fireChannelUnregistered() { - ctx.fireChannelUnregistered(); - return this; - } - - @Override - public ChannelHandlerContext fireChannelActive() { - ctx.fireChannelActive(); - return this; - } - - @Override - public ChannelHandlerContext fireChannelInactive() { - ctx.fireChannelInactive(); - return this; - } - - @Override - public ChannelHandlerContext fireExceptionCaught(Throwable cause) { - ctx.fireExceptionCaught(cause); - return this; - } - - @Override - public ChannelHandlerContext fireUserEventTriggered(Object evt) { - ctx.fireUserEventTriggered(evt); - return this; - } - - @Override - public ChannelHandlerContext fireChannelRead(Object msg) { - if (failOnMissingResponse && msg instanceof LastMemcacheContent) { - requestResponseCounter.decrementAndGet(); - } - ctx.fireChannelRead(msg); - return this; - } - - @Override - public ChannelHandlerContext fireChannelReadComplete() { - ctx.fireChannelReadComplete(); - return this; - } - - @Override - public ChannelHandlerContext fireChannelWritabilityChanged() { - ctx.fireChannelWritabilityChanged(); - return this; - } - - @Override - public ChannelHandlerContext read() { - ctx.read(); - return this; - } - - @Override - public ChannelHandlerContext flush() { - ctx.flush(); - return this; - } - - @Override - public ChannelPipeline pipeline() { - return ctx.pipeline(); - } - - @Override - public ByteBufAllocator alloc() { - return ctx.alloc(); - } - - @Override - public BufferAllocator bufferAllocator() { - return ctx.bufferAllocator(); - } - - @Override - @Deprecated - public Attribute attr(AttributeKey key) { - return ctx.attr(key); - } - - @Override - @Deprecated - public boolean hasAttr(AttributeKey key) { - return ctx.hasAttr(key); - } - - @Override - public Future bind(SocketAddress localAddress) { - return ctx.bind(localAddress); - } - - @Override - public Future connect(SocketAddress remoteAddress) { - return ctx.connect(remoteAddress); - } - - @Override - public Future connect(SocketAddress remoteAddress, SocketAddress localAddress) { - return ctx.connect(remoteAddress, localAddress); - } - - @Override - public Future disconnect() { - return ctx.disconnect(); - } - - @Override - public Future close() { - return ctx.close(); - } - - @Override - public Future deregister() { - return ctx.deregister(); - } - - @Override - public Future register() { - return ctx.register(); - } - - @Override - public Future write(Object msg) { - return ctx.write(msg); - } - - @Override - public Future writeAndFlush(Object msg) { - return ctx.writeAndFlush(msg); - } - - @Override - public Promise newPromise() { - return ctx.newPromise(); - } - - @Override - public Future newSucceededFuture() { - return ctx.newSucceededFuture(); - } - - @Override - public Future newFailedFuture(Throwable cause) { - return ctx.newFailedFuture(cause); - } - }; - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - super.decode(context, in); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - super.channelInactive(ctx); - - if (failOnMissingResponse) { - long missingResponses = requestResponseCounter.get(); - if (missingResponses > 0) { - ctx.fireExceptionCaught(new PrematureChannelClosureException( - "channel gone inactive with " + missingResponses + - " missing response(s)")); - } - } - } - } -} diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheMessage.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheMessage.java deleted file mode 100644 index 3682b11873..0000000000 --- a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheMessage.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.memcache.binary; - -import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.memcache.MemcacheMessage; -import io.netty.util.internal.UnstableApi; - -/** - * An interface that defines a binary Memcache message, providing common properties for - * {@link BinaryMemcacheRequest} and {@link BinaryMemcacheResponse}. - *

- * A {@link BinaryMemcacheMessage} always consists of a header and optional extras or/and - * a key. - * - * @see BinaryMemcacheRequest - * @see BinaryMemcacheResponse - */ -@UnstableApi -public interface BinaryMemcacheMessage extends MemcacheMessage { - - /** - * Returns the magic byte for the message. - * - * @return the magic byte. - */ - byte magic(); - - /** - * Sets the magic byte. - * - * @param magic the magic byte to use. - * @see BinaryMemcacheOpcodes for typesafe opcodes. - */ - BinaryMemcacheMessage setMagic(byte magic); - - /** - * Returns the opcode for the message. - * - * @return the opcode. - */ - byte opcode(); - - /** - * Sets the opcode for the message. - * - * @param code the opcode to use. - */ - BinaryMemcacheMessage setOpcode(byte code); - - /** - * Returns the key length of the message. - *

- * This may return 0, since the key is optional. - * - * @return the key length. - */ - short keyLength(); - - /** - * Return the extras length of the message. - *

- * This may be 0, since the extras content is optional. - * - * @return the extras length. - */ - byte extrasLength(); - - /** - * Returns the data type of the message. - * - * @return the data type of the message. - */ - byte dataType(); - - /** - * Sets the data type of the message. - * - * @param dataType the data type of the message. - */ - BinaryMemcacheMessage setDataType(byte dataType); - - /** - * Returns the total body length. - *

- * Note that this may be 0, since the body is optional. - * - * @return the total body length. - */ - int totalBodyLength(); - - /** - * Sets the total body length. - *

- * Note that this may be 0, since the body length is optional. - * - * @param totalBodyLength the total body length. - */ - BinaryMemcacheMessage setTotalBodyLength(int totalBodyLength); - - /** - * Returns the opaque value. - * - * @return the opaque value. - */ - int opaque(); - - /** - * Sets the opaque value. - * - * @param opaque the opaque value to use. - */ - BinaryMemcacheMessage setOpaque(int opaque); - - /** - * Returns the CAS identifier. - * - * @return the CAS identifier. - */ - long cas(); - - /** - * Sets the CAS identifier. - * - * @param cas the CAS identifier to use. - */ - BinaryMemcacheMessage setCas(long cas); - - /** - * Returns the optional key of the document. - * - * @return the key of the document. - */ - ByteBuf key(); - - /** - * Sets the key of the document. {@link ByteBuf#release()} ownership of {@code key} - * is transferred to this {@link BinaryMemcacheMessage}. - * - * @param key the key of the message. {@link ByteBuf#release()} ownership is transferred - * to this {@link BinaryMemcacheMessage}. - */ - BinaryMemcacheMessage setKey(ByteBuf key); - - /** - * Returns a {@link ByteBuf} representation of the optional extras. - * - * @return the optional extras. - */ - ByteBuf extras(); - - /** - * Sets the extras buffer on the message. {@link ByteBuf#release()} ownership of {@code extras} - * is transferred to this {@link BinaryMemcacheMessage}. - * - * @param extras the extras buffer of the document. {@link ByteBuf#release()} ownership is transferred - * to this {@link BinaryMemcacheMessage}. - */ - BinaryMemcacheMessage setExtras(ByteBuf extras); - - /** - * Increases the reference count by {@code 1}. - */ - @Override - BinaryMemcacheMessage retain(); - - /** - * Increases the reference count by the specified {@code increment}. - */ - @Override - BinaryMemcacheMessage retain(int increment); - - @Override - BinaryMemcacheMessage touch(); - - @Override - BinaryMemcacheMessage touch(Object hint); -} diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheObjectAggregator.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheObjectAggregator.java deleted file mode 100644 index 2d105617e1..0000000000 --- a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheObjectAggregator.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.memcache.binary; - -import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.memcache.AbstractMemcacheObjectAggregator; -import io.netty.handler.codec.memcache.FullMemcacheMessage; -import io.netty.handler.codec.memcache.MemcacheContent; -import io.netty.handler.codec.memcache.MemcacheObject; -import io.netty.util.internal.UnstableApi; - -/** - * An object aggregator for the memcache binary protocol. - * - * It aggregates {@link BinaryMemcacheMessage}s and {@link MemcacheContent} into {@link FullBinaryMemcacheRequest}s - * or {@link FullBinaryMemcacheResponse}s. - */ -@UnstableApi -public class BinaryMemcacheObjectAggregator extends AbstractMemcacheObjectAggregator { - - public BinaryMemcacheObjectAggregator(int maxContentLength) { - super(maxContentLength); - } - - @Override - protected boolean isStartMessage(MemcacheObject msg) throws Exception { - return msg instanceof BinaryMemcacheMessage; - } - - @Override - protected FullMemcacheMessage beginAggregation(BinaryMemcacheMessage start, ByteBuf content) throws Exception { - if (start instanceof BinaryMemcacheRequest) { - return toFullRequest((BinaryMemcacheRequest) start, content); - } - - if (start instanceof BinaryMemcacheResponse) { - return toFullResponse((BinaryMemcacheResponse) start, content); - } - - // Should not reach here. - throw new Error(); - } - - private static FullBinaryMemcacheRequest toFullRequest(BinaryMemcacheRequest request, ByteBuf content) { - ByteBuf key = request.key() == null ? null : request.key().retain(); - ByteBuf extras = request.extras() == null ? null : request.extras().retain(); - DefaultFullBinaryMemcacheRequest fullRequest = - new DefaultFullBinaryMemcacheRequest(key, extras, content); - - fullRequest.setMagic(request.magic()); - fullRequest.setOpcode(request.opcode()); - fullRequest.setKeyLength(request.keyLength()); - fullRequest.setExtrasLength(request.extrasLength()); - fullRequest.setDataType(request.dataType()); - fullRequest.setTotalBodyLength(request.totalBodyLength()); - fullRequest.setOpaque(request.opaque()); - fullRequest.setCas(request.cas()); - fullRequest.setReserved(request.reserved()); - - return fullRequest; - } - - private static FullBinaryMemcacheResponse toFullResponse(BinaryMemcacheResponse response, ByteBuf content) { - ByteBuf key = response.key() == null ? null : response.key().retain(); - ByteBuf extras = response.extras() == null ? null : response.extras().retain(); - DefaultFullBinaryMemcacheResponse fullResponse = - new DefaultFullBinaryMemcacheResponse(key, extras, content); - - fullResponse.setMagic(response.magic()); - fullResponse.setOpcode(response.opcode()); - fullResponse.setKeyLength(response.keyLength()); - fullResponse.setExtrasLength(response.extrasLength()); - fullResponse.setDataType(response.dataType()); - fullResponse.setTotalBodyLength(response.totalBodyLength()); - fullResponse.setOpaque(response.opaque()); - fullResponse.setCas(response.cas()); - fullResponse.setStatus(response.status()); - - return fullResponse; - } -} diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheOpcodes.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheOpcodes.java deleted file mode 100644 index d94bbaa6c6..0000000000 --- a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheOpcodes.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.memcache.binary; - -import io.netty.util.internal.UnstableApi; - -/** - * Represents all Opcodes that can occur in a {@link BinaryMemcacheMessage}. - *

- * This class can be extended if a custom application needs to implement a superset of the normally supported - * operations by a vanilla memcached protocol. - */ -@UnstableApi -public final class BinaryMemcacheOpcodes { - - private BinaryMemcacheOpcodes() { - // disallow construction - } - - public static final byte GET = 0x00; - public static final byte SET = 0x01; - public static final byte ADD = 0x02; - public static final byte REPLACE = 0x03; - public static final byte DELETE = 0x04; - public static final byte INCREMENT = 0x05; - public static final byte DECREMENT = 0x06; - public static final byte QUIT = 0x07; - public static final byte FLUSH = 0x08; - public static final byte GETQ = 0x09; - public static final byte NOOP = 0x0a; - public static final byte VERSION = 0x0b; - public static final byte GETK = 0x0c; - public static final byte GETKQ = 0x0d; - public static final byte APPEND = 0x0e; - public static final byte PREPEND = 0x0f; - public static final byte STAT = 0x10; - public static final byte SETQ = 0x11; - public static final byte ADDQ = 0x12; - public static final byte REPLACEQ = 0x13; - public static final byte DELETEQ = 0x14; - public static final byte INCREMENTQ = 0x15; - public static final byte DECREMENTQ = 0x16; - public static final byte QUITQ = 0x17; - public static final byte FLUSHQ = 0x18; - public static final byte APPENDQ = 0x19; - public static final byte PREPENDQ = 0x1a; - public static final byte TOUCH = 0x1c; - public static final byte GAT = 0x1d; - public static final byte GATQ = 0x1e; - public static final byte GATK = 0x23; - public static final byte GATKQ = 0x24; - public static final byte SASL_LIST_MECHS = 0x20; - public static final byte SASL_AUTH = 0x21; - public static final byte SASL_STEP = 0x22; -} diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheRequest.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheRequest.java deleted file mode 100644 index 8485e3eb79..0000000000 --- a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheRequest.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.memcache.binary; - -import io.netty.util.internal.UnstableApi; - -/** - * Represents a full {@link BinaryMemcacheRequest}, which contains the header and optional key and extras. - */ -@UnstableApi -public interface BinaryMemcacheRequest extends BinaryMemcacheMessage { - - /** - * Returns the reserved field value. - * - * @return the reserved field value. - */ - short reserved(); - - /** - * Sets the reserved field value. - * - * @param reserved the reserved field value. - */ - BinaryMemcacheRequest setReserved(short reserved); - - @Override - BinaryMemcacheRequest retain(); - - @Override - BinaryMemcacheRequest retain(int increment); - - @Override - BinaryMemcacheRequest touch(); - - @Override - BinaryMemcacheRequest touch(Object hint); -} diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheRequestDecoder.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheRequestDecoder.java deleted file mode 100644 index ba6ef91683..0000000000 --- a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheRequestDecoder.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.memcache.binary; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.internal.UnstableApi; - -/** - * The decoder part which takes care of decoding the request-specific headers. - */ -@UnstableApi -public class BinaryMemcacheRequestDecoder - extends AbstractBinaryMemcacheDecoder { - - public BinaryMemcacheRequestDecoder() { - this(DEFAULT_MAX_CHUNK_SIZE); - } - - public BinaryMemcacheRequestDecoder(int chunkSize) { - super(chunkSize); - } - - @Override - protected BinaryMemcacheRequest decodeHeader(ByteBuf in) { - DefaultBinaryMemcacheRequest header = new DefaultBinaryMemcacheRequest(); - header.setMagic(in.readByte()); - header.setOpcode(in.readByte()); - header.setKeyLength(in.readShort()); - header.setExtrasLength(in.readByte()); - header.setDataType(in.readByte()); - header.setReserved(in.readShort()); - header.setTotalBodyLength(in.readInt()); - header.setOpaque(in.readInt()); - header.setCas(in.readLong()); - return header; - } - - @Override - protected BinaryMemcacheRequest buildInvalidMessage() { - return new DefaultBinaryMemcacheRequest(Unpooled.EMPTY_BUFFER, Unpooled.EMPTY_BUFFER); - } -} diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheRequestEncoder.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheRequestEncoder.java deleted file mode 100644 index 0d92091b43..0000000000 --- a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheRequestEncoder.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.memcache.binary; - -import io.netty.buffer.ByteBuf; -import io.netty.util.internal.UnstableApi; - -/** - * The encoder part which takes care of encoding the request headers. - */ -@UnstableApi -public class BinaryMemcacheRequestEncoder - extends AbstractBinaryMemcacheEncoder { - - @Override - protected void encodeHeader(ByteBuf buf, BinaryMemcacheRequest msg) { - buf.writeByte(msg.magic()); - buf.writeByte(msg.opcode()); - buf.writeShort(msg.keyLength()); - buf.writeByte(msg.extrasLength()); - buf.writeByte(msg.dataType()); - buf.writeShort(msg.reserved()); - buf.writeInt(msg.totalBodyLength()); - buf.writeInt(msg.opaque()); - buf.writeLong(msg.cas()); - } - -} diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheResponse.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheResponse.java deleted file mode 100644 index 5d50861b93..0000000000 --- a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheResponse.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.memcache.binary; - -import io.netty.util.internal.UnstableApi; - -/** - * Represents a full {@link BinaryMemcacheResponse}, which contains the header and optional key and extras. - */ -@UnstableApi -public interface BinaryMemcacheResponse extends BinaryMemcacheMessage { - - /** - * Returns the status of the response. - * - * @return the status of the response. - */ - short status(); - - /** - * Sets the status of the response. - * - * @param status the status to set. - */ - BinaryMemcacheResponse setStatus(short status); - - @Override - BinaryMemcacheResponse retain(); - - @Override - BinaryMemcacheResponse retain(int increment); - - @Override - BinaryMemcacheResponse touch(); - - @Override - BinaryMemcacheResponse touch(Object hint); -} diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheResponseDecoder.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheResponseDecoder.java deleted file mode 100644 index 392b6852b1..0000000000 --- a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheResponseDecoder.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.memcache.binary; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.internal.UnstableApi; - -/** - * The decoder which takes care of decoding the response headers. - */ -@UnstableApi -public class BinaryMemcacheResponseDecoder - extends AbstractBinaryMemcacheDecoder { - - public BinaryMemcacheResponseDecoder() { - this(DEFAULT_MAX_CHUNK_SIZE); - } - - public BinaryMemcacheResponseDecoder(int chunkSize) { - super(chunkSize); - } - - @Override - protected BinaryMemcacheResponse decodeHeader(ByteBuf in) { - DefaultBinaryMemcacheResponse header = new DefaultBinaryMemcacheResponse(); - header.setMagic(in.readByte()); - header.setOpcode(in.readByte()); - header.setKeyLength(in.readShort()); - header.setExtrasLength(in.readByte()); - header.setDataType(in.readByte()); - header.setStatus(in.readShort()); - header.setTotalBodyLength(in.readInt()); - header.setOpaque(in.readInt()); - header.setCas(in.readLong()); - return header; - } - - @Override - protected BinaryMemcacheResponse buildInvalidMessage() { - return new DefaultBinaryMemcacheResponse(Unpooled.EMPTY_BUFFER, Unpooled.EMPTY_BUFFER); - } -} diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheResponseEncoder.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheResponseEncoder.java deleted file mode 100644 index 639db83555..0000000000 --- a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheResponseEncoder.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.memcache.binary; - -import io.netty.buffer.ByteBuf; -import io.netty.util.internal.UnstableApi; - -/** - * The encoder which takes care of encoding the response headers. - */ -@UnstableApi -public class BinaryMemcacheResponseEncoder - extends AbstractBinaryMemcacheEncoder { - - @Override - protected void encodeHeader(ByteBuf buf, BinaryMemcacheResponse msg) { - buf.writeByte(msg.magic()); - buf.writeByte(msg.opcode()); - buf.writeShort(msg.keyLength()); - buf.writeByte(msg.extrasLength()); - buf.writeByte(msg.dataType()); - buf.writeShort(msg.status()); - buf.writeInt(msg.totalBodyLength()); - buf.writeInt(msg.opaque()); - buf.writeLong(msg.cas()); - } - -} diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheResponseStatus.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheResponseStatus.java deleted file mode 100644 index 6af9c8c363..0000000000 --- a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheResponseStatus.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.memcache.binary; - -import io.netty.util.internal.UnstableApi; - -/** - * Contains all possible status values a {@link BinaryMemcacheResponse} can return. - */ -@UnstableApi -public final class BinaryMemcacheResponseStatus { - - private BinaryMemcacheResponseStatus() { - // disallow construction - } - - public static final short SUCCESS = 0x00; - public static final short KEY_ENOENT = 0x01; - public static final short KEY_EEXISTS = 0x02; - public static final short E2BIG = 0x03; - public static final short EINVA = 0x04; - public static final short NOT_STORED = 0x05; - public static final short DELTA_BADVAL = 0x06; - public static final short AUTH_ERROR = 0x20; - public static final short AUTH_CONTINUE = 0x21; - public static final short UNKNOWN_COMMAND = 0x81; - public static final short ENOMEM = 0x82; -} diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheServerCodec.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheServerCodec.java deleted file mode 100644 index 9a766f161f..0000000000 --- a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheServerCodec.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.memcache.binary; - -import io.netty.channel.CombinedChannelDuplexHandler; -import io.netty.util.internal.UnstableApi; - -/** - * The full server codec that combines the correct encoder and decoder. - *

- * Use this codec if you need to implement a server that speaks the memcache binary protocol. - * Internally, it combines the {@link BinaryMemcacheRequestDecoder} and the - * {@link BinaryMemcacheResponseEncoder} to request decoding and response encoding. - */ -@UnstableApi -public class BinaryMemcacheServerCodec extends - CombinedChannelDuplexHandler { - - public BinaryMemcacheServerCodec() { - this(AbstractBinaryMemcacheDecoder.DEFAULT_MAX_CHUNK_SIZE); - } - - public BinaryMemcacheServerCodec(int decodeChunkSize) { - super(new BinaryMemcacheRequestDecoder(decodeChunkSize), new BinaryMemcacheResponseEncoder()); - } -} diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/DefaultBinaryMemcacheRequest.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/DefaultBinaryMemcacheRequest.java deleted file mode 100644 index a4bd3359cc..0000000000 --- a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/DefaultBinaryMemcacheRequest.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.memcache.binary; - -import io.netty.buffer.ByteBuf; -import io.netty.util.internal.UnstableApi; - -/** - * The default implementation of the {@link BinaryMemcacheRequest}. - */ -@UnstableApi -public class DefaultBinaryMemcacheRequest extends AbstractBinaryMemcacheMessage implements BinaryMemcacheRequest { - - /** - * Default magic byte for a request. - */ - public static final byte REQUEST_MAGIC_BYTE = (byte) 0x80; - - private short reserved; - - /** - * Create a new {@link DefaultBinaryMemcacheRequest} with the header only. - */ - public DefaultBinaryMemcacheRequest() { - this(null, null); - } - - /** - * Create a new {@link DefaultBinaryMemcacheRequest} with the header and key. - * - * @param key the key to use. - */ - public DefaultBinaryMemcacheRequest(ByteBuf key) { - this(key, null); - } - - /** - * Create a new {@link DefaultBinaryMemcacheRequest} with the header only. - * - * @param key the key to use. - * @param extras the extras to use. - */ - public DefaultBinaryMemcacheRequest(ByteBuf key, ByteBuf extras) { - super(key, extras); - setMagic(REQUEST_MAGIC_BYTE); - } - - @Override - public short reserved() { - return reserved; - } - - @Override - public BinaryMemcacheRequest setReserved(short reserved) { - this.reserved = reserved; - return this; - } - - @Override - public BinaryMemcacheRequest retain() { - super.retain(); - return this; - } - - @Override - public BinaryMemcacheRequest retain(int increment) { - super.retain(increment); - return this; - } - - @Override - public BinaryMemcacheRequest touch() { - super.touch(); - return this; - } - - @Override - public BinaryMemcacheRequest touch(Object hint) { - super.touch(hint); - return this; - } - - /** - * Copies special metadata hold by this instance to the provided instance - * - * @param dst The instance where to copy the metadata of this instance to - */ - void copyMeta(DefaultBinaryMemcacheRequest dst) { - super.copyMeta(dst); - dst.reserved = reserved; - } -} diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/DefaultBinaryMemcacheResponse.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/DefaultBinaryMemcacheResponse.java deleted file mode 100644 index c6c8876023..0000000000 --- a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/DefaultBinaryMemcacheResponse.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.memcache.binary; - -import io.netty.buffer.ByteBuf; -import io.netty.util.internal.UnstableApi; - -/** - * The default implementation of the {@link BinaryMemcacheResponse}. - */ -@UnstableApi -public class DefaultBinaryMemcacheResponse extends AbstractBinaryMemcacheMessage implements BinaryMemcacheResponse { - - /** - * Default magic byte for a request. - */ - public static final byte RESPONSE_MAGIC_BYTE = (byte) 0x81; - - private short status; - - /** - * Create a new {@link DefaultBinaryMemcacheResponse} with the header only. - */ - public DefaultBinaryMemcacheResponse() { - this(null, null); - } - - /** - * Create a new {@link DefaultBinaryMemcacheResponse} with the header and key. - * - * @param key the key to use. - */ - public DefaultBinaryMemcacheResponse(ByteBuf key) { - this(key, null); - } - - /** - * Create a new {@link DefaultBinaryMemcacheResponse} with the header, key and extras. - * - * @param key the key to use. - * @param extras the extras to use. - */ - public DefaultBinaryMemcacheResponse(ByteBuf key, ByteBuf extras) { - super(key, extras); - setMagic(RESPONSE_MAGIC_BYTE); - } - - @Override - public short status() { - return status; - } - - @Override - public BinaryMemcacheResponse setStatus(short status) { - this.status = status; - return this; - } - - @Override - public BinaryMemcacheResponse retain() { - super.retain(); - return this; - } - - @Override - public BinaryMemcacheResponse retain(int increment) { - super.retain(increment); - return this; - } - - @Override - public BinaryMemcacheResponse touch() { - super.touch(); - return this; - } - - @Override - public BinaryMemcacheResponse touch(Object hint) { - super.touch(hint); - return this; - } - - /** - * Copies special metadata hold by this instance to the provided instance - * - * @param dst The instance where to copy the metadata of this instance to - */ - void copyMeta(DefaultBinaryMemcacheResponse dst) { - super.copyMeta(dst); - dst.status = status; - } -} diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/DefaultFullBinaryMemcacheRequest.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/DefaultFullBinaryMemcacheRequest.java deleted file mode 100644 index fed8250e29..0000000000 --- a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/DefaultFullBinaryMemcacheRequest.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.memcache.binary; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.internal.UnstableApi; - -/** - * The default implementation of a {@link FullBinaryMemcacheRequest}. - */ -@UnstableApi -public class DefaultFullBinaryMemcacheRequest extends DefaultBinaryMemcacheRequest - implements FullBinaryMemcacheRequest { - - private final ByteBuf content; - - /** - * Create a new {@link DefaultBinaryMemcacheRequest} with the header, key and extras. - * - * @param key the key to use. - * @param extras the extras to use. - */ - public DefaultFullBinaryMemcacheRequest(ByteBuf key, ByteBuf extras) { - this(key, extras, Unpooled.buffer(0)); - } - - /** - * Create a new {@link DefaultBinaryMemcacheRequest} with the header, key, extras and content. - * - * @param key the key to use. - * @param extras the extras to use. - * @param content the content of the full request. - */ - public DefaultFullBinaryMemcacheRequest(ByteBuf key, ByteBuf extras, - ByteBuf content) { - super(key, extras); - requireNonNull(content, "content"); - - this.content = content; - setTotalBodyLength(keyLength() + extrasLength() + content.readableBytes()); - } - - @Override - public ByteBuf content() { - return content; - } - - @Override - public FullBinaryMemcacheRequest retain() { - super.retain(); - return this; - } - - @Override - public FullBinaryMemcacheRequest retain(int increment) { - super.retain(increment); - return this; - } - - @Override - public FullBinaryMemcacheRequest touch() { - super.touch(); - return this; - } - - @Override - public FullBinaryMemcacheRequest touch(Object hint) { - super.touch(hint); - content.touch(hint); - return this; - } - - @Override - protected void deallocate() { - super.deallocate(); - content.release(); - } - - @Override - public FullBinaryMemcacheRequest copy() { - ByteBuf key = key(); - if (key != null) { - key = key.copy(); - } - ByteBuf extras = extras(); - if (extras != null) { - extras = extras.copy(); - } - return newInstance(key, extras, content().copy()); - } - - @Override - public FullBinaryMemcacheRequest duplicate() { - ByteBuf key = key(); - if (key != null) { - key = key.duplicate(); - } - ByteBuf extras = extras(); - if (extras != null) { - extras = extras.duplicate(); - } - return newInstance(key, extras, content().duplicate()); - } - - @Override - public FullBinaryMemcacheRequest retainedDuplicate() { - return replace(content().retainedDuplicate()); - } - - @Override - public FullBinaryMemcacheRequest replace(ByteBuf content) { - ByteBuf key = key(); - if (key != null) { - key = key.retainedDuplicate(); - } - ByteBuf extras = extras(); - if (extras != null) { - extras = extras.retainedDuplicate(); - } - return newInstance(key, extras, content); - } - - private DefaultFullBinaryMemcacheRequest newInstance(ByteBuf key, ByteBuf extras, ByteBuf content) { - DefaultFullBinaryMemcacheRequest newInstance = new DefaultFullBinaryMemcacheRequest(key, extras, content); - copyMeta(newInstance); - return newInstance; - } -} diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/DefaultFullBinaryMemcacheResponse.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/DefaultFullBinaryMemcacheResponse.java deleted file mode 100644 index 4dbc3588a6..0000000000 --- a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/DefaultFullBinaryMemcacheResponse.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.memcache.binary; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.internal.UnstableApi; - -/** - * The default implementation of a {@link FullBinaryMemcacheResponse}. - */ -@UnstableApi -public class DefaultFullBinaryMemcacheResponse extends DefaultBinaryMemcacheResponse - implements FullBinaryMemcacheResponse { - - private final ByteBuf content; - - /** - * Create a new {@link DefaultFullBinaryMemcacheResponse} with the header, key and extras. - * - * @param key the key to use. - * @param extras the extras to use. - */ - public DefaultFullBinaryMemcacheResponse(ByteBuf key, ByteBuf extras) { - this(key, extras, Unpooled.buffer(0)); - } - - /** - * Create a new {@link DefaultFullBinaryMemcacheResponse} with the header, key, extras and content. - * - * @param key the key to use. - * @param extras the extras to use. - * @param content the content of the full request. - */ - public DefaultFullBinaryMemcacheResponse(ByteBuf key, ByteBuf extras, - ByteBuf content) { - super(key, extras); - requireNonNull(content, "content"); - - this.content = content; - setTotalBodyLength(keyLength() + extrasLength() + content.readableBytes()); - } - - @Override - public ByteBuf content() { - return content; - } - - @Override - public FullBinaryMemcacheResponse retain() { - super.retain(); - return this; - } - - @Override - public FullBinaryMemcacheResponse retain(int increment) { - super.retain(increment); - return this; - } - - @Override - public FullBinaryMemcacheResponse touch() { - super.touch(); - return this; - } - - @Override - public FullBinaryMemcacheResponse touch(Object hint) { - super.touch(hint); - content.touch(hint); - return this; - } - - @Override - protected void deallocate() { - super.deallocate(); - content.release(); - } - - @Override - public FullBinaryMemcacheResponse copy() { - ByteBuf key = key(); - if (key != null) { - key = key.copy(); - } - ByteBuf extras = extras(); - if (extras != null) { - extras = extras.copy(); - } - return newInstance(key, extras, content().copy()); - } - - @Override - public FullBinaryMemcacheResponse duplicate() { - ByteBuf key = key(); - if (key != null) { - key = key.duplicate(); - } - ByteBuf extras = extras(); - if (extras != null) { - extras = extras.duplicate(); - } - return newInstance(key, extras, content().duplicate()); - } - - @Override - public FullBinaryMemcacheResponse retainedDuplicate() { - return replace(content().retainedDuplicate()); - } - - @Override - public FullBinaryMemcacheResponse replace(ByteBuf content) { - ByteBuf key = key(); - if (key != null) { - key = key.retainedDuplicate(); - } - ByteBuf extras = extras(); - if (extras != null) { - extras = extras.retainedDuplicate(); - } - return newInstance(key, extras, content); - } - - private FullBinaryMemcacheResponse newInstance(ByteBuf key, ByteBuf extras, ByteBuf content) { - DefaultFullBinaryMemcacheResponse newInstance = new DefaultFullBinaryMemcacheResponse(key, extras, content); - copyMeta(newInstance); - return newInstance; - } -} diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/FullBinaryMemcacheRequest.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/FullBinaryMemcacheRequest.java deleted file mode 100644 index be73a56346..0000000000 --- a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/FullBinaryMemcacheRequest.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.memcache.binary; - -import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.memcache.FullMemcacheMessage; -import io.netty.util.internal.UnstableApi; - -/** - * A {@link BinaryMemcacheRequest} that also includes the content. - */ -@UnstableApi -public interface FullBinaryMemcacheRequest extends BinaryMemcacheRequest, FullMemcacheMessage { - - @Override - FullBinaryMemcacheRequest copy(); - - @Override - FullBinaryMemcacheRequest duplicate(); - - @Override - FullBinaryMemcacheRequest retainedDuplicate(); - - @Override - FullBinaryMemcacheRequest replace(ByteBuf content); - - @Override - FullBinaryMemcacheRequest retain(int increment); - - @Override - FullBinaryMemcacheRequest retain(); - - @Override - FullBinaryMemcacheRequest touch(); - - @Override - FullBinaryMemcacheRequest touch(Object hint); -} diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/FullBinaryMemcacheResponse.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/FullBinaryMemcacheResponse.java deleted file mode 100644 index 031d64f402..0000000000 --- a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/FullBinaryMemcacheResponse.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.memcache.binary; - -import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.memcache.FullMemcacheMessage; -import io.netty.util.internal.UnstableApi; - -/** - * A {@link BinaryMemcacheResponse} that also includes the content. - */ -@UnstableApi -public interface FullBinaryMemcacheResponse extends BinaryMemcacheResponse, FullMemcacheMessage { - - @Override - FullBinaryMemcacheResponse copy(); - - @Override - FullBinaryMemcacheResponse duplicate(); - - @Override - FullBinaryMemcacheResponse retainedDuplicate(); - - @Override - FullBinaryMemcacheResponse replace(ByteBuf content); - - @Override - FullBinaryMemcacheResponse retain(int increment); - - @Override - FullBinaryMemcacheResponse retain(); - - @Override - FullBinaryMemcacheResponse touch(); - - @Override - FullBinaryMemcacheResponse touch(Object hint); -} diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/package-info.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/package-info.java deleted file mode 100644 index f4f26bf222..0000000000 --- a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/package-info.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * Implementations and Interfaces for the Memcache Binary protocol. - */ -@UnstableApi -package io.netty.handler.codec.memcache.binary; - -import io.netty.util.internal.UnstableApi; diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/package-info.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/package-info.java deleted file mode 100644 index 763c270d81..0000000000 --- a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/package-info.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * Common superset of ascii and binary classes. - */ -@UnstableApi -package io.netty.handler.codec.memcache; - -import io.netty.util.internal.UnstableApi; diff --git a/codec-memcache/src/test/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheDecoderTest.java b/codec-memcache/src/test/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheDecoderTest.java deleted file mode 100644 index c70f534075..0000000000 --- a/codec-memcache/src/test/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheDecoderTest.java +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.memcache.binary; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.memcache.LastMemcacheContent; -import io.netty.handler.codec.memcache.MemcacheContent; -import io.netty.util.CharsetUtil; -import io.netty.util.ReferenceCounted; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.hamcrest.CoreMatchers.*; -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.core.IsNull.notNullValue; -import static org.hamcrest.core.IsNull.nullValue; -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** - * Verifies the correct functionality of the {@link AbstractBinaryMemcacheDecoder}. - *

- * While technically there are both a {@link BinaryMemcacheRequestDecoder} and a {@link BinaryMemcacheResponseDecoder} - * they implement the same basics and just differ in the type of headers returned. - */ -public class BinaryMemcacheDecoderTest { - - /** - * Represents a GET request header with a key size of three. - */ - private static final byte[] GET_REQUEST = { - (byte) 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x6f, 0x6f - }; - - private static final byte[] SET_REQUEST_WITH_CONTENT = { - (byte) 0x80, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x6f, 0x6f, 0x01, 0x02, 0x03, 0x04, 0x05, - 0x06, 0x07, 0x08 - }; - - private static final byte[] GET_RESPONSE_CHUNK_1 = { - (byte) 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x6f, 0x74, 0x20, 0x66, 0x6f, 0x75, 0x6e, - 0x64, (byte) 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x6f, 0x74, 0x20, 0x66, 0x6f, 0x75, - }; - - private static final byte[] GET_RESPONSE_CHUNK_2 = { - 0x6e, 0x64, (byte) 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x6f, 0x74, 0x20, 0x66, 0x6f, - 0x75, 0x6e, 0x64 - }; - - private EmbeddedChannel channel; - - @BeforeEach - public void setup() throws Exception { - channel = new EmbeddedChannel(new BinaryMemcacheRequestDecoder()); - } - - @AfterEach - public void teardown() throws Exception { - channel.finishAndReleaseAll(); - } - - /** - * This tests a simple GET request with a key as the value. - */ - @Test - public void shouldDecodeRequestWithSimpleValue() { - ByteBuf incoming = Unpooled.buffer(); - incoming.writeBytes(GET_REQUEST); - channel.writeInbound(incoming); - - BinaryMemcacheRequest request = channel.readInbound(); - - assertThat(request, notNullValue()); - assertThat(request.key(), notNullValue()); - assertThat(request.extras(), nullValue()); - - assertThat(request.keyLength(), is((short) 3)); - assertThat(request.extrasLength(), is((byte) 0)); - assertThat(request.totalBodyLength(), is(3)); - - request.release(); - assertThat(channel.readInbound(), instanceOf(LastMemcacheContent.class)); - } - - /** - * This test makes sure that large content is emitted in chunks. - */ - @Test - public void shouldDecodeRequestWithChunkedContent() { - int smallBatchSize = 2; - channel = new EmbeddedChannel(new BinaryMemcacheRequestDecoder(smallBatchSize)); - - ByteBuf incoming = Unpooled.buffer(); - incoming.writeBytes(SET_REQUEST_WITH_CONTENT); - channel.writeInbound(incoming); - - BinaryMemcacheRequest request = channel.readInbound(); - - assertThat(request, notNullValue()); - assertThat(request.key(), notNullValue()); - assertThat(request.extras(), nullValue()); - - assertThat(request.keyLength(), is((short) 3)); - assertThat(request.extrasLength(), is((byte) 0)); - assertThat(request.totalBodyLength(), is(11)); - - request.release(); - - int expectedContentChunks = 4; - for (int i = 1; i <= expectedContentChunks; i++) { - MemcacheContent content = channel.readInbound(); - if (i < expectedContentChunks) { - assertThat(content, instanceOf(MemcacheContent.class)); - } else { - assertThat(content, instanceOf(LastMemcacheContent.class)); - } - assertThat(content.content().readableBytes(), is(2)); - content.release(); - } - assertThat(channel.readInbound(), nullValue()); - } - - /** - * This test makes sure that even when the decoder is confronted with various chunk - * sizes in the middle of decoding, it can recover and decode all the time eventually. - */ - @Test - public void shouldHandleNonUniformNetworkBatches() { - ByteBuf incoming = Unpooled.copiedBuffer(SET_REQUEST_WITH_CONTENT); - while (incoming.isReadable()) { - channel.writeInbound(incoming.readBytes(5)); - } - incoming.release(); - - BinaryMemcacheRequest request = channel.readInbound(); - - assertThat(request, notNullValue()); - assertThat(request.key(), notNullValue()); - assertThat(request.extras(), nullValue()); - - request.release(); - - MemcacheContent content1 = channel.readInbound(); - MemcacheContent content2 = channel.readInbound(); - - assertThat(content1, instanceOf(MemcacheContent.class)); - assertThat(content2, instanceOf(LastMemcacheContent.class)); - - assertThat(content1.content().readableBytes(), is(3)); - assertThat(content2.content().readableBytes(), is(5)); - - content1.release(); - content2.release(); - } - - /** - * This test makes sure that even when more requests arrive in the same batch, they - * get emitted as separate messages. - */ - @Test - public void shouldHandleTwoMessagesInOneBatch() { - channel.writeInbound(Unpooled.buffer().writeBytes(GET_REQUEST).writeBytes(GET_REQUEST)); - - BinaryMemcacheRequest request = channel.readInbound(); - assertThat(request, instanceOf(BinaryMemcacheRequest.class)); - assertThat(request, notNullValue()); - request.release(); - - Object lastContent = channel.readInbound(); - assertThat(lastContent, instanceOf(LastMemcacheContent.class)); - ((ReferenceCounted) lastContent).release(); - - request = channel.readInbound(); - assertThat(request, instanceOf(BinaryMemcacheRequest.class)); - assertThat(request, notNullValue()); - request.release(); - - lastContent = channel.readInbound(); - assertThat(lastContent, instanceOf(LastMemcacheContent.class)); - ((ReferenceCounted) lastContent).release(); - } - - @Test - public void shouldDecodeSeparatedValues() { - String msgBody = "Not found"; - channel = new EmbeddedChannel(new BinaryMemcacheResponseDecoder()); - - channel.writeInbound(Unpooled.buffer().writeBytes(GET_RESPONSE_CHUNK_1)); - channel.writeInbound(Unpooled.buffer().writeBytes(GET_RESPONSE_CHUNK_2)); - - // First message - BinaryMemcacheResponse response = channel.readInbound(); - assertThat(response.status(), is(BinaryMemcacheResponseStatus.KEY_ENOENT)); - assertThat(response.totalBodyLength(), is(msgBody.length())); - response.release(); - - // First message first content chunk - MemcacheContent content = channel.readInbound(); - assertThat(content, instanceOf(LastMemcacheContent.class)); - assertThat(content.content().toString(CharsetUtil.UTF_8), is(msgBody)); - content.release(); - - // Second message - response = channel.readInbound(); - assertThat(response.status(), is(BinaryMemcacheResponseStatus.KEY_ENOENT)); - assertThat(response.totalBodyLength(), is(msgBody.length())); - response.release(); - - // Second message first content chunk - content = channel.readInbound(); - assertThat(content, instanceOf(MemcacheContent.class)); - assertThat(content.content().toString(CharsetUtil.UTF_8), is(msgBody.substring(0, 7))); - content.release(); - - // Second message second content chunk - content = channel.readInbound(); - assertThat(content, instanceOf(LastMemcacheContent.class)); - assertThat(content.content().toString(CharsetUtil.UTF_8), is(msgBody.substring(7, 9))); - content.release(); - - // Third message - response = channel.readInbound(); - assertThat(response.status(), is(BinaryMemcacheResponseStatus.KEY_ENOENT)); - assertThat(response.totalBodyLength(), is(msgBody.length())); - response.release(); - - // Third message first content chunk - content = channel.readInbound(); - assertThat(content, instanceOf(LastMemcacheContent.class)); - assertThat(content.content().toString(CharsetUtil.UTF_8), is(msgBody)); - content.release(); - } - - @Test - public void shouldRetainCurrentMessageWhenSendingItOut() { - channel = new EmbeddedChannel( - new BinaryMemcacheRequestEncoder(), - new BinaryMemcacheRequestDecoder()); - - ByteBuf key = Unpooled.copiedBuffer("Netty", CharsetUtil.UTF_8); - ByteBuf extras = Unpooled.copiedBuffer("extras", CharsetUtil.UTF_8); - BinaryMemcacheRequest request = new DefaultBinaryMemcacheRequest(key, extras); - - assertTrue(channel.writeOutbound(request)); - for (;;) { - ByteBuf buffer = channel.readOutbound(); - if (buffer == null) { - break; - } - channel.writeInbound(buffer); - } - BinaryMemcacheRequest read = channel.readInbound(); - read.release(); - // tearDown will call "channel.finish()" - } -} diff --git a/codec-memcache/src/test/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheEncoderTest.java b/codec-memcache/src/test/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheEncoderTest.java deleted file mode 100644 index 076c9809ec..0000000000 --- a/codec-memcache/src/test/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheEncoderTest.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.memcache.binary; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.EncoderException; -import io.netty.handler.codec.memcache.DefaultLastMemcacheContent; -import io.netty.handler.codec.memcache.DefaultMemcacheContent; -import io.netty.util.CharsetUtil; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.hamcrest.CoreMatchers.*; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.core.IsEqual.equalTo; -import static org.junit.jupiter.api.Assertions.assertThrows; - -/** - * Verifies the correct functionality of the {@link AbstractBinaryMemcacheEncoder}. - */ -public class BinaryMemcacheEncoderTest { - - public static final int DEFAULT_HEADER_SIZE = 24; - - private EmbeddedChannel channel; - - @BeforeEach - public void setup() throws Exception { - channel = new EmbeddedChannel(new BinaryMemcacheRequestEncoder()); - } - - @AfterEach - public void teardown() throws Exception { - channel.finishAndReleaseAll(); - } - - @Test - public void shouldEncodeDefaultHeader() { - BinaryMemcacheRequest request = new DefaultBinaryMemcacheRequest(); - - boolean result = channel.writeOutbound(request); - assertThat(result, is(true)); - - ByteBuf written = channel.readOutbound(); - assertThat(written.readableBytes(), is(DEFAULT_HEADER_SIZE)); - assertThat(written.readByte(), is((byte) 0x80)); - assertThat(written.readByte(), is((byte) 0x00)); - written.release(); - } - - @Test - public void shouldEncodeCustomHeader() { - BinaryMemcacheRequest request = new DefaultBinaryMemcacheRequest(); - request.setMagic((byte) 0xAA); - request.setOpcode(BinaryMemcacheOpcodes.GET); - - boolean result = channel.writeOutbound(request); - assertThat(result, is(true)); - - ByteBuf written = channel.readOutbound(); - assertThat(written.readableBytes(), is(DEFAULT_HEADER_SIZE)); - assertThat(written.readByte(), is((byte) 0xAA)); - assertThat(written.readByte(), is(BinaryMemcacheOpcodes.GET)); - written.release(); - } - - @Test - public void shouldEncodeExtras() { - String extrasContent = "netty<3memcache"; - ByteBuf extras = Unpooled.copiedBuffer(extrasContent, CharsetUtil.UTF_8); - int extrasLength = extras.readableBytes(); - - BinaryMemcacheRequest request = new DefaultBinaryMemcacheRequest(Unpooled.EMPTY_BUFFER, extras); - - boolean result = channel.writeOutbound(request); - assertThat(result, is(true)); - - ByteBuf written = channel.readOutbound(); - assertThat(written.readableBytes(), is(DEFAULT_HEADER_SIZE + extrasLength)); - written.skipBytes(DEFAULT_HEADER_SIZE); - assertThat(written.readSlice(extrasLength).toString(CharsetUtil.UTF_8), equalTo(extrasContent)); - written.release(); - } - - @Test - public void shouldEncodeKey() { - ByteBuf key = Unpooled.copiedBuffer("netty", CharsetUtil.UTF_8); - int keyLength = key.readableBytes(); - - BinaryMemcacheRequest request = new DefaultBinaryMemcacheRequest(key); - - boolean result = channel.writeOutbound(request); - assertThat(result, is(true)); - - ByteBuf written = channel.readOutbound(); - assertThat(written.readableBytes(), is(DEFAULT_HEADER_SIZE + keyLength)); - written.skipBytes(DEFAULT_HEADER_SIZE); - assertThat(written.readSlice(keyLength).toString(CharsetUtil.UTF_8), equalTo("netty")); - written.release(); - } - - @Test - public void shouldEncodeContent() { - DefaultMemcacheContent content1 = - new DefaultMemcacheContent(Unpooled.copiedBuffer("Netty", CharsetUtil.UTF_8)); - DefaultLastMemcacheContent content2 = - new DefaultLastMemcacheContent(Unpooled.copiedBuffer(" Rocks!", CharsetUtil.UTF_8)); - int totalBodyLength = content1.content().readableBytes() + content2.content().readableBytes(); - - BinaryMemcacheRequest request = new DefaultBinaryMemcacheRequest(); - request.setTotalBodyLength(totalBodyLength); - - boolean result = channel.writeOutbound(request); - assertThat(result, is(true)); - result = channel.writeOutbound(content1); - assertThat(result, is(true)); - result = channel.writeOutbound(content2); - assertThat(result, is(true)); - - ByteBuf written = channel.readOutbound(); - assertThat(written.readableBytes(), is(DEFAULT_HEADER_SIZE)); - written.release(); - - written = channel.readOutbound(); - assertThat(written.readableBytes(), is(content1.content().readableBytes())); - assertThat( - written.readSlice(content1.content().readableBytes()).toString(CharsetUtil.UTF_8), - is("Netty") - ); - written.release(); - - written = channel.readOutbound(); - assertThat(written.readableBytes(), is(content2.content().readableBytes())); - assertThat( - written.readSlice(content2.content().readableBytes()).toString(CharsetUtil.UTF_8), - is(" Rocks!") - ); - written.release(); - } - - @Test - public void shouldFailWithoutLastContent() { - channel.writeOutbound(new DefaultMemcacheContent(Unpooled.EMPTY_BUFFER)); - assertThrows(EncoderException.class, () -> channel.writeOutbound(new DefaultBinaryMemcacheRequest())); - } -} diff --git a/codec-memcache/src/test/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheMessageTest.java b/codec-memcache/src/test/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheMessageTest.java deleted file mode 100644 index e8e4f14942..0000000000 --- a/codec-memcache/src/test/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheMessageTest.java +++ /dev/null @@ -1,121 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.memcache.binary; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.CharsetUtil; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class BinaryMemcacheMessageTest { - - @Test - public void testSetLengths() { - ByteBuf key = Unpooled.copiedBuffer("Netty Rocks!", CharsetUtil.UTF_8); - ByteBuf extras = Unpooled.copiedBuffer("some extras", CharsetUtil.UTF_8); - ByteBuf content = Unpooled.copiedBuffer("content", CharsetUtil.UTF_8); - try { - testSettingLengths(new DefaultBinaryMemcacheRequest(), 0, 0, 0); - testSettingLengths(new DefaultBinaryMemcacheRequest(key.retain()), key.readableBytes(), 0, 0); - testSettingLengths(new DefaultBinaryMemcacheRequest(key.retain(), extras.retain()), - key.readableBytes(), extras.readableBytes(), 0); - - testSettingLengths(new DefaultBinaryMemcacheResponse(), 0, 0, 0); - testSettingLengths(new DefaultBinaryMemcacheResponse(key.retain()), key.readableBytes(), 0, 0); - testSettingLengths(new DefaultBinaryMemcacheResponse(key.retain(), extras.retain()), - key.readableBytes(), extras.readableBytes(), 0); - - testSettingLengths(new DefaultFullBinaryMemcacheRequest(key.retain(), extras.retain()), - key.readableBytes(), extras.readableBytes(), 0); - testSettingLengths(new DefaultFullBinaryMemcacheRequest(null, extras.retain()), - 0, extras.readableBytes(), 0); - testSettingLengths(new DefaultFullBinaryMemcacheRequest(key.retain(), null), - key.readableBytes(), 0, 0); - testSettingLengths(new DefaultFullBinaryMemcacheRequest(null, null), 0, 0, 0); - testSettingLengths(new DefaultFullBinaryMemcacheRequest(key.retain(), extras.retain(), content.retain()), - key.readableBytes(), extras.readableBytes(), content.readableBytes()); - testSettingLengths(new DefaultFullBinaryMemcacheRequest(null, extras.retain(), content.retain()), - 0, extras.readableBytes(), content.readableBytes()); - testSettingLengths(new DefaultFullBinaryMemcacheRequest(key.retain(), null, content.retain()), - key.readableBytes(), 0, content.readableBytes()); - testSettingLengths(new DefaultFullBinaryMemcacheRequest(null, null, content.retain()), - 0, 0, content.readableBytes()); - - testSettingLengths(new DefaultFullBinaryMemcacheResponse(key.retain(), extras.retain()), - key.readableBytes(), extras.readableBytes(), 0); - testSettingLengths(new DefaultFullBinaryMemcacheResponse(null, extras.retain()), - 0, extras.readableBytes(), 0); - testSettingLengths(new DefaultFullBinaryMemcacheResponse(key.retain(), null), - key.readableBytes(), 0, 0); - testSettingLengths(new DefaultFullBinaryMemcacheResponse(null, null), 0, 0, 0); - testSettingLengths(new DefaultFullBinaryMemcacheResponse(key.retain(), extras.retain(), content.retain()), - key.readableBytes(), extras.readableBytes(), content.readableBytes()); - testSettingLengths(new DefaultFullBinaryMemcacheResponse(null, extras.retain(), content.retain()), - 0, extras.readableBytes(), content.readableBytes()); - testSettingLengths(new DefaultFullBinaryMemcacheResponse(key.retain(), null, content.retain()), - key.readableBytes(), 0, content.readableBytes()); - testSettingLengths(new DefaultFullBinaryMemcacheResponse(null, null, content.retain()), - 0, 0, content.readableBytes()); - } finally { - key.release(); - extras.release(); - content.release(); - } - } - - private static void testSettingLengths(BinaryMemcacheMessage message, - int initialKeyLength, int initialExtrasLength, int contentLength) { - ByteBuf key = Unpooled.copiedBuffer("netty", CharsetUtil.UTF_8); - ByteBuf extras = Unpooled.copiedBuffer("extras", CharsetUtil.UTF_8); - ByteBuf key2 = Unpooled.copiedBuffer("netty!", CharsetUtil.UTF_8); - ByteBuf extras2 = Unpooled.copiedBuffer("extras!", CharsetUtil.UTF_8); - try { - assertEquals(initialKeyLength, message.keyLength()); - assertEquals(initialExtrasLength, message.extrasLength()); - assertEquals(initialKeyLength + initialExtrasLength + contentLength, message.totalBodyLength()); - - message.setKey(key.retain()); - assertEquals(key.readableBytes(), message.keyLength()); - assertEquals(initialExtrasLength, message.extrasLength()); - assertEquals(key.readableBytes() + initialExtrasLength + contentLength, message.totalBodyLength()); - - message.setExtras(extras.retain()); - assertEquals(key.readableBytes(), message.keyLength()); - assertEquals(extras.readableBytes(), message.extrasLength()); - assertEquals(key.readableBytes() + extras.readableBytes() + contentLength, message.totalBodyLength()); - - // Replace the previous key - message.setKey(key2.retain()); - assertEquals(key2.readableBytes(), message.keyLength()); - assertEquals(extras.readableBytes(), message.extrasLength()); - assertEquals(key2.readableBytes() + extras.readableBytes() + contentLength, message.totalBodyLength()); - - // Replace the previous extras - message.setExtras(extras2.retain()); - assertEquals(key2.readableBytes(), message.keyLength()); - assertEquals(extras2.readableBytes(), message.extrasLength()); - assertEquals(key2.readableBytes() + extras2.readableBytes() + contentLength, message.totalBodyLength()); - } finally { - key.release(); - extras.release(); - key2.release(); - extras2.release(); - message.release(); - } - } -} diff --git a/codec-memcache/src/test/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheObjectAggregatorTest.java b/codec-memcache/src/test/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheObjectAggregatorTest.java deleted file mode 100644 index 79a58c6152..0000000000 --- a/codec-memcache/src/test/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheObjectAggregatorTest.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.memcache.binary; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.memcache.DefaultLastMemcacheContent; -import io.netty.handler.codec.memcache.DefaultMemcacheContent; -import io.netty.util.CharsetUtil; -import org.junit.jupiter.api.Test; - -import static org.hamcrest.CoreMatchers.*; -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.core.IsNull.notNullValue; -import static org.hamcrest.core.IsNull.nullValue; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** - * Verifies the correct functionality of the {@link BinaryMemcacheObjectAggregator}. - */ -public class BinaryMemcacheObjectAggregatorTest { - - private static final byte[] SET_REQUEST_WITH_CONTENT = { - (byte) 0x80, 0x01, 0x00, 0x03, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0B, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x66, 0x6f, 0x6f, - 0x01, 0x02, 0x03, 0x04, - 0x05, 0x06, 0x07, 0x08 - }; - - public static final int MAX_CONTENT_SIZE = 2 << 10; - - private EmbeddedChannel channel; - - @Test - public void shouldAggregateChunksOnDecode() { - int smallBatchSize = 2; - channel = new EmbeddedChannel( - new BinaryMemcacheRequestDecoder(smallBatchSize), - new BinaryMemcacheObjectAggregator(MAX_CONTENT_SIZE)); - - ByteBuf incoming = Unpooled.buffer(); - incoming.writeBytes(SET_REQUEST_WITH_CONTENT); - channel.writeInbound(incoming); - - FullBinaryMemcacheRequest request = channel.readInbound(); - - assertThat(request, instanceOf(FullBinaryMemcacheRequest.class)); - assertThat(request, notNullValue()); - assertThat(request.key(), notNullValue()); - assertThat(request.extras(), nullValue()); - - assertThat(request.content().readableBytes(), is(8)); - assertThat(request.content().readByte(), is((byte) 0x01)); - assertThat(request.content().readByte(), is((byte) 0x02)); - request.release(); - - assertThat(channel.readInbound(), nullValue()); - - assertFalse(channel.finish()); - } - - @Test - public void shouldRetainByteBufWhenAggregating() { - channel = new EmbeddedChannel( - new BinaryMemcacheRequestEncoder(), - new BinaryMemcacheRequestDecoder(), - new BinaryMemcacheObjectAggregator(MAX_CONTENT_SIZE)); - - ByteBuf key = Unpooled.copiedBuffer("Netty", CharsetUtil.UTF_8); - ByteBuf extras = Unpooled.copiedBuffer("extras", CharsetUtil.UTF_8); - BinaryMemcacheRequest request = new DefaultBinaryMemcacheRequest(key, extras); - - DefaultMemcacheContent content1 = - new DefaultMemcacheContent(Unpooled.copiedBuffer("Netty", CharsetUtil.UTF_8)); - DefaultLastMemcacheContent content2 = - new DefaultLastMemcacheContent(Unpooled.copiedBuffer(" Rocks!", CharsetUtil.UTF_8)); - int totalBodyLength = key.readableBytes() + extras.readableBytes() + - content1.content().readableBytes() + content2.content().readableBytes(); - request.setTotalBodyLength(totalBodyLength); - - assertTrue(channel.writeOutbound(request, content1, content2)); - - assertThat(channel.outboundMessages().size(), is(3)); - assertTrue(channel.writeInbound(channel.readOutbound(), channel.readOutbound(), channel.readOutbound())); - - FullBinaryMemcacheRequest read = channel.readInbound(); - assertThat(read, notNullValue()); - assertThat(read.key().toString(CharsetUtil.UTF_8), is("Netty")); - assertThat(read.extras().toString(CharsetUtil.UTF_8), is("extras")); - assertThat(read.content().toString(CharsetUtil.UTF_8), is("Netty Rocks!")); - - read.release(); - assertFalse(channel.finish()); - } -} diff --git a/codec-memcache/src/test/java/io/netty/handler/codec/memcache/binary/DefaultFullBinaryMemcacheRequestTest.java b/codec-memcache/src/test/java/io/netty/handler/codec/memcache/binary/DefaultFullBinaryMemcacheRequestTest.java deleted file mode 100644 index 6abe252f08..0000000000 --- a/codec-memcache/src/test/java/io/netty/handler/codec/memcache/binary/DefaultFullBinaryMemcacheRequestTest.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.memcache.binary; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.CharsetUtil; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotSame; - -public class DefaultFullBinaryMemcacheRequestTest { - - private DefaultFullBinaryMemcacheRequest request; - - @BeforeEach - public void setUp() { - request = new DefaultFullBinaryMemcacheRequest( - Unpooled.copiedBuffer("key", CharsetUtil.UTF_8), - Unpooled.wrappedBuffer(new byte[]{1, 3, 4, 9}), - Unpooled.copiedBuffer("some value", CharsetUtil.UTF_8)); - request.setReserved((short) 534); - request.setMagic((byte) 0x03); - request.setOpcode((byte) 0x02); - request.setKeyLength((short) 32); - request.setExtrasLength((byte) 34); - request.setDataType((byte) 43); - request.setTotalBodyLength(345); - request.setOpaque(3); - request.setCas(345345L); - } - - @Test - public void fullCopy() { - FullBinaryMemcacheRequest newInstance = request.copy(); - try { - assertCopy(request, request.content(), newInstance); - } finally { - request.release(); - newInstance.release(); - } - } - - @Test - public void fullDuplicate() { - FullBinaryMemcacheRequest newInstance = request.duplicate(); - try { - assertCopy(request, request.content(), newInstance); - } finally { - request.release(); - } - } - - @Test - public void fullReplace() { - ByteBuf newContent = Unpooled.copiedBuffer("new value", CharsetUtil.UTF_8); - FullBinaryMemcacheRequest newInstance = request.replace(newContent); - try { - assertCopy(request, newContent, newInstance); - } finally { - request.release(); - newInstance.release(); - } - } - - private void assertCopy(FullBinaryMemcacheRequest expected, ByteBuf expectedContent, - FullBinaryMemcacheRequest actual) { - assertNotSame(expected, actual); - - assertEquals(expected.key(), actual.key()); - assertEquals(expected.extras(), actual.extras()); - assertEquals(expectedContent, actual.content()); - - assertEquals(expected.reserved(), actual.reserved()); - assertEquals(expected.magic(), actual.magic()); - assertEquals(expected.opcode(), actual.opcode()); - assertEquals(expected.keyLength(), actual.keyLength()); - assertEquals(expected.extrasLength(), actual.extrasLength()); - assertEquals(expected.dataType(), actual.dataType()); - assertEquals(expected.totalBodyLength(), actual.totalBodyLength()); - assertEquals(expected.opaque(), actual.opaque()); - assertEquals(expected.cas(), actual.cas()); - } -} diff --git a/codec-memcache/src/test/java/io/netty/handler/codec/memcache/binary/DefaultFullBinaryMemcacheResponseTest.java b/codec-memcache/src/test/java/io/netty/handler/codec/memcache/binary/DefaultFullBinaryMemcacheResponseTest.java deleted file mode 100644 index 9a0882982a..0000000000 --- a/codec-memcache/src/test/java/io/netty/handler/codec/memcache/binary/DefaultFullBinaryMemcacheResponseTest.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.memcache.binary; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.CharsetUtil; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotSame; - -public class DefaultFullBinaryMemcacheResponseTest { - - private DefaultFullBinaryMemcacheResponse response; - - @BeforeEach - public void setUp() { - response = new DefaultFullBinaryMemcacheResponse( - Unpooled.copiedBuffer("key", CharsetUtil.UTF_8), - Unpooled.wrappedBuffer(new byte[]{1, 3, 4, 9}), - Unpooled.copiedBuffer("some value", CharsetUtil.UTF_8)); - response.setStatus((short) 1); - response.setMagic((byte) 0x03); - response.setOpcode((byte) 0x02); - response.setKeyLength((short) 32); - response.setExtrasLength((byte) 34); - response.setDataType((byte) 43); - response.setTotalBodyLength(345); - response.setOpaque(3); - response.setCas(345345L); - } - - @Test - public void fullCopy() { - FullBinaryMemcacheResponse newInstance = response.copy(); - try { - assertResponseEquals(response, response.content(), newInstance); - } finally { - response.release(); - newInstance.release(); - } - } - - @Test - public void fullDuplicate() { - try { - assertResponseEquals(response, response.content(), response.duplicate()); - } finally { - response.release(); - } - } - - @Test - public void fullReplace() { - ByteBuf newContent = Unpooled.copiedBuffer("new value", CharsetUtil.UTF_8); - FullBinaryMemcacheResponse newInstance = response.replace(newContent); - try { - assertResponseEquals(response, newContent, newInstance); - } finally { - response.release(); - newInstance.release(); - } - } - - private void assertResponseEquals(FullBinaryMemcacheResponse expected, ByteBuf expectedContent, - FullBinaryMemcacheResponse actual) { - assertNotSame(expected, actual); - - assertEquals(expected.key(), actual.key()); - assertEquals(expected.extras(), actual.extras()); - assertEquals(expectedContent, actual.content()); - - assertEquals(expected.status(), actual.status()); - assertEquals(expected.magic(), actual.magic()); - assertEquals(expected.opcode(), actual.opcode()); - assertEquals(expected.keyLength(), actual.keyLength()); - assertEquals(expected.extrasLength(), actual.extrasLength()); - assertEquals(expected.dataType(), actual.dataType()); - assertEquals(expected.totalBodyLength(), actual.totalBodyLength()); - assertEquals(expected.opaque(), actual.opaque()); - assertEquals(expected.cas(), actual.cas()); - } -} diff --git a/codec-memcache/src/test/java/io/netty/handler/codec/memcache/binary/FullMemcacheMessageRequestTest.java b/codec-memcache/src/test/java/io/netty/handler/codec/memcache/binary/FullMemcacheMessageRequestTest.java deleted file mode 100644 index bff6c23f56..0000000000 --- a/codec-memcache/src/test/java/io/netty/handler/codec/memcache/binary/FullMemcacheMessageRequestTest.java +++ /dev/null @@ -1,64 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.memcache.binary; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.CharsetUtil; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class FullMemcacheMessageRequestTest { - - private EmbeddedChannel channel; - - @BeforeEach - public void setup() throws Exception { - channel = new EmbeddedChannel( - new BinaryMemcacheRequestEncoder(), - new BinaryMemcacheRequestDecoder(), - new BinaryMemcacheObjectAggregator(1024)); - } - - @AfterEach - public void teardown() throws Exception { - assertFalse(channel.finish()); - } - - @Test - public void testEncodeDecode() throws Exception { - ByteBuf key = Unpooled.wrappedBuffer("key".getBytes(CharsetUtil.UTF_8)); - ByteBuf content = Unpooled.wrappedBuffer("content".getBytes(CharsetUtil.UTF_8)); - ByteBuf extras = Unpooled.wrappedBuffer("extras".getBytes(CharsetUtil.UTF_8)); - FullBinaryMemcacheRequest req = new DefaultFullBinaryMemcacheRequest(key, extras, content); - assertTrue(channel.writeOutbound(req)); - // header + content - assertEquals(2, channel.outboundMessages().size()); - assertTrue(channel.writeInbound(channel.readOutbound(), channel.readOutbound())); - - FullBinaryMemcacheRequest read = channel.readInbound(); - assertEquals("key", read.key().toString(CharsetUtil.UTF_8)); - assertEquals("content", read.content().toString(CharsetUtil.UTF_8)); - assertEquals("extras", read.extras().toString(CharsetUtil.UTF_8)); - read.release(); - } -} diff --git a/codec-memcache/src/test/java/io/netty/handler/codec/memcache/binary/FullMemcacheMessageResponseTest.java b/codec-memcache/src/test/java/io/netty/handler/codec/memcache/binary/FullMemcacheMessageResponseTest.java deleted file mode 100644 index 2181e7620b..0000000000 --- a/codec-memcache/src/test/java/io/netty/handler/codec/memcache/binary/FullMemcacheMessageResponseTest.java +++ /dev/null @@ -1,64 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.memcache.binary; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.CharsetUtil; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class FullMemcacheMessageResponseTest { - - private EmbeddedChannel channel; - - @BeforeEach - public void setup() throws Exception { - channel = new EmbeddedChannel( - new BinaryMemcacheResponseEncoder(), - new BinaryMemcacheResponseDecoder(), - new BinaryMemcacheObjectAggregator(1024)); - } - - @AfterEach - public void teardown() throws Exception { - assertFalse(channel.finish()); - } - - @Test - public void testEncodeDecode() throws Exception { - ByteBuf key = Unpooled.wrappedBuffer("key".getBytes(CharsetUtil.UTF_8)); - ByteBuf content = Unpooled.wrappedBuffer("content".getBytes(CharsetUtil.UTF_8)); - ByteBuf extras = Unpooled.wrappedBuffer("extras".getBytes(CharsetUtil.UTF_8)); - FullBinaryMemcacheResponse resp = new DefaultFullBinaryMemcacheResponse(key, extras, content); - assertTrue(channel.writeOutbound(resp)); - // header + content - assertEquals(2, channel.outboundMessages().size()); - assertTrue(channel.writeInbound(channel.readOutbound(), channel.readOutbound())); - - FullBinaryMemcacheResponse read = channel.readInbound(); - assertEquals("key", read.key().toString(CharsetUtil.UTF_8)); - assertEquals("content", read.content().toString(CharsetUtil.UTF_8)); - assertEquals("extras", read.extras().toString(CharsetUtil.UTF_8)); - read.release(); - } -} diff --git a/codec-mqtt/pom.xml b/codec-mqtt/pom.xml deleted file mode 100644 index 8e30303d1f..0000000000 --- a/codec-mqtt/pom.xml +++ /dev/null @@ -1,61 +0,0 @@ - - - - - 4.0.0 - - io.netty - netty-parent - 5.0.0.Final-SNAPSHOT - - - netty-codec-mqtt - jar - - Netty/Codec/MQTT - - - io.netty.codec.mqtt - - - - - ${project.groupId} - netty-common - ${project.version} - - - ${project.groupId} - netty-buffer - ${project.version} - - - ${project.groupId} - netty-transport - ${project.version} - - - ${project.groupId} - netty-codec - ${project.version} - - - org.mockito - mockito-core - - - diff --git a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttCodecUtil.java b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttCodecUtil.java deleted file mode 100644 index 788b6a4185..0000000000 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttCodecUtil.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.mqtt; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.DecoderException; -import io.netty.util.Attribute; -import io.netty.util.AttributeKey; - -import static io.netty.handler.codec.mqtt.MqttConstant.MIN_CLIENT_ID_LENGTH; - -final class MqttCodecUtil { - - private static final char[] TOPIC_WILDCARDS = {'#', '+'}; - - static final AttributeKey MQTT_VERSION_KEY = AttributeKey.valueOf("NETTY_CODEC_MQTT_VERSION"); - - static MqttVersion getMqttVersion(ChannelHandlerContext ctx) { - Attribute attr = ctx.channel().attr(MQTT_VERSION_KEY); - MqttVersion version = attr.get(); - if (version == null) { - return MqttVersion.MQTT_3_1_1; - } - return version; - } - - static void setMqttVersion(ChannelHandlerContext ctx, MqttVersion version) { - Attribute attr = ctx.channel().attr(MQTT_VERSION_KEY); - attr.set(version); - } - - static boolean isValidPublishTopicName(String topicName) { - // publish topic name must not contain any wildcard - for (char c : TOPIC_WILDCARDS) { - if (topicName.indexOf(c) >= 0) { - return false; - } - } - return true; - } - - static boolean isValidMessageId(int messageId) { - return messageId != 0; - } - - static boolean isValidClientId(MqttVersion mqttVersion, int maxClientIdLength, String clientId) { - if (mqttVersion == MqttVersion.MQTT_3_1) { - return clientId != null && clientId.length() >= MIN_CLIENT_ID_LENGTH && - clientId.length() <= maxClientIdLength; - } - if (mqttVersion == MqttVersion.MQTT_3_1_1 || mqttVersion == MqttVersion.MQTT_5) { - // In 3.1.3.1 Client Identifier of MQTT 3.1.1 and 5.0 specifications, The Server MAY allow ClientId’s - // that contain more than 23 encoded bytes. And, The Server MAY allow zero-length ClientId. - return clientId != null; - } - throw new IllegalArgumentException(mqttVersion + " is unknown mqtt version"); - } - - static MqttFixedHeader validateFixedHeader(ChannelHandlerContext ctx, MqttFixedHeader mqttFixedHeader) { - switch (mqttFixedHeader.messageType()) { - case PUBREL: - case SUBSCRIBE: - case UNSUBSCRIBE: - if (mqttFixedHeader.qosLevel() != MqttQoS.AT_LEAST_ONCE) { - throw new DecoderException(mqttFixedHeader.messageType().name() + " message must have QoS 1"); - } - return mqttFixedHeader; - case AUTH: - if (MqttCodecUtil.getMqttVersion(ctx) != MqttVersion.MQTT_5) { - throw new DecoderException("AUTH message requires at least MQTT 5"); - } - return mqttFixedHeader; - default: - return mqttFixedHeader; - } - } - - static MqttFixedHeader resetUnusedFields(MqttFixedHeader mqttFixedHeader) { - switch (mqttFixedHeader.messageType()) { - case CONNECT: - case CONNACK: - case PUBACK: - case PUBREC: - case PUBCOMP: - case SUBACK: - case UNSUBACK: - case PINGREQ: - case PINGRESP: - case DISCONNECT: - if (mqttFixedHeader.isDup() || - mqttFixedHeader.qosLevel() != MqttQoS.AT_MOST_ONCE || - mqttFixedHeader.isRetain()) { - return new MqttFixedHeader( - mqttFixedHeader.messageType(), - false, - MqttQoS.AT_MOST_ONCE, - false, - mqttFixedHeader.remainingLength()); - } - return mqttFixedHeader; - case PUBREL: - case SUBSCRIBE: - case UNSUBSCRIBE: - if (mqttFixedHeader.isRetain()) { - return new MqttFixedHeader( - mqttFixedHeader.messageType(), - mqttFixedHeader.isDup(), - mqttFixedHeader.qosLevel(), - false, - mqttFixedHeader.remainingLength()); - } - return mqttFixedHeader; - default: - return mqttFixedHeader; - } - } - - private MqttCodecUtil() { } -} diff --git a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttConnAckMessage.java b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttConnAckMessage.java deleted file mode 100644 index 349b1e7f8b..0000000000 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttConnAckMessage.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.mqtt; - -/** - * See MQTTV3.1/connack - */ -public final class MqttConnAckMessage extends MqttMessage { - - public MqttConnAckMessage(MqttFixedHeader mqttFixedHeader, MqttConnAckVariableHeader variableHeader) { - super(mqttFixedHeader, variableHeader); - } - - @Override - public MqttConnAckVariableHeader variableHeader() { - return (MqttConnAckVariableHeader) super.variableHeader(); - } -} diff --git a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttConnAckVariableHeader.java b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttConnAckVariableHeader.java deleted file mode 100644 index 4fc01f63d8..0000000000 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttConnAckVariableHeader.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.mqtt; - -import io.netty.util.internal.StringUtil; - -/** - * Variable header of {@link MqttConnectMessage} - */ -public final class MqttConnAckVariableHeader { - - private final MqttConnectReturnCode connectReturnCode; - - private final boolean sessionPresent; - - private final MqttProperties properties; - - public MqttConnAckVariableHeader(MqttConnectReturnCode connectReturnCode, boolean sessionPresent) { - this(connectReturnCode, sessionPresent, MqttProperties.NO_PROPERTIES); - } - - public MqttConnAckVariableHeader(MqttConnectReturnCode connectReturnCode, boolean sessionPresent, - MqttProperties properties) { - this.connectReturnCode = connectReturnCode; - this.sessionPresent = sessionPresent; - this.properties = MqttProperties.withEmptyDefaults(properties); - } - - public MqttConnectReturnCode connectReturnCode() { - return connectReturnCode; - } - - public boolean isSessionPresent() { - return sessionPresent; - } - - public MqttProperties properties() { - return properties; - } - - @Override - public String toString() { - return new StringBuilder(StringUtil.simpleClassName(this)) - .append('[') - .append("connectReturnCode=").append(connectReturnCode) - .append(", sessionPresent=").append(sessionPresent) - .append(']') - .toString(); - } -} diff --git a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttConnectMessage.java b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttConnectMessage.java deleted file mode 100644 index a7c71f014a..0000000000 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttConnectMessage.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.mqtt; - -/** - * See MQTTV3.1/connect - */ -public final class MqttConnectMessage extends MqttMessage { - - public MqttConnectMessage( - MqttFixedHeader mqttFixedHeader, - MqttConnectVariableHeader variableHeader, - MqttConnectPayload payload) { - super(mqttFixedHeader, variableHeader, payload); - } - - @Override - public MqttConnectVariableHeader variableHeader() { - return (MqttConnectVariableHeader) super.variableHeader(); - } - - @Override - public MqttConnectPayload payload() { - return (MqttConnectPayload) super.payload(); - } -} diff --git a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttConnectPayload.java b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttConnectPayload.java deleted file mode 100644 index 9de27bf16b..0000000000 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttConnectPayload.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.mqtt; - -import java.util.Arrays; - -import io.netty.util.CharsetUtil; -import io.netty.util.internal.StringUtil; - -/** - * Payload of {@link MqttConnectMessage} - */ -public final class MqttConnectPayload { - - private final String clientIdentifier; - private final MqttProperties willProperties; - private final String willTopic; - private final byte[] willMessage; - private final String userName; - private final byte[] password; - - /** - * @deprecated use {@link MqttConnectPayload#MqttConnectPayload(String, - * MqttProperties, String, byte[], String, byte[])} instead - */ - @Deprecated - public MqttConnectPayload( - String clientIdentifier, - String willTopic, - String willMessage, - String userName, - String password) { - this( - clientIdentifier, - MqttProperties.NO_PROPERTIES, - willTopic, - willMessage.getBytes(CharsetUtil.UTF_8), - userName, - password.getBytes(CharsetUtil.UTF_8)); - } - - public MqttConnectPayload( - String clientIdentifier, - String willTopic, - byte[] willMessage, - String userName, - byte[] password) { - this(clientIdentifier, - MqttProperties.NO_PROPERTIES, - willTopic, - willMessage, - userName, - password); - } - - public MqttConnectPayload( - String clientIdentifier, - MqttProperties willProperties, - String willTopic, - byte[] willMessage, - String userName, - byte[] password) { - this.clientIdentifier = clientIdentifier; - this.willProperties = MqttProperties.withEmptyDefaults(willProperties); - this.willTopic = willTopic; - this.willMessage = willMessage; - this.userName = userName; - this.password = password; - } - - public String clientIdentifier() { - return clientIdentifier; - } - - public MqttProperties willProperties() { - return willProperties; - } - - public String willTopic() { - return willTopic; - } - - /** - * @deprecated use {@link MqttConnectPayload#willMessageInBytes()} instead - */ - @Deprecated - public String willMessage() { - return willMessage == null ? null : new String(willMessage, CharsetUtil.UTF_8); - } - - public byte[] willMessageInBytes() { - return willMessage; - } - - public String userName() { - return userName; - } - - /** - * @deprecated use {@link MqttConnectPayload#passwordInBytes()} instead - */ - @Deprecated - public String password() { - return password == null ? null : new String(password, CharsetUtil.UTF_8); - } - - public byte[] passwordInBytes() { - return password; - } - - @Override - public String toString() { - return new StringBuilder(StringUtil.simpleClassName(this)) - .append('[') - .append("clientIdentifier=").append(clientIdentifier) - .append(", willTopic=").append(willTopic) - .append(", willMessage=").append(Arrays.toString(willMessage)) - .append(", userName=").append(userName) - .append(", password=").append(Arrays.toString(password)) - .append(']') - .toString(); - } -} diff --git a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttConnectReturnCode.java b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttConnectReturnCode.java deleted file mode 100644 index f9b2a0ef13..0000000000 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttConnectReturnCode.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.mqtt; - -/** - * Return Code of {@link MqttConnAckMessage} - */ -public enum MqttConnectReturnCode { - CONNECTION_ACCEPTED((byte) 0x00), - //MQTT 3 codes - CONNECTION_REFUSED_UNACCEPTABLE_PROTOCOL_VERSION((byte) 0X01), - CONNECTION_REFUSED_IDENTIFIER_REJECTED((byte) 0x02), - CONNECTION_REFUSED_SERVER_UNAVAILABLE((byte) 0x03), - CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD((byte) 0x04), - CONNECTION_REFUSED_NOT_AUTHORIZED((byte) 0x05), - //MQTT 5 codes - CONNECTION_REFUSED_UNSPECIFIED_ERROR((byte) 0x80), - CONNECTION_REFUSED_MALFORMED_PACKET((byte) 0x81), - CONNECTION_REFUSED_PROTOCOL_ERROR((byte) 0x82), - CONNECTION_REFUSED_IMPLEMENTATION_SPECIFIC((byte) 0x83), - CONNECTION_REFUSED_UNSUPPORTED_PROTOCOL_VERSION((byte) 0x84), - CONNECTION_REFUSED_CLIENT_IDENTIFIER_NOT_VALID((byte) 0x85), - CONNECTION_REFUSED_BAD_USERNAME_OR_PASSWORD((byte) 0x86), - CONNECTION_REFUSED_NOT_AUTHORIZED_5((byte) 0x87), - CONNECTION_REFUSED_SERVER_UNAVAILABLE_5((byte) 0x88), - CONNECTION_REFUSED_SERVER_BUSY((byte) 0x89), - CONNECTION_REFUSED_BANNED((byte) 0x8A), - CONNECTION_REFUSED_BAD_AUTHENTICATION_METHOD((byte) 0x8C), - CONNECTION_REFUSED_TOPIC_NAME_INVALID((byte) 0x90), - CONNECTION_REFUSED_PACKET_TOO_LARGE((byte) 0x95), - CONNECTION_REFUSED_QUOTA_EXCEEDED((byte) 0x97), - CONNECTION_REFUSED_PAYLOAD_FORMAT_INVALID((byte) 0x99), - CONNECTION_REFUSED_RETAIN_NOT_SUPPORTED((byte) 0x9A), - CONNECTION_REFUSED_QOS_NOT_SUPPORTED((byte) 0x9B), - CONNECTION_REFUSED_USE_ANOTHER_SERVER((byte) 0x9C), - CONNECTION_REFUSED_SERVER_MOVED((byte) 0x9D), - CONNECTION_REFUSED_CONNECTION_RATE_EXCEEDED((byte) 0x9F); - - private static final MqttConnectReturnCode[] VALUES; - - static { - MqttConnectReturnCode[] values = values(); - VALUES = new MqttConnectReturnCode[160]; - for (MqttConnectReturnCode code : values) { - final int unsignedByte = code.byteValue & 0xFF; - // Suppress a warning about out of bounds access since the enum contains only correct values - VALUES[unsignedByte] = code; // lgtm [java/index-out-of-bounds] - } - } - - private final byte byteValue; - - MqttConnectReturnCode(byte byteValue) { - this.byteValue = byteValue; - } - - public byte byteValue() { - return byteValue; - } - - public static MqttConnectReturnCode valueOf(byte b) { - final int unsignedByte = b & 0xFF; - MqttConnectReturnCode mqttConnectReturnCode = null; - try { - mqttConnectReturnCode = VALUES[unsignedByte]; - } catch (ArrayIndexOutOfBoundsException ignored) { - // no op - } - if (mqttConnectReturnCode == null) { - throw new IllegalArgumentException("unknown connect return code: " + unsignedByte); - } - return mqttConnectReturnCode; - } -} diff --git a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttConnectVariableHeader.java b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttConnectVariableHeader.java deleted file mode 100644 index 21c42fe8a2..0000000000 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttConnectVariableHeader.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.mqtt; - -import io.netty.util.internal.StringUtil; - -/** - * Variable Header for the {@link MqttConnectMessage} - */ -public final class MqttConnectVariableHeader { - - private final String name; - private final int version; - private final boolean hasUserName; - private final boolean hasPassword; - private final boolean isWillRetain; - private final int willQos; - private final boolean isWillFlag; - private final boolean isCleanSession; - private final int keepAliveTimeSeconds; - private final MqttProperties properties; - - public MqttConnectVariableHeader( - String name, - int version, - boolean hasUserName, - boolean hasPassword, - boolean isWillRetain, - int willQos, - boolean isWillFlag, - boolean isCleanSession, - int keepAliveTimeSeconds) { - this(name, - version, - hasUserName, - hasPassword, - isWillRetain, - willQos, - isWillFlag, - isCleanSession, - keepAliveTimeSeconds, - MqttProperties.NO_PROPERTIES); - } - - public MqttConnectVariableHeader( - String name, - int version, - boolean hasUserName, - boolean hasPassword, - boolean isWillRetain, - int willQos, - boolean isWillFlag, - boolean isCleanSession, - int keepAliveTimeSeconds, - MqttProperties properties) { - this.name = name; - this.version = version; - this.hasUserName = hasUserName; - this.hasPassword = hasPassword; - this.isWillRetain = isWillRetain; - this.willQos = willQos; - this.isWillFlag = isWillFlag; - this.isCleanSession = isCleanSession; - this.keepAliveTimeSeconds = keepAliveTimeSeconds; - this.properties = MqttProperties.withEmptyDefaults(properties); - } - - public String name() { - return name; - } - - public int version() { - return version; - } - - public boolean hasUserName() { - return hasUserName; - } - - public boolean hasPassword() { - return hasPassword; - } - - public boolean isWillRetain() { - return isWillRetain; - } - - public int willQos() { - return willQos; - } - - public boolean isWillFlag() { - return isWillFlag; - } - - public boolean isCleanSession() { - return isCleanSession; - } - - public int keepAliveTimeSeconds() { - return keepAliveTimeSeconds; - } - - public MqttProperties properties() { - return properties; - } - - @Override - public String toString() { - return new StringBuilder(StringUtil.simpleClassName(this)) - .append('[') - .append("name=").append(name) - .append(", version=").append(version) - .append(", hasUserName=").append(hasUserName) - .append(", hasPassword=").append(hasPassword) - .append(", isWillRetain=").append(isWillRetain) - .append(", isWillFlag=").append(isWillFlag) - .append(", isCleanSession=").append(isCleanSession) - .append(", keepAliveTimeSeconds=").append(keepAliveTimeSeconds) - .append(']') - .toString(); - } -} diff --git a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttConstant.java b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttConstant.java deleted file mode 100644 index 3de70310b3..0000000000 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttConstant.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.mqtt; - -public final class MqttConstant { - - private MqttConstant() { - } - - /** - * Default max bytes in message - */ - public static final int DEFAULT_MAX_BYTES_IN_MESSAGE = 8092; - - /** - * min client id length - */ - public static final int MIN_CLIENT_ID_LENGTH = 1; - - /** - * Default max client id length,In the mqtt3.1 protocol, - * the default maximum Client Identifier length is 23 - */ - public static final int DEFAULT_MAX_CLIENT_ID_LENGTH = 23; - -} 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 deleted file mode 100644 index 24558775b0..0000000000 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttDecoder.java +++ /dev/null @@ -1,829 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.mqtt; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.DecoderException; -import io.netty.handler.codec.ReplayingDecoder; -import io.netty.handler.codec.TooLongFrameException; -import io.netty.handler.codec.mqtt.MqttDecoder.DecoderState; -import io.netty.handler.codec.mqtt.MqttProperties.IntegerProperty; -import io.netty.util.CharsetUtil; -import io.netty.util.internal.ObjectUtil; - -import java.util.ArrayList; -import java.util.List; - -import static io.netty.handler.codec.mqtt.MqttCodecUtil.isValidClientId; -import static io.netty.handler.codec.mqtt.MqttCodecUtil.isValidMessageId; -import static io.netty.handler.codec.mqtt.MqttCodecUtil.isValidPublishTopicName; -import static io.netty.handler.codec.mqtt.MqttCodecUtil.resetUnusedFields; -import static io.netty.handler.codec.mqtt.MqttCodecUtil.validateFixedHeader; -import static io.netty.handler.codec.mqtt.MqttConstant.DEFAULT_MAX_BYTES_IN_MESSAGE; -import static io.netty.handler.codec.mqtt.MqttConstant.DEFAULT_MAX_CLIENT_ID_LENGTH; -import static io.netty.handler.codec.mqtt.MqttSubscriptionOption.RetainedHandlingPolicy; - -/** - * Decodes Mqtt messages from bytes, following - * the MQTT protocol specification - * v3.1 - * or - * v5.0, depending on the - * version specified in the CONNECT message that first goes through the channel. - */ -public final class MqttDecoder extends ReplayingDecoder { - - /** - * States of the decoder. - * We start at READ_FIXED_HEADER, followed by - * READ_VARIABLE_HEADER and finally READ_PAYLOAD. - */ - enum DecoderState { - READ_FIXED_HEADER, - READ_VARIABLE_HEADER, - READ_PAYLOAD, - BAD_MESSAGE, - } - - private MqttFixedHeader mqttFixedHeader; - private Object variableHeader; - private int bytesRemainingInVariablePart; - - private final int maxBytesInMessage; - private final int maxClientIdLength; - - public MqttDecoder() { - this(DEFAULT_MAX_BYTES_IN_MESSAGE, DEFAULT_MAX_CLIENT_ID_LENGTH); - } - - public MqttDecoder(int maxBytesInMessage) { - this(maxBytesInMessage, DEFAULT_MAX_CLIENT_ID_LENGTH); - } - - public MqttDecoder(int maxBytesInMessage, int maxClientIdLength) { - super(DecoderState.READ_FIXED_HEADER); - this.maxBytesInMessage = ObjectUtil.checkPositive(maxBytesInMessage, "maxBytesInMessage"); - this.maxClientIdLength = ObjectUtil.checkPositive(maxClientIdLength, "maxClientIdLength"); - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception { - switch (state()) { - case READ_FIXED_HEADER: try { - mqttFixedHeader = decodeFixedHeader(ctx, buffer); - bytesRemainingInVariablePart = mqttFixedHeader.remainingLength(); - checkpoint(DecoderState.READ_VARIABLE_HEADER); - // fall through - } catch (Exception cause) { - ctx.fireChannelRead(invalidMessage(cause)); - return; - } - - case READ_VARIABLE_HEADER: try { - final Result decodedVariableHeader = decodeVariableHeader(ctx, buffer, mqttFixedHeader); - variableHeader = decodedVariableHeader.value; - if (bytesRemainingInVariablePart > maxBytesInMessage) { - buffer.skipBytes(actualReadableBytes()); - throw new TooLongFrameException("too large message: " + bytesRemainingInVariablePart + " bytes"); - } - bytesRemainingInVariablePart -= decodedVariableHeader.numberOfBytesConsumed; - checkpoint(DecoderState.READ_PAYLOAD); - // fall through - } catch (Exception cause) { - ctx.fireChannelRead(invalidMessage(cause)); - return; - } - - case READ_PAYLOAD: try { - final Result decodedPayload = - decodePayload( - ctx, - buffer, - mqttFixedHeader.messageType(), - bytesRemainingInVariablePart, - maxClientIdLength, - variableHeader); - bytesRemainingInVariablePart -= decodedPayload.numberOfBytesConsumed; - if (bytesRemainingInVariablePart != 0) { - throw new DecoderException( - "non-zero remaining payload bytes: " + - bytesRemainingInVariablePart + " (" + mqttFixedHeader.messageType() + ')'); - } - checkpoint(DecoderState.READ_FIXED_HEADER); - MqttMessage message = MqttMessageFactory.newMessage( - mqttFixedHeader, variableHeader, decodedPayload.value); - mqttFixedHeader = null; - variableHeader = null; - ctx.fireChannelRead(message); - break; - } catch (Exception cause) { - ctx.fireChannelRead(invalidMessage(cause)); - return; - } - - case BAD_MESSAGE: - // Keep discarding until disconnection. - buffer.skipBytes(actualReadableBytes()); - break; - - default: - // Shouldn't reach here. - throw new Error(); - } - } - - private MqttMessage invalidMessage(Throwable cause) { - checkpoint(DecoderState.BAD_MESSAGE); - return MqttMessageFactory.newInvalidMessage(mqttFixedHeader, variableHeader, cause); - } - - /** - * Decodes the fixed header. It's one byte for the flags and then variable - * bytes for the remaining length. - * - * See - * https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/errata01/os/mqtt-v3.1.1-errata01-os-complete.html#_Toc442180841 - * for more information. - * - * @param buffer the buffer to decode from - * @return the fixed header - */ - private static MqttFixedHeader decodeFixedHeader(ChannelHandlerContext ctx, ByteBuf buffer) { - short b1 = buffer.readUnsignedByte(); - - MqttMessageType messageType = MqttMessageType.valueOf(b1 >> 4); - boolean dupFlag = (b1 & 0x08) == 0x08; - int qosLevel = (b1 & 0x06) >> 1; - boolean retain = (b1 & 0x01) != 0; - - switch (messageType) { - case PUBLISH: - if (qosLevel == 3) { - throw new DecoderException("Illegal QOS Level in fixed header of PUBLISH message (" - + qosLevel + ')'); - } - break; - - case PUBREL: - case SUBSCRIBE: - case UNSUBSCRIBE: - if (dupFlag) { - throw new DecoderException("Illegal BIT 3 in fixed header of " + messageType - + " message, must be 0, found 1"); - } - if (qosLevel != 1) { - throw new DecoderException("Illegal QOS Level in fixed header of " + messageType - + " message, must be 1, found " + qosLevel); - } - if (retain) { - throw new DecoderException("Illegal BIT 0 in fixed header of " + messageType - + " message, must be 0, found 1"); - } - break; - - case AUTH: - case CONNACK: - case CONNECT: - case DISCONNECT: - case PINGREQ: - case PINGRESP: - case PUBACK: - case PUBCOMP: - case PUBREC: - case SUBACK: - case UNSUBACK: - if (dupFlag) { - throw new DecoderException("Illegal BIT 3 in fixed header of " + messageType - + " message, must be 0, found 1"); - } - if (qosLevel != 0) { - throw new DecoderException("Illegal BIT 2 or 1 in fixed header of " + messageType - + " message, must be 0, found " + qosLevel); - } - if (retain) { - throw new DecoderException("Illegal BIT 0 in fixed header of " + messageType - + " message, must be 0, found 1"); - } - break; - default: - throw new DecoderException("Unknown message type, do not know how to validate fixed header"); - } - - int remainingLength = 0; - int multiplier = 1; - short digit; - int loops = 0; - do { - digit = buffer.readUnsignedByte(); - remainingLength += (digit & 127) * multiplier; - multiplier *= 128; - loops++; - } while ((digit & 128) != 0 && loops < 4); - - // MQTT protocol limits Remaining Length to 4 bytes - if (loops == 4 && (digit & 128) != 0) { - throw new DecoderException("remaining length exceeds 4 digits (" + messageType + ')'); - } - MqttFixedHeader decodedFixedHeader = - new MqttFixedHeader(messageType, dupFlag, MqttQoS.valueOf(qosLevel), retain, remainingLength); - return validateFixedHeader(ctx, resetUnusedFields(decodedFixedHeader)); - } - - /** - * Decodes the variable header (if any) - * @param buffer the buffer to decode from - * @param mqttFixedHeader MqttFixedHeader of the same message - * @return the variable header - */ - private Result decodeVariableHeader(ChannelHandlerContext ctx, ByteBuf buffer, MqttFixedHeader mqttFixedHeader) { - switch (mqttFixedHeader.messageType()) { - case CONNECT: - return decodeConnectionVariableHeader(ctx, buffer); - - case CONNACK: - return decodeConnAckVariableHeader(ctx, buffer); - - case UNSUBSCRIBE: - case SUBSCRIBE: - case SUBACK: - case UNSUBACK: - return decodeMessageIdAndPropertiesVariableHeader(ctx, buffer); - - case PUBACK: - case PUBREC: - case PUBCOMP: - case PUBREL: - return decodePubReplyMessage(buffer); - - case PUBLISH: - return decodePublishVariableHeader(ctx, buffer, mqttFixedHeader); - - case DISCONNECT: - case AUTH: - return decodeReasonCodeAndPropertiesVariableHeader(buffer); - - case PINGREQ: - case PINGRESP: - // Empty variable header - return new Result<>(null, 0); - default: - //shouldn't reach here - throw new DecoderException("Unknown message type: " + mqttFixedHeader.messageType()); - } - } - - private static Result decodeConnectionVariableHeader( - ChannelHandlerContext ctx, - ByteBuf buffer) { - final Result protoString = decodeString(buffer); - int numberOfBytesConsumed = protoString.numberOfBytesConsumed; - - final byte protocolLevel = buffer.readByte(); - numberOfBytesConsumed += 1; - - MqttVersion version = MqttVersion.fromProtocolNameAndLevel(protoString.value, protocolLevel); - MqttCodecUtil.setMqttVersion(ctx, version); - - final int b1 = buffer.readUnsignedByte(); - numberOfBytesConsumed += 1; - - final int keepAlive = decodeMsbLsb(buffer); - numberOfBytesConsumed += 2; - - final boolean hasUserName = (b1 & 0x80) == 0x80; - final boolean hasPassword = (b1 & 0x40) == 0x40; - final boolean willRetain = (b1 & 0x20) == 0x20; - final int willQos = (b1 & 0x18) >> 3; - final boolean willFlag = (b1 & 0x04) == 0x04; - final boolean cleanSession = (b1 & 0x02) == 0x02; - if (version == MqttVersion.MQTT_3_1_1 || version == MqttVersion.MQTT_5) { - final boolean zeroReservedFlag = (b1 & 0x01) == 0x0; - if (!zeroReservedFlag) { - // MQTT v3.1.1: The Server MUST validate that the reserved flag in the CONNECT Control Packet is - // set to zero and disconnect the Client if it is not zero. - // See https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc385349230 - throw new DecoderException("non-zero reserved flag"); - } - } - - final MqttProperties properties; - if (version == MqttVersion.MQTT_5) { - final Result propertiesResult = decodeProperties(buffer); - properties = propertiesResult.value; - numberOfBytesConsumed += propertiesResult.numberOfBytesConsumed; - } else { - properties = MqttProperties.NO_PROPERTIES; - } - - final MqttConnectVariableHeader mqttConnectVariableHeader = new MqttConnectVariableHeader( - version.protocolName(), - version.protocolLevel(), - hasUserName, - hasPassword, - willRetain, - willQos, - willFlag, - cleanSession, - keepAlive, - properties); - return new Result<>(mqttConnectVariableHeader, numberOfBytesConsumed); - } - - private static Result decodeConnAckVariableHeader( - ChannelHandlerContext ctx, - ByteBuf buffer) { - final MqttVersion mqttVersion = MqttCodecUtil.getMqttVersion(ctx); - final boolean sessionPresent = (buffer.readUnsignedByte() & 0x01) == 0x01; - byte returnCode = buffer.readByte(); - int numberOfBytesConsumed = 2; - - final MqttProperties properties; - if (mqttVersion == MqttVersion.MQTT_5) { - final Result propertiesResult = decodeProperties(buffer); - properties = propertiesResult.value; - numberOfBytesConsumed += propertiesResult.numberOfBytesConsumed; - } else { - properties = MqttProperties.NO_PROPERTIES; - } - - final MqttConnAckVariableHeader mqttConnAckVariableHeader = - new MqttConnAckVariableHeader(MqttConnectReturnCode.valueOf(returnCode), sessionPresent, properties); - return new Result<>(mqttConnAckVariableHeader, numberOfBytesConsumed); - } - - private static Result decodeMessageIdAndPropertiesVariableHeader( - ChannelHandlerContext ctx, - ByteBuf buffer) { - final MqttVersion mqttVersion = MqttCodecUtil.getMqttVersion(ctx); - final int packetId = decodeMessageId(buffer); - - final MqttMessageIdAndPropertiesVariableHeader mqttVariableHeader; - final int mqtt5Consumed; - - if (mqttVersion == MqttVersion.MQTT_5) { - final Result properties = decodeProperties(buffer); - mqttVariableHeader = new MqttMessageIdAndPropertiesVariableHeader(packetId, properties.value); - mqtt5Consumed = properties.numberOfBytesConsumed; - } else { - mqttVariableHeader = new MqttMessageIdAndPropertiesVariableHeader(packetId, - MqttProperties.NO_PROPERTIES); - mqtt5Consumed = 0; - } - - return new Result(mqttVariableHeader, - 2 + mqtt5Consumed); - } - - private Result decodePubReplyMessage(ByteBuf buffer) { - final int packetId = decodeMessageId(buffer); - - final MqttPubReplyMessageVariableHeader mqttPubAckVariableHeader; - final int consumed; - final int packetIdNumberOfBytesConsumed = 2; - if (bytesRemainingInVariablePart > 3) { - final byte reasonCode = buffer.readByte(); - final Result properties = decodeProperties(buffer); - mqttPubAckVariableHeader = new MqttPubReplyMessageVariableHeader(packetId, - reasonCode, - properties.value); - consumed = packetIdNumberOfBytesConsumed + 1 + properties.numberOfBytesConsumed; - } else if (bytesRemainingInVariablePart > 2) { - final byte reasonCode = buffer.readByte(); - mqttPubAckVariableHeader = new MqttPubReplyMessageVariableHeader(packetId, - reasonCode, - MqttProperties.NO_PROPERTIES); - consumed = packetIdNumberOfBytesConsumed + 1; - } else { - mqttPubAckVariableHeader = new MqttPubReplyMessageVariableHeader(packetId, - (byte) 0, - MqttProperties.NO_PROPERTIES); - consumed = packetIdNumberOfBytesConsumed; - } - - return new Result(mqttPubAckVariableHeader, consumed); - } - - private Result decodeReasonCodeAndPropertiesVariableHeader( - ByteBuf buffer) { - final byte reasonCode; - final MqttProperties properties; - final int consumed; - if (bytesRemainingInVariablePart > 1) { - reasonCode = buffer.readByte(); - final Result propertiesResult = decodeProperties(buffer); - properties = propertiesResult.value; - consumed = 1 + propertiesResult.numberOfBytesConsumed; - } else if (bytesRemainingInVariablePart > 0) { - reasonCode = buffer.readByte(); - properties = MqttProperties.NO_PROPERTIES; - consumed = 1; - } else { - reasonCode = 0; - properties = MqttProperties.NO_PROPERTIES; - consumed = 0; - } - final MqttReasonCodeAndPropertiesVariableHeader mqttReasonAndPropsVariableHeader = - new MqttReasonCodeAndPropertiesVariableHeader(reasonCode, properties); - - return new Result( - mqttReasonAndPropsVariableHeader, - consumed); - } - - private Result decodePublishVariableHeader( - ChannelHandlerContext ctx, - ByteBuf buffer, - MqttFixedHeader mqttFixedHeader) { - final MqttVersion mqttVersion = MqttCodecUtil.getMqttVersion(ctx); - final Result decodedTopic = decodeString(buffer); - if (!isValidPublishTopicName(decodedTopic.value)) { - throw new DecoderException("invalid publish topic name: " + decodedTopic.value + " (contains wildcards)"); - } - int numberOfBytesConsumed = decodedTopic.numberOfBytesConsumed; - - int messageId = -1; - if (mqttFixedHeader.qosLevel().value() > 0) { - messageId = decodeMessageId(buffer); - numberOfBytesConsumed += 2; - } - - final MqttProperties properties; - if (mqttVersion == MqttVersion.MQTT_5) { - final Result propertiesResult = decodeProperties(buffer); - properties = propertiesResult.value; - numberOfBytesConsumed += propertiesResult.numberOfBytesConsumed; - } else { - properties = MqttProperties.NO_PROPERTIES; - } - - final MqttPublishVariableHeader mqttPublishVariableHeader = - new MqttPublishVariableHeader(decodedTopic.value, messageId, properties); - return new Result<>(mqttPublishVariableHeader, numberOfBytesConsumed); - } - - /** - * @return messageId with numberOfBytesConsumed is 2 - */ - private static int decodeMessageId(ByteBuf buffer) { - final int messageId = decodeMsbLsb(buffer); - if (!isValidMessageId(messageId)) { - throw new DecoderException("invalid messageId: " + messageId); - } - return messageId; - } - - /** - * Decodes the payload. - * - * @param buffer the buffer to decode from - * @param messageType type of the message being decoded - * @param bytesRemainingInVariablePart bytes remaining - * @param variableHeader variable header of the same message - * @return the payload - */ - private static Result decodePayload( - ChannelHandlerContext ctx, - ByteBuf buffer, - MqttMessageType messageType, - int bytesRemainingInVariablePart, - int maxClientIdLength, - Object variableHeader) { - switch (messageType) { - case CONNECT: - return decodeConnectionPayload(buffer, maxClientIdLength, (MqttConnectVariableHeader) variableHeader); - - case SUBSCRIBE: - return decodeSubscribePayload(buffer, bytesRemainingInVariablePart); - - case SUBACK: - return decodeSubackPayload(buffer, bytesRemainingInVariablePart); - - case UNSUBSCRIBE: - return decodeUnsubscribePayload(buffer, bytesRemainingInVariablePart); - - case UNSUBACK: - return decodeUnsubAckPayload(ctx, buffer, bytesRemainingInVariablePart); - - case PUBLISH: - return decodePublishPayload(buffer, bytesRemainingInVariablePart); - - default: - // unknown payload , no byte consumed - return new Result<>(null, 0); - } - } - - private static Result decodeConnectionPayload( - ByteBuf buffer, - int maxClientIdLength, - MqttConnectVariableHeader mqttConnectVariableHeader) { - final Result decodedClientId = decodeString(buffer); - final String decodedClientIdValue = decodedClientId.value; - final MqttVersion mqttVersion = MqttVersion.fromProtocolNameAndLevel(mqttConnectVariableHeader.name(), - (byte) mqttConnectVariableHeader.version()); - if (!isValidClientId(mqttVersion, maxClientIdLength, decodedClientIdValue)) { - throw new MqttIdentifierRejectedException("invalid clientIdentifier: " + decodedClientIdValue); - } - int numberOfBytesConsumed = decodedClientId.numberOfBytesConsumed; - - Result decodedWillTopic = null; - byte[] decodedWillMessage = null; - - final MqttProperties willProperties; - if (mqttConnectVariableHeader.isWillFlag()) { - if (mqttVersion == MqttVersion.MQTT_5) { - final Result propertiesResult = decodeProperties(buffer); - willProperties = propertiesResult.value; - numberOfBytesConsumed += propertiesResult.numberOfBytesConsumed; - } else { - willProperties = MqttProperties.NO_PROPERTIES; - } - decodedWillTopic = decodeString(buffer, 0, 32767); - numberOfBytesConsumed += decodedWillTopic.numberOfBytesConsumed; - decodedWillMessage = decodeByteArray(buffer); - numberOfBytesConsumed += decodedWillMessage.length + 2; - } else { - willProperties = MqttProperties.NO_PROPERTIES; - } - Result decodedUserName = null; - byte[] decodedPassword = null; - if (mqttConnectVariableHeader.hasUserName()) { - decodedUserName = decodeString(buffer); - numberOfBytesConsumed += decodedUserName.numberOfBytesConsumed; - } - if (mqttConnectVariableHeader.hasPassword()) { - decodedPassword = decodeByteArray(buffer); - numberOfBytesConsumed += decodedPassword.length + 2; - } - - final MqttConnectPayload mqttConnectPayload = - new MqttConnectPayload( - decodedClientId.value, - willProperties, - decodedWillTopic != null ? decodedWillTopic.value : null, - decodedWillMessage, - decodedUserName != null ? decodedUserName.value : null, - decodedPassword); - return new Result<>(mqttConnectPayload, numberOfBytesConsumed); - } - - private static Result decodeSubscribePayload( - ByteBuf buffer, - int bytesRemainingInVariablePart) { - final List subscribeTopics = new ArrayList<>(); - int numberOfBytesConsumed = 0; - while (numberOfBytesConsumed < bytesRemainingInVariablePart) { - final Result decodedTopicName = decodeString(buffer); - numberOfBytesConsumed += decodedTopicName.numberOfBytesConsumed; - //See 3.8.3.1 Subscription Options of MQTT 5.0 specification for optionByte details - final short optionByte = buffer.readUnsignedByte(); - - MqttQoS qos = MqttQoS.valueOf(optionByte & 0x03); - boolean noLocal = ((optionByte & 0x04) >> 2) == 1; - boolean retainAsPublished = ((optionByte & 0x08) >> 3) == 1; - RetainedHandlingPolicy retainHandling = RetainedHandlingPolicy.valueOf((optionByte & 0x30) >> 4); - - final MqttSubscriptionOption subscriptionOption = new MqttSubscriptionOption(qos, - noLocal, - retainAsPublished, - retainHandling); - - numberOfBytesConsumed++; - subscribeTopics.add(new MqttTopicSubscription(decodedTopicName.value, subscriptionOption)); - } - return new Result<>(new MqttSubscribePayload(subscribeTopics), numberOfBytesConsumed); - } - - private static Result decodeSubackPayload( - ByteBuf buffer, - int bytesRemainingInVariablePart) { - final List grantedQos = new ArrayList<>(bytesRemainingInVariablePart); - int numberOfBytesConsumed = 0; - while (numberOfBytesConsumed < bytesRemainingInVariablePart) { - int reasonCode = buffer.readUnsignedByte(); - numberOfBytesConsumed++; - grantedQos.add(reasonCode); - } - return new Result<>(new MqttSubAckPayload(grantedQos), numberOfBytesConsumed); - } - - private static Result decodeUnsubAckPayload( - ChannelHandlerContext ctx, - ByteBuf buffer, - int bytesRemainingInVariablePart) { - final List reasonCodes = new ArrayList(bytesRemainingInVariablePart); - int numberOfBytesConsumed = 0; - while (numberOfBytesConsumed < bytesRemainingInVariablePart) { - short reasonCode = buffer.readUnsignedByte(); - numberOfBytesConsumed++; - reasonCodes.add(reasonCode); - } - return new Result(new MqttUnsubAckPayload(reasonCodes), numberOfBytesConsumed); - } - - private static Result decodeUnsubscribePayload( - ByteBuf buffer, - int bytesRemainingInVariablePart) { - final List unsubscribeTopics = new ArrayList<>(); - int numberOfBytesConsumed = 0; - while (numberOfBytesConsumed < bytesRemainingInVariablePart) { - final Result decodedTopicName = decodeString(buffer); - numberOfBytesConsumed += decodedTopicName.numberOfBytesConsumed; - unsubscribeTopics.add(decodedTopicName.value); - } - return new Result<>( - new MqttUnsubscribePayload(unsubscribeTopics), - numberOfBytesConsumed); - } - - private static Result decodePublishPayload(ByteBuf buffer, int bytesRemainingInVariablePart) { - ByteBuf b = buffer.readRetainedSlice(bytesRemainingInVariablePart); - return new Result<>(b, bytesRemainingInVariablePart); - } - - private static Result decodeString(ByteBuf buffer) { - return decodeString(buffer, 0, Integer.MAX_VALUE); - } - - private static Result decodeString(ByteBuf buffer, int minBytes, int maxBytes) { - int size = decodeMsbLsb(buffer); - int numberOfBytesConsumed = 2; - if (size < minBytes || size > maxBytes) { - buffer.skipBytes(size); - numberOfBytesConsumed += size; - return new Result<>(null, numberOfBytesConsumed); - } - String s = buffer.toString(buffer.readerIndex(), size, CharsetUtil.UTF_8); - buffer.skipBytes(size); - numberOfBytesConsumed += size; - return new Result<>(s, numberOfBytesConsumed); - } - - /** - * - * @return the decoded byte[], numberOfBytesConsumed = byte[].length + 2 - */ - private static byte[] decodeByteArray(ByteBuf buffer) { - int size = decodeMsbLsb(buffer); - byte[] bytes = new byte[size]; - buffer.readBytes(bytes); - return bytes; - } - - // packing utils to reduce the amount of garbage while decoding ints - private static long packInts(int a, int b) { - return (((long) a) << 32) | (b & 0xFFFFFFFFL); - } - - private static int unpackA(long ints) { - return (int) (ints >> 32); - } - - private static int unpackB(long ints) { - return (int) ints; - } - - /** - * numberOfBytesConsumed = 2. return decoded result. - */ - private static int decodeMsbLsb(ByteBuf buffer) { - int min = 0; - int max = 65535; - short msbSize = buffer.readUnsignedByte(); - short lsbSize = buffer.readUnsignedByte(); - int result = msbSize << 8 | lsbSize; - if (result < min || result > max) { - result = -1; - } - return result; - } - - /** - * See 1.5.5 Variable Byte Integer section of MQTT 5.0 specification for encoding/decoding rules - * - * @param buffer the buffer to decode from - * @return result pack with a = decoded integer, b = numberOfBytesConsumed. Need to unpack to read them. - * @throws DecoderException if bad MQTT protocol limits Remaining Length - */ - private static long decodeVariableByteInteger(ByteBuf buffer) { - int remainingLength = 0; - int multiplier = 1; - short digit; - int loops = 0; - do { - digit = buffer.readUnsignedByte(); - remainingLength += (digit & 127) * multiplier; - multiplier *= 128; - loops++; - } while ((digit & 128) != 0 && loops < 4); - - if (loops == 4 && (digit & 128) != 0) { - throw new DecoderException("MQTT protocol limits Remaining Length to 4 bytes"); - } - return packInts(remainingLength, loops); - } - - private static final class Result { - - private final T value; - private final int numberOfBytesConsumed; - - Result(T value, int numberOfBytesConsumed) { - this.value = value; - this.numberOfBytesConsumed = numberOfBytesConsumed; - } - } - - private static Result decodeProperties(ByteBuf buffer) { - final long propertiesLength = decodeVariableByteInteger(buffer); - int totalPropertiesLength = unpackA(propertiesLength); - int numberOfBytesConsumed = unpackB(propertiesLength); - - MqttProperties decodedProperties = new MqttProperties(); - while (numberOfBytesConsumed < totalPropertiesLength) { - long propertyId = decodeVariableByteInteger(buffer); - final int propertyIdValue = unpackA(propertyId); - numberOfBytesConsumed += unpackB(propertyId); - MqttProperties.MqttPropertyType propertyType = MqttProperties.MqttPropertyType.valueOf(propertyIdValue); - switch (propertyType) { - case PAYLOAD_FORMAT_INDICATOR: - case REQUEST_PROBLEM_INFORMATION: - case REQUEST_RESPONSE_INFORMATION: - case MAXIMUM_QOS: - case RETAIN_AVAILABLE: - case WILDCARD_SUBSCRIPTION_AVAILABLE: - case SUBSCRIPTION_IDENTIFIER_AVAILABLE: - case SHARED_SUBSCRIPTION_AVAILABLE: - final int b1 = buffer.readUnsignedByte(); - numberOfBytesConsumed++; - decodedProperties.add(new IntegerProperty(propertyIdValue, b1)); - break; - case SERVER_KEEP_ALIVE: - case RECEIVE_MAXIMUM: - case TOPIC_ALIAS_MAXIMUM: - case TOPIC_ALIAS: - final int int2BytesResult = decodeMsbLsb(buffer); - numberOfBytesConsumed += 2; - decodedProperties.add(new IntegerProperty(propertyIdValue, int2BytesResult)); - break; - case PUBLICATION_EXPIRY_INTERVAL: - case SESSION_EXPIRY_INTERVAL: - case WILL_DELAY_INTERVAL: - case MAXIMUM_PACKET_SIZE: - final int maxPacketSize = buffer.readInt(); - numberOfBytesConsumed += 4; - decodedProperties.add(new IntegerProperty(propertyIdValue, maxPacketSize)); - break; - case SUBSCRIPTION_IDENTIFIER: - long vbIntegerResult = decodeVariableByteInteger(buffer); - numberOfBytesConsumed += unpackB(vbIntegerResult); - decodedProperties.add(new IntegerProperty(propertyIdValue, unpackA(vbIntegerResult))); - break; - case CONTENT_TYPE: - case RESPONSE_TOPIC: - case ASSIGNED_CLIENT_IDENTIFIER: - case AUTHENTICATION_METHOD: - case RESPONSE_INFORMATION: - case SERVER_REFERENCE: - case REASON_STRING: - final Result stringResult = decodeString(buffer); - numberOfBytesConsumed += stringResult.numberOfBytesConsumed; - decodedProperties.add(new MqttProperties.StringProperty(propertyIdValue, stringResult.value)); - break; - case USER_PROPERTY: - final Result keyResult = decodeString(buffer); - final Result valueResult = decodeString(buffer); - numberOfBytesConsumed += keyResult.numberOfBytesConsumed; - numberOfBytesConsumed += valueResult.numberOfBytesConsumed; - decodedProperties.add(new MqttProperties.UserProperty(keyResult.value, valueResult.value)); - break; - case CORRELATION_DATA: - case AUTHENTICATION_DATA: - final byte[] binaryDataResult = decodeByteArray(buffer); - numberOfBytesConsumed += binaryDataResult.length + 2; - decodedProperties.add(new MqttProperties.BinaryProperty(propertyIdValue, binaryDataResult)); - break; - default: - //shouldn't reach here - throw new DecoderException("Unknown property type: " + propertyType); - } - } - - return new Result(decodedProperties, numberOfBytesConsumed); - } -} diff --git a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttEncoder.java b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttEncoder.java deleted file mode 100644 index 9a601ead1c..0000000000 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttEncoder.java +++ /dev/null @@ -1,741 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.mqtt; - -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.handler.codec.EncoderException; -import io.netty.handler.codec.MessageToMessageEncoder; -import io.netty.util.internal.EmptyArrays; - -import java.util.List; - -import static io.netty.buffer.ByteBufUtil.*; -import static io.netty.handler.codec.mqtt.MqttCodecUtil.getMqttVersion; -import static io.netty.handler.codec.mqtt.MqttCodecUtil.isValidClientId; -import static io.netty.handler.codec.mqtt.MqttCodecUtil.setMqttVersion; -import static io.netty.handler.codec.mqtt.MqttConstant.DEFAULT_MAX_CLIENT_ID_LENGTH; - -/** - * Encodes Mqtt messages into bytes following the protocol specification v3.1 - * as described here MQTTV3.1 - * or v5.0 as described here MQTTv5.0 - - * depending on the version specified in the first CONNECT message that goes through the channel. - */ -@ChannelHandler.Sharable -public final class MqttEncoder extends MessageToMessageEncoder { - - public static final MqttEncoder INSTANCE = new MqttEncoder(); - - private MqttEncoder() { } - - @Override - protected void encode(ChannelHandlerContext ctx, MqttMessage msg, List out) throws Exception { - out.add(doEncode(ctx, msg)); - } - - /** - * This is the main encoding method. - * It's only visible for testing. - * - * @param message MQTT message to encode - * @return ByteBuf with encoded bytes - */ - static ByteBuf doEncode(ChannelHandlerContext ctx, - MqttMessage message) { - - switch (message.fixedHeader().messageType()) { - case CONNECT: - return encodeConnectMessage(ctx, (MqttConnectMessage) message); - - case CONNACK: - return encodeConnAckMessage(ctx, (MqttConnAckMessage) message); - - case PUBLISH: - return encodePublishMessage(ctx, (MqttPublishMessage) message); - - case SUBSCRIBE: - return encodeSubscribeMessage(ctx, (MqttSubscribeMessage) message); - - case UNSUBSCRIBE: - return encodeUnsubscribeMessage(ctx, (MqttUnsubscribeMessage) message); - - case SUBACK: - return encodeSubAckMessage(ctx, (MqttSubAckMessage) message); - - case UNSUBACK: - if (message instanceof MqttUnsubAckMessage) { - return encodeUnsubAckMessage(ctx, (MqttUnsubAckMessage) message); - } - return encodeMessageWithOnlySingleByteFixedHeaderAndMessageId(ctx.alloc(), message); - - case PUBACK: - case PUBREC: - case PUBREL: - case PUBCOMP: - return encodePubReplyMessage(ctx, message); - - case DISCONNECT: - case AUTH: - return encodeReasonCodePlusPropertiesMessage(ctx, message); - - case PINGREQ: - case PINGRESP: - return encodeMessageWithOnlySingleByteFixedHeader(ctx.alloc(), message); - - default: - throw new IllegalArgumentException( - "Unknown message type: " + message.fixedHeader().messageType().value()); - } - } - - private static ByteBuf encodeConnectMessage( - ChannelHandlerContext ctx, - MqttConnectMessage message) { - int payloadBufferSize = 0; - - MqttFixedHeader mqttFixedHeader = message.fixedHeader(); - MqttConnectVariableHeader variableHeader = message.variableHeader(); - MqttConnectPayload payload = message.payload(); - MqttVersion mqttVersion = MqttVersion.fromProtocolNameAndLevel(variableHeader.name(), - (byte) variableHeader.version()); - setMqttVersion(ctx, mqttVersion); - - // as MQTT 3.1 & 3.1.1 spec, If the User Name Flag is set to 0, the Password Flag MUST be set to 0 - if (!variableHeader.hasUserName() && variableHeader.hasPassword()) { - throw new EncoderException("Without a username, the password MUST be not set"); - } - - // Client id - String clientIdentifier = payload.clientIdentifier(); - if (!isValidClientId(mqttVersion, DEFAULT_MAX_CLIENT_ID_LENGTH, clientIdentifier)) { - throw new MqttIdentifierRejectedException("invalid clientIdentifier: " + clientIdentifier); - } - int clientIdentifierBytes = utf8Bytes(clientIdentifier); - payloadBufferSize += 2 + clientIdentifierBytes; - - // Will topic and message - String willTopic = payload.willTopic(); - int willTopicBytes = nullableUtf8Bytes(willTopic); - byte[] willMessage = payload.willMessageInBytes(); - byte[] willMessageBytes = willMessage != null ? willMessage : EmptyArrays.EMPTY_BYTES; - if (variableHeader.isWillFlag()) { - payloadBufferSize += 2 + willTopicBytes; - payloadBufferSize += 2 + willMessageBytes.length; - } - - String userName = payload.userName(); - int userNameBytes = nullableUtf8Bytes(userName); - if (variableHeader.hasUserName()) { - payloadBufferSize += 2 + userNameBytes; - } - - byte[] password = payload.passwordInBytes(); - byte[] passwordBytes = password != null ? password : EmptyArrays.EMPTY_BYTES; - if (variableHeader.hasPassword()) { - payloadBufferSize += 2 + passwordBytes.length; - } - - // Fixed and variable header - byte[] protocolNameBytes = mqttVersion.protocolNameBytes(); - ByteBuf propertiesBuf = encodePropertiesIfNeeded( - mqttVersion, - ctx.alloc(), - message.variableHeader().properties()); - try { - final ByteBuf willPropertiesBuf; - if (variableHeader.isWillFlag()) { - willPropertiesBuf = encodePropertiesIfNeeded(mqttVersion, ctx.alloc(), payload.willProperties()); - payloadBufferSize += willPropertiesBuf.readableBytes(); - } else { - willPropertiesBuf = Unpooled.EMPTY_BUFFER; - } - try { - int variableHeaderBufferSize = 2 + protocolNameBytes.length + 4 + propertiesBuf.readableBytes(); - - int variablePartSize = variableHeaderBufferSize + payloadBufferSize; - int fixedHeaderBufferSize = 1 + getVariableLengthInt(variablePartSize); - ByteBuf buf = ctx.alloc().buffer(fixedHeaderBufferSize + variablePartSize); - buf.writeByte(getFixedHeaderByte1(mqttFixedHeader)); - writeVariableLengthInt(buf, variablePartSize); - - buf.writeShort(protocolNameBytes.length); - buf.writeBytes(protocolNameBytes); - - buf.writeByte(variableHeader.version()); - buf.writeByte(getConnVariableHeaderFlag(variableHeader)); - buf.writeShort(variableHeader.keepAliveTimeSeconds()); - buf.writeBytes(propertiesBuf); - - // Payload - writeExactUTF8String(buf, clientIdentifier, clientIdentifierBytes); - if (variableHeader.isWillFlag()) { - buf.writeBytes(willPropertiesBuf); - writeExactUTF8String(buf, willTopic, willTopicBytes); - buf.writeShort(willMessageBytes.length); - buf.writeBytes(willMessageBytes, 0, willMessageBytes.length); - } - if (variableHeader.hasUserName()) { - writeExactUTF8String(buf, userName, userNameBytes); - } - if (variableHeader.hasPassword()) { - buf.writeShort(passwordBytes.length); - buf.writeBytes(passwordBytes, 0, passwordBytes.length); - } - return buf; - } finally { - willPropertiesBuf.release(); - } - } finally { - propertiesBuf.release(); - } - } - - private static int getConnVariableHeaderFlag(MqttConnectVariableHeader variableHeader) { - int flagByte = 0; - if (variableHeader.hasUserName()) { - flagByte |= 0x80; - } - if (variableHeader.hasPassword()) { - flagByte |= 0x40; - } - if (variableHeader.isWillRetain()) { - flagByte |= 0x20; - } - flagByte |= (variableHeader.willQos() & 0x03) << 3; - if (variableHeader.isWillFlag()) { - flagByte |= 0x04; - } - if (variableHeader.isCleanSession()) { - flagByte |= 0x02; - } - return flagByte; - } - - private static ByteBuf encodeConnAckMessage( - ChannelHandlerContext ctx, - MqttConnAckMessage message) { - final MqttVersion mqttVersion = getMqttVersion(ctx); - ByteBuf propertiesBuf = encodePropertiesIfNeeded(mqttVersion, - ctx.alloc(), - message.variableHeader().properties()); - - try { - ByteBuf buf = ctx.alloc().buffer(4 + propertiesBuf.readableBytes()); - buf.writeByte(getFixedHeaderByte1(message.fixedHeader())); - writeVariableLengthInt(buf, 2 + propertiesBuf.readableBytes()); - buf.writeByte(message.variableHeader().isSessionPresent() ? 0x01 : 0x00); - buf.writeByte(message.variableHeader().connectReturnCode().byteValue()); - buf.writeBytes(propertiesBuf); - return buf; - } finally { - propertiesBuf.release(); - } - } - - private static ByteBuf encodeSubscribeMessage( - ChannelHandlerContext ctx, - MqttSubscribeMessage message) { - MqttVersion mqttVersion = getMqttVersion(ctx); - ByteBuf propertiesBuf = encodePropertiesIfNeeded(mqttVersion, - ctx.alloc(), - message.idAndPropertiesVariableHeader().properties()); - - try { - final int variableHeaderBufferSize = 2 + propertiesBuf.readableBytes(); - int payloadBufferSize = 0; - - MqttFixedHeader mqttFixedHeader = message.fixedHeader(); - MqttMessageIdVariableHeader variableHeader = message.variableHeader(); - MqttSubscribePayload payload = message.payload(); - - for (MqttTopicSubscription topic : payload.topicSubscriptions()) { - String topicName = topic.topicName(); - int topicNameBytes = utf8Bytes(topicName); - payloadBufferSize += 2 + topicNameBytes; - payloadBufferSize += 1; - } - - int variablePartSize = variableHeaderBufferSize + payloadBufferSize; - int fixedHeaderBufferSize = 1 + getVariableLengthInt(variablePartSize); - - ByteBuf buf = ctx.alloc().buffer(fixedHeaderBufferSize + variablePartSize); - buf.writeByte(getFixedHeaderByte1(mqttFixedHeader)); - writeVariableLengthInt(buf, variablePartSize); - - // Variable Header - int messageId = variableHeader.messageId(); - buf.writeShort(messageId); - buf.writeBytes(propertiesBuf); - - // Payload - for (MqttTopicSubscription topic : payload.topicSubscriptions()) { - writeUnsafeUTF8String(buf, topic.topicName()); - if (mqttVersion == MqttVersion.MQTT_3_1_1 || mqttVersion == MqttVersion.MQTT_3_1) { - buf.writeByte(topic.qualityOfService().value()); - } else { - final MqttSubscriptionOption option = topic.option(); - - int optionEncoded = option.retainHandling().value() << 4; - if (option.isRetainAsPublished()) { - optionEncoded |= 0x08; - } - if (option.isNoLocal()) { - optionEncoded |= 0x04; - } - optionEncoded |= option.qos().value(); - - buf.writeByte(optionEncoded); - } - } - - return buf; - } finally { - propertiesBuf.release(); - } - } - - private static ByteBuf encodeUnsubscribeMessage( - ChannelHandlerContext ctx, - MqttUnsubscribeMessage message) { - MqttVersion mqttVersion = getMqttVersion(ctx); - ByteBuf propertiesBuf = encodePropertiesIfNeeded(mqttVersion, - ctx.alloc(), - message.idAndPropertiesVariableHeader().properties()); - - try { - final int variableHeaderBufferSize = 2 + propertiesBuf.readableBytes(); - int payloadBufferSize = 0; - - MqttFixedHeader mqttFixedHeader = message.fixedHeader(); - MqttMessageIdVariableHeader variableHeader = message.variableHeader(); - MqttUnsubscribePayload payload = message.payload(); - - for (String topicName : payload.topics()) { - int topicNameBytes = utf8Bytes(topicName); - payloadBufferSize += 2 + topicNameBytes; - } - - int variablePartSize = variableHeaderBufferSize + payloadBufferSize; - int fixedHeaderBufferSize = 1 + getVariableLengthInt(variablePartSize); - - ByteBuf buf = ctx.alloc().buffer(fixedHeaderBufferSize + variablePartSize); - buf.writeByte(getFixedHeaderByte1(mqttFixedHeader)); - writeVariableLengthInt(buf, variablePartSize); - - // Variable Header - int messageId = variableHeader.messageId(); - buf.writeShort(messageId); - buf.writeBytes(propertiesBuf); - - // Payload - for (String topicName : payload.topics()) { - writeUnsafeUTF8String(buf, topicName); - } - - return buf; - } finally { - propertiesBuf.release(); - } - } - - private static ByteBuf encodeSubAckMessage( - ChannelHandlerContext ctx, - MqttSubAckMessage message) { - MqttVersion mqttVersion = getMqttVersion(ctx); - ByteBuf propertiesBuf = encodePropertiesIfNeeded(mqttVersion, - ctx.alloc(), - message.idAndPropertiesVariableHeader().properties()); - try { - int variableHeaderBufferSize = 2 + propertiesBuf.readableBytes(); - int payloadBufferSize = message.payload().grantedQoSLevels().size(); - int variablePartSize = variableHeaderBufferSize + payloadBufferSize; - int fixedHeaderBufferSize = 1 + getVariableLengthInt(variablePartSize); - ByteBuf buf = ctx.alloc().buffer(fixedHeaderBufferSize + variablePartSize); - buf.writeByte(getFixedHeaderByte1(message.fixedHeader())); - writeVariableLengthInt(buf, variablePartSize); - buf.writeShort(message.variableHeader().messageId()); - buf.writeBytes(propertiesBuf); - for (int code: message.payload().reasonCodes()) { - buf.writeByte(code); - } - - return buf; - } finally { - propertiesBuf.release(); - } - } - - private static ByteBuf encodeUnsubAckMessage( - ChannelHandlerContext ctx, - MqttUnsubAckMessage message) { - if (message.variableHeader() instanceof MqttMessageIdAndPropertiesVariableHeader) { - MqttVersion mqttVersion = getMqttVersion(ctx); - ByteBuf propertiesBuf = encodePropertiesIfNeeded(mqttVersion, - ctx.alloc(), - message.idAndPropertiesVariableHeader().properties()); - try { - int variableHeaderBufferSize = 2 + propertiesBuf.readableBytes(); - MqttUnsubAckPayload payload = message.payload(); - int payloadBufferSize = payload == null ? 0 : payload.unsubscribeReasonCodes().size(); - int variablePartSize = variableHeaderBufferSize + payloadBufferSize; - int fixedHeaderBufferSize = 1 + getVariableLengthInt(variablePartSize); - ByteBuf buf = ctx.alloc().buffer(fixedHeaderBufferSize + variablePartSize); - buf.writeByte(getFixedHeaderByte1(message.fixedHeader())); - writeVariableLengthInt(buf, variablePartSize); - buf.writeShort(message.variableHeader().messageId()); - buf.writeBytes(propertiesBuf); - - if (payload != null) { - for (Short reasonCode : payload.unsubscribeReasonCodes()) { - buf.writeByte(reasonCode); - } - } - - return buf; - } finally { - propertiesBuf.release(); - } - } else { - return encodeMessageWithOnlySingleByteFixedHeaderAndMessageId(ctx.alloc(), message); - } - } - - private static ByteBuf encodePublishMessage( - ChannelHandlerContext ctx, - MqttPublishMessage message) { - MqttVersion mqttVersion = getMqttVersion(ctx); - MqttFixedHeader mqttFixedHeader = message.fixedHeader(); - MqttPublishVariableHeader variableHeader = message.variableHeader(); - ByteBuf payload = message.payload().duplicate(); - - String topicName = variableHeader.topicName(); - int topicNameBytes = utf8Bytes(topicName); - - ByteBuf propertiesBuf = encodePropertiesIfNeeded(mqttVersion, - ctx.alloc(), - message.variableHeader().properties()); - - try { - int variableHeaderBufferSize = 2 + topicNameBytes + - (mqttFixedHeader.qosLevel().value() > 0 ? 2 : 0) + propertiesBuf.readableBytes(); - int payloadBufferSize = payload.readableBytes(); - int variablePartSize = variableHeaderBufferSize + payloadBufferSize; - int fixedHeaderBufferSize = 1 + getVariableLengthInt(variablePartSize); - - ByteBuf buf = ctx.alloc().buffer(fixedHeaderBufferSize + variablePartSize); - buf.writeByte(getFixedHeaderByte1(mqttFixedHeader)); - writeVariableLengthInt(buf, variablePartSize); - writeExactUTF8String(buf, topicName, topicNameBytes); - if (mqttFixedHeader.qosLevel().value() > 0) { - buf.writeShort(variableHeader.packetId()); - } - buf.writeBytes(propertiesBuf); - buf.writeBytes(payload); - - return buf; - } finally { - propertiesBuf.release(); - } - } - - private static ByteBuf encodePubReplyMessage(ChannelHandlerContext ctx, - MqttMessage message) { - if (message.variableHeader() instanceof MqttPubReplyMessageVariableHeader) { - MqttFixedHeader mqttFixedHeader = message.fixedHeader(); - MqttPubReplyMessageVariableHeader variableHeader = - (MqttPubReplyMessageVariableHeader) message.variableHeader(); - int msgId = variableHeader.messageId(); - - final ByteBuf propertiesBuf; - final boolean includeReasonCode; - final int variableHeaderBufferSize; - final MqttVersion mqttVersion = getMqttVersion(ctx); - if (mqttVersion == MqttVersion.MQTT_5 && - (variableHeader.reasonCode() != MqttPubReplyMessageVariableHeader.REASON_CODE_OK || - !variableHeader.properties().isEmpty())) { - propertiesBuf = encodeProperties(ctx.alloc(), variableHeader.properties()); - includeReasonCode = true; - variableHeaderBufferSize = 3 + propertiesBuf.readableBytes(); - } else { - propertiesBuf = Unpooled.EMPTY_BUFFER; - includeReasonCode = false; - variableHeaderBufferSize = 2; - } - - try { - final int fixedHeaderBufferSize = 1 + getVariableLengthInt(variableHeaderBufferSize); - ByteBuf buf = ctx.alloc().buffer(fixedHeaderBufferSize + variableHeaderBufferSize); - buf.writeByte(getFixedHeaderByte1(mqttFixedHeader)); - writeVariableLengthInt(buf, variableHeaderBufferSize); - buf.writeShort(msgId); - if (includeReasonCode) { - buf.writeByte(variableHeader.reasonCode()); - } - buf.writeBytes(propertiesBuf); - - return buf; - } finally { - propertiesBuf.release(); - } - } else { - return encodeMessageWithOnlySingleByteFixedHeaderAndMessageId(ctx.alloc(), message); - } - } - - private static ByteBuf encodeMessageWithOnlySingleByteFixedHeaderAndMessageId( - ByteBufAllocator byteBufAllocator, - MqttMessage message) { - MqttFixedHeader mqttFixedHeader = message.fixedHeader(); - MqttMessageIdVariableHeader variableHeader = (MqttMessageIdVariableHeader) message.variableHeader(); - int msgId = variableHeader.messageId(); - - int variableHeaderBufferSize = 2; // variable part only has a message id - int fixedHeaderBufferSize = 1 + getVariableLengthInt(variableHeaderBufferSize); - ByteBuf buf = byteBufAllocator.buffer(fixedHeaderBufferSize + variableHeaderBufferSize); - buf.writeByte(getFixedHeaderByte1(mqttFixedHeader)); - writeVariableLengthInt(buf, variableHeaderBufferSize); - buf.writeShort(msgId); - - return buf; - } - - private static ByteBuf encodeReasonCodePlusPropertiesMessage( - ChannelHandlerContext ctx, - MqttMessage message) { - if (message.variableHeader() instanceof MqttReasonCodeAndPropertiesVariableHeader) { - MqttVersion mqttVersion = getMqttVersion(ctx); - MqttFixedHeader mqttFixedHeader = message.fixedHeader(); - MqttReasonCodeAndPropertiesVariableHeader variableHeader = - (MqttReasonCodeAndPropertiesVariableHeader) message.variableHeader(); - - final ByteBuf propertiesBuf; - final boolean includeReasonCode; - final int variableHeaderBufferSize; - if (mqttVersion == MqttVersion.MQTT_5 && - (variableHeader.reasonCode() != MqttReasonCodeAndPropertiesVariableHeader.REASON_CODE_OK || - !variableHeader.properties().isEmpty())) { - propertiesBuf = encodeProperties(ctx.alloc(), variableHeader.properties()); - includeReasonCode = true; - variableHeaderBufferSize = 1 + propertiesBuf.readableBytes(); - } else { - propertiesBuf = Unpooled.EMPTY_BUFFER; - includeReasonCode = false; - variableHeaderBufferSize = 0; - } - - try { - final int fixedHeaderBufferSize = 1 + getVariableLengthInt(variableHeaderBufferSize); - ByteBuf buf = ctx.alloc().buffer(fixedHeaderBufferSize + variableHeaderBufferSize); - buf.writeByte(getFixedHeaderByte1(mqttFixedHeader)); - writeVariableLengthInt(buf, variableHeaderBufferSize); - if (includeReasonCode) { - buf.writeByte(variableHeader.reasonCode()); - } - buf.writeBytes(propertiesBuf); - - return buf; - } finally { - propertiesBuf.release(); - } - } else { - return encodeMessageWithOnlySingleByteFixedHeader(ctx.alloc(), message); - } - } - - private static ByteBuf encodeMessageWithOnlySingleByteFixedHeader( - ByteBufAllocator byteBufAllocator, - MqttMessage message) { - MqttFixedHeader mqttFixedHeader = message.fixedHeader(); - ByteBuf buf = byteBufAllocator.buffer(2); - buf.writeByte(getFixedHeaderByte1(mqttFixedHeader)); - buf.writeByte(0); - - return buf; - } - - private static ByteBuf encodePropertiesIfNeeded(MqttVersion mqttVersion, - ByteBufAllocator byteBufAllocator, - MqttProperties mqttProperties) { - if (mqttVersion == MqttVersion.MQTT_5) { - return encodeProperties(byteBufAllocator, mqttProperties); - } - return Unpooled.EMPTY_BUFFER; - } - - private static ByteBuf encodeProperties(ByteBufAllocator byteBufAllocator, - MqttProperties mqttProperties) { - ByteBuf propertiesHeaderBuf = byteBufAllocator.buffer(); - // encode also the Properties part - try { - ByteBuf propertiesBuf = byteBufAllocator.buffer(); - try { - for (MqttProperties.MqttProperty property : mqttProperties.listAll()) { - MqttProperties.MqttPropertyType propertyType = - MqttProperties.MqttPropertyType.valueOf(property.propertyId); - switch (propertyType) { - case PAYLOAD_FORMAT_INDICATOR: - case REQUEST_PROBLEM_INFORMATION: - case REQUEST_RESPONSE_INFORMATION: - case MAXIMUM_QOS: - case RETAIN_AVAILABLE: - case WILDCARD_SUBSCRIPTION_AVAILABLE: - case SUBSCRIPTION_IDENTIFIER_AVAILABLE: - case SHARED_SUBSCRIPTION_AVAILABLE: - writeVariableLengthInt(propertiesBuf, property.propertyId); - final byte bytePropValue = ((MqttProperties.IntegerProperty) property).value.byteValue(); - propertiesBuf.writeByte(bytePropValue); - break; - case SERVER_KEEP_ALIVE: - case RECEIVE_MAXIMUM: - case TOPIC_ALIAS_MAXIMUM: - case TOPIC_ALIAS: - writeVariableLengthInt(propertiesBuf, property.propertyId); - final short twoBytesInPropValue = - ((MqttProperties.IntegerProperty) property).value.shortValue(); - propertiesBuf.writeShort(twoBytesInPropValue); - break; - case PUBLICATION_EXPIRY_INTERVAL: - case SESSION_EXPIRY_INTERVAL: - case WILL_DELAY_INTERVAL: - case MAXIMUM_PACKET_SIZE: - writeVariableLengthInt(propertiesBuf, property.propertyId); - final int fourBytesIntPropValue = ((MqttProperties.IntegerProperty) property).value; - propertiesBuf.writeInt(fourBytesIntPropValue); - break; - case SUBSCRIPTION_IDENTIFIER: - writeVariableLengthInt(propertiesBuf, property.propertyId); - final int vbi = ((MqttProperties.IntegerProperty) property).value; - writeVariableLengthInt(propertiesBuf, vbi); - break; - case CONTENT_TYPE: - case RESPONSE_TOPIC: - case ASSIGNED_CLIENT_IDENTIFIER: - case AUTHENTICATION_METHOD: - case RESPONSE_INFORMATION: - case SERVER_REFERENCE: - case REASON_STRING: - writeVariableLengthInt(propertiesBuf, property.propertyId); - writeEagerUTF8String(propertiesBuf, ((MqttProperties.StringProperty) property).value); - break; - case USER_PROPERTY: - final List pairs = - ((MqttProperties.UserProperties) property).value; - for (MqttProperties.StringPair pair : pairs) { - writeVariableLengthInt(propertiesBuf, property.propertyId); - writeEagerUTF8String(propertiesBuf, pair.key); - writeEagerUTF8String(propertiesBuf, pair.value); - } - break; - case CORRELATION_DATA: - case AUTHENTICATION_DATA: - writeVariableLengthInt(propertiesBuf, property.propertyId); - final byte[] binaryPropValue = ((MqttProperties.BinaryProperty) property).value; - propertiesBuf.writeShort(binaryPropValue.length); - propertiesBuf.writeBytes(binaryPropValue, 0, binaryPropValue.length); - break; - default: - //shouldn't reach here - throw new EncoderException("Unknown property type: " + propertyType); - } - } - writeVariableLengthInt(propertiesHeaderBuf, propertiesBuf.readableBytes()); - propertiesHeaderBuf.writeBytes(propertiesBuf); - - return propertiesHeaderBuf; - } finally { - propertiesBuf.release(); - } - } catch (RuntimeException e) { - propertiesHeaderBuf.release(); - throw e; - } - } - - private static int getFixedHeaderByte1(MqttFixedHeader header) { - int ret = 0; - ret |= header.messageType().value() << 4; - if (header.isDup()) { - ret |= 0x08; - } - ret |= header.qosLevel().value() << 1; - if (header.isRetain()) { - ret |= 0x01; - } - return ret; - } - - private static void writeVariableLengthInt(ByteBuf buf, int num) { - do { - int digit = num % 128; - num /= 128; - if (num > 0) { - digit |= 0x80; - } - buf.writeByte(digit); - } while (num > 0); - } - - private static int nullableUtf8Bytes(String s) { - return s == null? 0 : utf8Bytes(s); - } - - private static int nullableMaxUtf8Bytes(String s) { - return s == null? 0 : utf8MaxBytes(s); - } - - private static void writeExactUTF8String(ByteBuf buf, String s, int utf8Length) { - buf.ensureWritable(utf8Length + 2); - buf.writeShort(utf8Length); - if (utf8Length > 0) { - final int writtenUtf8Length = reserveAndWriteUtf8(buf, s, utf8Length); - assert writtenUtf8Length == utf8Length; - } - } - - private static void writeEagerUTF8String(ByteBuf buf, String s) { - final int maxUtf8Length = nullableMaxUtf8Bytes(s); - buf.ensureWritable(maxUtf8Length + 2); - final int writerIndex = buf.writerIndex(); - final int startUtf8String = writerIndex + 2; - buf.writerIndex(startUtf8String); - final int utf8Length = s != null? reserveAndWriteUtf8(buf, s, maxUtf8Length) : 0; - buf.setShort(writerIndex, utf8Length); - } - - private static void writeUnsafeUTF8String(ByteBuf buf, String s) { - final int writerIndex = buf.writerIndex(); - final int startUtf8String = writerIndex + 2; - // no need to reserve any capacity here, already done earlier: that's why is Unsafe - buf.writerIndex(startUtf8String); - final int utf8Length = s != null? reserveAndWriteUtf8(buf, s, 0) : 0; - buf.setShort(writerIndex, utf8Length); - } - - private static int getVariableLengthInt(int num) { - int count = 0; - do { - num /= 128; - count++; - } while (num > 0); - return count; - } - -} diff --git a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttFixedHeader.java b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttFixedHeader.java deleted file mode 100644 index 023bdb05cd..0000000000 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttFixedHeader.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.mqtt; - -import static java.util.Objects.requireNonNull; - -import io.netty.util.internal.StringUtil; - -/** - * See - * MQTTV3.1/fixed-header - */ -public final class MqttFixedHeader { - - private final MqttMessageType messageType; - private final boolean isDup; - private final MqttQoS qosLevel; - private final boolean isRetain; - private final int remainingLength; - - public MqttFixedHeader( - MqttMessageType messageType, - boolean isDup, - MqttQoS qosLevel, - boolean isRetain, - int remainingLength) { - this.messageType = requireNonNull(messageType, "messageType"); - this.isDup = isDup; - this.qosLevel = requireNonNull(qosLevel, "qosLevel"); - this.isRetain = isRetain; - this.remainingLength = remainingLength; - } - - public MqttMessageType messageType() { - return messageType; - } - - public boolean isDup() { - return isDup; - } - - public MqttQoS qosLevel() { - return qosLevel; - } - - public boolean isRetain() { - return isRetain; - } - - public int remainingLength() { - return remainingLength; - } - - @Override - public String toString() { - return new StringBuilder(StringUtil.simpleClassName(this)) - .append('[') - .append("messageType=").append(messageType) - .append(", isDup=").append(isDup) - .append(", qosLevel=").append(qosLevel) - .append(", isRetain=").append(isRetain) - .append(", remainingLength=").append(remainingLength) - .append(']') - .toString(); - } -} diff --git a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttIdentifierRejectedException.java b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttIdentifierRejectedException.java deleted file mode 100644 index 72639cdd7c..0000000000 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttIdentifierRejectedException.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.mqtt; - -import io.netty.handler.codec.DecoderException; - -/** - * A {@link MqttIdentifierRejectedException} which is thrown when a CONNECT request contains invalid client identifier. - */ -public final class MqttIdentifierRejectedException extends DecoderException { - - private static final long serialVersionUID = -1323503322689614981L; - - /** - * Creates a new instance - */ - public MqttIdentifierRejectedException() { } - - /** - * Creates a new instance - */ - public MqttIdentifierRejectedException(String message, Throwable cause) { - super(message, cause); - } - - /** - * Creates a new instance - */ - public MqttIdentifierRejectedException(String message) { - super(message); - } - - /** - * Creates a new instance - */ - public MqttIdentifierRejectedException(Throwable cause) { - super(cause); - } - -} diff --git a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttMessage.java b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttMessage.java deleted file mode 100644 index 1fd00f855e..0000000000 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttMessage.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.mqtt; - -import io.netty.handler.codec.DecoderResult; -import io.netty.util.internal.StringUtil; - -/** - * Base class for all MQTT message types. - */ -public class MqttMessage { - - private final MqttFixedHeader mqttFixedHeader; - private final Object variableHeader; - private final Object payload; - private final DecoderResult decoderResult; - - // Constants for fixed-header only message types with all flags set to 0 (see - // https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Table_2.2_-) - public static final MqttMessage PINGREQ = new MqttMessage(new MqttFixedHeader(MqttMessageType.PINGREQ, false, - MqttQoS.AT_MOST_ONCE, false, 0)); - - public static final MqttMessage PINGRESP = new MqttMessage(new MqttFixedHeader(MqttMessageType.PINGRESP, false, - MqttQoS.AT_MOST_ONCE, false, 0)); - - public static final MqttMessage DISCONNECT = new MqttMessage(new MqttFixedHeader(MqttMessageType.DISCONNECT, false, - MqttQoS.AT_MOST_ONCE, false, 0)); - - public MqttMessage(MqttFixedHeader mqttFixedHeader) { - this(mqttFixedHeader, null, null); - } - - public MqttMessage(MqttFixedHeader mqttFixedHeader, Object variableHeader) { - this(mqttFixedHeader, variableHeader, null); - } - - public MqttMessage(MqttFixedHeader mqttFixedHeader, Object variableHeader, Object payload) { - this(mqttFixedHeader, variableHeader, payload, DecoderResult.SUCCESS); - } - - public MqttMessage( - MqttFixedHeader mqttFixedHeader, - Object variableHeader, - Object payload, - DecoderResult decoderResult) { - this.mqttFixedHeader = mqttFixedHeader; - this.variableHeader = variableHeader; - this.payload = payload; - this.decoderResult = decoderResult; - } - - public MqttFixedHeader fixedHeader() { - return mqttFixedHeader; - } - - public Object variableHeader() { - return variableHeader; - } - - public Object payload() { - return payload; - } - - public DecoderResult decoderResult() { - return decoderResult; - } - - @Override - public String toString() { - return new StringBuilder(StringUtil.simpleClassName(this)) - .append('[') - .append("fixedHeader=").append(fixedHeader() != null ? fixedHeader().toString() : "") - .append(", variableHeader=").append(variableHeader() != null ? variableHeader.toString() : "") - .append(", payload=").append(payload() != null ? payload.toString() : "") - .append(']') - .toString(); - } -} diff --git a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttMessageBuilders.java b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttMessageBuilders.java deleted file mode 100644 index d07a6dee5f..0000000000 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttMessageBuilders.java +++ /dev/null @@ -1,763 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.mqtt; - -import static io.netty.util.internal.ObjectUtil.checkPositive; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.handler.codec.mqtt.MqttProperties.MqttPropertyType; -import io.netty.util.CharsetUtil; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -public final class MqttMessageBuilders { - - public static final class PublishBuilder { - private String topic; - private boolean retained; - private MqttQoS qos; - private ByteBuf payload; - private int messageId; - private MqttProperties mqttProperties; - - PublishBuilder() { - } - - public PublishBuilder topicName(String topic) { - this.topic = topic; - return this; - } - - public PublishBuilder retained(boolean retained) { - this.retained = retained; - return this; - } - - public PublishBuilder qos(MqttQoS qos) { - this.qos = qos; - return this; - } - - public PublishBuilder payload(ByteBuf payload) { - this.payload = payload; - return this; - } - - public PublishBuilder messageId(int messageId) { - this.messageId = messageId; - return this; - } - - public PublishBuilder properties(MqttProperties properties) { - this.mqttProperties = properties; - return this; - } - - public MqttPublishMessage build() { - MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.PUBLISH, false, qos, retained, 0); - MqttPublishVariableHeader mqttVariableHeader = - new MqttPublishVariableHeader(topic, messageId, mqttProperties); - return new MqttPublishMessage(mqttFixedHeader, mqttVariableHeader, Unpooled.buffer().writeBytes(payload)); - } - } - - public static final class ConnectBuilder { - - private MqttVersion version = MqttVersion.MQTT_3_1_1; - private String clientId; - private boolean cleanSession; - private boolean hasUser; - private boolean hasPassword; - private int keepAliveSecs; - private MqttProperties willProperties = MqttProperties.NO_PROPERTIES; - private boolean willFlag; - private boolean willRetain; - private MqttQoS willQos = MqttQoS.AT_MOST_ONCE; - private String willTopic; - private byte[] willMessage; - private String username; - private byte[] password; - private MqttProperties properties = MqttProperties.NO_PROPERTIES; - - ConnectBuilder() { - } - - public ConnectBuilder protocolVersion(MqttVersion version) { - this.version = version; - return this; - } - - public ConnectBuilder clientId(String clientId) { - this.clientId = clientId; - return this; - } - - public ConnectBuilder cleanSession(boolean cleanSession) { - this.cleanSession = cleanSession; - return this; - } - - public ConnectBuilder keepAlive(int keepAliveSecs) { - this.keepAliveSecs = keepAliveSecs; - return this; - } - - public ConnectBuilder willFlag(boolean willFlag) { - this.willFlag = willFlag; - return this; - } - - public ConnectBuilder willQoS(MqttQoS willQos) { - this.willQos = willQos; - return this; - } - - public ConnectBuilder willTopic(String willTopic) { - this.willTopic = willTopic; - return this; - } - - /** - * @deprecated use {@link ConnectBuilder#willMessage(byte[])} instead - */ - @Deprecated - public ConnectBuilder willMessage(String willMessage) { - willMessage(willMessage == null ? null : willMessage.getBytes(CharsetUtil.UTF_8)); - return this; - } - - public ConnectBuilder willMessage(byte[] willMessage) { - this.willMessage = willMessage; - return this; - } - - public ConnectBuilder willRetain(boolean willRetain) { - this.willRetain = willRetain; - return this; - } - - public ConnectBuilder willProperties(MqttProperties willProperties) { - this.willProperties = willProperties; - return this; - } - - public ConnectBuilder hasUser(boolean value) { - this.hasUser = value; - return this; - } - - public ConnectBuilder hasPassword(boolean value) { - this.hasPassword = value; - return this; - } - - public ConnectBuilder username(String username) { - this.hasUser = username != null; - this.username = username; - return this; - } - - /** - * @deprecated use {@link ConnectBuilder#password(byte[])} instead - */ - @Deprecated - public ConnectBuilder password(String password) { - password(password == null ? null : password.getBytes(CharsetUtil.UTF_8)); - return this; - } - - public ConnectBuilder password(byte[] password) { - this.hasPassword = password != null; - this.password = password; - return this; - } - - public ConnectBuilder properties(MqttProperties properties) { - this.properties = properties; - return this; - } - - public MqttConnectMessage build() { - MqttFixedHeader mqttFixedHeader = - new MqttFixedHeader(MqttMessageType.CONNECT, false, MqttQoS.AT_MOST_ONCE, false, 0); - MqttConnectVariableHeader mqttConnectVariableHeader = - new MqttConnectVariableHeader( - version.protocolName(), - version.protocolLevel(), - hasUser, - hasPassword, - willRetain, - willQos.value(), - willFlag, - cleanSession, - keepAliveSecs, - properties); - MqttConnectPayload mqttConnectPayload = - new MqttConnectPayload(clientId, willProperties, willTopic, willMessage, username, password); - return new MqttConnectMessage(mqttFixedHeader, mqttConnectVariableHeader, mqttConnectPayload); - } - } - - public static final class SubscribeBuilder { - - private List subscriptions; - private int messageId; - private MqttProperties properties; - - SubscribeBuilder() { - } - - public SubscribeBuilder addSubscription(MqttQoS qos, String topic) { - ensureSubscriptionsExist(); - subscriptions.add(new MqttTopicSubscription(topic, qos)); - return this; - } - - public SubscribeBuilder addSubscription(String topic, MqttSubscriptionOption option) { - ensureSubscriptionsExist(); - subscriptions.add(new MqttTopicSubscription(topic, option)); - return this; - } - - public SubscribeBuilder messageId(int messageId) { - this.messageId = messageId; - return this; - } - - public SubscribeBuilder properties(MqttProperties properties) { - this.properties = properties; - return this; - } - - public MqttSubscribeMessage build() { - MqttFixedHeader mqttFixedHeader = - new MqttFixedHeader(MqttMessageType.SUBSCRIBE, false, MqttQoS.AT_LEAST_ONCE, false, 0); - MqttMessageIdAndPropertiesVariableHeader mqttVariableHeader = - new MqttMessageIdAndPropertiesVariableHeader(messageId, properties); - MqttSubscribePayload mqttSubscribePayload = new MqttSubscribePayload(subscriptions); - return new MqttSubscribeMessage(mqttFixedHeader, mqttVariableHeader, mqttSubscribePayload); - } - - private void ensureSubscriptionsExist() { - if (subscriptions == null) { - subscriptions = new ArrayList(5); - } - } - } - - public static final class UnsubscribeBuilder { - - private List topicFilters; - private int messageId; - private MqttProperties properties; - - UnsubscribeBuilder() { - } - - public UnsubscribeBuilder addTopicFilter(String topic) { - if (topicFilters == null) { - topicFilters = new ArrayList<>(5); - } - topicFilters.add(topic); - return this; - } - - public UnsubscribeBuilder messageId(int messageId) { - this.messageId = messageId; - return this; - } - - public UnsubscribeBuilder properties(MqttProperties properties) { - this.properties = properties; - return this; - } - - public MqttUnsubscribeMessage build() { - MqttFixedHeader mqttFixedHeader = - new MqttFixedHeader(MqttMessageType.UNSUBSCRIBE, false, MqttQoS.AT_LEAST_ONCE, false, 0); - MqttMessageIdAndPropertiesVariableHeader mqttVariableHeader = - new MqttMessageIdAndPropertiesVariableHeader(messageId, properties); - MqttUnsubscribePayload mqttSubscribePayload = new MqttUnsubscribePayload(topicFilters); - return new MqttUnsubscribeMessage(mqttFixedHeader, mqttVariableHeader, mqttSubscribePayload); - } - } - - public interface PropertiesInitializer { - void apply(T builder); - } - - public static final class ConnAckBuilder { - - private MqttConnectReturnCode returnCode; - private boolean sessionPresent; - private MqttProperties properties = MqttProperties.NO_PROPERTIES; - private ConnAckPropertiesBuilder propsBuilder; - - private ConnAckBuilder() { - } - - public ConnAckBuilder returnCode(MqttConnectReturnCode returnCode) { - this.returnCode = returnCode; - return this; - } - - public ConnAckBuilder sessionPresent(boolean sessionPresent) { - this.sessionPresent = sessionPresent; - return this; - } - - public ConnAckBuilder properties(MqttProperties properties) { - this.properties = properties; - return this; - } - - public ConnAckBuilder properties(PropertiesInitializer consumer) { - if (propsBuilder == null) { - propsBuilder = new ConnAckPropertiesBuilder(); - } - consumer.apply(propsBuilder); - return this; - } - - public MqttConnAckMessage build() { - if (propsBuilder != null) { - properties = propsBuilder.build(); - } - MqttFixedHeader mqttFixedHeader = - new MqttFixedHeader(MqttMessageType.CONNACK, false, MqttQoS.AT_MOST_ONCE, false, 0); - MqttConnAckVariableHeader mqttConnAckVariableHeader = - new MqttConnAckVariableHeader(returnCode, sessionPresent, properties); - return new MqttConnAckMessage(mqttFixedHeader, mqttConnAckVariableHeader); - } - } - - public static final class ConnAckPropertiesBuilder { - private String clientId; - private Long sessionExpiryInterval; - private int receiveMaximum; - private Byte maximumQos; - private boolean retain; - private Long maximumPacketSize; - private int topicAliasMaximum; - private String reasonString; - private final MqttProperties.UserProperties userProperties = new MqttProperties.UserProperties(); - private Boolean wildcardSubscriptionAvailable; - private Boolean subscriptionIdentifiersAvailable; - private Boolean sharedSubscriptionAvailable; - private Integer serverKeepAlive; - private String responseInformation; - private String serverReference; - private String authenticationMethod; - private byte[] authenticationData; - - public MqttProperties build() { - final MqttProperties props = new MqttProperties(); - if (clientId != null) { - props.add(new MqttProperties.StringProperty(MqttPropertyType.ASSIGNED_CLIENT_IDENTIFIER.value(), - clientId)); - } - if (sessionExpiryInterval != null) { - props.add(new MqttProperties.IntegerProperty( - MqttPropertyType.SESSION_EXPIRY_INTERVAL.value(), sessionExpiryInterval.intValue())); - } - if (receiveMaximum > 0) { - props.add(new MqttProperties.IntegerProperty(MqttPropertyType.RECEIVE_MAXIMUM.value(), receiveMaximum)); - } - if (maximumQos != null) { - props.add(new MqttProperties.IntegerProperty(MqttPropertyType.MAXIMUM_QOS.value(), receiveMaximum)); - } - props.add(new MqttProperties.IntegerProperty(MqttPropertyType.RETAIN_AVAILABLE.value(), retain ? 1 : 0)); - if (maximumPacketSize != null) { - props.add(new MqttProperties.IntegerProperty(MqttPropertyType.MAXIMUM_PACKET_SIZE.value(), - maximumPacketSize.intValue())); - } - props.add(new MqttProperties.IntegerProperty(MqttPropertyType.TOPIC_ALIAS_MAXIMUM.value(), - topicAliasMaximum)); - if (reasonString != null) { - props.add(new MqttProperties.StringProperty(MqttPropertyType.REASON_STRING.value(), reasonString)); - } - props.add(userProperties); - if (wildcardSubscriptionAvailable != null) { - props.add(new MqttProperties.IntegerProperty(MqttPropertyType.WILDCARD_SUBSCRIPTION_AVAILABLE.value(), - wildcardSubscriptionAvailable ? 1 : 0)); - } - if (subscriptionIdentifiersAvailable != null) { - props.add(new MqttProperties.IntegerProperty(MqttPropertyType.SUBSCRIPTION_IDENTIFIER_AVAILABLE.value(), - subscriptionIdentifiersAvailable ? 1 : 0)); - } - if (sharedSubscriptionAvailable != null) { - props.add(new MqttProperties.IntegerProperty(MqttPropertyType.SHARED_SUBSCRIPTION_AVAILABLE.value(), - sharedSubscriptionAvailable ? 1 : 0)); - } - if (serverKeepAlive != null) { - props.add(new MqttProperties.IntegerProperty(MqttPropertyType.SERVER_KEEP_ALIVE.value(), - serverKeepAlive)); - } - if (responseInformation != null) { - props.add(new MqttProperties.StringProperty(MqttPropertyType.RESPONSE_INFORMATION.value(), - responseInformation)); - } - if (serverReference != null) { - props.add(new MqttProperties.StringProperty(MqttPropertyType.SERVER_REFERENCE.value(), - serverReference)); - } - if (authenticationMethod != null) { - props.add(new MqttProperties.StringProperty(MqttPropertyType.AUTHENTICATION_METHOD.value(), - authenticationMethod)); - } - if (authenticationData != null) { - props.add(new MqttProperties.BinaryProperty(MqttPropertyType.AUTHENTICATION_DATA.value(), - authenticationData)); - } - - return props; - } - - public ConnAckPropertiesBuilder sessionExpiryInterval(long seconds) { - this.sessionExpiryInterval = seconds; - return this; - } - - public ConnAckPropertiesBuilder receiveMaximum(int value) { - this.receiveMaximum = checkPositive(value, "value"); - return this; - } - - public ConnAckPropertiesBuilder maximumQos(byte value) { - if (value != 0 && value != 1) { - throw new IllegalArgumentException("maximum QoS property could be 0 or 1"); - } - this.maximumQos = value; - return this; - } - - public ConnAckPropertiesBuilder retainAvailable(boolean retain) { - this.retain = retain; - return this; - } - - public ConnAckPropertiesBuilder maximumPacketSize(long size) { - this.maximumPacketSize = checkPositive(size, "size"); - return this; - } - - public ConnAckPropertiesBuilder assignedClientId(String clientId) { - this.clientId = clientId; - return this; - } - - public ConnAckPropertiesBuilder topicAliasMaximum(int value) { - this.topicAliasMaximum = value; - return this; - } - - public ConnAckPropertiesBuilder reasonString(String reason) { - this.reasonString = reason; - return this; - } - - public ConnAckPropertiesBuilder userProperty(String name, String value) { - userProperties.add(name, value); - return this; - } - - public ConnAckPropertiesBuilder wildcardSubscriptionAvailable(boolean value) { - this.wildcardSubscriptionAvailable = value; - return this; - } - - public ConnAckPropertiesBuilder subscriptionIdentifiersAvailable(boolean value) { - this.subscriptionIdentifiersAvailable = value; - return this; - } - - public ConnAckPropertiesBuilder sharedSubscriptionAvailable(boolean value) { - this.sharedSubscriptionAvailable = value; - return this; - } - - public ConnAckPropertiesBuilder serverKeepAlive(int seconds) { - this.serverKeepAlive = seconds; - return this; - } - - public ConnAckPropertiesBuilder responseInformation(String value) { - this.responseInformation = value; - return this; - } - - public ConnAckPropertiesBuilder serverReference(String host) { - this.serverReference = host; - return this; - } - - public ConnAckPropertiesBuilder authenticationMethod(String methodName) { - this.authenticationMethod = methodName; - return this; - } - - public ConnAckPropertiesBuilder authenticationData(byte[] rawData) { - this.authenticationData = rawData.clone(); - return this; - } - } - - public static final class PubAckBuilder { - - private int packetId; - private byte reasonCode; - private MqttProperties properties; - - PubAckBuilder() { - } - - public PubAckBuilder reasonCode(byte reasonCode) { - this.reasonCode = reasonCode; - return this; - } - - public PubAckBuilder packetId(int packetId) { - this.packetId = packetId; - return this; - } - - /** - * @deprecated use {@link PubAckBuilder#packetId(int)} instead - */ - @Deprecated - public PubAckBuilder packetId(short packetId) { - return packetId(packetId & 0xFFFF); - } - - public PubAckBuilder properties(MqttProperties properties) { - this.properties = properties; - return this; - } - - public MqttMessage build() { - MqttFixedHeader mqttFixedHeader = - new MqttFixedHeader(MqttMessageType.PUBACK, false, MqttQoS.AT_MOST_ONCE, false, 0); - MqttPubReplyMessageVariableHeader mqttPubAckVariableHeader = - new MqttPubReplyMessageVariableHeader(packetId, reasonCode, properties); - return new MqttMessage(mqttFixedHeader, mqttPubAckVariableHeader); - } - } - - public static final class SubAckBuilder { - - private int packetId; - private MqttProperties properties; - private final List grantedQoses = new ArrayList(); - - SubAckBuilder() { - } - - public SubAckBuilder packetId(int packetId) { - this.packetId = packetId; - return this; - } - - /** - * @deprecated use {@link SubAckBuilder#packetId(int)} instead - */ - @Deprecated - public SubAckBuilder packetId(short packetId) { - return packetId(packetId & 0xFFFF); - } - - public SubAckBuilder properties(MqttProperties properties) { - this.properties = properties; - return this; - } - - public SubAckBuilder addGrantedQos(MqttQoS qos) { - this.grantedQoses.add(qos); - return this; - } - - public SubAckBuilder addGrantedQoses(MqttQoS... qoses) { - this.grantedQoses.addAll(Arrays.asList(qoses)); - return this; - } - - public MqttSubAckMessage build() { - MqttFixedHeader mqttFixedHeader = - new MqttFixedHeader(MqttMessageType.SUBACK, false, MqttQoS.AT_MOST_ONCE, false, 0); - MqttMessageIdAndPropertiesVariableHeader mqttSubAckVariableHeader = - new MqttMessageIdAndPropertiesVariableHeader(packetId, properties); - - //transform to primitive types - int[] grantedQoses = new int[this.grantedQoses.size()]; - int i = 0; - for (MqttQoS grantedQos : this.grantedQoses) { - grantedQoses[i++] = grantedQos.value(); - } - - MqttSubAckPayload subAckPayload = new MqttSubAckPayload(grantedQoses); - return new MqttSubAckMessage(mqttFixedHeader, mqttSubAckVariableHeader, subAckPayload); - } - } - - public static final class UnsubAckBuilder { - - private int packetId; - private MqttProperties properties; - private final List reasonCodes = new ArrayList(); - - UnsubAckBuilder() { - } - - public UnsubAckBuilder packetId(int packetId) { - this.packetId = packetId; - return this; - } - - /** - * @deprecated use {@link UnsubAckBuilder#packetId(int)} instead - */ - @Deprecated - public UnsubAckBuilder packetId(short packetId) { - return packetId(packetId & 0xFFFF); - } - - public UnsubAckBuilder properties(MqttProperties properties) { - this.properties = properties; - return this; - } - - public UnsubAckBuilder addReasonCode(short reasonCode) { - this.reasonCodes.add(reasonCode); - return this; - } - - public UnsubAckBuilder addReasonCodes(Short... reasonCodes) { - this.reasonCodes.addAll(Arrays.asList(reasonCodes)); - return this; - } - - public MqttUnsubAckMessage build() { - MqttFixedHeader mqttFixedHeader = - new MqttFixedHeader(MqttMessageType.UNSUBACK, false, MqttQoS.AT_MOST_ONCE, false, 0); - MqttMessageIdAndPropertiesVariableHeader mqttSubAckVariableHeader = - new MqttMessageIdAndPropertiesVariableHeader(packetId, properties); - - MqttUnsubAckPayload subAckPayload = new MqttUnsubAckPayload(reasonCodes); - return new MqttUnsubAckMessage(mqttFixedHeader, mqttSubAckVariableHeader, subAckPayload); - } - } - - public static final class DisconnectBuilder { - - private MqttProperties properties; - private byte reasonCode; - - DisconnectBuilder() { - } - - public DisconnectBuilder properties(MqttProperties properties) { - this.properties = properties; - return this; - } - - public DisconnectBuilder reasonCode(byte reasonCode) { - this.reasonCode = reasonCode; - return this; - } - - public MqttMessage build() { - MqttFixedHeader mqttFixedHeader = - new MqttFixedHeader(MqttMessageType.DISCONNECT, false, MqttQoS.AT_MOST_ONCE, false, 0); - MqttReasonCodeAndPropertiesVariableHeader mqttDisconnectVariableHeader = - new MqttReasonCodeAndPropertiesVariableHeader(reasonCode, properties); - - return new MqttMessage(mqttFixedHeader, mqttDisconnectVariableHeader); - } - } - - public static final class AuthBuilder { - - private MqttProperties properties; - private byte reasonCode; - - AuthBuilder() { - } - - public AuthBuilder properties(MqttProperties properties) { - this.properties = properties; - return this; - } - - public AuthBuilder reasonCode(byte reasonCode) { - this.reasonCode = reasonCode; - return this; - } - - public MqttMessage build() { - MqttFixedHeader mqttFixedHeader = - new MqttFixedHeader(MqttMessageType.AUTH, false, MqttQoS.AT_MOST_ONCE, false, 0); - MqttReasonCodeAndPropertiesVariableHeader mqttAuthVariableHeader = - new MqttReasonCodeAndPropertiesVariableHeader(reasonCode, properties); - - return new MqttMessage(mqttFixedHeader, mqttAuthVariableHeader); - } - } - - public static ConnectBuilder connect() { - return new ConnectBuilder(); - } - - public static ConnAckBuilder connAck() { - return new ConnAckBuilder(); - } - - public static PublishBuilder publish() { - return new PublishBuilder(); - } - - public static SubscribeBuilder subscribe() { - return new SubscribeBuilder(); - } - - public static UnsubscribeBuilder unsubscribe() { - return new UnsubscribeBuilder(); - } - - public static PubAckBuilder pubAck() { - return new PubAckBuilder(); - } - - public static SubAckBuilder subAck() { - return new SubAckBuilder(); - } - - public static UnsubAckBuilder unsubAck() { - return new UnsubAckBuilder(); - } - - public static DisconnectBuilder disconnect() { - return new DisconnectBuilder(); - } - - public static AuthBuilder auth() { - return new AuthBuilder(); - } - - private MqttMessageBuilders() { - } -} 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 deleted file mode 100644 index f06478b1ef..0000000000 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttMessageFactory.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.mqtt; - -import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.DecoderResult; - -/** - * Utility class with factory methods to create different types of MQTT messages. - */ -public final class MqttMessageFactory { - - public static MqttMessage newMessage(MqttFixedHeader mqttFixedHeader, Object variableHeader, Object payload) { - switch (mqttFixedHeader.messageType()) { - case CONNECT : - return new MqttConnectMessage( - mqttFixedHeader, - (MqttConnectVariableHeader) variableHeader, - (MqttConnectPayload) payload); - - case CONNACK: - return new MqttConnAckMessage(mqttFixedHeader, (MqttConnAckVariableHeader) variableHeader); - - case SUBSCRIBE: - return new MqttSubscribeMessage( - mqttFixedHeader, - (MqttMessageIdVariableHeader) variableHeader, - (MqttSubscribePayload) payload); - - case SUBACK: - return new MqttSubAckMessage( - mqttFixedHeader, - (MqttMessageIdVariableHeader) variableHeader, - (MqttSubAckPayload) payload); - - case UNSUBACK: - return new MqttUnsubAckMessage( - mqttFixedHeader, - (MqttMessageIdVariableHeader) variableHeader, - (MqttUnsubAckPayload) payload); - - case UNSUBSCRIBE: - return new MqttUnsubscribeMessage( - mqttFixedHeader, - (MqttMessageIdVariableHeader) variableHeader, - (MqttUnsubscribePayload) payload); - - case PUBLISH: - return new MqttPublishMessage( - mqttFixedHeader, - (MqttPublishVariableHeader) variableHeader, - (ByteBuf) payload); - - case PUBACK: - //Having MqttPubReplyMessageVariableHeader or MqttMessageIdVariableHeader - return new MqttPubAckMessage(mqttFixedHeader, (MqttMessageIdVariableHeader) variableHeader); - case PUBREC: - case PUBREL: - case PUBCOMP: - //Having MqttPubReplyMessageVariableHeader or MqttMessageIdVariableHeader - return new MqttMessage(mqttFixedHeader, variableHeader); - - case PINGREQ: - case PINGRESP: - return new MqttMessage(mqttFixedHeader); - - case DISCONNECT: - case AUTH: - //Having MqttReasonCodeAndPropertiesVariableHeader - return new MqttMessage(mqttFixedHeader, - variableHeader); - - default: - throw new IllegalArgumentException("unknown message type: " + mqttFixedHeader.messageType()); - } - } - - 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/main/java/io/netty/handler/codec/mqtt/MqttMessageIdAndPropertiesVariableHeader.java b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttMessageIdAndPropertiesVariableHeader.java deleted file mode 100644 index f9cd636082..0000000000 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttMessageIdAndPropertiesVariableHeader.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.mqtt; - -import io.netty.util.internal.StringUtil; - -/** - * Variable Header containing, Packet Id and Properties as in MQTT v5 spec. - */ -public final class MqttMessageIdAndPropertiesVariableHeader extends MqttMessageIdVariableHeader { - - private final MqttProperties properties; - - public MqttMessageIdAndPropertiesVariableHeader(int messageId, MqttProperties properties) { - super(messageId); - if (messageId < 1 || messageId > 0xffff) { - throw new IllegalArgumentException("messageId: " + messageId + " (expected: 1 ~ 65535)"); - } - this.properties = MqttProperties.withEmptyDefaults(properties); - } - - public MqttProperties properties() { - return properties; - } - - @Override - public String toString() { - return StringUtil.simpleClassName(this) + "[" + - "messageId=" + messageId() + - ", properties=" + properties + - ']'; - } - - @Override - MqttMessageIdAndPropertiesVariableHeader withDefaultEmptyProperties() { - return this; - } -} diff --git a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttMessageIdVariableHeader.java b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttMessageIdVariableHeader.java deleted file mode 100644 index 2f8d8683b2..0000000000 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttMessageIdVariableHeader.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.mqtt; - -import io.netty.util.internal.StringUtil; - -/** - * Variable Header containing only Message Id - * See MQTTV3.1/msg-id - */ -public class MqttMessageIdVariableHeader { - - private final int messageId; - - public static MqttMessageIdVariableHeader from(int messageId) { - if (messageId < 1 || messageId > 0xffff) { - throw new IllegalArgumentException("messageId: " + messageId + " (expected: 1 ~ 65535)"); - } - return new MqttMessageIdVariableHeader(messageId); - } - - protected MqttMessageIdVariableHeader(int messageId) { - this.messageId = messageId; - } - - public int messageId() { - return messageId; - } - - @Override - public String toString() { - return new StringBuilder(StringUtil.simpleClassName(this)) - .append('[') - .append("messageId=").append(messageId) - .append(']') - .toString(); - } - - public MqttMessageIdAndPropertiesVariableHeader withEmptyProperties() { - return new MqttMessageIdAndPropertiesVariableHeader(messageId, MqttProperties.NO_PROPERTIES); - } - - MqttMessageIdAndPropertiesVariableHeader withDefaultEmptyProperties() { - return withEmptyProperties(); - } -} diff --git a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttMessageType.java b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttMessageType.java deleted file mode 100644 index b9124235da..0000000000 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttMessageType.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.mqtt; - -/** - * MQTT Message Types. - */ -public enum MqttMessageType { - CONNECT(1), - CONNACK(2), - PUBLISH(3), - PUBACK(4), - PUBREC(5), - PUBREL(6), - PUBCOMP(7), - SUBSCRIBE(8), - SUBACK(9), - UNSUBSCRIBE(10), - UNSUBACK(11), - PINGREQ(12), - PINGRESP(13), - DISCONNECT(14), - AUTH(15); - - private static final MqttMessageType[] VALUES; - - static { - // this prevent values to be assigned with the wrong order - // and ensure valueOf to work fine - final MqttMessageType[] values = values(); - VALUES = new MqttMessageType[values.length + 1]; - for (MqttMessageType mqttMessageType : values) { - final int value = mqttMessageType.value; - if (VALUES[value] != null) { - throw new AssertionError("value already in use: " + value); - } - VALUES[value] = mqttMessageType; - } - } - - private final int value; - - MqttMessageType(int value) { - this.value = value; - } - - public int value() { - return value; - } - - public static MqttMessageType valueOf(int type) { - if (type <= 0 || type >= VALUES.length) { - throw new IllegalArgumentException("unknown message type: " + type); - } - return VALUES[type]; - } -} - diff --git a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttProperties.java b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttProperties.java deleted file mode 100644 index 04a52525d1..0000000000 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttProperties.java +++ /dev/null @@ -1,414 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.mqtt; - -import io.netty.util.collection.IntObjectHashMap; - -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.ArrayList; - -/** - * MQTT Properties container - * */ -public final class MqttProperties { - - public enum MqttPropertyType { - // single byte properties - PAYLOAD_FORMAT_INDICATOR(0x01), - REQUEST_PROBLEM_INFORMATION(0x17), - REQUEST_RESPONSE_INFORMATION(0x19), - MAXIMUM_QOS(0x24), - RETAIN_AVAILABLE(0x25), - WILDCARD_SUBSCRIPTION_AVAILABLE(0x28), - SUBSCRIPTION_IDENTIFIER_AVAILABLE(0x29), - SHARED_SUBSCRIPTION_AVAILABLE(0x2A), - - // two bytes properties - SERVER_KEEP_ALIVE(0x13), - RECEIVE_MAXIMUM(0x21), - TOPIC_ALIAS_MAXIMUM(0x22), - TOPIC_ALIAS(0x23), - - // four bytes properties - PUBLICATION_EXPIRY_INTERVAL(0x02), - SESSION_EXPIRY_INTERVAL(0x11), - WILL_DELAY_INTERVAL(0x18), - MAXIMUM_PACKET_SIZE(0x27), - - // Variable Byte Integer - SUBSCRIPTION_IDENTIFIER(0x0B), - - // UTF-8 Encoded String properties - CONTENT_TYPE(0x03), - RESPONSE_TOPIC(0x08), - ASSIGNED_CLIENT_IDENTIFIER(0x12), - AUTHENTICATION_METHOD(0x15), - RESPONSE_INFORMATION(0x1A), - SERVER_REFERENCE(0x1C), - REASON_STRING(0x1F), - USER_PROPERTY(0x26), - - // Binary Data - CORRELATION_DATA(0x09), - AUTHENTICATION_DATA(0x16); - - private static final MqttPropertyType[] VALUES; - - static { - VALUES = new MqttPropertyType[43]; - for (MqttPropertyType v : values()) { - VALUES[v.value] = v; - } - } - - private final int value; - - MqttPropertyType(int value) { - this.value = value; - } - - public int value() { - return value; - } - - public static MqttPropertyType valueOf(int type) { - MqttPropertyType t = null; - try { - t = VALUES[type]; - } catch (ArrayIndexOutOfBoundsException ignored) { - // nop - } - if (t == null) { - throw new IllegalArgumentException("unknown property type: " + type); - } - return t; - } - } - - public static final MqttProperties NO_PROPERTIES = new MqttProperties(false); - - static MqttProperties withEmptyDefaults(MqttProperties properties) { - if (properties == null) { - return MqttProperties.NO_PROPERTIES; - } - return properties; - } - - /** - * MQTT property base class - * - * @param property type - */ - public abstract static class MqttProperty { - final T value; - final int propertyId; - - protected MqttProperty(int propertyId, T value) { - this.propertyId = propertyId; - this.value = value; - } - - /** - * Get MQTT property value - * - * @return property value - */ - public T value() { - return value; - } - - /** - * Get MQTT property ID - * @return property ID - */ - public int propertyId() { - return propertyId; - } - - @Override - public int hashCode() { - return propertyId + 31 * value.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - MqttProperty that = (MqttProperty) obj; - return this.propertyId == that.propertyId && this.value.equals(that.value); - } - } - - public static final class IntegerProperty extends MqttProperty { - - public IntegerProperty(int propertyId, Integer value) { - super(propertyId, value); - } - - @Override - public String toString() { - return "IntegerProperty(" + propertyId + ", " + value + ")"; - } - } - - public static final class StringProperty extends MqttProperty { - - public StringProperty(int propertyId, String value) { - super(propertyId, value); - } - - @Override - public String toString() { - return "StringProperty(" + propertyId + ", " + value + ")"; - } - } - - public static final class StringPair { - public final String key; - public final String value; - - public StringPair(String key, String value) { - this.key = key; - this.value = value; - } - - @Override - public int hashCode() { - return key.hashCode() + 31 * value.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - StringPair that = (StringPair) obj; - - return that.key.equals(this.key) && that.value.equals(this.value); - } - } - - //User properties are the only properties that may be included multiple times and - //are the only properties where ordering is required. Therefore, they need a special handling - public static final class UserProperties extends MqttProperty> { - public UserProperties() { - super(MqttPropertyType.USER_PROPERTY.value, new ArrayList()); - } - - /** - * Create user properties from the collection of the String pair values - * - * @param values string pairs. Collection entries are copied, collection itself isn't shared - */ - public UserProperties(Collection values) { - this(); - this.value.addAll(values); - } - - private static UserProperties fromUserPropertyCollection(Collection properties) { - UserProperties userProperties = new UserProperties(); - for (UserProperty property: properties) { - userProperties.add(new StringPair(property.value.key, property.value.value)); - } - return userProperties; - } - - public void add(StringPair pair) { - this.value.add(pair); - } - - public void add(String key, String value) { - this.value.add(new StringPair(key, value)); - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder("UserProperties("); - boolean first = true; - for (StringPair pair: value) { - if (!first) { - builder.append(", "); - } - builder.append(pair.key + "->" + pair.value); - first = false; - } - builder.append(")"); - return builder.toString(); - } - } - - public static final class UserProperty extends MqttProperty { - public UserProperty(String key, String value) { - super(MqttPropertyType.USER_PROPERTY.value, new StringPair(key, value)); - } - - @Override - public String toString() { - return "UserProperty(" + value.key + ", " + value.value + ")"; - } - } - - public static final class BinaryProperty extends MqttProperty { - - public BinaryProperty(int propertyId, byte[] value) { - super(propertyId, value); - } - - @Override - public String toString() { - return "BinaryProperty(" + propertyId + ", " + value.length + " bytes)"; - } - } - - public MqttProperties() { - this(true); - } - - private MqttProperties(boolean canModify) { - this.canModify = canModify; - } - - private IntObjectHashMap props; - private List userProperties; - private List subscriptionIds; - private final boolean canModify; - - public void add(MqttProperty property) { - if (!canModify) { - throw new UnsupportedOperationException("adding property isn't allowed"); - } - IntObjectHashMap props = this.props; - if (property.propertyId == MqttPropertyType.USER_PROPERTY.value) { - List userProperties = this.userProperties; - if (userProperties == null) { - userProperties = new ArrayList(1); - this.userProperties = userProperties; - } - if (property instanceof UserProperty) { - userProperties.add((UserProperty) property); - } else if (property instanceof UserProperties) { - for (StringPair pair: ((UserProperties) property).value) { - userProperties.add(new UserProperty(pair.key, pair.value)); - } - } else { - throw new IllegalArgumentException("User property must be of UserProperty or UserProperties type"); - } - } else if (property.propertyId == MqttPropertyType.SUBSCRIPTION_IDENTIFIER.value) { - List subscriptionIds = this.subscriptionIds; - if (subscriptionIds == null) { - subscriptionIds = new ArrayList(1); - this.subscriptionIds = subscriptionIds; - } - if (property instanceof IntegerProperty) { - subscriptionIds.add((IntegerProperty) property); - } else { - throw new IllegalArgumentException("Subscription ID must be an integer property"); - } - } else { - if (props == null) { - props = new IntObjectHashMap(); - this.props = props; - } - props.put(property.propertyId, property); - } - } - - public Collection listAll() { - IntObjectHashMap props = this.props; - if (props == null && subscriptionIds == null && userProperties == null) { - return Collections.emptyList(); - } - if (subscriptionIds == null && userProperties == null) { - return props.values(); - } - if (props == null && userProperties == null) { - return subscriptionIds; - } - List propValues = new ArrayList(props != null ? props.size() : 1); - if (props != null) { - propValues.addAll(props.values()); - } - if (subscriptionIds != null) { - propValues.addAll(subscriptionIds); - } - if (userProperties != null) { - propValues.add(UserProperties.fromUserPropertyCollection(userProperties)); - } - return propValues; - } - - public boolean isEmpty() { - IntObjectHashMap props = this.props; - return props == null || props.isEmpty(); - } - - /** - * Get property by ID. If there are multiple properties of this type (can be with Subscription ID) - * then return the first one. - * - * @param propertyId ID of the property - * @return a property if it is set, null otherwise - */ - public MqttProperty getProperty(int propertyId) { - if (propertyId == MqttPropertyType.USER_PROPERTY.value) { - //special handling to keep compatibility with earlier versions - List userProperties = this.userProperties; - if (userProperties == null) { - return null; - } - return UserProperties.fromUserPropertyCollection(userProperties); - } - if (propertyId == MqttPropertyType.SUBSCRIPTION_IDENTIFIER.value) { - List subscriptionIds = this.subscriptionIds; - if (subscriptionIds == null || subscriptionIds.isEmpty()) { - return null; - } - return subscriptionIds.get(0); - } - IntObjectHashMap props = this.props; - return props == null ? null : props.get(propertyId); - } - - /** - * Get properties by ID. - * Some properties (Subscription ID and User Properties) may occur multiple times, - * this method returns all their values in order. - * - * @param propertyId ID of the property - * @return all properties having specified ID - */ - public List getProperties(int propertyId) { - if (propertyId == MqttPropertyType.USER_PROPERTY.value) { - return userProperties == null ? Collections.emptyList() : userProperties; - } - if (propertyId == MqttPropertyType.SUBSCRIPTION_IDENTIFIER.value) { - return subscriptionIds == null ? Collections.emptyList() : subscriptionIds; - } - IntObjectHashMap props = this.props; - return (props == null || !props.containsKey(propertyId)) ? - Collections.emptyList() : - Collections.singletonList(props.get(propertyId)); - } -} diff --git a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttPubAckMessage.java b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttPubAckMessage.java deleted file mode 100644 index 4199bc19ba..0000000000 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttPubAckMessage.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.mqtt; - -/** - * See MQTTV3.1/puback - */ -public final class MqttPubAckMessage extends MqttMessage { - - public MqttPubAckMessage(MqttFixedHeader mqttFixedHeader, MqttMessageIdVariableHeader variableHeader) { - super(mqttFixedHeader, variableHeader); - } - - @Override - public MqttMessageIdVariableHeader variableHeader() { - return (MqttMessageIdVariableHeader) super.variableHeader(); - } -} diff --git a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttPubReplyMessageVariableHeader.java b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttPubReplyMessageVariableHeader.java deleted file mode 100644 index f253dac545..0000000000 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttPubReplyMessageVariableHeader.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.mqtt; - -import io.netty.util.internal.StringUtil; - -/** - * Variable Header containing Packet Id, reason code and Properties as in MQTT v5 spec. - */ -public final class MqttPubReplyMessageVariableHeader extends MqttMessageIdVariableHeader { - - private final byte reasonCode; - private final MqttProperties properties; - - public static final byte REASON_CODE_OK = 0; - - public MqttPubReplyMessageVariableHeader(int messageId, byte reasonCode, MqttProperties properties) { - super(messageId); - if (messageId < 1 || messageId > 0xffff) { - throw new IllegalArgumentException("messageId: " + messageId + " (expected: 1 ~ 65535)"); - } - this.reasonCode = reasonCode; - this.properties = MqttProperties.withEmptyDefaults(properties); - } - - public byte reasonCode() { - return reasonCode; - } - - public MqttProperties properties() { - return properties; - } - - @Override - public String toString() { - return StringUtil.simpleClassName(this) + "[" + - "messageId=" + messageId() + - ", reasonCode=" + reasonCode + - ", properties=" + properties + - ']'; - } -} diff --git a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttPublishMessage.java b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttPublishMessage.java deleted file mode 100644 index 60e70acfb9..0000000000 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttPublishMessage.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.mqtt; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufHolder; -import io.netty.buffer.ByteBufUtil; - -/** - * See MQTTV3.1/publish - */ -public class MqttPublishMessage extends MqttMessage implements ByteBufHolder { - - public MqttPublishMessage( - MqttFixedHeader mqttFixedHeader, - MqttPublishVariableHeader variableHeader, - ByteBuf payload) { - super(mqttFixedHeader, variableHeader, payload); - } - - @Override - public MqttPublishVariableHeader variableHeader() { - return (MqttPublishVariableHeader) super.variableHeader(); - } - - @Override - public ByteBuf payload() { - return content(); - } - - @Override - public ByteBuf content() { - return ByteBufUtil.ensureAccessible((ByteBuf) super.payload()); - } - - @Override - public MqttPublishMessage copy() { - return replace(content().copy()); - } - - @Override - public MqttPublishMessage duplicate() { - return replace(content().duplicate()); - } - - @Override - public MqttPublishMessage retainedDuplicate() { - return replace(content().retainedDuplicate()); - } - - @Override - public MqttPublishMessage replace(ByteBuf content) { - return new MqttPublishMessage(fixedHeader(), variableHeader(), content); - } - - @Override - public int refCnt() { - return content().refCnt(); - } - - @Override - public MqttPublishMessage retain() { - content().retain(); - return this; - } - - @Override - public MqttPublishMessage retain(int increment) { - content().retain(increment); - return this; - } - - @Override - public MqttPublishMessage touch() { - content().touch(); - return this; - } - - @Override - public MqttPublishMessage touch(Object hint) { - content().touch(hint); - return this; - } - - @Override - public boolean release() { - return content().release(); - } - - @Override - public boolean release(int decrement) { - return content().release(decrement); - } - -} diff --git a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttPublishVariableHeader.java b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttPublishVariableHeader.java deleted file mode 100644 index 2e0bbdf83b..0000000000 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttPublishVariableHeader.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.mqtt; - -import io.netty.util.internal.StringUtil; - -/** - * Variable Header of the {@link MqttPublishMessage} - */ -public final class MqttPublishVariableHeader { - - private final String topicName; - private final int packetId; - private final MqttProperties properties; - - public MqttPublishVariableHeader(String topicName, int packetId) { - this(topicName, packetId, MqttProperties.NO_PROPERTIES); - } - - public MqttPublishVariableHeader(String topicName, int packetId, MqttProperties properties) { - this.topicName = topicName; - this.packetId = packetId; - this.properties = MqttProperties.withEmptyDefaults(properties); - } - - public String topicName() { - return topicName; - } - - /** - * @deprecated Use {@link #packetId()} instead. - */ - @Deprecated - public int messageId() { - return packetId; - } - - public int packetId() { - return packetId; - } - - public MqttProperties properties() { - return properties; - } - - @Override - public String toString() { - return new StringBuilder(StringUtil.simpleClassName(this)) - .append('[') - .append("topicName=").append(topicName) - .append(", packetId=").append(packetId) - .append(']') - .toString(); - } -} diff --git a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttQoS.java b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttQoS.java deleted file mode 100644 index 2ebfc0df3e..0000000000 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttQoS.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.mqtt; - -public enum MqttQoS { - AT_MOST_ONCE(0), - AT_LEAST_ONCE(1), - EXACTLY_ONCE(2), - FAILURE(0x80); - - private final int value; - - MqttQoS(int value) { - this.value = value; - } - - public int value() { - return value; - } - - public static MqttQoS valueOf(int value) { - switch (value) { - case 0: - return AT_MOST_ONCE; - case 1: - return AT_LEAST_ONCE; - case 2: - return EXACTLY_ONCE; - case 0x80: - return FAILURE; - default: - throw new IllegalArgumentException("invalid QoS: " + value); - } - } -} diff --git a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttReasonCodeAndPropertiesVariableHeader.java b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttReasonCodeAndPropertiesVariableHeader.java deleted file mode 100644 index 0923b9b116..0000000000 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttReasonCodeAndPropertiesVariableHeader.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.mqtt; - -import io.netty.util.internal.StringUtil; - -/** - * Variable Header for AUTH and DISCONNECT messages represented by {@link MqttMessage} - */ -public final class MqttReasonCodeAndPropertiesVariableHeader { - - private final byte reasonCode; - private final MqttProperties properties; - - public static final byte REASON_CODE_OK = 0; - - public MqttReasonCodeAndPropertiesVariableHeader(byte reasonCode, - MqttProperties properties) { - this.reasonCode = reasonCode; - this.properties = MqttProperties.withEmptyDefaults(properties); - } - - public byte reasonCode() { - return reasonCode; - } - - public MqttProperties properties() { - return properties; - } - - @Override - public String toString() { - return new StringBuilder(StringUtil.simpleClassName(this)) - .append('[') - .append("reasonCode=").append(reasonCode) - .append(", properties=").append(properties) - .append(']') - .toString(); - } -} diff --git a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttSubAckMessage.java b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttSubAckMessage.java deleted file mode 100644 index c69b226e24..0000000000 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttSubAckMessage.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.mqtt; - -/** - * See MQTTV3.1/suback - */ -public final class MqttSubAckMessage extends MqttMessage { - - public MqttSubAckMessage( - MqttFixedHeader mqttFixedHeader, - MqttMessageIdAndPropertiesVariableHeader variableHeader, - MqttSubAckPayload payload) { - super(mqttFixedHeader, variableHeader, payload); - } - - public MqttSubAckMessage( - MqttFixedHeader mqttFixedHeader, - MqttMessageIdVariableHeader variableHeader, - MqttSubAckPayload payload) { - this(mqttFixedHeader, variableHeader.withDefaultEmptyProperties(), payload); - } - - @Override - public MqttMessageIdVariableHeader variableHeader() { - return (MqttMessageIdVariableHeader) super.variableHeader(); - } - - public MqttMessageIdAndPropertiesVariableHeader idAndPropertiesVariableHeader() { - return (MqttMessageIdAndPropertiesVariableHeader) super.variableHeader(); - } - - @Override - public MqttSubAckPayload payload() { - return (MqttSubAckPayload) super.payload(); - } -} diff --git a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttSubAckPayload.java b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttSubAckPayload.java deleted file mode 100644 index 6bf468b816..0000000000 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttSubAckPayload.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.mqtt; - -import static java.util.Objects.requireNonNull; - -import io.netty.util.internal.StringUtil; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * Payload of the {@link MqttSubAckMessage} - */ -public class MqttSubAckPayload { - - private final List reasonCodes; - - public MqttSubAckPayload(int... reasonCodes) { - requireNonNull(reasonCodes, "reasonCodes"); - - List list = new ArrayList(reasonCodes.length); - for (int v: reasonCodes) { - list.add(v); - } - this.reasonCodes = Collections.unmodifiableList(list); - } - - public MqttSubAckPayload(Iterable grantedQoSLevels) { - requireNonNull(grantedQoSLevels, "grantedQoSLevels"); - List list = new ArrayList<>(); - for (Integer v: grantedQoSLevels) { - if (v == null) { - break; - } - list.add(v); - } - this.reasonCodes = Collections.unmodifiableList(list); - } - - public List grantedQoSLevels() { - List qosLevels = new ArrayList<>(reasonCodes.size()); - for (int code: reasonCodes) { - if (code > MqttQoS.EXACTLY_ONCE.value()) { - qosLevels.add(MqttQoS.FAILURE.value()); - } else { - qosLevels.add(code); - } - } - return qosLevels; - } - - public List reasonCodes() { - return reasonCodes; - } - - @Override - public String toString() { - return new StringBuilder(StringUtil.simpleClassName(this)) - .append('[') - .append("reasonCodes=").append(reasonCodes) - .append(']') - .toString(); - } -} diff --git a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttSubscribeMessage.java b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttSubscribeMessage.java deleted file mode 100644 index 86bf0c52ce..0000000000 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttSubscribeMessage.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.mqtt; - -/** - * See - * MQTTV3.1/subscribe - */ -public final class MqttSubscribeMessage extends MqttMessage { - - public MqttSubscribeMessage( - MqttFixedHeader mqttFixedHeader, - MqttMessageIdAndPropertiesVariableHeader variableHeader, - MqttSubscribePayload payload) { - super(mqttFixedHeader, variableHeader, payload); - } - - public MqttSubscribeMessage( - MqttFixedHeader mqttFixedHeader, - MqttMessageIdVariableHeader variableHeader, - MqttSubscribePayload payload) { - this(mqttFixedHeader, variableHeader.withDefaultEmptyProperties(), payload); - } - - @Override - public MqttMessageIdVariableHeader variableHeader() { - return (MqttMessageIdVariableHeader) super.variableHeader(); - } - - public MqttMessageIdAndPropertiesVariableHeader idAndPropertiesVariableHeader() { - return (MqttMessageIdAndPropertiesVariableHeader) super.variableHeader(); - } - - @Override - public MqttSubscribePayload payload() { - return (MqttSubscribePayload) super.payload(); - } -} diff --git a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttSubscribePayload.java b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttSubscribePayload.java deleted file mode 100644 index 2d5c037878..0000000000 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttSubscribePayload.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.mqtt; - -import io.netty.util.internal.StringUtil; - -import java.util.Collections; -import java.util.List; - -/** - * Payload of the {@link MqttSubscribeMessage} - */ -public final class MqttSubscribePayload { - - private final List topicSubscriptions; - - public MqttSubscribePayload(List topicSubscriptions) { - this.topicSubscriptions = Collections.unmodifiableList(topicSubscriptions); - } - - public List topicSubscriptions() { - return topicSubscriptions; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(StringUtil.simpleClassName(this)).append('['); - for (int i = 0; i < topicSubscriptions.size(); i++) { - builder.append(topicSubscriptions.get(i)).append(", "); - } - if (!topicSubscriptions.isEmpty()) { - builder.setLength(builder.length() - 2); - } - return builder.append(']').toString(); - } -} diff --git a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttSubscriptionOption.java b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttSubscriptionOption.java deleted file mode 100644 index 28a6b9cd3b..0000000000 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttSubscriptionOption.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.mqtt; - -/** - * Model the SubscriptionOption used in Subscribe MQTT v5 packet - */ -public final class MqttSubscriptionOption { - - public enum RetainedHandlingPolicy { - SEND_AT_SUBSCRIBE(0), - SEND_AT_SUBSCRIBE_IF_NOT_YET_EXISTS(1), - DONT_SEND_AT_SUBSCRIBE(2); - - private final int value; - - RetainedHandlingPolicy(int value) { - this.value = value; - } - - public int value() { - return value; - } - - public static RetainedHandlingPolicy valueOf(int value) { - switch (value) { - case 0: - return SEND_AT_SUBSCRIBE; - case 1: - return SEND_AT_SUBSCRIBE_IF_NOT_YET_EXISTS; - case 2: - return DONT_SEND_AT_SUBSCRIBE; - default: - throw new IllegalArgumentException("invalid RetainedHandlingPolicy: " + value); - } - } - } - - private final MqttQoS qos; - private final boolean noLocal; - private final boolean retainAsPublished; - private final RetainedHandlingPolicy retainHandling; - - public static MqttSubscriptionOption onlyFromQos(MqttQoS qos) { - return new MqttSubscriptionOption(qos, false, false, RetainedHandlingPolicy.SEND_AT_SUBSCRIBE); - } - - public MqttSubscriptionOption(MqttQoS qos, - boolean noLocal, - boolean retainAsPublished, - RetainedHandlingPolicy retainHandling) { - this.qos = qos; - this.noLocal = noLocal; - this.retainAsPublished = retainAsPublished; - this.retainHandling = retainHandling; - } - - public MqttQoS qos() { - return qos; - } - - public boolean isNoLocal() { - return noLocal; - } - - public boolean isRetainAsPublished() { - return retainAsPublished; - } - - public RetainedHandlingPolicy retainHandling() { - return retainHandling; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - MqttSubscriptionOption that = (MqttSubscriptionOption) o; - - if (noLocal != that.noLocal) { - return false; - } - if (retainAsPublished != that.retainAsPublished) { - return false; - } - if (qos != that.qos) { - return false; - } - return retainHandling == that.retainHandling; - } - - @Override - public int hashCode() { - int result = qos.hashCode(); - result = 31 * result + (noLocal ? 1 : 0); - result = 31 * result + (retainAsPublished ? 1 : 0); - result = 31 * result + retainHandling.hashCode(); - return result; - } - - @Override - public String toString() { - return "SubscriptionOption[" + - "qos=" + qos + - ", noLocal=" + noLocal + - ", retainAsPublished=" + retainAsPublished + - ", retainHandling=" + retainHandling + - ']'; - } -} diff --git a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttTopicSubscription.java b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttTopicSubscription.java deleted file mode 100644 index c565e0394c..0000000000 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttTopicSubscription.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.mqtt; - -import io.netty.util.internal.StringUtil; - -/** - * Contains a topic name and Qos Level. - * This is part of the {@link MqttSubscribePayload} - */ -public final class MqttTopicSubscription { - - private final String topicFilter; - private final MqttSubscriptionOption option; - - public MqttTopicSubscription(String topicFilter, MqttQoS qualityOfService) { - this.topicFilter = topicFilter; - this.option = MqttSubscriptionOption.onlyFromQos(qualityOfService); - } - - public MqttTopicSubscription(String topicFilter, MqttSubscriptionOption option) { - this.topicFilter = topicFilter; - this.option = option; - } - - public String topicName() { - return topicFilter; - } - - public MqttQoS qualityOfService() { - return option.qos(); - } - - public MqttSubscriptionOption option() { - return option; - } - - @Override - public String toString() { - return new StringBuilder(StringUtil.simpleClassName(this)) - .append('[') - .append("topicFilter=").append(topicFilter) - .append(", option=").append(this.option) - .append(']') - .toString(); - } -} diff --git a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttUnacceptableProtocolVersionException.java b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttUnacceptableProtocolVersionException.java deleted file mode 100644 index 10f948e2b4..0000000000 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttUnacceptableProtocolVersionException.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.mqtt; - -import io.netty.handler.codec.DecoderException; - -/** - * A {@link MqttUnacceptableProtocolVersionException} which is thrown when - * a CONNECT request contains unacceptable protocol version. - */ -public final class MqttUnacceptableProtocolVersionException extends DecoderException { - - private static final long serialVersionUID = 4914652213232455749L; - - /** - * Creates a new instance - */ - public MqttUnacceptableProtocolVersionException() { } - - /** - * Creates a new instance - */ - public MqttUnacceptableProtocolVersionException(String message, Throwable cause) { - super(message, cause); - } - - /** - * Creates a new instance - */ - public MqttUnacceptableProtocolVersionException(String message) { - super(message); - } - - /** - * Creates a new instance - */ - public MqttUnacceptableProtocolVersionException(Throwable cause) { - super(cause); - } - -} diff --git a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttUnsubAckMessage.java b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttUnsubAckMessage.java deleted file mode 100644 index c2c291daad..0000000000 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttUnsubAckMessage.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.mqtt; - -/** - * See - * MQTTV3.1/unsuback - */ -public final class MqttUnsubAckMessage extends MqttMessage { - - public MqttUnsubAckMessage(MqttFixedHeader mqttFixedHeader, - MqttMessageIdAndPropertiesVariableHeader variableHeader, - MqttUnsubAckPayload payload) { - super(mqttFixedHeader, variableHeader, MqttUnsubAckPayload.withEmptyDefaults(payload)); - } - - public MqttUnsubAckMessage(MqttFixedHeader mqttFixedHeader, - MqttMessageIdVariableHeader variableHeader, - MqttUnsubAckPayload payload) { - this(mqttFixedHeader, fallbackVariableHeader(variableHeader), payload); - } - public MqttUnsubAckMessage(MqttFixedHeader mqttFixedHeader, - MqttMessageIdVariableHeader variableHeader) { - this(mqttFixedHeader, variableHeader, null); - } - - private static MqttMessageIdAndPropertiesVariableHeader fallbackVariableHeader( - MqttMessageIdVariableHeader variableHeader) { - if (variableHeader instanceof MqttMessageIdAndPropertiesVariableHeader) { - return (MqttMessageIdAndPropertiesVariableHeader) variableHeader; - } - return new MqttMessageIdAndPropertiesVariableHeader(variableHeader.messageId(), MqttProperties.NO_PROPERTIES); - } - - @Override - public MqttMessageIdVariableHeader variableHeader() { - return (MqttMessageIdVariableHeader) super.variableHeader(); - } - - public MqttMessageIdAndPropertiesVariableHeader idAndPropertiesVariableHeader() { - return (MqttMessageIdAndPropertiesVariableHeader) super.variableHeader(); - } - - @Override - public MqttUnsubAckPayload payload() { - return (MqttUnsubAckPayload) super.payload(); - } -} diff --git a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttUnsubAckPayload.java b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttUnsubAckPayload.java deleted file mode 100644 index 705a49b99d..0000000000 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttUnsubAckPayload.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.mqtt; - -import io.netty.util.internal.ObjectUtil; -import io.netty.util.internal.StringUtil; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * Payload for MQTT unsuback message as in V5. - */ -public final class MqttUnsubAckPayload { - - private final List unsubscribeReasonCodes; - - private static final MqttUnsubAckPayload EMPTY = new MqttUnsubAckPayload(); - - public static MqttUnsubAckPayload withEmptyDefaults(MqttUnsubAckPayload payload) { - if (payload == null) { - return EMPTY; - } else { - return payload; - } - } - - public MqttUnsubAckPayload(short... unsubscribeReasonCodes) { - ObjectUtil.checkNotNull(unsubscribeReasonCodes, "unsubscribeReasonCodes"); - - List list = new ArrayList(unsubscribeReasonCodes.length); - for (Short v: unsubscribeReasonCodes) { - list.add(v); - } - this.unsubscribeReasonCodes = Collections.unmodifiableList(list); - } - - public MqttUnsubAckPayload(Iterable unsubscribeReasonCodes) { - ObjectUtil.checkNotNull(unsubscribeReasonCodes, "unsubscribeReasonCodes"); - - List list = new ArrayList(); - for (Short v: unsubscribeReasonCodes) { - ObjectUtil.checkNotNull(v, "unsubscribeReasonCode"); - list.add(v); - } - this.unsubscribeReasonCodes = Collections.unmodifiableList(list); - } - - public List unsubscribeReasonCodes() { - return unsubscribeReasonCodes; - } - - @Override - public String toString() { - return new StringBuilder(StringUtil.simpleClassName(this)) - .append('[') - .append("unsubscribeReasonCodes=").append(unsubscribeReasonCodes) - .append(']') - .toString(); - } -} diff --git a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttUnsubscribeMessage.java b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttUnsubscribeMessage.java deleted file mode 100644 index 9f7fdc9a70..0000000000 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttUnsubscribeMessage.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.mqtt; - -/** - * See - * MQTTV3.1/unsubscribe - */ -public final class MqttUnsubscribeMessage extends MqttMessage { - - public MqttUnsubscribeMessage( - MqttFixedHeader mqttFixedHeader, - MqttMessageIdAndPropertiesVariableHeader variableHeader, - MqttUnsubscribePayload payload) { - super(mqttFixedHeader, variableHeader, payload); - } - - public MqttUnsubscribeMessage( - MqttFixedHeader mqttFixedHeader, - MqttMessageIdVariableHeader variableHeader, - MqttUnsubscribePayload payload) { - this(mqttFixedHeader, variableHeader.withDefaultEmptyProperties(), payload); - } - - @Override - public MqttMessageIdVariableHeader variableHeader() { - return (MqttMessageIdVariableHeader) super.variableHeader(); - } - - public MqttMessageIdAndPropertiesVariableHeader idAndPropertiesVariableHeader() { - return (MqttMessageIdAndPropertiesVariableHeader) super.variableHeader(); - } - - @Override - public MqttUnsubscribePayload payload() { - return (MqttUnsubscribePayload) super.payload(); - } -} diff --git a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttUnsubscribePayload.java b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttUnsubscribePayload.java deleted file mode 100644 index 8df798eec5..0000000000 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttUnsubscribePayload.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.mqtt; - -import io.netty.util.internal.StringUtil; - -import java.util.Collections; -import java.util.List; - -/** - * Payload of the {@link MqttUnsubscribeMessage} - */ -public final class MqttUnsubscribePayload { - - private final List topics; - - public MqttUnsubscribePayload(List topics) { - this.topics = Collections.unmodifiableList(topics); - } - - public List topics() { - return topics; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(StringUtil.simpleClassName(this)).append('['); - for (int i = 0; i < topics.size(); i++) { - builder.append("topicName = ").append(topics.get(i)).append(", "); - } - if (!topics.isEmpty()) { - builder.setLength(builder.length() - 2); - } - return builder.append("]").toString(); - } -} diff --git a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttVersion.java b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttVersion.java deleted file mode 100644 index 74470418cc..0000000000 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/MqttVersion.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.mqtt; - -import static java.util.Objects.requireNonNull; - -import io.netty.util.CharsetUtil; - -/** - * Mqtt version specific constant values used by multiple classes in mqtt-codec. - */ -public enum MqttVersion { - MQTT_3_1("MQIsdp", (byte) 3), - MQTT_3_1_1("MQTT", (byte) 4), - MQTT_5("MQTT", (byte) 5); - - private final String name; - private final byte level; - - MqttVersion(String protocolName, byte protocolLevel) { - name = requireNonNull(protocolName, "protocolName"); - level = protocolLevel; - } - - public String protocolName() { - return name; - } - - public byte[] protocolNameBytes() { - return name.getBytes(CharsetUtil.UTF_8); - } - - public byte protocolLevel() { - return level; - } - - public static MqttVersion fromProtocolNameAndLevel(String protocolName, byte protocolLevel) { - MqttVersion mv = null; - switch (protocolLevel) { - case 3: - mv = MQTT_3_1; - break; - case 4: - mv = MQTT_3_1_1; - break; - case 5: - mv = MQTT_5; - break; - default: - break; - } - if (mv == null) { - throw new MqttUnacceptableProtocolVersionException(protocolName + " is an unknown protocol name"); - } - if (mv.name.equals(protocolName)) { - return mv; - } - throw new MqttUnacceptableProtocolVersionException(protocolName + " and " + protocolLevel + " don't match"); - } -} diff --git a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/package-info.java b/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/package-info.java deleted file mode 100644 index 73a699aed1..0000000000 --- a/codec-mqtt/src/main/java/io/netty/handler/codec/mqtt/package-info.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -/** - * Encoder, decoder and different Message Types for MQTT. - */ -package io.netty.handler.codec.mqtt; 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 deleted file mode 100644 index 283286744e..0000000000 --- a/codec-mqtt/src/test/java/io/netty/handler/codec/mqtt/MqttCodecTest.java +++ /dev/null @@ -1,1102 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.mqtt; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.DecoderException; -import io.netty.handler.codec.EncoderException; -import io.netty.util.Attribute; -import io.netty.util.CharsetUtil; -import io.netty.util.ReferenceCountUtil; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; - -import static io.netty.handler.codec.mqtt.MqttTestUtils.validateProperties; -import static io.netty.handler.codec.mqtt.MqttTestUtils.validateSubscribePayload; -import static io.netty.handler.codec.mqtt.MqttTestUtils.validateUnsubscribePayload; -import static io.netty.handler.codec.mqtt.MqttProperties.MqttPropertyType.*; -import static io.netty.handler.codec.mqtt.MqttQoS.AT_LEAST_ONCE; -import static io.netty.handler.codec.mqtt.MqttSubscriptionOption.RetainedHandlingPolicy.SEND_AT_SUBSCRIBE_IF_NOT_YET_EXISTS; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.clearInvocations; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.CoreMatchers.instanceOf; - -/** - * Unit tests for MqttEncoder and MqttDecoder. - */ -public class MqttCodecTest { - - private static final String CLIENT_ID = "RANDOM_TEST_CLIENT"; - private static final String WILL_TOPIC = "/my_will"; - private static final String WILL_MESSAGE = "gone"; - private static final String USER_NAME = "happy_user"; - private static final String PASSWORD = "123_or_no_pwd"; - - private static final int KEEP_ALIVE_SECONDS = 600; - - private static final ByteBufAllocator ALLOCATOR = new UnpooledByteBufAllocator(false); - - private static final int DEFAULT_MAX_BYTES_IN_MESSAGE = 8092; - - @Mock - private final ChannelHandlerContext ctx = mock(ChannelHandlerContext.class); - - @Mock - private final Channel channel = mock(Channel.class); - - @Mock - private final Attribute versionAttrMock = mock(Attribute.class); - - private final List out = new ArrayList(); - - private final MqttDecoder mqttDecoder = new MqttDecoder(); - - /** - * MqttDecoder with an unrealistic max payload size of 1 byte. - */ - private final MqttDecoder mqttDecoderLimitedMessageSize = new MqttDecoder(1); - @BeforeEach - public void setup() throws Exception { - MockitoAnnotations.initMocks(this); - when(ctx.channel()).thenReturn(channel); - when(ctx.alloc()).thenReturn(ALLOCATOR); - when(ctx.fireChannelRead(any())).then(new Answer() { - @Override - public ChannelHandlerContext answer(InvocationOnMock invocation) { - out.add(invocation.getArguments()[0]); - return ctx; - } - }); - when(channel.attr(MqttCodecUtil.MQTT_VERSION_KEY)).thenReturn(versionAttrMock); - mqttDecoder.handlerAdded(ctx); - mqttDecoderLimitedMessageSize.handlerAdded(ctx); - } - - @AfterEach - public void after() { - for (Object o : out) { - ReferenceCountUtil.release(o); - } - out.clear(); - } - - @Test - public void testConnectMessageForMqtt31() throws Exception { - final MqttConnectMessage message = createConnectMessage(MqttVersion.MQTT_3_1); - ByteBuf byteBuf = MqttEncoder.doEncode(ctx, message); - - ArgumentCaptor captor = ArgumentCaptor.forClass(MqttConnectMessage.class); - mqttDecoder.channelRead(ctx, byteBuf); - verify(ctx).fireChannelRead(captor.capture()); - - final MqttConnectMessage decodedMessage = captor.getValue(); - - validateFixedHeaders(message.fixedHeader(), decodedMessage.fixedHeader()); - validateConnectVariableHeader(message.variableHeader(), decodedMessage.variableHeader()); - validateConnectPayload(message.payload(), decodedMessage.payload()); - } - - @Test - public void testConnectMessageForMqtt311() throws Exception { - final MqttConnectMessage message = createConnectMessage(MqttVersion.MQTT_3_1_1); - ByteBuf byteBuf = MqttEncoder.doEncode(ctx, message); - - ArgumentCaptor captor = ArgumentCaptor.forClass(MqttConnectMessage.class); - mqttDecoder.channelRead(ctx, byteBuf); - verify(ctx).fireChannelRead(captor.capture()); - - final MqttConnectMessage decodedMessage = captor.getValue(); - - validateFixedHeaders(message.fixedHeader(), decodedMessage.fixedHeader()); - validateConnectVariableHeader(message.variableHeader(), decodedMessage.variableHeader()); - validateConnectPayload(message.payload(), decodedMessage.payload()); - } - - @Test - public void testConnectMessageWithNonZeroReservedFlagForMqtt311() throws Exception { - final MqttConnectMessage message = createConnectMessage(MqttVersion.MQTT_3_1_1); - ByteBuf byteBuf = MqttEncoder.doEncode(ctx, message); - // Set the reserved flag in the CONNECT Packet to 1 - byteBuf.setByte(9, byteBuf.getByte(9) | 0x1); - - ArgumentCaptor captor = ArgumentCaptor.forClass(MqttMessage.class); - mqttDecoder.channelRead(ctx, byteBuf); - verify(ctx).fireChannelRead(captor.capture()); - - final MqttMessage decodedMessage = captor.getValue(); - assertTrue(decodedMessage.decoderResult().isFailure()); - Throwable cause = decodedMessage.decoderResult().cause(); - assertThat(cause, instanceOf(DecoderException.class)); - assertEquals("non-zero reserved flag", cause.getMessage()); - } - - @Test - public void testConnectMessageNonZeroReservedBit0Mqtt311() throws Exception { - final MqttConnectMessage message = createConnectMessage(MqttVersion.MQTT_3_1_1); - ByteBuf byteBuf = MqttEncoder.doEncode(ctx, message); - byte firstByte = byteBuf.getByte(0); - byteBuf.setByte(0, (byte) (firstByte | 1)); // set bit 0 to 1 - ArgumentCaptor captor = ArgumentCaptor.forClass(MqttMessage.class); - mqttDecoder.channelRead(ctx, byteBuf); - verify(ctx).fireChannelRead(captor.capture()); - checkForSingleDecoderException(captor); - } - - @Test - public void testConnectMessageNonZeroReservedBit1Mqtt311() throws Exception { - final MqttConnectMessage message = createConnectMessage(MqttVersion.MQTT_3_1_1); - ByteBuf byteBuf = MqttEncoder.doEncode(ctx, message); - byte firstByte = byteBuf.getByte(0); - byteBuf.setByte(0, (byte) (firstByte | 2)); // set bit 1 to 1 - ArgumentCaptor captor = ArgumentCaptor.forClass(MqttMessage.class); - mqttDecoder.channelRead(ctx, byteBuf); - verify(ctx).fireChannelRead(captor.capture()); - checkForSingleDecoderException(captor); - } - - @Test - public void testConnectMessageNonZeroReservedBit2Mqtt311() throws Exception { - final MqttConnectMessage message = createConnectMessage(MqttVersion.MQTT_3_1_1); - ByteBuf byteBuf = MqttEncoder.doEncode(ctx, message); - byte firstByte = byteBuf.getByte(0); - byteBuf.setByte(0, (byte) (firstByte | 4)); // set bit 2 to 1 - ArgumentCaptor captor = ArgumentCaptor.forClass(MqttMessage.class); - mqttDecoder.channelRead(ctx, byteBuf); - verify(ctx).fireChannelRead(captor.capture()); - checkForSingleDecoderException(captor); - } - - @Test - public void testConnectMessageNonZeroReservedBit3Mqtt311() throws Exception { - final MqttConnectMessage message = createConnectMessage(MqttVersion.MQTT_3_1_1); - ByteBuf byteBuf = MqttEncoder.doEncode(ctx, message); - byte firstByte = byteBuf.getByte(0); - byteBuf.setByte(0, (byte) (firstByte | 8)); // set bit 3 to 1 - ArgumentCaptor captor = ArgumentCaptor.forClass(MqttMessage.class); - mqttDecoder.channelRead(ctx, byteBuf); - verify(ctx).fireChannelRead(captor.capture()); - checkForSingleDecoderException(captor); - } - - @Test - public void testSubscribeMessageNonZeroReservedBit0Mqtt311() throws Exception { - final MqttSubscribeMessage message = createSubscribeMessage(); - ByteBuf byteBuf = MqttEncoder.doEncode(ctx, message); - byte firstByte = byteBuf.getByte(0); - byteBuf.setByte(0, (byte) (firstByte | 1)); // set bit 1 to 0 - ArgumentCaptor captor = ArgumentCaptor.forClass(MqttMessage.class); - mqttDecoder.channelRead(ctx, byteBuf); - verify(ctx).fireChannelRead(captor.capture()); - checkForSingleDecoderException(captor); - } - - @Test - public void testSubscribeMessageZeroReservedBit1Mqtt311() throws Exception { - final MqttSubscribeMessage message = createSubscribeMessage(); - ByteBuf byteBuf = MqttEncoder.doEncode(ctx, message); - byte firstByte = byteBuf.getByte(0); - byteBuf.setByte(0, (byte) (firstByte & ~2)); // set bit 1 to 0 - ArgumentCaptor captor = ArgumentCaptor.forClass(MqttMessage.class); - mqttDecoder.channelRead(ctx, byteBuf); - verify(ctx).fireChannelRead(captor.capture()); - checkForSingleDecoderException(captor); - } - - private void checkForSingleDecoderException(ArgumentCaptor captor) { - final MqttMessage result = captor.getValue(); - assertThat(result.decoderResult().cause(), instanceOf(DecoderException.class)); - } - - @Test - public void testConnectMessageNoPassword() throws Exception { - final MqttConnectMessage message = createConnectMessage( - MqttVersion.MQTT_3_1_1, - null, - PASSWORD, - MqttProperties.NO_PROPERTIES, - MqttProperties.NO_PROPERTIES); - - assertThrows(EncoderException.class, new Executable() { - @Override - public void execute() { - MqttEncoder.doEncode(ctx, message); - } - }); - } - - @Test - public void testConnAckMessage() throws Exception { - final MqttConnAckMessage message = createConnAckMessage(); - ByteBuf byteBuf = MqttEncoder.doEncode(ctx, message); - - ArgumentCaptor captor = ArgumentCaptor.forClass(MqttConnAckMessage.class); - mqttDecoder.channelRead(ctx, byteBuf); - verify(ctx).fireChannelRead(captor.capture()); - - final MqttConnAckMessage decodedMessage = captor.getValue(); - validateFixedHeaders(message.fixedHeader(), decodedMessage.fixedHeader()); - validateConnAckVariableHeader(message.variableHeader(), decodedMessage.variableHeader()); - } - - @Test - public void testPublishMessage() throws Exception { - final MqttPublishMessage message = createPublishMessage(); - ByteBuf byteBuf = MqttEncoder.doEncode(ctx, message); - - ArgumentCaptor captor = ArgumentCaptor.forClass(MqttPublishMessage.class); - mqttDecoder.channelRead(ctx, byteBuf); - verify(ctx).fireChannelRead(captor.capture()); - - final MqttPublishMessage decodedMessage = captor.getValue(); - validateFixedHeaders(message.fixedHeader(), decodedMessage.fixedHeader()); - validatePublishVariableHeader(message.variableHeader(), decodedMessage.variableHeader()); - validatePublishPayload(message.payload(), decodedMessage.payload()); - } - - @Test - public void testPubAckMessage() throws Exception { - testMessageWithOnlyFixedHeaderAndMessageIdVariableHeader(MqttMessageType.PUBACK); - } - - @Test - public void testPubRecMessage() throws Exception { - testMessageWithOnlyFixedHeaderAndMessageIdVariableHeader(MqttMessageType.PUBREC); - } - - @Test - public void testPubRelMessage() throws Exception { - testMessageWithOnlyFixedHeaderAndMessageIdVariableHeader(MqttMessageType.PUBREL); - } - - @Test - public void testPubCompMessage() throws Exception { - testMessageWithOnlyFixedHeaderAndMessageIdVariableHeader(MqttMessageType.PUBCOMP); - } - - @Test - public void testSubscribeMessage() throws Exception { - final MqttSubscribeMessage message = createSubscribeMessage(); - ByteBuf byteBuf = MqttEncoder.doEncode(ctx, message); - - ArgumentCaptor captor = ArgumentCaptor.forClass(MqttSubscribeMessage.class); - mqttDecoder.channelRead(ctx, byteBuf); - verify(ctx).fireChannelRead(captor.capture()); - - final MqttSubscribeMessage decodedMessage = captor.getValue(); - validateFixedHeaders(message.fixedHeader(), decodedMessage.fixedHeader()); - validateMessageIdVariableHeader(message.variableHeader(), decodedMessage.variableHeader()); - validateSubscribePayload(message.payload(), decodedMessage.payload()); - } - - @Test - public void testSubAckMessage() throws Exception { - final MqttSubAckMessage message = createSubAckMessage(); - ByteBuf byteBuf = MqttEncoder.doEncode(ctx, message); - - ArgumentCaptor captor = ArgumentCaptor.forClass(MqttSubAckMessage.class); - mqttDecoder.channelRead(ctx, byteBuf); - verify(ctx).fireChannelRead(captor.capture()); - - final MqttSubAckMessage decodedMessage = captor.getValue(); - validateFixedHeaders(message.fixedHeader(), decodedMessage.fixedHeader()); - validateMessageIdVariableHeader(message.variableHeader(), decodedMessage.variableHeader()); - validateSubAckPayload(message.payload(), decodedMessage.payload()); - } - - @Test - public void testSubAckMessageWithFailureInPayload() throws Exception { - MqttFixedHeader mqttFixedHeader = - new MqttFixedHeader(MqttMessageType.SUBACK, false, MqttQoS.AT_MOST_ONCE, false, 0); - MqttMessageIdVariableHeader mqttMessageIdVariableHeader = MqttMessageIdVariableHeader.from(12345); - MqttSubAckPayload mqttSubAckPayload = new MqttSubAckPayload(MqttQoS.FAILURE.value()); - MqttSubAckMessage message = - new MqttSubAckMessage(mqttFixedHeader, mqttMessageIdVariableHeader, mqttSubAckPayload); - - ByteBuf byteBuf = MqttEncoder.doEncode(ctx, message); - - ArgumentCaptor captor = ArgumentCaptor.forClass(MqttSubAckMessage.class); - mqttDecoder.channelRead(ctx, byteBuf); - verify(ctx).fireChannelRead(captor.capture()); - - MqttSubAckMessage decodedMessage = captor.getValue(); - validateFixedHeaders(message.fixedHeader(), decodedMessage.fixedHeader()); - validateMessageIdVariableHeader(message.variableHeader(), decodedMessage.variableHeader()); - validateSubAckPayload(message.payload(), decodedMessage.payload()); - assertEquals(1, decodedMessage.payload().grantedQoSLevels().size()); - assertEquals(MqttQoS.FAILURE, MqttQoS.valueOf(decodedMessage.payload().grantedQoSLevels().get(0))); - } - - @Test - public void testUnSubscribeMessage() throws Exception { - final MqttUnsubscribeMessage message = createUnsubscribeMessage(); - ByteBuf byteBuf = MqttEncoder.doEncode(ctx, message); - - ArgumentCaptor captor = ArgumentCaptor.forClass(MqttUnsubscribeMessage.class); - mqttDecoder.channelRead(ctx, byteBuf); - verify(ctx).fireChannelRead(captor.capture()); - - final MqttUnsubscribeMessage decodedMessage = captor.getValue(); - validateFixedHeaders(message.fixedHeader(), decodedMessage.fixedHeader()); - validateMessageIdVariableHeader(message.variableHeader(), decodedMessage.variableHeader()); - validateUnsubscribePayload(message.payload(), decodedMessage.payload()); - } - - @Test - public void testUnsubAckMessage() throws Exception { - testMessageWithOnlyFixedHeaderAndMessageIdVariableHeader(MqttMessageType.UNSUBACK); - } - - @Test - public void testPingReqMessage() throws Exception { - testMessageWithOnlyFixedHeader(MqttMessage.PINGREQ); - } - - @Test - public void testPingRespMessage() throws Exception { - testMessageWithOnlyFixedHeader(MqttMessage.PINGRESP); - } - - @Test - public void testDisconnectMessage() throws Exception { - testMessageWithOnlyFixedHeader(MqttMessage.DISCONNECT); - } - - //All 0..F message type codes are valid in MQTT 5 - @Test - public void testUnknownMessageType() throws Exception { - - final MqttMessage message = createMessageWithFixedHeader(MqttMessageType.PINGREQ); - ByteBuf byteBuf = MqttEncoder.doEncode(ctx, message); - // setting an invalid message type (15, reserved and forbidden by MQTT 3.1.1 spec) - byteBuf.setByte(0, 0xF0); - - ArgumentCaptor captor = ArgumentCaptor.forClass(MqttMessage.class); - mqttDecoder.channelRead(ctx, byteBuf); - verify(ctx).fireChannelRead(captor.capture()); - - final MqttMessage decodedMessage = captor.getValue(); - assertTrue(decodedMessage.decoderResult().isFailure()); - Throwable cause = decodedMessage.decoderResult().cause(); - assertThat(cause, instanceOf(DecoderException.class)); - assertEquals("AUTH message requires at least MQTT 5", cause.getMessage()); - } - - @Test - public void testConnectMessageForMqtt31TooLarge() throws Exception { - final MqttConnectMessage message = createConnectMessage(MqttVersion.MQTT_3_1); - ByteBuf byteBuf = MqttEncoder.doEncode(ctx, message); - ArgumentCaptor captor = ArgumentCaptor.forClass(MqttMessage.class); - mqttDecoderLimitedMessageSize.channelRead(ctx, byteBuf); - verify(ctx).fireChannelRead(captor.capture()); - - final MqttMessage decodedMessage = captor.getValue(); - assertEquals(0, byteBuf.readableBytes()); - - validateFixedHeaders(message.fixedHeader(), decodedMessage.fixedHeader()); - validateConnectVariableHeader(message.variableHeader(), - (MqttConnectVariableHeader) decodedMessage.variableHeader()); - validateDecoderExceptionTooLargeMessage(decodedMessage); - } - - @Test - public void testConnectMessageForMqtt311TooLarge() throws Exception { - final MqttConnectMessage message = createConnectMessage(MqttVersion.MQTT_3_1_1); - ByteBuf byteBuf = MqttEncoder.doEncode(ctx, message); - - ArgumentCaptor captor = ArgumentCaptor.forClass(MqttMessage.class); - mqttDecoderLimitedMessageSize.channelRead(ctx, byteBuf); - verify(ctx).fireChannelRead(captor.capture()); - - final MqttMessage decodedMessage = captor.getValue(); - assertEquals(0, byteBuf.readableBytes()); - - validateFixedHeaders(message.fixedHeader(), decodedMessage.fixedHeader()); - validateConnectVariableHeader(message.variableHeader(), - (MqttConnectVariableHeader) decodedMessage.variableHeader()); - validateDecoderExceptionTooLargeMessage(decodedMessage); - } - - @Test - public void testConnAckMessageTooLarge() throws Exception { - final MqttConnAckMessage message = createConnAckMessage(); - ByteBuf byteBuf = MqttEncoder.doEncode(ctx, message); - ArgumentCaptor captor = ArgumentCaptor.forClass(MqttMessage.class); - mqttDecoderLimitedMessageSize.channelRead(ctx, byteBuf); - verify(ctx).fireChannelRead(captor.capture()); - - final MqttMessage decodedMessage = captor.getValue(); - assertEquals(0, byteBuf.readableBytes()); - - validateFixedHeaders(message.fixedHeader(), decodedMessage.fixedHeader()); - validateDecoderExceptionTooLargeMessage(decodedMessage); - } - - @Test - public void testPublishMessageTooLarge() throws Exception { - final MqttPublishMessage message = createPublishMessage(); - ByteBuf byteBuf = MqttEncoder.doEncode(ctx, message); - ArgumentCaptor captor = ArgumentCaptor.forClass(MqttMessage.class); - mqttDecoderLimitedMessageSize.channelRead(ctx, byteBuf); - verify(ctx).fireChannelRead(captor.capture()); - - final MqttMessage decodedMessage = captor.getValue(); - assertEquals(0, byteBuf.readableBytes()); - - validateFixedHeaders(message.fixedHeader(), decodedMessage.fixedHeader()); - validatePublishVariableHeader(message.variableHeader(), - (MqttPublishVariableHeader) decodedMessage.variableHeader()); - validateDecoderExceptionTooLargeMessage(decodedMessage); - } - - @Test - public void testSubscribeMessageTooLarge() throws Exception { - final MqttSubscribeMessage message = createSubscribeMessage(); - ByteBuf byteBuf = MqttEncoder.doEncode(ctx, message); - - ArgumentCaptor captor = ArgumentCaptor.forClass(MqttMessage.class); - mqttDecoderLimitedMessageSize.channelRead(ctx, byteBuf); - verify(ctx).fireChannelRead(captor.capture()); - - final MqttMessage decodedMessage = captor.getValue(); - assertEquals(0, byteBuf.readableBytes()); - - validateFixedHeaders(message.fixedHeader(), decodedMessage.fixedHeader()); - validateMessageIdVariableHeader(message.variableHeader(), - (MqttMessageIdVariableHeader) decodedMessage.variableHeader()); - validateDecoderExceptionTooLargeMessage(decodedMessage); - } - - @Test - public void testSubAckMessageTooLarge() throws Exception { - final MqttSubAckMessage message = createSubAckMessage(); - ByteBuf byteBuf = MqttEncoder.doEncode(ctx, message); - - ArgumentCaptor captor = ArgumentCaptor.forClass(MqttMessage.class); - mqttDecoderLimitedMessageSize.channelRead(ctx, byteBuf); - verify(ctx).fireChannelRead(captor.capture()); - - final MqttMessage decodedMessage = captor.getValue(); - assertEquals(0, byteBuf.readableBytes()); - - validateFixedHeaders(message.fixedHeader(), decodedMessage.fixedHeader()); - validateMessageIdVariableHeader(message.variableHeader(), - (MqttMessageIdVariableHeader) decodedMessage.variableHeader()); - validateDecoderExceptionTooLargeMessage(decodedMessage); - } - - @Test - public void testUnSubscribeMessageTooLarge() throws Exception { - final MqttUnsubscribeMessage message = createUnsubscribeMessage(); - ByteBuf byteBuf = MqttEncoder.doEncode(ctx, message); - - ArgumentCaptor captor = ArgumentCaptor.forClass(MqttMessage.class); - mqttDecoderLimitedMessageSize.channelRead(ctx, byteBuf); - verify(ctx).fireChannelRead(captor.capture()); - - final MqttMessage decodedMessage = captor.getValue(); - assertEquals(0, byteBuf.readableBytes()); - - validateFixedHeaders(message.fixedHeader(), decodedMessage.fixedHeader()); - validateMessageIdVariableHeader(message.variableHeader(), - (MqttMessageIdVariableHeader) decodedMessage.variableHeader()); - validateDecoderExceptionTooLargeMessage(decodedMessage); - } - - @Test - public void testConnectMessageForMqtt5() throws Exception { - MqttProperties props = new MqttProperties(); - props.add(new MqttProperties.IntegerProperty(SESSION_EXPIRY_INTERVAL.value(), 10)); - props.add(new MqttProperties.StringProperty(AUTHENTICATION_METHOD.value(), "Plain")); - MqttProperties willProps = new MqttProperties(); - willProps.add(new MqttProperties.IntegerProperty(WILL_DELAY_INTERVAL.value(), 100)); - final MqttConnectMessage message = - createConnectMessage(MqttVersion.MQTT_5, USER_NAME, PASSWORD, props, willProps); - ByteBuf byteBuf = MqttEncoder.doEncode(ctx, message); - - ArgumentCaptor captor = ArgumentCaptor.forClass(MqttConnectMessage.class); - mqttDecoder.channelRead(ctx, byteBuf); - verify(ctx).fireChannelRead(captor.capture()); - - final MqttConnectMessage decodedMessage = captor.getValue(); - validateFixedHeaders(message.fixedHeader(), decodedMessage.fixedHeader()); - validateConnectVariableHeader(message.variableHeader(), decodedMessage.variableHeader()); - validateConnectPayload(message.payload(), decodedMessage.payload()); - } - - @Test - public void testConnAckMessageForMqtt5() throws Exception { - MqttProperties props = new MqttProperties(); - props.add(new MqttProperties.IntegerProperty(SESSION_EXPIRY_INTERVAL.value(), 10)); - props.add(new MqttProperties.IntegerProperty(MAXIMUM_QOS.value(), 1)); - props.add(new MqttProperties.IntegerProperty(MAXIMUM_PACKET_SIZE.value(), 1000)); - final MqttConnAckMessage message = createConnAckMessage(props); - ByteBuf byteBuf = MqttEncoder.doEncode(ctx, message); - - ArgumentCaptor captor = ArgumentCaptor.forClass(MqttConnAckMessage.class); - mqttDecoder.channelRead(ctx, byteBuf); - verify(ctx).fireChannelRead(captor.capture()); - - final MqttConnAckMessage decodedMessage = captor.getValue(); - validateFixedHeaders(message.fixedHeader(), decodedMessage.fixedHeader()); - validateConnAckVariableHeader(message.variableHeader(), decodedMessage.variableHeader()); - } - - @Test - public void testPublishMessageForMqtt5() throws Exception { - when(versionAttrMock.get()).thenReturn(MqttVersion.MQTT_5); - MqttProperties props = new MqttProperties(); - props.add(new MqttProperties.IntegerProperty(SUBSCRIPTION_IDENTIFIER.value(), 10)); - props.add(new MqttProperties.IntegerProperty(SUBSCRIPTION_IDENTIFIER.value(), 20)); - props.add(new MqttProperties.IntegerProperty(PAYLOAD_FORMAT_INDICATOR.value(), 6)); - props.add(new MqttProperties.UserProperty("isSecret", "true")); - props.add(new MqttProperties.UserProperty("tag", "firstTag")); - props.add(new MqttProperties.UserProperty("tag", "secondTag")); - assertEquals(2, - props.getProperties(SUBSCRIPTION_IDENTIFIER.value()).size()); - assertEquals(3, - props.getProperties(USER_PROPERTY.value()).size()); - assertEquals(3, - ((MqttProperties.UserProperties) props.getProperty(USER_PROPERTY.value())).value.size()); - final MqttPublishMessage message = createPublishMessage(props); - ByteBuf byteBuf = MqttEncoder.doEncode(ctx, message); - - ArgumentCaptor captor = ArgumentCaptor.forClass(MqttPublishMessage.class); - mqttDecoder.channelRead(ctx, byteBuf); - verify(ctx).fireChannelRead(captor.capture()); - - final MqttPublishMessage decodedMessage = captor.getValue(); - validateFixedHeaders(message.fixedHeader(), decodedMessage.fixedHeader()); - validatePublishVariableHeader(message.variableHeader(), decodedMessage.variableHeader()); - validatePublishPayload(message.payload(), decodedMessage.payload()); - } - - @Test - public void testPubAckMessageForMqtt5() throws Exception { - when(versionAttrMock.get()).thenReturn(MqttVersion.MQTT_5); - MqttProperties props = new MqttProperties(); - props.add(new MqttProperties.IntegerProperty(PAYLOAD_FORMAT_INDICATOR.value(), 6)); - //0x87 - Not Authorized - final MqttMessage message = createPubAckMessage((byte) 0x87, props); - ByteBuf byteBuf = MqttEncoder.doEncode(ctx, message); - - ArgumentCaptor captor = ArgumentCaptor.forClass(MqttMessage.class); - mqttDecoder.channelRead(ctx, byteBuf); - verify(ctx).fireChannelRead(captor.capture()); - - final MqttMessage decodedMessage = captor.getValue(); - validateFixedHeaders(message.fixedHeader(), decodedMessage.fixedHeader()); - validatePubReplyVariableHeader((MqttPubReplyMessageVariableHeader) message.variableHeader(), - (MqttPubReplyMessageVariableHeader) decodedMessage.variableHeader()); - } - - @Test - public void testPubAckMessageSkipCodeForMqtt5() throws Exception { - //Code 0 (Success) and no properties - skip encoding code and properties - final MqttMessage message = createPubAckMessage((byte) 0, MqttProperties.NO_PROPERTIES); - ByteBuf byteBuf = MqttEncoder.doEncode(ctx, message); - - ArgumentCaptor captor = ArgumentCaptor.forClass(MqttMessage.class); - mqttDecoder.channelRead(ctx, byteBuf); - verify(ctx).fireChannelRead(captor.capture()); - - final MqttMessage decodedMessage = captor.getValue(); - validateFixedHeaders(message.fixedHeader(), decodedMessage.fixedHeader()); - validatePubReplyVariableHeader((MqttPubReplyMessageVariableHeader) message.variableHeader(), - (MqttPubReplyMessageVariableHeader) decodedMessage.variableHeader()); - } - - @Test - public void testSubAckMessageForMqtt5() throws Exception { - MqttProperties props = new MqttProperties(); - props.add(new MqttProperties.IntegerProperty(PAYLOAD_FORMAT_INDICATOR.value(), 6)); - final MqttSubAckMessage message = createSubAckMessage(props, new int[] {1, 2, 0, 0x87 /* not authorized */}); - ByteBuf byteBuf = MqttEncoder.doEncode(ctx, message); - - ArgumentCaptor captor = ArgumentCaptor.forClass(MqttSubAckMessage.class); - mqttDecoder.channelRead(ctx, byteBuf); - verify(ctx).fireChannelRead(captor.capture()); - - final MqttSubAckMessage decodedMessage = captor.getValue(); - validateFixedHeaders(message.fixedHeader(), decodedMessage.fixedHeader()); - validatePacketIdAndPropertiesVariableHeader( - (MqttMessageIdAndPropertiesVariableHeader) message.variableHeader(), - (MqttMessageIdAndPropertiesVariableHeader) decodedMessage.variableHeader()); - validateSubAckPayload(message.payload(), decodedMessage.payload()); - assertArrayEquals(new Integer[] {1, 2, 0, 0x80}, - decodedMessage.payload().grantedQoSLevels().toArray()); - } - - @Test - public void testSubscribeMessageForMqtt5() throws Exception { - when(versionAttrMock.get()).thenReturn(MqttVersion.MQTT_5); - - MqttProperties props = new MqttProperties(); - props.add(new MqttProperties.IntegerProperty(PAYLOAD_FORMAT_INDICATOR.value(), 6)); - final MqttSubscribeMessage message = MqttMessageBuilders.subscribe() - .messageId((short) 1) - .properties(props) - .addSubscription("/topic", new MqttSubscriptionOption(AT_LEAST_ONCE, - true, - true, - SEND_AT_SUBSCRIBE_IF_NOT_YET_EXISTS)) - .build(); - ByteBuf byteBuf = MqttEncoder.doEncode(ctx, message); - - ArgumentCaptor captor = ArgumentCaptor.forClass(MqttSubscribeMessage.class); - mqttDecoder.channelRead(ctx, byteBuf); - verify(ctx).fireChannelRead(captor.capture()); - - final MqttSubscribeMessage decodedMessage = captor.getValue(); - - validateFixedHeaders(message.fixedHeader(), decodedMessage.fixedHeader()); - final MqttMessageIdAndPropertiesVariableHeader expectedHeader = - (MqttMessageIdAndPropertiesVariableHeader) message.variableHeader(); - final MqttMessageIdAndPropertiesVariableHeader actualHeader = - (MqttMessageIdAndPropertiesVariableHeader) decodedMessage.variableHeader(); - validatePacketIdAndPropertiesVariableHeader(expectedHeader, actualHeader); - validateSubscribePayload(message.payload(), decodedMessage.payload()); - } - - @Test - public void testSubscribeMessageMqtt5EncodeAsMqtt3() throws Exception { - when(versionAttrMock.get()).thenReturn(MqttVersion.MQTT_3_1_1); - - //Set parameters only available in MQTT5 to see if they're dropped when encoding as MQTT3 - MqttProperties props = new MqttProperties(); - props.add(new MqttProperties.IntegerProperty(PAYLOAD_FORMAT_INDICATOR.value(), 6)); - final MqttSubscribeMessage message = MqttMessageBuilders.subscribe() - .messageId((short) 1) - .properties(props) - .addSubscription("/topic", new MqttSubscriptionOption(AT_LEAST_ONCE, - true, - true, - SEND_AT_SUBSCRIBE_IF_NOT_YET_EXISTS)) - .build(); - ByteBuf byteBuf = MqttEncoder.doEncode(ctx, message); - - ArgumentCaptor captor = ArgumentCaptor.forClass(MqttSubscribeMessage.class); - mqttDecoder.channelRead(ctx, byteBuf); - verify(ctx).fireChannelRead(captor.capture()); - final MqttSubscribeMessage decodedMessage = captor.getValue(); - final MqttSubscribeMessage expectedMessage = MqttMessageBuilders.subscribe() - .messageId((short) 1) - .addSubscription("/topic", MqttSubscriptionOption.onlyFromQos(AT_LEAST_ONCE)) - .build(); - validateFixedHeaders(expectedMessage.fixedHeader(), decodedMessage.fixedHeader()); - final MqttMessageIdAndPropertiesVariableHeader expectedHeader = - (MqttMessageIdAndPropertiesVariableHeader) expectedMessage.variableHeader(); - final MqttMessageIdAndPropertiesVariableHeader actualHeader = - (MqttMessageIdAndPropertiesVariableHeader) decodedMessage.variableHeader(); - validatePacketIdAndPropertiesVariableHeader(expectedHeader, actualHeader); - validateSubscribePayload(expectedMessage.payload(), decodedMessage.payload()); - } - - @Test - public void testUnsubAckMessageForMqtt5() throws Exception { - when(versionAttrMock.get()).thenReturn(MqttVersion.MQTT_5); - - MqttProperties props = new MqttProperties(); - props.add(new MqttProperties.IntegerProperty(PAYLOAD_FORMAT_INDICATOR.value(), 6)); - final MqttUnsubAckMessage message = MqttMessageBuilders.unsubAck() - .packetId((short) 1) - .properties(props) - .addReasonCode((short) 0x83) - .build(); - ByteBuf byteBuf = MqttEncoder.doEncode(ctx, message); - - ArgumentCaptor captor = ArgumentCaptor.forClass(MqttUnsubAckMessage.class); - mqttDecoder.channelRead(ctx, byteBuf); - verify(ctx).fireChannelRead(captor.capture()); - - final MqttUnsubAckMessage decodedMessage = captor.getValue(); - validateFixedHeaders(message.fixedHeader(), decodedMessage.fixedHeader()); - validatePacketIdAndPropertiesVariableHeader( - (MqttMessageIdAndPropertiesVariableHeader) message.variableHeader(), - (MqttMessageIdAndPropertiesVariableHeader) decodedMessage.variableHeader()); - assertEquals(message.payload().unsubscribeReasonCodes(), - decodedMessage.payload().unsubscribeReasonCodes()); - } - - @Test - public void testDisconnectMessageForMqtt5() throws Exception { - when(versionAttrMock.get()).thenReturn(MqttVersion.MQTT_5); - - MqttProperties props = new MqttProperties(); - props.add(new MqttProperties.IntegerProperty(SESSION_EXPIRY_INTERVAL.value(), 6)); - final MqttMessage message = MqttMessageBuilders.disconnect() - .reasonCode((byte) 0x96) // Message rate too high - .properties(props) - .build(); - ByteBuf byteBuf = MqttEncoder.doEncode(ctx, message); - - ArgumentCaptor captor = ArgumentCaptor.forClass(MqttMessage.class); - mqttDecoder.channelRead(ctx, byteBuf); - verify(ctx).fireChannelRead(captor.capture()); - - final MqttMessage decodedMessage = captor.getValue(); - validateFixedHeaders(message.fixedHeader(), decodedMessage.fixedHeader()); - validateReasonCodeAndPropertiesVariableHeader( - (MqttReasonCodeAndPropertiesVariableHeader) message.variableHeader(), - (MqttReasonCodeAndPropertiesVariableHeader) decodedMessage.variableHeader()); - } - - @Test - public void testDisconnectMessageSkipCodeForMqtt5() throws Exception { - //code 0 and no properties - skip encoding code and properties - final MqttMessage message = MqttMessageBuilders.disconnect() - .reasonCode((byte) 0) // ok - .properties(MqttProperties.NO_PROPERTIES) - .build(); - ByteBuf byteBuf = MqttEncoder.doEncode(ctx, message); - - ArgumentCaptor captor = ArgumentCaptor.forClass(MqttMessage.class); - mqttDecoder.channelRead(ctx, byteBuf); - verify(ctx).fireChannelRead(captor.capture()); - - final MqttMessage decodedMessage = captor.getValue(); - validateFixedHeaders(message.fixedHeader(), decodedMessage.fixedHeader()); - validateReasonCodeAndPropertiesVariableHeader( - (MqttReasonCodeAndPropertiesVariableHeader) message.variableHeader(), - (MqttReasonCodeAndPropertiesVariableHeader) decodedMessage.variableHeader()); - } - - @Test - public void testAuthMessageForMqtt5() throws Exception { - when(versionAttrMock.get()).thenReturn(MqttVersion.MQTT_5); - - MqttProperties props = new MqttProperties(); - props.add(new MqttProperties.BinaryProperty(AUTHENTICATION_DATA.value(), "secret".getBytes(CharsetUtil.UTF_8))); - final MqttMessage message = MqttMessageBuilders.auth() - .reasonCode((byte) 0x18) // Continue authentication - .properties(props) - .build(); - ByteBuf byteBuf = MqttEncoder.doEncode(ctx, message); - - ArgumentCaptor captor = ArgumentCaptor.forClass(MqttMessage.class); - mqttDecoder.channelRead(ctx, byteBuf); - verify(ctx).fireChannelRead(captor.capture()); - - final MqttMessage decodedMessage = captor.getValue(); - validateFixedHeaders(message.fixedHeader(), decodedMessage.fixedHeader()); - validateReasonCodeAndPropertiesVariableHeader( - (MqttReasonCodeAndPropertiesVariableHeader) message.variableHeader(), - (MqttReasonCodeAndPropertiesVariableHeader) decodedMessage.variableHeader()); - } - - @Test - public void testMqttVersionDetection() throws Exception { - clearInvocations(versionAttrMock); - //Encode CONNECT message so that encoder would initialize its version - final MqttConnectMessage connectMessage = createConnectMessage(MqttVersion.MQTT_5); - ByteBuf connectByteBuf = MqttEncoder.doEncode(ctx, connectMessage); - - verify(versionAttrMock, times(1)).set(MqttVersion.MQTT_5); - clearInvocations(versionAttrMock); - - ArgumentCaptor captor = ArgumentCaptor.forClass(MqttConnectMessage.class); - mqttDecoder.channelRead(ctx, connectByteBuf); - verify(ctx).fireChannelRead(captor.capture()); - verify(versionAttrMock, times(1)).set(MqttVersion.MQTT_5); - - final MqttConnectMessage decodedConnectMessage = captor.getValue(); - validateFixedHeaders(connectMessage.fixedHeader(), decodedConnectMessage.fixedHeader()); - validateConnectVariableHeader(connectMessage.variableHeader(), decodedConnectMessage.variableHeader()); - validateConnectPayload(connectMessage.payload(), decodedConnectMessage.payload()); - - verifyNoMoreInteractions(versionAttrMock); - } - - private void testMessageWithOnlyFixedHeader(MqttMessage message) throws Exception { - ByteBuf byteBuf = MqttEncoder.doEncode(ctx, message); - - ArgumentCaptor captor = ArgumentCaptor.forClass(MqttMessage.class); - mqttDecoder.channelRead(ctx, byteBuf); - verify(ctx).fireChannelRead(captor.capture()); - - final MqttMessage decodedMessage = captor.getValue(); - validateFixedHeaders(message.fixedHeader(), decodedMessage.fixedHeader()); - } - - private void testMessageWithOnlyFixedHeaderAndMessageIdVariableHeader(MqttMessageType messageType) - throws Exception { - MqttMessage message = createMessageWithFixedHeaderAndMessageIdVariableHeader(messageType); - - ByteBuf byteBuf = MqttEncoder.doEncode(ctx, message); - - ArgumentCaptor captor = ArgumentCaptor.forClass(MqttMessage.class); - mqttDecoder.channelRead(ctx, byteBuf); - verify(ctx).fireChannelRead(captor.capture()); - - final MqttMessage decodedMessage = captor.getValue(); - validateFixedHeaders(message.fixedHeader(), decodedMessage.fixedHeader()); - validateMessageIdVariableHeader( - (MqttMessageIdVariableHeader) message.variableHeader(), - (MqttMessageIdVariableHeader) decodedMessage.variableHeader()); - } - - // Factory methods of different MQTT - // Message types to help testing - - private static MqttMessage createMessageWithFixedHeader(MqttMessageType messageType) { - return new MqttMessage(new MqttFixedHeader(messageType, false, MqttQoS.AT_MOST_ONCE, false, 0)); - } - - private static MqttMessage createMessageWithFixedHeaderAndMessageIdVariableHeader(MqttMessageType messageType) { - MqttFixedHeader mqttFixedHeader = - new MqttFixedHeader( - messageType, - false, - messageType == MqttMessageType.PUBREL ? MqttQoS.AT_LEAST_ONCE : MqttQoS.AT_MOST_ONCE, - false, - 0); - MqttMessageIdVariableHeader mqttMessageIdVariableHeader = MqttMessageIdVariableHeader.from(12345); - return new MqttMessage(mqttFixedHeader, mqttMessageIdVariableHeader); - } - - private static MqttConnectMessage createConnectMessage(MqttVersion mqttVersion) { - return createConnectMessage(mqttVersion, - USER_NAME, - PASSWORD, - MqttProperties.NO_PROPERTIES, - MqttProperties.NO_PROPERTIES); - } - - private static MqttConnectMessage createConnectMessage(MqttVersion mqttVersion, - String username, - String password, - MqttProperties properties, - MqttProperties willProperties) { - return MqttMessageBuilders.connect() - .clientId(CLIENT_ID) - .protocolVersion(mqttVersion) - .username(username) - .password(password.getBytes(CharsetUtil.UTF_8)) - .properties(properties) - .willRetain(true) - .willQoS(MqttQoS.AT_LEAST_ONCE) - .willFlag(true) - .willTopic(WILL_TOPIC) - .willMessage(WILL_MESSAGE.getBytes(CharsetUtil.UTF_8)) - .willProperties(willProperties) - .cleanSession(true) - .keepAlive(KEEP_ALIVE_SECONDS) - .build(); - } - - private static MqttConnAckMessage createConnAckMessage() { - return createConnAckMessage(MqttProperties.NO_PROPERTIES); - } - - private static MqttConnAckMessage createConnAckMessage(MqttProperties properties) { - return MqttMessageBuilders.connAck() - .returnCode(MqttConnectReturnCode.CONNECTION_ACCEPTED) - .properties(properties) - .sessionPresent(true) - .build(); - } - - private static MqttPublishMessage createPublishMessage() { - return createPublishMessage(MqttProperties.NO_PROPERTIES); - } - - private static MqttPublishMessage createPublishMessage(MqttProperties properties) { - MqttFixedHeader mqttFixedHeader = - new MqttFixedHeader(MqttMessageType.PUBLISH, false, MqttQoS.AT_LEAST_ONCE, true, 0); - MqttPublishVariableHeader mqttPublishVariableHeader = new MqttPublishVariableHeader("/abc", - 1234, - properties); - ByteBuf payload = ALLOCATOR.buffer(); - payload.writeBytes("whatever".getBytes(CharsetUtil.UTF_8)); - return new MqttPublishMessage(mqttFixedHeader, mqttPublishVariableHeader, payload); - } - - private static MqttSubscribeMessage createSubscribeMessage() { - MqttFixedHeader mqttFixedHeader = - new MqttFixedHeader(MqttMessageType.SUBSCRIBE, false, MqttQoS.AT_LEAST_ONCE, false, 0); - MqttMessageIdVariableHeader mqttMessageIdVariableHeader = MqttMessageIdVariableHeader.from(12345); - - List topicSubscriptions = new LinkedList<>(); - topicSubscriptions.add(new MqttTopicSubscription("/abc", MqttQoS.AT_LEAST_ONCE)); - topicSubscriptions.add(new MqttTopicSubscription("/def", MqttQoS.AT_LEAST_ONCE)); - topicSubscriptions.add(new MqttTopicSubscription("/xyz", MqttQoS.EXACTLY_ONCE)); - - MqttSubscribePayload mqttSubscribePayload = new MqttSubscribePayload(topicSubscriptions); - return new MqttSubscribeMessage(mqttFixedHeader, mqttMessageIdVariableHeader, mqttSubscribePayload); - } - - private static MqttSubAckMessage createSubAckMessage() { - return createSubAckMessage(MqttProperties.NO_PROPERTIES, new int[] {1, 2, 0}); - } - - private static MqttSubAckMessage createSubAckMessage(MqttProperties properties, int[] reasonCodes) { - MqttFixedHeader mqttFixedHeader = - new MqttFixedHeader(MqttMessageType.SUBACK, false, MqttQoS.AT_MOST_ONCE, false, 0); - MqttMessageIdVariableHeader mqttMessageIdVariableHeader = MqttMessageIdVariableHeader.from(12345); - MqttSubAckPayload mqttSubAckPayload = new MqttSubAckPayload(reasonCodes); - return new MqttSubAckMessage(mqttFixedHeader, mqttMessageIdVariableHeader, mqttSubAckPayload); - } - - private static MqttUnsubscribeMessage createUnsubscribeMessage() { - MqttFixedHeader mqttFixedHeader = - new MqttFixedHeader(MqttMessageType.UNSUBSCRIBE, false, MqttQoS.AT_LEAST_ONCE, false, 0); - MqttMessageIdVariableHeader mqttMessageIdVariableHeader = MqttMessageIdVariableHeader.from(12345); - - List topics = new LinkedList<>(); - topics.add("/abc"); - topics.add("/def"); - topics.add("/xyz"); - - MqttUnsubscribePayload mqttUnsubscribePayload = new MqttUnsubscribePayload(topics); - return new MqttUnsubscribeMessage(mqttFixedHeader, mqttMessageIdVariableHeader, mqttUnsubscribePayload); - } - - private MqttMessage createPubAckMessage(byte reasonCode, MqttProperties properties) { - return MqttMessageBuilders.pubAck() - .packetId((short) 1) - .reasonCode(reasonCode) - .properties(properties) - .build(); - } - - // Helper methods to compare expected and actual - // MQTT messages - - private static void validateFixedHeaders(MqttFixedHeader expected, MqttFixedHeader actual) { - assertEquals(expected.messageType(), actual.messageType()); - assertEquals(expected.qosLevel(), actual.qosLevel()); - } - - private static void validateConnectVariableHeader( - MqttConnectVariableHeader expected, - MqttConnectVariableHeader actual) { - assertEquals(expected.name(), actual.name()); - assertEquals(expected.keepAliveTimeSeconds(), actual.keepAliveTimeSeconds()); - assertEquals(expected.version(), actual.version()); - assertEquals(expected.version(), actual.version()); - validateProperties(expected.properties(), actual.properties()); - assertEquals(expected.willQos(), actual.willQos()); - - assertEquals(expected.hasUserName(), actual.hasUserName()); - assertEquals(expected.hasPassword(), actual.hasPassword()); - assertEquals(expected.isCleanSession(), actual.isCleanSession()); - assertEquals(expected.isWillFlag(), actual.isWillFlag()); - assertEquals(expected.isWillRetain(), actual.isWillRetain()); - } - - private static void validateConnectPayload(MqttConnectPayload expected, MqttConnectPayload actual) { - assertEquals(expected.clientIdentifier(), actual.clientIdentifier()); - assertEquals(expected.userName(), actual.userName()); - assertEquals(expected.password(), actual.password()); - assertArrayEquals(expected.passwordInBytes(), actual.passwordInBytes()); - assertEquals(expected.willMessage(), actual.willMessage()); - assertArrayEquals(expected.willMessageInBytes(), actual.willMessageInBytes()); - assertEquals(expected.willTopic(), actual.willTopic()); - validateProperties(expected.willProperties(), actual.willProperties()); - } - - private static void validateConnAckVariableHeader( - MqttConnAckVariableHeader expected, - MqttConnAckVariableHeader actual) { - assertEquals(expected.connectReturnCode(), actual.connectReturnCode()); - } - - private static void validatePublishVariableHeader( - MqttPublishVariableHeader expected, - MqttPublishVariableHeader actual) { - assertEquals(expected.topicName(), actual.topicName()); - assertEquals(expected.packetId(), actual.packetId()); - validateProperties(expected.properties(), actual.properties()); - } - - private static void validatePublishPayload(ByteBuf expected, ByteBuf actual) { - assertEquals(0, expected.compareTo(actual)); - } - - private static void validateMessageIdVariableHeader( - MqttMessageIdVariableHeader expected, - MqttMessageIdVariableHeader actual) { - assertEquals(expected.messageId(), actual.messageId()); - } - - private static void validateSubAckPayload(MqttSubAckPayload expected, MqttSubAckPayload actual) { - assertArrayEquals(expected.reasonCodes().toArray(), actual.reasonCodes().toArray()); - assertArrayEquals(expected.grantedQoSLevels().toArray(), actual.grantedQoSLevels().toArray()); - } - - private static void validateDecoderExceptionTooLargeMessage(MqttMessage message) { - assertNull(message.payload()); - assertTrue(message.decoderResult().isFailure()); - Throwable cause = message.decoderResult().cause(); - assertThat(cause, instanceOf(DecoderException.class)); - - assertTrue(cause.getMessage().contains("too large message:")); - } - - private static void validatePubReplyVariableHeader( - MqttPubReplyMessageVariableHeader expected, - MqttPubReplyMessageVariableHeader actual) { - assertEquals(expected.messageId(), actual.messageId()); - assertEquals(expected.reasonCode(), actual.reasonCode()); - - final MqttProperties expectedProps = expected.properties(); - final MqttProperties actualProps = actual.properties(); - validateProperties(expectedProps, actualProps); - } - - private void validatePacketIdAndPropertiesVariableHeader(MqttMessageIdAndPropertiesVariableHeader expected, - MqttMessageIdAndPropertiesVariableHeader actual) { - assertEquals(expected.messageId(), actual.messageId()); - final MqttProperties expectedProps = expected.properties(); - final MqttProperties actualProps = actual.properties(); - validateProperties(expectedProps, actualProps); - } - - private void validateReasonCodeAndPropertiesVariableHeader(MqttReasonCodeAndPropertiesVariableHeader expected, - MqttReasonCodeAndPropertiesVariableHeader actual) { - assertEquals(expected.reasonCode(), actual.reasonCode()); - final MqttProperties expectedProps = expected.properties(); - final MqttProperties actualProps = actual.properties(); - validateProperties(expectedProps, actualProps); - } -} diff --git a/codec-mqtt/src/test/java/io/netty/handler/codec/mqtt/MqttConnectPayloadTest.java b/codec-mqtt/src/test/java/io/netty/handler/codec/mqtt/MqttConnectPayloadTest.java deleted file mode 100644 index 39a651cc14..0000000000 --- a/codec-mqtt/src/test/java/io/netty/handler/codec/mqtt/MqttConnectPayloadTest.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.mqtt; - -import static org.junit.jupiter.api.Assertions.assertNull; - -import io.netty.util.CharsetUtil; -import org.junit.jupiter.api.Test; - -import java.util.Collections; - -public class MqttConnectPayloadTest { - - @Test - public void testNullWillMessage() throws Exception { - String clientIdentifier = "clientIdentifier"; - String willTopic = "willTopic"; - byte[] willMessage = null; - String userName = "userName"; - byte[] password = "password".getBytes(CharsetUtil.UTF_8); - MqttConnectPayload mqttConnectPayload = new MqttConnectPayload(clientIdentifier, - MqttProperties.NO_PROPERTIES, - willTopic, - willMessage, - userName, - password); - - assertNull(mqttConnectPayload.willMessageInBytes()); - assertNull(mqttConnectPayload.willMessage()); - } - - @Test - public void testNullPassword() throws Exception { - String clientIdentifier = "clientIdentifier"; - String willTopic = "willTopic"; - byte[] willMessage = "willMessage".getBytes(CharsetUtil.UTF_8); - String userName = "userName"; - byte[] password = null; - MqttConnectPayload mqttConnectPayload = new MqttConnectPayload(clientIdentifier, - MqttProperties.NO_PROPERTIES, - willTopic, - willMessage, - userName, - password); - - assertNull(mqttConnectPayload.passwordInBytes()); - assertNull(mqttConnectPayload.password()); - } - - @Test - public void testBuilderNullPassword() throws Exception { - MqttMessageBuilders.ConnectBuilder builder = new MqttMessageBuilders.ConnectBuilder(); - builder.password((String) null); - - MqttConnectPayload mqttConnectPayload = builder.build().payload(); - - assertNull(mqttConnectPayload.passwordInBytes()); - assertNull(mqttConnectPayload.password()); - - builder = new MqttMessageBuilders.ConnectBuilder(); - builder.password((byte[]) null); - - mqttConnectPayload = builder.build().payload(); - - assertNull(mqttConnectPayload.passwordInBytes()); - assertNull(mqttConnectPayload.password()); - } - - @Test - public void testBuilderNullWillMessage() throws Exception { - MqttMessageBuilders.ConnectBuilder builder = new MqttMessageBuilders.ConnectBuilder(); - builder.willMessage((String) null); - - MqttConnectPayload mqttConnectPayload = builder.build().payload(); - - assertNull(mqttConnectPayload.willMessageInBytes()); - assertNull(mqttConnectPayload.willMessage()); - - builder = new MqttMessageBuilders.ConnectBuilder(); - builder.willMessage((byte[]) null); - - mqttConnectPayload = builder.build().payload(); - - assertNull(mqttConnectPayload.willMessageInBytes()); - assertNull(mqttConnectPayload.willMessage()); - } - - /* See https://github.com/netty/netty/pull/9202 */ - @Test - public void testEmptyTopicsToString() { - new MqttSubscribePayload(Collections.emptyList()).toString(); - new MqttUnsubscribePayload(Collections.emptyList()).toString(); - } -} diff --git a/codec-mqtt/src/test/java/io/netty/handler/codec/mqtt/MqttMessageBuildersPacketIdTest.java b/codec-mqtt/src/test/java/io/netty/handler/codec/mqtt/MqttMessageBuildersPacketIdTest.java deleted file mode 100644 index 09ccff0a52..0000000000 --- a/codec-mqtt/src/test/java/io/netty/handler/codec/mqtt/MqttMessageBuildersPacketIdTest.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.mqtt; - -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -import java.util.Arrays; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class MqttMessageBuildersPacketIdTest { - - static Iterable data() { - // we take a subset of valid packetIds - return Arrays.asList( - 0x0001, - 0x000F, - 0x00FF, - 0x0FFF, - 0xFFFF - ); - } - - @ParameterizedTest() - @MethodSource("data") - public void testUnsubAckMessageIdAsShort(Integer id) { - final MqttUnsubAckMessage msg = MqttMessageBuilders.unsubAck() - .packetId(id.shortValue()) - .build(); - - assertEquals( - id.intValue(), - msg.variableHeader().messageId() - ); - } - - @ParameterizedTest - @MethodSource("data") - public void testSubAckMessageIdAsShort(Integer id) { - final MqttSubAckMessage msg = MqttMessageBuilders.subAck() - .packetId(id.shortValue()) - .build(); - - assertEquals( - id.intValue(), - msg.variableHeader().messageId() - ); - } - - @ParameterizedTest - @MethodSource("data") - public void testPubAckMessageIdAsShort(Integer id) { - final MqttMessage msg = MqttMessageBuilders.pubAck() - .packetId(id.shortValue()) - .build(); - - assertEquals( - id.intValue(), - ((MqttMessageIdVariableHeader) msg.variableHeader()).messageId() - ); - } - - @ParameterizedTest - @MethodSource("data") - public void testUnsubAckMessageIdAsInt(Integer id) { - final MqttUnsubAckMessage msg = MqttMessageBuilders.unsubAck() - .packetId(id) - .build(); - - assertEquals( - id.intValue(), - msg.variableHeader().messageId() - ); - } - - @ParameterizedTest - @MethodSource("data") - public void testSubAckMessageIdAsInt(Integer id) { - final MqttSubAckMessage msg = MqttMessageBuilders.subAck() - .packetId(id) - .build(); - - assertEquals( - id.intValue(), - msg.variableHeader().messageId() - ); - } - - @ParameterizedTest - @MethodSource("data") - public void testPubAckMessageIdAsInt(Integer id) { - final MqttMessage msg = MqttMessageBuilders.pubAck() - .packetId(id) - .build(); - - assertEquals( - id.intValue(), - ((MqttMessageIdVariableHeader) msg.variableHeader()).messageId() - ); - } -} diff --git a/codec-mqtt/src/test/java/io/netty/handler/codec/mqtt/MqttMessageBuildersTest.java b/codec-mqtt/src/test/java/io/netty/handler/codec/mqtt/MqttMessageBuildersTest.java deleted file mode 100644 index 3c902961fc..0000000000 --- a/codec-mqtt/src/test/java/io/netty/handler/codec/mqtt/MqttMessageBuildersTest.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.mqtt; - -import io.netty.handler.codec.mqtt.MqttMessageBuilders.PropertiesInitializer; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class MqttMessageBuildersTest { - - @Test - public void testConnAckWithProperties() { - final MqttConnAckMessage ackMsg = MqttMessageBuilders.connAck() - .properties(new PropertiesInitializer() { - @Override - public void apply(MqttMessageBuilders.ConnAckPropertiesBuilder builder) { - builder.assignedClientId("client1234"); - builder.userProperty("custom_property", "value"); - } - }).build(); - - final String clientId = (String) ackMsg.variableHeader() - .properties() - .getProperty(MqttProperties.MqttPropertyType.ASSIGNED_CLIENT_IDENTIFIER.value()) - .value(); - - assertEquals("client1234", clientId); - } - -} diff --git a/codec-mqtt/src/test/java/io/netty/handler/codec/mqtt/MqttMessageFactoryTest.java b/codec-mqtt/src/test/java/io/netty/handler/codec/mqtt/MqttMessageFactoryTest.java deleted file mode 100644 index f959ead7d8..0000000000 --- a/codec-mqtt/src/test/java/io/netty/handler/codec/mqtt/MqttMessageFactoryTest.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.mqtt; - -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.List; - -import static io.netty.handler.codec.mqtt.MqttQoS.AT_LEAST_ONCE; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static io.netty.handler.codec.mqtt.MqttTestUtils.validateProperties; -import static io.netty.handler.codec.mqtt.MqttTestUtils.validateSubscribePayload; -import static io.netty.handler.codec.mqtt.MqttTestUtils.validateUnsubscribePayload; - -public class MqttMessageFactoryTest { - private static final String SAMPLE_TOPIC = "a/b/c"; - private static final int SAMPLE_MESSAGE_ID = 123; - - @Test - public void createUnsubAckV3() { - MqttFixedHeader fixedHeader = - new MqttFixedHeader(MqttMessageType.UNSUBACK, false, MqttQoS.AT_MOST_ONCE, false, 0); - MqttMessageIdVariableHeader variableHeader = - MqttMessageIdVariableHeader.from(SAMPLE_MESSAGE_ID); - - MqttMessage unsuback = MqttMessageFactory.newMessage(fixedHeader, variableHeader, null); - - assertEquals(MqttMessageType.UNSUBACK, unsuback.fixedHeader().messageType()); - MqttMessageIdAndPropertiesVariableHeader actualVariableHeader = - (MqttMessageIdAndPropertiesVariableHeader) unsuback.variableHeader(); - assertEquals(SAMPLE_MESSAGE_ID, actualVariableHeader.messageId()); - validateProperties(MqttProperties.NO_PROPERTIES, actualVariableHeader.properties()); - MqttUnsubAckPayload actualPayload = (MqttUnsubAckPayload) unsuback.payload(); - assertNotNull(actualPayload); - assertEquals(0, actualPayload.unsubscribeReasonCodes().size()); - } - - @Test - public void createUnsubAckV5() { - MqttFixedHeader fixedHeader = - new MqttFixedHeader(MqttMessageType.UNSUBACK, false, MqttQoS.AT_MOST_ONCE, false, 0); - MqttProperties properties = new MqttProperties(); - String reasonString = "All right"; - properties.add(new MqttProperties.StringProperty( - MqttProperties.MqttPropertyType.REASON_STRING.value(), - reasonString)); - MqttMessageIdAndPropertiesVariableHeader variableHeader = - new MqttMessageIdAndPropertiesVariableHeader(SAMPLE_MESSAGE_ID, properties); - MqttUnsubAckPayload payload = new MqttUnsubAckPayload((short) 0x80 /*unspecified error*/); - - MqttMessage unsuback = MqttMessageFactory.newMessage(fixedHeader, variableHeader, payload); - - assertEquals(MqttMessageType.UNSUBACK, unsuback.fixedHeader().messageType()); - MqttMessageIdAndPropertiesVariableHeader actualVariableHeader = - (MqttMessageIdAndPropertiesVariableHeader) unsuback.variableHeader(); - assertEquals(SAMPLE_MESSAGE_ID, actualVariableHeader.messageId()); - validateProperties(properties, actualVariableHeader.properties()); - MqttUnsubAckPayload actualPayload = (MqttUnsubAckPayload) unsuback.payload(); - assertEquals(payload.unsubscribeReasonCodes(), actualPayload.unsubscribeReasonCodes()); - } - - @Test - public void createSubscribeV3() { - MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.SUBSCRIBE, false, AT_LEAST_ONCE, false, 0); - - MqttMessageIdVariableHeader variableHeader = MqttMessageIdVariableHeader.from(SAMPLE_MESSAGE_ID); - List subscriptions = new ArrayList(); - subscriptions.add(new MqttTopicSubscription(SAMPLE_TOPIC, MqttQoS.AT_MOST_ONCE)); - - MqttSubscribePayload payload = new MqttSubscribePayload(subscriptions); - - MqttMessage subscribe = MqttMessageFactory.newMessage(fixedHeader, variableHeader, payload); - - assertEquals(MqttMessageType.SUBSCRIBE, subscribe.fixedHeader().messageType()); - MqttMessageIdAndPropertiesVariableHeader actualVariableHeader = - (MqttMessageIdAndPropertiesVariableHeader) subscribe.variableHeader(); - assertEquals(SAMPLE_MESSAGE_ID, actualVariableHeader.messageId()); - validateProperties(MqttProperties.NO_PROPERTIES, actualVariableHeader.properties()); - MqttSubscribePayload actualPayload = (MqttSubscribePayload) subscribe.payload(); - validateSubscribePayload(payload, actualPayload); - } - - @Test - public void createSubscribeV5() { - MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.SUBSCRIBE, false, AT_LEAST_ONCE, false, 0); - - MqttProperties properties = new MqttProperties(); - properties.add(new MqttProperties.UserProperty("correlationId", "111222")); - MqttMessageIdAndPropertiesVariableHeader variableHeader = - new MqttMessageIdAndPropertiesVariableHeader(SAMPLE_MESSAGE_ID, properties); - List subscriptions = new ArrayList(); - subscriptions.add(new MqttTopicSubscription(SAMPLE_TOPIC, MqttQoS.AT_MOST_ONCE)); - - MqttSubscribePayload payload = new MqttSubscribePayload(subscriptions); - - MqttMessage subscribe = MqttMessageFactory.newMessage(fixedHeader, variableHeader, payload); - - assertEquals(MqttMessageType.SUBSCRIBE, subscribe.fixedHeader().messageType()); - MqttMessageIdAndPropertiesVariableHeader actualVariableHeader = - (MqttMessageIdAndPropertiesVariableHeader) subscribe.variableHeader(); - assertEquals(SAMPLE_MESSAGE_ID, actualVariableHeader.messageId()); - validateProperties(properties, actualVariableHeader.properties()); - MqttSubscribePayload actualPayload = (MqttSubscribePayload) subscribe.payload(); - validateSubscribePayload(payload, actualPayload); - } - - @Test - public void createUnsubscribeV3() { - MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.UNSUBSCRIBE, false, AT_LEAST_ONCE, false, 0); - - MqttMessageIdVariableHeader variableHeader = MqttMessageIdVariableHeader.from(SAMPLE_MESSAGE_ID); - - List topics = new ArrayList(); - topics.add(SAMPLE_TOPIC); - MqttUnsubscribePayload payload = new MqttUnsubscribePayload(topics); - - MqttMessage unsubscribe = MqttMessageFactory.newMessage(fixedHeader, variableHeader, payload); - - assertEquals(MqttMessageType.UNSUBSCRIBE, unsubscribe.fixedHeader().messageType()); - MqttMessageIdAndPropertiesVariableHeader actualVariableHeader = - (MqttMessageIdAndPropertiesVariableHeader) unsubscribe.variableHeader(); - assertEquals(SAMPLE_MESSAGE_ID, actualVariableHeader.messageId()); - validateProperties(MqttProperties.NO_PROPERTIES, actualVariableHeader.properties()); - MqttUnsubscribePayload actualPayload = (MqttUnsubscribePayload) unsubscribe.payload(); - validateUnsubscribePayload(payload, actualPayload); - } - - @Test - public void createUnsubscribeV5() { - MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.UNSUBSCRIBE, false, AT_LEAST_ONCE, false, 0); - - MqttProperties properties = new MqttProperties(); - properties.add(new MqttProperties.UserProperty("correlationId", "111222")); - MqttMessageIdAndPropertiesVariableHeader variableHeader = - new MqttMessageIdAndPropertiesVariableHeader(SAMPLE_MESSAGE_ID, properties); - - List topics = new ArrayList(); - topics.add(SAMPLE_TOPIC); - MqttUnsubscribePayload payload = new MqttUnsubscribePayload(topics); - - MqttMessage unsubscribe = MqttMessageFactory.newMessage(fixedHeader, variableHeader, payload); - - assertEquals(MqttMessageType.UNSUBSCRIBE, unsubscribe.fixedHeader().messageType()); - MqttMessageIdAndPropertiesVariableHeader actualVariableHeader = - (MqttMessageIdAndPropertiesVariableHeader) unsubscribe.variableHeader(); - assertEquals(SAMPLE_MESSAGE_ID, actualVariableHeader.messageId()); - validateProperties(properties, actualVariableHeader.properties()); - MqttUnsubscribePayload actualPayload = (MqttUnsubscribePayload) unsubscribe.payload(); - validateUnsubscribePayload(payload, actualPayload); - } -} diff --git a/codec-mqtt/src/test/java/io/netty/handler/codec/mqtt/MqttPropertiesTest.java b/codec-mqtt/src/test/java/io/netty/handler/codec/mqtt/MqttPropertiesTest.java deleted file mode 100644 index 580056a158..0000000000 --- a/codec-mqtt/src/test/java/io/netty/handler/codec/mqtt/MqttPropertiesTest.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.mqtt; - - -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import static io.netty.handler.codec.mqtt.MqttProperties.MqttPropertyType.CONTENT_TYPE; -import static io.netty.handler.codec.mqtt.MqttProperties.MqttPropertyType.PAYLOAD_FORMAT_INDICATOR; -import static io.netty.handler.codec.mqtt.MqttProperties.MqttPropertyType.SUBSCRIPTION_IDENTIFIER; -import static io.netty.handler.codec.mqtt.MqttProperties.MqttPropertyType.USER_PROPERTY; -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class MqttPropertiesTest { - - private MqttProperties createSampleProperties() { - MqttProperties props = new MqttProperties(); - props.add(new MqttProperties.IntegerProperty(SUBSCRIPTION_IDENTIFIER.value(), 10)); - props.add(new MqttProperties.IntegerProperty(SUBSCRIPTION_IDENTIFIER.value(), 20)); - props.add(new MqttProperties.IntegerProperty(PAYLOAD_FORMAT_INDICATOR.value(), 6)); - props.add(new MqttProperties.StringProperty(CONTENT_TYPE.value(), "text/plain")); - props.add(new MqttProperties.UserProperty("isSecret", "true")); - props.add(new MqttProperties.UserProperty("tag", "firstTag")); - props.add(new MqttProperties.UserProperty("tag", "secondTag")); - return props; - } - - @Test - public void testGetProperty() { - MqttProperties props = createSampleProperties(); - - assertEquals( - "text/plain", - ((MqttProperties.StringProperty) props.getProperty(CONTENT_TYPE.value())).value); - assertEquals( - 10, - ((MqttProperties.IntegerProperty) props.getProperty(SUBSCRIPTION_IDENTIFIER.value())).value.intValue()); - - List expectedUserProps = new ArrayList(); - expectedUserProps.add(new MqttProperties.StringPair("isSecret", "true")); - expectedUserProps.add(new MqttProperties.StringPair("tag", "firstTag")); - expectedUserProps.add(new MqttProperties.StringPair("tag", "secondTag")); - List actualUserProps = - ((MqttProperties.UserProperties) props.getProperty(USER_PROPERTY.value())).value; - assertEquals(expectedUserProps, actualUserProps); - } - - @Test - public void testGetProperties() { - MqttProperties props = createSampleProperties(); - - assertEquals( - Collections.singletonList(new MqttProperties.StringProperty(CONTENT_TYPE.value(), "text/plain")), - props.getProperties(CONTENT_TYPE.value())); - - List expectedSubscriptionIds = new ArrayList(); - expectedSubscriptionIds.add(new MqttProperties.IntegerProperty(SUBSCRIPTION_IDENTIFIER.value(), 10)); - expectedSubscriptionIds.add(new MqttProperties.IntegerProperty(SUBSCRIPTION_IDENTIFIER.value(), 20)); - assertEquals( - expectedSubscriptionIds, - props.getProperties(SUBSCRIPTION_IDENTIFIER.value())); - - List expectedUserProps = new ArrayList(); - expectedUserProps.add(new MqttProperties.UserProperty("isSecret", "true")); - expectedUserProps.add(new MqttProperties.UserProperty("tag", "firstTag")); - expectedUserProps.add(new MqttProperties.UserProperty("tag", "secondTag")); - List actualUserProps = - (List) props.getProperties(USER_PROPERTY.value()); - assertEquals(expectedUserProps, actualUserProps); - } - - @Test - public void testListAll() { - MqttProperties props = createSampleProperties(); - - List expectedProperties = new ArrayList(); - expectedProperties.add(new MqttProperties.IntegerProperty(PAYLOAD_FORMAT_INDICATOR.value(), 6)); - expectedProperties.add(new MqttProperties.StringProperty(CONTENT_TYPE.value(), "text/plain")); - - expectedProperties.add(new MqttProperties.IntegerProperty(SUBSCRIPTION_IDENTIFIER.value(), 10)); - expectedProperties.add(new MqttProperties.IntegerProperty(SUBSCRIPTION_IDENTIFIER.value(), 20)); - - MqttProperties.UserProperties expectedUserProperties = new MqttProperties.UserProperties(); - expectedUserProperties.add(new MqttProperties.StringPair("isSecret", "true")); - expectedUserProperties.add(new MqttProperties.StringPair("tag", "firstTag")); - expectedUserProperties.add(new MqttProperties.StringPair("tag", "secondTag")); - - expectedProperties.add(expectedUserProperties); - - assertEquals(expectedProperties, props.listAll()); - } - -} diff --git a/codec-mqtt/src/test/java/io/netty/handler/codec/mqtt/MqttTestUtils.java b/codec-mqtt/src/test/java/io/netty/handler/codec/mqtt/MqttTestUtils.java deleted file mode 100644 index 24cea09cb2..0000000000 --- a/codec-mqtt/src/test/java/io/netty/handler/codec/mqtt/MqttTestUtils.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.mqtt; - -import io.netty.buffer.ByteBufUtil; - -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.fail; - -public final class MqttTestUtils { - private MqttTestUtils() { - } - - public static void validateProperties(MqttProperties expected, MqttProperties actual) { - for (MqttProperties.MqttProperty expectedProperty : expected.listAll()) { - MqttProperties.MqttProperty actualProperty = actual.getProperty(expectedProperty.propertyId); - List actualProperties = - actual.getProperties(expectedProperty.propertyId); - switch (MqttProperties.MqttPropertyType.valueOf(expectedProperty.propertyId)) { - // one byte value integer property - case PAYLOAD_FORMAT_INDICATOR: - case REQUEST_PROBLEM_INFORMATION: - case REQUEST_RESPONSE_INFORMATION: - case MAXIMUM_QOS: - case RETAIN_AVAILABLE: - case WILDCARD_SUBSCRIPTION_AVAILABLE: - case SUBSCRIPTION_IDENTIFIER_AVAILABLE: - case SHARED_SUBSCRIPTION_AVAILABLE: { - final Integer expectedValue = ((MqttProperties.IntegerProperty) expectedProperty).value; - final Integer actualValue = ((MqttProperties.IntegerProperty) actualProperty).value; - assertEquals(expectedValue, actualValue, "one byte property doesn't match"); - break; - } - // two byte value integer property - case SERVER_KEEP_ALIVE: - case RECEIVE_MAXIMUM: - case TOPIC_ALIAS_MAXIMUM: - case TOPIC_ALIAS: { - final Integer expectedValue = ((MqttProperties.IntegerProperty) expectedProperty).value; - final Integer actualValue = ((MqttProperties.IntegerProperty) actualProperty).value; - assertEquals(expectedValue, actualValue, "two byte property doesn't match"); - break; - } - // four byte value integer property - case PUBLICATION_EXPIRY_INTERVAL: - case SESSION_EXPIRY_INTERVAL: - case WILL_DELAY_INTERVAL: - case MAXIMUM_PACKET_SIZE: { - final Integer expectedValue = ((MqttProperties.IntegerProperty) expectedProperty).value; - final Integer actualValue = ((MqttProperties.IntegerProperty) actualProperty).value; - assertEquals(expectedValue, actualValue, "four byte property doesn't match"); - break; - } - // four byte value integer property - case SUBSCRIPTION_IDENTIFIER: { - final Integer expectedValue = ((MqttProperties.IntegerProperty) expectedProperty).value; - assertContainsValue("Subscription ID doesn't match", expectedValue, actualProperties); - break; - } - // UTF-8 string value integer property - case CONTENT_TYPE: - case RESPONSE_TOPIC: - case ASSIGNED_CLIENT_IDENTIFIER: - case AUTHENTICATION_METHOD: - case RESPONSE_INFORMATION: - case SERVER_REFERENCE: - case REASON_STRING: { - final String expectedValue = ((MqttProperties.StringProperty) expectedProperty).value; - final String actualValue = ((MqttProperties.StringProperty) actualProperty).value; - assertEquals(expectedValue, actualValue, "String property doesn't match"); - break; - } - // User property - case USER_PROPERTY: { - final List expectedPairs = - ((MqttProperties.UserProperties) expectedProperty).value; - final List actualPairs = - ((MqttProperties.UserProperties) actualProperty).value; - assertEquals(expectedPairs, actualPairs, "User properties count doesn't match"); - for (int i = 0; i < expectedPairs.size(); i++) { - assertEquals(expectedPairs.get(i), actualPairs.get(i), "User property mismatch"); - } - break; - } - // byte[] property - case CORRELATION_DATA: - case AUTHENTICATION_DATA: { - final byte[] expectedValue = ((MqttProperties.BinaryProperty) expectedProperty).value; - final byte[] actualValue = ((MqttProperties.BinaryProperty) actualProperty).value; - final String expectedHexDump = ByteBufUtil.hexDump(expectedValue); - final String actualHexDump = ByteBufUtil.hexDump(actualValue); - assertEquals(expectedHexDump, actualHexDump, "byte[] property doesn't match"); - break; - } - default: - fail("Property Id not recognized " + Integer.toHexString(expectedProperty.propertyId)); - } - } - for (MqttProperties.MqttProperty actualProperty : actual.listAll()) { - MqttProperties.MqttProperty expectedProperty = expected.getProperty(actualProperty.propertyId); - assertNotNull(expectedProperty, "Property " + actualProperty.propertyId + " not expected"); - } - } - - private static void assertContainsValue(String message, - Integer expectedValue, - List properties) { - for (MqttProperties.MqttProperty property: properties) { - if (property instanceof MqttProperties.IntegerProperty && - ((MqttProperties.IntegerProperty) property).value == expectedValue) { - return; - } - } - fail(message + " - properties didn't contain expected integer value " + expectedValue + ": " + properties); - } - - public static void validateSubscribePayload(MqttSubscribePayload expected, MqttSubscribePayload actual) { - List expectedTopicSubscriptions = expected.topicSubscriptions(); - List actualTopicSubscriptions = actual.topicSubscriptions(); - - assertEquals( - expectedTopicSubscriptions.size(), - actualTopicSubscriptions.size(), - "MqttSubscribePayload TopicSubscriptionList size mismatch"); - for (int i = 0; i < expectedTopicSubscriptions.size(); i++) { - validateTopicSubscription(expectedTopicSubscriptions.get(i), actualTopicSubscriptions.get(i)); - } - } - - public static void validateTopicSubscription( - MqttTopicSubscription expected, - MqttTopicSubscription actual) { - assertEquals(expected.topicName(), actual.topicName(), "MqttTopicSubscription TopicName mismatch"); - assertEquals( - expected.qualityOfService(), - actual.qualityOfService(), - "MqttTopicSubscription Qos mismatch"); - assertEquals( - expected.option(), - actual.option(), - "MqttTopicSubscription options mismatch"); - } - - public static void validateUnsubscribePayload(MqttUnsubscribePayload expected, MqttUnsubscribePayload actual) { - assertArrayEquals( - expected.topics().toArray(), - actual.topics().toArray(), - "MqttUnsubscribePayload TopicList mismatch"); - } -} diff --git a/codec-redis/pom.xml b/codec-redis/pom.xml deleted file mode 100644 index 94b5b12d7a..0000000000 --- a/codec-redis/pom.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - 4.0.0 - - io.netty - netty-parent - 5.0.0.Final-SNAPSHOT - - - netty-codec-redis - jar - - Netty/Codec/Redis - - - io.netty.codec.redis - - - - - ${project.groupId} - netty-common - ${project.version} - - - ${project.groupId} - netty-buffer - ${project.version} - - - ${project.groupId} - netty-transport - ${project.version} - - - ${project.groupId} - netty-codec - ${project.version} - - - - diff --git a/codec-redis/src/main/java/io/netty/handler/codec/redis/AbstractStringRedisMessage.java b/codec-redis/src/main/java/io/netty/handler/codec/redis/AbstractStringRedisMessage.java deleted file mode 100644 index 627e39fc50..0000000000 --- a/codec-redis/src/main/java/io/netty/handler/codec/redis/AbstractStringRedisMessage.java +++ /dev/null @@ -1,53 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.redis; - -import static java.util.Objects.requireNonNull; - -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.UnstableApi; - -/** - * Abstract class for Simple Strings or Errors. - */ -@UnstableApi -public abstract class AbstractStringRedisMessage implements RedisMessage { - - private final String content; - - AbstractStringRedisMessage(String content) { - this.content = requireNonNull(content, "content"); - } - - /** - * Get string content of this {@link AbstractStringRedisMessage}. - * - * @return content of this message. - */ - public final String content() { - return content; - } - - @Override - public String toString() { - return new StringBuilder(StringUtil.simpleClassName(this)) - .append('[') - .append("content=") - .append(content) - .append(']').toString(); - } - -} diff --git a/codec-redis/src/main/java/io/netty/handler/codec/redis/ArrayHeaderRedisMessage.java b/codec-redis/src/main/java/io/netty/handler/codec/redis/ArrayHeaderRedisMessage.java deleted file mode 100644 index cedf64ed5f..0000000000 --- a/codec-redis/src/main/java/io/netty/handler/codec/redis/ArrayHeaderRedisMessage.java +++ /dev/null @@ -1,63 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.redis; - -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.UnstableApi; - -/** - * Header of Redis Array Message. - */ -@UnstableApi -public class ArrayHeaderRedisMessage implements RedisMessage { - - private final long length; - - /** - * Creates a {@link ArrayHeaderRedisMessage} for the given {@code length}. - */ - public ArrayHeaderRedisMessage(long length) { - if (length < RedisConstants.NULL_VALUE) { - throw new RedisCodecException("length: " + length + " (expected: >= " + RedisConstants.NULL_VALUE + ")"); - } - this.length = length; - } - - /** - * Get length of this array object. - */ - public final long length() { - return length; - } - - /** - * Returns whether the content of this message is {@code null}. - * - * @return indicates whether the content of this message is {@code null}. - */ - public boolean isNull() { - return length == RedisConstants.NULL_VALUE; - } - - @Override - public String toString() { - return new StringBuilder(StringUtil.simpleClassName(this)) - .append('[') - .append("length=") - .append(length) - .append(']').toString(); - } -} diff --git a/codec-redis/src/main/java/io/netty/handler/codec/redis/ArrayRedisMessage.java b/codec-redis/src/main/java/io/netty/handler/codec/redis/ArrayRedisMessage.java deleted file mode 100644 index ba29fb2fb0..0000000000 --- a/codec-redis/src/main/java/io/netty/handler/codec/redis/ArrayRedisMessage.java +++ /dev/null @@ -1,178 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.redis; - -import static java.util.Objects.requireNonNull; - -import io.netty.util.AbstractReferenceCounted; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.UnstableApi; - -import java.util.Collections; -import java.util.List; - -/** - * Arrays of RESP. - */ -@UnstableApi -public class ArrayRedisMessage extends AbstractReferenceCounted implements RedisMessage { - - private final List children; - - private ArrayRedisMessage() { - children = Collections.emptyList(); - } - - /** - * Creates a {@link ArrayRedisMessage} for the given {@code content}. - * - * @param children the children. - */ - public ArrayRedisMessage(List children) { - // do not retain here. children are already retained when created. - this.children = requireNonNull(children, "children"); - } - - /** - * Get children of this Arrays. It can be null or empty. - * - * @return list of {@link RedisMessage}s. - */ - public final List children() { - return children; - } - - /** - * Returns whether the content of this message is {@code null}. - * - * @return indicates whether the content of this message is {@code null}. - */ - public boolean isNull() { - return false; - } - - @Override - protected void deallocate() { - for (RedisMessage msg : children) { - ReferenceCountUtil.release(msg); - } - } - - @Override - public ArrayRedisMessage touch(Object hint) { - for (RedisMessage msg : children) { - ReferenceCountUtil.touch(msg); - } - return this; - } - - @Override - public String toString() { - return new StringBuilder(StringUtil.simpleClassName(this)) - .append('[') - .append("children=") - .append(children.size()) - .append(']').toString(); - } - - /** - * A predefined null array instance for {@link ArrayRedisMessage}. - */ - public static final ArrayRedisMessage NULL_INSTANCE = new ArrayRedisMessage() { - @Override - public boolean isNull() { - return true; - } - - @Override - public ArrayRedisMessage retain() { - return this; - } - - @Override - public ArrayRedisMessage retain(int increment) { - return this; - } - - @Override - public ArrayRedisMessage touch() { - return this; - } - - @Override - public ArrayRedisMessage touch(Object hint) { - return this; - } - - @Override - public boolean release() { - return false; - } - - @Override - public boolean release(int decrement) { - return false; - } - - @Override - public String toString() { - return "NullArrayRedisMessage"; - } - }; - - /** - * A predefined empty array instance for {@link ArrayRedisMessage}. - */ - public static final ArrayRedisMessage EMPTY_INSTANCE = new ArrayRedisMessage() { - - @Override - public ArrayRedisMessage retain() { - return this; - } - - @Override - public ArrayRedisMessage retain(int increment) { - return this; - } - - @Override - public ArrayRedisMessage touch() { - return this; - } - - @Override - public ArrayRedisMessage touch(Object hint) { - return this; - } - - @Override - public boolean release() { - return false; - } - - @Override - public boolean release(int decrement) { - return false; - } - - @Override - public String toString() { - return "EmptyArrayRedisMessage"; - } - }; - -} diff --git a/codec-redis/src/main/java/io/netty/handler/codec/redis/BulkStringHeaderRedisMessage.java b/codec-redis/src/main/java/io/netty/handler/codec/redis/BulkStringHeaderRedisMessage.java deleted file mode 100644 index d7fc86ff2e..0000000000 --- a/codec-redis/src/main/java/io/netty/handler/codec/redis/BulkStringHeaderRedisMessage.java +++ /dev/null @@ -1,55 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.redis; - -import io.netty.util.internal.UnstableApi; - -/** - * The header of Bulk Strings in RESP. - */ -@UnstableApi -public class BulkStringHeaderRedisMessage implements RedisMessage { - - private final int bulkStringLength; - - /** - * Creates a {@link BulkStringHeaderRedisMessage}. - * - * @param bulkStringLength follow content length. - */ - public BulkStringHeaderRedisMessage(int bulkStringLength) { - if (bulkStringLength <= 0) { - throw new RedisCodecException("bulkStringLength: " + bulkStringLength + " (expected: > 0)"); - } - this.bulkStringLength = bulkStringLength; - } - - /** - * Return {@code bulkStringLength} for this content. - */ - public final int bulkStringLength() { - return bulkStringLength; - } - - /** - * Returns whether the content of this message is {@code null}. - * - * @return indicates whether the content of this message is {@code null}. - */ - public boolean isNull() { - return bulkStringLength == RedisConstants.NULL_VALUE; - } -} diff --git a/codec-redis/src/main/java/io/netty/handler/codec/redis/BulkStringRedisContent.java b/codec-redis/src/main/java/io/netty/handler/codec/redis/BulkStringRedisContent.java deleted file mode 100644 index ed54a337c7..0000000000 --- a/codec-redis/src/main/java/io/netty/handler/codec/redis/BulkStringRedisContent.java +++ /dev/null @@ -1,56 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.redis; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufHolder; -import io.netty.channel.ChannelPipeline; -import io.netty.util.internal.UnstableApi; - -/** - * A chunk of bulk strings which is used for Redis chunked transfer-encoding. - * {@link RedisDecoder} generates {@link BulkStringRedisContent} after - * {@link BulkStringHeaderRedisMessage} when the content is large or the encoding of the content is chunked. - * If you prefer not to receive {@link BulkStringRedisContent} in your handler, - * place {@link RedisBulkStringAggregator} after {@link RedisDecoder} in the {@link ChannelPipeline}. - */ -@UnstableApi -public interface BulkStringRedisContent extends RedisMessage, ByteBufHolder { - - @Override - BulkStringRedisContent copy(); - - @Override - BulkStringRedisContent duplicate(); - - @Override - BulkStringRedisContent retainedDuplicate(); - - @Override - BulkStringRedisContent replace(ByteBuf content); - - @Override - BulkStringRedisContent retain(); - - @Override - BulkStringRedisContent retain(int increment); - - @Override - BulkStringRedisContent touch(); - - @Override - BulkStringRedisContent touch(Object hint); -} diff --git a/codec-redis/src/main/java/io/netty/handler/codec/redis/DefaultBulkStringRedisContent.java b/codec-redis/src/main/java/io/netty/handler/codec/redis/DefaultBulkStringRedisContent.java deleted file mode 100644 index 9b49ef03c0..0000000000 --- a/codec-redis/src/main/java/io/netty/handler/codec/redis/DefaultBulkStringRedisContent.java +++ /dev/null @@ -1,90 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.redis; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.DefaultByteBufHolder; -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.UnstableApi; - -/** - * A default implementation of {@link BulkStringRedisContent}. - */ -@UnstableApi -public class DefaultBulkStringRedisContent extends DefaultByteBufHolder implements BulkStringRedisContent { - - /** - * Creates a {@link DefaultBulkStringRedisContent} for the given {@code content}. - * - * @param content the content, can be {@code null}. - */ - public DefaultBulkStringRedisContent(ByteBuf content) { - super(content); - } - - @Override - public BulkStringRedisContent copy() { - return (BulkStringRedisContent) super.copy(); - } - - @Override - public BulkStringRedisContent duplicate() { - return (BulkStringRedisContent) super.duplicate(); - } - - @Override - public BulkStringRedisContent retainedDuplicate() { - return (BulkStringRedisContent) super.retainedDuplicate(); - } - - @Override - public BulkStringRedisContent replace(ByteBuf content) { - return new DefaultBulkStringRedisContent(content); - } - - @Override - public BulkStringRedisContent retain() { - super.retain(); - return this; - } - - @Override - public BulkStringRedisContent retain(int increment) { - super.retain(increment); - return this; - } - - @Override - public BulkStringRedisContent touch() { - super.touch(); - return this; - } - - @Override - public BulkStringRedisContent touch(Object hint) { - super.touch(hint); - return this; - } - - @Override - public String toString() { - return new StringBuilder(StringUtil.simpleClassName(this)) - .append('[') - .append("content=") - .append(content()) - .append(']').toString(); - } -} diff --git a/codec-redis/src/main/java/io/netty/handler/codec/redis/DefaultLastBulkStringRedisContent.java b/codec-redis/src/main/java/io/netty/handler/codec/redis/DefaultLastBulkStringRedisContent.java deleted file mode 100644 index a4f44317b7..0000000000 --- a/codec-redis/src/main/java/io/netty/handler/codec/redis/DefaultLastBulkStringRedisContent.java +++ /dev/null @@ -1,79 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.redis; - -import io.netty.buffer.ByteBuf; -import io.netty.util.internal.UnstableApi; - -/** - * A default implementation for {@link LastBulkStringRedisContent}. - */ -@UnstableApi -public final class DefaultLastBulkStringRedisContent extends DefaultBulkStringRedisContent - implements LastBulkStringRedisContent { - - /** - * Creates a {@link DefaultLastBulkStringRedisContent} for the given {@code content}. - * @param content the content, can be {@code null}. - */ - public DefaultLastBulkStringRedisContent(ByteBuf content) { - super(content); - } - - @Override - public LastBulkStringRedisContent copy() { - return (LastBulkStringRedisContent) super.copy(); - } - - @Override - public LastBulkStringRedisContent duplicate() { - return (LastBulkStringRedisContent) super.duplicate(); - } - - @Override - public LastBulkStringRedisContent retainedDuplicate() { - return (LastBulkStringRedisContent) super.retainedDuplicate(); - } - - @Override - public LastBulkStringRedisContent replace(ByteBuf content) { - return new DefaultLastBulkStringRedisContent(content); - } - - @Override - public LastBulkStringRedisContent retain() { - super.retain(); - return this; - } - - @Override - public LastBulkStringRedisContent retain(int increment) { - super.retain(increment); - return this; - } - - @Override - public LastBulkStringRedisContent touch() { - super.touch(); - return this; - } - - @Override - public LastBulkStringRedisContent touch(Object hint) { - super.touch(hint); - return this; - } -} diff --git a/codec-redis/src/main/java/io/netty/handler/codec/redis/ErrorRedisMessage.java b/codec-redis/src/main/java/io/netty/handler/codec/redis/ErrorRedisMessage.java deleted file mode 100644 index 8bd83b57b0..0000000000 --- a/codec-redis/src/main/java/io/netty/handler/codec/redis/ErrorRedisMessage.java +++ /dev/null @@ -1,35 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.redis; - -import io.netty.util.internal.UnstableApi; - -/** - * Errors of RESP. - */ -@UnstableApi -public final class ErrorRedisMessage extends AbstractStringRedisMessage { - - /** - * Creates a {@link ErrorRedisMessage} from {@code content}. - * - * @param content the message content, must not be {@code null}. - */ - public ErrorRedisMessage(String content) { - super(content); - } - -} diff --git a/codec-redis/src/main/java/io/netty/handler/codec/redis/FixedRedisMessagePool.java b/codec-redis/src/main/java/io/netty/handler/codec/redis/FixedRedisMessagePool.java deleted file mode 100644 index e8ddc93b29..0000000000 --- a/codec-redis/src/main/java/io/netty/handler/codec/redis/FixedRedisMessagePool.java +++ /dev/null @@ -1,186 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.redis; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.CharsetUtil; -import io.netty.util.collection.LongObjectHashMap; -import io.netty.util.collection.LongObjectMap; -import io.netty.util.internal.UnstableApi; - -import java.util.HashMap; -import java.util.Map; - -/** - * A default fixed redis message pool. - */ -@UnstableApi -public final class FixedRedisMessagePool implements RedisMessagePool { - - public enum RedisReplyKey { - OK, PONG, QUEUED - } - - public enum RedisErrorKey { - ERR("ERR"), - ERR_IDX("ERR index out of range"), - ERR_NOKEY("ERR no such key"), - ERR_SAMEOBJ("ERR source and destination objects are the same"), - ERR_SYNTAX("ERR syntax error"), - BUSY("BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE."), - BUSYKEY("BUSYKEY Target key name already exists."), - EXECABORT("EXECABORT Transaction discarded because of previous errors."), - LOADING("LOADING Redis is loading the dataset in memory"), - MASTERDOWN("MASTERDOWN Link with MASTER is down and slave-serve-stale-data is set to 'no'."), - MISCONF("MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. " + - "Commands that may modify the data set are disabled. Please check Redis logs for details " + - "about the error."), - NOREPLICAS("NOREPLICAS Not enough good slaves to write."), - NOSCRIPT("NOSCRIPT No matching script. Please use EVAL."), - OOM("OOM command not allowed when used memory > 'maxmemory'."), - READONLY("READONLY You can't write against a read only slave."), - WRONGTYPE("WRONGTYPE Operation against a key holding the wrong kind of value"), - NOT_AUTH("NOAUTH Authentication required."); - - private final String msg; - - RedisErrorKey(String msg) { - this.msg = msg; - } - - @Override - public String toString() { - return msg; - } - } - - private static final long MIN_CACHED_INTEGER_NUMBER = RedisConstants.NULL_VALUE; // inclusive - private static final long MAX_CACHED_INTEGER_NUMBER = 128; // exclusive - - // cached integer size cannot larger than `int` range because of Collection. - private static final int SIZE_CACHED_INTEGER_NUMBER = (int) (MAX_CACHED_INTEGER_NUMBER - MIN_CACHED_INTEGER_NUMBER); - - /** - * A shared object for {@link FixedRedisMessagePool}. - */ - public static final FixedRedisMessagePool INSTANCE = new FixedRedisMessagePool(); - - // internal caches. - private final Map byteBufToSimpleStrings; - private final Map stringToSimpleStrings; - private final Map keyToSimpleStrings; - private final Map byteBufToErrors; - private final Map stringToErrors; - private final Map keyToErrors; - private final Map byteBufToIntegers; - private final LongObjectMap longToIntegers; - private final LongObjectMap longToByteBufs; - - /** - * Creates a {@link FixedRedisMessagePool} instance. - */ - private FixedRedisMessagePool() { - keyToSimpleStrings = new HashMap<>(RedisReplyKey.values().length, 1.0f); - stringToSimpleStrings = new HashMap<>(RedisReplyKey.values().length, 1.0f); - byteBufToSimpleStrings = new HashMap<>(RedisReplyKey.values().length, 1.0f); - for (RedisReplyKey value : RedisReplyKey.values()) { - ByteBuf key = Unpooled.unreleasableBuffer(Unpooled.wrappedBuffer( - value.name().getBytes(CharsetUtil.UTF_8))).asReadOnly(); - SimpleStringRedisMessage message = new SimpleStringRedisMessage(new String(Unpooled.unreleasableBuffer( - Unpooled.wrappedBuffer(value.name().getBytes(CharsetUtil.UTF_8))).array())); - stringToSimpleStrings.put(value.name(), message); - keyToSimpleStrings.put(value, message); - byteBufToSimpleStrings.put(key, message); - } - - keyToErrors = new HashMap<>(RedisErrorKey.values().length, 1.0f); - stringToErrors = new HashMap<>(RedisErrorKey.values().length, 1.0f); - byteBufToErrors = new HashMap<>(RedisErrorKey.values().length, 1.0f); - for (RedisErrorKey value : RedisErrorKey.values()) { - ByteBuf key = Unpooled.unreleasableBuffer(Unpooled.wrappedBuffer( - value.toString().getBytes(CharsetUtil.UTF_8))).asReadOnly(); - ErrorRedisMessage message = new ErrorRedisMessage(new String(Unpooled.unreleasableBuffer( - Unpooled.wrappedBuffer(value.toString().getBytes(CharsetUtil.UTF_8))).array())); - stringToErrors.put(value.toString(), message); - keyToErrors.put(value, message); - byteBufToErrors.put(key, message); - } - - byteBufToIntegers = new HashMap<>(SIZE_CACHED_INTEGER_NUMBER, 1.0f); - longToIntegers = new LongObjectHashMap<>(SIZE_CACHED_INTEGER_NUMBER, 1.0f); - longToByteBufs = new LongObjectHashMap<>(SIZE_CACHED_INTEGER_NUMBER, 1.0f); - for (long value = MIN_CACHED_INTEGER_NUMBER; value < MAX_CACHED_INTEGER_NUMBER; value++) { - byte[] keyBytes = RedisCodecUtil.longToAsciiBytes(value); - ByteBuf keyByteBuf = Unpooled.unreleasableBuffer(Unpooled.wrappedBuffer(keyBytes)).asReadOnly(); - IntegerRedisMessage cached = new IntegerRedisMessage(value); - byteBufToIntegers.put(keyByteBuf, cached); - longToIntegers.put(value, cached); - longToByteBufs.put(value, keyBytes); - } - } - - @Override - public SimpleStringRedisMessage getSimpleString(String content) { - return stringToSimpleStrings.get(content); - } - - /** - * Returns {@link SimpleStringRedisMessage} for the given {@link RedisReplyKey} - * or {@code null} if it does not exist. - */ - public SimpleStringRedisMessage getSimpleString(RedisReplyKey key) { - return keyToSimpleStrings.get(key); - } - - @Override - public SimpleStringRedisMessage getSimpleString(ByteBuf content) { - return byteBufToSimpleStrings.get(content); - } - - @Override - public ErrorRedisMessage getError(String content) { - return stringToErrors.get(content); - } - - /** - * Returns {@link ErrorRedisMessage} for the given {@link RedisErrorKey} - * or {@code null} if it does not exist. - */ - public ErrorRedisMessage getError(RedisErrorKey key) { - return keyToErrors.get(key); - } - - @Override - public ErrorRedisMessage getError(ByteBuf content) { - return byteBufToErrors.get(content); - } - - @Override - public IntegerRedisMessage getInteger(long value) { - return longToIntegers.get(value); - } - - @Override - public IntegerRedisMessage getInteger(ByteBuf content) { - return byteBufToIntegers.get(content); - } - - @Override - public byte[] getByteBufOfInteger(long value) { - return longToByteBufs.get(value); - } -} diff --git a/codec-redis/src/main/java/io/netty/handler/codec/redis/FullBulkStringRedisMessage.java b/codec-redis/src/main/java/io/netty/handler/codec/redis/FullBulkStringRedisMessage.java deleted file mode 100644 index 8274c0697c..0000000000 --- a/codec-redis/src/main/java/io/netty/handler/codec/redis/FullBulkStringRedisMessage.java +++ /dev/null @@ -1,231 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.redis; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.DefaultByteBufHolder; -import io.netty.buffer.Unpooled; -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.UnstableApi; - -/** - * An aggregated bulk string of RESP. - */ -@UnstableApi -public class FullBulkStringRedisMessage extends DefaultByteBufHolder implements LastBulkStringRedisContent { - - private FullBulkStringRedisMessage() { - this(Unpooled.EMPTY_BUFFER); - } - - /** - * Creates a {@link FullBulkStringRedisMessage} for the given {@code content}. - * - * @param content the content, must not be {@code null}. If content is null or empty, - * use {@link FullBulkStringRedisMessage#NULL_INSTANCE} or {@link FullBulkStringRedisMessage#EMPTY_INSTANCE} - * instead of constructor. - */ - public FullBulkStringRedisMessage(ByteBuf content) { - super(content); - } - - /** - * Returns whether the content of this message is {@code null}. - * - * @return indicates whether the content of this message is {@code null}. - */ - public boolean isNull() { - return false; - } - - @Override - public String toString() { - return new StringBuilder(StringUtil.simpleClassName(this)) - .append('[') - .append("content=") - .append(content()) - .append(']').toString(); - } - - /** - * A predefined null instance of {@link FullBulkStringRedisMessage}. - */ - public static final FullBulkStringRedisMessage NULL_INSTANCE = new FullBulkStringRedisMessage() { - @Override - public boolean isNull() { - return true; - } - - @Override - public ByteBuf content() { - return Unpooled.EMPTY_BUFFER; - } - - @Override - public FullBulkStringRedisMessage copy() { - return this; - } - - @Override - public FullBulkStringRedisMessage duplicate() { - return this; - } - - @Override - public FullBulkStringRedisMessage retainedDuplicate() { - return this; - } - - @Override - public int refCnt() { - return 1; - } - - @Override - public FullBulkStringRedisMessage retain() { - return this; - } - - @Override - public FullBulkStringRedisMessage retain(int increment) { - return this; - } - - @Override - public FullBulkStringRedisMessage touch() { - return this; - } - - @Override - public FullBulkStringRedisMessage touch(Object hint) { - return this; - } - - @Override - public boolean release() { - return false; - } - - @Override - public boolean release(int decrement) { - return false; - } - }; - - /** - * A predefined empty instance of {@link FullBulkStringRedisMessage}. - */ - public static final FullBulkStringRedisMessage EMPTY_INSTANCE = new FullBulkStringRedisMessage() { - @Override - public ByteBuf content() { - return Unpooled.EMPTY_BUFFER; - } - - @Override - public FullBulkStringRedisMessage copy() { - return this; - } - - @Override - public FullBulkStringRedisMessage duplicate() { - return this; - } - - @Override - public FullBulkStringRedisMessage retainedDuplicate() { - return this; - } - - @Override - public int refCnt() { - return 1; - } - - @Override - public FullBulkStringRedisMessage retain() { - return this; - } - - @Override - public FullBulkStringRedisMessage retain(int increment) { - return this; - } - - @Override - public FullBulkStringRedisMessage touch() { - return this; - } - - @Override - public FullBulkStringRedisMessage touch(Object hint) { - return this; - } - - @Override - public boolean release() { - return false; - } - - @Override - public boolean release(int decrement) { - return false; - } - }; - - @Override - public FullBulkStringRedisMessage copy() { - return (FullBulkStringRedisMessage) super.copy(); - } - - @Override - public FullBulkStringRedisMessage duplicate() { - return (FullBulkStringRedisMessage) super.duplicate(); - } - - @Override - public FullBulkStringRedisMessage retainedDuplicate() { - return (FullBulkStringRedisMessage) super.retainedDuplicate(); - } - - @Override - public FullBulkStringRedisMessage replace(ByteBuf content) { - return new FullBulkStringRedisMessage(content); - } - - @Override - public FullBulkStringRedisMessage retain() { - super.retain(); - return this; - } - - @Override - public FullBulkStringRedisMessage retain(int increment) { - super.retain(increment); - return this; - } - - @Override - public FullBulkStringRedisMessage touch() { - super.touch(); - return this; - } - - @Override - public FullBulkStringRedisMessage touch(Object hint) { - super.touch(hint); - return this; - } -} diff --git a/codec-redis/src/main/java/io/netty/handler/codec/redis/InlineCommandRedisMessage.java b/codec-redis/src/main/java/io/netty/handler/codec/redis/InlineCommandRedisMessage.java deleted file mode 100644 index a07736ca6a..0000000000 --- a/codec-redis/src/main/java/io/netty/handler/codec/redis/InlineCommandRedisMessage.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.redis; - -import io.netty.util.internal.UnstableApi; - -/** - * Inline commands of RESP. - */ -@UnstableApi -public final class InlineCommandRedisMessage extends AbstractStringRedisMessage { - - /** - * Creates a {@link InlineCommandRedisMessage} for the given {@code content}. - * - * @param content the message content, must not be {@code null}. - */ - public InlineCommandRedisMessage(String content) { - super(content); - } - -} diff --git a/codec-redis/src/main/java/io/netty/handler/codec/redis/IntegerRedisMessage.java b/codec-redis/src/main/java/io/netty/handler/codec/redis/IntegerRedisMessage.java deleted file mode 100644 index 90a3bfd995..0000000000 --- a/codec-redis/src/main/java/io/netty/handler/codec/redis/IntegerRedisMessage.java +++ /dev/null @@ -1,55 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.redis; - -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.UnstableApi; - -/** - * Integers of RESP. - */ -@UnstableApi -public final class IntegerRedisMessage implements RedisMessage { - - private final long value; - - /** - * Creates a {@link IntegerRedisMessage} for the given {@code content}. - * - * @param value the message content. - */ - public IntegerRedisMessage(long value) { - this.value = value; - } - - /** - * Get long value of this {@link IntegerRedisMessage}. - * - * @return long value - */ - public long value() { - return value; - } - - @Override - public String toString() { - return new StringBuilder(StringUtil.simpleClassName(this)) - .append('[') - .append("value=") - .append(value) - .append(']').toString(); - } -} diff --git a/codec-redis/src/main/java/io/netty/handler/codec/redis/LastBulkStringRedisContent.java b/codec-redis/src/main/java/io/netty/handler/codec/redis/LastBulkStringRedisContent.java deleted file mode 100644 index d45402abd6..0000000000 --- a/codec-redis/src/main/java/io/netty/handler/codec/redis/LastBulkStringRedisContent.java +++ /dev/null @@ -1,117 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.redis; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.internal.UnstableApi; - -/** - * A last chunk of Bulk Strings. - */ -@UnstableApi -public interface LastBulkStringRedisContent extends BulkStringRedisContent { - - /** - * The 'end of content' marker in chunked encoding. - */ - LastBulkStringRedisContent EMPTY_LAST_CONTENT = new LastBulkStringRedisContent() { - - @Override - public ByteBuf content() { - return Unpooled.EMPTY_BUFFER; - } - - @Override - public LastBulkStringRedisContent copy() { - return this; - } - - @Override - public LastBulkStringRedisContent duplicate() { - return this; - } - - @Override - public LastBulkStringRedisContent retainedDuplicate() { - return this; - } - - @Override - public LastBulkStringRedisContent replace(ByteBuf content) { - return new DefaultLastBulkStringRedisContent(content); - } - - @Override - public LastBulkStringRedisContent retain(int increment) { - return this; - } - - @Override - public LastBulkStringRedisContent retain() { - return this; - } - - @Override - public int refCnt() { - return 1; - } - - @Override - public LastBulkStringRedisContent touch() { - return this; - } - - @Override - public LastBulkStringRedisContent touch(Object hint) { - return this; - } - - @Override - public boolean release() { - return false; - } - - @Override - public boolean release(int decrement) { - return false; - } - }; - - @Override - LastBulkStringRedisContent copy(); - - @Override - LastBulkStringRedisContent duplicate(); - - @Override - LastBulkStringRedisContent retainedDuplicate(); - - @Override - LastBulkStringRedisContent replace(ByteBuf content); - - @Override - LastBulkStringRedisContent retain(); - - @Override - LastBulkStringRedisContent retain(int increment); - - @Override - LastBulkStringRedisContent touch(); - - @Override - LastBulkStringRedisContent touch(Object hint); -} diff --git a/codec-redis/src/main/java/io/netty/handler/codec/redis/RedisArrayAggregator.java b/codec-redis/src/main/java/io/netty/handler/codec/redis/RedisArrayAggregator.java deleted file mode 100644 index 2231c38178..0000000000 --- a/codec-redis/src/main/java/io/netty/handler/codec/redis/RedisArrayAggregator.java +++ /dev/null @@ -1,93 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.redis; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.CodecException; -import io.netty.handler.codec.MessageToMessageDecoder; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.internal.UnstableApi; - -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Deque; -import java.util.List; - -/** - * Aggregates {@link RedisMessage} parts into {@link ArrayRedisMessage}. This decoder - * should be used together with {@link RedisDecoder}. - */ -@UnstableApi -public final class RedisArrayAggregator extends MessageToMessageDecoder { - - private final Deque depths = new ArrayDeque<>(4); - - @Override - protected void decode(ChannelHandlerContext ctx, RedisMessage msg) throws Exception { - if (msg instanceof ArrayHeaderRedisMessage) { - msg = decodeRedisArrayHeader((ArrayHeaderRedisMessage) msg); - if (msg == null) { - return; - } - } else { - ReferenceCountUtil.retain(msg); - } - - while (!depths.isEmpty()) { - AggregateState current = depths.peek(); - current.children.add(msg); - - // if current aggregation completed, go to parent aggregation. - if (current.children.size() == current.length) { - msg = new ArrayRedisMessage(current.children); - depths.pop(); - } else { - // not aggregated yet. try next time. - return; - } - } - - ctx.fireChannelRead(msg); - } - - private RedisMessage decodeRedisArrayHeader(ArrayHeaderRedisMessage header) { - if (header.isNull()) { - return ArrayRedisMessage.NULL_INSTANCE; - } else if (header.length() == 0L) { - return ArrayRedisMessage.EMPTY_INSTANCE; - } else if (header.length() > 0L) { - // Currently, this codec doesn't support `long` length for arrays because Java's List.size() is int. - if (header.length() > Integer.MAX_VALUE) { - throw new CodecException("this codec doesn't support longer length than " + Integer.MAX_VALUE); - } - - // start aggregating array - depths.push(new AggregateState((int) header.length())); - return null; - } else { - throw new CodecException("bad length: " + header.length()); - } - } - - private static final class AggregateState { - private final int length; - private final List children; - AggregateState(int length) { - this.length = length; - this.children = new ArrayList<>(length); - } - } -} diff --git a/codec-redis/src/main/java/io/netty/handler/codec/redis/RedisBulkStringAggregator.java b/codec-redis/src/main/java/io/netty/handler/codec/redis/RedisBulkStringAggregator.java deleted file mode 100644 index 9df8503397..0000000000 --- a/codec-redis/src/main/java/io/netty/handler/codec/redis/RedisBulkStringAggregator.java +++ /dev/null @@ -1,100 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.redis; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelPipeline; -import io.netty.handler.codec.MessageAggregator; -import io.netty.util.internal.UnstableApi; - -/** - * A {@link ChannelHandler} that aggregates an {@link BulkStringHeaderRedisMessage} - * and its following {@link BulkStringRedisContent}s into a single {@link FullBulkStringRedisMessage} - * with no following {@link BulkStringRedisContent}s. It is useful when you don't want to take - * care of {@link RedisMessage}s whose transfer encoding is 'chunked'. Insert this - * handler after {@link RedisDecoder} in the {@link ChannelPipeline}: - *
- * {@link ChannelPipeline} p = ...;
- * ...
- * p.addLast("encoder", new {@link RedisEncoder}());
- * p.addLast("decoder", new {@link RedisDecoder}());
- * p.addLast("aggregator", new {@link RedisBulkStringAggregator}());
- * ...
- * p.addLast("handler", new HttpRequestHandler());
- * 
- * Be aware that you need to have the {@link RedisEncoder} before the {@link RedisBulkStringAggregator} - * in the {@link ChannelPipeline}. - */ -@UnstableApi -public final class RedisBulkStringAggregator extends MessageAggregator { - - /** - * Creates a new instance. - */ - public RedisBulkStringAggregator() { - super(RedisConstants.REDIS_MESSAGE_MAX_LENGTH); - } - - @Override - protected boolean isStartMessage(RedisMessage msg) throws Exception { - return msg instanceof BulkStringHeaderRedisMessage && !isAggregated(msg); - } - - @Override - protected boolean isContentMessage(RedisMessage msg) throws Exception { - return msg instanceof BulkStringRedisContent; - } - - @Override - protected boolean isLastContentMessage(BulkStringRedisContent msg) throws Exception { - return msg instanceof LastBulkStringRedisContent; - } - - @Override - protected boolean isAggregated(RedisMessage msg) throws Exception { - return msg instanceof FullBulkStringRedisMessage; - } - - @Override - protected boolean isContentLengthInvalid(BulkStringHeaderRedisMessage start, int maxContentLength) - throws Exception { - return start.bulkStringLength() > maxContentLength; - } - - @Override - protected Object newContinueResponse(BulkStringHeaderRedisMessage start, int maxContentLength, - ChannelPipeline pipeline) throws Exception { - return null; - } - - @Override - protected boolean closeAfterContinueResponse(Object msg) throws Exception { - throw new UnsupportedOperationException(); - } - - @Override - protected boolean ignoreContentAfterContinueResponse(Object msg) throws Exception { - throw new UnsupportedOperationException(); - } - - @Override - protected FullBulkStringRedisMessage beginAggregation(BulkStringHeaderRedisMessage start, ByteBuf content) - throws Exception { - return new FullBulkStringRedisMessage(content); - } -} diff --git a/codec-redis/src/main/java/io/netty/handler/codec/redis/RedisCodecException.java b/codec-redis/src/main/java/io/netty/handler/codec/redis/RedisCodecException.java deleted file mode 100644 index febd3db447..0000000000 --- a/codec-redis/src/main/java/io/netty/handler/codec/redis/RedisCodecException.java +++ /dev/null @@ -1,42 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.redis; - -import io.netty.handler.codec.CodecException; -import io.netty.util.internal.UnstableApi; - -/** - * An {@link Exception} which is thrown by {@link RedisEncoder} or {@link RedisDecoder}. - */ -@UnstableApi -public final class RedisCodecException extends CodecException { - - private static final long serialVersionUID = 5570454251549268063L; - - /** - * Creates a new instance. - */ - public RedisCodecException(String message) { - super(message); - } - - /** - * Creates a new instance. - */ - public RedisCodecException(Throwable cause) { - super(cause); - } -} diff --git a/codec-redis/src/main/java/io/netty/handler/codec/redis/RedisCodecUtil.java b/codec-redis/src/main/java/io/netty/handler/codec/redis/RedisCodecUtil.java deleted file mode 100644 index 95daba495c..0000000000 --- a/codec-redis/src/main/java/io/netty/handler/codec/redis/RedisCodecUtil.java +++ /dev/null @@ -1,55 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.redis; - -import io.netty.util.CharsetUtil; -import io.netty.util.internal.PlatformDependent; - -/** - * Utilities for codec-redis. - */ -final class RedisCodecUtil { - - private RedisCodecUtil() { - } - - static byte[] longToAsciiBytes(long value) { - return Long.toString(value).getBytes(CharsetUtil.US_ASCII); - } - - /** - * Returns a {@code short} value using endian order. - */ - static short makeShort(char first, char second) { - return PlatformDependent.BIG_ENDIAN_NATIVE_ORDER ? - (short) ((second << 8) | first) : (short) ((first << 8) | second); - } - - /** - * Returns a {@code byte[]} of {@code short} value. This is opposite of {@code makeShort()}. - */ - static byte[] shortToBytes(short value) { - byte[] bytes = new byte[2]; - if (PlatformDependent.BIG_ENDIAN_NATIVE_ORDER) { - bytes[1] = (byte) ((value >> 8) & 0xff); - bytes[0] = (byte) (value & 0xff); - } else { - bytes[0] = (byte) ((value >> 8) & 0xff); - bytes[1] = (byte) (value & 0xff); - } - return bytes; - } -} diff --git a/codec-redis/src/main/java/io/netty/handler/codec/redis/RedisConstants.java b/codec-redis/src/main/java/io/netty/handler/codec/redis/RedisConstants.java deleted file mode 100644 index bed626c3cc..0000000000 --- a/codec-redis/src/main/java/io/netty/handler/codec/redis/RedisConstants.java +++ /dev/null @@ -1,46 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.redis; - -/** - * Constant values for Redis encoder/decoder. - */ -final class RedisConstants { - - private RedisConstants() { - } - - static final int TYPE_LENGTH = 1; - - static final int EOL_LENGTH = 2; - - static final int NULL_LENGTH = 2; - - static final int NULL_VALUE = -1; - - static final int REDIS_MESSAGE_MAX_LENGTH = 512 * 1024 * 1024; // 512MB - - // 64KB is max inline length of current Redis server implementation. - static final int REDIS_INLINE_MESSAGE_MAX_LENGTH = 64 * 1024; - - static final int POSITIVE_LONG_MAX_LENGTH = 19; // length of Long.MAX_VALUE - - static final int LONG_MAX_LENGTH = POSITIVE_LONG_MAX_LENGTH + 1; // +1 is sign - - static final short NULL_SHORT = RedisCodecUtil.makeShort('-', '1'); - - static final short EOL_SHORT = RedisCodecUtil.makeShort('\r', '\n'); -} diff --git a/codec-redis/src/main/java/io/netty/handler/codec/redis/RedisDecoder.java b/codec-redis/src/main/java/io/netty/handler/codec/redis/RedisDecoder.java deleted file mode 100644 index 231a349888..0000000000 --- a/codec-redis/src/main/java/io/netty/handler/codec/redis/RedisDecoder.java +++ /dev/null @@ -1,330 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.redis; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.ByteToMessageDecoder; -import io.netty.util.ByteProcessor; -import io.netty.util.CharsetUtil; -import io.netty.util.internal.UnstableApi; - -/** - * Decodes the Redis protocol into {@link RedisMessage} objects following - * RESP (REdis Serialization Protocol). - * - * {@link RedisMessage} parts can be aggregated to {@link RedisMessage} using - * {@link RedisArrayAggregator} or processed directly. - */ -@UnstableApi -public final class RedisDecoder extends ByteToMessageDecoder { - - private final ToPositiveLongProcessor toPositiveLongProcessor = new ToPositiveLongProcessor(); - - private final boolean decodeInlineCommands; - private final int maxInlineMessageLength; - private final RedisMessagePool messagePool; - - // current decoding states - private State state = State.DECODE_TYPE; - private RedisMessageType type; - private int remainingBulkLength; - - private enum State { - DECODE_TYPE, - DECODE_INLINE, // SIMPLE_STRING, ERROR, INTEGER - DECODE_LENGTH, // BULK_STRING, ARRAY_HEADER - DECODE_BULK_STRING_EOL, - DECODE_BULK_STRING_CONTENT, - } - - /** - * Creates a new instance with default {@code maxInlineMessageLength} and {@code messagePool} - * and inline command decoding disabled. - */ - public RedisDecoder() { - this(false); - } - - /** - * Creates a new instance with default {@code maxInlineMessageLength} and {@code messagePool}. - * @param decodeInlineCommands if {@code true}, inline commands will be decoded. - */ - public RedisDecoder(boolean decodeInlineCommands) { - this(RedisConstants.REDIS_INLINE_MESSAGE_MAX_LENGTH, FixedRedisMessagePool.INSTANCE, decodeInlineCommands); - } - - /** - * Creates a new instance with inline command decoding disabled. - * @param maxInlineMessageLength the maximum length of inline message. - * @param messagePool the predefined message pool. - */ - public RedisDecoder(int maxInlineMessageLength, RedisMessagePool messagePool) { - this(maxInlineMessageLength, messagePool, false); - } - - /** - * Creates a new instance. - * @param maxInlineMessageLength the maximum length of inline message. - * @param messagePool the predefined message pool. - * @param decodeInlineCommands if {@code true}, inline commands will be decoded. - */ - public RedisDecoder(int maxInlineMessageLength, RedisMessagePool messagePool, boolean decodeInlineCommands) { - if (maxInlineMessageLength <= 0 || maxInlineMessageLength > RedisConstants.REDIS_MESSAGE_MAX_LENGTH) { - throw new RedisCodecException("maxInlineMessageLength: " + maxInlineMessageLength + - " (expected: <= " + RedisConstants.REDIS_MESSAGE_MAX_LENGTH + ")"); - } - this.maxInlineMessageLength = maxInlineMessageLength; - this.messagePool = messagePool; - this.decodeInlineCommands = decodeInlineCommands; - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - try { - for (;;) { - switch (state) { - case DECODE_TYPE: - if (!decodeType(in)) { - return; - } - break; - case DECODE_INLINE: - if (!decodeInline(ctx, in)) { - return; - } - break; - case DECODE_LENGTH: - if (!decodeLength(ctx, in)) { - return; - } - break; - case DECODE_BULK_STRING_EOL: - if (!decodeBulkStringEndOfLine(ctx, in)) { - return; - } - break; - case DECODE_BULK_STRING_CONTENT: - if (!decodeBulkStringContent(ctx, in)) { - return; - } - break; - default: - throw new RedisCodecException("Unknown state: " + state); - } - } - } catch (RedisCodecException e) { - resetDecoder(); - throw e; - } catch (Exception e) { - resetDecoder(); - throw new RedisCodecException(e); - } - } - - private void resetDecoder() { - state = State.DECODE_TYPE; - remainingBulkLength = 0; - } - - private boolean decodeType(ByteBuf in) throws Exception { - if (!in.isReadable()) { - return false; - } - - type = RedisMessageType.readFrom(in, decodeInlineCommands); - state = type.isInline() ? State.DECODE_INLINE : State.DECODE_LENGTH; - return true; - } - - private boolean decodeInline(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - ByteBuf lineBytes = readLine(in); - if (lineBytes == null) { - if (in.readableBytes() > maxInlineMessageLength) { - throw new RedisCodecException("length: " + in.readableBytes() + - " (expected: <= " + maxInlineMessageLength + ")"); - } - return false; - } - ctx.fireChannelRead(newInlineRedisMessage(type, lineBytes)); - resetDecoder(); - return true; - } - - private boolean decodeLength(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - ByteBuf lineByteBuf = readLine(in); - if (lineByteBuf == null) { - return false; - } - final long length = parseRedisNumber(lineByteBuf); - if (length < RedisConstants.NULL_VALUE) { - throw new RedisCodecException("length: " + length + " (expected: >= " + RedisConstants.NULL_VALUE + ")"); - } - switch (type) { - case ARRAY_HEADER: - ctx.fireChannelRead(new ArrayHeaderRedisMessage(length)); - resetDecoder(); - return true; - case BULK_STRING: - if (length > RedisConstants.REDIS_MESSAGE_MAX_LENGTH) { - throw new RedisCodecException("length: " + length + " (expected: <= " + - RedisConstants.REDIS_MESSAGE_MAX_LENGTH + ")"); - } - remainingBulkLength = (int) length; // range(int) is already checked. - return decodeBulkString(ctx, in); - default: - throw new RedisCodecException("bad type: " + type); - } - } - - private boolean decodeBulkString(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - switch (remainingBulkLength) { - case RedisConstants.NULL_VALUE: // $-1\r\n - ctx.fireChannelRead(FullBulkStringRedisMessage.NULL_INSTANCE); - resetDecoder(); - return true; - case 0: - state = State.DECODE_BULK_STRING_EOL; - return decodeBulkStringEndOfLine(ctx, in); - default: // expectedBulkLength is always positive. - ctx.fireChannelRead(new BulkStringHeaderRedisMessage(remainingBulkLength)); - state = State.DECODE_BULK_STRING_CONTENT; - return decodeBulkStringContent(ctx, in); - } - } - - // $0\r\n \r\n - private boolean decodeBulkStringEndOfLine(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - if (in.readableBytes() < RedisConstants.EOL_LENGTH) { - return false; - } - readEndOfLine(in); - ctx.fireChannelRead(FullBulkStringRedisMessage.EMPTY_INSTANCE); - resetDecoder(); - return true; - } - - // ${expectedBulkLength}\r\n {data...}\r\n - private boolean decodeBulkStringContent(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - final int readableBytes = in.readableBytes(); - if (readableBytes == 0 || remainingBulkLength == 0 && readableBytes < RedisConstants.EOL_LENGTH) { - return false; - } - - // if this is last frame. - if (readableBytes >= remainingBulkLength + RedisConstants.EOL_LENGTH) { - ByteBuf content = in.readSlice(remainingBulkLength); - readEndOfLine(in); - // Only call retain after readEndOfLine(...) as the method may throw an exception. - ctx.fireChannelRead(new DefaultLastBulkStringRedisContent(content.retain())); - resetDecoder(); - return true; - } - - // chunked write. - int toRead = Math.min(remainingBulkLength, readableBytes); - remainingBulkLength -= toRead; - ctx.fireChannelRead(new DefaultBulkStringRedisContent(in.readSlice(toRead).retain())); - return true; - } - - private static void readEndOfLine(final ByteBuf in) { - final short delim = in.readShort(); - if (RedisConstants.EOL_SHORT == delim) { - return; - } - final byte[] bytes = RedisCodecUtil.shortToBytes(delim); - throw new RedisCodecException("delimiter: [" + bytes[0] + "," + bytes[1] + "] (expected: \\r\\n)"); - } - - private RedisMessage newInlineRedisMessage(RedisMessageType messageType, ByteBuf content) { - switch (messageType) { - case INLINE_COMMAND: - return new InlineCommandRedisMessage(content.toString(CharsetUtil.UTF_8)); - case SIMPLE_STRING: { - SimpleStringRedisMessage cached = messagePool.getSimpleString(content); - return cached != null ? cached : new SimpleStringRedisMessage(content.toString(CharsetUtil.UTF_8)); - } - case ERROR: { - ErrorRedisMessage cached = messagePool.getError(content); - return cached != null ? cached : new ErrorRedisMessage(content.toString(CharsetUtil.UTF_8)); - } - case INTEGER: { - IntegerRedisMessage cached = messagePool.getInteger(content); - return cached != null ? cached : new IntegerRedisMessage(parseRedisNumber(content)); - } - default: - throw new RedisCodecException("bad type: " + messageType); - } - } - - private static ByteBuf readLine(ByteBuf in) { - if (!in.isReadable(RedisConstants.EOL_LENGTH)) { - return null; - } - final int lfIndex = in.forEachByte(ByteProcessor.FIND_LF); - if (lfIndex < 0) { - return null; - } - ByteBuf data = in.readSlice(lfIndex - in.readerIndex() - 1); // `-1` is for CR - readEndOfLine(in); // validate CR LF - return data; - } - - private long parseRedisNumber(ByteBuf byteBuf) { - final int readableBytes = byteBuf.readableBytes(); - final boolean negative = readableBytes > 0 && byteBuf.getByte(byteBuf.readerIndex()) == '-'; - final int extraOneByteForNegative = negative ? 1 : 0; - if (readableBytes <= extraOneByteForNegative) { - throw new RedisCodecException("no number to parse: " + byteBuf.toString(CharsetUtil.US_ASCII)); - } - if (readableBytes > RedisConstants.POSITIVE_LONG_MAX_LENGTH + extraOneByteForNegative) { - throw new RedisCodecException("too many characters to be a valid RESP Integer: " + - byteBuf.toString(CharsetUtil.US_ASCII)); - } - if (negative) { - return -parsePositiveNumber(byteBuf.skipBytes(extraOneByteForNegative)); - } - return parsePositiveNumber(byteBuf); - } - - private long parsePositiveNumber(ByteBuf byteBuf) { - toPositiveLongProcessor.reset(); - byteBuf.forEachByte(toPositiveLongProcessor); - return toPositiveLongProcessor.content(); - } - - private static final class ToPositiveLongProcessor implements ByteProcessor { - private long result; - - @Override - public boolean process(byte value) { - if (value < '0' || value > '9') { - throw new RedisCodecException("bad byte in number: " + value); - } - result = result * 10 + (value - '0'); - return true; - } - - public long content() { - return result; - } - - public void reset() { - result = 0; - } - } -} diff --git a/codec-redis/src/main/java/io/netty/handler/codec/redis/RedisEncoder.java b/codec-redis/src/main/java/io/netty/handler/codec/redis/RedisEncoder.java deleted file mode 100644 index 4497c028fe..0000000000 --- a/codec-redis/src/main/java/io/netty/handler/codec/redis/RedisEncoder.java +++ /dev/null @@ -1,208 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.redis; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.ByteBufUtil; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.CodecException; -import io.netty.handler.codec.MessageToMessageEncoder; -import io.netty.util.internal.UnstableApi; - -import java.util.List; - -/** - * Encodes {@link RedisMessage} into bytes following - * RESP (REdis Serialization Protocol). - */ -@UnstableApi -public class RedisEncoder extends MessageToMessageEncoder { - - private final RedisMessagePool messagePool; - - /** - * Creates a new instance with default {@code messagePool}. - */ - public RedisEncoder() { - this(FixedRedisMessagePool.INSTANCE); - } - - /** - * Creates a new instance. - * @param messagePool the predefined message pool. - */ - public RedisEncoder(RedisMessagePool messagePool) { - this.messagePool = requireNonNull(messagePool, "messagePool"); - } - - @Override - protected void encode(ChannelHandlerContext ctx, RedisMessage msg, List out) throws Exception { - try { - writeRedisMessage(ctx.alloc(), msg, out); - } catch (CodecException e) { - throw e; - } catch (Exception e) { - throw new CodecException(e); - } - } - - private void writeRedisMessage(ByteBufAllocator allocator, RedisMessage msg, List out) { - if (msg instanceof InlineCommandRedisMessage) { - writeInlineCommandMessage(allocator, (InlineCommandRedisMessage) msg, out); - } else if (msg instanceof SimpleStringRedisMessage) { - writeSimpleStringMessage(allocator, (SimpleStringRedisMessage) msg, out); - } else if (msg instanceof ErrorRedisMessage) { - writeErrorMessage(allocator, (ErrorRedisMessage) msg, out); - } else if (msg instanceof IntegerRedisMessage) { - writeIntegerMessage(allocator, (IntegerRedisMessage) msg, out); - } else if (msg instanceof FullBulkStringRedisMessage) { - writeFullBulkStringMessage(allocator, (FullBulkStringRedisMessage) msg, out); - } else if (msg instanceof BulkStringRedisContent) { - writeBulkStringContent(allocator, (BulkStringRedisContent) msg, out); - } else if (msg instanceof BulkStringHeaderRedisMessage) { - writeBulkStringHeader(allocator, (BulkStringHeaderRedisMessage) msg, out); - } else if (msg instanceof ArrayHeaderRedisMessage) { - writeArrayHeader(allocator, (ArrayHeaderRedisMessage) msg, out); - } else if (msg instanceof ArrayRedisMessage) { - writeArrayMessage(allocator, (ArrayRedisMessage) msg, out); - } else { - throw new CodecException("unknown message type: " + msg); - } - } - - private static void writeInlineCommandMessage(ByteBufAllocator allocator, InlineCommandRedisMessage msg, - List out) { - writeString(allocator, RedisMessageType.INLINE_COMMAND, msg.content(), out); - } - - private static void writeSimpleStringMessage(ByteBufAllocator allocator, SimpleStringRedisMessage msg, - List out) { - writeString(allocator, RedisMessageType.SIMPLE_STRING, msg.content(), out); - } - - private static void writeErrorMessage(ByteBufAllocator allocator, ErrorRedisMessage msg, List out) { - writeString(allocator, RedisMessageType.ERROR, msg.content(), out); - } - - private static void writeString(ByteBufAllocator allocator, RedisMessageType type, String content, - List out) { - ByteBuf buf = allocator.ioBuffer(type.length() + ByteBufUtil.utf8MaxBytes(content) + - RedisConstants.EOL_LENGTH); - type.writeTo(buf); - ByteBufUtil.writeUtf8(buf, content); - buf.writeShort(RedisConstants.EOL_SHORT); - out.add(buf); - } - - private void writeIntegerMessage(ByteBufAllocator allocator, IntegerRedisMessage msg, List out) { - ByteBuf buf = allocator.ioBuffer(RedisConstants.TYPE_LENGTH + RedisConstants.LONG_MAX_LENGTH + - RedisConstants.EOL_LENGTH); - RedisMessageType.INTEGER.writeTo(buf); - buf.writeBytes(numberToBytes(msg.value())); - buf.writeShort(RedisConstants.EOL_SHORT); - out.add(buf); - } - - private void writeBulkStringHeader(ByteBufAllocator allocator, BulkStringHeaderRedisMessage msg, List out) { - final ByteBuf buf = allocator.ioBuffer(RedisConstants.TYPE_LENGTH + - (msg.isNull() ? RedisConstants.NULL_LENGTH : - RedisConstants.LONG_MAX_LENGTH + RedisConstants.EOL_LENGTH)); - RedisMessageType.BULK_STRING.writeTo(buf); - if (msg.isNull()) { - buf.writeShort(RedisConstants.NULL_SHORT); - } else { - buf.writeBytes(numberToBytes(msg.bulkStringLength())); - buf.writeShort(RedisConstants.EOL_SHORT); - } - out.add(buf); - } - - private static void writeBulkStringContent(ByteBufAllocator allocator, BulkStringRedisContent msg, - List out) { - out.add(msg.content().retain()); - if (msg instanceof LastBulkStringRedisContent) { - out.add(allocator.ioBuffer(RedisConstants.EOL_LENGTH).writeShort(RedisConstants.EOL_SHORT)); - } - } - - private void writeFullBulkStringMessage(ByteBufAllocator allocator, FullBulkStringRedisMessage msg, - List out) { - if (msg.isNull()) { - ByteBuf buf = allocator.ioBuffer(RedisConstants.TYPE_LENGTH + RedisConstants.NULL_LENGTH + - RedisConstants.EOL_LENGTH); - RedisMessageType.BULK_STRING.writeTo(buf); - buf.writeShort(RedisConstants.NULL_SHORT); - buf.writeShort(RedisConstants.EOL_SHORT); - out.add(buf); - } else { - ByteBuf headerBuf = allocator.ioBuffer(RedisConstants.TYPE_LENGTH + RedisConstants.LONG_MAX_LENGTH + - RedisConstants.EOL_LENGTH); - RedisMessageType.BULK_STRING.writeTo(headerBuf); - headerBuf.writeBytes(numberToBytes(msg.content().readableBytes())); - headerBuf.writeShort(RedisConstants.EOL_SHORT); - out.add(headerBuf); - out.add(msg.content().retain()); - out.add(allocator.ioBuffer(RedisConstants.EOL_LENGTH).writeShort(RedisConstants.EOL_SHORT)); - } - } - - /** - * Write array header only without body. Use this if you want to write arrays as streaming. - */ - private void writeArrayHeader(ByteBufAllocator allocator, ArrayHeaderRedisMessage msg, List out) { - writeArrayHeader(allocator, msg.isNull(), msg.length(), out); - } - - /** - * Write full constructed array message. - */ - private void writeArrayMessage(ByteBufAllocator allocator, ArrayRedisMessage msg, List out) { - if (msg.isNull()) { - writeArrayHeader(allocator, msg.isNull(), RedisConstants.NULL_VALUE, out); - } else { - writeArrayHeader(allocator, msg.isNull(), msg.children().size(), out); - for (RedisMessage child : msg.children()) { - writeRedisMessage(allocator, child, out); - } - } - } - - private void writeArrayHeader(ByteBufAllocator allocator, boolean isNull, long length, List out) { - if (isNull) { - final ByteBuf buf = allocator.ioBuffer(RedisConstants.TYPE_LENGTH + RedisConstants.NULL_LENGTH + - RedisConstants.EOL_LENGTH); - RedisMessageType.ARRAY_HEADER.writeTo(buf); - buf.writeShort(RedisConstants.NULL_SHORT); - buf.writeShort(RedisConstants.EOL_SHORT); - out.add(buf); - } else { - final ByteBuf buf = allocator.ioBuffer(RedisConstants.TYPE_LENGTH + RedisConstants.LONG_MAX_LENGTH + - RedisConstants.EOL_LENGTH); - RedisMessageType.ARRAY_HEADER.writeTo(buf); - buf.writeBytes(numberToBytes(length)); - buf.writeShort(RedisConstants.EOL_SHORT); - out.add(buf); - } - } - - private byte[] numberToBytes(long value) { - byte[] bytes = messagePool.getByteBufOfInteger(value); - return bytes != null ? bytes : RedisCodecUtil.longToAsciiBytes(value); - } -} diff --git a/codec-redis/src/main/java/io/netty/handler/codec/redis/RedisMessage.java b/codec-redis/src/main/java/io/netty/handler/codec/redis/RedisMessage.java deleted file mode 100644 index 02fb7dbe59..0000000000 --- a/codec-redis/src/main/java/io/netty/handler/codec/redis/RedisMessage.java +++ /dev/null @@ -1,25 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.redis; - -import io.netty.util.internal.UnstableApi; - -/** - * RedisMessage is base interface for codec-redis. - */ -@UnstableApi -public interface RedisMessage { -} diff --git a/codec-redis/src/main/java/io/netty/handler/codec/redis/RedisMessagePool.java b/codec-redis/src/main/java/io/netty/handler/codec/redis/RedisMessagePool.java deleted file mode 100644 index eb585b8bb3..0000000000 --- a/codec-redis/src/main/java/io/netty/handler/codec/redis/RedisMessagePool.java +++ /dev/null @@ -1,62 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.redis; - -import io.netty.buffer.ByteBuf; -import io.netty.util.internal.UnstableApi; - -/** - * A strategy interface for caching {@link RedisMessage}s. - */ -@UnstableApi - -public interface RedisMessagePool { - - /** - * Returns {@link SimpleStringRedisMessage} for given {@code content}. Returns {@code null} it does not exist. - */ - SimpleStringRedisMessage getSimpleString(String content); - - /** - * Returns {@link SimpleStringRedisMessage} for given {@code content}. Returns {@code null} it does not exist. - */ - SimpleStringRedisMessage getSimpleString(ByteBuf content); - - /** - * Returns {@link ErrorRedisMessage} for given {@code content}. Returns {@code null} it does not exist. - */ - ErrorRedisMessage getError(String content); - - /** - * Returns {@link ErrorRedisMessage} for given {@code content}. Returns {@code null} it does not exist. - */ - ErrorRedisMessage getError(ByteBuf content); - - /** - * Returns {@link IntegerRedisMessage} for given {@code value}. Returns {@code null} it does not exist. - */ - IntegerRedisMessage getInteger(long value); - - /** - * Returns {@link IntegerRedisMessage} for given {@code content}. Returns {@code null} it does not exist. - */ - IntegerRedisMessage getInteger(ByteBuf content); - - /** - * Returns {@code byte[]} for given {@code msg}. Returns {@code null} it does not exist. - */ - byte[] getByteBufOfInteger(long value); -} diff --git a/codec-redis/src/main/java/io/netty/handler/codec/redis/RedisMessageType.java b/codec-redis/src/main/java/io/netty/handler/codec/redis/RedisMessageType.java deleted file mode 100644 index f78507df91..0000000000 --- a/codec-redis/src/main/java/io/netty/handler/codec/redis/RedisMessageType.java +++ /dev/null @@ -1,99 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.redis; - -import io.netty.buffer.ByteBuf; -import io.netty.util.internal.UnstableApi; - -/** - * Type of RESP (REdis Serialization Protocol). - */ -@UnstableApi -public enum RedisMessageType { - - INLINE_COMMAND(null, true), - SIMPLE_STRING((byte) '+', true), - ERROR((byte) '-', true), - INTEGER((byte) ':', true), - BULK_STRING((byte) '$', false), - ARRAY_HEADER((byte) '*', false); - - private final Byte value; - private final boolean inline; - - RedisMessageType(Byte value, boolean inline) { - this.value = value; - this.inline = inline; - } - - /** - * Returns length of this type. - */ - public int length() { - return value != null ? RedisConstants.TYPE_LENGTH : 0; - } - - /** - * Returns {@code true} if this type is inline type, or returns {@code false}. If this is {@code true}, - * this type doesn't have length field. - */ - public boolean isInline() { - return inline; - } - - /** - * Determine {@link RedisMessageType} based on the type prefix {@code byte} read from given the buffer. - */ - public static RedisMessageType readFrom(ByteBuf in, boolean decodeInlineCommands) { - final int initialIndex = in.readerIndex(); - final RedisMessageType type = valueOf(in.readByte()); - if (type == INLINE_COMMAND) { - if (!decodeInlineCommands) { - throw new RedisCodecException("Decoding of inline commands is disabled"); - } - // reset index to make content readable again - in.readerIndex(initialIndex); - } - return type; - } - - /** - * Write the message type's prefix to the given buffer. - */ - public void writeTo(ByteBuf out) { - if (value == null) { - return; - } - out.writeByte(value.byteValue()); - } - - private static RedisMessageType valueOf(byte value) { - switch (value) { - case '+': - return SIMPLE_STRING; - case '-': - return ERROR; - case ':': - return INTEGER; - case '$': - return BULK_STRING; - case '*': - return ARRAY_HEADER; - default: - return INLINE_COMMAND; - } - } -} diff --git a/codec-redis/src/main/java/io/netty/handler/codec/redis/SimpleStringRedisMessage.java b/codec-redis/src/main/java/io/netty/handler/codec/redis/SimpleStringRedisMessage.java deleted file mode 100644 index dcd13a0de9..0000000000 --- a/codec-redis/src/main/java/io/netty/handler/codec/redis/SimpleStringRedisMessage.java +++ /dev/null @@ -1,35 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.codec.redis; - -import io.netty.util.internal.UnstableApi; - -/** - * Simple Strings of RESP. - */ -@UnstableApi -public final class SimpleStringRedisMessage extends AbstractStringRedisMessage { - - /** - * Creates a {@link SimpleStringRedisMessage} for the given {@code content}. - * - * @param content the message content, must not be {@code null}. - */ - public SimpleStringRedisMessage(String content) { - super(content); - } - -} diff --git a/codec-redis/src/main/java/io/netty/handler/codec/redis/package-info.java b/codec-redis/src/main/java/io/netty/handler/codec/redis/package-info.java deleted file mode 100644 index b41e8a1f49..0000000000 --- a/codec-redis/src/main/java/io/netty/handler/codec/redis/package-info.java +++ /dev/null @@ -1,22 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -/** - * Encoder, decoder for Redis. - */ -@UnstableApi -package io.netty.handler.codec.redis; - -import io.netty.util.internal.UnstableApi; diff --git a/codec-redis/src/test/java/io/netty/handler/codec/redis/FixedRedisMessagePoolTest.java b/codec-redis/src/test/java/io/netty/handler/codec/redis/FixedRedisMessagePoolTest.java deleted file mode 100644 index 2ee4d25e2e..0000000000 --- a/codec-redis/src/test/java/io/netty/handler/codec/redis/FixedRedisMessagePoolTest.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.redis; - -import org.junit.jupiter.api.Test; - -import static io.netty.handler.codec.redis.RedisCodecTestUtil.byteBufOf; -import static org.junit.jupiter.api.Assertions.*; - -/** - * Verifies the correct functionality of the {@link FixedRedisMessagePool}. - */ -public class FixedRedisMessagePoolTest { - - @Test - public void shouldGetSameMessageObject() { - FixedRedisMessagePool pool = FixedRedisMessagePool.INSTANCE; - - SimpleStringRedisMessage fromStr = pool.getSimpleString("OK"); - SimpleStringRedisMessage fromEnum = pool.getSimpleString(FixedRedisMessagePool.RedisReplyKey.OK); - SimpleStringRedisMessage fromByteBuf = pool.getSimpleString(byteBufOf("OK")); - - assertEquals(fromStr.content(), "OK"); - assertEquals(fromStr, fromEnum); - assertEquals(fromStr, fromByteBuf); - - ErrorRedisMessage errorFromStr = pool.getError("NOAUTH Authentication required."); - ErrorRedisMessage errorFromEnum = pool.getError(FixedRedisMessagePool.RedisErrorKey.NOT_AUTH); - ErrorRedisMessage errorFromByteBuf = pool.getError(byteBufOf("NOAUTH Authentication required.")); - - assertEquals(errorFromStr.content(), "NOAUTH Authentication required."); - assertEquals(errorFromStr, errorFromEnum); - assertEquals(errorFromStr, errorFromByteBuf); - } - - @Test - public void shouldReturnNullByNotExistKey() { - FixedRedisMessagePool pool = FixedRedisMessagePool.INSTANCE; - - SimpleStringRedisMessage message1 = pool.getSimpleString("Not exist"); - SimpleStringRedisMessage message2 = pool.getSimpleString(byteBufOf("Not exist")); - - assertNull(message1); - assertNull(message2); - - ErrorRedisMessage error1 = pool.getError("Not exist"); - ErrorRedisMessage error2 = pool.getError(byteBufOf("Not exist")); - - assertNull(error1); - assertNull(error2); - } - - @Test - public void shouldReturnDifferentMessage() { - FixedRedisMessagePool pool = FixedRedisMessagePool.INSTANCE; - - SimpleStringRedisMessage okMessage = pool.getSimpleString(FixedRedisMessagePool.RedisReplyKey.OK); - SimpleStringRedisMessage pongMessage = pool.getSimpleString(FixedRedisMessagePool.RedisReplyKey.PONG); - - assertNotEquals(okMessage, pongMessage); - } -} diff --git a/codec-redis/src/test/java/io/netty/handler/codec/redis/RedisCodecTestUtil.java b/codec-redis/src/test/java/io/netty/handler/codec/redis/RedisCodecTestUtil.java deleted file mode 100644 index 7a9f12326a..0000000000 --- a/codec-redis/src/test/java/io/netty/handler/codec/redis/RedisCodecTestUtil.java +++ /dev/null @@ -1,53 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.redis; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.CharsetUtil; - -final class RedisCodecTestUtil { - - private RedisCodecTestUtil() { - } - - static byte[] bytesOf(long value) { - return bytesOf(Long.toString(value)); - } - - static byte[] bytesOf(String s) { - return s.getBytes(CharsetUtil.UTF_8); - } - - static byte[] bytesOf(ByteBuf buf) { - byte[] data = new byte[buf.readableBytes()]; - buf.readBytes(data); - return data; - } - - static String stringOf(ByteBuf buf) { - return new String(bytesOf(buf)); - } - - static ByteBuf byteBufOf(String s) { - return byteBufOf(bytesOf(s)); - } - - static ByteBuf byteBufOf(byte[] data) { - return Unpooled.wrappedBuffer(data); - } -} diff --git a/codec-redis/src/test/java/io/netty/handler/codec/redis/RedisDecoderTest.java b/codec-redis/src/test/java/io/netty/handler/codec/redis/RedisDecoderTest.java deleted file mode 100644 index 90b04cf7df..0000000000 --- a/codec-redis/src/test/java/io/netty/handler/codec/redis/RedisDecoderTest.java +++ /dev/null @@ -1,323 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.redis; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.DecoderException; -import io.netty.util.IllegalReferenceCountException; -import io.netty.util.ReferenceCountUtil; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.List; - -import static io.netty.handler.codec.redis.RedisCodecTestUtil.*; -import static org.hamcrest.CoreMatchers.*; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** - * Verifies the correct functionality of the {@link RedisDecoder} and {@link RedisArrayAggregator}. - */ -public class RedisDecoderTest { - - private EmbeddedChannel channel; - - @BeforeEach - public void setup() throws Exception { - channel = newChannel(false); - } - - private static EmbeddedChannel newChannel(boolean decodeInlineCommands) { - return new EmbeddedChannel( - new RedisDecoder(decodeInlineCommands), - new RedisBulkStringAggregator(), - new RedisArrayAggregator()); - } - - @AfterEach - public void teardown() throws Exception { - assertFalse(channel.finish()); - } - - @Test - public void splitEOLDoesNotInfiniteLoop() throws Exception { - assertFalse(channel.writeInbound(byteBufOf("$6\r\nfoobar\r"))); - assertTrue(channel.writeInbound(byteBufOf("\n"))); - - RedisMessage msg = channel.readInbound(); - assertTrue(msg instanceof FullBulkStringRedisMessage); - ReferenceCountUtil.release(msg); - } - - @Test - public void shouldNotDecodeInlineCommandByDefault() { - assertThrows(DecoderException.class, () -> { - assertFalse(channel.writeInbound(byteBufOf("P"))); - assertFalse(channel.writeInbound(byteBufOf("I"))); - assertFalse(channel.writeInbound(byteBufOf("N"))); - assertFalse(channel.writeInbound(byteBufOf("G"))); - assertTrue(channel.writeInbound(byteBufOf("\r\n"))); - - channel.readInbound(); - }); - } - - @Test - public void shouldDecodeInlineCommand() { - channel = newChannel(true); - - assertFalse(channel.writeInbound(byteBufOf("P"))); - assertFalse(channel.writeInbound(byteBufOf("I"))); - assertFalse(channel.writeInbound(byteBufOf("N"))); - assertFalse(channel.writeInbound(byteBufOf("G"))); - assertTrue(channel.writeInbound(byteBufOf("\r\n"))); - - InlineCommandRedisMessage msg = channel.readInbound(); - - assertThat(msg.content(), is("PING")); - - ReferenceCountUtil.release(msg); - } - - @Test - public void shouldDecodeSimpleString() { - assertFalse(channel.writeInbound(byteBufOf("+"))); - assertFalse(channel.writeInbound(byteBufOf("O"))); - assertFalse(channel.writeInbound(byteBufOf("K"))); - assertTrue(channel.writeInbound(byteBufOf("\r\n"))); - - SimpleStringRedisMessage msg = channel.readInbound(); - - assertThat(msg.content(), is("OK")); - - ReferenceCountUtil.release(msg); - } - - @Test - public void shouldDecodeTwoSimpleStrings() { - assertFalse(channel.writeInbound(byteBufOf("+"))); - assertFalse(channel.writeInbound(byteBufOf("O"))); - assertFalse(channel.writeInbound(byteBufOf("K"))); - assertTrue(channel.writeInbound(byteBufOf("\r\n+SEC"))); - assertTrue(channel.writeInbound(byteBufOf("OND\r\n"))); - - SimpleStringRedisMessage msg1 = channel.readInbound(); - assertThat(msg1.content(), is("OK")); - ReferenceCountUtil.release(msg1); - - SimpleStringRedisMessage msg2 = channel.readInbound(); - assertThat(msg2.content(), is("SECOND")); - ReferenceCountUtil.release(msg2); - } - - @Test - public void shouldDecodeError() { - String content = "ERROR sample message"; - assertFalse(channel.writeInbound(byteBufOf("-"))); - assertFalse(channel.writeInbound(byteBufOf(content))); - assertFalse(channel.writeInbound(byteBufOf("\r"))); - assertTrue(channel.writeInbound(byteBufOf("\n"))); - - ErrorRedisMessage msg = channel.readInbound(); - - assertThat(msg.content(), is(content)); - - ReferenceCountUtil.release(msg); - } - - @Test - public void shouldDecodeInteger() { - long value = 1234L; - byte[] content = bytesOf(value); - assertFalse(channel.writeInbound(byteBufOf(":"))); - assertFalse(channel.writeInbound(byteBufOf(content))); - assertTrue(channel.writeInbound(byteBufOf("\r\n"))); - - IntegerRedisMessage msg = channel.readInbound(); - - assertThat(msg.value(), is(value)); - - ReferenceCountUtil.release(msg); - } - - @Test - public void shouldDecodeBulkString() { - String buf1 = "bulk\nst"; - String buf2 = "ring\ntest\n1234"; - byte[] content = bytesOf(buf1 + buf2); - assertFalse(channel.writeInbound(byteBufOf("$"))); - assertFalse(channel.writeInbound(byteBufOf(Integer.toString(content.length)))); - assertFalse(channel.writeInbound(byteBufOf("\r\n"))); - assertFalse(channel.writeInbound(byteBufOf(buf1))); - assertFalse(channel.writeInbound(byteBufOf(buf2))); - assertTrue(channel.writeInbound(byteBufOf("\r\n"))); - - FullBulkStringRedisMessage msg = channel.readInbound(); - - assertThat(bytesOf(msg.content()), is(content)); - - ReferenceCountUtil.release(msg); - } - - @Test - public void shouldDecodeEmptyBulkString() { - byte[] content = bytesOf(""); - assertFalse(channel.writeInbound(byteBufOf("$"))); - assertFalse(channel.writeInbound(byteBufOf(Integer.toString(content.length)))); - assertFalse(channel.writeInbound(byteBufOf("\r\n"))); - assertFalse(channel.writeInbound(byteBufOf(content))); - assertTrue(channel.writeInbound(byteBufOf("\r\n"))); - - FullBulkStringRedisMessage msg = channel.readInbound(); - - assertThat(bytesOf(msg.content()), is(content)); - - ReferenceCountUtil.release(msg); - } - - @Test - public void shouldDecodeNullBulkString() { - assertFalse(channel.writeInbound(byteBufOf("$"))); - assertFalse(channel.writeInbound(byteBufOf(Integer.toString(-1)))); - assertTrue(channel.writeInbound(byteBufOf("\r\n"))); - - assertTrue(channel.writeInbound(byteBufOf("$"))); - assertTrue(channel.writeInbound(byteBufOf(Integer.toString(-1)))); - assertTrue(channel.writeInbound(byteBufOf("\r\n"))); - - FullBulkStringRedisMessage msg1 = channel.readInbound(); - assertThat(msg1.isNull(), is(true)); - ReferenceCountUtil.release(msg1); - - FullBulkStringRedisMessage msg2 = channel.readInbound(); - assertThat(msg2.isNull(), is(true)); - ReferenceCountUtil.release(msg2); - - FullBulkStringRedisMessage msg3 = channel.readInbound(); - assertThat(msg3, is(nullValue())); - } - - @Test - public void shouldDecodeSimpleArray() throws Exception { - assertFalse(channel.writeInbound(byteBufOf("*3\r\n"))); - assertFalse(channel.writeInbound(byteBufOf(":1234\r\n"))); - assertFalse(channel.writeInbound(byteBufOf("+sim"))); - assertFalse(channel.writeInbound(byteBufOf("ple\r\n-err"))); - assertTrue(channel.writeInbound(byteBufOf("or\r\n"))); - - ArrayRedisMessage msg = channel.readInbound(); - List children = msg.children(); - - assertThat(msg.children().size(), is(equalTo(3))); - - assertThat(children.get(0), instanceOf(IntegerRedisMessage.class)); - assertThat(((IntegerRedisMessage) children.get(0)).value(), is(1234L)); - assertThat(children.get(1), instanceOf(SimpleStringRedisMessage.class)); - assertThat(((SimpleStringRedisMessage) children.get(1)).content(), is("simple")); - assertThat(children.get(2), instanceOf(ErrorRedisMessage.class)); - assertThat(((ErrorRedisMessage) children.get(2)).content(), is("error")); - - ReferenceCountUtil.release(msg); - } - - @Test - public void shouldDecodeNestedArray() throws Exception { - ByteBuf buf = Unpooled.buffer(); - buf.writeBytes(byteBufOf("*2\r\n")); - buf.writeBytes(byteBufOf("*3\r\n:1\r\n:2\r\n:3\r\n")); - buf.writeBytes(byteBufOf("*2\r\n+Foo\r\n-Bar\r\n")); - assertTrue(channel.writeInbound(buf)); - - ArrayRedisMessage msg = channel.readInbound(); - List children = msg.children(); - - assertThat(msg.children().size(), is(2)); - - ArrayRedisMessage intArray = (ArrayRedisMessage) children.get(0); - ArrayRedisMessage strArray = (ArrayRedisMessage) children.get(1); - - assertThat(intArray.children().size(), is(3)); - assertThat(((IntegerRedisMessage) intArray.children().get(0)).value(), is(1L)); - assertThat(((IntegerRedisMessage) intArray.children().get(1)).value(), is(2L)); - assertThat(((IntegerRedisMessage) intArray.children().get(2)).value(), is(3L)); - - assertThat(strArray.children().size(), is(2)); - assertThat(((SimpleStringRedisMessage) strArray.children().get(0)).content(), is("Foo")); - assertThat(((ErrorRedisMessage) strArray.children().get(1)).content(), is("Bar")); - - ReferenceCountUtil.release(msg); - } - - @Test - public void shouldErrorOnDoubleReleaseArrayReferenceCounted() { - ByteBuf buf = Unpooled.buffer(); - buf.writeBytes(byteBufOf("*2\r\n")); - buf.writeBytes(byteBufOf("*3\r\n:1\r\n:2\r\n:3\r\n")); - buf.writeBytes(byteBufOf("*2\r\n+Foo\r\n-Bar\r\n")); - assertTrue(channel.writeInbound(buf)); - - ArrayRedisMessage msg = channel.readInbound(); - - ReferenceCountUtil.release(msg); - assertThrows(IllegalReferenceCountException.class, () -> ReferenceCountUtil.release(msg)); - } - - @Test - public void shouldErrorOnReleaseArrayChildReferenceCounted() { - ByteBuf buf = Unpooled.buffer(); - buf.writeBytes(byteBufOf("*2\r\n")); - buf.writeBytes(byteBufOf("*3\r\n:1\r\n:2\r\n:3\r\n")); - buf.writeBytes(byteBufOf("$3\r\nFoo\r\n")); - assertTrue(channel.writeInbound(buf)); - - ArrayRedisMessage msg = channel.readInbound(); - - List children = msg.children(); - ReferenceCountUtil.release(msg); - assertThrows(IllegalReferenceCountException.class, () -> ReferenceCountUtil.release(children.get(1))); - } - - @Test - public void shouldErrorOnReleasecontentOfArrayChildReferenceCounted() throws Exception { - ByteBuf buf = Unpooled.buffer(); - buf.writeBytes(byteBufOf("*2\r\n")); - buf.writeBytes(byteBufOf("$3\r\nFoo\r\n$3\r\nBar\r\n")); - assertTrue(channel.writeInbound(buf)); - - ArrayRedisMessage msg = channel.readInbound(); - - List children = msg.children(); - ByteBuf childBuf = ((FullBulkStringRedisMessage) children.get(0)).content(); - ReferenceCountUtil.release(msg); - assertThrows(IllegalReferenceCountException.class, () -> ReferenceCountUtil.release(childBuf)); - } - - @Test - public void testPredefinedMessagesNotEqual() { - // both EMPTY_INSTANCE and NULL_INSTANCE have EMPTY_BUFFER as their 'data', - // however we need to check that they are not equal between themselves. - assertNotEquals(FullBulkStringRedisMessage.EMPTY_INSTANCE, FullBulkStringRedisMessage.NULL_INSTANCE); - assertNotEquals(FullBulkStringRedisMessage.NULL_INSTANCE, FullBulkStringRedisMessage.EMPTY_INSTANCE); - } -} diff --git a/codec-redis/src/test/java/io/netty/handler/codec/redis/RedisEncoderTest.java b/codec-redis/src/test/java/io/netty/handler/codec/redis/RedisEncoderTest.java deleted file mode 100644 index efa3a2af71..0000000000 --- a/codec-redis/src/test/java/io/netty/handler/codec/redis/RedisEncoderTest.java +++ /dev/null @@ -1,195 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.redis; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.List; - -import static io.netty.handler.codec.redis.RedisCodecTestUtil.*; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.hamcrest.MatcherAssert.assertThat; - -/** - * Verifies the correct functionality of the {@link RedisEncoder}. - */ -public class RedisEncoderTest { - - private EmbeddedChannel channel; - - @BeforeEach - public void setup() throws Exception { - channel = new EmbeddedChannel(new RedisEncoder()); - } - - @AfterEach - public void teardown() throws Exception { - assertFalse(channel.finish()); - } - - @Test - public void shouldEncodeInlineCommand() { - RedisMessage msg = new InlineCommandRedisMessage("ping"); - - boolean result = channel.writeOutbound(msg); - assertThat(result, is(true)); - - ByteBuf written = readAll(channel); - assertThat(bytesOf(written), is(bytesOf("ping\r\n"))); - written.release(); - } - - @Test - public void shouldEncodeSimpleString() { - RedisMessage msg = new SimpleStringRedisMessage("simple"); - - boolean result = channel.writeOutbound(msg); - assertThat(result, is(true)); - - ByteBuf written = readAll(channel); - assertThat(bytesOf(written), is(bytesOf("+simple\r\n"))); - written.release(); - } - - @Test - public void shouldEncodeError() { - RedisMessage msg = new ErrorRedisMessage("error1"); - - boolean result = channel.writeOutbound(msg); - assertThat(result, is(true)); - - ByteBuf written = readAll(channel); - assertThat(bytesOf(written), is(equalTo(bytesOf("-error1\r\n")))); - written.release(); - } - - @Test - public void shouldEncodeInteger() { - RedisMessage msg = new IntegerRedisMessage(1234L); - - boolean result = channel.writeOutbound(msg); - assertThat(result, is(true)); - - ByteBuf written = readAll(channel); - assertThat(bytesOf(written), is(equalTo(bytesOf(":1234\r\n")))); - written.release(); - } - - @Test - public void shouldEncodeBulkStringContent() { - RedisMessage header = new BulkStringHeaderRedisMessage(16); - RedisMessage body1 = new DefaultBulkStringRedisContent(byteBufOf("bulk\nstr").retain()); - RedisMessage body2 = new DefaultLastBulkStringRedisContent(byteBufOf("ing\ntest").retain()); - - assertThat(channel.writeOutbound(header), is(true)); - assertThat(channel.writeOutbound(body1), is(true)); - assertThat(channel.writeOutbound(body2), is(true)); - - ByteBuf written = readAll(channel); - assertThat(bytesOf(written), is(equalTo(bytesOf("$16\r\nbulk\nstring\ntest\r\n")))); - written.release(); - } - - @Test - public void shouldEncodeFullBulkString() { - ByteBuf bulkString = byteBufOf("bulk\nstring\ntest").retain(); - int length = bulkString.readableBytes(); - RedisMessage msg = new FullBulkStringRedisMessage(bulkString); - - boolean result = channel.writeOutbound(msg); - assertThat(result, is(true)); - - ByteBuf written = readAll(channel); - assertThat(bytesOf(written), is(equalTo(bytesOf("$" + length + "\r\nbulk\nstring\ntest\r\n")))); - written.release(); - } - - @Test - public void shouldEncodeSimpleArray() { - List children = new ArrayList<>(); - children.add(new FullBulkStringRedisMessage(byteBufOf("foo").retain())); - children.add(new FullBulkStringRedisMessage(byteBufOf("bar").retain())); - RedisMessage msg = new ArrayRedisMessage(children); - - boolean result = channel.writeOutbound(msg); - assertThat(result, is(true)); - - ByteBuf written = readAll(channel); - assertThat(bytesOf(written), is(equalTo(bytesOf("*2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n")))); - written.release(); - } - - @Test - public void shouldEncodeNullArray() { - RedisMessage msg = ArrayRedisMessage.NULL_INSTANCE; - - boolean result = channel.writeOutbound(msg); - assertThat(result, is(true)); - - ByteBuf written = readAll(channel); - assertThat(bytesOf(written), is(equalTo(bytesOf("*-1\r\n")))); - written.release(); - } - - @Test - public void shouldEncodeEmptyArray() { - RedisMessage msg = ArrayRedisMessage.EMPTY_INSTANCE; - - boolean result = channel.writeOutbound(msg); - assertThat(result, is(true)); - - ByteBuf written = readAll(channel); - assertThat(bytesOf(written), is(equalTo(bytesOf("*0\r\n")))); - written.release(); - } - - @Test - public void shouldEncodeNestedArray() { - List grandChildren = new ArrayList<>(); - grandChildren.add(new FullBulkStringRedisMessage(byteBufOf("bar"))); - grandChildren.add(new IntegerRedisMessage(-1234L)); - List children = new ArrayList<>(); - children.add(new SimpleStringRedisMessage("foo")); - children.add(new ArrayRedisMessage(grandChildren)); - RedisMessage msg = new ArrayRedisMessage(children); - - boolean result = channel.writeOutbound(msg); - assertThat(result, is(true)); - - ByteBuf written = readAll(channel); - assertThat(bytesOf(written), is(equalTo(bytesOf("*2\r\n+foo\r\n*2\r\n$3\r\nbar\r\n:-1234\r\n")))); - written.release(); - } - - private static ByteBuf readAll(EmbeddedChannel channel) { - ByteBuf buf = Unpooled.buffer(); - ByteBuf read; - while ((read = channel.readOutbound()) != null) { - buf.writeBytes(read); - read.release(); - } - return buf; - } -} diff --git a/codec-smtp/pom.xml b/codec-smtp/pom.xml deleted file mode 100644 index 7221787d0d..0000000000 --- a/codec-smtp/pom.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - 4.0.0 - - io.netty - netty-parent - 5.0.0.Final-SNAPSHOT - - - netty-codec-smtp - jar - - Netty/Codec/SMTP - - - io.netty.codec.smtp - - - - - ${project.groupId} - netty-common - ${project.version} - - - ${project.groupId} - netty-buffer - ${project.version} - - - ${project.groupId} - netty-transport - ${project.version} - - - ${project.groupId} - netty-codec - ${project.version} - - - - diff --git a/codec-smtp/src/main/java/io/netty/handler/codec/smtp/DefaultLastSmtpContent.java b/codec-smtp/src/main/java/io/netty/handler/codec/smtp/DefaultLastSmtpContent.java deleted file mode 100644 index c01abeadd9..0000000000 --- a/codec-smtp/src/main/java/io/netty/handler/codec/smtp/DefaultLastSmtpContent.java +++ /dev/null @@ -1,77 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.smtp; - -import io.netty.buffer.ByteBuf; -import io.netty.util.internal.UnstableApi; - -/** - * Default implementation of {@link LastSmtpContent} that does no validation of the raw data passed in. - */ -@UnstableApi -public final class DefaultLastSmtpContent extends DefaultSmtpContent implements LastSmtpContent { - - /** - * Creates a new instance using the given data. - */ - public DefaultLastSmtpContent(ByteBuf data) { - super(data); - } - - @Override - public LastSmtpContent copy() { - return (LastSmtpContent) super.copy(); - } - - @Override - public LastSmtpContent duplicate() { - return (LastSmtpContent) super.duplicate(); - } - - @Override - public LastSmtpContent retainedDuplicate() { - return (LastSmtpContent) super.retainedDuplicate(); - } - - @Override - public LastSmtpContent replace(ByteBuf content) { - return new DefaultLastSmtpContent(content); - } - - @Override - public DefaultLastSmtpContent retain() { - super.retain(); - return this; - } - - @Override - public DefaultLastSmtpContent retain(int increment) { - super.retain(increment); - return this; - } - - @Override - public DefaultLastSmtpContent touch() { - super.touch(); - return this; - } - - @Override - public DefaultLastSmtpContent touch(Object hint) { - super.touch(hint); - return this; - } -} diff --git a/codec-smtp/src/main/java/io/netty/handler/codec/smtp/DefaultSmtpContent.java b/codec-smtp/src/main/java/io/netty/handler/codec/smtp/DefaultSmtpContent.java deleted file mode 100644 index 4a67cb53b7..0000000000 --- a/codec-smtp/src/main/java/io/netty/handler/codec/smtp/DefaultSmtpContent.java +++ /dev/null @@ -1,78 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.smtp; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.DefaultByteBufHolder; -import io.netty.util.internal.UnstableApi; - -/** - * Default implementation of {@link SmtpContent} that does no validation of the raw data passed in. - */ -@UnstableApi -public class DefaultSmtpContent extends DefaultByteBufHolder implements SmtpContent { - - /** - * Creates a new instance using the given data. - */ - public DefaultSmtpContent(ByteBuf data) { - super(data); - } - - @Override - public SmtpContent copy() { - return (SmtpContent) super.copy(); - } - - @Override - public SmtpContent duplicate() { - return (SmtpContent) super.duplicate(); - } - - @Override - public SmtpContent retainedDuplicate() { - return (SmtpContent) super.retainedDuplicate(); - } - - @Override - public SmtpContent replace(ByteBuf content) { - return new DefaultSmtpContent(content); - } - - @Override - public SmtpContent retain() { - super.retain(); - return this; - } - - @Override - public SmtpContent retain(int increment) { - super.retain(increment); - return this; - } - - @Override - public SmtpContent touch() { - super.touch(); - return this; - } - - @Override - public SmtpContent touch(Object hint) { - super.touch(hint); - return this; - } -} diff --git a/codec-smtp/src/main/java/io/netty/handler/codec/smtp/DefaultSmtpRequest.java b/codec-smtp/src/main/java/io/netty/handler/codec/smtp/DefaultSmtpRequest.java deleted file mode 100644 index 2bab0fdf84..0000000000 --- a/codec-smtp/src/main/java/io/netty/handler/codec/smtp/DefaultSmtpRequest.java +++ /dev/null @@ -1,101 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.smtp; - -import static java.util.Objects.requireNonNull; - -import io.netty.util.internal.UnstableApi; - -import java.util.Collections; -import java.util.List; - -/** - * Default {@link SmtpRequest} implementation. - */ -@UnstableApi -public final class DefaultSmtpRequest implements SmtpRequest { - - private final SmtpCommand command; - private final List parameters; - - /** - * Creates a new instance with the given command and no parameters. - */ - public DefaultSmtpRequest(SmtpCommand command) { - this.command = requireNonNull(command, "command"); - parameters = Collections.emptyList(); - } - - /** - * Creates a new instance with the given command and parameters. - */ - public DefaultSmtpRequest(SmtpCommand command, CharSequence... parameters) { - this.command = requireNonNull(command, "command"); - this.parameters = SmtpUtils.toUnmodifiableList(parameters); - } - - /** - * Creates a new instance with the given command and parameters. - */ - public DefaultSmtpRequest(CharSequence command, CharSequence... parameters) { - this(SmtpCommand.valueOf(command), parameters); - } - - DefaultSmtpRequest(SmtpCommand command, List parameters) { - this.command = requireNonNull(command, "command"); - this.parameters = parameters != null ? - Collections.unmodifiableList(parameters) : Collections.emptyList(); - } - - @Override - public SmtpCommand command() { - return command; - } - - @Override - public List parameters() { - return parameters; - } - - @Override - public int hashCode() { - return command.hashCode() * 31 + parameters.hashCode(); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof DefaultSmtpRequest)) { - return false; - } - - if (o == this) { - return true; - } - - DefaultSmtpRequest other = (DefaultSmtpRequest) o; - - return command().equals(other.command()) && - parameters().equals(other.parameters()); - } - - @Override - public String toString() { - return "DefaultSmtpRequest{" + - "command=" + command + - ", parameters=" + parameters + - '}'; - } -} diff --git a/codec-smtp/src/main/java/io/netty/handler/codec/smtp/DefaultSmtpResponse.java b/codec-smtp/src/main/java/io/netty/handler/codec/smtp/DefaultSmtpResponse.java deleted file mode 100644 index f0d9e76873..0000000000 --- a/codec-smtp/src/main/java/io/netty/handler/codec/smtp/DefaultSmtpResponse.java +++ /dev/null @@ -1,96 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.smtp; - -import io.netty.util.internal.UnstableApi; - -import java.util.Collections; -import java.util.List; - -/** - * Default {@link SmtpResponse} implementation. - */ -@UnstableApi -public final class DefaultSmtpResponse implements SmtpResponse { - - private final int code; - private final List details; - - /** - * Creates a new instance with the given smtp code and no details. - */ - public DefaultSmtpResponse(int code) { - this(code, (List) null); - } - - /** - * Creates a new instance with the given smtp code and details. - */ - public DefaultSmtpResponse(int code, CharSequence... details) { - this(code, SmtpUtils.toUnmodifiableList(details)); - } - - DefaultSmtpResponse(int code, List details) { - if (code < 100 || code > 599) { - throw new IllegalArgumentException("code must be 100 <= code <= 599"); - } - this.code = code; - if (details == null) { - this.details = Collections.emptyList(); - } else { - this.details = Collections.unmodifiableList(details); - } - } - - @Override - public int code() { - return code; - } - - @Override - public List details() { - return details; - } - - @Override - public int hashCode() { - return code * 31 + details.hashCode(); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof DefaultSmtpResponse)) { - return false; - } - - if (o == this) { - return true; - } - - DefaultSmtpResponse other = (DefaultSmtpResponse) o; - - return code() == other.code() && - details().equals(other.details()); - } - - @Override - public String toString() { - return "DefaultSmtpResponse{" + - "code=" + code + - ", details=" + details + - '}'; - } -} diff --git a/codec-smtp/src/main/java/io/netty/handler/codec/smtp/LastSmtpContent.java b/codec-smtp/src/main/java/io/netty/handler/codec/smtp/LastSmtpContent.java deleted file mode 100644 index e1be1f7e28..0000000000 --- a/codec-smtp/src/main/java/io/netty/handler/codec/smtp/LastSmtpContent.java +++ /dev/null @@ -1,119 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.smtp; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.internal.UnstableApi; - -/** - * The last part of a sequence of {@link SmtpContent}s that are sent after a {@code DATA} request. - * Be aware that a {@link SmtpContent} / {@link LastSmtpContent} sequence must always use CRLF as line delimiter - * and the lines that start with a DOT must be escaped with an extra DOT as - * specified by RFC2821. - */ -@UnstableApi -public interface LastSmtpContent extends SmtpContent { - - /** - * Empty {@link LastSmtpContent}. - */ - LastSmtpContent EMPTY_LAST_CONTENT = new LastSmtpContent() { - @Override - public LastSmtpContent copy() { - return this; - } - - @Override - public LastSmtpContent duplicate() { - return this; - } - - @Override - public LastSmtpContent retainedDuplicate() { - return this; - } - - @Override - public LastSmtpContent replace(ByteBuf content) { - return new DefaultLastSmtpContent(content); - } - - @Override - public LastSmtpContent retain() { - return this; - } - - @Override - public LastSmtpContent retain(int increment) { - return this; - } - - @Override - public LastSmtpContent touch() { - return this; - } - - @Override - public LastSmtpContent touch(Object hint) { - return this; - } - - @Override - public ByteBuf content() { - return Unpooled.EMPTY_BUFFER; - } - - @Override - public int refCnt() { - return 1; - } - - @Override - public boolean release() { - return false; - } - - @Override - public boolean release(int decrement) { - return false; - } - }; - - @Override - LastSmtpContent copy(); - - @Override - LastSmtpContent duplicate(); - - @Override - LastSmtpContent retainedDuplicate(); - - @Override - LastSmtpContent replace(ByteBuf content); - - @Override - LastSmtpContent retain(); - - @Override - LastSmtpContent retain(int increment); - - @Override - LastSmtpContent touch(); - - @Override - LastSmtpContent touch(Object hint); -} diff --git a/codec-smtp/src/main/java/io/netty/handler/codec/smtp/SmtpCommand.java b/codec-smtp/src/main/java/io/netty/handler/codec/smtp/SmtpCommand.java deleted file mode 100644 index 2a7edf89fe..0000000000 --- a/codec-smtp/src/main/java/io/netty/handler/codec/smtp/SmtpCommand.java +++ /dev/null @@ -1,114 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.smtp; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.util.AsciiString; -import io.netty.util.internal.UnstableApi; - -import java.util.HashMap; -import java.util.Map; - -/** - * The command part of a {@link SmtpRequest}. - */ -@UnstableApi -public final class SmtpCommand { - public static final SmtpCommand EHLO = new SmtpCommand(AsciiString.cached("EHLO")); - public static final SmtpCommand HELO = new SmtpCommand(AsciiString.cached("HELO")); - public static final SmtpCommand AUTH = new SmtpCommand(AsciiString.cached("AUTH")); - public static final SmtpCommand MAIL = new SmtpCommand(AsciiString.cached("MAIL")); - public static final SmtpCommand RCPT = new SmtpCommand(AsciiString.cached("RCPT")); - public static final SmtpCommand DATA = new SmtpCommand(AsciiString.cached("DATA")); - public static final SmtpCommand NOOP = new SmtpCommand(AsciiString.cached("NOOP")); - public static final SmtpCommand RSET = new SmtpCommand(AsciiString.cached("RSET")); - public static final SmtpCommand EXPN = new SmtpCommand(AsciiString.cached("EXPN")); - public static final SmtpCommand VRFY = new SmtpCommand(AsciiString.cached("VRFY")); - public static final SmtpCommand HELP = new SmtpCommand(AsciiString.cached("HELP")); - public static final SmtpCommand QUIT = new SmtpCommand(AsciiString.cached("QUIT")); - public static final SmtpCommand EMPTY = new SmtpCommand(AsciiString.cached("")); - - private static final Map COMMANDS = new HashMap<>(); - static { - COMMANDS.put(EHLO.name().toString(), EHLO); - COMMANDS.put(HELO.name().toString(), HELO); - COMMANDS.put(AUTH.name().toString(), AUTH); - COMMANDS.put(MAIL.name().toString(), MAIL); - COMMANDS.put(RCPT.name().toString(), RCPT); - COMMANDS.put(DATA.name().toString(), DATA); - COMMANDS.put(NOOP.name().toString(), NOOP); - COMMANDS.put(RSET.name().toString(), RSET); - COMMANDS.put(EXPN.name().toString(), EXPN); - COMMANDS.put(VRFY.name().toString(), VRFY); - COMMANDS.put(HELP.name().toString(), HELP); - COMMANDS.put(QUIT.name().toString(), QUIT); - COMMANDS.put(EMPTY.name().toString(), EMPTY); - } - - /** - * Returns the {@link SmtpCommand} for the given command name. - */ - public static SmtpCommand valueOf(CharSequence commandName) { - requireNonNull(commandName, "commandName"); - SmtpCommand command = COMMANDS.get(commandName.toString()); - return command != null ? command : new SmtpCommand(AsciiString.of(commandName)); - } - - private final AsciiString name; - - private SmtpCommand(AsciiString name) { - this.name = name; - } - - /** - * Return the command name. - */ - public AsciiString name() { - return name; - } - - void encode(ByteBuf buffer) { - ByteBufUtil.writeAscii(buffer, name); - } - - boolean isContentExpected() { - return this.equals(DATA); - } - - @Override - public int hashCode() { - return name.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } - if (!(obj instanceof SmtpCommand)) { - return false; - } - return name.contentEqualsIgnoreCase(((SmtpCommand) obj).name()); - } - - @Override - public String toString() { - return "SmtpCommand{name=" + name + '}'; - } -} diff --git a/codec-smtp/src/main/java/io/netty/handler/codec/smtp/SmtpContent.java b/codec-smtp/src/main/java/io/netty/handler/codec/smtp/SmtpContent.java deleted file mode 100644 index e01ff5bd6e..0000000000 --- a/codec-smtp/src/main/java/io/netty/handler/codec/smtp/SmtpContent.java +++ /dev/null @@ -1,53 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.smtp; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufHolder; -import io.netty.util.internal.UnstableApi; - -/** - * Content that is sent after the {@code DATA} request. - * Be aware that a {@link SmtpContent} / {@link LastSmtpContent} sequence must always use CRLF as line delimiter - * and the lines that start with a DOT must be escaped with an extra DOT as - * specified by RFC2821. - */ -@UnstableApi -public interface SmtpContent extends ByteBufHolder { - @Override - SmtpContent copy(); - - @Override - SmtpContent duplicate(); - - @Override - SmtpContent retainedDuplicate(); - - @Override - SmtpContent replace(ByteBuf content); - - @Override - SmtpContent retain(); - - @Override - SmtpContent retain(int increment); - - @Override - SmtpContent touch(); - - @Override - SmtpContent touch(Object hint); -} diff --git a/codec-smtp/src/main/java/io/netty/handler/codec/smtp/SmtpRequest.java b/codec-smtp/src/main/java/io/netty/handler/codec/smtp/SmtpRequest.java deleted file mode 100644 index 8d9bfdec2c..0000000000 --- a/codec-smtp/src/main/java/io/netty/handler/codec/smtp/SmtpRequest.java +++ /dev/null @@ -1,37 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.smtp; - -import io.netty.util.internal.UnstableApi; - -import java.util.List; - -/** - * An SMTP request. - */ -@UnstableApi -public interface SmtpRequest { - - /** - * Returns the {@link SmtpCommand} that belongs to the request. - */ - SmtpCommand command(); - - /** - * Returns a {@link List} which holds all the parameters of a request, which may be an empty list. - */ - List parameters(); -} diff --git a/codec-smtp/src/main/java/io/netty/handler/codec/smtp/SmtpRequestEncoder.java b/codec-smtp/src/main/java/io/netty/handler/codec/smtp/SmtpRequestEncoder.java deleted file mode 100644 index 3845fbbaa1..0000000000 --- a/codec-smtp/src/main/java/io/netty/handler/codec/smtp/SmtpRequestEncoder.java +++ /dev/null @@ -1,115 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.smtp; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToMessageEncoder; -import io.netty.util.internal.UnstableApi; - -import java.util.Iterator; -import java.util.List; -import java.util.RandomAccess; - -/** - * Encoder for SMTP requests. - */ -@UnstableApi -public final class SmtpRequestEncoder extends MessageToMessageEncoder { - private static final int CRLF_SHORT = ('\r' << 8) | '\n'; - private static final byte SP = ' '; - private static final ByteBuf DOT_CRLF_BUFFER = Unpooled.unreleasableBuffer( - Unpooled.directBuffer(3).writeByte('.').writeByte('\r').writeByte('\n')); - - private boolean contentExpected; - - @Override - public boolean acceptOutboundMessage(Object msg) throws Exception { - return msg instanceof SmtpRequest || msg instanceof SmtpContent; - } - - @Override - protected void encode(ChannelHandlerContext ctx, Object msg, List out) throws Exception { - if (msg instanceof SmtpRequest) { - final SmtpRequest req = (SmtpRequest) msg; - if (contentExpected) { - if (req.command().equals(SmtpCommand.RSET)) { - contentExpected = false; - } else { - throw new IllegalStateException("SmtpContent expected"); - } - } - boolean release = true; - final ByteBuf buffer = ctx.alloc().buffer(); - try { - req.command().encode(buffer); - boolean notEmpty = req.command() != SmtpCommand.EMPTY; - writeParameters(req.parameters(), buffer, notEmpty); - ByteBufUtil.writeShortBE(buffer, CRLF_SHORT); - out.add(buffer); - release = false; - if (req.command().isContentExpected()) { - contentExpected = true; - } - } finally { - if (release) { - buffer.release(); - } - } - } - - if (msg instanceof SmtpContent) { - if (!contentExpected) { - throw new IllegalStateException("No SmtpContent expected"); - } - final ByteBuf content = ((SmtpContent) msg).content(); - out.add(content.retain()); - if (msg instanceof LastSmtpContent) { - out.add(DOT_CRLF_BUFFER.retainedDuplicate()); - contentExpected = false; - } - } - } - - private static void writeParameters(List parameters, ByteBuf out, boolean commandNotEmpty) { - if (parameters.isEmpty()) { - return; - } - if (commandNotEmpty) { - out.writeByte(SP); - } - if (parameters instanceof RandomAccess) { - final int sizeMinusOne = parameters.size() - 1; - for (int i = 0; i < sizeMinusOne; i++) { - ByteBufUtil.writeAscii(out, parameters.get(i)); - out.writeByte(SP); - } - ByteBufUtil.writeAscii(out, parameters.get(sizeMinusOne)); - } else { - final Iterator params = parameters.iterator(); - for (;;) { - ByteBufUtil.writeAscii(out, params.next()); - if (params.hasNext()) { - out.writeByte(SP); - } else { - break; - } - } - } - } -} diff --git a/codec-smtp/src/main/java/io/netty/handler/codec/smtp/SmtpRequests.java b/codec-smtp/src/main/java/io/netty/handler/codec/smtp/SmtpRequests.java deleted file mode 100644 index 4a826610d0..0000000000 --- a/codec-smtp/src/main/java/io/netty/handler/codec/smtp/SmtpRequests.java +++ /dev/null @@ -1,148 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.smtp; - -import static java.util.Objects.requireNonNull; - -import io.netty.util.AsciiString; -import io.netty.util.internal.UnstableApi; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * Provides utility methods to create {@link SmtpRequest}s. - */ -@UnstableApi -public final class SmtpRequests { - - private static final SmtpRequest DATA = new DefaultSmtpRequest(SmtpCommand.DATA); - private static final SmtpRequest NOOP = new DefaultSmtpRequest(SmtpCommand.NOOP); - private static final SmtpRequest RSET = new DefaultSmtpRequest(SmtpCommand.RSET); - private static final SmtpRequest HELP_NO_ARG = new DefaultSmtpRequest(SmtpCommand.HELP); - private static final SmtpRequest QUIT = new DefaultSmtpRequest(SmtpCommand.QUIT); - private static final AsciiString FROM_NULL_SENDER = AsciiString.cached("FROM:<>"); - - /** - * Creates a {@code HELO} request. - */ - public static SmtpRequest helo(CharSequence hostname) { - return new DefaultSmtpRequest(SmtpCommand.HELO, hostname); - } - - /** - * Creates a {@code EHLO} request. - */ - public static SmtpRequest ehlo(CharSequence hostname) { - return new DefaultSmtpRequest(SmtpCommand.EHLO, hostname); - } - - /** - * Creates a {@code EMPTY} request. - */ - public static SmtpRequest empty(CharSequence... parameter) { - return new DefaultSmtpRequest(SmtpCommand.EMPTY, parameter); - } - - /** - * Creates a {@code AUTH} request. - */ - public static SmtpRequest auth(CharSequence... parameter) { - return new DefaultSmtpRequest(SmtpCommand.AUTH, parameter); - } - - /** - * Creates a {@code NOOP} request. - */ - public static SmtpRequest noop() { - return NOOP; - } - - /** - * Creates a {@code DATA} request. - */ - public static SmtpRequest data() { - return DATA; - } - - /** - * Creates a {@code RSET} request. - */ - public static SmtpRequest rset() { - return RSET; - } - - /** - * Creates a {@code HELP} request. - */ - public static SmtpRequest help(String cmd) { - return cmd == null ? HELP_NO_ARG : new DefaultSmtpRequest(SmtpCommand.HELP, cmd); - } - - /** - * Creates a {@code QUIT} request. - */ - public static SmtpRequest quit() { - return QUIT; - } - - /** - * Creates a {@code MAIL} request. - */ - public static SmtpRequest mail(CharSequence sender, CharSequence... mailParameters) { - if (mailParameters == null || mailParameters.length == 0) { - return new DefaultSmtpRequest(SmtpCommand.MAIL, - sender != null ? "FROM:<" + sender + '>' : FROM_NULL_SENDER); - } else { - List params = new ArrayList<>(mailParameters.length + 1); - params.add(sender != null? "FROM:<" + sender + '>' : FROM_NULL_SENDER); - Collections.addAll(params, mailParameters); - return new DefaultSmtpRequest(SmtpCommand.MAIL, params); - } - } - - /** - * Creates a {@code RCPT} request. - */ - public static SmtpRequest rcpt(CharSequence recipient, CharSequence... rcptParameters) { - requireNonNull(recipient, "recipient"); - if (rcptParameters == null || rcptParameters.length == 0) { - return new DefaultSmtpRequest(SmtpCommand.RCPT, "TO:<" + recipient + '>'); - } else { - List params = new ArrayList<>(rcptParameters.length + 1); - params.add("TO:<" + recipient + '>'); - Collections.addAll(params, rcptParameters); - return new DefaultSmtpRequest(SmtpCommand.RCPT, params); - } - } - - /** - * Creates a {@code EXPN} request. - */ - public static SmtpRequest expn(CharSequence mailingList) { - return new DefaultSmtpRequest(SmtpCommand.EXPN, requireNonNull(mailingList, "mailingList")); - } - - /** - * Creates a {@code VRFY} request. - */ - public static SmtpRequest vrfy(CharSequence user) { - return new DefaultSmtpRequest(SmtpCommand.VRFY, requireNonNull(user, "user")); - } - - private SmtpRequests() { } -} diff --git a/codec-smtp/src/main/java/io/netty/handler/codec/smtp/SmtpResponse.java b/codec-smtp/src/main/java/io/netty/handler/codec/smtp/SmtpResponse.java deleted file mode 100644 index 433e9b3017..0000000000 --- a/codec-smtp/src/main/java/io/netty/handler/codec/smtp/SmtpResponse.java +++ /dev/null @@ -1,37 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.smtp; - -import io.netty.util.internal.UnstableApi; - -import java.util.List; - -/** - * A SMTP response - */ -@UnstableApi -public interface SmtpResponse { - - /** - * Returns the response code. - */ - int code(); - - /** - * Returns the details if any. - */ - List details(); -} diff --git a/codec-smtp/src/main/java/io/netty/handler/codec/smtp/SmtpResponseDecoder.java b/codec-smtp/src/main/java/io/netty/handler/codec/smtp/SmtpResponseDecoder.java deleted file mode 100644 index c7c4113a1e..0000000000 --- a/codec-smtp/src/main/java/io/netty/handler/codec/smtp/SmtpResponseDecoder.java +++ /dev/null @@ -1,117 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.smtp; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.DecoderException; -import io.netty.handler.codec.LineBasedFrameDecoder; -import io.netty.util.CharsetUtil; -import io.netty.util.internal.UnstableApi; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * Decoder for SMTP responses. - */ -@UnstableApi -public final class SmtpResponseDecoder extends LineBasedFrameDecoder { - - private List details; - - /** - * Creates a new instance that enforces the given {@code maxLineLength}. - */ - public SmtpResponseDecoder(int maxLineLength) { - super(maxLineLength); - } - - @Override - protected SmtpResponse decode0(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception { - ByteBuf frame = (ByteBuf) super.decode0(ctx, buffer); - if (frame == null) { - // No full line received yet. - return null; - } - try { - final int readable = frame.readableBytes(); - final int readerIndex = frame.readerIndex(); - if (readable < 3) { - throw newDecoderException(buffer, readerIndex, readable); - } - final int code = parseCode(frame); - final int separator = frame.readByte(); - final CharSequence detail = frame.isReadable() ? frame.toString(CharsetUtil.US_ASCII) : null; - - List details = this.details; - - switch (separator) { - case ' ': - // Marks the end of a response. - this.details = null; - if (details != null) { - if (detail != null) { - details.add(detail); - } - } else { - if (detail == null) { - details = Collections.emptyList(); - } else { - details = Collections.singletonList(detail); - } - } - return new DefaultSmtpResponse(code, details); - case '-': - // Multi-line response. - if (detail != null) { - if (details == null) { - // Using initial capacity as it is very unlikely that we will receive a multi-line response - // with more then 3 lines. - this.details = details = new ArrayList<>(4); - } - details.add(detail); - } - break; - default: - throw newDecoderException(buffer, readerIndex, readable); - } - } finally { - frame.release(); - } - return null; - } - - private static DecoderException newDecoderException(ByteBuf buffer, int readerIndex, int readable) { - return new DecoderException( - "Received invalid line: '" + buffer.toString(readerIndex, readable, CharsetUtil.US_ASCII) + '\''); - } - - /** - * Parses the io.netty.handler.codec.smtp code without any allocation, which is three digits. - */ - private static int parseCode(ByteBuf buffer) { - final int first = parseNumber(buffer.readByte()) * 100; - final int second = parseNumber(buffer.readByte()) * 10; - final int third = parseNumber(buffer.readByte()); - return first + second + third; - } - - private static int parseNumber(byte b) { - return Character.digit((char) b, 10); - } -} diff --git a/codec-smtp/src/main/java/io/netty/handler/codec/smtp/SmtpUtils.java b/codec-smtp/src/main/java/io/netty/handler/codec/smtp/SmtpUtils.java deleted file mode 100644 index a1af99658b..0000000000 --- a/codec-smtp/src/main/java/io/netty/handler/codec/smtp/SmtpUtils.java +++ /dev/null @@ -1,32 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.smtp; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -final class SmtpUtils { - - static List toUnmodifiableList(CharSequence... sequences) { - if (sequences == null || sequences.length == 0) { - return Collections.emptyList(); - } - return Collections.unmodifiableList(Arrays.asList(sequences)); - } - - private SmtpUtils() { } -} diff --git a/codec-smtp/src/main/java/io/netty/handler/codec/smtp/package-info.java b/codec-smtp/src/main/java/io/netty/handler/codec/smtp/package-info.java deleted file mode 100644 index 135675fa7f..0000000000 --- a/codec-smtp/src/main/java/io/netty/handler/codec/smtp/package-info.java +++ /dev/null @@ -1,23 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * SMTP codec. - */ -@UnstableApi -package io.netty.handler.codec.smtp; - -import io.netty.util.internal.UnstableApi; diff --git a/codec-smtp/src/test/java/io/netty/handler/codec/smtp/SmtpCommandTest.java b/codec-smtp/src/test/java/io/netty/handler/codec/smtp/SmtpCommandTest.java deleted file mode 100644 index c53b9d1158..0000000000 --- a/codec-smtp/src/test/java/io/netty/handler/codec/smtp/SmtpCommandTest.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.smtp; - -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotSame; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class SmtpCommandTest { - @Test - public void getCommandFromCache() { - assertSame(SmtpCommand.DATA, SmtpCommand.valueOf("DATA")); - assertSame(SmtpCommand.EHLO, SmtpCommand.valueOf("EHLO")); - assertNotSame(SmtpCommand.EHLO, SmtpCommand.valueOf("ehlo")); - } - - @Test - public void equalsIgnoreCase() { - assertEquals(SmtpCommand.MAIL, SmtpCommand.valueOf("mail")); - assertEquals(SmtpCommand.valueOf("test"), SmtpCommand.valueOf("TEST")); - } - - @Test - public void isContentExpected() { - assertTrue(SmtpCommand.valueOf("DATA").isContentExpected()); - assertTrue(SmtpCommand.valueOf("data").isContentExpected()); - - assertFalse(SmtpCommand.HELO.isContentExpected()); - assertFalse(SmtpCommand.HELP.isContentExpected()); - assertFalse(SmtpCommand.valueOf("DATA2").isContentExpected()); - } -} diff --git a/codec-smtp/src/test/java/io/netty/handler/codec/smtp/SmtpRequestEncoderTest.java b/codec-smtp/src/test/java/io/netty/handler/codec/smtp/SmtpRequestEncoderTest.java deleted file mode 100644 index 2be93528e0..0000000000 --- a/codec-smtp/src/test/java/io/netty/handler/codec/smtp/SmtpRequestEncoderTest.java +++ /dev/null @@ -1,160 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.smtp; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.EncoderException; -import io.netty.util.CharsetUtil; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class SmtpRequestEncoderTest { - - @Test - public void testEncodeEhlo() { - testEncode(SmtpRequests.ehlo("localhost"), "EHLO localhost\r\n"); - } - - @Test - public void testEncodeHelo() { - testEncode(SmtpRequests.helo("localhost"), "HELO localhost\r\n"); - } - - @Test - public void testEncodeAuth() { - testEncode(SmtpRequests.auth("LOGIN"), "AUTH LOGIN\r\n"); - } - - @Test - public void testEncodeAuthWithParameter() { - testEncode(SmtpRequests.auth("PLAIN", "dGVzdAB0ZXN0ADEyMzQ="), "AUTH PLAIN dGVzdAB0ZXN0ADEyMzQ=\r\n"); - } - - @Test - public void testEncodeEmpty() { - testEncode(SmtpRequests.empty("dGVzdAB0ZXN0ADEyMzQ="), "dGVzdAB0ZXN0ADEyMzQ=\r\n"); - } - - @Test - public void testEncodeMail() { - testEncode(SmtpRequests.mail("me@netty.io"), "MAIL FROM:\r\n"); - } - - @Test - public void testEncodeMailNullSender() { - testEncode(SmtpRequests.mail(null), "MAIL FROM:<>\r\n"); - } - - @Test - public void testEncodeRcpt() { - testEncode(SmtpRequests.rcpt("me@netty.io"), "RCPT TO:\r\n"); - } - - @Test - public void testEncodeNoop() { - testEncode(SmtpRequests.noop(), "NOOP\r\n"); - } - - @Test - public void testEncodeRset() { - testEncode(SmtpRequests.rset(), "RSET\r\n"); - } - - @Test - public void testEncodeHelp() { - testEncode(SmtpRequests.help(null), "HELP\r\n"); - } - - @Test - public void testEncodeHelpWithArg() { - testEncode(SmtpRequests.help("MAIL"), "HELP MAIL\r\n"); - } - - @Test - public void testEncodeData() { - testEncode(SmtpRequests.data(), "DATA\r\n"); - } - - @Test - public void testEncodeDataAndContent() { - EmbeddedChannel channel = new EmbeddedChannel(new SmtpRequestEncoder()); - assertTrue(channel.writeOutbound(SmtpRequests.data())); - assertTrue(channel.writeOutbound( - new DefaultSmtpContent(Unpooled.copiedBuffer("Subject: Test\r\n\r\n", CharsetUtil.US_ASCII)))); - assertTrue(channel.writeOutbound( - new DefaultLastSmtpContent(Unpooled.copiedBuffer("Test\r\n", CharsetUtil.US_ASCII)))); - assertTrue(channel.finish()); - - assertEquals("DATA\r\nSubject: Test\r\n\r\nTest\r\n.\r\n", getWrittenString(channel)); - } - - @Test - public void testThrowsIfContentExpected() { - EmbeddedChannel channel = new EmbeddedChannel(new SmtpRequestEncoder()); - try { - assertTrue(channel.writeOutbound(SmtpRequests.data())); - assertThrows(EncoderException.class, () ->channel.writeOutbound(SmtpRequests.noop())); - } finally { - channel.finishAndReleaseAll(); - } - } - - @Test - public void testRsetClearsContentExpectedFlag() { - EmbeddedChannel channel = new EmbeddedChannel(new SmtpRequestEncoder()); - - assertTrue(channel.writeOutbound(SmtpRequests.data())); - assertTrue(channel.writeOutbound(SmtpRequests.rset())); - assertTrue(channel.writeOutbound(SmtpRequests.noop())); - assertTrue(channel.finish()); - - assertEquals("DATA\r\nRSET\r\nNOOP\r\n", getWrittenString(channel)); - } - - private static String getWrittenString(EmbeddedChannel channel) { - ByteBuf written = Unpooled.buffer(); - - for (;;) { - ByteBuf buffer = channel.readOutbound(); - if (buffer == null) { - break; - } - written.writeBytes(buffer); - buffer.release(); - } - - String writtenString = written.toString(CharsetUtil.US_ASCII); - written.release(); - - return writtenString; - } - - private static void testEncode(SmtpRequest request, String expected) { - EmbeddedChannel channel = new EmbeddedChannel(new SmtpRequestEncoder()); - assertTrue(channel.writeOutbound(request)); - assertTrue(channel.finish()); - ByteBuf buffer = channel.readOutbound(); - assertEquals(expected, buffer.toString(CharsetUtil.US_ASCII)); - buffer.release(); - assertNull(channel.readOutbound()); - } -} diff --git a/codec-smtp/src/test/java/io/netty/handler/codec/smtp/SmtpResponseDecoderTest.java b/codec-smtp/src/test/java/io/netty/handler/codec/smtp/SmtpResponseDecoderTest.java deleted file mode 100644 index edcdcd6701..0000000000 --- a/codec-smtp/src/test/java/io/netty/handler/codec/smtp/SmtpResponseDecoderTest.java +++ /dev/null @@ -1,138 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.smtp; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.DecoderException; -import io.netty.util.CharsetUtil; -import org.junit.jupiter.api.Test; - -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class SmtpResponseDecoderTest { - - @Test - public void testDecodeOneLineResponse() { - EmbeddedChannel channel = newChannel(); - assertTrue(channel.writeInbound(newBuffer("200 Ok\r\n"))); - assertTrue(channel.finish()); - - SmtpResponse response = channel.readInbound(); - assertEquals(200, response.code()); - List sequences = response.details(); - assertEquals(1, sequences.size()); - - assertEquals("Ok", sequences.get(0).toString()); - assertNull(channel.readInbound()); - } - - @Test - public void testDecodeOneLineResponseNoDetails() { - EmbeddedChannel channel = newChannel(); - assertTrue(channel.writeInbound(newBuffer("250 \r\n"))); - assertTrue(channel.finish()); - - SmtpResponse response = channel.readInbound(); - assertEquals(250, response.code()); - List sequences = response.details(); - assertEquals(0, sequences.size()); - } - - @Test - public void testDecodeOneLineResponseChunked() { - EmbeddedChannel channel = newChannel(); - assertFalse(channel.writeInbound(newBuffer("200 Ok"))); - assertTrue(channel.writeInbound(newBuffer("\r\n"))); - assertTrue(channel.finish()); - - SmtpResponse response = channel.readInbound(); - assertEquals(200, response.code()); - List sequences = response.details(); - assertEquals(1, sequences.size()); - - assertEquals("Ok", sequences.get(0).toString()); - assertNull(channel.readInbound()); - } - - @Test - public void testDecodeTwoLineResponse() { - EmbeddedChannel channel = newChannel(); - assertTrue(channel.writeInbound(newBuffer("200-Hello\r\n200 Ok\r\n"))); - assertTrue(channel.finish()); - - SmtpResponse response = channel.readInbound(); - assertEquals(200, response.code()); - List sequences = response.details(); - assertEquals(2, sequences.size()); - - assertEquals("Hello", sequences.get(0).toString()); - assertEquals("Ok", sequences.get(1).toString()); - assertNull(channel.readInbound()); - } - - @Test - public void testDecodeTwoLineResponseChunked() { - EmbeddedChannel channel = newChannel(); - assertFalse(channel.writeInbound(newBuffer("200-"))); - assertFalse(channel.writeInbound(newBuffer("Hello\r\n2"))); - assertFalse(channel.writeInbound(newBuffer("00 Ok"))); - assertTrue(channel.writeInbound(newBuffer("\r\n"))); - assertTrue(channel.finish()); - - SmtpResponse response = channel.readInbound(); - assertEquals(200, response.code()); - List sequences = response.details(); - assertEquals(2, sequences.size()); - - assertEquals("Hello", sequences.get(0).toString()); - assertEquals("Ok", sequences.get(1).toString()); - assertNull(channel.readInbound()); - } - - @Test - public void testDecodeInvalidSeparator() { - EmbeddedChannel channel = newChannel(); - assertThrows(DecoderException.class, () -> channel.writeInbound(newBuffer("200:Ok\r\n"))); - } - - @Test - public void testDecodeInvalidCode() { - EmbeddedChannel channel = newChannel(); - assertThrows(DecoderException.class, () -> channel.writeInbound(newBuffer("xyz Ok\r\n"))); - } - - @Test - public void testDecodeInvalidLine() { - EmbeddedChannel channel = newChannel(); - assertThrows(DecoderException.class, () -> channel.writeInbound(newBuffer("Ok\r\n"))); - } - - private static EmbeddedChannel newChannel() { - return new EmbeddedChannel(new SmtpResponseDecoder(Integer.MAX_VALUE)); - } - - private static ByteBuf newBuffer(CharSequence seq) { - return Unpooled.copiedBuffer(seq, CharsetUtil.US_ASCII); - } -} diff --git a/codec-socks/pom.xml b/codec-socks/pom.xml deleted file mode 100644 index 0a00f8d728..0000000000 --- a/codec-socks/pom.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - 4.0.0 - - io.netty - netty-parent - 5.0.0.Final-SNAPSHOT - - - netty-codec-socks - jar - - Netty/Codec/Socks - - - io.netty.codec.socks - - - - - ${project.groupId} - netty-common - ${project.version} - - - ${project.groupId} - netty-buffer - ${project.version} - - - ${project.groupId} - netty-transport - ${project.version} - - - ${project.groupId} - netty-codec - ${project.version} - - - - diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAddressType.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAddressType.java deleted file mode 100644 index 990db7059b..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAddressType.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.socks; - -public enum SocksAddressType { - IPv4((byte) 0x01), - DOMAIN((byte) 0x03), - IPv6((byte) 0x04), - UNKNOWN((byte) 0xff); - - private final byte b; - - SocksAddressType(byte b) { - this.b = b; - } - - /** - * @deprecated Use {@link #valueOf(byte)} instead. - */ - @Deprecated - public static SocksAddressType fromByte(byte b) { - return valueOf(b); - } - - public static SocksAddressType valueOf(byte b) { - for (SocksAddressType code : values()) { - if (code.b == b) { - return code; - } - } - return UNKNOWN; - } - - public byte byteValue() { - return b; - } -} - diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAuthRequest.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAuthRequest.java deleted file mode 100644 index b52575548c..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAuthRequest.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socks; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.util.CharsetUtil; - -import java.nio.charset.CharsetEncoder; - -/** - * An socks auth request. - * - * @see SocksAuthResponse - * @see SocksAuthRequestDecoder - */ -public final class SocksAuthRequest extends SocksRequest { - private static final SocksSubnegotiationVersion SUBNEGOTIATION_VERSION = SocksSubnegotiationVersion.AUTH_PASSWORD; - private final String username; - private final String password; - - public SocksAuthRequest(String username, String password) { - super(SocksRequestType.AUTH); - requireNonNull(username, "username"); - requireNonNull(password, "password"); - - final CharsetEncoder asciiEncoder = CharsetUtil.encoder(CharsetUtil.US_ASCII); - if (!asciiEncoder.canEncode(username) || !asciiEncoder.canEncode(password)) { - throw new IllegalArgumentException( - "username: " + username + " or password: **** values should be in pure ascii"); - } - if (username.length() > 255) { - throw new IllegalArgumentException("username: " + username + " exceeds 255 char limit"); - } - if (password.length() > 255) { - throw new IllegalArgumentException("password: **** exceeds 255 char limit"); - } - this.username = username; - this.password = password; - } - - /** - * Returns username that needs to be authenticated - * - * @return username that needs to be authenticated - */ - public String username() { - return username; - } - - /** - * Returns password that needs to be validated - * - * @return password that needs to be validated - */ - public String password() { - return password; - } - - @Override - public void encodeAsByteBuf(ByteBuf byteBuf) { - byteBuf.writeByte(SUBNEGOTIATION_VERSION.byteValue()); - byteBuf.writeByte(username.length()); - byteBuf.writeCharSequence(username, CharsetUtil.US_ASCII); - byteBuf.writeByte(password.length()); - byteBuf.writeCharSequence(password, CharsetUtil.US_ASCII); - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAuthRequestDecoder.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAuthRequestDecoder.java deleted file mode 100644 index dd939b64e9..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAuthRequestDecoder.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socks; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.ReplayingDecoder; -import io.netty.handler.codec.socks.SocksAuthRequestDecoder.State; - -/** - * Decodes {@link ByteBuf}s into {@link SocksAuthRequest}. - * Before returning SocksRequest decoder removes itself from pipeline. - */ -public class SocksAuthRequestDecoder extends ReplayingDecoder { - - private String username; - - public SocksAuthRequestDecoder() { - super(State.CHECK_PROTOCOL_VERSION); - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf) throws Exception { - switch (state()) { - case CHECK_PROTOCOL_VERSION: { - if (byteBuf.readByte() != SocksSubnegotiationVersion.AUTH_PASSWORD.byteValue()) { - ctx.fireChannelRead(SocksCommonUtils.UNKNOWN_SOCKS_REQUEST); - break; - } - checkpoint(State.READ_USERNAME); - } - case READ_USERNAME: { - int fieldLength = byteBuf.readByte(); - username = SocksCommonUtils.readUsAscii(byteBuf, fieldLength); - checkpoint(State.READ_PASSWORD); - } - case READ_PASSWORD: { - int fieldLength = byteBuf.readByte(); - String password = SocksCommonUtils.readUsAscii(byteBuf, fieldLength); - ctx.fireChannelRead(new SocksAuthRequest(username, password)); - break; - } - default: { - throw new Error(); - } - } - ctx.pipeline().remove(this); - } - - enum State { - CHECK_PROTOCOL_VERSION, - READ_USERNAME, - READ_PASSWORD - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAuthResponse.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAuthResponse.java deleted file mode 100644 index bce7c5859b..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAuthResponse.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socks; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; - -/** - * An socks auth response. - * - * @see SocksAuthRequest - * @see SocksAuthResponseDecoder - */ -public final class SocksAuthResponse extends SocksResponse { - private static final SocksSubnegotiationVersion SUBNEGOTIATION_VERSION = SocksSubnegotiationVersion.AUTH_PASSWORD; - private final SocksAuthStatus authStatus; - - public SocksAuthResponse(SocksAuthStatus authStatus) { - super(SocksResponseType.AUTH); - requireNonNull(authStatus, "authStatus"); - this.authStatus = authStatus; - } - - /** - * Returns the {@link SocksAuthStatus} of this {@link SocksAuthResponse} - * - * @return The {@link SocksAuthStatus} of this {@link SocksAuthResponse} - */ - public SocksAuthStatus authStatus() { - return authStatus; - } - - @Override - public void encodeAsByteBuf(ByteBuf byteBuf) { - byteBuf.writeByte(SUBNEGOTIATION_VERSION.byteValue()); - byteBuf.writeByte(authStatus.byteValue()); - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAuthResponseDecoder.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAuthResponseDecoder.java deleted file mode 100644 index 1eae5c5b1f..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAuthResponseDecoder.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socks; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.ReplayingDecoder; -import io.netty.handler.codec.socks.SocksAuthResponseDecoder.State; - -/** - * Decodes {@link ByteBuf}s into {@link SocksAuthResponse}. - * Before returning SocksResponse decoder removes itself from pipeline. - */ -public class SocksAuthResponseDecoder extends ReplayingDecoder { - - public SocksAuthResponseDecoder() { - super(State.CHECK_PROTOCOL_VERSION); - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf) - throws Exception { - switch (state()) { - case CHECK_PROTOCOL_VERSION: { - if (byteBuf.readByte() != SocksSubnegotiationVersion.AUTH_PASSWORD.byteValue()) { - ctx.fireChannelRead(SocksCommonUtils.UNKNOWN_SOCKS_RESPONSE); - break; - } - checkpoint(State.READ_AUTH_RESPONSE); - } - case READ_AUTH_RESPONSE: { - SocksAuthStatus authStatus = SocksAuthStatus.valueOf(byteBuf.readByte()); - ctx.fireChannelRead(new SocksAuthResponse(authStatus)); - break; - } - default: { - throw new Error(); - } - } - ctx.pipeline().remove(this); - } - - enum State { - CHECK_PROTOCOL_VERSION, - READ_AUTH_RESPONSE - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAuthScheme.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAuthScheme.java deleted file mode 100644 index 9101a00b17..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAuthScheme.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.socks; - -public enum SocksAuthScheme { - NO_AUTH((byte) 0x00), - AUTH_GSSAPI((byte) 0x01), - AUTH_PASSWORD((byte) 0x02), - UNKNOWN((byte) 0xff); - - private final byte b; - - SocksAuthScheme(byte b) { - this.b = b; - } - - /** - * @deprecated Use {@link #valueOf(byte)} instead. - */ - @Deprecated - public static SocksAuthScheme fromByte(byte b) { - return valueOf(b); - } - - public static SocksAuthScheme valueOf(byte b) { - for (SocksAuthScheme code : values()) { - if (code.b == b) { - return code; - } - } - return UNKNOWN; - } - - public byte byteValue() { - return b; - } -} - diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAuthStatus.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAuthStatus.java deleted file mode 100644 index 81a8f7cfac..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAuthStatus.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.socks; - -public enum SocksAuthStatus { - SUCCESS((byte) 0x00), - FAILURE((byte) 0xff); - - private final byte b; - - SocksAuthStatus(byte b) { - this.b = b; - } - - /** - * @deprecated Use {@link #valueOf(byte)} instead. - */ - @Deprecated - public static SocksAuthStatus fromByte(byte b) { - return valueOf(b); - } - - public static SocksAuthStatus valueOf(byte b) { - for (SocksAuthStatus code : values()) { - if (code.b == b) { - return code; - } - } - return FAILURE; - } - - public byte byteValue() { - return b; - } -} - diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCmdRequest.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCmdRequest.java deleted file mode 100644 index 21b1d8377e..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCmdRequest.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socks; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.util.CharsetUtil; -import io.netty.util.NetUtil; - -import java.net.IDN; - -/** - * An socks cmd request. - * - * @see SocksCmdResponse - * @see SocksCmdRequestDecoder - */ -public final class SocksCmdRequest extends SocksRequest { - private final SocksCmdType cmdType; - private final SocksAddressType addressType; - private final String host; - private final int port; - - public SocksCmdRequest(SocksCmdType cmdType, SocksAddressType addressType, String host, int port) { - super(SocksRequestType.CMD); - requireNonNull(cmdType, "cmdType"); - requireNonNull(addressType, "addressType"); - requireNonNull(host, "host"); - switch (addressType) { - case IPv4: - if (!NetUtil.isValidIpV4Address(host)) { - throw new IllegalArgumentException(host + " is not a valid IPv4 address"); - } - break; - case DOMAIN: - String asciiHost = IDN.toASCII(host); - if (asciiHost.length() > 255) { - throw new IllegalArgumentException(host + " IDN: " + asciiHost + " exceeds 255 char limit"); - } - host = asciiHost; - break; - case IPv6: - if (!NetUtil.isValidIpV6Address(host)) { - throw new IllegalArgumentException(host + " is not a valid IPv6 address"); - } - break; - case UNKNOWN: - break; - } - if (port <= 0 || port >= 65536) { - throw new IllegalArgumentException(port + " is not in bounds 0 < x < 65536"); - } - this.cmdType = cmdType; - this.addressType = addressType; - this.host = host; - this.port = port; - } - - /** - * Returns the {@link SocksCmdType} of this {@link SocksCmdRequest} - * - * @return The {@link SocksCmdType} of this {@link SocksCmdRequest} - */ - public SocksCmdType cmdType() { - return cmdType; - } - - /** - * Returns the {@link SocksAddressType} of this {@link SocksCmdRequest} - * - * @return The {@link SocksAddressType} of this {@link SocksCmdRequest} - */ - public SocksAddressType addressType() { - return addressType; - } - - /** - * Returns host that is used as a parameter in {@link SocksCmdType} - * - * @return host that is used as a parameter in {@link SocksCmdType} - */ - public String host() { - return addressType == SocksAddressType.DOMAIN ? IDN.toUnicode(host) : host; - } - - /** - * Returns port that is used as a parameter in {@link SocksCmdType} - * - * @return port that is used as a parameter in {@link SocksCmdType} - */ - public int port() { - return port; - } - - @Override - public void encodeAsByteBuf(ByteBuf byteBuf) { - byteBuf.writeByte(protocolVersion().byteValue()); - byteBuf.writeByte(cmdType.byteValue()); - byteBuf.writeByte(0x00); - byteBuf.writeByte(addressType.byteValue()); - switch (addressType) { - case IPv4: { - byteBuf.writeBytes(NetUtil.createByteArrayFromIpAddressString(host)); - byteBuf.writeShort(port); - break; - } - - case DOMAIN: { - byteBuf.writeByte(host.length()); - byteBuf.writeCharSequence(host, CharsetUtil.US_ASCII); - byteBuf.writeShort(port); - break; - } - - case IPv6: { - byteBuf.writeBytes(NetUtil.createByteArrayFromIpAddressString(host)); - byteBuf.writeShort(port); - break; - } - } - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCmdRequestDecoder.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCmdRequestDecoder.java deleted file mode 100644 index ef32573aa9..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCmdRequestDecoder.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socks; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.ReplayingDecoder; -import io.netty.handler.codec.socks.SocksCmdRequestDecoder.State; -import io.netty.util.NetUtil; - -/** - * Decodes {@link ByteBuf}s into {@link SocksCmdRequest}. - * Before returning SocksRequest decoder removes itself from pipeline. - */ -public class SocksCmdRequestDecoder extends ReplayingDecoder { - - private SocksCmdType cmdType; - private SocksAddressType addressType; - - public SocksCmdRequestDecoder() { - super(State.CHECK_PROTOCOL_VERSION); - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf) throws Exception { - switch (state()) { - case CHECK_PROTOCOL_VERSION: { - if (byteBuf.readByte() != SocksProtocolVersion.SOCKS5.byteValue()) { - ctx.fireChannelRead(SocksCommonUtils.UNKNOWN_SOCKS_REQUEST); - break; - } - checkpoint(State.READ_CMD_HEADER); - } - case READ_CMD_HEADER: { - cmdType = SocksCmdType.valueOf(byteBuf.readByte()); - byteBuf.skipBytes(1); // reserved - addressType = SocksAddressType.valueOf(byteBuf.readByte()); - checkpoint(State.READ_CMD_ADDRESS); - } - case READ_CMD_ADDRESS: { - switch (addressType) { - case IPv4: { - String host = NetUtil.intToIpAddress(byteBuf.readInt()); - int port = byteBuf.readUnsignedShort(); - ctx.fireChannelRead(new SocksCmdRequest(cmdType, addressType, host, port)); - break; - } - case DOMAIN: { - int fieldLength = byteBuf.readByte(); - String host = SocksCommonUtils.readUsAscii(byteBuf, fieldLength); - int port = byteBuf.readUnsignedShort(); - ctx.fireChannelRead(new SocksCmdRequest(cmdType, addressType, host, port)); - break; - } - case IPv6: { - byte[] bytes = new byte[16]; - byteBuf.readBytes(bytes); - String host = SocksCommonUtils.ipv6toStr(bytes); - int port = byteBuf.readUnsignedShort(); - ctx.fireChannelRead(new SocksCmdRequest(cmdType, addressType, host, port)); - break; - } - case UNKNOWN: { - ctx.fireChannelRead(SocksCommonUtils.UNKNOWN_SOCKS_REQUEST); - break; - } - default: { - throw new Error(); - } - } - break; - } - default: { - throw new Error(); - } - } - ctx.pipeline().remove(this); - } - - enum State { - CHECK_PROTOCOL_VERSION, - READ_CMD_HEADER, - READ_CMD_ADDRESS - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCmdResponse.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCmdResponse.java deleted file mode 100644 index c44771aba0..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCmdResponse.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socks; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.util.CharsetUtil; -import io.netty.util.NetUtil; - -import java.net.IDN; - -/** - * A socks cmd response. - * - * @see SocksCmdRequest - * @see SocksCmdResponseDecoder - */ -public final class SocksCmdResponse extends SocksResponse { - private final SocksCmdStatus cmdStatus; - - private final SocksAddressType addressType; - private final String host; - private final int port; - - // All arrays are initialized on construction time to 0/false/null remove array Initialization - private static final byte[] DOMAIN_ZEROED = {0x00}; - private static final byte[] IPv4_HOSTNAME_ZEROED = {0x00, 0x00, 0x00, 0x00}; - private static final byte[] IPv6_HOSTNAME_ZEROED = {0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00}; - - public SocksCmdResponse(SocksCmdStatus cmdStatus, SocksAddressType addressType) { - this(cmdStatus, addressType, null, 0); - } - - /** - * Constructs new response and includes provided host and port as part of it. - * - * @param cmdStatus status of the response - * @param addressType type of host parameter - * @param host host (BND.ADDR field) is address that server used when connecting to the target host. - * When null a value of 4/8 0x00 octets will be used for IPv4/IPv6 and a single 0x00 byte will be - * used for domain addressType. Value is converted to ASCII using {@link IDN#toASCII(String)}. - * @param port port (BND.PORT field) that the server assigned to connect to the target host - * @throws NullPointerException in case cmdStatus or addressType are missing - * @throws IllegalArgumentException in case host or port cannot be validated - * @see IDN#toASCII(String) - */ - public SocksCmdResponse(SocksCmdStatus cmdStatus, SocksAddressType addressType, String host, int port) { - super(SocksResponseType.CMD); - requireNonNull(cmdStatus, "cmdStatus"); - requireNonNull(addressType, "addressType"); - if (host != null) { - switch (addressType) { - case IPv4: - if (!NetUtil.isValidIpV4Address(host)) { - throw new IllegalArgumentException(host + " is not a valid IPv4 address"); - } - break; - case DOMAIN: - String asciiHost = IDN.toASCII(host); - if (asciiHost.length() > 255) { - throw new IllegalArgumentException(host + " IDN: " + asciiHost + " exceeds 255 char limit"); - } - host = asciiHost; - break; - case IPv6: - if (!NetUtil.isValidIpV6Address(host)) { - throw new IllegalArgumentException(host + " is not a valid IPv6 address"); - } - break; - case UNKNOWN: - break; - } - } - if (port < 0 || port > 65535) { - throw new IllegalArgumentException(port + " is not in bounds 0 <= x <= 65535"); - } - this.cmdStatus = cmdStatus; - this.addressType = addressType; - this.host = host; - this.port = port; - } - - /** - * Returns the {@link SocksCmdStatus} of this {@link SocksCmdResponse} - * - * @return The {@link SocksCmdStatus} of this {@link SocksCmdResponse} - */ - public SocksCmdStatus cmdStatus() { - return cmdStatus; - } - - /** - * Returns the {@link SocksAddressType} of this {@link SocksCmdResponse} - * - * @return The {@link SocksAddressType} of this {@link SocksCmdResponse} - */ - public SocksAddressType addressType() { - return addressType; - } - - /** - * Returns host that is used as a parameter in {@link SocksCmdType}. - * Host (BND.ADDR field in response) is address that server used when connecting to the target host. - * This is typically different from address which client uses to connect to the SOCKS server. - * - * @return host that is used as a parameter in {@link SocksCmdType} - * or null when there was no host specified during response construction - */ - public String host() { - return host != null && addressType == SocksAddressType.DOMAIN ? IDN.toUnicode(host) : host; - } - - /** - * Returns port that is used as a parameter in {@link SocksCmdType}. - * Port (BND.PORT field in response) is port that the server assigned to connect to the target host. - * - * @return port that is used as a parameter in {@link SocksCmdType} - */ - public int port() { - return port; - } - - @Override - public void encodeAsByteBuf(ByteBuf byteBuf) { - byteBuf.writeByte(protocolVersion().byteValue()); - byteBuf.writeByte(cmdStatus.byteValue()); - byteBuf.writeByte(0x00); - byteBuf.writeByte(addressType.byteValue()); - switch (addressType) { - case IPv4: { - byte[] hostContent = host == null ? - IPv4_HOSTNAME_ZEROED : NetUtil.createByteArrayFromIpAddressString(host); - byteBuf.writeBytes(hostContent); - byteBuf.writeShort(port); - break; - } - case DOMAIN: { - if (host != null) { - byteBuf.writeByte(host.length()); - byteBuf.writeCharSequence(host, CharsetUtil.US_ASCII); - } else { - byteBuf.writeByte(DOMAIN_ZEROED.length); - byteBuf.writeBytes(DOMAIN_ZEROED); - } - byteBuf.writeShort(port); - break; - } - case IPv6: { - byte[] hostContent = host == null - ? IPv6_HOSTNAME_ZEROED : NetUtil.createByteArrayFromIpAddressString(host); - byteBuf.writeBytes(hostContent); - byteBuf.writeShort(port); - break; - } - } - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCmdResponseDecoder.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCmdResponseDecoder.java deleted file mode 100644 index c688916a62..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCmdResponseDecoder.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socks; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.ReplayingDecoder; -import io.netty.handler.codec.socks.SocksCmdResponseDecoder.State; -import io.netty.util.NetUtil; - -/** - * Decodes {@link ByteBuf}s into {@link SocksCmdResponse}. - * Before returning SocksResponse decoder removes itself from pipeline. - */ -public class SocksCmdResponseDecoder extends ReplayingDecoder { - - private SocksCmdStatus cmdStatus; - private SocksAddressType addressType; - - public SocksCmdResponseDecoder() { - super(State.CHECK_PROTOCOL_VERSION); - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf) throws Exception { - switch (state()) { - case CHECK_PROTOCOL_VERSION: { - if (byteBuf.readByte() != SocksProtocolVersion.SOCKS5.byteValue()) { - ctx.fireChannelRead(SocksCommonUtils.UNKNOWN_SOCKS_RESPONSE); - break; - } - checkpoint(State.READ_CMD_HEADER); - } - case READ_CMD_HEADER: { - cmdStatus = SocksCmdStatus.valueOf(byteBuf.readByte()); - byteBuf.skipBytes(1); // reserved - addressType = SocksAddressType.valueOf(byteBuf.readByte()); - checkpoint(State.READ_CMD_ADDRESS); - } - case READ_CMD_ADDRESS: { - switch (addressType) { - case IPv4: { - String host = NetUtil.intToIpAddress(byteBuf.readInt()); - int port = byteBuf.readUnsignedShort(); - ctx.fireChannelRead(new SocksCmdResponse(cmdStatus, addressType, host, port)); - break; - } - case DOMAIN: { - int fieldLength = byteBuf.readByte(); - String host = SocksCommonUtils.readUsAscii(byteBuf, fieldLength); - int port = byteBuf.readUnsignedShort(); - ctx.fireChannelRead(new SocksCmdResponse(cmdStatus, addressType, host, port)); - break; - } - case IPv6: { - byte[] bytes = new byte[16]; - byteBuf.readBytes(bytes); - String host = SocksCommonUtils.ipv6toStr(bytes); - int port = byteBuf.readUnsignedShort(); - ctx.fireChannelRead(new SocksCmdResponse(cmdStatus, addressType, host, port)); - break; - } - case UNKNOWN: { - ctx.fireChannelRead(SocksCommonUtils.UNKNOWN_SOCKS_RESPONSE); - break; - } - default: { - throw new Error(); - } - } - break; - } - default: { - throw new Error(); - } - } - ctx.pipeline().remove(this); - } - - enum State { - CHECK_PROTOCOL_VERSION, - READ_CMD_HEADER, - READ_CMD_ADDRESS - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCmdStatus.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCmdStatus.java deleted file mode 100644 index 765925eefa..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCmdStatus.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.socks; - -public enum SocksCmdStatus { - SUCCESS((byte) 0x00), - FAILURE((byte) 0x01), - FORBIDDEN((byte) 0x02), - NETWORK_UNREACHABLE((byte) 0x03), - HOST_UNREACHABLE((byte) 0x04), - REFUSED((byte) 0x05), - TTL_EXPIRED((byte) 0x06), - COMMAND_NOT_SUPPORTED((byte) 0x07), - ADDRESS_NOT_SUPPORTED((byte) 0x08), - UNASSIGNED((byte) 0xff); - - private final byte b; - - SocksCmdStatus(byte b) { - this.b = b; - } - - /** - * @deprecated Use {@link #valueOf(byte)} instead. - */ - @Deprecated - public static SocksCmdStatus fromByte(byte b) { - return valueOf(b); - } - - public static SocksCmdStatus valueOf(byte b) { - for (SocksCmdStatus code : values()) { - if (code.b == b) { - return code; - } - } - return UNASSIGNED; - } - - public byte byteValue() { - return b; - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCmdType.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCmdType.java deleted file mode 100644 index 9e67e37648..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCmdType.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.socks; - -public enum SocksCmdType { - CONNECT((byte) 0x01), - BIND((byte) 0x02), - UDP((byte) 0x03), - UNKNOWN((byte) 0xff); - - private final byte b; - - SocksCmdType(byte b) { - this.b = b; - } - - /** - * @deprecated Use {@link #valueOf(byte)} instead. - */ - @Deprecated - public static SocksCmdType fromByte(byte b) { - return valueOf(b); - } - - public static SocksCmdType valueOf(byte b) { - for (SocksCmdType code : values()) { - if (code.b == b) { - return code; - } - } - return UNKNOWN; - } - - public byte byteValue() { - return b; - } -} - diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCommonUtils.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCommonUtils.java deleted file mode 100644 index 1d23bfda53..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCommonUtils.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socks; - -import io.netty.buffer.ByteBuf; -import io.netty.util.CharsetUtil; -import io.netty.util.internal.StringUtil; - -final class SocksCommonUtils { - public static final SocksRequest UNKNOWN_SOCKS_REQUEST = new UnknownSocksRequest(); - public static final SocksResponse UNKNOWN_SOCKS_RESPONSE = new UnknownSocksResponse(); - - /** - * A constructor to stop this class being constructed. - */ - private SocksCommonUtils() { - // NOOP - } - - private static final char ipv6hextetSeparator = ':'; - - /** - * Converts numeric IPv6 to standard (non-compressed) format. - */ - public static String ipv6toStr(byte[] src) { - assert src.length == 16; - StringBuilder sb = new StringBuilder(39); - ipv6toStr(sb, src, 0, 8); - return sb.toString(); - } - - private static void ipv6toStr(StringBuilder sb, byte[] src, int fromHextet, int toHextet) { - int i; - toHextet --; - for (i = fromHextet; i < toHextet; i++) { - appendHextet(sb, src, i); - sb.append(ipv6hextetSeparator); - } - - appendHextet(sb, src, i); - } - - private static void appendHextet(StringBuilder sb, byte[] src, int i) { - StringUtil.toHexString(sb, src, i << 1, 2); - } - - static String readUsAscii(ByteBuf buffer, int length) { - String s = buffer.toString(buffer.readerIndex(), length, CharsetUtil.US_ASCII); - buffer.skipBytes(length); - return s; - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksInitRequest.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksInitRequest.java deleted file mode 100644 index b5c9d4c798..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksInitRequest.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socks; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; - -import java.util.Collections; -import java.util.List; - -/** - * An socks init request. - * - * @see SocksInitResponse - * @see SocksInitRequestDecoder - */ -public final class SocksInitRequest extends SocksRequest { - private final List authSchemes; - - public SocksInitRequest(List authSchemes) { - super(SocksRequestType.INIT); - requireNonNull(authSchemes, "authSchemes"); - this.authSchemes = authSchemes; - } - - /** - * Returns the List<{@link SocksAuthScheme}> of this {@link SocksInitRequest} - * - * @return The List<{@link SocksAuthScheme}> of this {@link SocksInitRequest} - */ - public List authSchemes() { - return Collections.unmodifiableList(authSchemes); - } - - @Override - public void encodeAsByteBuf(ByteBuf byteBuf) { - byteBuf.writeByte(protocolVersion().byteValue()); - byteBuf.writeByte(authSchemes.size()); - for (SocksAuthScheme authScheme : authSchemes) { - byteBuf.writeByte(authScheme.byteValue()); - } - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksInitRequestDecoder.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksInitRequestDecoder.java deleted file mode 100644 index ff23c2bef9..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksInitRequestDecoder.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socks; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.ReplayingDecoder; -import io.netty.handler.codec.socks.SocksInitRequestDecoder.State; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * Decodes {@link ByteBuf}s into {@link SocksInitRequest}. - * Before returning SocksRequest decoder removes itself from pipeline. - */ -public class SocksInitRequestDecoder extends ReplayingDecoder { - - public SocksInitRequestDecoder() { - super(State.CHECK_PROTOCOL_VERSION); - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf) throws Exception { - switch (state()) { - case CHECK_PROTOCOL_VERSION: { - if (byteBuf.readByte() != SocksProtocolVersion.SOCKS5.byteValue()) { - ctx.fireChannelRead(SocksCommonUtils.UNKNOWN_SOCKS_REQUEST); - break; - } - checkpoint(State.READ_AUTH_SCHEMES); - } - case READ_AUTH_SCHEMES: { - final byte authSchemeNum = byteBuf.readByte(); - final List authSchemes; - if (authSchemeNum > 0) { - authSchemes = new ArrayList<>(authSchemeNum); - for (int i = 0; i < authSchemeNum; i++) { - authSchemes.add(SocksAuthScheme.valueOf(byteBuf.readByte())); - } - } else { - authSchemes = Collections.emptyList(); - } - ctx.fireChannelRead(new SocksInitRequest(authSchemes)); - break; - } - default: { - throw new Error(); - } - } - ctx.pipeline().remove(this); - } - - enum State { - CHECK_PROTOCOL_VERSION, - READ_AUTH_SCHEMES - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksInitResponse.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksInitResponse.java deleted file mode 100644 index 3ae72c6881..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksInitResponse.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socks; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; - -/** - * An socks init response. - * - * @see SocksInitRequest - * @see SocksInitResponseDecoder - */ -public final class SocksInitResponse extends SocksResponse { - private final SocksAuthScheme authScheme; - - public SocksInitResponse(SocksAuthScheme authScheme) { - super(SocksResponseType.INIT); - requireNonNull(authScheme, "authScheme"); - this.authScheme = authScheme; - } - - /** - * Returns the {@link SocksAuthScheme} of this {@link SocksInitResponse} - * - * @return The {@link SocksAuthScheme} of this {@link SocksInitResponse} - */ - public SocksAuthScheme authScheme() { - return authScheme; - } - - @Override - public void encodeAsByteBuf(ByteBuf byteBuf) { - byteBuf.writeByte(protocolVersion().byteValue()); - byteBuf.writeByte(authScheme.byteValue()); - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksInitResponseDecoder.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksInitResponseDecoder.java deleted file mode 100644 index a457c066ce..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksInitResponseDecoder.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socks; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.ReplayingDecoder; -import io.netty.handler.codec.socks.SocksInitResponseDecoder.State; - -/** - * Decodes {@link ByteBuf}s into {@link SocksInitResponse}. - * Before returning SocksResponse decoder removes itself from pipeline. - */ -public class SocksInitResponseDecoder extends ReplayingDecoder { - - public SocksInitResponseDecoder() { - super(State.CHECK_PROTOCOL_VERSION); - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf) throws Exception { - switch (state()) { - case CHECK_PROTOCOL_VERSION: { - if (byteBuf.readByte() != SocksProtocolVersion.SOCKS5.byteValue()) { - ctx.fireChannelRead(SocksCommonUtils.UNKNOWN_SOCKS_RESPONSE); - break; - } - checkpoint(State.READ_PREFERRED_AUTH_TYPE); - } - case READ_PREFERRED_AUTH_TYPE: { - SocksAuthScheme authScheme = SocksAuthScheme.valueOf(byteBuf.readByte()); - ctx.fireChannelRead(new SocksInitResponse(authScheme)); - break; - } - default: { - throw new Error(); - } - } - ctx.pipeline().remove(this); - } - - enum State { - CHECK_PROTOCOL_VERSION, - READ_PREFERRED_AUTH_TYPE - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksMessage.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksMessage.java deleted file mode 100644 index d038d7e5d7..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksMessage.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socks; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; - -/** - * An abstract class that defines a SocksMessage, providing common properties for - * {@link SocksRequest} and {@link SocksResponse}. - * - * @see SocksRequest - * @see SocksResponse - */ - -public abstract class SocksMessage { - private final SocksMessageType type; - private final SocksProtocolVersion protocolVersion = SocksProtocolVersion.SOCKS5; - - protected SocksMessage(SocksMessageType type) { - requireNonNull(type, "type"); - this.type = type; - } - - /** - * Returns the {@link SocksMessageType} of this {@link SocksMessage} - * - * @return The {@link SocksMessageType} of this {@link SocksMessage} - */ - public SocksMessageType type() { - return type; - } - - /** - * Returns the {@link SocksProtocolVersion} of this {@link SocksMessage} - * - * @return The {@link SocksProtocolVersion} of this {@link SocksMessage} - */ - public SocksProtocolVersion protocolVersion() { - return protocolVersion; - } - - /** - * @deprecated Do not use; this method was intended for an internal use only. - */ - @Deprecated - public abstract void encodeAsByteBuf(ByteBuf byteBuf); -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksMessageEncoder.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksMessageEncoder.java deleted file mode 100644 index 44be82c4c8..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksMessageEncoder.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socks; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToByteEncoder; - -/** - * Encodes an {@link SocksMessage} into a {@link ByteBuf}. - * {@link MessageToByteEncoder} implementation. - * Use this with {@link SocksInitRequest}, {@link SocksInitResponse}, {@link SocksAuthRequest}, - * {@link SocksAuthResponse}, {@link SocksCmdRequest} and {@link SocksCmdResponse} - */ -@ChannelHandler.Sharable -public class SocksMessageEncoder extends MessageToByteEncoder { - @Override - @SuppressWarnings("deprecation") - protected void encode(ChannelHandlerContext ctx, SocksMessage msg, ByteBuf out) throws Exception { - msg.encodeAsByteBuf(out); - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksMessageType.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksMessageType.java deleted file mode 100644 index 5a98180e30..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksMessageType.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.socks; - -public enum SocksMessageType { - REQUEST, - RESPONSE, - UNKNOWN -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksProtocolVersion.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksProtocolVersion.java deleted file mode 100644 index 9e8f64326e..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksProtocolVersion.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.socks; - -public enum SocksProtocolVersion { - SOCKS4a((byte) 0x04), - SOCKS5((byte) 0x05), - UNKNOWN((byte) 0xff); - - private final byte b; - - SocksProtocolVersion(byte b) { - this.b = b; - } - - /** - * @deprecated Use {@link #valueOf(byte)} instead. - */ - @Deprecated - public static SocksProtocolVersion fromByte(byte b) { - return valueOf(b); - } - - public static SocksProtocolVersion valueOf(byte b) { - for (SocksProtocolVersion code : values()) { - if (code.b == b) { - return code; - } - } - return UNKNOWN; - } - - public byte byteValue() { - return b; - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksRequest.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksRequest.java deleted file mode 100644 index b16ca22142..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksRequest.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socks; - -import static java.util.Objects.requireNonNull; - -/** - * An abstract class that defines a SocksRequest, providing common properties for - * {@link SocksInitRequest}, {@link SocksAuthRequest}, {@link SocksCmdRequest} and {@link UnknownSocksRequest}. - * - * @see SocksInitRequest - * @see SocksAuthRequest - * @see SocksCmdRequest - * @see UnknownSocksRequest - */ -public abstract class SocksRequest extends SocksMessage { - private final SocksRequestType requestType; - - protected SocksRequest(SocksRequestType requestType) { - super(SocksMessageType.REQUEST); - requireNonNull(requestType, "requestType"); - this.requestType = requestType; - } - - /** - * Returns socks request type - * - * @return socks request type - */ - public SocksRequestType requestType() { - return requestType; - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksRequestType.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksRequestType.java deleted file mode 100644 index 74e3ff0f03..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksRequestType.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.socks; - -/** - * Type of socks request - */ -public enum SocksRequestType { - INIT, - AUTH, - CMD, - UNKNOWN -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksResponse.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksResponse.java deleted file mode 100644 index bc12499cb0..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksResponse.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socks; - -import static java.util.Objects.requireNonNull; - -/** - * An abstract class that defines a SocksResponse, providing common properties for - * {@link SocksInitResponse}, {@link SocksAuthResponse}, {@link SocksCmdResponse} and {@link UnknownSocksResponse}. - * - * @see SocksInitResponse - * @see SocksAuthResponse - * @see SocksCmdResponse - * @see UnknownSocksResponse - */ -public abstract class SocksResponse extends SocksMessage { - private final SocksResponseType responseType; - - protected SocksResponse(SocksResponseType responseType) { - super(SocksMessageType.RESPONSE); - requireNonNull(responseType, "responseType"); - this.responseType = responseType; - } - - /** - * Returns socks response type - * - * @return socks response type - */ - public SocksResponseType responseType() { - return responseType; - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksResponseType.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksResponseType.java deleted file mode 100644 index 8e85d931d8..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksResponseType.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.socks; - -/** - * Type of socks response - */ -public enum SocksResponseType { - INIT, - AUTH, - CMD, - UNKNOWN -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksSubnegotiationVersion.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksSubnegotiationVersion.java deleted file mode 100644 index fe7470da6e..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksSubnegotiationVersion.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.socks; - -public enum SocksSubnegotiationVersion { - AUTH_PASSWORD((byte) 0x01), - UNKNOWN((byte) 0xff); - - private final byte b; - - SocksSubnegotiationVersion(byte b) { - this.b = b; - } - - /** - * @deprecated Use {@link #valueOf(byte)} instead. - */ - @Deprecated - public static SocksSubnegotiationVersion fromByte(byte b) { - return valueOf(b); - } - - public static SocksSubnegotiationVersion valueOf(byte b) { - for (SocksSubnegotiationVersion code : values()) { - if (code.b == b) { - return code; - } - } - return UNKNOWN; - } - - public byte byteValue() { - return b; - } -} - diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/UnknownSocksRequest.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/UnknownSocksRequest.java deleted file mode 100644 index ec09504f19..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/UnknownSocksRequest.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socks; - -import io.netty.buffer.ByteBuf; - -/** - * An unknown socks request. - * - * @see SocksInitRequestDecoder - * @see SocksAuthRequestDecoder - * @see SocksCmdRequestDecoder - */ -public final class UnknownSocksRequest extends SocksRequest { - - public UnknownSocksRequest() { - super(SocksRequestType.UNKNOWN); - } - - @Override - public void encodeAsByteBuf(ByteBuf byteBuf) { - // NOOP - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/UnknownSocksResponse.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/UnknownSocksResponse.java deleted file mode 100644 index 3d0cf74306..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/UnknownSocksResponse.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socks; - -import io.netty.buffer.ByteBuf; - -/** - * An unknown socks response. - * - * @see SocksInitResponseDecoder - * @see SocksAuthResponseDecoder - * @see SocksCmdResponseDecoder - */ -public final class UnknownSocksResponse extends SocksResponse { - - public UnknownSocksResponse() { - super(SocksResponseType.UNKNOWN); - } - - @Override - public void encodeAsByteBuf(ByteBuf byteBuf) { - // NOOP - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/package-info.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/package-info.java deleted file mode 100644 index 95f4c12d61..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * Encoder, decoder and their related message types for Socks. - */ -package io.netty.handler.codec.socks; diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/AbstractSocksMessage.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/AbstractSocksMessage.java deleted file mode 100644 index cbd6c18a77..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/AbstractSocksMessage.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.socksx; - -import static java.util.Objects.requireNonNull; - -import io.netty.handler.codec.DecoderResult; - -/** - * An abstract {@link SocksMessage}. - */ -public abstract class AbstractSocksMessage implements SocksMessage { - - private DecoderResult decoderResult = DecoderResult.SUCCESS; - - @Override - public DecoderResult decoderResult() { - return decoderResult; - } - - @Override - public void setDecoderResult(DecoderResult decoderResult) { - requireNonNull(decoderResult, "decoderResult"); - this.decoderResult = decoderResult; - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/SocksMessage.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/SocksMessage.java deleted file mode 100755 index cc8d2aced1..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/SocksMessage.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socksx; - -import io.netty.handler.codec.DecoderResultProvider; - -/** - * An interface that all SOCKS protocol messages implement. - */ -public interface SocksMessage extends DecoderResultProvider { - - /** - * Returns the protocol version of this message. - */ - SocksVersion version(); -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/SocksPortUnificationServerHandler.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/SocksPortUnificationServerHandler.java deleted file mode 100644 index a57cbb75ee..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/SocksPortUnificationServerHandler.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.socksx; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.handler.codec.ByteToMessageDecoder; -import io.netty.handler.codec.socksx.v4.Socks4ServerDecoder; -import io.netty.handler.codec.socksx.v4.Socks4ServerEncoder; -import io.netty.handler.codec.socksx.v5.Socks5AddressEncoder; -import io.netty.handler.codec.socksx.v5.Socks5InitialRequestDecoder; -import io.netty.handler.codec.socksx.v5.Socks5ServerEncoder; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -/** - * Detects the version of the current SOCKS connection and initializes the pipeline with - * {@link Socks4ServerDecoder} or {@link Socks5InitialRequestDecoder}. - */ -public class SocksPortUnificationServerHandler extends ByteToMessageDecoder { - - private static final InternalLogger logger = - InternalLoggerFactory.getInstance(SocksPortUnificationServerHandler.class); - - private final Socks5ServerEncoder socks5encoder; - - /** - * Creates a new instance with the default configuration. - */ - public SocksPortUnificationServerHandler() { - this(Socks5ServerEncoder.DEFAULT); - } - - /** - * Creates a new instance with the specified {@link Socks5ServerEncoder}. - * This constructor is useful when a user wants to use an alternative {@link Socks5AddressEncoder}. - */ - public SocksPortUnificationServerHandler(Socks5ServerEncoder socks5encoder) { - requireNonNull(socks5encoder, "socks5encoder"); - - this.socks5encoder = socks5encoder; - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - final int readerIndex = in.readerIndex(); - if (in.writerIndex() == readerIndex) { - return; - } - - ChannelPipeline p = ctx.pipeline(); - final byte versionVal = in.getByte(readerIndex); - SocksVersion version = SocksVersion.valueOf(versionVal); - - switch (version) { - case SOCKS4a: - logKnownVersion(ctx, version); - p.addAfter(ctx.name(), null, Socks4ServerEncoder.INSTANCE); - p.addAfter(ctx.name(), null, new Socks4ServerDecoder()); - break; - case SOCKS5: - logKnownVersion(ctx, version); - p.addAfter(ctx.name(), null, socks5encoder); - p.addAfter(ctx.name(), null, new Socks5InitialRequestDecoder()); - break; - default: - logUnknownVersion(ctx, versionVal); - in.skipBytes(in.readableBytes()); - ctx.close(); - return; - } - - p.remove(this); - } - - private static void logKnownVersion(ChannelHandlerContext ctx, SocksVersion version) { - logger.debug("{} Protocol version: {}({})", ctx.channel(), version); - } - - private static void logUnknownVersion(ChannelHandlerContext ctx, byte versionVal) { - if (logger.isDebugEnabled()) { - logger.debug("{} Unknown protocol version: {}", ctx.channel(), versionVal & 0xFF); - } - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/SocksVersion.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/SocksVersion.java deleted file mode 100755 index 0a2c40942a..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/SocksVersion.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.socksx; - -/** - * The version of SOCKS protocol. - */ -public enum SocksVersion { - /** - * SOCKS protocol version 4a (or 4) - */ - SOCKS4a((byte) 0x04), - /** - * SOCKS protocol version 5 - */ - SOCKS5((byte) 0x05), - /** - * Unknown protocol version - */ - UNKNOWN((byte) 0xff); - - /** - * Returns the {@link SocksVersion} that corresponds to the specified version field value, - * as defined in the protocol specification. - * - * @return {@link #UNKNOWN} if the specified value does not represent a known SOCKS protocol version - */ - public static SocksVersion valueOf(byte b) { - if (b == SOCKS4a.byteValue()) { - return SOCKS4a; - } - if (b == SOCKS5.byteValue()) { - return SOCKS5; - } - return UNKNOWN; - } - - private final byte b; - - SocksVersion(byte b) { - this.b = b; - } - - /** - * Returns the value of the version field, as defined in the protocol specification. - */ - public byte byteValue() { - return b; - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/package-info.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/package-info.java deleted file mode 100755 index cda76655ab..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * Encoder, decoder and their related message types for SOCKS protocol. - */ -package io.netty.handler.codec.socksx; diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v4/AbstractSocks4Message.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v4/AbstractSocks4Message.java deleted file mode 100644 index 1d65d64c8a..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v4/AbstractSocks4Message.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.socksx.v4; - -import io.netty.handler.codec.socksx.AbstractSocksMessage; -import io.netty.handler.codec.socksx.SocksVersion; - -/** - * An abstract {@link Socks4Message}. - */ -public abstract class AbstractSocks4Message extends AbstractSocksMessage implements Socks4Message { - @Override - public final SocksVersion version() { - return SocksVersion.SOCKS4a; - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v4/DefaultSocks4CommandRequest.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v4/DefaultSocks4CommandRequest.java deleted file mode 100755 index 5fb883a77f..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v4/DefaultSocks4CommandRequest.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socksx.v4; - -import static java.util.Objects.requireNonNull; - -import io.netty.handler.codec.DecoderResult; -import io.netty.util.internal.StringUtil; - -import java.net.IDN; - -/** - * The default {@link Socks4CommandRequest}. - */ -public class DefaultSocks4CommandRequest extends AbstractSocks4Message implements Socks4CommandRequest { - - private final Socks4CommandType type; - private final String dstAddr; - private final int dstPort; - private final String userId; - - /** - * Creates a new instance. - * - * @param type the type of the request - * @param dstAddr the {@code DSTIP} field of the request - * @param dstPort the {@code DSTPORT} field of the request - */ - public DefaultSocks4CommandRequest(Socks4CommandType type, String dstAddr, int dstPort) { - this(type, dstAddr, dstPort, ""); - } - - /** - * Creates a new instance. - * - * @param type the type of the request - * @param dstAddr the {@code DSTIP} field of the request - * @param dstPort the {@code DSTPORT} field of the request - * @param userId the {@code USERID} field of the request - */ - public DefaultSocks4CommandRequest(Socks4CommandType type, String dstAddr, int dstPort, String userId) { - requireNonNull(type, "type"); - requireNonNull(dstAddr, "dstAddr"); - if (dstPort <= 0 || dstPort >= 65536) { - throw new IllegalArgumentException("dstPort: " + dstPort + " (expected: 1~65535)"); - } - requireNonNull(userId, "userId"); - - this.userId = userId; - this.type = type; - this.dstAddr = IDN.toASCII(dstAddr); - this.dstPort = dstPort; - } - - @Override - public Socks4CommandType type() { - return type; - } - - @Override - public String dstAddr() { - return dstAddr; - } - - @Override - public int dstPort() { - return dstPort; - } - - @Override - public String userId() { - return userId; - } - - @Override - public String toString() { - StringBuilder buf = new StringBuilder(128); - buf.append(StringUtil.simpleClassName(this)); - - DecoderResult decoderResult = decoderResult(); - if (!decoderResult.isSuccess()) { - buf.append("(decoderResult: "); - buf.append(decoderResult); - buf.append(", type: "); - } else { - buf.append("(type: "); - } - buf.append(type()); - buf.append(", dstAddr: "); - buf.append(dstAddr()); - buf.append(", dstPort: "); - buf.append(dstPort()); - buf.append(", userId: "); - buf.append(userId()); - buf.append(')'); - - return buf.toString(); - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v4/DefaultSocks4CommandResponse.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v4/DefaultSocks4CommandResponse.java deleted file mode 100755 index e4a9ba28d8..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v4/DefaultSocks4CommandResponse.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socksx.v4; - -import static java.util.Objects.requireNonNull; - -import io.netty.handler.codec.DecoderResult; -import io.netty.util.NetUtil; -import io.netty.util.internal.StringUtil; - -/** - * The default {@link Socks4CommandResponse}. - */ -public class DefaultSocks4CommandResponse extends AbstractSocks4Message implements Socks4CommandResponse { - - private final Socks4CommandStatus status; - private final String dstAddr; - private final int dstPort; - - /** - * Creates a new instance. - * - * @param status the status of the response - */ - public DefaultSocks4CommandResponse(Socks4CommandStatus status) { - this(status, null, 0); - } - - /** - * Creates a new instance. - * - * @param status the status of the response - * @param dstAddr the {@code DSTIP} field of the response - * @param dstPort the {@code DSTPORT} field of the response - */ - public DefaultSocks4CommandResponse(Socks4CommandStatus status, String dstAddr, int dstPort) { - requireNonNull(status, "status"); - if (dstAddr != null) { - if (!NetUtil.isValidIpV4Address(dstAddr)) { - throw new IllegalArgumentException( - "dstAddr: " + dstAddr + " (expected: a valid IPv4 address)"); - } - } - if (dstPort < 0 || dstPort > 65535) { - throw new IllegalArgumentException("dstPort: " + dstPort + " (expected: 0~65535)"); - } - - this.status = status; - this.dstAddr = dstAddr; - this.dstPort = dstPort; - } - - @Override - public Socks4CommandStatus status() { - return status; - } - - @Override - public String dstAddr() { - return dstAddr; - } - - @Override - public int dstPort() { - return dstPort; - } - - @Override - public String toString() { - StringBuilder buf = new StringBuilder(96); - buf.append(StringUtil.simpleClassName(this)); - - DecoderResult decoderResult = decoderResult(); - if (!decoderResult.isSuccess()) { - buf.append("(decoderResult: "); - buf.append(decoderResult); - buf.append(", dstAddr: "); - } else { - buf.append("(dstAddr: "); - } - buf.append(dstAddr()); - buf.append(", dstPort: "); - buf.append(dstPort()); - buf.append(')'); - - return buf.toString(); - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v4/Socks4ClientDecoder.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v4/Socks4ClientDecoder.java deleted file mode 100755 index fd23220bc0..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v4/Socks4ClientDecoder.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socksx.v4; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.DecoderException; -import io.netty.handler.codec.DecoderResult; -import io.netty.handler.codec.ReplayingDecoder; -import io.netty.handler.codec.socksx.v4.Socks4ClientDecoder.State; -import io.netty.util.NetUtil; - -/** - * Decodes a single {@link Socks4CommandResponse} from the inbound {@link ByteBuf}s. - * On successful decode, this decoder will forward the received data to the next handler, so that - * other handler can remove this decoder later. On failed decode, this decoder will discard the - * received data, so that other handler closes the connection later. - */ -public class Socks4ClientDecoder extends ReplayingDecoder { - - enum State { - START, - SUCCESS, - FAILURE - } - - public Socks4ClientDecoder() { - super(State.START); - setSingleDecode(true); - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - try { - switch (state()) { - case START: { - final int version = in.readUnsignedByte(); - if (version != 0) { - throw new DecoderException("unsupported reply version: " + version + " (expected: 0)"); - } - - final Socks4CommandStatus status = Socks4CommandStatus.valueOf(in.readByte()); - final int dstPort = in.readUnsignedShort(); - final String dstAddr = NetUtil.intToIpAddress(in.readInt()); - - ctx.fireChannelRead(new DefaultSocks4CommandResponse(status, dstAddr, dstPort)); - checkpoint(State.SUCCESS); - } - case SUCCESS: { - int readableBytes = actualReadableBytes(); - if (readableBytes > 0) { - ctx.fireChannelRead(in.readRetainedSlice(readableBytes)); - } - break; - } - case FAILURE: { - in.skipBytes(actualReadableBytes()); - break; - } - } - } catch (Exception e) { - fail(ctx, e); - } - } - - private void fail(ChannelHandlerContext ctx, Exception cause) { - if (!(cause instanceof DecoderException)) { - cause = new DecoderException(cause); - } - - Socks4CommandResponse m = new DefaultSocks4CommandResponse(Socks4CommandStatus.REJECTED_OR_FAILED); - m.setDecoderResult(DecoderResult.failure(cause)); - ctx.fireChannelRead(m); - - checkpoint(State.FAILURE); - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v4/Socks4ClientEncoder.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v4/Socks4ClientEncoder.java deleted file mode 100644 index ef44716516..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v4/Socks4ClientEncoder.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.socksx.v4; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToByteEncoder; -import io.netty.util.NetUtil; - -/** - * Encodes a {@link Socks4CommandRequest} into a {@link ByteBuf}. - */ -@Sharable -public final class Socks4ClientEncoder extends MessageToByteEncoder { - - /** - * The singleton instance of {@link Socks4ClientEncoder} - */ - public static final Socks4ClientEncoder INSTANCE = new Socks4ClientEncoder(); - - private static final byte[] IPv4_DOMAIN_MARKER = {0x00, 0x00, 0x00, 0x01}; - - private Socks4ClientEncoder() { } - - @Override - protected void encode(ChannelHandlerContext ctx, Socks4CommandRequest msg, ByteBuf out) throws Exception { - out.writeByte(msg.version().byteValue()); - out.writeByte(msg.type().byteValue()); - out.writeShort(msg.dstPort()); - if (NetUtil.isValidIpV4Address(msg.dstAddr())) { - out.writeBytes(NetUtil.createByteArrayFromIpAddressString(msg.dstAddr())); - ByteBufUtil.writeAscii(out, msg.userId()); - out.writeByte(0); - } else { - out.writeBytes(IPv4_DOMAIN_MARKER); - ByteBufUtil.writeAscii(out, msg.userId()); - out.writeByte(0); - ByteBufUtil.writeAscii(out, msg.dstAddr()); - out.writeByte(0); - } - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v4/Socks4CommandRequest.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v4/Socks4CommandRequest.java deleted file mode 100755 index 167edf30f9..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v4/Socks4CommandRequest.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socksx.v4; - -/** - * A SOCKS4a {@code CONNECT} or {@code BIND} request. - */ -public interface Socks4CommandRequest extends Socks4Message { - - /** - * Returns the type of this request. - */ - Socks4CommandType type(); - - /** - * Returns the {@code USERID} field of this request. - */ - String userId(); - - /** - * Returns the {@code DSTIP} field of this request. - */ - String dstAddr(); - - /** - * Returns the {@code DSTPORT} field of this request. - */ - int dstPort(); -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v4/Socks4CommandResponse.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v4/Socks4CommandResponse.java deleted file mode 100755 index b37e85a38d..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v4/Socks4CommandResponse.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socksx.v4; - -/** - * A SOCKS4a response. - */ -public interface Socks4CommandResponse extends Socks4Message { - - /** - * Returns the status of this response. - */ - Socks4CommandStatus status(); - - /** - * Returns the {@code DSTIP} field of this response. - */ - String dstAddr(); - - /** - * Returns the {@code DSTPORT} field of this response. - */ - int dstPort(); -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v4/Socks4CommandStatus.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v4/Socks4CommandStatus.java deleted file mode 100755 index 22bf275e7e..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v4/Socks4CommandStatus.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socksx.v4; - -import static java.util.Objects.requireNonNull; - -/** - * The status of {@link Socks4CommandResponse}. - */ -public class Socks4CommandStatus implements Comparable { - - public static final Socks4CommandStatus SUCCESS = new Socks4CommandStatus(0x5a, "SUCCESS"); - public static final Socks4CommandStatus REJECTED_OR_FAILED = new Socks4CommandStatus(0x5b, "REJECTED_OR_FAILED"); - public static final Socks4CommandStatus IDENTD_UNREACHABLE = new Socks4CommandStatus(0x5c, "IDENTD_UNREACHABLE"); - public static final Socks4CommandStatus IDENTD_AUTH_FAILURE = new Socks4CommandStatus(0x5d, "IDENTD_AUTH_FAILURE"); - - public static Socks4CommandStatus valueOf(byte b) { - switch (b) { - case 0x5a: - return SUCCESS; - case 0x5b: - return REJECTED_OR_FAILED; - case 0x5c: - return IDENTD_UNREACHABLE; - case 0x5d: - return IDENTD_AUTH_FAILURE; - } - - return new Socks4CommandStatus(b); - } - - private final byte byteValue; - private final String name; - private String text; - - public Socks4CommandStatus(int byteValue) { - this(byteValue, "UNKNOWN"); - } - - public Socks4CommandStatus(int byteValue, String name) { - requireNonNull(name, "name"); - - this.byteValue = (byte) byteValue; - this.name = name; - } - - public byte byteValue() { - return byteValue; - } - - public boolean isSuccess() { - return byteValue == 0x5a; - } - - @Override - public int hashCode() { - return byteValue; - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof Socks4CommandStatus)) { - return false; - } - - return byteValue == ((Socks4CommandStatus) obj).byteValue; - } - - @Override - public int compareTo(Socks4CommandStatus o) { - return byteValue - o.byteValue; - } - - @Override - public String toString() { - String text = this.text; - if (text == null) { - this.text = text = name + '(' + (byteValue & 0xFF) + ')'; - } - return text; - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v4/Socks4CommandType.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v4/Socks4CommandType.java deleted file mode 100755 index c57d3d52df..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v4/Socks4CommandType.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socksx.v4; - -import static java.util.Objects.requireNonNull; - -/** - * The type of {@link Socks4CommandRequest}. - */ -public class Socks4CommandType implements Comparable { - - public static final Socks4CommandType CONNECT = new Socks4CommandType(0x01, "CONNECT"); - public static final Socks4CommandType BIND = new Socks4CommandType(0x02, "BIND"); - - public static Socks4CommandType valueOf(byte b) { - switch (b) { - case 0x01: - return CONNECT; - case 0x02: - return BIND; - } - - return new Socks4CommandType(b); - } - - private final byte byteValue; - private final String name; - private String text; - - public Socks4CommandType(int byteValue) { - this(byteValue, "UNKNOWN"); - } - - public Socks4CommandType(int byteValue, String name) { - requireNonNull(name, "name"); - this.byteValue = (byte) byteValue; - this.name = name; - } - - public byte byteValue() { - return byteValue; - } - - @Override - public int hashCode() { - return byteValue; - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof Socks4CommandType)) { - return false; - } - - return byteValue == ((Socks4CommandType) obj).byteValue; - } - - @Override - public int compareTo(Socks4CommandType o) { - return byteValue - o.byteValue; - } - - @Override - public String toString() { - String text = this.text; - if (text == null) { - this.text = text = name + '(' + (byteValue & 0xFF) + ')'; - } - return text; - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v4/Socks4Message.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v4/Socks4Message.java deleted file mode 100644 index b02558328e..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v4/Socks4Message.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.socksx.v4; - -import io.netty.handler.codec.socksx.SocksMessage; - -/** - * A tag interface that all SOCKS4a protocol messages implement. - */ -public interface Socks4Message extends SocksMessage { - // Tag interface -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v4/Socks4ServerDecoder.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v4/Socks4ServerDecoder.java deleted file mode 100755 index 433469ddfb..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v4/Socks4ServerDecoder.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socksx.v4; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.DecoderException; -import io.netty.handler.codec.DecoderResult; -import io.netty.handler.codec.ReplayingDecoder; -import io.netty.handler.codec.socksx.SocksVersion; -import io.netty.handler.codec.socksx.v4.Socks4ServerDecoder.State; -import io.netty.util.CharsetUtil; -import io.netty.util.NetUtil; - -/** - * Decodes a single {@link Socks4CommandRequest} from the inbound {@link ByteBuf}s. - * On successful decode, this decoder will forward the received data to the next handler, so that - * other handler can remove this decoder later. On failed decode, this decoder will discard the - * received data, so that other handler closes the connection later. - */ -public class Socks4ServerDecoder extends ReplayingDecoder { - - private static final int MAX_FIELD_LENGTH = 255; - - enum State { - START, - READ_USERID, - READ_DOMAIN, - SUCCESS, - FAILURE - } - - private Socks4CommandType type; - private String dstAddr; - private int dstPort; - private String userId; - - public Socks4ServerDecoder() { - super(State.START); - setSingleDecode(true); - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - try { - switch (state()) { - case START: { - final int version = in.readUnsignedByte(); - if (version != SocksVersion.SOCKS4a.byteValue()) { - throw new DecoderException("unsupported protocol version: " + version); - } - - type = Socks4CommandType.valueOf(in.readByte()); - dstPort = in.readUnsignedShort(); - dstAddr = NetUtil.intToIpAddress(in.readInt()); - checkpoint(State.READ_USERID); - } - case READ_USERID: { - userId = readString("userid", in); - checkpoint(State.READ_DOMAIN); - } - case READ_DOMAIN: { - // Check for Socks4a protocol marker 0.0.0.x - if (!"0.0.0.0".equals(dstAddr) && dstAddr.startsWith("0.0.0.")) { - dstAddr = readString("dstAddr", in); - } - ctx.fireChannelRead(new DefaultSocks4CommandRequest(type, dstAddr, dstPort, userId)); - checkpoint(State.SUCCESS); - } - case SUCCESS: { - int readableBytes = actualReadableBytes(); - if (readableBytes > 0) { - ctx.fireChannelRead(in.readRetainedSlice(readableBytes)); - } - break; - } - case FAILURE: { - in.skipBytes(actualReadableBytes()); - break; - } - } - } catch (Exception e) { - fail(ctx, e); - } - } - - private void fail(ChannelHandlerContext ctx, Exception cause) { - if (!(cause instanceof DecoderException)) { - cause = new DecoderException(cause); - } - - Socks4CommandRequest m = new DefaultSocks4CommandRequest( - type != null? type : Socks4CommandType.CONNECT, - dstAddr != null? dstAddr : "", - dstPort != 0? dstPort : 65535, - userId != null? userId : ""); - - m.setDecoderResult(DecoderResult.failure(cause)); - ctx.fireChannelRead(m); - - checkpoint(State.FAILURE); - } - - /** - * Reads a variable-length NUL-terminated string as defined in SOCKS4. - */ - private static String readString(String fieldName, ByteBuf in) { - int length = in.bytesBefore(MAX_FIELD_LENGTH + 1, (byte) 0); - if (length < 0) { - throw new DecoderException("field '" + fieldName + "' longer than " + MAX_FIELD_LENGTH + " chars"); - } - - String value = in.readSlice(length).toString(CharsetUtil.US_ASCII); - in.skipBytes(1); // Skip the NUL. - - return value; - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v4/Socks4ServerEncoder.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v4/Socks4ServerEncoder.java deleted file mode 100644 index 0551a2f2d8..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v4/Socks4ServerEncoder.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.socksx.v4; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToByteEncoder; -import io.netty.util.NetUtil; - -/** - * Encodes a {@link Socks4CommandResponse} into a {@link ByteBuf}. - */ -@Sharable -public final class Socks4ServerEncoder extends MessageToByteEncoder { - - public static final Socks4ServerEncoder INSTANCE = new Socks4ServerEncoder(); - - private static final byte[] IPv4_HOSTNAME_ZEROED = { 0x00, 0x00, 0x00, 0x00 }; - - private Socks4ServerEncoder() { } - - @Override - protected void encode(ChannelHandlerContext ctx, Socks4CommandResponse msg, ByteBuf out) throws Exception { - out.writeByte(0); - out.writeByte(msg.status().byteValue()); - out.writeShort(msg.dstPort()); - out.writeBytes(msg.dstAddr() == null? IPv4_HOSTNAME_ZEROED - : NetUtil.createByteArrayFromIpAddressString(msg.dstAddr())); - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v4/package-info.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v4/package-info.java deleted file mode 100755 index dffd8537b8..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v4/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * Encoder, decoder and their related message types for SOCKSv4 protocol. - */ -package io.netty.handler.codec.socksx.v4; diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/AbstractSocks5Message.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/AbstractSocks5Message.java deleted file mode 100644 index d69c7d05ee..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/AbstractSocks5Message.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.socksx.v5; - -import io.netty.handler.codec.socksx.AbstractSocksMessage; -import io.netty.handler.codec.socksx.SocksVersion; - -/** - * An abstract {@link Socks5Message}. - */ -public abstract class AbstractSocks5Message extends AbstractSocksMessage implements Socks5Message { - @Override - public final SocksVersion version() { - return SocksVersion.SOCKS5; - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/DefaultSocks5CommandRequest.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/DefaultSocks5CommandRequest.java deleted file mode 100755 index b037db880e..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/DefaultSocks5CommandRequest.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socksx.v5; - -import static java.util.Objects.requireNonNull; - -import io.netty.handler.codec.DecoderResult; -import io.netty.util.NetUtil; -import io.netty.util.internal.StringUtil; - -import java.net.IDN; - -/** - * The default {@link Socks5CommandRequest}. - */ -public final class DefaultSocks5CommandRequest extends AbstractSocks5Message implements Socks5CommandRequest { - - private final Socks5CommandType type; - private final Socks5AddressType dstAddrType; - private final String dstAddr; - private final int dstPort; - - public DefaultSocks5CommandRequest( - Socks5CommandType type, Socks5AddressType dstAddrType, String dstAddr, int dstPort) { - requireNonNull(type, "type"); - requireNonNull(dstAddrType, "dstAddrType"); - requireNonNull(dstAddr, "dstAddr"); - - if (dstAddrType == Socks5AddressType.IPv4) { - if (!NetUtil.isValidIpV4Address(dstAddr)) { - throw new IllegalArgumentException("dstAddr: " + dstAddr + " (expected: a valid IPv4 address)"); - } - } else if (dstAddrType == Socks5AddressType.DOMAIN) { - dstAddr = IDN.toASCII(dstAddr); - if (dstAddr.length() > 255) { - throw new IllegalArgumentException("dstAddr: " + dstAddr + " (expected: less than 256 chars)"); - } - } else if (dstAddrType == Socks5AddressType.IPv6) { - if (!NetUtil.isValidIpV6Address(dstAddr)) { - throw new IllegalArgumentException("dstAddr: " + dstAddr + " (expected: a valid IPv6 address"); - } - } - - if (dstPort < 0 || dstPort > 65535) { - throw new IllegalArgumentException("dstPort: " + dstPort + " (expected: 0~65535)"); - } - - this.type = type; - this.dstAddrType = dstAddrType; - this.dstAddr = dstAddr; - this.dstPort = dstPort; - } - - @Override - public Socks5CommandType type() { - return type; - } - - @Override - public Socks5AddressType dstAddrType() { - return dstAddrType; - } - - @Override - public String dstAddr() { - return dstAddr; - } - - @Override - public int dstPort() { - return dstPort; - } - - @Override - public String toString() { - StringBuilder buf = new StringBuilder(128); - buf.append(StringUtil.simpleClassName(this)); - - DecoderResult decoderResult = decoderResult(); - if (!decoderResult.isSuccess()) { - buf.append("(decoderResult: "); - buf.append(decoderResult); - buf.append(", type: "); - } else { - buf.append("(type: "); - } - buf.append(type()); - buf.append(", dstAddrType: "); - buf.append(dstAddrType()); - buf.append(", dstAddr: "); - buf.append(dstAddr()); - buf.append(", dstPort: "); - buf.append(dstPort()); - buf.append(')'); - - return buf.toString(); - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/DefaultSocks5CommandResponse.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/DefaultSocks5CommandResponse.java deleted file mode 100755 index 7c2f5247c3..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/DefaultSocks5CommandResponse.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socksx.v5; - -import static java.util.Objects.requireNonNull; - -import io.netty.handler.codec.DecoderResult; -import io.netty.util.NetUtil; -import io.netty.util.internal.StringUtil; - -import java.net.IDN; - -/** - * The default {@link Socks5CommandResponse}. - */ -public final class DefaultSocks5CommandResponse extends AbstractSocks5Message implements Socks5CommandResponse { - - private final Socks5CommandStatus status; - private final Socks5AddressType bndAddrType; - private final String bndAddr; - private final int bndPort; - - public DefaultSocks5CommandResponse(Socks5CommandStatus status, Socks5AddressType bndAddrType) { - this(status, bndAddrType, null, 0); - } - - public DefaultSocks5CommandResponse( - Socks5CommandStatus status, Socks5AddressType bndAddrType, String bndAddr, int bndPort) { - requireNonNull(status, "status"); - requireNonNull(bndAddrType, "bndAddrType"); - if (bndAddr != null) { - if (bndAddrType == Socks5AddressType.IPv4) { - if (!NetUtil.isValidIpV4Address(bndAddr)) { - throw new IllegalArgumentException("bndAddr: " + bndAddr + " (expected: a valid IPv4 address)"); - } - } else if (bndAddrType == Socks5AddressType.DOMAIN) { - bndAddr = IDN.toASCII(bndAddr); - if (bndAddr.length() > 255) { - throw new IllegalArgumentException("bndAddr: " + bndAddr + " (expected: less than 256 chars)"); - } - } else if (bndAddrType == Socks5AddressType.IPv6) { - if (!NetUtil.isValidIpV6Address(bndAddr)) { - throw new IllegalArgumentException("bndAddr: " + bndAddr + " (expected: a valid IPv6 address)"); - } - } - } - - if (bndPort < 0 || bndPort > 65535) { - throw new IllegalArgumentException("bndPort: " + bndPort + " (expected: 0~65535)"); - } - this.status = status; - this.bndAddrType = bndAddrType; - this.bndAddr = bndAddr; - this.bndPort = bndPort; - } - - @Override - public Socks5CommandStatus status() { - return status; - } - - @Override - public Socks5AddressType bndAddrType() { - return bndAddrType; - } - - @Override - public String bndAddr() { - return bndAddr; - } - - @Override - public int bndPort() { - return bndPort; - } - - @Override - public String toString() { - StringBuilder buf = new StringBuilder(128); - buf.append(StringUtil.simpleClassName(this)); - - DecoderResult decoderResult = decoderResult(); - if (!decoderResult.isSuccess()) { - buf.append("(decoderResult: "); - buf.append(decoderResult); - buf.append(", status: "); - } else { - buf.append("(status: "); - } - buf.append(status()); - buf.append(", bndAddrType: "); - buf.append(bndAddrType()); - buf.append(", bndAddr: "); - buf.append(bndAddr()); - buf.append(", bndPort: "); - buf.append(bndPort()); - buf.append(')'); - - return buf.toString(); - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/DefaultSocks5InitialRequest.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/DefaultSocks5InitialRequest.java deleted file mode 100755 index 16415dcc1f..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/DefaultSocks5InitialRequest.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socksx.v5; - -import static java.util.Objects.requireNonNull; -import static io.netty.util.internal.ObjectUtil.checkNonEmpty; - -import io.netty.handler.codec.DecoderResult; -import io.netty.util.internal.StringUtil; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * The default {@link Socks5InitialRequest}. - */ -public class DefaultSocks5InitialRequest extends AbstractSocks5Message implements Socks5InitialRequest { - - private final List authMethods; - - public DefaultSocks5InitialRequest(Socks5AuthMethod... authMethods) { - requireNonNull(authMethods, "authMethods"); - - List list = new ArrayList<>(authMethods.length); - for (Socks5AuthMethod m: authMethods) { - if (m == null) { - break; - } - list.add(m); - } - - this.authMethods = Collections.unmodifiableList(checkNonEmpty(list, "list")); - } - - public DefaultSocks5InitialRequest(Iterable authMethods) { - requireNonNull(authMethods, "authMethods"); - - List list = new ArrayList<>(); - for (Socks5AuthMethod m: authMethods) { - if (m == null) { - break; - } - list.add(m); - } - - this.authMethods = Collections.unmodifiableList(checkNonEmpty(list, "list")); - } - - @Override - public List authMethods() { - return authMethods; - } - - @Override - public String toString() { - StringBuilder buf = new StringBuilder(StringUtil.simpleClassName(this)); - - DecoderResult decoderResult = decoderResult(); - if (!decoderResult.isSuccess()) { - buf.append("(decoderResult: "); - buf.append(decoderResult); - buf.append(", authMethods: "); - } else { - buf.append("(authMethods: "); - } - buf.append(authMethods()); - buf.append(')'); - - return buf.toString(); - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/DefaultSocks5InitialResponse.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/DefaultSocks5InitialResponse.java deleted file mode 100755 index 5a66b2e1fa..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/DefaultSocks5InitialResponse.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socksx.v5; - -import static java.util.Objects.requireNonNull; - -import io.netty.handler.codec.DecoderResult; -import io.netty.util.internal.StringUtil; - -/** - * The default {@link Socks5InitialResponse}. - */ -public class DefaultSocks5InitialResponse extends AbstractSocks5Message implements Socks5InitialResponse { - - private final Socks5AuthMethod authMethod; - - public DefaultSocks5InitialResponse(Socks5AuthMethod authMethod) { - requireNonNull(authMethod, "authMethod"); - this.authMethod = authMethod; - } - - @Override - public Socks5AuthMethod authMethod() { - return authMethod; - } - - @Override - public String toString() { - StringBuilder buf = new StringBuilder(StringUtil.simpleClassName(this)); - - DecoderResult decoderResult = decoderResult(); - if (!decoderResult.isSuccess()) { - buf.append("(decoderResult: "); - buf.append(decoderResult); - buf.append(", authMethod: "); - } else { - buf.append("(authMethod: "); - } - buf.append(authMethod()); - buf.append(')'); - - return buf.toString(); - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/DefaultSocks5PasswordAuthRequest.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/DefaultSocks5PasswordAuthRequest.java deleted file mode 100755 index 21fd623e82..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/DefaultSocks5PasswordAuthRequest.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socksx.v5; - -import static java.util.Objects.requireNonNull; - -import io.netty.handler.codec.DecoderResult; -import io.netty.util.internal.StringUtil; - -/** - * The default {@link Socks5PasswordAuthRequest}. - */ -public class DefaultSocks5PasswordAuthRequest extends AbstractSocks5Message implements Socks5PasswordAuthRequest { - - private final String username; - private final String password; - - public DefaultSocks5PasswordAuthRequest(String username, String password) { - requireNonNull(username, "username"); - requireNonNull(password, "password"); - - if (username.length() > 255) { - throw new IllegalArgumentException("username: **** (expected: less than 256 chars)"); - } - if (password.length() > 255) { - throw new IllegalArgumentException("password: **** (expected: less than 256 chars)"); - } - - this.username = username; - this.password = password; - } - - @Override - public String username() { - return username; - } - - @Override - public String password() { - return password; - } - - @Override - public String toString() { - StringBuilder buf = new StringBuilder(StringUtil.simpleClassName(this)); - - DecoderResult decoderResult = decoderResult(); - if (!decoderResult.isSuccess()) { - buf.append("(decoderResult: "); - buf.append(decoderResult); - buf.append(", username: "); - } else { - buf.append("(username: "); - } - buf.append(username()); - buf.append(", password: ****)"); - - return buf.toString(); - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/DefaultSocks5PasswordAuthResponse.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/DefaultSocks5PasswordAuthResponse.java deleted file mode 100755 index 79fa051d6f..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/DefaultSocks5PasswordAuthResponse.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socksx.v5; - -import static java.util.Objects.requireNonNull; - -import io.netty.handler.codec.DecoderResult; -import io.netty.util.internal.StringUtil; - -/** - * The default {@link Socks5PasswordAuthResponse}. - */ -public class DefaultSocks5PasswordAuthResponse extends AbstractSocks5Message implements Socks5PasswordAuthResponse { - - private final Socks5PasswordAuthStatus status; - - public DefaultSocks5PasswordAuthResponse(Socks5PasswordAuthStatus status) { - requireNonNull(status, "status"); - - this.status = status; - } - - @Override - public Socks5PasswordAuthStatus status() { - return status; - } - - @Override - public String toString() { - StringBuilder buf = new StringBuilder(StringUtil.simpleClassName(this)); - - DecoderResult decoderResult = decoderResult(); - if (!decoderResult.isSuccess()) { - buf.append("(decoderResult: "); - buf.append(decoderResult); - buf.append(", status: "); - } else { - buf.append("(status: "); - } - buf.append(status()); - buf.append(')'); - - return buf.toString(); - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5AddressDecoder.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5AddressDecoder.java deleted file mode 100644 index 348367aa15..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5AddressDecoder.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.socksx.v5; - -import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.DecoderException; -import io.netty.util.CharsetUtil; -import io.netty.util.NetUtil; - -/** - * Decodes a SOCKS5 address field into its string representation. - * - * @see Socks5CommandRequestDecoder - * @see Socks5CommandResponseDecoder - */ -public interface Socks5AddressDecoder { - - Socks5AddressDecoder DEFAULT = new Socks5AddressDecoder() { - - private static final int IPv6_LEN = 16; - - @Override - public String decodeAddress(Socks5AddressType addrType, ByteBuf in) throws Exception { - if (addrType == Socks5AddressType.IPv4) { - return NetUtil.intToIpAddress(in.readInt()); - } - if (addrType == Socks5AddressType.DOMAIN) { - final int length = in.readUnsignedByte(); - final String domain = in.toString(in.readerIndex(), length, CharsetUtil.US_ASCII); - in.skipBytes(length); - return domain; - } - if (addrType == Socks5AddressType.IPv6) { - if (in.hasArray()) { - final int readerIdx = in.readerIndex(); - in.readerIndex(readerIdx + IPv6_LEN); - return NetUtil.bytesToIpAddress(in.array(), in.arrayOffset() + readerIdx, IPv6_LEN); - } else { - byte[] tmp = new byte[IPv6_LEN]; - in.readBytes(tmp); - return NetUtil.bytesToIpAddress(tmp); - } - } else { - throw new DecoderException("unsupported address type: " + (addrType.byteValue() & 0xFF)); - } - } - }; - - /** - * Decodes a SOCKS5 address field into its string representation. - * - * @param addrType the type of the address - * @param in the input buffer which contains the SOCKS5 address field at its reader index - */ - String decodeAddress(Socks5AddressType addrType, ByteBuf in) throws Exception; -} 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 deleted file mode 100644 index 334fa01ec6..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5AddressEncoder.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.socksx.v5; - -import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.EncoderException; -import io.netty.util.CharsetUtil; -import io.netty.util.NetUtil; - -/** - * Encodes a SOCKS5 address into binary representation. - * - * @see Socks5ClientEncoder - * @see Socks5ServerEncoder - */ -public interface Socks5AddressEncoder { - - Socks5AddressEncoder DEFAULT = (addrType, addrValue, out) -> { - final byte typeVal = addrType.byteValue(); - if (typeVal == Socks5AddressType.IPv4.byteValue()) { - if (addrValue != null) { - out.writeBytes(NetUtil.createByteArrayFromIpAddressString(addrValue)); - } else { - out.writeInt(0); - } - } else if (typeVal == Socks5AddressType.DOMAIN.byteValue()) { - if (addrValue != null) { - out.writeByte(addrValue.length()); - out.writeCharSequence(addrValue, CharsetUtil.US_ASCII); - } else { - out.writeByte(0); - } - } else if (typeVal == Socks5AddressType.IPv6.byteValue()) { - if (addrValue != null) { - out.writeBytes(NetUtil.createByteArrayFromIpAddressString(addrValue)); - } else { - out.writeLong(0); - out.writeLong(0); - } - } else { - throw new EncoderException("unsupported addrType: " + (addrType.byteValue() & 0xFF)); - } - }; - - /** - * Encodes a SOCKS5 address. - * - * @param addrType the type of the address - * @param addrValue the string representation of the address - * @param out the output buffer where the encoded SOCKS5 address field will be written to - */ - void encodeAddress(Socks5AddressType addrType, String addrValue, ByteBuf out) throws Exception; -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5AddressType.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5AddressType.java deleted file mode 100755 index 687e14f5b8..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5AddressType.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.socksx.v5; - -import static java.util.Objects.requireNonNull; - -/** - * The type of address in {@link Socks5CommandRequest} and {@link Socks5CommandResponse}. - */ -public class Socks5AddressType implements Comparable { - - public static final Socks5AddressType IPv4 = new Socks5AddressType(0x01, "IPv4"); - public static final Socks5AddressType DOMAIN = new Socks5AddressType(0x03, "DOMAIN"); - public static final Socks5AddressType IPv6 = new Socks5AddressType(0x04, "IPv6"); - - public static Socks5AddressType valueOf(byte b) { - switch (b) { - case 0x01: - return IPv4; - case 0x03: - return DOMAIN; - case 0x04: - return IPv6; - } - - return new Socks5AddressType(b); - } - - private final byte byteValue; - private final String name; - private String text; - - public Socks5AddressType(int byteValue) { - this(byteValue, "UNKNOWN"); - } - - public Socks5AddressType(int byteValue, String name) { - requireNonNull(name, "name"); - - this.byteValue = (byte) byteValue; - this.name = name; - } - - public byte byteValue() { - return byteValue; - } - - @Override - public int hashCode() { - return byteValue; - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof Socks5AddressType)) { - return false; - } - - return byteValue == ((Socks5AddressType) obj).byteValue; - } - - @Override - public int compareTo(Socks5AddressType o) { - return byteValue - o.byteValue; - } - - @Override - public String toString() { - String text = this.text; - if (text == null) { - this.text = text = name + '(' + (byteValue & 0xFF) + ')'; - } - return text; - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5AuthMethod.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5AuthMethod.java deleted file mode 100755 index 490c34681b..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5AuthMethod.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.socksx.v5; - -import static java.util.Objects.requireNonNull; - -/** - * The authentication method of SOCKS5. - */ -public class Socks5AuthMethod implements Comparable { - - public static final Socks5AuthMethod NO_AUTH = new Socks5AuthMethod(0x00, "NO_AUTH"); - public static final Socks5AuthMethod GSSAPI = new Socks5AuthMethod(0x01, "GSSAPI"); - public static final Socks5AuthMethod PASSWORD = new Socks5AuthMethod(0x02, "PASSWORD"); - - /** - * Indicates that the server does not accept any authentication methods the client proposed. - */ - public static final Socks5AuthMethod UNACCEPTED = new Socks5AuthMethod(0xff, "UNACCEPTED"); - - public static Socks5AuthMethod valueOf(byte b) { - switch (b) { - case 0x00: - return NO_AUTH; - case 0x01: - return GSSAPI; - case 0x02: - return PASSWORD; - case (byte) 0xFF: - return UNACCEPTED; - } - - return new Socks5AuthMethod(b); - } - - private final byte byteValue; - private final String name; - private String text; - - public Socks5AuthMethod(int byteValue) { - this(byteValue, "UNKNOWN"); - } - - public Socks5AuthMethod(int byteValue, String name) { - requireNonNull(name, "name"); - - this.byteValue = (byte) byteValue; - this.name = name; - } - - public byte byteValue() { - return byteValue; - } - - @Override - public int hashCode() { - return byteValue; - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof Socks5AuthMethod)) { - return false; - } - - return byteValue == ((Socks5AuthMethod) obj).byteValue; - } - - @Override - public int compareTo(Socks5AuthMethod o) { - return byteValue - o.byteValue; - } - - @Override - public String toString() { - String text = this.text; - if (text == null) { - this.text = text = name + '(' + (byteValue & 0xFF) + ')'; - } - return text; - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5ClientEncoder.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5ClientEncoder.java deleted file mode 100644 index 834bc2f7a8..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5ClientEncoder.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.socksx.v5; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.EncoderException; -import io.netty.handler.codec.MessageToByteEncoder; -import io.netty.util.internal.StringUtil; - -import java.util.List; -import java.util.RandomAccess; - -/** - * Encodes a client-side {@link Socks5Message} into a {@link ByteBuf}. - */ -@Sharable -public class Socks5ClientEncoder extends MessageToByteEncoder { - - public static final Socks5ClientEncoder DEFAULT = new Socks5ClientEncoder(); - - private final Socks5AddressEncoder addressEncoder; - - /** - * Creates a new instance with the default {@link Socks5AddressEncoder}. - */ - protected Socks5ClientEncoder() { - this(Socks5AddressEncoder.DEFAULT); - } - - /** - * Creates a new instance with the specified {@link Socks5AddressEncoder}. - */ - public Socks5ClientEncoder(Socks5AddressEncoder addressEncoder) { - requireNonNull(addressEncoder, "addressEncoder"); - - this.addressEncoder = addressEncoder; - } - - /** - * Returns the {@link Socks5AddressEncoder} of this encoder. - */ - protected final Socks5AddressEncoder addressEncoder() { - return addressEncoder; - } - - @Override - protected void encode(ChannelHandlerContext ctx, Socks5Message msg, ByteBuf out) throws Exception { - if (msg instanceof Socks5InitialRequest) { - encodeAuthMethodRequest((Socks5InitialRequest) msg, out); - } else if (msg instanceof Socks5PasswordAuthRequest) { - encodePasswordAuthRequest((Socks5PasswordAuthRequest) msg, out); - } else if (msg instanceof Socks5CommandRequest) { - encodeCommandRequest((Socks5CommandRequest) msg, out); - } else { - throw new EncoderException("unsupported message type: " + StringUtil.simpleClassName(msg)); - } - } - - private static void encodeAuthMethodRequest(Socks5InitialRequest msg, ByteBuf out) { - out.writeByte(msg.version().byteValue()); - - final List authMethods = msg.authMethods(); - final int numAuthMethods = authMethods.size(); - out.writeByte(numAuthMethods); - - if (authMethods instanceof RandomAccess) { - for (int i = 0; i < numAuthMethods; i ++) { - out.writeByte(authMethods.get(i).byteValue()); - } - } else { - for (Socks5AuthMethod a: authMethods) { - out.writeByte(a.byteValue()); - } - } - } - - private static void encodePasswordAuthRequest(Socks5PasswordAuthRequest msg, ByteBuf out) { - out.writeByte(0x01); - - final String username = msg.username(); - out.writeByte(username.length()); - ByteBufUtil.writeAscii(out, username); - - final String password = msg.password(); - out.writeByte(password.length()); - ByteBufUtil.writeAscii(out, password); - } - - private void encodeCommandRequest(Socks5CommandRequest msg, ByteBuf out) throws Exception { - out.writeByte(msg.version().byteValue()); - out.writeByte(msg.type().byteValue()); - out.writeByte(0x00); - - final Socks5AddressType dstAddrType = msg.dstAddrType(); - out.writeByte(dstAddrType.byteValue()); - addressEncoder.encodeAddress(dstAddrType, msg.dstAddr(), out); - out.writeShort(msg.dstPort()); - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5CommandRequest.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5CommandRequest.java deleted file mode 100755 index 59d23212cd..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5CommandRequest.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socksx.v5; - -/** - * A SOCKS5 request detail message, as defined in - * the section 4, RFC1928. - */ -public interface Socks5CommandRequest extends Socks5Message { - - /** - * Returns the type of this request. - */ - Socks5CommandType type(); - - /** - * Returns the type of the {@code DST.ADDR} field of this request. - */ - Socks5AddressType dstAddrType(); - - /** - * Returns the {@code DST.ADDR} field of this request. - */ - String dstAddr(); - - /** - * Returns the {@code DST.PORT} field of this request. - */ - int dstPort(); -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5CommandRequestDecoder.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5CommandRequestDecoder.java deleted file mode 100644 index 6daa91a9b0..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5CommandRequestDecoder.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.socksx.v5; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.DecoderException; -import io.netty.handler.codec.DecoderResult; -import io.netty.handler.codec.ReplayingDecoder; -import io.netty.handler.codec.socksx.SocksVersion; -import io.netty.handler.codec.socksx.v5.Socks5CommandRequestDecoder.State; - -/** - * Decodes a single {@link Socks5CommandRequest} from the inbound {@link ByteBuf}s. - * On successful decode, this decoder will forward the received data to the next handler, so that - * other handler can remove or replace this decoder later. On failed decode, this decoder will - * discard the received data, so that other handler closes the connection later. - */ -public class Socks5CommandRequestDecoder extends ReplayingDecoder { - - enum State { - INIT, - SUCCESS, - FAILURE - } - - private final Socks5AddressDecoder addressDecoder; - - public Socks5CommandRequestDecoder() { - this(Socks5AddressDecoder.DEFAULT); - } - - public Socks5CommandRequestDecoder(Socks5AddressDecoder addressDecoder) { - super(State.INIT); - requireNonNull(addressDecoder, "addressDecoder"); - - this.addressDecoder = addressDecoder; - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - try { - switch (state()) { - case INIT: { - final byte version = in.readByte(); - if (version != SocksVersion.SOCKS5.byteValue()) { - throw new DecoderException( - "unsupported version: " + version + " (expected: " + SocksVersion.SOCKS5.byteValue() + ')'); - } - - final Socks5CommandType type = Socks5CommandType.valueOf(in.readByte()); - in.skipBytes(1); // RSV - final Socks5AddressType dstAddrType = Socks5AddressType.valueOf(in.readByte()); - final String dstAddr = addressDecoder.decodeAddress(dstAddrType, in); - final int dstPort = in.readUnsignedShort(); - - ctx.fireChannelRead(new DefaultSocks5CommandRequest(type, dstAddrType, dstAddr, dstPort)); - checkpoint(State.SUCCESS); - } - case SUCCESS: { - int readableBytes = actualReadableBytes(); - if (readableBytes > 0) { - ctx.fireChannelRead(in.readRetainedSlice(readableBytes)); - } - break; - } - case FAILURE: { - in.skipBytes(actualReadableBytes()); - break; - } - } - } catch (Exception e) { - fail(ctx, e); - } - } - - private void fail(ChannelHandlerContext ctx, Exception cause) { - if (!(cause instanceof DecoderException)) { - cause = new DecoderException(cause); - } - - checkpoint(State.FAILURE); - - Socks5Message m = new DefaultSocks5CommandRequest( - Socks5CommandType.CONNECT, Socks5AddressType.IPv4, "0.0.0.0", 1); - m.setDecoderResult(DecoderResult.failure(cause)); - ctx.fireChannelRead(m); - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5CommandResponse.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5CommandResponse.java deleted file mode 100755 index d86c4792ce..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5CommandResponse.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socksx.v5; - -/** - * A response to a SOCKS5 request detail message, as defined in - * the section 6, RFC1928. - */ -public interface Socks5CommandResponse extends Socks5Message { - - /** - * Returns the status of this response. - */ - Socks5CommandStatus status(); - - /** - * Returns the address type of the {@code BND.ADDR} field of this response. - */ - Socks5AddressType bndAddrType(); - - /** - * Returns the {@code BND.ADDR} field of this response. - */ - String bndAddr(); - - /** - * Returns the {@code BND.PORT} field of this response. - */ - int bndPort(); -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5CommandResponseDecoder.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5CommandResponseDecoder.java deleted file mode 100644 index cce06f752d..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5CommandResponseDecoder.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.socksx.v5; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.DecoderException; -import io.netty.handler.codec.DecoderResult; -import io.netty.handler.codec.ReplayingDecoder; -import io.netty.handler.codec.socksx.SocksVersion; -import io.netty.handler.codec.socksx.v5.Socks5CommandResponseDecoder.State; - -/** - * Decodes a single {@link Socks5CommandResponse} from the inbound {@link ByteBuf}s. - * On successful decode, this decoder will forward the received data to the next handler, so that - * other handler can remove or replace this decoder later. On failed decode, this decoder will - * discard the received data, so that other handler closes the connection later. - */ -public class Socks5CommandResponseDecoder extends ReplayingDecoder { - - enum State { - INIT, - SUCCESS, - FAILURE - } - - private final Socks5AddressDecoder addressDecoder; - - public Socks5CommandResponseDecoder() { - this(Socks5AddressDecoder.DEFAULT); - } - - public Socks5CommandResponseDecoder(Socks5AddressDecoder addressDecoder) { - super(State.INIT); - requireNonNull(addressDecoder, "addressDecoder"); - - this.addressDecoder = addressDecoder; - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - try { - switch (state()) { - case INIT: { - final byte version = in.readByte(); - if (version != SocksVersion.SOCKS5.byteValue()) { - throw new DecoderException( - "unsupported version: " + version + " (expected: " + SocksVersion.SOCKS5.byteValue() + ')'); - } - final Socks5CommandStatus status = Socks5CommandStatus.valueOf(in.readByte()); - in.skipBytes(1); // Reserved - final Socks5AddressType addrType = Socks5AddressType.valueOf(in.readByte()); - final String addr = addressDecoder.decodeAddress(addrType, in); - final int port = in.readUnsignedShort(); - - ctx.fireChannelRead(new DefaultSocks5CommandResponse(status, addrType, addr, port)); - checkpoint(State.SUCCESS); - } - case SUCCESS: { - int readableBytes = actualReadableBytes(); - if (readableBytes > 0) { - ctx.fireChannelRead(in.readRetainedSlice(readableBytes)); - } - break; - } - case FAILURE: { - in.skipBytes(actualReadableBytes()); - break; - } - } - } catch (Exception e) { - fail(ctx, e); - } - } - - private void fail(ChannelHandlerContext ctx, Exception cause) { - if (!(cause instanceof DecoderException)) { - cause = new DecoderException(cause); - } - - checkpoint(State.FAILURE); - - Socks5Message m = new DefaultSocks5CommandResponse( - Socks5CommandStatus.FAILURE, Socks5AddressType.IPv4, null, 0); - m.setDecoderResult(DecoderResult.failure(cause)); - ctx.fireChannelRead(m); - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5CommandStatus.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5CommandStatus.java deleted file mode 100755 index 20632e77de..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5CommandStatus.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.socksx.v5; - -import static java.util.Objects.requireNonNull; - -/** - * The status of {@link Socks5CommandResponse}. - */ -public class Socks5CommandStatus implements Comparable { - - public static final Socks5CommandStatus SUCCESS = new Socks5CommandStatus(0x00, "SUCCESS"); - public static final Socks5CommandStatus FAILURE = new Socks5CommandStatus(0x01, "FAILURE"); - public static final Socks5CommandStatus FORBIDDEN = new Socks5CommandStatus(0x02, "FORBIDDEN"); - public static final Socks5CommandStatus NETWORK_UNREACHABLE = new Socks5CommandStatus(0x03, "NETWORK_UNREACHABLE"); - public static final Socks5CommandStatus HOST_UNREACHABLE = new Socks5CommandStatus(0x04, "HOST_UNREACHABLE"); - public static final Socks5CommandStatus CONNECTION_REFUSED = new Socks5CommandStatus(0x05, "CONNECTION_REFUSED"); - public static final Socks5CommandStatus TTL_EXPIRED = new Socks5CommandStatus(0x06, "TTL_EXPIRED"); - public static final Socks5CommandStatus COMMAND_UNSUPPORTED = new Socks5CommandStatus(0x07, "COMMAND_UNSUPPORTED"); - public static final Socks5CommandStatus ADDRESS_UNSUPPORTED = new Socks5CommandStatus(0x08, "ADDRESS_UNSUPPORTED"); - - public static Socks5CommandStatus valueOf(byte b) { - switch (b) { - case 0x00: - return SUCCESS; - case 0x01: - return FAILURE; - case 0x02: - return FORBIDDEN; - case 0x03: - return NETWORK_UNREACHABLE; - case 0x04: - return HOST_UNREACHABLE; - case 0x05: - return CONNECTION_REFUSED; - case 0x06: - return TTL_EXPIRED; - case 0x07: - return COMMAND_UNSUPPORTED; - case 0x08: - return ADDRESS_UNSUPPORTED; - } - - return new Socks5CommandStatus(b); - } - - private final byte byteValue; - private final String name; - private String text; - - public Socks5CommandStatus(int byteValue) { - this(byteValue, "UNKNOWN"); - } - - public Socks5CommandStatus(int byteValue, String name) { - requireNonNull(name, "name"); - - this.byteValue = (byte) byteValue; - this.name = name; - } - - public byte byteValue() { - return byteValue; - } - - public boolean isSuccess() { - return byteValue == 0; - } - - @Override - public int hashCode() { - return byteValue; - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof Socks5CommandStatus)) { - return false; - } - - return byteValue == ((Socks5CommandStatus) obj).byteValue; - } - - @Override - public int compareTo(Socks5CommandStatus o) { - return byteValue - o.byteValue; - } - - @Override - public String toString() { - String text = this.text; - if (text == null) { - this.text = text = name + '(' + (byteValue & 0xFF) + ')'; - } - return text; - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5CommandType.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5CommandType.java deleted file mode 100755 index e1c2676999..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5CommandType.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.socksx.v5; - -import static java.util.Objects.requireNonNull; - -/** - * The type of {@link Socks5CommandRequest}. - */ -public class Socks5CommandType implements Comparable { - - public static final Socks5CommandType CONNECT = new Socks5CommandType(0x01, "CONNECT"); - public static final Socks5CommandType BIND = new Socks5CommandType(0x02, "BIND"); - public static final Socks5CommandType UDP_ASSOCIATE = new Socks5CommandType(0x03, "UDP_ASSOCIATE"); - - public static Socks5CommandType valueOf(byte b) { - switch (b) { - case 0x01: - return CONNECT; - case 0x02: - return BIND; - case 0x03: - return UDP_ASSOCIATE; - } - - return new Socks5CommandType(b); - } - - private final byte byteValue; - private final String name; - private String text; - - public Socks5CommandType(int byteValue) { - this(byteValue, "UNKNOWN"); - } - - public Socks5CommandType(int byteValue, String name) { - requireNonNull(name, "name"); - - this.byteValue = (byte) byteValue; - this.name = name; - } - - public byte byteValue() { - return byteValue; - } - - @Override - public int hashCode() { - return byteValue; - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof Socks5CommandType)) { - return false; - } - - return byteValue == ((Socks5CommandType) obj).byteValue; - } - - @Override - public int compareTo(Socks5CommandType o) { - return byteValue - o.byteValue; - } - - @Override - public String toString() { - String text = this.text; - if (text == null) { - this.text = text = name + '(' + (byteValue & 0xFF) + ')'; - } - return text; - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5InitialRequest.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5InitialRequest.java deleted file mode 100755 index 6ea9b8358d..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5InitialRequest.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socksx.v5; - -import java.util.List; - -/** - * An initial SOCKS5 authentication method selection request, as defined in - * the section 3, RFC1928. - */ -public interface Socks5InitialRequest extends Socks5Message { - /** - * Returns the list of desired authentication methods. - */ - List authMethods(); -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5InitialRequestDecoder.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5InitialRequestDecoder.java deleted file mode 100644 index 4540b246e2..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5InitialRequestDecoder.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.socksx.v5; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.DecoderException; -import io.netty.handler.codec.DecoderResult; -import io.netty.handler.codec.ReplayingDecoder; -import io.netty.handler.codec.socksx.SocksVersion; -import io.netty.handler.codec.socksx.v5.Socks5InitialRequestDecoder.State; - -/** - * Decodes a single {@link Socks5InitialRequest} from the inbound {@link ByteBuf}s. - * On successful decode, this decoder will forward the received data to the next handler, so that - * other handler can remove or replace this decoder later. On failed decode, this decoder will - * discard the received data, so that other handler closes the connection later. - */ -public class Socks5InitialRequestDecoder extends ReplayingDecoder { - - enum State { - INIT, - SUCCESS, - FAILURE - } - - public Socks5InitialRequestDecoder() { - super(State.INIT); - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - try { - switch (state()) { - case INIT: { - final byte version = in.readByte(); - if (version != SocksVersion.SOCKS5.byteValue()) { - throw new DecoderException( - "unsupported version: " + version + " (expected: " + SocksVersion.SOCKS5.byteValue() + ')'); - } - - final int authMethodCnt = in.readUnsignedByte(); - - final Socks5AuthMethod[] authMethods = new Socks5AuthMethod[authMethodCnt]; - for (int i = 0; i < authMethodCnt; i++) { - authMethods[i] = Socks5AuthMethod.valueOf(in.readByte()); - } - - ctx.fireChannelRead(new DefaultSocks5InitialRequest(authMethods)); - checkpoint(State.SUCCESS); - } - case SUCCESS: { - int readableBytes = actualReadableBytes(); - if (readableBytes > 0) { - ctx.fireChannelRead(in.readRetainedSlice(readableBytes)); - } - break; - } - case FAILURE: { - in.skipBytes(actualReadableBytes()); - break; - } - } - } catch (Exception e) { - fail(ctx, e); - } - } - - private void fail(ChannelHandlerContext ctx, Exception cause) { - if (!(cause instanceof DecoderException)) { - cause = new DecoderException(cause); - } - - checkpoint(State.FAILURE); - - Socks5Message m = new DefaultSocks5InitialRequest(Socks5AuthMethod.NO_AUTH); - m.setDecoderResult(DecoderResult.failure(cause)); - ctx.fireChannelRead(m); - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5InitialResponse.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5InitialResponse.java deleted file mode 100755 index 61be5816b6..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5InitialResponse.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socksx.v5; - -/** - * An initial SOCKS5 authentication method selection request, as defined in - * the section 3, RFC1928. - */ -public interface Socks5InitialResponse extends Socks5Message { - - /** - * Returns the {@code METHOD} field of this response. - */ - Socks5AuthMethod authMethod(); -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5InitialResponseDecoder.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5InitialResponseDecoder.java deleted file mode 100644 index 5da33bae5f..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5InitialResponseDecoder.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.socksx.v5; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.DecoderException; -import io.netty.handler.codec.DecoderResult; -import io.netty.handler.codec.ReplayingDecoder; -import io.netty.handler.codec.socksx.SocksVersion; -import io.netty.handler.codec.socksx.v5.Socks5InitialResponseDecoder.State; - -/** - * Decodes a single {@link Socks5InitialResponse} from the inbound {@link ByteBuf}s. - * On successful decode, this decoder will forward the received data to the next handler, so that - * other handler can remove or replace this decoder later. On failed decode, this decoder will - * discard the received data, so that other handler closes the connection later. - */ -public class Socks5InitialResponseDecoder extends ReplayingDecoder { - - enum State { - INIT, - SUCCESS, - FAILURE - } - - public Socks5InitialResponseDecoder() { - super(State.INIT); - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - try { - switch (state()) { - case INIT: { - final byte version = in.readByte(); - if (version != SocksVersion.SOCKS5.byteValue()) { - throw new DecoderException( - "unsupported version: " + version + " (expected: " + SocksVersion.SOCKS5.byteValue() + ')'); - } - - final Socks5AuthMethod authMethod = Socks5AuthMethod.valueOf(in.readByte()); - ctx.fireChannelRead(new DefaultSocks5InitialResponse(authMethod)); - checkpoint(State.SUCCESS); - } - case SUCCESS: { - int readableBytes = actualReadableBytes(); - if (readableBytes > 0) { - ctx.fireChannelRead(in.readRetainedSlice(readableBytes)); - } - break; - } - case FAILURE: { - in.skipBytes(actualReadableBytes()); - break; - } - } - } catch (Exception e) { - fail(ctx, e); - } - } - - private void fail(ChannelHandlerContext ctx, Exception cause) { - if (!(cause instanceof DecoderException)) { - cause = new DecoderException(cause); - } - - checkpoint(State.FAILURE); - - Socks5Message m = new DefaultSocks5InitialResponse(Socks5AuthMethod.UNACCEPTED); - m.setDecoderResult(DecoderResult.failure(cause)); - ctx.fireChannelRead(m); - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5Message.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5Message.java deleted file mode 100644 index 46944f9da5..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5Message.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.socksx.v5; - -import io.netty.handler.codec.socksx.SocksMessage; - -/** - * A tag interface that all SOCKS5 protocol messages implement. - */ -public interface Socks5Message extends SocksMessage { - // Tag interface -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5PasswordAuthRequest.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5PasswordAuthRequest.java deleted file mode 100755 index 14526bee1a..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5PasswordAuthRequest.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socksx.v5; - -/** - * A SOCKS5 subnegotiation request for username-password authentication, as defined in - * the section 2, RFC1929. - */ -public interface Socks5PasswordAuthRequest extends Socks5Message { - - /** - * Returns the username of this request. - */ - String username(); - - /** - * Returns the password of this request. - */ - String password(); -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5PasswordAuthRequestDecoder.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5PasswordAuthRequestDecoder.java deleted file mode 100644 index d8981a0e42..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5PasswordAuthRequestDecoder.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.socksx.v5; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.DecoderException; -import io.netty.handler.codec.DecoderResult; -import io.netty.handler.codec.ReplayingDecoder; -import io.netty.handler.codec.socksx.v5.Socks5PasswordAuthRequestDecoder.State; -import io.netty.util.CharsetUtil; - -/** - * Decodes a single {@link Socks5PasswordAuthRequest} from the inbound {@link ByteBuf}s. - * On successful decode, this decoder will forward the received data to the next handler, so that - * other handler can remove or replace this decoder later. On failed decode, this decoder will - * discard the received data, so that other handler closes the connection later. - */ -public class Socks5PasswordAuthRequestDecoder extends ReplayingDecoder { - - enum State { - INIT, - SUCCESS, - FAILURE - } - - public Socks5PasswordAuthRequestDecoder() { - super(State.INIT); - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - try { - switch (state()) { - case INIT: { - final int startOffset = in.readerIndex(); - final byte version = in.getByte(startOffset); - if (version != 1) { - throw new DecoderException("unsupported subnegotiation version: " + version + " (expected: 1)"); - } - - final int usernameLength = in.getUnsignedByte(startOffset + 1); - final int passwordLength = in.getUnsignedByte(startOffset + 2 + usernameLength); - final int totalLength = usernameLength + passwordLength + 3; - - in.skipBytes(totalLength); - ctx.fireChannelRead(new DefaultSocks5PasswordAuthRequest( - in.toString(startOffset + 2, usernameLength, CharsetUtil.US_ASCII), - in.toString(startOffset + 3 + usernameLength, passwordLength, CharsetUtil.US_ASCII))); - - checkpoint(State.SUCCESS); - } - case SUCCESS: { - int readableBytes = actualReadableBytes(); - if (readableBytes > 0) { - ctx.fireChannelRead(in.readRetainedSlice(readableBytes)); - } - break; - } - case FAILURE: { - in.skipBytes(actualReadableBytes()); - break; - } - } - } catch (Exception e) { - fail(ctx, e); - } - } - - private void fail(ChannelHandlerContext ctx, Exception cause) { - if (!(cause instanceof DecoderException)) { - cause = new DecoderException(cause); - } - - checkpoint(State.FAILURE); - - Socks5Message m = new DefaultSocks5PasswordAuthRequest("", ""); - m.setDecoderResult(DecoderResult.failure(cause)); - ctx.fireChannelRead(m); - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5PasswordAuthResponse.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5PasswordAuthResponse.java deleted file mode 100755 index 119d1c4bcd..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5PasswordAuthResponse.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socksx.v5; - -/** - * A SOCKS5 subnegotiation response for username-password authentication, as defined in - * the section 2, RFC1929. - */ -public interface Socks5PasswordAuthResponse extends Socks5Message { - /** - * Returns the status of this response. - */ - Socks5PasswordAuthStatus status(); -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5PasswordAuthResponseDecoder.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5PasswordAuthResponseDecoder.java deleted file mode 100644 index c2e50ae1af..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5PasswordAuthResponseDecoder.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.socksx.v5; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.DecoderException; -import io.netty.handler.codec.DecoderResult; -import io.netty.handler.codec.ReplayingDecoder; -import io.netty.handler.codec.socksx.v5.Socks5PasswordAuthResponseDecoder.State; - -/** - * Decodes a single {@link Socks5PasswordAuthResponse} from the inbound {@link ByteBuf}s. - * On successful decode, this decoder will forward the received data to the next handler, so that - * other handler can remove or replace this decoder later. On failed decode, this decoder will - * discard the received data, so that other handler closes the connection later. - */ -public class Socks5PasswordAuthResponseDecoder extends ReplayingDecoder { - - enum State { - INIT, - SUCCESS, - FAILURE - } - - public Socks5PasswordAuthResponseDecoder() { - super(State.INIT); - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - try { - switch (state()) { - case INIT: { - final byte version = in.readByte(); - if (version != 1) { - throw new DecoderException("unsupported subnegotiation version: " + version + " (expected: 1)"); - } - - ctx.fireChannelRead( - new DefaultSocks5PasswordAuthResponse(Socks5PasswordAuthStatus.valueOf(in.readByte()))); - checkpoint(State.SUCCESS); - } - case SUCCESS: { - int readableBytes = actualReadableBytes(); - if (readableBytes > 0) { - ctx.fireChannelRead(in.readRetainedSlice(readableBytes)); - } - break; - } - case FAILURE: { - in.skipBytes(actualReadableBytes()); - break; - } - } - } catch (Exception e) { - fail(ctx, e); - } - } - - private void fail(ChannelHandlerContext ctx, Exception cause) { - if (!(cause instanceof DecoderException)) { - cause = new DecoderException(cause); - } - - checkpoint(State.FAILURE); - - Socks5Message m = new DefaultSocks5PasswordAuthResponse(Socks5PasswordAuthStatus.FAILURE); - m.setDecoderResult(DecoderResult.failure(cause)); - ctx.fireChannelRead(m); - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5PasswordAuthStatus.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5PasswordAuthStatus.java deleted file mode 100755 index 3322beb076..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5PasswordAuthStatus.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.socksx.v5; - -import static java.util.Objects.requireNonNull; - -/** - * The status of {@link Socks5PasswordAuthResponse}. - */ -public class Socks5PasswordAuthStatus implements Comparable { - - public static final Socks5PasswordAuthStatus SUCCESS = new Socks5PasswordAuthStatus(0x00, "SUCCESS"); - public static final Socks5PasswordAuthStatus FAILURE = new Socks5PasswordAuthStatus(0xFF, "FAILURE"); - - public static Socks5PasswordAuthStatus valueOf(byte b) { - switch (b) { - case 0x00: - return SUCCESS; - case (byte) 0xFF: - return FAILURE; - } - - return new Socks5PasswordAuthStatus(b); - } - - private final byte byteValue; - private final String name; - private String text; - - public Socks5PasswordAuthStatus(int byteValue) { - this(byteValue, "UNKNOWN"); - } - - public Socks5PasswordAuthStatus(int byteValue, String name) { - requireNonNull(name, "name"); - - this.byteValue = (byte) byteValue; - this.name = name; - } - - public byte byteValue() { - return byteValue; - } - - public boolean isSuccess() { - return byteValue == 0; - } - - @Override - public int hashCode() { - return byteValue; - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof Socks5PasswordAuthStatus)) { - return false; - } - - return byteValue == ((Socks5PasswordAuthStatus) obj).byteValue; - } - - @Override - public int compareTo(Socks5PasswordAuthStatus o) { - return byteValue - o.byteValue; - } - - @Override - public String toString() { - String text = this.text; - if (text == null) { - this.text = text = name + '(' + (byteValue & 0xFF) + ')'; - } - return text; - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5ServerEncoder.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5ServerEncoder.java deleted file mode 100644 index f9aa24d49e..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/Socks5ServerEncoder.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.socksx.v5; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.EncoderException; -import io.netty.handler.codec.MessageToByteEncoder; -import io.netty.util.internal.StringUtil; - -/** - * Encodes a server-side {@link Socks5Message} into a {@link ByteBuf}. - */ -@Sharable -public class Socks5ServerEncoder extends MessageToByteEncoder { - - public static final Socks5ServerEncoder DEFAULT = new Socks5ServerEncoder(Socks5AddressEncoder.DEFAULT); - - private final Socks5AddressEncoder addressEncoder; - - /** - * Creates a new instance with the default {@link Socks5AddressEncoder}. - */ - protected Socks5ServerEncoder() { - this(Socks5AddressEncoder.DEFAULT); - } - - /** - * Creates a new instance with the specified {@link Socks5AddressEncoder}. - */ - public Socks5ServerEncoder(Socks5AddressEncoder addressEncoder) { - requireNonNull(addressEncoder, "addressEncoder"); - - this.addressEncoder = addressEncoder; - } - - /** - * Returns the {@link Socks5AddressEncoder} of this encoder. - */ - protected final Socks5AddressEncoder addressEncoder() { - return addressEncoder; - } - - @Override - protected void encode(ChannelHandlerContext ctx, Socks5Message msg, ByteBuf out) throws Exception { - if (msg instanceof Socks5InitialResponse) { - encodeAuthMethodResponse((Socks5InitialResponse) msg, out); - } else if (msg instanceof Socks5PasswordAuthResponse) { - encodePasswordAuthResponse((Socks5PasswordAuthResponse) msg, out); - } else if (msg instanceof Socks5CommandResponse) { - encodeCommandResponse((Socks5CommandResponse) msg, out); - } else { - throw new EncoderException("unsupported message type: " + StringUtil.simpleClassName(msg)); - } - } - - private static void encodeAuthMethodResponse(Socks5InitialResponse msg, ByteBuf out) { - out.writeByte(msg.version().byteValue()); - out.writeByte(msg.authMethod().byteValue()); - } - - private static void encodePasswordAuthResponse(Socks5PasswordAuthResponse msg, ByteBuf out) { - out.writeByte(0x01); - out.writeByte(msg.status().byteValue()); - } - - private void encodeCommandResponse(Socks5CommandResponse msg, ByteBuf out) throws Exception { - out.writeByte(msg.version().byteValue()); - out.writeByte(msg.status().byteValue()); - out.writeByte(0x00); - - final Socks5AddressType bndAddrType = msg.bndAddrType(); - out.writeByte(bndAddrType.byteValue()); - addressEncoder.encodeAddress(bndAddrType, msg.bndAddr(), out); - - out.writeShort(msg.bndPort()); - } -} diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/package-info.java b/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/package-info.java deleted file mode 100755 index 622d2e4c72..0000000000 --- a/codec-socks/src/main/java/io/netty/handler/codec/socksx/v5/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * Encoder, decoder and their related message types for SOCKSv5 protocol. - */ -package io.netty.handler.codec.socksx.v5; diff --git a/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksAuthRequestDecoderTest.java b/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksAuthRequestDecoderTest.java deleted file mode 100644 index bf72863994..0000000000 --- a/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksAuthRequestDecoderTest.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socks; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; - -public class SocksAuthRequestDecoderTest { - - private static final String username = "testUserName"; - private static final String password = "testPassword"; - - @Test - public void testAuthRequestDecoder() { - SocksAuthRequest msg = new SocksAuthRequest(username, password); - SocksAuthRequestDecoder decoder = new SocksAuthRequestDecoder(); - EmbeddedChannel embedder = new EmbeddedChannel(decoder); - SocksCommonTestUtils.writeMessageIntoEmbedder(embedder, msg); - msg = embedder.readInbound(); - assertEquals(username, msg.username()); - assertEquals(password, msg.password()); - assertNull(embedder.readInbound()); - } - - @Test - public void testAuthRequestDecoderPartialSend() { - EmbeddedChannel ch = new EmbeddedChannel(new SocksAuthRequestDecoder()); - ByteBuf byteBuf = Unpooled.buffer(16); - - // Send username and password size - byteBuf.writeByte(SocksSubnegotiationVersion.AUTH_PASSWORD.byteValue()); - byteBuf.writeByte(username.length()); - byteBuf.writeBytes(username.getBytes()); - byteBuf.writeByte(password.length()); - ch.writeInbound(byteBuf); - - // Check that channel is empty - assertNull(ch.readInbound()); - - // Send password - ByteBuf byteBuf2 = Unpooled.buffer(); - byteBuf2.writeBytes(password.getBytes()); - ch.writeInbound(byteBuf2); - - // Read message from channel - SocksAuthRequest msg = ch.readInbound(); - - // Check message - assertEquals(username, msg.username()); - assertEquals(password, msg.password()); - - assertFalse(ch.finishAndReleaseAll()); - } -} diff --git a/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksAuthRequestTest.java b/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksAuthRequestTest.java deleted file mode 100644 index 6de75a30e0..0000000000 --- a/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksAuthRequestTest.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socks; - -import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class SocksAuthRequestTest { - @Test - public void testConstructorParamsAreNotNull() { - try { - new SocksAuthRequest(null, ""); - } catch (Exception e) { - assertTrue(e instanceof NullPointerException); - } - try { - new SocksAuthRequest("", null); - } catch (Exception e) { - assertTrue(e instanceof NullPointerException); - } - } - - @Test - public void testUsernameOrPasswordIsNotAscii() { - try { - new SocksAuthRequest("Ī€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽ", "password"); - } catch (Exception e) { - assertTrue(e instanceof IllegalArgumentException); - } - try { - new SocksAuthRequest("username", "Ī€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽ"); - } catch (Exception e) { - assertTrue(e instanceof IllegalArgumentException); - } - } - - @Test - public void testUsernameOrPasswordLengthIsLessThan255Chars() { - try { - new SocksAuthRequest( - "passwordpasswordpasswordpasswordpasswordpasswordpassword" + - "passwordpasswordpasswordpasswordpasswordpasswordpassword" + - "passwordpasswordpasswordpasswordpasswordpasswordpassword" + - "passwordpasswordpasswordpasswordpasswordpasswordpassword" + - "passwordpasswordpasswordpasswordpasswordpasswordpassword" + - "passwordpasswordpasswordpasswordpasswordpasswordpassword" + - "passwordpasswordpasswordpasswordpasswordpasswordpassword" + - "passwordpasswordpasswordpasswordpasswordpasswordpassword", - "password"); - } catch (Exception e) { - assertTrue(e instanceof IllegalArgumentException); - } - try { - new SocksAuthRequest("password", - "passwordpasswordpasswordpasswordpasswordpasswordpassword" + - "passwordpasswordpasswordpasswordpasswordpasswordpassword" + - "passwordpasswordpasswordpasswordpasswordpasswordpassword" + - "passwordpasswordpasswordpasswordpasswordpasswordpassword" + - "passwordpasswordpasswordpasswordpasswordpasswordpassword" + - "passwordpasswordpasswordpasswordpasswordpasswordpassword" + - "passwordpasswordpasswordpasswordpasswordpasswordpassword" + - "passwordpasswordpasswordpasswordpasswordpasswordpassword"); - } catch (Exception e) { - assertTrue(e instanceof IllegalArgumentException); - } - } - -} diff --git a/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksAuthResponseDecoderTest.java b/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksAuthResponseDecoderTest.java deleted file mode 100644 index 5faa0166ca..0000000000 --- a/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksAuthResponseDecoderTest.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socks; - -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; - -public class SocksAuthResponseDecoderTest { - private static final InternalLogger logger = InternalLoggerFactory.getInstance(SocksAuthResponseDecoderTest.class); - - private static void testSocksAuthResponseDecoderWithDifferentParams(SocksAuthStatus authStatus) { - logger.debug("Testing SocksAuthResponseDecoder with authStatus: " + authStatus); - SocksAuthResponse msg = new SocksAuthResponse(authStatus); - SocksAuthResponseDecoder decoder = new SocksAuthResponseDecoder(); - EmbeddedChannel embedder = new EmbeddedChannel(decoder); - SocksCommonTestUtils.writeMessageIntoEmbedder(embedder, msg); - msg = embedder.readInbound(); - assertSame(msg.authStatus(), authStatus); - assertNull(embedder.readInbound()); - } - - @Test - public void testSocksCmdResponseDecoder() { - for (SocksAuthStatus authStatus: SocksAuthStatus.values()) { - testSocksAuthResponseDecoderWithDifferentParams(authStatus); - } - } -} diff --git a/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksAuthResponseTest.java b/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksAuthResponseTest.java deleted file mode 100644 index dc92a469f1..0000000000 --- a/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksAuthResponseTest.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socks; - -import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class SocksAuthResponseTest { - @Test - public void testConstructorParamsAreNotNull() { - try { - new SocksAuthResponse(null); - } catch (Exception e) { - assertTrue(e instanceof NullPointerException); - } - } - -} diff --git a/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksCmdRequestDecoderTest.java b/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksCmdRequestDecoderTest.java deleted file mode 100644 index f7ea485b48..0000000000 --- a/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksCmdRequestDecoderTest.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socks; - -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.internal.SocketUtils; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; -import org.junit.jupiter.api.Test; - -import java.net.UnknownHostException; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class SocksCmdRequestDecoderTest { - private static final InternalLogger logger = InternalLoggerFactory.getInstance(SocksCmdRequestDecoderTest.class); - - private static void testSocksCmdRequestDecoderWithDifferentParams(SocksCmdType cmdType, - SocksAddressType addressType, - String host, - int port) { - logger.debug("Testing cmdType: " + cmdType + " addressType: " + addressType + " host: " + host + - " port: " + port); - SocksCmdRequest msg = new SocksCmdRequest(cmdType, addressType, host, port); - SocksCmdRequestDecoder decoder = new SocksCmdRequestDecoder(); - EmbeddedChannel embedder = new EmbeddedChannel(decoder); - SocksCommonTestUtils.writeMessageIntoEmbedder(embedder, msg); - if (msg.addressType() == SocksAddressType.UNKNOWN) { - assertTrue(embedder.readInbound() instanceof UnknownSocksRequest); - } else { - msg = embedder.readInbound(); - assertSame(msg.cmdType(), cmdType); - assertSame(msg.addressType(), addressType); - assertEquals(msg.host(), host); - assertEquals(msg.port(), port); - } - assertNull(embedder.readInbound()); - } - - @Test - public void testCmdRequestDecoderIPv4() { - String[] hosts = {"127.0.0.1", }; - int[] ports = {1, 32769, 65535 }; - for (SocksCmdType cmdType : SocksCmdType.values()) { - for (String host : hosts) { - for (int port : ports) { - testSocksCmdRequestDecoderWithDifferentParams(cmdType, SocksAddressType.IPv4, host, port); - } - } - } - } - - @Test - public void testCmdRequestDecoderIPv6() throws UnknownHostException { - String[] hosts = {SocksCommonUtils.ipv6toStr(SocketUtils.addressByName("::1").getAddress())}; - int[] ports = {1, 32769, 65535}; - for (SocksCmdType cmdType : SocksCmdType.values()) { - for (String host : hosts) { - for (int port : ports) { - testSocksCmdRequestDecoderWithDifferentParams(cmdType, SocksAddressType.IPv6, host, port); - } - } - } - } - - @Test - public void testCmdRequestDecoderDomain() { - String[] hosts = {"google.com" , - "Ų…ØĢاŲ„.ØĨØŽØĒØ¨Ø§Øą", - "Ī€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽ", - "Ų…ØĢاŲ„.ØĸØ˛Ų…ایشی", - "ĐŋŅ€Đ¸ĐŧĐĩŅ€.иŅĐŋŅ‹Ņ‚Đ°ĐŊиĐĩ", - "בײַשפÖŧיל.ט×ĸץט", - "䞋子.æĩ‹č¯•", - "䞋子.æ¸ŦčŠĻ", - "ā¤‰ā¤Ļā¤žā¤šā¤°ā¤Ŗ.ā¤Ēā¤°āĨ€ā¤•āĨā¤ˇā¤ž", - "䞋え.テ゚ト", - "ė‹¤ëĄ€.테ėŠ¤íŠ¸", - "āŽ‰āŽ¤āŽžāŽ°āŽŖāŽŽā¯.āŽĒāŽ°āŽŋāŽŸā¯āŽšā¯ˆ"}; - int[] ports = {1, 32769, 65535}; - for (SocksCmdType cmdType : SocksCmdType.values()) { - for (String host : hosts) { - for (int port : ports) { - testSocksCmdRequestDecoderWithDifferentParams(cmdType, SocksAddressType.DOMAIN, host, port); - } - } - } - } - - @Test - public void testCmdRequestDecoderUnknown() { - String host = "google.com"; - int port = 80; - for (SocksCmdType cmdType : SocksCmdType.values()) { - testSocksCmdRequestDecoderWithDifferentParams(cmdType, SocksAddressType.UNKNOWN, host, port); - } - } -} 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 deleted file mode 100644 index 9ef5432d62..0000000000 --- a/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksCmdRequestTest.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socks; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.CharsetUtil; -import org.junit.jupiter.api.Test; - -import java.net.IDN; -import java.nio.CharBuffer; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class SocksCmdRequestTest { - @Test - public void testConstructorParamsAreNotNull() { - try { - new SocksCmdRequest(null, SocksAddressType.UNKNOWN, "", 1); - } catch (Exception e) { - assertTrue(e instanceof NullPointerException); - } - - try { - new SocksCmdRequest(SocksCmdType.UNKNOWN, null, "", 1); - } catch (Exception e) { - assertTrue(e instanceof NullPointerException); - } - - try { - new SocksCmdRequest(SocksCmdType.UNKNOWN, SocksAddressType.UNKNOWN, null, 1); - } catch (Exception e) { - assertTrue(e instanceof NullPointerException); - } - } - - @Test - public void testIPv4CorrectAddress() { - try { - new SocksCmdRequest(SocksCmdType.BIND, SocksAddressType.IPv4, "54.54.1111.253", 1); - } catch (Exception e) { - assertTrue(e instanceof IllegalArgumentException); - } - } - - @Test - public void testIPv6CorrectAddress() { - try { - new SocksCmdRequest(SocksCmdType.BIND, SocksAddressType.IPv6, "xxx:xxx:xxx", 1); - } catch (Exception e) { - assertTrue(e instanceof IllegalArgumentException); - } - } - - @Test - public void testIDNNotExceeds255CharsLimit() { - try { - new SocksCmdRequest(SocksCmdType.BIND, SocksAddressType.DOMAIN, - "Ī€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽĪ€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽĪ€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽĪ€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽ" + - "Ī€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽĪ€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽĪ€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽĪ€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽ" + - "Ī€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽĪ€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽĪ€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽĪ€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽ" + - "Ī€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽĪ€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽĪ€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽĪ€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽ", 1); - } catch (Exception e) { - assertTrue(e instanceof IllegalArgumentException); - } - } - - @Test - public void testHostNotEncodedForUnknown() { - String asciiHost = "xn--e1aybc.xn--p1ai"; - short port = 10000; - - SocksCmdRequest rq = new SocksCmdRequest(SocksCmdType.BIND, SocksAddressType.UNKNOWN, asciiHost, port); - assertEquals(asciiHost, rq.host()); - - ByteBuf buffer = Unpooled.buffer(16); - rq.encodeAsByteBuf(buffer); - - buffer.readerIndex(0); - assertEquals(SocksProtocolVersion.SOCKS5.byteValue(), buffer.readByte()); - assertEquals(SocksCmdType.BIND.byteValue(), buffer.readByte()); - assertEquals((byte) 0x00, buffer.readByte()); - assertEquals(SocksAddressType.UNKNOWN.byteValue(), buffer.readByte()); - assertFalse(buffer.isReadable()); - - buffer.release(); - } - - @Test - public void testIDNEncodeToAsciiForDomain() { - String host = "Ņ‚ĐĩŅŅ‚.Ņ€Ņ„"; - CharBuffer asciiHost = CharBuffer.wrap(IDN.toASCII(host)); - short port = 10000; - - SocksCmdRequest rq = new SocksCmdRequest(SocksCmdType.BIND, SocksAddressType.DOMAIN, host, port); - assertEquals(host, rq.host()); - - ByteBuf buffer = Unpooled.buffer(24); - rq.encodeAsByteBuf(buffer); - - buffer.readerIndex(0); - assertEquals(SocksProtocolVersion.SOCKS5.byteValue(), buffer.readByte()); - assertEquals(SocksCmdType.BIND.byteValue(), buffer.readByte()); - assertEquals((byte) 0x00, buffer.readByte()); - assertEquals(SocksAddressType.DOMAIN.byteValue(), buffer.readByte()); - assertEquals((byte) asciiHost.length(), buffer.readUnsignedByte()); - assertEquals(asciiHost, - CharBuffer.wrap(buffer.readCharSequence(asciiHost.length(), CharsetUtil.US_ASCII))); - assertEquals(port, buffer.readUnsignedShort()); - - buffer.release(); - } - - @Test - public void testValidPortRange() { - try { - new SocksCmdRequest(SocksCmdType.BIND, SocksAddressType.DOMAIN, - "Ī€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽĪ€ÎąĪÎŦδÎĩΚ", 0); - } catch (Exception e) { - assertTrue(e instanceof IllegalArgumentException); - } - - try { - new SocksCmdRequest(SocksCmdType.BIND, SocksAddressType.DOMAIN, - "Ī€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽĪ€ÎąĪÎŦδÎĩΚ", 65536); - } catch (Exception e) { - assertTrue(e instanceof IllegalArgumentException); - } - } -} diff --git a/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksCmdResponseDecoderTest.java b/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksCmdResponseDecoderTest.java deleted file mode 100644 index d0d9a89579..0000000000 --- a/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksCmdResponseDecoderTest.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socks; - -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class SocksCmdResponseDecoderTest { - private static final InternalLogger logger = InternalLoggerFactory.getInstance(SocksCmdResponseDecoderTest.class); - - private static void testSocksCmdResponseDecoderWithDifferentParams( - SocksCmdStatus cmdStatus, SocksAddressType addressType, String host, int port) { - logger.debug("Testing cmdStatus: " + cmdStatus + " addressType: " + addressType); - SocksResponse msg = new SocksCmdResponse(cmdStatus, addressType, host, port); - SocksCmdResponseDecoder decoder = new SocksCmdResponseDecoder(); - EmbeddedChannel embedder = new EmbeddedChannel(decoder); - SocksCommonTestUtils.writeMessageIntoEmbedder(embedder, msg); - if (addressType == SocksAddressType.UNKNOWN) { - assertTrue(embedder.readInbound() instanceof UnknownSocksResponse); - } else { - msg = embedder.readInbound(); - assertEquals(((SocksCmdResponse) msg).cmdStatus(), cmdStatus); - if (host != null) { - assertEquals(((SocksCmdResponse) msg).host(), host); - } - assertEquals(((SocksCmdResponse) msg).port(), port); - } - assertNull(embedder.readInbound()); - } - - /** - * Verifies that sent socks messages are decoded correctly. - */ - @Test - public void testSocksCmdResponseDecoder() { - for (SocksCmdStatus cmdStatus : SocksCmdStatus.values()) { - for (SocksAddressType addressType : SocksAddressType.values()) { - testSocksCmdResponseDecoderWithDifferentParams(cmdStatus, addressType, null, 0); - } - } - } - - /** - * Verifies that invalid bound host will fail with IllegalArgumentException during encoding. - */ - @Test - public void testInvalidAddress() { - assertThrows(IllegalArgumentException.class, - () -> testSocksCmdResponseDecoderWithDifferentParams( - SocksCmdStatus.SUCCESS, SocksAddressType.IPv4, "1", 80)); - } - - /** - * Verifies that send socks messages are decoded correctly when bound host and port are set. - */ - @Test - public void testSocksCmdResponseDecoderIncludingHost() { - for (SocksCmdStatus cmdStatus : SocksCmdStatus.values()) { - testSocksCmdResponseDecoderWithDifferentParams(cmdStatus, SocksAddressType.IPv4, - "127.0.0.1", 80); - testSocksCmdResponseDecoderWithDifferentParams(cmdStatus, SocksAddressType.DOMAIN, - "testDomain.com", 80); - testSocksCmdResponseDecoderWithDifferentParams(cmdStatus, SocksAddressType.IPv6, - "2001:db8:85a3:42:1000:8a2e:370:7334", 80); - testSocksCmdResponseDecoderWithDifferentParams(cmdStatus, SocksAddressType.IPv6, - "1111:111:11:1:0:0:0:1", 80); - } - } -} 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 deleted file mode 100644 index 9f9e62d9b3..0000000000 --- a/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksCmdResponseTest.java +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socks; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.CharsetUtil; -import org.junit.jupiter.api.Test; - -import java.net.IDN; -import java.nio.CharBuffer; - -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class SocksCmdResponseTest { - @Test - public void testConstructorParamsAreNotNull() { - try { - new SocksCmdResponse(null, SocksAddressType.UNKNOWN); - } catch (Exception e) { - assertTrue(e instanceof NullPointerException); - } - try { - new SocksCmdResponse(SocksCmdStatus.UNASSIGNED, null); - } catch (Exception e) { - assertTrue(e instanceof NullPointerException); - } - } - - /** - * Verifies content of the response when domain is not specified. - */ - @Test - public void testEmptyDomain() { - SocksCmdResponse socksCmdResponse = new SocksCmdResponse(SocksCmdStatus.SUCCESS, SocksAddressType.DOMAIN); - assertNull(socksCmdResponse.host()); - assertEquals(0, socksCmdResponse.port()); - ByteBuf buffer = Unpooled.buffer(20); - socksCmdResponse.encodeAsByteBuf(buffer); - byte[] expected = { - 0x05, // version - 0x00, // success reply - 0x00, // reserved - 0x03, // address type domain - 0x01, // length of domain - 0x00, // domain value - 0x00, // port value - 0x00 - }; - assertByteBufEquals(expected, buffer); - } - - /** - * Verifies content of the response when IPv4 address is specified. - */ - @Test - public void testIPv4Host() { - SocksCmdResponse socksCmdResponse = new SocksCmdResponse(SocksCmdStatus.SUCCESS, SocksAddressType.IPv4, - "127.0.0.1", 80); - assertEquals("127.0.0.1", socksCmdResponse.host()); - assertEquals(80, socksCmdResponse.port()); - ByteBuf buffer = Unpooled.buffer(20); - socksCmdResponse.encodeAsByteBuf(buffer); - byte[] expected = { - 0x05, // version - 0x00, // success reply - 0x00, // reserved - 0x01, // address type IPv4 - 0x7F, // address 127.0.0.1 - 0x00, - 0x00, - 0x01, - 0x00, // port - 0x50 - }; - assertByteBufEquals(expected, buffer); - } - - /** - * Verifies that empty domain is allowed Response. - */ - @Test - public void testEmptyBoundAddress() { - SocksCmdResponse socksCmdResponse = new SocksCmdResponse(SocksCmdStatus.SUCCESS, SocksAddressType.DOMAIN, - "", 80); - assertEquals("", socksCmdResponse.host()); - assertEquals(80, socksCmdResponse.port()); - ByteBuf buffer = Unpooled.buffer(20); - socksCmdResponse.encodeAsByteBuf(buffer); - byte[] expected = { - 0x05, // version - 0x00, // success reply - 0x00, // reserved - 0x03, // address type domain - 0x00, // domain length - 0x00, // port - 0x50 - }; - assertByteBufEquals(expected, buffer); - } - - @Test - public void testHostNotEncodedForUnknown() { - String asciiHost = "xn--e1aybc.xn--p1ai"; - short port = 10000; - - SocksCmdResponse rs = new SocksCmdResponse(SocksCmdStatus.SUCCESS, SocksAddressType.UNKNOWN, asciiHost, port); - assertEquals(asciiHost, rs.host()); - - ByteBuf buffer = Unpooled.buffer(16); - rs.encodeAsByteBuf(buffer); - - buffer.readerIndex(0); - assertEquals(SocksProtocolVersion.SOCKS5.byteValue(), buffer.readByte()); - assertEquals(SocksCmdStatus.SUCCESS.byteValue(), buffer.readByte()); - assertEquals((byte) 0x00, buffer.readByte()); - assertEquals(SocksAddressType.UNKNOWN.byteValue(), buffer.readByte()); - assertFalse(buffer.isReadable()); - - buffer.release(); - } - - @Test - public void testIDNEncodeToAsciiForDomain() { - String host = "Ņ‚ĐĩŅŅ‚.Ņ€Ņ„"; - CharBuffer asciiHost = CharBuffer.wrap(IDN.toASCII(host)); - short port = 10000; - - SocksCmdResponse rs = new SocksCmdResponse(SocksCmdStatus.SUCCESS, SocksAddressType.DOMAIN, host, port); - assertEquals(host, rs.host()); - - ByteBuf buffer = Unpooled.buffer(24); - rs.encodeAsByteBuf(buffer); - - buffer.readerIndex(0); - assertEquals(SocksProtocolVersion.SOCKS5.byteValue(), buffer.readByte()); - assertEquals(SocksCmdStatus.SUCCESS.byteValue(), buffer.readByte()); - assertEquals((byte) 0x00, buffer.readByte()); - assertEquals(SocksAddressType.DOMAIN.byteValue(), buffer.readByte()); - assertEquals((byte) asciiHost.length(), buffer.readUnsignedByte()); - assertEquals(asciiHost, - CharBuffer.wrap(buffer.readCharSequence(asciiHost.length(), CharsetUtil.US_ASCII))); - assertEquals(port, buffer.readUnsignedShort()); - - buffer.release(); - } - - /** - * Verifies that Response cannot be constructed with invalid IP. - */ - @Test - public void testInvalidBoundAddress() { - assertThrows(IllegalArgumentException.class, - () -> new SocksCmdResponse(SocksCmdStatus.SUCCESS, SocksAddressType.IPv4, "127.0.0", 1000)); - } - - private static void assertByteBufEquals(byte[] expected, ByteBuf actual) { - byte[] actualBytes = new byte[actual.readableBytes()]; - actual.readBytes(actualBytes); - assertEquals(expected.length, actualBytes.length, "Generated response has incorrect length"); - assertArrayEquals(expected, actualBytes, "Generated response differs from expected"); - } - - @Test - public void testValidPortRange() { - try { - new SocksCmdResponse(SocksCmdStatus.SUCCESS, SocksAddressType.IPv4, "127.0.0", 0); - } catch (Exception e) { - assertTrue(e instanceof IllegalArgumentException); - } - - try { - new SocksCmdResponse(SocksCmdStatus.SUCCESS, SocksAddressType.IPv4, "127.0.0", 65536); - } catch (Exception e) { - assertTrue(e instanceof IllegalArgumentException); - } - } -} diff --git a/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksCommonTestUtils.java b/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksCommonTestUtils.java deleted file mode 100644 index cc0a9bc42f..0000000000 --- a/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksCommonTestUtils.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socks; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; - -final class SocksCommonTestUtils { - /** - * A constructor to stop this class being constructed. - */ - private SocksCommonTestUtils() { - //NOOP - } - - @SuppressWarnings("deprecation") - public static void writeMessageIntoEmbedder(EmbeddedChannel embedder, SocksMessage msg) { - ByteBuf buf = Unpooled.buffer(); - msg.encodeAsByteBuf(buf); - embedder.writeInbound(buf); - } -} diff --git a/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksInitRequestTest.java b/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksInitRequestTest.java deleted file mode 100644 index 9b4749f512..0000000000 --- a/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksInitRequestTest.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socks; - -import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class SocksInitRequestTest { - @Test - public void testConstructorParamsAreNotNull() { - try { - new SocksInitRequest(null); - } catch (Exception e) { - assertTrue(e instanceof NullPointerException); - } - } -} diff --git a/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksInitResponseTest.java b/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksInitResponseTest.java deleted file mode 100644 index 7e09505e99..0000000000 --- a/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksInitResponseTest.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socks; - -import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class SocksInitResponseTest { - @Test - public void testConstructorParamsAreNotNull() { - try { - new SocksInitResponse(null); - } catch (Exception e) { - assertTrue(e instanceof NullPointerException); - } - } -} diff --git a/codec-socks/src/test/java/io/netty/handler/codec/socksx/v4/Socks4ClientDecoderTest.java b/codec-socks/src/test/java/io/netty/handler/codec/socksx/v4/Socks4ClientDecoderTest.java deleted file mode 100755 index dca53ea91d..0000000000 --- a/codec-socks/src/test/java/io/netty/handler/codec/socksx/v4/Socks4ClientDecoderTest.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socksx.v4; - -import io.netty.channel.embedded.EmbeddedChannel; -import org.junit.jupiter.api.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; - -public class Socks4ClientDecoderTest { - private static final Logger logger = LoggerFactory.getLogger(Socks4ClientDecoderTest.class); - - private static void test(Socks4CommandStatus cmdStatus, String dstAddr, int dstPort) { - logger.debug("Testing cmdStatus: " + cmdStatus); - Socks4CommandResponse msg = new DefaultSocks4CommandResponse(cmdStatus, dstAddr, dstPort); - EmbeddedChannel embedder = new EmbeddedChannel(new Socks4ClientDecoder()); - Socks4CommonTestUtils.writeMessageIntoEmbedder(embedder, msg); - - msg = embedder.readInbound(); - assertEquals(msg.status(), cmdStatus); - if (dstAddr != null) { - assertEquals(msg.dstAddr(), dstAddr); - } - assertEquals(msg.dstPort(), dstPort); - assertNull(embedder.readInbound()); - } - - /** - * Verifies that sent socks messages are decoded correctly. - */ - @Test - public void testSocksCmdResponseDecoder() { - test(Socks4CommandStatus.IDENTD_AUTH_FAILURE, null, 0); - test(Socks4CommandStatus.IDENTD_UNREACHABLE, null, 0); - test(Socks4CommandStatus.REJECTED_OR_FAILED, null, 0); - test(Socks4CommandStatus.SUCCESS, null, 0); - } -} diff --git a/codec-socks/src/test/java/io/netty/handler/codec/socksx/v4/Socks4CommonTestUtils.java b/codec-socks/src/test/java/io/netty/handler/codec/socksx/v4/Socks4CommonTestUtils.java deleted file mode 100755 index a0f733991c..0000000000 --- a/codec-socks/src/test/java/io/netty/handler/codec/socksx/v4/Socks4CommonTestUtils.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socksx.v4; - -import io.netty.channel.embedded.EmbeddedChannel; - -final class Socks4CommonTestUtils { - /** - * A constructor to stop this class being constructed. - */ - private Socks4CommonTestUtils() { - //NOOP - } - - public static void writeMessageIntoEmbedder(EmbeddedChannel embedder, Socks4Message msg) { - EmbeddedChannel out; - if (msg instanceof Socks4CommandRequest) { - out = new EmbeddedChannel(Socks4ClientEncoder.INSTANCE); - } else { - out = new EmbeddedChannel(Socks4ServerEncoder.INSTANCE); - } - out.writeOutbound(msg); - embedder.writeInbound((Object) out.readOutbound()); - out.finish(); - } -} diff --git a/codec-socks/src/test/java/io/netty/handler/codec/socksx/v4/Socks4ServerDecoderTest.java b/codec-socks/src/test/java/io/netty/handler/codec/socksx/v4/Socks4ServerDecoderTest.java deleted file mode 100755 index 940f2aba31..0000000000 --- a/codec-socks/src/test/java/io/netty/handler/codec/socksx/v4/Socks4ServerDecoderTest.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socksx.v4; - -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; -import org.junit.jupiter.api.Test; - -import java.util.Arrays; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; - -public class Socks4ServerDecoderTest { - private static final InternalLogger logger = InternalLoggerFactory.getInstance(Socks4ServerDecoderTest.class); - - private static void test(String userId, Socks4CommandType type, String dstAddr, int dstPort) { - logger.debug( - "Testing type: " + type + " dstAddr: " + dstAddr + " dstPort: " + dstPort + - " userId: " + userId); - - Socks4CommandRequest msg = new DefaultSocks4CommandRequest(type, dstAddr, dstPort, userId); - EmbeddedChannel embedder = new EmbeddedChannel(new Socks4ServerDecoder()); - Socks4CommonTestUtils.writeMessageIntoEmbedder(embedder, msg); - msg = embedder.readInbound(); - assertSame(msg.type(), type); - assertEquals(msg.dstAddr(), dstAddr); - assertEquals(msg.dstPort(), dstPort); - assertEquals(msg.userId(), userId); - assertNull(embedder.readInbound()); - } - - @Test - public void testCmdRequestDecoder() { - String[] hosts = { "127.0.0.1", }; - String[] userIds = { "test", }; - int[] ports = {1, 32769, 65535}; - - for (Socks4CommandType cmdType : Arrays.asList(Socks4CommandType.BIND, - Socks4CommandType.CONNECT)) { - for (String userId : userIds) { - for (String host : hosts) { - for (int port : ports) { - test(userId, cmdType, host, port); - } - } - } - } - } -} diff --git a/codec-socks/src/test/java/io/netty/handler/codec/socksx/v5/DefaultSocks5CommandRequestTest.java b/codec-socks/src/test/java/io/netty/handler/codec/socksx/v5/DefaultSocks5CommandRequestTest.java deleted file mode 100755 index 3aab28332c..0000000000 --- a/codec-socks/src/test/java/io/netty/handler/codec/socksx/v5/DefaultSocks5CommandRequestTest.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socksx.v5; - -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.*; - -public class DefaultSocks5CommandRequestTest { - @Test - public void testConstructorParamsAreNotNull() { - try { - new DefaultSocks5CommandRequest(null, Socks5AddressType.DOMAIN, "", 1); - } catch (Exception e) { - assertTrue(e instanceof NullPointerException); - } - - try { - new DefaultSocks5CommandRequest(Socks5CommandType.CONNECT, null, "", 1); - } catch (Exception e) { - assertTrue(e instanceof NullPointerException); - } - - try { - new DefaultSocks5CommandRequest(Socks5CommandType.CONNECT, Socks5AddressType.DOMAIN, null, 1); - } catch (Exception e) { - assertTrue(e instanceof NullPointerException); - } - } - - @Test - public void testIPv4CorrectAddress() { - try { - new DefaultSocks5CommandRequest(Socks5CommandType.BIND, Socks5AddressType.IPv4, "54.54.1111.253", 1); - } catch (Exception e) { - assertTrue(e instanceof IllegalArgumentException); - } - } - - @Test - public void testIPv6CorrectAddress() { - try { - new DefaultSocks5CommandRequest(Socks5CommandType.BIND, Socks5AddressType.IPv6, "xxx:xxx:xxx", 1); - } catch (Exception e) { - assertTrue(e instanceof IllegalArgumentException); - } - } - - @Test - public void testIDNNotExceeds255CharsLimit() { - try { - new DefaultSocks5CommandRequest(Socks5CommandType.BIND, Socks5AddressType.DOMAIN, - "Ī€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽĪ€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽĪ€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽĪ€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽ" + - "Ī€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽĪ€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽĪ€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽĪ€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽ" + - "Ī€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽĪ€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽĪ€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽĪ€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽ" + - "Ī€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽĪ€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽĪ€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽĪ€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽ", 1); - } catch (Exception e) { - assertTrue(e instanceof IllegalArgumentException); - } - } - - @Test - public void testValidPortRange() { - try { - new DefaultSocks5CommandRequest(Socks5CommandType.BIND, Socks5AddressType.DOMAIN, - "Ī€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽĪ€ÎąĪÎŦδÎĩΚ", -1); - } catch (Exception e) { - assertTrue(e instanceof IllegalArgumentException); - } - - try { - new DefaultSocks5CommandRequest(Socks5CommandType.BIND, Socks5AddressType.DOMAIN, - "Ī€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽĪ€ÎąĪÎŦδÎĩΚ", 65536); - } catch (Exception e) { - assertTrue(e instanceof IllegalArgumentException); - } - - new DefaultSocks5CommandRequest(Socks5CommandType.BIND, Socks5AddressType.DOMAIN, - "Ī€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽĪ€ÎąĪÎŦδÎĩΚ", 0); - new DefaultSocks5CommandRequest(Socks5CommandType.BIND, Socks5AddressType.DOMAIN, - "Ī€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽĪ€ÎąĪÎŦδÎĩΚ", 65535); - } -} 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 deleted file mode 100755 index 45a66dd50e..0000000000 --- a/codec-socks/src/test/java/io/netty/handler/codec/socksx/v5/DefaultSocks5CommandResponseTest.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socksx.v5; - -import io.netty.buffer.ByteBuf; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class DefaultSocks5CommandResponseTest { - @Test - public void testConstructorParamsAreNotNull() { - try { - new DefaultSocks5CommandResponse(null, Socks5AddressType.DOMAIN); - } catch (Exception e) { - assertTrue(e instanceof NullPointerException); - } - try { - new DefaultSocks5CommandResponse(Socks5CommandStatus.FAILURE, null); - } catch (Exception e) { - assertTrue(e instanceof NullPointerException); - } - } - - /** - * Verifies content of the response when domain is not specified. - */ - @Test - public void testEmptyDomain() { - Socks5CommandResponse socks5CmdResponse = new DefaultSocks5CommandResponse( - Socks5CommandStatus.SUCCESS, Socks5AddressType.DOMAIN); - assertNull(socks5CmdResponse.bndAddr()); - assertEquals(0, socks5CmdResponse.bndPort()); - - ByteBuf buffer = Socks5CommonTestUtils.encodeServer(socks5CmdResponse); - byte[] expected = { - 0x05, // version - 0x00, // success reply - 0x00, // reserved - 0x03, // address type domain - 0x00, // length of domain - 0x00, // port value - 0x00 - }; - assertByteBufEquals(expected, buffer); - buffer.release(); - } - - /** - * Verifies content of the response when IPv4 address is specified. - */ - @Test - public void testIPv4Host() { - Socks5CommandResponse socks5CmdResponse = new DefaultSocks5CommandResponse( - Socks5CommandStatus.SUCCESS, Socks5AddressType.IPv4, "127.0.0.1", 80); - assertEquals("127.0.0.1", socks5CmdResponse.bndAddr()); - assertEquals(80, socks5CmdResponse.bndPort()); - - ByteBuf buffer = Socks5CommonTestUtils.encodeServer(socks5CmdResponse); - byte[] expected = { - 0x05, // version - 0x00, // success reply - 0x00, // reserved - 0x01, // address type IPv4 - 0x7F, // address 127.0.0.1 - 0x00, - 0x00, - 0x01, - 0x00, // port - 0x50 - }; - assertByteBufEquals(expected, buffer); - buffer.release(); - } - - /** - * Verifies that empty domain is allowed Response. - */ - @Test - public void testEmptyBoundAddress() { - Socks5CommandResponse socks5CmdResponse = new DefaultSocks5CommandResponse( - Socks5CommandStatus.SUCCESS, Socks5AddressType.DOMAIN, "", 80); - assertEquals("", socks5CmdResponse.bndAddr()); - assertEquals(80, socks5CmdResponse.bndPort()); - - ByteBuf buffer = Socks5CommonTestUtils.encodeServer(socks5CmdResponse); - byte[] expected = { - 0x05, // version - 0x00, // success reply - 0x00, // reserved - 0x03, // address type domain - 0x00, // domain length - 0x00, // port - 0x50 - }; - assertByteBufEquals(expected, buffer); - buffer.release(); - } - - /** - * Verifies that Response cannot be constructed with invalid IP. - */ - @Test - public void testInvalidBoundAddress() { - assertThrows(IllegalArgumentException.class, () -> new DefaultSocks5CommandResponse( - Socks5CommandStatus.SUCCESS, Socks5AddressType.IPv4, "127.0.0", 1000)); - } - - private static void assertByteBufEquals(byte[] expected, ByteBuf actual) { - byte[] actualBytes = new byte[actual.readableBytes()]; - actual.readBytes(actualBytes); - assertEquals(expected.length, actualBytes.length, "Generated response has incorrect length"); - assertArrayEquals(expected, actualBytes, "Generated response differs from expected"); - } - - @Test - public void testValidPortRange() { - try { - new DefaultSocks5CommandResponse(Socks5CommandStatus.SUCCESS, Socks5AddressType.IPv4, "127.0.0", 0); - } catch (Exception e) { - assertTrue(e instanceof IllegalArgumentException); - } - - try { - new DefaultSocks5CommandResponse(Socks5CommandStatus.SUCCESS, Socks5AddressType.IPv4, "127.0.0", 65536); - } catch (Exception e) { - assertTrue(e instanceof IllegalArgumentException); - } - } -} diff --git a/codec-socks/src/test/java/io/netty/handler/codec/socksx/v5/DefaultSocks5InitialRequestTest.java b/codec-socks/src/test/java/io/netty/handler/codec/socksx/v5/DefaultSocks5InitialRequestTest.java deleted file mode 100755 index 49e3824b91..0000000000 --- a/codec-socks/src/test/java/io/netty/handler/codec/socksx/v5/DefaultSocks5InitialRequestTest.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socksx.v5; - -import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class DefaultSocks5InitialRequestTest { - @Test - public void testConstructorParamsAreNotEmpty() { - try { - new DefaultSocks5InitialRequest(); - } catch (Exception e) { - assertTrue(e instanceof IllegalArgumentException); - } - } -} diff --git a/codec-socks/src/test/java/io/netty/handler/codec/socksx/v5/DefaultSocks5InitialResponseTest.java b/codec-socks/src/test/java/io/netty/handler/codec/socksx/v5/DefaultSocks5InitialResponseTest.java deleted file mode 100755 index ffc59e9562..0000000000 --- a/codec-socks/src/test/java/io/netty/handler/codec/socksx/v5/DefaultSocks5InitialResponseTest.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socksx.v5; - -import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class DefaultSocks5InitialResponseTest { - @Test - public void testConstructorParamsAreNotNull() { - try { - new DefaultSocks5InitialResponse(null); - } catch (Exception e) { - assertTrue(e instanceof NullPointerException); - } - } -} diff --git a/codec-socks/src/test/java/io/netty/handler/codec/socksx/v5/DefaultSocks5PasswordAuthRequestTest.java b/codec-socks/src/test/java/io/netty/handler/codec/socksx/v5/DefaultSocks5PasswordAuthRequestTest.java deleted file mode 100755 index 6c9d40fae6..0000000000 --- a/codec-socks/src/test/java/io/netty/handler/codec/socksx/v5/DefaultSocks5PasswordAuthRequestTest.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socksx.v5; - -import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class DefaultSocks5PasswordAuthRequestTest { - @Test - public void testConstructorParamsAreNotNull() { - try { - new DefaultSocks5PasswordAuthRequest(null, ""); - } catch (Exception e) { - assertTrue(e instanceof NullPointerException); - } - try { - new DefaultSocks5PasswordAuthRequest("", null); - } catch (Exception e) { - assertTrue(e instanceof NullPointerException); - } - } - - @Test - public void testUsernameOrPasswordIsNotAscii() { - try { - new DefaultSocks5PasswordAuthRequest("Ī€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽ", "password"); - } catch (Exception e) { - assertTrue(e instanceof IllegalArgumentException); - } - try { - new DefaultSocks5PasswordAuthRequest("username", "Ī€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽ"); - } catch (Exception e) { - assertTrue(e instanceof IllegalArgumentException); - } - } - - @Test - public void testUsernameOrPasswordLengthIsLessThan255Chars() { - try { - new DefaultSocks5PasswordAuthRequest( - "passwordpasswordpasswordpasswordpasswordpasswordpassword" + - "passwordpasswordpasswordpasswordpasswordpasswordpassword" + - "passwordpasswordpasswordpasswordpasswordpasswordpassword" + - "passwordpasswordpasswordpasswordpasswordpasswordpassword" + - "passwordpasswordpasswordpasswordpasswordpasswordpassword" + - "passwordpasswordpasswordpasswordpasswordpasswordpassword" + - "passwordpasswordpasswordpasswordpasswordpasswordpassword" + - "passwordpasswordpasswordpasswordpasswordpasswordpassword", - "password"); - } catch (Exception e) { - assertTrue(e instanceof IllegalArgumentException); - } - try { - new DefaultSocks5PasswordAuthRequest("password", - "passwordpasswordpasswordpasswordpasswordpasswordpassword" + - "passwordpasswordpasswordpasswordpasswordpasswordpassword" + - "passwordpasswordpasswordpasswordpasswordpasswordpassword" + - "passwordpasswordpasswordpasswordpasswordpasswordpassword" + - "passwordpasswordpasswordpasswordpasswordpasswordpassword" + - "passwordpasswordpasswordpasswordpasswordpasswordpassword" + - "passwordpasswordpasswordpasswordpasswordpasswordpassword" + - "passwordpasswordpasswordpasswordpasswordpasswordpassword"); - } catch (Exception e) { - assertTrue(e instanceof IllegalArgumentException); - } - } -} diff --git a/codec-socks/src/test/java/io/netty/handler/codec/socksx/v5/DefaultSocks5PasswordAuthResponseTest.java b/codec-socks/src/test/java/io/netty/handler/codec/socksx/v5/DefaultSocks5PasswordAuthResponseTest.java deleted file mode 100755 index e6c477450f..0000000000 --- a/codec-socks/src/test/java/io/netty/handler/codec/socksx/v5/DefaultSocks5PasswordAuthResponseTest.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socksx.v5; - -import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class DefaultSocks5PasswordAuthResponseTest { - @Test - public void testConstructorParamsAreNotNull() { - try { - new DefaultSocks5PasswordAuthResponse(null); - } catch (Exception e) { - assertTrue(e instanceof NullPointerException); - } - } -} diff --git a/codec-socks/src/test/java/io/netty/handler/codec/socksx/v5/Socks5CommandRequestDecoderTest.java b/codec-socks/src/test/java/io/netty/handler/codec/socksx/v5/Socks5CommandRequestDecoderTest.java deleted file mode 100755 index 9deae37caa..0000000000 --- a/codec-socks/src/test/java/io/netty/handler/codec/socksx/v5/Socks5CommandRequestDecoderTest.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socksx.v5; - -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.NetUtil; -import io.netty.util.internal.SocketUtils; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; -import org.junit.jupiter.api.Test; - -import java.net.IDN; -import java.net.UnknownHostException; -import java.util.Arrays; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; - -public class Socks5CommandRequestDecoderTest { - private static final InternalLogger logger = - InternalLoggerFactory.getInstance(Socks5CommandRequestDecoderTest.class); - - private static void test( - Socks5CommandType type, Socks5AddressType dstAddrType, String dstAddr, int dstPort) { - logger.debug( - "Testing type: " + type + " dstAddrType: " + dstAddrType + - " dstAddr: " + dstAddr + " dstPort: " + dstPort); - - Socks5CommandRequest msg = - new DefaultSocks5CommandRequest(type, dstAddrType, dstAddr, dstPort); - EmbeddedChannel embedder = new EmbeddedChannel(new Socks5CommandRequestDecoder()); - Socks5CommonTestUtils.writeFromClientToServer(embedder, msg); - msg = embedder.readInbound(); - assertSame(msg.type(), type); - assertSame(msg.dstAddrType(), dstAddrType); - assertEquals(msg.dstAddr(), IDN.toASCII(dstAddr)); - assertEquals(msg.dstPort(), dstPort); - assertNull(embedder.readInbound()); - } - - @Test - public void testCmdRequestDecoderIPv4() { - String[] hosts = {"127.0.0.1", }; - int[] ports = {1, 32769, 65535 }; - for (Socks5CommandType cmdType: Arrays.asList(Socks5CommandType.BIND, - Socks5CommandType.CONNECT, - Socks5CommandType.UDP_ASSOCIATE)) { - for (String host : hosts) { - for (int port : ports) { - test(cmdType, Socks5AddressType.IPv4, host, port); - } - } - } - } - - @Test - public void testCmdRequestDecoderIPv6() throws UnknownHostException { - String[] hosts = { - NetUtil.bytesToIpAddress(SocketUtils.addressByName("::1").getAddress()) }; - int[] ports = {1, 32769, 65535}; - for (Socks5CommandType cmdType: Arrays.asList(Socks5CommandType.BIND, - Socks5CommandType.CONNECT, - Socks5CommandType.UDP_ASSOCIATE)) { - for (String host : hosts) { - for (int port : ports) { - test(cmdType, Socks5AddressType.IPv6, host, port); - } - } - } - } - - @Test - public void testCmdRequestDecoderDomain() { - String[] hosts = {"google.com" , - "Ų…ØĢاŲ„.ØĨØŽØĒØ¨Ø§Øą", - "Ī€ÎąĪÎŦδÎĩΚÎŗÎŧÎą.δÎŋÎēΚÎŧÎŽ", - "Ų…ØĢاŲ„.ØĸØ˛Ų…ایشی", - "ĐŋŅ€Đ¸ĐŧĐĩŅ€.иŅĐŋŅ‹Ņ‚Đ°ĐŊиĐĩ", - "בײַשפÖŧיל.ט×ĸץט", - "䞋子.æĩ‹č¯•", - "䞋子.æ¸ŦčŠĻ", - "ā¤‰ā¤Ļā¤žā¤šā¤°ā¤Ŗ.ā¤Ēā¤°āĨ€ā¤•āĨā¤ˇā¤ž", - "䞋え.テ゚ト", - "ė‹¤ëĄ€.테ėŠ¤íŠ¸", - "āŽ‰āŽ¤āŽžāŽ°āŽŖāŽŽā¯.āŽĒāŽ°āŽŋāŽŸā¯āŽšā¯ˆ"}; - int[] ports = {1, 32769, 65535}; - for (Socks5CommandType cmdType: Arrays.asList(Socks5CommandType.BIND, - Socks5CommandType.CONNECT, - Socks5CommandType.UDP_ASSOCIATE)) { - for (String host : hosts) { - for (int port : ports) { - test(cmdType, Socks5AddressType.DOMAIN, host, port); - } - } - } - } -} diff --git a/codec-socks/src/test/java/io/netty/handler/codec/socksx/v5/Socks5CommandResponseDecoderTest.java b/codec-socks/src/test/java/io/netty/handler/codec/socksx/v5/Socks5CommandResponseDecoderTest.java deleted file mode 100755 index 4c5cae52cd..0000000000 --- a/codec-socks/src/test/java/io/netty/handler/codec/socksx/v5/Socks5CommandResponseDecoderTest.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socksx.v5; - -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; -import org.junit.jupiter.api.Test; - -import java.util.Arrays; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; - -public class Socks5CommandResponseDecoderTest { - - private static final InternalLogger logger = - InternalLoggerFactory.getInstance(Socks5CommandResponseDecoderTest.class); - - private static final Socks5CommandStatus[] STATUSES = { - Socks5CommandStatus.ADDRESS_UNSUPPORTED, - Socks5CommandStatus.COMMAND_UNSUPPORTED, - Socks5CommandStatus.CONNECTION_REFUSED, - Socks5CommandStatus.FAILURE, - Socks5CommandStatus.FORBIDDEN, - Socks5CommandStatus.HOST_UNREACHABLE, - Socks5CommandStatus.NETWORK_UNREACHABLE, - Socks5CommandStatus.SUCCESS, - Socks5CommandStatus.TTL_EXPIRED - }; - - private static void test( - Socks5CommandStatus status, Socks5AddressType bndAddrType, String bndAddr, int bndPort) { - logger.debug("Testing status: " + status + " bndAddrType: " + bndAddrType); - Socks5CommandResponse msg = - new DefaultSocks5CommandResponse(status, bndAddrType, bndAddr, bndPort); - EmbeddedChannel embedder = new EmbeddedChannel(new Socks5CommandResponseDecoder()); - Socks5CommonTestUtils.writeFromServerToClient(embedder, msg); - msg = embedder.readInbound(); - assertEquals(msg.status(), status); - if (bndAddr != null) { - assertEquals(msg.bndAddr(), bndAddr); - } - assertEquals(msg.bndPort(), bndPort); - assertNull(embedder.readInbound()); - } - - /** - * Verifies that sent socks messages are decoded correctly. - */ - @Test - public void testSocksCmdResponseDecoder() { - for (Socks5CommandStatus cmdStatus: STATUSES) { - for (Socks5AddressType addressType : Arrays.asList(Socks5AddressType.DOMAIN, - Socks5AddressType.IPv4, - Socks5AddressType.IPv6)) { - test(cmdStatus, addressType, null, 0); - } - } - } - - /** - * Verifies that invalid bound host will fail with IllegalArgumentException during encoding. - */ - @Test - public void testInvalidAddress() { - assertThrows(IllegalArgumentException.class, - () -> test(Socks5CommandStatus.SUCCESS, Socks5AddressType.IPv4, "1", 80)); - } - - /** - * Verifies that send socks messages are decoded correctly when bound host and port are set. - */ - @Test - public void testSocksCmdResponseDecoderIncludingHost() { - for (Socks5CommandStatus cmdStatus : STATUSES) { - test(cmdStatus, Socks5AddressType.IPv4, - "127.0.0.1", 80); - test(cmdStatus, Socks5AddressType.DOMAIN, - "testDomain.com", 80); - test(cmdStatus, Socks5AddressType.IPv6, - "2001:db8:85a3:42:1000:8a2e:370:7334", 80); - test(cmdStatus, Socks5AddressType.IPv6, - "1111:111:11:1::1", 80); - } - } -} diff --git a/codec-socks/src/test/java/io/netty/handler/codec/socksx/v5/Socks5CommonTestUtils.java b/codec-socks/src/test/java/io/netty/handler/codec/socksx/v5/Socks5CommonTestUtils.java deleted file mode 100755 index fe7a220dd7..0000000000 --- a/codec-socks/src/test/java/io/netty/handler/codec/socksx/v5/Socks5CommonTestUtils.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socksx.v5; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.embedded.EmbeddedChannel; - -final class Socks5CommonTestUtils { - /** - * A constructor to stop this class being constructed. - */ - private Socks5CommonTestUtils() { - //NOOP - } - - public static void writeFromClientToServer(EmbeddedChannel embedder, Socks5Message msg) { - embedder.writeInbound(encodeClient(msg)); - } - - public static void writeFromServerToClient(EmbeddedChannel embedder, Socks5Message msg) { - embedder.writeInbound(encodeServer(msg)); - } - - public static ByteBuf encodeClient(Socks5Message msg) { - EmbeddedChannel out = new EmbeddedChannel(Socks5ClientEncoder.DEFAULT); - out.writeOutbound(msg); - - ByteBuf encoded = out.readOutbound(); - out.finish(); - - return encoded; - } - - public static ByteBuf encodeServer(Socks5Message msg) { - EmbeddedChannel out = new EmbeddedChannel(Socks5ServerEncoder.DEFAULT); - out.writeOutbound(msg); - - ByteBuf encoded = out.readOutbound(); - out.finish(); - - return encoded; - } -} diff --git a/codec-socks/src/test/java/io/netty/handler/codec/socksx/v5/Socks5InitialRequestDecoderTest.java b/codec-socks/src/test/java/io/netty/handler/codec/socksx/v5/Socks5InitialRequestDecoderTest.java deleted file mode 100644 index c5e58f1c00..0000000000 --- a/codec-socks/src/test/java/io/netty/handler/codec/socksx/v5/Socks5InitialRequestDecoderTest.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socksx.v5; - -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.DecoderResult; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class Socks5InitialRequestDecoderTest { - @Test - public void testUnpackingCausesDecodeFail() { - EmbeddedChannel e = new EmbeddedChannel(new Socks5InitialRequestDecoder()); - assertFalse(e.writeInbound(Unpooled.wrappedBuffer(new byte[]{5, 2, 0}))); - assertTrue(e.writeInbound(Unpooled.wrappedBuffer(new byte[]{1}))); - Object o = e.readInbound(); - - assertTrue(o instanceof DefaultSocks5InitialRequest); - DefaultSocks5InitialRequest req = (DefaultSocks5InitialRequest) o; - assertSame(req.decoderResult(), DecoderResult.SUCCESS); - assertFalse(e.finish()); - } -} diff --git a/codec-socks/src/test/java/io/netty/handler/codec/socksx/v5/Socks5PasswordAuthRequestDecoderTest.java b/codec-socks/src/test/java/io/netty/handler/codec/socksx/v5/Socks5PasswordAuthRequestDecoderTest.java deleted file mode 100755 index 6effd29c0b..0000000000 --- a/codec-socks/src/test/java/io/netty/handler/codec/socksx/v5/Socks5PasswordAuthRequestDecoderTest.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socksx.v5; - -import io.netty.channel.embedded.EmbeddedChannel; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; - -public class Socks5PasswordAuthRequestDecoderTest { - - @Test - public void testAuthRequestDecoder() { - String username = "testUsername"; - String password = "testPassword"; - Socks5PasswordAuthRequest msg = new DefaultSocks5PasswordAuthRequest(username, password); - EmbeddedChannel embedder = new EmbeddedChannel(new Socks5PasswordAuthRequestDecoder()); - Socks5CommonTestUtils.writeFromClientToServer(embedder, msg); - msg = embedder.readInbound(); - assertEquals(username, msg.username()); - assertEquals(password, msg.password()); - assertNull(embedder.readInbound()); - } -} diff --git a/codec-socks/src/test/java/io/netty/handler/codec/socksx/v5/Socks5PasswordAuthResponseDecoderTest.java b/codec-socks/src/test/java/io/netty/handler/codec/socksx/v5/Socks5PasswordAuthResponseDecoderTest.java deleted file mode 100755 index 5c4e4a4484..0000000000 --- a/codec-socks/src/test/java/io/netty/handler/codec/socksx/v5/Socks5PasswordAuthResponseDecoderTest.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.socksx.v5; - -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; - -public class Socks5PasswordAuthResponseDecoderTest { - private static final InternalLogger logger = InternalLoggerFactory.getInstance( - Socks5PasswordAuthResponseDecoderTest.class); - - private static void test(Socks5PasswordAuthStatus status) { - logger.debug("Testing Socks5PasswordAuthResponseDecoder with status: " + status); - Socks5PasswordAuthResponse msg = new DefaultSocks5PasswordAuthResponse(status); - EmbeddedChannel embedder = new EmbeddedChannel(new Socks5PasswordAuthResponseDecoder()); - Socks5CommonTestUtils.writeFromServerToClient(embedder, msg); - msg = embedder.readInbound(); - assertSame(msg.status(), status); - assertNull(embedder.readInbound()); - } - - @Test - public void testSocksCmdResponseDecoder() { - test(Socks5PasswordAuthStatus.SUCCESS); - test(Socks5PasswordAuthStatus.FAILURE); - } -} diff --git a/codec-stomp/pom.xml b/codec-stomp/pom.xml deleted file mode 100644 index 82140cc4c6..0000000000 --- a/codec-stomp/pom.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - 4.0.0 - - io.netty - netty-parent - 5.0.0.Final-SNAPSHOT - - - netty-codec-stomp - jar - - Netty/Codec/Stomp - - - io.netty.codec.stomp - - - - - ${project.groupId} - netty-common - ${project.version} - - - ${project.groupId} - netty-buffer - ${project.version} - - - ${project.groupId} - netty-transport - ${project.version} - - - ${project.groupId} - netty-codec - ${project.version} - - - - diff --git a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/DefaultLastStompContentSubframe.java b/codec-stomp/src/main/java/io/netty/handler/codec/stomp/DefaultLastStompContentSubframe.java deleted file mode 100644 index d1c79ed1ad..0000000000 --- a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/DefaultLastStompContentSubframe.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.stomp; - -import io.netty.buffer.ByteBuf; - -/** - * The default implementation for the {@link LastStompContentSubframe}. - */ -public class DefaultLastStompContentSubframe extends DefaultStompContentSubframe implements LastStompContentSubframe { - - public DefaultLastStompContentSubframe(ByteBuf content) { - super(content); - } - - @Override - public LastStompContentSubframe copy() { - return (LastStompContentSubframe) super.copy(); - } - - @Override - public LastStompContentSubframe duplicate() { - return (LastStompContentSubframe) super.duplicate(); - } - - @Override - public LastStompContentSubframe retainedDuplicate() { - return (LastStompContentSubframe) super.retainedDuplicate(); - } - - @Override - public LastStompContentSubframe replace(ByteBuf content) { - return new DefaultLastStompContentSubframe(content); - } - - @Override - public DefaultLastStompContentSubframe retain() { - super.retain(); - return this; - } - - @Override - public LastStompContentSubframe retain(int increment) { - super.retain(increment); - return this; - } - - @Override - public LastStompContentSubframe touch() { - super.touch(); - return this; - } - - @Override - public LastStompContentSubframe touch(Object hint) { - super.touch(hint); - return this; - } - - @Override - public String toString() { - return "DefaultLastStompContent{" + - "decoderResult=" + decoderResult() + - '}'; - } -} diff --git a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/DefaultStompContentSubframe.java b/codec-stomp/src/main/java/io/netty/handler/codec/stomp/DefaultStompContentSubframe.java deleted file mode 100644 index cc823d98f2..0000000000 --- a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/DefaultStompContentSubframe.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.stomp; - - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.DefaultByteBufHolder; -import io.netty.handler.codec.DecoderResult; - -/** - * The default {@link StompContentSubframe} implementation. - */ -public class DefaultStompContentSubframe extends DefaultByteBufHolder implements StompContentSubframe { - private DecoderResult decoderResult = DecoderResult.SUCCESS; - - public DefaultStompContentSubframe(ByteBuf content) { - super(content); - } - - @Override - public StompContentSubframe copy() { - return (StompContentSubframe) super.copy(); - } - - @Override - public StompContentSubframe duplicate() { - return (StompContentSubframe) super.duplicate(); - } - - @Override - public StompContentSubframe retainedDuplicate() { - return (StompContentSubframe) super.retainedDuplicate(); - } - - @Override - public StompContentSubframe replace(ByteBuf content) { - return new DefaultStompContentSubframe(content); - } - - @Override - public StompContentSubframe retain() { - super.retain(); - return this; - } - - @Override - public StompContentSubframe retain(int increment) { - super.retain(increment); - return this; - } - - @Override - public StompContentSubframe touch() { - super.touch(); - return this; - } - - @Override - public StompContentSubframe touch(Object hint) { - super.touch(hint); - return this; - } - - @Override - public DecoderResult decoderResult() { - return decoderResult; - } - - @Override - public void setDecoderResult(DecoderResult decoderResult) { - this.decoderResult = decoderResult; - } - - @Override - public String toString() { - return "DefaultStompContent{" + - "decoderResult=" + decoderResult + - '}'; - } -} diff --git a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/DefaultStompFrame.java b/codec-stomp/src/main/java/io/netty/handler/codec/stomp/DefaultStompFrame.java deleted file mode 100644 index 42ac309e70..0000000000 --- a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/DefaultStompFrame.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.stomp; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.CharsetUtil; - -/** - * Default implementation of {@link StompFrame}. - */ -public class DefaultStompFrame extends DefaultStompHeadersSubframe implements StompFrame { - - private final ByteBuf content; - - public DefaultStompFrame(StompCommand command) { - this(command, Unpooled.buffer(0)); - } - - public DefaultStompFrame(StompCommand command, ByteBuf content) { - this(command, content, null); - } - - DefaultStompFrame(StompCommand command, ByteBuf content, DefaultStompHeaders headers) { - super(command, headers); - requireNonNull(content, "content"); - - this.content = content; - } - - @Override - public ByteBuf content() { - return content; - } - - @Override - public StompFrame copy() { - return replace(content.copy()); - } - - @Override - public StompFrame duplicate() { - return replace(content.duplicate()); - } - - @Override - public StompFrame retainedDuplicate() { - return replace(content.retainedDuplicate()); - } - - @Override - public StompFrame replace(ByteBuf content) { - return new DefaultStompFrame(command, content, headers.copy()); - } - - @Override - public int refCnt() { - return content.refCnt(); - } - - @Override - public StompFrame retain() { - content.retain(); - return this; - } - - @Override - public StompFrame retain(int increment) { - content.retain(increment); - return this; - } - - @Override - public StompFrame touch() { - content.touch(); - return this; - } - - @Override - public StompFrame touch(Object hint) { - content.touch(hint); - return this; - } - - @Override - public boolean release() { - return content.release(); - } - - @Override - public boolean release(int decrement) { - return content.release(decrement); - } - - @Override - public String toString() { - return "DefaultStompFrame{" + - "command=" + command + - ", headers=" + headers + - ", content=" + content.toString(CharsetUtil.UTF_8) + - '}'; - } -} diff --git a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/DefaultStompHeaders.java b/codec-stomp/src/main/java/io/netty/handler/codec/stomp/DefaultStompHeaders.java deleted file mode 100644 index 6ffbe334f8..0000000000 --- a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/DefaultStompHeaders.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.stomp; - -import java.util.Iterator; -import java.util.List; -import java.util.Map.Entry; - -import io.netty.handler.codec.CharSequenceValueConverter; -import io.netty.handler.codec.DefaultHeaders; -import io.netty.handler.codec.HeadersUtils; - -import static io.netty.util.AsciiString.CASE_INSENSITIVE_HASHER; -import static io.netty.util.AsciiString.CASE_SENSITIVE_HASHER; - -public class DefaultStompHeaders - extends DefaultHeaders implements StompHeaders { - public DefaultStompHeaders() { - super(CASE_SENSITIVE_HASHER, CharSequenceValueConverter.INSTANCE); - } - - @Override - public String getAsString(CharSequence name) { - return HeadersUtils.getAsString(this, name); - } - - @Override - public List getAllAsString(CharSequence name) { - return HeadersUtils.getAllAsString(this, name); - } - - @Override - public Iterator> iteratorAsString() { - return HeadersUtils.iteratorAsString(this); - } - - @Override - public boolean contains(CharSequence name, CharSequence value) { - return contains(name, value, false); - } - - @Override - public boolean contains(CharSequence name, CharSequence value, boolean ignoreCase) { - return contains(name, value, - ignoreCase ? CASE_INSENSITIVE_HASHER : CASE_SENSITIVE_HASHER); - } - - @Override - public DefaultStompHeaders copy() { - DefaultStompHeaders copyHeaders = new DefaultStompHeaders(); - copyHeaders.addImpl(this); - return copyHeaders; - } -} diff --git a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/DefaultStompHeadersSubframe.java b/codec-stomp/src/main/java/io/netty/handler/codec/stomp/DefaultStompHeadersSubframe.java deleted file mode 100644 index 40b7486d0f..0000000000 --- a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/DefaultStompHeadersSubframe.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.stomp; - -import static java.util.Objects.requireNonNull; - -import io.netty.handler.codec.DecoderResult; - -/** - * Default implementation of {@link StompHeadersSubframe}. - */ -public class DefaultStompHeadersSubframe implements StompHeadersSubframe { - - protected final StompCommand command; - protected DecoderResult decoderResult = DecoderResult.SUCCESS; - protected final DefaultStompHeaders headers; - - public DefaultStompHeadersSubframe(StompCommand command) { - this(command, null); - } - - DefaultStompHeadersSubframe(StompCommand command, DefaultStompHeaders headers) { - requireNonNull(command, "command"); - - this.command = command; - this.headers = headers == null ? new DefaultStompHeaders() : headers; - } - - @Override - public StompCommand command() { - return command; - } - - @Override - public StompHeaders headers() { - return headers; - } - - @Override - public DecoderResult decoderResult() { - return decoderResult; - } - - @Override - public void setDecoderResult(DecoderResult decoderResult) { - this.decoderResult = decoderResult; - } - - @Override - public String toString() { - return "StompFrame{" + - "command=" + command + - ", headers=" + headers + - '}'; - } -} diff --git a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/LastStompContentSubframe.java b/codec-stomp/src/main/java/io/netty/handler/codec/stomp/LastStompContentSubframe.java deleted file mode 100644 index 7adb27d421..0000000000 --- a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/LastStompContentSubframe.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.stomp; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.handler.codec.DecoderResult; - -/** - * The last {@link StompContentSubframe} which signals the end of the content batch - *

- * Note, even when no content is emitted by the protocol, an - * empty {@link LastStompContentSubframe} is issued to make the upstream parsing - * easier. - */ -public interface LastStompContentSubframe extends StompContentSubframe { - - LastStompContentSubframe EMPTY_LAST_CONTENT = new LastStompContentSubframe() { - @Override - public ByteBuf content() { - return Unpooled.EMPTY_BUFFER; - } - - @Override - public LastStompContentSubframe copy() { - return EMPTY_LAST_CONTENT; - } - - @Override - public LastStompContentSubframe duplicate() { - return this; - } - - @Override - public LastStompContentSubframe retainedDuplicate() { - return this; - } - - @Override - public LastStompContentSubframe replace(ByteBuf content) { - return new DefaultLastStompContentSubframe(content); - } - - @Override - public LastStompContentSubframe retain() { - return this; - } - - @Override - public LastStompContentSubframe retain(int increment) { - return this; - } - - @Override - public LastStompContentSubframe touch() { - return this; - } - - @Override - public LastStompContentSubframe touch(Object hint) { - return this; - } - - @Override - public int refCnt() { - return 1; - } - - @Override - public boolean release() { - return false; - } - - @Override - public boolean release(int decrement) { - return false; - } - - @Override - public DecoderResult decoderResult() { - return DecoderResult.SUCCESS; - } - - @Override - public void setDecoderResult(DecoderResult result) { - throw new UnsupportedOperationException("read only"); - } - }; - - @Override - LastStompContentSubframe copy(); - - @Override - LastStompContentSubframe duplicate(); - - @Override - LastStompContentSubframe retainedDuplicate(); - - @Override - LastStompContentSubframe replace(ByteBuf content); - - @Override - LastStompContentSubframe retain(); - - @Override - LastStompContentSubframe retain(int increment); - - @Override - LastStompContentSubframe touch(); - - @Override - LastStompContentSubframe touch(Object hint); -} diff --git a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompCommand.java b/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompCommand.java deleted file mode 100644 index 3d8587fb8b..0000000000 --- a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompCommand.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.stomp; - -/** - * STOMP command - */ -public enum StompCommand { - STOMP, - CONNECT, - CONNECTED, - SEND, - SUBSCRIBE, - UNSUBSCRIBE, - ACK, - NACK, - BEGIN, - ABORT, - COMMIT, - DISCONNECT, - MESSAGE, - RECEIPT, - ERROR, - UNKNOWN -} diff --git a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompConstants.java b/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompConstants.java deleted file mode 100644 index d0d16e63e9..0000000000 --- a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompConstants.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.stomp; - -final class StompConstants { - - static final byte CR = 13; - static final byte LF = 10; - static final byte NUL = 0; - static final byte COLON = 58; - - private StompConstants() { } -} diff --git a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompContentSubframe.java b/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompContentSubframe.java deleted file mode 100644 index 9ff6f2841a..0000000000 --- a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompContentSubframe.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.stomp; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufHolder; -import io.netty.channel.ChannelPipeline; - -/** - * An STOMP chunk which is used for STOMP chunked transfer-encoding. {@link StompSubframeDecoder} generates - * {@link StompContentSubframe} after {@link StompHeadersSubframe} when the content is large or the encoding of - * the content is 'chunked. If you prefer not to receive multiple {@link StompSubframe}s for a single - * {@link StompFrame}, place {@link StompSubframeAggregator} after {@link StompSubframeDecoder} in the - * {@link ChannelPipeline}. - */ -public interface StompContentSubframe extends ByteBufHolder, StompSubframe { - @Override - StompContentSubframe copy(); - - @Override - StompContentSubframe duplicate(); - - @Override - StompContentSubframe retainedDuplicate(); - - @Override - StompContentSubframe replace(ByteBuf content); - - @Override - StompContentSubframe retain(); - - @Override - StompContentSubframe retain(int increment); - - @Override - StompContentSubframe touch(); - - @Override - StompContentSubframe touch(Object hint); -} diff --git a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompFrame.java b/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompFrame.java deleted file mode 100644 index 1fc2aba673..0000000000 --- a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompFrame.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.stomp; - -import io.netty.buffer.ByteBuf; - -/** - * Combines {@link StompHeadersSubframe} and {@link LastStompContentSubframe} into one - * frame. So it represent a complete STOMP frame. - */ -public interface StompFrame extends StompHeadersSubframe, LastStompContentSubframe { - @Override - StompFrame copy(); - - @Override - StompFrame duplicate(); - - @Override - StompFrame retainedDuplicate(); - - @Override - StompFrame replace(ByteBuf content); - - @Override - StompFrame retain(); - - @Override - StompFrame retain(int increment); - - @Override - StompFrame touch(); - - @Override - StompFrame touch(Object hint); -} diff --git a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompHeaders.java b/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompHeaders.java deleted file mode 100644 index f6822c7cd2..0000000000 --- a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompHeaders.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.stomp; - -import io.netty.handler.codec.Headers; -import io.netty.util.AsciiString; - -import java.util.Iterator; -import java.util.List; -import java.util.Map.Entry; - -/** - * The multimap data structure for the STOMP header names and values. It also provides the constants for the standard - * STOMP header names and values. - */ -public interface StompHeaders extends Headers { - - AsciiString ACCEPT_VERSION = AsciiString.cached("accept-version"); - AsciiString HOST = AsciiString.cached("host"); - AsciiString LOGIN = AsciiString.cached("login"); - AsciiString PASSCODE = AsciiString.cached("passcode"); - AsciiString HEART_BEAT = AsciiString.cached("heart-beat"); - AsciiString VERSION = AsciiString.cached("version"); - AsciiString SESSION = AsciiString.cached("session"); - AsciiString SERVER = AsciiString.cached("server"); - AsciiString DESTINATION = AsciiString.cached("destination"); - AsciiString ID = AsciiString.cached("id"); - AsciiString ACK = AsciiString.cached("ack"); - AsciiString TRANSACTION = AsciiString.cached("transaction"); - AsciiString RECEIPT = AsciiString.cached("receipt"); - AsciiString MESSAGE_ID = AsciiString.cached("message-id"); - AsciiString SUBSCRIPTION = AsciiString.cached("subscription"); - AsciiString RECEIPT_ID = AsciiString.cached("receipt-id"); - AsciiString MESSAGE = AsciiString.cached("message"); - AsciiString CONTENT_LENGTH = AsciiString.cached("content-length"); - AsciiString CONTENT_TYPE = AsciiString.cached("content-type"); - - /** - * {@link Headers#get(Object)} and convert the result to a {@link String}. - * @param name the name of the header to retrieve - * @return the first header value if the header is found. {@code null} if there's no such header. - */ - String getAsString(CharSequence name); - - /** - * {@link Headers#getAll(Object)} and convert each element of {@link List} to a {@link String}. - * @param name the name of the header to retrieve - * @return a {@link List} of header values or an empty {@link List} if no values are found. - */ - List getAllAsString(CharSequence name); - - /** - * {@link #iterator()} that converts each {@link Entry}'s key and value to a {@link String}. - */ - Iterator> iteratorAsString(); - - /** - * Returns {@code true} if a header with the {@code name} and {@code value} exists, {@code false} otherwise. - *

- * If {@code ignoreCase} is {@code true} then a case insensitive compare is done on the value. - * @param name the name of the header to find - * @param value the value of the header to find - * @param ignoreCase {@code true} then a case insensitive compare is run to compare values. - * otherwise a case sensitive compare is run to compare values. - */ - boolean contains(CharSequence name, CharSequence value, boolean ignoreCase); -} diff --git a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompHeadersSubframe.java b/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompHeadersSubframe.java deleted file mode 100644 index 78c880193c..0000000000 --- a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompHeadersSubframe.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.stomp; - -/** - * An interface that defines a {@link StompFrame}'s command and headers. - * - * @see StompCommand - * @see StompHeaders - */ -public interface StompHeadersSubframe extends StompSubframe { - /** - * Returns command of this frame. - */ - StompCommand command(); - - /** - * Returns headers of this frame. - */ - StompHeaders headers(); -} diff --git a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompSubframe.java b/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompSubframe.java deleted file mode 100644 index 1745830128..0000000000 --- a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompSubframe.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.stomp; - -import io.netty.handler.codec.DecoderResultProvider; - -/** - * Defines a common interface for all {@link StompSubframe} implementations. - */ -public interface StompSubframe extends DecoderResultProvider { } diff --git a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompSubframeAggregator.java b/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompSubframeAggregator.java deleted file mode 100644 index 0d0de466b5..0000000000 --- a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompSubframeAggregator.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.stomp; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelPipeline; -import io.netty.handler.codec.MessageAggregator; -import io.netty.handler.codec.TooLongFrameException; - -/** - * A {@link ChannelHandler} that aggregates an {@link StompHeadersSubframe} - * and its following {@link StompContentSubframe}s into a single {@link StompFrame}. - * It is useful when you don't want to take care of STOMP frames whose content is 'chunked'. Insert this - * handler after {@link StompSubframeDecoder} in the {@link ChannelPipeline}: - */ -public class StompSubframeAggregator - extends MessageAggregator { - - /** - * Creates a new instance. - * - * @param maxContentLength - * the maximum length of the aggregated content. - * If the length of the aggregated content exceeds this value, - * a {@link TooLongFrameException} will be raised. - */ - public StompSubframeAggregator(int maxContentLength) { - super(maxContentLength); - } - - @Override - protected boolean isStartMessage(StompSubframe msg) throws Exception { - return msg instanceof StompHeadersSubframe; - } - - @Override - protected boolean isContentMessage(StompSubframe msg) throws Exception { - return msg instanceof StompContentSubframe; - } - - @Override - protected boolean isLastContentMessage(StompContentSubframe msg) throws Exception { - return msg instanceof LastStompContentSubframe; - } - - @Override - protected boolean isAggregated(StompSubframe msg) throws Exception { - return msg instanceof StompFrame; - } - - @Override - protected boolean isContentLengthInvalid(StompHeadersSubframe start, int maxContentLength) { - return (int) Math.min(Integer.MAX_VALUE, start.headers().getLong(StompHeaders.CONTENT_LENGTH, -1)) > - maxContentLength; - } - - @Override - protected Object newContinueResponse(StompHeadersSubframe start, int maxContentLength, ChannelPipeline pipeline) { - return null; - } - - @Override - protected boolean closeAfterContinueResponse(Object msg) throws Exception { - throw new UnsupportedOperationException(); - } - - @Override - protected boolean ignoreContentAfterContinueResponse(Object msg) throws Exception { - throw new UnsupportedOperationException(); - } - - @Override - protected StompFrame beginAggregation(StompHeadersSubframe start, ByteBuf content) throws Exception { - StompFrame ret = new DefaultStompFrame(start.command(), content); - ret.headers().set(start.headers()); - return ret; - } -} 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 deleted file mode 100644 index 567f7c87cd..0000000000 --- a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompSubframeDecoder.java +++ /dev/null @@ -1,383 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.stomp; - - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.DecoderException; -import io.netty.handler.codec.DecoderResult; -import io.netty.handler.codec.ReplayingDecoder; -import io.netty.handler.codec.TooLongFrameException; -import io.netty.handler.codec.stomp.StompSubframeDecoder.State; -import io.netty.util.ByteProcessor; -import io.netty.util.internal.AppendableCharSequence; -import io.netty.util.internal.ObjectUtil; -import io.netty.util.internal.StringUtil; - -import java.util.Objects; - -import static io.netty.buffer.ByteBufUtil.*; - -/** - * Decodes {@link ByteBuf}s into {@link StompHeadersSubframe}s and {@link StompContentSubframe}s. - * - *

Parameters to control memory consumption:

- * {@code maxLineLength} the maximum length of line - restricts length of command and header lines If the length of the - * initial line exceeds this value, a {@link TooLongFrameException} will be raised. - *
- * {@code maxChunkSize} The maximum length of the content or each chunk. If the content length (or the length of each - * chunk) exceeds this value, the content or chunk ill be split into multiple {@link StompContentSubframe}s whose length - * is {@code maxChunkSize} at maximum. - * - *

Chunked Content

- *

- * If the content of a stomp message is greater than {@code maxChunkSize} the transfer encoding of the HTTP message is - * 'chunked', this decoder generates multiple {@link StompContentSubframe} instances to avoid excessive memory - * consumption. Note, that every message, even with no content decodes with {@link LastStompContentSubframe} at the end - * to simplify upstream message parsing. - */ -public class StompSubframeDecoder extends ReplayingDecoder { - - private static final int DEFAULT_CHUNK_SIZE = 8132; - private static final int DEFAULT_MAX_LINE_LENGTH = 1024; - - enum State { - SKIP_CONTROL_CHARACTERS, - READ_HEADERS, - READ_CONTENT, - FINALIZE_FRAME_READ, - BAD_FRAME, - INVALID_CHUNK - } - - private final Utf8LineParser commandParser; - private final HeaderParser headerParser; - private final int maxChunkSize; - private int alreadyReadChunkSize; - private LastStompContentSubframe lastContent; - private long contentLength = -1; - - public StompSubframeDecoder() { - this(DEFAULT_MAX_LINE_LENGTH, DEFAULT_CHUNK_SIZE); - } - - public StompSubframeDecoder(boolean validateHeaders) { - this(DEFAULT_MAX_LINE_LENGTH, DEFAULT_CHUNK_SIZE, validateHeaders); - } - - public StompSubframeDecoder(int maxLineLength, int maxChunkSize) { - this(maxLineLength, maxChunkSize, false); - } - - public StompSubframeDecoder(int maxLineLength, int maxChunkSize, boolean validateHeaders) { - super(State.SKIP_CONTROL_CHARACTERS); - ObjectUtil.checkPositive(maxLineLength, "maxLineLength"); - ObjectUtil.checkPositive(maxChunkSize, "maxChunkSize"); - this.maxChunkSize = maxChunkSize; - commandParser = new Utf8LineParser(new AppendableCharSequence(16), maxLineLength); - headerParser = new HeaderParser(new AppendableCharSequence(128), maxLineLength, validateHeaders); - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - switch (state()) { - case SKIP_CONTROL_CHARACTERS: - skipControlCharacters(in); - checkpoint(State.READ_HEADERS); - // Fall through. - case READ_HEADERS: - StompCommand command = StompCommand.UNKNOWN; - StompHeadersSubframe frame = null; - try { - command = readCommand(in); - frame = new DefaultStompHeadersSubframe(command); - checkpoint(readHeaders(in, frame.headers())); - ctx.fireChannelRead(frame); - } catch (Exception e) { - if (frame == null) { - frame = new DefaultStompHeadersSubframe(command); - } - frame.setDecoderResult(DecoderResult.failure(e)); - ctx.fireChannelRead(frame); - checkpoint(State.BAD_FRAME); - return; - } - break; - case BAD_FRAME: - in.skipBytes(actualReadableBytes()); - return; - } - try { - switch (state()) { - case READ_CONTENT: - int toRead = in.readableBytes(); - if (toRead == 0) { - return; - } - if (toRead > maxChunkSize) { - toRead = maxChunkSize; - } - if (contentLength >= 0) { - int remainingLength = (int) (contentLength - alreadyReadChunkSize); - if (toRead > remainingLength) { - toRead = remainingLength; - } - ByteBuf chunkBuffer = readBytes(ctx.alloc(), in, toRead); - if ((alreadyReadChunkSize += toRead) >= contentLength) { - lastContent = new DefaultLastStompContentSubframe(chunkBuffer); - checkpoint(State.FINALIZE_FRAME_READ); - } else { - ctx.fireChannelRead(new DefaultStompContentSubframe(chunkBuffer)); - return; - } - } else { - int nulIndex = indexOf(in, in.readerIndex(), in.writerIndex(), StompConstants.NUL); - if (nulIndex == in.readerIndex()) { - checkpoint(State.FINALIZE_FRAME_READ); - } else { - if (nulIndex > 0) { - toRead = nulIndex - in.readerIndex(); - } else { - toRead = in.writerIndex() - in.readerIndex(); - } - ByteBuf chunkBuffer = readBytes(ctx.alloc(), in, toRead); - alreadyReadChunkSize += toRead; - if (nulIndex > 0) { - lastContent = new DefaultLastStompContentSubframe(chunkBuffer); - checkpoint(State.FINALIZE_FRAME_READ); - } else { - ctx.fireChannelRead(new DefaultStompContentSubframe(chunkBuffer)); - return; - } - } - } - // Fall through. - case FINALIZE_FRAME_READ: - skipNullCharacter(in); - if (lastContent == null) { - lastContent = LastStompContentSubframe.EMPTY_LAST_CONTENT; - } - ctx.fireChannelRead(lastContent); - resetDecoder(); - } - } catch (Exception e) { - StompContentSubframe errorContent = new DefaultLastStompContentSubframe(Unpooled.EMPTY_BUFFER); - errorContent.setDecoderResult(DecoderResult.failure(e)); - ctx.fireChannelRead(errorContent); - checkpoint(State.BAD_FRAME); - } - } - - private StompCommand readCommand(ByteBuf in) { - CharSequence commandSequence = commandParser.parse(in); - if (commandSequence == null) { - throw new DecoderException("Failed to read command from channel"); - } - String commandStr = commandSequence.toString(); - try { - return StompCommand.valueOf(commandStr); - } catch (IllegalArgumentException iae) { - throw new DecoderException("Cannot to parse command " + commandStr); - } - } - - private State readHeaders(ByteBuf buffer, StompHeaders headers) { - for (;;) { - boolean headerRead = headerParser.parseHeader(headers, buffer); - if (!headerRead) { - if (headers.contains(StompHeaders.CONTENT_LENGTH)) { - contentLength = getContentLength(headers); - if (contentLength == 0) { - return State.FINALIZE_FRAME_READ; - } - } - return State.READ_CONTENT; - } - } - } - - private static long getContentLength(StompHeaders headers) { - long contentLength = headers.getLong(StompHeaders.CONTENT_LENGTH, 0L); - if (contentLength < 0) { - throw new DecoderException(StompHeaders.CONTENT_LENGTH + " must be non-negative"); - } - return contentLength; - } - - private static void skipNullCharacter(ByteBuf buffer) { - byte b = buffer.readByte(); - if (b != StompConstants.NUL) { - throw new IllegalStateException("unexpected byte in buffer " + b + " while expecting NULL byte"); - } - } - - private static void skipControlCharacters(ByteBuf buffer) { - byte b; - for (;;) { - b = buffer.readByte(); - if (b != StompConstants.CR && b != StompConstants.LF) { - buffer.readerIndex(buffer.readerIndex() - 1); - break; - } - } - } - - private void resetDecoder() { - checkpoint(State.SKIP_CONTROL_CHARACTERS); - contentLength = -1; - alreadyReadChunkSize = 0; - lastContent = null; - } - - private static class Utf8LineParser implements ByteProcessor { - - private final AppendableCharSequence charSeq; - private final int maxLineLength; - - private int lineLength; - private char interim; - private boolean nextRead; - - Utf8LineParser(AppendableCharSequence charSeq, int maxLineLength) { - this.charSeq = Objects.requireNonNull(charSeq, "charSeq"); - this.maxLineLength = maxLineLength; - } - - AppendableCharSequence parse(ByteBuf byteBuf) { - reset(); - int offset = byteBuf.forEachByte(this); - if (offset == -1) { - return null; - } - - byteBuf.readerIndex(offset + 1); - return charSeq; - } - - AppendableCharSequence charSequence() { - return charSeq; - } - - @Override - public boolean process(byte nextByte) { - if (nextByte == StompConstants.CR) { - ++lineLength; - return true; - } - - if (nextByte == StompConstants.LF) { - return false; - } - - if (++lineLength > maxLineLength) { - throw new TooLongFrameException("An STOMP line is larger than " + maxLineLength + " bytes."); - } - - // 1 byte - 0xxxxxxx - 7 bits - // 2 byte - 110xxxxx 10xxxxxx - 11 bits - // 3 byte - 1110xxxx 10xxxxxx 10xxxxxx - 16 bits - if (nextRead) { - interim |= (nextByte & 0x3F) << 6; - nextRead = false; - } else if (interim != 0) { // flush 2 or 3 byte - charSeq.append((char) (interim | (nextByte & 0x3F))); - interim = 0; - } else if (nextByte >= 0) { // INITIAL BRANCH - // The first 128 characters (US-ASCII) need one byte. - charSeq.append((char) nextByte); - } else if ((nextByte & 0xE0) == 0xC0) { - // The next 1920 characters need two bytes and we can define - // a first byte by mask 110xxxxx. - interim = (char) ((nextByte & 0x1F) << 6); - } else { - // The rest of characters need three bytes. - interim = (char) ((nextByte & 0x0F) << 12); - nextRead = true; - } - - return true; - } - - protected void reset() { - charSeq.reset(); - lineLength = 0; - interim = 0; - nextRead = false; - } - } - - private static final class HeaderParser extends Utf8LineParser { - - private final boolean validateHeaders; - - private String name; - private boolean valid; - - HeaderParser(AppendableCharSequence charSeq, int maxLineLength, boolean validateHeaders) { - super(charSeq, maxLineLength); - this.validateHeaders = validateHeaders; - } - - boolean parseHeader(StompHeaders headers, ByteBuf buf) { - AppendableCharSequence value = super.parse(buf); - if (value == null || (name == null && value.length() == 0)) { - return false; - } - - if (valid) { - headers.add(name, value.toString()); - } else if (validateHeaders) { - if (StringUtil.isNullOrEmpty(name)) { - throw new IllegalArgumentException("received an invalid header line '" + value + '\''); - } - String line = name + ':' + value; - throw new IllegalArgumentException("a header value or name contains a prohibited character ':'" - + ", " + line); - } - return true; - } - - @Override - public boolean process(byte nextByte) { - if (nextByte == StompConstants.COLON) { - if (name == null) { - AppendableCharSequence charSeq = charSequence(); - if (charSeq.length() != 0) { - name = charSeq.substring(0, charSeq.length()); - charSeq.reset(); - valid = true; - return true; - } else { - name = StringUtil.EMPTY_STRING; - } - } else { - valid = false; - } - } - - return super.process(nextByte); - } - - @Override - protected void reset() { - name = null; - valid = false; - super.reset(); - } - } -} diff --git a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompSubframeEncoder.java b/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompSubframeEncoder.java deleted file mode 100644 index 8bd20e2646..0000000000 --- a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompSubframeEncoder.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.stomp; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToMessageEncoder; - -import java.util.List; -import java.util.Map.Entry; - -import static io.netty.handler.codec.stomp.StompConstants.*; - -/** - * Encodes a {@link StompFrame} or a {@link StompSubframe} into a {@link ByteBuf}. - */ -public class StompSubframeEncoder extends MessageToMessageEncoder { - - @Override - protected void encode(ChannelHandlerContext ctx, StompSubframe msg, List out) throws Exception { - if (msg instanceof StompFrame) { - StompFrame stompFrame = (StompFrame) msg; - ByteBuf buf = encodeFullFrame(stompFrame, ctx); - - out.add(convertFullFrame(stompFrame, buf)); - } else if (msg instanceof StompHeadersSubframe) { - StompHeadersSubframe stompHeadersSubframe = (StompHeadersSubframe) msg; - ByteBuf buf = ctx.alloc().buffer(headersSubFrameSize(stompHeadersSubframe)); - encodeHeaders(stompHeadersSubframe, buf); - - out.add(convertHeadersSubFrame(stompHeadersSubframe, buf)); - } else if (msg instanceof StompContentSubframe) { - StompContentSubframe stompContentSubframe = (StompContentSubframe) msg; - ByteBuf buf = encodeContent(stompContentSubframe, ctx); - - out.add(convertContentSubFrame(stompContentSubframe, buf)); - } - } - - /** - * An extension method to convert a STOMP encoded buffer to a different message type - * based on an original {@link StompFrame} full frame. - * - *

By default an encoded buffer is returned as is. - */ - protected Object convertFullFrame(StompFrame original, ByteBuf encoded) { - return encoded; - } - - /** - * An extension method to convert a STOMP encoded buffer to a different message type - * based on an original {@link StompHeadersSubframe} headers sub frame. - * - *

By default an encoded buffer is returned as is. - */ - protected Object convertHeadersSubFrame(StompHeadersSubframe original, ByteBuf encoded) { - return encoded; - } - - /** - * An extension method to convert a STOMP encoded buffer to a different message type - * based on an original {@link StompHeadersSubframe} content sub frame. - * - *

By default an encoded buffer is returned as is. - */ - protected Object convertContentSubFrame(StompContentSubframe original, ByteBuf encoded) { - return encoded; - } - - /** - * Returns a heuristic size for headers (32 bytes per header line) + (2 bytes for colon and eol) + (additional - * command buffer). - */ - protected int headersSubFrameSize(StompHeadersSubframe headersSubframe) { - int estimatedSize = headersSubframe.headers().size() * 34 + 48; - if (estimatedSize < 128) { - return 128; - } else if (estimatedSize < 256) { - return 256; - } - - return estimatedSize; - } - - private ByteBuf encodeFullFrame(StompFrame frame, ChannelHandlerContext ctx) { - int contentReadableBytes = frame.content().readableBytes(); - ByteBuf buf = ctx.alloc().buffer(headersSubFrameSize(frame) + contentReadableBytes); - encodeHeaders(frame, buf); - - if (contentReadableBytes > 0) { - buf.writeBytes(frame.content()); - } - - return buf.writeByte(NUL); - } - - private static void encodeHeaders(StompHeadersSubframe frame, ByteBuf buf) { - ByteBufUtil.writeUtf8(buf, frame.command().toString()); - buf.writeByte(StompConstants.LF); - - for (Entry entry : frame.headers()) { - ByteBufUtil.writeUtf8(buf, entry.getKey()); - buf.writeByte(StompConstants.COLON); - ByteBufUtil.writeUtf8(buf, entry.getValue()); - buf.writeByte(StompConstants.LF); - } - - buf.writeByte(StompConstants.LF); - } - - private static ByteBuf encodeContent(StompContentSubframe content, ChannelHandlerContext ctx) { - if (content instanceof LastStompContentSubframe) { - ByteBuf buf = ctx.alloc().buffer(content.content().readableBytes() + 1); - buf.writeBytes(content.content()); - buf.writeByte(StompConstants.NUL); - return buf; - } - - return content.content().retain(); - } -} diff --git a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/package-info.java b/codec-stomp/src/main/java/io/netty/handler/codec/stomp/package-info.java deleted file mode 100644 index 4b6207cefc..0000000000 --- a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * STOMP codec - */ -package io.netty.handler.codec.stomp; diff --git a/codec-stomp/src/test/java/io/netty/handler/codec/stomp/DefaultStompFrameTest.java b/codec-stomp/src/test/java/io/netty/handler/codec/stomp/DefaultStompFrameTest.java deleted file mode 100644 index 8448653c2c..0000000000 --- a/codec-stomp/src/test/java/io/netty/handler/codec/stomp/DefaultStompFrameTest.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.stomp; - -import io.netty.util.AsciiString; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class DefaultStompFrameTest { - - @Test - public void testStompFrameCopy() { - StompFrame sourceFrame = new DefaultStompFrame(StompCommand.CONNECT); - - assertTrue(sourceFrame.headers().isEmpty()); - - sourceFrame.headers().set(StompHeaders.HOST, "localhost"); - - StompFrame copyFrame = sourceFrame.copy(); - - assertEquals(sourceFrame.headers(), copyFrame.headers()); - assertEquals(sourceFrame.content(), copyFrame.content()); - - AsciiString copyHeaderName = new AsciiString("foo"); - AsciiString copyHeaderValue = new AsciiString("bar"); - copyFrame.headers().set(copyHeaderName, copyHeaderValue); - - assertFalse(sourceFrame.headers().contains(copyHeaderName, copyHeaderValue)); - assertTrue(copyFrame.headers().contains(copyHeaderName, copyHeaderValue)); - - assertEquals(1, sourceFrame.headers().size()); - assertEquals(2, copyFrame.headers().size()); - assertNotEquals(sourceFrame.headers(), copyFrame.headers()); - - assertTrue(sourceFrame.release()); - assertTrue(copyFrame.release()); - } -} diff --git a/codec-stomp/src/test/java/io/netty/handler/codec/stomp/StompCommandDecodeTest.java b/codec-stomp/src/test/java/io/netty/handler/codec/stomp/StompCommandDecodeTest.java deleted file mode 100644 index 2626896cd7..0000000000 --- a/codec-stomp/src/test/java/io/netty/handler/codec/stomp/StompCommandDecodeTest.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.stomp; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import java.util.Arrays; -import java.util.Collection; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -import static io.netty.util.CharsetUtil.*; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class StompCommandDecodeTest { - - private EmbeddedChannel channel; - - @BeforeEach - public void setUp() { - channel = new EmbeddedChannel(new StompSubframeDecoder(true)); - } - - @AfterEach - public void tearDown() { - assertFalse(channel.finish()); - } - - @ParameterizedTest(name = "{index}: testDecodeCommand({0}) = {1}") - @MethodSource("stompCommands") - public void testDecodeCommand(String rawCommand, StompCommand expectedCommand, Boolean valid) { - byte[] frameContent = String.format("%s\n\n\0", rawCommand).getBytes(UTF_8); - ByteBuf incoming = Unpooled.wrappedBuffer(frameContent); - assertTrue(channel.writeInbound(incoming)); - - StompHeadersSubframe frame = channel.readInbound(); - - assertNotNull(frame); - assertEquals(expectedCommand, frame.command()); - - if (valid) { - assertTrue(frame.decoderResult().isSuccess()); - - StompContentSubframe content = channel.readInbound(); - assertSame(LastStompContentSubframe.EMPTY_LAST_CONTENT, content); - content.release(); - } else { - assertTrue(frame.decoderResult().isFailure()); - assertNull(channel.readInbound()); - } - } - - public static Collection stompCommands() { - return Arrays.asList(new Object[][] { - { "STOMP", StompCommand.STOMP, true }, - { "CONNECT", StompCommand.CONNECT, true }, - { "SEND", StompCommand.SEND, true }, - { "SUBSCRIBE", StompCommand.SUBSCRIBE, true }, - { "UNSUBSCRIBE", StompCommand.UNSUBSCRIBE, true }, - { "ACK", StompCommand.ACK, true }, - { "NACK", StompCommand.NACK, true }, - { "BEGIN", StompCommand.BEGIN, true }, - { "ABORT", StompCommand.ABORT, true }, - { "COMMIT", StompCommand.COMMIT, true }, - { "DISCONNECT", StompCommand.DISCONNECT, true }, - - // invalid commands - { "INVALID", StompCommand.UNKNOWN, false }, - { "disconnect", StompCommand.UNKNOWN , false } - }); - } -} diff --git a/codec-stomp/src/test/java/io/netty/handler/codec/stomp/StompHeadersTest.java b/codec-stomp/src/test/java/io/netty/handler/codec/stomp/StompHeadersTest.java deleted file mode 100644 index 4159a5ab3f..0000000000 --- a/codec-stomp/src/test/java/io/netty/handler/codec/stomp/StompHeadersTest.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.stomp; - -import io.netty.util.AsciiString; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; - -public class StompHeadersTest { - @Test - public void testHeadersCaseSensitive() { - DefaultStompHeaders headers = new DefaultStompHeaders(); - AsciiString foo = new AsciiString("foo"); - AsciiString value = new AsciiString("value"); - headers.add(foo, value); - assertNull(headers.get("Foo")); - assertEquals(value, headers.get(foo)); - assertEquals(value, headers.get(foo.toString())); - } -} diff --git a/codec-stomp/src/test/java/io/netty/handler/codec/stomp/StompSubframeAggregatorTest.java b/codec-stomp/src/test/java/io/netty/handler/codec/stomp/StompSubframeAggregatorTest.java deleted file mode 100644 index 85ac6c4d19..0000000000 --- a/codec-stomp/src/test/java/io/netty/handler/codec/stomp/StompSubframeAggregatorTest.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.stomp; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.TooLongFrameException; -import io.netty.util.CharsetUtil; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; - -public class StompSubframeAggregatorTest { - - private EmbeddedChannel channel; - - @BeforeEach - public void setup() throws Exception { - channel = new EmbeddedChannel(new StompSubframeDecoder(), new StompSubframeAggregator(100000)); - } - - @AfterEach - public void teardown() throws Exception { - assertFalse(channel.finish()); - } - - @Test - public void testSingleFrameDecoding() { - ByteBuf incoming = Unpooled.buffer(); - incoming.writeBytes(StompTestConstants.CONNECT_FRAME.getBytes()); - channel.writeInbound(incoming); - - StompFrame frame = channel.readInbound(); - frame.release(); - - assertNull(channel.readInbound()); - } - - @Test - public void testSingleFrameWithBodyAndContentLength() { - ByteBuf incoming = Unpooled.buffer(); - incoming.writeBytes(StompTestConstants.SEND_FRAME_2.getBytes()); - channel.writeInbound(incoming); - - StompFrame frame = channel.readInbound(); - assertNotNull(frame); - assertEquals(StompCommand.SEND, frame.command()); - assertEquals("hello, queue a!!!", frame.content().toString(CharsetUtil.UTF_8)); - frame.release(); - - assertNull(channel.readInbound()); - } - - @Test - public void testSingleFrameWithBodyAndNoContentLength() { - ByteBuf incoming = Unpooled.buffer(); - incoming.writeBytes(StompTestConstants.SEND_FRAME_4.getBytes()); - channel.writeInbound(incoming); - - StompFrame frame = channel.readInbound(); - assertNotNull(frame); - assertEquals(StompCommand.SEND, frame.command()); - assertEquals("body", frame.content().toString(CharsetUtil.UTF_8)); - frame.release(); - - assertNull(channel.readInbound()); - } - - @Test - public void testSingleFrameWithSplitBodyAndNoContentLength() { - for (int n = 0; n < StompTestConstants.SEND_FRAMES_3.length; ++n) { - ByteBuf incoming = Unpooled.buffer(); - incoming.writeBytes(StompTestConstants.SEND_FRAMES_3[n].getBytes()); - channel.writeInbound(incoming); - channel.flush(); - } - - StompFrame frame = channel.readInbound(); - assertNotNull(frame); - assertEquals(StompCommand.SEND, frame.command()); - assertEquals("first part of body\nsecond part of body", frame.content().toString(CharsetUtil.UTF_8)); - frame.release(); - - assertNull(channel.readInbound()); - } - - @Test - public void testSingleFrameChunked() { - EmbeddedChannel channel = new EmbeddedChannel( - new StompSubframeDecoder(10000, 5), new StompSubframeAggregator(100000)); - ByteBuf incoming = Unpooled.buffer(); - incoming.writeBytes(StompTestConstants.SEND_FRAME_2.getBytes()); - channel.writeInbound(incoming); - - StompFrame frame = channel.readInbound(); - assertNotNull(frame); - assertEquals(StompCommand.SEND, frame.command()); - frame.release(); - - assertNull(channel.readInbound()); - } - - @Test - public void testMultipleFramesDecoding() { - ByteBuf incoming = Unpooled.buffer(); - incoming.writeBytes(StompTestConstants.CONNECT_FRAME.getBytes()); - incoming.writeBytes(StompTestConstants.CONNECTED_FRAME.getBytes()); - channel.writeInbound(incoming); - channel.writeInbound(Unpooled.wrappedBuffer(StompTestConstants.SEND_FRAME_1.getBytes())); - - StompFrame frame = channel.readInbound(); - assertEquals(StompCommand.CONNECT, frame.command()); - frame.release(); - - frame = channel.readInbound(); - assertEquals(StompCommand.CONNECTED, frame.command()); - frame.release(); - - frame = channel.readInbound(); - assertEquals(StompCommand.SEND, frame.command()); - frame.release(); - - assertNull(channel.readInbound()); - } - - @Test - public void testTooLongFrameException() { - EmbeddedChannel channel = new EmbeddedChannel(new StompSubframeDecoder(), new StompSubframeAggregator(10)); - assertThrows(TooLongFrameException.class, - () -> channel.writeInbound(Unpooled.wrappedBuffer(StompTestConstants.SEND_FRAME_1.getBytes()))); - } -} diff --git a/codec-stomp/src/test/java/io/netty/handler/codec/stomp/StompSubframeDecoderTest.java b/codec-stomp/src/test/java/io/netty/handler/codec/stomp/StompSubframeDecoderTest.java deleted file mode 100644 index becab86bc5..0000000000 --- a/codec-stomp/src/test/java/io/netty/handler/codec/stomp/StompSubframeDecoderTest.java +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.stomp; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static io.netty.handler.codec.stomp.StompTestConstants.*; -import static io.netty.util.CharsetUtil.*; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class StompSubframeDecoderTest { - - private EmbeddedChannel channel; - - @BeforeEach - public void setup() throws Exception { - channel = new EmbeddedChannel(new StompSubframeDecoder()); - } - - @AfterEach - public void teardown() throws Exception { - assertFalse(channel.finish()); - } - - @Test - public void testSingleFrameDecoding() { - ByteBuf incoming = Unpooled.buffer(); - incoming.writeBytes(StompTestConstants.CONNECT_FRAME.getBytes()); - channel.writeInbound(incoming); - - StompHeadersSubframe frame = channel.readInbound(); - assertNotNull(frame); - assertEquals(StompCommand.CONNECT, frame.command()); - - StompContentSubframe content = channel.readInbound(); - assertSame(LastStompContentSubframe.EMPTY_LAST_CONTENT, content); - content.release(); - - Object o = channel.readInbound(); - assertNull(o); - } - - @Test - public void testSingleFrameWithBodyAndContentLength() { - ByteBuf incoming = Unpooled.buffer(); - incoming.writeBytes(StompTestConstants.SEND_FRAME_2.getBytes()); - channel.writeInbound(incoming); - - StompHeadersSubframe frame = channel.readInbound(); - assertNotNull(frame); - assertEquals(StompCommand.SEND, frame.command()); - - StompContentSubframe content = channel.readInbound(); - assertTrue(content instanceof LastStompContentSubframe); - String s = content.content().toString(UTF_8); - assertEquals("hello, queue a!!!", s); - content.release(); - - assertNull(channel.readInbound()); - } - - @Test - public void testSingleFrameWithBodyWithoutContentLength() { - ByteBuf incoming = Unpooled.buffer(); - incoming.writeBytes(StompTestConstants.SEND_FRAME_1.getBytes()); - channel.writeInbound(incoming); - - StompHeadersSubframe frame = channel.readInbound(); - assertNotNull(frame); - assertEquals(StompCommand.SEND, frame.command()); - - StompContentSubframe content = channel.readInbound(); - assertTrue(content instanceof LastStompContentSubframe); - String s = content.content().toString(UTF_8); - assertEquals("hello, queue a!", s); - content.release(); - - assertNull(channel.readInbound()); - } - - @Test - public void testSingleFrameChunked() { - EmbeddedChannel channel = new EmbeddedChannel(new StompSubframeDecoder(10000, 5)); - - ByteBuf incoming = Unpooled.buffer(); - incoming.writeBytes(StompTestConstants.SEND_FRAME_2.getBytes()); - channel.writeInbound(incoming); - - StompHeadersSubframe frame = channel.readInbound(); - assertNotNull(frame); - assertEquals(StompCommand.SEND, frame.command()); - - StompContentSubframe content = channel.readInbound(); - String s = content.content().toString(UTF_8); - assertEquals("hello", s); - content.release(); - - content = channel.readInbound(); - s = content.content().toString(UTF_8); - assertEquals(", que", s); - content.release(); - - content = channel.readInbound(); - s = content.content().toString(UTF_8); - assertEquals("ue a!", s); - content.release(); - - content = channel.readInbound(); - s = content.content().toString(UTF_8); - assertEquals("!!", s); - content.release(); - - assertNull(channel.readInbound()); - } - - @Test - public void testMultipleFramesDecoding() { - ByteBuf incoming = Unpooled.buffer(); - incoming.writeBytes(StompTestConstants.CONNECT_FRAME.getBytes()); - incoming.writeBytes(StompTestConstants.CONNECTED_FRAME.getBytes()); - channel.writeInbound(incoming); - - StompHeadersSubframe frame = channel.readInbound(); - assertNotNull(frame); - assertEquals(StompCommand.CONNECT, frame.command()); - - StompContentSubframe content = channel.readInbound(); - assertSame(LastStompContentSubframe.EMPTY_LAST_CONTENT, content); - content.release(); - - StompHeadersSubframe frame2 = channel.readInbound(); - assertNotNull(frame2); - assertEquals(StompCommand.CONNECTED, frame2.command()); - - StompContentSubframe content2 = channel.readInbound(); - assertSame(LastStompContentSubframe.EMPTY_LAST_CONTENT, content2); - content2.release(); - - assertNull(channel.readInbound()); - } - - @Test - public void testValidateHeadersDecodingDisabled() { - ByteBuf invalidIncoming = Unpooled.copiedBuffer(FRAME_WITH_INVALID_HEADER.getBytes(UTF_8)); - assertTrue(channel.writeInbound(invalidIncoming)); - - StompHeadersSubframe frame = channel.readInbound(); - assertNotNull(frame); - assertEquals(StompCommand.SEND, frame.command()); - assertTrue(frame.headers().contains("destination")); - assertTrue(frame.headers().contains("content-type")); - assertFalse(frame.headers().contains("current-time")); - - StompContentSubframe content = channel.readInbound(); - String s = content.content().toString(UTF_8); - assertEquals("some body", s); - content.release(); - } - - @Test - public void testValidateHeadersDecodingEnabled() { - channel = new EmbeddedChannel(new StompSubframeDecoder(true)); - - ByteBuf invalidIncoming = Unpooled.wrappedBuffer(FRAME_WITH_INVALID_HEADER.getBytes(UTF_8)); - assertTrue(channel.writeInbound(invalidIncoming)); - - StompHeadersSubframe frame = channel.readInbound(); - assertNotNull(frame); - assertTrue(frame.decoderResult().isFailure()); - assertEquals("a header value or name contains a prohibited character ':', current-time:2000-01-01T00:00:00", - frame.decoderResult().cause().getMessage()); - } - - @Test - public void testNotValidFrameWithEmptyHeaderName() { - channel = new EmbeddedChannel(new StompSubframeDecoder(true)); - - ByteBuf invalidIncoming = Unpooled.wrappedBuffer(FRAME_WITH_EMPTY_HEADER_NAME.getBytes(UTF_8)); - assertTrue(channel.writeInbound(invalidIncoming)); - - StompHeadersSubframe frame = channel.readInbound(); - assertNotNull(frame); - assertTrue(frame.decoderResult().isFailure()); - assertEquals("received an invalid header line ':header-value'", - frame.decoderResult().cause().getMessage()); - } - - @Test - public void testUtf8FrameDecoding() { - channel = new EmbeddedChannel(new StompSubframeDecoder(true)); - - ByteBuf incoming = Unpooled.wrappedBuffer(SEND_FRAME_UTF8.getBytes(UTF_8)); - assertTrue(channel.writeInbound(incoming)); - - StompHeadersSubframe headersSubFrame = channel.readInbound(); - assertNotNull(headersSubFrame); - assertFalse(headersSubFrame.decoderResult().isFailure()); - assertEquals("/queue/№11±♛ĐŊĐĩŅ‚Ņ‚и♕", headersSubFrame.headers().getAsString("destination")); - assertTrue(headersSubFrame.headers().contains("content-type")); - - StompContentSubframe contentSubFrame = channel.readInbound(); - assertNotNull(contentSubFrame); - assertEquals("body", contentSubFrame.content().toString(UTF_8)); - assertTrue(contentSubFrame.release()); - } -} diff --git a/codec-stomp/src/test/java/io/netty/handler/codec/stomp/StompSubframeEncoderTest.java b/codec-stomp/src/test/java/io/netty/handler/codec/stomp/StompSubframeEncoderTest.java deleted file mode 100644 index acb46478c7..0000000000 --- a/codec-stomp/src/test/java/io/netty/handler/codec/stomp/StompSubframeEncoderTest.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.stomp; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.AsciiString; -import io.netty.util.CharsetUtil; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static io.netty.handler.codec.stomp.StompTestConstants.*; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class StompSubframeEncoderTest { - - private EmbeddedChannel channel; - - @BeforeEach - public void setup() throws Exception { - channel = new EmbeddedChannel(new StompSubframeEncoder()); - } - - @AfterEach - public void teardown() throws Exception { - assertFalse(channel.finish()); - } - - @Test - public void testFrameAndContentEncoding() { - StompHeadersSubframe frame = new DefaultStompHeadersSubframe(StompCommand.CONNECT); - StompHeaders headers = frame.headers(); - headers.set(StompHeaders.HOST, "stomp.github.org"); - headers.set(StompHeaders.ACCEPT_VERSION, "1.1,1.2"); - channel.writeOutbound(frame); - channel.writeOutbound(LastStompContentSubframe.EMPTY_LAST_CONTENT); - ByteBuf aggregatedBuffer = Unpooled.buffer(); - ByteBuf byteBuf = channel.readOutbound(); - assertNotNull(byteBuf); - aggregatedBuffer.writeBytes(byteBuf); - byteBuf.release(); - - byteBuf = channel.readOutbound(); - assertNotNull(byteBuf); - aggregatedBuffer.writeBytes(byteBuf); - byteBuf.release(); - - aggregatedBuffer.readerIndex(0); - String content = aggregatedBuffer.toString(CharsetUtil.UTF_8); - assertEquals(StompTestConstants.CONNECT_FRAME, content); - aggregatedBuffer.release(); - } - - @Test - public void testUtf8FrameEncoding() { - StompFrame frame = new DefaultStompFrame(StompCommand.SEND, - Unpooled.wrappedBuffer("body".getBytes(CharsetUtil.UTF_8))); - StompHeaders incoming = frame.headers(); - incoming.set(StompHeaders.DESTINATION, "/queue/№11±♛ĐŊĐĩŅ‚Ņ‚и♕"); - incoming.set(StompHeaders.CONTENT_TYPE, AsciiString.of("text/plain")); - - channel.writeOutbound(frame); - - ByteBuf fullFrame = channel.readOutbound(); - assertEquals(SEND_FRAME_UTF8, fullFrame.toString(CharsetUtil.UTF_8)); - assertTrue(fullFrame.release()); - } - - @Test - public void testOneBufferForStompFrameWithEmptyContent() { - StompFrame connectedFrame = new DefaultStompFrame(StompCommand.CONNECTED); - connectedFrame.headers().set(StompHeaders.VERSION, "1.2"); - - assertTrue(channel.writeOutbound(connectedFrame)); - - ByteBuf stompBuffer = channel.readOutbound(); - - assertNotNull(stompBuffer); - assertNull(channel.readOutbound()); - assertEquals("CONNECTED\nversion:1.2\n\n\0", stompBuffer.toString(CharsetUtil.UTF_8)); - assertTrue(stompBuffer.release()); - } -} diff --git a/codec-stomp/src/test/java/io/netty/handler/codec/stomp/StompTestConstants.java b/codec-stomp/src/test/java/io/netty/handler/codec/stomp/StompTestConstants.java deleted file mode 100644 index bf2efcfc6a..0000000000 --- a/codec-stomp/src/test/java/io/netty/handler/codec/stomp/StompTestConstants.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.stomp; - -public final class StompTestConstants { - public static final String CONNECT_FRAME = - "CONNECT\n" + - "host:stomp.github.org\n" + - "accept-version:1.1,1.2\n" + - '\n' + - '\0'; - public static final String CONNECTED_FRAME = - "CONNECTED\n" + - "version:1.2\n" + - '\n' + - "\0\n"; - public static final String SEND_FRAME_1 = - "SEND\n" + - "destination:/queue/a\n" + - "content-type:text/plain\n" + - '\n' + - "hello, queue a!" + - "\0\n"; - public static final String SEND_FRAME_2 = - "SEND\n" + - "destination:/queue/a\n" + - "content-type:text/plain\n" + - "content-length:17\n" + - '\n' + - "hello, queue a!!!" + - "\0\n"; - public static final String[] SEND_FRAMES_3 = { - "SEND\n" + - "destination:/queue/a\n" + - "content-type:text/plain\n" + - '\n' + - "first part of body\n", - "second part of body\0" - }; - - public static final String SEND_FRAME_4 = "SEND\n" + - "destination:/queue/a\n" + - "content-type:text/plain\n" + - '\n' + - "body\0"; - - public static final String FRAME_WITH_INVALID_HEADER = "SEND\n" + - "destination:/some-destination\n" + - "content-type:text/plain\n" + - "current-time:2000-01-01T00:00:00\n" + - '\n' + - "some body\0"; - - public static final String FRAME_WITH_EMPTY_HEADER_NAME = "SEND\n" + - "destination:/some-destination\n" + - "content-type:text/plain\n" + - ":header-value\n" + - '\n' + - "some body\0"; - - public static final String SEND_FRAME_UTF8 = "SEND\n" + - "destination:/queue/№11±♛ĐŊĐĩŅ‚Ņ‚и♕\n" + - "content-type:text/plain\n" + - '\n' + - "body\0"; - - private StompTestConstants() { } -} diff --git a/codec-xml/pom.xml b/codec-xml/pom.xml deleted file mode 100644 index 39b309b087..0000000000 --- a/codec-xml/pom.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - - - 4.0.0 - - io.netty - netty-parent - 5.0.0.Final-SNAPSHOT - - - netty-codec-xml - jar - - Netty/Codec/XML - - - io.netty.codec.xml - - - - - ${project.groupId} - netty-buffer - ${project.version} - - - ${project.groupId} - netty-transport - ${project.version} - - - ${project.groupId} - netty-codec - ${project.version} - - - com.fasterxml - aalto-xml - - - - 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 deleted file mode 100644 index c78943386e..0000000000 --- a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlAttribute.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.xml; - - -/** - * XML attributes, it is part of {@link XmlElement} - */ -public class XmlAttribute { - - private final String type; - private final String name; - private final String prefix; - private final String namespace; - private final String value; - - public XmlAttribute(String type, String name, String prefix, String namespace, String value) { - this.type = type; - this.name = name; - this.prefix = prefix; - this.namespace = namespace; - this.value = value; - } - - public String type() { - return type; - } - - public String name() { - return name; - } - - public String prefix() { - return prefix; - } - - public String namespace() { - return namespace; - } - - public String value() { - return value; - } - - @Override - public boolean equals(Object o) { - 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; - } - - return true; - } - - @Override - public int hashCode() { - int result = type != null ? type.hashCode() : 0; - result = 31 * result + name.hashCode(); - result = 31 * result + (prefix != null ? prefix.hashCode() : 0); - result = 31 * result + (namespace != null ? namespace.hashCode() : 0); - result = 31 * result + (value != null ? value.hashCode() : 0); - return result; - } - - @Override - public String toString() { - return "XmlAttribute{" + - "type='" + type + '\'' + - ", name='" + name + '\'' + - ", prefix='" + prefix + '\'' + - ", namespace='" + namespace + '\'' + - ", value='" + value + '\'' + - '}'; - } -} diff --git a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlCdata.java b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlCdata.java deleted file mode 100644 index d86fe29289..0000000000 --- a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlCdata.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.xml; - -/** - * XML CDATA ... - */ -public class XmlCdata extends XmlContent { - - public XmlCdata(String data) { - super(data); - } - -} diff --git a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlCharacters.java b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlCharacters.java deleted file mode 100644 index 9c90f2c9f5..0000000000 --- a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlCharacters.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.xml; - -/** - * XML characters, e.g. <test>characters</test>, but not CDATA. - */ -public class XmlCharacters extends XmlContent { - - public XmlCharacters(String data) { - super(data); - } - -} diff --git a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlComment.java b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlComment.java deleted file mode 100644 index 794d65ac49..0000000000 --- a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlComment.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.xml; - -/** - * XML Comment - */ -public class XmlComment extends XmlContent { - - public XmlComment(String data) { - super(data); - } - -} 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 deleted file mode 100644 index 00063c4cad..0000000000 --- a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlContent.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.xml; - -/** - * XML Content is base class for XML CDATA, Comments, Characters and Space - */ -public abstract class XmlContent { - - private final String data; - - protected XmlContent(String data) { - this.data = data; - } - - public String data() { - return data; - } - - @Override - public boolean equals(Object o) { - 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; - } - - return true; - } - - @Override - public int hashCode() { - return data != null ? data.hashCode() : 0; - } - - @Override - public String toString() { - return "XmlContent{" + - "data='" + data + '\'' + - '}'; - } -} 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 deleted file mode 100644 index 16fad018bb..0000000000 --- a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlDTD.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.xml; - -/** - * DTD (Document Type Definition) - */ -public class XmlDTD { - - private final String text; - - public XmlDTD(String text) { - this.text = text; - } - - public String text() { - return text; - } - - @Override - public boolean equals(Object o) { - 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; - } - - return true; - } - - @Override - public int hashCode() { - return text != null ? text.hashCode() : 0; - } - - @Override - public String toString() { - return "XmlDTD{" + - "text='" + text + '\'' + - '}'; - } - -} diff --git a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlDecoder.java b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlDecoder.java deleted file mode 100644 index d0e0145a60..0000000000 --- a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlDecoder.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.xml; - -import com.fasterxml.aalto.AsyncByteArrayFeeder; -import com.fasterxml.aalto.AsyncXMLInputFactory; -import com.fasterxml.aalto.AsyncXMLStreamReader; -import com.fasterxml.aalto.stax.InputFactoryImpl; -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.ByteToMessageDecoder; - -import javax.xml.stream.XMLStreamConstants; -import javax.xml.stream.XMLStreamException; - -/** - * Async XML decoder based on Aalto XML parser. - * - * Parses the incoming data into one of XML messages defined in this package. - */ - -public class XmlDecoder extends ByteToMessageDecoder { - - private static final AsyncXMLInputFactory XML_INPUT_FACTORY = new InputFactoryImpl(); - private static final XmlDocumentEnd XML_DOCUMENT_END = XmlDocumentEnd.INSTANCE; - - private final AsyncXMLStreamReader streamReader = XML_INPUT_FACTORY.createAsyncForByteArray(); - private final AsyncByteArrayFeeder streamFeeder = streamReader.getInputFeeder(); - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - byte[] buffer = new byte[in.readableBytes()]; - in.readBytes(buffer); - try { - streamFeeder.feedInput(buffer, 0, buffer.length); - } catch (XMLStreamException exception) { - in.skipBytes(in.readableBytes()); - throw exception; - } - - while (!streamFeeder.needMoreInput()) { - int type = streamReader.next(); - switch (type) { - case XMLStreamConstants.START_DOCUMENT: - ctx.fireChannelRead(new XmlDocumentStart(streamReader.getEncoding(), streamReader.getVersion(), - streamReader.isStandalone(), streamReader.getCharacterEncodingScheme())); - break; - case XMLStreamConstants.END_DOCUMENT: - ctx.fireChannelRead(XML_DOCUMENT_END); - break; - case XMLStreamConstants.START_ELEMENT: - XmlElementStart elementStart = new XmlElementStart(streamReader.getLocalName(), - streamReader.getName().getNamespaceURI(), streamReader.getPrefix()); - for (int x = 0; x < streamReader.getAttributeCount(); x++) { - XmlAttribute attribute = new XmlAttribute(streamReader.getAttributeType(x), - streamReader.getAttributeLocalName(x), streamReader.getAttributePrefix(x), - streamReader.getAttributeNamespace(x), streamReader.getAttributeValue(x)); - elementStart.attributes().add(attribute); - } - for (int x = 0; x < streamReader.getNamespaceCount(); x++) { - XmlNamespace namespace = new XmlNamespace(streamReader.getNamespacePrefix(x), - streamReader.getNamespaceURI(x)); - elementStart.namespaces().add(namespace); - } - ctx.fireChannelRead(elementStart); - break; - case XMLStreamConstants.END_ELEMENT: - XmlElementEnd elementEnd = new XmlElementEnd(streamReader.getLocalName(), - streamReader.getName().getNamespaceURI(), streamReader.getPrefix()); - for (int x = 0; x < streamReader.getNamespaceCount(); x++) { - XmlNamespace namespace = new XmlNamespace(streamReader.getNamespacePrefix(x), - streamReader.getNamespaceURI(x)); - elementEnd.namespaces().add(namespace); - } - ctx.fireChannelRead(elementEnd); - break; - case XMLStreamConstants.PROCESSING_INSTRUCTION: - ctx.fireChannelRead( - new XmlProcessingInstruction(streamReader.getPIData(), streamReader.getPITarget())); - break; - case XMLStreamConstants.CHARACTERS: - ctx.fireChannelRead(new XmlCharacters(streamReader.getText())); - break; - case XMLStreamConstants.COMMENT: - ctx.fireChannelRead(new XmlComment(streamReader.getText())); - break; - case XMLStreamConstants.SPACE: - ctx.fireChannelRead(new XmlSpace(streamReader.getText())); - break; - case XMLStreamConstants.ENTITY_REFERENCE: - ctx.fireChannelRead(new XmlEntityReference(streamReader.getLocalName(), streamReader.getText())); - break; - case XMLStreamConstants.DTD: - ctx.fireChannelRead(new XmlDTD(streamReader.getText())); - break; - case XMLStreamConstants.CDATA: - ctx.fireChannelRead(new XmlCdata(streamReader.getText())); - break; - } - } - } - -} diff --git a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlDocumentEnd.java b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlDocumentEnd.java deleted file mode 100644 index e9ad54d762..0000000000 --- a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlDocumentEnd.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.xml; - -/** - * End of XML document - */ -public final class XmlDocumentEnd { - - public static final XmlDocumentEnd INSTANCE = new XmlDocumentEnd(); - - private XmlDocumentEnd() { - } - -} 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 deleted file mode 100644 index 79e0d6170b..0000000000 --- a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlDocumentStart.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.xml; - -/** - * Beginning of the XML document ... i.e. XML header - */ -public class XmlDocumentStart { - - private final String encoding; - private final String version; - private final boolean standalone; - private final String encodingScheme; - - public XmlDocumentStart(String encoding, String version, boolean standalone, String encodingScheme) { - this.encoding = encoding; - this.version = version; - this.standalone = standalone; - this.encodingScheme = encodingScheme; - } - - /** Return defined or guessed XML encoding **/ - public String encoding() { - return encoding; - } - - /** Return defined XML version or null **/ - public String version() { - return version; - } - - /** Return standalonity of the document **/ - public boolean standalone() { - return standalone; - } - - /** Return defined encoding or null **/ - public String encodingScheme() { - return encodingScheme; - } - - @Override - public boolean equals(Object o) { - 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 (encodingScheme != null ? !encodingScheme.equals(that.encodingScheme) : that.encodingScheme != null) { - return false; - } - if (version != null ? !version.equals(that.version) : that.version != null) { - return false; - } - - return true; - } - - @Override - public int hashCode() { - int result = encoding != null ? encoding.hashCode() : 0; - result = 31 * result + (version != null ? version.hashCode() : 0); - result = 31 * result + (standalone ? 1 : 0); - result = 31 * result + (encodingScheme != null ? encodingScheme.hashCode() : 0); - return result; - } - - @Override - public String toString() { - return "XmlDocumentStart{" + - "encoding='" + encoding + '\'' + - ", version='" + version + '\'' + - ", standalone=" + standalone + - ", encodingScheme='" + encodingScheme + '\'' + - '}'; - } -} 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 deleted file mode 100644 index 83e62b2105..0000000000 --- a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlElement.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.xml; - -import java.util.LinkedList; -import java.util.List; - -/** - * Generic XML element in document, {@link XmlElementStart} represents open element and provides access to attributes, - * {@link XmlElementEnd} represents closing element. - */ -public abstract class XmlElement { - - private final String name; - private final String namespace; - private final String prefix; - - private final List namespaces = new LinkedList<>(); - - protected XmlElement(String name, String namespace, String prefix) { - this.name = name; - this.namespace = namespace; - this.prefix = prefix; - } - - public String name() { - return name; - } - - public String namespace() { - return namespace; - } - - public String prefix() { - return prefix; - } - - public List namespaces() { - return namespaces; - } - - @Override - public boolean equals(Object o) { - 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.equals(that.namespaces)) { - return false; - } - if (prefix != null ? !prefix.equals(that.prefix) : that.prefix != null) { - return false; - } - - return true; - } - - @Override - public int hashCode() { - int result = name.hashCode(); - result = 31 * result + (namespace != null ? namespace.hashCode() : 0); - result = 31 * result + (prefix != null ? prefix.hashCode() : 0); - result = 31 * result + namespaces.hashCode(); - return result; - } - - @Override - public String toString() { - return ", name='" + name + '\'' + - ", namespace='" + namespace + '\'' + - ", prefix='" + prefix + '\'' + - ", namespaces=" + namespaces; - } - -} diff --git a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlElementEnd.java b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlElementEnd.java deleted file mode 100644 index 7d1bc40313..0000000000 --- a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlElementEnd.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.xml; - -/** - * Specific {@link XmlElement} representing end of element. - */ -public class XmlElementEnd extends XmlElement { - - public XmlElementEnd(String name, String namespace, String prefix) { - super(name, namespace, prefix); - } - - @Override - public String toString() { - return "XmlElementStart{" + - super.toString() + - "} "; - } - -} 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 deleted file mode 100644 index ce7d70167c..0000000000 --- a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlElementStart.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.xml; - -import java.util.LinkedList; -import java.util.List; - -/** - * Specific {@link XmlElement} representing beginning of element. - */ -public class XmlElementStart extends XmlElement { - - private final List attributes = new LinkedList<>(); - - public XmlElementStart(String name, String namespace, String prefix) { - super(name, namespace, prefix); - } - - public List attributes() { - return 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; - } - - XmlElementStart that = (XmlElementStart) o; - - return attributes.equals(that.attributes); - } - - @Override - public int hashCode() { - int result = super.hashCode(); - result = 31 * result + attributes.hashCode(); - return result; - } - - @Override - public String toString() { - return "XmlElementStart{" + - "attributes=" + attributes + - super.toString() + - "} "; - } - -} 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 deleted file mode 100644 index 387cb7fe5b..0000000000 --- a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlEntityReference.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.xml; - -/** - * XML entity reference ... {@code &#nnnn;} - */ -public class XmlEntityReference { - - private final String name; - private final String text; - - public XmlEntityReference(String name, String text) { - this.name = name; - this.text = text; - } - - public String name() { - return name; - } - - public String text() { - return text; - } - - @Override - public boolean equals(Object o) { - 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; - } - return text != null ? text.equals(that.text) : that.text == null; - } - - @Override - public int hashCode() { - int result = name != null ? name.hashCode() : 0; - result = 31 * result + (text != null ? text.hashCode() : 0); - return result; - } - - @Override - public String toString() { - return "XmlEntityReference{" + - "name='" + name + '\'' + - ", text='" + text + '\'' + - '}'; - } -} 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 deleted file mode 100644 index b375fee816..0000000000 --- a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlNamespace.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.xml; - -/** - * XML namespace is part of XML element. - */ -public class XmlNamespace { - - private final String prefix; - private final String uri; - - public XmlNamespace(String prefix, String uri) { - this.prefix = prefix; - this.uri = uri; - } - - public String prefix() { - return prefix; - } - - public String uri() { - return uri; - } - - @Override - public boolean equals(Object o) { - 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; - } - - return true; - } - - @Override - public int hashCode() { - int result = prefix != null ? prefix.hashCode() : 0; - result = 31 * result + (uri != null ? uri.hashCode() : 0); - return result; - } - - @Override - public String toString() { - return "XmlNamespace{" + - "prefix='" + prefix + '\'' + - ", uri='" + uri + '\'' + - '}'; - } - -} 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 deleted file mode 100644 index f61e6d18af..0000000000 --- a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlProcessingInstruction.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.xml; - -/** - * XML processing instruction - */ -public class XmlProcessingInstruction { - - private final String data; - private final String target; - - public XmlProcessingInstruction(String data, String target) { - this.data = data; - this.target = target; - } - - public String data() { - return data; - } - - public String target() { - return target; - } - - @Override - public boolean equals(Object o) { - 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; - } - - return true; - } - - @Override - public int hashCode() { - int result = data != null ? data.hashCode() : 0; - result = 31 * result + (target != null ? target.hashCode() : 0); - return result; - } - - @Override - public String toString() { - return "XmlProcessingInstruction{" + - "data='" + data + '\'' + - ", target='" + target + '\'' + - '}'; - } - -} diff --git a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlSpace.java b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlSpace.java deleted file mode 100644 index c91b8605f2..0000000000 --- a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlSpace.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.xml; - -/** - * White space characters - */ -public class XmlSpace extends XmlContent { - - public XmlSpace(String data) { - super(data); - } - -} diff --git a/codec-xml/src/main/java/io/netty/handler/codec/xml/package-info.java b/codec-xml/src/main/java/io/netty/handler/codec/xml/package-info.java deleted file mode 100644 index d7c9a2bd7c..0000000000 --- a/codec-xml/src/main/java/io/netty/handler/codec/xml/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * XML codec provides asynchronous and non-blocking XML parser based on the - * Aalto XML parser. - */ -package io.netty.handler.codec.xml; diff --git a/codec-xml/src/test/java/io/netty/handler/codec/xml/XmlDecoderTest.java b/codec-xml/src/test/java/io/netty/handler/codec/xml/XmlDecoderTest.java deleted file mode 100644 index 154aed8056..0000000000 --- a/codec-xml/src/test/java/io/netty/handler/codec/xml/XmlDecoderTest.java +++ /dev/null @@ -1,300 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.xml; - -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.CharsetUtil; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.hamcrest.CoreMatchers.*; -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.core.IsNull.nullValue; - -/** - * Verifies the basic functionality of the {@link XmlDecoder}. - * XML borrowed from - * - * Woodstox : Validate against XML Schema - */ -public class XmlDecoderTest { - - private static final String XML1 = "" + - "" + - "" + - "" + - "" + - "±1\n" + - "Alba ]]>" + - " " + - "100"; - - private static final String XML3 = ""; - - private static final String XML4 = ""; - - private EmbeddedChannel channel; - - @BeforeEach - public void setup() throws Exception { - channel = new EmbeddedChannel(new XmlDecoder()); - } - - @AfterEach - public void teardown() throws Exception { - channel.finish(); - } - - /** - * This test feeds basic XML and verifies the resulting messages - */ - @Test - public void shouldDecodeRequestWithSimpleXml() { - Object temp; - - write(XML1); - - temp = channel.readInbound(); - assertThat(temp, instanceOf(XmlDocumentStart.class)); - assertThat(((XmlDocumentStart) temp).version(), is("1.0")); - assertThat(((XmlDocumentStart) temp).encoding(), is("UTF-8")); - assertThat(((XmlDocumentStart) temp).standalone(), is(false)); - assertThat(((XmlDocumentStart) temp).encodingScheme(), is(nullValue())); - - temp = channel.readInbound(); - assertThat(temp, instanceOf(XmlDTD.class)); - assertThat(((XmlDTD) temp).text(), is("employee.dtd")); - - temp = channel.readInbound(); - assertThat(temp, instanceOf(XmlProcessingInstruction.class)); - assertThat(((XmlProcessingInstruction) temp).target(), is("xml-stylesheet")); - assertThat(((XmlProcessingInstruction) temp).data(), is("type=\"text/css\" href=\"netty.css\"")); - - temp = channel.readInbound(); - assertThat(temp, instanceOf(XmlProcessingInstruction.class)); - assertThat(((XmlProcessingInstruction) temp).target(), is("xml-test")); - assertThat(((XmlProcessingInstruction) temp).data(), is("")); - - temp = channel.readInbound(); - assertThat(temp, instanceOf(XmlElementStart.class)); - assertThat(((XmlElementStart) temp).name(), is("employee")); - assertThat(((XmlElementStart) temp).prefix(), is("")); - assertThat(((XmlElementStart) temp).namespace(), is("")); - assertThat(((XmlElementStart) temp).attributes().size(), is(0)); - assertThat(((XmlElementStart) temp).namespaces().size(), is(1)); - assertThat(((XmlElementStart) temp).namespaces().get(0).prefix(), is("nettya")); - assertThat(((XmlElementStart) temp).namespaces().get(0).uri(), is("https://netty.io/netty/a")); - - temp = channel.readInbound(); - assertThat(temp, instanceOf(XmlElementStart.class)); - assertThat(((XmlElementStart) temp).name(), is("id")); - assertThat(((XmlElementStart) temp).prefix(), is("nettya")); - assertThat(((XmlElementStart) temp).namespace(), is("https://netty.io/netty/a")); - assertThat(((XmlElementStart) temp).attributes().size(), is(0)); - assertThat(((XmlElementStart) temp).namespaces().size(), is(0)); - - temp = channel.readInbound(); - assertThat(temp, instanceOf(XmlEntityReference.class)); - assertThat(((XmlEntityReference) temp).name(), is("plusmn")); - assertThat(((XmlEntityReference) temp).text(), is("")); - - temp = channel.readInbound(); - assertThat(temp, instanceOf(XmlCharacters.class)); - assertThat(((XmlCharacters) temp).data(), is("1")); - - temp = channel.readInbound(); - assertThat(temp, instanceOf(XmlElementEnd.class)); - assertThat(((XmlElementEnd) temp).name(), is("id")); - assertThat(((XmlElementEnd) temp).prefix(), is("nettya")); - assertThat(((XmlElementEnd) temp).namespace(), is("https://netty.io/netty/a")); - - temp = channel.readInbound(); - assertThat(temp, instanceOf(XmlCharacters.class)); - assertThat(((XmlCharacters) temp).data(), is("\n")); - - temp = channel.readInbound(); - assertThat(temp, nullValue()); - - write(XML2); - - temp = channel.readInbound(); - assertThat(temp, instanceOf(XmlElementStart.class)); - assertThat(((XmlElementStart) temp).name(), is("name")); - assertThat(((XmlElementStart) temp).prefix(), is("")); - assertThat(((XmlElementStart) temp).namespace(), is("")); - assertThat(((XmlElementStart) temp).attributes().size(), is(1)); - assertThat(((XmlElementStart) temp).attributes().get(0).name(), is("type")); - assertThat(((XmlElementStart) temp).attributes().get(0).value(), is("given")); - assertThat(((XmlElementStart) temp).attributes().get(0).prefix(), is("")); - assertThat(((XmlElementStart) temp).attributes().get(0).namespace(), is("")); - assertThat(((XmlElementStart) temp).namespaces().size(), is(0)); - - temp = channel.readInbound(); - assertThat(temp, instanceOf(XmlCharacters.class)); - assertThat(((XmlCharacters) temp).data(), is("Alba")); - - temp = channel.readInbound(); - assertThat(temp, instanceOf(XmlElementEnd.class)); - assertThat(((XmlElementEnd) temp).name(), is("name")); - assertThat(((XmlElementEnd) temp).prefix(), is("")); - assertThat(((XmlElementEnd) temp).namespace(), is("")); - - temp = channel.readInbound(); - assertThat(temp, instanceOf(XmlCdata.class)); - assertThat(((XmlCdata) temp).data(), is(" ")); - - temp = channel.readInbound(); - assertThat(temp, instanceOf(XmlCharacters.class)); - assertThat(((XmlCharacters) temp).data(), is(" ")); - - temp = channel.readInbound(); - assertThat(temp, instanceOf(XmlComment.class)); - assertThat(((XmlComment) temp).data(), is(" namespaced ")); - - temp = channel.readInbound(); - assertThat(temp, instanceOf(XmlElementStart.class)); - assertThat(((XmlElementStart) temp).name(), is("salary")); - assertThat(((XmlElementStart) temp).prefix(), is("nettyb")); - assertThat(((XmlElementStart) temp).namespace(), is("https://netty.io/netty/b")); - assertThat(((XmlElementStart) temp).attributes().size(), is(1)); - assertThat(((XmlElementStart) temp).attributes().get(0).name(), is("period")); - assertThat(((XmlElementStart) temp).attributes().get(0).value(), is("weekly")); - assertThat(((XmlElementStart) temp).attributes().get(0).prefix(), is("nettyb")); - assertThat(((XmlElementStart) temp).attributes().get(0).namespace(), is("https://netty.io/netty/b")); - assertThat(((XmlElementStart) temp).namespaces().size(), is(1)); - assertThat(((XmlElementStart) temp).namespaces().get(0).prefix(), is("nettyb")); - assertThat(((XmlElementStart) temp).namespaces().get(0).uri(), is("https://netty.io/netty/b")); - - temp = channel.readInbound(); - assertThat(temp, instanceOf(XmlCharacters.class)); - assertThat(((XmlCharacters) temp).data(), is("100")); - - temp = channel.readInbound(); - assertThat(temp, instanceOf(XmlElementEnd.class)); - assertThat(((XmlElementEnd) temp).name(), is("salary")); - assertThat(((XmlElementEnd) temp).prefix(), is("nettyb")); - assertThat(((XmlElementEnd) temp).namespace(), is("https://netty.io/netty/b")); - assertThat(((XmlElementEnd) temp).namespaces().size(), is(1)); - assertThat(((XmlElementEnd) temp).namespaces().get(0).prefix(), is("nettyb")); - assertThat(((XmlElementEnd) temp).namespaces().get(0).uri(), is("https://netty.io/netty/b")); - - temp = channel.readInbound(); - assertThat(temp, instanceOf(XmlElementStart.class)); - assertThat(((XmlElementStart) temp).name(), is("last")); - assertThat(((XmlElementStart) temp).prefix(), is("")); - assertThat(((XmlElementStart) temp).namespace(), is("")); - assertThat(((XmlElementStart) temp).attributes().size(), is(0)); - assertThat(((XmlElementStart) temp).namespaces().size(), is(0)); - - temp = channel.readInbound(); - assertThat(temp, instanceOf(XmlElementEnd.class)); - assertThat(((XmlElementEnd) temp).name(), is("last")); - assertThat(((XmlElementEnd) temp).prefix(), is("")); - assertThat(((XmlElementEnd) temp).namespace(), is("")); - assertThat(((XmlElementEnd) temp).namespaces().size(), is(0)); - - temp = channel.readInbound(); - assertThat(temp, instanceOf(XmlElementEnd.class)); - assertThat(((XmlElementEnd) temp).name(), is("employee")); - assertThat(((XmlElementEnd) temp).prefix(), is("")); - assertThat(((XmlElementEnd) temp).namespace(), is("")); - assertThat(((XmlElementEnd) temp).namespaces().size(), is(1)); - assertThat(((XmlElementEnd) temp).namespaces().get(0).prefix(), is("nettya")); - assertThat(((XmlElementEnd) temp).namespaces().get(0).uri(), is("https://netty.io/netty/a")); - - temp = channel.readInbound(); - assertThat(temp, nullValue()); - } - - /** - * This test checks for different attributes in XML header - */ - @Test - public void shouldDecodeXmlHeader() { - Object temp; - - write(XML3); - - temp = channel.readInbound(); - assertThat(temp, instanceOf(XmlDocumentStart.class)); - assertThat(((XmlDocumentStart) temp).version(), is("1.1")); - assertThat(((XmlDocumentStart) temp).encoding(), is("UTF-8")); - assertThat(((XmlDocumentStart) temp).standalone(), is(true)); - assertThat(((XmlDocumentStart) temp).encodingScheme(), is("UTF-8")); - - temp = channel.readInbound(); - assertThat(temp, instanceOf(XmlElementStart.class)); - assertThat(((XmlElementStart) temp).name(), is("netty")); - assertThat(((XmlElementStart) temp).prefix(), is("")); - assertThat(((XmlElementStart) temp).namespace(), is("")); - assertThat(((XmlElementStart) temp).attributes().size(), is(0)); - assertThat(((XmlElementStart) temp).namespaces().size(), is(0)); - - temp = channel.readInbound(); - assertThat(temp, instanceOf(XmlElementEnd.class)); - assertThat(((XmlElementEnd) temp).name(), is("netty")); - assertThat(((XmlElementEnd) temp).prefix(), is("")); - assertThat(((XmlElementEnd) temp).namespace(), is("")); - assertThat(((XmlElementEnd) temp).namespaces().size(), is(0)); - - temp = channel.readInbound(); - assertThat(temp, nullValue()); - } - - /** - * This test checks for no XML header - */ - @Test - public void shouldDecodeWithoutHeader() { - Object temp; - - write(XML4); - - temp = channel.readInbound(); - assertThat(temp, instanceOf(XmlDocumentStart.class)); - assertThat(((XmlDocumentStart) temp).version(), is(nullValue())); - assertThat(((XmlDocumentStart) temp).encoding(), is("UTF-8")); - assertThat(((XmlDocumentStart) temp).standalone(), is(false)); - assertThat(((XmlDocumentStart) temp).encodingScheme(), is(nullValue())); - - temp = channel.readInbound(); - assertThat(temp, instanceOf(XmlElementStart.class)); - assertThat(((XmlElementStart) temp).name(), is("netty")); - assertThat(((XmlElementStart) temp).prefix(), is("")); - assertThat(((XmlElementStart) temp).namespace(), is("")); - assertThat(((XmlElementStart) temp).attributes().size(), is(0)); - assertThat(((XmlElementStart) temp).namespaces().size(), is(0)); - - temp = channel.readInbound(); - assertThat(temp, instanceOf(XmlElementEnd.class)); - assertThat(((XmlElementEnd) temp).name(), is("netty")); - assertThat(((XmlElementEnd) temp).prefix(), is("")); - assertThat(((XmlElementEnd) temp).namespace(), is("")); - assertThat(((XmlElementEnd) temp).namespaces().size(), is(0)); - - temp = channel.readInbound(); - assertThat(temp, nullValue()); - } - - private void write(String content) { - assertThat(channel.writeInbound(Unpooled.copiedBuffer(content, CharsetUtil.UTF_8)), is(true)); - } - -} diff --git a/codec/pom.xml b/codec/pom.xml deleted file mode 100644 index a4dfdee81a..0000000000 --- a/codec/pom.xml +++ /dev/null @@ -1,132 +0,0 @@ - - - - - 4.0.0 - - io.netty - netty-parent - 5.0.0.Final-SNAPSHOT - - - netty-codec - jar - - Netty/Codec - - - io.netty.codec - - - - - ${project.groupId} - netty-common - ${project.version} - - - ${project.groupId} - netty-buffer - ${project.version} - - - ${project.groupId} - netty-transport - ${project.version} - - - com.google.protobuf - protobuf-java - true - - - com.google.protobuf.nano - protobuf-javanano - true - - - org.jboss.marshalling - jboss-marshalling - true - - - com.ning - compress-lzf - true - - - net.jpountz.lz4 - lz4 - true - - - com.github.jponge - lzma-java - true - - - com.github.luben - zstd-jni - true - - - com.aayushatharva.brotli4j - brotli4j - true - - - com.aayushatharva.brotli4j - native-linux-x86_64 - true - - - com.aayushatharva.brotli4j - native-osx-x86_64 - true - - - com.aayushatharva.brotli4j - native-windows-x86_64 - true - - - - org.mockito - mockito-core - - - - - org.jboss.marshalling - jboss-marshalling-serial - test - - - org.jboss.marshalling - jboss-marshalling-river - test - - - - - org.apache.commons - commons-compress - test - - - - diff --git a/codec/src/main/java/io/netty/handler/codec/AsciiHeadersEncoder.java b/codec/src/main/java/io/netty/handler/codec/AsciiHeadersEncoder.java deleted file mode 100644 index 8d0186428b..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/AsciiHeadersEncoder.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec; - -import static java.util.Objects.requireNonNull; - -import java.util.Map.Entry; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.util.AsciiString; -import io.netty.util.CharsetUtil; - -public final class AsciiHeadersEncoder { - - /** - * The separator characters to insert between a header name and a header value. - */ - public enum SeparatorType { - /** - * {@code ':'} - */ - COLON, - /** - * {@code ': '} - */ - COLON_SPACE, - } - - /** - * The newline characters to insert between header entries. - */ - public enum NewlineType { - /** - * {@code '\n'} - */ - LF, - /** - * {@code '\r\n'} - */ - CRLF - } - - private final ByteBuf buf; - private final SeparatorType separatorType; - private final NewlineType newlineType; - - public AsciiHeadersEncoder(ByteBuf buf) { - this(buf, SeparatorType.COLON_SPACE, NewlineType.CRLF); - } - - public AsciiHeadersEncoder(ByteBuf buf, SeparatorType separatorType, NewlineType newlineType) { - requireNonNull(buf, "buf"); - requireNonNull(separatorType, "separatorType"); - requireNonNull(newlineType, "newlineType"); - - this.buf = buf; - this.separatorType = separatorType; - this.newlineType = newlineType; - } - - public void encode(Entry entry) { - final CharSequence name = entry.getKey(); - final CharSequence value = entry.getValue(); - final ByteBuf buf = this.buf; - final int nameLen = name.length(); - final int valueLen = value.length(); - final int entryLen = nameLen + valueLen + 4; - int offset = buf.writerIndex(); - buf.ensureWritable(entryLen); - writeAscii(buf, offset, name); - offset += nameLen; - - switch (separatorType) { - case COLON: - buf.setByte(offset ++, ':'); - break; - case COLON_SPACE: - buf.setByte(offset ++, ':'); - buf.setByte(offset ++, ' '); - break; - default: - throw new Error(); - } - - writeAscii(buf, offset, value); - offset += valueLen; - - switch (newlineType) { - case LF: - buf.setByte(offset ++, '\n'); - break; - case CRLF: - buf.setByte(offset ++, '\r'); - buf.setByte(offset ++, '\n'); - break; - default: - throw new Error(); - } - - buf.writerIndex(offset); - } - - private static void writeAscii(ByteBuf buf, int offset, CharSequence value) { - if (value instanceof AsciiString) { - ByteBufUtil.copy((AsciiString) value, 0, buf, offset, value.length()); - } else { - buf.setCharSequence(offset, value, CharsetUtil.US_ASCII); - } - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/ByteToMessageCodec.java b/codec/src/main/java/io/netty/handler/codec/ByteToMessageCodec.java deleted file mode 100644 index a5aa3f91b5..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/ByteToMessageCodec.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerAdapter; -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.concurrent.Future; -import io.netty.util.internal.TypeParameterMatcher; - -/** - * A Codec for on-the-fly encoding/decoding of bytes to messages and vise-versa. - * - * This can be thought of as a combination of {@link ByteToMessageDecoder} and {@link MessageToByteEncoder}. - * - * Be aware that sub-classes of {@link ByteToMessageCodec} MUST NOT - * annotated with {@link @Sharable}. - */ -public abstract class ByteToMessageCodec extends ChannelHandlerAdapter { - - private final TypeParameterMatcher outboundMsgMatcher; - private final MessageToByteEncoder encoder; - - private final ByteToMessageDecoder decoder = new ByteToMessageDecoder() { - @Override - public void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - ByteToMessageCodec.this.decode(ctx, in); - } - - @Override - protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - ByteToMessageCodec.this.decodeLast(ctx, in); - } - }; - - /** - * see {@link #ByteToMessageCodec(boolean)} with {@code true} as boolean parameter. - */ - protected ByteToMessageCodec() { - this(true); - } - - /** - * see {@link #ByteToMessageCodec(Class, boolean)} with {@code true} as boolean value. - */ - protected ByteToMessageCodec(Class outboundMessageType) { - this(outboundMessageType, true); - } - - /** - * Create a new instance which will try to detect the types to match out of the type parameter of the class. - * - * @param preferDirect {@code true} if a direct {@link ByteBuf} should be tried to be used as target for - * the encoded messages. If {@code false} is used it will allocate a heap - * {@link ByteBuf}, which is backed by an byte array. - */ - protected ByteToMessageCodec(boolean preferDirect) { - ensureNotSharable(); - outboundMsgMatcher = TypeParameterMatcher.find(this, ByteToMessageCodec.class, "I"); - encoder = new Encoder(preferDirect); - } - - /** - * Create a new instance - * - * @param outboundMessageType The type of messages to match - * @param preferDirect {@code true} if a direct {@link ByteBuf} should be tried to be used as target for - * the encoded messages. If {@code false} is used it will allocate a heap - * {@link ByteBuf}, which is backed by an byte array. - */ - protected ByteToMessageCodec(Class outboundMessageType, boolean preferDirect) { - ensureNotSharable(); - outboundMsgMatcher = TypeParameterMatcher.get(outboundMessageType); - encoder = new Encoder(preferDirect); - } - - /** - * Returns {@code true} if and only if the specified message can be encoded by this codec. - * - * @param msg the message - */ - public boolean acceptOutboundMessage(Object msg) throws Exception { - return outboundMsgMatcher.match(msg); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - decoder.channelRead(ctx, msg); - } - - @Override - public Future write(ChannelHandlerContext ctx, Object msg) { - return encoder.write(ctx, msg); - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { - decoder.channelReadComplete(ctx); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - decoder.channelInactive(ctx); - } - - @Override - public void handlerAdded(ChannelHandlerContext ctx) throws Exception { - try { - decoder.handlerAdded(ctx); - } finally { - encoder.handlerAdded(ctx); - } - } - - @Override - public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { - try { - decoder.handlerRemoved(ctx); - } finally { - encoder.handlerRemoved(ctx); - } - } - - /** - * @see MessageToByteEncoder#encode(ChannelHandlerContext, Object, ByteBuf) - */ - protected abstract void encode(ChannelHandlerContext ctx, I msg, ByteBuf out) throws Exception; - - /** - * @see ByteToMessageDecoder#decode(ChannelHandlerContext, ByteBuf) - */ - protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception; - - /** - * @see ByteToMessageDecoder#decodeLast(ChannelHandlerContext, ByteBuf) - */ - protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - if (in.isReadable()) { - // Only call decode() if there is something left in the buffer to decode. - // See https://github.com/netty/netty/issues/4386 - decode(ctx, in); - } - } - - private final class Encoder extends MessageToByteEncoder { - Encoder(boolean preferDirect) { - super(preferDirect); - } - - @Override - public boolean acceptOutboundMessage(Object msg) throws Exception { - return ByteToMessageCodec.this.acceptOutboundMessage(msg); - } - - @Override - protected void encode(ChannelHandlerContext ctx, I msg, ByteBuf out) throws Exception { - ByteToMessageCodec.this.encode(ctx, msg, out); - } - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/ByteToMessageDecoder.java b/codec/src/main/java/io/netty/handler/codec/ByteToMessageDecoder.java deleted file mode 100644 index eb37e0c36b..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/ByteToMessageDecoder.java +++ /dev/null @@ -1,702 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.ByteBufConvertible; -import io.netty.buffer.CompositeByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.buffer.api.BufferAllocator; -import io.netty.channel.Channel; -import io.netty.channel.ChannelConfig; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerAdapter; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.socket.ChannelInputShutdownEvent; -import io.netty.util.Attribute; -import io.netty.util.AttributeKey; -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; -import io.netty.util.internal.StringUtil; - -import java.net.SocketAddress; - -import static io.netty.util.internal.ObjectUtil.checkPositive; -import static java.lang.Integer.MAX_VALUE; -import static java.util.Objects.requireNonNull; - -/** - * {@link ChannelHandler} which decodes bytes in a stream-like fashion from one {@link ByteBuf} to an - * other Message type. - * - * For example here is an implementation which reads all readable bytes from - * the input {@link ByteBuf}, creates a new {@link ByteBuf} and forward it to the next {@link ChannelHandler} - * in the {@link ChannelPipeline}. - * - *

- *     public class SquareDecoder extends {@link ByteToMessageDecoder} {
- *         {@code @Override}
- *         public void decode({@link ChannelHandlerContext} ctx, {@link ByteBuf} in)
- *                 throws {@link Exception} {
- *             ctx.fireChannelRead(in.readBytes(in.readableBytes()));
- *         }
- *     }
- * 
- * - *

Frame detection

- *

- * Generally frame detection should be handled earlier in the pipeline by adding a - * {@link DelimiterBasedFrameDecoder}, {@link FixedLengthFrameDecoder}, {@link LengthFieldBasedFrameDecoder}, - * or {@link LineBasedFrameDecoder}. - *

- * If a custom frame decoder is required, then one needs to be careful when implementing - * one with {@link ByteToMessageDecoder}. Ensure there are enough bytes in the buffer for a - * complete frame by checking {@link ByteBuf#readableBytes()}. If there are not enough bytes - * for a complete frame, return without modifying the reader index to allow more bytes to arrive. - *

- * To check for complete frames without modifying the reader index, use methods like {@link ByteBuf#getInt(int)}. - * One MUST use the reader index when using methods like {@link ByteBuf#getInt(int)}. - * For example calling in.getInt(0) is assuming the frame starts at the beginning of the buffer, which - * is not always the case. Use in.getInt(in.readerIndex()) instead. - *

Pitfalls

- *

- * Be aware that sub-classes of {@link ByteToMessageDecoder} MUST NOT - * annotated with {@link @Sharable}. - *

- * Some methods such as {@link ByteBuf#readBytes(int)} will cause a memory leak if the returned buffer - * is not released or fired through the {@link ChannelPipeline} via - * {@link ChannelHandlerContext#fireChannelRead(Object)}. Use derived buffers like {@link ByteBuf#readSlice(int)} to - * avoid leaking memory. - */ -public abstract class ByteToMessageDecoder extends ChannelHandlerAdapter { - - /** - * Cumulate {@link ByteBuf}s by merge them into one {@link ByteBuf}'s, using memory copies. - */ - public static final Cumulator MERGE_CUMULATOR = (alloc, cumulation, in) -> { - if (!cumulation.isReadable() && in.isContiguous()) { - // If cumulation is empty and input buffer is contiguous, use it directly - cumulation.release(); - return in; - } - try { - final int required = in.readableBytes(); - if (required > cumulation.maxWritableBytes() || - (required > cumulation.maxFastWritableBytes() && cumulation.refCnt() > 1) || - cumulation.isReadOnly()) { - // Expand cumulation (by replacing it) under the following conditions: - // - cumulation cannot be resized to accommodate the additional data - // - cumulation can be expanded with a reallocation operation to accommodate but the buffer is - // assumed to be shared (e.g. refCnt() > 1) and the reallocation may not be safe. - return expandCumulation(alloc, cumulation, in); - } - cumulation.writeBytes(in, in.readerIndex(), required); - in.readerIndex(in.writerIndex()); - return cumulation; - } 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(); - } - }; - - /** - * Cumulate {@link ByteBuf}s by add them to a {@link CompositeByteBuf} and so do no memory copy whenever possible. - * Be aware that {@link CompositeByteBuf} use a more complex indexing implementation so depending on your use-case - * and the decoder implementation this may be slower then just use the {@link #MERGE_CUMULATOR}. - */ - - public static final Cumulator COMPOSITE_CUMULATOR = (alloc, cumulation, in) -> { - if (!cumulation.isReadable()) { - cumulation.release(); - return in; - } - CompositeByteBuf composite = null; - try { - if (cumulation instanceof CompositeByteBuf && cumulation.refCnt() == 1) { - composite = (CompositeByteBuf) cumulation; - // Writer index must equal capacity if we are going to "write" - // new components to the end - if (composite.writerIndex() != composite.capacity()) { - composite.capacity(composite.writerIndex()); - } - } else { - composite = alloc.compositeBuffer(MAX_VALUE).addFlattenedComponents(true, cumulation); - } - composite.addFlattenedComponents(true, in); - in = null; - return composite; - } finally { - if (in != null) { - // We must release if the ownership was not transferred as otherwise it may produce a leak - in.release(); - // Also release any new buffer allocated if we're not returning it - if (composite != null && composite != cumulation) { - composite.release(); - } - } - } - }; - - ByteBuf cumulation; - private Cumulator cumulator = MERGE_CUMULATOR; - private boolean singleDecode; - private boolean first; - - /** - * This flag is used to determine if we need to call {@link ChannelHandlerContext#read()} to consume more data - * when {@link ChannelConfig#isAutoRead()} is {@code false}. - */ - private boolean firedChannelRead; - - private int discardAfterReads = 16; - private int numReads; - private ByteToMessageDecoderContext context; - - protected ByteToMessageDecoder() { - ensureNotSharable(); - } - - /** - * If set then only one message is decoded on each {@link #channelRead(ChannelHandlerContext, Object)} - * call. This may be useful if you need to do some protocol upgrade and want to make sure nothing is mixed up. - * - * Default is {@code false} as this has performance impacts. - */ - public void setSingleDecode(boolean singleDecode) { - this.singleDecode = singleDecode; - } - - /** - * If {@code true} then only one message is decoded on each - * {@link #channelRead(ChannelHandlerContext, Object)} call. - * - * Default is {@code false} as this has performance impacts. - */ - public boolean isSingleDecode() { - return singleDecode; - } - - /** - * Set the {@link Cumulator} to use for cumulate the received {@link ByteBuf}s. - */ - public void setCumulator(Cumulator cumulator) { - requireNonNull(cumulator, "cumulator"); - this.cumulator = cumulator; - } - - /** - * Set the number of reads after which {@link ByteBuf#discardSomeReadBytes()} are called and so free up memory. - * The default is {@code 16}. - */ - public void setDiscardAfterReads(int discardAfterReads) { - checkPositive(discardAfterReads, "discardAfterReads"); - this.discardAfterReads = discardAfterReads; - } - - /** - * Returns the actual number of readable bytes in the internal cumulative - * buffer of this decoder. You usually do not need to rely on this value - * to write a decoder. Use it only when you must use it at your own risk. - * This method is a shortcut to {@link #internalBuffer() internalBuffer().readableBytes()}. - */ - protected int actualReadableBytes() { - return internalBuffer().readableBytes(); - } - - /** - * Returns the internal cumulative buffer of this decoder. You usually - * do not need to access the internal buffer directly to write a decoder. - * Use it only when you must use it at your own risk. - */ - protected ByteBuf internalBuffer() { - if (cumulation != null) { - return cumulation; - } else { - return Unpooled.EMPTY_BUFFER; - } - } - - @Override - public final void handlerAdded(ChannelHandlerContext ctx) throws Exception { - context = new ByteToMessageDecoderContext(ctx); - handlerAdded0(context); - } - - protected void handlerAdded0(ChannelHandlerContext ctx) throws Exception { - } - - @Override - public final void handlerRemoved(ChannelHandlerContext ctx) throws Exception { - ByteBuf buf = cumulation; - if (buf != null) { - // Directly set this to null so we are sure we not access it in any other method here anymore. - cumulation = null; - numReads = 0; - int readable = buf.readableBytes(); - if (readable > 0) { - ctx.fireChannelRead(buf); - ctx.fireChannelReadComplete(); - } else { - buf.release(); - } - } - handlerRemoved0(context); - } - - /** - * Gets called after the {@link ByteToMessageDecoder} was removed from the actual context and it doesn't handle - * events anymore. - */ - protected void handlerRemoved0(ChannelHandlerContext ctx) throws Exception { } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - if (msg instanceof ByteBufConvertible) { - try { - ByteBuf data = ((ByteBufConvertible) msg).asByteBuf(); - first = cumulation == null; - if (first) { - cumulation = data; - } else { - cumulation = cumulator.cumulate(ctx.alloc(), cumulation, data); - } - assert context.ctx == ctx || ctx == context; - - callDecode(context, cumulation); - } catch (DecoderException e) { - throw e; - } catch (Exception e) { - throw new DecoderException(e); - } finally { - if (cumulation != null && !cumulation.isReadable()) { - numReads = 0; - cumulation.release(); - cumulation = null; - } else if (++ numReads >= discardAfterReads) { - // We did enough reads already try to discard some bytes so we not risk to see a OOME. - // See https://github.com/netty/netty/issues/4275 - numReads = 0; - discardSomeReadBytes(); - } - - firedChannelRead |= context.fireChannelReadCallCount() > 0; - context.reset(); - } - } else { - ctx.fireChannelRead(msg); - } - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { - numReads = 0; - discardSomeReadBytes(); - if (!firedChannelRead && !ctx.channel().config().isAutoRead()) { - ctx.read(); - } - firedChannelRead = false; - ctx.fireChannelReadComplete(); - } - - protected final void discardSomeReadBytes() { - if (cumulation != null && !first && cumulation.refCnt() == 1) { - // discard some bytes if possible to make more room in the - // buffer but only if the refCnt == 1 as otherwise the user may have - // used slice().retain() or duplicate().retain(). - // - // See: - // - https://github.com/netty/netty/issues/2327 - // - https://github.com/netty/netty/issues/1764 - cumulation.discardSomeReadBytes(); - } - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - assert context.ctx == ctx || ctx == context; - channelInputClosed(context, true); - } - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - ctx.fireUserEventTriggered(evt); - if (evt instanceof ChannelInputShutdownEvent) { - // The decodeLast method is invoked when a channelInactive event is encountered. - // This method is responsible for ending requests in some situations and must be called - // when the input has been shutdown. - assert context.ctx == ctx || ctx == context; - channelInputClosed(context, false); - } - } - - private void channelInputClosed(ByteToMessageDecoderContext ctx, boolean callChannelInactive) { - try { - channelInputClosed(ctx); - } catch (DecoderException e) { - throw e; - } catch (Exception e) { - throw new DecoderException(e); - } finally { - if (cumulation != null) { - cumulation.release(); - cumulation = null; - } - if (ctx.fireChannelReadCallCount() > 0) { - ctx.reset(); - // Something was read, call fireChannelReadComplete() - ctx.fireChannelReadComplete(); - } - if (callChannelInactive) { - ctx.fireChannelInactive(); - } - } - } - - /** - * Called when the input of the channel was closed which may be because it changed to inactive or because of - * {@link ChannelInputShutdownEvent}. - */ - void channelInputClosed(ByteToMessageDecoderContext ctx) throws Exception { - if (cumulation != null) { - callDecode(ctx, cumulation); - // If callDecode(...) removed the handle from the pipeline we should not call decodeLast(...) as this would - // be unexpected. - if (!ctx.isRemoved()) { - // Use Unpooled.EMPTY_BUFFER if cumulation become null after calling callDecode(...). - // See https://github.com/netty/netty/issues/10802. - ByteBuf buffer = cumulation == null ? Unpooled.EMPTY_BUFFER : cumulation; - decodeLast(ctx, buffer); - } - } else { - decodeLast(ctx, Unpooled.EMPTY_BUFFER); - } - } - - /** - * Called once data should be decoded from the given {@link ByteBuf}. This method will call - * {@link #decode(ChannelHandlerContext, ByteBuf)} as long as decoding should take place. - * - * @param ctx the {@link ChannelHandlerContext} which this {@link ByteToMessageDecoder} belongs to - * @param in the {@link ByteBuf} from which to read data - */ - void callDecode(ByteToMessageDecoderContext ctx, ByteBuf in) { - try { - while (in.isReadable() && !ctx.isRemoved()) { - - int oldInputLength = in.readableBytes(); - int numReadCalled = ctx.fireChannelReadCallCount(); - decodeRemovalReentryProtection(ctx, in); - - // Check if this handler was removed before continuing the loop. - // If it was removed, it is not safe to continue to operate on the buffer. - // - // See https://github.com/netty/netty/issues/1664 - if (ctx.isRemoved()) { - break; - } - - if (numReadCalled == ctx.fireChannelReadCallCount()) { - if (oldInputLength == in.readableBytes()) { - break; - } else { - continue; - } - } - - if (oldInputLength == in.readableBytes()) { - throw new DecoderException( - StringUtil.simpleClassName(getClass()) + - ".decode() did not read anything but decoded a message."); - } - - if (isSingleDecode()) { - break; - } - } - } catch (DecoderException e) { - throw e; - } catch (Exception cause) { - throw new DecoderException(cause); - } - } - - /** - * Decode the from one {@link ByteBuf} to an other. This method will be called till either the input - * {@link ByteBuf} has nothing to read when return from this method or till nothing was read from the input - * {@link ByteBuf}. - * - * @param ctx the {@link ChannelHandlerContext} which this {@link ByteToMessageDecoder} belongs to - * @param in the {@link ByteBuf} from which to read data - * @throws Exception is thrown if an error occurs - */ - protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception; - - /** - * Decode the from one {@link ByteBuf} to an other. This method will be called till either the input - * {@link ByteBuf} has nothing to read when return from this method or till nothing was read from the input - * {@link ByteBuf}. - * - * @param ctx the {@link ChannelHandlerContext} which this {@link ByteToMessageDecoder} belongs to - * @param in the {@link ByteBuf} from which to read data - * @throws Exception is thrown if an error occurs - */ - final void decodeRemovalReentryProtection(ChannelHandlerContext ctx, ByteBuf in) - throws Exception { - decode(ctx, in); - } - - /** - * Is called one last time when the {@link ChannelHandlerContext} goes in-active. Which means the - * {@link #channelInactive(ChannelHandlerContext)} was triggered. - * - * By default this will just call {@link #decode(ChannelHandlerContext, ByteBuf)} but sub-classes may - * override this for some special cleanup operation. - */ - protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - if (in.isReadable()) { - // Only call decode() if there is something left in the buffer to decode. - // See https://github.com/netty/netty/issues/4386 - decodeRemovalReentryProtection(ctx, in); - } - } - - private static ByteBuf expandCumulation(ByteBufAllocator alloc, ByteBuf oldCumulation, ByteBuf in) { - ByteBuf newCumulation = alloc.buffer(alloc.calculateNewCapacity( - oldCumulation.readableBytes() + in.readableBytes(), MAX_VALUE)); - ByteBuf toRelease = newCumulation; - try { - newCumulation.writeBytes(oldCumulation); - newCumulation.writeBytes(in); - toRelease = oldCumulation; - return newCumulation; - } finally { - toRelease.release(); - } - } - - /** - * Cumulate {@link ByteBuf}s. - */ - public interface Cumulator { - /** - * Cumulate the given {@link ByteBuf}s and return the {@link ByteBuf} that holds the cumulated bytes. - * The implementation is responsible to correctly handle the life-cycle of the given {@link ByteBuf}s and so - * call {@link ByteBuf#release()} if a {@link ByteBuf} is fully consumed. - */ - ByteBuf cumulate(ByteBufAllocator alloc, ByteBuf cumulation, ByteBuf in); - } - - // Package private so we can also make use of it in ReplayingDecoder. - static final class ByteToMessageDecoderContext implements ChannelHandlerContext { - private final ChannelHandlerContext ctx; - private int fireChannelReadCalled; - - private ByteToMessageDecoderContext(ChannelHandlerContext ctx) { - this.ctx = ctx; - } - - void reset() { - fireChannelReadCalled = 0; - } - - int fireChannelReadCallCount() { - return fireChannelReadCalled; - } - - @Override - public Channel channel() { - return ctx.channel(); - } - - @Override - public EventExecutor executor() { - return ctx.executor(); - } - - @Override - public String name() { - return ctx.name(); - } - - @Override - public ChannelHandler handler() { - return ctx.handler(); - } - - @Override - public boolean isRemoved() { - return ctx.isRemoved(); - } - - @Override - public ChannelHandlerContext fireChannelRegistered() { - ctx.fireChannelRegistered(); - return this; - } - - @Override - public ChannelHandlerContext fireChannelUnregistered() { - ctx.fireChannelUnregistered(); - return this; - } - - @Override - public ChannelHandlerContext fireChannelActive() { - ctx.fireChannelActive(); - return this; - } - - @Override - public ChannelHandlerContext fireChannelInactive() { - ctx.fireChannelInactive(); - return this; - } - - @Override - public ChannelHandlerContext fireExceptionCaught(Throwable cause) { - ctx.fireExceptionCaught(cause); - return this; - } - - @Override - public ChannelHandlerContext fireUserEventTriggered(Object evt) { - ctx.fireUserEventTriggered(evt); - return this; - } - - @Override - public ChannelHandlerContext fireChannelRead(Object msg) { - fireChannelReadCalled ++; - ctx.fireChannelRead(msg); - return this; - } - - @Override - public ChannelHandlerContext fireChannelReadComplete() { - ctx.fireChannelReadComplete(); - return this; - } - - @Override - public ChannelHandlerContext fireChannelWritabilityChanged() { - ctx.fireChannelWritabilityChanged(); - return this; - } - - @Override - public Future register() { - return ctx.register(); - } - - @Override - public ChannelHandlerContext read() { - ctx.read(); - return this; - } - - @Override - public ChannelHandlerContext flush() { - ctx.flush(); - return this; - } - - @Override - public ChannelPipeline pipeline() { - return ctx.pipeline(); - } - - @Override - public ByteBufAllocator alloc() { - return ctx.alloc(); - } - - @Override - public BufferAllocator bufferAllocator() { - return ctx.bufferAllocator(); - } - - @Override - @Deprecated - public Attribute attr(AttributeKey key) { - return ctx.attr(key); - } - - @Override - @Deprecated - public boolean hasAttr(AttributeKey key) { - return ctx.hasAttr(key); - } - - @Override - public Future bind(SocketAddress localAddress) { - return ctx.bind(localAddress); - } - - @Override - public Future connect(SocketAddress remoteAddress) { - return ctx.connect(remoteAddress); - } - - @Override - public Future connect(SocketAddress remoteAddress, SocketAddress localAddress) { - return ctx.connect(remoteAddress, localAddress); - } - - @Override - public Future disconnect() { - return ctx.disconnect(); - } - - @Override - public Future close() { - return ctx.close(); - } - - @Override - public Future deregister() { - return ctx.deregister(); - } - - @Override - public Future write(Object msg) { - return ctx.write(msg); - } - - @Override - public Future writeAndFlush(Object msg) { - return ctx.writeAndFlush(msg); - } - - @Override - public Promise newPromise() { - return ctx.newPromise(); - } - - @Override - public Future newSucceededFuture() { - return ctx.newSucceededFuture(); - } - - @Override - public Future newFailedFuture(Throwable cause) { - return ctx.newFailedFuture(cause); - } - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/ByteToMessageDecoderForBuffer.java b/codec/src/main/java/io/netty/handler/codec/ByteToMessageDecoderForBuffer.java deleted file mode 100644 index 6a02154414..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/ByteToMessageDecoderForBuffer.java +++ /dev/null @@ -1,686 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.api.Buffer; -import io.netty.buffer.api.BufferAllocator; -import io.netty.buffer.api.CompositeBuffer; -import io.netty.channel.Channel; -import io.netty.channel.ChannelConfig; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerAdapter; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.socket.ChannelInputShutdownEvent; -import io.netty.util.Attribute; -import io.netty.util.AttributeKey; -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; -import io.netty.util.internal.StringUtil; - -import java.net.SocketAddress; - -import static io.netty.util.internal.MathUtil.safeFindNextPositivePowerOfTwo; -import static java.util.Objects.requireNonNull; - -/** - * {@link ChannelHandler} which decodes bytes in a stream-like fashion from one {@link Buffer} to an - * other Message type. - * - * For example here is an implementation which reads all readable bytes from - * the input {@link Buffer}, creates a new {@link Buffer} and forward it to the next {@link ChannelHandler} - * in the {@link ChannelPipeline}. - * - *

- *     public class SquareDecoder extends {@link ByteToMessageDecoderForBuffer} {
- *         {@code @Override}
- *         public void decode({@link ChannelHandlerContext} ctx, {@link Buffer} in)
- *                 throws {@link Exception} {
- *             ctx.fireChannelRead(in.readBytes(in.readableBytes()));
- *         }
- *     }
- * 
- * - *

Frame detection

- *

- * Generally frame detection should be handled earlier in the pipeline by adding a - * {@link DelimiterBasedFrameDecoder}, {@link FixedLengthFrameDecoder}, {@link LengthFieldBasedFrameDecoder}, - * or {@link LineBasedFrameDecoder}. - *

- * If a custom frame decoder is required, then one needs to be careful when implementing - * one with {@link ByteToMessageDecoderForBuffer}. Ensure there are enough bytes in the buffer for a - * complete frame by checking {@link Buffer#readableBytes()}. If there are not enough bytes - * for a complete frame, return without modifying the reader index to allow more bytes to arrive. - *

- * To check for complete frames without modifying the reader index, use methods like {@link Buffer#getInt(int)}. - * One MUST use the reader index when using methods like {@link Buffer#getInt(int)}. - * For example calling in.getInt(0) is assuming the frame starts at the beginning of the buffer, which - * is not always the case. Use in.getInt(in.readerIndex()) instead. - *

Pitfalls

- *

- * Be aware that sub-classes of {@link ByteToMessageDecoderForBuffer} MUST NOT - * annotated with {@link @Sharable}. - */ -public abstract class ByteToMessageDecoderForBuffer extends ChannelHandlerAdapter { - - /** - * Cumulate {@link Buffer}s by merge them into one {@link Buffer}'s, using memory copies. - */ - public static final Cumulator MERGE_CUMULATOR = new MergeCumulator(); - - /** - * Cumulate {@link Buffer}s by add them to a {@link CompositeBuffer} and so do no memory copy whenever possible. - * Be aware that {@link CompositeBuffer} use a more complex indexing implementation so depending on your use-case - * and the decoder implementation this may be slower then just use the {@link #MERGE_CUMULATOR}. - */ - public static final Cumulator COMPOSITE_CUMULATOR = new CompositeBufferCumulator(); - - private final int discardAfterReads = 16; - private final Cumulator cumulator; - - private Buffer cumulation; - private boolean singleDecode; - private boolean first; - /** - * This flag is used to determine if we need to call {@link ChannelHandlerContext#read()} to consume more data - * when {@link ChannelConfig#isAutoRead()} is {@code false}. - */ - private boolean firedChannelRead; - private int numReads; - private ByteToMessageDecoderContext context; - - protected ByteToMessageDecoderForBuffer() { - this(MERGE_CUMULATOR); - } - - protected ByteToMessageDecoderForBuffer(Cumulator cumulator) { - this.cumulator = requireNonNull(cumulator, "cumulator"); - ensureNotSharable(); - } - - /** - * If set then only one message is decoded on each {@link #channelRead(ChannelHandlerContext, Object)} - * call. This may be useful if you need to do some protocol upgrade and want to make sure nothing is mixed up. - * - * Default is {@code false} as this has performance impacts. - */ - public void setSingleDecode(boolean singleDecode) { - this.singleDecode = singleDecode; - } - - /** - * If {@code true} then only one message is decoded on each - * {@link #channelRead(ChannelHandlerContext, Object)} call. - * - * Default is {@code false} as this has performance impacts. - */ - public boolean isSingleDecode() { - return singleDecode; - } - - /** - * Returns the actual number of readable bytes in the internal cumulative - * buffer of this decoder. You usually do not need to rely on this value - * to write a decoder. Use it only when you must use it at your own risk. - * This method is a shortcut to {@link #internalBuffer() internalBuffer().readableBytes()}. - */ - protected int actualReadableBytes() { - return internalBuffer().readableBytes(); - } - - /** - * Returns the internal cumulative buffer of this decoder, if exists, else {@code null}. You usually - * do not need to access the internal buffer directly to write a decoder. - * Use it only when you must use it at your own risk. - * - * @return Internal {@link Buffer} if exists, else {@code null}. - */ - protected Buffer internalBuffer() { - return cumulation; - } - - @Override - public final void handlerAdded(ChannelHandlerContext ctx) throws Exception { - context = new ByteToMessageDecoderContext(ctx); - handlerAdded0(context); - } - - protected void handlerAdded0(ChannelHandlerContext ctx) throws Exception { - } - - @Override - public final void handlerRemoved(ChannelHandlerContext ctx) throws Exception { - Buffer buf = cumulation; - if (buf != null) { - // Directly set this to null so we are sure we not access it in any other method here anymore. - cumulation = null; - numReads = 0; - int readable = buf.readableBytes(); - if (readable > 0) { - ctx.fireChannelRead(buf); - ctx.fireChannelReadComplete(); - } else { - buf.close(); - } - } - handlerRemoved0(context); - } - - /** - * Gets called after the {@link ByteToMessageDecoderForBuffer} was removed from the actual context and it doesn't - * handle events anymore. - */ - protected void handlerRemoved0(ChannelHandlerContext ctx) throws Exception { } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - if (msg instanceof Buffer) { - try { - Buffer data = (Buffer) msg; - first = cumulation == null; - if (first) { - if (data.readOnly()) { - cumulation = CompositeBuffer.compose(ctx.bufferAllocator(), data.copy().send()); - data.close(); - } else { - cumulation = data; - } - } else { - cumulation = cumulator.cumulate(ctx.bufferAllocator(), cumulation, data); - } - assert context.ctx == ctx || ctx == context; - - callDecode(context, cumulation); - } catch (DecoderException e) { - throw e; - } catch (Exception e) { - throw new DecoderException(e); - } finally { - if (cumulation != null && cumulation.readableBytes() == 0) { - numReads = 0; - if (cumulation.isAccessible()) { - cumulation.close(); - } - cumulation = null; - } else if (++numReads >= discardAfterReads) { - // We did enough reads already try to discard some bytes so we not risk to see a OOME. - // See https://github.com/netty/netty/issues/4275 - numReads = 0; - discardSomeReadBytes(); - } - - firedChannelRead |= context.fireChannelReadCallCount() > 0; - context.reset(); - } - } else { - ctx.fireChannelRead(msg); - } - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { - numReads = 0; - discardSomeReadBytes(); - if (!firedChannelRead && !ctx.channel().config().isAutoRead()) { - ctx.read(); - } - firedChannelRead = false; - ctx.fireChannelReadComplete(); - } - - protected final void discardSomeReadBytes() { - if (cumulation != null && !first) { - // discard some bytes if possible to make more room in the buffer. - cumulation.compact(); - } - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - assert context.ctx == ctx || ctx == context; - channelInputClosed(context, true); - } - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - ctx.fireUserEventTriggered(evt); - if (evt instanceof ChannelInputShutdownEvent) { - // The decodeLast method is invoked when a channelInactive event is encountered. - // This method is responsible for ending requests in some situations and must be called - // when the input has been shutdown. - assert context.ctx == ctx || ctx == context; - channelInputClosed(context, false); - } - } - - private void channelInputClosed(ByteToMessageDecoderContext ctx, boolean callChannelInactive) { - try { - channelInputClosed(ctx); - } catch (DecoderException e) { - throw e; - } catch (Exception e) { - throw new DecoderException(e); - } finally { - if (cumulation != null) { - cumulation.close(); - cumulation = null; - } - if (ctx.fireChannelReadCallCount() > 0) { - ctx.reset(); - // Something was read, call fireChannelReadComplete() - ctx.fireChannelReadComplete(); - } - if (callChannelInactive) { - ctx.fireChannelInactive(); - } - } - } - - /** - * Called when the input of the channel was closed which may be because it changed to inactive or because of - * {@link ChannelInputShutdownEvent}. - */ - void channelInputClosed(ByteToMessageDecoderContext ctx) throws Exception { - if (cumulation != null) { - callDecode(ctx, cumulation); - // If callDecode(...) removed the handle from the pipeline we should not call decodeLast(...) as this would - // be unexpected. - if (!ctx.isRemoved()) { - // Use Unpooled.EMPTY_BUFFER if cumulation become null after calling callDecode(...). - // See https://github.com/netty/netty/issues/10802. - Buffer buffer = cumulation == null ? ctx.bufferAllocator().allocate(0) : cumulation; - decodeLast(ctx, buffer); - } - } else { - decodeLast(ctx, ctx.bufferAllocator().allocate(0)); - } - } - - /** - * Called once data should be decoded from the given {@link Buffer}. This method will call - * {@link #decode(ChannelHandlerContext, Buffer)} as long as decoding should take place. - * - * @param ctx the {@link ChannelHandlerContext} which this {@link ByteToMessageDecoderForBuffer} belongs to - * @param in the {@link Buffer} from which to read data - */ - void callDecode(ByteToMessageDecoderContext ctx, Buffer in) { - try { - while (in.readableBytes() > 0 && !ctx.isRemoved()) { - - int oldInputLength = in.readableBytes(); - int numReadCalled = ctx.fireChannelReadCallCount(); - decodeRemovalReentryProtection(ctx, in); - - // Check if this handler was removed before continuing the loop. - // If it was removed, it is not safe to continue to operate on the buffer. - // - // See https://github.com/netty/netty/issues/1664 - if (ctx.isRemoved()) { - break; - } - - if (numReadCalled == ctx.fireChannelReadCallCount()) { - if (oldInputLength == in.readableBytes()) { - break; - } else { - continue; - } - } - - if (oldInputLength == in.readableBytes()) { - throw new DecoderException( - StringUtil.simpleClassName(getClass()) + - ".decode() did not read anything but decoded a message."); - } - - if (isSingleDecode()) { - break; - } - } - } catch (DecoderException e) { - throw e; - } catch (Exception cause) { - throw new DecoderException(cause); - } - } - - /** - * Decode the from one {@link Buffer} to another. This method will be called till either the input - * {@link Buffer} has nothing to read when return from this method or till nothing was read from the input - * {@link Buffer}. - * - * @param ctx the {@link ChannelHandlerContext} which this {@link ByteToMessageDecoderForBuffer} belongs to - * @param in the {@link Buffer} from which to read data - * @throws Exception is thrown if an error occurs - */ - protected abstract void decode(ChannelHandlerContext ctx, Buffer in) throws Exception; - - /** - * Decode the from one {@link Buffer} to an other. This method will be called till either the input - * {@link Buffer} has nothing to read when return from this method or till nothing was read from the input - * {@link Buffer}. - * - * @param ctx the {@link ChannelHandlerContext} which this {@link ByteToMessageDecoderForBuffer} belongs to - * @param in the {@link Buffer} from which to read data - * @throws Exception is thrown if an error occurs - */ - final void decodeRemovalReentryProtection(ChannelHandlerContext ctx, Buffer in) - throws Exception { - decode(ctx, in); - } - - /** - * Is called one last time when the {@link ChannelHandlerContext} goes in-active. Which means the - * {@link #channelInactive(ChannelHandlerContext)} was triggered. - * - * By default this will just call {@link #decode(ChannelHandlerContext, Buffer)} but sub-classes may - * override this for some special cleanup operation. - */ - protected void decodeLast(ChannelHandlerContext ctx, Buffer in) throws Exception { - if (in.readableBytes() > 0) { - // Only call decode() if there is something left in the buffer to decode. - // See https://github.com/netty/netty/issues/4386 - decodeRemovalReentryProtection(ctx, in); - } - } - - private static Buffer expandCumulationAndWrite(BufferAllocator alloc, Buffer oldCumulation, Buffer in) { - final int newSize = safeFindNextPositivePowerOfTwo(oldCumulation.readableBytes() + in.readableBytes()); - Buffer newCumulation = oldCumulation.readOnly() ? alloc.allocate(newSize) : - oldCumulation.ensureWritable(newSize); - try { - if (newCumulation != oldCumulation) { - newCumulation.writeBytes(oldCumulation); - } - newCumulation.writeBytes(in); - return newCumulation; - } finally { - if (newCumulation != oldCumulation) { - oldCumulation.close(); - } - } - } - - /** - * Cumulate {@link ByteBuf}s. - */ - public interface Cumulator { - /** - * Cumulate the given {@link Buffer}s and return the {@link Buffer} that holds the cumulated bytes. - * The implementation is responsible to correctly handle the life-cycle of the given {@link Buffer}s and so - * call {@link Buffer#close()} if a {@link Buffer} is fully consumed. - */ - Buffer cumulate(BufferAllocator alloc, Buffer cumulation, Buffer in); - } - - // Package private so we can also make use of it in ReplayingDecoder. - static final class ByteToMessageDecoderContext implements ChannelHandlerContext { - private final ChannelHandlerContext ctx; - private int fireChannelReadCalled; - - private ByteToMessageDecoderContext(ChannelHandlerContext ctx) { - this.ctx = ctx; - } - - void reset() { - fireChannelReadCalled = 0; - } - - int fireChannelReadCallCount() { - return fireChannelReadCalled; - } - - @Override - public Channel channel() { - return ctx.channel(); - } - - @Override - public EventExecutor executor() { - return ctx.executor(); - } - - @Override - public String name() { - return ctx.name(); - } - - @Override - public ChannelHandler handler() { - return ctx.handler(); - } - - @Override - public boolean isRemoved() { - return ctx.isRemoved(); - } - - @Override - public ChannelHandlerContext fireChannelRegistered() { - ctx.fireChannelRegistered(); - return this; - } - - @Override - public ChannelHandlerContext fireChannelUnregistered() { - ctx.fireChannelUnregistered(); - return this; - } - - @Override - public ChannelHandlerContext fireChannelActive() { - ctx.fireChannelActive(); - return this; - } - - @Override - public ChannelHandlerContext fireChannelInactive() { - ctx.fireChannelInactive(); - return this; - } - - @Override - public ChannelHandlerContext fireExceptionCaught(Throwable cause) { - ctx.fireExceptionCaught(cause); - return this; - } - - @Override - public ChannelHandlerContext fireUserEventTriggered(Object evt) { - ctx.fireUserEventTriggered(evt); - return this; - } - - @Override - public ChannelHandlerContext fireChannelRead(Object msg) { - fireChannelReadCalled ++; - ctx.fireChannelRead(msg); - return this; - } - - @Override - public ChannelHandlerContext fireChannelReadComplete() { - ctx.fireChannelReadComplete(); - return this; - } - - @Override - public ChannelHandlerContext fireChannelWritabilityChanged() { - ctx.fireChannelWritabilityChanged(); - return this; - } - - @Override - public Future register() { - return ctx.register(); - } - - @Override - public ChannelHandlerContext read() { - ctx.read(); - return this; - } - - @Override - public ChannelHandlerContext flush() { - ctx.flush(); - return this; - } - - @Override - public ChannelPipeline pipeline() { - return ctx.pipeline(); - } - - @Override - public ByteBufAllocator alloc() { - return ctx.alloc(); - } - - @Override - public BufferAllocator bufferAllocator() { - return ctx.bufferAllocator(); - } - - @Override - @Deprecated - public Attribute attr(AttributeKey key) { - return ctx.attr(key); - } - - @Override - @Deprecated - public boolean hasAttr(AttributeKey key) { - return ctx.hasAttr(key); - } - - @Override - public Future bind(SocketAddress localAddress) { - return ctx.bind(localAddress); - } - - @Override - public Future connect(SocketAddress remoteAddress) { - return ctx.connect(remoteAddress); - } - - @Override - public Future connect(SocketAddress remoteAddress, SocketAddress localAddress) { - return ctx.connect(remoteAddress, localAddress); - } - - @Override - public Future disconnect() { - return ctx.disconnect(); - } - - @Override - public Future close() { - return ctx.close(); - } - - @Override - public Future deregister() { - return ctx.deregister(); - } - - @Override - public Future write(Object msg) { - return ctx.write(msg); - } - - @Override - public Future writeAndFlush(Object msg) { - return ctx.writeAndFlush(msg); - } - - @Override - public Promise newPromise() { - return ctx.newPromise(); - } - - @Override - public Future newSucceededFuture() { - return ctx.newSucceededFuture(); - } - - @Override - public Future newFailedFuture(Throwable cause) { - return ctx.newFailedFuture(cause); - } - } - - private static final class CompositeBufferCumulator implements Cumulator { - @Override - public Buffer cumulate(BufferAllocator alloc, Buffer cumulation, Buffer in) { - if (cumulation.readableBytes() == 0) { - cumulation.close(); - return in; - } - CompositeBuffer composite; - try (in) { - if (CompositeBuffer.isComposite(cumulation)) { - CompositeBuffer tmp = (CompositeBuffer) cumulation; - // Since we are extending the composite buffer below, we have to make sure there is no space to - // write in the existing cumulation. - if (tmp.writerOffset() < tmp.capacity()) { - composite = tmp.split(); - tmp.close(); - } else { - composite = tmp; - } - } else { - composite = CompositeBuffer.compose(alloc, cumulation.send()); - } - composite.extendWith((in.readOnly() ? in.copy() : in).send()); - return composite; - } - } - - @Override - public String toString() { - return "CompositeBufferCumulator"; - } - } - - private static final class MergeCumulator implements Cumulator { - @Override - public Buffer cumulate(BufferAllocator alloc, Buffer cumulation, Buffer in) { - if (cumulation.readableBytes() == 0) { - // If cumulation is empty and input buffer is contiguous, use it directly - cumulation.close(); - return in; - } - // We must close input Buffer in all cases as otherwise it may produce a leak if writeBytes(...) throw - // for whatever close (for example because of OutOfMemoryError) - try (in) { - final int required = in.readableBytes(); - if (required > cumulation.writableBytes() || cumulation.readOnly()) { - return expandCumulationAndWrite(alloc, cumulation, in); - } - cumulation.writeBytes(in); - return cumulation; - } - } - - @Override - public String toString() { - return "MergeCumulator"; - } - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/CharSequenceValueConverter.java b/codec/src/main/java/io/netty/handler/codec/CharSequenceValueConverter.java deleted file mode 100644 index cfcd60d0d7..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/CharSequenceValueConverter.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec; - -import io.netty.util.AsciiString; -import io.netty.util.internal.PlatformDependent; - -import java.text.ParseException; -import java.util.Date; - -/** - * Converts to/from native types, general {@link Object}, and {@link CharSequence}s. - */ -public class CharSequenceValueConverter implements ValueConverter { - public static final CharSequenceValueConverter INSTANCE = new CharSequenceValueConverter(); - private static final AsciiString TRUE_ASCII = new AsciiString("true"); - - @Override - public CharSequence convertObject(Object value) { - if (value instanceof CharSequence) { - return (CharSequence) value; - } - return value.toString(); - } - - @Override - public CharSequence convertInt(int value) { - return String.valueOf(value); - } - - @Override - public CharSequence convertLong(long value) { - return String.valueOf(value); - } - - @Override - public CharSequence convertDouble(double value) { - return String.valueOf(value); - } - - @Override - public CharSequence convertChar(char value) { - return String.valueOf(value); - } - - @Override - public CharSequence convertBoolean(boolean value) { - return String.valueOf(value); - } - - @Override - public CharSequence convertFloat(float value) { - return String.valueOf(value); - } - - @Override - public boolean convertToBoolean(CharSequence value) { - return AsciiString.contentEqualsIgnoreCase(value, TRUE_ASCII); - } - - @Override - public CharSequence convertByte(byte value) { - return String.valueOf(value); - } - - @Override - public byte convertToByte(CharSequence value) { - if (value instanceof AsciiString && value.length() == 1) { - return ((AsciiString) value).byteAt(0); - } - return Byte.parseByte(value.toString()); - } - - @Override - public char convertToChar(CharSequence value) { - return value.charAt(0); - } - - @Override - public CharSequence convertShort(short value) { - return String.valueOf(value); - } - - @Override - public short convertToShort(CharSequence value) { - if (value instanceof AsciiString) { - return ((AsciiString) value).parseShort(); - } - return Short.parseShort(value.toString()); - } - - @Override - public int convertToInt(CharSequence value) { - if (value instanceof AsciiString) { - return ((AsciiString) value).parseInt(); - } - return Integer.parseInt(value.toString()); - } - - @Override - public long convertToLong(CharSequence value) { - if (value instanceof AsciiString) { - return ((AsciiString) value).parseLong(); - } - return Long.parseLong(value.toString()); - } - - @Override - public CharSequence convertTimeMillis(long value) { - return DateFormatter.format(new Date(value)); - } - - @Override - public long convertToTimeMillis(CharSequence value) { - Date date = DateFormatter.parseHttpDate(value); - if (date == null) { - PlatformDependent.throwException(new ParseException("header can't be parsed into a Date: " + value, 0)); - return 0; - } - return date.getTime(); - } - - @Override - public float convertToFloat(CharSequence value) { - if (value instanceof AsciiString) { - return ((AsciiString) value).parseFloat(); - } - return Float.parseFloat(value.toString()); - } - - @Override - public double convertToDouble(CharSequence value) { - if (value instanceof AsciiString) { - return ((AsciiString) value).parseDouble(); - } - return Double.parseDouble(value.toString()); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/CodecException.java b/codec/src/main/java/io/netty/handler/codec/CodecException.java deleted file mode 100644 index d7e8ed059d..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/CodecException.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -/** - * An {@link Exception} which is thrown by a codec. - */ -public class CodecException extends RuntimeException { - - private static final long serialVersionUID = -1464830400709348473L; - - /** - * Creates a new instance. - */ - public CodecException() { - } - - /** - * Creates a new instance. - */ - public CodecException(String message, Throwable cause) { - super(message, cause); - } - - /** - * Creates a new instance. - */ - public CodecException(String message) { - super(message); - } - - /** - * Creates a new instance. - */ - public CodecException(Throwable cause) { - super(cause); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/CodecOutputList.java b/codec/src/main/java/io/netty/handler/codec/CodecOutputList.java deleted file mode 100644 index 0c220f35a9..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/CodecOutputList.java +++ /dev/null @@ -1,229 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import static java.util.Objects.requireNonNull; - -import io.netty.util.concurrent.FastThreadLocal; -import io.netty.util.internal.MathUtil; - -import java.util.AbstractList; -import java.util.RandomAccess; - -/** - * Special {@link AbstractList} implementation which is used within our codec base classes. - */ -final class CodecOutputList extends AbstractList implements RandomAccess { - - private static final CodecOutputListRecycler NOOP_RECYCLER = object -> { - // drop on the floor and let the GC handle it. - }; - - private static final FastThreadLocal CODEC_OUTPUT_LISTS_POOL = - new FastThreadLocal() { - @Override - protected CodecOutputLists initialValue() throws Exception { - // 16 CodecOutputList per Thread are cached. - return new CodecOutputLists(16); - } - }; - - private interface CodecOutputListRecycler { - void recycle(CodecOutputList codecOutputList); - } - - private static final class CodecOutputLists implements CodecOutputListRecycler { - private final CodecOutputList[] elements; - private final int mask; - - private int currentIdx; - private int count; - - CodecOutputLists(int numElements) { - elements = new CodecOutputList[MathUtil.safeFindNextPositivePowerOfTwo(numElements)]; - for (int i = 0; i < elements.length; ++i) { - // Size of 16 should be good enough for the majority of all users as an initial capacity. - elements[i] = new CodecOutputList(this, 16); - } - count = elements.length; - currentIdx = elements.length; - mask = elements.length - 1; - } - - public CodecOutputList getOrCreate() { - if (count == 0) { - // Return a new CodecOutputList which will not be cached. We use a size of 4 to keep the overhead - // low. - return new CodecOutputList(NOOP_RECYCLER, 4); - } - --count; - - int idx = (currentIdx - 1) & mask; - CodecOutputList list = elements[idx]; - currentIdx = idx; - return list; - } - - @Override - public void recycle(CodecOutputList codecOutputList) { - int idx = currentIdx; - elements[idx] = codecOutputList; - currentIdx = (idx + 1) & mask; - ++count; - assert count <= elements.length; - } - } - - static CodecOutputList newInstance() { - return CODEC_OUTPUT_LISTS_POOL.get().getOrCreate(); - } - - private final CodecOutputListRecycler recycler; - private int size; - private Object[] array; - private boolean insertSinceRecycled; - - private CodecOutputList(CodecOutputListRecycler recycler, int size) { - this.recycler = recycler; - array = new Object[size]; - } - - @Override - public Object get(int index) { - checkIndex(index); - return array[index]; - } - - @Override - public int size() { - return size; - } - - @Override - public boolean add(Object element) { - requireNonNull(element, "element"); - try { - insert(size, element); - } catch (IndexOutOfBoundsException ignore) { - // This should happen very infrequently so we just catch the exception and try again. - expandArray(); - insert(size, element); - } - ++ size; - return true; - } - - @Override - public Object set(int index, Object element) { - requireNonNull(element, "element"); - checkIndex(index); - - Object old = array[index]; - insert(index, element); - return old; - } - - @Override - public void add(int index, Object element) { - requireNonNull(element, "element"); - checkIndex(index); - - if (size == array.length) { - expandArray(); - } - - if (index != size) { - System.arraycopy(array, index, array, index + 1, size - index); - } - - insert(index, element); - ++ size; - } - - @Override - public Object remove(int index) { - checkIndex(index); - Object old = array[index]; - - int len = size - index - 1; - if (len > 0) { - System.arraycopy(array, index + 1, array, index, len); - } - array[-- size] = null; - - return old; - } - - @Override - public void clear() { - // We only set the size to 0 and not null out the array. Null out the array will explicit requested by - // calling recycle() - size = 0; - } - - /** - * Returns {@code true} if any elements where added or set. This will be reset once {@link #recycle()} was called. - */ - boolean insertSinceRecycled() { - return insertSinceRecycled; - } - - /** - * Recycle the array which will clear it and null out all entries in the internal storage. - */ - void recycle() { - for (int i = 0 ; i < size; i ++) { - array[i] = null; - } - size = 0; - insertSinceRecycled = false; - - recycler.recycle(this); - } - - /** - * Returns the element on the given index. This operation will not do any range-checks and so is considered unsafe. - */ - Object getUnsafe(int index) { - return array[index]; - } - - private void checkIndex(int index) { - if (index >= size) { - throw new IndexOutOfBoundsException("expected: index < (" - + size + "),but actual is (" + size + ")"); - } - } - - private void insert(int index, Object element) { - array[index] = element; - insertSinceRecycled = true; - } - - private void expandArray() { - // double capacity - int newCapacity = array.length << 1; - - if (newCapacity < 0) { - throw new OutOfMemoryError(); - } - - Object[] newArray = new Object[newCapacity]; - System.arraycopy(array, 0, newArray, 0, array.length); - - array = newArray; - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/CorruptedFrameException.java b/codec/src/main/java/io/netty/handler/codec/CorruptedFrameException.java deleted file mode 100644 index b98f35b469..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/CorruptedFrameException.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -/** - * An {@link DecoderException} which is thrown when the received frame data could not be decoded by - * an inbound handler. - */ -public class CorruptedFrameException extends DecoderException { - - private static final long serialVersionUID = 3918052232492988408L; - - /** - * Creates a new instance. - */ - public CorruptedFrameException() { - } - - /** - * Creates a new instance. - */ - public CorruptedFrameException(String message, Throwable cause) { - super(message, cause); - } - - /** - * Creates a new instance. - */ - public CorruptedFrameException(String message) { - super(message); - } - - /** - * Creates a new instance. - */ - public CorruptedFrameException(Throwable cause) { - super(cause); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/DatagramPacketDecoder.java b/codec/src/main/java/io/netty/handler/codec/DatagramPacketDecoder.java deleted file mode 100644 index 2511e51426..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/DatagramPacketDecoder.java +++ /dev/null @@ -1,115 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.socket.DatagramPacket; -import io.netty.handler.codec.protobuf.ProtobufDecoder; - -/** - * A decoder that decodes the content of the received {@link DatagramPacket} using - * the specified {@link ByteBuf} decoder. E.g., - * - *

- * {@link ChannelPipeline} pipeline = ...;
- * pipeline.addLast("udpDecoder", new {@link DatagramPacketDecoder}(new {@link ProtobufDecoder}(...));
- * 
- */ -public class DatagramPacketDecoder extends MessageToMessageDecoder { - - private final MessageToMessageDecoder decoder; - - /** - * Create a {@link DatagramPacket} decoder using the specified {@link ByteBuf} decoder. - * - * @param decoder the specified {@link ByteBuf} decoder - */ - public DatagramPacketDecoder(MessageToMessageDecoder decoder) { - this.decoder = requireNonNull(decoder, "decoder"); - } - - @Override - public boolean acceptInboundMessage(Object msg) throws Exception { - if (msg instanceof DatagramPacket) { - return decoder.acceptInboundMessage(((DatagramPacket) msg).content()); - } - return false; - } - - @Override - protected void decode(ChannelHandlerContext ctx, DatagramPacket msg) throws Exception { - decoder.decode(ctx, msg.content()); - } - - @Override - public void channelRegistered(ChannelHandlerContext ctx) throws Exception { - decoder.channelRegistered(ctx); - } - - @Override - public void channelUnregistered(ChannelHandlerContext ctx) throws Exception { - decoder.channelUnregistered(ctx); - } - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - decoder.channelActive(ctx); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - decoder.channelInactive(ctx); - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { - decoder.channelReadComplete(ctx); - } - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - decoder.userEventTriggered(ctx, evt); - } - - @Override - public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception { - decoder.channelWritabilityChanged(ctx); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - decoder.exceptionCaught(ctx, cause); - } - - @Override - public void handlerAdded(ChannelHandlerContext ctx) throws Exception { - decoder.handlerAdded(ctx); - } - - @Override - public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { - decoder.handlerRemoved(ctx); - } - - @Override - public boolean isSharable() { - return decoder.isSharable(); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/DatagramPacketEncoder.java b/codec/src/main/java/io/netty/handler/codec/DatagramPacketEncoder.java deleted file mode 100644 index 9a2c681834..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/DatagramPacketEncoder.java +++ /dev/null @@ -1,144 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import io.netty.buffer.ByteBufConvertible; -import io.netty.channel.AddressedEnvelope; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.socket.DatagramPacket; -import io.netty.handler.codec.protobuf.ProtobufEncoder; -import io.netty.util.concurrent.Future; -import io.netty.util.internal.StringUtil; - -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.util.List; - -import static java.util.Objects.requireNonNull; - -/** - * An encoder that encodes the content in {@link AddressedEnvelope} to {@link DatagramPacket} using - * the specified message encoder. E.g., - * - *

- * {@link ChannelPipeline} pipeline = ...;
- * pipeline.addLast("udpEncoder", new {@link DatagramPacketEncoder}(new {@link ProtobufEncoder}(...));
- * 
- * - * Note: As UDP packets are out-of-order, you should make sure the encoded message size are not greater than - * the max safe packet size in your particular network path which guarantees no packet fragmentation. - * - * @param the type of message to be encoded - */ -public class DatagramPacketEncoder extends MessageToMessageEncoder> { - - private final MessageToMessageEncoder encoder; - - /** - * Create an encoder that encodes the content in {@link AddressedEnvelope} to {@link DatagramPacket} using - * the specified message encoder. - * - * @param encoder the specified message encoder - */ - public DatagramPacketEncoder(MessageToMessageEncoder encoder) { - this.encoder = requireNonNull(encoder, "encoder"); - } - - @Override - public boolean acceptOutboundMessage(Object msg) throws Exception { - if (super.acceptOutboundMessage(msg)) { - @SuppressWarnings("rawtypes") - AddressedEnvelope envelope = (AddressedEnvelope) msg; - return encoder.acceptOutboundMessage(envelope.content()) - && (envelope.sender() instanceof InetSocketAddress || envelope.sender() == null) - && envelope.recipient() instanceof InetSocketAddress; - } - return false; - } - - @Override - protected void encode( - ChannelHandlerContext ctx, AddressedEnvelope msg, List out) throws Exception { - assert out.isEmpty(); - - encoder.encode(ctx, msg.content(), out); - if (out.size() != 1) { - throw new EncoderException( - StringUtil.simpleClassName(encoder) + " must produce only one message."); - } - Object content = out.get(0); - if (content instanceof ByteBufConvertible) { - // Replace the ByteBuf with a DatagramPacket. - out.set(0, new DatagramPacket(((ByteBufConvertible) content).asByteBuf(), msg.recipient(), msg.sender())); - } else { - throw new EncoderException( - StringUtil.simpleClassName(encoder) + " must produce only ByteBuf."); - } - } - - @Override - public Future bind(ChannelHandlerContext ctx, SocketAddress localAddress) { - return encoder.bind(ctx, localAddress); - } - - @Override - public Future connect( - ChannelHandlerContext ctx, SocketAddress remoteAddress, - SocketAddress localAddress) { - return encoder.connect(ctx, remoteAddress, localAddress); - } - - @Override - public Future disconnect(ChannelHandlerContext ctx) { - return encoder.disconnect(ctx); - } - - @Override - public Future close(ChannelHandlerContext ctx) { - return encoder.close(ctx); - } - - @Override - public Future deregister(ChannelHandlerContext ctx) { - return encoder.deregister(ctx); - } - - @Override - public void read(ChannelHandlerContext ctx) { - encoder.read(ctx); - } - - @Override - public void flush(ChannelHandlerContext ctx) { - encoder.flush(ctx); - } - - @Override - public void handlerAdded(ChannelHandlerContext ctx) throws Exception { - encoder.handlerAdded(ctx); - } - - @Override - public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { - encoder.handlerRemoved(ctx); - } - - @Override - public boolean isSharable() { - return encoder.isSharable(); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/DateFormatter.java b/codec/src/main/java/io/netty/handler/codec/DateFormatter.java deleted file mode 100644 index 284b916b83..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/DateFormatter.java +++ /dev/null @@ -1,448 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import static java.util.Objects.requireNonNull; - -import io.netty.util.AsciiString; -import io.netty.util.concurrent.FastThreadLocal; - -import java.util.BitSet; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.TimeZone; - -/** - * A formatter for HTTP header dates, such as "Expires" and "Date" headers, or "expires" field in "Set-Cookie". - * - * On the parsing side, it honors RFC6265 (so it supports RFC1123). - * Note that: - *
    - *
  • Day of week is ignored and not validated
  • - *
  • Timezone is ignored, as RFC6265 assumes UTC
  • - *
- * If you're looking for a date format that validates day of week, or supports other timezones, consider using - * java.util.DateTimeFormatter.RFC_1123_DATE_TIME. - * - * On the formatting side, it uses a subset of RFC1123 (2 digit day-of-month and 4 digit year) as per RFC2616. - * This subset supports RFC6265. - * - * @see RFC6265 for the parsing side - * @see RFC1123 and - * RFC2616 for the encoding side. - */ -public final class DateFormatter { - - private static final BitSet DELIMITERS = new BitSet(); - static { - DELIMITERS.set(0x09); - for (char c = 0x20; c <= 0x2F; c++) { - DELIMITERS.set(c); - } - for (char c = 0x3B; c <= 0x40; c++) { - DELIMITERS.set(c); - } - for (char c = 0x5B; c <= 0x60; c++) { - DELIMITERS.set(c); - } - for (char c = 0x7B; c <= 0x7E; c++) { - DELIMITERS.set(c); - } - } - - private static final String[] DAY_OF_WEEK_TO_SHORT_NAME = - new String[]{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; - - private static final String[] CALENDAR_MONTH_TO_SHORT_NAME = - new String[]{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; - - private static final FastThreadLocal INSTANCES = - new FastThreadLocal() { - @Override - protected DateFormatter initialValue() { - return new DateFormatter(); - } - }; - - /** - * Parse some text into a {@link Date}, according to RFC6265 - * @param txt text to parse - * @return a {@link Date}, or null if text couldn't be parsed - */ - public static Date parseHttpDate(CharSequence txt) { - return parseHttpDate(txt, 0, txt.length()); - } - - /** - * Parse some text into a {@link Date}, according to RFC6265 - * @param txt text to parse - * @param start the start index inside {@code txt} - * @param end the end index inside {@code txt} - * @return a {@link Date}, or null if text couldn't be parsed - */ - public static Date parseHttpDate(CharSequence txt, int start, int end) { - int length = end - start; - if (length == 0) { - return null; - } else if (length < 0) { - throw new IllegalArgumentException("Can't have end < start"); - } else if (length > 64) { - throw new IllegalArgumentException("Can't parse more than 64 chars, " + - "looks like a user error or a malformed header"); - } - return formatter().parse0(requireNonNull(txt, "txt"), start, end); - } - - /** - * Format a {@link Date} into RFC1123 format - * @param date the date to format - * @return a RFC1123 string - */ - public static String format(Date date) { - return formatter().format0(requireNonNull(date, "date")); - } - - /** - * Append a {@link Date} to a {@link StringBuilder} into RFC1123 format - * @param date the date to format - * @param sb the StringBuilder - * @return the same StringBuilder - */ - public static StringBuilder append(Date date, StringBuilder sb) { - return formatter().append0(requireNonNull(date, "date"), requireNonNull(sb, "sb")); - } - - private static DateFormatter formatter() { - DateFormatter formatter = INSTANCES.get(); - formatter.reset(); - return formatter; - } - - // delimiter = %x09 / %x20-2F / %x3B-40 / %x5B-60 / %x7B-7E - private static boolean isDelim(char c) { - return DELIMITERS.get(c); - } - - private static boolean isDigit(char c) { - return c >= 48 && c <= 57; - } - - private static int getNumericalValue(char c) { - return c - 48; - } - - private final GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC")); - private final StringBuilder sb = new StringBuilder(29); // Sun, 27 Nov 2016 19:37:15 GMT - private boolean timeFound; - private int hours; - private int minutes; - private int seconds; - private boolean dayOfMonthFound; - private int dayOfMonth; - private boolean monthFound; - private int month; - private boolean yearFound; - private int year; - - private DateFormatter() { - reset(); - } - - public void reset() { - timeFound = false; - hours = -1; - minutes = -1; - seconds = -1; - dayOfMonthFound = false; - dayOfMonth = -1; - monthFound = false; - month = -1; - yearFound = false; - year = -1; - cal.clear(); - sb.setLength(0); - } - - private boolean tryParseTime(CharSequence txt, int tokenStart, int tokenEnd) { - int len = tokenEnd - tokenStart; - - // h:m:s to hh:mm:ss - if (len < 5 || len > 8) { - return false; - } - - int localHours = -1; - int localMinutes = -1; - int localSeconds = -1; - int currentPartNumber = 0; - int currentPartValue = 0; - int numDigits = 0; - - for (int i = tokenStart; i < tokenEnd; i++) { - char c = txt.charAt(i); - if (isDigit(c)) { - currentPartValue = currentPartValue * 10 + getNumericalValue(c); - if (++numDigits > 2) { - return false; // too many digits in this part - } - } else if (c == ':') { - if (numDigits == 0) { - // no digits between separators - return false; - } - switch (currentPartNumber) { - case 0: - // flushing hours - localHours = currentPartValue; - break; - case 1: - // flushing minutes - localMinutes = currentPartValue; - break; - default: - // invalid, too many : - return false; - } - currentPartValue = 0; - currentPartNumber++; - numDigits = 0; - } else { - // invalid char - return false; - } - } - - if (numDigits > 0) { - // pending seconds - localSeconds = currentPartValue; - } - - if (localHours >= 0 && localMinutes >= 0 && localSeconds >= 0) { - hours = localHours; - minutes = localMinutes; - seconds = localSeconds; - return true; - } - - return false; - } - - private boolean tryParseDayOfMonth(CharSequence txt, int tokenStart, int tokenEnd) { - int len = tokenEnd - tokenStart; - - if (len == 1) { - char c0 = txt.charAt(tokenStart); - if (isDigit(c0)) { - dayOfMonth = getNumericalValue(c0); - return true; - } - - } else if (len == 2) { - char c0 = txt.charAt(tokenStart); - char c1 = txt.charAt(tokenStart + 1); - if (isDigit(c0) && isDigit(c1)) { - dayOfMonth = getNumericalValue(c0) * 10 + getNumericalValue(c1); - return true; - } - } - - return false; - } - - private boolean tryParseMonth(CharSequence txt, int tokenStart, int tokenEnd) { - int len = tokenEnd - tokenStart; - - if (len != 3) { - return false; - } - - 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 (monthChar1 == 'f' && monthChar2 == 'e' && monthChar3 == 'b') { - month = Calendar.FEBRUARY; - } else if (monthChar1 == 'm' && monthChar2 == 'a' && monthChar3 == 'r') { - month = Calendar.MARCH; - } else if (monthChar1 == 'a' && monthChar2 == 'p' && monthChar3 == 'r') { - month = Calendar.APRIL; - } else if (monthChar1 == 'm' && monthChar2 == 'a' && monthChar3 == 'y') { - month = Calendar.MAY; - } else if (monthChar1 == 'j' && monthChar2 == 'u' && monthChar3 == 'n') { - month = Calendar.JUNE; - } else if (monthChar1 == 'j' && monthChar2 == 'u' && monthChar3 == 'l') { - month = Calendar.JULY; - } else if (monthChar1 == 'a' && monthChar2 == 'u' && monthChar3 == 'g') { - month = Calendar.AUGUST; - } else if (monthChar1 == 's' && monthChar2 == 'e' && monthChar3 == 'p') { - month = Calendar.SEPTEMBER; - } else if (monthChar1 == 'o' && monthChar2 == 'c' && monthChar3 == 't') { - month = Calendar.OCTOBER; - } else if (monthChar1 == 'n' && monthChar2 == 'o' && monthChar3 == 'v') { - month = Calendar.NOVEMBER; - } else if (monthChar1 == 'd' && monthChar2 == 'e' && monthChar3 == 'c') { - month = Calendar.DECEMBER; - } else { - return false; - } - - return true; - } - - private boolean tryParseYear(CharSequence txt, int tokenStart, int tokenEnd) { - int len = tokenEnd - tokenStart; - - if (len == 2) { - char c0 = txt.charAt(tokenStart); - char c1 = txt.charAt(tokenStart + 1); - if (isDigit(c0) && isDigit(c1)) { - year = getNumericalValue(c0) * 10 + getNumericalValue(c1); - return true; - } - - } else if (len == 4) { - char c0 = txt.charAt(tokenStart); - char c1 = txt.charAt(tokenStart + 1); - char c2 = txt.charAt(tokenStart + 2); - char c3 = txt.charAt(tokenStart + 3); - if (isDigit(c0) && isDigit(c1) && isDigit(c2) && isDigit(c3)) { - year = getNumericalValue(c0) * 1000 + - getNumericalValue(c1) * 100 + - getNumericalValue(c2) * 10 + - getNumericalValue(c3); - return true; - } - } - - return false; - } - - private boolean parseToken(CharSequence txt, int tokenStart, int tokenEnd) { - // return true if all parts are found - if (!timeFound) { - timeFound = tryParseTime(txt, tokenStart, tokenEnd); - if (timeFound) { - return dayOfMonthFound && monthFound && yearFound; - } - } - - if (!dayOfMonthFound) { - dayOfMonthFound = tryParseDayOfMonth(txt, tokenStart, tokenEnd); - if (dayOfMonthFound) { - return timeFound && monthFound && yearFound; - } - } - - if (!monthFound) { - monthFound = tryParseMonth(txt, tokenStart, tokenEnd); - if (monthFound) { - return timeFound && dayOfMonthFound && yearFound; - } - } - - if (!yearFound) { - yearFound = tryParseYear(txt, tokenStart, tokenEnd); - } - return timeFound && dayOfMonthFound && monthFound && yearFound; - } - - private Date parse0(CharSequence txt, int start, int end) { - boolean allPartsFound = parse1(txt, start, end); - return allPartsFound && normalizeAndValidate() ? computeDate() : null; - } - - private boolean parse1(CharSequence txt, int start, int end) { - // return true if all parts are found - int tokenStart = -1; - - for (int i = start; i < end; i++) { - char c = txt.charAt(i); - - if (isDelim(c)) { - if (tokenStart != -1) { - // terminate token - if (parseToken(txt, tokenStart, i)) { - return true; - } - tokenStart = -1; - } - } else if (tokenStart == -1) { - // start new token - tokenStart = i; - } - } - - // terminate trailing token - return tokenStart != -1 && parseToken(txt, tokenStart, txt.length()); - } - - private boolean normalizeAndValidate() { - if (dayOfMonth < 1 - || dayOfMonth > 31 - || hours > 23 - || minutes > 59 - || seconds > 59) { - return false; - } - - if (year >= 70 && year <= 99) { - year += 1900; - } else if (year >= 0 && year < 70) { - year += 2000; - } else if (year < 1601) { - // invalid value - return false; - } - return true; - } - - private Date computeDate() { - cal.set(Calendar.DAY_OF_MONTH, dayOfMonth); - cal.set(Calendar.MONTH, month); - cal.set(Calendar.YEAR, year); - cal.set(Calendar.HOUR_OF_DAY, hours); - cal.set(Calendar.MINUTE, minutes); - cal.set(Calendar.SECOND, seconds); - return cal.getTime(); - } - - private String format0(Date date) { - append0(date, sb); - return sb.toString(); - } - - private StringBuilder append0(Date date, StringBuilder sb) { - cal.setTime(date); - - sb.append(DAY_OF_WEEK_TO_SHORT_NAME[cal.get(Calendar.DAY_OF_WEEK) - 1]).append(", "); - appendZeroLeftPadded(cal.get(Calendar.DAY_OF_MONTH), sb).append(' '); - sb.append(CALENDAR_MONTH_TO_SHORT_NAME[cal.get(Calendar.MONTH)]).append(' '); - sb.append(cal.get(Calendar.YEAR)).append(' '); - appendZeroLeftPadded(cal.get(Calendar.HOUR_OF_DAY), sb).append(':'); - appendZeroLeftPadded(cal.get(Calendar.MINUTE), sb).append(':'); - return appendZeroLeftPadded(cal.get(Calendar.SECOND), sb).append(" GMT"); - } - - private static StringBuilder appendZeroLeftPadded(int value, StringBuilder sb) { - if (value < 10) { - sb.append('0'); - } - return sb.append(value); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/DecoderException.java b/codec/src/main/java/io/netty/handler/codec/DecoderException.java deleted file mode 100644 index 0a1ff993c2..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/DecoderException.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -/** - * An {@link CodecException} which is thrown by a decoder. - */ -public class DecoderException extends CodecException { - - private static final long serialVersionUID = 6926716840699621852L; - - /** - * Creates a new instance. - */ - public DecoderException() { - } - - /** - * Creates a new instance. - */ - public DecoderException(String message, Throwable cause) { - super(message, cause); - } - - /** - * Creates a new instance. - */ - public DecoderException(String message) { - super(message); - } - - /** - * Creates a new instance. - */ - public DecoderException(Throwable cause) { - super(cause); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/DecoderResult.java b/codec/src/main/java/io/netty/handler/codec/DecoderResult.java deleted file mode 100644 index 3fdfa1d84a..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/DecoderResult.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import static java.util.Objects.requireNonNull; - -import io.netty.util.Signal; - -public class DecoderResult { - - protected static final Signal SIGNAL_UNFINISHED = Signal.valueOf(DecoderResult.class, "UNFINISHED"); - protected static final Signal SIGNAL_SUCCESS = Signal.valueOf(DecoderResult.class, "SUCCESS"); - - public static final DecoderResult UNFINISHED = new DecoderResult(SIGNAL_UNFINISHED); - public static final DecoderResult SUCCESS = new DecoderResult(SIGNAL_SUCCESS); - - public static DecoderResult failure(Throwable cause) { - requireNonNull(cause, "cause"); - return new DecoderResult(cause); - } - - private final Throwable cause; - - protected DecoderResult(Throwable cause) { - requireNonNull(cause, "cause"); - this.cause = cause; - } - - public boolean isFinished() { - return cause != SIGNAL_UNFINISHED; - } - - public boolean isSuccess() { - return cause == SIGNAL_SUCCESS; - } - - public boolean isFailure() { - return cause != SIGNAL_SUCCESS && cause != SIGNAL_UNFINISHED; - } - - public Throwable cause() { - if (isFailure()) { - return cause; - } else { - return null; - } - } - - @Override - public String toString() { - if (isFinished()) { - if (isSuccess()) { - return "success"; - } - - String cause = cause().toString(); - return new StringBuilder(cause.length() + 17) - .append("failure(") - .append(cause) - .append(')') - .toString(); - } else { - return "unfinished"; - } - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/DecoderResultProvider.java b/codec/src/main/java/io/netty/handler/codec/DecoderResultProvider.java deleted file mode 100644 index 1418c9070e..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/DecoderResultProvider.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec; - -/** - * Provides the accessor methods for the {@link DecoderResult} property of a decoded message. - */ -public interface DecoderResultProvider { - /** - * Returns the result of decoding this object. - */ - DecoderResult decoderResult(); - - /** - * Updates the result of decoding this object. This method is supposed to be invoked by a decoder. - * Do not call this method unless you know what you are doing. - */ - void setDecoderResult(DecoderResult result); -} diff --git a/codec/src/main/java/io/netty/handler/codec/DefaultHeaders.java b/codec/src/main/java/io/netty/handler/codec/DefaultHeaders.java deleted file mode 100644 index 78ea2bd203..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/DefaultHeaders.java +++ /dev/null @@ -1,1222 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec; - -import io.netty.util.HashingStrategy; - -import java.util.Arrays; -import java.util.Collections; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.NoSuchElementException; -import java.util.Set; - -import static io.netty.util.HashingStrategy.JAVA_HASHER; -import static io.netty.util.internal.MathUtil.findNextPositivePowerOfTwo; -import static java.lang.Math.max; -import static java.lang.Math.min; -import static java.util.Objects.requireNonNull; - -/** - * Default implementation of {@link Headers}; - * - * @param the type of the header name. - * @param the type of the header value. - * @param the type to use for return values when the intention is to return {@code this} object. - */ -public class DefaultHeaders> implements Headers { - /** - * Constant used to seed the hash code generation. Could be anything but this was borrowed from murmur3. - */ - static final int HASH_CODE_SEED = 0xc2b2ae35; - - private final HeaderEntry[] entries; - protected final HeaderEntry head; - - private final byte hashMask; - private final ValueConverter valueConverter; - private final NameValidator nameValidator; - private final HashingStrategy hashingStrategy; - int size; - - public interface NameValidator { - /** - * Verify that {@code name} is valid. - * @param name The name to validate. - * @throws RuntimeException if {@code name} is not valid. - */ - void validateName(K name); - - @SuppressWarnings("rawtypes") - NameValidator NOT_NULL = name -> requireNonNull(name, "name"); - } - - @SuppressWarnings("unchecked") - public DefaultHeaders(ValueConverter valueConverter) { - this(JAVA_HASHER, valueConverter); - } - - @SuppressWarnings("unchecked") - public DefaultHeaders(ValueConverter valueConverter, NameValidator nameValidator) { - this(JAVA_HASHER, valueConverter, nameValidator); - } - - @SuppressWarnings("unchecked") - public DefaultHeaders(HashingStrategy nameHashingStrategy, ValueConverter valueConverter) { - this(nameHashingStrategy, valueConverter, NameValidator.NOT_NULL); - } - - public DefaultHeaders(HashingStrategy nameHashingStrategy, - ValueConverter valueConverter, NameValidator nameValidator) { - this(nameHashingStrategy, valueConverter, nameValidator, 16); - } - - /** - * Create a new instance. - * @param nameHashingStrategy Used to hash and equality compare names. - * @param valueConverter Used to convert values to/from native types. - * @param nameValidator Used to validate name elements. - * @param arraySizeHint A hint as to how large the hash data structure should be. - * The next positive power of two will be used. An upper bound may be enforced. - */ - @SuppressWarnings("unchecked") - public DefaultHeaders(HashingStrategy nameHashingStrategy, - ValueConverter valueConverter, NameValidator nameValidator, int arraySizeHint) { - this.valueConverter = requireNonNull(valueConverter, "valueConverter"); - this.nameValidator = requireNonNull(nameValidator, "nameValidator"); - this.hashingStrategy = requireNonNull(nameHashingStrategy, "nameHashingStrategy"); - // Enforce a bound of [2, 128] because hashMask is a byte. The max possible value of hashMask is one less - // than the length of this array, and we want the mask to be > 0. - entries = new DefaultHeaders.HeaderEntry[findNextPositivePowerOfTwo(max(2, min(arraySizeHint, 128)))]; - hashMask = (byte) (entries.length - 1); - head = new HeaderEntry<>(); - } - - @Override - public V get(K name) { - requireNonNull(name, "name"); - - int h = hashingStrategy.hashCode(name); - int i = index(h); - HeaderEntry e = entries[i]; - V value = null; - // loop until the first header was found - while (e != null) { - if (e.hash == h && hashingStrategy.equals(name, e.key)) { - value = e.value; - } - - e = e.next; - } - return value; - } - - @Override - public V get(K name, V defaultValue) { - V value = get(name); - if (value == null) { - return defaultValue; - } - return value; - } - - @Override - public V getAndRemove(K name) { - int h = hashingStrategy.hashCode(name); - return remove0(h, index(h), requireNonNull(name, "name")); - } - - @Override - public V getAndRemove(K name, V defaultValue) { - V value = getAndRemove(name); - if (value == null) { - return defaultValue; - } - return value; - } - - @Override - public List getAll(K name) { - requireNonNull(name, "name"); - - int h = hashingStrategy.hashCode(name); - int i = index(h); - HeaderEntry e = entries[i]; - - if (e == null) { - return Collections.emptyList(); - } - - LinkedList values = new LinkedList<>(); - - do { - if (e.hash == h && hashingStrategy.equals(name, e.key)) { - values.addFirst(e.getValue()); - } - e = e.next; - } while (e != null); - return values; - } - - /** - * Equivalent to {@link #getAll(Object)} but no intermediate list is generated. - * @param name the name of the header to retrieve - * @return an {@link Iterator} of header values corresponding to {@code name}. - */ - public Iterator valueIterator(K name) { - return new ValueIterator(name); - } - - @Override - public List getAllAndRemove(K name) { - List all = getAll(name); - remove(name); - return all; - } - - @Override - public boolean contains(K name) { - return get(name) != null; - } - - @Override - public boolean containsObject(K name, Object value) { - return contains(name, valueConverter.convertObject(requireNonNull(value, "value"))); - } - - @Override - public boolean containsBoolean(K name, boolean value) { - return contains(name, valueConverter.convertBoolean(value)); - } - - @Override - public boolean containsByte(K name, byte value) { - return contains(name, valueConverter.convertByte(value)); - } - - @Override - public boolean containsChar(K name, char value) { - return contains(name, valueConverter.convertChar(value)); - } - - @Override - public boolean containsShort(K name, short value) { - return contains(name, valueConverter.convertShort(value)); - } - - @Override - public boolean containsInt(K name, int value) { - return contains(name, valueConverter.convertInt(value)); - } - - @Override - public boolean containsLong(K name, long value) { - return contains(name, valueConverter.convertLong(value)); - } - - @Override - public boolean containsFloat(K name, float value) { - return contains(name, valueConverter.convertFloat(value)); - } - - @Override - public boolean containsDouble(K name, double value) { - return contains(name, valueConverter.convertDouble(value)); - } - - @Override - public boolean containsTimeMillis(K name, long value) { - return contains(name, valueConverter.convertTimeMillis(value)); - } - - @SuppressWarnings("unchecked") - @Override - public boolean contains(K name, V value) { - return contains(name, value, JAVA_HASHER); - } - - public final boolean contains(K name, V value, HashingStrategy valueHashingStrategy) { - requireNonNull(name, "name"); - - int h = hashingStrategy.hashCode(name); - int i = index(h); - HeaderEntry e = entries[i]; - while (e != null) { - if (e.hash == h && hashingStrategy.equals(name, e.key) && valueHashingStrategy.equals(value, e.value)) { - return true; - } - e = e.next; - } - return false; - } - - @Override - public int size() { - return size; - } - - @Override - public boolean isEmpty() { - return head == head.after; - } - - @Override - public Set names() { - if (isEmpty()) { - return Collections.emptySet(); - } - Set names = new LinkedHashSet<>(size()); - HeaderEntry e = head.after; - while (e != head) { - names.add(e.getKey()); - e = e.after; - } - return names; - } - - @Override - public T add(K name, V value) { - nameValidator.validateName(name); - requireNonNull(value, "value"); - int h = hashingStrategy.hashCode(name); - int i = index(h); - add0(h, i, name, value); - return thisT(); - } - - @Override - public T add(K name, Iterable values) { - nameValidator.validateName(name); - int h = hashingStrategy.hashCode(name); - int i = index(h); - for (V v: values) { - add0(h, i, name, v); - } - return thisT(); - } - - @Override - public T add(K name, V... values) { - nameValidator.validateName(name); - int h = hashingStrategy.hashCode(name); - int i = index(h); - for (V v: values) { - add0(h, i, name, v); - } - return thisT(); - } - - @Override - public T addObject(K name, Object value) { - return add(name, valueConverter.convertObject(requireNonNull(value, "value"))); - } - - @Override - public T addObject(K name, Iterable values) { - for (Object value : values) { - addObject(name, value); - } - return thisT(); - } - - @Override - public T addObject(K name, Object... values) { - for (Object value: values) { - addObject(name, value); - } - return thisT(); - } - - @Override - public T addInt(K name, int value) { - return add(name, valueConverter.convertInt(value)); - } - - @Override - public T addLong(K name, long value) { - return add(name, valueConverter.convertLong(value)); - } - - @Override - public T addDouble(K name, double value) { - return add(name, valueConverter.convertDouble(value)); - } - - @Override - public T addTimeMillis(K name, long value) { - return add(name, valueConverter.convertTimeMillis(value)); - } - - @Override - public T addChar(K name, char value) { - return add(name, valueConverter.convertChar(value)); - } - - @Override - public T addBoolean(K name, boolean value) { - return add(name, valueConverter.convertBoolean(value)); - } - - @Override - public T addFloat(K name, float value) { - return add(name, valueConverter.convertFloat(value)); - } - - @Override - public T addByte(K name, byte value) { - return add(name, valueConverter.convertByte(value)); - } - - @Override - public T addShort(K name, short value) { - return add(name, valueConverter.convertShort(value)); - } - - @Override - public T add(Headers headers) { - if (headers == this) { - throw new IllegalArgumentException("can't add to itself."); - } - addImpl(headers); - return thisT(); - } - - protected void addImpl(Headers headers) { - if (headers instanceof DefaultHeaders) { - @SuppressWarnings("unchecked") - final DefaultHeaders defaultHeaders = - (DefaultHeaders) headers; - HeaderEntry e = defaultHeaders.head.after; - if (defaultHeaders.hashingStrategy == hashingStrategy && - defaultHeaders.nameValidator == nameValidator) { - // Fastest copy - while (e != defaultHeaders.head) { - add0(e.hash, index(e.hash), e.key, e.value); - e = e.after; - } - } else { - // Fast copy - while (e != defaultHeaders.head) { - add(e.key, e.value); - e = e.after; - } - } - } else { - // Slow copy - for (Entry header : headers) { - add(header.getKey(), header.getValue()); - } - } - } - - @Override - public T set(K name, V value) { - nameValidator.validateName(name); - requireNonNull(value, "value"); - int h = hashingStrategy.hashCode(name); - int i = index(h); - remove0(h, i, name); - add0(h, i, name, value); - return thisT(); - } - - @Override - public T set(K name, Iterable values) { - nameValidator.validateName(name); - requireNonNull(values, "values"); - - int h = hashingStrategy.hashCode(name); - int i = index(h); - - remove0(h, i, name); - for (V v: values) { - if (v == null) { - break; - } - add0(h, i, name, v); - } - - return thisT(); - } - - @Override - public T set(K name, V... values) { - nameValidator.validateName(name); - requireNonNull(values, "values"); - - int h = hashingStrategy.hashCode(name); - int i = index(h); - - remove0(h, i, name); - for (V v: values) { - if (v == null) { - break; - } - add0(h, i, name, v); - } - - return thisT(); - } - - @Override - public T setObject(K name, Object value) { - requireNonNull(value, "value"); - V convertedValue = requireNonNull(valueConverter.convertObject(value), "convertedValue"); - return set(name, convertedValue); - } - - @Override - public T setObject(K name, Iterable values) { - nameValidator.validateName(name); - - int h = hashingStrategy.hashCode(name); - int i = index(h); - - remove0(h, i, name); - for (Object v: values) { - if (v == null) { - break; - } - add0(h, i, name, valueConverter.convertObject(v)); - } - - return thisT(); - } - - @Override - public T setObject(K name, Object... values) { - nameValidator.validateName(name); - - int h = hashingStrategy.hashCode(name); - int i = index(h); - - remove0(h, i, name); - for (Object v: values) { - if (v == null) { - break; - } - add0(h, i, name, valueConverter.convertObject(v)); - } - - return thisT(); - } - - @Override - public T setInt(K name, int value) { - return set(name, valueConverter.convertInt(value)); - } - - @Override - public T setLong(K name, long value) { - return set(name, valueConverter.convertLong(value)); - } - - @Override - public T setDouble(K name, double value) { - return set(name, valueConverter.convertDouble(value)); - } - - @Override - public T setTimeMillis(K name, long value) { - return set(name, valueConverter.convertTimeMillis(value)); - } - - @Override - public T setFloat(K name, float value) { - return set(name, valueConverter.convertFloat(value)); - } - - @Override - public T setChar(K name, char value) { - return set(name, valueConverter.convertChar(value)); - } - - @Override - public T setBoolean(K name, boolean value) { - return set(name, valueConverter.convertBoolean(value)); - } - - @Override - public T setByte(K name, byte value) { - return set(name, valueConverter.convertByte(value)); - } - - @Override - public T setShort(K name, short value) { - return set(name, valueConverter.convertShort(value)); - } - - @Override - public T set(Headers headers) { - if (headers != this) { - clear(); - addImpl(headers); - } - return thisT(); - } - - @Override - public T setAll(Headers headers) { - if (headers != this) { - for (K key : headers.names()) { - remove(key); - } - addImpl(headers); - } - return thisT(); - } - - @Override - public boolean remove(K name) { - return getAndRemove(name) != null; - } - - @Override - public T clear() { - Arrays.fill(entries, null); - head.before = head.after = head; - size = 0; - return thisT(); - } - - @Override - public Iterator> iterator() { - return new HeaderIterator(); - } - - @Override - public Boolean getBoolean(K name) { - V v = get(name); - try { - return v != null ? valueConverter.convertToBoolean(v) : null; - } catch (RuntimeException ignore) { - return null; - } - } - - @Override - public boolean getBoolean(K name, boolean defaultValue) { - Boolean v = getBoolean(name); - return v != null ? v : defaultValue; - } - - @Override - public Byte getByte(K name) { - V v = get(name); - try { - return v != null ? valueConverter.convertToByte(v) : null; - } catch (RuntimeException ignore) { - return null; - } - } - - @Override - public byte getByte(K name, byte defaultValue) { - Byte v = getByte(name); - return v != null ? v : defaultValue; - } - - @Override - public Character getChar(K name) { - V v = get(name); - try { - return v != null ? valueConverter.convertToChar(v) : null; - } catch (RuntimeException ignore) { - return null; - } - } - - @Override - public char getChar(K name, char defaultValue) { - Character v = getChar(name); - return v != null ? v : defaultValue; - } - - @Override - public Short getShort(K name) { - V v = get(name); - try { - return v != null ? valueConverter.convertToShort(v) : null; - } catch (RuntimeException ignore) { - return null; - } - } - - @Override - public short getShort(K name, short defaultValue) { - Short v = getShort(name); - return v != null ? v : defaultValue; - } - - @Override - public Integer getInt(K name) { - V v = get(name); - try { - return v != null ? valueConverter.convertToInt(v) : null; - } catch (RuntimeException ignore) { - return null; - } - } - - @Override - public int getInt(K name, int defaultValue) { - Integer v = getInt(name); - return v != null ? v : defaultValue; - } - - @Override - public Long getLong(K name) { - V v = get(name); - try { - return v != null ? valueConverter.convertToLong(v) : null; - } catch (RuntimeException ignore) { - return null; - } - } - - @Override - public long getLong(K name, long defaultValue) { - Long v = getLong(name); - return v != null ? v : defaultValue; - } - - @Override - public Float getFloat(K name) { - V v = get(name); - try { - return v != null ? valueConverter.convertToFloat(v) : null; - } catch (RuntimeException ignore) { - return null; - } - } - - @Override - public float getFloat(K name, float defaultValue) { - Float v = getFloat(name); - return v != null ? v : defaultValue; - } - - @Override - public Double getDouble(K name) { - V v = get(name); - try { - return v != null ? valueConverter.convertToDouble(v) : null; - } catch (RuntimeException ignore) { - return null; - } - } - - @Override - public double getDouble(K name, double defaultValue) { - Double v = getDouble(name); - return v != null ? v : defaultValue; - } - - @Override - public Long getTimeMillis(K name) { - V v = get(name); - try { - return v != null ? valueConverter.convertToTimeMillis(v) : null; - } catch (RuntimeException ignore) { - return null; - } - } - - @Override - public long getTimeMillis(K name, long defaultValue) { - Long v = getTimeMillis(name); - return v != null ? v : defaultValue; - } - - @Override - public Boolean getBooleanAndRemove(K name) { - V v = getAndRemove(name); - try { - return v != null ? valueConverter.convertToBoolean(v) : null; - } catch (RuntimeException ignore) { - return null; - } - } - - @Override - public boolean getBooleanAndRemove(K name, boolean defaultValue) { - Boolean v = getBooleanAndRemove(name); - return v != null ? v : defaultValue; - } - - @Override - public Byte getByteAndRemove(K name) { - V v = getAndRemove(name); - try { - return v != null ? valueConverter.convertToByte(v) : null; - } catch (RuntimeException ignore) { - return null; - } - } - - @Override - public byte getByteAndRemove(K name, byte defaultValue) { - Byte v = getByteAndRemove(name); - return v != null ? v : defaultValue; - } - - @Override - public Character getCharAndRemove(K name) { - V v = getAndRemove(name); - try { - return v != null ? valueConverter.convertToChar(v) : null; - } catch (RuntimeException ignore) { - return null; - } - } - - @Override - public char getCharAndRemove(K name, char defaultValue) { - Character v = getCharAndRemove(name); - return v != null ? v : defaultValue; - } - - @Override - public Short getShortAndRemove(K name) { - V v = getAndRemove(name); - try { - return v != null ? valueConverter.convertToShort(v) : null; - } catch (RuntimeException ignore) { - return null; - } - } - - @Override - public short getShortAndRemove(K name, short defaultValue) { - Short v = getShortAndRemove(name); - return v != null ? v : defaultValue; - } - - @Override - public Integer getIntAndRemove(K name) { - V v = getAndRemove(name); - try { - return v != null ? valueConverter.convertToInt(v) : null; - } catch (RuntimeException ignore) { - return null; - } - } - - @Override - public int getIntAndRemove(K name, int defaultValue) { - Integer v = getIntAndRemove(name); - return v != null ? v : defaultValue; - } - - @Override - public Long getLongAndRemove(K name) { - V v = getAndRemove(name); - try { - return v != null ? valueConverter.convertToLong(v) : null; - } catch (RuntimeException ignore) { - return null; - } - } - - @Override - public long getLongAndRemove(K name, long defaultValue) { - Long v = getLongAndRemove(name); - return v != null ? v : defaultValue; - } - - @Override - public Float getFloatAndRemove(K name) { - V v = getAndRemove(name); - try { - return v != null ? valueConverter.convertToFloat(v) : null; - } catch (RuntimeException ignore) { - return null; - } - } - - @Override - public float getFloatAndRemove(K name, float defaultValue) { - Float v = getFloatAndRemove(name); - return v != null ? v : defaultValue; - } - - @Override - public Double getDoubleAndRemove(K name) { - V v = getAndRemove(name); - try { - return v != null ? valueConverter.convertToDouble(v) : null; - } catch (RuntimeException ignore) { - return null; - } - } - - @Override - public double getDoubleAndRemove(K name, double defaultValue) { - Double v = getDoubleAndRemove(name); - return v != null ? v : defaultValue; - } - - @Override - public Long getTimeMillisAndRemove(K name) { - V v = getAndRemove(name); - try { - return v != null ? valueConverter.convertToTimeMillis(v) : null; - } catch (RuntimeException ignore) { - return null; - } - } - - @Override - public long getTimeMillisAndRemove(K name, long defaultValue) { - Long v = getTimeMillisAndRemove(name); - return v != null ? v : defaultValue; - } - - @SuppressWarnings("unchecked") - @Override - public boolean equals(Object o) { - if (!(o instanceof Headers)) { - return false; - } - - return equals((Headers) o, JAVA_HASHER); - } - - @SuppressWarnings("unchecked") - @Override - public int hashCode() { - return hashCode(JAVA_HASHER); - } - - /** - * Test this object for equality against {@code h2}. - * @param h2 The object to check equality for. - * @param valueHashingStrategy Defines how values will be compared for equality. - * @return {@code true} if this object equals {@code h2} given {@code valueHashingStrategy}. - * {@code false} otherwise. - */ - public final boolean equals(Headers h2, HashingStrategy valueHashingStrategy) { - if (h2.size() != size()) { - return false; - } - - if (this == h2) { - return true; - } - - for (K name : names()) { - List otherValues = h2.getAll(name); - List values = getAll(name); - if (otherValues.size() != values.size()) { - return false; - } - for (int i = 0; i < otherValues.size(); i++) { - if (!valueHashingStrategy.equals(otherValues.get(i), values.get(i))) { - return false; - } - } - } - return true; - } - - /** - * Generate a hash code for this object given a {@link HashingStrategy} to generate hash codes for - * individual values. - * @param valueHashingStrategy Defines how values will be hashed. - */ - public final int hashCode(HashingStrategy valueHashingStrategy) { - int result = HASH_CODE_SEED; - for (K name : names()) { - result = 31 * result + hashingStrategy.hashCode(name); - List values = getAll(name); - for (int i = 0; i < values.size(); ++i) { - result = 31 * result + valueHashingStrategy.hashCode(values.get(i)); - } - } - return result; - } - - @Override - public String toString() { - return HeadersUtils.toString(getClass(), iterator(), size()); - } - - protected HeaderEntry newHeaderEntry(int h, K name, V value, HeaderEntry next) { - return new HeaderEntry<>(h, name, value, next, head); - } - - protected ValueConverter valueConverter() { - return valueConverter; - } - - private int index(int hash) { - return hash & hashMask; - } - - private void add0(int h, int i, K name, V value) { - // Update the hash table. - entries[i] = newHeaderEntry(h, name, value, entries[i]); - ++size; - } - - /** - * @return the first value inserted whose hash code equals {@code h} and whose name is equal to {@code name}. - */ - private V remove0(int h, int i, K name) { - HeaderEntry e = entries[i]; - if (e == null) { - return null; - } - - V value = null; - HeaderEntry next = e.next; - while (next != null) { - if (next.hash == h && hashingStrategy.equals(name, next.key)) { - value = next.value; - e.next = next.next; - next.remove(); - --size; - } else { - e = next; - } - - next = e.next; - } - - e = entries[i]; - if (e.hash == h && hashingStrategy.equals(name, e.key)) { - if (value == null) { - value = e.value; - } - entries[i] = e.next; - e.remove(); - --size; - } - - return value; - } - - HeaderEntry remove0(HeaderEntry entry, HeaderEntry previous) { - int i = index(entry.hash); - HeaderEntry firstEntry = entries[i]; - if (firstEntry == entry) { - entries[i] = entry.next; - previous = entries[i]; - } else if (previous == null) { - // If we don't have any existing starting point, then start from the beginning. - previous = firstEntry; - HeaderEntry next = firstEntry.next; - while (next != null && next != entry) { - previous = next; - next = next.next; - } - assert next != null: "Entry not found in its hash bucket: " + entry; - previous.next = entry.next; - } else { - previous.next = entry.next; - } - entry.remove(); - --size; - return previous; - } - - @SuppressWarnings("unchecked") - private T thisT() { - return (T) this; - } - - /** - * Returns a deep copy of this instance. - */ - public DefaultHeaders copy() { - DefaultHeaders copy = new DefaultHeaders<>( - hashingStrategy, valueConverter, nameValidator, entries.length); - copy.addImpl(this); - return copy; - } - - private final class HeaderIterator implements Iterator> { - private HeaderEntry current = head; - - @Override - public boolean hasNext() { - return current.after != head; - } - - @Override - public Entry next() { - current = current.after; - - if (current == head) { - throw new NoSuchElementException(); - } - - return current; - } - - @Override - public void remove() { - throw new UnsupportedOperationException("read only"); - } - } - - private final class ValueIterator implements Iterator { - private final K name; - private final int hash; - private HeaderEntry removalPrevious; - private HeaderEntry previous; - private HeaderEntry next; - - ValueIterator(K name) { - this.name = requireNonNull(name, "name"); - hash = hashingStrategy.hashCode(name); - calculateNext(entries[index(hash)]); - } - - @Override - public boolean hasNext() { - return next != null; - } - - @Override - public V next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - if (previous != null) { - removalPrevious = previous; - } - previous = next; - calculateNext(next.next); - return previous.value; - } - - @Override - public void remove() { - if (previous == null) { - throw new IllegalStateException(); - } - removalPrevious = remove0(previous, removalPrevious); - previous = null; - } - - private void calculateNext(HeaderEntry entry) { - while (entry != null) { - if (entry.hash == hash && hashingStrategy.equals(name, entry.key)) { - next = entry; - return; - } - entry = entry.next; - } - next = null; - } - } - - protected static class HeaderEntry implements Map.Entry { - protected final int hash; - protected final K key; - protected V value; - /** - * In bucket linked list - */ - protected HeaderEntry next; - /** - * Overall insertion order linked list - */ - protected HeaderEntry before, after; - - protected HeaderEntry(int hash, K key) { - this.hash = hash; - this.key = key; - } - - HeaderEntry(int hash, K key, V value, HeaderEntry next, HeaderEntry head) { - this.hash = hash; - this.key = key; - this.value = value; - this.next = next; - - after = head; - before = head.before; - pointNeighborsToThis(); - } - - HeaderEntry() { - hash = -1; - key = null; - before = after = this; - } - - protected final void pointNeighborsToThis() { - before.after = this; - after.before = this; - } - - public final HeaderEntry before() { - return before; - } - - public final HeaderEntry after() { - return after; - } - - protected void remove() { - before.after = after; - after.before = before; - } - - @Override - public final K getKey() { - return key; - } - - @Override - public final V getValue() { - return value; - } - - @Override - public final V setValue(V value) { - requireNonNull(value, "value"); - V oldValue = this.value; - this.value = value; - return oldValue; - } - - @Override - public final String toString() { - return key.toString() + '=' + value.toString(); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof Map.Entry)) { - return false; - } - Map.Entry other = (Map.Entry) o; - return (getKey() == null ? other.getKey() == null : getKey().equals(other.getKey())) && - (getValue() == null ? other.getValue() == null : getValue().equals(other.getValue())); - } - - @Override - public int hashCode() { - return (key == null ? 0 : key.hashCode()) ^ (value == null ? 0 : value.hashCode()); - } - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/DefaultHeadersImpl.java b/codec/src/main/java/io/netty/handler/codec/DefaultHeadersImpl.java deleted file mode 100644 index d7953a2f41..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/DefaultHeadersImpl.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec; - -import io.netty.util.HashingStrategy; - -/** - * A concrete implementation of {@link DefaultHeaders} that allows for direct instantiation. - * @param the type of the header name. - * @param the type of the header value. - */ -public final class DefaultHeadersImpl extends DefaultHeaders> { - public DefaultHeadersImpl(HashingStrategy nameHashingStrategy, - ValueConverter valueConverter, NameValidator nameValidator) { - super(nameHashingStrategy, valueConverter, nameValidator); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/DelimiterBasedFrameDecoder.java b/codec/src/main/java/io/netty/handler/codec/DelimiterBasedFrameDecoder.java deleted file mode 100644 index 6ee7ebf640..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/DelimiterBasedFrameDecoder.java +++ /dev/null @@ -1,348 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import static io.netty.util.internal.ObjectUtil.checkPositive; -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; - -/** - * A decoder that splits the received {@link ByteBuf}s by one or more - * delimiters. It is particularly useful for decoding the frames which ends - * with a delimiter such as {@link Delimiters#nulDelimiter() NUL} or - * {@linkplain Delimiters#lineDelimiter() newline characters}. - * - *

Predefined delimiters

- *

- * {@link Delimiters} defines frequently used delimiters for convenience' sake. - * - *

Specifying more than one delimiter

- *

- * {@link DelimiterBasedFrameDecoder} allows you to specify more than one - * delimiter. If more than one delimiter is found in the buffer, it chooses - * the delimiter which produces the shortest frame. For example, if you have - * the following data in the buffer: - *

- * +--------------+
- * | ABC\nDEF\r\n |
- * +--------------+
- * 
- * a {@link DelimiterBasedFrameDecoder}({@link Delimiters#lineDelimiter() Delimiters.lineDelimiter()}) - * will choose {@code '\n'} as the first delimiter and produce two frames: - *
- * +-----+-----+
- * | ABC | DEF |
- * +-----+-----+
- * 
- * rather than incorrectly choosing {@code '\r\n'} as the first delimiter: - *
- * +----------+
- * | ABC\nDEF |
- * +----------+
- * 
- */ -public class DelimiterBasedFrameDecoder extends ByteToMessageDecoder { - - private final ByteBuf[] delimiters; - private final int maxFrameLength; - private final boolean stripDelimiter; - private final boolean failFast; - private boolean discardingTooLongFrame; - private int tooLongFrameLength; - /** Set only when decoding with "\n" and "\r\n" as the delimiter. */ - private final LineBasedFrameDecoder lineBasedDecoder; - - /** - * Creates a new instance. - * - * @param maxFrameLength the maximum length of the decoded frame. - * A {@link TooLongFrameException} is thrown if - * the length of the frame exceeds this value. - * @param delimiter the delimiter - */ - public DelimiterBasedFrameDecoder(int maxFrameLength, ByteBuf delimiter) { - this(maxFrameLength, true, delimiter); - } - - /** - * Creates a new instance. - * - * @param maxFrameLength the maximum length of the decoded frame. - * A {@link TooLongFrameException} is thrown if - * the length of the frame exceeds this value. - * @param stripDelimiter whether the decoded frame should strip out the - * delimiter or not - * @param delimiter the delimiter - */ - public DelimiterBasedFrameDecoder( - int maxFrameLength, boolean stripDelimiter, ByteBuf delimiter) { - this(maxFrameLength, stripDelimiter, true, delimiter); - } - - /** - * Creates a new instance. - * - * @param maxFrameLength the maximum length of the decoded frame. - * A {@link TooLongFrameException} is thrown if - * the length of the frame exceeds this value. - * @param stripDelimiter whether the decoded frame should strip out the - * delimiter or not - * @param failFast If true, a {@link TooLongFrameException} is - * thrown as soon as the decoder notices the length of the - * frame will exceed maxFrameLength regardless of - * whether the entire frame has been read. - * If false, a {@link TooLongFrameException} is - * thrown after the entire frame that exceeds - * maxFrameLength has been read. - * @param delimiter the delimiter - */ - public DelimiterBasedFrameDecoder( - int maxFrameLength, boolean stripDelimiter, boolean failFast, - ByteBuf delimiter) { - this(maxFrameLength, stripDelimiter, failFast, new ByteBuf[] { - delimiter.slice(delimiter.readerIndex(), delimiter.readableBytes())}); - } - - /** - * Creates a new instance. - * - * @param maxFrameLength the maximum length of the decoded frame. - * A {@link TooLongFrameException} is thrown if - * the length of the frame exceeds this value. - * @param delimiters the delimiters - */ - public DelimiterBasedFrameDecoder(int maxFrameLength, ByteBuf... delimiters) { - this(maxFrameLength, true, delimiters); - } - - /** - * Creates a new instance. - * - * @param maxFrameLength the maximum length of the decoded frame. - * A {@link TooLongFrameException} is thrown if - * the length of the frame exceeds this value. - * @param stripDelimiter whether the decoded frame should strip out the - * delimiter or not - * @param delimiters the delimiters - */ - public DelimiterBasedFrameDecoder( - int maxFrameLength, boolean stripDelimiter, ByteBuf... delimiters) { - this(maxFrameLength, stripDelimiter, true, delimiters); - } - - /** - * Creates a new instance. - * - * @param maxFrameLength the maximum length of the decoded frame. - * A {@link TooLongFrameException} is thrown if - * the length of the frame exceeds this value. - * @param stripDelimiter whether the decoded frame should strip out the - * delimiter or not - * @param failFast If true, a {@link TooLongFrameException} is - * thrown as soon as the decoder notices the length of the - * frame will exceed maxFrameLength regardless of - * whether the entire frame has been read. - * If false, a {@link TooLongFrameException} is - * thrown after the entire frame that exceeds - * maxFrameLength has been read. - * @param delimiters the delimiters - */ - public DelimiterBasedFrameDecoder( - int maxFrameLength, boolean stripDelimiter, boolean failFast, ByteBuf... delimiters) { - validateMaxFrameLength(maxFrameLength); - requireNonNull(delimiters, "delimiters"); - if (delimiters.length == 0) { - throw new IllegalArgumentException("empty delimiters"); - } - - if (isLineBased(delimiters) && !isSubclass()) { - lineBasedDecoder = new LineBasedFrameDecoder(maxFrameLength, stripDelimiter, failFast); - this.delimiters = null; - } else { - this.delimiters = new ByteBuf[delimiters.length]; - for (int i = 0; i < delimiters.length; i ++) { - ByteBuf d = delimiters[i]; - validateDelimiter(d); - this.delimiters[i] = d.slice(d.readerIndex(), d.readableBytes()); - } - lineBasedDecoder = null; - } - this.maxFrameLength = maxFrameLength; - this.stripDelimiter = stripDelimiter; - this.failFast = failFast; - } - - /** Returns true if the delimiters are "\n" and "\r\n". */ - private static boolean isLineBased(final ByteBuf[] delimiters) { - if (delimiters.length != 2) { - return false; - } - ByteBuf a = delimiters[0]; - ByteBuf b = delimiters[1]; - if (a.capacity() < b.capacity()) { - a = delimiters[1]; - b = delimiters[0]; - } - return a.capacity() == 2 && b.capacity() == 1 - && a.getByte(0) == '\r' && a.getByte(1) == '\n' - && b.getByte(0) == '\n'; - } - - /** - * Return {@code true} if the current instance is a subclass of DelimiterBasedFrameDecoder - */ - private boolean isSubclass() { - return getClass() != DelimiterBasedFrameDecoder.class; - } - - @Override - protected final void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - Object decoded = decode0(ctx, in); - if (decoded != null) { - ctx.fireChannelRead(decoded); - } - } - - /** - * Create a frame out of the {@link ByteBuf} and return it. - * - * @param ctx the {@link ChannelHandlerContext} which this {@link ByteToMessageDecoder} belongs to - * @param buffer the {@link ByteBuf} from which to read data - * @return frame the {@link ByteBuf} which represent the frame or {@code null} if no frame could - * be created. - */ - protected Object decode0(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception { - if (lineBasedDecoder != null) { - return lineBasedDecoder.decode0(ctx, buffer); - } - // Try all delimiters and choose the delimiter which yields the shortest frame. - int minFrameLength = Integer.MAX_VALUE; - ByteBuf minDelim = null; - for (ByteBuf delim: delimiters) { - int frameLength = indexOf(buffer, delim); - if (frameLength >= 0 && frameLength < minFrameLength) { - minFrameLength = frameLength; - minDelim = delim; - } - } - - if (minDelim != null) { - int minDelimLength = minDelim.capacity(); - ByteBuf frame; - - if (discardingTooLongFrame) { - // We've just finished discarding a very large frame. - // Go back to the initial state. - discardingTooLongFrame = false; - buffer.skipBytes(minFrameLength + minDelimLength); - - int tooLongFrameLength = this.tooLongFrameLength; - this.tooLongFrameLength = 0; - if (!failFast) { - fail(tooLongFrameLength); - } - return null; - } - - if (minFrameLength > maxFrameLength) { - // Discard read frame. - buffer.skipBytes(minFrameLength + minDelimLength); - fail(minFrameLength); - return null; - } - - if (stripDelimiter) { - frame = buffer.readRetainedSlice(minFrameLength); - buffer.skipBytes(minDelimLength); - } else { - frame = buffer.readRetainedSlice(minFrameLength + minDelimLength); - } - - return frame; - } else { - if (!discardingTooLongFrame) { - if (buffer.readableBytes() > maxFrameLength) { - // Discard the content of the buffer until a delimiter is found. - tooLongFrameLength = buffer.readableBytes(); - buffer.skipBytes(buffer.readableBytes()); - discardingTooLongFrame = true; - if (failFast) { - fail(tooLongFrameLength); - } - } - } else { - // Still discarding the buffer since a delimiter is not found. - tooLongFrameLength += buffer.readableBytes(); - buffer.skipBytes(buffer.readableBytes()); - } - return null; - } - } - - private void fail(long frameLength) { - if (frameLength > 0) { - throw new TooLongFrameException( - "frame length exceeds " + maxFrameLength + - ": " + frameLength + " - discarded"); - } else { - throw new TooLongFrameException( - "frame length exceeds " + maxFrameLength + - " - discarding"); - } - } - - /** - * Returns the number of bytes between the readerIndex of the haystack and - * the first needle found in the haystack. -1 is returned if no needle is - * found in the haystack. - */ - private static int indexOf(ByteBuf haystack, ByteBuf needle) { - for (int i = haystack.readerIndex(); i < haystack.writerIndex(); i ++) { - int haystackIndex = i; - int needleIndex; - for (needleIndex = 0; needleIndex < needle.capacity(); needleIndex ++) { - if (haystack.getByte(haystackIndex) != needle.getByte(needleIndex)) { - break; - } else { - haystackIndex ++; - if (haystackIndex == haystack.writerIndex() && - needleIndex != needle.capacity() - 1) { - return -1; - } - } - } - - if (needleIndex == needle.capacity()) { - // Found the needle from the haystack! - return i - haystack.readerIndex(); - } - } - return -1; - } - - private static void validateDelimiter(ByteBuf delimiter) { - requireNonNull(delimiter, "delimiter"); - if (!delimiter.isReadable()) { - throw new IllegalArgumentException("empty delimiter"); - } - } - - private static void validateMaxFrameLength(int maxFrameLength) { - checkPositive(maxFrameLength, "maxFrameLength"); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/Delimiters.java b/codec/src/main/java/io/netty/handler/codec/Delimiters.java deleted file mode 100644 index 93f086be83..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/Delimiters.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; - -/** - * A set of commonly used delimiters for {@link DelimiterBasedFrameDecoder}. - */ -public final class Delimiters { - - /** - * Returns a {@code NUL (0x00)} delimiter, which could be used for - * Flash XML socket or any similar protocols. - */ - public static ByteBuf[] nulDelimiter() { - return new ByteBuf[] { - Unpooled.wrappedBuffer(new byte[] { 0 }) }; - } - - /** - * Returns {@code CR ('\r')} and {@code LF ('\n')} delimiters, which could - * be used for text-based line protocols. - */ - public static ByteBuf[] lineDelimiter() { - return new ByteBuf[] { - Unpooled.wrappedBuffer(new byte[] { '\r', '\n' }), - Unpooled.wrappedBuffer(new byte[] { '\n' }), - }; - } - - private Delimiters() { - // Unused - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/EmptyHeaders.java b/codec/src/main/java/io/netty/handler/codec/EmptyHeaders.java deleted file mode 100644 index c1024970f4..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/EmptyHeaders.java +++ /dev/null @@ -1,526 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec; - -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Map.Entry; -import java.util.Set; - -import static io.netty.handler.codec.DefaultHeaders.HASH_CODE_SEED; - -public class EmptyHeaders> implements Headers { - @Override - public V get(K name) { - return null; - } - - @Override - public V get(K name, V defaultValue) { - return defaultValue; - } - - @Override - public V getAndRemove(K name) { - return null; - } - - @Override - public V getAndRemove(K name, V defaultValue) { - return defaultValue; - } - - @Override - public List getAll(K name) { - return Collections.emptyList(); - } - - @Override - public List getAllAndRemove(K name) { - return Collections.emptyList(); - } - - @Override - public Boolean getBoolean(K name) { - return null; - } - - @Override - public boolean getBoolean(K name, boolean defaultValue) { - return defaultValue; - } - - @Override - public Byte getByte(K name) { - return null; - } - - @Override - public byte getByte(K name, byte defaultValue) { - return defaultValue; - } - - @Override - public Character getChar(K name) { - return null; - } - - @Override - public char getChar(K name, char defaultValue) { - return defaultValue; - } - - @Override - public Short getShort(K name) { - return null; - } - - @Override - public short getShort(K name, short defaultValue) { - return defaultValue; - } - - @Override - public Integer getInt(K name) { - return null; - } - - @Override - public int getInt(K name, int defaultValue) { - return defaultValue; - } - - @Override - public Long getLong(K name) { - return null; - } - - @Override - public long getLong(K name, long defaultValue) { - return defaultValue; - } - - @Override - public Float getFloat(K name) { - return null; - } - - @Override - public float getFloat(K name, float defaultValue) { - return defaultValue; - } - - @Override - public Double getDouble(K name) { - return null; - } - - @Override - public double getDouble(K name, double defaultValue) { - return defaultValue; - } - - @Override - public Long getTimeMillis(K name) { - return null; - } - - @Override - public long getTimeMillis(K name, long defaultValue) { - return defaultValue; - } - - @Override - public Boolean getBooleanAndRemove(K name) { - return null; - } - - @Override - public boolean getBooleanAndRemove(K name, boolean defaultValue) { - return defaultValue; - } - - @Override - public Byte getByteAndRemove(K name) { - return null; - } - - @Override - public byte getByteAndRemove(K name, byte defaultValue) { - return defaultValue; - } - - @Override - public Character getCharAndRemove(K name) { - return null; - } - - @Override - public char getCharAndRemove(K name, char defaultValue) { - return defaultValue; - } - - @Override - public Short getShortAndRemove(K name) { - return null; - } - - @Override - public short getShortAndRemove(K name, short defaultValue) { - return defaultValue; - } - - @Override - public Integer getIntAndRemove(K name) { - return null; - } - - @Override - public int getIntAndRemove(K name, int defaultValue) { - return defaultValue; - } - - @Override - public Long getLongAndRemove(K name) { - return null; - } - - @Override - public long getLongAndRemove(K name, long defaultValue) { - return defaultValue; - } - - @Override - public Float getFloatAndRemove(K name) { - return null; - } - - @Override - public float getFloatAndRemove(K name, float defaultValue) { - return defaultValue; - } - - @Override - public Double getDoubleAndRemove(K name) { - return null; - } - - @Override - public double getDoubleAndRemove(K name, double defaultValue) { - return defaultValue; - } - - @Override - public Long getTimeMillisAndRemove(K name) { - return null; - } - - @Override - public long getTimeMillisAndRemove(K name, long defaultValue) { - return defaultValue; - } - - @Override - public boolean contains(K name) { - return false; - } - - @Override - public boolean contains(K name, V value) { - return false; - } - - @Override - public boolean containsObject(K name, Object value) { - return false; - } - - @Override - public boolean containsBoolean(K name, boolean value) { - return false; - } - - @Override - public boolean containsByte(K name, byte value) { - return false; - } - - @Override - public boolean containsChar(K name, char value) { - return false; - } - - @Override - public boolean containsShort(K name, short value) { - return false; - } - - @Override - public boolean containsInt(K name, int value) { - return false; - } - - @Override - public boolean containsLong(K name, long value) { - return false; - } - - @Override - public boolean containsFloat(K name, float value) { - return false; - } - - @Override - public boolean containsDouble(K name, double value) { - return false; - } - - @Override - public boolean containsTimeMillis(K name, long value) { - return false; - } - - @Override - public int size() { - return 0; - } - - @Override - public boolean isEmpty() { - return true; - } - - @Override - public Set names() { - return Collections.emptySet(); - } - - @Override - public T add(K name, V value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public T add(K name, Iterable values) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public T add(K name, V... values) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public T addObject(K name, Object value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public T addObject(K name, Iterable values) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public T addObject(K name, Object... values) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public T addBoolean(K name, boolean value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public T addByte(K name, byte value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public T addChar(K name, char value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public T addShort(K name, short value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public T addInt(K name, int value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public T addLong(K name, long value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public T addFloat(K name, float value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public T addDouble(K name, double value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public T addTimeMillis(K name, long value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public T add(Headers headers) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public T set(K name, V value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public T set(K name, Iterable values) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public T set(K name, V... values) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public T setObject(K name, Object value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public T setObject(K name, Iterable values) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public T setObject(K name, Object... values) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public T setBoolean(K name, boolean value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public T setByte(K name, byte value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public T setChar(K name, char value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public T setShort(K name, short value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public T setInt(K name, int value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public T setLong(K name, long value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public T setFloat(K name, float value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public T setDouble(K name, double value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public T setTimeMillis(K name, long value) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public T set(Headers headers) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public T setAll(Headers headers) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public boolean remove(K name) { - return false; - } - - @Override - public T clear() { - return thisT(); - } - - /** - * Equivalent to {@link #getAll(Object)} but no intermediate list is generated. - * @param name the name of the header to retrieve - * @return an {@link Iterator} of header values corresponding to {@code name}. - */ - public Iterator valueIterator(@SuppressWarnings("unused") K name) { - List empty = Collections.emptyList(); - return empty.iterator(); - } - - @Override - public Iterator> iterator() { - List> empty = Collections.emptyList(); - return empty.iterator(); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof Headers)) { - return false; - } - - Headers rhs = (Headers) o; - return isEmpty() && rhs.isEmpty(); - } - - @Override - public int hashCode() { - return HASH_CODE_SEED; - } - - @Override - public String toString() { - return new StringBuilder(getClass().getSimpleName()).append('[').append(']').toString(); - } - - @SuppressWarnings("unchecked") - private T thisT() { - return (T) this; - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/EncoderException.java b/codec/src/main/java/io/netty/handler/codec/EncoderException.java deleted file mode 100644 index ebf6efb352..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/EncoderException.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -/** - * An {@link CodecException} which is thrown by an encoder. - */ -public class EncoderException extends CodecException { - - private static final long serialVersionUID = -5086121160476476774L; - - /** - * Creates a new instance. - */ - public EncoderException() { - } - - /** - * Creates a new instance. - */ - public EncoderException(String message, Throwable cause) { - super(message, cause); - } - - /** - * Creates a new instance. - */ - public EncoderException(String message) { - super(message); - } - - /** - * Creates a new instance. - */ - public EncoderException(Throwable cause) { - super(cause); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/FixedLengthFrameDecoder.java b/codec/src/main/java/io/netty/handler/codec/FixedLengthFrameDecoder.java deleted file mode 100644 index 04e3b22e52..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/FixedLengthFrameDecoder.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import io.netty.buffer.api.Buffer; -import io.netty.channel.ChannelHandlerContext; - -import static io.netty.util.internal.ObjectUtil.checkPositive; - -/** - * A decoder that splits the received {@link Buffer}s by the fixed number - * of bytes. For example, if you received the following four fragmented packets: - *
- * +---+----+------+----+
- * | A | BC | DEFG | HI |
- * +---+----+------+----+
- * 
- * A {@link FixedLengthFrameDecoder}{@code (3)} will decode them into the - * following three packets with the fixed length: - *
- * +-----+-----+-----+
- * | ABC | DEF | GHI |
- * +-----+-----+-----+
- * 
- */ -public class FixedLengthFrameDecoder extends ByteToMessageDecoderForBuffer { - - private final int frameLength; - - /** - * Creates a new instance. - * - * @param frameLength the length of the frame - */ - public FixedLengthFrameDecoder(int frameLength) { - checkPositive(frameLength, "frameLength"); - this.frameLength = frameLength; - } - - /** - * Creates a new instance. - * - * @param frameLength the length of the frame - */ - public FixedLengthFrameDecoder(int frameLength, Cumulator cumulator) { - super(cumulator); - checkPositive(frameLength, "frameLength"); - this.frameLength = frameLength; - } - - @Override - protected final void decode(ChannelHandlerContext ctx, Buffer in) throws Exception { - Object decoded = decode0(ctx, in); - if (decoded != null) { - ctx.fireChannelRead(decoded); - } - } - - /** - * Create a frame out of the {@link Buffer} and return it. - * - * @param ctx the {@link ChannelHandlerContext} which this {@link ByteToMessageDecoder} belongs to - * @param in the {@link Buffer} from which to read data - * @return frame the {@link Buffer} which represent the frame or {@code null} if no frame could - * be created. - */ - protected Object decode0(@SuppressWarnings("UnusedParameters") ChannelHandlerContext ctx, Buffer in) - throws Exception { - if (in.readableBytes() < frameLength) { - return null; - } else { - return in.split(in.readerOffset() + frameLength); - } - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/Headers.java b/codec/src/main/java/io/netty/handler/codec/Headers.java deleted file mode 100644 index 97301ba136..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/Headers.java +++ /dev/null @@ -1,998 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec; - -import java.util.Iterator; -import java.util.List; -import java.util.Map.Entry; -import java.util.Set; - -/** - * Common interface for {@link Headers} which represents a mapping of key to value. - * Duplicate keys may be allowed by implementations. - * - * @param the type of the header name. - * @param the type of the header value. - * @param the type to use for return values when the intention is to return {@code this} object. - */ -public interface Headers> extends Iterable> { - /** - * Returns the value of a header with the specified name. If there is more than one value for the specified name, - * the first value in insertion order is returned. - * - * @param name the name of the header to retrieve - * @return the first header value if the header is found. {@code null} if there's no such header - */ - V get(K name); - - /** - * Returns the value of a header with the specified name. If there is more than one value for the specified name, - * the first value in insertion order is returned. - * - * @param name the name of the header to retrieve - * @param defaultValue the default value - * @return the first header value or {@code defaultValue} if there is no such header - */ - V get(K name, V defaultValue); - - /** - * Returns the value of a header with the specified name and removes it from this object. If there is more than - * one value for the specified name, the first value in insertion order is returned. - * - * @param name the name of the header to retrieve - * @return the first header value or {@code null} if there is no such header - */ - V getAndRemove(K name); - - /** - * Returns the value of a header with the specified name and removes it from this object. If there is more than - * one value for the specified name, the first value in insertion order is returned. - * - * @param name the name of the header to retrieve - * @param defaultValue the default value - * @return the first header value or {@code defaultValue} if there is no such header - */ - V getAndRemove(K name, V defaultValue); - - /** - * Returns all values for the header with the specified name. The returned {@link List} can't be modified. - * - * @param name the name of the header to retrieve - * @return a {@link List} of header values or an empty {@link List} if no values are found. - */ - List getAll(K name); - - /** - * Returns all values for the header with the specified name and removes them from this object. - * The returned {@link List} can't be modified. - * - * @param name the name of the header to retrieve - * @return a {@link List} of header values or an empty {@link List} if no values are found. - */ - List getAllAndRemove(K name); - - /** - * Returns the {@code boolean} value of a header with the specified name. If there is more than one value for the - * specified name, the first value in insertion order is returned. - * - * @param name the name of the header to retrieve - * @return the {@code boolean} value of the first value in insertion order or {@code null} if there is no such - * value or it can't be converted to {@code boolean}. - */ - Boolean getBoolean(K name); - - /** - * Returns the {@code boolean} value of a header with the specified name. If there is more than one value for the - * specified name, the first value in insertion order is returned. - * - * @param name the name of the header to retrieve - * @param defaultValue the default value - * @return the {@code boolean} value of the first value in insertion order or {@code defaultValue} if there is no - * such value or it can't be converted to {@code boolean}. - */ - boolean getBoolean(K name, boolean defaultValue); - - /** - * Returns the {@code byte} value of a header with the specified name. If there is more than one value for the - * specified name, the first value in insertion order is returned. - * - * @param name the name of the header to retrieve - * @return the {@code byte} value of the first value in insertion order or {@code null} if there is no such - * value or it can't be converted to {@code byte}. - */ - Byte getByte(K name); - - /** - * Returns the {@code byte} value of a header with the specified name. If there is more than one value for the - * specified name, the first value in insertion order is returned. - * - * @param name the name of the header to retrieve - * @param defaultValue the default value - * @return the {@code byte} value of the first value in insertion order or {@code defaultValue} if there is no - * such value or it can't be converted to {@code byte}. - */ - byte getByte(K name, byte defaultValue); - - /** - * Returns the {@code char} value of a header with the specified name. If there is more than one value for the - * specified name, the first value in insertion order is returned. - * - * @param name the name of the header to retrieve - * @return the {@code char} value of the first value in insertion order or {@code null} if there is no such - * value or it can't be converted to {@code char}. - */ - Character getChar(K name); - - /** - * Returns the {@code char} value of a header with the specified name. If there is more than one value for the - * specified name, the first value in insertion order is returned. - * - * @param name the name of the header to retrieve - * @param defaultValue the default value - * @return the {@code char} value of the first value in insertion order or {@code defaultValue} if there is no - * such value or it can't be converted to {@code char}. - */ - char getChar(K name, char defaultValue); - - /** - * Returns the {@code short} value of a header with the specified name. If there is more than one value for the - * specified name, the first value in insertion order is returned. - * - * @param name the name of the header to retrieve - * @return the {@code short} value of the first value in insertion order or {@code null} if there is no such - * value or it can't be converted to {@code short}. - */ - Short getShort(K name); - - /** - * Returns the {@code short} value of a header with the specified name. If there is more than one value for the - * specified name, the first value in insertion order is returned. - * - * @param name the name of the header to retrieve - * @param defaultValue the default value - * @return the {@code short} value of the first value in insertion order or {@code defaultValue} if there is no - * such value or it can't be converted to {@code short}. - */ - short getShort(K name, short defaultValue); - - /** - * Returns the {@code int} value of a header with the specified name. If there is more than one value for the - * specified name, the first value in insertion order is returned. - * - * @param name the name of the header to retrieve - * @return the {@code int} value of the first value in insertion order or {@code null} if there is no such - * value or it can't be converted to {@code int}. - */ - Integer getInt(K name); - - /** - * Returns the {@code int} value of a header with the specified name. If there is more than one value for the - * specified name, the first value in insertion order is returned. - * - * @param name the name of the header to retrieve - * @param defaultValue the default value - * @return the {@code int} value of the first value in insertion order or {@code defaultValue} if there is no - * such value or it can't be converted to {@code int}. - */ - int getInt(K name, int defaultValue); - - /** - * Returns the {@code long} value of a header with the specified name. If there is more than one value for the - * specified name, the first value in insertion order is returned. - * - * @param name the name of the header to retrieve - * @return the {@code long} value of the first value in insertion order or {@code null} if there is no such - * value or it can't be converted to {@code long}. - */ - Long getLong(K name); - - /** - * Returns the {@code long} value of a header with the specified name. If there is more than one value for the - * specified name, the first value in insertion order is returned. - * - * @param name the name of the header to retrieve - * @param defaultValue the default value - * @return the {@code long} value of the first value in insertion order or {@code defaultValue} if there is no - * such value or it can't be converted to {@code long}. - */ - long getLong(K name, long defaultValue); - - /** - * Returns the {@code float} value of a header with the specified name. If there is more than one value for the - * specified name, the first value in insertion order is returned. - * - * @param name the name of the header to retrieve - * @return the {@code float} value of the first value in insertion order or {@code null} if there is no such - * value or it can't be converted to {@code float}. - */ - Float getFloat(K name); - - /** - * Returns the {@code float} value of a header with the specified name. If there is more than one value for the - * specified name, the first value in insertion order is returned. - * - * @param name the name of the header to retrieve - * @param defaultValue the default value - * @return the {@code float} value of the first value in insertion order or {@code defaultValue} if there is no - * such value or it can't be converted to {@code float}. - */ - float getFloat(K name, float defaultValue); - - /** - * Returns the {@code double} value of a header with the specified name. If there is more than one value for the - * specified name, the first value in insertion order is returned. - * - * @param name the name of the header to retrieve - * @return the {@code double} value of the first value in insertion order or {@code null} if there is no such - * value or it can't be converted to {@code double}. - */ - Double getDouble(K name); - - /** - * Returns the {@code double} value of a header with the specified name. If there is more than one value for the - * specified name, the first value in insertion order is returned. - * - * @param name the name of the header to retrieve - * @param defaultValue the default value - * @return the {@code double} value of the first value in insertion order or {@code defaultValue} if there is no - * such value or it can't be converted to {@code double}. - */ - double getDouble(K name, double defaultValue); - - /** - * Returns the value of a header with the specified name in milliseconds. If there is more than one value for the - * specified name, the first value in insertion order is returned. - * - * @param name the name of the header to retrieve - * @return the milliseconds value of the first value in insertion order or {@code null} if there is no such - * value or it can't be converted to milliseconds. - */ - Long getTimeMillis(K name); - - /** - * Returns the value of a header with the specified name in milliseconds. If there is more than one value for the - * specified name, the first value in insertion order is returned. - * - * @param name the name of the header to retrieve - * @param defaultValue the default value - * @return the milliseconds value of the first value in insertion order or {@code defaultValue} if there is no such - * value or it can't be converted to milliseconds. - */ - long getTimeMillis(K name, long defaultValue); - - /** - * Returns the {@code boolean} value of a header with the specified {@code name} and removes the header from this - * object. If there is more than one value for the specified name, the first value in insertion order is returned. - * In any case all values for {@code name} are removed. - *

- * If an exception occurs during the translation from type {@code T} all entries with {@code name} may still - * be removed. - * @param name the name of the header to retrieve - * @return the {@code boolean} value of the first value in insertion order or {@code null} if there is no - * such value or it can't be converted to {@code boolean}. - */ - Boolean getBooleanAndRemove(K name); - - /** - * Returns the {@code boolean} value of a header with the specified {@code name} and removes the header from this - * object. If there is more than one value for the specified name, the first value in insertion order is returned. - * In any case all values for {@code name} are removed. - *

- * If an exception occurs during the translation from type {@code T} all entries with {@code name} may still - * be removed. - * @param name the name of the header to search - * @param defaultValue the default value - * @return the {@code boolean} value of the first value in insertion order or {@code defaultValue} if there is no - * such value or it can't be converted to {@code boolean}. - */ - boolean getBooleanAndRemove(K name, boolean defaultValue); - - /** - * Returns the {@code byte} value of a header with the specified {@code name} and removes the header from this - * object. If there is more than one value for the specified name, the first value in insertion order is returned. - * In any case all values for {@code name} are removed. - *

- * If an exception occurs during the translation from type {@code T} all entries with {@code name} may still - * be removed. - * @param name the name of the header to search - * @return the {@code byte} value of the first value in insertion order or {@code null} if there is no - * such value or it can't be converted to {@code byte}. - */ - Byte getByteAndRemove(K name); - - /** - * Returns the {@code byte} value of a header with the specified {@code name} and removes the header from this - * object. If there is more than one value for the specified name, the first value in insertion order is returned. - * In any case all values for {@code name} are removed. - *

- * If an exception occurs during the translation from type {@code T} all entries with {@code name} may still - * be removed. - * @param name the name of the header to search - * @param defaultValue the default value - * @return the {@code byte} value of the first value in insertion order or {@code defaultValue} if there is no - * such value or it can't be converted to {@code byte}. - */ - byte getByteAndRemove(K name, byte defaultValue); - - /** - * Returns the {@code char} value of a header with the specified {@code name} and removes the header from this - * object. If there is more than one value for the specified name, the first value in insertion order is returned. - * In any case all values for {@code name} are removed. - *

- * If an exception occurs during the translation from type {@code T} all entries with {@code name} may still - * be removed. - * @param name the name of the header to search - * @return the {@code char} value of the first value in insertion order or {@code null} if there is no - * such value or it can't be converted to {@code char}. - */ - Character getCharAndRemove(K name); - - /** - * Returns the {@code char} value of a header with the specified {@code name} and removes the header from this - * object. If there is more than one value for the specified name, the first value in insertion order is returned. - * In any case all values for {@code name} are removed. - *

- * If an exception occurs during the translation from type {@code T} all entries with {@code name} may still - * be removed. - * @param name the name of the header to search - * @param defaultValue the default value - * @return the {@code char} value of the first value in insertion order or {@code defaultValue} if there is no - * such value or it can't be converted to {@code char}. - */ - char getCharAndRemove(K name, char defaultValue); - - /** - * Returns the {@code short} value of a header with the specified {@code name} and removes the header from this - * object. If there is more than one value for the specified name, the first value in insertion order is returned. - * In any case all values for {@code name} are removed. - *

- * If an exception occurs during the translation from type {@code T} all entries with {@code name} may still - * be removed. - * @param name the name of the header to search - * @return the {@code short} value of the first value in insertion order or {@code null} if there is no - * such value or it can't be converted to {@code short}. - */ - Short getShortAndRemove(K name); - - /** - * Returns the {@code short} value of a header with the specified {@code name} and removes the header from this - * object. If there is more than one value for the specified name, the first value in insertion order is returned. - * In any case all values for {@code name} are removed. - *

- * If an exception occurs during the translation from type {@code T} all entries with {@code name} may still - * be removed. - * @param name the name of the header to search - * @param defaultValue the default value - * @return the {@code short} value of the first value in insertion order or {@code defaultValue} if there is no - * such value or it can't be converted to {@code short}. - */ - short getShortAndRemove(K name, short defaultValue); - - /** - * Returns the {@code int} value of a header with the specified {@code name} and removes the header from this - * object. If there is more than one value for the specified name, the first value in insertion order is returned. - * In any case all values for {@code name} are removed. - *

- * If an exception occurs during the translation from type {@code T} all entries with {@code name} may still - * be removed. - * @param name the name of the header to search - * @return the {@code int} value of the first value in insertion order or {@code null} if there is no - * such value or it can't be converted to {@code int}. - */ - Integer getIntAndRemove(K name); - - /** - * Returns the {@code int} value of a header with the specified {@code name} and removes the header from this - * object. If there is more than one value for the specified name, the first value in insertion order is returned. - * In any case all values for {@code name} are removed. - *

- * If an exception occurs during the translation from type {@code T} all entries with {@code name} may still - * be removed. - * @param name the name of the header to search - * @param defaultValue the default value - * @return the {@code int} value of the first value in insertion order or {@code defaultValue} if there is no - * such value or it can't be converted to {@code int}. - */ - int getIntAndRemove(K name, int defaultValue); - - /** - * Returns the {@code long} value of a header with the specified {@code name} and removes the header from this - * object. If there is more than one value for the specified name, the first value in insertion order is returned. - * In any case all values for {@code name} are removed. - *

- * If an exception occurs during the translation from type {@code T} all entries with {@code name} may still - * be removed. - * @param name the name of the header to search - * @return the {@code long} value of the first value in insertion order or {@code null} if there is no - * such value or it can't be converted to {@code long}. - */ - Long getLongAndRemove(K name); - - /** - * Returns the {@code long} value of a header with the specified {@code name} and removes the header from this - * object. If there is more than one value for the specified name, the first value in insertion order is returned. - * In any case all values for {@code name} are removed. - *

- * If an exception occurs during the translation from type {@code T} all entries with {@code name} may still - * be removed. - * @param name the name of the header to search - * @param defaultValue the default value - * @return the {@code long} value of the first value in insertion order or {@code defaultValue} if there is no - * such value or it can't be converted to {@code long}. - */ - long getLongAndRemove(K name, long defaultValue); - - /** - * Returns the {@code float} value of a header with the specified {@code name} and removes the header from this - * object. If there is more than one value for the specified name, the first value in insertion order is returned. - * In any case all values for {@code name} are removed. - *

- * If an exception occurs during the translation from type {@code T} all entries with {@code name} may still - * be removed. - * @param name the name of the header to search - * @return the {@code float} value of the first value in insertion order or {@code null} if there is no - * such value or it can't be converted to {@code float}. - */ - Float getFloatAndRemove(K name); - - /** - * Returns the {@code float} value of a header with the specified {@code name} and removes the header from this - * object. If there is more than one value for the specified name, the first value in insertion order is returned. - * In any case all values for {@code name} are removed. - *

- * If an exception occurs during the translation from type {@code T} all entries with {@code name} may still - * be removed. - * @param name the name of the header to search - * @param defaultValue the default value - * @return the {@code float} value of the first value in insertion order or {@code defaultValue} if there is no - * such value or it can't be converted to {@code float}. - */ - float getFloatAndRemove(K name, float defaultValue); - - /** - * Returns the {@code double} value of a header with the specified {@code name} and removes the header from this - * object. If there is more than one value for the specified name, the first value in insertion order is returned. - * In any case all values for {@code name} are removed. - *

- * If an exception occurs during the translation from type {@code T} all entries with {@code name} may still - * be removed. - * @param name the name of the header to search - * @return the {@code double} value of the first value in insertion order or {@code null} if there is no - * such value or it can't be converted to {@code double}. - */ - Double getDoubleAndRemove(K name); - - /** - * Returns the {@code double} value of a header with the specified {@code name} and removes the header from this - * object. If there is more than one value for the specified name, the first value in insertion order is returned. - * In any case all values for {@code name} are removed. - *

- * If an exception occurs during the translation from type {@code T} all entries with {@code name} may still - * be removed. - * @param name the name of the header to search - * @param defaultValue the default value - * @return the {@code double} value of the first value in insertion order or {@code defaultValue} if there is no - * such value or it can't be converted to {@code double}. - */ - double getDoubleAndRemove(K name, double defaultValue); - - /** - * Returns the value of a header with the specified {@code name} in milliseconds and removes the header from this - * object. If there is more than one value for the specified {@code name}, the first value in insertion order is - * returned. In any case all values for {@code name} are removed. - *

- * If an exception occurs during the translation from type {@code T} all entries with {@code name} may still - * be removed. - * @param name the name of the header to retrieve - * @return the milliseconds value of the first value in insertion order or {@code null} if there is no such - * value or it can't be converted to milliseconds. - */ - Long getTimeMillisAndRemove(K name); - - /** - * Returns the value of a header with the specified {@code name} in milliseconds and removes the header from this - * object. If there is more than one value for the specified {@code name}, the first value in insertion order is - * returned. In any case all values for {@code name} are removed. - *

- * If an exception occurs during the translation from type {@code T} all entries with {@code name} may still - * be removed. - * @param name the name of the header to retrieve - * @param defaultValue the default value - * @return the milliseconds value of the first value in insertion order or {@code defaultValue} if there is no such - * value or it can't be converted to milliseconds. - */ - long getTimeMillisAndRemove(K name, long defaultValue); - - /** - * Returns {@code true} if a header with the {@code name} exists, {@code false} otherwise. - * - * @param name the header name - */ - boolean contains(K name); - - /** - * Returns {@code true} if a header with the {@code name} and {@code value} exists, {@code false} otherwise. - *

- * The {@link Object#equals(Object)} method is used to test for equality of {@code value}. - *

- * @param name the header name - * @param value the header value of the header to find - */ - boolean contains(K name, V value); - - /** - * Returns {@code true} if a header with the name and value exists. - * - * @param name the header name - * @param value the header value - * @return {@code true} if it contains it {@code false} otherwise - */ - boolean containsObject(K name, Object value); - - /** - * Returns {@code true} if a header with the name and value exists. - * - * @param name the header name - * @param value the header value - * @return {@code true} if it contains it {@code false} otherwise - */ - boolean containsBoolean(K name, boolean value); - - /** - * Returns {@code true} if a header with the name and value exists. - * - * @param name the header name - * @param value the header value - * @return {@code true} if it contains it {@code false} otherwise - */ - boolean containsByte(K name, byte value); - - /** - * Returns {@code true} if a header with the name and value exists. - * - * @param name the header name - * @param value the header value - * @return {@code true} if it contains it {@code false} otherwise - */ - boolean containsChar(K name, char value); - - /** - * Returns {@code true} if a header with the name and value exists. - * - * @param name the header name - * @param value the header value - * @return {@code true} if it contains it {@code false} otherwise - */ - boolean containsShort(K name, short value); - - /** - * Returns {@code true} if a header with the name and value exists. - * - * @param name the header name - * @param value the header value - * @return {@code true} if it contains it {@code false} otherwise - */ - boolean containsInt(K name, int value); - - /** - * Returns {@code true} if a header with the name and value exists. - * - * @param name the header name - * @param value the header value - * @return {@code true} if it contains it {@code false} otherwise - */ - boolean containsLong(K name, long value); - - /** - * Returns {@code true} if a header with the name and value exists. - * - * @param name the header name - * @param value the header value - * @return {@code true} if it contains it {@code false} otherwise - */ - boolean containsFloat(K name, float value); - - /** - * Returns {@code true} if a header with the name and value exists. - * - * @param name the header name - * @param value the header value - * @return {@code true} if it contains it {@code false} otherwise - */ - boolean containsDouble(K name, double value); - - /** - * Returns {@code true} if a header with the name and value exists. - * - * @param name the header name - * @param value the header value - * @return {@code true} if it contains it {@code false} otherwise - */ - boolean containsTimeMillis(K name, long value); - - /** - * Returns the number of headers in this object. - */ - int size(); - - /** - * Returns {@code true} if {@link #size()} equals {@code 0}. - */ - boolean isEmpty(); - - /** - * Returns a {@link Set} of all header names in this object. The returned {@link Set} cannot be modified. - */ - Set names(); - - /** - * Adds a new header with the specified {@code name} and {@code value}. - * - * @param name the name of the header - * @param value the value of the header - * @return {@code this} - */ - T add(K name, V value); - - /** - * Adds new headers with the specified {@code name} and {@code values}. This method is semantically equivalent to - * - *
-     * for (T value : values) {
-     *     headers.add(name, value);
-     * }
-     * 
- * - * @param name the header name - * @param values the values of the header - * @return {@code this} - */ - T add(K name, Iterable values); - - /** - * Adds new headers with the specified {@code name} and {@code values}. This method is semantically equivalent to - * - *
-     * for (T value : values) {
-     *     headers.add(name, value);
-     * }
-     * 
- * - * @param name the header name - * @param values the values of the header - * @return {@code this} - */ - T add(K name, V... values); - - /** - * Adds a new header. Before the {@code value} is added, it's converted to type {@code T}. - * - * @param name the header name - * @param value the value of the header - * @return {@code this} - */ - T addObject(K name, Object value); - - /** - * Adds a new header with the specified name and values. This method is equivalent to - * - *
-     * for (Object v : values) {
-     *     headers.addObject(name, v);
-     * }
-     * 
- * - * @param name the header name - * @param values the value of the header - * @return {@code this} - */ - T addObject(K name, Iterable values); - - /** - * Adds a new header with the specified name and values. This method is equivalent to - * - *
-     * for (Object v : values) {
-     *     headers.addObject(name, v);
-     * }
-     * 
- * - * @param name the header name - * @param values the value of the header - * @return {@code this} - */ - T addObject(K name, Object... values); - - /** - * Adds a new header. - * - * @param name the header name - * @param value the value of the header - * @return {@code this} - */ - T addBoolean(K name, boolean value); - - /** - * Adds a new header. - * - * @param name the header name - * @param value the value of the header - * @return {@code this} - */ - T addByte(K name, byte value); - - /** - * Adds a new header. - * - * @param name the header name - * @param value the value of the header - * @return {@code this} - */ - T addChar(K name, char value); - - /** - * Adds a new header. - * - * @param name the header name - * @param value the value of the header - * @return {@code this} - */ - T addShort(K name, short value); - - /** - * Adds a new header. - * - * @param name the header name - * @param value the value of the header - * @return {@code this} - */ - T addInt(K name, int value); - - /** - * Adds a new header. - * - * @param name the header name - * @param value the value of the header - * @return {@code this} - */ - T addLong(K name, long value); - - /** - * Adds a new header. - * - * @param name the header name - * @param value the value of the header - * @return {@code this} - */ - T addFloat(K name, float value); - - /** - * Adds a new header. - * - * @param name the header name - * @param value the value of the header - * @return {@code this} - */ - T addDouble(K name, double value); - - /** - * Adds a new header. - * - * @param name the header name - * @param value the value of the header - * @return {@code this} - */ - T addTimeMillis(K name, long value); - - /** - * Adds all header names and values of {@code headers} to this object. - * - * @throws IllegalArgumentException if {@code headers == this}. - * @return {@code this} - */ - T add(Headers headers); - - /** - * Sets a header with the specified name and value. Any existing headers with the same name are overwritten. - * - * @param name the header name - * @param value the value of the header - * @return {@code this} - */ - T set(K name, V value); - - /** - * Sets a new header with the specified name and values. This method is equivalent to - * - *
-     * for (T v : values) {
-     *     headers.addObject(name, v);
-     * }
-     * 
- * - * @param name the header name - * @param values the value of the header - * @return {@code this} - */ - T set(K name, Iterable values); - - /** - * Sets a header with the specified name and values. Any existing headers with this name are removed. This method - * is equivalent to: - * - *
-     * headers.remove(name);
-     * for (T v : values) {
-     *     headers.add(name, v);
-     * }
-     * 
- * - * @param name the header name - * @param values the value of the header - * @return {@code this} - */ - T set(K name, V... values); - - /** - * Sets a new header. Any existing headers with this name are removed. Before the {@code value} is add, it's - * converted to type {@code T}. - * - * @param name the header name - * @param value the value of the header - * @throws NullPointerException if either {@code name} or {@code value} before or after its conversion is - * {@code null}. - * @return {@code this} - */ - T setObject(K name, Object value); - - /** - * Sets a header with the specified name and values. Any existing headers with this name are removed. This method - * is equivalent to: - * - *
-     * headers.remove(name);
-     * for (Object v : values) {
-     *     headers.addObject(name, v);
-     * }
-     * 
- * - * @param name the header name - * @param values the values of the header - * @return {@code this} - */ - T setObject(K name, Iterable values); - - /** - * Sets a header with the specified name and values. Any existing headers with this name are removed. This method - * is equivalent to: - * - *
-     * headers.remove(name);
-     * for (Object v : values) {
-     *     headers.addObject(name, v);
-     * }
-     * 
- * - * @param name the header name - * @param values the values of the header - * @return {@code this} - */ - T setObject(K name, Object... values); - - /** - * Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}. - * @param name The name to modify - * @param value The value - * @return {@code this} - */ - T setBoolean(K name, boolean value); - - /** - * Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}. - * @param name The name to modify - * @param value The value - * @return {@code this} - */ - T setByte(K name, byte value); - - /** - * Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}. - * @param name The name to modify - * @param value The value - * @return {@code this} - */ - T setChar(K name, char value); - - /** - * Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}. - * @param name The name to modify - * @param value The value - * @return {@code this} - */ - T setShort(K name, short value); - - /** - * Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}. - * @param name The name to modify - * @param value The value - * @return {@code this} - */ - T setInt(K name, int value); - - /** - * Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}. - * @param name The name to modify - * @param value The value - * @return {@code this} - */ - T setLong(K name, long value); - - /** - * Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}. - * @param name The name to modify - * @param value The value - * @return {@code this} - */ - T setFloat(K name, float value); - - /** - * Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}. - * @param name The name to modify - * @param value The value - * @return {@code this} - */ - T setDouble(K name, double value); - - /** - * Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}. - * @param name The name to modify - * @param value The value - * @return {@code this} - */ - T setTimeMillis(K name, long value); - - /** - * Clears the current header entries and copies all header entries of the specified {@code headers}. - * - * @return {@code this} - */ - T set(Headers headers); - - /** - * Retains all current headers but calls {@link #set(K, V)} for each entry in {@code headers}. - * - * @param headers The headers used to {@link #set(K, V)} values in this instance - * @return {@code this} - */ - T setAll(Headers headers); - - /** - * Removes all headers with the specified {@code name}. - * - * @param name the header name - * @return {@code true} if at least one entry has been removed. - */ - boolean remove(K name); - - /** - * Removes all headers. After a call to this method {@link #size()} equals {@code 0}. - * - * @return {@code this} - */ - T clear(); - - @Override - Iterator> iterator(); -} diff --git a/codec/src/main/java/io/netty/handler/codec/HeadersUtils.java b/codec/src/main/java/io/netty/handler/codec/HeadersUtils.java deleted file mode 100644 index 54f6101a9c..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/HeadersUtils.java +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import static java.util.Objects.requireNonNull; - -import java.util.AbstractCollection; -import java.util.AbstractList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.Map.Entry; -import java.util.Set; - -/** - * Provides utility methods related to {@link Headers}. - */ -public final class HeadersUtils { - - private HeadersUtils() { - } - - /** - * {@link Headers#get(Object)} and convert each element of {@link List} to a {@link String}. - * @param name the name of the header to retrieve - * @return a {@link List} of header values or an empty {@link List} if no values are found. - */ - public static List getAllAsString(Headers headers, K name) { - final List allNames = headers.getAll(name); - return new AbstractList() { - @Override - public String get(int index) { - V value = allNames.get(index); - return value != null ? value.toString() : null; - } - - @Override - public int size() { - return allNames.size(); - } - }; - } - - /** - * {@link Headers#get(Object)} and convert the result to a {@link String}. - * @param headers the headers to get the {@code name} from - * @param name the name of the header to retrieve - * @return the first header value if the header is found. {@code null} if there's no such entry. - */ - public static String getAsString(Headers headers, K name) { - V orig = headers.get(name); - return orig != null ? orig.toString() : null; - } - - /** - * {@link Headers#iterator()} which converts each {@link Entry}'s key and value to a {@link String}. - */ - public static Iterator> iteratorAsString( - Iterable> headers) { - return new StringEntryIterator(headers.iterator()); - } - - /** - * Helper for implementing toString for {@link DefaultHeaders} and wrappers such as DefaultHttpHeaders. - * @param headersClass the class of headers - * @param headersIt the iterator on the actual headers - * @param size the size of the iterator - * @return a String representation of the headers - */ - public static String toString(Class headersClass, Iterator> headersIt, int size) { - String simpleName = headersClass.getSimpleName(); - if (size == 0) { - return simpleName + "[]"; - } else { - // original capacity assumes 20 chars per headers - StringBuilder sb = new StringBuilder(simpleName.length() + 2 + size * 20) - .append(simpleName) - .append('['); - while (headersIt.hasNext()) { - Entry header = headersIt.next(); - sb.append(header.getKey()).append(": ").append(header.getValue()).append(", "); - } - sb.setLength(sb.length() - 2); - return sb.append(']').toString(); - } - } - - /** - * {@link Headers#names()} and convert each element of {@link Set} to a {@link String}. - * @param headers the headers to get the names from - * @return a {@link Set} of header values or an empty {@link Set} if no values are found. - */ - public static Set namesAsString(Headers headers) { - return new CharSequenceDelegatingStringSet(headers.names()); - } - - private static final class StringEntryIterator implements Iterator> { - private final Iterator> iter; - - StringEntryIterator(Iterator> iter) { - this.iter = iter; - } - - @Override - public boolean hasNext() { - return iter.hasNext(); - } - - @Override - public Entry next() { - return new StringEntry(iter.next()); - } - - @Override - public void remove() { - iter.remove(); - } - } - - private static final class StringEntry implements Entry { - private final Entry entry; - private String name; - private String value; - - StringEntry(Entry entry) { - this.entry = entry; - } - - @Override - public String getKey() { - if (name == null) { - name = entry.getKey().toString(); - } - return name; - } - - @Override - public String getValue() { - if (value == null && entry.getValue() != null) { - value = entry.getValue().toString(); - } - return value; - } - - @Override - public String setValue(String value) { - String old = getValue(); - entry.setValue(value); - return old; - } - - @Override - public String toString() { - return entry.toString(); - } - } - - private static final class StringIterator implements Iterator { - private final Iterator iter; - - StringIterator(Iterator iter) { - this.iter = iter; - } - - @Override - public boolean hasNext() { - return iter.hasNext(); - } - - @Override - public String next() { - T next = iter.next(); - return next != null ? next.toString() : null; - } - - @Override - public void remove() { - iter.remove(); - } - } - - private static final class CharSequenceDelegatingStringSet extends DelegatingStringSet { - CharSequenceDelegatingStringSet(Set allNames) { - super(allNames); - } - - @Override - public boolean add(String e) { - return allNames.add(e); - } - - @Override - public boolean addAll(Collection c) { - return allNames.addAll(c); - } - } - - private abstract static class DelegatingStringSet extends AbstractCollection implements Set { - protected final Set allNames; - - DelegatingStringSet(Set allNames) { - this.allNames = requireNonNull(allNames, "allNames"); - } - - @Override - public int size() { - return allNames.size(); - } - - @Override - public boolean isEmpty() { - return allNames.isEmpty(); - } - - @Override - public boolean contains(Object o) { - return allNames.contains(o.toString()); - } - - @Override - public Iterator iterator() { - return new StringIterator<>(allNames.iterator()); - } - - @Override - public boolean remove(Object o) { - return allNames.remove(o); - } - - @Override - public void clear() { - allNames.clear(); - } - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/LengthFieldBasedFrameDecoder.java b/codec/src/main/java/io/netty/handler/codec/LengthFieldBasedFrameDecoder.java deleted file mode 100644 index f98ec407e8..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/LengthFieldBasedFrameDecoder.java +++ /dev/null @@ -1,511 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import static io.netty.util.internal.ObjectUtil.checkPositive; -import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; -import static java.util.Objects.requireNonNull; - -import java.nio.ByteOrder; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; - -/** - * A decoder that splits the received {@link ByteBuf}s dynamically by the - * value of the length field in the message. It is particularly useful when you - * decode a binary message which has an integer header field that represents the - * length of the message body or the whole message. - *

- * {@link LengthFieldBasedFrameDecoder} has many configuration parameters so - * that it can decode any message with a length field, which is often seen in - * proprietary client-server protocols. Here are some example that will give - * you the basic idea on which option does what. - * - *

2 bytes length field at offset 0, do not strip header

- * - * The value of the length field in this example is 12 (0x0C) which - * represents the length of "HELLO, WORLD". By default, the decoder assumes - * that the length field represents the number of the bytes that follows the - * length field. Therefore, it can be decoded with the simplistic parameter - * combination. - *
- * lengthFieldOffset   = 0
- * lengthFieldLength   = 2
- * lengthAdjustment    = 0
- * initialBytesToStrip = 0 (= do not strip header)
- *
- * BEFORE DECODE (14 bytes)         AFTER DECODE (14 bytes)
- * +--------+----------------+      +--------+----------------+
- * | Length | Actual Content |----->| Length | Actual Content |
- * | 0x000C | "HELLO, WORLD" |      | 0x000C | "HELLO, WORLD" |
- * +--------+----------------+      +--------+----------------+
- * 
- * - *

2 bytes length field at offset 0, strip header

- * - * Because we can get the length of the content by calling - * {@link ByteBuf#readableBytes()}, you might want to strip the length - * field by specifying initialBytesToStrip. In this example, we - * specified 2, that is same with the length of the length field, to - * strip the first two bytes. - *
- * lengthFieldOffset   = 0
- * lengthFieldLength   = 2
- * lengthAdjustment    = 0
- * initialBytesToStrip = 2 (= the length of the Length field)
- *
- * BEFORE DECODE (14 bytes)         AFTER DECODE (12 bytes)
- * +--------+----------------+      +----------------+
- * | Length | Actual Content |----->| Actual Content |
- * | 0x000C | "HELLO, WORLD" |      | "HELLO, WORLD" |
- * +--------+----------------+      +----------------+
- * 
- * - *

2 bytes length field at offset 0, do not strip header, the length field - * represents the length of the whole message

- * - * In most cases, the length field represents the length of the message body - * only, as shown in the previous examples. However, in some protocols, the - * length field represents the length of the whole message, including the - * message header. In such a case, we specify a non-zero - * lengthAdjustment. Because the length value in this example message - * is always greater than the body length by 2, we specify -2 - * as lengthAdjustment for compensation. - *
- * lengthFieldOffset   =  0
- * lengthFieldLength   =  2
- * lengthAdjustment    = -2 (= the length of the Length field)
- * initialBytesToStrip =  0
- *
- * BEFORE DECODE (14 bytes)         AFTER DECODE (14 bytes)
- * +--------+----------------+      +--------+----------------+
- * | Length | Actual Content |----->| Length | Actual Content |
- * | 0x000E | "HELLO, WORLD" |      | 0x000E | "HELLO, WORLD" |
- * +--------+----------------+      +--------+----------------+
- * 
- * - *

3 bytes length field at the end of 5 bytes header, do not strip header

- * - * The following message is a simple variation of the first example. An extra - * header value is prepended to the message. lengthAdjustment is zero - * again because the decoder always takes the length of the prepended data into - * account during frame length calculation. - *
- * lengthFieldOffset   = 2 (= the length of Header 1)
- * lengthFieldLength   = 3
- * lengthAdjustment    = 0
- * initialBytesToStrip = 0
- *
- * BEFORE DECODE (17 bytes)                      AFTER DECODE (17 bytes)
- * +----------+----------+----------------+      +----------+----------+----------------+
- * | Header 1 |  Length  | Actual Content |----->| Header 1 |  Length  | Actual Content |
- * |  0xCAFE  | 0x00000C | "HELLO, WORLD" |      |  0xCAFE  | 0x00000C | "HELLO, WORLD" |
- * +----------+----------+----------------+      +----------+----------+----------------+
- * 
- * - *

3 bytes length field at the beginning of 5 bytes header, do not strip header

- * - * This is an advanced example that shows the case where there is an extra - * header between the length field and the message body. You have to specify a - * positive lengthAdjustment so that the decoder counts the extra - * header into the frame length calculation. - *
- * lengthFieldOffset   = 0
- * lengthFieldLength   = 3
- * lengthAdjustment    = 2 (= the length of Header 1)
- * initialBytesToStrip = 0
- *
- * BEFORE DECODE (17 bytes)                      AFTER DECODE (17 bytes)
- * +----------+----------+----------------+      +----------+----------+----------------+
- * |  Length  | Header 1 | Actual Content |----->|  Length  | Header 1 | Actual Content |
- * | 0x00000C |  0xCAFE  | "HELLO, WORLD" |      | 0x00000C |  0xCAFE  | "HELLO, WORLD" |
- * +----------+----------+----------------+      +----------+----------+----------------+
- * 
- * - *

2 bytes length field at offset 1 in the middle of 4 bytes header, - * strip the first header field and the length field

- * - * This is a combination of all the examples above. There are the prepended - * header before the length field and the extra header after the length field. - * The prepended header affects the lengthFieldOffset and the extra - * header affects the lengthAdjustment. We also specified a non-zero - * initialBytesToStrip to strip the length field and the prepended - * header from the frame. If you don't want to strip the prepended header, you - * could specify 0 for initialBytesToSkip. - *
- * lengthFieldOffset   = 1 (= the length of HDR1)
- * lengthFieldLength   = 2
- * lengthAdjustment    = 1 (= the length of HDR2)
- * initialBytesToStrip = 3 (= the length of HDR1 + LEN)
- *
- * BEFORE DECODE (16 bytes)                       AFTER DECODE (13 bytes)
- * +------+--------+------+----------------+      +------+----------------+
- * | HDR1 | Length | HDR2 | Actual Content |----->| HDR2 | Actual Content |
- * | 0xCA | 0x000C | 0xFE | "HELLO, WORLD" |      | 0xFE | "HELLO, WORLD" |
- * +------+--------+------+----------------+      +------+----------------+
- * 
- * - *

2 bytes length field at offset 1 in the middle of 4 bytes header, - * strip the first header field and the length field, the length field - * represents the length of the whole message

- * - * Let's give another twist to the previous example. The only difference from - * the previous example is that the length field represents the length of the - * whole message instead of the message body, just like the third example. - * We have to count the length of HDR1 and Length into lengthAdjustment. - * Please note that we don't need to take the length of HDR2 into account - * because the length field already includes the whole header length. - *
- * lengthFieldOffset   =  1
- * lengthFieldLength   =  2
- * lengthAdjustment    = -3 (= the length of HDR1 + LEN, negative)
- * initialBytesToStrip =  3
- *
- * BEFORE DECODE (16 bytes)                       AFTER DECODE (13 bytes)
- * +------+--------+------+----------------+      +------+----------------+
- * | HDR1 | Length | HDR2 | Actual Content |----->| HDR2 | Actual Content |
- * | 0xCA | 0x0010 | 0xFE | "HELLO, WORLD" |      | 0xFE | "HELLO, WORLD" |
- * +------+--------+------+----------------+      +------+----------------+
- * 
- * @see LengthFieldPrepender - */ -public class LengthFieldBasedFrameDecoder extends ByteToMessageDecoder { - - private final ByteOrder byteOrder; - private final int maxFrameLength; - private final int lengthFieldOffset; - private final int lengthFieldLength; - private final int lengthFieldEndOffset; - private final int lengthAdjustment; - private final int initialBytesToStrip; - private final boolean failFast; - private boolean discardingTooLongFrame; - private long tooLongFrameLength; - private long bytesToDiscard; - - /** - * Creates a new instance. - * - * @param maxFrameLength - * the maximum length of the frame. If the length of the frame is - * greater than this value, {@link TooLongFrameException} will be - * thrown. - * @param lengthFieldOffset - * the offset of the length field - * @param lengthFieldLength - * the length of the length field - */ - public LengthFieldBasedFrameDecoder( - int maxFrameLength, - int lengthFieldOffset, int lengthFieldLength) { - this(maxFrameLength, lengthFieldOffset, lengthFieldLength, 0, 0); - } - - /** - * Creates a new instance. - * - * @param maxFrameLength - * the maximum length of the frame. If the length of the frame is - * greater than this value, {@link TooLongFrameException} will be - * thrown. - * @param lengthFieldOffset - * the offset of the length field - * @param lengthFieldLength - * the length of the length field - * @param lengthAdjustment - * the compensation value to add to the value of the length field - * @param initialBytesToStrip - * the number of first bytes to strip out from the decoded frame - */ - public LengthFieldBasedFrameDecoder( - int maxFrameLength, - int lengthFieldOffset, int lengthFieldLength, - int lengthAdjustment, int initialBytesToStrip) { - this( - maxFrameLength, - lengthFieldOffset, lengthFieldLength, lengthAdjustment, - initialBytesToStrip, true); - } - - /** - * Creates a new instance. - * - * @param maxFrameLength - * the maximum length of the frame. If the length of the frame is - * greater than this value, {@link TooLongFrameException} will be - * thrown. - * @param lengthFieldOffset - * the offset of the length field - * @param lengthFieldLength - * the length of the length field - * @param lengthAdjustment - * the compensation value to add to the value of the length field - * @param initialBytesToStrip - * the number of first bytes to strip out from the decoded frame - * @param failFast - * If true, a {@link TooLongFrameException} is thrown as - * soon as the decoder notices the length of the frame will exceed - * maxFrameLength regardless of whether the entire frame - * has been read. If false, a {@link TooLongFrameException} - * is thrown after the entire frame that exceeds maxFrameLength - * has been read. - */ - public LengthFieldBasedFrameDecoder( - int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, - int lengthAdjustment, int initialBytesToStrip, boolean failFast) { - this( - ByteOrder.BIG_ENDIAN, maxFrameLength, lengthFieldOffset, lengthFieldLength, - lengthAdjustment, initialBytesToStrip, failFast); - } - - /** - * Creates a new instance. - * - * @param byteOrder - * the {@link ByteOrder} of the length field - * @param maxFrameLength - * the maximum length of the frame. If the length of the frame is - * greater than this value, {@link TooLongFrameException} will be - * thrown. - * @param lengthFieldOffset - * the offset of the length field - * @param lengthFieldLength - * the length of the length field - * @param lengthAdjustment - * the compensation value to add to the value of the length field - * @param initialBytesToStrip - * the number of first bytes to strip out from the decoded frame - * @param failFast - * If true, a {@link TooLongFrameException} is thrown as - * soon as the decoder notices the length of the frame will exceed - * maxFrameLength regardless of whether the entire frame - * has been read. If false, a {@link TooLongFrameException} - * is thrown after the entire frame that exceeds maxFrameLength - * has been read. - */ - public LengthFieldBasedFrameDecoder( - ByteOrder byteOrder, int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, - int lengthAdjustment, int initialBytesToStrip, boolean failFast) { - requireNonNull(byteOrder, "byteOrder"); - - checkPositive(maxFrameLength, "maxFrameLength"); - - checkPositiveOrZero(lengthFieldOffset, "lengthFieldOffset"); - - checkPositiveOrZero(initialBytesToStrip, "initialBytesToStrip"); - - if (lengthFieldOffset > maxFrameLength - lengthFieldLength) { - throw new IllegalArgumentException( - "maxFrameLength (" + maxFrameLength + ") " + - "must be equal to or greater than " + - "lengthFieldOffset (" + lengthFieldOffset + ") + " + - "lengthFieldLength (" + lengthFieldLength + ")."); - } - - this.byteOrder = byteOrder; - this.maxFrameLength = maxFrameLength; - this.lengthFieldOffset = lengthFieldOffset; - this.lengthFieldLength = lengthFieldLength; - this.lengthAdjustment = lengthAdjustment; - lengthFieldEndOffset = lengthFieldOffset + lengthFieldLength; - this.initialBytesToStrip = initialBytesToStrip; - this.failFast = failFast; - } - - @Override - protected final void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - Object decoded = decode0(ctx, in); - if (decoded != null) { - ctx.fireChannelRead(decoded); - } - } - - private void discardingTooLongFrame(ByteBuf in) { - long bytesToDiscard = this.bytesToDiscard; - int localBytesToDiscard = (int) Math.min(bytesToDiscard, in.readableBytes()); - in.skipBytes(localBytesToDiscard); - bytesToDiscard -= localBytesToDiscard; - this.bytesToDiscard = bytesToDiscard; - - failIfNecessary(false); - } - - private static void failOnNegativeLengthField(ByteBuf in, long frameLength, int lengthFieldEndOffset) { - in.skipBytes(lengthFieldEndOffset); - throw new CorruptedFrameException( - "negative pre-adjustment length field: " + frameLength); - } - - private static void failOnFrameLengthLessThanLengthFieldEndOffset(ByteBuf in, - long frameLength, - int lengthFieldEndOffset) { - in.skipBytes(lengthFieldEndOffset); - throw new CorruptedFrameException( - "Adjusted frame length (" + frameLength + ") is less " + - "than lengthFieldEndOffset: " + lengthFieldEndOffset); - } - - private void exceededFrameLength(ByteBuf in, long frameLength) { - long discard = frameLength - in.readableBytes(); - tooLongFrameLength = frameLength; - - if (discard < 0) { - // buffer contains more bytes then the frameLength so we can discard all now - in.skipBytes((int) frameLength); - } else { - // Enter the discard mode and discard everything received so far. - discardingTooLongFrame = true; - bytesToDiscard = discard; - in.skipBytes(in.readableBytes()); - } - failIfNecessary(true); - } - - private static void failOnFrameLengthLessThanInitialBytesToStrip(ByteBuf in, - long frameLength, - int initialBytesToStrip) { - in.skipBytes((int) frameLength); - throw new CorruptedFrameException( - "Adjusted frame length (" + frameLength + ") is less " + - "than initialBytesToStrip: " + initialBytesToStrip); - } - - /** - * Create a frame out of the {@link ByteBuf} and return it. - * - * @param ctx the {@link ChannelHandlerContext} which this {@link ByteToMessageDecoder} belongs to - * @param in the {@link ByteBuf} from which to read data - * @return frame the {@link ByteBuf} which represent the frame or {@code null} if no frame could - * be created. - */ - protected Object decode0(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - if (discardingTooLongFrame) { - discardingTooLongFrame(in); - } - - if (in.readableBytes() < lengthFieldEndOffset) { - return null; - } - - int actualLengthFieldOffset = in.readerIndex() + lengthFieldOffset; - long frameLength = getUnadjustedFrameLength(in, actualLengthFieldOffset, lengthFieldLength, byteOrder); - - if (frameLength < 0) { - failOnNegativeLengthField(in, frameLength, lengthFieldEndOffset); - } - - frameLength += lengthAdjustment + lengthFieldEndOffset; - - if (frameLength < lengthFieldEndOffset) { - failOnFrameLengthLessThanLengthFieldEndOffset(in, frameLength, lengthFieldEndOffset); - } - - if (frameLength > maxFrameLength) { - exceededFrameLength(in, frameLength); - return null; - } - - // never overflows because it's less than maxFrameLength - int frameLengthInt = (int) frameLength; - if (in.readableBytes() < frameLengthInt) { - return null; - } - - if (initialBytesToStrip > frameLengthInt) { - failOnFrameLengthLessThanInitialBytesToStrip(in, frameLength, initialBytesToStrip); - } - in.skipBytes(initialBytesToStrip); - - // extract frame - int readerIndex = in.readerIndex(); - int actualFrameLength = frameLengthInt - initialBytesToStrip; - ByteBuf frame = extractFrame(ctx, in, readerIndex, actualFrameLength); - in.readerIndex(readerIndex + actualFrameLength); - return frame; - } - - /** - * Decodes the specified region of the buffer into an unadjusted frame length. The default implementation is - * capable of decoding the specified region into an unsigned 8/16/24/32/64 bit integer. Override this method to - * decode the length field encoded differently. Note that this method must not modify the state of the specified - * buffer (e.g. {@code readerIndex}, {@code writerIndex}, and the content of the buffer.) - * - * @throws DecoderException if failed to decode the specified region - */ - protected long getUnadjustedFrameLength(ByteBuf buf, int offset, int length, ByteOrder order) { - buf = buf.order(order); - long frameLength; - switch (length) { - case 1: - frameLength = buf.getUnsignedByte(offset); - break; - case 2: - frameLength = buf.getUnsignedShort(offset); - break; - case 3: - frameLength = buf.getUnsignedMedium(offset); - break; - case 4: - frameLength = buf.getUnsignedInt(offset); - break; - case 8: - frameLength = buf.getLong(offset); - break; - default: - throw new DecoderException( - "unsupported lengthFieldLength: " + lengthFieldLength + " (expected: 1, 2, 3, 4, or 8)"); - } - return frameLength; - } - - private void failIfNecessary(boolean firstDetectionOfTooLongFrame) { - if (bytesToDiscard == 0) { - // Reset to the initial state and tell the handlers that - // the frame was too large. - long tooLongFrameLength = this.tooLongFrameLength; - this.tooLongFrameLength = 0; - discardingTooLongFrame = false; - if (!failFast || firstDetectionOfTooLongFrame) { - fail(tooLongFrameLength); - } - } else { - // Keep discarding and notify handlers if necessary. - if (failFast && firstDetectionOfTooLongFrame) { - fail(tooLongFrameLength); - } - } - } - - /** - * Extract the sub-region of the specified buffer. - */ - protected ByteBuf extractFrame(ChannelHandlerContext ctx, ByteBuf buffer, int index, int length) { - return buffer.retainedSlice(index, length); - } - - private void fail(long frameLength) { - if (frameLength > 0) { - throw new TooLongFrameException( - "Adjusted frame length exceeds " + maxFrameLength + - ": " + frameLength + " - discarded"); - } else { - throw new TooLongFrameException( - "Adjusted frame length exceeds " + maxFrameLength + - " - discarding"); - } - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/LengthFieldPrepender.java b/codec/src/main/java/io/netty/handler/codec/LengthFieldPrepender.java deleted file mode 100644 index bf5743f18a..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/LengthFieldPrepender.java +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; - -import java.nio.ByteOrder; -import java.util.List; - - -/** - * An encoder that prepends the length of the message. The length value is - * prepended as a binary form. - *

- * For example, {@link LengthFieldPrepender}(2) will encode the - * following 12-bytes string: - *

- * +----------------+
- * | "HELLO, WORLD" |
- * +----------------+
- * 
- * into the following: - *
- * +--------+----------------+
- * + 0x000C | "HELLO, WORLD" |
- * +--------+----------------+
- * 
- * If you turned on the {@code lengthIncludesLengthFieldLength} flag in the - * constructor, the encoded data would look like the following - * (12 (original data) + 2 (prepended data) = 14 (0xE)): - *
- * +--------+----------------+
- * + 0x000E | "HELLO, WORLD" |
- * +--------+----------------+
- * 
- */ -@Sharable -public class LengthFieldPrepender extends MessageToMessageEncoder { - - private final ByteOrder byteOrder; - private final int lengthFieldLength; - private final boolean lengthIncludesLengthFieldLength; - private final int lengthAdjustment; - - /** - * Creates a new instance. - * - * @param lengthFieldLength the length of the prepended length field. - * Only 1, 2, 3, 4, and 8 are allowed. - * - * @throws IllegalArgumentException - * if {@code lengthFieldLength} is not 1, 2, 3, 4, or 8 - */ - public LengthFieldPrepender(int lengthFieldLength) { - this(lengthFieldLength, false); - } - - /** - * Creates a new instance. - * - * @param lengthFieldLength the length of the prepended length field. - * Only 1, 2, 3, 4, and 8 are allowed. - * @param lengthIncludesLengthFieldLength - * if {@code true}, the length of the prepended - * length field is added to the value of the - * prepended length field. - * - * @throws IllegalArgumentException - * if {@code lengthFieldLength} is not 1, 2, 3, 4, or 8 - */ - public LengthFieldPrepender(int lengthFieldLength, boolean lengthIncludesLengthFieldLength) { - this(lengthFieldLength, 0, lengthIncludesLengthFieldLength); - } - - /** - * Creates a new instance. - * - * @param lengthFieldLength the length of the prepended length field. - * Only 1, 2, 3, 4, and 8 are allowed. - * @param lengthAdjustment the compensation value to add to the value - * of the length field - * - * @throws IllegalArgumentException - * if {@code lengthFieldLength} is not 1, 2, 3, 4, or 8 - */ - public LengthFieldPrepender(int lengthFieldLength, int lengthAdjustment) { - this(lengthFieldLength, lengthAdjustment, false); - } - - /** - * Creates a new instance. - * - * @param lengthFieldLength the length of the prepended length field. - * Only 1, 2, 3, 4, and 8 are allowed. - * @param lengthAdjustment the compensation value to add to the value - * of the length field - * @param lengthIncludesLengthFieldLength - * if {@code true}, the length of the prepended - * length field is added to the value of the - * prepended length field. - * - * @throws IllegalArgumentException - * if {@code lengthFieldLength} is not 1, 2, 3, 4, or 8 - */ - public LengthFieldPrepender(int lengthFieldLength, int lengthAdjustment, boolean lengthIncludesLengthFieldLength) { - this(ByteOrder.BIG_ENDIAN, lengthFieldLength, lengthAdjustment, lengthIncludesLengthFieldLength); - } - - /** - * Creates a new instance. - * - * @param byteOrder the {@link ByteOrder} of the length field - * @param lengthFieldLength the length of the prepended length field. - * Only 1, 2, 3, 4, and 8 are allowed. - * @param lengthAdjustment the compensation value to add to the value - * of the length field - * @param lengthIncludesLengthFieldLength - * if {@code true}, the length of the prepended - * length field is added to the value of the - * prepended length field. - * - * @throws IllegalArgumentException - * if {@code lengthFieldLength} is not 1, 2, 3, 4, or 8 - */ - public LengthFieldPrepender( - ByteOrder byteOrder, int lengthFieldLength, - int lengthAdjustment, boolean lengthIncludesLengthFieldLength) { - if (lengthFieldLength != 1 && lengthFieldLength != 2 && - lengthFieldLength != 3 && lengthFieldLength != 4 && - lengthFieldLength != 8) { - throw new IllegalArgumentException( - "lengthFieldLength must be either 1, 2, 3, 4, or 8: " + - lengthFieldLength); - } - requireNonNull(byteOrder, "byteOrder"); - - this.byteOrder = byteOrder; - this.lengthFieldLength = lengthFieldLength; - this.lengthIncludesLengthFieldLength = lengthIncludesLengthFieldLength; - this.lengthAdjustment = lengthAdjustment; - } - - @Override - protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List out) throws Exception { - int length = msg.readableBytes() + lengthAdjustment; - if (lengthIncludesLengthFieldLength) { - length += lengthFieldLength; - } - - checkPositiveOrZero(length, "length"); - - switch (lengthFieldLength) { - case 1: - if (length >= 256) { - throw new IllegalArgumentException( - "length does not fit into a byte: " + length); - } - out.add(ctx.alloc().buffer(1).order(byteOrder).writeByte((byte) length)); - break; - case 2: - if (length >= 65536) { - throw new IllegalArgumentException( - "length does not fit into a short integer: " + length); - } - out.add(ctx.alloc().buffer(2).order(byteOrder).writeShort((short) length)); - break; - case 3: - if (length >= 16777216) { - throw new IllegalArgumentException( - "length does not fit into a medium integer: " + length); - } - out.add(ctx.alloc().buffer(3).order(byteOrder).writeMedium(length)); - break; - case 4: - out.add(ctx.alloc().buffer(4).order(byteOrder).writeInt(length)); - break; - case 8: - out.add(ctx.alloc().buffer(8).order(byteOrder).writeLong(length)); - break; - default: - throw new Error("should not reach here"); - } - out.add(msg.retain()); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/LineBasedFrameDecoder.java b/codec/src/main/java/io/netty/handler/codec/LineBasedFrameDecoder.java deleted file mode 100644 index 7faa5b0078..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/LineBasedFrameDecoder.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.ByteProcessor; - -/** - * 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 { - - /** Maximum length of a frame we're willing to decode. */ - private final int maxLength; - /** Whether or not to throw an exception as soon as we exceed maxLength. */ - private final boolean failFast; - private final boolean stripDelimiter; - - /** True if we're discarding input because we're already over maxLength. */ - private boolean discarding; - private int discardedBytes; - - /** Last scan position. */ - private int offset; - - /** - * Creates a new decoder. - * @param maxLength the maximum length of the decoded frame. - * A {@link TooLongFrameException} is thrown if - * the length of the frame exceeds this value. - */ - public LineBasedFrameDecoder(final int maxLength) { - this(maxLength, true, false); - } - - /** - * Creates a new decoder. - * @param maxLength the maximum length of the decoded frame. - * A {@link TooLongFrameException} is thrown if - * the length of the frame exceeds this value. - * @param stripDelimiter whether the decoded frame should strip out the - * delimiter or not - * @param failFast If true, a {@link TooLongFrameException} is - * thrown as soon as the decoder notices the length of the - * frame will exceed maxFrameLength regardless of - * whether the entire frame has been read. - * If false, a {@link TooLongFrameException} is - * thrown after the entire frame that exceeds - * maxFrameLength has been read. - */ - public LineBasedFrameDecoder(final int maxLength, final boolean stripDelimiter, final boolean failFast) { - this.maxLength = maxLength; - this.failFast = failFast; - this.stripDelimiter = stripDelimiter; - } - - @Override - protected final void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - Object decoded = decode0(ctx, in); - if (decoded != null) { - ctx.fireChannelRead(decoded); - } - } - - /** - * Create a frame out of the {@link ByteBuf} and return it. - * - * @param ctx the {@link ChannelHandlerContext} which this {@link ByteToMessageDecoder} belongs to - * @param buffer the {@link ByteBuf} from which to read data - * @return frame the {@link ByteBuf} which represent the frame or {@code null} if no frame could - * be created. - */ - protected Object decode0(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception { - final int eol = findEndOfLine(buffer); - if (!discarding) { - if (eol >= 0) { - final ByteBuf frame; - final int length = eol - buffer.readerIndex(); - final int delimLength = buffer.getByte(eol) == '\r'? 2 : 1; - - if (length > maxLength) { - buffer.readerIndex(eol + delimLength); - fail(ctx, length); - return null; - } - - if (stripDelimiter) { - frame = buffer.readRetainedSlice(length); - buffer.skipBytes(delimLength); - } else { - frame = buffer.readRetainedSlice(length + delimLength); - } - - return frame; - } else { - final int length = buffer.readableBytes(); - if (length > maxLength) { - discardedBytes = length; - buffer.readerIndex(buffer.writerIndex()); - discarding = true; - offset = 0; - if (failFast) { - fail(ctx, "over " + discardedBytes); - } - } - return null; - } - } else { - if (eol >= 0) { - final int length = discardedBytes + eol - buffer.readerIndex(); - final int delimLength = buffer.getByte(eol) == '\r'? 2 : 1; - buffer.readerIndex(eol + delimLength); - discardedBytes = 0; - discarding = false; - if (!failFast) { - fail(ctx, length); - } - } 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; - } - } - - private void fail(final ChannelHandlerContext ctx, int length) { - fail(ctx, String.valueOf(length)); - } - - private void fail(final ChannelHandlerContext ctx, String length) { - ctx.fireExceptionCaught( - new TooLongFrameException( - "frame length (" + length + ") exceeds the allowed maximum (" + maxLength + ')')); - } - - /** - * Returns the index in the buffer of the end of line found. - * Returns -1 if no end of line was found in the buffer. - */ - private int findEndOfLine(final ByteBuf buffer) { - int totalLength = buffer.readableBytes(); - int i = buffer.forEachByte(buffer.readerIndex() + offset, totalLength - offset, ByteProcessor.FIND_LF); - if (i >= 0) { - offset = 0; - if (i > 0 && buffer.getByte(i - 1) == '\r') { - i--; - } - } else { - offset = totalLength; - } - return i; - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/MessageAggregationException.java b/codec/src/main/java/io/netty/handler/codec/MessageAggregationException.java deleted file mode 100644 index cc96847f5f..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/MessageAggregationException.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec; - -/** - * Raised by {@link MessageAggregator} when aggregation fails due to an unexpected message sequence. - */ -public class MessageAggregationException extends IllegalStateException { - - private static final long serialVersionUID = -1995826182950310255L; - - public MessageAggregationException() { } - - public MessageAggregationException(String s) { - super(s); - } - - public MessageAggregationException(String message, Throwable cause) { - super(message, cause); - } - - public MessageAggregationException(Throwable cause) { - super(cause); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/MessageAggregator.java b/codec/src/main/java/io/netty/handler/codec/MessageAggregator.java deleted file mode 100644 index e83eb7e27f..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/MessageAggregator.java +++ /dev/null @@ -1,464 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufHolder; -import io.netty.buffer.CompositeByteBuf; -import io.netty.channel.ChannelFutureListeners; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.FutureContextListener; - -import static io.netty.buffer.Unpooled.EMPTY_BUFFER; -import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; - -/** - * An abstract {@link ChannelHandler} that aggregates a series of message objects into a single aggregated message. - *

- * 'A series of messages' is composed of the following: - *

    - *
  • a single start message which optionally contains the first part of the content, and
  • - *
  • 1 or more content messages.
  • - *
- * The content of the aggregated message will be the merged content of the start message and its following content - * messages. If this aggregator encounters a content message where {@link #isLastContentMessage(ByteBufHolder)} - * return {@code true} for, the aggregator will finish the aggregation and produce the aggregated message and expect - * another start message. - *

- * - * @param the type that covers both start message and content message - * @param the type of the start message - * @param the type of the content message (must be a subtype of {@link ByteBufHolder}) - * @param the type of the aggregated message (must be a subtype of {@code S} and {@link ByteBufHolder}) - */ -public abstract class MessageAggregator - extends MessageToMessageDecoder { - - private static final int DEFAULT_MAX_COMPOSITEBUFFER_COMPONENTS = 1024; - - private final int maxContentLength; - private O currentMessage; - private boolean handlingOversizedMessage; - - private int maxCumulationBufferComponents = DEFAULT_MAX_COMPOSITEBUFFER_COMPONENTS; - private ChannelHandlerContext ctx; - private FutureContextListener continueResponseWriteListener; - - private boolean aggregating; - - /** - * Creates a new instance. - * - * @param maxContentLength - * the maximum length of the aggregated content. - * If the length of the aggregated content exceeds this value, - * {@link #handleOversizedMessage(ChannelHandlerContext, Object)} will be called. - */ - protected MessageAggregator(int maxContentLength) { - validateMaxContentLength(maxContentLength); - this.maxContentLength = maxContentLength; - } - - protected MessageAggregator(int maxContentLength, Class inboundMessageType) { - super(inboundMessageType); - validateMaxContentLength(maxContentLength); - this.maxContentLength = maxContentLength; - } - - private static void validateMaxContentLength(int maxContentLength) { - checkPositiveOrZero(maxContentLength, "maxContentLength"); - } - - @Override - public boolean acceptInboundMessage(Object msg) throws Exception { - // No need to match last and full types because they are subset of first and middle types. - if (!super.acceptInboundMessage(msg)) { - return false; - } - - @SuppressWarnings("unchecked") - I in = (I) msg; - - if (isAggregated(in)) { - return false; - } - - // NOTE: It's tempting to make this check only if aggregating is false. There are however - // side conditions in decode(...) in respect to large messages. - if (isStartMessage(in)) { - aggregating = true; - return true; - } else if (aggregating && isContentMessage(in)) { - return true; - } - - return false; - } - - /** - * Returns {@code true} if and only if the specified message is a start message. Typically, this method is - * implemented as a single {@code return} statement with {@code instanceof}: - *
-     * return msg instanceof MyStartMessage;
-     * 
- */ - protected abstract boolean isStartMessage(I msg) throws Exception; - - /** - * Returns {@code true} if and only if the specified message is a content message. Typically, this method is - * implemented as a single {@code return} statement with {@code instanceof}: - *
-     * return msg instanceof MyContentMessage;
-     * 
- */ - protected abstract boolean isContentMessage(I msg) throws Exception; - - /** - * Returns {@code true} if and only if the specified message is the last content message. Typically, this method is - * implemented as a single {@code return} statement with {@code instanceof}: - *
-     * return msg instanceof MyLastContentMessage;
-     * 
- * or with {@code instanceof} and boolean field check: - *
-     * return msg instanceof MyContentMessage && msg.isLastFragment();
-     * 
- */ - protected abstract boolean isLastContentMessage(C msg) throws Exception; - - /** - * Returns {@code true} if and only if the specified message is already aggregated. If this method returns - * {@code true}, this handler will simply forward the message to the next handler as-is. - */ - protected abstract boolean isAggregated(I msg) throws Exception; - - /** - * Returns the maximum allowed length of the aggregated message in bytes. - */ - public final int maxContentLength() { - return maxContentLength; - } - - /** - * Returns the maximum number of components in the cumulation buffer. If the number of - * the components in the cumulation buffer exceeds this value, the components of the - * cumulation buffer are consolidated into a single component, involving memory copies. - * The default value of this property is {@value #DEFAULT_MAX_COMPOSITEBUFFER_COMPONENTS}. - */ - public final int maxCumulationBufferComponents() { - return maxCumulationBufferComponents; - } - - /** - * Sets the maximum number of components in the cumulation buffer. If the number of - * the components in the cumulation buffer exceeds this value, the components of the - * cumulation buffer are consolidated into a single component, involving memory copies. - * The default value of this property is {@value #DEFAULT_MAX_COMPOSITEBUFFER_COMPONENTS} - * and its minimum allowed value is {@code 2}. - */ - public final void setMaxCumulationBufferComponents(int maxCumulationBufferComponents) { - if (maxCumulationBufferComponents < 2) { - throw new IllegalArgumentException( - "maxCumulationBufferComponents: " + maxCumulationBufferComponents + - " (expected: >= 2)"); - } - - if (ctx == null) { - this.maxCumulationBufferComponents = maxCumulationBufferComponents; - } else { - throw new IllegalStateException( - "decoder properties cannot be changed once the decoder is added to a pipeline."); - } - } - - /** - * @deprecated This method will be removed in future releases. - */ - @Deprecated - public final boolean isHandlingOversizedMessage() { - return handlingOversizedMessage; - } - - protected final ChannelHandlerContext ctx() { - if (ctx == null) { - throw new IllegalStateException("not added to a pipeline yet"); - } - return ctx; - } - - @Override - protected void decode(final ChannelHandlerContext ctx, I msg) throws Exception { - assert aggregating; - if (isStartMessage(msg)) { - handlingOversizedMessage = false; - if (currentMessage != null) { - currentMessage.release(); - currentMessage = null; - throw new MessageAggregationException(); - } - - @SuppressWarnings("unchecked") - S m = (S) msg; - - // Send the continue response if necessary (e.g. 'Expect: 100-continue' header) - // Check before content length. Failing an expectation may result in a different response being sent. - Object continueResponse = newContinueResponse(m, maxContentLength, ctx.pipeline()); - if (continueResponse != null) { - // Cache the write listener for reuse. - FutureContextListener listener = continueResponseWriteListener; - if (listener == null) { - continueResponseWriteListener = listener = (context, future) -> { - if (future.isFailed()) { - context.fireExceptionCaught(future.cause()); - } - }; - } - - // Make sure to call this before writing, otherwise reference counts may be invalid. - boolean closeAfterWrite = closeAfterContinueResponse(continueResponse); - handlingOversizedMessage = ignoreContentAfterContinueResponse(continueResponse); - - Future future = ctx.writeAndFlush(continueResponse).addListener(ctx, listener); - - if (closeAfterWrite) { - future.addListener(ctx, ChannelFutureListeners.CLOSE); - return; - } - if (handlingOversizedMessage) { - return; - } - } else if (isContentLengthInvalid(m, maxContentLength)) { - // if content length is set, preemptively close if it's too large - invokeHandleOversizedMessage(ctx, m); - return; - } - - if (m instanceof DecoderResultProvider && !((DecoderResultProvider) m).decoderResult().isSuccess()) { - O aggregated; - if (m instanceof ByteBufHolder) { - aggregated = beginAggregation(m, ((ByteBufHolder) m).content().retain()); - } else { - aggregated = beginAggregation(m, EMPTY_BUFFER); - } - finishAggregation(aggregated); - ctx.fireChannelRead(aggregated); - return; - } - - // A streamed message - initialize the cumulative buffer, and wait for incoming chunks. - CompositeByteBuf content = ctx.alloc().compositeBuffer(maxCumulationBufferComponents); - if (m instanceof ByteBufHolder) { - appendPartialContent(content, ((ByteBufHolder) m).content()); - } - currentMessage = beginAggregation(m, content); - } else if (isContentMessage(msg)) { - if (currentMessage == null) { - // it is possible that a TooLongFrameException was already thrown but we can still discard data - // until the begging of the next request/response. - return; - } - - // Merge the received chunk into the content of the current message. - CompositeByteBuf content = (CompositeByteBuf) currentMessage.content(); - - @SuppressWarnings("unchecked") - final C m = (C) msg; - // Handle oversized message. - if (content.readableBytes() > maxContentLength - m.content().readableBytes()) { - // By convention, full message type extends first message type. - @SuppressWarnings("unchecked") - S s = (S) currentMessage; - invokeHandleOversizedMessage(ctx, s); - return; - } - - // Append the content of the chunk. - appendPartialContent(content, m.content()); - - // Give the subtypes a chance to merge additional information such as trailing headers. - aggregate(currentMessage, m); - - final boolean last; - if (m instanceof DecoderResultProvider) { - DecoderResult decoderResult = ((DecoderResultProvider) m).decoderResult(); - if (!decoderResult.isSuccess()) { - if (currentMessage instanceof DecoderResultProvider) { - ((DecoderResultProvider) currentMessage).setDecoderResult( - DecoderResult.failure(decoderResult.cause())); - } - last = true; - } else { - last = isLastContentMessage(m); - } - } else { - last = isLastContentMessage(m); - } - - if (last) { - finishAggregation0(currentMessage); - - // All done - O message = currentMessage; - currentMessage = null; - ctx.fireChannelRead(message); - } - } else { - throw new MessageAggregationException(); - } - } - - private static void appendPartialContent(CompositeByteBuf content, ByteBuf partialContent) { - if (partialContent.isReadable()) { - content.addComponent(true, partialContent.retain()); - } - } - - /** - * Determine if the message {@code start}'s content length is known, and if it greater than - * {@code maxContentLength}. - * @param start The message which may indicate the content length. - * @param maxContentLength The maximum allowed content length. - * @return {@code true} if the message {@code start}'s content length is known, and if it greater than - * {@code maxContentLength}. {@code false} otherwise. - */ - protected abstract boolean isContentLengthInvalid(S start, int maxContentLength) throws Exception; - - /** - * Returns the 'continue response' for the specified start message if necessary. For example, this method is - * useful to handle an HTTP 100-continue header. - * - * @return the 'continue response', or {@code null} if there's no message to send - */ - protected abstract Object newContinueResponse(S start, int maxContentLength, ChannelPipeline pipeline) - throws Exception; - - /** - * Determine if the channel should be closed after the result of - * {@link #newContinueResponse(Object, int, ChannelPipeline)} is written. - * @param msg The return value from {@link #newContinueResponse(Object, int, ChannelPipeline)}. - * @return {@code true} if the channel should be closed after the result of - * {@link #newContinueResponse(Object, int, ChannelPipeline)} is written. {@code false} otherwise. - */ - protected abstract boolean closeAfterContinueResponse(Object msg) throws Exception; - - /** - * Determine if all objects for the current request/response should be ignored or not. - * Messages will stop being ignored the next time {@link #isContentMessage(Object)} returns {@code true}. - * - * @param msg The return value from {@link #newContinueResponse(Object, int, ChannelPipeline)}. - * @return {@code true} if all objects for the current request/response should be ignored or not. - * {@code false} otherwise. - */ - protected abstract boolean ignoreContentAfterContinueResponse(Object msg) throws Exception; - - /** - * Creates a new aggregated message from the specified start message and the specified content. If the start - * message implements {@link ByteBufHolder}, its content is appended to the specified {@code content}. - * This aggregator will continue to append the received content to the specified {@code content}. - */ - protected abstract O beginAggregation(S start, ByteBuf content) throws Exception; - - /** - * Transfers the information provided by the specified content message to the specified aggregated message. - * Note that the content of the specified content message has been appended to the content of the specified - * aggregated message already, so that you don't need to. Use this method to transfer the additional information - * that the content message provides to {@code aggregated}. - */ - 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. - */ - protected void finishAggregation(O aggregated) throws Exception { } - - private void invokeHandleOversizedMessage(ChannelHandlerContext ctx, S oversized) throws Exception { - handlingOversizedMessage = true; - currentMessage = null; - try { - handleOversizedMessage(ctx, oversized); - } finally { - // Release the message in case it is a full one. - ReferenceCountUtil.release(oversized); - } - } - - /** - * Invoked when an incoming request exceeds the maximum content length. The default behvaior is to trigger an - * {@code exceptionCaught()} event with a {@link TooLongFrameException}. - * - * @param ctx the {@link ChannelHandlerContext} - * @param oversized the accumulated message up to this point, whose type is {@code S} or {@code O} - */ - protected void handleOversizedMessage(ChannelHandlerContext ctx, S oversized) throws Exception { - ctx.fireExceptionCaught( - new TooLongFrameException("content length exceeded " + maxContentLength() + " bytes.")); - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { - // We might need keep reading the channel until the full message is aggregated. - // - // See https://github.com/netty/netty/issues/6583 - if (currentMessage != null && !ctx.channel().config().isAutoRead()) { - ctx.read(); - } - ctx.fireChannelReadComplete(); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - try { - // release current message if it is not null as it may be a left-over - super.channelInactive(ctx); - } finally { - releaseCurrentMessage(); - } - } - - @Override - public void handlerAdded(ChannelHandlerContext ctx) throws Exception { - this.ctx = ctx; - } - - @Override - public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { - try { - super.handlerRemoved(ctx); - } finally { - // release current message if it is not null as it may be a left-over as there is not much more we can do in - // this case - releaseCurrentMessage(); - } - } - - private void releaseCurrentMessage() { - if (currentMessage != null) { - currentMessage.release(); - currentMessage = null; - handlingOversizedMessage = false; - aggregating = false; - } - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/MessageToByteEncoder.java b/codec/src/main/java/io/netty/handler/codec/MessageToByteEncoder.java deleted file mode 100644 index b1bb21d166..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/MessageToByteEncoder.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerAdapter; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.concurrent.Future; -import io.netty.util.internal.TypeParameterMatcher; - - -/** - * {@link ChannelHandler} which encodes message in a stream-like fashion from one message to an - * {@link ByteBuf}. - * - * - * Example implementation which encodes {@link Integer}s to a {@link ByteBuf}. - * - *
- *     public class IntegerEncoder extends {@link MessageToByteEncoder}<{@link Integer}> {
- *         {@code @Override}
- *         public void encode({@link ChannelHandlerContext} ctx, {@link Integer} msg, {@link ByteBuf} out)
- *                 throws {@link Exception} {
- *             out.writeInt(msg);
- *         }
- *     }
- * 
- */ -public abstract class MessageToByteEncoder extends ChannelHandlerAdapter { - - private final TypeParameterMatcher matcher; - private final boolean preferDirect; - - /** - * see {@link #MessageToByteEncoder(boolean)} with {@code true} as boolean parameter. - */ - protected MessageToByteEncoder() { - this(true); - } - - /** - * see {@link #MessageToByteEncoder(Class, boolean)} with {@code true} as boolean value. - */ - protected MessageToByteEncoder(Class outboundMessageType) { - this(outboundMessageType, true); - } - - /** - * Create a new instance which will try to detect the types to match out of the type parameter of the class. - * - * @param preferDirect {@code true} if a direct {@link ByteBuf} should be tried to be used as target for - * the encoded messages. If {@code false} is used it will allocate a heap - * {@link ByteBuf}, which is backed by an byte array. - */ - protected MessageToByteEncoder(boolean preferDirect) { - matcher = TypeParameterMatcher.find(this, MessageToByteEncoder.class, "I"); - this.preferDirect = preferDirect; - } - - /** - * Create a new instance - * - * @param outboundMessageType The type of messages to match - * @param preferDirect {@code true} if a direct {@link ByteBuf} should be tried to be used as target for - * the encoded messages. If {@code false} is used it will allocate a heap - * {@link ByteBuf}, which is backed by an byte array. - */ - protected MessageToByteEncoder(Class outboundMessageType, boolean preferDirect) { - matcher = TypeParameterMatcher.get(outboundMessageType); - this.preferDirect = preferDirect; - } - - /** - * Returns {@code true} if the given message should be handled. If {@code false} it will be passed to the next - * {@link ChannelHandler} in the {@link ChannelPipeline}. - */ - public boolean acceptOutboundMessage(Object msg) throws Exception { - return matcher.match(msg); - } - - @Override - public Future write(ChannelHandlerContext ctx, Object msg) { - ByteBuf buf = null; - try { - if (acceptOutboundMessage(msg)) { - @SuppressWarnings("unchecked") - I cast = (I) msg; - buf = allocateBuffer(ctx, cast, preferDirect); - try { - encode(ctx, cast, buf); - } finally { - ReferenceCountUtil.release(cast); - } - - if (buf.isReadable()) { - Future f = ctx.write(buf); - buf = null; - return f; - } - return ctx.write(Unpooled.EMPTY_BUFFER); - } - return ctx.write(msg); - } catch (EncoderException e) { - return ctx.newFailedFuture(e); - } catch (Throwable e) { - return ctx.newFailedFuture(new EncoderException(e)); - } finally { - if (buf != null) { - buf.release(); - } - } - } - - /** - * Allocate a {@link ByteBuf} which will be used as argument of {@link #encode(ChannelHandlerContext, I, ByteBuf)}. - * Sub-classes may override this method to return {@link ByteBuf} with a perfect matching {@code initialCapacity}. - */ - protected ByteBuf allocateBuffer(ChannelHandlerContext ctx, @SuppressWarnings("unused") I msg, - boolean preferDirect) throws Exception { - if (preferDirect) { - return ctx.alloc().ioBuffer(); - } else { - return ctx.alloc().heapBuffer(); - } - } - - /** - * Encode a message into a {@link ByteBuf}. This method will be called for each written message that can be handled - * by this encoder. - * - * @param ctx the {@link ChannelHandlerContext} which this {@link MessageToByteEncoder} belongs to - * @param msg the message to encode - * @param out the {@link ByteBuf} into which the encoded message will be written - * @throws Exception is thrown if an error occurs - */ - protected abstract void encode(ChannelHandlerContext ctx, I msg, ByteBuf out) throws Exception; - - protected boolean isPreferDirect() { - return preferDirect; - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/MessageToMessageCodec.java b/codec/src/main/java/io/netty/handler/codec/MessageToMessageCodec.java deleted file mode 100644 index 721f3a3636..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/MessageToMessageCodec.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import io.netty.channel.ChannelHandlerAdapter; -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.ReferenceCounted; -import io.netty.util.concurrent.Future; -import io.netty.util.internal.TypeParameterMatcher; - -import java.util.List; - -/** - * A Codec for on-the-fly encoding/decoding of message. - * - * This can be thought of as a combination of {@link MessageToMessageDecoder} and {@link MessageToMessageEncoder}. - * - * Here is an example of a {@link MessageToMessageCodec} which just decode from {@link Integer} to {@link Long} - * and encode from {@link Long} to {@link Integer}. - * - *
- *     public class NumberCodec extends
- *             {@link MessageToMessageCodec}<{@link Integer}, {@link Long}> {
- *         {@code @Override}
- *         public {@link Long} decode({@link ChannelHandlerContext} ctx, {@link Integer} msg, List<Object> out)
- *                 throws {@link Exception} {
- *             out.add(msg.longValue());
- *         }
- *
- *         {@code @Override}
- *         public {@link Integer} encode({@link ChannelHandlerContext} ctx, {@link Long} msg, List<Object> out)
- *                 throws {@link Exception} {
- *             out.add(msg.intValue());
- *         }
- *     }
- * 
- * - * Be aware that you need to call {@link ReferenceCounted#retain()} on messages that are just passed through if they - * are of type {@link ReferenceCounted}. This is needed as the {@link MessageToMessageCodec} will call - * {@link ReferenceCounted#release()} on encoded / decoded messages. - */ -public abstract class MessageToMessageCodec extends ChannelHandlerAdapter { - - private final MessageToMessageEncoder encoder = new MessageToMessageEncoder() { - - @Override - public boolean acceptOutboundMessage(Object msg) throws Exception { - return MessageToMessageCodec.this.acceptOutboundMessage(msg); - } - - @Override - @SuppressWarnings("unchecked") - protected void encode(ChannelHandlerContext ctx, Object msg, List out) throws Exception { - MessageToMessageCodec.this.encode(ctx, (OUTBOUND_IN) msg, out); - } - }; - - private final MessageToMessageDecoder decoder = new MessageToMessageDecoder() { - - @Override - public boolean acceptInboundMessage(Object msg) throws Exception { - return MessageToMessageCodec.this.acceptInboundMessage(msg); - } - - @Override - @SuppressWarnings("unchecked") - protected void decode(ChannelHandlerContext ctx, Object msg) throws Exception { - MessageToMessageCodec.this.decode(ctx, (INBOUND_IN) msg); - } - }; - - private final TypeParameterMatcher inboundMsgMatcher; - private final TypeParameterMatcher outboundMsgMatcher; - - /** - * Create a new instance which will try to detect the types to decode and encode out of the type parameter - * of the class. - */ - protected MessageToMessageCodec() { - inboundMsgMatcher = TypeParameterMatcher.find(this, MessageToMessageCodec.class, "INBOUND_IN"); - outboundMsgMatcher = TypeParameterMatcher.find(this, MessageToMessageCodec.class, "OUTBOUND_IN"); - } - - /** - * Create a new instance. - * - * @param inboundMessageType The type of messages to decode - * @param outboundMessageType The type of messages to encode - */ - protected MessageToMessageCodec( - Class inboundMessageType, Class outboundMessageType) { - inboundMsgMatcher = TypeParameterMatcher.get(inboundMessageType); - outboundMsgMatcher = TypeParameterMatcher.get(outboundMessageType); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - decoder.channelRead(ctx, msg); - } - - @Override - public Future write(ChannelHandlerContext ctx, Object msg) { - return encoder.write(ctx, msg); - } - - /** - * Returns {@code true} if and only if the specified message can be decoded by this codec. - * - * @param msg the message - */ - public boolean acceptInboundMessage(Object msg) throws Exception { - return inboundMsgMatcher.match(msg); - } - - /** - * Returns {@code true} if and only if the specified message can be encoded by this codec. - * - * @param msg the message - */ - public boolean acceptOutboundMessage(Object msg) throws Exception { - return outboundMsgMatcher.match(msg); - } - - /** - * @see MessageToMessageEncoder#encode(ChannelHandlerContext, Object, List) - */ - protected abstract void encode(ChannelHandlerContext ctx, OUTBOUND_IN msg, List out) - throws Exception; - - /** - * @see MessageToMessageDecoder#decode(ChannelHandlerContext, Object) - */ - protected abstract void decode(ChannelHandlerContext ctx, INBOUND_IN msg) - throws Exception; -} diff --git a/codec/src/main/java/io/netty/handler/codec/MessageToMessageDecoder.java b/codec/src/main/java/io/netty/handler/codec/MessageToMessageDecoder.java deleted file mode 100644 index 032f0d13e2..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/MessageToMessageDecoder.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerAdapter; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.ReferenceCounted; -import io.netty.util.internal.TypeParameterMatcher; - -/** - * {@link ChannelHandler} which decodes from one message to an other message. - * - * - * For example here is an implementation which decodes a {@link String} to an {@link Integer} which represent - * the length of the {@link String}. - * - *
- *     public class StringToIntegerDecoder extends
- *             {@link MessageToMessageDecoder}<{@link String}> {
- *
- *         {@code @Override}
- *         public void decode({@link ChannelHandlerContext} ctx, {@link String} message,
- *                            List<Object> out) throws {@link Exception} {
- *             out.add(message.length());
- *         }
- *     }
- * 
- * - * Be aware that you need to call {@link ReferenceCounted#retain()} on messages that are just passed through if they - * are of type {@link ReferenceCounted}. This is needed as the {@link MessageToMessageDecoder} will call - * {@link ReferenceCounted#release()} on decoded messages. - * - */ -public abstract class MessageToMessageDecoder extends ChannelHandlerAdapter { - - private final TypeParameterMatcher matcher; - - /** - * Create a new instance which will try to detect the types to match out of the type parameter of the class. - */ - protected MessageToMessageDecoder() { - matcher = TypeParameterMatcher.find(this, MessageToMessageDecoder.class, "I"); - } - - /** - * Create a new instance - * - * @param inboundMessageType The type of messages to match and so decode - */ - protected MessageToMessageDecoder(Class inboundMessageType) { - matcher = TypeParameterMatcher.get(inboundMessageType); - } - - /** - * Returns {@code true} if the given message should be handled. If {@code false} it will be passed to the next - * {@link ChannelHandler} in the {@link ChannelPipeline}. - */ - public boolean acceptInboundMessage(Object msg) throws Exception { - return matcher.match(msg); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - try { - if (acceptInboundMessage(msg)) { - @SuppressWarnings("unchecked") - I cast = (I) msg; - try { - decode(ctx, cast); - } finally { - ReferenceCountUtil.release(cast); - } - } else { - ctx.fireChannelRead(msg); - } - } catch (DecoderException e) { - throw e; - } catch (Exception e) { - throw new DecoderException(e); - } - } - - /** - * Decode from one message to an other. This method will be called for each written message that can be handled - * by this decoder. - * - * @param ctx the {@link ChannelHandlerContext} which this {@link MessageToMessageDecoder} belongs to - * @param msg the message to decode to an other one - * @throws Exception is thrown if an error occurs - */ - protected abstract void decode(ChannelHandlerContext ctx, I msg) throws Exception; -} diff --git a/codec/src/main/java/io/netty/handler/codec/MessageToMessageEncoder.java b/codec/src/main/java/io/netty/handler/codec/MessageToMessageEncoder.java deleted file mode 100644 index ded2114c8d..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/MessageToMessageEncoder.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerAdapter; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.ReferenceCounted; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; -import io.netty.util.concurrent.PromiseCombiner; -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.TypeParameterMatcher; - -import java.util.List; - -/** - * {@link ChannelHandler} which encodes from one message to an other message - * - * For example here is an implementation which decodes an {@link Integer} to an {@link String}. - * - *
- *     public class IntegerToStringEncoder extends
- *             {@link MessageToMessageEncoder}<{@link Integer}> {
- *
- *         {@code @Override}
- *         public void encode({@link ChannelHandlerContext} ctx, {@link Integer} message, List<Object> out)
- *                 throws {@link Exception} {
- *             out.add(message.toString());
- *         }
- *     }
- * 
- * - * Be aware that you need to call {@link ReferenceCounted#retain()} on messages that are just passed through if they - * are of type {@link ReferenceCounted}. This is needed as the {@link MessageToMessageEncoder} will call - * {@link ReferenceCounted#release()} on encoded messages. - */ -public abstract class MessageToMessageEncoder extends ChannelHandlerAdapter { - - private final TypeParameterMatcher matcher; - - /** - * Create a new instance which will try to detect the types to match out of the type parameter of the class. - */ - protected MessageToMessageEncoder() { - matcher = TypeParameterMatcher.find(this, MessageToMessageEncoder.class, "I"); - } - - /** - * Create a new instance - * - * @param outboundMessageType The type of messages to match and so encode - */ - protected MessageToMessageEncoder(Class outboundMessageType) { - matcher = TypeParameterMatcher.get(outboundMessageType); - } - - /** - * Returns {@code true} if the given message should be handled. If {@code false} it will be passed to the next - * {@link ChannelHandler} in the {@link ChannelPipeline}. - */ - public boolean acceptOutboundMessage(Object msg) throws Exception { - return matcher.match(msg); - } - - @Override - public Future write(ChannelHandlerContext ctx, Object msg) { - CodecOutputList out = null; - try { - if (acceptOutboundMessage(msg)) { - out = CodecOutputList.newInstance(); - @SuppressWarnings("unchecked") - I cast = (I) msg; - Promise promise = ctx.newPromise(); - try { - try { - encode(ctx, cast, out); - } finally { - ReferenceCountUtil.release(cast); - } - - if (out.isEmpty()) { - throw new EncoderException( - StringUtil.simpleClassName(this) + " must produce at least one message."); - } - } finally { - final int sizeMinusOne = out.size() - 1; - if (sizeMinusOne == 0) { - ctx.write(out.getUnsafe(0)).cascadeTo(promise); - } else { - writePromiseCombiner(ctx, out, promise); - } - } - return promise.asFuture(); - } else { - return ctx.write(msg); - } - } catch (EncoderException e) { - return ctx.newFailedFuture(e); - } catch (Throwable t) { - return ctx.newFailedFuture(new EncoderException(t)); - } finally { - if (out != null) { - out.recycle(); - } - } - } - - private static void writePromiseCombiner(ChannelHandlerContext ctx, CodecOutputList out, Promise promise) { - final PromiseCombiner combiner = new PromiseCombiner(ctx.executor()); - 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. - * - * @param ctx the {@link ChannelHandlerContext} which this {@link MessageToMessageEncoder} belongs to - * @param msg the message to encode to an other one - * @param out the {@link List} into which the encoded msg should be added - * needs to do some kind of aggregation - * @throws Exception is thrown if an error occurs - */ - protected abstract void encode(ChannelHandlerContext ctx, I msg, List out) throws Exception; -} diff --git a/codec/src/main/java/io/netty/handler/codec/PrematureChannelClosureException.java b/codec/src/main/java/io/netty/handler/codec/PrematureChannelClosureException.java deleted file mode 100644 index 680f2d0d91..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/PrematureChannelClosureException.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import io.netty.channel.Channel; - -/** - * A {@link CodecException} which is thrown when a {@link Channel} is closed unexpectedly before - * the codec finishes handling the current message, such as missing response while waiting for a - * request. - */ -public class PrematureChannelClosureException extends CodecException { - - private static final long serialVersionUID = 4907642202594703094L; - - /** - * Creates a new instance. - */ - public PrematureChannelClosureException() { } - - /** - * Creates a new instance. - */ - public PrematureChannelClosureException(String message, Throwable cause) { - super(message, cause); - } - - /** - * Creates a new instance. - */ - public PrematureChannelClosureException(String message) { - super(message); - } - - /** - * Creates a new instance. - */ - public PrematureChannelClosureException(Throwable cause) { - super(cause); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/ProtocolDetectionResult.java b/codec/src/main/java/io/netty/handler/codec/ProtocolDetectionResult.java deleted file mode 100644 index f5c0b0ec29..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/ProtocolDetectionResult.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import static java.util.Objects.requireNonNull; - -/** - * Result of detecting a protocol. - * - * @param the type of the protocol - */ -public final class ProtocolDetectionResult { - - @SuppressWarnings({ "rawtypes", "unchecked" }) - private static final ProtocolDetectionResult NEEDS_MORE_DATA = - new ProtocolDetectionResult(ProtocolDetectionState.NEEDS_MORE_DATA, null); - @SuppressWarnings({ "rawtypes", "unchecked" }) - private static final ProtocolDetectionResult INVALID = - new ProtocolDetectionResult(ProtocolDetectionState.INVALID, null); - - private final ProtocolDetectionState state; - private final T result; - - /** - * Returns a {@link ProtocolDetectionResult} that signals that more data is needed to detect the protocol. - */ - @SuppressWarnings("unchecked") - public static ProtocolDetectionResult needsMoreData() { - return NEEDS_MORE_DATA; - } - - /** - * Returns a {@link ProtocolDetectionResult} that signals the data was invalid for the protocol. - */ - @SuppressWarnings("unchecked") - public static ProtocolDetectionResult invalid() { - return INVALID; - } - - /** - * Returns a {@link ProtocolDetectionResult} which holds the detected protocol. - */ - @SuppressWarnings("unchecked") - public static ProtocolDetectionResult detected(T protocol) { - return new ProtocolDetectionResult<>(ProtocolDetectionState.DETECTED, requireNonNull(protocol, "protocol")); - } - - private ProtocolDetectionResult(ProtocolDetectionState state, T result) { - this.state = state; - this.result = result; - } - - /** - * Return the {@link ProtocolDetectionState}. If the state is {@link ProtocolDetectionState#DETECTED} you - * can retrieve the protocol via {@link #detectedProtocol()}. - */ - public ProtocolDetectionState state() { - return state; - } - - /** - * Returns the protocol if {@link #state()} returns {@link ProtocolDetectionState#DETECTED}, otherwise {@code null}. - */ - public T detectedProtocol() { - return result; - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/ProtocolDetectionState.java b/codec/src/main/java/io/netty/handler/codec/ProtocolDetectionState.java deleted file mode 100644 index 0f072fc113..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/ProtocolDetectionState.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -/** - * The state of the current detection. - */ -public enum ProtocolDetectionState { - /** - * Need more data to detect the protocol. - */ - NEEDS_MORE_DATA, - - /** - * The data was invalid. - */ - INVALID, - - /** - * Protocol was detected, - */ - DETECTED -} diff --git a/codec/src/main/java/io/netty/handler/codec/ReplayingDecoder.java b/codec/src/main/java/io/netty/handler/codec/ReplayingDecoder.java deleted file mode 100644 index e61c7fe52b..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/ReplayingDecoder.java +++ /dev/null @@ -1,403 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.util.Signal; -import io.netty.util.internal.StringUtil; - -/** - * A specialized variation of {@link ByteToMessageDecoder} which enables implementation - * of a non-blocking decoder in the blocking I/O paradigm. - *

- * The biggest difference between {@link ReplayingDecoder} and - * {@link ByteToMessageDecoder} is that {@link ReplayingDecoder} allows you to - * implement the {@code decode()} and {@code decodeLast()} methods just like - * all required bytes were received already, rather than checking the - * availability of the required bytes. For example, the following - * {@link ByteToMessageDecoder} implementation: - *

- * public class IntegerHeaderFrameDecoder extends {@link ByteToMessageDecoder} {
- *
- *   {@code @Override}
- *   protected void decode({@link ChannelHandlerContext} ctx, {@link ByteBuf} buf) throws Exception {
- *
- *     if (buf.readableBytes() < 4) {
- *        return;
- *     }
- *
- *     buf.markReaderIndex();
- *     int length = buf.readInt();
- *
- *     if (buf.readableBytes() < length) {
- *        buf.resetReaderIndex();
- *        return;
- *     }
- *
- *     ctx.fireChannelRead(buf.readBytes(length));
- *   }
- * }
- * 
- * is simplified like the following with {@link ReplayingDecoder}: - *
- * public class IntegerHeaderFrameDecoder
- *      extends {@link ReplayingDecoder}<{@link Void}> {
- *
- *   protected void decode({@link ChannelHandlerContext} ctx,
- *                           {@link ByteBuf} buf) throws Exception {
- *
- *     out.add(buf.readBytes(buf.readInt()));
- *   }
- * }
- * 
- * - *

How does this work?

- *

- * {@link ReplayingDecoder} passes a specialized {@link ByteBuf} - * implementation which throws an {@link Error} of certain type when there's not - * enough data in the buffer. In the {@code IntegerHeaderFrameDecoder} above, - * you just assumed that there will be 4 or more bytes in the buffer when - * you call {@code buf.readInt()}. If there's really 4 bytes in the buffer, - * it will return the integer header as you expected. Otherwise, the - * {@link Error} will be raised and the control will be returned to - * {@link ReplayingDecoder}. If {@link ReplayingDecoder} catches the - * {@link Error}, then it will rewind the {@code readerIndex} of the buffer - * back to the 'initial' position (i.e. the beginning of the buffer) and call - * the {@code decode(..)} method again when more data is received into the - * buffer. - *

- * Please note that {@link ReplayingDecoder} always throws the same cached - * {@link Error} instance to avoid the overhead of creating a new {@link Error} - * and filling its stack trace for every throw. - * - *

Limitations

- *

- * At the cost of the simplicity, {@link ReplayingDecoder} enforces you a few - * limitations: - *

    - *
  • Some buffer operations are prohibited.
  • - *
  • Performance can be worse if the network is slow and the message - * format is complicated unlike the example above. In this case, your - * decoder might have to decode the same part of the message over and over - * again.
  • - *
  • You must keep in mind that {@code decode(..)} method can be called many - * times to decode a single message. For example, the following code will - * not work: - *
     public class MyDecoder extends {@link ReplayingDecoder}<{@link Void}> {
    - *
    - *   private final Queue<Integer> values = new LinkedList<Integer>();
    - *
    - *   {@code @Override}
    - *   public void decode({@link ChannelHandlerContext} ctx, {@link ByteBuf} buf) throws Exception {
    - *
    - *     // A message contains 2 integers.
    - *     values.offer(buf.readInt());
    - *     values.offer(buf.readInt());
    - *
    - *     // This assertion will fail intermittently since values.offer()
    - *     // can be called more than two times!
    - *     assert values.size() == 2;
    - *     ctx.fireChannelRead(values.poll() + values.poll());
    - *   }
    - * }
    - * The correct implementation looks like the following, and you can also - * utilize the 'checkpoint' feature which is explained in detail in the - * next section. - *
     public class MyDecoder extends {@link ReplayingDecoder}<{@link Void}> {
    - *
    - *   private final Queue<Integer> values = new LinkedList<Integer>();
    - *
    - *   {@code @Override}
    - *   public void decode({@link ChannelHandlerContext} ctx, {@link ByteBuf} buf) throws Exception {
    - *
    - *     // Revert the state of the variable that might have been changed
    - *     // since the last partial decode.
    - *     values.clear();
    - *
    - *     // A message contains 2 integers.
    - *     values.offer(buf.readInt());
    - *     values.offer(buf.readInt());
    - *
    - *     // Now we know this assertion will never fail.
    - *     assert values.size() == 2;
    - *     ctx.fireChannelRead(values.poll() + values.poll());
    - *   }
    - * }
    - *
  • - *
- * - *

Improving the performance

- *

- * Fortunately, the performance of a complex decoder implementation can be - * improved significantly with the {@code checkpoint()} method. The - * {@code checkpoint()} method updates the 'initial' position of the buffer so - * that {@link ReplayingDecoder} rewinds the {@code readerIndex} of the buffer - * to the last position where you called the {@code checkpoint()} method. - * - *

Calling {@code checkpoint(T)} with an {@link Enum}

- *

- * Although you can just use {@code checkpoint()} method and manage the state - * of the decoder by yourself, the easiest way to manage the state of the - * decoder is to create an {@link Enum} type which represents the current state - * of the decoder and to call {@code checkpoint(T)} method whenever the state - * changes. You can have as many states as you want depending on the - * complexity of the message you want to decode: - * - *

- * public enum MyDecoderState {
- *   READ_LENGTH,
- *   READ_CONTENT;
- * }
- *
- * public class IntegerHeaderFrameDecoder
- *      extends {@link ReplayingDecoder}<MyDecoderState> {
- *
- *   private int length;
- *
- *   public IntegerHeaderFrameDecoder() {
- *     // Set the initial state.
- *     super(MyDecoderState.READ_LENGTH);
- *   }
- *
- *   {@code @Override}
- *   protected void decode({@link ChannelHandlerContext} ctx, {@link ByteBuf} buf) throws Exception {
- *     switch (state()) {
- *     case READ_LENGTH:
- *       length = buf.readInt();
- *       checkpoint(MyDecoderState.READ_CONTENT);
- *     case READ_CONTENT:
- *       ByteBuf frame = buf.readBytes(length);
- *       checkpoint(MyDecoderState.READ_LENGTH);
- *       ctx.fireChannelRead(frame);
- *       break;
- *     default:
- *       throw new Error("Shouldn't reach here.");
- *     }
- *   }
- * }
- * 
- * - *

Calling {@code checkpoint()} with no parameter

- *

- * An alternative way to manage the decoder state is to manage it by yourself. - *

- * public class IntegerHeaderFrameDecoder
- *      extends {@link ReplayingDecoder}<{@link Void}> {
- *
- *   private boolean readLength;
- *   private int length;
- *
- *   {@code @Override}
- *   protected void decode({@link ChannelHandlerContext} ctx, {@link ByteBuf} buf) throws Exception {
- *     if (!readLength) {
- *       length = buf.readInt();
- *       readLength = true;
- *       checkpoint();
- *     }
- *
- *     if (readLength) {
- *       ByteBuf frame = buf.readBytes(length);
- *       readLength = false;
- *       checkpoint();
- *       ctx.fireChannelRead(frame);
- *     }
- *   }
- * }
- * 
- * - *

Replacing a decoder with another decoder in a pipeline

- *

- * If you are going to write a protocol multiplexer, you will probably want to - * replace a {@link ReplayingDecoder} (protocol detector) with another - * {@link ReplayingDecoder}, {@link ByteToMessageDecoder} or {@link MessageToMessageDecoder} - * (actual protocol decoder). - * It is not possible to achieve this simply by calling - * {@link ChannelPipeline#replace(ChannelHandler, String, ChannelHandler)}, but - * some additional steps are required: - *

- * public class FirstDecoder extends {@link ReplayingDecoder}<{@link Void}> {
- *
- *     {@code @Override}
- *     protected void decode({@link ChannelHandlerContext} ctx, {@link ByteBuf} buf) {
- *         ...
- *         // Decode the first message
- *         Object firstMessage = ...;
- *
- *         // Add the second decoder
- *         ctx.pipeline().addLast("second", new SecondDecoder());
- *
- *         if (buf.isReadable()) {
- *             // Hand off the remaining data to the second decoder
- *             ctx.fireChannelRead(firstMessage);
- *             ctx.fireChannelRead(buf.readBytes(super.actualReadableBytes()));
- *         } else {
- *             // Nothing to hand off
- *             ctx.fireChannelRead(firstMessage);
- *         }
- *         // Remove the first decoder (me)
- *         ctx.pipeline().remove(this);
- *     }
- * 
- * @param - * the state type which is usually an {@link Enum}; use {@link Void} if state management is - * unused - */ -public abstract class ReplayingDecoder extends ByteToMessageDecoder { - - static final Signal REPLAY = Signal.valueOf(ReplayingDecoder.class, "REPLAY"); - - private final ReplayingDecoderByteBuf replayable = new ReplayingDecoderByteBuf(); - private S state; - private int checkpoint = -1; - - /** - * Creates a new instance with no initial state (i.e: {@code null}). - */ - protected ReplayingDecoder() { - this(null); - } - - /** - * Creates a new instance with the specified initial state. - */ - protected ReplayingDecoder(S initialState) { - state = initialState; - } - - /** - * Stores the internal cumulative buffer's reader position. - */ - protected void checkpoint() { - checkpoint = internalBuffer().readerIndex(); - } - - /** - * Stores the internal cumulative buffer's reader position and updates - * the current decoder state. - */ - protected void checkpoint(S state) { - checkpoint(); - state(state); - } - - /** - * Returns the current state of this decoder. - * @return the current state of this decoder - */ - protected S state() { - return state; - } - - /** - * Sets the current state of this decoder. - * @return the old state of this decoder - */ - protected S state(S newState) { - S oldState = state; - state = newState; - return oldState; - } - - @Override - final void channelInputClosed(ByteToMessageDecoderContext ctx) throws Exception { - try { - replayable.terminate(); - if (cumulation != null) { - callDecode(ctx, internalBuffer()); - } else { - replayable.setCumulation(Unpooled.EMPTY_BUFFER); - } - decodeLast(ctx, replayable); - } catch (Signal replay) { - // Ignore - replay.expect(REPLAY); - } - } - - @Override - protected void callDecode(ByteToMessageDecoderContext ctx, ByteBuf in) { - replayable.setCumulation(in); - try { - while (in.isReadable()) { - int oldReaderIndex = checkpoint = in.readerIndex(); - - S oldState = state; - int oldInputLength = in.readableBytes(); - try { - int oldNumRead = ctx.fireChannelReadCallCount(); - decodeRemovalReentryProtection(ctx, replayable); - - // Check if this handler was removed before continuing the loop. - // If it was removed, it is not safe to continue to operate on the buffer. - // - // See https://github.com/netty/netty/issues/1664 - if (ctx.isRemoved()) { - break; - } - - if (oldNumRead == ctx.fireChannelReadCallCount()) { - if (oldInputLength == in.readableBytes() && oldState == state) { - throw new DecoderException( - StringUtil.simpleClassName(getClass()) + ".decode() must consume the inbound " + - "data or change its state if it did not decode anything."); - } else { - // Previous data has been discarded or caused state transition. - // Probably it is reading on. - continue; - } - } - } catch (Signal replay) { - replay.expect(REPLAY); - - // Check if this handler was removed before continuing the loop. - // If it was removed, it is not safe to continue to operate on the buffer. - // - // See https://github.com/netty/netty/issues/1664 - if (ctx.isRemoved()) { - break; - } - - // Return to the checkpoint (or oldPosition) and retry. - int checkpoint = this.checkpoint; - if (checkpoint >= 0) { - in.readerIndex(checkpoint); - } else { - // Called by cleanup() - no need to maintain the readerIndex - // anymore because the buffer has been released already. - } - break; - } - - if (oldReaderIndex == in.readerIndex() && oldState == state) { - throw new DecoderException( - StringUtil.simpleClassName(getClass()) + ".decode() method must consume the inbound data " + - "or change its state if it decoded something."); - } - if (isSingleDecode()) { - break; - } - } - } catch (DecoderException e) { - throw e; - } catch (Exception cause) { - throw new DecoderException(cause); - } - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/ReplayingDecoderByteBuf.java b/codec/src/main/java/io/netty/handler/codec/ReplayingDecoderByteBuf.java deleted file mode 100644 index 44b050be3b..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/ReplayingDecoderByteBuf.java +++ /dev/null @@ -1,1126 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.SwappedByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.ByteProcessor; -import io.netty.util.Signal; -import io.netty.util.internal.StringUtil; - -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.channels.FileChannel; -import java.nio.channels.GatheringByteChannel; -import java.nio.channels.ScatteringByteChannel; -import java.nio.charset.Charset; - -/** - * Special {@link ByteBuf} implementation which is used by the {@link ReplayingDecoder} - */ -final class ReplayingDecoderByteBuf extends ByteBuf { - - private static final Signal REPLAY = ReplayingDecoder.REPLAY; - - private ByteBuf buffer; - private boolean terminated; - private SwappedByteBuf swapped; - - static final ReplayingDecoderByteBuf EMPTY_BUFFER = new ReplayingDecoderByteBuf(Unpooled.EMPTY_BUFFER); - - static { - EMPTY_BUFFER.terminate(); - } - - ReplayingDecoderByteBuf() { } - - ReplayingDecoderByteBuf(ByteBuf buffer) { - setCumulation(buffer); - } - - void setCumulation(ByteBuf buffer) { - this.buffer = buffer; - } - - void terminate() { - terminated = true; - } - - @Override - public int capacity() { - if (terminated) { - return buffer.capacity(); - } else { - return Integer.MAX_VALUE; - } - } - - @Override - public ByteBuf capacity(int newCapacity) { - throw reject(); - } - - @Override - public int maxCapacity() { - return capacity(); - } - - @Override - public ByteBufAllocator alloc() { - return buffer.alloc(); - } - - @Override - public boolean isReadOnly() { - return false; - } - - @SuppressWarnings("deprecation") - @Override - public ByteBuf asReadOnly() { - return Unpooled.unmodifiableBuffer(this); - } - - @Override - public boolean isDirect() { - return buffer.isDirect(); - } - - @Override - public boolean hasArray() { - return false; - } - - @Override - public byte[] array() { - throw new UnsupportedOperationException(); - } - - @Override - public int arrayOffset() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean hasMemoryAddress() { - return false; - } - - @Override - public long memoryAddress() { - throw new UnsupportedOperationException(); - } - - @Override - public ByteBuf clear() { - throw reject(); - } - - @Override - public boolean equals(Object obj) { - return this == obj; - } - - @Override - public int compareTo(ByteBuf buffer) { - throw reject(); - } - - @Override - public ByteBuf copy() { - throw reject(); - } - - @Override - public ByteBuf copy(int index, int length) { - checkIndex(index, length); - return buffer.copy(index, length); - } - - @Override - public ByteBuf discardReadBytes() { - throw reject(); - } - - @Override - public ByteBuf ensureWritable(int writableBytes) { - throw reject(); - } - - @Override - public int ensureWritable(int minWritableBytes, boolean force) { - throw reject(); - } - - @Override - public ByteBuf duplicate() { - throw reject(); - } - - @Override - public ByteBuf retainedDuplicate() { - throw reject(); - } - - @Override - public boolean getBoolean(int index) { - checkIndex(index, 1); - return buffer.getBoolean(index); - } - - @Override - public byte getByte(int index) { - checkIndex(index, 1); - return buffer.getByte(index); - } - - @Override - public short getUnsignedByte(int index) { - checkIndex(index, 1); - return buffer.getUnsignedByte(index); - } - - @Override - public ByteBuf getBytes(int index, byte[] dst, int dstIndex, int length) { - checkIndex(index, length); - buffer.getBytes(index, dst, dstIndex, length); - return this; - } - - @Override - public ByteBuf getBytes(int index, byte[] dst) { - checkIndex(index, dst.length); - buffer.getBytes(index, dst); - return this; - } - - @Override - public ByteBuf getBytes(int index, ByteBuffer dst) { - throw reject(); - } - - @Override - public ByteBuf getBytes(int index, ByteBuf dst, int dstIndex, int length) { - checkIndex(index, length); - buffer.getBytes(index, dst, dstIndex, length); - return this; - } - - @Override - public ByteBuf getBytes(int index, ByteBuf dst, int length) { - throw reject(); - } - - @Override - public ByteBuf getBytes(int index, ByteBuf dst) { - throw reject(); - } - - @Override - public int getBytes(int index, GatheringByteChannel out, int length) { - throw reject(); - } - - @Override - public int getBytes(int index, FileChannel out, long position, int length) { - throw reject(); - } - - @Override - public ByteBuf getBytes(int index, OutputStream out, int length) { - throw reject(); - } - - @Override - public int getInt(int index) { - checkIndex(index, 4); - return buffer.getInt(index); - } - - @Override - public int getIntLE(int index) { - checkIndex(index, 4); - return buffer.getIntLE(index); - } - - @Override - public long getUnsignedInt(int index) { - checkIndex(index, 4); - return buffer.getUnsignedInt(index); - } - - @Override - public long getUnsignedIntLE(int index) { - checkIndex(index, 4); - return buffer.getUnsignedIntLE(index); - } - - @Override - public long getLong(int index) { - checkIndex(index, 8); - return buffer.getLong(index); - } - - @Override - public long getLongLE(int index) { - checkIndex(index, 8); - return buffer.getLongLE(index); - } - - @Override - public int getMedium(int index) { - checkIndex(index, 3); - return buffer.getMedium(index); - } - - @Override - public int getMediumLE(int index) { - checkIndex(index, 3); - return buffer.getMediumLE(index); - } - - @Override - public int getUnsignedMedium(int index) { - checkIndex(index, 3); - return buffer.getUnsignedMedium(index); - } - - @Override - public int getUnsignedMediumLE(int index) { - checkIndex(index, 3); - return buffer.getUnsignedMediumLE(index); - } - - @Override - public short getShort(int index) { - checkIndex(index, 2); - return buffer.getShort(index); - } - - @Override - public short getShortLE(int index) { - checkIndex(index, 2); - return buffer.getShortLE(index); - } - - @Override - public int getUnsignedShort(int index) { - checkIndex(index, 2); - return buffer.getUnsignedShort(index); - } - - @Override - public int getUnsignedShortLE(int index) { - checkIndex(index, 2); - return buffer.getUnsignedShortLE(index); - } - - @Override - public char getChar(int index) { - checkIndex(index, 2); - return buffer.getChar(index); - } - - @Override - public float getFloat(int index) { - checkIndex(index, 4); - return buffer.getFloat(index); - } - - @Override - public double getDouble(int index) { - checkIndex(index, 8); - return buffer.getDouble(index); - } - - @Override - public CharSequence getCharSequence(int index, int length, Charset charset) { - checkIndex(index, length); - return buffer.getCharSequence(index, length, charset); - } - - @Override - public int hashCode() { - throw reject(); - } - - @Override - public int indexOf(int fromIndex, int toIndex, byte value) { - if (fromIndex == toIndex) { - return -1; - } - - if (Math.max(fromIndex, toIndex) > buffer.writerIndex()) { - throw REPLAY; - } - - return buffer.indexOf(fromIndex, toIndex, value); - } - - @Override - public int bytesBefore(byte value) { - int bytes = buffer.bytesBefore(value); - if (bytes < 0) { - throw REPLAY; - } - return bytes; - } - - @Override - public int bytesBefore(int length, byte value) { - return bytesBefore(buffer.readerIndex(), length, value); - } - - @Override - public int bytesBefore(int index, int length, byte value) { - final int writerIndex = buffer.writerIndex(); - if (index >= writerIndex) { - throw REPLAY; - } - - if (index <= writerIndex - length) { - return buffer.bytesBefore(index, length, value); - } - - int res = buffer.bytesBefore(index, writerIndex - index, value); - if (res < 0) { - throw REPLAY; - } else { - return res; - } - } - - @Override - public int forEachByte(ByteProcessor processor) { - int ret = buffer.forEachByte(processor); - if (ret < 0) { - throw REPLAY; - } else { - return ret; - } - } - - @Override - public int forEachByte(int index, int length, ByteProcessor processor) { - final int writerIndex = buffer.writerIndex(); - if (index >= writerIndex) { - throw REPLAY; - } - - if (index <= writerIndex - length) { - return buffer.forEachByte(index, length, processor); - } - - int ret = buffer.forEachByte(index, writerIndex - index, processor); - if (ret < 0) { - throw REPLAY; - } else { - return ret; - } - } - - @Override - public int forEachByteDesc(ByteProcessor processor) { - if (terminated) { - return buffer.forEachByteDesc(processor); - } else { - throw reject(); - } - } - - @Override - public int forEachByteDesc(int index, int length, ByteProcessor processor) { - if (index + length > buffer.writerIndex()) { - throw REPLAY; - } - - return buffer.forEachByteDesc(index, length, processor); - } - - @Override - public ByteOrder order() { - return buffer.order(); - } - - @Override - public ByteBuf order(ByteOrder endianness) { - requireNonNull(endianness, "endianness"); - if (endianness == order()) { - return this; - } - - SwappedByteBuf swapped = this.swapped; - if (swapped == null) { - this.swapped = swapped = new SwappedByteBuf(this); - } - return swapped; - } - - @Override - public boolean isReadable() { - return !terminated || buffer.isReadable(); - } - - @Override - public boolean isReadable(int size) { - return !terminated || buffer.isReadable(size); - } - - @Override - public int readableBytes() { - if (terminated) { - return buffer.readableBytes(); - } else { - return Integer.MAX_VALUE - buffer.readerIndex(); - } - } - - @Override - public boolean readBoolean() { - checkReadableBytes(1); - return buffer.readBoolean(); - } - - @Override - public byte readByte() { - checkReadableBytes(1); - return buffer.readByte(); - } - - @Override - public short readUnsignedByte() { - checkReadableBytes(1); - return buffer.readUnsignedByte(); - } - - @Override - public ByteBuf readBytes(byte[] dst, int dstIndex, int length) { - checkReadableBytes(length); - buffer.readBytes(dst, dstIndex, length); - return this; - } - - @Override - public ByteBuf readBytes(byte[] dst) { - checkReadableBytes(dst.length); - buffer.readBytes(dst); - return this; - } - - @Override - public ByteBuf readBytes(ByteBuffer dst) { - throw reject(); - } - - @Override - public ByteBuf readBytes(ByteBuf dst, int dstIndex, int length) { - checkReadableBytes(length); - buffer.readBytes(dst, dstIndex, length); - return this; - } - - @Override - public ByteBuf readBytes(ByteBuf dst, int length) { - throw reject(); - } - - @Override - public ByteBuf readBytes(ByteBuf dst) { - checkReadableBytes(dst.writableBytes()); - buffer.readBytes(dst); - return this; - } - - @Override - public int readBytes(GatheringByteChannel out, int length) { - throw reject(); - } - - @Override - public int readBytes(FileChannel out, long position, int length) { - throw reject(); - } - - @Override - public ByteBuf readBytes(int length) { - checkReadableBytes(length); - return buffer.readBytes(length); - } - - @Override - public ByteBuf readSlice(int length) { - checkReadableBytes(length); - return buffer.readSlice(length); - } - - @Override - public ByteBuf readRetainedSlice(int length) { - checkReadableBytes(length); - return buffer.readRetainedSlice(length); - } - - @Override - public ByteBuf readBytes(OutputStream out, int length) { - throw reject(); - } - - @Override - public int readerIndex() { - return buffer.readerIndex(); - } - - @Override - public ByteBuf readerIndex(int readerIndex) { - buffer.readerIndex(readerIndex); - return this; - } - - @Override - public int readInt() { - checkReadableBytes(4); - return buffer.readInt(); - } - - @Override - public int readIntLE() { - checkReadableBytes(4); - return buffer.readIntLE(); - } - - @Override - public long readUnsignedInt() { - checkReadableBytes(4); - return buffer.readUnsignedInt(); - } - - @Override - public long readUnsignedIntLE() { - checkReadableBytes(4); - return buffer.readUnsignedIntLE(); - } - - @Override - public long readLong() { - checkReadableBytes(8); - return buffer.readLong(); - } - - @Override - public long readLongLE() { - checkReadableBytes(8); - return buffer.readLongLE(); - } - - @Override - public int readMedium() { - checkReadableBytes(3); - return buffer.readMedium(); - } - - @Override - public int readMediumLE() { - checkReadableBytes(3); - return buffer.readMediumLE(); - } - - @Override - public int readUnsignedMedium() { - checkReadableBytes(3); - return buffer.readUnsignedMedium(); - } - - @Override - public int readUnsignedMediumLE() { - checkReadableBytes(3); - return buffer.readUnsignedMediumLE(); - } - - @Override - public short readShort() { - checkReadableBytes(2); - return buffer.readShort(); - } - - @Override - public short readShortLE() { - checkReadableBytes(2); - return buffer.readShortLE(); - } - - @Override - public int readUnsignedShort() { - checkReadableBytes(2); - return buffer.readUnsignedShort(); - } - - @Override - public int readUnsignedShortLE() { - checkReadableBytes(2); - return buffer.readUnsignedShortLE(); - } - - @Override - public char readChar() { - checkReadableBytes(2); - return buffer.readChar(); - } - - @Override - public float readFloat() { - checkReadableBytes(4); - return buffer.readFloat(); - } - - @Override - public double readDouble() { - checkReadableBytes(8); - return buffer.readDouble(); - } - - @Override - public CharSequence readCharSequence(int length, Charset charset) { - checkReadableBytes(length); - return buffer.readCharSequence(length, charset); - } - - @Override - public ByteBuf setBoolean(int index, boolean value) { - throw reject(); - } - - @Override - public ByteBuf setByte(int index, int value) { - throw reject(); - } - - @Override - public ByteBuf setBytes(int index, byte[] src, int srcIndex, int length) { - throw reject(); - } - - @Override - public ByteBuf setBytes(int index, byte[] src) { - throw reject(); - } - - @Override - public ByteBuf setBytes(int index, ByteBuffer src) { - throw reject(); - } - - @Override - public ByteBuf setBytes(int index, ByteBuf src, int srcIndex, int length) { - throw reject(); - } - - @Override - public ByteBuf setBytes(int index, ByteBuf src, int length) { - throw reject(); - } - - @Override - public ByteBuf setBytes(int index, ByteBuf src) { - throw reject(); - } - - @Override - public int setBytes(int index, InputStream in, int length) { - throw reject(); - } - - @Override - public ByteBuf setZero(int index, int length) { - throw reject(); - } - - @Override - public int setBytes(int index, ScatteringByteChannel in, int length) { - throw reject(); - } - - @Override - public int setBytes(int index, FileChannel in, long position, int length) { - throw reject(); - } - - @Override - public ByteBuf setIndex(int readerIndex, int writerIndex) { - throw reject(); - } - - @Override - public ByteBuf setInt(int index, int value) { - throw reject(); - } - - @Override - public ByteBuf setIntLE(int index, int value) { - throw reject(); - } - - @Override - public ByteBuf setLong(int index, long value) { - throw reject(); - } - - @Override - public ByteBuf setLongLE(int index, long value) { - throw reject(); - } - - @Override - public ByteBuf setMedium(int index, int value) { - throw reject(); - } - - @Override - public ByteBuf setMediumLE(int index, int value) { - throw reject(); - } - - @Override - public ByteBuf setShort(int index, int value) { - throw reject(); - } - - @Override - public ByteBuf setShortLE(int index, int value) { - throw reject(); - } - - @Override - public ByteBuf setChar(int index, int value) { - throw reject(); - } - - @Override - public ByteBuf setFloat(int index, float value) { - throw reject(); - } - - @Override - public ByteBuf setDouble(int index, double value) { - throw reject(); - } - - @Override - public ByteBuf skipBytes(int length) { - checkReadableBytes(length); - buffer.skipBytes(length); - return this; - } - - @Override - public ByteBuf slice() { - throw reject(); - } - - @Override - public ByteBuf retainedSlice() { - throw reject(); - } - - @Override - public ByteBuf slice(int index, int length) { - checkIndex(index, length); - return buffer.slice(index, length); - } - - @Override - public ByteBuf retainedSlice(int index, int length) { - checkIndex(index, length); - return buffer.slice(index, length); - } - - @Override - public int nioBufferCount() { - return buffer.nioBufferCount(); - } - - @Override - public ByteBuffer nioBuffer() { - throw reject(); - } - - @Override - public ByteBuffer nioBuffer(int index, int length) { - checkIndex(index, length); - return buffer.nioBuffer(index, length); - } - - @Override - public ByteBuffer[] nioBuffers() { - throw reject(); - } - - @Override - public ByteBuffer[] nioBuffers(int index, int length) { - checkIndex(index, length); - return buffer.nioBuffers(index, length); - } - - @Override - public ByteBuffer internalNioBuffer(int index, int length) { - checkIndex(index, length); - return buffer.internalNioBuffer(index, length); - } - - @Override - public String toString(int index, int length, Charset charset) { - checkIndex(index, length); - return buffer.toString(index, length, charset); - } - - @Override - public String toString(Charset charsetName) { - throw reject(); - } - - @Override - public String toString() { - return StringUtil.simpleClassName(this) + '(' + - "ridx=" + - readerIndex() + - ", " + - "widx=" + - writerIndex() + - ')'; - } - - @Override - public boolean isWritable() { - return false; - } - - @Override - public boolean isWritable(int size) { - return false; - } - - @Override - public int writableBytes() { - return 0; - } - - @Override - public int maxWritableBytes() { - return 0; - } - - @Override - public ByteBuf writeBoolean(boolean value) { - throw reject(); - } - - @Override - public ByteBuf writeByte(int value) { - throw reject(); - } - - @Override - public ByteBuf writeBytes(byte[] src, int srcIndex, int length) { - throw reject(); - } - - @Override - public ByteBuf writeBytes(byte[] src) { - throw reject(); - } - - @Override - public ByteBuf writeBytes(ByteBuffer src) { - throw reject(); - } - - @Override - public ByteBuf writeBytes(ByteBuf src, int srcIndex, int length) { - throw reject(); - } - - @Override - public ByteBuf writeBytes(ByteBuf src, int length) { - throw reject(); - } - - @Override - public ByteBuf writeBytes(ByteBuf src) { - throw reject(); - } - - @Override - public int writeBytes(InputStream in, int length) { - throw reject(); - } - - @Override - public int writeBytes(ScatteringByteChannel in, int length) { - throw reject(); - } - - @Override - public int writeBytes(FileChannel in, long position, int length) { - throw reject(); - } - - @Override - public ByteBuf writeInt(int value) { - throw reject(); - } - - @Override - public ByteBuf writeIntLE(int value) { - throw reject(); - } - - @Override - public ByteBuf writeLong(long value) { - throw reject(); - } - - @Override - public ByteBuf writeLongLE(long value) { - throw reject(); - } - - @Override - public ByteBuf writeMedium(int value) { - throw reject(); - } - - @Override - public ByteBuf writeMediumLE(int value) { - throw reject(); - } - - @Override - public ByteBuf writeZero(int length) { - throw reject(); - } - - @Override - public int writerIndex() { - return buffer.writerIndex(); - } - - @Override - public ByteBuf writerIndex(int writerIndex) { - throw reject(); - } - - @Override - public ByteBuf writeShort(int value) { - throw reject(); - } - - @Override - public ByteBuf writeShortLE(int value) { - throw reject(); - } - - @Override - public ByteBuf writeChar(int value) { - throw reject(); - } - - @Override - public ByteBuf writeFloat(float value) { - throw reject(); - } - - @Override - public ByteBuf writeDouble(double value) { - throw reject(); - } - - @Override - public int setCharSequence(int index, CharSequence sequence, Charset charset) { - throw reject(); - } - - @Override - public int writeCharSequence(CharSequence sequence, Charset charset) { - throw reject(); - } - - private void checkIndex(int index, int length) { - if (index + length > buffer.writerIndex()) { - throw REPLAY; - } - } - - private void checkReadableBytes(int readableBytes) { - if (buffer.readableBytes() < readableBytes) { - throw REPLAY; - } - } - - @Override - public ByteBuf discardSomeReadBytes() { - throw reject(); - } - - @Override - public int refCnt() { - return buffer.refCnt(); - } - - @Override - public ByteBuf retain() { - throw reject(); - } - - @Override - public ByteBuf retain(int increment) { - throw reject(); - } - - @Override - public ByteBuf touch() { - buffer.touch(); - return this; - } - - @Override - public ByteBuf touch(Object hint) { - buffer.touch(hint); - return this; - } - - @Override - public boolean release() { - throw reject(); - } - - @Override - public boolean release(int decrement) { - throw reject(); - } - - @Override - public ByteBuf unwrap() { - throw reject(); - } - - private static UnsupportedOperationException reject() { - return new UnsupportedOperationException("not a replayable operation"); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/TooLongFrameException.java b/codec/src/main/java/io/netty/handler/codec/TooLongFrameException.java deleted file mode 100644 index e905f5261e..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/TooLongFrameException.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -/** - * An {@link DecoderException} which is thrown when the length of the frame - * decoded is greater than the allowed maximum. - */ -public class TooLongFrameException extends DecoderException { - - private static final long serialVersionUID = -1995801950698951640L; - - /** - * Creates a new instance. - */ - public TooLongFrameException() { - } - - /** - * Creates a new instance. - */ - public TooLongFrameException(String message, Throwable cause) { - super(message, cause); - } - - /** - * Creates a new instance. - */ - public TooLongFrameException(String message) { - super(message); - } - - /** - * Creates a new instance. - */ - public TooLongFrameException(Throwable cause) { - super(cause); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/UnsupportedMessageTypeException.java b/codec/src/main/java/io/netty/handler/codec/UnsupportedMessageTypeException.java deleted file mode 100644 index 48a8f04aae..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/UnsupportedMessageTypeException.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -/** - * Thrown if an unsupported message is received by an codec. - */ -public class UnsupportedMessageTypeException extends CodecException { - - private static final long serialVersionUID = 2799598826487038726L; - - public UnsupportedMessageTypeException( - Object message, Class... expectedTypes) { - super(message( - message == null? "null" : message.getClass().getName(), expectedTypes)); - } - - public UnsupportedMessageTypeException() { } - - public UnsupportedMessageTypeException(String message, Throwable cause) { - super(message, cause); - } - - public UnsupportedMessageTypeException(String s) { - super(s); - } - - public UnsupportedMessageTypeException(Throwable cause) { - super(cause); - } - - private static String message( - String actualType, Class... expectedTypes) { - StringBuilder buf = new StringBuilder(actualType); - - if (expectedTypes != null && expectedTypes.length > 0) { - buf.append(" (expected: ").append(expectedTypes[0].getName()); - for (int i = 1; i < expectedTypes.length; i ++) { - Class t = expectedTypes[i]; - if (t == null) { - break; - } - buf.append(", ").append(t.getName()); - } - buf.append(')'); - } - - return buf.toString(); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/UnsupportedValueConverter.java b/codec/src/main/java/io/netty/handler/codec/UnsupportedValueConverter.java deleted file mode 100644 index 256704535e..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/UnsupportedValueConverter.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -/** - * {@link UnsupportedOperationException} will be thrown from all {@link ValueConverter} methods. - */ -public final class UnsupportedValueConverter implements ValueConverter { - @SuppressWarnings("rawtypes") - private static final UnsupportedValueConverter INSTANCE = new UnsupportedValueConverter(); - private UnsupportedValueConverter() { } - - @SuppressWarnings("unchecked") - public static UnsupportedValueConverter instance() { - return (UnsupportedValueConverter) INSTANCE; - } - - @Override - public V convertObject(Object value) { - throw new UnsupportedOperationException(); - } - - @Override - public V convertBoolean(boolean value) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean convertToBoolean(V value) { - throw new UnsupportedOperationException(); - } - - @Override - public V convertByte(byte value) { - throw new UnsupportedOperationException(); - } - - @Override - public byte convertToByte(V value) { - throw new UnsupportedOperationException(); - } - - @Override - public V convertChar(char value) { - throw new UnsupportedOperationException(); - } - - @Override - public char convertToChar(V value) { - throw new UnsupportedOperationException(); - } - - @Override - public V convertShort(short value) { - throw new UnsupportedOperationException(); - } - - @Override - public short convertToShort(V value) { - throw new UnsupportedOperationException(); - } - - @Override - public V convertInt(int value) { - throw new UnsupportedOperationException(); - } - - @Override - public int convertToInt(V value) { - throw new UnsupportedOperationException(); - } - - @Override - public V convertLong(long value) { - throw new UnsupportedOperationException(); - } - - @Override - public long convertToLong(V value) { - throw new UnsupportedOperationException(); - } - - @Override - public V convertTimeMillis(long value) { - throw new UnsupportedOperationException(); - } - - @Override - public long convertToTimeMillis(V value) { - throw new UnsupportedOperationException(); - } - - @Override - public V convertFloat(float value) { - throw new UnsupportedOperationException(); - } - - @Override - public float convertToFloat(V value) { - throw new UnsupportedOperationException(); - } - - @Override - public V convertDouble(double value) { - throw new UnsupportedOperationException(); - } - - @Override - public double convertToDouble(V value) { - throw new UnsupportedOperationException(); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/ValueConverter.java b/codec/src/main/java/io/netty/handler/codec/ValueConverter.java deleted file mode 100644 index 4480f11eac..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/ValueConverter.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */package io.netty.handler.codec; - -/** - * Converts to/from a generic object to the type. - */ -public interface ValueConverter { - T convertObject(Object value); - - T convertBoolean(boolean value); - - boolean convertToBoolean(T value); - - T convertByte(byte value); - - byte convertToByte(T value); - - T convertChar(char value); - - char convertToChar(T value); - - T convertShort(short value); - - short convertToShort(T value); - - T convertInt(int value); - - int convertToInt(T value); - - T convertLong(long value); - - long convertToLong(T value); - - T convertTimeMillis(long value); - - long convertToTimeMillis(T value); - - T convertFloat(float value); - - float convertToFloat(T value); - - T convertDouble(double value); - - double convertToDouble(T value); -} diff --git a/codec/src/main/java/io/netty/handler/codec/base64/Base64.java b/codec/src/main/java/io/netty/handler/codec/base64/Base64.java deleted file mode 100644 index e5d1b1f5ce..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/base64/Base64.java +++ /dev/null @@ -1,431 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -/* - * Written by Robert Harder and released to the public domain, as explained at - * https://creativecommons.org/licenses/publicdomain - */ -package io.netty.handler.codec.base64; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.util.ByteProcessor; - -import java.nio.ByteOrder; - -import static java.util.Objects.requireNonNull; - -/** - * Utility class for {@link ByteBuf} that encodes and decodes to and from - * Base64 notation. - *

- * The encoding and decoding algorithm in this class has been derived from - * Robert Harder's Public Domain - * Base64 Encoder/Decoder. - */ -public final class Base64 { - - /** Maximum line length (76) of Base64 output. */ - private static final int MAX_LINE_LENGTH = 76; - - /** The equals sign (=) as a byte. */ - private static final byte EQUALS_SIGN = (byte) '='; - - /** The new line character (\n) as a byte. */ - private static final byte NEW_LINE = (byte) '\n'; - - private static final byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding - - private static final byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding - - private static byte[] alphabet(Base64Dialect dialect) { - requireNonNull(dialect, "dialect"); - return dialect.alphabet; - } - - private static byte[] decodabet(Base64Dialect dialect) { - requireNonNull(dialect, "dialect"); - return dialect.decodabet; - } - - private static boolean breakLines(Base64Dialect dialect) { - requireNonNull(dialect, "dialect"); - return dialect.breakLinesByDefault; - } - - public static ByteBuf encode(ByteBuf src) { - return encode(src, Base64Dialect.STANDARD); - } - - public static ByteBuf encode(ByteBuf src, Base64Dialect dialect) { - return encode(src, breakLines(dialect), dialect); - } - - public static ByteBuf encode(ByteBuf src, boolean breakLines) { - return encode(src, breakLines, Base64Dialect.STANDARD); - } - - public static ByteBuf encode(ByteBuf src, boolean breakLines, Base64Dialect dialect) { - requireNonNull(src, "src"); - - ByteBuf dest = encode(src, src.readerIndex(), src.readableBytes(), breakLines, dialect); - src.readerIndex(src.writerIndex()); - return dest; - } - - public static ByteBuf encode(ByteBuf src, int off, int len) { - return encode(src, off, len, Base64Dialect.STANDARD); - } - - public static ByteBuf encode(ByteBuf src, int off, int len, Base64Dialect dialect) { - return encode(src, off, len, breakLines(dialect), dialect); - } - - public static ByteBuf encode( - ByteBuf src, int off, int len, boolean breakLines) { - return encode(src, off, len, breakLines, Base64Dialect.STANDARD); - } - - public static ByteBuf encode( - ByteBuf src, int off, int len, boolean breakLines, Base64Dialect dialect) { - return encode(src, off, len, breakLines, dialect, src.alloc()); - } - - public static ByteBuf encode( - ByteBuf src, int off, int len, boolean breakLines, Base64Dialect dialect, ByteBufAllocator allocator) { - requireNonNull(src, "src"); - requireNonNull(dialect, "dialect"); - - ByteBuf dest = allocator.buffer(encodedBufferSize(len, breakLines)).order(src.order()); - byte[] alphabet = alphabet(dialect); - int d = 0; - int e = 0; - int len2 = len - 2; - int lineLength = 0; - for (; d < len2; d += 3, e += 4) { - encode3to4(src, d + off, 3, dest, e, alphabet); - - lineLength += 4; - - if (breakLines && lineLength == MAX_LINE_LENGTH) { - dest.setByte(e + 4, NEW_LINE); - e ++; - lineLength = 0; - } // end if: end of line - } // end for: each piece of array - - if (d < len) { - encode3to4(src, d + off, len - d, dest, e, alphabet); - e += 4; - } // end if: some padding needed - - // Remove last byte if it's a newline - if (e > 1 && dest.getByte(e - 1) == NEW_LINE) { - e--; - } - - return dest.slice(0, e); - } - - private static void encode3to4( - ByteBuf src, int srcOffset, int numSigBytes, ByteBuf dest, int destOffset, byte[] alphabet) { - // 1 2 3 - // 01234567890123456789012345678901 Bit position - // --------000000001111111122222222 Array position from threeBytes - // --------| || || || | Six bit groups to index ALPHABET - // >>18 >>12 >> 6 >> 0 Right shift necessary - // 0x3f 0x3f 0x3f Additional AND - - // Create buffer with zero-padding if there are only one or two - // significant bytes passed in the array. - // We have to shift left 24 in order to flush out the 1's that appear - // when Java treats a value as negative that is cast from a byte to an int. - if (src.order() == ByteOrder.BIG_ENDIAN) { - final int inBuff; - switch (numSigBytes) { - case 1: - inBuff = toInt(src.getByte(srcOffset)); - break; - case 2: - inBuff = toIntBE(src.getShort(srcOffset)); - break; - default: - inBuff = numSigBytes <= 0 ? 0 : toIntBE(src.getMedium(srcOffset)); - break; - } - encode3to4BigEndian(inBuff, numSigBytes, dest, destOffset, alphabet); - } else { - final int inBuff; - switch (numSigBytes) { - case 1: - inBuff = toInt(src.getByte(srcOffset)); - break; - case 2: - inBuff = toIntLE(src.getShort(srcOffset)); - break; - default: - inBuff = numSigBytes <= 0 ? 0 : toIntLE(src.getMedium(srcOffset)); - break; - } - encode3to4LittleEndian(inBuff, numSigBytes, dest, destOffset, alphabet); - } - } - - // package-private for testing - static int encodedBufferSize(int len, boolean breakLines) { - // Cast len to long to prevent overflow - long len43 = ((long) len << 2) / 3; - - // Account for padding - long ret = (len43 + 3) & ~3; - - if (breakLines) { - ret += len43 / MAX_LINE_LENGTH; - } - - return ret < Integer.MAX_VALUE ? (int) ret : Integer.MAX_VALUE; - } - - private static int toInt(byte value) { - return (value & 0xff) << 16; - } - - private static int toIntBE(short value) { - return (value & 0xff00) << 8 | (value & 0xff) << 8; - } - - private static int toIntLE(short value) { - return (value & 0xff) << 16 | (value & 0xff00); - } - - private static int toIntBE(int mediumValue) { - return (mediumValue & 0xff0000) | (mediumValue & 0xff00) | (mediumValue & 0xff); - } - - private static int toIntLE(int mediumValue) { - return (mediumValue & 0xff) << 16 | (mediumValue & 0xff00) | (mediumValue & 0xff0000) >>> 16; - } - - private static void encode3to4BigEndian( - int inBuff, int numSigBytes, ByteBuf dest, int destOffset, byte[] alphabet) { - // Packing bytes into an int to reduce bound and reference count checking. - switch (numSigBytes) { - case 3: - dest.setInt(destOffset, alphabet[inBuff >>> 18 ] << 24 | - alphabet[inBuff >>> 12 & 0x3f] << 16 | - alphabet[inBuff >>> 6 & 0x3f] << 8 | - alphabet[inBuff & 0x3f]); - break; - case 2: - dest.setInt(destOffset, alphabet[inBuff >>> 18 ] << 24 | - alphabet[inBuff >>> 12 & 0x3f] << 16 | - alphabet[inBuff >>> 6 & 0x3f] << 8 | - EQUALS_SIGN); - break; - case 1: - dest.setInt(destOffset, alphabet[inBuff >>> 18 ] << 24 | - alphabet[inBuff >>> 12 & 0x3f] << 16 | - EQUALS_SIGN << 8 | - EQUALS_SIGN); - break; - default: - // NOOP - break; - } - } - - private static void encode3to4LittleEndian( - int inBuff, int numSigBytes, ByteBuf dest, int destOffset, byte[] alphabet) { - // Packing bytes into an int to reduce bound and reference count checking. - switch (numSigBytes) { - case 3: - dest.setInt(destOffset, alphabet[inBuff >>> 18 ] | - alphabet[inBuff >>> 12 & 0x3f] << 8 | - alphabet[inBuff >>> 6 & 0x3f] << 16 | - alphabet[inBuff & 0x3f] << 24); - break; - case 2: - dest.setInt(destOffset, alphabet[inBuff >>> 18 ] | - alphabet[inBuff >>> 12 & 0x3f] << 8 | - alphabet[inBuff >>> 6 & 0x3f] << 16 | - EQUALS_SIGN << 24); - break; - case 1: - dest.setInt(destOffset, alphabet[inBuff >>> 18 ] | - alphabet[inBuff >>> 12 & 0x3f] << 8 | - EQUALS_SIGN << 16 | - EQUALS_SIGN << 24); - break; - default: - // NOOP - break; - } - } - - public static ByteBuf decode(ByteBuf src) { - return decode(src, Base64Dialect.STANDARD); - } - - public static ByteBuf decode(ByteBuf src, Base64Dialect dialect) { - requireNonNull(src, "src"); - - ByteBuf dest = decode(src, src.readerIndex(), src.readableBytes(), dialect); - src.readerIndex(src.writerIndex()); - return dest; - } - - public static ByteBuf decode( - ByteBuf src, int off, int len) { - return decode(src, off, len, Base64Dialect.STANDARD); - } - - public static ByteBuf decode( - ByteBuf src, int off, int len, Base64Dialect dialect) { - return decode(src, off, len, dialect, src.alloc()); - } - - public static ByteBuf decode( - ByteBuf src, int off, int len, Base64Dialect dialect, ByteBufAllocator allocator) { - requireNonNull(src, "src"); - requireNonNull(dialect, "dialect"); - - // Using a ByteProcessor to reduce bound and reference count checking. - return new Decoder().decode(src, off, len, allocator, dialect); - } - - // package-private for testing - static int decodedBufferSize(int len) { - return len - (len >>> 2); - } - - private static final class Decoder implements ByteProcessor { - private final byte[] b4 = new byte[4]; - private int b4Posn; - private byte[] decodabet; - private int outBuffPosn; - private ByteBuf dest; - - ByteBuf decode(ByteBuf src, int off, int len, ByteBufAllocator allocator, Base64Dialect dialect) { - dest = allocator.buffer(decodedBufferSize(len)).order(src.order()); // Upper limit on size of output - - decodabet = decodabet(dialect); - try { - src.forEachByte(off, len, this); - return dest.slice(0, outBuffPosn); - } catch (Throwable cause) { - dest.release(); - throw cause; - } - } - - @Override - public boolean process(byte value) { - if (value > 0) { - byte sbiDecode = decodabet[value]; - if (sbiDecode >= WHITE_SPACE_ENC) { // White space, Equals sign or better - if (sbiDecode >= EQUALS_SIGN_ENC) { // Equals sign or better - b4[b4Posn ++] = value; - if (b4Posn > 3) { // Quartet built - outBuffPosn += decode4to3(b4, dest, outBuffPosn, decodabet); - b4Posn = 0; - - // If that was the equals sign, break out of 'for' loop - return value != EQUALS_SIGN; - } - } - return true; - } - } - throw new IllegalArgumentException( - "invalid Base64 input character: " + (short) (value & 0xFF) + " (decimal)"); - } - - private static int decode4to3(byte[] src, ByteBuf dest, int destOffset, byte[] decodabet) { - final byte src0 = src[0]; - final byte src1 = src[1]; - final byte src2 = src[2]; - final int decodedValue; - if (src2 == EQUALS_SIGN) { - // Example: Dk== - try { - decodedValue = (decodabet[src0] & 0xff) << 2 | (decodabet[src1] & 0xff) >>> 4; - } catch (IndexOutOfBoundsException ignored) { - throw new IllegalArgumentException("not encoded in Base64"); - } - dest.setByte(destOffset, decodedValue); - return 1; - } - - final byte src3 = src[3]; - if (src3 == EQUALS_SIGN) { - // Example: DkL= - final byte b1 = decodabet[src1]; - // Packing bytes into a short to reduce bound and reference count checking. - try { - if (dest.order() == ByteOrder.BIG_ENDIAN) { - // The decodabet bytes are meant to straddle byte boundaries and so we must carefully mask out - // the bits we care about. - decodedValue = ((decodabet[src0] & 0x3f) << 2 | (b1 & 0xf0) >> 4) << 8 | - (b1 & 0xf) << 4 | (decodabet[src2] & 0xfc) >>> 2; - } else { - // This is just a simple byte swap of the operation above. - decodedValue = (decodabet[src0] & 0x3f) << 2 | (b1 & 0xf0) >> 4 | - ((b1 & 0xf) << 4 | (decodabet[src2] & 0xfc) >>> 2) << 8; - } - } catch (IndexOutOfBoundsException ignored) { - throw new IllegalArgumentException("not encoded in Base64"); - } - dest.setShort(destOffset, decodedValue); - return 2; - } - - // Example: DkLE - try { - if (dest.order() == ByteOrder.BIG_ENDIAN) { - decodedValue = (decodabet[src0] & 0x3f) << 18 | - (decodabet[src1] & 0xff) << 12 | - (decodabet[src2] & 0xff) << 6 | - decodabet[src3] & 0xff; - } else { - final byte b1 = decodabet[src1]; - final byte b2 = decodabet[src2]; - // The goal is to byte swap the BIG_ENDIAN case above. There are 2 interesting things to consider: - // 1. We are byte swapping a 3 byte data type. The left and the right byte switch, but the middle - // remains the same. - // 2. The contents straddles byte boundaries. This means bytes will be pulled apart during the byte - // swapping process. - decodedValue = (decodabet[src0] & 0x3f) << 2 | - // The bottom half of b1 remains in the middle. - (b1 & 0xf) << 12 | - // The top half of b1 are the least significant bits after the swap. - (b1 & 0xf0) >>> 4 | - // The bottom 2 bits of b2 will be the most significant bits after the swap. - (b2 & 0x3) << 22 | - // The remaining 6 bits of b2 remain in the middle. - (b2 & 0xfc) << 6 | - (decodabet[src3] & 0xff) << 16; - } - } catch (IndexOutOfBoundsException ignored) { - throw new IllegalArgumentException("not encoded in Base64"); - } - dest.setMedium(destOffset, decodedValue); - return 3; - } - } - - private Base64() { - // Unused - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/base64/Base64Decoder.java b/codec/src/main/java/io/netty/handler/codec/base64/Base64Decoder.java deleted file mode 100644 index 7fc9ad2bc0..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/base64/Base64Decoder.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.base64; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.handler.codec.ByteToMessageDecoder; -import io.netty.handler.codec.DelimiterBasedFrameDecoder; -import io.netty.handler.codec.Delimiters; -import io.netty.handler.codec.MessageToMessageDecoder; - -/** - * Decodes a Base64-encoded {@link ByteBuf} or US-ASCII {@link String} - * into a {@link ByteBuf}. Please note that this decoder must be used - * with a proper {@link ByteToMessageDecoder} such as {@link DelimiterBasedFrameDecoder} - * if you are using a stream-based transport such as TCP/IP. A typical decoder - * setup for TCP/IP would be: - *

- * {@link ChannelPipeline} pipeline = ...;
- *
- * // Decoders
- * pipeline.addLast("frameDecoder", new {@link DelimiterBasedFrameDecoder}(80, {@link Delimiters#nulDelimiter()}));
- * pipeline.addLast("base64Decoder", new {@link Base64Decoder}());
- *
- * // Encoder
- * pipeline.addLast("base64Encoder", new {@link Base64Encoder}());
- * 
- */ -@Sharable -public class Base64Decoder extends MessageToMessageDecoder { - - private final Base64Dialect dialect; - - public Base64Decoder() { - this(Base64Dialect.STANDARD); - } - - public Base64Decoder(Base64Dialect dialect) { - requireNonNull(dialect, "dialect"); - this.dialect = dialect; - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { - ctx.fireChannelRead(Base64.decode(msg, msg.readerIndex(), msg.readableBytes(), dialect)); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/base64/Base64Dialect.java b/codec/src/main/java/io/netty/handler/codec/base64/Base64Dialect.java deleted file mode 100644 index c40721f456..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/base64/Base64Dialect.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -/* - * Written by Robert Harder and released to the public domain, as explained at - * https://creativecommons.org/licenses/publicdomain - */ -package io.netty.handler.codec.base64; - -/** - * Enumeration of supported Base64 dialects. - *

- * The internal lookup tables in this class has been derived from - * Robert Harder's Public Domain - * Base64 Encoder/Decoder. - */ -public enum Base64Dialect { - /** - * Standard Base64 encoding as described in the Section 3 of - * RFC3548. - */ - STANDARD(new byte[] { - (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', - (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', - (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', - (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', - (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', - (byte) 'Z', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', - (byte) 'e', (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', - (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', - (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', - (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', - (byte) 'y', (byte) 'z', (byte) '0', (byte) '1', (byte) '2', - (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7', - (byte) '8', (byte) '9', (byte) '+', (byte) '/' }, - new byte[] { - -9, -9, -9, -9, -9, -9, - -9, -9, -9, // Decimal 0 - 8 - -5, -5, // Whitespace: Tab and Linefeed - -9, -9, // Decimal 11 - 12 - -5, // Whitespace: Carriage Return - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26 - -9, -9, -9, -9, -9, // Decimal 27 - 31 - -5, // Whitespace: Space - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42 - 62, // Plus sign at decimal 43 - -9, -9, -9, // Decimal 44 - 46 - 63, // Slash at decimal 47 - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine - -9, -9, -9, // Decimal 58 - 60 - -1, // Equals sign at decimal 61 - -9, -9, -9, // Decimal 62 - 64 - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N' - 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z' - -9, -9, -9, -9, -9, -9, // Decimal 91 - 96 - 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm' - 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z' - -9, -9, -9, -9, -9 // Decimal 123 - 127 - /* -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 128 - 140 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 141 - 153 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 154 - 166 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 167 - 179 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 180 - 192 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 193 - 205 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 206 - 218 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 219 - 231 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 232 - 244 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 245 - 255 */ - }, true), - /** - * Base64-like encoding that is URL-safe as described in the Section 4 of - * RFC3548. It is - * important to note that data encoded this way is not officially - * valid Base64, or at the very least should not be called Base64 without - * also specifying that is was encoded using the URL-safe dialect. - */ - URL_SAFE(new byte[] { - (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', - (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', - (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', - (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', - (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', - (byte) 'Z', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', - (byte) 'e', (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', - (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', - (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', - (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', - (byte) 'y', (byte) 'z', (byte) '0', (byte) '1', (byte) '2', - (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7', - (byte) '8', (byte) '9', (byte) '-', (byte) '_' }, - new byte[] { - -9, -9, -9, -9, -9, -9, - -9, -9, -9, // Decimal 0 - 8 - -5, -5, // Whitespace: Tab and Linefeed - -9, -9, // Decimal 11 - 12 - -5, // Whitespace: Carriage Return - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26 - -9, -9, -9, -9, -9, // Decimal 27 - 31 - -5, // Whitespace: Space - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42 - -9, // Plus sign at decimal 43 - -9, // Decimal 44 - 62, // Minus sign at decimal 45 - -9, // Decimal 46 - -9, // Slash at decimal 47 - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine - -9, -9, -9, // Decimal 58 - 60 - -1, // Equals sign at decimal 61 - -9, -9, -9, // Decimal 62 - 64 - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N' - 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z' - -9, -9, -9, -9, // Decimal 91 - 94 - 63, // Underscore at decimal 95 - -9, // Decimal 96 - 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm' - 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z' - -9, -9, -9, -9, -9, // Decimal 123 - 127 - /* -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 128 - 140 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 141 - 153 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 154 - 166 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 167 - 179 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 180 - 192 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 193 - 205 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 206 - 218 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 219 - 231 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 232 - 244 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 245 - 255 */ - }, false), - /** - * Special "ordered" dialect of Base64 described in - * RFC1940. - */ - ORDERED(new byte[] { - (byte) '-', (byte) '0', (byte) '1', (byte) '2', (byte) '3', - (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8', - (byte) '9', (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', - (byte) 'E', (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I', - (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N', - (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', - (byte) 'T', (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X', - (byte) 'Y', (byte) 'Z', (byte) '_', (byte) 'a', (byte) 'b', - (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f', (byte) 'g', - (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', - (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p', (byte) 'q', - (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', (byte) 'v', - (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z' }, - new byte[] { - -9, -9, -9, -9, -9, -9, - -9, -9, -9, // Decimal 0 - 8 - -5, -5, // Whitespace: Tab and Linefeed - -9, -9, // Decimal 11 - 12 - -5, // Whitespace: Carriage Return - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26 - -9, -9, -9, -9, -9, // Decimal 27 - 31 - -5, // Whitespace: Space - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42 - -9, // Plus sign at decimal 43 - -9, // Decimal 44 - 0, // Minus sign at decimal 45 - -9, // Decimal 46 - -9, // Slash at decimal 47 - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, // Numbers zero through nine - -9, -9, -9, // Decimal 58 - 60 - -1, // Equals sign at decimal 61 - -9, -9, -9, // Decimal 62 - 64 - 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, // Letters 'A' through 'M' - 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, // Letters 'N' through 'Z' - -9, -9, -9, -9, // Decimal 91 - 94 - 37, // Underscore at decimal 95 - -9, // Decimal 96 - 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, // Letters 'a' through 'm' - 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // Letters 'n' through 'z' - -9, -9, -9, -9, -9 // Decimal 123 - 127 - /* -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 128 - 140 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 141 - 153 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 154 - 166 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 167 - 179 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 180 - 192 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 193 - 205 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 206 - 218 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 219 - 231 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 232 - 244 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 245 - 255 */ - }, true); - - final byte[] alphabet; - final byte[] decodabet; - final boolean breakLinesByDefault; - - Base64Dialect(byte[] alphabet, byte[] decodabet, boolean breakLinesByDefault) { - this.alphabet = alphabet; - this.decodabet = decodabet; - this.breakLinesByDefault = breakLinesByDefault; - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/base64/Base64Encoder.java b/codec/src/main/java/io/netty/handler/codec/base64/Base64Encoder.java deleted file mode 100644 index b13a1ab0c5..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/base64/Base64Encoder.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.base64; - -import static java.util.Objects.requireNonNull; - -import java.util.List; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.handler.codec.DelimiterBasedFrameDecoder; -import io.netty.handler.codec.Delimiters; -import io.netty.handler.codec.MessageToMessageEncoder; - -/** - * Encodes a {@link ByteBuf} into a Base64-encoded {@link ByteBuf}. - * A typical setup for TCP/IP would be: - *

- * {@link ChannelPipeline} pipeline = ...;
- *
- * // Decoders
- * pipeline.addLast("frameDecoder", new {@link DelimiterBasedFrameDecoder}(80, {@link Delimiters#nulDelimiter()}));
- * pipeline.addLast("base64Decoder", new {@link Base64Decoder}());
- *
- * // Encoder
- * pipeline.addLast("base64Encoder", new {@link Base64Encoder}());
- * 
- */ -@Sharable -public class Base64Encoder extends MessageToMessageEncoder { - - private final boolean breakLines; - private final Base64Dialect dialect; - - public Base64Encoder() { - this(true); - } - - public Base64Encoder(boolean breakLines) { - this(breakLines, Base64Dialect.STANDARD); - } - - public Base64Encoder(boolean breakLines, Base64Dialect dialect) { - requireNonNull(dialect, "dialect"); - - this.breakLines = breakLines; - this.dialect = dialect; - } - - @Override - protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List out) throws Exception { - out.add(Base64.encode(msg, msg.readerIndex(), msg.readableBytes(), breakLines, dialect)); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/base64/package-info.java b/codec/src/main/java/io/netty/handler/codec/base64/package-info.java deleted file mode 100644 index 96a8549612..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/base64/package-info.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * Encoder and decoder which transform a - * Base64-encoded - * {@link java.lang.String} or {@link io.netty.buffer.ByteBuf} - * into a decoded {@link io.netty.buffer.ByteBuf} and vice versa. - */ -package io.netty.handler.codec.base64; 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 deleted file mode 100644 index 2dcbff80e8..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/bytes/ByteArrayDecoder.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.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; -import io.netty.handler.codec.LengthFieldPrepender; -import io.netty.handler.codec.MessageToMessageDecoder; - -/** - * Decodes a received {@link ByteBuf} into an array of bytes. - * A typical setup for TCP/IP would be: - *
- * {@link ChannelPipeline} pipeline = ...;
- *
- * // Decoders
- * pipeline.addLast("frameDecoder",
- *                  new {@link LengthFieldBasedFrameDecoder}(1048576, 0, 4, 0, 4));
- * pipeline.addLast("bytesDecoder",
- *                  new {@link ByteArrayDecoder}());
- *
- * // Encoder
- * pipeline.addLast("frameEncoder", new {@link LengthFieldPrepender}(4));
- * pipeline.addLast("bytesEncoder", new {@link ByteArrayEncoder}());
- * 
- * and then you can use an array of bytes instead of a {@link ByteBuf} - * as a message: - *
- * void channelRead({@link ChannelHandlerContext} ctx, byte[] bytes) {
- *     ...
- * }
- * 
- */ -public class ByteArrayDecoder extends MessageToMessageDecoder { - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { - // copy the ByteBuf content to a byte array - ctx.fireChannelRead(ByteBufUtil.getBytes(msg)); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/bytes/ByteArrayEncoder.java b/codec/src/main/java/io/netty/handler/codec/bytes/ByteArrayEncoder.java deleted file mode 100644 index 7b82c9ba90..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/bytes/ByteArrayEncoder.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.bytes; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.handler.codec.LengthFieldBasedFrameDecoder; -import io.netty.handler.codec.LengthFieldPrepender; -import io.netty.handler.codec.MessageToMessageEncoder; - -import java.util.List; - -/** - * Encodes the requested array of bytes into a {@link ByteBuf}. - * A typical setup for TCP/IP would be: - *
- * {@link ChannelPipeline} pipeline = ...;
- *
- * // Decoders
- * pipeline.addLast("frameDecoder",
- *                  new {@link LengthFieldBasedFrameDecoder}(1048576, 0, 4, 0, 4));
- * pipeline.addLast("bytesDecoder",
- *                  new {@link ByteArrayDecoder}());
- *
- * // Encoder
- * pipeline.addLast("frameEncoder", new {@link LengthFieldPrepender}(4));
- * pipeline.addLast("bytesEncoder", new {@link ByteArrayEncoder}());
- * 
- * and then you can use an array of bytes instead of a {@link ByteBuf} - * as a message: - *
- * void channelRead({@link ChannelHandlerContext} ctx, byte[] bytes) {
- *     ...
- * }
- * 
- */ -@Sharable -public class ByteArrayEncoder extends MessageToMessageEncoder { - @Override - protected void encode(ChannelHandlerContext ctx, byte[] msg, List out) throws Exception { - out.add(Unpooled.wrappedBuffer(msg)); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/bytes/package-info.java b/codec/src/main/java/io/netty/handler/codec/bytes/package-info.java deleted file mode 100644 index 19444651a1..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/bytes/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * Encoder and decoder which transform an array of bytes into a - * {@link io.netty.buffer.ByteBuf} and vice versa. - */ -package io.netty.handler.codec.bytes; diff --git a/codec/src/main/java/io/netty/handler/codec/compression/Brotli.java b/codec/src/main/java/io/netty/handler/codec/compression/Brotli.java deleted file mode 100644 index 19935ee9a1..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/Brotli.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.compression; - -import com.aayushatharva.brotli4j.Brotli4jLoader; -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -public final class Brotli { - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(Brotli.class); - private static final ClassNotFoundException CNFE; - private static Throwable cause; - - static { - ClassNotFoundException cnfe = null; - - try { - Class.forName("com.aayushatharva.brotli4j.Brotli4jLoader", false, - PlatformDependent.getClassLoader(Brotli.class)); - } catch (ClassNotFoundException t) { - cnfe = t; - logger.debug( - "brotli4j not in the classpath; Brotli support will be unavailable."); - } - - CNFE = cnfe; - - // If in the classpath, try to load the native library and initialize brotli4j. - if (cnfe == null) { - cause = Brotli4jLoader.getUnavailabilityCause(); - if (cause != null) { - logger.debug("Failed to load brotli4j; Brotli support will be unavailable.", cause); - } - } - } - - /** - * - * @return true when brotli4j is in the classpath - * and native library is available on this platform and could be loaded - */ - public static boolean isAvailable() { - return CNFE == null && Brotli4jLoader.isAvailable(); - } - - /** - * Throws when brotli support is missing from the classpath or is unavailable on this platform - * @throws Throwable a ClassNotFoundException if brotli4j is missing - * or a UnsatisfiedLinkError if brotli4j native lib can't be loaded - */ - public static void ensureAvailability() throws Throwable { - if (CNFE != null) { - throw CNFE; - } - Brotli4jLoader.ensureAvailability(); - } - - /** - * Returns {@link Throwable} of unavailability cause - */ - public static Throwable cause() { - return cause; - } - - private Brotli() { - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/BrotliDecoder.java b/codec/src/main/java/io/netty/handler/codec/compression/BrotliDecoder.java deleted file mode 100644 index 1a65fe4c3a..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/BrotliDecoder.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.compression; - -import com.aayushatharva.brotli4j.decoder.DecoderJNI; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.ByteToMessageDecoder; -import io.netty.util.internal.ObjectUtil; - -import java.nio.ByteBuffer; - -/** - * Uncompresses a {@link ByteBuf} encoded with the brotli format. - * - * See brotli. - */ -public final class BrotliDecoder extends ByteToMessageDecoder { - - private enum State { - DONE, NEEDS_MORE_INPUT, ERROR - } - - static { - try { - Brotli.ensureAvailability(); - } catch (Throwable throwable) { - throw new ExceptionInInitializerError(throwable); - } - } - - private final int inputBufferSize; - private DecoderJNI.Wrapper decoder; - private boolean destroyed; - - /** - * Creates a new BrotliDecoder with a default 8kB input buffer - */ - public BrotliDecoder() { - this(8 * 1024); - } - - /** - * Creates a new BrotliDecoder - * @param inputBufferSize desired size of the input buffer in bytes - */ - public BrotliDecoder(int inputBufferSize) { - this.inputBufferSize = ObjectUtil.checkPositive(inputBufferSize, "inputBufferSize"); - } - - private ByteBuf pull(ByteBufAllocator alloc) { - ByteBuffer nativeBuffer = decoder.pull(); - // nativeBuffer actually wraps brotli's internal buffer so we need to copy its content - ByteBuf copy = alloc.buffer(nativeBuffer.remaining()); - copy.writeBytes(nativeBuffer); - return copy; - } - - private State decompress(ChannelHandlerContext ctx, ByteBuf input, ByteBufAllocator alloc) { - for (;;) { - switch (decoder.getStatus()) { - case DONE: - return State.DONE; - - case OK: - decoder.push(0); - break; - - case NEEDS_MORE_INPUT: - if (decoder.hasOutput()) { - ctx.fireChannelRead(pull(alloc)); - } - - if (!input.isReadable()) { - return State.NEEDS_MORE_INPUT; - } - - ByteBuffer decoderInputBuffer = decoder.getInputBuffer(); - decoderInputBuffer.clear(); - int readBytes = readBytes(input, decoderInputBuffer); - decoder.push(readBytes); - break; - - case NEEDS_MORE_OUTPUT: - ctx.fireChannelRead(pull(alloc)); - break; - - default: - return State.ERROR; - } - } - } - - private static int readBytes(ByteBuf in, ByteBuffer dest) { - int limit = Math.min(in.readableBytes(), dest.remaining()); - ByteBuffer slice = dest.slice(); - slice.limit(limit); - in.readBytes(slice); - dest.position(dest.position() + limit); - return limit; - } - - @Override - public void handlerAdded0(ChannelHandlerContext ctx) throws Exception { - decoder = new DecoderJNI.Wrapper(inputBufferSize); - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - if (destroyed) { - // Skip data received after finished. - in.skipBytes(in.readableBytes()); - return; - } - - if (!in.isReadable()) { - return; - } - - try { - State state = decompress(ctx, in, ctx.alloc()); - if (state == State.DONE) { - destroy(); - } else if (state == State.ERROR) { - throw new DecompressionException("Brotli stream corrupted"); - } - } catch (Exception e) { - destroy(); - throw e; - } - } - - private void destroy() { - if (!destroyed) { - destroyed = true; - decoder.destroy(); - } - } - - @Override - protected void handlerRemoved0(ChannelHandlerContext ctx) throws Exception { - try { - destroy(); - } finally { - super.handlerRemoved0(ctx); - } - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - try { - destroy(); - } finally { - super.channelInactive(ctx); - } - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/BrotliEncoder.java b/codec/src/main/java/io/netty/handler/codec/compression/BrotliEncoder.java deleted file mode 100644 index 233f132570..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/BrotliEncoder.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import com.aayushatharva.brotli4j.encoder.Encoder; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToByteEncoder; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.internal.ObjectUtil; - -/** - * Compress a {@link ByteBuf} with the brotli format. - * - * See brotli. - */ -@ChannelHandler.Sharable -public final class BrotliEncoder extends MessageToByteEncoder { - - private final Encoder.Parameters parameters; - - /** - * Create a new {@link BrotliEncoder} Instance - * with {@link Encoder.Parameters#setQuality(int)} set to 4 - * and {@link Encoder.Parameters#setMode(Encoder.Mode)} set to {@link Encoder.Mode#TEXT} - */ - public BrotliEncoder() { - this(BrotliOptions.DEFAULT); - } - - /** - * Create a new {@link BrotliEncoder} Instance - * - * @param parameters {@link Encoder.Parameters} Instance - */ - public BrotliEncoder(Encoder.Parameters parameters) { - this.parameters = ObjectUtil.checkNotNull(parameters, "Parameters"); - } - - /** - * Create a new {@link BrotliEncoder} Instance - * - * @param brotliOptions {@link BrotliOptions} to use. - */ - public BrotliEncoder(BrotliOptions brotliOptions) { - this(brotliOptions.parameters()); - } - - @Override - protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) throws Exception { - // NO-OP - } - - @Override - protected ByteBuf allocateBuffer(ChannelHandlerContext ctx, ByteBuf msg, boolean preferDirect) throws Exception { - // If ByteBuf is unreadable, then return EMPTY_BUFFER. - if (!msg.isReadable()) { - return Unpooled.EMPTY_BUFFER; - } - - try { - byte[] uncompressed = ByteBufUtil.getBytes(msg, msg.readerIndex(), msg.readableBytes(), false); - byte[] compressed = Encoder.compress(uncompressed, parameters); - if (preferDirect) { - ByteBuf out = ctx.alloc().ioBuffer(compressed.length); - out.writeBytes(compressed); - return out; - } else { - return Unpooled.wrappedBuffer(compressed); - } - } catch (Exception e) { - ReferenceCountUtil.release(msg); - throw e; - } - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/BrotliOptions.java b/codec/src/main/java/io/netty/handler/codec/compression/BrotliOptions.java deleted file mode 100644 index 737f5d149c..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/BrotliOptions.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import com.aayushatharva.brotli4j.encoder.Encoder; -import io.netty.util.internal.ObjectUtil; - -/** - * {@link BrotliOptions} holds {@link Encoder.Parameters} for - * Brotli compression. - */ -public final class BrotliOptions implements CompressionOptions { - - private final Encoder.Parameters parameters; - - /** - * @see StandardCompressionOptions#brotli() - */ - static final BrotliOptions DEFAULT = new BrotliOptions( - new Encoder.Parameters().setQuality(4).setMode(Encoder.Mode.TEXT) - ); - - BrotliOptions(Encoder.Parameters parameters) { - if (!Brotli.isAvailable()) { - throw new IllegalStateException("Brotli is not available", Brotli.cause()); - } - - this.parameters = ObjectUtil.checkNotNull(parameters, "Parameters"); - } - - public Encoder.Parameters parameters() { - return parameters; - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/ByteBufChecksum.java b/codec/src/main/java/io/netty/handler/codec/compression/ByteBufChecksum.java deleted file mode 100644 index 2ee293f1f4..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/ByteBufChecksum.java +++ /dev/null @@ -1,125 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.util.ByteProcessor; - -import java.nio.ByteBuffer; -import java.util.zip.Adler32; -import java.util.zip.CRC32; -import java.util.zip.Checksum; - -/** - * {@link Checksum} implementation which can directly act on a {@link ByteBuf}. - * - * Implementations may optimize access patterns depending on if the {@link ByteBuf} is backed by a - * byte array ({@link ByteBuf#hasArray()} is {@code true}) or not. - */ -abstract class ByteBufChecksum implements Checksum { - - private final ByteProcessor updateProcessor = value -> { - update(value); - return true; - }; - - static ByteBufChecksum wrapChecksum(Checksum checksum) { - requireNonNull(checksum, "checksum"); - if (checksum instanceof ByteBufChecksum) { - return (ByteBufChecksum) checksum; - } - if (checksum instanceof Adler32) { - return new OptimizedByteBufChecksum((Adler32) checksum) { - @Override - public void update(ByteBuffer b) { - checksum.update(b); - } - }; - } - if (checksum instanceof CRC32) { - return new OptimizedByteBufChecksum((CRC32) checksum) { - @Override - public void update(ByteBuffer b) { - checksum.update(b); - } - }; - } - return new SlowByteBufChecksum<>(checksum); - } - - /** - * @see #update(byte[], int, int) - */ - public void update(ByteBuf b, int off, int len) { - if (b.hasArray()) { - update(b.array(), b.arrayOffset() + off, len); - } else { - b.forEachByte(off, len, updateProcessor); - } - } - - private abstract static class OptimizedByteBufChecksum extends SlowByteBufChecksum { - OptimizedByteBufChecksum(T checksum) { - super(checksum); - } - - @Override - public void update(ByteBuf b, int off, int len) { - if (b.hasArray()) { - update(b.array(), b.arrayOffset() + off, len); - } else { - try { - update(CompressionUtil.safeNioBuffer(b, off, len)); - } catch (Throwable cause) { - throw new Error(); - } - } - } - - public abstract void update(ByteBuffer b); - } - - private static class SlowByteBufChecksum extends ByteBufChecksum { - - protected final T checksum; - - SlowByteBufChecksum(T checksum) { - this.checksum = checksum; - } - - @Override - public void update(int b) { - checksum.update(b); - } - - @Override - public void update(byte[] b, int off, int len) { - checksum.update(b, off, len); - } - - @Override - public long getValue() { - return checksum.getValue(); - } - - @Override - public void reset() { - checksum.reset(); - } - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/Bzip2BitReader.java b/codec/src/main/java/io/netty/handler/codec/compression/Bzip2BitReader.java deleted file mode 100644 index aabb880ac8..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/Bzip2BitReader.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.buffer.ByteBuf; - -/** - * An bit reader that allows the reading of single bit booleans, bit strings of - * arbitrary length (up to 32 bits), and bit aligned 32-bit integers. A single byte - * at a time is read from the {@link ByteBuf} when more bits are required. - */ -class Bzip2BitReader { - /** - * Maximum count of possible readable bytes to check. - */ - private static final int MAX_COUNT_OF_READABLE_BYTES = Integer.MAX_VALUE >>> 3; - - /** - * The {@link ByteBuf} from which to read data. - */ - private ByteBuf in; - - /** - * A buffer of bits read from the input stream that have not yet been returned. - */ - private long bitBuffer; - - /** - * The number of bits currently buffered in {@link #bitBuffer}. - */ - private int bitCount; - - /** - * Set the {@link ByteBuf} from which to read data. - */ - void setByteBuf(ByteBuf in) { - this.in = in; - } - - /** - * Reads up to 32 bits from the {@link ByteBuf}. - * @param count The number of bits to read (maximum {@code 32} as a size of {@code int}) - * @return The bits requested, right-aligned within the integer - */ - int readBits(final int count) { - if (count < 0 || count > 32) { - throw new IllegalArgumentException("count: " + count + " (expected: 0-32 )"); - } - int bitCount = this.bitCount; - long bitBuffer = this.bitBuffer; - - if (bitCount < count) { - long readData; - int offset; - switch (in.readableBytes()) { - case 1: { - readData = in.readUnsignedByte(); - offset = 8; - break; - } - case 2: { - readData = in.readUnsignedShort(); - offset = 16; - break; - } - case 3: { - readData = in.readUnsignedMedium(); - offset = 24; - break; - } - default: { - readData = in.readUnsignedInt(); - offset = 32; - break; - } - } - - bitBuffer = bitBuffer << offset | readData; - bitCount += offset; - this.bitBuffer = bitBuffer; - } - - this.bitCount = bitCount -= count; - return (int) (bitBuffer >>> bitCount & (count != 32 ? (1 << count) - 1 : 0xFFFFFFFFL)); - } - - /** - * Reads a single bit from the {@link ByteBuf}. - * @return {@code true} if the bit read was {@code 1}, otherwise {@code false} - */ - boolean readBoolean() { - return readBits(1) != 0; - } - - /** - * Reads 32 bits of input as an integer. - * @return The integer read - */ - int readInt() { - return readBits(32); - } - - /** - * Refill the {@link ByteBuf} by one byte. - */ - void refill() { - int readData = in.readUnsignedByte(); - bitBuffer = bitBuffer << 8 | readData; - bitCount += 8; - } - - /** - * Checks that at least one bit is available for reading. - * @return {@code true} if one bit is available for reading, otherwise {@code false} - */ - boolean isReadable() { - return bitCount > 0 || in.isReadable(); - } - - /** - * Checks that the specified number of bits available for reading. - * @param count The number of bits to check - * @return {@code true} if {@code count} bits are available for reading, otherwise {@code false} - */ - boolean hasReadableBits(int count) { - if (count < 0) { - throw new IllegalArgumentException("count: " + count + " (expected value greater than 0)"); - } - return bitCount >= count || (in.readableBytes() << 3 & Integer.MAX_VALUE) >= count - bitCount; - } - - /** - * Checks that the specified number of bytes available for reading. - * @param count The number of bytes to check - * @return {@code true} if {@code count} bytes are available for reading, otherwise {@code false} - */ - boolean hasReadableBytes(int count) { - if (count < 0 || count > MAX_COUNT_OF_READABLE_BYTES) { - throw new IllegalArgumentException("count: " + count - + " (expected: 0-" + MAX_COUNT_OF_READABLE_BYTES + ')'); - } - return hasReadableBits(count << 3); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/Bzip2BitWriter.java b/codec/src/main/java/io/netty/handler/codec/compression/Bzip2BitWriter.java deleted file mode 100644 index 6916bc0b17..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/Bzip2BitWriter.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.buffer.ByteBuf; - -/** - * A bit writer that allows the writing of single bit booleans, unary numbers, bit strings - * of arbitrary length (up to 32 bits), and bit aligned 32-bit integers. A single byte at a - * time is written to the {@link ByteBuf} when sufficient bits have been accumulated. - */ -final class Bzip2BitWriter { - /** - * A buffer of bits waiting to be written to the output stream. - */ - private long bitBuffer; - - /** - * The number of bits currently buffered in {@link #bitBuffer}. - */ - private int bitCount; - - /** - * Writes up to 32 bits to the output {@link ByteBuf}. - * @param count The number of bits to write (maximum {@code 32} as a size of {@code int}) - * @param value The bits to write - */ - void writeBits(ByteBuf out, final int count, final long value) { - if (count < 0 || count > 32) { - throw new IllegalArgumentException("count: " + count + " (expected: 0-32)"); - } - int bitCount = this.bitCount; - long bitBuffer = this.bitBuffer | value << 64 - count >>> bitCount; - bitCount += count; - - if (bitCount >= 32) { - out.writeInt((int) (bitBuffer >>> 32)); - bitBuffer <<= 32; - bitCount -= 32; - } - this.bitBuffer = bitBuffer; - this.bitCount = bitCount; - } - - /** - * Writes a single bit to the output {@link ByteBuf}. - * @param value The bit to write - */ - void writeBoolean(ByteBuf out, final boolean value) { - int bitCount = this.bitCount + 1; - long bitBuffer = this.bitBuffer | (value ? 1L << 64 - bitCount : 0L); - - if (bitCount == 32) { - out.writeInt((int) (bitBuffer >>> 32)); - bitBuffer = 0; - bitCount = 0; - } - this.bitBuffer = bitBuffer; - this.bitCount = bitCount; - } - - /** - * Writes a zero-terminated unary number to the output {@link ByteBuf}. - * Example of the output for value = 6: {@code 1111110} - * @param value The number of {@code 1} to write - */ - void writeUnary(ByteBuf out, int value) { - if (value < 0) { - throw new IllegalArgumentException("value: " + value + " (expected 0 or more)"); - } - while (value-- > 0) { - writeBoolean(out, true); - } - writeBoolean(out, false); - } - - /** - * Writes an integer as 32 bits to the output {@link ByteBuf}. - * @param value The integer to write - */ - void writeInt(ByteBuf out, final int value) { - writeBits(out, 32, value); - } - - /** - * Writes any remaining bits to the output {@link ByteBuf}, - * zero padding to a whole byte as required. - */ - void flush(ByteBuf out) { - final int bitCount = this.bitCount; - - if (bitCount > 0) { - final long bitBuffer = this.bitBuffer; - final int shiftToRight = 64 - bitCount; - - if (bitCount <= 8) { - out.writeByte((int) (bitBuffer >>> shiftToRight << 8 - bitCount)); - } else if (bitCount <= 16) { - out.writeShort((int) (bitBuffer >>> shiftToRight << 16 - bitCount)); - } else if (bitCount <= 24) { - out.writeMedium((int) (bitBuffer >>> shiftToRight << 24 - bitCount)); - } else { - out.writeInt((int) (bitBuffer >>> shiftToRight << 32 - bitCount)); - } - } - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/Bzip2BlockCompressor.java b/codec/src/main/java/io/netty/handler/codec/compression/Bzip2BlockCompressor.java deleted file mode 100644 index b91d23250c..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/Bzip2BlockCompressor.java +++ /dev/null @@ -1,290 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.buffer.ByteBuf; -import io.netty.util.ByteProcessor; - -import static io.netty.handler.codec.compression.Bzip2Constants.*; - -/** - * Compresses and writes a single Bzip2 block.

- * - * Block encoding consists of the following stages:
- * 1. Run-Length Encoding[1] - {@link #write(int)}
- * 2. Burrows Wheeler Transform - {@link #close(ByteBuf)} (through {@link Bzip2DivSufSort})
- * 3. Write block header - {@link #close(ByteBuf)}
- * 4. Move To Front Transform - {@link #close(ByteBuf)} (through {@link Bzip2HuffmanStageEncoder})
- * 5. Run-Length Encoding[2] - {@link #close(ByteBuf)} (through {@link Bzip2HuffmanStageEncoder})
- * 6. Create and write Huffman tables - {@link #close(ByteBuf)} (through {@link Bzip2HuffmanStageEncoder})
- * 7. Huffman encode and write data - {@link #close(ByteBuf)} (through {@link Bzip2HuffmanStageEncoder}) - */ -final class Bzip2BlockCompressor { - private final ByteProcessor writeProcessor = this::write; - - /** - * A writer that provides bit-level writes. - */ - private final Bzip2BitWriter writer; - - /** - * CRC builder for the block. - */ - private final Crc32 crc = new Crc32(); - - /** - * The RLE'd block data. - */ - private final byte[] block; - - /** - * Current length of the data within the {@link #block} array. - */ - private int blockLength; - - /** - * A limit beyond which new data will not be accepted into the block. - */ - private final int blockLengthLimit; - - /** - * The values that are present within the RLE'd block data. For each index, {@code true} if that - * value is present within the data, otherwise {@code false}. - */ - private final boolean[] blockValuesPresent = new boolean[256]; - - /** - * The Burrows Wheeler Transformed block data. - */ - private final int[] bwtBlock; - - /** - * The current RLE value being accumulated (undefined when {@link #rleLength} is 0). - */ - private int rleCurrentValue = -1; - - /** - * The repeat count of the current RLE value. - */ - private int rleLength; - - /** - * @param writer The {@link Bzip2BitWriter} which provides bit-level writes - * @param blockSize The declared block size in bytes. Up to this many bytes will be accepted - * into the block after Run-Length Encoding is applied - */ - Bzip2BlockCompressor(final Bzip2BitWriter writer, final int blockSize) { - this.writer = writer; - - // One extra byte is added to allow for the block wrap applied in close() - block = new byte[blockSize + 1]; - bwtBlock = new int[blockSize + 1]; - blockLengthLimit = blockSize - 6; // 5 bytes for one RLE run plus one byte - see {@link #write(int)} - } - - /** - * Write the Huffman symbol to output byte map. - */ - private void writeSymbolMap(ByteBuf out) { - Bzip2BitWriter writer = this.writer; - - final boolean[] blockValuesPresent = this.blockValuesPresent; - final boolean[] condensedInUse = new boolean[16]; - - for (int i = 0; i < condensedInUse.length; i++) { - for (int j = 0, k = i << 4; j < HUFFMAN_SYMBOL_RANGE_SIZE; j++, k++) { - if (blockValuesPresent[k]) { - condensedInUse[i] = true; - } - } - } - - for (boolean isCondensedInUse : condensedInUse) { - writer.writeBoolean(out, isCondensedInUse); - } - - for (int i = 0; i < condensedInUse.length; i++) { - if (condensedInUse[i]) { - for (int j = 0, k = i << 4; j < HUFFMAN_SYMBOL_RANGE_SIZE; j++, k++) { - writer.writeBoolean(out, blockValuesPresent[k]); - } - } - } - } - - /** - * Writes an RLE run to the block array, updating the block CRC and present values array as required. - * @param value The value to write - * @param runLength The run length of the value to write - */ - private void writeRun(final int value, int runLength) { - final int blockLength = this.blockLength; - final byte[] block = this.block; - - blockValuesPresent[value] = true; - crc.updateCRC(value, runLength); - - final byte byteValue = (byte) value; - switch (runLength) { - case 1: - block[blockLength] = byteValue; - this.blockLength = blockLength + 1; - break; - case 2: - block[blockLength] = byteValue; - block[blockLength + 1] = byteValue; - this.blockLength = blockLength + 2; - break; - case 3: - block[blockLength] = byteValue; - block[blockLength + 1] = byteValue; - block[blockLength + 2] = byteValue; - this.blockLength = blockLength + 3; - break; - default: - runLength -= 4; - blockValuesPresent[runLength] = true; - block[blockLength] = byteValue; - block[blockLength + 1] = byteValue; - block[blockLength + 2] = byteValue; - block[blockLength + 3] = byteValue; - block[blockLength + 4] = (byte) runLength; - this.blockLength = blockLength + 5; - break; - } - } - - /** - * Writes a byte to the block, accumulating to an RLE run where possible. - * @param value The byte to write - * @return {@code true} if the byte was written, or {@code false} if the block is already full - */ - boolean write(final int value) { - if (blockLength > blockLengthLimit) { - return false; - } - final int rleCurrentValue = this.rleCurrentValue; - final int rleLength = this.rleLength; - - if (rleLength == 0) { - this.rleCurrentValue = value; - this.rleLength = 1; - } else if (rleCurrentValue != value) { - // This path commits us to write 6 bytes - one RLE run (5 bytes) plus one extra - writeRun(rleCurrentValue & 0xff, rleLength); - this.rleCurrentValue = value; - this.rleLength = 1; - } else { - if (rleLength == 254) { - writeRun(rleCurrentValue & 0xff, 255); - this.rleLength = 0; - } else { - this.rleLength = rleLength + 1; - } - } - return true; - } - - /** - * Writes an array to the block. - * @param buffer The buffer to write - * @param offset The offset within the input data to write from - * @param length The number of bytes of input data to write - * @return The actual number of input bytes written. May be less than the number requested, or - * zero if the block is already full - */ - int write(final ByteBuf buffer, int offset, int length) { - int index = buffer.forEachByte(offset, length, writeProcessor); - return index == -1 ? length : index - offset; - } - - /** - * Compresses and writes out the block. - */ - void close(ByteBuf out) { - // If an RLE run is in progress, write it out - if (rleLength > 0) { - writeRun(rleCurrentValue & 0xff, rleLength); - } - - // Apply a one byte block wrap required by the BWT implementation - block[blockLength] = block[0]; - - // Perform the Burrows Wheeler Transform - Bzip2DivSufSort divSufSort = new Bzip2DivSufSort(block, bwtBlock, blockLength); - int bwtStartPointer = divSufSort.bwt(); - - Bzip2BitWriter writer = this.writer; - - // Write out the block header - writer.writeBits(out, 24, BLOCK_HEADER_MAGIC_1); - writer.writeBits(out, 24, BLOCK_HEADER_MAGIC_2); - writer.writeInt(out, crc.getCRC()); - writer.writeBoolean(out, false); // Randomised block flag. We never create randomised blocks - writer.writeBits(out, 24, bwtStartPointer); - - // Write out the symbol map - writeSymbolMap(out); - - // Perform the Move To Front Transform and Run-Length Encoding[2] stages - Bzip2MTFAndRLE2StageEncoder mtfEncoder = new Bzip2MTFAndRLE2StageEncoder(bwtBlock, blockLength, - blockValuesPresent); - mtfEncoder.encode(); - - // Perform the Huffman Encoding stage and write out the encoded data - Bzip2HuffmanStageEncoder huffmanEncoder = new Bzip2HuffmanStageEncoder(writer, - mtfEncoder.mtfBlock(), - mtfEncoder.mtfLength(), - mtfEncoder.mtfAlphabetSize(), - mtfEncoder.mtfSymbolFrequencies()); - huffmanEncoder.encode(out); - } - - /** - * Gets available size of the current block. - * @return Number of available bytes which can be written - */ - int availableSize() { - if (blockLength == 0) { - return blockLengthLimit + 2; - } - return blockLengthLimit - blockLength + 1; - } - - /** - * Determines if the block is full and ready for compression. - * @return {@code true} if the block is full, otherwise {@code false} - */ - boolean isFull() { - return blockLength > blockLengthLimit; - } - - /** - * Determines if any bytes have been written to the block. - * @return {@code true} if one or more bytes has been written to the block, otherwise {@code false} - */ - boolean isEmpty() { - return blockLength == 0 && rleLength == 0; - } - - /** - * Gets the CRC of the completed block. Only valid after calling {@link #close(ByteBuf)}. - * @return The block's CRC - */ - int crc() { - return crc.getCRC(); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/Bzip2BlockDecompressor.java b/codec/src/main/java/io/netty/handler/codec/compression/Bzip2BlockDecompressor.java deleted file mode 100644 index 801900c487..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/Bzip2BlockDecompressor.java +++ /dev/null @@ -1,347 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import static io.netty.handler.codec.compression.Bzip2Constants.*; - -/** - * Reads and decompresses a single Bzip2 block.

- * - * Block decoding consists of the following stages:
- * 1. Read block header
- * 2. Read Huffman tables
- * 3. Read and decode Huffman encoded data - {@link #decodeHuffmanData(Bzip2HuffmanStageDecoder)}
- * 4. Run-Length Decoding[2] - {@link #decodeHuffmanData(Bzip2HuffmanStageDecoder)}
- * 5. Inverse Move To Front Transform - {@link #decodeHuffmanData(Bzip2HuffmanStageDecoder)}
- * 6. Inverse Burrows Wheeler Transform - {@link #initialiseInverseBWT()}
- * 7. Run-Length Decoding[1] - {@link #read()}
- * 8. Optional Block De-Randomisation - {@link #read()} (through {@link #decodeNextBWTByte()}) - */ -final class Bzip2BlockDecompressor { - /** - * A reader that provides bit-level reads. - */ - private final Bzip2BitReader reader; - - /** - * Calculates the block CRC from the fully decoded bytes of the block. - */ - private final Crc32 crc = new Crc32(); - - /** - * The CRC of the current block as read from the block header. - */ - private final int blockCRC; - - /** - * {@code true} if the current block is randomised, otherwise {@code false}. - */ - private final boolean blockRandomised; - - /* Huffman Decoding stage */ - /** - * The end-of-block Huffman symbol. Decoding of the block ends when this is encountered. - */ - int huffmanEndOfBlockSymbol; - - /** - * Bitmap, of ranges of 16 bytes, present/not present. - */ - int huffmanInUse16; - - /** - * A map from Huffman symbol index to output character. Some types of data (e.g. ASCII text) - * may contain only a limited number of byte values; Huffman symbols are only allocated to - * those values that actually occur in the uncompressed data. - */ - final byte[] huffmanSymbolMap = new byte[256]; - - /* Move To Front stage */ - /** - * Counts of each byte value within the {@link Bzip2BlockDecompressor#huffmanSymbolMap} data. - * Collected at the Move To Front stage, consumed by the Inverse Burrows Wheeler Transform stage. - */ - private final int[] bwtByteCounts = new int[256]; - - /** - * The Burrows-Wheeler Transform processed data. Read at the Move To Front stage, consumed by the - * Inverse Burrows Wheeler Transform stage. - */ - private final byte[] bwtBlock; - - /** - * Starting pointer into BWT for after untransform. - */ - private final int bwtStartPointer; - - /* Inverse Burrows-Wheeler Transform stage */ - /** - * At each position contains the union of :- - * An output character (8 bits) - * A pointer from each position to its successor (24 bits, left shifted 8 bits) - * As the pointer cannot exceed the maximum block size of 900k, 24 bits is more than enough to - * hold it; Folding the character data into the spare bits while performing the inverse BWT, - * when both pieces of information are available, saves a large number of memory accesses in - * the final decoding stages. - */ - private int[] bwtMergedPointers; - - /** - * The current merged pointer into the Burrow-Wheeler Transform array. - */ - private int bwtCurrentMergedPointer; - - /** - * The actual length in bytes of the current block at the Inverse Burrows Wheeler Transform - * stage (before final Run-Length Decoding). - */ - private int bwtBlockLength; - - /** - * The number of output bytes that have been decoded up to the Inverse Burrows Wheeler Transform stage. - */ - private int bwtBytesDecoded; - - /* Run-Length Encoding and Random Perturbation stage */ - /** - * The most recently RLE decoded byte. - */ - private int rleLastDecodedByte = -1; - - /** - * The number of previous identical output bytes decoded. After 4 identical bytes, the next byte - * decoded is an RLE repeat count. - */ - private int rleAccumulator; - - /** - * The RLE repeat count of the current decoded byte. When this reaches zero, a new byte is decoded. - */ - private int rleRepeat; - - /** - * If the current block is randomised, the position within the RNUMS randomisation array. - */ - private int randomIndex; - - /** - * If the current block is randomised, the remaining count at the current RNUMS position. - */ - private int randomCount = Bzip2Rand.rNums(0) - 1; - - /** - * Table for Move To Front transformations. - */ - private final Bzip2MoveToFrontTable symbolMTF = new Bzip2MoveToFrontTable(); - - // This variables is used to save current state if we haven't got enough readable bits - private int repeatCount; - private int repeatIncrement = 1; - private int mtfValue; - - Bzip2BlockDecompressor(final int blockSize, final int blockCRC, final boolean blockRandomised, - final int bwtStartPointer, final Bzip2BitReader reader) { - - bwtBlock = new byte[blockSize]; - - this.blockCRC = blockCRC; - this.blockRandomised = blockRandomised; - this.bwtStartPointer = bwtStartPointer; - - this.reader = reader; - } - - /** - * Reads the Huffman encoded data from the input stream, performs Run-Length Decoding and - * applies the Move To Front transform to reconstruct the Burrows-Wheeler Transform array. - */ - boolean decodeHuffmanData(final Bzip2HuffmanStageDecoder huffmanDecoder) { - final Bzip2BitReader reader = this.reader; - final byte[] bwtBlock = this.bwtBlock; - final byte[] huffmanSymbolMap = this.huffmanSymbolMap; - final int streamBlockSize = this.bwtBlock.length; - final int huffmanEndOfBlockSymbol = this.huffmanEndOfBlockSymbol; - final int[] bwtByteCounts = this.bwtByteCounts; - final Bzip2MoveToFrontTable symbolMTF = this.symbolMTF; - - int bwtBlockLength = this.bwtBlockLength; - int repeatCount = this.repeatCount; - int repeatIncrement = this.repeatIncrement; - int mtfValue = this.mtfValue; - - for (;;) { - if (!reader.hasReadableBits(HUFFMAN_DECODE_MAX_CODE_LENGTH)) { - this.bwtBlockLength = bwtBlockLength; - this.repeatCount = repeatCount; - this.repeatIncrement = repeatIncrement; - this.mtfValue = mtfValue; - return false; - } - final int nextSymbol = huffmanDecoder.nextSymbol(); - - if (nextSymbol == HUFFMAN_SYMBOL_RUNA) { - repeatCount += repeatIncrement; - repeatIncrement <<= 1; - } else if (nextSymbol == HUFFMAN_SYMBOL_RUNB) { - repeatCount += repeatIncrement << 1; - repeatIncrement <<= 1; - } else { - if (repeatCount > 0) { - if (bwtBlockLength + repeatCount > streamBlockSize) { - throw new DecompressionException("block exceeds declared block size"); - } - final byte nextByte = huffmanSymbolMap[mtfValue]; - bwtByteCounts[nextByte & 0xff] += repeatCount; - while (--repeatCount >= 0) { - bwtBlock[bwtBlockLength++] = nextByte; - } - - repeatCount = 0; - repeatIncrement = 1; - } - - if (nextSymbol == huffmanEndOfBlockSymbol) { - break; - } - - if (bwtBlockLength >= streamBlockSize) { - throw new DecompressionException("block exceeds declared block size"); - } - - mtfValue = symbolMTF.indexToFront(nextSymbol - 1) & 0xff; - - final byte nextByte = huffmanSymbolMap[mtfValue]; - bwtByteCounts[nextByte & 0xff]++; - bwtBlock[bwtBlockLength++] = nextByte; - } - } - if (bwtBlockLength > MAX_BLOCK_LENGTH) { - throw new DecompressionException("block length exceeds max block length: " - + bwtBlockLength + " > " + MAX_BLOCK_LENGTH); - } - - this.bwtBlockLength = bwtBlockLength; - initialiseInverseBWT(); - return true; - } - - /** - * Set up the Inverse Burrows-Wheeler Transform merged pointer array. - */ - private void initialiseInverseBWT() { - final int bwtStartPointer = this.bwtStartPointer; - final byte[] bwtBlock = this.bwtBlock; - final int[] bwtMergedPointers = new int[bwtBlockLength]; - final int[] characterBase = new int[256]; - - if (bwtStartPointer < 0 || bwtStartPointer >= bwtBlockLength) { - throw new DecompressionException("start pointer invalid"); - } - - // Cumulative character counts - System.arraycopy(bwtByteCounts, 0, characterBase, 1, 255); - for (int i = 2; i <= 255; i++) { - characterBase[i] += characterBase[i - 1]; - } - - // Merged-Array Inverse Burrows-Wheeler Transform - // Combining the output characters and forward pointers into a single array here, where we - // have already read both of the corresponding values, cuts down on memory accesses in the - // final walk through the array - for (int i = 0; i < bwtBlockLength; i++) { - int value = bwtBlock[i] & 0xff; - bwtMergedPointers[characterBase[value]++] = (i << 8) + value; - } - - this.bwtMergedPointers = bwtMergedPointers; - bwtCurrentMergedPointer = bwtMergedPointers[bwtStartPointer]; - } - - /** - * Decodes a byte from the final Run-Length Encoding stage, pulling a new byte from the - * Burrows-Wheeler Transform stage when required. - * @return The decoded byte, or -1 if there are no more bytes - */ - public int read() { - while (rleRepeat < 1) { - if (bwtBytesDecoded == bwtBlockLength) { - return -1; - } - - int nextByte = decodeNextBWTByte(); - if (nextByte != rleLastDecodedByte) { - // New byte, restart accumulation - rleLastDecodedByte = nextByte; - rleRepeat = 1; - rleAccumulator = 1; - crc.updateCRC(nextByte); - } else { - if (++rleAccumulator == 4) { - // Accumulation complete, start repetition - int rleRepeat = decodeNextBWTByte() + 1; - this.rleRepeat = rleRepeat; - rleAccumulator = 0; - crc.updateCRC(nextByte, rleRepeat); - } else { - rleRepeat = 1; - crc.updateCRC(nextByte); - } - } - } - rleRepeat--; - - return rleLastDecodedByte; - } - - /** - * Decodes a byte from the Burrows-Wheeler Transform stage. If the block has randomisation - * applied, reverses the randomisation. - * @return The decoded byte - */ - private int decodeNextBWTByte() { - int mergedPointer = bwtCurrentMergedPointer; - int nextDecodedByte = mergedPointer & 0xff; - bwtCurrentMergedPointer = bwtMergedPointers[mergedPointer >>> 8]; - - if (blockRandomised) { - if (--randomCount == 0) { - nextDecodedByte ^= 1; - randomIndex = (randomIndex + 1) % 512; - randomCount = Bzip2Rand.rNums(randomIndex); - } - } - bwtBytesDecoded++; - - return nextDecodedByte; - } - - public int blockLength() { - return bwtBlockLength; - } - - /** - * Verify and return the block CRC. This method may only be called - * after all of the block's bytes have been read. - * @return The block CRC - */ - int checkCRC() { - final int computedBlockCRC = crc.getCRC(); - if (blockCRC != computedBlockCRC) { - throw new DecompressionException("block CRC error"); - } - return computedBlockCRC; - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/Bzip2Constants.java b/codec/src/main/java/io/netty/handler/codec/compression/Bzip2Constants.java deleted file mode 100644 index 087f45faa0..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/Bzip2Constants.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -/** - * Constants for both the {@link Bzip2Encoder} and the {@link Bzip2Decoder}. - */ -final class Bzip2Constants { - - /** - * Magic number of Bzip2 stream. - */ - static final int MAGIC_NUMBER = 'B' << 16 | 'Z' << 8 | 'h'; - - /** - * Block header magic number. Equals to BCD (pi). - */ - static final int BLOCK_HEADER_MAGIC_1 = 0x314159; - static final int BLOCK_HEADER_MAGIC_2 = 0x265359; - - /** - * End of stream magic number. Equals to BCD sqrt(pi). - */ - static final int END_OF_STREAM_MAGIC_1 = 0x177245; - static final int END_OF_STREAM_MAGIC_2 = 0x385090; - - /** - * Base block size. - */ - static final int BASE_BLOCK_SIZE = 100000; - - /** - * Minimum and maximum size of one block. - * Must be multiplied by {@link Bzip2Constants#BASE_BLOCK_SIZE}. - */ - static final int MIN_BLOCK_SIZE = 1; - static final int MAX_BLOCK_SIZE = 9; - - static final int MAX_BLOCK_LENGTH = MAX_BLOCK_SIZE * BASE_BLOCK_SIZE; - - /** - * Maximum possible Huffman alphabet size. - */ - static final int HUFFMAN_MAX_ALPHABET_SIZE = 258; - - /** - * The longest Huffman code length created by the encoder. - */ - static final int HUFFMAN_ENCODE_MAX_CODE_LENGTH = 20; - - /** - * The longest Huffman code length accepted by the decoder. - */ - static final int HUFFMAN_DECODE_MAX_CODE_LENGTH = 23; - - /** - * Huffman symbols used for run-length encoding. - */ - static final int HUFFMAN_SYMBOL_RUNA = 0; - static final int HUFFMAN_SYMBOL_RUNB = 1; - - /** - * Huffman symbols range size for Huffman used map. - */ - static final int HUFFMAN_SYMBOL_RANGE_SIZE = 16; - - /** - * Maximum length of zero-terminated bit runs of MTF'ed Huffman table. - */ - static final int HUFFMAN_SELECTOR_LIST_MAX_LENGTH = 6; - - /** - * Number of symbols decoded after which a new Huffman table is selected. - */ - static final int HUFFMAN_GROUP_RUN_LENGTH = 50; - - /** - * Maximum possible number of Huffman table selectors. - */ - static final int MAX_SELECTORS = 2 + 900000 / HUFFMAN_GROUP_RUN_LENGTH; // 18002 - - /** - * Minimum number of alternative Huffman tables. - */ - static final int HUFFMAN_MINIMUM_TABLES = 2; - - /** - * Maximum number of alternative Huffman tables. - */ - static final int HUFFMAN_MAXIMUM_TABLES = 6; - - private Bzip2Constants() { } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/Bzip2Decoder.java b/codec/src/main/java/io/netty/handler/codec/compression/Bzip2Decoder.java deleted file mode 100644 index f28bad932b..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/Bzip2Decoder.java +++ /dev/null @@ -1,329 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.ByteToMessageDecoder; - -import static io.netty.handler.codec.compression.Bzip2Constants.*; - -/** - * Uncompresses a {@link ByteBuf} encoded with the Bzip2 format. - * - * See Bzip2. - */ -public class Bzip2Decoder extends ByteToMessageDecoder { - /** - * Current state of stream. - */ - private enum State { - INIT, - INIT_BLOCK, - INIT_BLOCK_PARAMS, - RECEIVE_HUFFMAN_USED_MAP, - RECEIVE_HUFFMAN_USED_BITMAPS, - RECEIVE_SELECTORS_NUMBER, - RECEIVE_SELECTORS, - RECEIVE_HUFFMAN_LENGTH, - DECODE_HUFFMAN_DATA, - EOF - } - private State currentState = State.INIT; - - /** - * A reader that provides bit-level reads. - */ - private final Bzip2BitReader reader = new Bzip2BitReader(); - - /** - * The decompressor for the current block. - */ - private Bzip2BlockDecompressor blockDecompressor; - - /** - * Bzip2 Huffman coding stage. - */ - private Bzip2HuffmanStageDecoder huffmanStageDecoder; - - /** - * Always: in the range 0 .. 9. The current block size is 100000 * this number. - */ - private int blockSize; - - /** - * The CRC of the current block as read from the block header. - */ - private int blockCRC; - - /** - * The merged CRC of all blocks decompressed so far. - */ - private int streamCRC; - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - if (!in.isReadable()) { - return; - } - - final Bzip2BitReader reader = this.reader; - reader.setByteBuf(in); - - for (;;) { - switch (currentState) { - case INIT: - if (in.readableBytes() < 4) { - return; - } - int magicNumber = in.readUnsignedMedium(); - if (magicNumber != MAGIC_NUMBER) { - throw new DecompressionException("Unexpected stream identifier contents. Mismatched bzip2 " + - "protocol version?"); - } - int blockSize = in.readByte() - '0'; - if (blockSize < MIN_BLOCK_SIZE || blockSize > MAX_BLOCK_SIZE) { - throw new DecompressionException("block size is invalid"); - } - this.blockSize = blockSize * BASE_BLOCK_SIZE; - - streamCRC = 0; - currentState = State.INIT_BLOCK; - // fall through - case INIT_BLOCK: - if (!reader.hasReadableBytes(10)) { - return; - } - // Get the block magic bytes. - final int magic1 = reader.readBits(24); - final int magic2 = reader.readBits(24); - if (magic1 == END_OF_STREAM_MAGIC_1 && magic2 == END_OF_STREAM_MAGIC_2) { - // End of stream was reached. Check the combined CRC. - final int storedCombinedCRC = reader.readInt(); - if (storedCombinedCRC != streamCRC) { - throw new DecompressionException("stream CRC error"); - } - currentState = State.EOF; - break; - } - if (magic1 != BLOCK_HEADER_MAGIC_1 || magic2 != BLOCK_HEADER_MAGIC_2) { - throw new DecompressionException("bad block header"); - } - blockCRC = reader.readInt(); - currentState = State.INIT_BLOCK_PARAMS; - // fall through - case INIT_BLOCK_PARAMS: - if (!reader.hasReadableBits(25)) { - return; - } - final boolean blockRandomised = reader.readBoolean(); - final int bwtStartPointer = reader.readBits(24); - - blockDecompressor = new Bzip2BlockDecompressor(this.blockSize, blockCRC, - blockRandomised, bwtStartPointer, reader); - currentState = State.RECEIVE_HUFFMAN_USED_MAP; - // fall through - case RECEIVE_HUFFMAN_USED_MAP: - if (!reader.hasReadableBits(16)) { - return; - } - blockDecompressor.huffmanInUse16 = reader.readBits(16); - currentState = State.RECEIVE_HUFFMAN_USED_BITMAPS; - // fall through - case RECEIVE_HUFFMAN_USED_BITMAPS: - Bzip2BlockDecompressor blockDecompressor = this.blockDecompressor; - final int inUse16 = blockDecompressor.huffmanInUse16; - final int bitNumber = Integer.bitCount(inUse16); - final byte[] huffmanSymbolMap = blockDecompressor.huffmanSymbolMap; - - if (!reader.hasReadableBits(bitNumber * HUFFMAN_SYMBOL_RANGE_SIZE + 3)) { - return; - } - - int huffmanSymbolCount = 0; - if (bitNumber > 0) { - for (int i = 0; i < 16; i++) { - if ((inUse16 & 1 << 15 >>> i) != 0) { - for (int j = 0, k = i << 4; j < HUFFMAN_SYMBOL_RANGE_SIZE; j++, k++) { - if (reader.readBoolean()) { - huffmanSymbolMap[huffmanSymbolCount++] = (byte) k; - } - } - } - } - } - blockDecompressor.huffmanEndOfBlockSymbol = huffmanSymbolCount + 1; - - int totalTables = reader.readBits(3); - if (totalTables < HUFFMAN_MINIMUM_TABLES || totalTables > HUFFMAN_MAXIMUM_TABLES) { - throw new DecompressionException("incorrect huffman groups number"); - } - int alphaSize = huffmanSymbolCount + 2; - if (alphaSize > HUFFMAN_MAX_ALPHABET_SIZE) { - throw new DecompressionException("incorrect alphabet size"); - } - huffmanStageDecoder = new Bzip2HuffmanStageDecoder(reader, totalTables, alphaSize); - currentState = State.RECEIVE_SELECTORS_NUMBER; - // fall through - case RECEIVE_SELECTORS_NUMBER: - if (!reader.hasReadableBits(15)) { - return; - } - int totalSelectors = reader.readBits(15); - if (totalSelectors < 1 || totalSelectors > MAX_SELECTORS) { - throw new DecompressionException("incorrect selectors number"); - } - huffmanStageDecoder.selectors = new byte[totalSelectors]; - - currentState = State.RECEIVE_SELECTORS; - // fall through - case RECEIVE_SELECTORS: - Bzip2HuffmanStageDecoder huffmanStageDecoder = this.huffmanStageDecoder; - byte[] selectors = huffmanStageDecoder.selectors; - totalSelectors = selectors.length; - final Bzip2MoveToFrontTable tableMtf = huffmanStageDecoder.tableMTF; - - int currSelector; - // Get zero-terminated bit runs (0..62) of MTF'ed Huffman table. length = 1..6 - for (currSelector = huffmanStageDecoder.currentSelector; - currSelector < totalSelectors; currSelector++) { - if (!reader.hasReadableBits(HUFFMAN_SELECTOR_LIST_MAX_LENGTH)) { - // Save state if end of current ByteBuf was reached - huffmanStageDecoder.currentSelector = currSelector; - return; - } - int index = 0; - while (reader.readBoolean()) { - index++; - } - selectors[currSelector] = tableMtf.indexToFront(index); - } - - currentState = State.RECEIVE_HUFFMAN_LENGTH; - // fall through - case RECEIVE_HUFFMAN_LENGTH: - huffmanStageDecoder = this.huffmanStageDecoder; - totalTables = huffmanStageDecoder.totalTables; - final byte[][] codeLength = huffmanStageDecoder.tableCodeLengths; - alphaSize = huffmanStageDecoder.alphabetSize; - - /* Now the coding tables */ - int currGroup; - int currLength = huffmanStageDecoder.currentLength; - int currAlpha = 0; - boolean modifyLength = huffmanStageDecoder.modifyLength; - boolean saveStateAndReturn = false; - loop: for (currGroup = huffmanStageDecoder.currentGroup; currGroup < totalTables; currGroup++) { - // start_huffman_length - if (!reader.hasReadableBits(5)) { - saveStateAndReturn = true; - break; - } - if (currLength < 0) { - currLength = reader.readBits(5); - } - for (currAlpha = huffmanStageDecoder.currentAlpha; currAlpha < alphaSize; currAlpha++) { - // delta_bit_length: 1..40 - if (!reader.isReadable()) { - saveStateAndReturn = true; - break loop; - } - while (modifyLength || reader.readBoolean()) { // 0=>next symbol; 1=>alter length - if (!reader.isReadable()) { - modifyLength = true; - saveStateAndReturn = true; - break loop; - } - // 1=>decrement length; 0=>increment length - currLength += reader.readBoolean() ? -1 : 1; - modifyLength = false; - if (!reader.isReadable()) { - saveStateAndReturn = true; - break loop; - } - } - codeLength[currGroup][currAlpha] = (byte) currLength; - } - currLength = -1; - currAlpha = huffmanStageDecoder.currentAlpha = 0; - modifyLength = false; - } - if (saveStateAndReturn) { - // Save state if end of current ByteBuf was reached - huffmanStageDecoder.currentGroup = currGroup; - huffmanStageDecoder.currentLength = currLength; - huffmanStageDecoder.currentAlpha = currAlpha; - huffmanStageDecoder.modifyLength = modifyLength; - return; - } - - // Finally create the Huffman tables - huffmanStageDecoder.createHuffmanDecodingTables(); - currentState = State.DECODE_HUFFMAN_DATA; - // fall through - case DECODE_HUFFMAN_DATA: - blockDecompressor = this.blockDecompressor; - final int oldReaderIndex = in.readerIndex(); - final boolean decoded = blockDecompressor.decodeHuffmanData(this.huffmanStageDecoder); - if (!decoded) { - return; - } - // It used to avoid "Bzip2Decoder.decode() did not read anything but decoded a message" exception. - // Because previous operation may read only a few bits from Bzip2BitReader.bitBuffer and - // don't read incoming ByteBuf. - if (in.readerIndex() == oldReaderIndex && in.isReadable()) { - reader.refill(); - } - - final int blockLength = blockDecompressor.blockLength(); - ByteBuf uncompressed = ctx.alloc().buffer(blockLength); - try { - int uncByte; - while ((uncByte = blockDecompressor.read()) >= 0) { - uncompressed.writeByte(uncByte); - } - // We did read all the data, lets reset the state and do the CRC check. - currentState = State.INIT_BLOCK; - int currentBlockCRC = blockDecompressor.checkCRC(); - streamCRC = (streamCRC << 1 | streamCRC >>> 31) ^ currentBlockCRC; - - ctx.fireChannelRead(uncompressed); - uncompressed = null; - } finally { - if (uncompressed != null) { - uncompressed.release(); - } - } - // Return here so the ByteBuf that was put in the List will be forwarded to the user and so can be - // released as soon as possible. - return; - case EOF: - in.skipBytes(in.readableBytes()); - return; - default: - throw new IllegalStateException(); - } - } - } - - /** - * Returns {@code true} if and only if the end of the compressed stream - * has been reached. - */ - public boolean isClosed() { - return currentState == State.EOF; - } -} 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 deleted file mode 100644 index a4d56b13f8..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/Bzip2DivSufSort.java +++ /dev/null @@ -1,2117 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -/** - * DivSufSort suffix array generator.
- * - * Based on libdivsufsort 1.2.3 patched to support Bzip2.
- * This is a simple conversion of the original C with two minor bugfixes applied (see "BUGFIX" - * comments within the class). Documentation within the class is largely absent. - */ -final class Bzip2DivSufSort { - - private static final int STACK_SIZE = 64; - private static final int BUCKET_A_SIZE = 256; - private static final int BUCKET_B_SIZE = 65536; - private static final int SS_BLOCKSIZE = 1024; - private static final int INSERTIONSORT_THRESHOLD = 8; - - private static final int[] LOG_2_TABLE = { - -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 - }; - - private final int[] SA; - private final byte[] T; - private final int n; - - /** - * @param block The input array - * @param bwtBlock The output array - * @param blockLength The length of the input data - */ - Bzip2DivSufSort(final byte[] block, final int[] bwtBlock, final int blockLength) { - T = block; - SA = bwtBlock; - n = blockLength; - } - - private static void swapElements(final int[] array1, final int idx1, final int[] array2, final int idx2) { - final int temp = array1[idx1]; - array1[idx1] = array2[idx2]; - array2[idx2] = temp; - } - - private int ssCompare(final int p1, final int p2, final int depth) { - final int[] SA = this.SA; - final byte[] T = this.T; - - // pointers within T - final int U1n = SA[p1 + 1] + 2; - final int U2n = SA[p2 + 1] + 2; - - int U1 = depth + SA[p1]; - int U2 = depth + SA[p2]; - - while (U1 < U1n && U2 < U2n && T[U1] == T[U2]) { - ++U1; - ++U2; - } - - return U1 < U1n ? - U2 < U2n ? (T[U1] & 0xff) - (T[U2] & 0xff) : 1 - : U2 < U2n ? -1 : 0; - } - - private int ssCompareLast(int pa, int p1, int p2, int depth, int size) { - final int[] SA = this.SA; - final byte[] T = this.T; - - int U1 = depth + SA[p1]; - int U2 = depth + SA[p2]; - int U1n = size; - int U2n = SA[p2 + 1] + 2; - - while (U1 < U1n && U2 < U2n && T[U1] == T[U2]) { - ++U1; - ++U2; - } - - if (U1 < U1n) { - return U2 < U2n ? (T[U1] & 0xff) - (T[U2] & 0xff) : 1; - } - if (U2 == U2n) { - return 1; - } - - U1 %= size; - U1n = SA[pa] + 2; - while (U1 < U1n && U2 < U2n && T[U1] == T[U2]) { - ++U1; - ++U2; - } - - return U1 < U1n ? - U2 < U2n ? (T[U1] & 0xff) - (T[U2] & 0xff) : 1 - : U2 < U2n ? -1 : 0; - } - - private void ssInsertionSort(int pa, int first, int last, int depth) { - final int[] SA = this.SA; - - int i, j; // pointer within SA - int t; - int r; - - for (i = last - 2; first <= i; --i) { - for (t = SA[i], j = i + 1; 0 < (r = ssCompare(pa + t, pa + SA[j], depth));) { - do { - SA[j - 1] = SA[j]; - } while (++j < last && SA[j] < 0); - if (last <= j) { - break; - } - } - if (r == 0) { - SA[j] = ~SA[j]; - } - SA[j - 1] = t; - } - } - - private void ssFixdown(int td, int pa, int sa, int i, int size) { - final int[] SA = this.SA; - final byte[] T = this.T; - - int j, k; - int v; - int c, d, e; - - for (v = SA[sa + i], c = T[td + SA[pa + v]] & 0xff; (j = 2 * i + 1) < size; SA[sa + i] = SA[sa + k], i = k) { - d = T[td + SA[pa + SA[sa + (k = j++)]]] & 0xff; - if (d < (e = T[td + SA[pa + SA[sa + j]]] & 0xff)) { - k = j; - d = e; - } - if (d <= c) { - break; - } - } - SA[sa + i] = v; - } - - private void ssHeapSort(int td, int pa, int sa, int size) { - final int[] SA = this.SA; - final byte[] T = this.T; - - int i, m; - int t; - - m = size; - if (size % 2 == 0) { - m--; - if ((T[td + SA[pa + SA[sa + m / 2]]] & 0xff) < (T[td + SA[pa + SA[sa + m]]] & 0xff)) { - swapElements(SA, sa + m, SA, sa + m / 2); - } - } - - for (i = m / 2 - 1; 0 <= i; --i) { - ssFixdown(td, pa, sa, i, m); - } - - if (size % 2 == 0) { - swapElements(SA, sa, SA, sa + m); - ssFixdown(td, pa, sa, 0, m); - } - - for (i = m - 1; 0 < i; --i) { - t = SA[sa]; - SA[sa] = SA[sa + i]; - ssFixdown(td, pa, sa, 0, i); - SA[sa + i] = t; - } - } - - private int ssMedian3(final int td, final int pa, int v1, int v2, int v3) { - final int[] SA = this.SA; - final byte[] T = this.T; - - int T_v1 = T[td + SA[pa + SA[v1]]] & 0xff; - int T_v2 = T[td + SA[pa + SA[v2]]] & 0xff; - int T_v3 = T[td + SA[pa + SA[v3]]] & 0xff; - - if (T_v1 > T_v2) { - final int temp = v1; - v1 = v2; - v2 = temp; - final int T_vtemp = T_v1; - T_v1 = T_v2; - T_v2 = T_vtemp; - } - if (T_v2 > T_v3) { - if (T_v1 > T_v3) { - return v1; - } - return v3; - } - return v2; - } - - private int ssMedian5(final int td, final int pa, int v1, int v2, int v3, int v4, int v5) { - final int[] SA = this.SA; - final byte[] T = this.T; - - int T_v1 = T[td + SA[pa + SA[v1]]] & 0xff; - int T_v2 = T[td + SA[pa + SA[v2]]] & 0xff; - int T_v3 = T[td + SA[pa + SA[v3]]] & 0xff; - int T_v4 = T[td + SA[pa + SA[v4]]] & 0xff; - int T_v5 = T[td + SA[pa + SA[v5]]] & 0xff; - int temp; - int T_vtemp; - - if (T_v2 > T_v3) { - temp = v2; - v2 = v3; - v3 = temp; - T_vtemp = T_v2; - T_v2 = T_v3; - T_v3 = T_vtemp; - } - if (T_v4 > T_v5) { - temp = v4; - v4 = v5; - v5 = temp; - T_vtemp = T_v4; - T_v4 = T_v5; - T_v5 = T_vtemp; - } - if (T_v2 > T_v4) { - temp = v2; - v4 = temp; - T_vtemp = T_v2; - T_v4 = T_vtemp; - temp = v3; - v3 = v5; - v5 = temp; - T_vtemp = T_v3; - T_v3 = T_v5; - T_v5 = T_vtemp; - } - if (T_v1 > T_v3) { - temp = v1; - v1 = v3; - v3 = temp; - T_vtemp = T_v1; - T_v1 = T_v3; - T_v3 = T_vtemp; - } - if (T_v1 > T_v4) { - temp = v1; - v4 = temp; - T_vtemp = T_v1; - T_v4 = T_vtemp; - v3 = v5; - T_v3 = T_v5; - } - if (T_v3 > T_v4) { - return v4; - } - return v3; - } - - private int ssPivot(final int td, final int pa, final int first, final int last) { - int middle; - int t; - - t = last - first; - middle = first + t / 2; - - if (t <= 512) { - if (t <= 32) { - return ssMedian3(td, pa, first, middle, last - 1); - } - t >>= 2; - return ssMedian5(td, pa, first, first + t, middle, last - 1 - t, last - 1); - } - t >>= 3; - return ssMedian3( - td, pa, - ssMedian3(td, pa, first, first + t, first + (t << 1)), - ssMedian3(td, pa, middle - t, middle, middle + t), - ssMedian3(td, pa, last - 1 - (t << 1), last - 1 - t, last - 1) - ); - } - - private static int ssLog(final int n) { - return (n & 0xff00) != 0 ? - 8 + LOG_2_TABLE[n >> 8 & 0xff] - : LOG_2_TABLE[n & 0xff]; - } - - private int ssSubstringPartition(final int pa, final int first, final int last, final int depth) { - final int[] SA = this.SA; - - int a, b; - int t; - - for (a = first - 1, b = last;;) { - while (++a < b && (SA[pa + SA[a]] + depth >= SA[pa + SA[a] + 1] + 1)) { - SA[a] = ~SA[a]; - } - --b; - while (a < b && (SA[pa + SA[b]] + depth < SA[pa + SA[b] + 1] + 1)) { - --b; - } - - if (b <= a) { - break; - } - t = ~SA[b]; - SA[b] = SA[a]; - SA[a] = t; - } - if (first < a) { - SA[first] = ~SA[first]; - } - return a; - } - - private static class StackEntry { - final int a; - final int b; - final int c; - final int d; - - StackEntry(final int a, final int b, final int c, final int d) { - this.a = a; - this.b = b; - this.c = c; - this.d = d; - } - } - - private void ssMultiKeyIntroSort(final int pa, int first, int last, int depth) { - final int[] SA = this.SA; - final byte[] T = this.T; - - final StackEntry[] stack = new StackEntry[STACK_SIZE]; - - int Td; - int a, b, c, d, e, f; - int s, t; - int ssize; - int limit; - int v, x = 0; - - for (ssize = 0, limit = ssLog(last - first);;) { - if (last - first <= INSERTIONSORT_THRESHOLD) { - if (1 < last - first) { - ssInsertionSort(pa, first, last, depth); - } - if (ssize == 0) { - return; - } - StackEntry entry = stack[--ssize]; - first = entry.a; - last = entry.b; - depth = entry.c; - limit = entry.d; - continue; - } - - Td = depth; - if (limit-- == 0) { - ssHeapSort(Td, pa, first, last - first); - } - if (limit < 0) { - for (a = first + 1, v = T[Td + SA[pa + SA[first]]] & 0xff; a < last; ++a) { - if ((x = T[Td + SA[pa + SA[a]]] & 0xff) != v) { - if (1 < a - first) { - break; - } - v = x; - first = a; - } - } - if ((T[Td + SA[pa + SA[first]] - 1] & 0xff) < v) { - first = ssSubstringPartition(pa, first, a, depth); - } - if (a - first <= last - a) { - if (1 < a - first) { - stack[ssize++] = new StackEntry(a, last, depth, -1); - last = a; - depth += 1; - limit = ssLog(a - first); - } else { - first = a; - limit = -1; - } - } else { - if (1 < last - a) { - stack[ssize++] = new StackEntry(first, a, depth + 1, ssLog(a - first)); - first = a; - limit = -1; - } else { - last = a; - depth += 1; - limit = ssLog(a - first); - } - } - continue; - } - - a = ssPivot(Td, pa, first, last); - v = T[Td + SA[pa + SA[a]]] & 0xff; - swapElements(SA, first, SA, a); - - b = first + 1; - while (b < last && (x = T[Td + SA[pa + SA[b]]] & 0xff) == v) { - ++b; - } - if ((a = b) < last && x < v) { - while (++b < last && (x = T[Td + SA[pa + SA[b]]] & 0xff) <= v) { - if (x == v) { - swapElements(SA, b, SA, a); - ++a; - } - } - } - - c = last - 1; - while (b < c && (x = T[Td + SA[pa + SA[c]]] & 0xff) == v) { - --c; - } - if (b < (d = c) && x > v) { - while (b < --c && (x = T[Td + SA[pa + SA[c]]] & 0xff) >= v) { - if (x == v) { - swapElements(SA, c, SA, d); - --d; - } - } - } - while (b < c) { - swapElements(SA, b, SA, c); - while (++b < c && (x = T[Td + SA[pa + SA[b]]] & 0xff) <= v) { - if (x == v) { - swapElements(SA, b, SA, a); - ++a; - } - } - while (b < --c && (x = T[Td + SA[pa + SA[c]]] & 0xff) >= v) { - if (x == v) { - swapElements(SA, c, SA, d); - --d; - } - } - } - - if (a <= d) { - c = b - 1; - - if ((s = a - first) > (t = b - a)) { - s = t; - } - for (e = first, f = b - s; 0 < s; --s, ++e, ++f) { - swapElements(SA, e, SA, f); - } - if ((s = d - c) > (t = last - d - 1)) { - s = t; - } - for (e = b, f = last - s; 0 < s; --s, ++e, ++f) { - swapElements(SA, e, SA, f); - } - - a = first + (b - a); - c = last - (d - c); - b = v <= (T[Td + SA[pa + SA[a]] - 1] & 0xff) ? a : ssSubstringPartition(pa, a, c, depth); - - if (a - first <= last - c) { - if (last - c <= c - b) { - stack[ssize++] = new StackEntry(b, c, depth + 1, ssLog(c - b)); - stack[ssize++] = new StackEntry(c, last, depth, limit); - last = a; - } else if (a - first <= c - b) { - stack[ssize++] = new StackEntry(c, last, depth, limit); - stack[ssize++] = new StackEntry(b, c, depth + 1, ssLog(c - b)); - last = a; - } else { - stack[ssize++] = new StackEntry(c, last, depth, limit); - stack[ssize++] = new StackEntry(first, a, depth, limit); - first = b; - last = c; - depth += 1; - limit = ssLog(c - b); - } - } else { - if (a - first <= c - b) { - stack[ssize++] = new StackEntry(b, c, depth + 1, ssLog(c - b)); - stack[ssize++] = new StackEntry(first, a, depth, limit); - first = c; - } else if (last - c <= c - b) { - stack[ssize++] = new StackEntry(first, a, depth, limit); - stack[ssize++] = new StackEntry(b, c, depth + 1, ssLog(c - b)); - first = c; - } else { - stack[ssize++] = new StackEntry(first, a, depth, limit); - stack[ssize++] = new StackEntry(c, last, depth, limit); - first = b; - last = c; - depth += 1; - limit = ssLog(c - b); - } - } - } else { - limit += 1; - if ((T[Td + SA[pa + SA[first]] - 1] & 0xff) < v) { - first = ssSubstringPartition(pa, first, last, depth); - limit = ssLog(last - first); - } - depth += 1; - } - } - } - - private static void ssBlockSwap(final int[] array1, final int first1, - final int[] array2, final int first2, final int size) { - int a, b; - int i; - for (i = size, a = first1, b = first2; 0 < i; --i, ++a, ++b) { - swapElements(array1, a, array2, b); - } - } - - private void ssMergeForward(final int pa, int[] buf, final int bufoffset, - final int first, final int middle, final int last, final int depth) { - final int[] SA = this.SA; - - int bufend; - int i, j, k; - int t; - int r; - - bufend = bufoffset + (middle - first) - 1; - ssBlockSwap(buf, bufoffset, SA, first, middle - first); - - for (t = SA[first], i = first, j = bufoffset, k = middle;;) { - r = ssCompare(pa + buf[j], pa + SA[k], depth); - if (r < 0) { - do { - SA[i++] = buf[j]; - if (bufend <= j) { - buf[j] = t; - return; - } - buf[j++] = SA[i]; - } while (buf[j] < 0); - } else if (r > 0) { - do { - SA[i++] = SA[k]; - SA[k++] = SA[i]; - if (last <= k) { - while (j < bufend) { - SA[i++] = buf[j]; buf[j++] = SA[i]; - } - SA[i] = buf[j]; buf[j] = t; - return; - } - } while (SA[k] < 0); - } else { - SA[k] = ~SA[k]; - do { - SA[i++] = buf[j]; - if (bufend <= j) { - buf[j] = t; - return; - } - buf[j++] = SA[i]; - } while (buf[j] < 0); - - do { - SA[i++] = SA[k]; - SA[k++] = SA[i]; - if (last <= k) { - while (j < bufend) { - SA[i++] = buf[j]; - buf[j++] = SA[i]; - } - SA[i] = buf[j]; buf[j] = t; - return; - } - } while (SA[k] < 0); - } - } - } - - private void ssMergeBackward(final int pa, int[] buf, final int bufoffset, - final int first, final int middle, final int last, final int depth) { - final int[] SA = this.SA; - - int p1, p2; - int bufend; - int i, j, k; - int t; - int r; - int x; - - bufend = bufoffset + (last - middle); - ssBlockSwap(buf, bufoffset, SA, middle, last - middle); - - x = 0; - if (buf[bufend - 1] < 0) { - x |= 1; - p1 = pa + ~buf[bufend - 1]; - } else { - p1 = pa + buf[bufend - 1]; - } - if (SA[middle - 1] < 0) { - x |= 2; - p2 = pa + ~SA[middle - 1]; - } else { - p2 = pa + SA[middle - 1]; - } - for (t = SA[last - 1], i = last - 1, j = bufend - 1, k = middle - 1;;) { - - r = ssCompare(p1, p2, depth); - if (r > 0) { - if ((x & 1) != 0) { - do { - SA[i--] = buf[j]; - buf[j--] = SA[i]; - } while (buf[j] < 0); - x ^= 1; - } - SA[i--] = buf[j]; - if (j <= bufoffset) { - buf[j] = t; - return; - } - buf[j--] = SA[i]; - - if (buf[j] < 0) { - x |= 1; - p1 = pa + ~buf[j]; - } else { - p1 = pa + buf[j]; - } - } else if (r < 0) { - if ((x & 2) != 0) { - do { - SA[i--] = SA[k]; - SA[k--] = SA[i]; - } while (SA[k] < 0); - x ^= 2; - } - SA[i--] = SA[k]; - SA[k--] = SA[i]; - if (k < first) { - while (bufoffset < j) { - SA[i--] = buf[j]; - buf[j--] = SA[i]; - } - SA[i] = buf[j]; - buf[j] = t; - return; - } - - if (SA[k] < 0) { - x |= 2; - p2 = pa + ~SA[k]; - } else { - p2 = pa + SA[k]; - } - } else { - if ((x & 1) != 0) { - do { - SA[i--] = buf[j]; - buf[j--] = SA[i]; - } while (buf[j] < 0); - x ^= 1; - } - SA[i--] = ~buf[j]; - if (j <= bufoffset) { - buf[j] = t; - return; - } - buf[j--] = SA[i]; - - if ((x & 2) != 0) { - do { - SA[i--] = SA[k]; - SA[k--] = SA[i]; - } while (SA[k] < 0); - x ^= 2; - } - SA[i--] = SA[k]; - SA[k--] = SA[i]; - if (k < first) { - while (bufoffset < j) { - SA[i--] = buf[j]; - buf[j--] = SA[i]; - } - SA[i] = buf[j]; - buf[j] = t; - return; - } - - if (buf[j] < 0) { - x |= 1; - p1 = pa + ~buf[j]; - } else { - p1 = pa + buf[j]; - } - if (SA[k] < 0) { - x |= 2; - p2 = pa + ~SA[k]; - } else { - p2 = pa + SA[k]; - } - } - } - } - - private static int getIDX(final int a) { - return 0 <= a ? a : ~a; - } - - private void ssMergeCheckEqual(final int pa, final int depth, final int a) { - final int[] SA = this.SA; - - if (0 <= SA[a] && ssCompare(pa + getIDX(SA[a - 1]), pa + SA[a], depth) == 0) { - SA[a] = ~SA[a]; - } - } - - private void ssMerge(final int pa, int first, int middle, int last, int[] buf, - final int bufoffset, final int bufsize, final int depth) { - final int[] SA = this.SA; - - final StackEntry[] stack = new StackEntry[STACK_SIZE]; - - int i, j; - int m, len, half; - int ssize; - int check, next; - - for (check = 0, ssize = 0;;) { - - if (last - middle <= bufsize) { - if (first < middle && middle < last) { - ssMergeBackward(pa, buf, bufoffset, first, middle, last, depth); - } - - if ((check & 1) != 0) { - ssMergeCheckEqual(pa, depth, first); - } - if ((check & 2) != 0) { - ssMergeCheckEqual(pa, depth, last); - } - if (ssize == 0) { - return; - } - StackEntry entry = stack[--ssize]; - first = entry.a; - middle = entry.b; - last = entry.c; - check = entry.d; - continue; - } - - if (middle - first <= bufsize) { - if (first < middle) { - ssMergeForward(pa, buf, bufoffset, first, middle, last, depth); - } - if ((check & 1) != 0) { - ssMergeCheckEqual(pa, depth, first); - } - if ((check & 2) != 0) { - ssMergeCheckEqual(pa, depth, last); - } - if (ssize == 0) { - return; - } - StackEntry entry = stack[--ssize]; - first = entry.a; - middle = entry.b; - last = entry.c; - check = entry.d; - continue; - } - - for (m = 0, len = Math.min(middle - first, last - middle), half = len >> 1; - 0 < len; - len = half, half >>= 1) { - - if (ssCompare(pa + getIDX(SA[middle + m + half]), - pa + getIDX(SA[middle - m - half - 1]), depth) < 0) { - m += half + 1; - half -= (len & 1) ^ 1; - } - } - - if (0 < m) { - ssBlockSwap(SA, middle - m, SA, middle, m); - i = j = middle; - next = 0; - if (middle + m < last) { - if (SA[middle + m] < 0) { - while (SA[i - 1] < 0) { - --i; - } - SA[middle + m] = ~SA[middle + m]; - } - for (j = middle; SA[j] < 0;) { - ++j; - } - next = 1; - } - if (i - first <= last - j) { - stack[ssize++] = new StackEntry(j, middle + m, last, (check & 2) | (next & 1)); - middle -= m; - last = i; - check &= 1; - } else { - if (i == middle && middle == j) { - next <<= 1; - } - stack[ssize++] = new StackEntry(first, middle - m, i, (check & 1) | (next & 2)); - first = j; - middle += m; - check = (check & 2) | (next & 1); - } - } else { - if ((check & 1) != 0) { - ssMergeCheckEqual(pa, depth, first); - } - ssMergeCheckEqual(pa, depth, middle); - if ((check & 2) != 0) { - ssMergeCheckEqual(pa, depth, last); - } - if (ssize == 0) { - return; - } - StackEntry entry = stack[--ssize]; - first = entry.a; - middle = entry.b; - last = entry.c; - check = entry.d; - } - } - } - - private void subStringSort(final int pa, int first, final int last, - final int[] buf, final int bufoffset, final int bufsize, - final int depth, final boolean lastsuffix, final int size) { - final int[] SA = this.SA; - - int a, b; - int[] curbuf; - int curbufoffset; - int i, j, k; - int curbufsize; - - if (lastsuffix) { - ++first; - } - for (a = first, i = 0; a + SS_BLOCKSIZE < last; a += SS_BLOCKSIZE, ++i) { - ssMultiKeyIntroSort(pa, a, a + SS_BLOCKSIZE, depth); - curbuf = SA; - curbufoffset = a + SS_BLOCKSIZE; - curbufsize = last - (a + SS_BLOCKSIZE); - if (curbufsize <= bufsize) { - curbufsize = bufsize; - curbuf = buf; - curbufoffset = bufoffset; - } - for (b = a, k = SS_BLOCKSIZE, j = i; (j & 1) != 0; b -= k, k <<= 1, j >>>= 1) { - ssMerge(pa, b - k, b, b + k, curbuf, curbufoffset, curbufsize, depth); - } - } - - ssMultiKeyIntroSort(pa, a, last, depth); - - for (k = SS_BLOCKSIZE; i != 0; k <<= 1, i >>= 1) { - if ((i & 1) != 0) { - ssMerge(pa, a - k, a, last, buf, bufoffset, bufsize, depth); - a -= k; - } - } - - if (lastsuffix) { - int r; - for (a = first, i = SA[first - 1], r = 1; - a < last && (SA[a] < 0 || 0 < (r = ssCompareLast(pa, pa + i, pa + SA[a], depth, size))); - ++a) { - SA[a - 1] = SA[a]; - } - if (r == 0) { - SA[a] = ~SA[a]; - } - SA[a - 1] = i; - } - } - - /*----------------------------------------------------------------------------*/ - - private int trGetC(final int isa, final int isaD, final int isaN, final int p) { - return isaD + p < isaN ? - SA[isaD + p] - : SA[isa + ((isaD - isa + p) % (isaN - isa))]; - } - - private void trFixdown(final int isa, final int isaD, final int isaN, final int sa, int i, final int size) { - final int[] SA = this.SA; - - int j, k; - int v; - int c, d, e; - - for (v = SA[sa + i], c = trGetC(isa, isaD, isaN, v); (j = 2 * i + 1) < size; SA[sa + i] = SA[sa + k], i = k) { - k = j++; - d = trGetC(isa, isaD, isaN, SA[sa + k]); - if (d < (e = trGetC(isa, isaD, isaN, SA[sa + j]))) { - k = j; - d = e; - } - if (d <= c) { - break; - } - } - SA[sa + i] = v; - } - - private void trHeapSort(final int isa, final int isaD, final int isaN, final int sa, final int size) { - final int[] SA = this.SA; - - int i, m; - int t; - - m = size; - if (size % 2 == 0) { - m--; - if (trGetC(isa, isaD, isaN, SA[sa + m / 2]) < trGetC(isa, isaD, isaN, SA[sa + m])) { - swapElements(SA, sa + m, SA, sa + m / 2); - } - } - - for (i = m / 2 - 1; 0 <= i; --i) { - trFixdown(isa, isaD, isaN, sa, i, m); - } - - if (size % 2 == 0) { - swapElements(SA, sa, SA, sa + m); - trFixdown(isa, isaD, isaN, sa, 0, m); - } - - for (i = m - 1; 0 < i; --i) { - t = SA[sa]; - SA[sa] = SA[sa + i]; - trFixdown(isa, isaD, isaN, sa, 0, i); - SA[sa + i] = t; - } - } - - private void trInsertionSort(final int isa, final int isaD, final int isaN, int first, int last) { - final int[] SA = this.SA; - - int a, b; - int t, r; - - for (a = first + 1; a < last; ++a) { - for (t = SA[a], b = a - 1; 0 > (r = trGetC(isa, isaD, isaN, t) - trGetC(isa, isaD, isaN, SA[b]));) { - do { - SA[b + 1] = SA[b]; - } while (first <= --b && SA[b] < 0); - if (b < first) { - break; - } - } - if (r == 0) { - SA[b] = ~SA[b]; - } - SA[b + 1] = t; - } - } - - private static int trLog(int n) { - return (n & 0xffff0000) != 0 ? - (n & 0xff000000) != 0 ? 24 + LOG_2_TABLE[n >> 24 & 0xff] : LOG_2_TABLE[n >> 16 & 0xff + 16] - : (n & 0x0000ff00) != 0 ? 8 + LOG_2_TABLE[n >> 8 & 0xff] : LOG_2_TABLE[n & 0xff]; - } - - private int trMedian3(final int isa, final int isaD, final int isaN, int v1, int v2, int v3) { - final int[] SA = this.SA; - - int SA_v1 = trGetC(isa, isaD, isaN, SA[v1]); - int SA_v2 = trGetC(isa, isaD, isaN, SA[v2]); - int SA_v3 = trGetC(isa, isaD, isaN, SA[v3]); - - if (SA_v1 > SA_v2) { - final int temp = v1; - v1 = v2; - v2 = temp; - final int SA_vtemp = SA_v1; - SA_v1 = SA_v2; - SA_v2 = SA_vtemp; - } - if (SA_v2 > SA_v3) { - if (SA_v1 > SA_v3) { - return v1; - } - return v3; - } - - return v2; - } - - private int trMedian5(final int isa, final int isaD, final int isaN, int v1, int v2, int v3, int v4, int v5) { - final int[] SA = this.SA; - - int SA_v1 = trGetC(isa, isaD, isaN, SA[v1]); - int SA_v2 = trGetC(isa, isaD, isaN, SA[v2]); - int SA_v3 = trGetC(isa, isaD, isaN, SA[v3]); - int SA_v4 = trGetC(isa, isaD, isaN, SA[v4]); - int SA_v5 = trGetC(isa, isaD, isaN, SA[v5]); - int temp; - int SA_vtemp; - - if (SA_v2 > SA_v3) { - temp = v2; - v2 = v3; - v3 = temp; - SA_vtemp = SA_v2; - SA_v2 = SA_v3; - SA_v3 = SA_vtemp; - } - if (SA_v4 > SA_v5) { - temp = v4; - v4 = v5; - v5 = temp; - SA_vtemp = SA_v4; - SA_v4 = SA_v5; - SA_v5 = SA_vtemp; - } - if (SA_v2 > SA_v4) { - temp = v2; - v4 = temp; - SA_vtemp = SA_v2; - SA_v4 = SA_vtemp; - temp = v3; - v3 = v5; - v5 = temp; - SA_vtemp = SA_v3; - SA_v3 = SA_v5; - SA_v5 = SA_vtemp; - } - if (SA_v1 > SA_v3) { - temp = v1; - v1 = v3; - v3 = temp; - SA_vtemp = SA_v1; - SA_v1 = SA_v3; - SA_v3 = SA_vtemp; - } - if (SA_v1 > SA_v4) { - temp = v1; - v4 = temp; - SA_vtemp = SA_v1; - SA_v4 = SA_vtemp; - v3 = v5; - SA_v3 = SA_v5; - } - if (SA_v3 > SA_v4) { - return v4; - } - return v3; - } - - private int trPivot(final int isa, final int isaD, final int isaN, final int first, final int last) { - final int middle; - int t; - - t = last - first; - middle = first + t / 2; - - if (t <= 512) { - if (t <= 32) { - return trMedian3(isa, isaD, isaN, first, middle, last - 1); - } - t >>= 2; - return trMedian5( - isa, isaD, isaN, - first, first + t, - middle, - last - 1 - t, last - 1 - ); - } - t >>= 3; - return trMedian3( - isa, isaD, isaN, - trMedian3(isa, isaD, isaN, first, first + t, first + (t << 1)), - trMedian3(isa, isaD, isaN, middle - t, middle, middle + t), - trMedian3(isa, isaD, isaN, last - 1 - (t << 1), last - 1 - t, last - 1) - ); - } - - /*---------------------------------------------------------------------------*/ - - private void lsUpdateGroup(final int isa, final int first, final int last) { - final int[] SA = this.SA; - - int a, b; - int t; - - for (a = first; a < last; ++a) { - if (0 <= SA[a]) { - b = a; - do { - SA[isa + SA[a]] = a; - } while (++a < last && 0 <= SA[a]); - SA[b] = b - a; - if (last <= a) { - break; - } - } - b = a; - do { - SA[a] = ~SA[a]; - } while (SA[++a] < 0); - t = a; - do { - SA[isa + SA[b]] = t; - } while (++b <= a); - } - } - - private void lsIntroSort(final int isa, final int isaD, final int isaN, int first, int last) { - final int[] SA = this.SA; - - final StackEntry[] stack = new StackEntry[STACK_SIZE]; - - int a, b, c, d, e, f; - int s, t; - int limit; - int v, x = 0; - int ssize; - - for (ssize = 0, limit = trLog(last - first);;) { - if (last - first <= INSERTIONSORT_THRESHOLD) { - if (1 < last - first) { - trInsertionSort(isa, isaD, isaN, first, last); - lsUpdateGroup(isa, first, last); - } else if (last - first == 1) { - SA[first] = -1; - } - if (ssize == 0) { - return; - } - StackEntry entry = stack[--ssize]; - first = entry.a; - last = entry.b; - limit = entry.c; - continue; - } - - if (limit-- == 0) { - trHeapSort(isa, isaD, isaN, first, last - first); - for (a = last - 1; first < a; a = b) { - for (x = trGetC(isa, isaD, isaN, SA[a]), b = a - 1; - first <= b && trGetC(isa, isaD, isaN, SA[b]) == x; - --b) { - SA[b] = ~SA[b]; - } - } - lsUpdateGroup(isa, first, last); - if (ssize == 0) { - return; - } - StackEntry entry = stack[--ssize]; - first = entry.a; - last = entry.b; - limit = entry.c; - continue; - } - - a = trPivot(isa, isaD, isaN, first, last); - swapElements(SA, first, SA, a); - v = trGetC(isa, isaD, isaN, SA[first]); - - b = first + 1; - while (b < last && (x = trGetC(isa, isaD, isaN, SA[b])) == v) { - ++b; - } - if ((a = b) < last && x < v) { - while (++b < last && (x = trGetC(isa, isaD, isaN, SA[b])) <= v) { - if (x == v) { - swapElements(SA, b, SA, a); - ++a; - } - } - } - - c = last - 1; - while (b < c && (x = trGetC(isa, isaD, isaN, SA[c])) == v) { - --c; - } - if (b < (d = c) && x > v) { - while (b < --c && (x = trGetC(isa, isaD, isaN, SA[c])) >= v) { - if (x == v) { - swapElements(SA, c, SA, d); - --d; - } - } - } - while (b < c) { - swapElements(SA, b, SA, c); - while (++b < c && (x = trGetC(isa, isaD, isaN, SA[b])) <= v) { - if (x == v) { - swapElements(SA, b, SA, a); - ++a; - } - } - while (b < --c && (x = trGetC(isa, isaD, isaN, SA[c])) >= v) { - if (x == v) { - swapElements(SA, c, SA, d); - --d; - } - } - } - - if (a <= d) { - c = b - 1; - - if ((s = a - first) > (t = b - a)) { - s = t; - } - for (e = first, f = b - s; 0 < s; --s, ++e, ++f) { - swapElements(SA, e, SA, f); - } - if ((s = d - c) > (t = last - d - 1)) { - s = t; - } - for (e = b, f = last - s; 0 < s; --s, ++e, ++f) { - swapElements(SA, e, SA, f); - } - - a = first + (b - a); - b = last - (d - c); - - for (c = first, v = a - 1; c < a; ++c) { - SA[isa + SA[c]] = v; - } - if (b < last) { - for (c = a, v = b - 1; c < b; ++c) { - SA[isa + SA[c]] = v; - } - } - if ((b - a) == 1) { - SA[a] = - 1; - } - - if (a - first <= last - b) { - if (first < a) { - stack[ssize++] = new StackEntry(b, last, limit, 0); - last = a; - } else { - first = b; - } - } else { - if (b < last) { - stack[ssize++] = new StackEntry(first, a, limit, 0); - first = b; - } else { - last = a; - } - } - } else { - if (ssize == 0) { - return; - } - StackEntry entry = stack[--ssize]; - first = entry.a; - last = entry.b; - limit = entry.c; - } - } - } - - private void lsSort(final int isa, final int n, final int depth) { - final int[] SA = this.SA; - - int isaD; - int first, last, i; - int t, skip; - - for (isaD = isa + depth; -n < SA[0]; isaD += isaD - isa) { - first = 0; - skip = 0; - do { - if ((t = SA[first]) < 0) { - first -= t; - skip += t; - } else { - if (skip != 0) { - SA[first + skip] = skip; - skip = 0; - } - last = SA[isa + t] + 1; - lsIntroSort(isa, isaD, isa + n, first, last); - first = last; - } - } while (first < n); - if (skip != 0) { - SA[first + skip] = skip; - } - if (n < isaD - isa) { - first = 0; - do { - if ((t = SA[first]) < 0) { - first -= t; - } else { - last = SA[isa + t] + 1; - for (i = first; i < last; ++i) { - SA[isa + SA[i]] = i; - } - first = last; - } - } while (first < n); - break; - } - } - } - - /*---------------------------------------------------------------------------*/ - - private static class PartitionResult { - final int first; - final int last; - - PartitionResult(final int first, final int last) { - this.first = first; - this.last = last; - } - } - - private PartitionResult trPartition(final int isa, final int isaD, final int isaN, - int first, int last, final int v) { - final int[] SA = this.SA; - - int a, b, c, d, e, f; - int t, s; - int x = 0; - - b = first; - while (b < last && (x = trGetC(isa, isaD, isaN, SA[b])) == v) { - ++b; - } - if ((a = b) < last && x < v) { - while (++b < last && (x = trGetC(isa, isaD, isaN, SA[b])) <= v) { - if (x == v) { - swapElements(SA, b, SA, a); - ++a; - } - } - } - - c = last - 1; - while (b < c && (x = trGetC(isa, isaD, isaN, SA[c])) == v) { - --c; - } - if (b < (d = c) && x > v) { - while (b < --c && (x = trGetC(isa, isaD, isaN, SA[c])) >= v) { - if (x == v) { - swapElements(SA, c, SA, d); - --d; - } - } - } - while (b < c) { - swapElements(SA, b, SA, c); - while (++b < c && (x = trGetC(isa, isaD, isaN, SA[b])) <= v) { - if (x == v) { - swapElements(SA, b, SA, a); - ++a; - } - } - while (b < --c && (x = trGetC(isa, isaD, isaN, SA[c])) >= v) { - if (x == v) { - swapElements(SA, c, SA, d); - --d; - } - } - } - - if (a <= d) { - c = b - 1; - if ((s = a - first) > (t = b - a)) { - s = t; - } - for (e = first, f = b - s; 0 < s; --s, ++e, ++f) { - swapElements(SA, e, SA, f); - } - if ((s = d - c) > (t = last - d - 1)) { - s = t; - } - for (e = b, f = last - s; 0 < s; --s, ++e, ++f) { - swapElements(SA, e, SA, f); - } - first += b - a; - last -= d - c; - } - return new PartitionResult(first, last); - } - - private void trCopy(final int isa, final int isaN, final int first, - final int a, final int b, final int last, final int depth) { - final int[] SA = this.SA; - - int c, d, e; - int s, v; - - v = b - 1; - - for (c = first, d = a - 1; c <= d; ++c) { - if ((s = SA[c] - depth) < 0) { - s += isaN - isa; - } - if (SA[isa + s] == v) { - SA[++d] = s; - SA[isa + s] = d; - } - } - for (c = last - 1, e = d + 1, d = b; e < d; --c) { - if ((s = SA[c] - depth) < 0) { - s += isaN - isa; - } - if (SA[isa + s] == v) { - SA[--d] = s; - SA[isa + s] = d; - } - } - } - - private void trIntroSort(final int isa, int isaD, int isaN, int first, - int last, final TRBudget budget, final int size) { - final int[] SA = this.SA; - - final StackEntry[] stack = new StackEntry[STACK_SIZE]; - - int a, b, c, d, e, f; - int s, t; - int v, x = 0; - int limit, next; - int ssize; - - for (ssize = 0, limit = trLog(last - first);;) { - if (limit < 0) { - if (limit == -1) { - if (!budget.update(size, last - first)) { - break; - } - PartitionResult result = trPartition(isa, isaD - 1, isaN, first, last, last - 1); - a = result.first; - b = result.last; - if (first < a || b < last) { - if (a < last) { - for (c = first, v = a - 1; c < a; ++c) { - SA[isa + SA[c]] = v; - } - } - if (b < last) { - for (c = a, v = b - 1; c < b; ++c) { - SA[isa + SA[c]] = v; - } - } - - stack[ssize++] = new StackEntry(0, a, b, 0); - stack[ssize++] = new StackEntry(isaD - 1, first, last, -2); - if (a - first <= last - b) { - if (1 < a - first) { - stack[ssize++] = new StackEntry(isaD, b, last, trLog(last - b)); - last = a; limit = trLog(a - first); - } else if (1 < last - b) { - first = b; limit = trLog(last - b); - } else { - if (ssize == 0) { - return; - } - StackEntry entry = stack[--ssize]; - isaD = entry.a; - first = entry.b; - last = entry.c; - limit = entry.d; - } - } else { - if (1 < last - b) { - stack[ssize++] = new StackEntry(isaD, first, a, trLog(a - first)); - first = b; - limit = trLog(last - b); - } else if (1 < a - first) { - last = a; - limit = trLog(a - first); - } else { - if (ssize == 0) { - return; - } - StackEntry entry = stack[--ssize]; - isaD = entry.a; - first = entry.b; - last = entry.c; - limit = entry.d; - } - } - } else { - for (c = first; c < last; ++c) { - SA[isa + SA[c]] = c; - } - if (ssize == 0) { - return; - } - StackEntry entry = stack[--ssize]; - isaD = entry.a; - first = entry.b; - last = entry.c; - limit = entry.d; - } - } else if (limit == -2) { - a = stack[--ssize].b; - b = stack[ssize].c; - trCopy(isa, isaN, first, a, b, last, isaD - isa); - if (ssize == 0) { - return; - } - StackEntry entry = stack[--ssize]; - isaD = entry.a; - first = entry.b; - last = entry.c; - limit = entry.d; - } else { - if (0 <= SA[first]) { - a = first; - do { - SA[isa + SA[a]] = a; - } while (++a < last && 0 <= SA[a]); - first = a; - } - if (first < last) { - a = first; - do { - SA[a] = ~SA[a]; - } while (SA[++a] < 0); - next = SA[isa + SA[a]] != SA[isaD + SA[a]] ? trLog(a - first + 1) : -1; - if (++a < last) { - for (b = first, v = a - 1; b < a; ++b) { - SA[isa + SA[b]] = v; - } - } - - if (a - first <= last - a) { - stack[ssize++] = new StackEntry(isaD, a, last, -3); - isaD += 1; last = a; limit = next; - } else { - if (1 < last - a) { - stack[ssize++] = new StackEntry(isaD + 1, first, a, next); - first = a; limit = -3; - } else { - isaD += 1; last = a; limit = next; - } - } - } else { - if (ssize == 0) { - return; - } - StackEntry entry = stack[--ssize]; - isaD = entry.a; - first = entry.b; - last = entry.c; - limit = entry.d; - } - } - continue; - } - - if (last - first <= INSERTIONSORT_THRESHOLD) { - if (!budget.update(size, last - first)) { - break; - } - trInsertionSort(isa, isaD, isaN, first, last); - limit = -3; - continue; - } - - if (limit-- == 0) { - if (!budget.update(size, last - first)) { - break; - } - trHeapSort(isa, isaD, isaN, first, last - first); - for (a = last - 1; first < a; a = b) { - for (x = trGetC(isa, isaD, isaN, SA[a]), b = a - 1; - first <= b && trGetC(isa, isaD, isaN, SA[b]) == x; - --b) { - SA[b] = ~SA[b]; - } - } - limit = -3; - continue; - } - - a = trPivot(isa, isaD, isaN, first, last); - - swapElements(SA, first, SA, a); - v = trGetC(isa, isaD, isaN, SA[first]); - - b = first + 1; - while (b < last && (x = trGetC(isa, isaD, isaN, SA[b])) == v) { - ++b; - } - if ((a = b) < last && x < v) { - while (++b < last && (x = trGetC(isa, isaD, isaN, SA[b])) <= v) { - if (x == v) { - swapElements(SA, b, SA, a); - ++a; - } - } - } - - c = last - 1; - while (b < c && (x = trGetC(isa, isaD, isaN, SA[c])) == v) { - --c; - } - if (b < (d = c) && x > v) { - while (b < --c && (x = trGetC(isa, isaD, isaN, SA[c])) >= v) { - if (x == v) { - swapElements(SA, c, SA, d); - --d; - } - } - } - while (b < c) { - swapElements(SA, b, SA, c); - while (++b < c && (x = trGetC(isa, isaD, isaN, SA[b])) <= v) { - if (x == v) { - swapElements(SA, b, SA, a); - ++a; - } - } - while (b < --c && (x = trGetC(isa, isaD, isaN, SA[c])) >= v) { - if (x == v) { - swapElements(SA, c, SA, d); - --d; - } - } - } - - if (a <= d) { - c = b - 1; - - if ((s = a - first) > (t = b - a)) { - s = t; - } - for (e = first, f = b - s; 0 < s; --s, ++e, ++f) { - swapElements(SA, e, SA, f); - } - if ((s = d - c) > (t = last - d - 1)) { - s = t; - } - for (e = b, f = last - s; 0 < s; --s, ++e, ++f) { - swapElements(SA, e, SA, f); - } - - a = first + (b - a); - b = last - (d - c); - next = SA[isa + SA[a]] != v ? trLog(b - a) : -1; - - for (c = first, v = a - 1; c < a; ++c) { - SA[isa + SA[c]] = v; - } - if (b < last) { - for (c = a, v = b - 1; c < b; ++c) { - SA[isa + SA[c]] = v; } - } - - if (a - first <= last - b) { - if (last - b <= b - a) { - if (1 < a - first) { - stack[ssize++] = new StackEntry(isaD + 1, a, b, next); - stack[ssize++] = new StackEntry(isaD, b, last, limit); - last = a; - } else if (1 < last - b) { - stack[ssize++] = new StackEntry(isaD + 1, a, b, next); - first = b; - } else if (1 < b - a) { - isaD += 1; - first = a; - last = b; - limit = next; - } else { - if (ssize == 0) { - return; - } - StackEntry entry = stack[--ssize]; - isaD = entry.a; - first = entry.b; - last = entry.c; - limit = entry.d; - } - } else if (a - first <= b - a) { - if (1 < a - first) { - stack[ssize++] = new StackEntry(isaD, b, last, limit); - stack[ssize++] = new StackEntry(isaD + 1, a, b, next); - last = a; - } else if (1 < b - a) { - stack[ssize++] = new StackEntry(isaD, b, last, limit); - isaD += 1; - first = a; - last = b; - limit = next; - } else { - first = b; - } - } else { - if (1 < b - a) { - stack[ssize++] = new StackEntry(isaD, b, last, limit); - stack[ssize++] = new StackEntry(isaD, first, a, limit); - isaD += 1; - first = a; - last = b; - limit = next; - } else { - stack[ssize++] = new StackEntry(isaD, b, last, limit); - last = a; - } - } - } else { - if (a - first <= b - a) { - if (1 < last - b) { - stack[ssize++] = new StackEntry(isaD + 1, a, b, next); - stack[ssize++] = new StackEntry(isaD, first, a, limit); - first = b; - } else if (1 < a - first) { - stack[ssize++] = new StackEntry(isaD + 1, a, b, next); - last = a; - } else if (1 < b - a) { - isaD += 1; - first = a; - last = b; - limit = next; - } else { - stack[ssize++] = new StackEntry(isaD, first, last, limit); - } - } else if (last - b <= b - a) { - if (1 < last - b) { - stack[ssize++] = new StackEntry(isaD, first, a, limit); - stack[ssize++] = new StackEntry(isaD + 1, a, b, next); - first = b; - } else if (1 < b - a) { - stack[ssize++] = new StackEntry(isaD, first, a, limit); - isaD += 1; - first = a; - last = b; - limit = next; - } else { - last = a; - } - } else { - if (1 < b - a) { - stack[ssize++] = new StackEntry(isaD, first, a, limit); - stack[ssize++] = new StackEntry(isaD, b, last, limit); - isaD += 1; - first = a; - last = b; - limit = next; - } else { - stack[ssize++] = new StackEntry(isaD, first, a, limit); - first = b; - } - } - } - } else { - if (!budget.update(size, last - first)) { - break; // BUGFIX : Added to prevent an infinite loop in the original code - } - limit += 1; isaD += 1; - } - } - - for (s = 0; s < ssize; ++s) { - if (stack[s].d == -3) { - lsUpdateGroup(isa, stack[s].b, stack[s].c); - } - } - } - - private static class TRBudget { - int budget; - int chance; - - TRBudget(final int budget, final int chance) { - this.budget = budget; - this.chance = chance; - } - - boolean update(final int size, final int n) { - budget -= n; - if (budget <= 0) { - if (--chance == 0) { - return false; - } - budget += size; - } - return true; - } - } - - private void trSort(final int isa, final int n, final int depth) { - final int[] SA = this.SA; - - int first = 0, last; - int t; - - if (-n < SA[0]) { - TRBudget budget = new TRBudget(n, trLog(n) * 2 / 3 + 1); - do { - if ((t = SA[first]) < 0) { - first -= t; - } else { - last = SA[isa + t] + 1; - if (1 < last - first) { - trIntroSort(isa, isa + depth, isa + n, first, last, budget, n); - if (budget.chance == 0) { - /* Switch to Larsson-Sadakane sorting algorithm */ - if (0 < first) { - SA[0] = -first; - } - lsSort(isa, n, depth); - break; - } - } - first = last; - } - } while (first < n); - } - } - - /*---------------------------------------------------------------------------*/ - - private static int BUCKET_B(final int c0, final int c1) { - return (c1 << 8) | c0; - } - - private static int BUCKET_BSTAR(final int c0, final int c1) { - return (c0 << 8) | c1; - } - - private int sortTypeBstar(final int[] bucketA, final int[] bucketB) { - final byte[] T = this.T; - final int[] SA = this.SA; - final int n = this.n; - final int[] tempbuf = new int[256]; - - int[] buf; - int PAb, ISAb, bufoffset; - int i, j, k, t, m, bufsize; - int c0, c1; - int flag; - - for (i = 1, flag = 1; i < n; ++i) { - if (T[i - 1] != T[i]) { - if ((T[i - 1] & 0xff) > (T[i] & 0xff)) { - flag = 0; - } - break; - } - } - i = n - 1; - m = n; - - int ti, ti1, t0; - if ((ti = T[i] & 0xff) < (t0 = T[0] & 0xff) || (T[i] == T[0] && flag != 0)) { - if (flag == 0) { - ++bucketB[BUCKET_BSTAR(ti, t0)]; - SA[--m] = i; - } else { - ++bucketB[BUCKET_B(ti, t0)]; - } - for (--i; 0 <= i && (ti = T[i] & 0xff) <= (ti1 = T[i + 1] & 0xff); --i) { - ++bucketB[BUCKET_B(ti, ti1)]; - } - } - - while (0 <= i) { - do { - ++bucketA[T[i] & 0xff]; - } while (0 <= --i && (T[i] & 0xff) >= (T[i + 1] & 0xff)); - if (0 <= i) { - ++bucketB[BUCKET_BSTAR(T[i] & 0xff, T[i + 1] & 0xff)]; - SA[--m] = i; - for (--i; 0 <= i && (ti = T[i] & 0xff) <= (ti1 = T[i + 1] & 0xff); --i) { - ++bucketB[BUCKET_B(ti, ti1)]; - } - } - } - m = n - m; - if (m == 0) { - for (i = 0; i < n; ++i) { - SA[i] = i; - } - return 0; - } - - for (c0 = 0, i = -1, j = 0; c0 < 256; ++c0) { - t = i + bucketA[c0]; - bucketA[c0] = i + j; - i = t + bucketB[BUCKET_B(c0, c0)]; - for (c1 = c0 + 1; c1 < 256; ++c1) { - j += bucketB[BUCKET_BSTAR(c0, c1)]; - bucketB[(c0 << 8) | c1] = j; - i += bucketB[BUCKET_B(c0, c1)]; - } - } - - PAb = n - m; - ISAb = m; - for (i = m - 2; 0 <= i; --i) { - t = SA[PAb + i]; - c0 = T[t] & 0xff; - c1 = T[t + 1] & 0xff; - SA[--bucketB[BUCKET_BSTAR(c0, c1)]] = i; - } - t = SA[PAb + m - 1]; - c0 = T[t] & 0xff; - c1 = T[t + 1] & 0xff; - SA[--bucketB[BUCKET_BSTAR(c0, c1)]] = m - 1; - - buf = SA; - bufoffset = m; - bufsize = n - 2 * m; - if (bufsize <= 256) { - buf = tempbuf; - bufoffset = 0; - bufsize = 256; - } - - for (c0 = 255, j = m; 0 < j; --c0) { - for (c1 = 255; c0 < c1; j = i, --c1) { - i = bucketB[BUCKET_BSTAR(c0, c1)]; - if (1 < j - i) { - subStringSort(PAb, i, j, buf, bufoffset, bufsize, 2, SA[i] == m - 1, n); - } - } - } - - for (i = m - 1; 0 <= i; --i) { - if (0 <= SA[i]) { - j = i; - do { - SA[ISAb + SA[i]] = i; - } while (0 <= --i && 0 <= SA[i]); - SA[i + 1] = i - j; - if (i <= 0) { - break; - } - } - j = i; - do { - SA[ISAb + (SA[i] = ~SA[i])] = j; - } while (SA[--i] < 0); - SA[ISAb + SA[i]] = j; - } - - trSort(ISAb, m, 1); - - i = n - 1; j = m; - if ((T[i] & 0xff) < (T[0] & 0xff) || (T[i] == T[0] && flag != 0)) { - if (flag == 0) { - SA[SA[ISAb + --j]] = i; - } - for (--i; 0 <= i && (T[i] & 0xff) <= (T[i + 1] & 0xff);) { - --i; - } - } - while (0 <= i) { - for (--i; 0 <= i && (T[i] & 0xff) >= (T[i + 1] & 0xff);) { - --i; - } - if (0 <= i) { - SA[SA[ISAb + --j]] = i; - for (--i; 0 <= i && (T[i] & 0xff) <= (T[i + 1] & 0xff);) { - --i; - } - } - } - - for (c0 = 255, i = n - 1, k = m - 1; 0 <= c0; --c0) { - for (c1 = 255; c0 < c1; --c1) { - t = i - bucketB[BUCKET_B(c0, c1)]; - bucketB[BUCKET_B(c0, c1)] = i + 1; - - for (i = t, j = bucketB[BUCKET_BSTAR(c0, c1)]; j <= k; --i, --k) { - SA[i] = SA[k]; - } - } - t = i - bucketB[BUCKET_B(c0, c0)]; - bucketB[BUCKET_B(c0, c0)] = i + 1; - if (c0 < 255) { - bucketB[BUCKET_BSTAR(c0, c0 + 1)] = t + 1; - } - i = bucketA[c0]; - } - return m; - } - - private int constructBWT(final int[] bucketA, final int[] bucketB) { - final byte[] T = this.T; - final int[] SA = this.SA; - final int n = this.n; - - int i, j, t = 0; - int s, s1; - int c0, c1, c2 = 0; - int orig = -1; - - for (c1 = 254; 0 <= c1; --c1) { - for (i = bucketB[BUCKET_BSTAR(c1, c1 + 1)], j = bucketA[c1 + 1], t = 0, c2 = -1; - i <= j; - --j) { - if (0 <= (s1 = s = SA[j])) { - if (--s < 0) { - s = n - 1; - } - if ((c0 = T[s] & 0xff) <= c1) { - SA[j] = ~s1; - if (0 < s && (T[s - 1] & 0xff) > c0) { - s = ~s; - } - if (c2 == c0) { - SA[--t] = s; - } else { - if (0 <= c2) { - bucketB[BUCKET_B(c2, c1)] = t; - } - SA[t = bucketB[BUCKET_B(c2 = c0, c1)] - 1] = s; - } - } - } else { - SA[j] = ~s; - } - } - } - - for (i = 0; i < n; ++i) { - if (0 <= (s1 = s = SA[i])) { - if (--s < 0) { - s = n - 1; - } - if ((c0 = T[s] & 0xff) >= (T[s + 1] & 0xff)) { - if (0 < s && (T[s - 1] & 0xff) < c0) { - s = ~s; - } - if (c0 == c2) { - SA[++t] = s; - } else { - if (c2 != -1) { - bucketA[c2] = t; // BUGFIX: Original code can write to bucketA[-1] - } - SA[t = bucketA[c2 = c0] + 1] = s; - } - } - } else { - s1 = ~s1; - } - - if (s1 == 0) { - SA[i] = T[n - 1]; - orig = i; - } else { - SA[i] = T[s1 - 1]; - } - } - return orig; - } - - /** - * Performs a Burrows Wheeler Transform on the input array. - * @return the index of the first character of the input array within the output array - */ - public int bwt() { - final int[] SA = this.SA; - final byte[] T = this.T; - final int n = this.n; - - final int[] bucketA = new int[BUCKET_A_SIZE]; - final int[] bucketB = new int[BUCKET_B_SIZE]; - - if (n == 0) { - return 0; - } - if (n == 1) { - SA[0] = T[0]; - return 0; - } - - int m = sortTypeBstar(bucketA, bucketB); - if (0 < m) { - return constructBWT(bucketA, bucketB); - } - return 0; - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/Bzip2Encoder.java b/codec/src/main/java/io/netty/handler/codec/compression/Bzip2Encoder.java deleted file mode 100644 index 6523436d85..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/Bzip2Encoder.java +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.handler.codec.MessageToByteEncoder; -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; - -import java.util.concurrent.TimeUnit; - -import static io.netty.handler.codec.compression.Bzip2Constants.BASE_BLOCK_SIZE; -import static io.netty.handler.codec.compression.Bzip2Constants.END_OF_STREAM_MAGIC_1; -import static io.netty.handler.codec.compression.Bzip2Constants.END_OF_STREAM_MAGIC_2; -import static io.netty.handler.codec.compression.Bzip2Constants.MAGIC_NUMBER; -import static io.netty.handler.codec.compression.Bzip2Constants.MAX_BLOCK_SIZE; -import static io.netty.handler.codec.compression.Bzip2Constants.MIN_BLOCK_SIZE; - -/** - * Compresses a {@link ByteBuf} using the Bzip2 algorithm. - * - * See Bzip2. - */ -public class Bzip2Encoder extends MessageToByteEncoder { - /** - * Current state of stream. - */ - private enum State { - INIT, - INIT_BLOCK, - WRITE_DATA, - CLOSE_BLOCK - } - - private State currentState = State.INIT; - - /** - * A writer that provides bit-level writes. - */ - private final Bzip2BitWriter writer = new Bzip2BitWriter(); - - /** - * The declared maximum block size of the stream (before final run-length decoding). - */ - private final int streamBlockSize; - - /** - * The merged CRC of all blocks compressed so far. - */ - private int streamCRC; - - /** - * The compressor for the current block. - */ - private Bzip2BlockCompressor blockCompressor; - - /** - * (@code true} if the compressed stream has been finished, otherwise {@code false}. - */ - private volatile boolean finished; - - /** - * Used to interact with its {@link ChannelPipeline} and other handlers. - */ - private volatile ChannelHandlerContext ctx; - - /** - * Creates a new bzip2 encoder with the maximum (900,000 byte) block size. - */ - public Bzip2Encoder() { - this(MAX_BLOCK_SIZE); - } - - /** - * Creates a new bzip2 encoder with the specified {@code blockSizeMultiplier}. - * @param blockSizeMultiplier - * The Bzip2 block size as a multiple of 100,000 bytes (minimum {@code 1}, maximum {@code 9}). - * Larger block sizes require more memory for both compression and decompression, - * but give better compression ratios. {@code 9} will usually be the best value to use. - */ - public Bzip2Encoder(final int blockSizeMultiplier) { - if (blockSizeMultiplier < MIN_BLOCK_SIZE || blockSizeMultiplier > MAX_BLOCK_SIZE) { - throw new IllegalArgumentException( - "blockSizeMultiplier: " + blockSizeMultiplier + " (expected: 1-9)"); - } - streamBlockSize = blockSizeMultiplier * BASE_BLOCK_SIZE; - } - - @Override - protected void encode(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) throws Exception { - if (finished) { - out.writeBytes(in); - return; - } - - for (;;) { - switch (currentState) { - case INIT: - out.ensureWritable(4); - out.writeMedium(MAGIC_NUMBER); - out.writeByte('0' + streamBlockSize / BASE_BLOCK_SIZE); - currentState = State.INIT_BLOCK; - // fall through - case INIT_BLOCK: - blockCompressor = new Bzip2BlockCompressor(writer, streamBlockSize); - currentState = State.WRITE_DATA; - // fall through - case WRITE_DATA: - if (!in.isReadable()) { - return; - } - Bzip2BlockCompressor blockCompressor = this.blockCompressor; - final int length = Math.min(in.readableBytes(), blockCompressor.availableSize()); - final int bytesWritten = blockCompressor.write(in, in.readerIndex(), length); - in.skipBytes(bytesWritten); - if (!blockCompressor.isFull()) { - if (in.isReadable()) { - break; - } else { - return; - } - } - currentState = State.CLOSE_BLOCK; - // fall through - case CLOSE_BLOCK: - closeBlock(out); - currentState = State.INIT_BLOCK; - break; - default: - throw new IllegalStateException(); - } - } - } - - /** - * Close current block and update {@link #streamCRC}. - */ - private void closeBlock(ByteBuf out) { - final Bzip2BlockCompressor blockCompressor = this.blockCompressor; - if (!blockCompressor.isEmpty()) { - blockCompressor.close(out); - final int blockCRC = blockCompressor.crc(); - streamCRC = (streamCRC << 1 | streamCRC >>> 31) ^ blockCRC; - } - } - - /** - * Returns {@code true} if and only if the end of the compressed stream has been reached. - */ - public boolean isClosed() { - return finished; - } - - /** - * Close this {@link Bzip2Encoder} and so finish the encoding. - * - * The returned {@link Future} will be notified once the operation completes. - */ - public Future close() { - ChannelHandlerContext ctx = ctx(); - EventExecutor executor = ctx.executor(); - if (executor.inEventLoop()) { - return finishEncode(ctx); - } else { - Promise promise = ctx.newPromise(); - executor.execute(() -> { - Future f = finishEncode(ctx()); - f.cascadeTo(promise); - }); - return promise.asFuture(); - } - } - - @Override - public Future close(final ChannelHandlerContext ctx) { - Future f = finishEncode(ctx); - if (f.isDone()) { - return ctx.close(); - } - Promise promise = ctx.newPromise(); - f.addListener(f1 -> ctx.close().cascadeTo(promise)); - // Ensure the channel is closed even if the write operation completes in time. - ctx.executor().schedule(() -> ctx.close().cascadeTo(promise), - 10, TimeUnit.SECONDS); // FIXME: Magic number - return promise.asFuture(); - } - - private Future finishEncode(final ChannelHandlerContext ctx) { - if (finished) { - return ctx.newSucceededFuture(); - } - finished = true; - - final ByteBuf footer = ctx.alloc().buffer(); - closeBlock(footer); - - final int streamCRC = this.streamCRC; - final Bzip2BitWriter writer = this.writer; - try { - writer.writeBits(footer, 24, END_OF_STREAM_MAGIC_1); - writer.writeBits(footer, 24, END_OF_STREAM_MAGIC_2); - writer.writeInt(footer, streamCRC); - writer.flush(footer); - } finally { - blockCompressor = null; - } - return ctx.writeAndFlush(footer); - } - - private ChannelHandlerContext ctx() { - ChannelHandlerContext ctx = this.ctx; - if (ctx == null) { - throw new IllegalStateException("not added to a pipeline"); - } - return ctx; - } - - @Override - public void handlerAdded(ChannelHandlerContext ctx) throws Exception { - this.ctx = ctx; - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/Bzip2HuffmanAllocator.java b/codec/src/main/java/io/netty/handler/codec/compression/Bzip2HuffmanAllocator.java deleted file mode 100644 index 246b92db19..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/Bzip2HuffmanAllocator.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -/** - * An in-place, length restricted Canonical Huffman code length allocator.
- * Based on the algorithm proposed by R. L. Milidi'u, A. A. Pessoa and E. S. Laber in - * In-place Length-Restricted Prefix Coding - * and incorporating additional ideas from the implementation of - * shcodec by Simakov Alexander. - */ -final class Bzip2HuffmanAllocator { - /** - * @param array The code length array - * @param i The input position - * @param nodesToMove The number of internal nodes to be relocated - * @return The smallest {@code k} such that {@code nodesToMove <= k <= i} and - * {@code i <= (array[k] % array.length)} - */ - private static int first(final int[] array, int i, final int nodesToMove) { - final int length = array.length; - final int limit = i; - int k = array.length - 2; - - while (i >= nodesToMove && array[i] % length > limit) { - k = i; - i -= limit - i + 1; - } - i = Math.max(nodesToMove - 1, i); - - while (k > i + 1) { - int temp = i + k >>> 1; - if (array[temp] % length > limit) { - k = temp; - } else { - i = temp; - } - } - return k; - } - - /** - * Fills the code array with extended parent pointers. - * @param array The code length array - */ - private static void setExtendedParentPointers(final int[] array) { - final int length = array.length; - array[0] += array[1]; - - for (int headNode = 0, tailNode = 1, topNode = 2; tailNode < length - 1; tailNode++) { - int temp; - if (topNode >= length || array[headNode] < array[topNode]) { - temp = array[headNode]; - array[headNode++] = tailNode; - } else { - temp = array[topNode++]; - } - - if (topNode >= length || (headNode < tailNode && array[headNode] < array[topNode])) { - temp += array[headNode]; - array[headNode++] = tailNode + length; - } else { - temp += array[topNode++]; - } - array[tailNode] = temp; - } - } - - /** - * Finds the number of nodes to relocate in order to achieve a given code length limit. - * @param array The code length array - * @param maximumLength The maximum bit length for the generated codes - * @return The number of nodes to relocate - */ - private static int findNodesToRelocate(final int[] array, final int maximumLength) { - int currentNode = array.length - 2; - for (int currentDepth = 1; currentDepth < maximumLength - 1 && currentNode > 1; currentDepth++) { - currentNode = first(array, currentNode - 1, 0); - } - return currentNode; - } - - /** - * A final allocation pass with no code length limit. - * @param array The code length array - */ - private static void allocateNodeLengths(final int[] array) { - int firstNode = array.length - 2; - int nextNode = array.length - 1; - - for (int currentDepth = 1, availableNodes = 2; availableNodes > 0; currentDepth++) { - final int lastNode = firstNode; - firstNode = first(array, lastNode - 1, 0); - - for (int i = availableNodes - (lastNode - firstNode); i > 0; i--) { - array[nextNode--] = currentDepth; - } - - availableNodes = (lastNode - firstNode) << 1; - } - } - - /** - * A final allocation pass that relocates nodes in order to achieve a maximum code length limit. - * @param array The code length array - * @param nodesToMove The number of internal nodes to be relocated - * @param insertDepth The depth at which to insert relocated nodes - */ - private static void allocateNodeLengthsWithRelocation(final int[] array, - final int nodesToMove, final int insertDepth) { - int firstNode = array.length - 2; - int nextNode = array.length - 1; - int currentDepth = insertDepth == 1 ? 2 : 1; - int nodesLeftToMove = insertDepth == 1 ? nodesToMove - 2 : nodesToMove; - - for (int availableNodes = currentDepth << 1; availableNodes > 0; currentDepth++) { - final int lastNode = firstNode; - firstNode = firstNode <= nodesToMove ? firstNode : first(array, lastNode - 1, nodesToMove); - - int offset = 0; - if (currentDepth >= insertDepth) { - offset = Math.min(nodesLeftToMove, 1 << (currentDepth - insertDepth)); - } else if (currentDepth == insertDepth - 1) { - offset = 1; - if (array[firstNode] == lastNode) { - firstNode++; - } - } - - for (int i = availableNodes - (lastNode - firstNode + offset); i > 0; i--) { - array[nextNode--] = currentDepth; - } - - nodesLeftToMove -= offset; - availableNodes = (lastNode - firstNode + offset) << 1; - } - } - - /** - * Allocates Canonical Huffman code lengths in place based on a sorted frequency array. - * @param array On input, a sorted array of symbol frequencies; On output, an array of Canonical - * Huffman code lengths - * @param maximumLength The maximum code length. Must be at least {@code ceil(log2(array.length))} - */ - static void allocateHuffmanCodeLengths(final int[] array, final int maximumLength) { - switch (array.length) { - case 2: - array[1] = 1; - // fall through - case 1: - array[0] = 1; - return; - } - - /* Pass 1 : Set extended parent pointers */ - setExtendedParentPointers(array); - - /* Pass 2 : Find number of nodes to relocate in order to achieve maximum code length */ - int nodesToRelocate = findNodesToRelocate(array, maximumLength); - - /* Pass 3 : Generate code lengths */ - if (array[0] % array.length >= nodesToRelocate) { - allocateNodeLengths(array); - } else { - int insertDepth = maximumLength - (32 - Integer.numberOfLeadingZeros(nodesToRelocate - 1)); - allocateNodeLengthsWithRelocation(array, nodesToRelocate, insertDepth); - } - } - - private Bzip2HuffmanAllocator() { } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/Bzip2HuffmanStageDecoder.java b/codec/src/main/java/io/netty/handler/codec/compression/Bzip2HuffmanStageDecoder.java deleted file mode 100644 index 149fb92ca9..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/Bzip2HuffmanStageDecoder.java +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import static io.netty.handler.codec.compression.Bzip2Constants.*; - -/** - * A decoder for the Bzip2 Huffman coding stage. - */ -final class Bzip2HuffmanStageDecoder { - /** - * A reader that provides bit-level reads. - */ - private final Bzip2BitReader reader; - - /** - * The Huffman table number to use for each group of 50 symbols. - */ - byte[] selectors; - - /** - * The minimum code length for each Huffman table. - */ - private final int[] minimumLengths; - - /** - * An array of values for each Huffman table that must be subtracted from the numerical value of - * a Huffman code of a given bit length to give its canonical code index. - */ - private final int[][] codeBases; - - /** - * An array of values for each Huffman table that gives the highest numerical value of a Huffman - * code of a given bit length. - */ - private final int[][] codeLimits; - - /** - * A mapping for each Huffman table from canonical code index to output symbol. - */ - private final int[][] codeSymbols; - - /** - * The Huffman table for the current group. - */ - private int currentTable; - - /** - * The index of the current group within the selectors array. - */ - private int groupIndex = -1; - - /** - * The byte position within the current group. A new group is selected every 50 decoded bytes. - */ - private int groupPosition = -1; - - /** - * Total number of used Huffman tables in range 2..6. - */ - final int totalTables; - - /** - * The total number of codes (uniform for each table). - */ - final int alphabetSize; - - /** - * Table for Move To Front transformations. - */ - final Bzip2MoveToFrontTable tableMTF = new Bzip2MoveToFrontTable(); - - // For saving state if end of current ByteBuf was reached - int currentSelector; - - /** - * The Canonical Huffman code lengths for each table. - */ - final byte[][] tableCodeLengths; - - // For saving state if end of current ByteBuf was reached - int currentGroup; - int currentLength = -1; - int currentAlpha; - boolean modifyLength; - - Bzip2HuffmanStageDecoder(final Bzip2BitReader reader, final int totalTables, final int alphabetSize) { - this.reader = reader; - this.totalTables = totalTables; - this.alphabetSize = alphabetSize; - - minimumLengths = new int[totalTables]; - codeBases = new int[totalTables][HUFFMAN_DECODE_MAX_CODE_LENGTH + 2]; - codeLimits = new int[totalTables][HUFFMAN_DECODE_MAX_CODE_LENGTH + 1]; - codeSymbols = new int[totalTables][HUFFMAN_MAX_ALPHABET_SIZE]; - tableCodeLengths = new byte[totalTables][HUFFMAN_MAX_ALPHABET_SIZE]; - } - - /** - * Constructs Huffman decoding tables from lists of Canonical Huffman code lengths. - */ - void createHuffmanDecodingTables() { - final int alphabetSize = this.alphabetSize; - - for (int table = 0; table < tableCodeLengths.length; table++) { - final int[] tableBases = codeBases[table]; - final int[] tableLimits = codeLimits[table]; - final int[] tableSymbols = codeSymbols[table]; - final byte[] codeLengths = tableCodeLengths[table]; - - int minimumLength = HUFFMAN_DECODE_MAX_CODE_LENGTH; - int maximumLength = 0; - - // Find the minimum and maximum code length for the table - for (int i = 0; i < alphabetSize; i++) { - final byte currLength = codeLengths[i]; - maximumLength = Math.max(currLength, maximumLength); - minimumLength = Math.min(currLength, minimumLength); - } - minimumLengths[table] = minimumLength; - - // Calculate the first output symbol for each code length - for (int i = 0; i < alphabetSize; i++) { - tableBases[codeLengths[i] + 1]++; - } - for (int i = 1, b = tableBases[0]; i < HUFFMAN_DECODE_MAX_CODE_LENGTH + 2; i++) { - b += tableBases[i]; - tableBases[i] = b; - } - - // Calculate the first and last Huffman code for each code length (codes at a given - // length are sequential in value) - for (int i = minimumLength, code = 0; i <= maximumLength; i++) { - int base = code; - code += tableBases[i + 1] - tableBases[i]; - tableBases[i] = base - tableBases[i]; - tableLimits[i] = code - 1; - code <<= 1; - } - - // Populate the mapping from canonical code index to output symbol - for (int bitLength = minimumLength, codeIndex = 0; bitLength <= maximumLength; bitLength++) { - for (int symbol = 0; symbol < alphabetSize; symbol++) { - if (codeLengths[symbol] == bitLength) { - tableSymbols[codeIndex++] = symbol; - } - } - } - } - - currentTable = selectors[0]; - } - - /** - * Decodes and returns the next symbol. - * @return The decoded symbol - */ - int nextSymbol() { - // Move to next group selector if required - if (++groupPosition % HUFFMAN_GROUP_RUN_LENGTH == 0) { - groupIndex++; - if (groupIndex == selectors.length) { - throw new DecompressionException("error decoding block"); - } - currentTable = selectors[groupIndex] & 0xff; - } - - final Bzip2BitReader reader = this.reader; - final int currentTable = this.currentTable; - final int[] tableLimits = codeLimits[currentTable]; - final int[] tableBases = codeBases[currentTable]; - final int[] tableSymbols = codeSymbols[currentTable]; - int codeLength = minimumLengths[currentTable]; - - // Starting with the minimum bit length for the table, read additional bits one at a time - // until a complete code is recognised - int codeBits = reader.readBits(codeLength); - for (; codeLength <= HUFFMAN_DECODE_MAX_CODE_LENGTH; codeLength++) { - if (codeBits <= tableLimits[codeLength]) { - // Convert the code to a symbol index and return - return tableSymbols[codeBits - tableBases[codeLength]]; - } - codeBits = codeBits << 1 | reader.readBits(1); - } - - throw new DecompressionException("a valid code was not recognised"); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/Bzip2HuffmanStageEncoder.java b/codec/src/main/java/io/netty/handler/codec/compression/Bzip2HuffmanStageEncoder.java deleted file mode 100644 index 2ad006b267..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/Bzip2HuffmanStageEncoder.java +++ /dev/null @@ -1,373 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.buffer.ByteBuf; - -import java.util.Arrays; - -import static io.netty.handler.codec.compression.Bzip2Constants.*; - -/** - * An encoder for the Bzip2 Huffman encoding stage. - */ -final class Bzip2HuffmanStageEncoder { - /** - * Used in initial Huffman table generation. - */ - private static final int HUFFMAN_HIGH_SYMBOL_COST = 15; - - /** - * The {@link Bzip2BitWriter} to which the Huffman tables and data is written. - */ - private final Bzip2BitWriter writer; - - /** - * The output of the Move To Front Transform and Run Length Encoding[2] stages. - */ - private final char[] mtfBlock; - - /** - * The actual number of values contained in the {@link #mtfBlock} array. - */ - private final int mtfLength; - - /** - * The number of unique values in the {@link #mtfBlock} array. - */ - private final int mtfAlphabetSize; - - /** - * The global frequencies of values within the {@link #mtfBlock} array. - */ - private final int[] mtfSymbolFrequencies; - - /** - * The Canonical Huffman code lengths for each table. - */ - private final int[][] huffmanCodeLengths; - - /** - * Merged code symbols for each table. The value at each position is ((code length << 24) | code). - */ - private final int[][] huffmanMergedCodeSymbols; - - /** - * The selectors for each segment. - */ - private final byte[] selectors; - - /** - * @param writer The {@link Bzip2BitWriter} which provides bit-level writes - * @param mtfBlock The MTF block data - * @param mtfLength The actual length of the MTF block - * @param mtfAlphabetSize The size of the MTF block's alphabet - * @param mtfSymbolFrequencies The frequencies the MTF block's symbols - */ - Bzip2HuffmanStageEncoder(final Bzip2BitWriter writer, final char[] mtfBlock, - final int mtfLength, final int mtfAlphabetSize, final int[] mtfSymbolFrequencies) { - this.writer = writer; - this.mtfBlock = mtfBlock; - this.mtfLength = mtfLength; - this.mtfAlphabetSize = mtfAlphabetSize; - this.mtfSymbolFrequencies = mtfSymbolFrequencies; - - final int totalTables = selectTableCount(mtfLength); - - huffmanCodeLengths = new int[totalTables][mtfAlphabetSize]; - huffmanMergedCodeSymbols = new int[totalTables][mtfAlphabetSize]; - selectors = new byte[(mtfLength + HUFFMAN_GROUP_RUN_LENGTH - 1) / HUFFMAN_GROUP_RUN_LENGTH]; - } - - /** - * Selects an appropriate table count for a given MTF length. - * @param mtfLength The length to select a table count for - * @return The selected table count - */ - private static int selectTableCount(final int mtfLength) { - if (mtfLength >= 2400) { - return 6; - } - if (mtfLength >= 1200) { - return 5; - } - if (mtfLength >= 600) { - return 4; - } - if (mtfLength >= 200) { - return 3; - } - return 2; - } - - /** - * Generate a Huffman code length table for a given list of symbol frequencies. - * @param alphabetSize The total number of symbols - * @param symbolFrequencies The frequencies of the symbols - * @param codeLengths The array to which the generated code lengths should be written - */ - private static void generateHuffmanCodeLengths(final int alphabetSize, - final int[] symbolFrequencies, final int[] codeLengths) { - - final int[] mergedFrequenciesAndIndices = new int[alphabetSize]; - final int[] sortedFrequencies = new int[alphabetSize]; - - // The Huffman allocator needs its input symbol frequencies to be sorted, but we need to - // return code lengths in the same order as the corresponding frequencies are passed in. - - // The symbol frequency and index are merged into a single array of - // integers - frequency in the high 23 bits, index in the low 9 bits. - // 2^23 = 8,388,608 which is higher than the maximum possible frequency for one symbol in a block - // 2^9 = 512 which is higher than the maximum possible alphabet size (== 258) - // Sorting this array simultaneously sorts the frequencies and - // leaves a lookup that can be used to cheaply invert the sort. - for (int i = 0; i < alphabetSize; i++) { - mergedFrequenciesAndIndices[i] = (symbolFrequencies[i] << 9) | i; - } - Arrays.sort(mergedFrequenciesAndIndices); - for (int i = 0; i < alphabetSize; i++) { - sortedFrequencies[i] = mergedFrequenciesAndIndices[i] >>> 9; - } - - // Allocate code lengths - the allocation is in place, - // so the code lengths will be in the sortedFrequencies array afterwards - Bzip2HuffmanAllocator.allocateHuffmanCodeLengths(sortedFrequencies, HUFFMAN_ENCODE_MAX_CODE_LENGTH); - - // Reverse the sort to place the code lengths in the same order as the symbols whose frequencies were passed in - for (int i = 0; i < alphabetSize; i++) { - codeLengths[mergedFrequenciesAndIndices[i] & 0x1ff] = sortedFrequencies[i]; - } - } - - /** - * Generate initial Huffman code length tables, giving each table a different low cost section - * of the alphabet that is roughly equal in overall cumulative frequency. Note that the initial - * tables are invalid for actual Huffman code generation, and only serve as the seed for later - * iterative optimisation in {@link #optimiseSelectorsAndHuffmanTables(boolean)}. - */ - private void generateHuffmanOptimisationSeeds() { - final int[][] huffmanCodeLengths = this.huffmanCodeLengths; - final int[] mtfSymbolFrequencies = this.mtfSymbolFrequencies; - final int mtfAlphabetSize = this.mtfAlphabetSize; - - final int totalTables = huffmanCodeLengths.length; - - int remainingLength = mtfLength; - int lowCostEnd = -1; - - for (int i = 0; i < totalTables; i++) { - - final int targetCumulativeFrequency = remainingLength / (totalTables - i); - final int lowCostStart = lowCostEnd + 1; - int actualCumulativeFrequency = 0; - - while (actualCumulativeFrequency < targetCumulativeFrequency && lowCostEnd < mtfAlphabetSize - 1) { - actualCumulativeFrequency += mtfSymbolFrequencies[++lowCostEnd]; - } - - if (lowCostEnd > lowCostStart && i != 0 && i != totalTables - 1 && (totalTables - i & 1) == 0) { - actualCumulativeFrequency -= mtfSymbolFrequencies[lowCostEnd--]; - } - - final int[] tableCodeLengths = huffmanCodeLengths[i]; - for (int j = 0; j < mtfAlphabetSize; j++) { - if (j < lowCostStart || j > lowCostEnd) { - tableCodeLengths[j] = HUFFMAN_HIGH_SYMBOL_COST; - } - } - - remainingLength -= actualCumulativeFrequency; - } - } - - /** - * Co-optimise the selector list and the alternative Huffman table code lengths. This method is - * called repeatedly in the hope that the total encoded size of the selectors, the Huffman code - * lengths and the block data encoded with them will converge towards a minimum.
- * If the data is highly incompressible, it is possible that the total encoded size will - * instead diverge (increase) slightly.
- * @param storeSelectors If {@code true}, write out the (final) chosen selectors - */ - private void optimiseSelectorsAndHuffmanTables(final boolean storeSelectors) { - final char[] mtfBlock = this.mtfBlock; - final byte[] selectors = this.selectors; - final int[][] huffmanCodeLengths = this.huffmanCodeLengths; - final int mtfLength = this.mtfLength; - final int mtfAlphabetSize = this.mtfAlphabetSize; - - final int totalTables = huffmanCodeLengths.length; - final int[][] tableFrequencies = new int[totalTables][mtfAlphabetSize]; - - int selectorIndex = 0; - - // Find the best table for each group of 50 block bytes based on the current Huffman code lengths - for (int groupStart = 0; groupStart < mtfLength;) { - - final int groupEnd = Math.min(groupStart + HUFFMAN_GROUP_RUN_LENGTH, mtfLength) - 1; - - // Calculate the cost of this group when encoded by each table - int[] cost = new int[totalTables]; - for (int i = groupStart; i <= groupEnd; i++) { - final int value = mtfBlock[i]; - for (int j = 0; j < totalTables; j++) { - cost[j] += huffmanCodeLengths[j][value]; - } - } - - // Find the table with the least cost for this group - byte bestTable = 0; - int bestCost = cost[0]; - for (byte i = 1 ; i < totalTables; i++) { - final int tableCost = cost[i]; - if (tableCost < bestCost) { - bestCost = tableCost; - bestTable = i; - } - } - - // Accumulate symbol frequencies for the table chosen for this block - final int[] bestGroupFrequencies = tableFrequencies[bestTable]; - for (int i = groupStart; i <= groupEnd; i++) { - bestGroupFrequencies[mtfBlock[i]]++; - } - - // Store a selector indicating the table chosen for this block - if (storeSelectors) { - selectors[selectorIndex++] = bestTable; - } - groupStart = groupEnd + 1; - } - - // Generate new Huffman code lengths based on the frequencies for each table accumulated in this iteration - for (int i = 0; i < totalTables; i++) { - generateHuffmanCodeLengths(mtfAlphabetSize, tableFrequencies[i], huffmanCodeLengths[i]); - } - } - - /** - * Assigns Canonical Huffman codes based on the calculated lengths. - */ - private void assignHuffmanCodeSymbols() { - final int[][] huffmanMergedCodeSymbols = this.huffmanMergedCodeSymbols; - final int[][] huffmanCodeLengths = this.huffmanCodeLengths; - final int mtfAlphabetSize = this.mtfAlphabetSize; - - final int totalTables = huffmanCodeLengths.length; - - for (int i = 0; i < totalTables; i++) { - final int[] tableLengths = huffmanCodeLengths[i]; - - int minimumLength = 32; - int maximumLength = 0; - for (int j = 0; j < mtfAlphabetSize; j++) { - final int length = tableLengths[j]; - if (length > maximumLength) { - maximumLength = length; - } - if (length < minimumLength) { - minimumLength = length; - } - } - - int code = 0; - for (int j = minimumLength; j <= maximumLength; j++) { - for (int k = 0; k < mtfAlphabetSize; k++) { - if ((huffmanCodeLengths[i][k] & 0xff) == j) { - huffmanMergedCodeSymbols[i][k] = (j << 24) | code; - code++; - } - } - code <<= 1; - } - } - } - - /** - * Write out the selector list and Huffman tables. - */ - private void writeSelectorsAndHuffmanTables(ByteBuf out) { - final Bzip2BitWriter writer = this.writer; - final byte[] selectors = this.selectors; - final int totalSelectors = selectors.length; - final int[][] huffmanCodeLengths = this.huffmanCodeLengths; - final int totalTables = huffmanCodeLengths.length; - final int mtfAlphabetSize = this.mtfAlphabetSize; - - writer.writeBits(out, 3, totalTables); - writer.writeBits(out, 15, totalSelectors); - - // Write the selectors - Bzip2MoveToFrontTable selectorMTF = new Bzip2MoveToFrontTable(); - for (byte selector : selectors) { - writer.writeUnary(out, selectorMTF.valueToFront(selector)); - } - - // Write the Huffman tables - for (final int[] tableLengths : huffmanCodeLengths) { - int currentLength = tableLengths[0]; - - writer.writeBits(out, 5, currentLength); - - for (int j = 0; j < mtfAlphabetSize; j++) { - final int codeLength = tableLengths[j]; - final int value = currentLength < codeLength ? 2 : 3; - int delta = Math.abs(codeLength - currentLength); - while (delta-- > 0) { - writer.writeBits(out, 2, value); - } - writer.writeBoolean(out, false); - currentLength = codeLength; - } - } - } - - /** - * Writes out the encoded block data. - */ - private void writeBlockData(ByteBuf out) { - final Bzip2BitWriter writer = this.writer; - final int[][] huffmanMergedCodeSymbols = this.huffmanMergedCodeSymbols; - final byte[] selectors = this.selectors; - final int mtfLength = this.mtfLength; - - int selectorIndex = 0; - for (int mtfIndex = 0; mtfIndex < mtfLength;) { - final int groupEnd = Math.min(mtfIndex + HUFFMAN_GROUP_RUN_LENGTH, mtfLength) - 1; - final int[] tableMergedCodeSymbols = huffmanMergedCodeSymbols[selectors[selectorIndex++]]; - - while (mtfIndex <= groupEnd) { - final int mergedCodeSymbol = tableMergedCodeSymbols[mtfBlock[mtfIndex++]]; - writer.writeBits(out, mergedCodeSymbol >>> 24, mergedCodeSymbol); - } - } - } - - /** - * Encodes and writes the block data. - */ - void encode(ByteBuf out) { - // Create optimised selector list and Huffman tables - generateHuffmanOptimisationSeeds(); - for (int i = 3; i >= 0; i--) { - optimiseSelectorsAndHuffmanTables(i == 0); - } - assignHuffmanCodeSymbols(); - - // Write out the tables and the block data encoded with them - writeSelectorsAndHuffmanTables(out); - writeBlockData(out); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/Bzip2MTFAndRLE2StageEncoder.java b/codec/src/main/java/io/netty/handler/codec/compression/Bzip2MTFAndRLE2StageEncoder.java deleted file mode 100644 index a3c2783b75..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/Bzip2MTFAndRLE2StageEncoder.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import static io.netty.handler.codec.compression.Bzip2Constants.*; - -/** - * An encoder for the Bzip2 Move To Front Transform and Run-Length Encoding[2] stages.
- * Although conceptually these two stages are separate, it is computationally efficient to perform - * them in one pass. - */ -final class Bzip2MTFAndRLE2StageEncoder { - /** - * The Burrows-Wheeler transformed block. - */ - private final int[] bwtBlock; - - /** - * Actual length of the data in the {@link #bwtBlock} array. - */ - private final int bwtLength; - - /** - * At each position, {@code true} if the byte value with that index is present within the block, - * otherwise {@code false}. - */ - private final boolean[] bwtValuesPresent; - - /** - * The output of the Move To Front Transform and Run-Length Encoding[2] stages. - */ - private final char[] mtfBlock; - - /** - * The actual number of values contained in the {@link #mtfBlock} array. - */ - private int mtfLength; - - /** - * The global frequencies of values within the {@link #mtfBlock} array. - */ - private final int[] mtfSymbolFrequencies = new int[HUFFMAN_MAX_ALPHABET_SIZE]; - - /** - * The encoded alphabet size. - */ - private int alphabetSize; - - /** - * @param bwtBlock The Burrows Wheeler Transformed block data - * @param bwtLength The actual length of the BWT data - * @param bwtValuesPresent The values that are present within the BWT data. For each index, - * {@code true} if that value is present within the data, otherwise {@code false} - */ - Bzip2MTFAndRLE2StageEncoder(final int[] bwtBlock, final int bwtLength, final boolean[] bwtValuesPresent) { - this.bwtBlock = bwtBlock; - this.bwtLength = bwtLength; - this.bwtValuesPresent = bwtValuesPresent; - mtfBlock = new char[bwtLength + 1]; - } - - /** - * Performs the Move To Front transform and Run Length Encoding[1] stages. - */ - void encode() { - final int bwtLength = this.bwtLength; - final boolean[] bwtValuesPresent = this.bwtValuesPresent; - final int[] bwtBlock = this.bwtBlock; - final char[] mtfBlock = this.mtfBlock; - final int[] mtfSymbolFrequencies = this.mtfSymbolFrequencies; - final byte[] huffmanSymbolMap = new byte[256]; - final Bzip2MoveToFrontTable symbolMTF = new Bzip2MoveToFrontTable(); - - int totalUniqueValues = 0; - for (int i = 0; i < huffmanSymbolMap.length; i++) { - if (bwtValuesPresent[i]) { - huffmanSymbolMap[i] = (byte) totalUniqueValues++; - } - } - final int endOfBlockSymbol = totalUniqueValues + 1; - - int mtfIndex = 0; - int repeatCount = 0; - int totalRunAs = 0; - int totalRunBs = 0; - for (int i = 0; i < bwtLength; i++) { - // Move To Front - final int mtfPosition = symbolMTF.valueToFront(huffmanSymbolMap[bwtBlock[i] & 0xff]); - // Run Length Encode - if (mtfPosition == 0) { - repeatCount++; - } else { - if (repeatCount > 0) { - repeatCount--; - while (true) { - if ((repeatCount & 1) == 0) { - mtfBlock[mtfIndex++] = HUFFMAN_SYMBOL_RUNA; - totalRunAs++; - } else { - mtfBlock[mtfIndex++] = HUFFMAN_SYMBOL_RUNB; - totalRunBs++; - } - - if (repeatCount <= 1) { - break; - } - repeatCount = (repeatCount - 2) >>> 1; - } - repeatCount = 0; - } - mtfBlock[mtfIndex++] = (char) (mtfPosition + 1); - mtfSymbolFrequencies[mtfPosition + 1]++; - } - } - - if (repeatCount > 0) { - repeatCount--; - while (true) { - if ((repeatCount & 1) == 0) { - mtfBlock[mtfIndex++] = HUFFMAN_SYMBOL_RUNA; - totalRunAs++; - } else { - mtfBlock[mtfIndex++] = HUFFMAN_SYMBOL_RUNB; - totalRunBs++; - } - - if (repeatCount <= 1) { - break; - } - repeatCount = (repeatCount - 2) >>> 1; - } - } - - mtfBlock[mtfIndex] = (char) endOfBlockSymbol; - mtfSymbolFrequencies[endOfBlockSymbol]++; - mtfSymbolFrequencies[HUFFMAN_SYMBOL_RUNA] += totalRunAs; - mtfSymbolFrequencies[HUFFMAN_SYMBOL_RUNB] += totalRunBs; - - mtfLength = mtfIndex + 1; - alphabetSize = endOfBlockSymbol + 1; - } - - /** - * @return The encoded MTF block - */ - char[] mtfBlock() { - return mtfBlock; - } - - /** - * @return The actual length of the MTF block - */ - int mtfLength() { - return mtfLength; - } - - /** - * @return The size of the MTF block's alphabet - */ - int mtfAlphabetSize() { - return alphabetSize; - } - - /** - * @return The frequencies of the MTF block's symbols - */ - int[] mtfSymbolFrequencies() { - return mtfSymbolFrequencies; - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/Bzip2MoveToFrontTable.java b/codec/src/main/java/io/netty/handler/codec/compression/Bzip2MoveToFrontTable.java deleted file mode 100644 index 71e66d945c..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/Bzip2MoveToFrontTable.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -/** - * A 256 entry Move To Front transform. - */ -final class Bzip2MoveToFrontTable { - /** - * The Move To Front list. - */ - private final byte[] mtf = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, - 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, - 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, - 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, - (byte) 128, (byte) 129, (byte) 130, (byte) 131, (byte) 132, (byte) 133, (byte) 134, (byte) 135, - (byte) 136, (byte) 137, (byte) 138, (byte) 139, (byte) 140, (byte) 141, (byte) 142, (byte) 143, - (byte) 144, (byte) 145, (byte) 146, (byte) 147, (byte) 148, (byte) 149, (byte) 150, (byte) 151, - (byte) 152, (byte) 153, (byte) 154, (byte) 155, (byte) 156, (byte) 157, (byte) 158, (byte) 159, - (byte) 160, (byte) 161, (byte) 162, (byte) 163, (byte) 164, (byte) 165, (byte) 166, (byte) 167, - (byte) 168, (byte) 169, (byte) 170, (byte) 171, (byte) 172, (byte) 173, (byte) 174, (byte) 175, - (byte) 176, (byte) 177, (byte) 178, (byte) 179, (byte) 180, (byte) 181, (byte) 182, (byte) 183, - (byte) 184, (byte) 185, (byte) 186, (byte) 187, (byte) 188, (byte) 189, (byte) 190, (byte) 191, - (byte) 192, (byte) 193, (byte) 194, (byte) 195, (byte) 196, (byte) 197, (byte) 198, (byte) 199, - (byte) 200, (byte) 201, (byte) 202, (byte) 203, (byte) 204, (byte) 205, (byte) 206, (byte) 207, - (byte) 208, (byte) 209, (byte) 210, (byte) 211, (byte) 212, (byte) 213, (byte) 214, (byte) 215, - (byte) 216, (byte) 217, (byte) 218, (byte) 219, (byte) 220, (byte) 221, (byte) 222, (byte) 223, - (byte) 224, (byte) 225, (byte) 226, (byte) 227, (byte) 228, (byte) 229, (byte) 230, (byte) 231, - (byte) 232, (byte) 233, (byte) 234, (byte) 235, (byte) 236, (byte) 237, (byte) 238, (byte) 239, - (byte) 240, (byte) 241, (byte) 242, (byte) 243, (byte) 244, (byte) 245, (byte) 246, (byte) 247, - (byte) 248, (byte) 249, (byte) 250, (byte) 251, (byte) 252, (byte) 253, (byte) 254, (byte) 255 - }; - - /** - * Moves a value to the head of the MTF list (forward Move To Front transform). - * @param value The value to move - * @return The position the value moved from - */ - int valueToFront(final byte value) { - int index = 0; - byte temp = mtf[0]; - if (value != temp) { - mtf[0] = value; - while (value != temp) { - index++; - final byte temp2 = temp; - temp = mtf[index]; - mtf[index] = temp2; - } - } - return index; - } - - /** - * Gets the value from a given index and moves it to the front of the MTF list (inverse Move To Front transform). - * @param index The index to move - * @return The value at the given index - */ - byte indexToFront(final int index) { - final byte value = mtf[index]; - System.arraycopy(mtf, 0, mtf, 1, index); - mtf[0] = value; - - return value; - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/Bzip2Rand.java b/codec/src/main/java/io/netty/handler/codec/compression/Bzip2Rand.java deleted file mode 100644 index 65a9e46593..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/Bzip2Rand.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -/** - * Random numbers for decompress Bzip2 blocks. - */ -final class Bzip2Rand { - /** - * The Bzip2 specification originally included the optional addition of a slight pseudo-random - * perturbation to the input data, in order to work around the block sorting algorithm's non- - * optimal performance on some types of input. The current mainline bzip2 does not require this - * and will not create randomised blocks, but compatibility is still required for old data (and - * third party compressors that haven't caught up). When decompressing a randomised block, for - * each value N in this array, a 1 will be XOR'd onto the output of the Burrows-Wheeler - * transform stage after N bytes, then the next N taken from the following entry. - */ - private static final int[] RNUMS = { - 619, 720, 127, 481, 931, 816, 813, 233, 566, 247, 985, 724, 205, 454, 863, 491, - 741, 242, 949, 214, 733, 859, 335, 708, 621, 574, 73, 654, 730, 472, 419, 436, - 278, 496, 867, 210, 399, 680, 480, 51, 878, 465, 811, 169, 869, 675, 611, 697, - 867, 561, 862, 687, 507, 283, 482, 129, 807, 591, 733, 623, 150, 238, 59, 379, - 684, 877, 625, 169, 643, 105, 170, 607, 520, 932, 727, 476, 693, 425, 174, 647, - 73, 122, 335, 530, 442, 853, 695, 249, 445, 515, 909, 545, 703, 919, 874, 474, - 882, 500, 594, 612, 641, 801, 220, 162, 819, 984, 589, 513, 495, 799, 161, 604, - 958, 533, 221, 400, 386, 867, 600, 782, 382, 596, 414, 171, 516, 375, 682, 485, - 911, 276, 98, 553, 163, 354, 666, 933, 424, 341, 533, 870, 227, 730, 475, 186, - 263, 647, 537, 686, 600, 224, 469, 68, 770, 919, 190, 373, 294, 822, 808, 206, - 184, 943, 795, 384, 383, 461, 404, 758, 839, 887, 715, 67, 618, 276, 204, 918, - 873, 777, 604, 560, 951, 160, 578, 722, 79, 804, 96, 409, 713, 940, 652, 934, - 970, 447, 318, 353, 859, 672, 112, 785, 645, 863, 803, 350, 139, 93, 354, 99, - 820, 908, 609, 772, 154, 274, 580, 184, 79, 626, 630, 742, 653, 282, 762, 623, - 680, 81, 927, 626, 789, 125, 411, 521, 938, 300, 821, 78, 343, 175, 128, 250, - 170, 774, 972, 275, 999, 639, 495, 78, 352, 126, 857, 956, 358, 619, 580, 124, - 737, 594, 701, 612, 669, 112, 134, 694, 363, 992, 809, 743, 168, 974, 944, 375, - 748, 52, 600, 747, 642, 182, 862, 81, 344, 805, 988, 739, 511, 655, 814, 334, - 249, 515, 897, 955, 664, 981, 649, 113, 974, 459, 893, 228, 433, 837, 553, 268, - 926, 240, 102, 654, 459, 51, 686, 754, 806, 760, 493, 403, 415, 394, 687, 700, - 946, 670, 656, 610, 738, 392, 760, 799, 887, 653, 978, 321, 576, 617, 626, 502, - 894, 679, 243, 440, 680, 879, 194, 572, 640, 724, 926, 56, 204, 700, 707, 151, - 457, 449, 797, 195, 791, 558, 945, 679, 297, 59, 87, 824, 713, 663, 412, 693, - 342, 606, 134, 108, 571, 364, 631, 212, 174, 643, 304, 329, 343, 97, 430, 751, - 497, 314, 983, 374, 822, 928, 140, 206, 73, 263, 980, 736, 876, 478, 430, 305, - 170, 514, 364, 692, 829, 82, 855, 953, 676, 246, 369, 970, 294, 750, 807, 827, - 150, 790, 288, 923, 804, 378, 215, 828, 592, 281, 565, 555, 710, 82, 896, 831, - 547, 261, 524, 462, 293, 465, 502, 56, 661, 821, 976, 991, 658, 869, 905, 758, - 745, 193, 768, 550, 608, 933, 378, 286, 215, 979, 792, 961, 61, 688, 793, 644, - 986, 403, 106, 366, 905, 644, 372, 567, 466, 434, 645, 210, 389, 550, 919, 135, - 780, 773, 635, 389, 707, 100, 626, 958, 165, 504, 920, 176, 193, 713, 857, 265, - 203, 50, 668, 108, 645, 990, 626, 197, 510, 357, 358, 850, 858, 364, 936, 638 - }; - - /** - * Return the random number at a specific index. - * - * @param i the index - * @return the random number - */ - static int rNums(int i) { - return RNUMS[i]; - } - - private Bzip2Rand() { } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/CompressionException.java b/codec/src/main/java/io/netty/handler/codec/compression/CompressionException.java deleted file mode 100644 index ccadcf89da..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/CompressionException.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.handler.codec.EncoderException; - -/** - * An {@link EncoderException} that is raised when compression failed. - */ -public class CompressionException extends EncoderException { - - private static final long serialVersionUID = 5603413481274811897L; - - /** - * Creates a new instance. - */ - public CompressionException() { - } - - /** - * Creates a new instance. - */ - public CompressionException(String message, Throwable cause) { - super(message, cause); - } - - /** - * Creates a new instance. - */ - public CompressionException(String message) { - super(message); - } - - /** - * Creates a new instance. - */ - public CompressionException(Throwable cause) { - super(cause); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/CompressionOptions.java b/codec/src/main/java/io/netty/handler/codec/compression/CompressionOptions.java deleted file mode 100644 index 9ee964619c..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/CompressionOptions.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -/** - * {@link CompressionOptions} provides compression options for - * various types of compressor types, like Brotli. - * - * A {@link CompressionOptions} instance is thread-safe - * and should be shared between multiple instances of Compressor. - */ -public interface CompressionOptions { - // Empty -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/CompressionUtil.java b/codec/src/main/java/io/netty/handler/codec/compression/CompressionUtil.java deleted file mode 100644 index 56e85ef47f..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/CompressionUtil.java +++ /dev/null @@ -1,48 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.buffer.ByteBuf; - -import java.nio.ByteBuffer; - -final class CompressionUtil { - - private CompressionUtil() { } - - static void checkChecksum(ByteBufChecksum checksum, ByteBuf uncompressed, int currentChecksum) { - checksum.reset(); - checksum.update(uncompressed, - uncompressed.readerIndex(), uncompressed.readableBytes()); - - final int checksumResult = (int) checksum.getValue(); - if (checksumResult != currentChecksum) { - throw new DecompressionException(String.format( - "stream corrupted: mismatching checksum: %d (expected: %d)", - checksumResult, currentChecksum)); - } - } - - static ByteBuffer safeNioBuffer(ByteBuf buffer) { - return buffer.nioBufferCount() == 1 ? buffer.internalNioBuffer(buffer.readerIndex(), buffer.readableBytes()) - : buffer.nioBuffer(); - } - - static ByteBuffer safeNioBuffer(ByteBuf buffer, int index, int length) { - return buffer.nioBufferCount() == 1 ? buffer.internalNioBuffer(index, length) - : buffer.nioBuffer(index, length); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/Crc32.java b/codec/src/main/java/io/netty/handler/codec/compression/Crc32.java deleted file mode 100644 index b80afeb649..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/Crc32.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -/** - * A CRC32 calculator. - */ -final class Crc32 { - /** - * A static CRC lookup table. - */ - private static final int[] crc32Table = { - 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, - 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, - 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, - 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, - 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, - 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, - 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, - 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd, - 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, - 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, - 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, - 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, - 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, - 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, - 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, - 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, - 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, - 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, - 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, - 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, - 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, - 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, - 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, - 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, - 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, - 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692, - 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, - 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, - 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, - 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, - 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, - 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a, - 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, - 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, - 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, - 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, - 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, - 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b, - 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, - 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, - 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, - 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, - 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, - 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, - 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, - 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, - 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, - 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, - 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, - 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, - 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, - 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, - 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, - 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, - 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, - 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, - 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, - 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, - 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, - 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, - 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, - 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c, - 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, - 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 - }; - - /** - * The current CRC. - */ - private int crc = 0xffffffff; - - /** - * @return The current CRC. - */ - public int getCRC() { - return ~crc; - } - - /** - * Update the CRC with a single byte. - * @param value The value to update the CRC with - */ - public void updateCRC(final int value) { - final int crc = this.crc; - this.crc = crc << 8 ^ crc32Table[(crc >> 24 ^ value) & 0xff]; - } - - /** - * Update the CRC with a sequence of identical bytes. - * @param value The value to update the CRC with - * @param count The number of bytes - */ - public void updateCRC(final int value, int count) { - while (count-- > 0) { - updateCRC(value); - } - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/Crc32c.java b/codec/src/main/java/io/netty/handler/codec/compression/Crc32c.java deleted file mode 100644 index f4b4a52b55..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/Crc32c.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -/** - * Implements CRC32-C as defined in: - * "Optimization of Cyclic Redundancy-CHeck Codes with 24 and 32 Parity Bits", - * IEEE Transactions on Communications 41(6): 883-892 (1993). - * - * The implementation of this class has been sourced from the Appendix of RFC 3309, - * but with masking due to Java not being able to support unsigned types. - */ -class Crc32c extends ByteBufChecksum { - private static final int[] CRC_TABLE = { - 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4, - 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB, - 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B, - 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24, - 0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B, - 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384, - 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54, - 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B, - 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A, - 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35, - 0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5, - 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA, - 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45, - 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A, - 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A, - 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595, - 0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48, - 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957, - 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687, - 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198, - 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927, - 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38, - 0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8, - 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7, - 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096, - 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789, - 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859, - 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46, - 0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9, - 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6, - 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36, - 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829, - 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C, - 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93, - 0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043, - 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C, - 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3, - 0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC, - 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C, - 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033, - 0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652, - 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D, - 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D, - 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982, - 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D, - 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622, - 0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2, - 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED, - 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530, - 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F, - 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF, - 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0, - 0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F, - 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540, - 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90, - 0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F, - 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE, - 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1, - 0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321, - 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E, - 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81, - 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E, - 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E, - 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351, - }; - - private static final long LONG_MASK = 0xFFFFFFFFL; - private static final int BYTE_MASK = 0xFF; - - private int crc = ~0; - - @Override - public void update(int b) { - crc = crc32c(crc, b); - } - - @Override - public void update(byte[] buffer, int offset, int length) { - int end = offset + length; - for (int i = offset; i < end; i++) { - update(buffer[i]); - } - } - - @Override - public long getValue() { - return (crc ^ LONG_MASK) & LONG_MASK; - } - - @Override - public void reset() { - crc = ~0; - } - - private static int crc32c(int crc, int b) { - return crc >>> 8 ^ CRC_TABLE[(crc ^ b & BYTE_MASK) & BYTE_MASK]; - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/DecompressionException.java b/codec/src/main/java/io/netty/handler/codec/compression/DecompressionException.java deleted file mode 100644 index b9dae26e80..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/DecompressionException.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.handler.codec.DecoderException; - -/** - * A {@link DecoderException} that is raised when decompression failed. - */ -public class DecompressionException extends DecoderException { - - private static final long serialVersionUID = 3546272712208105199L; - - /** - * Creates a new instance. - */ - public DecompressionException() { - } - - /** - * Creates a new instance. - */ - public DecompressionException(String message, Throwable cause) { - super(message, cause); - } - - /** - * Creates a new instance. - */ - public DecompressionException(String message) { - super(message); - } - - /** - * Creates a new instance. - */ - public DecompressionException(Throwable cause) { - super(cause); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/DeflateOptions.java b/codec/src/main/java/io/netty/handler/codec/compression/DeflateOptions.java deleted file mode 100644 index 85a67aaf03..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/DeflateOptions.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.util.internal.ObjectUtil; - -/** - * {@link DeflateOptions} holds {@link #compressionLevel()}, - * {@link #memLevel()} and {@link #windowBits()} for Deflate compression. - */ -public class DeflateOptions implements CompressionOptions { - - private final int compressionLevel; - private final int windowBits; - private final int memLevel; - - /** - * @see StandardCompressionOptions#deflate() - */ - static final DeflateOptions DEFAULT = new DeflateOptions( - 6, 15, 8 - ); - - /** - * @see StandardCompressionOptions#deflate(int, int, int) - */ - DeflateOptions(int compressionLevel, int windowBits, int memLevel) { - this.compressionLevel = ObjectUtil.checkInRange(compressionLevel, 0, 9, "compressionLevel"); - this.windowBits = ObjectUtil.checkInRange(windowBits, 9, 15, "windowBits"); - this.memLevel = ObjectUtil.checkInRange(memLevel, 1, 9, "memLevel"); - } - - public int compressionLevel() { - return compressionLevel; - } - - public int windowBits() { - return windowBits; - } - - public int memLevel() { - return memLevel; - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/FastLz.java b/codec/src/main/java/io/netty/handler/codec/compression/FastLz.java deleted file mode 100644 index bfb4f00aca..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/FastLz.java +++ /dev/null @@ -1,560 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.buffer.ByteBuf; - -/** - * Core of FastLZ compression algorithm. - * - * This class provides methods for compression and decompression of buffers and saves - * constants which use by {@link FastLzFrameEncoder} and {@link FastLzFrameDecoder}. - * - * This is refactored code of jfastlz - * library written by William Kinney. - */ -final class FastLz { - - private static final int MAX_DISTANCE = 8191; - private static final int MAX_FARDISTANCE = 65535 + MAX_DISTANCE - 1; - - private static final int HASH_LOG = 13; - private static final int HASH_SIZE = 1 << HASH_LOG; // 8192 - private static final int HASH_MASK = HASH_SIZE - 1; - - private static final int MAX_COPY = 32; - private static final int MAX_LEN = 256 + 8; - - private static final int MIN_RECOMENDED_LENGTH_FOR_LEVEL_2 = 1024 * 64; - - static final int MAGIC_NUMBER = 'F' << 16 | 'L' << 8 | 'Z'; - - static final byte BLOCK_TYPE_NON_COMPRESSED = 0x00; - static final byte BLOCK_TYPE_COMPRESSED = 0x01; - static final byte BLOCK_WITHOUT_CHECKSUM = 0x00; - static final byte BLOCK_WITH_CHECKSUM = 0x10; - - static final int OPTIONS_OFFSET = 3; - static final int CHECKSUM_OFFSET = 4; - - static final int MAX_CHUNK_LENGTH = 0xFFFF; - - /** - * Do not call {@link #compress(ByteBuf, int, int, ByteBuf, int, int)} for input buffers - * which length less than this value. - */ - static final int MIN_LENGTH_TO_COMPRESSION = 32; - - /** - * In this case {@link #compress(ByteBuf, int, int, ByteBuf, int, int)} will choose level - * automatically depending on the length of the input buffer. If length less than - * {@link #MIN_RECOMENDED_LENGTH_FOR_LEVEL_2} {@link #LEVEL_1} will be chosen, - * otherwise {@link #LEVEL_2}. - */ - static final int LEVEL_AUTO = 0; - - /** - * Level 1 is the fastest compression and generally useful for short data. - */ - static final int LEVEL_1 = 1; - - /** - * Level 2 is slightly slower but it gives better compression ratio. - */ - static final int LEVEL_2 = 2; - - /** - * The output buffer must be at least 6% larger than the input buffer and can not be smaller than 66 bytes. - * @param inputLength length of input buffer - * @return Maximum output buffer length - */ - static int calculateOutputBufferLength(int inputLength) { - final int outputLength = (int) (inputLength * 1.06); - return Math.max(outputLength, 66); - } - - /** - * Compress a block of data in the input buffer and returns the size of compressed block. - * The size of input buffer is specified by length. The minimum input buffer size is 32. - * - * If the input is not compressible, the return value might be larger than length (input buffer size). - */ - @SuppressWarnings("IdentityBinaryExpression") - static int compress(final ByteBuf input, final int inOffset, final int inLength, - final ByteBuf output, final int outOffset, final int proposedLevel) { - final int level; - if (proposedLevel == LEVEL_AUTO) { - level = inLength < MIN_RECOMENDED_LENGTH_FOR_LEVEL_2 ? LEVEL_1 : LEVEL_2; - } else { - level = proposedLevel; - } - - int ip = 0; - int ipBound = ip + inLength - 2; - int ipLimit = ip + inLength - 12; - - int op = 0; - - // const flzuint8* htab[HASH_SIZE]; - int[] htab = new int[HASH_SIZE]; - // const flzuint8** hslot; - int hslot; - // flzuint32 hval; - // int OK b/c address starting from 0 - int hval; - // flzuint32 copy; - // int OK b/c address starting from 0 - int copy; - - /* sanity check */ - if (inLength < 4) { - if (inLength != 0) { - // *op++ = length-1; - output.setByte(outOffset + op++, (byte) (inLength - 1)); - ipBound++; - while (ip <= ipBound) { - output.setByte(outOffset + op++, input.getByte(inOffset + ip++)); - } - return inLength + 1; - } - // else - return 0; - } - - /* initializes hash table */ - // for (hslot = htab; hslot < htab + HASH_SIZE; hslot++) - for (hslot = 0; hslot < HASH_SIZE; hslot++) { - //*hslot = ip; - htab[hslot] = ip; - } - - /* we start with literal copy */ - copy = 2; - output.setByte(outOffset + op++, MAX_COPY - 1); - output.setByte(outOffset + op++, input.getByte(inOffset + ip++)); - output.setByte(outOffset + op++, input.getByte(inOffset + ip++)); - - /* main loop */ - while (ip < ipLimit) { - int ref = 0; - - long distance = 0; - - /* minimum match length */ - // flzuint32 len = 3; - // int OK b/c len is 0 and octal based - int len = 3; - - /* comparison starting-point */ - int anchor = ip; - - boolean matchLabel = false; - - /* check for a run */ - if (level == LEVEL_2) { - //if(ip[0] == ip[-1] && FASTLZ_READU16(ip-1)==FASTLZ_READU16(ip+1)) - if (input.getByte(inOffset + ip) == input.getByte(inOffset + ip - 1) && - readU16(input, inOffset + ip - 1) == readU16(input, inOffset + ip + 1)) { - distance = 1; - ip += 3; - ref = anchor + (3 - 1); - - /* - * goto match; - */ - matchLabel = true; - } - } - if (!matchLabel) { - /* find potential match */ - // HASH_FUNCTION(hval,ip); - hval = hashFunction(input, inOffset + ip); - // hslot = htab + hval; - hslot = hval; - // ref = htab[hval]; - ref = htab[hval]; - - /* calculate distance to the match */ - distance = anchor - ref; - - /* update hash table */ - //*hslot = anchor; - htab[hslot] = anchor; - - /* is this a match? check the first 3 bytes */ - if (distance == 0 - || (level == LEVEL_1 ? distance >= MAX_DISTANCE : distance >= MAX_FARDISTANCE) - || input.getByte(inOffset + ref++) != input.getByte(inOffset + ip++) - || input.getByte(inOffset + ref++) != input.getByte(inOffset + ip++) - || input.getByte(inOffset + ref++) != input.getByte(inOffset + ip++)) { - /* - * goto literal; - */ - output.setByte(outOffset + op++, input.getByte(inOffset + anchor++)); - ip = anchor; - copy++; - if (copy == MAX_COPY) { - copy = 0; - output.setByte(outOffset + op++, MAX_COPY - 1); - } - continue; - } - - if (level == LEVEL_2) { - /* far, needs at least 5-byte match */ - if (distance >= MAX_DISTANCE) { - if (input.getByte(inOffset + ip++) != input.getByte(inOffset + ref++) - || input.getByte(inOffset + ip++) != input.getByte(inOffset + ref++)) { - /* - * goto literal; - */ - output.setByte(outOffset + op++, input.getByte(inOffset + anchor++)); - ip = anchor; - copy++; - if (copy == MAX_COPY) { - copy = 0; - output.setByte(outOffset + op++, MAX_COPY - 1); - } - continue; - } - len += 2; - } - } - } // end if(!matchLabel) - /* - * match: - */ - /* last matched byte */ - ip = anchor + len; - - /* distance is biased */ - distance--; - - if (distance == 0) { - /* zero distance means a run */ - //flzuint8 x = ip[-1]; - byte x = input.getByte(inOffset + ip - 1); - while (ip < ipBound) { - if (input.getByte(inOffset + ref++) != x) { - break; - } else { - ip++; - } - } - } else { - /* safe because the outer check against ip limit */ - boolean missMatch = false; - for (int i = 0; i < 8; i++) { - if (input.getByte(inOffset + ref++) != input.getByte(inOffset + ip++)) { - missMatch = true; - break; - } - } - if (!missMatch) { - while (ip < ipBound) { - if (input.getByte(inOffset + ref++) != input.getByte(inOffset + ip++)) { - break; - } - } - } - } - - /* if we have copied something, adjust the copy count */ - if (copy != 0) { - /* copy is biased, '0' means 1 byte copy */ - // *(op-copy-1) = copy-1; - output.setByte(outOffset + op - copy - 1, (byte) (copy - 1)); - } else { - /* back, to overwrite the copy count */ - op--; - } - - /* reset literal counter */ - copy = 0; - - /* length is biased, '1' means a match of 3 bytes */ - ip -= 3; - len = ip - anchor; - - /* encode the match */ - if (level == LEVEL_2) { - if (distance < MAX_DISTANCE) { - if (len < 7) { - output.setByte(outOffset + op++, (byte) ((len << 5) + (distance >>> 8))); - output.setByte(outOffset + op++, (byte) (distance & 255)); - } else { - output.setByte(outOffset + op++, (byte) ((7 << 5) + (distance >>> 8))); - for (len -= 7; len >= 255; len -= 255) { - output.setByte(outOffset + op++, (byte) 255); - } - output.setByte(outOffset + op++, (byte) len); - output.setByte(outOffset + op++, (byte) (distance & 255)); - } - } else { - /* far away, but not yet in the another galaxy... */ - if (len < 7) { - distance -= MAX_DISTANCE; - output.setByte(outOffset + op++, (byte) ((len << 5) + 31)); - output.setByte(outOffset + op++, (byte) 255); - output.setByte(outOffset + op++, (byte) (distance >>> 8)); - output.setByte(outOffset + op++, (byte) (distance & 255)); - } else { - distance -= MAX_DISTANCE; - output.setByte(outOffset + op++, (byte) ((7 << 5) + 31)); - for (len -= 7; len >= 255; len -= 255) { - output.setByte(outOffset + op++, (byte) 255); - } - output.setByte(outOffset + op++, (byte) len); - output.setByte(outOffset + op++, (byte) 255); - output.setByte(outOffset + op++, (byte) (distance >>> 8)); - output.setByte(outOffset + op++, (byte) (distance & 255)); - } - } - } else { - if (len > MAX_LEN - 2) { - while (len > MAX_LEN - 2) { - output.setByte(outOffset + op++, (byte) ((7 << 5) + (distance >>> 8))); - output.setByte(outOffset + op++, (byte) (MAX_LEN - 2 - 7 - 2)); - output.setByte(outOffset + op++, (byte) (distance & 255)); - len -= MAX_LEN - 2; - } - } - - if (len < 7) { - output.setByte(outOffset + op++, (byte) ((len << 5) + (distance >>> 8))); - output.setByte(outOffset + op++, (byte) (distance & 255)); - } else { - output.setByte(outOffset + op++, (byte) ((7 << 5) + (distance >>> 8))); - output.setByte(outOffset + op++, (byte) (len - 7)); - output.setByte(outOffset + op++, (byte) (distance & 255)); - } - } - - /* update the hash at match boundary */ - //HASH_FUNCTION(hval,ip); - hval = hashFunction(input, inOffset + ip); - htab[hval] = ip++; - - //HASH_FUNCTION(hval,ip); - hval = hashFunction(input, inOffset + ip); - htab[hval] = ip++; - - /* assuming literal copy */ - output.setByte(outOffset + op++, MAX_COPY - 1); - - continue; - - // Moved to be inline, with a 'continue' - /* - * literal: - * - output[outOffset + op++] = input[inOffset + anchor++]; - ip = anchor; - copy++; - if(copy == MAX_COPY){ - copy = 0; - output[outOffset + op++] = MAX_COPY-1; - } - */ - } - - /* left-over as literal copy */ - ipBound++; - while (ip <= ipBound) { - output.setByte(outOffset + op++, input.getByte(inOffset + ip++)); - copy++; - if (copy == MAX_COPY) { - copy = 0; - output.setByte(outOffset + op++, MAX_COPY - 1); - } - } - - /* if we have copied something, adjust the copy length */ - if (copy != 0) { - //*(op-copy-1) = copy-1; - output.setByte(outOffset + op - copy - 1, (byte) (copy - 1)); - } else { - op--; - } - - if (level == LEVEL_2) { - /* marker for fastlz2 */ - output.setByte(outOffset, output.getByte(outOffset) | 1 << 5); - } - - return op; - } - - /** - * Decompress a block of compressed data and returns the size of the decompressed block. - * If error occurs, e.g. the compressed data is corrupted or the output buffer is not large - * enough, then 0 (zero) will be returned instead. - * - * Decompression is memory safe and guaranteed not to write the output buffer - * more than what is specified in outLength. - */ - static int decompress(final ByteBuf input, final int inOffset, final int inLength, - final ByteBuf output, final int outOffset, final int outLength) { - //int level = ((*(const flzuint8*)input) >> 5) + 1; - final int level = (input.getByte(inOffset) >> 5) + 1; - if (level != LEVEL_1 && level != LEVEL_2) { - throw new DecompressionException(String.format( - "invalid level: %d (expected: %d or %d)", level, LEVEL_1, LEVEL_2 - )); - } - - // const flzuint8* ip = (const flzuint8*) input; - int ip = 0; - // flzuint8* op = (flzuint8*) output; - int op = 0; - // flzuint32 ctrl = (*ip++) & 31; - long ctrl = input.getByte(inOffset + ip++) & 31; - - int loop = 1; - do { - // const flzuint8* ref = op; - int ref = op; - // flzuint32 len = ctrl >> 5; - long len = ctrl >> 5; - // flzuint32 ofs = (ctrl & 31) << 8; - long ofs = (ctrl & 31) << 8; - - if (ctrl >= 32) { - len--; - // ref -= ofs; - ref -= ofs; - - int code; - if (len == 6) { - if (level == LEVEL_1) { - // len += *ip++; - len += input.getUnsignedByte(inOffset + ip++); - } else { - do { - code = input.getUnsignedByte(inOffset + ip++); - len += code; - } while (code == 255); - } - } - if (level == LEVEL_1) { - // ref -= *ip++; - ref -= input.getUnsignedByte(inOffset + ip++); - } else { - code = input.getUnsignedByte(inOffset + ip++); - ref -= code; - - /* match from 16-bit distance */ - // if(FASTLZ_UNEXPECT_CONDITIONAL(code==255)) - // if(FASTLZ_EXPECT_CONDITIONAL(ofs==(31 << 8))) - if (code == 255 && ofs == 31 << 8) { - ofs = input.getUnsignedByte(inOffset + ip++) << 8; - ofs += input.getUnsignedByte(inOffset + ip++); - - ref = (int) (op - ofs - MAX_DISTANCE); - } - } - - // if the output index + length of block(?) + 3(?) is over the output limit? - if (op + len + 3 > outLength) { - return 0; - } - - // if (FASTLZ_UNEXPECT_CONDITIONAL(ref-1 < (flzuint8 *)output)) - // if the address space of ref-1 is < the address of output? - // if we are still at the beginning of the output address? - if (ref - 1 < 0) { - return 0; - } - - if (ip < inLength) { - ctrl = input.getUnsignedByte(inOffset + ip++); - } else { - loop = 0; - } - - if (ref == op) { - /* optimize copy for a run */ - // flzuint8 b = ref[-1]; - byte b = output.getByte(outOffset + ref - 1); - output.setByte(outOffset + op++, b); - output.setByte(outOffset + op++, b); - output.setByte(outOffset + op++, b); - while (len != 0) { - output.setByte(outOffset + op++, b); - --len; - } - } else { - /* copy from reference */ - ref--; - - // *op++ = *ref++; - output.setByte(outOffset + op++, output.getByte(outOffset + ref++)); - output.setByte(outOffset + op++, output.getByte(outOffset + ref++)); - output.setByte(outOffset + op++, output.getByte(outOffset + ref++)); - - while (len != 0) { - output.setByte(outOffset + op++, output.getByte(outOffset + ref++)); - --len; - } - } - } else { - ctrl++; - - if (op + ctrl > outLength) { - return 0; - } - if (ip + ctrl > inLength) { - return 0; - } - - //*op++ = *ip++; - output.setByte(outOffset + op++, input.getByte(inOffset + ip++)); - - for (--ctrl; ctrl != 0; ctrl--) { - // *op++ = *ip++; - output.setByte(outOffset + op++, input.getByte(inOffset + ip++)); - } - - loop = ip < inLength ? 1 : 0; - if (loop != 0) { - // ctrl = *ip++; - ctrl = input.getUnsignedByte(inOffset + ip++); - } - } - - // while(FASTLZ_EXPECT_CONDITIONAL(loop)); - } while (loop != 0); - - // return op - (flzuint8*)output; - return op; - } - - private static int hashFunction(ByteBuf p, int offset) { - int v = readU16(p, offset); - v ^= readU16(p, offset + 1) ^ v >> 16 - HASH_LOG; - v &= HASH_MASK; - return v; - } - - private static int readU16(ByteBuf data, int offset) { - if (offset + 1 >= data.readableBytes()) { - return data.getUnsignedByte(offset); - } - return data.getUnsignedByte(offset + 1) << 8 | data.getUnsignedByte(offset); - } - - private FastLz() { } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/FastLzFrameDecoder.java b/codec/src/main/java/io/netty/handler/codec/compression/FastLzFrameDecoder.java deleted file mode 100644 index 454c7a20f0..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/FastLzFrameDecoder.java +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.ByteToMessageDecoder; - -import java.util.zip.Adler32; -import java.util.zip.Checksum; - -import static io.netty.handler.codec.compression.FastLz.*; - -/** - * Uncompresses a {@link ByteBuf} encoded by {@link FastLzFrameEncoder} using the FastLZ algorithm. - * - * See FastLZ format. - */ -public class FastLzFrameDecoder extends ByteToMessageDecoder { - /** - * Current state of decompression. - */ - private enum State { - INIT_BLOCK, - INIT_BLOCK_PARAMS, - DECOMPRESS_DATA, - CORRUPTED - } - - private State currentState = State.INIT_BLOCK; - - /** - * Underlying checksum calculator in use. - */ - private final ByteBufChecksum checksum; - - /** - * Length of current received chunk of data. - */ - private int chunkLength; - - /** - * Original of current received chunk of data. - * It is equal to {@link #chunkLength} for non compressed chunks. - */ - private int originalLength; - - /** - * Indicates is this chunk compressed or not. - */ - private boolean isCompressed; - - /** - * Indicates is this chunk has checksum or not. - */ - private boolean hasChecksum; - - /** - * Checksum value of current received chunk of data which has checksum. - */ - private int currentChecksum; - - /** - * Creates the fastest FastLZ decoder without checksum calculation. - */ - public FastLzFrameDecoder() { - this(false); - } - - /** - * Creates a FastLZ decoder with calculation of checksums as specified. - * - * @param validateChecksums - * If true, the checksum field will be validated against the actual - * uncompressed data, and if the checksums do not match, a suitable - * {@link DecompressionException} will be thrown. - * Note, that in this case decoder will use {@link java.util.zip.Adler32} - * as a default checksum calculator. - */ - public FastLzFrameDecoder(boolean validateChecksums) { - this(validateChecksums ? new Adler32() : null); - } - - /** - * Creates a FastLZ decoder with specified checksum calculator. - * - * @param checksum - * the {@link Checksum} instance to use to check data for integrity. - * You may set {@code null} if you do not want to validate checksum of each block. - */ - public FastLzFrameDecoder(Checksum checksum) { - this.checksum = checksum == null ? null : ByteBufChecksum.wrapChecksum(checksum); - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - try { - switch (currentState) { - case INIT_BLOCK: - if (in.readableBytes() < 4) { - break; - } - - final int magic = in.readUnsignedMedium(); - if (magic != MAGIC_NUMBER) { - throw new DecompressionException("unexpected block identifier"); - } - - final byte options = in.readByte(); - isCompressed = (options & 0x01) == BLOCK_TYPE_COMPRESSED; - hasChecksum = (options & 0x10) == BLOCK_WITH_CHECKSUM; - - currentState = State.INIT_BLOCK_PARAMS; - // fall through - case INIT_BLOCK_PARAMS: - if (in.readableBytes() < 2 + (isCompressed ? 2 : 0) + (hasChecksum ? 4 : 0)) { - break; - } - currentChecksum = hasChecksum ? in.readInt() : 0; - chunkLength = in.readUnsignedShort(); - originalLength = isCompressed ? in.readUnsignedShort() : chunkLength; - - currentState = State.DECOMPRESS_DATA; - // fall through - case DECOMPRESS_DATA: - final int chunkLength = this.chunkLength; - if (in.readableBytes() < chunkLength) { - break; - } - - final int idx = in.readerIndex(); - final int originalLength = this.originalLength; - - ByteBuf output = null; - - try { - if (isCompressed) { - - output = ctx.alloc().buffer(originalLength); - int outputOffset = output.writerIndex(); - final int decompressedBytes = decompress(in, idx, chunkLength, - output, outputOffset, originalLength); - if (originalLength != decompressedBytes) { - throw new DecompressionException(String.format( - "stream corrupted: originalLength(%d) and actual length(%d) mismatch", - originalLength, decompressedBytes)); - } - output.writerIndex(output.writerIndex() + decompressedBytes); - } else { - output = in.retainedSlice(idx, chunkLength); - } - - final ByteBufChecksum checksum = this.checksum; - if (hasChecksum && checksum != null) { - checksum.reset(); - checksum.update(output, output.readerIndex(), output.readableBytes()); - final int checksumResult = (int) checksum.getValue(); - if (checksumResult != currentChecksum) { - throw new DecompressionException(String.format( - "stream corrupted: mismatching checksum: %d (expected: %d)", - checksumResult, currentChecksum)); - } - } - - if (output.readableBytes() > 0) { - ctx.fireChannelRead(output); - } else { - output.release(); - } - output = null; - in.skipBytes(chunkLength); - - currentState = State.INIT_BLOCK; - } finally { - if (output != null) { - output.release(); - } - } - break; - case CORRUPTED: - in.skipBytes(in.readableBytes()); - break; - default: - throw new IllegalStateException(); - } - } catch (Exception e) { - currentState = State.CORRUPTED; - throw e; - } - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/FastLzFrameEncoder.java b/codec/src/main/java/io/netty/handler/codec/compression/FastLzFrameEncoder.java deleted file mode 100644 index 5543fad329..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/FastLzFrameEncoder.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToByteEncoder; - -import java.util.zip.Adler32; -import java.util.zip.Checksum; - -import static io.netty.handler.codec.compression.FastLz.*; - -/** - * Compresses a {@link ByteBuf} using the FastLZ algorithm. - * - * See FastLZ format. - */ -public class FastLzFrameEncoder extends MessageToByteEncoder { - /** - * Compression level. - */ - private final int level; - - /** - * Underlying checksum calculator in use. - */ - private final ByteBufChecksum checksum; - - /** - * Creates a FastLZ encoder without checksum calculator and with auto detection of compression level. - */ - public FastLzFrameEncoder() { - this(LEVEL_AUTO, null); - } - - /** - * Creates a FastLZ encoder with specified compression level and without checksum calculator. - * - * @param level supports only these values: - * 0 - Encoder will choose level automatically depending on the length of the input buffer. - * 1 - Level 1 is the fastest compression and generally useful for short data. - * 2 - Level 2 is slightly slower but it gives better compression ratio. - */ - public FastLzFrameEncoder(int level) { - this(level, null); - } - - /** - * Creates a FastLZ encoder with auto detection of compression - * level and calculation of checksums as specified. - * - * @param validateChecksums - * If true, the checksum of each block will be calculated and this value - * will be added to the header of block. - * By default {@link FastLzFrameEncoder} uses {@link java.util.zip.Adler32} - * for checksum calculation. - */ - public FastLzFrameEncoder(boolean validateChecksums) { - this(LEVEL_AUTO, validateChecksums ? new Adler32() : null); - } - - /** - * Creates a FastLZ encoder with specified compression level and checksum calculator. - * - * @param level supports only these values: - * 0 - Encoder will choose level automatically depending on the length of the input buffer. - * 1 - Level 1 is the fastest compression and generally useful for short data. - * 2 - Level 2 is slightly slower but it gives better compression ratio. - * @param checksum - * the {@link Checksum} instance to use to check data for integrity. - * You may set {@code null} if you don't want to validate checksum of each block. - */ - public FastLzFrameEncoder(int level, Checksum checksum) { - if (level != LEVEL_AUTO && level != LEVEL_1 && level != LEVEL_2) { - throw new IllegalArgumentException(String.format( - "level: %d (expected: %d or %d or %d)", level, LEVEL_AUTO, LEVEL_1, LEVEL_2)); - } - this.level = level; - this.checksum = checksum == null ? null : ByteBufChecksum.wrapChecksum(checksum); - } - - @Override - protected void encode(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) throws Exception { - final ByteBufChecksum checksum = this.checksum; - - for (;;) { - if (!in.isReadable()) { - return; - } - final int idx = in.readerIndex(); - final int length = Math.min(in.readableBytes(), MAX_CHUNK_LENGTH); - - final int outputIdx = out.writerIndex(); - out.setMedium(outputIdx, MAGIC_NUMBER); - int outputOffset = outputIdx + CHECKSUM_OFFSET + (checksum != null ? 4 : 0); - - final byte blockType; - final int chunkLength; - if (length < MIN_LENGTH_TO_COMPRESSION) { - blockType = BLOCK_TYPE_NON_COMPRESSED; - - out.ensureWritable(outputOffset + 2 + length); - final int outputPtr = outputOffset + 2; - - if (checksum != null) { - checksum.reset(); - checksum.update(in, idx, length); - out.setInt(outputIdx + CHECKSUM_OFFSET, (int) checksum.getValue()); - } - out.setBytes(outputPtr, in, idx, length); - chunkLength = length; - } else { - // try to compress - if (checksum != null) { - checksum.reset(); - checksum.update(in, idx, length); - out.setInt(outputIdx + CHECKSUM_OFFSET, (int) checksum.getValue()); - } - - final int maxOutputLength = calculateOutputBufferLength(length); - out.ensureWritable(outputOffset + 4 + maxOutputLength); - final int outputPtr = outputOffset + 4; - final int compressedLength = compress(in, in.readerIndex(), length, out, outputPtr, level); - - if (compressedLength < length) { - blockType = BLOCK_TYPE_COMPRESSED; - chunkLength = compressedLength; - - out.setShort(outputOffset, chunkLength); - outputOffset += 2; - } else { - blockType = BLOCK_TYPE_NON_COMPRESSED; - out.setBytes(outputOffset + 2, in, idx, length); - chunkLength = length; - } - } - out.setShort(outputOffset, length); - - out.setByte(outputIdx + OPTIONS_OFFSET, - blockType | (checksum != null ? BLOCK_WITH_CHECKSUM : BLOCK_WITHOUT_CHECKSUM)); - out.writerIndex(outputOffset + 2 + chunkLength); - in.skipBytes(length); - } - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/GzipOptions.java b/codec/src/main/java/io/netty/handler/codec/compression/GzipOptions.java deleted file mode 100644 index 1591e374f6..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/GzipOptions.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -/** - * {@link GzipOptions} holds {@link #compressionLevel()}, - * {@link #memLevel()} and {@link #windowBits()} for Gzip compression. - * This class is an extension of {@link DeflateOptions} - */ -public final class GzipOptions extends DeflateOptions { - - /** - * @see StandardCompressionOptions#gzip() - */ - static final GzipOptions DEFAULT = new GzipOptions( - 6, 15, 8 - ); - - /** - * @see StandardCompressionOptions#gzip(int, int, int) - */ - GzipOptions(int compressionLevel, int windowBits, int memLevel) { - super(compressionLevel, windowBits, memLevel); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/JdkZlibDecoder.java b/codec/src/main/java/io/netty/handler/codec/compression/JdkZlibDecoder.java deleted file mode 100644 index 5912122cd2..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/JdkZlibDecoder.java +++ /dev/null @@ -1,502 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.channel.ChannelHandlerContext; - -import java.nio.ByteBuffer; -import java.util.zip.CRC32; -import java.util.zip.DataFormatException; -import java.util.zip.Deflater; -import java.util.zip.Inflater; - -/** - * Decompress a {@link ByteBuf} using the inflate algorithm. - */ -public class JdkZlibDecoder extends ZlibDecoder { - private static final int FHCRC = 0x02; - private static final int FEXTRA = 0x04; - private static final int FNAME = 0x08; - private static final int FCOMMENT = 0x10; - private static final int FRESERVED = 0xE0; - - private Inflater inflater; - private final byte[] dictionary; - - // GZIP related - private final ByteBufChecksum crc; - private final boolean decompressConcatenated; - - private enum GzipState { - HEADER_START, - HEADER_END, - FLG_READ, - XLEN_READ, - SKIP_FNAME, - SKIP_COMMENT, - PROCESS_FHCRC, - FOOTER_START, - } - - private GzipState gzipState = GzipState.HEADER_START; - private int flags = -1; - private int xlen = -1; - - private volatile boolean finished; - - private boolean decideZlibOrNone; - - /** - * Creates a new instance with the default wrapper ({@link ZlibWrapper#ZLIB}). - */ - public JdkZlibDecoder() { - this(ZlibWrapper.ZLIB, null, false, 0); - } - - /** - * Creates a new instance with the default wrapper ({@link ZlibWrapper#ZLIB}) - * and the specified maximum buffer allocation. - * - * @param maxAllocation - * Maximum size of the decompression buffer. Must be >= 0. - * If zero, maximum size is decided by the {@link ByteBufAllocator}. - */ - public JdkZlibDecoder(int maxAllocation) { - this(ZlibWrapper.ZLIB, null, false, maxAllocation); - } - - /** - * Creates a new instance with the specified preset dictionary. The wrapper - * is always {@link ZlibWrapper#ZLIB} because it is the only format that - * supports the preset dictionary. - */ - public JdkZlibDecoder(byte[] dictionary) { - this(ZlibWrapper.ZLIB, dictionary, false, 0); - } - - /** - * Creates a new instance with the specified preset dictionary and maximum buffer allocation. - * The wrapper is always {@link ZlibWrapper#ZLIB} because it is the only format that - * supports the preset dictionary. - * - * @param maxAllocation - * Maximum size of the decompression buffer. Must be >= 0. - * If zero, maximum size is decided by the {@link ByteBufAllocator}. - */ - public JdkZlibDecoder(byte[] dictionary, int maxAllocation) { - this(ZlibWrapper.ZLIB, dictionary, false, maxAllocation); - } - - /** - * Creates a new instance with the specified wrapper. - * Be aware that only {@link ZlibWrapper#GZIP}, {@link ZlibWrapper#ZLIB} and {@link ZlibWrapper#NONE} are - * supported atm. - */ - public JdkZlibDecoder(ZlibWrapper wrapper) { - this(wrapper, null, false, 0); - } - - /** - * Creates a new instance with the specified wrapper and maximum buffer allocation. - * Be aware that only {@link ZlibWrapper#GZIP}, {@link ZlibWrapper#ZLIB} and {@link ZlibWrapper#NONE} are - * supported atm. - * - * @param maxAllocation - * Maximum size of the decompression buffer. Must be >= 0. - * If zero, maximum size is decided by the {@link ByteBufAllocator}. - */ - public JdkZlibDecoder(ZlibWrapper wrapper, int maxAllocation) { - this(wrapper, null, false, maxAllocation); - } - - public JdkZlibDecoder(ZlibWrapper wrapper, boolean decompressConcatenated) { - this(wrapper, null, decompressConcatenated, 0); - } - - public JdkZlibDecoder(ZlibWrapper wrapper, boolean decompressConcatenated, int maxAllocation) { - this(wrapper, null, decompressConcatenated, maxAllocation); - } - - public JdkZlibDecoder(boolean decompressConcatenated) { - this(ZlibWrapper.GZIP, null, decompressConcatenated, 0); - } - - public JdkZlibDecoder(boolean decompressConcatenated, int maxAllocation) { - this(ZlibWrapper.GZIP, null, decompressConcatenated, maxAllocation); - } - - private JdkZlibDecoder(ZlibWrapper wrapper, byte[] dictionary, boolean decompressConcatenated, int maxAllocation) { - super(maxAllocation); - - requireNonNull(wrapper, "wrapper"); - - this.decompressConcatenated = decompressConcatenated; - switch (wrapper) { - case GZIP: - inflater = new Inflater(true); - crc = ByteBufChecksum.wrapChecksum(new CRC32()); - break; - case NONE: - inflater = new Inflater(true); - crc = null; - break; - case ZLIB: - inflater = new Inflater(); - crc = null; - break; - case ZLIB_OR_NONE: - // Postpone the decision until decode(...) is called. - decideZlibOrNone = true; - crc = null; - break; - default: - throw new IllegalArgumentException("Only GZIP or ZLIB is supported, but you used " + wrapper); - } - this.dictionary = dictionary; - } - - @Override - public boolean isClosed() { - return finished; - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - if (finished) { - // Skip data received after finished. - in.skipBytes(in.readableBytes()); - return; - } - - int readableBytes = in.readableBytes(); - if (readableBytes == 0) { - return; - } - - if (decideZlibOrNone) { - // First two bytes are needed to decide if it's a ZLIB stream. - if (readableBytes < 2) { - return; - } - - boolean nowrap = !looksLikeZlib(in.getShort(in.readerIndex())); - inflater = new Inflater(nowrap); - decideZlibOrNone = false; - } - - if (crc != null) { - if (gzipState != GzipState.HEADER_END) { - if (gzipState == GzipState.FOOTER_START) { - if (!handleGzipFooter(in)) { - // Either there was not enough data or the input is finished. - return; - } - // If we consumed the footer we will start with the header again. - assert gzipState == GzipState.HEADER_START; - } - if (!readGZIPHeader(in)) { - // There was not enough data readable to read the GZIP header. - return; - } - // Some bytes may have been consumed, and so we must re-set the number of readable bytes. - readableBytes = in.readableBytes(); - if (readableBytes == 0) { - return; - } - } - } - - if (inflater.needsInput()) { - if (in.hasArray()) { - inflater.setInput(in.array(), in.arrayOffset() + in.readerIndex(), readableBytes); - } else { - byte[] array = new byte[readableBytes]; - in.getBytes(in.readerIndex(), array); - inflater.setInput(array); - } - } - - ByteBuf decompressed = prepareDecompressBuffer(ctx, null, inflater.getRemaining() << 1); - try { - boolean readFooter = false; - while (!inflater.needsInput()) { - int writerIndex = decompressed.writerIndex(); - int writable = decompressed.writableBytes(); - int outputLength; - if (decompressed.hasArray()) { - byte[] outArray = decompressed.array(); - int outIndex = decompressed.arrayOffset() + writerIndex; - outputLength = inflater.inflate(outArray, outIndex, writable); - } else if (decompressed.nioBufferCount() == 1) { - ByteBuffer buffer = decompressed.internalNioBuffer(writerIndex, writable); - outputLength = inflater.inflate(buffer); - } else { - throw new IllegalStateException( - "Decompress buffer must have array or exactly 1 NIO buffer: " + decompressed); - } - if (outputLength > 0) { - decompressed.writerIndex(writerIndex + outputLength); - if (crc != null) { - crc.update(decompressed, writerIndex, outputLength); - } - } else if (inflater.needsDictionary()) { - if (dictionary == null) { - throw new DecompressionException( - "decompression failure, unable to set dictionary as non was specified"); - } - inflater.setDictionary(dictionary); - } - - if (inflater.finished()) { - if (crc == null) { - finished = true; // Do not decode anymore. - } else { - readFooter = true; - } - break; - } else { - decompressed = prepareDecompressBuffer(ctx, decompressed, inflater.getRemaining() << 1); - } - } - - in.skipBytes(readableBytes - inflater.getRemaining()); - - if (readFooter) { - gzipState = GzipState.FOOTER_START; - handleGzipFooter(in); - } - } catch (DataFormatException e) { - throw new DecompressionException("decompression failure", e); - } finally { - if (decompressed.isReadable()) { - ctx.fireChannelRead(decompressed); - } else { - decompressed.release(); - } - } - } - - private boolean handleGzipFooter(ByteBuf in) { - if (readGZIPFooter(in)) { - finished = !decompressConcatenated; - - if (!finished) { - inflater.reset(); - crc.reset(); - gzipState = GzipState.HEADER_START; - return true; - } - } - return false; - } - - @Override - protected void decompressionBufferExhausted(ByteBuf buffer) { - finished = true; - } - - @Override - protected void handlerRemoved0(ChannelHandlerContext ctx) throws Exception { - super.handlerRemoved0(ctx); - if (inflater != null) { - inflater.end(); - } - } - - private boolean readGZIPHeader(ByteBuf in) { - switch (gzipState) { - case HEADER_START: - if (in.readableBytes() < 10) { - return false; - } - // read magic numbers - int magic0 = in.readByte(); - int magic1 = in.readByte(); - - if (magic0 != 31) { - throw new DecompressionException("Input is not in the GZIP format"); - } - crc.update(magic0); - crc.update(magic1); - - int method = in.readUnsignedByte(); - if (method != Deflater.DEFLATED) { - throw new DecompressionException("Unsupported compression method " - + method + " in the GZIP header"); - } - crc.update(method); - - flags = in.readUnsignedByte(); - crc.update(flags); - - if ((flags & FRESERVED) != 0) { - throw new DecompressionException( - "Reserved flags are set in the GZIP header"); - } - - // mtime (int) - crc.update(in, in.readerIndex(), 4); - in.skipBytes(4); - - crc.update(in.readUnsignedByte()); // extra flags - crc.update(in.readUnsignedByte()); // operating system - - gzipState = GzipState.FLG_READ; - // fall through - case FLG_READ: - if ((flags & FEXTRA) != 0) { - if (in.readableBytes() < 2) { - return false; - } - int xlen1 = in.readUnsignedByte(); - int xlen2 = in.readUnsignedByte(); - crc.update(xlen1); - crc.update(xlen2); - - xlen |= xlen1 << 8 | xlen2; - } - gzipState = GzipState.XLEN_READ; - // fall through - case XLEN_READ: - if (xlen != -1) { - if (in.readableBytes() < xlen) { - return false; - } - crc.update(in, in.readerIndex(), xlen); - in.skipBytes(xlen); - } - gzipState = GzipState.SKIP_FNAME; - // fall through - case SKIP_FNAME: - if (!skipIfNeeded(in, FNAME)) { - return false; - } - gzipState = GzipState.SKIP_COMMENT; - // fall through - case SKIP_COMMENT: - if (!skipIfNeeded(in, FCOMMENT)) { - return false; - } - gzipState = GzipState.PROCESS_FHCRC; - // fall through - case PROCESS_FHCRC: - if ((flags & FHCRC) != 0) { - if (!verifyCrc(in)) { - return false; - } - } - crc.reset(); - gzipState = GzipState.HEADER_END; - // fall through - case HEADER_END: - return true; - default: - throw new IllegalStateException(); - } - } - - /** - * Skip bytes in the input if needed until we find the end marker {@code 0x00}. - * @param in the input - * @param flagMask the mask that should be present in the {@code flags} when we need to skip bytes. - * @return {@code true} if the operation is complete and we can move to the next state, {@code false} if we need - * the retry again once we have more readable bytes. - */ - private boolean skipIfNeeded(ByteBuf in, int flagMask) { - if ((flags & flagMask) != 0) { - for (;;) { - if (!in.isReadable()) { - // We didnt find the end yet, need to retry again once more data is readable - return false; - } - int b = in.readUnsignedByte(); - crc.update(b); - if (b == 0x00) { - break; - } - } - } - // Skip is handled, we can move to the next processing state. - return true; - } - - /** - * Read the GZIP footer. - * - * @param in the input. - * @return {@code true} if the footer could be read, {@code false} if the read could not be performed as - * the input {@link ByteBuf} doesn't have enough readable bytes (8 bytes). - */ - private boolean readGZIPFooter(ByteBuf in) { - if (in.readableBytes() < 8) { - return false; - } - - boolean enoughData = verifyCrc(in); - assert enoughData; - - // read ISIZE and verify - int dataLength = 0; - for (int i = 0; i < 4; ++i) { - dataLength |= in.readUnsignedByte() << i * 8; - } - int readLength = inflater.getTotalOut(); - if (dataLength != readLength) { - throw new DecompressionException( - "Number of bytes mismatch. Expected: " + dataLength + ", Got: " + readLength); - } - return true; - } - - /** - * Verifies CRC. - * - * @param in the input. - * @return {@code true} if verification could be performed, {@code false} if verification could not be performed as - * the input {@link ByteBuf} doesn't have enough readable bytes (4 bytes). - */ - private boolean verifyCrc(ByteBuf in) { - if (in.readableBytes() < 4) { - return false; - } - long crcValue = 0; - for (int i = 0; i < 4; ++i) { - crcValue |= (long) in.readUnsignedByte() << i * 8; - } - long readCrc = crc.getValue(); - if (crcValue != readCrc) { - throw new DecompressionException( - "CRC value mismatch. Expected: " + crcValue + ", Got: " + readCrc); - } - return true; - } - - /* - * Returns true if the cmf_flg parameter (think: first two bytes of a zlib stream) - * indicates that this is a zlib stream. - *

- * You can lookup the details in the ZLIB RFC: - * RFC 1950. - */ - private static boolean looksLikeZlib(short cmf_flg) { - return (cmf_flg & 0x7800) == 0x7800 && - cmf_flg % 31 == 0; - } -} 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 deleted file mode 100644 index eee877b127..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/JdkZlibEncoder.java +++ /dev/null @@ -1,339 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; - -import java.nio.ByteBuffer; -import java.util.concurrent.TimeUnit; -import java.util.zip.CRC32; -import java.util.zip.Deflater; - -import static java.util.Objects.requireNonNull; - -/** - * Compresses a {@link ByteBuf} using the deflate algorithm. - */ -public class JdkZlibEncoder extends ZlibEncoder { - - private final ZlibWrapper wrapper; - private final Deflater deflater; - private volatile boolean finished; - private volatile ChannelHandlerContext ctx; - - /* - * GZIP support - */ - private final CRC32 crc = new CRC32(); - private static final byte[] gzipHeader = {0x1f, (byte) 0x8b, Deflater.DEFLATED, 0, 0, 0, 0, 0, 0, 0}; - private boolean writeHeader = true; - - /** - * Creates a new zlib encoder with the default compression level ({@code 6}) - * and the default wrapper ({@link ZlibWrapper#ZLIB}). - * - * @throws CompressionException if failed to initialize zlib - */ - public JdkZlibEncoder() { - this(6); - } - - /** - * Creates a new zlib encoder with the specified {@code compressionLevel} - * and the default wrapper ({@link ZlibWrapper#ZLIB}). - * - * @param compressionLevel - * {@code 1} yields the fastest compression and {@code 9} yields the - * best compression. {@code 0} means no compression. The default - * compression level is {@code 6}. - * - * @throws CompressionException if failed to initialize zlib - */ - public JdkZlibEncoder(int compressionLevel) { - this(ZlibWrapper.ZLIB, compressionLevel); - } - - /** - * Creates a new zlib encoder with the default compression level ({@code 6}) - * and the specified wrapper. - * - * @throws CompressionException if failed to initialize zlib - */ - public JdkZlibEncoder(ZlibWrapper wrapper) { - this(wrapper, 6); - } - - public JdkZlibEncoder(ZlibWrapper wrapper, int compressionLevel) { - this(wrapper, compressionLevel, false); - } - - /** - * Creates a new zlib encoder with the specified {@code compressionLevel} - * and the specified wrapper. - * - * @param compressionLevel - * {@code 1} yields the fastest compression and {@code 9} yields the - * best compression. {@code 0} means no compression. The default - * compression level is {@code 6}. - * @param preferDirectBuffers {@code true} if a direct {@link ByteBuf} should be tried to be used as target for - * decompression, or {@code false} if heap allocated {@link ByteBuf}s should be used. - * - * @throws CompressionException if failed to initialize zlib - */ - public JdkZlibEncoder(ZlibWrapper wrapper, int compressionLevel, boolean preferDirectBuffers) { - if (compressionLevel < 0 || compressionLevel > 9) { - throw new IllegalArgumentException( - "compressionLevel: " + compressionLevel + " (expected: 0-9)"); - } - requireNonNull(wrapper, "wrapper"); - if (wrapper == ZlibWrapper.ZLIB_OR_NONE) { - throw new IllegalArgumentException( - "wrapper '" + ZlibWrapper.ZLIB_OR_NONE + "' is not " + - "allowed for compression."); - } - - this.wrapper = wrapper; - deflater = new Deflater(compressionLevel, wrapper != ZlibWrapper.ZLIB); - } - - /** - * Creates a new zlib encoder with the default compression level ({@code 6}) - * and the specified preset dictionary. The wrapper is always - * {@link ZlibWrapper#ZLIB} because it is the only format that supports - * the preset dictionary. - * - * @param dictionary the preset dictionary - * - * @throws CompressionException if failed to initialize zlib - */ - public JdkZlibEncoder(byte[] dictionary) { - this(6, dictionary); - } - - /** - * Creates a new zlib encoder with the specified {@code compressionLevel} - * and the specified preset dictionary. The wrapper is always - * {@link ZlibWrapper#ZLIB} because it is the only format that supports - * the preset dictionary. - * - * @param compressionLevel - * {@code 1} yields the fastest compression and {@code 9} yields the - * best compression. {@code 0} means no compression. The default - * compression level is {@code 6}. - * @param dictionary the preset dictionary - * - * @throws CompressionException if failed to initialize zlib - */ - public JdkZlibEncoder(int compressionLevel, byte[] dictionary) { - if (compressionLevel < 0 || compressionLevel > 9) { - throw new IllegalArgumentException( - "compressionLevel: " + compressionLevel + " (expected: 0-9)"); - } - requireNonNull(dictionary, "dictionary"); - - wrapper = ZlibWrapper.ZLIB; - deflater = new Deflater(compressionLevel); - deflater.setDictionary(dictionary); - } - - @Override - public Future close() { - ChannelHandlerContext ctx = ctx(); - EventExecutor executor = ctx.executor(); - if (executor.inEventLoop()) { - return finishEncode(ctx); - } else { - Promise p = ctx.newPromise(); - executor.execute(() -> { - Future f = finishEncode(ctx()); - f.cascadeTo(p); - }); - return p.asFuture(); - } - } - - private ChannelHandlerContext ctx() { - ChannelHandlerContext ctx = this.ctx; - if (ctx == null) { - throw new IllegalStateException("not added to a pipeline"); - } - return ctx; - } - - @Override - public boolean isClosed() { - return finished; - } - - @Override - protected void encode(ChannelHandlerContext ctx, ByteBuf uncompressed, ByteBuf out) throws Exception { - if (finished) { - out.writeBytes(uncompressed); - return; - } - - int len = uncompressed.readableBytes(); - if (len == 0) { - return; - } - - int offset; - byte[] inAry; - if (uncompressed.hasArray()) { - // if it is backed by an array we not need to to do a copy at all - inAry = uncompressed.array(); - offset = uncompressed.arrayOffset() + uncompressed.readerIndex(); - // skip all bytes as we will consume all of them - uncompressed.skipBytes(len); - } else { - inAry = new byte[len]; - uncompressed.readBytes(inAry); - offset = 0; - } - - if (writeHeader) { - writeHeader = false; - if (wrapper == ZlibWrapper.GZIP) { - out.writeBytes(gzipHeader); - } - } - - if (wrapper == ZlibWrapper.GZIP) { - crc.update(inAry, offset, len); - } - - deflater.setInput(inAry, offset, len); - 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()); - } - } - } - } - - @Override - protected final ByteBuf allocateBuffer(ChannelHandlerContext ctx, ByteBuf msg, - boolean preferDirect) throws Exception { - int sizeEstimate = (int) Math.ceil(msg.readableBytes() * 1.001) + 12; - if (writeHeader) { - switch (wrapper) { - case GZIP: - sizeEstimate += gzipHeader.length; - break; - case ZLIB: - sizeEstimate += 2; // first two magic bytes - break; - default: - // no op - } - } - return ctx.alloc().buffer(sizeEstimate); - } - - @Override - public Future close(final ChannelHandlerContext ctx) { - Future f = finishEncode(ctx); - if (f.isDone()) { - return ctx.close(); - } - Promise promise = ctx.newPromise(); - f.addListener(f1 -> ctx.close().cascadeTo(promise)); - // Ensure the channel is closed even if the write operation completes in time. - ctx.executor().schedule(() -> ctx.close().cascadeTo(promise), - 10, TimeUnit.SECONDS); // FIXME: Magic number - return promise.asFuture(); - } - - private Future finishEncode(final ChannelHandlerContext ctx) { - if (finished) { - return ctx.newSucceededFuture(); - } - - finished = true; - ByteBuf footer = ctx.alloc().heapBuffer(); - if (writeHeader && wrapper == ZlibWrapper.GZIP) { - // Write the GZIP header first if not written yet. (i.e. user wrote nothing.) - writeHeader = false; - footer.writeBytes(gzipHeader); - } - - deflater.finish(); - - while (!deflater.finished()) { - deflate(footer); - if (!footer.isWritable()) { - // no more space so write it to the channel and continue - ctx.write(footer); - footer = ctx.alloc().heapBuffer(); - } - } - if (wrapper == ZlibWrapper.GZIP) { - int crcValue = (int) crc.getValue(); - int uncBytes = deflater.getTotalIn(); - footer.writeByte(crcValue); - footer.writeByte(crcValue >>> 8); - footer.writeByte(crcValue >>> 16); - footer.writeByte(crcValue >>> 24); - footer.writeByte(uncBytes); - footer.writeByte(uncBytes >>> 8); - footer.writeByte(uncBytes >>> 16); - footer.writeByte(uncBytes >>> 24); - } - deflater.end(); - return ctx.writeAndFlush(footer); - } - - private void deflate(ByteBuf out) { - if (out.hasArray()) { - int numBytes; - do { - int writerIndex = out.writerIndex(); - numBytes = deflater.deflate( - out.array(), out.arrayOffset() + writerIndex, out.writableBytes(), Deflater.SYNC_FLUSH); - out.writerIndex(writerIndex + numBytes); - } while (numBytes > 0); - } else if (out.nioBufferCount() == 1) { - // Use internalNioBuffer because nioBuffer is allowed to copy, - // which is fine for reading but not for writing. - int numBytes; - do { - int writerIndex = out.writerIndex(); - ByteBuffer buffer = out.internalNioBuffer(writerIndex, out.writableBytes()); - numBytes = deflater.deflate(buffer, Deflater.SYNC_FLUSH); - out.writerIndex(writerIndex + numBytes); - } while (numBytes > 0); - } else { - throw new IllegalArgumentException( - "Don't know how to deflate buffer without array or NIO buffer count of 1: " + out); - } - } - - @Override - public void handlerAdded(ChannelHandlerContext ctx) throws Exception { - this.ctx = ctx; - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/Lz4Constants.java b/codec/src/main/java/io/netty/handler/codec/compression/Lz4Constants.java deleted file mode 100644 index 3e33b95445..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/Lz4Constants.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -final class Lz4Constants { - /** - * Magic number of LZ4 block. - */ - static final long MAGIC_NUMBER = (long) 'L' << 56 | - (long) 'Z' << 48 | - (long) '4' << 40 | - (long) 'B' << 32 | - 'l' << 24 | - 'o' << 16 | - 'c' << 8 | - 'k'; - - /** - * Full length of LZ4 block header. - */ - static final int HEADER_LENGTH = 8 + // magic number - 1 + // token - 4 + // compressed length - 4 + // decompressed length - 4; // checksum - - /** - * Offsets of header's parts. - */ - static final int TOKEN_OFFSET = 8; - static final int COMPRESSED_LENGTH_OFFSET = TOKEN_OFFSET + 1; - static final int DECOMPRESSED_LENGTH_OFFSET = COMPRESSED_LENGTH_OFFSET + 4; - static final int CHECKSUM_OFFSET = DECOMPRESSED_LENGTH_OFFSET + 4; - - /** - * Base value for compression level. - */ - static final int COMPRESSION_LEVEL_BASE = 10; - - /** - * LZ4 block sizes. - */ - static final int MIN_BLOCK_SIZE = 64; - static final int MAX_BLOCK_SIZE = 1 << COMPRESSION_LEVEL_BASE + 0x0F; // 32 M - static final int DEFAULT_BLOCK_SIZE = 1 << 16; // 64 KB - - /** - * LZ4 block types. - */ - static final int BLOCK_TYPE_NON_COMPRESSED = 0x10; - static final int BLOCK_TYPE_COMPRESSED = 0x20; - - /** - * Default seed value for xxhash. - */ - static final int DEFAULT_SEED = 0x9747b28c; - - private Lz4Constants() { } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/Lz4FrameDecoder.java b/codec/src/main/java/io/netty/handler/codec/compression/Lz4FrameDecoder.java deleted file mode 100644 index 74be148994..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/Lz4FrameDecoder.java +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.ByteToMessageDecoder; -import net.jpountz.lz4.LZ4Exception; -import net.jpountz.lz4.LZ4Factory; -import net.jpountz.lz4.LZ4FastDecompressor; - -import java.util.zip.Checksum; - -import static io.netty.handler.codec.compression.Lz4Constants.*; -import static java.util.Objects.requireNonNull; - -/** - * Uncompresses a {@link ByteBuf} encoded with the LZ4 format. - * - * See original LZ4 Github project - * and LZ4 block format - * for full description. - * - * Since the original LZ4 block format does not contains size of compressed block and size of original data - * this encoder uses format like LZ4 Java library - * written by Adrien Grand and approved by Yann Collet (author of original LZ4 library). - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * * Magic * Token * Compressed * Decompressed * Checksum * + * LZ4 compressed * - * * * * length * length * * * block * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - */ -public class Lz4FrameDecoder extends ByteToMessageDecoder { - /** - * Current state of stream. - */ - private enum State { - INIT_BLOCK, - DECOMPRESS_DATA, - FINISHED, - CORRUPTED - } - - private State currentState = State.INIT_BLOCK; - - /** - * Underlying decompressor in use. - */ - private LZ4FastDecompressor decompressor; - - /** - * Underlying checksum calculator in use. - */ - private ByteBufChecksum checksum; - - /** - * Type of current block. - */ - private int blockType; - - /** - * Compressed length of current incoming block. - */ - private int compressedLength; - - /** - * Decompressed length of current incoming block. - */ - private int decompressedLength; - - /** - * Checksum value of current incoming block. - */ - private int currentChecksum; - - /** - * Creates the fastest LZ4 decoder. - * - * Note that by default, validation of the checksum header in each chunk is - * DISABLED for performance improvements. If performance is less of an issue, - * or if you would prefer the safety that checksum validation brings, please - * use the {@link #Lz4FrameDecoder(boolean)} constructor with the argument - * set to {@code true}. - */ - public Lz4FrameDecoder() { - this(false); - } - - /** - * Creates a LZ4 decoder with fastest decoder instance available on your machine. - * - * @param validateChecksums if {@code true}, the checksum field will be validated against the actual - * uncompressed data, and if the checksums do not match, a suitable - * {@link DecompressionException} will be thrown - */ - public Lz4FrameDecoder(boolean validateChecksums) { - this(LZ4Factory.fastestInstance(), validateChecksums); - } - - /** - * Creates a new LZ4 decoder with customizable implementation. - * - * @param factory user customizable {@link LZ4Factory} instance - * which may be JNI bindings to the original C implementation, a pure Java implementation - * or a Java implementation that uses the {@link sun.misc.Unsafe} - * @param validateChecksums if {@code true}, the checksum field will be validated against the actual - * uncompressed data, and if the checksums do not match, a suitable - * {@link DecompressionException} will be thrown. In this case encoder will use - * xxhash hashing for Java, based on Yann Collet's work available at - * Github. - */ - public Lz4FrameDecoder(LZ4Factory factory, boolean validateChecksums) { - this(factory, validateChecksums ? new Lz4XXHash32(DEFAULT_SEED) : null); - } - - /** - * Creates a new customizable LZ4 decoder. - * - * @param factory user customizable {@link LZ4Factory} instance - * which may be JNI bindings to the original C implementation, a pure Java implementation - * or a Java implementation that uses the {@link sun.misc.Unsafe} - * @param checksum the {@link Checksum} instance to use to check data for integrity. - * You may set {@code null} if you do not want to validate checksum of each block - */ - public Lz4FrameDecoder(LZ4Factory factory, Checksum checksum) { - requireNonNull(factory, "factory"); - decompressor = factory.fastDecompressor(); - this.checksum = checksum == null ? null : ByteBufChecksum.wrapChecksum(checksum); - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - try { - switch (currentState) { - case INIT_BLOCK: - if (in.readableBytes() < HEADER_LENGTH) { - break; - } - final long magic = in.readLong(); - if (magic != MAGIC_NUMBER) { - throw new DecompressionException("unexpected block identifier"); - } - - final int token = in.readByte(); - final int compressionLevel = (token & 0x0F) + COMPRESSION_LEVEL_BASE; - int blockType = token & 0xF0; - - int compressedLength = Integer.reverseBytes(in.readInt()); - if (compressedLength < 0 || compressedLength > MAX_BLOCK_SIZE) { - throw new DecompressionException(String.format( - "invalid compressedLength: %d (expected: 0-%d)", - compressedLength, MAX_BLOCK_SIZE)); - } - - int decompressedLength = Integer.reverseBytes(in.readInt()); - final int maxDecompressedLength = 1 << compressionLevel; - if (decompressedLength < 0 || decompressedLength > maxDecompressedLength) { - throw new DecompressionException(String.format( - "invalid decompressedLength: %d (expected: 0-%d)", - decompressedLength, maxDecompressedLength)); - } - if (decompressedLength == 0 && compressedLength != 0 - || decompressedLength != 0 && compressedLength == 0 - || blockType == BLOCK_TYPE_NON_COMPRESSED && decompressedLength != compressedLength) { - throw new DecompressionException(String.format( - "stream corrupted: compressedLength(%d) and decompressedLength(%d) mismatch", - compressedLength, decompressedLength)); - } - - int currentChecksum = Integer.reverseBytes(in.readInt()); - if (decompressedLength == 0 && compressedLength == 0) { - if (currentChecksum != 0) { - throw new DecompressionException("stream corrupted: checksum error"); - } - currentState = State.FINISHED; - decompressor = null; - checksum = null; - break; - } - - this.blockType = blockType; - this.compressedLength = compressedLength; - this.decompressedLength = decompressedLength; - this.currentChecksum = currentChecksum; - - currentState = State.DECOMPRESS_DATA; - // fall through - case DECOMPRESS_DATA: - blockType = this.blockType; - compressedLength = this.compressedLength; - decompressedLength = this.decompressedLength; - currentChecksum = this.currentChecksum; - - if (in.readableBytes() < compressedLength) { - break; - } - - final ByteBufChecksum checksum = this.checksum; - ByteBuf uncompressed = null; - - try { - switch (blockType) { - case BLOCK_TYPE_NON_COMPRESSED: - // Just pass through, we not update the readerIndex yet as we do this outside of the - // switch statement. - uncompressed = in.retainedSlice(in.readerIndex(), decompressedLength); - break; - case BLOCK_TYPE_COMPRESSED: - uncompressed = ctx.alloc().buffer(decompressedLength, decompressedLength); - - decompressor.decompress(CompressionUtil.safeNioBuffer(in), - uncompressed.internalNioBuffer(uncompressed.writerIndex(), decompressedLength)); - // Update the writerIndex now to reflect what we decompressed. - uncompressed.writerIndex(uncompressed.writerIndex() + decompressedLength); - break; - default: - throw new DecompressionException(String.format( - "unexpected blockType: %d (expected: %d or %d)", - blockType, BLOCK_TYPE_NON_COMPRESSED, BLOCK_TYPE_COMPRESSED)); - } - // Skip inbound bytes after we processed them. - in.skipBytes(compressedLength); - - if (checksum != null) { - CompressionUtil.checkChecksum(checksum, uncompressed, currentChecksum); - } - ctx.fireChannelRead(uncompressed); - uncompressed = null; - currentState = State.INIT_BLOCK; - } catch (LZ4Exception e) { - throw new DecompressionException(e); - } finally { - if (uncompressed != null) { - uncompressed.release(); - } - } - break; - case FINISHED: - case CORRUPTED: - in.skipBytes(in.readableBytes()); - break; - default: - throw new IllegalStateException(); - } - } catch (Exception e) { - currentState = State.CORRUPTED; - throw e; - } - } - - /** - * Returns {@code true} if and only if the end of the compressed stream - * has been reached. - */ - public boolean isClosed() { - return currentState == State.FINISHED; - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/Lz4FrameEncoder.java b/codec/src/main/java/io/netty/handler/codec/compression/Lz4FrameEncoder.java deleted file mode 100644 index 5b63aeee78..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/Lz4FrameEncoder.java +++ /dev/null @@ -1,398 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.compression; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.handler.codec.EncoderException; -import io.netty.handler.codec.MessageToByteEncoder; -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; -import io.netty.util.internal.ObjectUtil; -import net.jpountz.lz4.LZ4Compressor; -import net.jpountz.lz4.LZ4Exception; -import net.jpountz.lz4.LZ4Factory; - -import java.nio.ByteBuffer; -import java.util.concurrent.TimeUnit; -import java.util.zip.Checksum; - -import static io.netty.handler.codec.compression.Lz4Constants.BLOCK_TYPE_COMPRESSED; -import static io.netty.handler.codec.compression.Lz4Constants.BLOCK_TYPE_NON_COMPRESSED; -import static io.netty.handler.codec.compression.Lz4Constants.CHECKSUM_OFFSET; -import static io.netty.handler.codec.compression.Lz4Constants.COMPRESSED_LENGTH_OFFSET; -import static io.netty.handler.codec.compression.Lz4Constants.COMPRESSION_LEVEL_BASE; -import static io.netty.handler.codec.compression.Lz4Constants.DECOMPRESSED_LENGTH_OFFSET; -import static io.netty.handler.codec.compression.Lz4Constants.DEFAULT_BLOCK_SIZE; -import static io.netty.handler.codec.compression.Lz4Constants.DEFAULT_SEED; -import static io.netty.handler.codec.compression.Lz4Constants.HEADER_LENGTH; -import static io.netty.handler.codec.compression.Lz4Constants.MAGIC_NUMBER; -import static io.netty.handler.codec.compression.Lz4Constants.MAX_BLOCK_SIZE; -import static io.netty.handler.codec.compression.Lz4Constants.MIN_BLOCK_SIZE; -import static io.netty.handler.codec.compression.Lz4Constants.TOKEN_OFFSET; -import static java.util.Objects.requireNonNull; - -/** - * Compresses a {@link ByteBuf} using the LZ4 format. - * - * See original LZ4 Github project - * and LZ4 block format - * for full description. - * - * Since the original LZ4 block format does not contains size of compressed block and size of original data - * this encoder uses format like LZ4 Java library - * written by Adrien Grand and approved by Yann Collet (author of original LZ4 library). - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * * Magic * Token * Compressed * Decompressed * Checksum * + * LZ4 compressed * - * * * * length * length * * * block * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - */ -public class Lz4FrameEncoder extends MessageToByteEncoder { - static final int DEFAULT_MAX_ENCODE_SIZE = Integer.MAX_VALUE; - - private final int blockSize; - - /** - * Underlying compressor in use. - */ - private final LZ4Compressor compressor; - - /** - * Underlying checksum calculator in use. - */ - private final ByteBufChecksum checksum; - - /** - * Compression level of current LZ4 encoder (depends on {@link #blockSize}). - */ - private final int compressionLevel; - - /** - * Inner byte buffer for outgoing data. It's capacity will be {@link #blockSize}. - */ - private ByteBuf buffer; - - /** - * Maximum size for any buffer to write encoded (compressed) data into. - */ - private final int maxEncodeSize; - - /** - * Indicates if the compressed stream has been finished. - */ - private volatile boolean finished; - - /** - * Used to interact with its {@link ChannelPipeline} and other handlers. - */ - private volatile ChannelHandlerContext ctx; - - /** - * Creates the fastest LZ4 encoder with default block size (64 KB) - * and xxhash hashing for Java, based on Yann Collet's work available at - * Github. - */ - public Lz4FrameEncoder() { - this(false); - } - - /** - * Creates a new LZ4 encoder with hight or fast compression, default block size (64 KB) - * and xxhash hashing for Java, based on Yann Collet's work available at - * Github. - * - * @param highCompressor if {@code true} codec will use compressor which requires more memory - * and is slower but compresses more efficiently - */ - public Lz4FrameEncoder(boolean highCompressor) { - this(LZ4Factory.fastestInstance(), highCompressor, DEFAULT_BLOCK_SIZE, new Lz4XXHash32(DEFAULT_SEED)); - } - - /** - * Creates a new customizable LZ4 encoder. - * - * @param factory user customizable {@link LZ4Factory} instance - * which may be JNI bindings to the original C implementation, a pure Java implementation - * or a Java implementation that uses the {@link sun.misc.Unsafe} - * @param highCompressor if {@code true} codec will use compressor which requires more memory - * and is slower but compresses more efficiently - * @param blockSize the maximum number of bytes to try to compress at once, - * must be >= 64 and <= 32 M - * @param checksum the {@link Checksum} instance to use to check data for integrity - */ - public Lz4FrameEncoder(LZ4Factory factory, boolean highCompressor, int blockSize, Checksum checksum) { - this(factory, highCompressor, blockSize, checksum, DEFAULT_MAX_ENCODE_SIZE); - } - - /** - * Creates a new customizable LZ4 encoder. - * - * @param factory user customizable {@link LZ4Factory} instance - * which may be JNI bindings to the original C implementation, a pure Java implementation - * or a Java implementation that uses the {@link sun.misc.Unsafe} - * @param highCompressor if {@code true} codec will use compressor which requires more memory - * and is slower but compresses more efficiently - * @param blockSize the maximum number of bytes to try to compress at once, - * must be >= 64 and <= 32 M - * @param checksum the {@link Checksum} instance to use to check data for integrity - * @param maxEncodeSize the maximum size for an encode (compressed) buffer - */ - public Lz4FrameEncoder(LZ4Factory factory, boolean highCompressor, int blockSize, - Checksum checksum, int maxEncodeSize) { - requireNonNull(factory, "factory"); - requireNonNull(checksum, "checksum"); - - compressor = highCompressor ? factory.highCompressor() : factory.fastCompressor(); - this.checksum = ByteBufChecksum.wrapChecksum(checksum); - - compressionLevel = compressionLevel(blockSize); - this.blockSize = blockSize; - this.maxEncodeSize = ObjectUtil.checkPositive(maxEncodeSize, "maxEncodeSize"); - finished = false; - } - - /** - * Calculates compression level on the basis of block size. - */ - private static int compressionLevel(int blockSize) { - if (blockSize < MIN_BLOCK_SIZE || blockSize > MAX_BLOCK_SIZE) { - throw new IllegalArgumentException(String.format( - "blockSize: %d (expected: %d-%d)", blockSize, MIN_BLOCK_SIZE, MAX_BLOCK_SIZE)); - } - int compressionLevel = 32 - Integer.numberOfLeadingZeros(blockSize - 1); // ceil of log2 - compressionLevel = Math.max(0, compressionLevel - COMPRESSION_LEVEL_BASE); - return compressionLevel; - } - - @Override - protected ByteBuf allocateBuffer(ChannelHandlerContext ctx, ByteBuf msg, boolean preferDirect) { - return allocateBuffer(ctx, msg, preferDirect, true); - } - - private ByteBuf allocateBuffer(ChannelHandlerContext ctx, ByteBuf msg, boolean preferDirect, - boolean allowEmptyReturn) { - int targetBufSize = 0; - int remaining = msg.readableBytes() + buffer.readableBytes(); - - // quick overflow check - if (remaining < 0) { - throw new EncoderException("too much data to allocate a buffer for compression"); - } - - while (remaining > 0) { - int curSize = Math.min(blockSize, remaining); - remaining -= curSize; - // calculate the total compressed size of the current block (including header) and add to the total - targetBufSize += compressor.maxCompressedLength(curSize) + HEADER_LENGTH; - } - - // in addition to just the raw byte count, the headers (HEADER_LENGTH) per block (configured via - // #blockSize) will also add to the targetBufSize, and the combination of those would never wrap around - // again to be >= 0, this is a good check for the overflow case. - if (targetBufSize > maxEncodeSize || 0 > targetBufSize) { - throw new EncoderException(String.format("requested encode buffer size (%d bytes) exceeds the maximum " + - "allowable size (%d bytes)", targetBufSize, maxEncodeSize)); - } - - if (allowEmptyReturn && targetBufSize < blockSize) { - return Unpooled.EMPTY_BUFFER; - } - - if (preferDirect) { - return ctx.alloc().ioBuffer(targetBufSize, targetBufSize); - } else { - return ctx.alloc().heapBuffer(targetBufSize, targetBufSize); - } - } - - /** - * {@inheritDoc} - * - * Encodes the input buffer into {@link #blockSize} chunks in the output buffer. Data is only compressed and - * written once we hit the {@link #blockSize}; else, it is copied into the backing {@link #buffer} to await - * more data. - */ - @Override - protected void encode(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) throws Exception { - if (finished) { - if (!out.isWritable(in.readableBytes())) { - // out should be EMPTY_BUFFER because we should have allocated enough space above in allocateBuffer. - throw new IllegalStateException("encode finished and not enough space to write remaining data"); - } - out.writeBytes(in); - return; - } - - final ByteBuf buffer = this.buffer; - int length; - while ((length = in.readableBytes()) > 0) { - final int nextChunkSize = Math.min(length, buffer.writableBytes()); - in.readBytes(buffer, nextChunkSize); - - if (!buffer.isWritable()) { - flushBufferedData(out); - } - } - } - - private void flushBufferedData(ByteBuf out) { - int flushableBytes = buffer.readableBytes(); - if (flushableBytes == 0) { - return; - } - checksum.reset(); - checksum.update(buffer, buffer.readerIndex(), flushableBytes); - final int check = (int) checksum.getValue(); - - final int bufSize = compressor.maxCompressedLength(flushableBytes) + HEADER_LENGTH; - out.ensureWritable(bufSize); - final int idx = out.writerIndex(); - int compressedLength; - try { - ByteBuffer outNioBuffer = out.internalNioBuffer(idx + HEADER_LENGTH, out.writableBytes() - HEADER_LENGTH); - int pos = outNioBuffer.position(); - // We always want to start at position 0 as we take care of reusing the buffer in the encode(...) loop. - compressor.compress(buffer.internalNioBuffer(buffer.readerIndex(), flushableBytes), outNioBuffer); - compressedLength = outNioBuffer.position() - pos; - } catch (LZ4Exception e) { - throw new CompressionException(e); - } - final int blockType; - if (compressedLength >= flushableBytes) { - blockType = BLOCK_TYPE_NON_COMPRESSED; - compressedLength = flushableBytes; - out.setBytes(idx + HEADER_LENGTH, buffer, 0, flushableBytes); - } else { - blockType = BLOCK_TYPE_COMPRESSED; - } - - out.setLong(idx, MAGIC_NUMBER); - out.setByte(idx + TOKEN_OFFSET, (byte) (blockType | compressionLevel)); - out.setIntLE(idx + COMPRESSED_LENGTH_OFFSET, compressedLength); - out.setIntLE(idx + DECOMPRESSED_LENGTH_OFFSET, flushableBytes); - out.setIntLE(idx + CHECKSUM_OFFSET, check); - out.writerIndex(idx + HEADER_LENGTH + compressedLength); - buffer.clear(); - } - - @Override - public void flush(final ChannelHandlerContext ctx) { - if (buffer != null && buffer.isReadable()) { - final ByteBuf buf = allocateBuffer(ctx, Unpooled.EMPTY_BUFFER, isPreferDirect(), false); - flushBufferedData(buf); - ctx.write(buf); - } - ctx.flush(); - } - - private Future finishEncode(final ChannelHandlerContext ctx) { - if (finished) { - return ctx.newSucceededFuture(); - } - finished = true; - - final ByteBuf footer = ctx.alloc().heapBuffer( - compressor.maxCompressedLength(buffer.readableBytes()) + HEADER_LENGTH); - flushBufferedData(footer); - - footer.ensureWritable(HEADER_LENGTH); - final int idx = footer.writerIndex(); - footer.setLong(idx, MAGIC_NUMBER); - footer.setByte(idx + TOKEN_OFFSET, (byte) (BLOCK_TYPE_NON_COMPRESSED | compressionLevel)); - footer.setInt(idx + COMPRESSED_LENGTH_OFFSET, 0); - footer.setInt(idx + DECOMPRESSED_LENGTH_OFFSET, 0); - footer.setInt(idx + CHECKSUM_OFFSET, 0); - - footer.writerIndex(idx + HEADER_LENGTH); - - return ctx.writeAndFlush(footer); - } - - /** - * Returns {@code true} if and only if the compressed stream has been finished. - */ - public boolean isClosed() { - return finished; - } - - /** - * Close this {@link Lz4FrameEncoder} and so finish the encoding. - * - * The returned {@link java.util.concurrent.Future} will be notified once the operation completes. - */ - public Future close() { - ChannelHandlerContext ctx = ctx(); - EventExecutor executor = ctx.executor(); - if (executor.inEventLoop()) { - return finishEncode(ctx); - } else { - Promise promise = ctx.newPromise(); - executor.execute(() -> { - Future f = finishEncode(ctx()); - f.cascadeTo(promise); - }); - return promise.asFuture(); - } - } - - @Override - public Future close(final ChannelHandlerContext ctx) { - Future f = finishEncode(ctx); - if (f.isDone()) { - return ctx.close(); - } - Promise promise = ctx.newPromise(); - f.addListener(f1 -> ctx.close().cascadeTo(promise)); - // Ensure the channel is closed even if the write operation completes in time. - ctx.executor().schedule(() -> ctx.close().cascadeTo(promise), - 10, TimeUnit.SECONDS); // FIXME: Magic number - return promise.asFuture(); - } - - private ChannelHandlerContext ctx() { - ChannelHandlerContext ctx = this.ctx; - if (ctx == null) { - throw new IllegalStateException("not added to a pipeline"); - } - return ctx; - } - - @Override - public void handlerAdded(ChannelHandlerContext ctx) { - this.ctx = ctx; - // Ensure we use a heap based ByteBuf. - buffer = Unpooled.wrappedBuffer(new byte[blockSize]); - buffer.clear(); - } - - @Override - public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { - super.handlerRemoved(ctx); - if (buffer != null) { - buffer.release(); - buffer = null; - } - } - - final ByteBuf getBackingBuffer() { - return buffer; - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/Lz4XXHash32.java b/codec/src/main/java/io/netty/handler/codec/compression/Lz4XXHash32.java deleted file mode 100644 index 8b92ecf1d3..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/Lz4XXHash32.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.buffer.ByteBuf; -import net.jpountz.xxhash.StreamingXXHash32; -import net.jpountz.xxhash.XXHash32; -import net.jpountz.xxhash.XXHashFactory; - -import java.nio.ByteBuffer; -import java.util.zip.Checksum; - -/** - * A special-purpose {@link ByteBufChecksum} implementation for use with - * {@link Lz4FrameEncoder} and {@link Lz4FrameDecoder}. - * - * {@link StreamingXXHash32#asChecksum()} has a particularly nasty implementation - * of {@link Checksum#update(int)} that allocates a single-element byte array for - * every invocation. - * - * In addition to that, it doesn't implement an overload that accepts a {@link ByteBuffer} - * as an argument. - * - * Combined, this means that we can't use {@code ReflectiveByteBufChecksum} at all, - * and can't use {@code SlowByteBufChecksum} because of its atrocious performance - * with direct byte buffers (allocating an array and making a JNI call for every byte - * checksummed might be considered sub-optimal by some). - * - * Block version of xxHash32 ({@link XXHash32}), however, does provide - * {@link XXHash32#hash(ByteBuffer, int)} method that is efficient and does exactly - * what we need, with a caveat that we can only invoke it once before having to reset. - * This, however, is fine for our purposes, given the way we use it in - * {@link Lz4FrameEncoder} and {@link Lz4FrameDecoder}: - * {@code reset()}, followed by one {@code update()}, followed by {@code getValue()}. - */ -public final class Lz4XXHash32 extends ByteBufChecksum { - - private static final XXHash32 XXHASH32 = XXHashFactory.fastestInstance().hash32(); - - private final int seed; - private boolean used; - private int value; - - @SuppressWarnings("WeakerAccess") - public Lz4XXHash32(int seed) { - this.seed = seed; - } - - @Override - public void update(int b) { - throw new UnsupportedOperationException(); - } - - @Override - public void update(byte[] b, int off, int len) { - if (used) { - throw new IllegalStateException(); - } - value = XXHASH32.hash(b, off, len, seed); - used = true; - } - - @Override - public void update(ByteBuf b, int off, int len) { - if (used) { - throw new IllegalStateException(); - } - if (b.hasArray()) { - value = XXHASH32.hash(b.array(), b.arrayOffset() + off, len, seed); - } else { - value = XXHASH32.hash(CompressionUtil.safeNioBuffer(b, off, len), seed); - } - used = true; - } - - @Override - public long getValue() { - if (!used) { - throw new IllegalStateException(); - } - /* - * If you look carefully, you'll notice that the most significant nibble - * is being discarded; we believe this to be a bug, but this is what - * StreamingXXHash32#asChecksum() implementation of getValue() does, - * so we have to retain this behaviour for compatibility reasons. - */ - return value & 0xFFFFFFFL; - } - - @Override - public void reset() { - used = false; - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/LzfDecoder.java b/codec/src/main/java/io/netty/handler/codec/compression/LzfDecoder.java deleted file mode 100644 index c5811622fc..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/LzfDecoder.java +++ /dev/null @@ -1,240 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import com.ning.compress.BufferRecycler; -import com.ning.compress.lzf.ChunkDecoder; -import com.ning.compress.lzf.LZFChunk; -import com.ning.compress.lzf.util.ChunkDecoderFactory; -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.ByteToMessageDecoder; - -import static com.ning.compress.lzf.LZFChunk.BLOCK_TYPE_COMPRESSED; -import static com.ning.compress.lzf.LZFChunk.BLOCK_TYPE_NON_COMPRESSED; -import static com.ning.compress.lzf.LZFChunk.BYTE_V; -import static com.ning.compress.lzf.LZFChunk.BYTE_Z; -import static com.ning.compress.lzf.LZFChunk.HEADER_LEN_NOT_COMPRESSED; - -/** - * Uncompresses a {@link ByteBuf} encoded with the LZF format. - * - * See original LZF package - * and LZF format for full description. - */ -public class LzfDecoder extends ByteToMessageDecoder { - /** - * Current state of decompression. - */ - private enum State { - INIT_BLOCK, - INIT_ORIGINAL_LENGTH, - DECOMPRESS_DATA, - CORRUPTED - } - - private State currentState = State.INIT_BLOCK; - - /** - * Magic number of LZF chunk. - */ - private static final short MAGIC_NUMBER = BYTE_Z << 8 | BYTE_V; - - /** - * Underlying decoder in use. - */ - private ChunkDecoder decoder; - - /** - * Object that handles details of buffer recycling. - */ - private BufferRecycler recycler; - - /** - * Length of current received chunk of data. - */ - private int chunkLength; - - /** - * Original length of current received chunk of data. - * It is equal to {@link #chunkLength} for non compressed chunks. - */ - private int originalLength; - - /** - * Indicates is this chunk compressed or not. - */ - private boolean isCompressed; - - /** - * Creates a new LZF decoder with the most optimal available methods for underlying data access. - * It will "unsafe" instance if one can be used on current JVM. - * It should be safe to call this constructor as implementations are dynamically loaded; however, on some - * non-standard platforms it may be necessary to use {@link #LzfDecoder(boolean)} with {@code true} param. - */ - public LzfDecoder() { - this(false); - } - - /** - * Creates a new LZF decoder with specified decoding instance. - * - * @param safeInstance - * If {@code true} decoder will use {@link ChunkDecoder} that only uses standard JDK access methods, - * and should work on all Java platforms and JVMs. - * Otherwise decoder will try to use highly optimized {@link ChunkDecoder} implementation that uses - * Sun JDK's {@link sun.misc.Unsafe} class (which may be included by other JDK's as well). - */ - public LzfDecoder(boolean safeInstance) { - decoder = safeInstance ? - ChunkDecoderFactory.safeInstance() - : ChunkDecoderFactory.optimalInstance(); - - recycler = BufferRecycler.instance(); - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - try { - switch (currentState) { - case INIT_BLOCK: - if (in.readableBytes() < HEADER_LEN_NOT_COMPRESSED) { - break; - } - final int magic = in.readUnsignedShort(); - if (magic != MAGIC_NUMBER) { - throw new DecompressionException("unexpected block identifier"); - } - - final int type = in.readByte(); - switch (type) { - case BLOCK_TYPE_NON_COMPRESSED: - isCompressed = false; - currentState = State.DECOMPRESS_DATA; - break; - case BLOCK_TYPE_COMPRESSED: - isCompressed = true; - currentState = State.INIT_ORIGINAL_LENGTH; - break; - default: - throw new DecompressionException(String.format( - "unknown type of chunk: %d (expected: %d or %d)", - type, BLOCK_TYPE_NON_COMPRESSED, BLOCK_TYPE_COMPRESSED)); - } - chunkLength = in.readUnsignedShort(); - - // chunkLength can never exceed MAX_CHUNK_LEN as MAX_CHUNK_LEN is 64kb and readUnsignedShort can - // never return anything bigger as well. Let's add some check any way to make things easier in terms - // of debugging if we ever hit this because of an bug. - if (chunkLength > LZFChunk.MAX_CHUNK_LEN) { - throw new DecompressionException(String.format( - "chunk length exceeds maximum: %d (expected: =< %d)", - chunkLength, LZFChunk.MAX_CHUNK_LEN)); - } - - if (type != BLOCK_TYPE_COMPRESSED) { - break; - } - // fall through - case INIT_ORIGINAL_LENGTH: - if (in.readableBytes() < 2) { - break; - } - originalLength = in.readUnsignedShort(); - - // originalLength can never exceed MAX_CHUNK_LEN as MAX_CHUNK_LEN is 64kb and readUnsignedShort can - // never return anything bigger as well. Let's add some check any way to make things easier in terms - // of debugging if we ever hit this because of an bug. - if (originalLength > LZFChunk.MAX_CHUNK_LEN) { - throw new DecompressionException(String.format( - "original length exceeds maximum: %d (expected: =< %d)", - chunkLength, LZFChunk.MAX_CHUNK_LEN)); - } - - currentState = State.DECOMPRESS_DATA; - // fall through - case DECOMPRESS_DATA: - final int chunkLength = this.chunkLength; - if (in.readableBytes() < chunkLength) { - break; - } - final int originalLength = this.originalLength; - - if (isCompressed) { - final int idx = in.readerIndex(); - - final byte[] inputArray; - final int inPos; - if (in.hasArray()) { - inputArray = in.array(); - inPos = in.arrayOffset() + idx; - } else { - inputArray = recycler.allocInputBuffer(chunkLength); - in.getBytes(idx, inputArray, 0, chunkLength); - inPos = 0; - } - - ByteBuf uncompressed = ctx.alloc().heapBuffer(originalLength, originalLength); - final byte[] outputArray; - final int outPos; - if (uncompressed.hasArray()) { - outputArray = uncompressed.array(); - outPos = uncompressed.arrayOffset() + uncompressed.writerIndex(); - } else { - outputArray = new byte[originalLength]; - outPos = 0; - } - - boolean success = false; - try { - decoder.decodeChunk(inputArray, inPos, outputArray, outPos, outPos + originalLength); - if (uncompressed.hasArray()) { - uncompressed.writerIndex(uncompressed.writerIndex() + originalLength); - } else { - uncompressed.writeBytes(outputArray); - } - ctx.fireChannelRead(uncompressed); - in.skipBytes(chunkLength); - success = true; - } finally { - if (!success) { - uncompressed.release(); - } - } - - if (!in.hasArray()) { - recycler.releaseInputBuffer(inputArray); - } - } else if (chunkLength > 0) { - ctx.fireChannelRead(in.readRetainedSlice(chunkLength)); - } - - currentState = State.INIT_BLOCK; - break; - case CORRUPTED: - in.skipBytes(in.readableBytes()); - break; - default: - throw new IllegalStateException(); - } - } catch (Exception e) { - currentState = State.CORRUPTED; - decoder = null; - recycler = null; - throw e; - } - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/LzfEncoder.java b/codec/src/main/java/io/netty/handler/codec/compression/LzfEncoder.java deleted file mode 100644 index bba0fefe7c..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/LzfEncoder.java +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import com.ning.compress.BufferRecycler; -import com.ning.compress.lzf.ChunkEncoder; -import com.ning.compress.lzf.LZFChunk; -import com.ning.compress.lzf.LZFEncoder; -import com.ning.compress.lzf.util.ChunkEncoderFactory; -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToByteEncoder; - -import static com.ning.compress.lzf.LZFChunk.MAX_CHUNK_LEN; - -/** - * Compresses a {@link ByteBuf} using the LZF format. - *

- * See original LZF package - * and LZF format for full description. - */ -public class LzfEncoder extends MessageToByteEncoder { - - /** - * Minimum block size ready for compression. Blocks with length - * less than {@link #MIN_BLOCK_TO_COMPRESS} will write as uncompressed. - */ - private static final int MIN_BLOCK_TO_COMPRESS = 16; - - /** - * Compress threshold for LZF format. When the amount of input data is less than compressThreshold, - * we will construct an uncompressed output according to the LZF format. - *

- * When the value is less than {@see ChunkEncoder#MIN_BLOCK_TO_COMPRESS}, since LZF will not compress data - * that is less than {@see ChunkEncoder#MIN_BLOCK_TO_COMPRESS}, compressThreshold will not work. - */ - private final int compressThreshold; - - /** - * Underlying decoder in use. - */ - private final ChunkEncoder encoder; - - /** - * Object that handles details of buffer recycling. - */ - private final BufferRecycler recycler; - - /** - * Creates a new LZF encoder with the most optimal available methods for underlying data access. - * It will "unsafe" instance if one can be used on current JVM. - * It should be safe to call this constructor as implementations are dynamically loaded; however, on some - * non-standard platforms it may be necessary to use {@link #LzfEncoder(boolean)} with {@code true} param. - */ - public LzfEncoder() { - this(false); - } - - /** - * Creates a new LZF encoder with specified encoding instance. - * - * @param safeInstance If {@code true} encoder will use {@link ChunkEncoder} that only uses - * standard JDK access methods, and should work on all Java platforms and JVMs. - * Otherwise encoder will try to use highly optimized {@link ChunkEncoder} - * implementation that uses Sun JDK's {@link sun.misc.Unsafe} - * class (which may be included by other JDK's as well). - */ - public LzfEncoder(boolean safeInstance) { - this(safeInstance, MAX_CHUNK_LEN); - } - - /** - * Creates a new LZF encoder with specified encoding instance and compressThreshold. - * - * @param safeInstance If {@code true} encoder will use {@link ChunkEncoder} that only uses standard - * JDK access methods, and should work on all Java platforms and JVMs. - * Otherwise encoder will try to use highly optimized {@link ChunkEncoder} - * implementation that uses Sun JDK's {@link sun.misc.Unsafe} - * class (which may be included by other JDK's as well). - * @param totalLength Expected total length of content to compress; only matters for outgoing messages - * that is smaller than maximum chunk size (64k), to optimize encoding hash tables. - */ - public LzfEncoder(boolean safeInstance, int totalLength) { - this(safeInstance, totalLength, MIN_BLOCK_TO_COMPRESS); - } - - /** - * Creates a new LZF encoder with specified total length of encoded chunk. You can configure it to encode - * your data flow more efficient if you know the average size of messages that you send. - * - * @param totalLength Expected total length of content to compress; - * only matters for outgoing messages that is smaller than maximum chunk size (64k), - * to optimize encoding hash tables. - */ - public LzfEncoder(int totalLength) { - this(false, totalLength); - } - - /** - * Creates a new LZF encoder with specified settings. - * - * @param safeInstance If {@code true} encoder will use {@link ChunkEncoder} that only uses standard JDK - * access methods, and should work on all Java platforms and JVMs. - * Otherwise encoder will try to use highly optimized {@link ChunkEncoder} - * implementation that uses Sun JDK's {@link sun.misc.Unsafe} - * class (which may be included by other JDK's as well). - * @param totalLength Expected total length of content to compress; only matters for outgoing messages - * that is smaller than maximum chunk size (64k), to optimize encoding hash tables. - * @param compressThreshold Compress threshold for LZF format. When the amount of input data is less than - * compressThreshold, we will construct an uncompressed output according - * to the LZF format. - */ - public LzfEncoder(boolean safeInstance, int totalLength, int compressThreshold) { - super(false); - if (totalLength < MIN_BLOCK_TO_COMPRESS || totalLength > MAX_CHUNK_LEN) { - throw new IllegalArgumentException("totalLength: " + totalLength + - " (expected: " + MIN_BLOCK_TO_COMPRESS + '-' + MAX_CHUNK_LEN + ')'); - } - - if (compressThreshold < MIN_BLOCK_TO_COMPRESS) { - // not a suitable value. - throw new IllegalArgumentException("compressThreshold:" + compressThreshold + - " expected >=" + MIN_BLOCK_TO_COMPRESS); - } - this.compressThreshold = compressThreshold; - - this.encoder = safeInstance ? - ChunkEncoderFactory.safeNonAllocatingInstance(totalLength) - : ChunkEncoderFactory.optimalNonAllocatingInstance(totalLength); - - this.recycler = BufferRecycler.instance(); - } - - @Override - protected void encode(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) throws Exception { - final int length = in.readableBytes(); - final int idx = in.readerIndex(); - final byte[] input; - final int inputPtr; - if (in.hasArray()) { - input = in.array(); - inputPtr = in.arrayOffset() + idx; - } else { - input = recycler.allocInputBuffer(length); - in.getBytes(idx, input, 0, length); - inputPtr = 0; - } - - // Estimate may apparently under-count by one in some cases. - final int maxOutputLength = LZFEncoder.estimateMaxWorkspaceSize(length) + 1; - out.ensureWritable(maxOutputLength); - final byte[] output; - final int outputPtr; - if (out.hasArray()) { - output = out.array(); - outputPtr = out.arrayOffset() + out.writerIndex(); - } else { - output = new byte[maxOutputLength]; - outputPtr = 0; - } - - final int outputLength; - if (length >= compressThreshold) { - // compress. - outputLength = encodeCompress(input, inputPtr, length, output, outputPtr); - } else { - // not compress. - outputLength = encodeNonCompress(input, inputPtr, length, output, outputPtr); - } - - if (out.hasArray()) { - out.writerIndex(out.writerIndex() + outputLength); - } else { - out.writeBytes(output, 0, outputLength); - } - - in.skipBytes(length); - - if (!in.hasArray()) { - recycler.releaseInputBuffer(input); - } - } - - private int encodeCompress(byte[] input, int inputPtr, int length, byte[] output, int outputPtr) { - return LZFEncoder.appendEncoded(encoder, - input, inputPtr, length, output, outputPtr) - outputPtr; - } - - private static int lzfEncodeNonCompress(byte[] input, int inputPtr, int length, byte[] output, int outputPtr) { - int left = length; - int chunkLen = Math.min(LZFChunk.MAX_CHUNK_LEN, left); - outputPtr = LZFChunk.appendNonCompressed(input, inputPtr, chunkLen, output, outputPtr); - left -= chunkLen; - if (left < 1) { - return outputPtr; - } - inputPtr += chunkLen; - do { - chunkLen = Math.min(left, LZFChunk.MAX_CHUNK_LEN); - outputPtr = LZFChunk.appendNonCompressed(input, inputPtr, chunkLen, output, outputPtr); - inputPtr += chunkLen; - left -= chunkLen; - } while (left > 0); - return outputPtr; - } - - /** - * Use lzf uncompressed format to encode a piece of input. - */ - private static int encodeNonCompress(byte[] input, int inputPtr, int length, byte[] output, int outputPtr) { - return lzfEncodeNonCompress(input, inputPtr, length, output, outputPtr) - outputPtr; - } - - @Override - public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { - encoder.close(); - super.handlerRemoved(ctx); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/LzmaFrameEncoder.java b/codec/src/main/java/io/netty/handler/codec/compression/LzmaFrameEncoder.java deleted file mode 100644 index 368180286c..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/LzmaFrameEncoder.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufInputStream; -import io.netty.buffer.ByteBufOutputStream; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToByteEncoder; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; -import lzma.sdk.lzma.Base; -import lzma.sdk.lzma.Encoder; - -import java.io.InputStream; - -import static lzma.sdk.lzma.Encoder.*; - -/** - * Compresses a {@link ByteBuf} using the LZMA algorithm. - * - * See LZMA - * and LZMA format - * or documents in LZMA SDK archive. - */ -public class LzmaFrameEncoder extends MessageToByteEncoder { - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(LzmaFrameEncoder.class); - - private static final int MEDIUM_DICTIONARY_SIZE = 1 << 16; - - private static final int MIN_FAST_BYTES = 5; - private static final int MEDIUM_FAST_BYTES = 0x20; - private static final int MAX_FAST_BYTES = Base.kMatchMaxLen; - - private static final int DEFAULT_MATCH_FINDER = EMatchFinderTypeBT4; - - private static final int DEFAULT_LC = 3; - private static final int DEFAULT_LP = 0; - private static final int DEFAULT_PB = 2; - - /** - * Underlying LZMA encoder in use. - */ - private final Encoder encoder; - - /** - * The Properties field contains three properties which are encoded using the following formula: - * - *

{@code Properties = (pb * 5 + lp) * 9 + lc}

- * - * The field consists of - *
    - *
  1. the number of literal context bits (lc, [0, 8]);
  2. - *
  3. the number of literal position bits (lp, [0, 4]);
  4. - *
  5. the number of position bits (pb, [0, 4]).
  6. - *
- */ - private final byte properties; - - /** - * Dictionary Size is stored as an unsigned 32-bit little endian integer. - */ - private final int littleEndianDictionarySize; - - /** - * For log warning only once. - */ - private static boolean warningLogged; - - /** - * Creates LZMA encoder with default settings. - */ - public LzmaFrameEncoder() { - this(MEDIUM_DICTIONARY_SIZE); - } - - /** - * Creates LZMA encoder with specified {@code lc}, {@code lp}, {@code pb} - * values and the medium dictionary size of {@value #MEDIUM_DICTIONARY_SIZE}. - */ - public LzmaFrameEncoder(int lc, int lp, int pb) { - this(lc, lp, pb, MEDIUM_DICTIONARY_SIZE); - } - - /** - * Creates LZMA encoder with specified dictionary size and default values of - * {@code lc} = {@value #DEFAULT_LC}, - * {@code lp} = {@value #DEFAULT_LP}, - * {@code pb} = {@value #DEFAULT_PB}. - */ - public LzmaFrameEncoder(int dictionarySize) { - this(DEFAULT_LC, DEFAULT_LP, DEFAULT_PB, dictionarySize); - } - - /** - * Creates LZMA encoder with specified {@code lc}, {@code lp}, {@code pb} values and custom dictionary size. - */ - public LzmaFrameEncoder(int lc, int lp, int pb, int dictionarySize) { - this(lc, lp, pb, dictionarySize, false, MEDIUM_FAST_BYTES); - } - - /** - * Creates LZMA encoder with specified settings. - * - * @param lc - * the number of "literal context" bits, available values [0, 8], default value {@value #DEFAULT_LC}. - * @param lp - * the number of "literal position" bits, available values [0, 4], default value {@value #DEFAULT_LP}. - * @param pb - * the number of "position" bits, available values [0, 4], default value {@value #DEFAULT_PB}. - * @param dictionarySize - * available values [0, {@link java.lang.Integer#MAX_VALUE}], - * default value is {@value #MEDIUM_DICTIONARY_SIZE}. - * @param endMarkerMode - * indicates should {@link LzmaFrameEncoder} use end of stream marker or not. - * Note, that {@link LzmaFrameEncoder} always sets size of uncompressed data - * in LZMA header, so EOS marker is unnecessary. But you may use it for - * better portability. For full description see "LZMA Decoding modes" section - * of LZMA-Specification.txt in official LZMA SDK. - * @param numFastBytes - * available values [{@value #MIN_FAST_BYTES}, {@value #MAX_FAST_BYTES}]. - */ - public LzmaFrameEncoder(int lc, int lp, int pb, int dictionarySize, boolean endMarkerMode, int numFastBytes) { - if (lc < 0 || lc > 8) { - throw new IllegalArgumentException("lc: " + lc + " (expected: 0-8)"); - } - if (lp < 0 || lp > 4) { - throw new IllegalArgumentException("lp: " + lp + " (expected: 0-4)"); - } - if (pb < 0 || pb > 4) { - throw new IllegalArgumentException("pb: " + pb + " (expected: 0-4)"); - } - if (lc + lp > 4) { - if (!warningLogged) { - logger.warn("The latest versions of LZMA libraries (for example, XZ Utils) " + - "has an additional requirement: lc + lp <= 4. Data which don't follow " + - "this requirement cannot be decompressed with this libraries."); - warningLogged = true; - } - } - if (dictionarySize < 0) { - throw new IllegalArgumentException("dictionarySize: " + dictionarySize + " (expected: 0+)"); - } - if (numFastBytes < MIN_FAST_BYTES || numFastBytes > MAX_FAST_BYTES) { - throw new IllegalArgumentException(String.format( - "numFastBytes: %d (expected: %d-%d)", numFastBytes, MIN_FAST_BYTES, MAX_FAST_BYTES - )); - } - - encoder = new Encoder(); - encoder.setDictionarySize(dictionarySize); - encoder.setEndMarkerMode(endMarkerMode); - encoder.setMatchFinder(DEFAULT_MATCH_FINDER); - encoder.setNumFastBytes(numFastBytes); - encoder.setLcLpPb(lc, lp, pb); - - properties = (byte) ((pb * 5 + lp) * 9 + lc); - littleEndianDictionarySize = Integer.reverseBytes(dictionarySize); - } - - @Override - protected void encode(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) throws Exception { - final int length = in.readableBytes(); - try (InputStream bbIn = new ByteBufInputStream(in); - ByteBufOutputStream bbOut = new ByteBufOutputStream(out)) { - bbOut.writeByte(properties); - bbOut.writeInt(littleEndianDictionarySize); - bbOut.writeLong(Long.reverseBytes(length)); - encoder.code(bbIn, bbOut, -1, -1, null); - } - } - - @Override - protected ByteBuf allocateBuffer(ChannelHandlerContext ctx, ByteBuf in, boolean preferDirect) throws Exception { - final int length = in.readableBytes(); - final int maxOutputLength = maxOutputBufferLength(length); - return ctx.alloc().ioBuffer(maxOutputLength); - } - - /** - * Calculates maximum possible size of output buffer for not compressible data. - */ - private static int maxOutputBufferLength(int inputLength) { - double factor; - if (inputLength < 200) { - factor = 1.5; - } else if (inputLength < 500) { - factor = 1.2; - } else if (inputLength < 1000) { - factor = 1.1; - } else if (inputLength < 10000) { - factor = 1.05; - } else { - factor = 1.02; - } - return 13 + (int) (inputLength * factor); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/Snappy.java b/codec/src/main/java/io/netty/handler/codec/compression/Snappy.java deleted file mode 100644 index 610b33f7b1..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/Snappy.java +++ /dev/null @@ -1,677 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.buffer.ByteBuf; - -/** - * Uncompresses an input {@link ByteBuf} encoded with Snappy compression into an - * output {@link ByteBuf}. - * - * See snappy format. - */ -public final class Snappy { - - private static final int MAX_HT_SIZE = 1 << 14; - private static final int MIN_COMPRESSIBLE_BYTES = 15; - - // used as a return value to indicate that we haven't yet read our full preamble - private static final int PREAMBLE_NOT_FULL = -1; - private static final int NOT_ENOUGH_INPUT = -1; - - // constants for the tag types - private static final int LITERAL = 0; - private static final int COPY_1_BYTE_OFFSET = 1; - private static final int COPY_2_BYTE_OFFSET = 2; - private static final int COPY_4_BYTE_OFFSET = 3; - - private State state = State.READING_PREAMBLE; - private byte tag; - private int written; - - private enum State { - READING_PREAMBLE, - READING_TAG, - READING_LITERAL, - READING_COPY - } - - public void reset() { - state = State.READING_PREAMBLE; - tag = 0; - written = 0; - } - - public void encode(final ByteBuf in, final ByteBuf out, final int length) { - // Write the preamble length to the output buffer - for (int i = 0;; i ++) { - int b = length >>> i * 7; - if ((b & 0xFFFFFF80) != 0) { - out.writeByte(b & 0x7f | 0x80); - } else { - out.writeByte(b); - break; - } - } - - int inIndex = in.readerIndex(); - final int baseIndex = inIndex; - - final short[] table = getHashTable(length); - final int shift = Integer.numberOfLeadingZeros(table.length) + 1; - - int nextEmit = inIndex; - - if (length - inIndex >= MIN_COMPRESSIBLE_BYTES) { - int nextHash = hash(in, ++inIndex, shift); - outer: while (true) { - int skip = 32; - - int candidate; - int nextIndex = inIndex; - do { - inIndex = nextIndex; - int hash = nextHash; - int bytesBetweenHashLookups = skip++ >> 5; - nextIndex = inIndex + bytesBetweenHashLookups; - - // We need at least 4 remaining bytes to read the hash - if (nextIndex > length - 4) { - break outer; - } - - nextHash = hash(in, nextIndex, shift); - - candidate = baseIndex + table[hash]; - - table[hash] = (short) (inIndex - baseIndex); - } - while (in.getInt(inIndex) != in.getInt(candidate)); - - encodeLiteral(in, out, inIndex - nextEmit); - - int insertTail; - do { - int base = inIndex; - int matched = 4 + findMatchingLength(in, candidate + 4, inIndex + 4, length); - inIndex += matched; - int offset = base - candidate; - encodeCopy(out, offset, matched); - in.readerIndex(in.readerIndex() + matched); - insertTail = inIndex - 1; - nextEmit = inIndex; - if (inIndex >= length - 4) { - break outer; - } - - int prevHash = hash(in, insertTail, shift); - table[prevHash] = (short) (inIndex - baseIndex - 1); - int currentHash = hash(in, insertTail + 1, shift); - candidate = baseIndex + table[currentHash]; - table[currentHash] = (short) (inIndex - baseIndex); - } - while (in.getInt(insertTail + 1) == in.getInt(candidate)); - - nextHash = hash(in, insertTail + 2, shift); - ++inIndex; - } - } - - // If there are any remaining characters, write them out as a literal - if (nextEmit < length) { - encodeLiteral(in, out, length - nextEmit); - } - } - - /** - * Hashes the 4 bytes located at index, shifting the resulting hash into - * the appropriate range for our hash table. - * - * @param in The input buffer to read 4 bytes from - * @param index The index to read at - * @param shift The shift value, for ensuring that the resulting value is - * withing the range of our hash table size - * @return A 32-bit hash of 4 bytes located at index - */ - private static int hash(ByteBuf in, int index, int shift) { - return in.getInt(index) * 0x1e35a7bd >>> shift; - } - - /** - * Creates an appropriately sized hashtable for the given input size - * - * @param inputSize The size of our input, ie. the number of bytes we need to encode - * @return An appropriately sized empty hashtable - */ - private static short[] getHashTable(int inputSize) { - int htSize = 256; - while (htSize < MAX_HT_SIZE && htSize < inputSize) { - htSize <<= 1; - } - return new short[htSize]; - } - - /** - * Iterates over the supplied input buffer between the supplied minIndex and - * maxIndex to find how long our matched copy overlaps with an already-written - * literal value. - * - * @param in The input buffer to scan over - * @param minIndex The index in the input buffer to start scanning from - * @param inIndex The index of the start of our copy - * @param maxIndex The length of our input buffer - * @return The number of bytes for which our candidate copy is a repeat of - */ - private static int findMatchingLength(ByteBuf in, int minIndex, int inIndex, int maxIndex) { - int matched = 0; - - while (inIndex <= maxIndex - 4 && - in.getInt(inIndex) == in.getInt(minIndex + matched)) { - inIndex += 4; - matched += 4; - } - - while (inIndex < maxIndex && in.getByte(minIndex + matched) == in.getByte(inIndex)) { - ++inIndex; - ++matched; - } - - return matched; - } - - /** - * Calculates the minimum number of bits required to encode a value. This can - * then in turn be used to calculate the number of septets or octets (as - * appropriate) to use to encode a length parameter. - * - * @param value The value to calculate the minimum number of bits required to encode - * @return The minimum number of bits required to encode the supplied value - */ - private static int bitsToEncode(int value) { - int highestOneBit = Integer.highestOneBit(value); - int bitLength = 0; - while ((highestOneBit >>= 1) != 0) { - bitLength++; - } - - return bitLength; - } - - /** - * Writes a literal to the supplied output buffer by directly copying from - * the input buffer. The literal is taken from the current readerIndex - * up to the supplied length. - * - * @param in The input buffer to copy from - * @param out The output buffer to copy to - * @param length The length of the literal to copy - */ - static void encodeLiteral(ByteBuf in, ByteBuf out, int length) { - if (length < 61) { - out.writeByte(length - 1 << 2); - } else { - int bitLength = bitsToEncode(length - 1); - int bytesToEncode = 1 + bitLength / 8; - out.writeByte(59 + bytesToEncode << 2); - for (int i = 0; i < bytesToEncode; i++) { - out.writeByte(length - 1 >> i * 8 & 0x0ff); - } - } - - out.writeBytes(in, length); - } - - private static void encodeCopyWithOffset(ByteBuf out, int offset, int length) { - if (length < 12 && offset < 2048) { - out.writeByte(COPY_1_BYTE_OFFSET | length - 4 << 2 | offset >> 8 << 5); - out.writeByte(offset & 0x0ff); - } else { - out.writeByte(COPY_2_BYTE_OFFSET | length - 1 << 2); - out.writeByte(offset & 0x0ff); - out.writeByte(offset >> 8 & 0x0ff); - } - } - - /** - * Encodes a series of copies, each at most 64 bytes in length. - * - * @param out The output buffer to write the copy pointer to - * @param offset The offset at which the original instance lies - * @param length The length of the original instance - */ - private static void encodeCopy(ByteBuf out, int offset, int length) { - while (length >= 68) { - encodeCopyWithOffset(out, offset, 64); - length -= 64; - } - - if (length > 64) { - encodeCopyWithOffset(out, offset, 60); - length -= 60; - } - - encodeCopyWithOffset(out, offset, length); - } - - public void decode(ByteBuf in, ByteBuf out) { - while (in.isReadable()) { - switch (state) { - case READING_PREAMBLE: - int uncompressedLength = readPreamble(in); - if (uncompressedLength == PREAMBLE_NOT_FULL) { - // We've not yet read all of the preamble, so wait until we can - return; - } - if (uncompressedLength == 0) { - // Should never happen, but it does mean we have nothing further to do - return; - } - out.ensureWritable(uncompressedLength); - state = State.READING_TAG; - // fall through - case READING_TAG: - if (!in.isReadable()) { - return; - } - tag = in.readByte(); - switch (tag & 0x03) { - case LITERAL: - state = State.READING_LITERAL; - break; - case COPY_1_BYTE_OFFSET: - case COPY_2_BYTE_OFFSET: - case COPY_4_BYTE_OFFSET: - state = State.READING_COPY; - break; - } - break; - case READING_LITERAL: - int literalWritten = decodeLiteral(tag, in, out); - if (literalWritten != NOT_ENOUGH_INPUT) { - state = State.READING_TAG; - written += literalWritten; - } else { - // Need to wait for more data - return; - } - break; - case READING_COPY: - int decodeWritten; - switch (tag & 0x03) { - case COPY_1_BYTE_OFFSET: - decodeWritten = decodeCopyWith1ByteOffset(tag, in, out, written); - if (decodeWritten != NOT_ENOUGH_INPUT) { - state = State.READING_TAG; - written += decodeWritten; - } else { - // Need to wait for more data - return; - } - break; - case COPY_2_BYTE_OFFSET: - decodeWritten = decodeCopyWith2ByteOffset(tag, in, out, written); - if (decodeWritten != NOT_ENOUGH_INPUT) { - state = State.READING_TAG; - written += decodeWritten; - } else { - // Need to wait for more data - return; - } - break; - case COPY_4_BYTE_OFFSET: - decodeWritten = decodeCopyWith4ByteOffset(tag, in, out, written); - if (decodeWritten != NOT_ENOUGH_INPUT) { - state = State.READING_TAG; - written += decodeWritten; - } else { - // Need to wait for more data - return; - } - break; - } - } - } - } - - /** - * Reads the length varint (a series of bytes, where the lower 7 bits - * are data and the upper bit is a flag to indicate more bytes to be - * read). - * - * @param in The input buffer to read the preamble from - * @return The calculated length based on the input buffer, or 0 if - * no preamble is able to be calculated - */ - private static int readPreamble(ByteBuf in) { - int length = 0; - int byteIndex = 0; - while (in.isReadable()) { - int current = in.readUnsignedByte(); - length |= (current & 0x7f) << byteIndex++ * 7; - if ((current & 0x80) == 0) { - return length; - } - - if (byteIndex >= 4) { - throw new DecompressionException("Preamble is greater than 4 bytes"); - } - } - - return 0; - } - - /** - * Get the length varint (a series of bytes, where the lower 7 bits - * are data and the upper bit is a flag to indicate more bytes to be - * read). - * - * @param in The input buffer to get the preamble from - * @return The calculated length based on the input buffer, or 0 if - * no preamble is able to be calculated - */ - int getPreamble(ByteBuf in) { - if (state == State.READING_PREAMBLE) { - int readerIndex = in.readerIndex(); - try { - return readPreamble(in); - } finally { - in.readerIndex(readerIndex); - } - } - return 0; - } - - /** - * Reads a literal from the input buffer directly to the output buffer. - * A "literal" is an uncompressed segment of data stored directly in the - * byte stream. - * - * @param tag The tag that identified this segment as a literal is also - * used to encode part of the length of the data - * @param in The input buffer to read the literal from - * @param out The output buffer to write the literal to - * @return The number of bytes appended to the output buffer, or -1 to indicate "try again later" - */ - static int decodeLiteral(byte tag, ByteBuf in, ByteBuf out) { - int readerIndex = in.readerIndex(); - int length; - switch(tag >> 2 & 0x3F) { - case 60: - if (!in.isReadable()) { - return NOT_ENOUGH_INPUT; - } - length = in.readUnsignedByte(); - break; - case 61: - if (in.readableBytes() < 2) { - return NOT_ENOUGH_INPUT; - } - length = in.readUnsignedShortLE(); - break; - case 62: - if (in.readableBytes() < 3) { - return NOT_ENOUGH_INPUT; - } - length = in.readUnsignedMediumLE(); - break; - case 63: - if (in.readableBytes() < 4) { - return NOT_ENOUGH_INPUT; - } - length = in.readIntLE(); - break; - default: - length = tag >> 2 & 0x3F; - } - length += 1; - - if (in.readableBytes() < length) { - in.readerIndex(readerIndex); - return NOT_ENOUGH_INPUT; - } - - out.writeBytes(in, length); - return length; - } - - /** - * Reads a compressed reference offset and length from the supplied input - * buffer, seeks back to the appropriate place in the input buffer and - * writes the found data to the supplied output stream. - * - * @param tag The tag used to identify this as a copy is also used to encode - * the length and part of the offset - * @param in The input buffer to read from - * @param out The output buffer to write to - * @return The number of bytes appended to the output buffer, or -1 to indicate - * "try again later" - * @throws DecompressionException If the read offset is invalid - */ - private static int decodeCopyWith1ByteOffset(byte tag, ByteBuf in, ByteBuf out, int writtenSoFar) { - if (!in.isReadable()) { - return NOT_ENOUGH_INPUT; - } - - int initialIndex = out.writerIndex(); - int length = 4 + ((tag & 0x01c) >> 2); - int offset = (tag & 0x0e0) << 8 >> 5 | in.readUnsignedByte(); - - validateOffset(offset, writtenSoFar); - - int readerIndex = out.readerIndex(); - if (offset < length) { - int copies = length / offset; - for (; copies > 0; copies--) { - out.readerIndex(initialIndex - offset); - out.readBytes(out, offset); - } - if (length % offset != 0) { - out.readerIndex(initialIndex - offset); - out.readBytes(out, length % offset); - } - } else { - out.readerIndex(initialIndex - offset); - out.readBytes(out, length); - } - out.readerIndex(readerIndex); - - return length; - } - - /** - * Reads a compressed reference offset and length from the supplied input - * buffer, seeks back to the appropriate place in the input buffer and - * writes the found data to the supplied output stream. - * - * @param tag The tag used to identify this as a copy is also used to encode - * the length and part of the offset - * @param in The input buffer to read from - * @param out The output buffer to write to - * @throws DecompressionException If the read offset is invalid - * @return The number of bytes appended to the output buffer, or -1 to indicate - * "try again later" - */ - private static int decodeCopyWith2ByteOffset(byte tag, ByteBuf in, ByteBuf out, int writtenSoFar) { - if (in.readableBytes() < 2) { - return NOT_ENOUGH_INPUT; - } - - int initialIndex = out.writerIndex(); - int length = 1 + (tag >> 2 & 0x03f); - int offset = in.readUnsignedShortLE(); - - validateOffset(offset, writtenSoFar); - - int readerIndex = out.readerIndex(); - if (offset < length) { - int copies = length / offset; - for (; copies > 0; copies--) { - out.readerIndex(initialIndex - offset); - out.readBytes(out, offset); - } - if (length % offset != 0) { - out.readerIndex(initialIndex - offset); - out.readBytes(out, length % offset); - } - } else { - out.readerIndex(initialIndex - offset); - out.readBytes(out, length); - } - out.readerIndex(readerIndex); - - return length; - } - - /** - * Reads a compressed reference offset and length from the supplied input - * buffer, seeks back to the appropriate place in the input buffer and - * writes the found data to the supplied output stream. - * - * @param tag The tag used to identify this as a copy is also used to encode - * the length and part of the offset - * @param in The input buffer to read from - * @param out The output buffer to write to - * @return The number of bytes appended to the output buffer, or -1 to indicate - * "try again later" - * @throws DecompressionException If the read offset is invalid - */ - private static int decodeCopyWith4ByteOffset(byte tag, ByteBuf in, ByteBuf out, int writtenSoFar) { - if (in.readableBytes() < 4) { - return NOT_ENOUGH_INPUT; - } - - int initialIndex = out.writerIndex(); - int length = 1 + (tag >> 2 & 0x03F); - int offset = in.readIntLE(); - - validateOffset(offset, writtenSoFar); - - int readerIndex = out.readerIndex(); - if (offset < length) { - int copies = length / offset; - for (; copies > 0; copies--) { - out.readerIndex(initialIndex - offset); - out.readBytes(out, offset); - } - if (length % offset != 0) { - out.readerIndex(initialIndex - offset); - out.readBytes(out, length % offset); - } - } else { - out.readerIndex(initialIndex - offset); - out.readBytes(out, length); - } - out.readerIndex(readerIndex); - - return length; - } - - /** - * Validates that the offset extracted from a compressed reference is within - * the permissible bounds of an offset (0 < offset < Integer.MAX_VALUE), and does not - * exceed the length of the chunk currently read so far. - * - * @param offset The offset extracted from the compressed reference - * @param chunkSizeSoFar The number of bytes read so far from this chunk - * @throws DecompressionException if the offset is invalid - */ - private static void validateOffset(int offset, int chunkSizeSoFar) { - if (offset == 0) { - throw new DecompressionException("Offset is less than minimum permissible value"); - } - - if (offset < 0) { - // Due to arithmetic overflow - throw new DecompressionException("Offset is greater than maximum value supported by this implementation"); - } - - if (offset > chunkSizeSoFar) { - throw new DecompressionException("Offset exceeds size of chunk"); - } - } - - /** - * Computes the CRC32C checksum of the supplied data and performs the "mask" operation - * on the computed checksum - * - * @param data The input data to calculate the CRC32C checksum of - */ - static int calculateChecksum(ByteBuf data) { - return calculateChecksum(data, data.readerIndex(), data.readableBytes()); - } - - /** - * Computes the CRC32C checksum of the supplied data and performs the "mask" operation - * on the computed checksum - * - * @param data The input data to calculate the CRC32C checksum of - */ - static int calculateChecksum(ByteBuf data, int offset, int length) { - Crc32c crc32 = new Crc32c(); - try { - crc32.update(data, offset, length); - return maskChecksum(crc32.getValue()); - } finally { - crc32.reset(); - } - } - - /** - * Computes the CRC32C checksum of the supplied data, performs the "mask" operation - * on the computed checksum, and then compares the resulting masked checksum to the - * supplied checksum. - * - * @param expectedChecksum The checksum decoded from the stream to compare against - * @param data The input data to calculate the CRC32C checksum of - * @throws DecompressionException If the calculated and supplied checksums do not match - */ - static void validateChecksum(int expectedChecksum, ByteBuf data) { - validateChecksum(expectedChecksum, data, data.readerIndex(), data.readableBytes()); - } - - /** - * Computes the CRC32C checksum of the supplied data, performs the "mask" operation - * on the computed checksum, and then compares the resulting masked checksum to the - * supplied checksum. - * - * @param expectedChecksum The checksum decoded from the stream to compare against - * @param data The input data to calculate the CRC32C checksum of - * @throws DecompressionException If the calculated and supplied checksums do not match - */ - static void validateChecksum(int expectedChecksum, ByteBuf data, int offset, int length) { - final int actualChecksum = calculateChecksum(data, offset, length); - if (actualChecksum != expectedChecksum) { - throw new DecompressionException( - "mismatching checksum: " + Integer.toHexString(actualChecksum) + - " (expected: " + Integer.toHexString(expectedChecksum) + ')'); - } - } - - /** - * From the spec: - * - * "Checksums are not stored directly, but masked, as checksumming data and - * then its own checksum can be problematic. The masking is the same as used - * in Apache Hadoop: Rotate the checksum by 15 bits, then add the constant - * 0xa282ead8 (using wraparound as normal for unsigned integers)." - * - * @param checksum The actual checksum of the data - * @return The masked checksum - */ - static int maskChecksum(long checksum) { - return (int) ((checksum >> 15 | checksum << 17) + 0xa282ead8); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/SnappyFrameDecoder.java b/codec/src/main/java/io/netty/handler/codec/compression/SnappyFrameDecoder.java deleted file mode 100644 index fe345b2a36..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/SnappyFrameDecoder.java +++ /dev/null @@ -1,257 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.ByteToMessageDecoder; - -import static io.netty.handler.codec.compression.Snappy.validateChecksum; - -/** - * Uncompresses a {@link ByteBuf} encoded with the Snappy framing format. - * - * See Snappy framing format. - * - * Note that by default, validation of the checksum header in each chunk is - * DISABLED for performance improvements. If performance is less of an issue, - * or if you would prefer the safety that checksum validation brings, please - * use the {@link #SnappyFrameDecoder(boolean)} constructor with the argument - * set to {@code true}. - */ -public class SnappyFrameDecoder extends ByteToMessageDecoder { - - private enum ChunkType { - STREAM_IDENTIFIER, - COMPRESSED_DATA, - UNCOMPRESSED_DATA, - RESERVED_UNSKIPPABLE, - RESERVED_SKIPPABLE - } - - private static final int SNAPPY_IDENTIFIER_LEN = 6; - // See https://github.com/google/snappy/blob/1.1.9/framing_format.txt#L95 - private static final int MAX_UNCOMPRESSED_DATA_SIZE = 65536 + 4; - // See https://github.com/google/snappy/blob/1.1.9/framing_format.txt#L82 - private static final int MAX_DECOMPRESSED_DATA_SIZE = 65536; - // See https://github.com/google/snappy/blob/1.1.9/framing_format.txt#L82 - private static final int MAX_COMPRESSED_CHUNK_SIZE = 16777216 - 1; - - private final Snappy snappy = new Snappy(); - private final boolean validateChecksums; - - private boolean started; - private boolean corrupted; - private int numBytesToSkip; - - /** - * Creates a new snappy-framed decoder with validation of checksums - * turned OFF. To turn checksum validation on, please use the alternate - * {@link #SnappyFrameDecoder(boolean)} constructor. - */ - public SnappyFrameDecoder() { - this(false); - } - - /** - * Creates a new snappy-framed decoder with validation of checksums - * as specified. - * - * @param validateChecksums - * If true, the checksum field will be validated against the actual - * uncompressed data, and if the checksums do not match, a suitable - * {@link DecompressionException} will be thrown - */ - public SnappyFrameDecoder(boolean validateChecksums) { - this.validateChecksums = validateChecksums; - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - if (corrupted) { - in.skipBytes(in.readableBytes()); - return; - } - - if (numBytesToSkip != 0) { - // The last chunkType we detected was RESERVED_SKIPPABLE and we still have some bytes to skip. - int skipBytes = Math.min(numBytesToSkip, in.readableBytes()); - in.skipBytes(skipBytes); - numBytesToSkip -= skipBytes; - - // Let's return and try again. - return; - } - - try { - int idx = in.readerIndex(); - final int inSize = in.readableBytes(); - if (inSize < 4) { - // We need to be at least able to read the chunk type identifier (one byte), - // and the length of the chunk (3 bytes) in order to proceed - return; - } - - final int chunkTypeVal = in.getUnsignedByte(idx); - final ChunkType chunkType = mapChunkType((byte) chunkTypeVal); - final int chunkLength = in.getUnsignedMediumLE(idx + 1); - - switch (chunkType) { - case STREAM_IDENTIFIER: - if (chunkLength != SNAPPY_IDENTIFIER_LEN) { - throw new DecompressionException("Unexpected length of stream identifier: " + chunkLength); - } - - if (inSize < 4 + SNAPPY_IDENTIFIER_LEN) { - break; - } - - in.skipBytes(4); - int offset = in.readerIndex(); - in.skipBytes(SNAPPY_IDENTIFIER_LEN); - - checkByte(in.getByte(offset++), (byte) 's'); - checkByte(in.getByte(offset++), (byte) 'N'); - checkByte(in.getByte(offset++), (byte) 'a'); - checkByte(in.getByte(offset++), (byte) 'P'); - checkByte(in.getByte(offset++), (byte) 'p'); - checkByte(in.getByte(offset), (byte) 'Y'); - - started = true; - break; - case RESERVED_SKIPPABLE: - if (!started) { - throw new DecompressionException("Received RESERVED_SKIPPABLE tag before STREAM_IDENTIFIER"); - } - - in.skipBytes(4); - - int skipBytes = Math.min(chunkLength, in.readableBytes()); - in.skipBytes(skipBytes); - if (skipBytes != chunkLength) { - // We could skip all bytes, let's store the remaining so we can do so once we receive more - // data. - numBytesToSkip = chunkLength - skipBytes; - } - break; - case RESERVED_UNSKIPPABLE: - // The spec mandates that reserved unskippable chunks must immediately - // return an error, as we must assume that we cannot decode the stream - // correctly - throw new DecompressionException( - "Found reserved unskippable chunk type: 0x" + Integer.toHexString(chunkTypeVal)); - case UNCOMPRESSED_DATA: - if (!started) { - throw new DecompressionException("Received UNCOMPRESSED_DATA tag before STREAM_IDENTIFIER"); - } - if (chunkLength > MAX_UNCOMPRESSED_DATA_SIZE) { - throw new DecompressionException("Received UNCOMPRESSED_DATA larger than " + - MAX_UNCOMPRESSED_DATA_SIZE + " bytes"); - } - - if (inSize < 4 + chunkLength) { - return; - } - - in.skipBytes(4); - if (validateChecksums) { - int checksum = in.readIntLE(); - validateChecksum(checksum, in, in.readerIndex(), chunkLength - 4); - } else { - in.skipBytes(4); - } - ctx.fireChannelRead(in.readRetainedSlice(chunkLength - 4)); - break; - case COMPRESSED_DATA: - if (!started) { - throw new DecompressionException("Received COMPRESSED_DATA tag before STREAM_IDENTIFIER"); - } - - if (chunkLength > MAX_COMPRESSED_CHUNK_SIZE) { - throw new DecompressionException("Received COMPRESSED_DATA that contains" + - " chunk that exceeds " + MAX_COMPRESSED_CHUNK_SIZE + " bytes"); - } - - if (inSize < 4 + chunkLength) { - return; - } - - in.skipBytes(4); - int checksum = in.readIntLE(); - - int uncompressedSize = snappy.getPreamble(in); - if (uncompressedSize > MAX_DECOMPRESSED_DATA_SIZE) { - throw new DecompressionException("Received COMPRESSED_DATA that contains" + - " uncompressed data that exceeds " + MAX_DECOMPRESSED_DATA_SIZE + " bytes"); - } - - ByteBuf uncompressed = ctx.alloc().buffer(uncompressedSize, MAX_DECOMPRESSED_DATA_SIZE); - try { - if (validateChecksums) { - int oldWriterIndex = in.writerIndex(); - try { - in.writerIndex(in.readerIndex() + chunkLength - 4); - snappy.decode(in, uncompressed); - } finally { - in.writerIndex(oldWriterIndex); - } - validateChecksum(checksum, uncompressed, 0, uncompressed.writerIndex()); - } else { - snappy.decode(in.readSlice(chunkLength - 4), uncompressed); - } - ctx.fireChannelRead(uncompressed); - uncompressed = null; - } finally { - if (uncompressed != null) { - uncompressed.release(); - } - } - snappy.reset(); - break; - } - } catch (Exception e) { - corrupted = true; - throw e; - } - } - - private static void checkByte(byte actual, byte expect) { - if (actual != expect) { - throw new DecompressionException("Unexpected stream identifier contents. Mismatched snappy " + - "protocol version?"); - } - } - - /** - * Decodes the chunk type from the type tag byte. - * - * @param type The tag byte extracted from the stream - * @return The appropriate {@link ChunkType}, defaulting to {@link ChunkType#RESERVED_UNSKIPPABLE} - */ - private static ChunkType mapChunkType(byte type) { - if (type == 0) { - return ChunkType.COMPRESSED_DATA; - } else if (type == 1) { - return ChunkType.UNCOMPRESSED_DATA; - } else if (type == (byte) 0xff) { - return ChunkType.STREAM_IDENTIFIER; - } else if ((type & 0x80) == 0x80) { - return ChunkType.RESERVED_SKIPPABLE; - } else { - return ChunkType.RESERVED_UNSKIPPABLE; - } - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/SnappyFrameEncoder.java b/codec/src/main/java/io/netty/handler/codec/compression/SnappyFrameEncoder.java deleted file mode 100644 index 81ac24d17d..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/SnappyFrameEncoder.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToByteEncoder; - -import static io.netty.handler.codec.compression.Snappy.*; - -/** - * Compresses a {@link ByteBuf} using the Snappy framing format. - * - * See Snappy framing format. - */ -public class SnappyFrameEncoder extends MessageToByteEncoder { - /** - * The minimum amount that we'll consider actually attempting to compress. - * This value is preamble + the minimum length our Snappy service will - * compress (instead of just emitting a literal). - */ - private static final int MIN_COMPRESSIBLE_LENGTH = 18; - - /** - * All streams should start with the "Stream identifier", containing chunk - * type 0xff, a length field of 0x6, and 'sNaPpY' in ASCII. - */ - private static final byte[] STREAM_START = { - (byte) 0xff, 0x06, 0x00, 0x00, 0x73, 0x4e, 0x61, 0x50, 0x70, 0x59 - }; - - private final Snappy snappy = new Snappy(); - private boolean started; - - @Override - protected void encode(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) throws Exception { - if (!in.isReadable()) { - return; - } - - if (!started) { - started = true; - out.writeBytes(STREAM_START); - } - - int dataLength = in.readableBytes(); - if (dataLength > MIN_COMPRESSIBLE_LENGTH) { - for (;;) { - final int lengthIdx = out.writerIndex() + 1; - if (dataLength < MIN_COMPRESSIBLE_LENGTH) { - ByteBuf slice = in.readSlice(dataLength); - writeUnencodedChunk(slice, out, dataLength); - break; - } - - out.writeInt(0); - if (dataLength > Short.MAX_VALUE) { - ByteBuf slice = in.readSlice(Short.MAX_VALUE); - calculateAndWriteChecksum(slice, out); - snappy.encode(slice, out, Short.MAX_VALUE); - setChunkLength(out, lengthIdx); - dataLength -= Short.MAX_VALUE; - } else { - ByteBuf slice = in.readSlice(dataLength); - calculateAndWriteChecksum(slice, out); - snappy.encode(slice, out, dataLength); - setChunkLength(out, lengthIdx); - break; - } - } - } else { - writeUnencodedChunk(in, out, dataLength); - } - } - - private static void writeUnencodedChunk(ByteBuf in, ByteBuf out, int dataLength) { - out.writeByte(1); - writeChunkLength(out, dataLength + 4); - calculateAndWriteChecksum(in, out); - out.writeBytes(in, dataLength); - } - - private static void setChunkLength(ByteBuf out, int lengthIdx) { - int chunkLength = out.writerIndex() - lengthIdx - 3; - if (chunkLength >>> 24 != 0) { - throw new CompressionException("compressed data too large: " + chunkLength); - } - out.setMediumLE(lengthIdx, chunkLength); - } - - /** - * Writes the 2-byte chunk length to the output buffer. - * - * @param out The buffer to write to - * @param chunkLength The length to write - */ - private static void writeChunkLength(ByteBuf out, int chunkLength) { - out.writeMediumLE(chunkLength); - } - - /** - * Calculates and writes the 4-byte checksum to the output buffer - * - * @param slice The data to calculate the checksum for - * @param out The output buffer to write the checksum to - */ - private static void calculateAndWriteChecksum(ByteBuf slice, ByteBuf out) { - out.writeIntLE(calculateChecksum(slice)); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/SnappyFramedDecoder.java b/codec/src/main/java/io/netty/handler/codec/compression/SnappyFramedDecoder.java deleted file mode 100644 index 88a244f958..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/SnappyFramedDecoder.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.compression; - -/** - * @deprecated Use {@link SnappyFrameDecoder} instead. - */ -@Deprecated -public class SnappyFramedDecoder extends SnappyFrameDecoder { - // Nothing new. Just staying here for backward compatibility. -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/SnappyFramedEncoder.java b/codec/src/main/java/io/netty/handler/codec/compression/SnappyFramedEncoder.java deleted file mode 100644 index f755ff7918..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/SnappyFramedEncoder.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.compression; - -/** - * @deprecated Use {@link SnappyFrameEncoder} instead. - */ -@Deprecated -public class SnappyFramedEncoder extends SnappyFrameEncoder { - // Nothing new. Just staying here for backward compatibility. -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/StandardCompressionOptions.java b/codec/src/main/java/io/netty/handler/codec/compression/StandardCompressionOptions.java deleted file mode 100644 index 39926123c7..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/StandardCompressionOptions.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import com.aayushatharva.brotli4j.encoder.Encoder; - -/** - * Standard Compression Options for {@link BrotliOptions}, - * {@link GzipOptions} and {@link DeflateOptions} - */ -public final class StandardCompressionOptions { - - private StandardCompressionOptions() { - // Prevent outside initialization - } - - /** - * Default implementation of {@link BrotliOptions} with {@link Encoder.Parameters#setQuality(int)} set to 4 - * and {@link Encoder.Parameters#setMode(Encoder.Mode)} set to {@link Encoder.Mode#TEXT} - */ - public static BrotliOptions brotli() { - return BrotliOptions.DEFAULT; - } - - /** - * Create a new {@link BrotliOptions} - * - * @param parameters {@link Encoder.Parameters} Instance - * @throws NullPointerException If {@link Encoder.Parameters} is {@code null} - */ - public static BrotliOptions brotli(Encoder.Parameters parameters) { - return new BrotliOptions(parameters); - } - - /** - * Default implementation of {@link ZstdOptions} with{compressionLevel(int)} set to - * {@link ZstdConstants#DEFAULT_COMPRESSION_LEVEL},{@link ZstdConstants#DEFAULT_BLOCK_SIZE}, - * {@link ZstdConstants#MAX_BLOCK_SIZE} - */ - public static ZstdOptions zstd() { - return ZstdOptions.DEFAULT; - } - - /** - * Create a new {@link ZstdOptions} - * - * @param blockSize - * is used to calculate the compressionLevel - * @param maxEncodeSize - * specifies the size of the largest compressed object - * @param compressionLevel - * specifies the level of the compression - */ - public static ZstdOptions zstd(int compressionLevel, int blockSize, int maxEncodeSize) { - return new ZstdOptions(compressionLevel, blockSize, maxEncodeSize); - } - - /** - * Default implementation of {@link GzipOptions} with - * {@code compressionLevel()} set to 6, {@code windowBits()} set to 15 and {@code memLevel()} set to 8. - */ - public static GzipOptions gzip() { - return GzipOptions.DEFAULT; - } - - /** - * Create a new {@link GzipOptions} Instance - * - * @param compressionLevel {@code 1} yields the fastest compression and {@code 9} yields the - * best compression. {@code 0} means no compression. The default - * compression level is {@code 6}. - * - * @param windowBits The base two logarithm of the size of the history buffer. The - * value should be in the range {@code 9} to {@code 15} inclusive. - * Larger values result in better compression at the expense of - * memory usage. The default value is {@code 15}. - * - * @param memLevel How much memory should be allocated for the internal compression - * state. {@code 1} uses minimum memory and {@code 9} uses maximum - * memory. Larger values result in better and faster compression - * at the expense of memory usage. The default value is {@code 8} - */ - public static GzipOptions gzip(int compressionLevel, int windowBits, int memLevel) { - return new GzipOptions(compressionLevel, windowBits, memLevel); - } - - /** - * Default implementation of {@link DeflateOptions} with - * {@code compressionLevel} set to 6, {@code windowBits} set to 15 and {@code memLevel} set to 8. - */ - public static DeflateOptions deflate() { - return DeflateOptions.DEFAULT; - } - - /** - * Create a new {@link DeflateOptions} Instance - * - * @param compressionLevel {@code 1} yields the fastest compression and {@code 9} yields the - * best compression. {@code 0} means no compression. The default - * compression level is {@code 6}. - * - * @param windowBits The base two logarithm of the size of the history buffer. The - * value should be in the range {@code 9} to {@code 15} inclusive. - * Larger values result in better compression at the expense of - * memory usage. The default value is {@code 15}. - * - * @param memLevel How much memory should be allocated for the internal compression - * state. {@code 1} uses minimum memory and {@code 9} uses maximum - * memory. Larger values result in better and faster compression - * at the expense of memory usage. The default value is {@code 8} - */ - public static DeflateOptions deflate(int compressionLevel, int windowBits, int memLevel) { - return new DeflateOptions(compressionLevel, windowBits, memLevel); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/ZlibCodecFactory.java b/codec/src/main/java/io/netty/handler/codec/compression/ZlibCodecFactory.java deleted file mode 100644 index 49d9b65c67..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/ZlibCodecFactory.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -/** - * Creates a new {@link ZlibEncoder} and a new {@link ZlibDecoder}. - */ -public final class ZlibCodecFactory { - private static final boolean supportsWindowSizeAndMemLevel = true; - - /** - * Returns {@code true} if specify a custom window size and mem level is supported. - */ - public static boolean isSupportingWindowSizeAndMemLevel() { - return supportsWindowSizeAndMemLevel; - } - - public static ZlibEncoder newZlibEncoder(int compressionLevel) { - return new JdkZlibEncoder(compressionLevel); - } - - public static ZlibEncoder newZlibEncoder(ZlibWrapper wrapper) { - return new JdkZlibEncoder(wrapper); - } - - public static ZlibEncoder newZlibEncoder(ZlibWrapper wrapper, int compressionLevel) { - return new JdkZlibEncoder(wrapper, compressionLevel); - } - - public static ZlibEncoder newZlibEncoder(ZlibWrapper wrapper, int compressionLevel, int windowBits, int memLevel) { - return new JdkZlibEncoder(wrapper, compressionLevel); - } - - public static ZlibEncoder newZlibEncoder(byte[] dictionary) { - return new JdkZlibEncoder(dictionary); - } - - public static ZlibEncoder newZlibEncoder(int compressionLevel, byte[] dictionary) { - return new JdkZlibEncoder(compressionLevel, dictionary); - } - - public static ZlibEncoder newZlibEncoder(int compressionLevel, int windowBits, int memLevel, byte[] dictionary) { - return new JdkZlibEncoder(compressionLevel, dictionary); - } - - public static ZlibDecoder newZlibDecoder() { - return new JdkZlibDecoder(true); - } - - public static ZlibDecoder newZlibDecoder(ZlibWrapper wrapper) { - return new JdkZlibDecoder(wrapper, true); - } - - public static ZlibDecoder newZlibDecoder(byte[] dictionary) { - return new JdkZlibDecoder(dictionary); - } - - private ZlibCodecFactory() { - // Unused - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/ZlibDecoder.java b/codec/src/main/java/io/netty/handler/codec/compression/ZlibDecoder.java deleted file mode 100644 index 7f4f3eb618..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/ZlibDecoder.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.ByteToMessageDecoder; - -/** - * Decompresses a {@link ByteBuf} using the deflate algorithm. - */ -public abstract class ZlibDecoder extends ByteToMessageDecoder { - - /** - * Maximum allowed size of the decompression buffer. - */ - protected final int maxAllocation; - protected final boolean preferDirect = false; - - /** - * Same as {@link #ZlibDecoder(int)} with maxAllocation = 0. - */ - public ZlibDecoder() { - this(0); - } - - /** - * Construct a new ZlibDecoder. - * @param maxAllocation - * Maximum size of the decompression buffer. Must be >= 0. - * If zero, maximum size is decided by the {@link ByteBufAllocator}. - */ - public ZlibDecoder(int maxAllocation) { - this.maxAllocation = checkPositiveOrZero(maxAllocation, "maxAllocation"); - } - - /** - * Returns {@code true} if and only if the end of the compressed stream - * has been reached. - */ - public abstract boolean isClosed(); - - /** - * Allocate or expand the decompression buffer, without exceeding the maximum allocation. - * Calls {@link #decompressionBufferExhausted(ByteBuf)} if the buffer is full and cannot be expanded further. - */ - protected ByteBuf prepareDecompressBuffer(ChannelHandlerContext ctx, ByteBuf buffer, int preferredSize) { - if (buffer == null) { - if (maxAllocation == 0) { - return ctx.alloc().buffer(preferredSize); - } - - return ctx.alloc().buffer(Math.min(preferredSize, maxAllocation), maxAllocation); - } - - // this always expands the buffer if possible, even if the expansion is less than preferredSize - // we throw the exception only if the buffer could not be expanded at all - // this means that one final attempt to deserialize will always be made with the buffer at maxAllocation - if (buffer.ensureWritable(preferredSize, true) == 1) { - // buffer must be consumed so subclasses don't add it to output - // we therefore duplicate it when calling decompressionBufferExhausted() to guarantee non-interference - // but wait until after to consume it so the subclass can tell how much output is really in the buffer - decompressionBufferExhausted(buffer.duplicate()); - buffer.skipBytes(buffer.readableBytes()); - throw new DecompressionException("Decompression buffer has reached maximum size: " + buffer.maxCapacity()); - } - - return buffer; - } - - /** - * Called when the decompression buffer cannot be expanded further. - * Default implementation is a no-op, but subclasses can override in case they want to - * do something before the {@link DecompressionException} is thrown, such as log the - * data that was decompressed so far. - */ - protected void decompressionBufferExhausted(ByteBuf buffer) { - } - -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/ZlibEncoder.java b/codec/src/main/java/io/netty/handler/codec/compression/ZlibEncoder.java deleted file mode 100644 index 7752706ea6..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/ZlibEncoder.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.MessageToByteEncoder; -import io.netty.util.concurrent.Future; - -/** - * Compresses a {@link ByteBuf} using the deflate algorithm. - */ -public abstract class ZlibEncoder extends MessageToByteEncoder { - - protected ZlibEncoder() { - super(false); - } - - protected ZlibEncoder(boolean preferDirectBuffers) { - super(preferDirectBuffers); - } - - /** - * Returns {@code true} if and only if the end of the compressed stream - * has been reached. - */ - public abstract boolean isClosed(); - - /** - * Close this {@link ZlibEncoder} and so finish the encoding. - * - * The returned {@link Future} will be notified once the - * operation completes. - */ - public abstract Future close(); -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/ZlibWrapper.java b/codec/src/main/java/io/netty/handler/codec/compression/ZlibWrapper.java deleted file mode 100644 index bd64bef937..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/ZlibWrapper.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -/** - * The container file formats that wrap the stream compressed by the DEFLATE - * algorithm. - */ -public enum ZlibWrapper { - /** - * The ZLIB wrapper as specified in RFC 1950. - */ - ZLIB, - /** - * The GZIP wrapper as specified in RFC 1952. - */ - GZIP, - /** - * Raw DEFLATE stream only (no header and no footer). - */ - NONE, - /** - * Try {@link #ZLIB} first and then {@link #NONE} if the first attempt fails. - * Please note that you can specify this wrapper type only when decompressing. - */ - ZLIB_OR_NONE -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/Zstd.java b/codec/src/main/java/io/netty/handler/codec/compression/Zstd.java deleted file mode 100644 index 3d03524422..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/Zstd.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.compression; - -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -public final class Zstd { - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(Zstd.class); - private static final Throwable cause; - - static { - Throwable t = null; - - try { - Class.forName("com.github.luben.zstd.Zstd", false, - PlatformDependent.getClassLoader(Zstd.class)); - } catch (ClassNotFoundException e) { - t = e; - logger.debug( - "zstd-jni not in the classpath; Zstd support will be unavailable."); - } catch (Throwable e) { - t = e; - logger.debug("Failed to load zstd-jni; Zstd support will be unavailable.", t); - } - - cause = t; - } - - /** - * - * @return true when zstd-jni is in the classpath - * and native library is available on this platform and could be loaded - */ - public static boolean isAvailable() { - return cause == null; - } - - /** - * Throws when zstd support is missing from the classpath or is unavailable on this platform - * @throws Throwable a ClassNotFoundException if zstd-jni is missing - * or a ExceptionInInitializerError if zstd native lib can't be loaded - */ - public static void ensureAvailability() throws Throwable { - if (cause != null) { - throw cause; - } - } - - /** - * Returns {@link Throwable} of unavailability cause - */ - public static Throwable cause() { - return cause; - } - - private Zstd() { - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/ZstdConstants.java b/codec/src/main/java/io/netty/handler/codec/compression/ZstdConstants.java deleted file mode 100644 index 1588ba5348..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/ZstdConstants.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -final class ZstdConstants { - - /** - * Default compression level - */ - static final int DEFAULT_COMPRESSION_LEVEL = 3; - - /** - * Max compression level - */ - static final int MAX_COMPRESSION_LEVEL = 22; - - /** - * Max block size - */ - static final int MAX_BLOCK_SIZE = 1 << (DEFAULT_COMPRESSION_LEVEL + 7) + 0x0F; // 32 M - /** - * Default block size - */ - static final int DEFAULT_BLOCK_SIZE = 1 << 16; // 64 KB - - private ZstdConstants() { } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/ZstdEncoder.java b/codec/src/main/java/io/netty/handler/codec/compression/ZstdEncoder.java deleted file mode 100644 index 45badd2c75..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/ZstdEncoder.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import com.github.luben.zstd.Zstd; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.EncoderException; -import io.netty.handler.codec.MessageToByteEncoder; -import io.netty.util.internal.ObjectUtil; -import java.nio.ByteBuffer; - -import static io.netty.handler.codec.compression.ZstdConstants.DEFAULT_COMPRESSION_LEVEL; -import static io.netty.handler.codec.compression.ZstdConstants.DEFAULT_BLOCK_SIZE; -import static io.netty.handler.codec.compression.ZstdConstants.MAX_BLOCK_SIZE; -import static io.netty.handler.codec.compression.ZstdConstants.MAX_COMPRESSION_LEVEL; - -/** - * Compresses a {@link ByteBuf} using the Zstandard algorithm. - * See Zstandard. - */ -public final class ZstdEncoder extends MessageToByteEncoder { - - private final int blockSize; - private final int compressionLevel; - private final int maxEncodeSize; - private ByteBuf buffer; - - /** - * Creates a new Zstd encoder. - * - * Please note that if you use the default constructor, the default BLOCK_SIZE and MAX_BLOCK_SIZE - * will be used. If you want to specify BLOCK_SIZE and MAX_BLOCK_SIZE yourself, - * please use {@link ZstdEncoder(int,int)} constructor - */ - public ZstdEncoder() { - this(DEFAULT_COMPRESSION_LEVEL, DEFAULT_BLOCK_SIZE, MAX_BLOCK_SIZE); - } - - /** - * Creates a new Zstd encoder. - * @param compressionLevel - * specifies the level of the compression - */ - public ZstdEncoder(int compressionLevel) { - this(compressionLevel, DEFAULT_BLOCK_SIZE, MAX_BLOCK_SIZE); - } - - /** - * Creates a new Zstd encoder. - * @param blockSize - * is used to calculate the compressionLevel - * @param maxEncodeSize - * specifies the size of the largest compressed object - */ - public ZstdEncoder(int blockSize, int maxEncodeSize) { - this(DEFAULT_COMPRESSION_LEVEL, blockSize, maxEncodeSize); - } - - /** - * @param blockSize - * is used to calculate the compressionLevel - * @param maxEncodeSize - * specifies the size of the largest compressed object - * @param compressionLevel - * specifies the level of the compression - */ - public ZstdEncoder(int compressionLevel, int blockSize, int maxEncodeSize) { - super(true); - this.compressionLevel = ObjectUtil.checkInRange(compressionLevel, 0, MAX_COMPRESSION_LEVEL, "compressionLevel"); - this.blockSize = ObjectUtil.checkPositive(blockSize, "blockSize"); - this.maxEncodeSize = ObjectUtil.checkPositive(maxEncodeSize, "maxEncodeSize"); - } - - @Override - protected ByteBuf allocateBuffer(ChannelHandlerContext ctx, ByteBuf msg, boolean preferDirect) { - if (buffer == null) { - throw new IllegalStateException("not added to a pipeline," + - "or has been removed,buffer is null"); - } - - int remaining = msg.readableBytes() + buffer.readableBytes(); - - // quick overflow check - if (remaining < 0) { - throw new EncoderException("too much data to allocate a buffer for compression"); - } - - long bufferSize = 0; - while (remaining > 0) { - int curSize = Math.min(blockSize, remaining); - remaining -= curSize; - bufferSize += Zstd.compressBound(curSize); - } - - if (bufferSize > maxEncodeSize || 0 > bufferSize) { - throw new EncoderException("requested encode buffer size (" + bufferSize + " bytes) exceeds " + - "the maximum allowable size (" + maxEncodeSize + " bytes)"); - } - - return ctx.alloc().directBuffer((int) bufferSize); - } - - @Override - protected void encode(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) { - if (buffer == null) { - throw new IllegalStateException("not added to a pipeline," + - "or has been removed,buffer is null"); - } - - final ByteBuf buffer = this.buffer; - int length; - while ((length = in.readableBytes()) > 0) { - final int nextChunkSize = Math.min(length, buffer.writableBytes()); - in.readBytes(buffer, nextChunkSize); - - if (!buffer.isWritable()) { - flushBufferedData(out); - } - } - } - - private void flushBufferedData(ByteBuf out) { - final int flushableBytes = buffer.readableBytes(); - if (flushableBytes == 0) { - return; - } - - final int bufSize = (int) Zstd.compressBound(flushableBytes); - out.ensureWritable(bufSize); - final int idx = out.writerIndex(); - int compressedLength; - try { - ByteBuffer outNioBuffer = out.internalNioBuffer(idx, out.writableBytes()); - compressedLength = Zstd.compress( - outNioBuffer, - buffer.internalNioBuffer(buffer.readerIndex(), flushableBytes), - compressionLevel); - } catch (Exception e) { - throw new CompressionException(e); - } - - out.writerIndex(idx + compressedLength); - buffer.clear(); - } - - @Override - public void flush(final ChannelHandlerContext ctx) { - if (buffer != null && buffer.isReadable()) { - final ByteBuf buf = allocateBuffer(ctx, Unpooled.EMPTY_BUFFER, isPreferDirect()); - flushBufferedData(buf); - ctx.write(buf); - } - ctx.flush(); - } - - @Override - public void handlerAdded(ChannelHandlerContext ctx) { - buffer = ctx.alloc().directBuffer(blockSize); - buffer.clear(); - } - - @Override - public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { - super.handlerRemoved(ctx); - if (buffer != null) { - buffer.release(); - buffer = null; - } - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/ZstdOptions.java b/codec/src/main/java/io/netty/handler/codec/compression/ZstdOptions.java deleted file mode 100644 index 110a90fb39..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/ZstdOptions.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.util.internal.ObjectUtil; - -import static io.netty.handler.codec.compression.ZstdConstants.DEFAULT_COMPRESSION_LEVEL; -import static io.netty.handler.codec.compression.ZstdConstants.MAX_COMPRESSION_LEVEL; -import static io.netty.handler.codec.compression.ZstdConstants.DEFAULT_BLOCK_SIZE; -import static io.netty.handler.codec.compression.ZstdConstants.MAX_BLOCK_SIZE; - -/** - * {@link ZstdOptions} holds compressionLevel for - * Zstd compression. - */ -public class ZstdOptions implements CompressionOptions { - - private final int blockSize; - private final int compressionLevel; - private final int maxEncodeSize; - - /** - * Default implementation of {@link ZstdOptions} with{compressionLevel(int)} set to - * {@link ZstdConstants#DEFAULT_COMPRESSION_LEVEL},{@link ZstdConstants#DEFAULT_BLOCK_SIZE}, - * {@link ZstdConstants#MAX_BLOCK_SIZE} - */ - static final ZstdOptions DEFAULT = new ZstdOptions(DEFAULT_COMPRESSION_LEVEL, DEFAULT_BLOCK_SIZE, MAX_BLOCK_SIZE); - - /** - * Create a new {@link ZstdOptions} - * - * @param blockSize - * is used to calculate the compressionLevel - * @param maxEncodeSize - * specifies the size of the largest compressed object - * @param compressionLevel - * specifies the level of the compression - */ - ZstdOptions(int compressionLevel, int blockSize, int maxEncodeSize) { - if (!Zstd.isAvailable()) { - throw new IllegalStateException("zstd-jni is not available", Zstd.cause()); - } - - this.compressionLevel = ObjectUtil.checkInRange(compressionLevel, 0, MAX_COMPRESSION_LEVEL, "compressionLevel"); - this.blockSize = ObjectUtil.checkPositive(blockSize, "blockSize"); - this.maxEncodeSize = ObjectUtil.checkPositive(maxEncodeSize, "maxEncodeSize"); - } - - public int compressionLevel() { - return compressionLevel; - } - - public int blockSize() { - return blockSize; - } - - public int maxEncodeSize() { - return maxEncodeSize; - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/package-info.java b/codec/src/main/java/io/netty/handler/codec/compression/package-info.java deleted file mode 100644 index 9399aecad5..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/compression/package-info.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * Encoder and decoder which compresses and decompresses {@link io.netty.buffer.ByteBuf}s - * in a compression format such as zlib, - * gzip, and - * Snappy. - */ -package io.netty.handler.codec.compression; 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 deleted file mode 100644 index be1459d377..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/json/JsonObjectDecoder.java +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.json; - -import static io.netty.util.internal.ObjectUtil.checkPositive; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.ByteToMessageDecoder; -import io.netty.channel.ChannelHandler; -import io.netty.handler.codec.CorruptedFrameException; -import io.netty.handler.codec.TooLongFrameException; -import io.netty.channel.ChannelPipeline; - -/** - * 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. - */ -public class JsonObjectDecoder extends ByteToMessageDecoder { - - private static final int ST_CORRUPTED = -1; - private static final int ST_INIT = 0; - private static final int ST_DECODING_NORMAL = 1; - private static final int ST_DECODING_ARRAY_STREAM = 2; - - private int openBraces; - private int idx; - - private int lastReaderIndex; - - private int state; - private boolean insideString; - - private final int maxObjectLength; - private final boolean streamArrayElements; - - public JsonObjectDecoder() { - // 1 MB - this(1024 * 1024); - } - - public JsonObjectDecoder(int maxObjectLength) { - this(maxObjectLength, false); - } - - public JsonObjectDecoder(boolean streamArrayElements) { - this(1024 * 1024, streamArrayElements); - } - - /** - * @param maxObjectLength maximum number of bytes a JSON object/array may use (including braces and all). - * Objects exceeding this length are dropped and an {@link TooLongFrameException} - * is thrown. - * @param streamArrayElements if set to true and the "top level" JSON object is an array, each of its entries - * is passed through the pipeline individually and immediately after it was fully - * received, allowing for arrays with "infinitely" many elements. - * - */ - public JsonObjectDecoder(int maxObjectLength, boolean streamArrayElements) { - this.maxObjectLength = checkPositive(maxObjectLength, "maxObjectLength"); - this.streamArrayElements = streamArrayElements; - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - if (state == ST_CORRUPTED) { - in.skipBytes(in.readableBytes()); - return; - } - - if (this.idx > in.readerIndex() && lastReaderIndex != in.readerIndex()) { - this.idx = in.readerIndex() + (idx - lastReaderIndex); - } - - // index of next byte to process. - int idx = this.idx; - int wrtIdx = in.writerIndex(); - - if (wrtIdx > maxObjectLength) { - // buffer size exceeded maxObjectLength; discarding the complete buffer. - in.skipBytes(in.readableBytes()); - reset(); - throw new TooLongFrameException( - "object length exceeds " + maxObjectLength + ": " + wrtIdx + " bytes discarded"); - } - - for (/* use current idx */; idx < wrtIdx; idx++) { - byte c = in.getByte(idx); - if (state == ST_DECODING_NORMAL) { - decodeByte(c, in, idx); - - // All opening braces/brackets have been closed. That's enough to conclude - // that the JSON object/array is complete. - if (openBraces == 0) { - ByteBuf json = extractObject(ctx, in, in.readerIndex(), idx + 1 - in.readerIndex()); - if (json != null) { - ctx.fireChannelRead(json); - } - - // The JSON object/array was extracted => discard the bytes from - // the input buffer. - in.readerIndex(idx + 1); - // Reset the object state to get ready for the next JSON object/text - // coming along the byte stream. - reset(); - } - } else if (state == ST_DECODING_ARRAY_STREAM) { - decodeByte(c, in, idx); - - if (!insideString && (openBraces == 1 && c == ',' || openBraces == 0 && c == ']')) { - // skip leading spaces. No range check is needed and the loop will terminate - // because the byte at position idx is not a whitespace. - for (int i = in.readerIndex(); Character.isWhitespace(in.getByte(i)); i++) { - in.skipBytes(1); - } - - // skip trailing spaces. - int idxNoSpaces = idx - 1; - while (idxNoSpaces >= in.readerIndex() && Character.isWhitespace(in.getByte(idxNoSpaces))) { - idxNoSpaces--; - } - - ByteBuf json = extractObject(ctx, in, in.readerIndex(), idxNoSpaces + 1 - in.readerIndex()); - if (json != null) { - ctx.fireChannelRead(json); - } - - in.readerIndex(idx + 1); - - if (c == ']') { - reset(); - } - } - // JSON object/array detected. Accumulate bytes until all braces/brackets are closed. - } else if (c == '{' || c == '[') { - initDecoding(c); - - if (state == ST_DECODING_ARRAY_STREAM) { - // Discard the array bracket - in.skipBytes(1); - } - // Discard leading spaces in front of a JSON object/array. - } else if (Character.isWhitespace(c)) { - in.skipBytes(1); - } else { - state = ST_CORRUPTED; - throw new CorruptedFrameException( - "invalid JSON received at byte position " + idx + ": " + ByteBufUtil.hexDump(in)); - } - } - - if (in.readableBytes() == 0) { - this.idx = 0; - } else { - this.idx = idx; - } - this.lastReaderIndex = in.readerIndex(); - } - - /** - * Override this method if you want to filter the json objects/arrays that get passed through the pipeline. - */ - @SuppressWarnings("UnusedParameters") - protected ByteBuf extractObject(ChannelHandlerContext ctx, ByteBuf buffer, int index, int length) { - return buffer.retainedSlice(index, length); - } - - private void decodeByte(byte c, ByteBuf in, int idx) { - if ((c == '{' || c == '[') && !insideString) { - openBraces++; - } else if ((c == '}' || c == ']') && !insideString) { - openBraces--; - } else if (c == '"') { - // start of a new JSON string. It's necessary to detect strings as they may - // also contain braces/brackets and that could lead to incorrect results. - if (!insideString) { - insideString = true; - } else { - int backslashCount = 0; - idx--; - while (idx >= 0) { - if (in.getByte(idx) == '\\') { - backslashCount++; - idx--; - } else { - break; - } - } - // The double quote isn't escaped only if there are even "\"s. - if (backslashCount % 2 == 0) { - // Since the double quote isn't escaped then this is the end of a string. - insideString = false; - } - } - } - } - - private void initDecoding(byte openingBrace) { - openBraces = 1; - if (openingBrace == '[' && streamArrayElements) { - state = ST_DECODING_ARRAY_STREAM; - } else { - state = ST_DECODING_NORMAL; - } - } - - private void reset() { - insideString = false; - state = ST_INIT; - openBraces = 0; - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/json/package-info.java b/codec/src/main/java/io/netty/handler/codec/json/package-info.java deleted file mode 100644 index 6c2252ae7e..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/json/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * JSON specific codecs. - */ -package io.netty.handler.codec.json; diff --git a/codec/src/main/java/io/netty/handler/codec/marshalling/ChannelBufferByteInput.java b/codec/src/main/java/io/netty/handler/codec/marshalling/ChannelBufferByteInput.java deleted file mode 100644 index fae178af70..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/marshalling/ChannelBufferByteInput.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.marshalling; - -import io.netty.buffer.ByteBuf; -import org.jboss.marshalling.ByteInput; - -import java.io.IOException; - -/** - * {@link ByteInput} implementation which reads its data from a {@link ByteBuf} - */ -class ChannelBufferByteInput implements ByteInput { - - private final ByteBuf buffer; - - ChannelBufferByteInput(ByteBuf buffer) { - this.buffer = buffer; - } - - @Override - public void close() throws IOException { - // nothing to do - } - - @Override - public int available() throws IOException { - return buffer.readableBytes(); - } - - @Override - public int read() throws IOException { - if (buffer.isReadable()) { - return buffer.readByte() & 0xff; - } - return -1; - } - - @Override - public int read(byte[] array) throws IOException { - return read(array, 0, array.length); - } - - @Override - public int read(byte[] dst, int dstIndex, int length) throws IOException { - int available = available(); - if (available == 0) { - return -1; - } - - length = Math.min(available, length); - buffer.readBytes(dst, dstIndex, length); - return length; - } - - @Override - public long skip(long bytes) throws IOException { - int readable = buffer.readableBytes(); - if (readable < bytes) { - bytes = readable; - } - buffer.readerIndex((int) (buffer.readerIndex() + bytes)); - return bytes; - } - -} diff --git a/codec/src/main/java/io/netty/handler/codec/marshalling/ChannelBufferByteOutput.java b/codec/src/main/java/io/netty/handler/codec/marshalling/ChannelBufferByteOutput.java deleted file mode 100644 index 00b670406d..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/marshalling/ChannelBufferByteOutput.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.marshalling; - -import io.netty.buffer.ByteBuf; -import org.jboss.marshalling.ByteOutput; - -import java.io.IOException; - -/** - * {@link ByteOutput} implementation which writes the data to a {@link ByteBuf} - * - * - */ -class ChannelBufferByteOutput implements ByteOutput { - - private final ByteBuf buffer; - - /** - * Create a new instance which use the given {@link ByteBuf} - */ - ChannelBufferByteOutput(ByteBuf buffer) { - this.buffer = buffer; - } - - @Override - public void close() throws IOException { - // Nothing to do - } - - @Override - public void flush() throws IOException { - // nothing to do - } - - @Override - public void write(int b) throws IOException { - buffer.writeByte(b); - } - - @Override - public void write(byte[] bytes) throws IOException { - buffer.writeBytes(bytes); - } - - @Override - public void write(byte[] bytes, int srcIndex, int length) throws IOException { - buffer.writeBytes(bytes, srcIndex, length); - } - - /** - * Return the {@link ByteBuf} which contains the written content - * - */ - ByteBuf getBuffer() { - return buffer; - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/marshalling/CompatibleMarshallingDecoder.java b/codec/src/main/java/io/netty/handler/codec/marshalling/CompatibleMarshallingDecoder.java deleted file mode 100644 index b9864c10e7..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/marshalling/CompatibleMarshallingDecoder.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.marshalling; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.ReplayingDecoder; -import io.netty.handler.codec.TooLongFrameException; -import org.jboss.marshalling.ByteInput; -import org.jboss.marshalling.Unmarshaller; - -import java.io.ObjectStreamConstants; - -/** - * {@link ReplayingDecoder} which use an {@link Unmarshaller} to read the Object out of the {@link ByteBuf}. - * - * If you can you should use {@link MarshallingDecoder}. - */ -public class CompatibleMarshallingDecoder extends ReplayingDecoder { - protected final UnmarshallerProvider provider; - protected final int maxObjectSize; - private boolean discardingTooLongFrame; - - /** - * Create a new instance of {@link CompatibleMarshallingDecoder}. - * - * @param provider - * the {@link UnmarshallerProvider} which is used to obtain the {@link Unmarshaller} - * for the {@link Channel} - * @param maxObjectSize - * the maximal size (in bytes) of the {@link Object} to unmarshal. Once the size is - * exceeded the {@link Channel} will get closed. Use a maxObjectSize of - * {@link Integer#MAX_VALUE} to disable this. You should only do this if you are sure - * that the received Objects will never be big and the sending side are trusted, as this - * opens the possibility for a DOS-Attack due an {@link OutOfMemoryError}. - */ - public CompatibleMarshallingDecoder(UnmarshallerProvider provider, int maxObjectSize) { - this.provider = provider; - this.maxObjectSize = maxObjectSize; - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception { - if (discardingTooLongFrame) { - buffer.skipBytes(actualReadableBytes()); - checkpoint(); - return; - } - - // Call close in a finally block as the ReplayingDecoder will throw an Error if not enough bytes are - // readable. This helps to be sure that we do not leak resource - try (Unmarshaller unmarshaller = provider.getUnmarshaller(ctx)) { - ByteInput input = new ChannelBufferByteInput(buffer); - if (maxObjectSize != Integer.MAX_VALUE) { - input = new LimitingByteInput(input, maxObjectSize); - } - unmarshaller.start(input); - Object obj = unmarshaller.readObject(); - unmarshaller.finish(); - ctx.fireChannelRead(obj); - } catch (LimitingByteInput.TooBigObjectException ignored) { - discardingTooLongFrame = true; - throw new TooLongFrameException(); - } - } - - @Override - protected void decodeLast(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception { - switch (buffer.readableBytes()) { - case 0: - return; - case 1: - // Ignore the last TC_RESET - if (buffer.getByte(buffer.readerIndex()) == ObjectStreamConstants.TC_RESET) { - buffer.skipBytes(1); - return; - } - } - - decode(ctx, buffer); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - if (cause instanceof TooLongFrameException) { - ctx.close(); - } else { - super.exceptionCaught(ctx, cause); - } - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/marshalling/CompatibleMarshallingEncoder.java b/codec/src/main/java/io/netty/handler/codec/marshalling/CompatibleMarshallingEncoder.java deleted file mode 100644 index 4bc1701d31..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/marshalling/CompatibleMarshallingEncoder.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.marshalling; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToByteEncoder; - -import org.jboss.marshalling.Marshaller; - -/** - * {@link MessageToByteEncoder} implementation which uses JBoss Marshalling to marshal - * an Object. - * - * See JBoss Marshalling website - * for more information - * - * Use {@link MarshallingEncoder} if possible. - * - */ -@Sharable -public class CompatibleMarshallingEncoder extends MessageToByteEncoder { - - private final MarshallerProvider provider; - - /** - * Create a new instance of the {@link CompatibleMarshallingEncoder} - * - * @param provider the {@link MarshallerProvider} to use to get the {@link Marshaller} for a {@link Channel} - */ - public CompatibleMarshallingEncoder(MarshallerProvider provider) { - this.provider = provider; - } - - @Override - protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception { - Marshaller marshaller = provider.getMarshaller(ctx); - marshaller.start(new ChannelBufferByteOutput(out)); - marshaller.writeObject(msg); - marshaller.finish(); - marshaller.close(); - } - -} diff --git a/codec/src/main/java/io/netty/handler/codec/marshalling/ContextBoundUnmarshallerProvider.java b/codec/src/main/java/io/netty/handler/codec/marshalling/ContextBoundUnmarshallerProvider.java deleted file mode 100644 index 6cee887732..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/marshalling/ContextBoundUnmarshallerProvider.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.marshalling; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.Attribute; -import io.netty.util.AttributeKey; -import org.jboss.marshalling.MarshallerFactory; -import org.jboss.marshalling.MarshallingConfiguration; -import org.jboss.marshalling.Unmarshaller; - -/** - * {@link UnmarshallerProvider} which store a reference to the {@link Unmarshaller} in the - * {@link ChannelHandlerContext} via the {@link ChannelHandlerContext#attr(AttributeKey)} - * method. So the same {@link Unmarshaller} will be used during the life-time of a {@link Channel} - * for the {@link ChannelHandler}'s {@link ChannelHandlerContext}. - * - * - */ -public class ContextBoundUnmarshallerProvider extends DefaultUnmarshallerProvider { - - private static final AttributeKey UNMARSHALLER = AttributeKey.valueOf( - ContextBoundUnmarshallerProvider.class, "UNMARSHALLER"); - - public ContextBoundUnmarshallerProvider(MarshallerFactory factory, MarshallingConfiguration config) { - super(factory, config); - } - - @Override - public Unmarshaller getUnmarshaller(ChannelHandlerContext ctx) throws Exception { - Attribute attr = ctx.channel().attr(UNMARSHALLER); - Unmarshaller unmarshaller = attr.get(); - if (unmarshaller == null) { - unmarshaller = super.getUnmarshaller(ctx); - attr.set(unmarshaller); - } - return unmarshaller; - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/marshalling/DefaultMarshallerProvider.java b/codec/src/main/java/io/netty/handler/codec/marshalling/DefaultMarshallerProvider.java deleted file mode 100644 index 02d28efe97..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/marshalling/DefaultMarshallerProvider.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.marshalling; - -import io.netty.channel.ChannelHandlerContext; - -import org.jboss.marshalling.Marshaller; -import org.jboss.marshalling.MarshallerFactory; -import org.jboss.marshalling.MarshallingConfiguration; - -/** - * Default implementation of {@link MarshallerProvider} which just create a new {@link Marshaller} - * on ever {@link #getMarshaller(ChannelHandlerContext)} call. - */ -public class DefaultMarshallerProvider implements MarshallerProvider { - - private final MarshallerFactory factory; - private final MarshallingConfiguration config; - - /** - * Create a new instance - * - * @param factory the {@link MarshallerFactory} to use to create {@link Marshaller} - * @param config the {@link MarshallingConfiguration} - */ - public DefaultMarshallerProvider(MarshallerFactory factory, MarshallingConfiguration config) { - this.factory = factory; - this.config = config; - } - - @Override - public Marshaller getMarshaller(ChannelHandlerContext ctx) throws Exception { - return factory.createMarshaller(config); - } - -} diff --git a/codec/src/main/java/io/netty/handler/codec/marshalling/DefaultUnmarshallerProvider.java b/codec/src/main/java/io/netty/handler/codec/marshalling/DefaultUnmarshallerProvider.java deleted file mode 100644 index 57fa2dbda2..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/marshalling/DefaultUnmarshallerProvider.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.marshalling; - -import io.netty.channel.ChannelHandlerContext; - -import org.jboss.marshalling.MarshallerFactory; -import org.jboss.marshalling.MarshallingConfiguration; -import org.jboss.marshalling.Unmarshaller; - -/** - * Default implementation of {@link UnmarshallerProvider} which will just create a new {@link Unmarshaller} - * on every call to {@link #getUnmarshaller(ChannelHandlerContext)} - * - */ -public class DefaultUnmarshallerProvider implements UnmarshallerProvider { - - private final MarshallerFactory factory; - private final MarshallingConfiguration config; - - /** - * Create a new instance of {@link DefaultMarshallerProvider} - * - * @param factory the {@link MarshallerFactory} to use to create {@link Unmarshaller} - * @param config the {@link MarshallingConfiguration} - */ - public DefaultUnmarshallerProvider(MarshallerFactory factory, MarshallingConfiguration config) { - this.factory = factory; - this.config = config; - } - - @Override - public Unmarshaller getUnmarshaller(ChannelHandlerContext ctx) throws Exception { - return factory.createUnmarshaller(config); - } - -} diff --git a/codec/src/main/java/io/netty/handler/codec/marshalling/LimitingByteInput.java b/codec/src/main/java/io/netty/handler/codec/marshalling/LimitingByteInput.java deleted file mode 100644 index 46a3236e73..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/marshalling/LimitingByteInput.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.marshalling; - -import static io.netty.util.internal.ObjectUtil.checkPositive; - -import org.jboss.marshalling.ByteInput; - -import java.io.IOException; - -/** - * {@link ByteInput} implementation which wraps another {@link ByteInput} and throws a {@link TooBigObjectException} - * if the read limit was reached. - */ -class LimitingByteInput implements ByteInput { - - // Use a static instance here to remove the overhead of fillStacktrace - private static final TooBigObjectException EXCEPTION = new TooBigObjectException(); - - private final ByteInput input; - private final long limit; - private long read; - - LimitingByteInput(ByteInput input, long limit) { - this.input = input; - this.limit = checkPositive(limit, "limit"); - } - - @Override - public void close() throws IOException { - // Nothing to do - } - - @Override - public int available() throws IOException { - return readable(input.available()); - } - - @Override - public int read() throws IOException { - int readable = readable(1); - if (readable > 0) { - int b = input.read(); - read++; - return b; - } else { - throw EXCEPTION; - } - } - - @Override - public int read(byte[] array) throws IOException { - return read(array, 0, array.length); - } - - @Override - public int read(byte[] array, int offset, int length) throws IOException { - int readable = readable(length); - if (readable > 0) { - int i = input.read(array, offset, readable); - read += i; - return i; - } else { - throw EXCEPTION; - } - } - - @Override - public long skip(long bytes) throws IOException { - int readable = readable((int) bytes); - if (readable > 0) { - long i = input.skip(readable); - read += i; - return i; - } else { - throw EXCEPTION; - } - } - - private int readable(int length) { - return (int) Math.min(length, limit - read); - } - - /** - * Exception that will get thrown if the {@link Object} is too big to unmarshall - * - */ - static final class TooBigObjectException extends IOException { - private static final long serialVersionUID = 1L; - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/marshalling/MarshallerProvider.java b/codec/src/main/java/io/netty/handler/codec/marshalling/MarshallerProvider.java deleted file mode 100644 index 918c4613cc..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/marshalling/MarshallerProvider.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.marshalling; - -import io.netty.channel.ChannelHandlerContext; - -import org.jboss.marshalling.Marshaller; - -/** - * This provider is responsible to get a {@link Marshaller} for the given {@link ChannelHandlerContext}. - */ -public interface MarshallerProvider { - - /** - * Get a {@link Marshaller} for the given {@link ChannelHandlerContext} - */ - Marshaller getMarshaller(ChannelHandlerContext ctx) throws Exception; -} diff --git a/codec/src/main/java/io/netty/handler/codec/marshalling/MarshallingDecoder.java b/codec/src/main/java/io/netty/handler/codec/marshalling/MarshallingDecoder.java deleted file mode 100644 index d0f611ad10..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/marshalling/MarshallingDecoder.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.marshalling; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.LengthFieldBasedFrameDecoder; -import io.netty.handler.codec.TooLongFrameException; -import org.jboss.marshalling.ByteInput; -import org.jboss.marshalling.Unmarshaller; - -import java.io.StreamCorruptedException; -/** - * Decoder which MUST be used with {@link MarshallingEncoder}. - * - * A {@link LengthFieldBasedFrameDecoder} which use an {@link Unmarshaller} to read the Object out - * of the {@link ByteBuf}. - * - */ -public class MarshallingDecoder extends LengthFieldBasedFrameDecoder { - - private final UnmarshallerProvider provider; - - /** - * Creates a new decoder whose maximum object size is {@code 1048576} - * bytes. If the size of the received object is greater than - * {@code 1048576} bytes, a {@link StreamCorruptedException} will be - * raised. - * - */ - public MarshallingDecoder(UnmarshallerProvider provider) { - this(provider, 1048576); - } - - /** - * Creates a new decoder with the specified maximum object size. - * - * @param maxObjectSize the maximum byte length of the serialized object. - * if the length of the received object is greater - * than this value, {@link TooLongFrameException} - * will be raised. - */ - public MarshallingDecoder(UnmarshallerProvider provider, int maxObjectSize) { - super(maxObjectSize, 0, 4, 0, 4); - this.provider = provider; - } - - @Override - protected Object decode0(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - ByteBuf frame = (ByteBuf) super.decode0(ctx, in); - if (frame == null) { - return null; - } - - // Call close in a finally block as the ReplayingDecoder will throw an Error if not enough bytes are - // readable. This helps to be sure that we do not leak resource - try (Unmarshaller unmarshaller = provider.getUnmarshaller(ctx)) { - ByteInput input = new ChannelBufferByteInput(frame); - unmarshaller.start(input); - Object obj = unmarshaller.readObject(); - unmarshaller.finish(); - return obj; - } - } - - @Override - protected ByteBuf extractFrame(ChannelHandlerContext ctx, ByteBuf buffer, int index, int length) { - return buffer.slice(index, length); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/marshalling/MarshallingEncoder.java b/codec/src/main/java/io/netty/handler/codec/marshalling/MarshallingEncoder.java deleted file mode 100644 index 3fac6d54f2..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/marshalling/MarshallingEncoder.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.marshalling; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToByteEncoder; - -import org.jboss.marshalling.Marshaller; - -/** - * {@link MessageToByteEncoder} implementation which uses JBoss Marshalling to marshal - * an Object. Be aware that this encoder is not compatible with an other client that just use - * JBoss Marshalling as it includes the size of every {@link Object} that gets serialized in - * front of the {@link Object} itself. - * - * Use this with {@link MarshallingDecoder} - * - * See JBoss Marshalling website - * for more information - * - */ -@Sharable -public class MarshallingEncoder extends MessageToByteEncoder { - - private static final byte[] LENGTH_PLACEHOLDER = new byte[4]; - private final MarshallerProvider provider; - - /** - * Creates a new encoder. - * - * @param provider the {@link MarshallerProvider} to use - */ - public MarshallingEncoder(MarshallerProvider provider) { - this.provider = provider; - } - - @Override - protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception { - Marshaller marshaller = provider.getMarshaller(ctx); - int lengthPos = out.writerIndex(); - out.writeBytes(LENGTH_PLACEHOLDER); - ChannelBufferByteOutput output = new ChannelBufferByteOutput(out); - marshaller.start(output); - marshaller.writeObject(msg); - marshaller.finish(); - marshaller.close(); - - out.setInt(lengthPos, out.writerIndex() - lengthPos - 4); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/marshalling/ThreadLocalMarshallerProvider.java b/codec/src/main/java/io/netty/handler/codec/marshalling/ThreadLocalMarshallerProvider.java deleted file mode 100644 index 89c726f864..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/marshalling/ThreadLocalMarshallerProvider.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.marshalling; - -import io.netty.channel.ChannelHandlerContext; - -import io.netty.util.concurrent.FastThreadLocal; -import org.jboss.marshalling.Marshaller; -import org.jboss.marshalling.MarshallerFactory; -import org.jboss.marshalling.MarshallingConfiguration; - -/** - * {@link UnmarshallerProvider} implementation which use a {@link ThreadLocal} to store references - * to {@link Marshaller} instances. This may give you some performance boost if you need to marshall - * many small {@link Object}'s and your actual Thread count is not to big - */ -public class ThreadLocalMarshallerProvider implements MarshallerProvider { - private final FastThreadLocal marshallers = new FastThreadLocal<>(); - - private final MarshallerFactory factory; - private final MarshallingConfiguration config; - - /** - * Create a new instance of the {@link ThreadLocalMarshallerProvider} - * - * @param factory the {@link MarshallerFactory} to use to create {@link Marshaller}'s if needed - * @param config the {@link MarshallingConfiguration} to use - */ - public ThreadLocalMarshallerProvider(MarshallerFactory factory, MarshallingConfiguration config) { - this.factory = factory; - this.config = config; - } - - @Override - public Marshaller getMarshaller(ChannelHandlerContext ctx) throws Exception { - Marshaller marshaller = marshallers.get(); - if (marshaller == null) { - marshaller = factory.createMarshaller(config); - marshallers.set(marshaller); - } - return marshaller; - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/marshalling/ThreadLocalUnmarshallerProvider.java b/codec/src/main/java/io/netty/handler/codec/marshalling/ThreadLocalUnmarshallerProvider.java deleted file mode 100644 index 82618b40d1..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/marshalling/ThreadLocalUnmarshallerProvider.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.marshalling; - -import io.netty.channel.ChannelHandlerContext; - -import io.netty.util.concurrent.FastThreadLocal; -import org.jboss.marshalling.MarshallerFactory; -import org.jboss.marshalling.MarshallingConfiguration; -import org.jboss.marshalling.Unmarshaller; - -/** - * {@link UnmarshallerProvider} implementation which use a {@link ThreadLocal} to store references - * to {@link Unmarshaller} instances. This may give you some performance boost if you need to unmarshall - * many small {@link Object}'s. - */ -public class ThreadLocalUnmarshallerProvider implements UnmarshallerProvider { - private final FastThreadLocal unmarshallers = new FastThreadLocal<>(); - - private final MarshallerFactory factory; - private final MarshallingConfiguration config; - - /** - * Create a new instance of the {@link ThreadLocalUnmarshallerProvider} - * - * @param factory the {@link MarshallerFactory} to use to create {@link Unmarshaller}'s if needed - * @param config the {@link MarshallingConfiguration} to use - */ - public ThreadLocalUnmarshallerProvider(MarshallerFactory factory, MarshallingConfiguration config) { - this.factory = factory; - this.config = config; - } - - @Override - public Unmarshaller getUnmarshaller(ChannelHandlerContext ctx) throws Exception { - Unmarshaller unmarshaller = unmarshallers.get(); - if (unmarshaller == null) { - unmarshaller = factory.createUnmarshaller(config); - unmarshallers.set(unmarshaller); - } - return unmarshaller; - } - -} diff --git a/codec/src/main/java/io/netty/handler/codec/marshalling/UnmarshallerProvider.java b/codec/src/main/java/io/netty/handler/codec/marshalling/UnmarshallerProvider.java deleted file mode 100644 index dbe16fe2df..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/marshalling/UnmarshallerProvider.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.marshalling; - -import io.netty.channel.ChannelHandlerContext; - -import org.jboss.marshalling.Unmarshaller; - -/** - * This provider is responsible to get an {@link Unmarshaller} for a {@link ChannelHandlerContext} - * - */ -public interface UnmarshallerProvider { - - /** - * Get the {@link Unmarshaller} for the given {@link ChannelHandlerContext} - */ - Unmarshaller getUnmarshaller(ChannelHandlerContext ctx) throws Exception; -} diff --git a/codec/src/main/java/io/netty/handler/codec/marshalling/package-info.java b/codec/src/main/java/io/netty/handler/codec/marshalling/package-info.java deleted file mode 100644 index ee6cde69b4..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/marshalling/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * Decoder and Encoder which uses JBoss Marshalling. - * - */ -package io.netty.handler.codec.marshalling; diff --git a/codec/src/main/java/io/netty/handler/codec/package-info.java b/codec/src/main/java/io/netty/handler/codec/package-info.java deleted file mode 100644 index b25306ce11..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/package-info.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * Extensible decoder and its common implementations which deal with the - * packet fragmentation and reassembly issue found in a stream-based transport - * such as TCP/IP. - */ -package io.netty.handler.codec; 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 deleted file mode 100644 index 18f5725713..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufDecoder.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.protobuf; - -import static java.util.Objects.requireNonNull; - -import com.google.protobuf.ExtensionRegistry; -import com.google.protobuf.ExtensionRegistryLite; -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; -import io.netty.handler.codec.ByteToMessageDecoder; -import io.netty.handler.codec.LengthFieldBasedFrameDecoder; -import io.netty.handler.codec.LengthFieldPrepender; -import io.netty.handler.codec.MessageToMessageDecoder; - -/** - * Decodes a received {@link ByteBuf} into a - * Google Protocol Buffers - * {@link Message} and {@link MessageLite}. Please note that this decoder must - * be used with a proper {@link ByteToMessageDecoder} such as {@link ProtobufVarint32FrameDecoder} - * or {@link LengthFieldBasedFrameDecoder} if you are using a stream-based - * transport such as TCP/IP. A typical setup for TCP/IP would be: - *
- * {@link ChannelPipeline} pipeline = ...;
- *
- * // Decoders
- * pipeline.addLast("frameDecoder",
- *                  new {@link LengthFieldBasedFrameDecoder}(1048576, 0, 4, 0, 4));
- * pipeline.addLast("protobufDecoder",
- *                  new {@link ProtobufDecoder}(MyMessage.getDefaultInstance()));
- *
- * // Encoder
- * pipeline.addLast("frameEncoder", new {@link LengthFieldPrepender}(4));
- * pipeline.addLast("protobufEncoder", new {@link ProtobufEncoder}());
- * 
- * and then you can use a {@code MyMessage} instead of a {@link ByteBuf} - * as a message: - *
- * void channelRead({@link ChannelHandlerContext} ctx, Object msg) {
- *     MyMessage req = (MyMessage) msg;
- *     MyMessage res = MyMessage.newBuilder().setText(
- *                               "Did you say '" + req.getText() + "'?").build();
- *     ch.write(res);
- * }
- * 
- */ -@Sharable -public class ProtobufDecoder extends MessageToMessageDecoder { - - private static final boolean HAS_PARSER; - - static { - boolean hasParser = false; - try { - // MessageLite.getParserForType() is not available until protobuf 2.5.0. - MessageLite.class.getDeclaredMethod("getParserForType"); - hasParser = true; - } catch (Throwable t) { - // Ignore - } - - HAS_PARSER = hasParser; - } - - private final MessageLite prototype; - private final ExtensionRegistryLite extensionRegistry; - - /** - * Creates a new instance. - */ - public ProtobufDecoder(MessageLite prototype) { - this(prototype, null); - } - - public ProtobufDecoder(MessageLite prototype, ExtensionRegistry extensionRegistry) { - this(prototype, (ExtensionRegistryLite) extensionRegistry); - } - - public ProtobufDecoder(MessageLite prototype, ExtensionRegistryLite extensionRegistry) { - requireNonNull(prototype, "prototype"); - this.prototype = prototype.getDefaultInstanceForType(); - this.extensionRegistry = extensionRegistry; - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { - final byte[] array; - final int offset; - final int length = msg.readableBytes(); - if (msg.hasArray()) { - array = msg.array(); - offset = msg.arrayOffset() + msg.readerIndex(); - } else { - array = ByteBufUtil.getBytes(msg, msg.readerIndex(), length, false); - offset = 0; - } - - if (extensionRegistry == null) { - if (HAS_PARSER) { - ctx.fireChannelRead(prototype.getParserForType().parseFrom(array, offset, length)); - } else { - ctx.fireChannelRead(prototype.newBuilderForType().mergeFrom(array, offset, length).build()); - } - } else { - if (HAS_PARSER) { - ctx.fireChannelRead(prototype.getParserForType().parseFrom( - array, offset, length, extensionRegistry)); - } else { - ctx.fireChannelRead(prototype.newBuilderForType().mergeFrom( - array, offset, length, extensionRegistry).build()); - } - } - } -} 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 deleted file mode 100644 index 5b6df10cfe..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufDecoderNano.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.protobuf; - -import static java.util.Objects.requireNonNull; - -import com.google.protobuf.nano.MessageNano; - -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; -import io.netty.handler.codec.ByteToMessageDecoder; -import io.netty.handler.codec.LengthFieldBasedFrameDecoder; -import io.netty.handler.codec.MessageToMessageDecoder; - -/** - * Decodes a received {@link ByteBuf} into a - * Google Protocol Buffers - * {@link MessageNano}. Please note that this decoder must - * be used with a proper {@link ByteToMessageDecoder} such as {@link LengthFieldBasedFrameDecoder} - * if you are using a stream-based transport such as TCP/IP. A typical setup for TCP/IP would be: - *
- * {@link ChannelPipeline} pipeline = ...;
- *
- * // Decoders
- * pipeline.addLast("frameDecoder",
- *                  new {@link LengthFieldBasedFrameDecoder}(1048576, 0, 4, 0, 4));
- * pipeline.addLast("protobufDecoder",
- *                  new {@link ProtobufDecoderNano}(MyMessage.getDefaultInstance()));
- *
- * // Encoder
- * pipeline.addLast("frameEncoder", new {@link io.netty.handler.codec.LengthFieldPrepender}(4));
- * pipeline.addLast("protobufEncoder", new {@link ProtobufEncoderNano}());
- * 
- * and then you can use a {@code MyMessage} instead of a {@link ByteBuf} - * as a message: - *
- * void channelRead({@link ChannelHandlerContext} ctx, Object msg) {
- *     MyMessage req = (MyMessage) msg;
- *     MyMessage res = MyMessage.newBuilder().setText(
- *                               "Did you say '" + req.getText() + "'?").build();
- *     ch.write(res);
- * }
- * 
- */ -@Sharable -public class ProtobufDecoderNano extends MessageToMessageDecoder { - private final Class clazz; - /** - * Creates a new instance. - */ - public ProtobufDecoderNano(Class clazz) { - this.clazz = requireNonNull(clazz, "You must provide a Class"); - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { - final byte[] array; - final int offset; - final int length = msg.readableBytes(); - if (msg.hasArray()) { - array = msg.array(); - offset = msg.arrayOffset() + msg.readerIndex(); - } else { - array = ByteBufUtil.getBytes(msg, msg.readerIndex(), length, false); - offset = 0; - } - MessageNano prototype = clazz.getConstructor().newInstance(); - ctx.fireChannelRead(MessageNano.mergeFrom(prototype, array, offset, length)); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufEncoder.java b/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufEncoder.java deleted file mode 100644 index 776e425156..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufEncoder.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.protobuf; - -import com.google.protobuf.Message; -import com.google.protobuf.MessageLite; -import com.google.protobuf.MessageLiteOrBuilder; -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.handler.codec.LengthFieldBasedFrameDecoder; -import io.netty.handler.codec.LengthFieldPrepender; -import io.netty.handler.codec.MessageToMessageEncoder; - -import java.util.List; - -import static io.netty.buffer.Unpooled.*; - -/** - * Encodes the requested Google - * Protocol Buffers {@link Message} and {@link MessageLite} into a - * {@link ByteBuf}. A typical setup for TCP/IP would be: - *
- * {@link ChannelPipeline} pipeline = ...;
- *
- * // Decoders
- * pipeline.addLast("frameDecoder",
- *                  new {@link LengthFieldBasedFrameDecoder}(1048576, 0, 4, 0, 4));
- * pipeline.addLast("protobufDecoder",
- *                  new {@link ProtobufDecoder}(MyMessage.getDefaultInstance()));
- *
- * // Encoder
- * pipeline.addLast("frameEncoder", new {@link LengthFieldPrepender}(4));
- * pipeline.addLast("protobufEncoder", new {@link ProtobufEncoder}());
- * 
- * and then you can use a {@code MyMessage} instead of a {@link ByteBuf} - * as a message: - *
- * void channelRead({@link ChannelHandlerContext} ctx, Object msg) {
- *     MyMessage req = (MyMessage) msg;
- *     MyMessage res = MyMessage.newBuilder().setText(
- *                               "Did you say '" + req.getText() + "'?").build();
- *     ch.write(res);
- * }
- * 
- */ -@Sharable -public class ProtobufEncoder extends MessageToMessageEncoder { - @Override - protected void encode(ChannelHandlerContext ctx, MessageLiteOrBuilder msg, List out) - throws Exception { - if (msg instanceof MessageLite) { - out.add(wrappedBuffer(((MessageLite) msg).toByteArray())); - return; - } - if (msg instanceof MessageLite.Builder) { - out.add(wrappedBuffer(((MessageLite.Builder) msg).build().toByteArray())); - } - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufEncoderNano.java b/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufEncoderNano.java deleted file mode 100644 index e89c64f289..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufEncoderNano.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.protobuf; - -import com.google.protobuf.nano.CodedOutputByteBufferNano; -import com.google.protobuf.nano.MessageNano; - -import java.util.List; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.handler.codec.LengthFieldBasedFrameDecoder; -import io.netty.handler.codec.LengthFieldPrepender; -import io.netty.handler.codec.MessageToMessageEncoder; - -/** - * Encodes the requested Google - * Protocol Buffers {@link MessageNano} into a - * {@link ByteBuf}. A typical setup for TCP/IP would be: - *
- * {@link ChannelPipeline} pipeline = ...;
- *
- * // Decoders
- * pipeline.addLast("frameDecoder",
- *                  new {@link LengthFieldBasedFrameDecoder}(1048576, 0, 4, 0, 4));
- * pipeline.addLast("protobufDecoder",
- *                  new {@link ProtobufDecoderNano}(MyMessage.getDefaultInstance()));
- *
- * // Encoder
- * pipeline.addLast("frameEncoder", new {@link LengthFieldPrepender}(4));
- * pipeline.addLast("protobufEncoder", new {@link ProtobufEncoderNano}());
- * 
- * and then you can use a {@code MyMessage} instead of a {@link ByteBuf} - * as a message: - *
- * void channelRead({@link ChannelHandlerContext} ctx, Object msg) {
- *     MyMessage req = (MyMessage) msg;
- *     MyMessage res = MyMessage.newBuilder().setText(
- *                               "Did you say '" + req.getText() + "'?").build();
- *     ch.write(res);
- * }
- * 
- */ -@ChannelHandler.Sharable -public class ProtobufEncoderNano extends MessageToMessageEncoder { - @Override - protected void encode( - ChannelHandlerContext ctx, MessageNano msg, List out) throws Exception { - final int size = msg.getSerializedSize(); - final ByteBuf buffer = ctx.alloc().heapBuffer(size, size); - final byte[] array = buffer.array(); - CodedOutputByteBufferNano cobbn = CodedOutputByteBufferNano.newInstance(array, - buffer.arrayOffset(), buffer.capacity()); - msg.writeTo(cobbn); - buffer.writerIndex(size); - out.add(buffer); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufVarint32FrameDecoder.java b/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufVarint32FrameDecoder.java deleted file mode 100644 index c8faea1aef..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufVarint32FrameDecoder.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.protobuf; - -import com.google.protobuf.CodedInputStream; -import com.google.protobuf.nano.CodedInputByteBufferNano; -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.ByteToMessageDecoder; -import io.netty.handler.codec.CorruptedFrameException; - -/** - * A decoder that splits the received {@link ByteBuf}s dynamically by the - * value of the Google Protocol Buffers - * Base - * 128 Varints integer length field in the message. For example: - *
- * BEFORE DECODE (302 bytes)       AFTER DECODE (300 bytes)
- * +--------+---------------+      +---------------+
- * | Length | Protobuf Data |----->| Protobuf Data |
- * | 0xAC02 |  (300 bytes)  |      |  (300 bytes)  |
- * +--------+---------------+      +---------------+
- * 
- * - * @see CodedInputStream - * @see CodedInputByteBufferNano - */ -public class ProtobufVarint32FrameDecoder extends ByteToMessageDecoder { - - // TODO maxFrameLength + safe skip + fail-fast option - // (just like LengthFieldBasedFrameDecoder) - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) - throws Exception { - int readerIndex = in.readerIndex(); - int preIndex = in.readerIndex(); - int length = readRawVarint32(in); - if (preIndex == in.readerIndex()) { - return; - } - if (length < 0) { - throw new CorruptedFrameException("negative length: " + length); - } - - if (in.readableBytes() < length) { - in.readerIndex(readerIndex); - } else { - ctx.fireChannelRead(in.readRetainedSlice(length)); - } - } - - /** - * Reads variable length 32bit int from buffer - * - * @return decoded int if buffers readerIndex has been forwarded else nonsense value - */ - private static int readRawVarint32(ByteBuf buffer) { - if (!buffer.isReadable()) { - return 0; - } - int readerIndex = buffer.readerIndex(); - byte tmp = buffer.readByte(); - if (tmp >= 0) { - return tmp; - } else { - int result = tmp & 127; - if (!buffer.isReadable()) { - buffer.readerIndex(readerIndex); - return 0; - } - if ((tmp = buffer.readByte()) >= 0) { - result |= tmp << 7; - } else { - result |= (tmp & 127) << 7; - if (!buffer.isReadable()) { - buffer.readerIndex(readerIndex); - return 0; - } - if ((tmp = buffer.readByte()) >= 0) { - result |= tmp << 14; - } else { - result |= (tmp & 127) << 14; - if (!buffer.isReadable()) { - buffer.readerIndex(readerIndex); - return 0; - } - if ((tmp = buffer.readByte()) >= 0) { - result |= tmp << 21; - } else { - result |= (tmp & 127) << 21; - if (!buffer.isReadable()) { - buffer.readerIndex(readerIndex); - return 0; - } - result |= (tmp = buffer.readByte()) << 28; - if (tmp < 0) { - throw new CorruptedFrameException("malformed varint."); - } - } - } - } - return result; - } - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufVarint32LengthFieldPrepender.java b/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufVarint32LengthFieldPrepender.java deleted file mode 100644 index fbfe8508d7..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufVarint32LengthFieldPrepender.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.protobuf; - -import com.google.protobuf.CodedOutputStream; -import com.google.protobuf.nano.CodedOutputByteBufferNano; -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToByteEncoder; - -/** - * An encoder that prepends the Google Protocol Buffers - * Base - * 128 Varints integer length field. For example: - *
- * BEFORE ENCODE (300 bytes)       AFTER ENCODE (302 bytes)
- * +---------------+               +--------+---------------+
- * | Protobuf Data |-------------->| Length | Protobuf Data |
- * |  (300 bytes)  |               | 0xAC02 |  (300 bytes)  |
- * +---------------+               +--------+---------------+
- * 
* - * - * @see CodedOutputStream - * @see CodedOutputByteBufferNano - */ -@Sharable -public class ProtobufVarint32LengthFieldPrepender extends MessageToByteEncoder { - - @Override - protected void encode( - ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) throws Exception { - int bodyLen = msg.readableBytes(); - int headerLen = computeRawVarint32Size(bodyLen); - out.ensureWritable(headerLen + bodyLen); - writeRawVarint32(out, bodyLen); - out.writeBytes(msg, msg.readerIndex(), bodyLen); - } - - /** - * Writes protobuf varint32 to (@link ByteBuf). - * @param out to be written to - * @param value to be written - */ - static void writeRawVarint32(ByteBuf out, int value) { - while (true) { - if ((value & ~0x7F) == 0) { - out.writeByte(value); - return; - } else { - out.writeByte((value & 0x7F) | 0x80); - value >>>= 7; - } - } - } - - /** - * Computes size of protobuf varint32 after encoding. - * @param value which is to be encoded. - * @return size of value encoded as protobuf varint32. - */ - static int computeRawVarint32Size(final int value) { - if ((value & (0xffffffff << 7)) == 0) { - return 1; - } - if ((value & (0xffffffff << 14)) == 0) { - return 2; - } - if ((value & (0xffffffff << 21)) == 0) { - return 3; - } - if ((value & (0xffffffff << 28)) == 0) { - return 4; - } - return 5; - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/protobuf/package-info.java b/codec/src/main/java/io/netty/handler/codec/protobuf/package-info.java deleted file mode 100644 index 7f685b4c25..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/protobuf/package-info.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * Encoder and decoder which transform a - * Google Protocol Buffers - * {@link com.google.protobuf.Message} and {@link com.google.protobuf.nano.MessageNano} into a - * {@link io.netty.buffer.ByteBuf} and vice versa. - */ -package io.netty.handler.codec.protobuf; diff --git a/codec/src/main/java/io/netty/handler/codec/serialization/CachingClassResolver.java b/codec/src/main/java/io/netty/handler/codec/serialization/CachingClassResolver.java deleted file mode 100644 index 825b028be0..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/serialization/CachingClassResolver.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.serialization; - -import java.util.Map; - -class CachingClassResolver implements ClassResolver { - - private final Map> classCache; - private final ClassResolver delegate; - - CachingClassResolver(ClassResolver delegate, Map> classCache) { - this.delegate = delegate; - this.classCache = classCache; - } - - @Override - public Class resolve(String className) throws ClassNotFoundException { - // Query the cache first. - Class clazz; - clazz = classCache.get(className); - if (clazz != null) { - return clazz; - } - - // And then try to load. - clazz = delegate.resolve(className); - - classCache.put(className, clazz); - return clazz; - } - -} diff --git a/codec/src/main/java/io/netty/handler/codec/serialization/ClassLoaderClassResolver.java b/codec/src/main/java/io/netty/handler/codec/serialization/ClassLoaderClassResolver.java deleted file mode 100644 index 4e7fde639c..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/serialization/ClassLoaderClassResolver.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.serialization; - -class ClassLoaderClassResolver implements ClassResolver { - - private final ClassLoader classLoader; - - ClassLoaderClassResolver(ClassLoader classLoader) { - this.classLoader = classLoader; - } - - @Override - public Class resolve(String className) throws ClassNotFoundException { - try { - return classLoader.loadClass(className); - } catch (ClassNotFoundException ignored) { - return Class.forName(className, false, classLoader); - } - } - -} diff --git a/codec/src/main/java/io/netty/handler/codec/serialization/ClassResolver.java b/codec/src/main/java/io/netty/handler/codec/serialization/ClassResolver.java deleted file mode 100644 index f40855dca9..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/serialization/ClassResolver.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.serialization; - -/** - * please use {@link ClassResolvers} as instance factory - */ -public interface ClassResolver { - - Class resolve(String className) throws ClassNotFoundException; - -} 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 deleted file mode 100644 index 03c3962062..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/serialization/ClassResolvers.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.serialization; - -import io.netty.util.internal.PlatformDependent; - -import java.util.HashMap; -import java.util.concurrent.ConcurrentHashMap; - -public final class ClassResolvers { - - /** - * cache disabled - * @param classLoader - specific classLoader to use, or null if you want to revert to default - * @return new instance of class resolver - */ - public static ClassResolver cacheDisabled(ClassLoader classLoader) { - return new ClassLoaderClassResolver(defaultClassLoader(classLoader)); - } - - /** - * 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 - * @return new instance of class resolver - */ - public static ClassResolver weakCachingResolver(ClassLoader classLoader) { - return new CachingClassResolver( - new ClassLoaderClassResolver(defaultClassLoader(classLoader)), - new WeakReferenceMap<>(new HashMap<>())); - } - - /** - * 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 - * @return new instance of class resolver - */ - public static ClassResolver softCachingResolver(ClassLoader classLoader) { - return new CachingClassResolver( - new ClassLoaderClassResolver(defaultClassLoader(classLoader)), - new SoftReferenceMap<>(new HashMap<>())); - } - - /** - * 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 - * @return new instance of class resolver - */ - public static ClassResolver weakCachingConcurrentResolver(ClassLoader classLoader) { - return new CachingClassResolver( - new ClassLoaderClassResolver(defaultClassLoader(classLoader)), - new WeakReferenceMap<>(new ConcurrentHashMap<>())); - } - - /** - * 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 - * @return new instance of class resolver - */ - public static ClassResolver softCachingConcurrentResolver(ClassLoader classLoader) { - return new CachingClassResolver( - new ClassLoaderClassResolver(defaultClassLoader(classLoader)), - new SoftReferenceMap<>(new ConcurrentHashMap<>())); - } - - static ClassLoader defaultClassLoader(ClassLoader classLoader) { - if (classLoader != null) { - return classLoader; - } - - final ClassLoader contextClassLoader = PlatformDependent.getContextClassLoader(); - if (contextClassLoader != null) { - return contextClassLoader; - } - - return PlatformDependent.getClassLoader(ClassResolvers.class); - } - - private ClassResolvers() { - // Unused - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/serialization/CompactObjectInputStream.java b/codec/src/main/java/io/netty/handler/codec/serialization/CompactObjectInputStream.java deleted file mode 100644 index 8e9dbc0728..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/serialization/CompactObjectInputStream.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.serialization; - -import java.io.EOFException; -import java.io.IOException; -import java.io.InputStream; -import java.io.ObjectInputStream; -import java.io.ObjectStreamClass; -import java.io.StreamCorruptedException; - -class CompactObjectInputStream extends ObjectInputStream { - - private final ClassResolver classResolver; - - CompactObjectInputStream(InputStream in, ClassResolver classResolver) throws IOException { - super(in); - this.classResolver = classResolver; - } - - @Override - protected void readStreamHeader() throws IOException { - int version = readByte() & 0xFF; - if (version != STREAM_VERSION) { - throw new StreamCorruptedException( - "Unsupported version: " + version); - } - } - - @Override - protected ObjectStreamClass readClassDescriptor() - throws IOException, ClassNotFoundException { - int type = read(); - if (type < 0) { - throw new EOFException(); - } - switch (type) { - case CompactObjectOutputStream.TYPE_FAT_DESCRIPTOR: - return super.readClassDescriptor(); - case CompactObjectOutputStream.TYPE_THIN_DESCRIPTOR: - String className = readUTF(); - Class clazz = classResolver.resolve(className); - return ObjectStreamClass.lookupAny(clazz); - default: - throw new StreamCorruptedException( - "Unexpected class descriptor type: " + type); - } - } - - @Override - protected Class resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { - Class clazz; - try { - clazz = classResolver.resolve(desc.getName()); - } catch (ClassNotFoundException ignored) { - clazz = super.resolveClass(desc); - } - - return clazz; - } - -} diff --git a/codec/src/main/java/io/netty/handler/codec/serialization/CompactObjectOutputStream.java b/codec/src/main/java/io/netty/handler/codec/serialization/CompactObjectOutputStream.java deleted file mode 100644 index f8bdffb7a2..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/serialization/CompactObjectOutputStream.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.serialization; - -import java.io.IOException; -import java.io.ObjectOutputStream; -import java.io.ObjectStreamClass; -import java.io.OutputStream; - -class CompactObjectOutputStream extends ObjectOutputStream { - - static final int TYPE_FAT_DESCRIPTOR = 0; - static final int TYPE_THIN_DESCRIPTOR = 1; - - CompactObjectOutputStream(OutputStream out) throws IOException { - super(out); - } - - @Override - protected void writeStreamHeader() throws IOException { - writeByte(STREAM_VERSION); - } - - @Override - protected void writeClassDescriptor(ObjectStreamClass desc) throws IOException { - Class clazz = desc.forClass(); - if (clazz.isPrimitive() || clazz.isArray() || clazz.isInterface() || - desc.getSerialVersionUID() == 0) { - write(TYPE_FAT_DESCRIPTOR); - super.writeClassDescriptor(desc); - } else { - write(TYPE_THIN_DESCRIPTOR); - writeUTF(desc.getName()); - } - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/serialization/CompatibleObjectEncoder.java b/codec/src/main/java/io/netty/handler/codec/serialization/CompatibleObjectEncoder.java deleted file mode 100644 index 6e644fa535..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/serialization/CompatibleObjectEncoder.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.serialization; - -import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufOutputStream; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToByteEncoder; - -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.OutputStream; -import java.io.Serializable; - -/** - * An encoder which serializes a Java object into a {@link ByteBuf} - * (interoperability version). - *

- * This encoder is interoperable with the standard Java object streams such as - * {@link ObjectInputStream} and {@link ObjectOutputStream}. - */ -public class CompatibleObjectEncoder extends MessageToByteEncoder { - private final int resetInterval; - private int writtenObjects; - - /** - * Creates a new instance with the reset interval of {@code 16}. - */ - public CompatibleObjectEncoder() { - this(16); // Reset at every sixteen writes - } - - /** - * Creates a new instance. - * - * @param resetInterval - * the number of objects between {@link ObjectOutputStream#reset()}. - * {@code 0} will disable resetting the stream, but the remote - * peer will be at the risk of getting {@link OutOfMemoryError} in - * the long term. - */ - public CompatibleObjectEncoder(int resetInterval) { - this.resetInterval = checkPositiveOrZero(resetInterval, "resetInterval"); - } - - /** - * Creates a new {@link ObjectOutputStream} which wraps the specified - * {@link OutputStream}. Override this method to use a subclass of the - * {@link ObjectOutputStream}. - */ - protected ObjectOutputStream newObjectOutputStream(OutputStream out) throws Exception { - return new ObjectOutputStream(out); - } - - @Override - protected void encode(ChannelHandlerContext ctx, Serializable msg, ByteBuf out) throws Exception { - try (ObjectOutputStream oos = newObjectOutputStream(new ByteBufOutputStream(out))) { - if (resetInterval != 0) { - // Resetting will prevent OOM on the receiving side. - writtenObjects++; - if (writtenObjects % resetInterval == 0) { - oos.reset(); - } - } - - oos.writeObject(msg); - oos.flush(); - } - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/serialization/ObjectDecoder.java b/codec/src/main/java/io/netty/handler/codec/serialization/ObjectDecoder.java deleted file mode 100644 index f4b35f9618..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/serialization/ObjectDecoder.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.serialization; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufInputStream; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.LengthFieldBasedFrameDecoder; - -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.StreamCorruptedException; - -/** - * A decoder which deserializes the received {@link ByteBuf}s into Java - * objects. - *

- * Please note that the serialized form this decoder expects is not - * compatible with the standard {@link ObjectOutputStream}. Please use - * {@link ObjectEncoder} or {@link ObjectEncoderOutputStream} to ensure the - * interoperability with this decoder. - */ -public class ObjectDecoder extends LengthFieldBasedFrameDecoder { - - private final ClassResolver classResolver; - - /** - * Creates a new decoder whose maximum object size is {@code 1048576} - * bytes. If the size of the received object is greater than - * {@code 1048576} bytes, a {@link StreamCorruptedException} will be - * raised. - * - * @param classResolver the {@link ClassResolver} to use for this decoder - */ - public ObjectDecoder(ClassResolver classResolver) { - this(1048576, classResolver); - } - - /** - * Creates a new decoder with the specified maximum object size. - * - * @param maxObjectSize the maximum byte length of the serialized object. - * if the length of the received object is greater - * than this value, {@link StreamCorruptedException} - * will be raised. - * @param classResolver the {@link ClassResolver} which will load the class - * of the serialized object - */ - public ObjectDecoder(int maxObjectSize, ClassResolver classResolver) { - super(maxObjectSize, 0, 4, 0, 4); - this.classResolver = classResolver; - } - - @Override - protected Object decode0(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - ByteBuf frame = (ByteBuf) super.decode0(ctx, in); - if (frame == null) { - return null; - } - - try (ObjectInputStream ois = - new CompactObjectInputStream(new ByteBufInputStream(frame, true), classResolver)) { - return ois.readObject(); - } - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/serialization/ObjectDecoderInputStream.java b/codec/src/main/java/io/netty/handler/codec/serialization/ObjectDecoderInputStream.java deleted file mode 100644 index 9d5dcb05fd..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/serialization/ObjectDecoderInputStream.java +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.serialization; - -import static java.util.Objects.requireNonNull; - -import java.io.BufferedReader; -import java.io.DataInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.ObjectInput; -import java.io.StreamCorruptedException; - -/** - * An {@link ObjectInput} which is interoperable with {@link ObjectEncoder} - * and {@link ObjectEncoderOutputStream}. - */ -public class ObjectDecoderInputStream extends InputStream implements - ObjectInput { - - private final DataInputStream in; - private final int maxObjectSize; - private final ClassResolver classResolver; - - /** - * Creates a new {@link ObjectInput}. - * - * @param in - * the {@link InputStream} where the serialized form will be - * read from - */ - public ObjectDecoderInputStream(InputStream in) { - this(in, null); - } - - /** - * Creates a new {@link ObjectInput}. - * - * @param in - * the {@link InputStream} where the serialized form will be - * read from - * @param classLoader - * the {@link ClassLoader} which will load the class of the - * serialized object - */ - public ObjectDecoderInputStream(InputStream in, ClassLoader classLoader) { - this(in, classLoader, 1048576); - } - - /** - * Creates a new {@link ObjectInput}. - * - * @param in - * the {@link InputStream} where the serialized form will be - * read from - * @param maxObjectSize - * the maximum byte length of the serialized object. if the length - * of the received object is greater than this value, - * a {@link StreamCorruptedException} will be raised. - */ - public ObjectDecoderInputStream(InputStream in, int maxObjectSize) { - this(in, null, maxObjectSize); - } - - /** - * Creates a new {@link ObjectInput}. - * - * @param in - * the {@link InputStream} where the serialized form will be - * read from - * @param classLoader - * the {@link ClassLoader} which will load the class of the - * serialized object - * @param maxObjectSize - * the maximum byte length of the serialized object. if the length - * of the received object is greater than this value, - * a {@link StreamCorruptedException} will be raised. - */ - public ObjectDecoderInputStream(InputStream in, ClassLoader classLoader, int maxObjectSize) { - requireNonNull(in, "in"); - if (maxObjectSize <= 0) { - throw new IllegalArgumentException("maxObjectSize: " + maxObjectSize); - } - if (in instanceof DataInputStream) { - this.in = (DataInputStream) in; - } else { - this.in = new DataInputStream(in); - } - classResolver = ClassResolvers.weakCachingResolver(classLoader); - this.maxObjectSize = maxObjectSize; - } - - @Override - public Object readObject() throws ClassNotFoundException, IOException { - int dataLen = readInt(); - if (dataLen <= 0) { - throw new StreamCorruptedException("invalid data length: " + dataLen); - } - if (dataLen > maxObjectSize) { - throw new StreamCorruptedException( - "data length too big: " + dataLen + " (max: " + maxObjectSize + ')'); - } - - return new CompactObjectInputStream(in, classResolver).readObject(); - } - - @Override - public int available() throws IOException { - return in.available(); - } - - @Override - public void close() throws IOException { - in.close(); - } - - // Suppress a warning since the class is not thread-safe - @Override - public void mark(int readlimit) { // lgtm[java/non-sync-override] - in.mark(readlimit); - } - - @Override - public boolean markSupported() { - return in.markSupported(); - } - - // Suppress a warning since the class is not thread-safe - @Override - public int read() throws IOException { // lgtm[java/non-sync-override] - return in.read(); - } - - @Override - public final int read(byte[] b, int off, int len) throws IOException { - return in.read(b, off, len); - } - - @Override - public final int read(byte[] b) throws IOException { - return in.read(b); - } - - @Override - public final boolean readBoolean() throws IOException { - return in.readBoolean(); - } - - @Override - public final byte readByte() throws IOException { - return in.readByte(); - } - - @Override - public final char readChar() throws IOException { - return in.readChar(); - } - - @Override - public final double readDouble() throws IOException { - return in.readDouble(); - } - - @Override - public final float readFloat() throws IOException { - return in.readFloat(); - } - - @Override - public final void readFully(byte[] b, int off, int len) throws IOException { - in.readFully(b, off, len); - } - - @Override - public final void readFully(byte[] b) throws IOException { - in.readFully(b); - } - - @Override - public final int readInt() throws IOException { - return in.readInt(); - } - - /** - * @deprecated Use {@link BufferedReader#readLine()} instead. - */ - @Override - @Deprecated - public final String readLine() throws IOException { - return in.readLine(); - } - - @Override - public final long readLong() throws IOException { - return in.readLong(); - } - - @Override - public final short readShort() throws IOException { - return in.readShort(); - } - - @Override - public final int readUnsignedByte() throws IOException { - return in.readUnsignedByte(); - } - - @Override - public final int readUnsignedShort() throws IOException { - return in.readUnsignedShort(); - } - - @Override - public final String readUTF() throws IOException { - return in.readUTF(); - } - - @Override - public void reset() throws IOException { - in.reset(); - } - - @Override - public long skip(long n) throws IOException { - return in.skip(n); - } - - @Override - public final int skipBytes(int n) throws IOException { - return in.skipBytes(n); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/serialization/ObjectEncoder.java b/codec/src/main/java/io/netty/handler/codec/serialization/ObjectEncoder.java deleted file mode 100644 index 60b65e42d8..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/serialization/ObjectEncoder.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.serialization; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufOutputStream; -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToByteEncoder; - -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; - -/** - * An encoder which serializes a Java object into a {@link ByteBuf}. - *

- * Please note that the serialized form this encoder produces is not - * compatible with the standard {@link ObjectInputStream}. Please use - * {@link ObjectDecoder} or {@link ObjectDecoderInputStream} to ensure the - * interoperability with this encoder. - */ -@Sharable -public class ObjectEncoder extends MessageToByteEncoder { - private static final byte[] LENGTH_PLACEHOLDER = new byte[4]; - - @Override - protected void encode(ChannelHandlerContext ctx, Serializable msg, ByteBuf out) throws Exception { - int startIdx = out.writerIndex(); - - ByteBufOutputStream bout = new ByteBufOutputStream(out); - ObjectOutputStream oout = null; - try { - bout.write(LENGTH_PLACEHOLDER); - oout = new CompactObjectOutputStream(bout); - oout.writeObject(msg); - oout.flush(); - } finally { - if (oout != null) { - oout.close(); - } else { - bout.close(); - } - } - - int endIdx = out.writerIndex(); - - out.setInt(startIdx, endIdx - startIdx - 4); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/serialization/ObjectEncoderOutputStream.java b/codec/src/main/java/io/netty/handler/codec/serialization/ObjectEncoderOutputStream.java deleted file mode 100644 index 304aeba5b9..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/serialization/ObjectEncoderOutputStream.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.serialization; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufOutputStream; -import io.netty.buffer.Unpooled; - -import java.io.DataOutputStream; -import java.io.IOException; -import java.io.ObjectOutput; -import java.io.ObjectOutputStream; -import java.io.OutputStream; - -/** - * An {@link ObjectOutput} which is interoperable with {@link ObjectDecoder} - * and {@link ObjectDecoderInputStream}. - */ -public class ObjectEncoderOutputStream extends OutputStream implements - ObjectOutput { - - private final DataOutputStream out; - private final int estimatedLength; - - /** - * Creates a new {@link ObjectOutput} with the estimated length of 512 - * bytes. - * - * @param out - * the {@link OutputStream} where the serialized form will be - * written out - */ - public ObjectEncoderOutputStream(OutputStream out) { - this(out, 512); - } - - /** - * Creates a new {@link ObjectOutput}. - * - * @param out - * the {@link OutputStream} where the serialized form will be - * written out - * - * @param estimatedLength - * the estimated byte length of the serialized form of an object. - * If the length of the serialized form exceeds this value, the - * internal buffer will be expanded automatically at the cost of - * memory bandwidth. If this value is too big, it will also waste - * memory bandwidth. To avoid unnecessary memory copy or allocation - * cost, please specify the properly estimated value. - */ - public ObjectEncoderOutputStream(OutputStream out, int estimatedLength) { - requireNonNull(out, "out"); - if (estimatedLength < 0) { - throw new IllegalArgumentException("estimatedLength: " + estimatedLength); - } - - if (out instanceof DataOutputStream) { - this.out = (DataOutputStream) out; - } else { - this.out = new DataOutputStream(out); - } - this.estimatedLength = estimatedLength; - } - - @Override - public void writeObject(Object obj) throws IOException { - ByteBuf buf = Unpooled.buffer(estimatedLength); - try { - try (ObjectOutputStream oout = new CompactObjectOutputStream(new ByteBufOutputStream(buf))) { - oout.writeObject(obj); - oout.flush(); - } - - int objectSize = buf.readableBytes(); - writeInt(objectSize); - buf.getBytes(0, this, objectSize); - } finally { - buf.release(); - } - } - - @Override - public void write(int b) throws IOException { - out.write(b); - } - - @Override - public void close() throws IOException { - out.close(); - } - - @Override - public void flush() throws IOException { - out.flush(); - } - - public final int size() { - return out.size(); - } - - @Override - public void write(byte[] b, int off, int len) throws IOException { - out.write(b, off, len); - } - - @Override - public void write(byte[] b) throws IOException { - out.write(b); - } - - @Override - public final void writeBoolean(boolean v) throws IOException { - out.writeBoolean(v); - } - - @Override - public final void writeByte(int v) throws IOException { - out.writeByte(v); - } - - @Override - public final void writeBytes(String s) throws IOException { - out.writeBytes(s); - } - - @Override - public final void writeChar(int v) throws IOException { - out.writeChar(v); - } - - @Override - public final void writeChars(String s) throws IOException { - out.writeChars(s); - } - - @Override - public final void writeDouble(double v) throws IOException { - out.writeDouble(v); - } - - @Override - public final void writeFloat(float v) throws IOException { - out.writeFloat(v); - } - - @Override - public final void writeInt(int v) throws IOException { - out.writeInt(v); - } - - @Override - public final void writeLong(long v) throws IOException { - out.writeLong(v); - } - - @Override - public final void writeShort(int v) throws IOException { - out.writeShort(v); - } - - @Override - public final void writeUTF(String str) throws IOException { - out.writeUTF(str); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/serialization/ReferenceMap.java b/codec/src/main/java/io/netty/handler/codec/serialization/ReferenceMap.java deleted file mode 100644 index 28b641e962..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/serialization/ReferenceMap.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.serialization; - -import java.lang.ref.Reference; -import java.util.Collection; -import java.util.Map; -import java.util.Set; - -abstract class ReferenceMap implements Map { - - private final Map> delegate; - - protected ReferenceMap(Map> delegate) { - this.delegate = delegate; - } - - abstract Reference fold(V value); - - private V unfold(Reference ref) { - if (ref == null) { - return null; - } - - return ref.get(); - } - - @Override - public int size() { - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public boolean containsKey(Object key) { - return delegate.containsKey(key); - } - - @Override - public boolean containsValue(Object value) { - throw new UnsupportedOperationException(); - } - - @Override - public V get(Object key) { - return unfold(delegate.get(key)); - } - - @Override - public V put(K key, V value) { - return unfold(delegate.put(key, fold(value))); - } - - @Override - public V remove(Object key) { - return unfold(delegate.remove(key)); - } - - @Override - public void putAll(Map m) { - for (Entry entry : m.entrySet()) { - delegate.put(entry.getKey(), fold(entry.getValue())); - } - } - - @Override - public void clear() { - delegate.clear(); - } - - @Override - public Set keySet() { - return delegate.keySet(); - } - - @Override - public Collection values() { - throw new UnsupportedOperationException(); - } - - @Override - public Set> entrySet() { - throw new UnsupportedOperationException(); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/serialization/SoftReferenceMap.java b/codec/src/main/java/io/netty/handler/codec/serialization/SoftReferenceMap.java deleted file mode 100644 index 5d1c6773d4..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/serialization/SoftReferenceMap.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.serialization; - -import java.lang.ref.Reference; -import java.lang.ref.SoftReference; -import java.util.Map; - -final class SoftReferenceMap extends ReferenceMap { - - SoftReferenceMap(Map> delegate) { - super(delegate); - } - - @Override - Reference fold(V value) { - return new SoftReference<>(value); - } - -} diff --git a/codec/src/main/java/io/netty/handler/codec/serialization/WeakReferenceMap.java b/codec/src/main/java/io/netty/handler/codec/serialization/WeakReferenceMap.java deleted file mode 100644 index 8a8fef97b8..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/serialization/WeakReferenceMap.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.serialization; - -import java.lang.ref.Reference; -import java.lang.ref.WeakReference; -import java.util.Map; - -final class WeakReferenceMap extends ReferenceMap { - - WeakReferenceMap(Map> delegate) { - super(delegate); - } - - @Override - Reference fold(V value) { - return new WeakReference<>(value); - } - -} diff --git a/codec/src/main/java/io/netty/handler/codec/serialization/package-info.java b/codec/src/main/java/io/netty/handler/codec/serialization/package-info.java deleted file mode 100644 index a56cd6e008..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/serialization/package-info.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * Encoder, decoder and their compatibility stream implementations which - * transform a {@link java.io.Serializable} object into a byte buffer and - * vice versa. - */ -package io.netty.handler.codec.serialization; diff --git a/codec/src/main/java/io/netty/handler/codec/string/LineEncoder.java b/codec/src/main/java/io/netty/handler/codec/string/LineEncoder.java deleted file mode 100644 index 6a28b4000c..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/string/LineEncoder.java +++ /dev/null @@ -1,95 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.string; - -import static java.util.Objects.requireNonNull; - -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; -import io.netty.handler.codec.LineBasedFrameDecoder; -import io.netty.handler.codec.MessageToMessageEncoder; -import io.netty.util.CharsetUtil; - -import java.nio.CharBuffer; -import java.nio.charset.Charset; -import java.util.List; - -/** - * Apply a line separator to the requested {@link String} and encode it into a {@link ByteBuf}. - * A typical setup for a text-based line protocol in a TCP/IP socket would be: - *

- * {@link ChannelPipeline} pipeline = ...;
- *
- * // Decoders
- * pipeline.addLast("frameDecoder", new {@link LineBasedFrameDecoder}(80));
- * pipeline.addLast("stringDecoder", new {@link StringDecoder}(CharsetUtil.UTF_8));
- *
- * // Encoder
- * pipeline.addLast("lineEncoder", new {@link LineEncoder}(LineSeparator.UNIX, CharsetUtil.UTF_8));
- * 
- * and then you can use a {@link String} instead of a {@link ByteBuf} - * as a message: - *
- * void channelRead({@link ChannelHandlerContext} ctx, {@link String} msg) {
- *     ch.write("Did you say '" + msg + "'?");
- * }
- * 
- */ -@Sharable -public class LineEncoder extends MessageToMessageEncoder { - - private final Charset charset; - private final byte[] lineSeparator; - - /** - * Creates a new instance with the current system line separator and UTF-8 charset encoding. - */ - public LineEncoder() { - this(LineSeparator.DEFAULT, CharsetUtil.UTF_8); - } - - /** - * Creates a new instance with the specified line separator and UTF-8 charset encoding. - */ - public LineEncoder(LineSeparator lineSeparator) { - this(lineSeparator, CharsetUtil.UTF_8); - } - - /** - * Creates a new instance with the specified character set. - */ - public LineEncoder(Charset charset) { - this(LineSeparator.DEFAULT, charset); - } - - /** - * Creates a new instance with the specified line separator and character set. - */ - public LineEncoder(LineSeparator lineSeparator, Charset charset) { - this.charset = requireNonNull(charset, "charset"); - this.lineSeparator = requireNonNull(lineSeparator, "lineSeparator").value().getBytes(charset); - } - - @Override - protected void encode(ChannelHandlerContext ctx, CharSequence msg, List out) throws Exception { - ByteBuf buffer = ByteBufUtil.encodeString(ctx.alloc(), CharBuffer.wrap(msg), charset, lineSeparator.length); - buffer.writeBytes(lineSeparator); - out.add(buffer); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/string/LineSeparator.java b/codec/src/main/java/io/netty/handler/codec/string/LineSeparator.java deleted file mode 100644 index 847ff45d1d..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/string/LineSeparator.java +++ /dev/null @@ -1,84 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.string; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBufUtil; -import io.netty.util.CharsetUtil; -import io.netty.util.internal.StringUtil; - -/** - * A class to represent line separators in different environments. - */ -public final class LineSeparator { - - /** - * The default line separator in the current system. - */ - public static final LineSeparator DEFAULT = new LineSeparator(StringUtil.NEWLINE); - - /** - * The Unix line separator(LF) - */ - public static final LineSeparator UNIX = new LineSeparator("\n"); - - /** - * The Windows line separator(CRLF) - */ - public static final LineSeparator WINDOWS = new LineSeparator("\r\n"); - - private final String value; - - /** - * Create {@link LineSeparator} with the specified {@code lineSeparator} string. - */ - public LineSeparator(String lineSeparator) { - this.value = requireNonNull(lineSeparator, "lineSeparator"); - } - - /** - * Return the string value of this line separator. - */ - public String value() { - return value; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof LineSeparator)) { - return false; - } - LineSeparator that = (LineSeparator) o; - return value != null ? value.equals(that.value) : that.value == null; - } - - @Override - public int hashCode() { - return value != null ? value.hashCode() : 0; - } - - /** - * Return a hex dump of the line separator in UTF-8 encoding. - */ - @Override - public String toString() { - return ByteBufUtil.hexDump(value.getBytes(CharsetUtil.UTF_8)); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/string/StringDecoder.java b/codec/src/main/java/io/netty/handler/codec/string/StringDecoder.java deleted file mode 100644 index f84ae70cfa..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/string/StringDecoder.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.string; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.handler.codec.ByteToMessageDecoder; -import io.netty.handler.codec.DelimiterBasedFrameDecoder; -import io.netty.handler.codec.LineBasedFrameDecoder; -import io.netty.handler.codec.MessageToMessageDecoder; - -import java.nio.charset.Charset; - -/** - * Decodes a received {@link ByteBuf} into a {@link String}. Please - * note that this decoder must be used with a proper {@link ByteToMessageDecoder} - * such as {@link DelimiterBasedFrameDecoder} or {@link LineBasedFrameDecoder} - * if you are using a stream-based transport such as TCP/IP. A typical setup for a - * text-based line protocol in a TCP/IP socket would be: - *
- * {@link ChannelPipeline} pipeline = ...;
- *
- * // Decoders
- * pipeline.addLast("frameDecoder", new {@link LineBasedFrameDecoder}(80));
- * pipeline.addLast("stringDecoder", new {@link StringDecoder}(CharsetUtil.UTF_8));
- *
- * // Encoder
- * pipeline.addLast("stringEncoder", new {@link StringEncoder}(CharsetUtil.UTF_8));
- * 
- * and then you can use a {@link String} instead of a {@link ByteBuf} - * as a message: - *
- * void channelRead({@link ChannelHandlerContext} ctx, {@link String} msg) {
- *     ch.write("Did you say '" + msg + "'?\n");
- * }
- * 
- */ -@Sharable -public class StringDecoder extends MessageToMessageDecoder { - - // TODO Use CharsetDecoder instead. - private final Charset charset; - - /** - * Creates a new instance with the current system character set. - */ - public StringDecoder() { - this(Charset.defaultCharset()); - } - - /** - * Creates a new instance with the specified character set. - */ - public StringDecoder(Charset charset) { - requireNonNull(charset, "charset"); - this.charset = charset; - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { - ctx.fireChannelRead(msg.toString(charset)); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/string/StringEncoder.java b/codec/src/main/java/io/netty/handler/codec/string/StringEncoder.java deleted file mode 100644 index d27bc8cf3d..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/string/StringEncoder.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.string; - -import static java.util.Objects.requireNonNull; - -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; -import io.netty.handler.codec.LineBasedFrameDecoder; -import io.netty.handler.codec.MessageToMessageEncoder; - -import java.nio.CharBuffer; -import java.nio.charset.Charset; -import java.util.List; - -/** - * Encodes the requested {@link String} into a {@link ByteBuf}. - * A typical setup for a text-based line protocol in a TCP/IP socket would be: - *
- * {@link ChannelPipeline} pipeline = ...;
- *
- * // Decoders
- * pipeline.addLast("frameDecoder", new {@link LineBasedFrameDecoder}(80));
- * pipeline.addLast("stringDecoder", new {@link StringDecoder}(CharsetUtil.UTF_8));
- *
- * // Encoder
- * pipeline.addLast("stringEncoder", new {@link StringEncoder}(CharsetUtil.UTF_8));
- * 
- * and then you can use a {@link String} instead of a {@link ByteBuf} - * as a message: - *
- * void channelRead({@link ChannelHandlerContext} ctx, {@link String} msg) {
- *     ch.write("Did you say '" + msg + "'?\n");
- * }
- * 
- */ -@Sharable -public class StringEncoder extends MessageToMessageEncoder { - - private final Charset charset; - - /** - * Creates a new instance with the current system character set. - */ - public StringEncoder() { - this(Charset.defaultCharset()); - } - - /** - * Creates a new instance with the specified character set. - */ - public StringEncoder(Charset charset) { - requireNonNull(charset, "charset"); - this.charset = charset; - } - - @Override - protected void encode(ChannelHandlerContext ctx, CharSequence msg, List out) throws Exception { - if (msg.length() == 0) { - return; - } - - out.add(ByteBufUtil.encodeString(ctx.alloc(), CharBuffer.wrap(msg), charset)); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/string/package-info.java b/codec/src/main/java/io/netty/handler/codec/string/package-info.java deleted file mode 100644 index ba2b09bf18..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/string/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * Encoder and decoder which transform a {@link java.lang.String} into a - * {@link io.netty.buffer.ByteBuf} and vice versa. - */ -package io.netty.handler.codec.string; 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 deleted file mode 100644 index 2ffad3025d..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/xml/XmlFrameDecoder.java +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.xml; - -import static io.netty.util.internal.ObjectUtil.checkPositive; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.ByteToMessageDecoder; -import io.netty.handler.codec.CorruptedFrameException; -import io.netty.handler.codec.TooLongFrameException; - -/** - * A frame decoder for single separate XML based message streams. - *

- * A couple examples will better help illustrate - * what this decoder actually does. - *

- * Given an input array of bytes split over 3 frames like this: - *

- * +-----+-----+-----------+
- * | <an | Xml | Element/> |
- * +-----+-----+-----------+
- * 
- *

- * this decoder would output a single frame: - *

- *

- * +-----------------+
- * | <anXmlElement/> |
- * +-----------------+
- * 
- * - * Given an input array of bytes split over 5 frames like this: - *
- * +-----+-----+-----------+-----+----------------------------------+
- * | <an | Xml | Element/> | <ro | ot><child>content</child></root> |
- * +-----+-----+-----------+-----+----------------------------------+
- * 
- *

- * this decoder would output two frames: - *

- *

- * +-----------------+-------------------------------------+
- * | <anXmlElement/> | <root><child>content</child></root> |
- * +-----------------+-------------------------------------+
- * 
- * - *

- * 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, - * where an initial xml element opens the stream and only - * gets closed at the end of the session, although this class - * could probably allow for such type of message flow with - * minor modifications. - */ -public class XmlFrameDecoder extends ByteToMessageDecoder { - - private final int maxFrameLength; - - public XmlFrameDecoder(int maxFrameLength) { - this.maxFrameLength = checkPositive(maxFrameLength, "maxFrameLength"); - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - boolean openingBracketFound = false; - boolean atLeastOneXmlElementFound = false; - boolean inCDATASection = false; - long openBracketsCount = 0; - int length = 0; - int leadingWhiteSpaceCount = 0; - final int bufferLength = in.writerIndex(); - - if (bufferLength > maxFrameLength) { - // bufferLength exceeded maxFrameLength; dropping frame - in.skipBytes(in.readableBytes()); - fail(bufferLength); - return; - } - - for (int i = in.readerIndex(); i < bufferLength; i++) { - final byte readByte = in.getByte(i); - if (!openingBracketFound && Character.isWhitespace(readByte)) { - // xml has not started and whitespace char found - leadingWhiteSpaceCount++; - } else if (!openingBracketFound && readByte != '<') { - // garbage found before xml start - fail(ctx); - in.skipBytes(in.readableBytes()); - return; - } else if (!inCDATASection && readByte == '<') { - openingBracketFound = true; - - if (i < bufferLength - 1) { - final byte peekAheadByte = in.getByte(i + 1); - if (peekAheadByte == '/') { - // found we can decrement openBracketsCount - if (in.getByte(peekFurtherAheadIndex) == '>') { - openBracketsCount--; - break; - } - peekFurtherAheadIndex++; - } - } else if (isValidStartCharForXmlElement(peekAheadByte)) { - atLeastOneXmlElementFound = true; - // char after < is a valid xml element start char, - // incrementing openBracketsCount - openBracketsCount++; - } else if (peekAheadByte == '!') { - if (isCommentBlockStart(in, i)) { - // start found - openBracketsCount++; - } else if (isCDATABlockStart(in, i)) { - // start found - openBracketsCount++; - } - } - } else if (!inCDATASection && readByte == '/') { - if (i < bufferLength - 1 && in.getByte(i + 1) == '>') { - // found />, decrementing openBracketsCount - openBracketsCount--; - } - } else if (readByte == '>') { - length = i + 1; - - if (i - 1 > -1) { - final byte peekBehindByte = in.getByte(i - 1); - - if (!inCDATASection) { - if (peekBehindByte == '?') { - // an tag was closed - openBracketsCount--; - } else if (peekBehindByte == '-' && i - 2 > -1 && in.getByte(i - 2) == '-') { - // a was closed - openBracketsCount--; - } - } else if (peekBehindByte == ']' && i - 2 > -1 && in.getByte(i - 2) == ']') { - // a block was closed - openBracketsCount--; - inCDATASection = false; - } - } - - if (atLeastOneXmlElementFound && openBracketsCount == 0) { - // xml is balanced, bailing out - break; - } - } - } - - final int readerIndex = in.readerIndex(); - int xmlElementLength = length - readerIndex; - - if (openBracketsCount == 0 && xmlElementLength > 0) { - if (readerIndex + xmlElementLength >= bufferLength) { - xmlElementLength = in.readableBytes(); - } - final ByteBuf frame = - extractFrame(in, readerIndex + leadingWhiteSpaceCount, xmlElementLength - leadingWhiteSpaceCount); - in.skipBytes(xmlElementLength); - ctx.fireChannelRead(frame); - } - } - - private void fail(long frameLength) { - if (frameLength > 0) { - throw new TooLongFrameException( - "frame length exceeds " + maxFrameLength + ": " + frameLength + " - discarded"); - } else { - throw new TooLongFrameException( - "frame length exceeds " + maxFrameLength + " - discarding"); - } - } - - private static void fail(ChannelHandlerContext ctx) { - ctx.fireExceptionCaught(new CorruptedFrameException("frame contains content before the xml starts")); - } - - private static ByteBuf extractFrame(ByteBuf buffer, int index, int length) { - return buffer.copy(index, length); - } - - /** - * Asks whether the given byte is a valid - * start char for an xml element name. - *

- * Please refer to the - * NameStartChar - * formal definition in the W3C XML spec for further info. - * - * @param b the input char - * @return true if the char is a valid start char - */ - private static boolean isValidStartCharForXmlElement(final byte b) { - return b >= 'a' && b <= 'z' || b >= 'A' && b <= 'Z' || b == ':' || b == '_'; - } - - private static boolean isCommentBlockStart(final ByteBuf in, final int i) { - return i < in.writerIndex() - 3 - && in.getByte(i + 2) == '-' - && in.getByte(i + 3) == '-'; - } - - private static boolean isCDATABlockStart(final ByteBuf in, final int i) { - return i < in.writerIndex() - 8 - && in.getByte(i + 2) == '[' - && in.getByte(i + 3) == 'C' - && in.getByte(i + 4) == 'D' - && in.getByte(i + 5) == 'A' - && in.getByte(i + 6) == 'T' - && in.getByte(i + 7) == 'A' - && in.getByte(i + 8) == '['; - } - -} diff --git a/codec/src/main/java/io/netty/handler/codec/xml/package-info.java b/codec/src/main/java/io/netty/handler/codec/xml/package-info.java deleted file mode 100644 index 91d77f816a..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/xml/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * Xml specific codecs. - */ -package io.netty.handler.codec.xml; diff --git a/codec/src/test/java/io/netty/handler/codec/ByteToMessageCodecTest.java b/codec/src/test/java/io/netty/handler/codec/ByteToMessageCodecTest.java deleted file mode 100644 index 2daddc6dc5..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/ByteToMessageCodecTest.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.embedded.EmbeddedChannel; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class ByteToMessageCodecTest { - - @Test - public void testSharable() { - assertThrows(IllegalStateException.class, InvalidByteToMessageCodec::new); - } - - @Test - public void testSharable2() { - assertThrows(IllegalStateException.class, InvalidByteToMessageCodec2::new); - } - - @Test - public void testForwardPendingData() { - ByteToMessageCodec codec = new ByteToMessageCodec() { - @Override - protected void encode(ChannelHandlerContext ctx, Integer msg, ByteBuf out) throws Exception { - out.writeInt(msg); - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - if (in.readableBytes() >= 4) { - ctx.fireChannelRead(in.readInt()); - } - } - }; - - ByteBuf buffer = Unpooled.buffer(); - buffer.writeInt(1); - buffer.writeByte('0'); - - EmbeddedChannel ch = new EmbeddedChannel(codec); - assertTrue(ch.writeInbound(buffer)); - ch.pipeline().remove(codec); - assertTrue(ch.finish()); - assertEquals(1, (Integer) ch.readInbound()); - - ByteBuf buf = ch.readInbound(); - assertEquals(Unpooled.wrappedBuffer(new byte[]{'0'}), buf); - buf.release(); - assertNull(ch.readInbound()); - assertNull(ch.readOutbound()); - } - - @ChannelHandler.Sharable - private static final class InvalidByteToMessageCodec extends ByteToMessageCodec { - InvalidByteToMessageCodec() { - super(true); - } - - @Override - protected void encode(ChannelHandlerContext ctx, Integer msg, ByteBuf out) throws Exception { } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { } - } - - @ChannelHandler.Sharable - private static final class InvalidByteToMessageCodec2 extends ByteToMessageCodec { - InvalidByteToMessageCodec2() { - super(Integer.class, true); - } - - @Override - protected void encode(ChannelHandlerContext ctx, Integer msg, ByteBuf out) throws Exception { } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { } - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/ByteToMessageDecoderForBufferTest.java b/codec/src/test/java/io/netty/handler/codec/ByteToMessageDecoderForBufferTest.java deleted file mode 100644 index fc840cc66f..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/ByteToMessageDecoderForBufferTest.java +++ /dev/null @@ -1,653 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec; - -import io.netty.buffer.api.Buffer; -import io.netty.buffer.api.BufferAllocator; -import io.netty.buffer.api.BufferStub; -import io.netty.buffer.api.CompositeBuffer; -import io.netty.buffer.api.Send; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.channel.socket.ChannelInputShutdownEvent; -import io.netty.handler.codec.ByteToMessageDecoderForBuffer.Cumulator; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; - -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingDeque; -import java.util.concurrent.ThreadLocalRandom; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Consumer; -import java.util.stream.Stream; - -import static io.netty.buffer.api.BufferAllocator.offHeapPooled; -import static io.netty.buffer.api.BufferAllocator.offHeapUnpooled; -import static io.netty.buffer.api.BufferAllocator.onHeapPooled; -import static io.netty.buffer.api.BufferAllocator.onHeapUnpooled; -import static io.netty.buffer.api.CompositeBuffer.compose; -import static io.netty.handler.codec.ByteToMessageDecoderForBuffer.COMPOSITE_CUMULATOR; -import static io.netty.handler.codec.ByteToMessageDecoderForBuffer.MERGE_CUMULATOR; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.params.provider.Arguments.arguments; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class ByteToMessageDecoderForBufferTest { - private static final String PARAMETERIZED_NAME = "allocator = {0}, cumulator = {1}"; - - private BufferAllocator allocator; - - public static Stream allocators() { - return Stream.of( - arguments(onHeapUnpooled(), MERGE_CUMULATOR), - arguments(onHeapUnpooled(), COMPOSITE_CUMULATOR), - arguments(offHeapUnpooled(), MERGE_CUMULATOR), - arguments(offHeapUnpooled(), COMPOSITE_CUMULATOR), - arguments(onHeapPooled(), MERGE_CUMULATOR), - arguments(onHeapPooled(), COMPOSITE_CUMULATOR), - arguments(offHeapPooled(), MERGE_CUMULATOR), - arguments(offHeapPooled(), COMPOSITE_CUMULATOR) - ); - } - - @BeforeEach - public void closeAllocator() { - if (allocator != null) { - allocator.close(); - } - } - - @ParameterizedTest(name = PARAMETERIZED_NAME) - @MethodSource("allocators") - public void removeSelf(BufferAllocator allocator, Cumulator cumulator) { - this.allocator = allocator; - EmbeddedChannel channel = new EmbeddedChannel(new ByteToMessageDecoderForBuffer(cumulator) { - private boolean removed; - - @Override - protected void decode(ChannelHandlerContext ctx, Buffer in) { - assertFalse(removed); - in.readByte(); - removed = true; - ctx.pipeline().remove(this); - } - }); - - try (Buffer buf = newBufferWithData(allocator, 'a', 'b', 'c')) { - channel.writeInbound(buf.copy()); - try (Buffer b = channel.readInbound()) { - buf.readByte(); - assertContentEquals(b, buf); - } - } - } - - @ParameterizedTest(name = PARAMETERIZED_NAME) - @MethodSource("allocators") - public void removeSelfThenWriteToBuffer(BufferAllocator allocator, Cumulator cumulator) { - this.allocator = allocator; - try (Buffer buf = newBufferWithData(allocator, 4, 'a', 'b', 'c')) { - EmbeddedChannel channel = new EmbeddedChannel(new ByteToMessageDecoderForBuffer(cumulator) { - private boolean removed; - - @Override - protected void decode(ChannelHandlerContext ctx, Buffer in) { - assertFalse(removed); - in.readByte(); - removed = true; - ctx.pipeline().remove(this); - - // This should not let it keep calling decode - buf.writeByte((byte) 'd'); - } - }); - - channel.writeInbound(buf.copy()); - try (Buffer expected = allocator.allocate(8); Buffer b = channel.readInbound()) { - expected.writeBytes(new byte[]{'b', 'c'}); - assertContentEquals(expected, b); - } - } - } - - @ParameterizedTest(name = PARAMETERIZED_NAME) - @MethodSource("allocators") - public void internalBufferClearPostReadFully(BufferAllocator allocator, Cumulator cumulator) { - this.allocator = allocator; - Buffer buf = newBufferWithData(allocator, 'a'); - EmbeddedChannel channel = newInternalBufferTestChannel(cumulator, Buffer::readByte); - assertFalse(channel.writeInbound(buf)); - assertFalse(channel.finish()); - assertFalse(buf.isAccessible()); - } - - @ParameterizedTest(name = PARAMETERIZED_NAME) - @MethodSource("allocators") - public void internalBufferClearReadPartly(BufferAllocator allocator, Cumulator cumulator) { - this.allocator = allocator; - Buffer buf = newBufferWithData(allocator, 'a', 'b'); - EmbeddedChannel channel = newInternalBufferTestChannel(cumulator, Buffer::readByte); - assertTrue(channel.writeInbound(buf)); - try (Buffer expected = newBufferWithData(allocator, 'b'); Buffer b = channel.readInbound()) { - assertContentEquals(b, expected); - assertNull(channel.readInbound()); - assertFalse(channel.finish()); - } - assertFalse(buf.isAccessible()); - } - - private static EmbeddedChannel newInternalBufferTestChannel( - Cumulator cumulator, Consumer readBeforeRemove) { - return new EmbeddedChannel(new ByteToMessageDecoderForBuffer(cumulator) { - @Override - protected void decode(ChannelHandlerContext ctx, Buffer in) { - Buffer buffer = internalBuffer(); - assertNotNull(buffer); - assertTrue(buffer.isAccessible()); - readBeforeRemove.accept(in); - // Removal from pipeline should clear internal buffer - ctx.pipeline().remove(this); - } - - @Override - protected void handlerRemoved0(ChannelHandlerContext ctx) { - assertCumulationReleased(internalBuffer()); - } - }); - } - - @ParameterizedTest(name = PARAMETERIZED_NAME) - @MethodSource("allocators") - public void handlerRemovedWillNotReleaseBufferIfDecodeInProgress(BufferAllocator allocator, Cumulator cumulator) { - this.allocator = allocator; - EmbeddedChannel channel = new EmbeddedChannel(new ByteToMessageDecoderForBuffer(cumulator) { - @Override - protected void decode(ChannelHandlerContext ctx, Buffer in) { - ctx.pipeline().remove(this); - assertTrue(in.isAccessible()); - } - - @Override - protected void handlerRemoved0(ChannelHandlerContext ctx) { - assertCumulationReleased(internalBuffer()); - } - }); - - Buffer buf = newBufferWithRandomBytes(allocator); - assertTrue(channel.writeInbound(buf)); - assertTrue(channel.finishAndReleaseAll()); - assertFalse(buf.isAccessible()); - } - - private static void assertCumulationReleased(Buffer buffer) { - assertTrue(buffer == null || !buffer.isAccessible(), "unexpected value: " + buffer); - } - - @ParameterizedTest(name = PARAMETERIZED_NAME) - @MethodSource("allocators") - public void fireChannelReadCompleteOnInactive(BufferAllocator allocator, Cumulator cumulator) throws Exception { - this.allocator = allocator; - final BlockingQueue queue = new LinkedBlockingDeque<>(); - final Buffer buf = newBufferWithData(allocator, 'a', 'b'); - EmbeddedChannel channel = new EmbeddedChannel(new ByteToMessageDecoderForBuffer(cumulator) { - @Override - protected void decode(ChannelHandlerContext ctx, Buffer in) { - int readable = in.readableBytes(); - assertTrue(readable > 0); - in.readerOffset(in.readerOffset() + readable); - } - - @Override - protected void decodeLast(ChannelHandlerContext ctx, Buffer in) { - assertFalse(in.readableBytes() > 0); - ctx.fireChannelRead("data"); - } - }, new ChannelHandler() { - @Override - public void channelInactive(ChannelHandlerContext ctx) { - queue.add(3); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - queue.add(1); - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) { - if (!ctx.channel().isActive()) { - queue.add(2); - } - } - }); - assertFalse(channel.writeInbound(buf)); - assertFalse(channel.finish()); - assertEquals(1, (int) queue.take()); - assertEquals(2, (int) queue.take()); - assertEquals(3, (int) queue.take()); - assertTrue(queue.isEmpty()); - assertFalse(buf.isAccessible()); - } - - // See https://github.com/netty/netty/issues/4635 - @ParameterizedTest(name = PARAMETERIZED_NAME) - @MethodSource("allocators") - public void removeWhileInCallDecode(BufferAllocator allocator, Cumulator cumulator) { - this.allocator = allocator; - final Object upgradeMessage = new Object(); - final ByteToMessageDecoderForBuffer decoder = new ByteToMessageDecoderForBuffer(cumulator) { - @Override - protected void decode(ChannelHandlerContext ctx, Buffer in) { - assertEquals('a', in.readByte()); - ctx.fireChannelRead(upgradeMessage); - } - }; - - EmbeddedChannel channel = new EmbeddedChannel(decoder, new ChannelHandler() { - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - if (msg == upgradeMessage) { - ctx.pipeline().remove(decoder); - return; - } - ctx.fireChannelRead(msg); - } - }); - - try (Buffer buf = newBufferWithData(allocator, 'a', 'b', 'c')) { - assertTrue(channel.writeInbound(buf.copy())); - try (Buffer b = channel.readInbound()) { - buf.readByte(); - assertContentEquals(b, buf); - assertFalse(channel.finish()); - } - } - } - - @ParameterizedTest(name = PARAMETERIZED_NAME) - @MethodSource("allocators") - public void decodeLastEmptyBuffer(BufferAllocator allocator, Cumulator cumulator) { - this.allocator = allocator; - EmbeddedChannel channel = new EmbeddedChannel(new ByteToMessageDecoderForBuffer(cumulator) { - @Override - protected void decode(ChannelHandlerContext ctx, Buffer in) { - int readable = in.readableBytes(); - assertTrue(readable > 0); - ctx.fireChannelRead(transferBytes(ctx.bufferAllocator(), in, readable)); - } - }); - - try (Buffer buf = newBufferWithRandomBytes(allocator)) { - assertTrue(channel.writeInbound(buf.copy())); - try (Buffer b = channel.readInbound()) { - assertContentEquals(b, buf); - } - assertNull(channel.readInbound()); - assertFalse(channel.finish()); - assertNull(channel.readInbound()); - } - } - - @ParameterizedTest(name = PARAMETERIZED_NAME) - @MethodSource("allocators") - public void decodeLastNonEmptyBuffer(BufferAllocator allocator, Cumulator cumulator) { - this.allocator = allocator; - EmbeddedChannel channel = new EmbeddedChannel(new ByteToMessageDecoderForBuffer(cumulator) { - private boolean decodeLast; - - @Override - protected void decode(ChannelHandlerContext ctx, Buffer in) { - int readable = in.readableBytes(); - assertTrue(readable > 0); - if (!decodeLast && readable == 1) { - return; - } - final int length = decodeLast ? readable : readable - 1; - ctx.fireChannelRead(transferBytes(ctx.bufferAllocator(), in, length)); - } - - @Override - protected void decodeLast(ChannelHandlerContext ctx, Buffer in) throws Exception { - assertFalse(decodeLast); - decodeLast = true; - super.decodeLast(ctx, in); - } - }); - try (Buffer buf = newBufferWithRandomBytes(allocator)) { - assertTrue(channel.writeInbound(buf.copy())); - try (Buffer b = channel.readInbound()) { - assertContentEquals(b, buf.copy(0, buf.readableBytes() - 1)); - } - - assertNull(channel.readInbound()); - assertTrue(channel.finish()); - - try (Buffer b1 = channel.readInbound()) { - assertContentEquals(b1, buf.copy(buf.readableBytes() - 1, 1)); - } - - assertNull(channel.readInbound()); - } - } - - @ParameterizedTest(name = PARAMETERIZED_NAME) - @MethodSource("allocators") - public void readOnlyBuffer(BufferAllocator allocator, Cumulator cumulator) { - this.allocator = allocator; - EmbeddedChannel channel = new EmbeddedChannel(new ByteToMessageDecoderForBuffer(cumulator) { - @Override - protected void decode(ChannelHandlerContext ctx, Buffer in) { } - }); - assertFalse(channel.writeInbound(newBufferWithData(allocator, 8, 'a').makeReadOnly())); - assertFalse(channel.writeInbound(newBufferWithData(allocator, 'b'))); - assertFalse(channel.writeInbound(newBufferWithData(allocator, 'c').makeReadOnly())); - assertFalse(channel.finish()); - } - - static class WriteFailingBuffer extends BufferStub { - private final Error error = new Error(); - private int untilFailure; - - WriteFailingBuffer(BufferAllocator allocator, int untilFailure, int capacity) { - super(allocator.allocate(capacity)); - this.untilFailure = untilFailure; - } - - @Override - public Buffer writeBytes(Buffer source) { - if (--untilFailure <= 0) { - throw error; - } - return super.writeBytes(source); - } - - Error writeError() { - return error; - } - } - - @ParameterizedTest(name = PARAMETERIZED_NAME) - @MethodSource("allocators") - public void releaseWhenMergeCumulateThrows(BufferAllocator allocator) { - this.allocator = allocator; - try (WriteFailingBuffer oldCumulation = new WriteFailingBuffer(allocator, 1, 64)) { - oldCumulation.writeByte((byte) 0); - Buffer in = newBufferWithRandomBytes(allocator, 12); - final Error err = assertThrows(Error.class, () -> MERGE_CUMULATOR.cumulate(allocator, oldCumulation, in)); - assertSame(oldCumulation.writeError(), err); - assertFalse(in.isAccessible()); - assertTrue(oldCumulation.isAccessible()); - } - } - - @ParameterizedTest(name = PARAMETERIZED_NAME) - @MethodSource("allocators") - public void releaseWhenMergeCumulateThrowsInExpand(BufferAllocator allocator) { - this.allocator = allocator; - final WriteFailingBuffer cumulation = new WriteFailingBuffer(allocator, 1, 16) { - @Override - public int readableBytes() { - return 1; - } - }; - - Buffer in = newBufferWithRandomBytes(allocator, 12); - Throwable thrown = null; - try { - BufferAllocator mockAlloc = mock(BufferAllocator.class); - MERGE_CUMULATOR.cumulate(mockAlloc, cumulation, in); - } catch (Throwable t) { - thrown = t; - } - - assertFalse(in.isAccessible()); - - assertSame(cumulation.writeError(), thrown); - assertTrue(cumulation.isAccessible()); - } - - @ParameterizedTest(name = PARAMETERIZED_NAME) - @MethodSource("allocators") - public void releaseWhenMergeCumulateThrowsInExpandAndCumulatorIsReadOnly(BufferAllocator allocator) { - this.allocator = allocator; - Buffer oldCumulation = newBufferWithData(allocator, 8, (char) 1).makeReadOnly(); - final WriteFailingBuffer newCumulation = new WriteFailingBuffer(allocator, 1, 16) ; - - Buffer in = newBufferWithRandomBytes(allocator, 12); - Throwable thrown = null; - try { - BufferAllocator mockAlloc = mock(BufferAllocator.class); - when(mockAlloc.allocate(anyInt())).thenReturn(newCumulation); - MERGE_CUMULATOR.cumulate(mockAlloc, oldCumulation, in); - } catch (Throwable t) { - thrown = t; - } - - assertFalse(in.isAccessible()); - - assertSame(newCumulation.writeError(), thrown); - assertFalse(oldCumulation.isAccessible()); - assertTrue(newCumulation.isAccessible()); - } - - @ParameterizedTest(name = PARAMETERIZED_NAME) - @MethodSource("allocators") - public void releaseWhenCompositeCumulateThrows(BufferAllocator allocator) { - this.allocator = allocator; - final Error error = new Error(); - try (CompositeBuffer cumulation = compose(allocator, newBufferWithRandomBytes(allocator).send())) { - Buffer in = new BufferStub(newBufferWithRandomBytes(allocator, 12)) { - @Override - public Send send() { - return new Send<>() { - @Override - public Buffer receive() { - throw error; - } - - @Override - public void close() { - } - - @Override - public boolean referentIsInstanceOf(Class cls) { - return Buffer.class.isAssignableFrom(cls); - } - }; - } - }; - - final Error err = assertThrows(Error.class, () -> COMPOSITE_CUMULATOR.cumulate(allocator, cumulation, in)); - assertSame(error, err); - assertFalse(in.isAccessible()); - } - } - - @ParameterizedTest(name = PARAMETERIZED_NAME) - @MethodSource("allocators") - public void doesNotOverRead(BufferAllocator allocator, Cumulator cumulator) { - this.allocator = allocator; - class ReadInterceptingHandler implements ChannelHandler { - private int readsTriggered; - - @Override - public void read(ChannelHandlerContext ctx) { - readsTriggered++; - ctx.read(); - } - } - ReadInterceptingHandler interceptor = new ReadInterceptingHandler(); - - EmbeddedChannel channel = new EmbeddedChannel(); - channel.config().setAutoRead(false); - channel.pipeline().addLast(interceptor, new FixedLengthFrameDecoder(3, cumulator)); - assertEquals(0, interceptor.readsTriggered); - - // 0 complete frames, 1 partial frame: SHOULD trigger a read - channel.writeInbound(newBufferWithData(allocator, new byte[] { 0, 1 })); - assertEquals(1, interceptor.readsTriggered); - - // 2 complete frames, 0 partial frames: should NOT trigger a read - channel.writeInbound(newBufferWithData(allocator, new byte[]{2}), - newBufferWithData(allocator, new byte[]{3, 4, 5})); - assertEquals(1, interceptor.readsTriggered); - - // 1 complete frame, 1 partial frame: should NOT trigger a read - channel.writeInbound(newBufferWithData(allocator, new byte[] { 6, 7, 8 }), - newBufferWithData(allocator, new byte[] { 9 })); - assertEquals(1, interceptor.readsTriggered); - - // 1 complete frame, 1 partial frame: should NOT trigger a read - channel.writeInbound(newBufferWithData(allocator, new byte[] { 10, 11 }), - newBufferWithData(allocator, new byte[] { 12 })); - assertEquals(1, interceptor.readsTriggered); - - // 0 complete frames, 1 partial frame: SHOULD trigger a read - channel.writeInbound(newBufferWithData(allocator, new byte[] { 13 })); - assertEquals(2, interceptor.readsTriggered); - - // 1 complete frame, 0 partial frames: should NOT trigger a read - channel.writeInbound(newBufferWithData(allocator, new byte[] { 14 })); - assertEquals(2, interceptor.readsTriggered); - - for (int i = 0; i < 5; i++) { - try (Buffer read = channel.readInbound()) { - assertEquals(i * 3, read.getByte(0)); - assertEquals(i * 3 + 1, read.getByte(1)); - assertEquals(i * 3 + 2, read.getByte(2)); - } - } - assertFalse(channel.finish()); - } - - @ParameterizedTest(name = PARAMETERIZED_NAME) - @MethodSource("allocators") - public void testDisorder(BufferAllocator allocator, Cumulator cumulator) { - this.allocator = allocator; - ByteToMessageDecoderForBuffer decoder = new ByteToMessageDecoderForBuffer(cumulator) { - int count; - - //read 4 byte then remove this decoder - @Override - protected void decode(ChannelHandlerContext ctx, Buffer in) { - ctx.fireChannelRead(in.readByte()); - if (++count >= 4) { - ctx.pipeline().remove(this); - } - } - }; - EmbeddedChannel channel = new EmbeddedChannel(decoder); - assertTrue(channel.writeInbound(newBufferWithData(allocator, new byte[]{1, 2, 3, 4, 5}))); - assertEquals((byte) 1, (Byte) channel.readInbound()); - assertEquals((byte) 2, (Byte) channel.readInbound()); - assertEquals((byte) 3, (Byte) channel.readInbound()); - assertEquals((byte) 4, (Byte) channel.readInbound()); - Buffer buffer5 = channel.readInbound(); - assertNotNull(buffer5); - assertEquals((byte) 5, buffer5.readByte()); - assertFalse(buffer5.readableBytes() > 0); - assertTrue(buffer5.isAccessible()); - assertFalse(channel.finish()); - } - - @ParameterizedTest(name = PARAMETERIZED_NAME) - @MethodSource("allocators") - public void testDecodeLast(BufferAllocator allocator, Cumulator cumulator) { - this.allocator = allocator; - final AtomicBoolean removeHandler = new AtomicBoolean(); - EmbeddedChannel channel = new EmbeddedChannel(new ByteToMessageDecoderForBuffer(cumulator) { - - @Override - protected void decode(ChannelHandlerContext ctx, Buffer in) { - if (removeHandler.get()) { - ctx.pipeline().remove(this); - } - } - }); - - try (Buffer buf = newBufferWithRandomBytes(allocator)) { - assertFalse(channel.writeInbound(buf.copy())); - assertNull(channel.readInbound()); - removeHandler.set(true); - // This should trigger channelInputClosed(...) - channel.pipeline().fireUserEventTriggered(ChannelInputShutdownEvent.INSTANCE); - - assertTrue(channel.finish()); - try (Buffer b = channel.readInbound()) { - assertContentEquals(buf, b); - } - assertNull(channel.readInbound()); - } - } - - private static Buffer newBufferWithRandomBytes(BufferAllocator allocator) { - return newBufferWithRandomBytes(allocator, 1024); - } - - private static Buffer newBufferWithRandomBytes(BufferAllocator allocator, int length) { - final Buffer buf = allocator.allocate(length); - byte[] bytes = new byte[length]; - ThreadLocalRandom.current().nextBytes(bytes); - buf.writeBytes(bytes); - return buf; - } - - private static Buffer newBufferWithData(BufferAllocator allocator, int capacity, char... data) { - final Buffer buf = allocator.allocate(capacity); - for (char datum : data) { - buf.writeByte((byte) datum); - } - return buf; - } - - private static Buffer newBufferWithData(BufferAllocator allocator, char... data) { - return newBufferWithData(allocator, data.length, data); - } - - private static Buffer newBufferWithData(BufferAllocator allocator, byte... data) { - return allocator.allocate(data.length).writeBytes(data); - } - - private static void assertContentEquals(Buffer actual, Buffer expected) { - assertArrayEquals(readByteArray(expected), readByteArray(actual)); - } - - private static byte[] readByteArray(Buffer buf) { - byte[] bs = new byte[buf.readableBytes()]; - buf.copyInto(buf.readerOffset(), bs, 0, bs.length); - buf.readerOffset(buf.writerOffset()); - return bs; - } - - private static Buffer transferBytes(BufferAllocator allocator, Buffer src, int length) { - final Buffer msg = allocator.allocate(length); - src.copyInto(src.readerOffset(), msg, 0, length); - msg.writerOffset(length); - src.readerOffset(length); - return msg; - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/ByteToMessageDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/ByteToMessageDecoderTest.java deleted file mode 100644 index c85e7ec1c7..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/ByteToMessageDecoderTest.java +++ /dev/null @@ -1,540 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import io.netty.buffer.AbstractByteBufAllocator; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.CompositeByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty.buffer.UnpooledHeapByteBuf; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.channel.socket.ChannelInputShutdownEvent; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingDeque; -import java.util.concurrent.ThreadLocalRandom; -import java.util.concurrent.atomic.AtomicBoolean; - -import static io.netty.buffer.Unpooled.wrappedBuffer; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - -public class ByteToMessageDecoderTest { - - @Test - public void testRemoveItself() { - EmbeddedChannel channel = new EmbeddedChannel(new ByteToMessageDecoder() { - private boolean removed; - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) { - assertFalse(removed); - in.readByte(); - ctx.pipeline().remove(this); - removed = true; - } - }); - - ByteBuf buf = Unpooled.wrappedBuffer(new byte[] {'a', 'b', 'c'}); - channel.writeInbound(buf.copy()); - ByteBuf b = channel.readInbound(); - assertEquals(b, buf.skipBytes(1)); - b.release(); - buf.release(); - } - - @Test - public void testRemoveItselfWriteBuffer() { - final ByteBuf buf = Unpooled.buffer().writeBytes(new byte[] {'a', 'b', 'c'}); - EmbeddedChannel channel = new EmbeddedChannel(new ByteToMessageDecoder() { - private boolean removed; - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) { - assertFalse(removed); - in.readByte(); - ctx.pipeline().remove(this); - - // This should not let it keep call decode - buf.writeByte('d'); - removed = true; - } - }); - - channel.writeInbound(buf.copy()); - ByteBuf expected = Unpooled.wrappedBuffer(new byte[] {'b', 'c'}); - ByteBuf b = channel.readInbound(); - assertEquals(expected, b); - expected.release(); - buf.release(); - b.release(); - } - - /** - * Verifies that internal buffer of the ByteToMessageDecoder is released once decoder is removed from pipeline. In - * this case input is read fully. - */ - @Test - public void testInternalBufferClearReadAll() { - final ByteBuf buf = Unpooled.buffer().writeBytes(new byte[] {'a'}); - EmbeddedChannel channel = newInternalBufferTestChannel(); - assertFalse(channel.writeInbound(buf)); - assertFalse(channel.finish()); - } - - /** - * Verifies that internal buffer of the ByteToMessageDecoder is released once decoder is removed from pipeline. In - * this case input was not fully read. - */ - @Test - public void testInternalBufferClearReadPartly() { - final ByteBuf buf = Unpooled.buffer().writeBytes(new byte[] {'a', 'b'}); - EmbeddedChannel channel = newInternalBufferTestChannel(); - assertTrue(channel.writeInbound(buf)); - assertTrue(channel.finish()); - ByteBuf expected = Unpooled.wrappedBuffer(new byte[] {'b'}); - ByteBuf b = channel.readInbound(); - assertEquals(expected, b); - assertNull(channel.readInbound()); - expected.release(); - b.release(); - } - - private EmbeddedChannel newInternalBufferTestChannel() { - return new EmbeddedChannel(new ByteToMessageDecoder() { - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) { - ByteBuf byteBuf = internalBuffer(); - assertEquals(1, byteBuf.refCnt()); - in.readByte(); - // Removal from pipeline should clear internal buffer - ctx.pipeline().remove(this); - } - - @Override - protected void handlerRemoved0(ChannelHandlerContext ctx) { - assertCumulationReleased(internalBuffer()); - } - }); - } - - @Test - public void handlerRemovedWillNotReleaseBufferIfDecodeInProgress() { - EmbeddedChannel channel = new EmbeddedChannel(new ByteToMessageDecoder() { - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - ctx.pipeline().remove(this); - assertTrue(in.refCnt() != 0); - } - - @Override - protected void handlerRemoved0(ChannelHandlerContext ctx) { - assertCumulationReleased(internalBuffer()); - } - }); - byte[] bytes = new byte[1024]; - ThreadLocalRandom.current().nextBytes(bytes); - - assertTrue(channel.writeInbound(Unpooled.wrappedBuffer(bytes))); - assertTrue(channel.finishAndReleaseAll()); - } - - private static void assertCumulationReleased(ByteBuf byteBuf) { - assertTrue(byteBuf == null || byteBuf == Unpooled.EMPTY_BUFFER || byteBuf.refCnt() == 0, - "unexpected value: " + byteBuf); - } - - @Test - public void testFireChannelReadCompleteOnInactive() throws InterruptedException { - final BlockingQueue queue = new LinkedBlockingDeque<>(); - final ByteBuf buf = Unpooled.buffer().writeBytes(new byte[] {'a', 'b'}); - EmbeddedChannel channel = new EmbeddedChannel(new ByteToMessageDecoder() { - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) { - int readable = in.readableBytes(); - assertTrue(readable > 0); - in.skipBytes(readable); - } - - @Override - protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in) { - assertFalse(in.isReadable()); - ctx.fireChannelRead("data"); - } - }, new ChannelHandler() { - @Override - public void channelInactive(ChannelHandlerContext ctx) { - queue.add(3); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - queue.add(1); - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) { - if (!ctx.channel().isActive()) { - queue.add(2); - } - } - }); - assertFalse(channel.writeInbound(buf)); - channel.finish(); - assertEquals(1, (int) queue.take()); - assertEquals(2, (int) queue.take()); - assertEquals(3, (int) queue.take()); - assertTrue(queue.isEmpty()); - } - - // See https://github.com/netty/netty/issues/4635 - @Test - public void testRemoveWhileInCallDecode() { - final Object upgradeMessage = new Object(); - final ByteToMessageDecoder decoder = new ByteToMessageDecoder() { - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) { - assertEquals('a', in.readByte()); - ctx.fireChannelRead(upgradeMessage); - } - }; - - EmbeddedChannel channel = new EmbeddedChannel(decoder, new ChannelHandler() { - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - if (msg == upgradeMessage) { - ctx.pipeline().remove(decoder); - return; - } - ctx.fireChannelRead(msg); - } - }); - - ByteBuf buf = Unpooled.wrappedBuffer(new byte[] { 'a', 'b', 'c' }); - assertTrue(channel.writeInbound(buf.copy())); - ByteBuf b = channel.readInbound(); - assertEquals(b, buf.skipBytes(1)); - assertFalse(channel.finish()); - buf.release(); - b.release(); - } - - @Test - public void testDecodeLastEmptyBuffer() { - EmbeddedChannel channel = new EmbeddedChannel(new ByteToMessageDecoder() { - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) { - int readable = in.readableBytes(); - assertTrue(readable > 0); - ctx.fireChannelRead(in.readBytes(readable)); - } - }); - byte[] bytes = new byte[1024]; - ThreadLocalRandom.current().nextBytes(bytes); - - assertTrue(channel.writeInbound(Unpooled.copiedBuffer(bytes))); - assertBuffer(Unpooled.wrappedBuffer(bytes), channel.readInbound()); - assertNull(channel.readInbound()); - assertFalse(channel.finish()); - assertNull(channel.readInbound()); - } - - @Test - public void testDecodeLastNonEmptyBuffer() { - EmbeddedChannel channel = new EmbeddedChannel(new ByteToMessageDecoder() { - private boolean decodeLast; - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) { - int readable = in.readableBytes(); - assertTrue(readable > 0); - if (!decodeLast && readable == 1) { - return; - } - ctx.fireChannelRead(in.readBytes(decodeLast ? readable : readable - 1)); - } - - @Override - protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - assertFalse(decodeLast); - decodeLast = true; - super.decodeLast(ctx, in); - } - }); - byte[] bytes = new byte[1024]; - ThreadLocalRandom.current().nextBytes(bytes); - - assertTrue(channel.writeInbound(Unpooled.copiedBuffer(bytes))); - assertBuffer(Unpooled.wrappedBuffer(bytes, 0, bytes.length - 1), channel.readInbound()); - assertNull(channel.readInbound()); - assertTrue(channel.finish()); - assertBuffer(Unpooled.wrappedBuffer(bytes, bytes.length - 1, 1), channel.readInbound()); - assertNull(channel.readInbound()); - } - - private static void assertBuffer(ByteBuf expected, ByteBuf buffer) { - try { - assertEquals(expected, buffer); - } finally { - buffer.release(); - expected.release(); - } - } - - @Test - public void testReadOnlyBuffer() { - EmbeddedChannel channel = new EmbeddedChannel(new ByteToMessageDecoder() { - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) { } - }); - assertFalse(channel.writeInbound(Unpooled.buffer(8).writeByte(1).asReadOnly())); - assertFalse(channel.writeInbound(Unpooled.wrappedBuffer(new byte[] { (byte) 2 }))); - assertFalse(channel.finish()); - } - - static class WriteFailingByteBuf extends UnpooledHeapByteBuf { - private final Error error = new Error(); - private int untilFailure; - - WriteFailingByteBuf(int untilFailure, int capacity) { - super(UnpooledByteBufAllocator.DEFAULT, capacity, capacity); - this.untilFailure = untilFailure; - } - - @Override - public ByteBuf setBytes(int index, ByteBuf src, int srcIndex, int length) { - if (--untilFailure <= 0) { - throw error; - } - return super.setBytes(index, src, srcIndex, length); - } - - Error writeError() { - return error; - } - } - - @Test - public void releaseWhenMergeCumulateThrows() { - WriteFailingByteBuf oldCumulation = new WriteFailingByteBuf(1, 64); - oldCumulation.writeZero(1); - ByteBuf in = Unpooled.buffer().writeZero(12); - - Throwable thrown = null; - try { - ByteToMessageDecoder.MERGE_CUMULATOR.cumulate(UnpooledByteBufAllocator.DEFAULT, oldCumulation, in); - } catch (Throwable t) { - thrown = t; - } - - assertSame(oldCumulation.writeError(), thrown); - assertEquals(0, in.refCnt()); - assertEquals(1, oldCumulation.refCnt()); - oldCumulation.release(); - } - - @Test - public void releaseWhenMergeCumulateThrowsInExpand() { - releaseWhenMergeCumulateThrowsInExpand(1, true); - releaseWhenMergeCumulateThrowsInExpand(2, true); - releaseWhenMergeCumulateThrowsInExpand(3, false); // sentinel test case - } - - private void releaseWhenMergeCumulateThrowsInExpand(int untilFailure, boolean shouldFail) { - ByteBuf oldCumulation = UnpooledByteBufAllocator.DEFAULT.heapBuffer(8, 8).writeZero(1); - final WriteFailingByteBuf newCumulation = new WriteFailingByteBuf(untilFailure, 16); - - ByteBufAllocator allocator = new AbstractByteBufAllocator(false) { - @Override - public boolean isDirectBufferPooled() { - return false; - } - - @Override - protected ByteBuf newHeapBuffer(int initialCapacity, int maxCapacity) { - return newCumulation; - } - - @Override - protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) { - throw new UnsupportedOperationException(); - } - }; - - ByteBuf in = Unpooled.buffer().writeZero(12); - Throwable thrown = null; - try { - ByteToMessageDecoder.MERGE_CUMULATOR.cumulate(allocator, oldCumulation, in); - } catch (Throwable t) { - thrown = t; - } - - assertEquals(0, in.refCnt()); - - if (shouldFail) { - assertSame(newCumulation.writeError(), thrown); - assertEquals(1, oldCumulation.refCnt()); - oldCumulation.release(); - assertEquals(0, newCumulation.refCnt()); - } else { - assertNull(thrown); - assertEquals(0, oldCumulation.refCnt()); - assertEquals(1, newCumulation.refCnt()); - newCumulation.release(); - } - } - - @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; - } - @Override - public CompositeByteBuf addFlattenedComponents(boolean increaseWriterIndex, ByteBuf buffer) { - throw error; - } - }.writeZero(1); - 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()); - cumulation.release(); - } - } - - @Disabled("FixedLengthFrameDecoder is migrated to use Buffer") - @Test - public void testDoesNotOverRead() { - class ReadInterceptingHandler implements ChannelHandler { - private int readsTriggered; - - @Override - public void read(ChannelHandlerContext ctx) { - readsTriggered++; - ctx.read(); - } - } - ReadInterceptingHandler interceptor = new ReadInterceptingHandler(); - - EmbeddedChannel channel = new EmbeddedChannel(); - channel.config().setAutoRead(false); - channel.pipeline().addLast(interceptor, new FixedLengthFrameDecoder(3)); - assertEquals(0, interceptor.readsTriggered); - - // 0 complete frames, 1 partial frame: SHOULD trigger a read - channel.writeInbound(wrappedBuffer(new byte[] { 0, 1 })); - assertEquals(1, interceptor.readsTriggered); - - // 2 complete frames, 0 partial frames: should NOT trigger a read - channel.writeInbound(wrappedBuffer(new byte[] { 2 }), wrappedBuffer(new byte[] { 3, 4, 5 })); - assertEquals(1, interceptor.readsTriggered); - - // 1 complete frame, 1 partial frame: should NOT trigger a read - channel.writeInbound(wrappedBuffer(new byte[] { 6, 7, 8 }), wrappedBuffer(new byte[] { 9 })); - assertEquals(1, interceptor.readsTriggered); - - // 1 complete frame, 1 partial frame: should NOT trigger a read - channel.writeInbound(wrappedBuffer(new byte[] { 10, 11 }), wrappedBuffer(new byte[] { 12 })); - assertEquals(1, interceptor.readsTriggered); - - // 0 complete frames, 1 partial frame: SHOULD trigger a read - channel.writeInbound(wrappedBuffer(new byte[] { 13 })); - assertEquals(2, interceptor.readsTriggered); - - // 1 complete frame, 0 partial frames: should NOT trigger a read - channel.writeInbound(wrappedBuffer(new byte[] { 14 })); - assertEquals(2, interceptor.readsTriggered); - - for (int i = 0; i < 5; i++) { - ByteBuf read = channel.readInbound(); - assertEquals(i * 3 + 0, read.getByte(0)); - assertEquals(i * 3 + 1, read.getByte(1)); - assertEquals(i * 3 + 2, read.getByte(2)); - read.release(); - } - assertFalse(channel.finish()); - } - - @Test - public void testDisorder() { - ByteToMessageDecoder decoder = new ByteToMessageDecoder() { - int count; - - //read 4 byte then remove this decoder - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) { - ctx.fireChannelRead(in.readByte()); - if (++count >= 4) { - ctx.pipeline().remove(this); - } - } - }; - EmbeddedChannel channel = new EmbeddedChannel(decoder); - assertTrue(channel.writeInbound(Unpooled.wrappedBuffer(new byte[]{1, 2, 3, 4, 5}))); - assertEquals((byte) 1, (Byte) channel.readInbound()); - assertEquals((byte) 2, (Byte) channel.readInbound()); - assertEquals((byte) 3, (Byte) channel.readInbound()); - assertEquals((byte) 4, (Byte) channel.readInbound()); - ByteBuf buffer5 = channel.readInbound(); - assertEquals((byte) 5, buffer5.readByte()); - assertFalse(buffer5.isReadable()); - assertTrue(buffer5.release()); - assertFalse(channel.finish()); - } - - @Test - public void testDecodeLast() { - final AtomicBoolean removeHandler = new AtomicBoolean(); - EmbeddedChannel channel = new EmbeddedChannel(new ByteToMessageDecoder() { - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) { - if (removeHandler.get()) { - ctx.pipeline().remove(this); - } - } - }); - byte[] bytes = new byte[1024]; - ThreadLocalRandom.current().nextBytes(bytes); - - assertFalse(channel.writeInbound(Unpooled.copiedBuffer(bytes))); - assertNull(channel.readInbound()); - removeHandler.set(true); - // This should trigger channelInputClosed(...) - channel.pipeline().fireUserEventTriggered(ChannelInputShutdownEvent.INSTANCE); - - assertTrue(channel.finish()); - assertBuffer(Unpooled.wrappedBuffer(bytes), channel.readInbound()); - assertNull(channel.readInbound()); - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/CharSequenceValueConverterTest.java b/codec/src/test/java/io/netty/handler/codec/CharSequenceValueConverterTest.java deleted file mode 100644 index 05eab1c14c..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/CharSequenceValueConverterTest.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec; - -import io.netty.util.AsciiString; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class CharSequenceValueConverterTest { - - private final CharSequenceValueConverter converter = CharSequenceValueConverter.INSTANCE; - - @Test - public void testBoolean() { - assertTrue(converter.convertToBoolean(converter.convertBoolean(true))); - assertFalse(converter.convertToBoolean(converter.convertBoolean(false))); - } - - @Test - public void testByteFromAsciiString() { - assertEquals(127, converter.convertToByte(AsciiString.of("127"))); - } - - @Test - public void testByteFromEmptyAsciiString() { - assertThrows(NumberFormatException.class, () ->converter.convertToByte(AsciiString.EMPTY_STRING)); - } - - @Test - public void testByte() { - assertEquals(Byte.MAX_VALUE, converter.convertToByte(converter.convertByte(Byte.MAX_VALUE))); - } - - @Test - public void testChar() { - assertEquals(Character.MAX_VALUE, converter.convertToChar(converter.convertChar(Character.MAX_VALUE))); - } - - @Test - public void testDouble() { - assertEquals(Double.MAX_VALUE, converter.convertToDouble(converter.convertDouble(Double.MAX_VALUE)), 0); - } - - @Test - public void testFloat() { - assertEquals(Float.MAX_VALUE, converter.convertToFloat(converter.convertFloat(Float.MAX_VALUE)), 0); - } - - @Test - public void testInt() { - assertEquals(Integer.MAX_VALUE, converter.convertToInt(converter.convertInt(Integer.MAX_VALUE))); - } - - @Test - public void testShort() { - assertEquals(Short.MAX_VALUE, converter.convertToShort(converter.convertShort(Short.MAX_VALUE))); - } - - @Test - public void testLong() { - assertEquals(Long.MAX_VALUE, converter.convertToLong(converter.convertLong(Long.MAX_VALUE))); - } - - @Test - public void testTimeMillis() { - // Zero out the millis as this is what the convert is doing as well. - long millis = (System.currentTimeMillis() / 1000) * 1000; - assertEquals(millis, converter.convertToTimeMillis(converter.convertTimeMillis(millis))); - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/CodecOutputListTest.java b/codec/src/test/java/io/netty/handler/codec/CodecOutputListTest.java deleted file mode 100644 index 5da3395994..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/CodecOutputListTest.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -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(); - } - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/DatagramPacketDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/DatagramPacketDecoderTest.java deleted file mode 100644 index 98b4522a26..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/DatagramPacketDecoderTest.java +++ /dev/null @@ -1,96 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.channel.socket.DatagramPacket; -import io.netty.handler.codec.string.StringDecoder; -import io.netty.util.CharsetUtil; -import io.netty.util.internal.SocketUtils; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.net.InetSocketAddress; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class DatagramPacketDecoderTest { - - private EmbeddedChannel channel; - - @BeforeEach - public void setUp() { - channel = new EmbeddedChannel( - new DatagramPacketDecoder( - new StringDecoder(CharsetUtil.UTF_8))); - } - - @AfterEach - public void tearDown() { - assertFalse(channel.finish()); - } - - @Test - public void testDecode() { - InetSocketAddress recipient = SocketUtils.socketAddress("127.0.0.1", 10000); - InetSocketAddress sender = SocketUtils.socketAddress("127.0.0.1", 20000); - ByteBuf content = Unpooled.wrappedBuffer("netty".getBytes(CharsetUtil.UTF_8)); - assertTrue(channel.writeInbound(new DatagramPacket(content, recipient, sender))); - assertEquals("netty", channel.readInbound()); - } - - @Test - public void testIsNotSharable() { - testIsSharable(false); - } - - @Test - public void testIsSharable() { - testIsSharable(true); - } - - private static void testIsSharable(boolean sharable) { - MessageToMessageDecoder wrapped = new TestMessageToMessageDecoder(sharable); - DatagramPacketDecoder decoder = new DatagramPacketDecoder(wrapped); - assertEquals(wrapped.isSharable(), decoder.isSharable()); - } - - private static final class TestMessageToMessageDecoder extends MessageToMessageDecoder { - - private final boolean sharable; - - TestMessageToMessageDecoder(boolean sharable) { - this.sharable = sharable; - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { - // NOOP - } - - @Override - public boolean isSharable() { - return sharable; - } - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/DatagramPacketEncoderTest.java b/codec/src/test/java/io/netty/handler/codec/DatagramPacketEncoderTest.java deleted file mode 100644 index caffb6db96..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/DatagramPacketEncoderTest.java +++ /dev/null @@ -1,139 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.AddressedEnvelope; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.DefaultAddressedEnvelope; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.channel.socket.DatagramPacket; -import io.netty.handler.codec.string.StringEncoder; -import io.netty.util.CharsetUtil; -import io.netty.util.internal.SocketUtils; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.net.InetSocketAddress; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.*; - -public class DatagramPacketEncoderTest { - - private EmbeddedChannel channel; - - @BeforeEach - public void setUp() { - channel = new EmbeddedChannel( - new DatagramPacketEncoder( - new StringEncoder(CharsetUtil.UTF_8))); - } - - @AfterEach - public void tearDown() { - assertFalse(channel.finish()); - } - - @Test - public void testEncode() { - testEncode(false); - } - - @Test - public void testEncodeWithSenderIsNull() { - testEncode(true); - } - - private void testEncode(boolean senderIsNull) { - InetSocketAddress recipient = SocketUtils.socketAddress("127.0.0.1", 10000); - InetSocketAddress sender = senderIsNull ? null : SocketUtils.socketAddress("127.0.0.1", 20000); - assertTrue(channel.writeOutbound( - new DefaultAddressedEnvelope<>("netty", recipient, sender))); - DatagramPacket packet = channel.readOutbound(); - try { - assertEquals("netty", packet.content().toString(CharsetUtil.UTF_8)); - assertEquals(recipient, packet.recipient()); - assertEquals(sender, packet.sender()); - } finally { - packet.release(); - } - } - - @Test - public void testUnmatchedMessageType() { - InetSocketAddress recipient = SocketUtils.socketAddress("127.0.0.1", 10000); - InetSocketAddress sender = SocketUtils.socketAddress("127.0.0.1", 20000); - DefaultAddressedEnvelope envelope = - new DefaultAddressedEnvelope<>(1L, recipient, sender); - assertTrue(channel.writeOutbound(envelope)); - DefaultAddressedEnvelope output = channel.readOutbound(); - try { - assertSame(envelope, output); - } finally { - output.release(); - } - } - - @Test - public void testUnmatchedType() { - String netty = "netty"; - assertTrue(channel.writeOutbound(netty)); - assertSame(netty, channel.readOutbound()); - } - - @Test - public void testIsNotSharable() { - testSharable(false); - } - - @Test - public void testIsSharable() { - testSharable(true); - } - - private static void testSharable(boolean sharable) { - MessageToMessageEncoder> wrapped = - new TestMessageToMessageEncoder(sharable); - - DatagramPacketEncoder> encoder = - new DatagramPacketEncoder<>(wrapped); - assertEquals(wrapped.isSharable(), encoder.isSharable()); - } - - private static final class TestMessageToMessageEncoder - extends MessageToMessageEncoder> { - - private final boolean sharable; - - TestMessageToMessageEncoder(boolean sharable) { - this.sharable = sharable; - } - - @Override - protected void encode( - ChannelHandlerContext ctx, AddressedEnvelope msg, List out) { - // NOOP - } - - @Override - public boolean isSharable() { - return sharable; - } - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/DateFormatterTest.java b/codec/src/test/java/io/netty/handler/codec/DateFormatterTest.java deleted file mode 100644 index e7b3d1af4c..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/DateFormatterTest.java +++ /dev/null @@ -1,145 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import org.junit.jupiter.api.Test; - -import java.util.Calendar; -import java.util.Date; - -import static io.netty.handler.codec.DateFormatter.*; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; - -public class DateFormatterTest { - /** - * This date is set at "06 Nov 1994 08:49:37 GMT", from - * examples in RFC documentation - */ - private static final long TIMESTAMP = 784111777000L; - private static final Date DATE = new Date(TIMESTAMP); - - @Test - public void testParseWithSingleDigitDay() { - assertEquals(DATE, parseHttpDate("Sun, 6 Nov 1994 08:49:37 GMT")); - } - - @Test - public void testParseWithDoubleDigitDay() { - assertEquals(DATE, parseHttpDate("Sun, 06 Nov 1994 08:49:37 GMT")); - } - - @Test - public void testParseWithDashSeparatorSingleDigitDay() { - assertEquals(DATE, parseHttpDate("Sunday, 6-Nov-94 08:49:37 GMT")); - } - - @Test - public void testParseWithDashSeparatorDoubleDigitDay() { - assertEquals(DATE, parseHttpDate("Sunday, 06-Nov-94 08:49:37 GMT")); - } - - @Test - public void testParseWithoutGMT() { - assertEquals(DATE, parseHttpDate("Sun Nov 06 08:49:37 1994")); - } - - @Test - public void testParseWithFunkyTimezone() { - assertEquals(DATE, parseHttpDate("Sun Nov 06 08:49:37 1994 -0000")); - } - - @Test - public void testParseWithSingleDigitHourMinutesAndSecond() { - assertEquals(DATE, parseHttpDate("Sunday, 06-Nov-94 8:49:37 GMT")); - } - - @Test - public void testParseWithSingleDigitTime() { - assertEquals(DATE, parseHttpDate("Sunday, 06 Nov 1994 8:49:37 GMT")); - - Date _08_09_37 = new Date(TIMESTAMP - 40 * 60 * 1000); - assertEquals(_08_09_37, parseHttpDate("Sunday, 06 Nov 1994 8:9:37 GMT")); - assertEquals(_08_09_37, parseHttpDate("Sunday, 06 Nov 1994 8:09:37 GMT")); - - Date _08_09_07 = new Date(TIMESTAMP - (40 * 60 + 30) * 1000); - assertEquals(_08_09_07, parseHttpDate("Sunday, 06 Nov 1994 8:9:7 GMT")); - assertEquals(_08_09_07, parseHttpDate("Sunday, 06 Nov 1994 8:9:07 GMT")); - } - - @Test - public void testParseMidnight() { - assertEquals(new Date(784080000000L), parseHttpDate("Sunday, 06 Nov 1994 00:00:00 GMT")); - } - - @Test - public void testParseInvalidInput() { - // missing field - assertNull(parseHttpDate("Sun, Nov 1994 08:49:37 GMT")); - assertNull(parseHttpDate("Sun, 06 1994 08:49:37 GMT")); - assertNull(parseHttpDate("Sun, 06 Nov 08:49:37 GMT")); - assertNull(parseHttpDate("Sun, 06 Nov 1994 :49:37 GMT")); - assertNull(parseHttpDate("Sun, 06 Nov 1994 49:37 GMT")); - assertNull(parseHttpDate("Sun, 06 Nov 1994 08::37 GMT")); - assertNull(parseHttpDate("Sun, 06 Nov 1994 08:37 GMT")); - assertNull(parseHttpDate("Sun, 06 Nov 1994 08:49: GMT")); - assertNull(parseHttpDate("Sun, 06 Nov 1994 08:49 GMT")); - //invalid value - assertNull(parseHttpDate("Sun, 06 FOO 1994 08:49:37 GMT")); - assertNull(parseHttpDate("Sun, 36 Nov 1994 08:49:37 GMT")); - assertNull(parseHttpDate("Sun, 06 Nov 1994 28:49:37 GMT")); - assertNull(parseHttpDate("Sun, 06 Nov 1994 08:69:37 GMT")); - assertNull(parseHttpDate("Sun, 06 Nov 1994 08:49:67 GMT")); - //wrong number of digits in timestamp - assertNull(parseHttpDate("Sunday, 06 Nov 1994 0:0:000 GMT")); - assertNull(parseHttpDate("Sunday, 06 Nov 1994 0:000:0 GMT")); - assertNull(parseHttpDate("Sunday, 06 Nov 1994 000:0:0 GMT")); - } - - @Test - public void testFormat() { - assertEquals("Sun, 06 Nov 1994 08:49:37 GMT", format(DATE)); - } - - @Test - public void testAppend() { - StringBuilder sb = new StringBuilder(); - append(DATE, sb); - assertEquals("Sun, 06 Nov 1994 08:49:37 GMT", sb.toString()); - } - - @Test - public void testParseAllMonths() { - assertEquals(Calendar.JANUARY, getMonth(parseHttpDate("Sun, 06 Jan 1994 08:49:37 GMT"))); - assertEquals(Calendar.FEBRUARY, getMonth(parseHttpDate("Sun, 06 Feb 1994 08:49:37 GMT"))); - assertEquals(Calendar.MARCH, getMonth(parseHttpDate("Sun, 06 Mar 1994 08:49:37 GMT"))); - assertEquals(Calendar.APRIL, getMonth(parseHttpDate("Sun, 06 Apr 1994 08:49:37 GMT"))); - assertEquals(Calendar.MAY, getMonth(parseHttpDate("Sun, 06 May 1994 08:49:37 GMT"))); - assertEquals(Calendar.JUNE, getMonth(parseHttpDate("Sun, 06 Jun 1994 08:49:37 GMT"))); - assertEquals(Calendar.JULY, getMonth(parseHttpDate("Sun, 06 Jul 1994 08:49:37 GMT"))); - assertEquals(Calendar.AUGUST, getMonth(parseHttpDate("Sun, 06 Aug 1994 08:49:37 GMT"))); - assertEquals(Calendar.SEPTEMBER, getMonth(parseHttpDate("Sun, 06 Sep 1994 08:49:37 GMT"))); - assertEquals(Calendar.OCTOBER, getMonth(parseHttpDate("Sun Oct 06 08:49:37 1994"))); - assertEquals(Calendar.NOVEMBER, getMonth(parseHttpDate("Sun Nov 06 08:49:37 1994"))); - assertEquals(Calendar.DECEMBER, getMonth(parseHttpDate("Sun Dec 06 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/codec/src/test/java/io/netty/handler/codec/DefaultHeadersTest.java b/codec/src/test/java/io/netty/handler/codec/DefaultHeadersTest.java deleted file mode 100644 index da4a1456f8..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/DefaultHeadersTest.java +++ /dev/null @@ -1,817 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec; - -import io.netty.util.AsciiString; -import io.netty.util.HashingStrategy; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.NoSuchElementException; - -import static io.netty.util.AsciiString.of; -import static java.util.Arrays.asList; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsInAnyOrder; -import static org.hamcrest.Matchers.hasSize; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertNotSame; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - -/** - * Tests for {@link DefaultHeaders}. - */ -public class DefaultHeadersTest { - - private static final class TestDefaultHeaders extends - DefaultHeaders { - TestDefaultHeaders() { - this(CharSequenceValueConverter.INSTANCE); - } - - TestDefaultHeaders(ValueConverter converter) { - super(converter); - } - - TestDefaultHeaders(HashingStrategy nameHashingStrategy) { - super(nameHashingStrategy, CharSequenceValueConverter.INSTANCE); - } - } - - private static TestDefaultHeaders newInstance() { - return new TestDefaultHeaders(); - } - - @Test - public void addShouldIncreaseAndRemoveShouldDecreaseTheSize() { - TestDefaultHeaders headers = newInstance(); - assertEquals(0, headers.size()); - headers.add(of("name1"), of("value1"), of("value2")); - assertEquals(2, headers.size()); - headers.add(of("name2"), of("value3"), of("value4")); - assertEquals(4, headers.size()); - headers.add(of("name3"), of("value5")); - assertEquals(5, headers.size()); - - headers.remove(of("name3")); - assertEquals(4, headers.size()); - headers.remove(of("name1")); - assertEquals(2, headers.size()); - headers.remove(of("name2")); - assertEquals(0, headers.size()); - assertTrue(headers.isEmpty()); - } - - @Test - public void afterClearHeadersShouldBeEmpty() { - TestDefaultHeaders headers = newInstance(); - headers.add(of("name1"), of("value1")); - headers.add(of("name2"), of("value2")); - assertEquals(2, headers.size()); - headers.clear(); - assertEquals(0, headers.size()); - assertTrue(headers.isEmpty()); - assertFalse(headers.contains(of("name1"))); - assertFalse(headers.contains(of("name2"))); - } - - @Test - public void removingANameForASecondTimeShouldReturnFalse() { - TestDefaultHeaders headers = newInstance(); - headers.add(of("name1"), of("value1")); - headers.add(of("name2"), of("value2")); - assertTrue(headers.remove(of("name2"))); - assertFalse(headers.remove(of("name2"))); - } - - @Test - public void multipleValuesPerNameShouldBeAllowed() { - TestDefaultHeaders headers = newInstance(); - headers.add(of("name"), of("value1")); - headers.add(of("name"), of("value2")); - headers.add(of("name"), of("value3")); - assertEquals(3, headers.size()); - - List values = headers.getAll(of("name")); - assertEquals(3, values.size()); - assertTrue(values.containsAll(asList(of("value1"), of("value2"), of("value3")))); - } - - @Test - public void multipleValuesPerNameIteratorWithOtherNames() { - TestDefaultHeaders headers = newInstance(); - headers.add(of("name1"), of("value1")); - headers.add(of("name1"), of("value2")); - headers.add(of("name2"), of("value4")); - headers.add(of("name1"), of("value3")); - assertEquals(4, headers.size()); - - List values = new ArrayList<>(); - Iterator itr = headers.valueIterator(of("name1")); - while (itr.hasNext()) { - values.add(itr.next()); - itr.remove(); - } - assertEquals(3, values.size()); - assertEquals(1, headers.size()); - assertFalse(headers.isEmpty()); - assertTrue(values.containsAll(asList(of("value1"), of("value2"), of("value3")))); - itr = headers.valueIterator(of("name1")); - assertFalse(itr.hasNext()); - itr = headers.valueIterator(of("name2")); - assertTrue(itr.hasNext()); - assertEquals(of("value4"), itr.next()); - assertFalse(itr.hasNext()); - } - - @Test - public void multipleValuesPerNameIterator() { - TestDefaultHeaders headers = newInstance(); - headers.add(of("name1"), of("value1")); - headers.add(of("name1"), of("value2")); - assertEquals(2, headers.size()); - - List values = new ArrayList(); - Iterator itr = headers.valueIterator(of("name1")); - while (itr.hasNext()) { - values.add(itr.next()); - itr.remove(); - } - assertEquals(2, values.size()); - assertEquals(0, headers.size()); - assertTrue(headers.isEmpty()); - assertTrue(values.containsAll(asList(of("value1"), of("value2")))); - itr = headers.valueIterator(of("name1")); - assertFalse(itr.hasNext()); - } - - @Test - public void valuesItrRemoveThrowsWhenEmpty() { - TestDefaultHeaders headers = newInstance(); - assertEquals(0, headers.size()); - assertTrue(headers.isEmpty()); - Iterator itr = headers.valueIterator(of("name")); - assertThrows(IllegalStateException.class, itr::remove); - } - - @Test - public void valuesItrRemoveThrowsAfterLastElement() { - TestDefaultHeaders headers = newInstance(); - headers.add(of("name"), of("value1")); - assertEquals(1, headers.size()); - - List values = new ArrayList(); - Iterator itr = headers.valueIterator(of("name")); - while (itr.hasNext()) { - values.add(itr.next()); - itr.remove(); - } - assertEquals(1, values.size()); - assertEquals(0, headers.size()); - assertTrue(headers.isEmpty()); - assertTrue(values.contains(of("value1"))); - try { - itr.remove(); - fail(); - } catch (IllegalStateException ignored) { - // ignored - } - } - - @Test - public void multipleValuesPerNameIteratorEmpty() { - TestDefaultHeaders headers = newInstance(); - - List values = new ArrayList<>(); - Iterator itr = headers.valueIterator(of("name")); - while (itr.hasNext()) { - values.add(itr.next()); - } - assertEquals(0, values.size()); - try { - itr.next(); - fail(); - } catch (NoSuchElementException ignored) { - // ignored - } - } - - @Test - public void testContains() { - TestDefaultHeaders headers = newInstance(); - headers.addBoolean(of("boolean"), true); - assertTrue(headers.containsBoolean(of("boolean"), true)); - assertFalse(headers.containsBoolean(of("boolean"), false)); - - headers.addLong(of("long"), Long.MAX_VALUE); - assertTrue(headers.containsLong(of("long"), Long.MAX_VALUE)); - assertFalse(headers.containsLong(of("long"), Long.MIN_VALUE)); - - headers.addInt(of("int"), Integer.MIN_VALUE); - assertTrue(headers.containsInt(of("int"), Integer.MIN_VALUE)); - assertFalse(headers.containsInt(of("int"), Integer.MAX_VALUE)); - - headers.addShort(of("short"), Short.MAX_VALUE); - assertTrue(headers.containsShort(of("short"), Short.MAX_VALUE)); - assertFalse(headers.containsShort(of("short"), Short.MIN_VALUE)); - - headers.addChar(of("char"), Character.MAX_VALUE); - assertTrue(headers.containsChar(of("char"), Character.MAX_VALUE)); - assertFalse(headers.containsChar(of("char"), Character.MIN_VALUE)); - - headers.addByte(of("byte"), Byte.MAX_VALUE); - assertTrue(headers.containsByte(of("byte"), Byte.MAX_VALUE)); - assertFalse(headers.containsLong(of("byte"), Byte.MIN_VALUE)); - - headers.addDouble(of("double"), Double.MAX_VALUE); - assertTrue(headers.containsDouble(of("double"), Double.MAX_VALUE)); - assertFalse(headers.containsDouble(of("double"), Double.MIN_VALUE)); - - headers.addFloat(of("float"), Float.MAX_VALUE); - assertTrue(headers.containsFloat(of("float"), Float.MAX_VALUE)); - assertFalse(headers.containsFloat(of("float"), Float.MIN_VALUE)); - - long millis = System.currentTimeMillis(); - headers.addTimeMillis(of("millis"), millis); - assertTrue(headers.containsTimeMillis(of("millis"), millis)); - // This test doesn't work on midnight, January 1, 1970 UTC - assertFalse(headers.containsTimeMillis(of("millis"), 0)); - - headers.addObject(of("object"), "Hello World"); - assertTrue(headers.containsObject(of("object"), "Hello World")); - assertFalse(headers.containsObject(of("object"), "")); - - headers.add(of("name"), of("value")); - assertTrue(headers.contains(of("name"), of("value"))); - assertFalse(headers.contains(of("name"), of("value1"))); - } - - @Test - public void testCopy() throws Exception { - TestDefaultHeaders headers = newInstance(); - headers.addBoolean(of("boolean"), true); - headers.addLong(of("long"), Long.MAX_VALUE); - headers.addInt(of("int"), Integer.MIN_VALUE); - headers.addShort(of("short"), Short.MAX_VALUE); - headers.addChar(of("char"), Character.MAX_VALUE); - headers.addByte(of("byte"), Byte.MAX_VALUE); - headers.addDouble(of("double"), Double.MAX_VALUE); - headers.addFloat(of("float"), Float.MAX_VALUE); - long millis = System.currentTimeMillis(); - headers.addTimeMillis(of("millis"), millis); - headers.addObject(of("object"), "Hello World"); - headers.add(of("name"), of("value")); - - headers = newInstance().add(headers); - - assertTrue(headers.containsBoolean(of("boolean"), true)); - assertFalse(headers.containsBoolean(of("boolean"), false)); - - assertTrue(headers.containsLong(of("long"), Long.MAX_VALUE)); - assertFalse(headers.containsLong(of("long"), Long.MIN_VALUE)); - - assertTrue(headers.containsInt(of("int"), Integer.MIN_VALUE)); - assertFalse(headers.containsInt(of("int"), Integer.MAX_VALUE)); - - assertTrue(headers.containsShort(of("short"), Short.MAX_VALUE)); - assertFalse(headers.containsShort(of("short"), Short.MIN_VALUE)); - - assertTrue(headers.containsChar(of("char"), Character.MAX_VALUE)); - assertFalse(headers.containsChar(of("char"), Character.MIN_VALUE)); - - assertTrue(headers.containsByte(of("byte"), Byte.MAX_VALUE)); - assertFalse(headers.containsLong(of("byte"), Byte.MIN_VALUE)); - - assertTrue(headers.containsDouble(of("double"), Double.MAX_VALUE)); - assertFalse(headers.containsDouble(of("double"), Double.MIN_VALUE)); - - assertTrue(headers.containsFloat(of("float"), Float.MAX_VALUE)); - assertFalse(headers.containsFloat(of("float"), Float.MIN_VALUE)); - - assertTrue(headers.containsTimeMillis(of("millis"), millis)); - // This test doesn't work on midnight, January 1, 1970 UTC - assertFalse(headers.containsTimeMillis(of("millis"), 0)); - - assertTrue(headers.containsObject(of("object"), "Hello World")); - assertFalse(headers.containsObject(of("object"), "")); - - assertTrue(headers.contains(of("name"), of("value"))); - assertFalse(headers.contains(of("name"), of("value1"))); - } - - @Test - public void canMixConvertedAndNormalValues() { - TestDefaultHeaders headers = newInstance(); - headers.add(of("name"), of("value")); - headers.addInt(of("name"), 100); - headers.addBoolean(of("name"), false); - - assertEquals(3, headers.size()); - assertTrue(headers.contains(of("name"))); - assertTrue(headers.contains(of("name"), of("value"))); - assertTrue(headers.containsInt(of("name"), 100)); - assertTrue(headers.containsBoolean(of("name"), false)); - } - - @Test - public void testGetAndRemove() { - TestDefaultHeaders headers = newInstance(); - headers.add(of("name1"), of("value1")); - headers.add(of("name2"), of("value2"), of("value3")); - headers.add(of("name3"), of("value4"), of("value5"), of("value6")); - - assertEquals(of("value1"), headers.getAndRemove(of("name1"), of("defaultvalue"))); - assertEquals(of("value2"), headers.getAndRemove(of("name2"))); - assertNull(headers.getAndRemove(of("name2"))); - assertEquals(asList(of("value4"), of("value5"), of("value6")), headers.getAllAndRemove(of("name3"))); - assertEquals(0, headers.size()); - assertNull(headers.getAndRemove(of("noname"))); - assertEquals(of("defaultvalue"), headers.getAndRemove(of("noname"), of("defaultvalue"))); - } - - @Test - public void whenNameContainsMultipleValuesGetShouldReturnTheFirst() { - TestDefaultHeaders headers = newInstance(); - headers.add(of("name1"), of("value1"), of("value2")); - assertEquals(of("value1"), headers.get(of("name1"))); - } - - @Test - public void getWithDefaultValueWorks() { - TestDefaultHeaders headers = newInstance(); - headers.add(of("name1"), of("value1")); - - assertEquals(of("value1"), headers.get(of("name1"), of("defaultvalue"))); - assertEquals(of("defaultvalue"), headers.get(of("noname"), of("defaultvalue"))); - } - - @Test - public void setShouldOverWritePreviousValue() { - TestDefaultHeaders headers = newInstance(); - headers.set(of("name"), of("value1")); - headers.set(of("name"), of("value2")); - assertEquals(1, headers.size()); - assertEquals(1, headers.getAll(of("name")).size()); - assertEquals(of("value2"), headers.getAll(of("name")).get(0)); - assertEquals(of("value2"), headers.get(of("name"))); - } - - @Test - public void setAllShouldOverwriteSomeAndLeaveOthersUntouched() { - TestDefaultHeaders h1 = newInstance(); - - h1.add(of("name1"), of("value1")); - h1.add(of("name2"), of("value2")); - h1.add(of("name2"), of("value3")); - h1.add(of("name3"), of("value4")); - - TestDefaultHeaders h2 = newInstance(); - h2.add(of("name1"), of("value5")); - h2.add(of("name2"), of("value6")); - h2.add(of("name1"), of("value7")); - - TestDefaultHeaders expected = newInstance(); - expected.add(of("name1"), of("value5")); - expected.add(of("name2"), of("value6")); - expected.add(of("name1"), of("value7")); - expected.add(of("name3"), of("value4")); - - h1.setAll(h2); - - assertEquals(expected, h1); - } - - @Test - public void headersWithSameNamesAndValuesShouldBeEquivalent() { - TestDefaultHeaders headers1 = newInstance(); - headers1.add(of("name1"), of("value1")); - headers1.add(of("name2"), of("value2")); - headers1.add(of("name2"), of("value3")); - - TestDefaultHeaders headers2 = newInstance(); - headers2.add(of("name1"), of("value1")); - headers2.add(of("name2"), of("value2")); - headers2.add(of("name2"), of("value3")); - - assertEquals(headers1, headers2); - assertEquals(headers2, headers1); - assertEquals(headers1, headers1); - assertEquals(headers2, headers2); - assertEquals(headers1.hashCode(), headers2.hashCode()); - assertEquals(headers1.hashCode(), headers1.hashCode()); - assertEquals(headers2.hashCode(), headers2.hashCode()); - } - - @Test - public void emptyHeadersShouldBeEqual() { - TestDefaultHeaders headers1 = newInstance(); - TestDefaultHeaders headers2 = newInstance(); - assertNotSame(headers1, headers2); - assertEquals(headers1, headers2); - assertEquals(headers1.hashCode(), headers2.hashCode()); - } - - @Test - public void headersWithSameNamesButDifferentValuesShouldNotBeEquivalent() { - TestDefaultHeaders headers1 = newInstance(); - headers1.add(of("name1"), of("value1")); - TestDefaultHeaders headers2 = newInstance(); - headers1.add(of("name1"), of("value2")); - assertNotEquals(headers1, headers2); - } - - @Test - public void subsetOfHeadersShouldNotBeEquivalent() { - TestDefaultHeaders headers1 = newInstance(); - headers1.add(of("name1"), of("value1")); - headers1.add(of("name2"), of("value2")); - TestDefaultHeaders headers2 = newInstance(); - headers1.add(of("name1"), of("value1")); - assertNotEquals(headers1, headers2); - } - - @Test - public void headersWithDifferentNamesAndValuesShouldNotBeEquivalent() { - TestDefaultHeaders h1 = newInstance(); - h1.set(of("name1"), of("value1")); - TestDefaultHeaders h2 = newInstance(); - h2.set(of("name2"), of("value2")); - assertNotEquals(h1, h2); - assertNotEquals(h2, h1); - assertEquals(h1, h1); - assertEquals(h2, h2); - } - - @Test - public void iterateEmptyHeadersShouldThrow() { - Iterator> iterator = newInstance().iterator(); - assertFalse(iterator.hasNext()); - assertThrows(NoSuchElementException.class, iterator::next); - } - - @Test - public void iteratorShouldReturnAllNameValuePairs() { - TestDefaultHeaders headers1 = newInstance(); - headers1.add(of("name1"), of("value1"), of("value2")); - headers1.add(of("name2"), of("value3")); - headers1.add(of("name3"), of("value4"), of("value5"), of("value6")); - headers1.add(of("name1"), of("value7"), of("value8")); - assertEquals(8, headers1.size()); - - TestDefaultHeaders headers2 = newInstance(); - for (Entry entry : headers1) { - headers2.add(entry.getKey(), entry.getValue()); - } - - assertEquals(headers1, headers2); - } - - @Test - public void iteratorSetValueShouldChangeHeaderValue() { - TestDefaultHeaders headers = newInstance(); - headers.add(of("name1"), of("value1"), of("value2"), of("value3")); - headers.add(of("name2"), of("value4")); - assertEquals(4, headers.size()); - - Iterator> iter = headers.iterator(); - while (iter.hasNext()) { - Entry header = iter.next(); - if (of("name1").equals(header.getKey()) && of("value2").equals(header.getValue())) { - header.setValue(of("updatedvalue2")); - assertEquals(of("updatedvalue2"), header.getValue()); - } - if (of("name1").equals(header.getKey()) && of("value3").equals(header.getValue())) { - header.setValue(of("updatedvalue3")); - assertEquals(of("updatedvalue3"), header.getValue()); - } - } - - assertEquals(4, headers.size()); - assertTrue(headers.contains(of("name1"), of("updatedvalue2"))); - assertFalse(headers.contains(of("name1"), of("value2"))); - assertTrue(headers.contains(of("name1"), of("updatedvalue3"))); - assertFalse(headers.contains(of("name1"), of("value3"))); - } - - @Test - public void testEntryEquals() { - Map.Entry same1 = newInstance().add("name", "value").iterator().next(); - Map.Entry same2 = newInstance().add("name", "value").iterator().next(); - assertEquals(same1, same2); - assertEquals(same1.hashCode(), same2.hashCode()); - - Map.Entry nameDifferent1 = newInstance().add("name1", "value").iterator().next(); - Map.Entry nameDifferent2 = newInstance().add("name2", "value").iterator().next(); - assertNotEquals(nameDifferent1, nameDifferent2); - assertNotEquals(nameDifferent1.hashCode(), nameDifferent2.hashCode()); - - Map.Entry valueDifferent1 = newInstance().add("name", "value1").iterator().next(); - Map.Entry valueDifferent2 = newInstance().add("name", "value2").iterator().next(); - assertNotEquals(valueDifferent1, valueDifferent2); - assertNotEquals(valueDifferent1.hashCode(), valueDifferent2.hashCode()); - } - - @Test - public void getAllReturnsEmptyListForUnknownName() { - TestDefaultHeaders headers = newInstance(); - assertEquals(0, headers.getAll(of("noname")).size()); - } - - @Test - public void setHeadersShouldClearAndOverwrite() { - TestDefaultHeaders headers1 = newInstance(); - headers1.add(of("name"), of("value")); - - TestDefaultHeaders headers2 = newInstance(); - headers2.add(of("name"), of("newvalue")); - headers2.add(of("name1"), of("value1")); - - headers1.set(headers2); - assertEquals(headers1, headers2); - } - - @Test - public void setAllHeadersShouldOnlyOverwriteHeaders() { - TestDefaultHeaders headers1 = newInstance(); - headers1.add(of("name"), of("value")); - headers1.add(of("name1"), of("value1")); - - TestDefaultHeaders headers2 = newInstance(); - headers2.add(of("name"), of("newvalue")); - headers2.add(of("name2"), of("value2")); - - TestDefaultHeaders expected = newInstance(); - expected.add(of("name"), of("newvalue")); - expected.add(of("name1"), of("value1")); - expected.add(of("name2"), of("value2")); - - headers1.setAll(headers2); - assertEquals(headers1, expected); - } - - @Test - public void testAddSelf() { - TestDefaultHeaders headers = newInstance(); - assertThrows(IllegalArgumentException.class, () -> headers.add(headers)); - } - - @Test - public void testSetSelfIsNoOp() { - TestDefaultHeaders headers = newInstance(); - headers.add("name", "value"); - headers.set(headers); - assertEquals(1, headers.size()); - } - - @Test - public void testToString() { - TestDefaultHeaders headers = newInstance(); - headers.add(of("name1"), of("value1")); - headers.add(of("name1"), of("value2")); - headers.add(of("name2"), of("value3")); - assertEquals("TestDefaultHeaders[name1: value1, name1: value2, name2: value3]", headers.toString()); - - headers = newInstance(); - headers.add(of("name1"), of("value1")); - headers.add(of("name2"), of("value2")); - headers.add(of("name3"), of("value3")); - assertEquals("TestDefaultHeaders[name1: value1, name2: value2, name3: value3]", headers.toString()); - - headers = newInstance(); - headers.add(of("name1"), of("value1")); - assertEquals("TestDefaultHeaders[name1: value1]", headers.toString()); - - headers = newInstance(); - assertEquals("TestDefaultHeaders[]", headers.toString()); - } - - @Test - public void testNotThrowWhenConvertFails() { - TestDefaultHeaders headers = new TestDefaultHeaders(new ValueConverter() { - @Override - public CharSequence convertObject(Object value) { - throw new IllegalArgumentException(); - } - - @Override - public CharSequence convertBoolean(boolean value) { - throw new IllegalArgumentException(); - } - - @Override - public boolean convertToBoolean(CharSequence value) { - throw new IllegalArgumentException(); - } - - @Override - public CharSequence convertByte(byte value) { - throw new IllegalArgumentException(); - } - - @Override - public byte convertToByte(CharSequence value) { - throw new IllegalArgumentException(); - } - - @Override - public CharSequence convertChar(char value) { - throw new IllegalArgumentException(); - } - - @Override - public char convertToChar(CharSequence value) { - throw new IllegalArgumentException(); - } - - @Override - public CharSequence convertShort(short value) { - throw new IllegalArgumentException(); - } - - @Override - public short convertToShort(CharSequence value) { - throw new IllegalArgumentException(); - } - - @Override - public CharSequence convertInt(int value) { - throw new IllegalArgumentException(); - } - - @Override - public int convertToInt(CharSequence value) { - throw new IllegalArgumentException(); - } - - @Override - public CharSequence convertLong(long value) { - throw new IllegalArgumentException(); - } - - @Override - public long convertToLong(CharSequence value) { - throw new IllegalArgumentException(); - } - - @Override - public CharSequence convertTimeMillis(long value) { - throw new IllegalArgumentException(); - } - - @Override - public long convertToTimeMillis(CharSequence value) { - throw new IllegalArgumentException(); - } - - @Override - public CharSequence convertFloat(float value) { - throw new IllegalArgumentException(); - } - - @Override - public float convertToFloat(CharSequence value) { - throw new IllegalArgumentException(); - } - - @Override - public CharSequence convertDouble(double value) { - throw new IllegalArgumentException(); - } - - @Override - public double convertToDouble(CharSequence value) { - throw new IllegalArgumentException(); - } - }); - headers.set("name1", ""); - assertNull(headers.getInt("name1")); - assertEquals(1, headers.getInt("name1", 1)); - - assertNull(headers.getBoolean("")); - assertFalse(headers.getBoolean("name1", false)); - - assertNull(headers.getByte("name1")); - assertEquals(1, headers.getByte("name1", (byte) 1)); - - assertNull(headers.getChar("name")); - assertEquals('n', headers.getChar("name1", 'n')); - - assertNull(headers.getDouble("name")); - assertEquals(1, headers.getDouble("name1", 1), 0); - - assertNull(headers.getFloat("name")); - assertEquals(Float.MAX_VALUE, headers.getFloat("name1", Float.MAX_VALUE), 0); - - assertNull(headers.getLong("name")); - assertEquals(Long.MAX_VALUE, headers.getLong("name1", Long.MAX_VALUE)); - - assertNull(headers.getShort("name")); - assertEquals(Short.MAX_VALUE, headers.getShort("name1", Short.MAX_VALUE)); - - assertNull(headers.getTimeMillis("name")); - assertEquals(Long.MAX_VALUE, headers.getTimeMillis("name1", Long.MAX_VALUE)); - } - - @Test - public void testGetBooleanInvalidValue() { - TestDefaultHeaders headers = newInstance(); - headers.set("name1", "invalid"); - headers.set("name2", new AsciiString("invalid")); - headers.set("name3", new StringBuilder("invalid")); - - assertFalse(headers.getBoolean("name1", false)); - assertFalse(headers.getBoolean("name2", false)); - assertFalse(headers.getBoolean("name3", false)); - } - - @Test - public void testGetBooleanFalseValue() { - TestDefaultHeaders headers = newInstance(); - headers.set("name1", "false"); - headers.set("name2", new AsciiString("false")); - headers.set("name3", new StringBuilder("false")); - - assertFalse(headers.getBoolean("name1", true)); - assertFalse(headers.getBoolean("name2", true)); - assertFalse(headers.getBoolean("name3", true)); - } - - @Test - public void testGetBooleanTrueValue() { - TestDefaultHeaders headers = newInstance(); - headers.set("name1", "true"); - headers.set("name2", new AsciiString("true")); - headers.set("name3", new StringBuilder("true")); - - assertTrue(headers.getBoolean("name1", false)); - assertTrue(headers.getBoolean("name2", false)); - assertTrue(headers.getBoolean("name3", false)); - } - - @Test - public void handlingOfHeaderNameHashCollisions() { - TestDefaultHeaders headers = new TestDefaultHeaders(new HashingStrategy() { - @Override - public int hashCode(CharSequence obj) { - return 0; // Degenerate hashing strategy to enforce collisions. - } - - @Override - public boolean equals(CharSequence a, CharSequence b) { - return a.equals(b); - } - }); - - headers.add("Cookie", "a=b; c=d; e=f"); - headers.add("other", "text/plain"); // Add another header which will be saved in the same entries[index] - - simulateCookieSplitting(headers); - List cookies = headers.getAll("Cookie"); - - assertThat(cookies, hasSize(3)); - assertThat(cookies, containsInAnyOrder((CharSequence) "a=b", "c=d", "e=f")); - } - - /** - * Split up cookies into individual cookie crumb headers. - */ - static void simulateCookieSplitting(TestDefaultHeaders headers) { - Iterator cookieItr = headers.valueIterator("Cookie"); - if (!cookieItr.hasNext()) { - return; - } - // We want to avoid "concurrent modifications" of the headers while we are iterating. So we insert crumbs - // into an intermediate collection and insert them after the split process concludes. - List cookiesToAdd = new ArrayList(); - while (cookieItr.hasNext()) { - //noinspection DynamicRegexReplaceableByCompiledPattern - String[] cookies = cookieItr.next().toString().split("; "); - cookiesToAdd.addAll(asList(cookies)); - cookieItr.remove(); - } - for (CharSequence crumb : cookiesToAdd) { - headers.add("Cookie", crumb); - } - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/DelimiterBasedFrameDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/DelimiterBasedFrameDecoderTest.java deleted file mode 100644 index d3dd830be1..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/DelimiterBasedFrameDecoderTest.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.CharsetUtil; -import io.netty.util.ReferenceCountUtil; -import org.junit.jupiter.api.Test; - -import java.nio.charset.Charset; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; - -public class DelimiterBasedFrameDecoderTest { - - @Test - public void testMultipleLinesStrippedDelimiters() { - EmbeddedChannel ch = new EmbeddedChannel(new DelimiterBasedFrameDecoder(8192, true, - Delimiters.lineDelimiter())); - ch.writeInbound(Unpooled.copiedBuffer("TestLine\r\ng\r\n", Charset.defaultCharset())); - - ByteBuf buf = ch.readInbound(); - assertEquals("TestLine", buf.toString(Charset.defaultCharset())); - - ByteBuf buf2 = ch.readInbound(); - assertEquals("g", buf2.toString(Charset.defaultCharset())); - assertNull(ch.readInbound()); - ch.finish(); - - buf.release(); - buf2.release(); - } - - @Test - public void testIncompleteLinesStrippedDelimiters() { - EmbeddedChannel ch = new EmbeddedChannel(new DelimiterBasedFrameDecoder(8192, true, - Delimiters.lineDelimiter())); - ch.writeInbound(Unpooled.copiedBuffer("Test", Charset.defaultCharset())); - assertNull(ch.readInbound()); - ch.writeInbound(Unpooled.copiedBuffer("Line\r\ng\r\n", Charset.defaultCharset())); - - ByteBuf buf = ch.readInbound(); - assertEquals("TestLine", buf.toString(Charset.defaultCharset())); - - ByteBuf buf2 = ch.readInbound(); - assertEquals("g", buf2.toString(Charset.defaultCharset())); - assertNull(ch.readInbound()); - ch.finish(); - - buf.release(); - buf2.release(); - } - - @Test - public void testMultipleLines() { - EmbeddedChannel ch = new EmbeddedChannel(new DelimiterBasedFrameDecoder(8192, false, - Delimiters.lineDelimiter())); - ch.writeInbound(Unpooled.copiedBuffer("TestLine\r\ng\r\n", Charset.defaultCharset())); - - ByteBuf buf = ch.readInbound(); - assertEquals("TestLine\r\n", buf.toString(Charset.defaultCharset())); - - ByteBuf buf2 = ch.readInbound(); - assertEquals("g\r\n", buf2.toString(Charset.defaultCharset())); - assertNull(ch.readInbound()); - ch.finish(); - - buf.release(); - buf2.release(); - } - - @Test - public void testIncompleteLines() { - EmbeddedChannel ch = new EmbeddedChannel(new DelimiterBasedFrameDecoder(8192, false, - Delimiters.lineDelimiter())); - ch.writeInbound(Unpooled.copiedBuffer("Test", Charset.defaultCharset())); - assertNull(ch.readInbound()); - ch.writeInbound(Unpooled.copiedBuffer("Line\r\ng\r\n", Charset.defaultCharset())); - - ByteBuf buf = ch.readInbound(); - assertEquals("TestLine\r\n", buf.toString(Charset.defaultCharset())); - - ByteBuf buf2 = ch.readInbound(); - assertEquals("g\r\n", buf2.toString(Charset.defaultCharset())); - assertNull(ch.readInbound()); - ch.finish(); - - buf.release(); - buf2.release(); - } - - @Test - public void testDecode() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel( - new DelimiterBasedFrameDecoder(8192, true, Delimiters.lineDelimiter())); - - ch.writeInbound(Unpooled.copiedBuffer("first\r\nsecond\nthird", CharsetUtil.US_ASCII)); - - ByteBuf buf = ch.readInbound(); - assertEquals("first", buf.toString(CharsetUtil.US_ASCII)); - - ByteBuf buf2 = ch.readInbound(); - assertEquals("second", buf2.toString(CharsetUtil.US_ASCII)); - assertNull(ch.readInbound()); - ch.finish(); - - ReferenceCountUtil.release(ch.readInbound()); - - buf.release(); - buf2.release(); - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/EmptyHeadersTest.java b/codec/src/test/java/io/netty/handler/codec/EmptyHeadersTest.java deleted file mode 100644 index 5d6c8c38fa..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/EmptyHeadersTest.java +++ /dev/null @@ -1,454 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec; - -import org.junit.jupiter.api.Test; - -import java.util.Arrays; -import java.util.Collections; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class EmptyHeadersTest { - - private static final TestEmptyHeaders HEADERS = new TestEmptyHeaders(); - - @Test - public void testAddStringValue() { - assertThrows(UnsupportedOperationException.class, () -> HEADERS.add("name", "value")); - } - - @Test - public void testAddStringValues() { - assertThrows(UnsupportedOperationException.class, () -> HEADERS.add("name", "value1", "value2")); - } - - @Test - public void testAddStringValuesIterable() { - assertThrows(UnsupportedOperationException.class, () -> HEADERS.add("name", Arrays.asList("value1", "value2"))); - } - - @Test - public void testAddBoolean() { - assertThrows(UnsupportedOperationException.class, () -> HEADERS.addBoolean("name", true)); - } - - @Test - public void testAddByte() { - assertThrows(UnsupportedOperationException.class, () -> HEADERS.addByte("name", (byte) 1)); - } - - @Test - public void testAddChar() { - assertThrows(UnsupportedOperationException.class, () -> HEADERS.addChar("name", 'a')); - } - - @Test - public void testAddDouble() { - assertThrows(UnsupportedOperationException.class, () -> HEADERS.addDouble("name", 0)); - } - - @Test - public void testAddFloat() { - assertThrows(UnsupportedOperationException.class, () -> HEADERS.addFloat("name", 0)); - } - - @Test - public void testAddInt() { - assertThrows(UnsupportedOperationException.class, () -> HEADERS.addInt("name", 0)); - } - - @Test - public void testAddLong() { - assertThrows(UnsupportedOperationException.class, () -> HEADERS.addLong("name", 0)); - } - - @Test - public void testAddShort() { - assertThrows(UnsupportedOperationException.class, () -> HEADERS.addShort("name", (short) 0)); - } - - @Test - public void testAddTimeMillis() { - assertThrows(UnsupportedOperationException.class, () -> HEADERS.addTimeMillis("name", 0)); - } - - @Test - public void testSetStringValue() { - assertThrows(UnsupportedOperationException.class, () -> HEADERS.set("name", "value")); - } - - @Test - public void testSetStringValues() { - assertThrows(UnsupportedOperationException.class, () -> HEADERS.set("name", "value1", "value2")); - } - - @Test - public void testSetStringValuesIterable() { - assertThrows(UnsupportedOperationException.class, () -> HEADERS.set("name", Arrays.asList("value1", "value2"))); - } - - @Test - public void testSetBoolean() { - assertThrows(UnsupportedOperationException.class, () -> HEADERS.setBoolean("name", true)); - } - - @Test - public void testSetByte() { - assertThrows(UnsupportedOperationException.class, () -> HEADERS.setByte("name", (byte) 1)); - } - - @Test - public void testSetChar() { - assertThrows(UnsupportedOperationException.class, () -> HEADERS.setChar("name", 'a')); - } - - @Test - public void testSetDouble() { - assertThrows(UnsupportedOperationException.class, () -> HEADERS.setDouble("name", 0)); - } - - @Test - public void testSetFloat() { - assertThrows(UnsupportedOperationException.class, () -> HEADERS.setFloat("name", 0)); - } - - @Test - public void testSetInt() { - assertThrows(UnsupportedOperationException.class, () -> HEADERS.setInt("name", 0)); - } - - @Test - public void testSetLong() { - assertThrows(UnsupportedOperationException.class, () -> HEADERS.setLong("name", 0)); - } - - @Test - public void testSetShort() { - assertThrows(UnsupportedOperationException.class, () -> HEADERS.setShort("name", (short) 0)); - } - - @Test - public void testSetTimeMillis() { - assertThrows(UnsupportedOperationException.class, () -> HEADERS.setTimeMillis("name", 0)); - } - - @Test - public void testSetAll() { - assertThrows(UnsupportedOperationException.class, () -> HEADERS.setAll(new TestEmptyHeaders())); - } - - @Test - public void testSet() { - assertThrows(UnsupportedOperationException.class, () -> HEADERS.set(new TestEmptyHeaders())); - } - - @Test - public void testGet() { - assertNull(HEADERS.get("name1")); - } - - @Test - public void testGetDefault() { - assertEquals("default", HEADERS.get("name1", "default")); - } - - @Test - public void testGetAndRemove() { - assertNull(HEADERS.getAndRemove("name1")); - } - - @Test - public void testGetAndRemoveDefault() { - assertEquals("default", HEADERS.getAndRemove("name1", "default")); - } - - @Test - public void testGetAll() { - assertEquals(Collections.emptyList(), HEADERS.getAll("name1")); - } - - @Test - public void testGetAllAndRemove() { - assertEquals(Collections.emptyList(), HEADERS.getAllAndRemove("name1")); - } - - @Test - public void testGetBoolean() { - assertNull(HEADERS.getBoolean("name1")); - } - - @Test - public void testGetBooleanDefault() { - assertTrue(HEADERS.getBoolean("name1", true)); - } - - @Test - public void testGetBooleanAndRemove() { - assertNull(HEADERS.getBooleanAndRemove("name1")); - } - - @Test - public void testGetBooleanAndRemoveDefault() { - assertTrue(HEADERS.getBooleanAndRemove("name1", true)); - } - - @Test - public void testGetByte() { - assertNull(HEADERS.getByte("name1")); - } - - @Test - public void testGetByteDefault() { - assertEquals((byte) 0, HEADERS.getByte("name1", (byte) 0)); - } - - @Test - public void testGetByteAndRemove() { - assertNull(HEADERS.getByteAndRemove("name1")); - } - - @Test - public void testGetByteAndRemoveDefault() { - assertEquals((byte) 0, HEADERS.getByteAndRemove("name1", (byte) 0)); - } - - @Test - public void testGetChar() { - assertNull(HEADERS.getChar("name1")); - } - - @Test - public void testGetCharDefault() { - assertEquals('x', HEADERS.getChar("name1", 'x')); - } - - @Test - public void testGetCharAndRemove() { - assertNull(HEADERS.getCharAndRemove("name1")); - } - - @Test - public void testGetCharAndRemoveDefault() { - assertEquals('x', HEADERS.getCharAndRemove("name1", 'x')); - } - - @Test - public void testGetDouble() { - assertNull(HEADERS.getDouble("name1")); - } - - @Test - public void testGetDoubleDefault() { - assertEquals(1, HEADERS.getDouble("name1", 1), 0); - } - - @Test - public void testGetDoubleAndRemove() { - assertNull(HEADERS.getDoubleAndRemove("name1")); - } - - @Test - public void testGetDoubleAndRemoveDefault() { - assertEquals(1, HEADERS.getDoubleAndRemove("name1", 1), 0); - } - - @Test - public void testGetFloat() { - assertNull(HEADERS.getFloat("name1")); - } - - @Test - public void testGetFloatDefault() { - assertEquals(1, HEADERS.getFloat("name1", 1), 0); - } - - @Test - public void testGetFloatAndRemove() { - assertNull(HEADERS.getFloatAndRemove("name1")); - } - - @Test - public void testGetFloatAndRemoveDefault() { - assertEquals(1, HEADERS.getFloatAndRemove("name1", 1), 0); - } - - @Test - public void testGetInt() { - assertNull(HEADERS.getInt("name1")); - } - - @Test - public void testGetIntDefault() { - assertEquals(1, HEADERS.getInt("name1", 1)); - } - - @Test - public void testGetIntAndRemove() { - assertNull(HEADERS.getIntAndRemove("name1")); - } - - @Test - public void testGetIntAndRemoveDefault() { - assertEquals(1, HEADERS.getIntAndRemove("name1", 1)); - } - - @Test - public void testGetLong() { - assertNull(HEADERS.getLong("name1")); - } - - @Test - public void testGetLongDefault() { - assertEquals(1, HEADERS.getLong("name1", 1)); - } - - @Test - public void testGetLongAndRemove() { - assertNull(HEADERS.getLongAndRemove("name1")); - } - - @Test - public void testGetLongAndRemoveDefault() { - assertEquals(1, HEADERS.getLongAndRemove("name1", 1)); - } - - @Test - public void testGetShort() { - assertNull(HEADERS.getShort("name1")); - } - - @Test - public void testGetShortDefault() { - assertEquals(1, HEADERS.getShort("name1", (short) 1)); - } - - @Test - public void testGetShortAndRemove() { - assertNull(HEADERS.getShortAndRemove("name1")); - } - - @Test - public void testGetShortAndRemoveDefault() { - assertEquals(1, HEADERS.getShortAndRemove("name1", (short) 1)); - } - - @Test - public void testGetTimeMillis() { - assertNull(HEADERS.getTimeMillis("name1")); - } - - @Test - public void testGetTimeMillisDefault() { - assertEquals(1, HEADERS.getTimeMillis("name1", 1)); - } - - @Test - public void testGetTimeMillisAndRemove() { - assertNull(HEADERS.getTimeMillisAndRemove("name1")); - } - - @Test - public void testGetTimeMillisAndRemoveDefault() { - assertEquals(1, HEADERS.getTimeMillisAndRemove("name1", 1)); - } - - @Test - public void testContains() { - assertFalse(HEADERS.contains("name1")); - } - - @Test - public void testContainsWithValue() { - assertFalse(HEADERS.contains("name1", "value1")); - } - - @Test - public void testContainsBoolean() { - assertFalse(HEADERS.containsBoolean("name1", false)); - } - - @Test - public void testContainsByte() { - assertFalse(HEADERS.containsByte("name1", (byte) 'x')); - } - - @Test - public void testContainsChar() { - assertFalse(HEADERS.containsChar("name1", 'x')); - } - - @Test - public void testContainsDouble() { - assertFalse(HEADERS.containsDouble("name1", 1)); - } - - @Test - public void testContainsFloat() { - assertFalse(HEADERS.containsFloat("name1", 1)); - } - - @Test - public void testContainsInt() { - assertFalse(HEADERS.containsInt("name1", 1)); - } - - @Test - public void testContainsLong() { - assertFalse(HEADERS.containsLong("name1", 1)); - } - - @Test - public void testContainsShort() { - assertFalse(HEADERS.containsShort("name1", (short) 1)); - } - - @Test - public void testContainsTimeMillis() { - assertFalse(HEADERS.containsTimeMillis("name1", 1)); - } - - @Test - public void testContainsObject() { - assertFalse(HEADERS.containsObject("name1", "")); - } - - @Test - public void testIsEmpty() { - assertTrue(HEADERS.isEmpty()); - } - - @Test - public void testClear() { - assertSame(HEADERS, HEADERS.clear()); - } - - @Test - public void testSize() { - assertEquals(0, HEADERS.size()); - } - - @Test - public void testValueIterator() { - assertFalse(HEADERS.valueIterator("name1").hasNext()); - } - - private static final class TestEmptyHeaders extends EmptyHeaders { } -} diff --git a/codec/src/test/java/io/netty/handler/codec/LengthFieldBasedFrameDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/LengthFieldBasedFrameDecoderTest.java deleted file mode 100644 index afc86b34af..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/LengthFieldBasedFrameDecoderTest.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - -public class LengthFieldBasedFrameDecoderTest { - - @Test - public void testDiscardTooLongFrame1() { - ByteBuf buf = Unpooled.buffer(); - buf.writeInt(32); - for (int i = 0; i < 32; i++) { - buf.writeByte(i); - } - buf.writeInt(1); - buf.writeByte('a'); - EmbeddedChannel channel = new EmbeddedChannel(new LengthFieldBasedFrameDecoder(16, 0, 4)); - try { - channel.writeInbound(buf); - fail(); - } catch (TooLongFrameException e) { - // expected - } - assertTrue(channel.finish()); - - ByteBuf b = channel.readInbound(); - assertEquals(5, b.readableBytes()); - assertEquals(1, b.readInt()); - assertEquals('a', b.readByte()); - b.release(); - - assertNull(channel.readInbound()); - channel.finish(); - } - - @Test - public void testDiscardTooLongFrame2() { - ByteBuf buf = Unpooled.buffer(); - buf.writeInt(32); - for (int i = 0; i < 32; i++) { - buf.writeByte(i); - } - buf.writeInt(1); - buf.writeByte('a'); - EmbeddedChannel channel = new EmbeddedChannel(new LengthFieldBasedFrameDecoder(16, 0, 4)); - try { - channel.writeInbound(buf.readRetainedSlice(14)); - fail(); - } catch (TooLongFrameException e) { - // expected - } - assertTrue(channel.writeInbound(buf.readRetainedSlice(buf.readableBytes()))); - - assertTrue(channel.finish()); - - ByteBuf b = channel.readInbound(); - assertEquals(5, b.readableBytes()); - assertEquals(1, b.readInt()); - assertEquals('a', b.readByte()); - b.release(); - - assertNull(channel.readInbound()); - channel.finish(); - - buf.release(); - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/LineBasedFrameDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/LineBasedFrameDecoderTest.java deleted file mode 100644 index 82ff201ca3..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/LineBasedFrameDecoderTest.java +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.CharsetUtil; -import io.netty.util.ReferenceCountUtil; -import org.junit.jupiter.api.Test; - -import static io.netty.buffer.Unpooled.*; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.CoreMatchers.*; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - -public class LineBasedFrameDecoderTest { - @Test - public void testDecodeWithStrip() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new LineBasedFrameDecoder(8192, true, false)); - - ch.writeInbound(copiedBuffer("first\r\nsecond\nthird", CharsetUtil.US_ASCII)); - - ByteBuf buf = ch.readInbound(); - assertEquals("first", buf.toString(CharsetUtil.US_ASCII)); - - ByteBuf buf2 = ch.readInbound(); - assertEquals("second", buf2.toString(CharsetUtil.US_ASCII)); - assertNull(ch.readInbound()); - ch.finish(); - - ReferenceCountUtil.release(ch.readInbound()); - - buf.release(); - buf2.release(); - } - - @Test - public void testDecodeWithoutStrip() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new LineBasedFrameDecoder(8192, false, false)); - - ch.writeInbound(copiedBuffer("first\r\nsecond\nthird", CharsetUtil.US_ASCII)); - - ByteBuf buf = ch.readInbound(); - assertEquals("first\r\n", buf.toString(CharsetUtil.US_ASCII)); - - ByteBuf buf2 = ch.readInbound(); - assertEquals("second\n", buf2.toString(CharsetUtil.US_ASCII)); - assertNull(ch.readInbound()); - ch.finish(); - ReferenceCountUtil.release(ch.readInbound()); - - buf.release(); - buf2.release(); - } - - @Test - public void testTooLongLine1() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new LineBasedFrameDecoder(16, false, false)); - - try { - ch.writeInbound(copiedBuffer("12345678901234567890\r\nfirst\nsecond", CharsetUtil.US_ASCII)); - fail(); - } catch (Exception e) { - assertThat(e, is(instanceOf(TooLongFrameException.class))); - } - - ByteBuf buf = ch.readInbound(); - ByteBuf buf2 = copiedBuffer("first\n", CharsetUtil.US_ASCII); - assertThat(buf, is(buf2)); - assertThat(ch.finish(), is(false)); - - buf.release(); - buf2.release(); - } - - @Test - public void testTooLongLine2() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new LineBasedFrameDecoder(16, false, false)); - - assertFalse(ch.writeInbound(copiedBuffer("12345678901234567", CharsetUtil.US_ASCII))); - try { - ch.writeInbound(copiedBuffer("890\r\nfirst\r\n", CharsetUtil.US_ASCII)); - fail(); - } catch (Exception e) { - assertThat(e, is(instanceOf(TooLongFrameException.class))); - } - - ByteBuf buf = ch.readInbound(); - ByteBuf buf2 = copiedBuffer("first\r\n", CharsetUtil.US_ASCII); - assertThat(buf, is(buf2)); - assertThat(ch.finish(), is(false)); - - buf.release(); - buf2.release(); - } - - @Test - public void testTooLongLineWithFailFast() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new LineBasedFrameDecoder(16, false, true)); - - try { - ch.writeInbound(copiedBuffer("12345678901234567", CharsetUtil.US_ASCII)); - fail(); - } catch (Exception e) { - assertThat(e, is(instanceOf(TooLongFrameException.class))); - } - - assertThat(ch.writeInbound(copiedBuffer("890", CharsetUtil.US_ASCII)), is(false)); - assertThat(ch.writeInbound(copiedBuffer("123\r\nfirst\r\n", CharsetUtil.US_ASCII)), is(true)); - - ByteBuf buf = ch.readInbound(); - ByteBuf buf2 = copiedBuffer("first\r\n", CharsetUtil.US_ASCII); - assertThat(buf, is(buf2)); - assertThat(ch.finish(), is(false)); - - buf.release(); - buf2.release(); - } - - @Test - public void testDecodeSplitsCorrectly() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new LineBasedFrameDecoder(8192, false, false)); - - assertTrue(ch.writeInbound(copiedBuffer("line\r\n.\r\n", CharsetUtil.US_ASCII))); - - ByteBuf buf = ch.readInbound(); - assertEquals("line\r\n", buf.toString(CharsetUtil.US_ASCII)); - - ByteBuf buf2 = ch.readInbound(); - assertEquals(".\r\n", buf2.toString(CharsetUtil.US_ASCII)); - assertFalse(ch.finishAndReleaseAll()); - - buf.release(); - buf2.release(); - } - - @Test - public void testFragmentedDecode() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new LineBasedFrameDecoder(8192, false, false)); - - assertFalse(ch.writeInbound(copiedBuffer("huu", CharsetUtil.US_ASCII))); - assertNull(ch.readInbound()); - - assertFalse(ch.writeInbound(copiedBuffer("haa\r", CharsetUtil.US_ASCII))); - assertNull(ch.readInbound()); - - assertTrue(ch.writeInbound(copiedBuffer("\nhuuhaa\r\n", CharsetUtil.US_ASCII))); - ByteBuf buf = ch.readInbound(); - assertEquals("huuhaa\r\n", buf.toString(CharsetUtil.US_ASCII)); - - ByteBuf buf2 = ch.readInbound(); - assertEquals("huuhaa\r\n", buf2.toString(CharsetUtil.US_ASCII)); - assertFalse(ch.finishAndReleaseAll()); - - buf.release(); - buf2.release(); - } - - @Test - public void testEmptyLine() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new LineBasedFrameDecoder(8192, true, false)); - - assertTrue(ch.writeInbound(copiedBuffer("\nabcna\r\n", CharsetUtil.US_ASCII))); - - ByteBuf buf = ch.readInbound(); - assertEquals("", buf.toString(CharsetUtil.US_ASCII)); - - ByteBuf buf2 = ch.readInbound(); - assertEquals("abcna", buf2.toString(CharsetUtil.US_ASCII)); - - assertFalse(ch.finishAndReleaseAll()); - - 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()); - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/MessageAggregatorTest.java b/codec/src/test/java/io/netty/handler/codec/MessageAggregatorTest.java deleted file mode 100644 index e4c430ae19..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/MessageAggregatorTest.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec; - -import io.netty.channel.ChannelHandler; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.*; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufHolder; -import io.netty.buffer.DefaultByteBufHolder; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.CharsetUtil; - -public class MessageAggregatorTest { - private static final class ReadCounter implements ChannelHandler { - int value; - - @Override - public void read(ChannelHandlerContext ctx) { - value++; - ctx.read(); - } - } - - abstract static class MockMessageAggregator - extends MessageAggregator { - - protected MockMessageAggregator() { - super(1024); - } - - @Override - protected ByteBufHolder beginAggregation(ByteBufHolder start, ByteBuf content) throws Exception { - return start.replace(content); - } - } - - private static ByteBufHolder message(String string) { - return new DefaultByteBufHolder( - Unpooled.copiedBuffer(string, CharsetUtil.US_ASCII)); - } - - @SuppressWarnings("unchecked") - @Test - public void testReadFlowManagement() throws Exception { - ReadCounter counter = new ReadCounter(); - ByteBufHolder first = message("first"); - ByteBufHolder chunk = message("chunk"); - ByteBufHolder last = message("last"); - - MockMessageAggregator agg = spy(MockMessageAggregator.class); - when(agg.isStartMessage(first)).thenReturn(true); - when(agg.isContentMessage(chunk)).thenReturn(true); - when(agg.isContentMessage(last)).thenReturn(true); - when(agg.isLastContentMessage(last)).thenReturn(true); - - EmbeddedChannel embedded = new EmbeddedChannel(counter, agg); - embedded.config().setAutoRead(false); - - assertFalse(embedded.writeInbound(first)); - assertFalse(embedded.writeInbound(chunk)); - assertTrue(embedded.writeInbound(last)); - - assertEquals(3, counter.value); // 2 reads issued from MockMessageAggregator - // 1 read issued from EmbeddedChannel constructor - - ByteBufHolder all = new DefaultByteBufHolder(Unpooled.wrappedBuffer( - first.content().retain(), chunk.content().retain(), last.content().retain())); - ByteBufHolder out = embedded.readInbound(); - - assertEquals(all, out); - assertTrue(all.release() && out.release()); - assertFalse(embedded.finish()); - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/MessageToMessageEncoderTest.java b/codec/src/test/java/io/netty/handler/codec/MessageToMessageEncoderTest.java deleted file mode 100644 index 410afac13f..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/MessageToMessageEncoderTest.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.concurrent.Future; -import org.junit.jupiter.api.Test; - -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertThrows; - -public class MessageToMessageEncoderTest { - - /** - * Test-case for https://github.com/netty/netty/issues/1656 - */ - @Test - public void testException() { - EmbeddedChannel channel = new EmbeddedChannel(new MessageToMessageEncoder() { - @Override - protected void encode(ChannelHandlerContext ctx, Object msg, List out) throws Exception { - throw new Exception(); - } - }); - assertThrows(EncoderException.class, () -> 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 ChannelHandler() { - private boolean firstWritten; - @Override - public Future write(ChannelHandlerContext ctx, Object msg) { - if (firstWritten) { - return ctx.write(msg); - } else { - firstWritten = true; - return ctx.newFailedFuture(firstWriteException); - } - } - }; - - EmbeddedChannel channel = new EmbeddedChannel(writeThrower, encoder); - Object msg = new Object(); - Future write = channel.writeAndFlush(msg); - assertSame(firstWriteException, write.cause()); - assertSame(msg, channel.readOutbound()); - assertFalse(channel.finish()); - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/ReplayingDecoderByteBufTest.java b/codec/src/test/java/io/netty/handler/codec/ReplayingDecoderByteBufTest.java deleted file mode 100644 index 209dd0e8d7..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/ReplayingDecoderByteBufTest.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.CharsetUtil; -import io.netty.util.Signal; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class ReplayingDecoderByteBufTest { - - /** - * See https://github.com/netty/netty/issues/445 - */ - @Test - public void testGetUnsignedByte() { - ByteBuf buf = Unpooled.copiedBuffer("TestBuffer", CharsetUtil.ISO_8859_1); - ReplayingDecoderByteBuf buffer = new ReplayingDecoderByteBuf(buf); - - boolean error; - int i = 0; - try { - for (;;) { - buffer.getUnsignedByte(i); - i++; - } - } catch (Signal e) { - error = true; - } - - assertTrue(error); - assertEquals(10, i); - - buf.release(); - } - - /** - * See https://github.com/netty/netty/issues/445 - */ - @Test - public void testGetByte() { - ByteBuf buf = Unpooled.copiedBuffer("TestBuffer", CharsetUtil.ISO_8859_1); - ReplayingDecoderByteBuf buffer = new ReplayingDecoderByteBuf(buf); - - boolean error; - int i = 0; - try { - for (;;) { - buffer.getByte(i); - i++; - } - } catch (Signal e) { - error = true; - } - - assertTrue(error); - assertEquals(10, i); - - buf.release(); - } - - /** - * See https://github.com/netty/netty/issues/445 - */ - @Test - public void testGetBoolean() { - ByteBuf buf = Unpooled.buffer(10); - while (buf.isWritable()) { - buf.writeBoolean(true); - } - ReplayingDecoderByteBuf buffer = new ReplayingDecoderByteBuf(buf); - - boolean error; - int i = 0; - try { - for (;;) { - buffer.getBoolean(i); - i++; - } - } catch (Signal e) { - error = true; - } - - assertTrue(error); - assertEquals(10, i); - - buf.release(); - } - -} diff --git a/codec/src/test/java/io/netty/handler/codec/ReplayingDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/ReplayingDecoderTest.java deleted file mode 100644 index d9f975cb71..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/ReplayingDecoderTest.java +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.channel.socket.ChannelInputShutdownEvent; -import org.junit.jupiter.api.Test; - -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingDeque; -import java.util.concurrent.ThreadLocalRandom; -import java.util.concurrent.atomic.AtomicReference; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class ReplayingDecoderTest { - - @Test - public void testLineProtocol() { - EmbeddedChannel ch = new EmbeddedChannel(new LineDecoder()); - - // Ordinary input - ch.writeInbound(Unpooled.wrappedBuffer(new byte[] { 'A' })); - assertNull(ch.readInbound()); - ch.writeInbound(Unpooled.wrappedBuffer(new byte[] { 'B' })); - assertNull(ch.readInbound()); - ch.writeInbound(Unpooled.wrappedBuffer(new byte[] { 'C' })); - assertNull(ch.readInbound()); - ch.writeInbound(Unpooled.wrappedBuffer(new byte[] { '\n' })); - - ByteBuf buf = Unpooled.wrappedBuffer(new byte[] { 'A', 'B', 'C' }); - ByteBuf buf2 = ch.readInbound(); - assertEquals(buf, buf2); - - buf.release(); - buf2.release(); - - // Truncated input - ch.writeInbound(Unpooled.wrappedBuffer(new byte[] { 'A' })); - assertNull(ch.readInbound()); - - ch.finish(); - assertNull(ch.readInbound()); - } - - private static final class LineDecoder extends ReplayingDecoder { - - LineDecoder() { - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) { - ByteBuf msg = in.readBytes(in.bytesBefore((byte) '\n')); - in.skipBytes(1); - ctx.fireChannelRead(msg); - } - } - - @Test - public void testReplacement() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel(new BloatedLineDecoder()); - - // "AB" should be forwarded to LineDecoder by BloatedLineDecoder. - ch.writeInbound(Unpooled.wrappedBuffer(new byte[]{'A', 'B'})); - assertNull(ch.readInbound()); - - // "C\n" should be appended to "AB" so that LineDecoder decodes it correctly. - ch.writeInbound(Unpooled.wrappedBuffer(new byte[]{'C', '\n'})); - - ByteBuf buf = Unpooled.wrappedBuffer(new byte[] { 'A', 'B', 'C' }); - ByteBuf buf2 = ch.readInbound(); - assertEquals(buf, buf2); - - buf.release(); - buf2.release(); - - ch.finish(); - assertNull(ch.readInbound()); - } - - private static final class BloatedLineDecoder implements ChannelHandler { - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - ctx.pipeline().replace(this, "less-bloated", new LineDecoder()); - ctx.pipeline().fireChannelRead(msg); - } - } - - @Test - public void testSingleDecode() throws Exception { - LineDecoder decoder = new LineDecoder(); - decoder.setSingleDecode(true); - EmbeddedChannel ch = new EmbeddedChannel(decoder); - - // "C\n" should be appended to "AB" so that LineDecoder decodes it correctly. - ch.writeInbound(Unpooled.wrappedBuffer(new byte[]{'C', '\n' , 'B', '\n'})); - - ByteBuf buf = Unpooled.wrappedBuffer(new byte[] {'C'}); - ByteBuf buf2 = ch.readInbound(); - assertEquals(buf, buf2); - - buf.release(); - buf2.release(); - - assertNull(ch.readInbound(), "Must be null as it must only decode one frame"); - - ch.read(); - ch.finish(); - - buf = Unpooled.wrappedBuffer(new byte[] {'B'}); - buf2 = ch.readInbound(); - assertEquals(buf, buf2); - - buf.release(); - buf2.release(); - - assertNull(ch.readInbound()); - } - - @Test - public void testRemoveItself() { - EmbeddedChannel channel = new EmbeddedChannel(new ReplayingDecoder() { - private boolean removed; - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - assertFalse(removed); - in.readByte(); - ctx.pipeline().remove(this); - removed = true; - } - }); - - ByteBuf buf = Unpooled.wrappedBuffer(new byte[] {'a', 'b', 'c'}); - channel.writeInbound(buf.copy()); - ByteBuf b = channel.readInbound(); - assertEquals(b, buf.skipBytes(1)); - b.release(); - buf.release(); - } - - @Test - public void testRemoveItselfWithReplayError() { - EmbeddedChannel channel = new EmbeddedChannel(new ReplayingDecoder() { - private boolean removed; - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - assertFalse(removed); - ctx.pipeline().remove(this); - - in.readBytes(1000); - - removed = true; - } - }); - - ByteBuf buf = Unpooled.wrappedBuffer(new byte[] {'a', 'b', 'c'}); - channel.writeInbound(buf.copy()); - ByteBuf b = channel.readInbound(); - - assertEquals(b, buf, "Expect to have still all bytes in the buffer"); - b.release(); - buf.release(); - } - - @Test - public void testRemoveItselfWriteBuffer() { - final ByteBuf buf = Unpooled.buffer().writeBytes(new byte[] {'a', 'b', 'c'}); - EmbeddedChannel channel = new EmbeddedChannel(new ReplayingDecoder() { - private boolean removed; - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - assertFalse(removed); - in.readByte(); - ctx.pipeline().remove(this); - - // This should not let it keep call decode - buf.writeByte('d'); - removed = true; - } - }); - - channel.writeInbound(buf.copy()); - ByteBuf b = channel.readInbound(); - assertEquals(b, Unpooled.wrappedBuffer(new byte[] { 'b', 'c'})); - b.release(); - buf.release(); - } - - @Test - public void testFireChannelReadCompleteOnInactive() throws InterruptedException { - final BlockingQueue queue = new LinkedBlockingDeque<>(); - final ByteBuf buf = Unpooled.buffer().writeBytes(new byte[]{'a', 'b'}); - EmbeddedChannel channel = new EmbeddedChannel(new ReplayingDecoder() { - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - int readable = in.readableBytes(); - assertTrue(readable > 0); - in.skipBytes(readable); - ctx.fireChannelRead("data"); - } - - @Override - protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - assertFalse(in.isReadable()); - ctx.fireChannelRead("data"); - } - }, new ChannelHandler() { - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - queue.add(3); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - queue.add(1); - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { - if (!ctx.channel().isActive()) { - queue.add(2); - } - } - }); - assertFalse(channel.writeInbound(buf)); - channel.finish(); - assertEquals(1, (int) queue.take()); - assertEquals(1, (int) queue.take()); - assertEquals(2, (int) queue.take()); - assertEquals(3, (int) queue.take()); - assertTrue(queue.isEmpty()); - } - - @Test - public void testChannelInputShutdownEvent() { - final AtomicReference error = new AtomicReference<>(); - - EmbeddedChannel channel = new EmbeddedChannel(new ReplayingDecoder(0) { - private boolean decoded; - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - if (!(in instanceof ReplayingDecoderByteBuf)) { - error.set(new AssertionError("in must be of type " + ReplayingDecoderByteBuf.class - + " but was " + in.getClass())); - return; - } - if (!decoded) { - decoded = true; - in.readByte(); - state(1); - } else { - // This will throw an ReplayingError - in.skipBytes(Integer.MAX_VALUE); - } - } - }); - - assertFalse(channel.writeInbound(Unpooled.wrappedBuffer(new byte[] {0, 1}))); - channel.pipeline().fireUserEventTriggered(ChannelInputShutdownEvent.INSTANCE); - assertFalse(channel.finishAndReleaseAll()); - - Error err = error.get(); - if (err != null) { - throw err; - } - } - - @Test - public void handlerRemovedWillNotReleaseBufferIfDecodeInProgress() { - EmbeddedChannel channel = new EmbeddedChannel(new ReplayingDecoder() { - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - ctx.pipeline().remove(this); - assertTrue(in.refCnt() != 0); - } - - @Override - protected void handlerRemoved0(ChannelHandlerContext ctx) throws Exception { - assertCumulationReleased(internalBuffer()); - } - }); - byte[] bytes = new byte[1024]; - ThreadLocalRandom.current().nextBytes(bytes); - - assertTrue(channel.writeInbound(Unpooled.wrappedBuffer(bytes))); - assertTrue(channel.finishAndReleaseAll()); - } - - private static void assertCumulationReleased(ByteBuf byteBuf) { - assertTrue(byteBuf == null || byteBuf == Unpooled.EMPTY_BUFFER || byteBuf.refCnt() == 0, - "unexpected value: " + byteBuf); - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/base64/Base64Test.java b/codec/src/test/java/io/netty/handler/codec/base64/Base64Test.java deleted file mode 100644 index 90bf62e6e7..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/base64/Base64Test.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.base64; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.buffer.Unpooled; -import io.netty.util.CharsetUtil; -import io.netty.util.internal.StringUtil; -import org.junit.jupiter.api.Test; - -import java.io.ByteArrayInputStream; -import java.nio.ByteOrder; -import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; -import java.util.concurrent.ThreadLocalRandom; - -import static io.netty.buffer.Unpooled.copiedBuffer; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - -public class Base64Test { - - @Test - public void testNotAddNewLineWhenEndOnLimit() { - ByteBuf src = copiedBuffer("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcde", - CharsetUtil.US_ASCII); - ByteBuf expectedEncoded = - copiedBuffer("YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5emFiY2Rl", - CharsetUtil.US_ASCII); - testEncode(src, expectedEncoded); - } - - @Test - public void testAddNewLine() { - ByteBuf src = copiedBuffer("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz12345678", - CharsetUtil.US_ASCII); - ByteBuf expectedEncoded = - copiedBuffer("YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejEyMzQ1\nNjc4", - CharsetUtil.US_ASCII); - testEncode(src, expectedEncoded); - } - - @Test - public void testEncodeEmpty() { - ByteBuf src = Unpooled.EMPTY_BUFFER; - ByteBuf expectedEncoded = Unpooled.EMPTY_BUFFER; - testEncode(src, expectedEncoded); - } - - @Test - public void testPaddingNewline() throws Exception { - String cert = "-----BEGIN CERTIFICATE-----\n" + - "MIICqjCCAjGgAwIBAgICI1YwCQYHKoZIzj0EATAmMSQwIgYDVQQDDBtUcnVzdGVk\n" + - "IFRoaW4gQ2xpZW50IFJvb3QgQ0EwIhcRMTYwMTI0MTU0OTQ1LTA2MDAXDTE2MDQy\n" + - "NTIyNDk0NVowYzEwMC4GA1UEAwwnREMgMGRlYzI0MGYtOTI2OS00MDY5LWE2MTYt\n" + - "YjJmNTI0ZjA2ZGE0MREwDwYDVQQLDAhEQyBJUFNFQzEcMBoGA1UECgwTVHJ1c3Rl\n" + - "ZCBUaGluIENsaWVudDB2MBAGByqGSM49AgEGBSuBBAAiA2IABOB7pZYC24sF5gJm\n" + - "OHXhasxmrNYebdtSAiQRgz0M0pIsogsFeTU/W0HTlTOqwDDckphHESAKHVxa6EBL\n" + - "d+/8HYZ1AaCmXtG73XpaOyaRr3TipJl2IaJzwuehgDHs0L+qcqOB8TCB7jAwBgYr\n" + - "BgEBEAQEJgwkMGRlYzI0MGYtOTI2OS00MDY5LWE2MTYtYjJmNTI0ZjA2ZGE0MCMG\n" + - "CisGAQQBjCHbZwEEFQwTNDkwNzUyMjc1NjM3MTE3Mjg5NjAUBgorBgEEAYwh22cC\n" + - "BAYMBDIwNTkwCwYDVR0PBAQDAgXgMAkGA1UdEwQCMAAwHQYDVR0OBBYEFGWljaKj\n" + - "wiGqW61PgLL/zLxj4iirMB8GA1UdIwQYMBaAFA2FRBtG/dGnl0iXP2uKFwJHmEQI\n" + - "MCcGA1UdJQQgMB4GCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYBBQUHAwkwCQYHKoZI\n" + - "zj0EAQNoADBlAjAQFP8rMLUxl36u8610LsSCiRG8pP3gjuLaaJMm3tjbVue/TI4C\n" + - "z3iL8i96YWK0VxcCMQC7pf6Wk3RhUU2Sg6S9e6CiirFLDyzLkaWxuCnXcOwTvuXT\n" + - "HUQSeUCp2Q6ygS5qKyc=\n" + - "-----END CERTIFICATE-----"; - - String expected = "MIICqjCCAjGgAwIBAgICI1YwCQYHKoZIzj0EATAmMSQwIgYDVQQDDBtUcnVzdGVkIFRoaW4gQ2xp\n" + - "ZW50IFJvb3QgQ0EwIhcRMTYwMTI0MTU0OTQ1LTA2MDAXDTE2MDQyNTIyNDk0NVowYzEwMC4GA1UE\n" + - "AwwnREMgMGRlYzI0MGYtOTI2OS00MDY5LWE2MTYtYjJmNTI0ZjA2ZGE0MREwDwYDVQQLDAhEQyBJ\n" + - "UFNFQzEcMBoGA1UECgwTVHJ1c3RlZCBUaGluIENsaWVudDB2MBAGByqGSM49AgEGBSuBBAAiA2IA\n" + - "BOB7pZYC24sF5gJmOHXhasxmrNYebdtSAiQRgz0M0pIsogsFeTU/W0HTlTOqwDDckphHESAKHVxa\n" + - "6EBLd+/8HYZ1AaCmXtG73XpaOyaRr3TipJl2IaJzwuehgDHs0L+qcqOB8TCB7jAwBgYrBgEBEAQE\n" + - "JgwkMGRlYzI0MGYtOTI2OS00MDY5LWE2MTYtYjJmNTI0ZjA2ZGE0MCMGCisGAQQBjCHbZwEEFQwT\n" + - "NDkwNzUyMjc1NjM3MTE3Mjg5NjAUBgorBgEEAYwh22cCBAYMBDIwNTkwCwYDVR0PBAQDAgXgMAkG\n" + - "A1UdEwQCMAAwHQYDVR0OBBYEFGWljaKjwiGqW61PgLL/zLxj4iirMB8GA1UdIwQYMBaAFA2FRBtG\n" + - "/dGnl0iXP2uKFwJHmEQIMCcGA1UdJQQgMB4GCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYBBQUHAwkw\n" + - "CQYHKoZIzj0EAQNoADBlAjAQFP8rMLUxl36u8610LsSCiRG8pP3gjuLaaJMm3tjbVue/TI4Cz3iL\n" + - "8i96YWK0VxcCMQC7pf6Wk3RhUU2Sg6S9e6CiirFLDyzLkaWxuCnXcOwTvuXTHUQSeUCp2Q6ygS5q\n" + - "Kyc="; - - ByteBuf src = Unpooled.wrappedBuffer(certFromString(cert).getEncoded()); - ByteBuf expectedEncoded = copiedBuffer(expected, CharsetUtil.US_ASCII); - testEncode(src, expectedEncoded); - } - - private static X509Certificate certFromString(String string) throws Exception { - CertificateFactory factory = CertificateFactory.getInstance("X.509"); - ByteArrayInputStream bin = new ByteArrayInputStream(string.getBytes(CharsetUtil.US_ASCII)); - try { - return (X509Certificate) factory.generateCertificate(bin); - } finally { - bin.close(); - } - } - - private static void testEncode(ByteBuf src, ByteBuf expectedEncoded) { - ByteBuf encoded = Base64.encode(src, true, Base64Dialect.STANDARD); - try { - assertEquals(expectedEncoded, encoded); - } finally { - src.release(); - expectedEncoded.release(); - encoded.release(); - } - } - - @Test - public void testEncodeDecodeBE() { - testEncodeDecode(ByteOrder.BIG_ENDIAN); - } - - @Test - public void testEncodeDecodeLE() { - testEncodeDecode(ByteOrder.LITTLE_ENDIAN); - } - - private static void testEncodeDecode(ByteOrder order) { - testEncodeDecode(64, order); - testEncodeDecode(128, order); - testEncodeDecode(512, order); - testEncodeDecode(1024, order); - testEncodeDecode(4096, order); - testEncodeDecode(8192, order); - testEncodeDecode(16384, order); - } - - private static void testEncodeDecode(int size, ByteOrder order) { - byte[] bytes = new byte[size]; - ThreadLocalRandom.current().nextBytes(bytes); - - ByteBuf src = Unpooled.wrappedBuffer(bytes).order(order); - ByteBuf encoded = Base64.encode(src); - ByteBuf decoded = Base64.decode(encoded); - ByteBuf expectedBuf = Unpooled.wrappedBuffer(bytes); - try { - assertEquals(expectedBuf, decoded, - StringUtil.NEWLINE + "expected: " + ByteBufUtil.hexDump(expectedBuf) + - StringUtil.NEWLINE + "actual--: " + ByteBufUtil.hexDump(decoded)); - } finally { - src.release(); - encoded.release(); - decoded.release(); - expectedBuf.release(); - } - } - - @Test - public void testOverflowEncodedBufferSize() { - assertEquals(Integer.MAX_VALUE, Base64.encodedBufferSize(Integer.MAX_VALUE, true)); - assertEquals(Integer.MAX_VALUE, Base64.encodedBufferSize(Integer.MAX_VALUE, false)); - } - - @Test - public void testOverflowDecodedBufferSize() { - assertEquals(1610612736, Base64.decodedBufferSize(Integer.MAX_VALUE)); - } - - @Test - public void decodingFailsOnInvalidInputByte() { - char[] invalidChars = {'\u007F', '\u0080', '\u00BD', '\u00FF'}; - for (char invalidChar : invalidChars) { - ByteBuf buf = copiedBuffer("eHh4" + invalidChar, CharsetUtil.ISO_8859_1); - try { - Base64.decode(buf); - fail("Invalid character in not detected: " + invalidChar); - } catch (IllegalArgumentException ignored) { - // as expected - } finally { - assertTrue(buf.release()); - } - } - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/bytes/ByteArrayDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/bytes/ByteArrayDecoderTest.java deleted file mode 100644 index f7beef9409..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/bytes/ByteArrayDecoderTest.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.bytes; - -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.internal.EmptyArrays; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.Random; - -import static io.netty.buffer.Unpooled.*; -import static org.hamcrest.core.Is.*; -import static org.hamcrest.MatcherAssert.assertThat; - -public class ByteArrayDecoderTest { - - private EmbeddedChannel ch; - - @BeforeEach - public void setUp() { - ch = new EmbeddedChannel(new ByteArrayDecoder()); - } - - @Test - public void testDecode() { - byte[] b = new byte[2048]; - new Random().nextBytes(b); - ch.writeInbound(wrappedBuffer(b)); - assertThat((byte[]) ch.readInbound(), is(b)); - } - - @Test - public void testDecodeEmpty() { - ch.writeInbound(EMPTY_BUFFER); - assertThat((byte[]) ch.readInbound(), is(EmptyArrays.EMPTY_BYTES)); - } - - @Test - public void testDecodeOtherType() { - String str = "Meep!"; - ch.writeInbound(str); - assertThat(ch.readInbound(), is((Object) str)); - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/bytes/ByteArrayEncoderTest.java b/codec/src/test/java/io/netty/handler/codec/bytes/ByteArrayEncoderTest.java deleted file mode 100644 index 9e1df98387..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/bytes/ByteArrayEncoderTest.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.bytes; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.internal.EmptyArrays; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.Random; - -import static io.netty.buffer.Unpooled.*; -import static org.hamcrest.CoreMatchers.*; -import static org.hamcrest.MatcherAssert.assertThat; - -public class ByteArrayEncoderTest { - - private EmbeddedChannel ch; - - @BeforeEach - public void setUp() { - ch = new EmbeddedChannel(new ByteArrayEncoder()); - } - - @AfterEach - public void tearDown() { - assertThat(ch.finish(), is(false)); - } - - @Test - public void testEncode() { - byte[] b = new byte[2048]; - new Random().nextBytes(b); - ch.writeOutbound(b); - ByteBuf encoded = ch.readOutbound(); - assertThat(encoded, is(wrappedBuffer(b))); - encoded.release(); - } - - @Test - public void testEncodeEmpty() { - ch.writeOutbound(EmptyArrays.EMPTY_BYTES); - assertThat((ByteBuf) ch.readOutbound(), is(sameInstance(EMPTY_BUFFER))); - } - - @Test - public void testEncodeOtherType() { - String str = "Meep!"; - ch.writeOutbound(str); - assertThat(ch.readOutbound(), is((Object) str)); - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/compression/AbstractCompressionTest.java b/codec/src/test/java/io/netty/handler/codec/compression/AbstractCompressionTest.java deleted file mode 100644 index 933c33480d..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/compression/AbstractCompressionTest.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import java.util.Random; - -public abstract class AbstractCompressionTest { - - protected static final Random rand; - - protected static final byte[] BYTES_SMALL = new byte[256]; - protected static final byte[] BYTES_LARGE = new byte[256 * 1024]; - - static { - rand = new Random(); - fillArrayWithCompressibleData(BYTES_SMALL); - fillArrayWithCompressibleData(BYTES_LARGE); - } - - private static void fillArrayWithCompressibleData(byte[] array) { - for (int i = 0; i < array.length; i++) { - array[i] = i % 4 != 0 ? 0 : (byte) rand.nextInt(); - } - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/compression/AbstractDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/compression/AbstractDecoderTest.java deleted file mode 100644 index 099db803ac..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/compression/AbstractDecoderTest.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.CompositeByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.TestInstance; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -public abstract class AbstractDecoderTest extends AbstractCompressionTest { - - protected static final ByteBuf WRAPPED_BYTES_SMALL; - protected static final ByteBuf WRAPPED_BYTES_LARGE; - - static { - WRAPPED_BYTES_SMALL = Unpooled.wrappedBuffer(BYTES_SMALL); - WRAPPED_BYTES_LARGE = Unpooled.wrappedBuffer(BYTES_LARGE); - } - - protected EmbeddedChannel channel; - - protected byte[] compressedBytesSmall; - protected byte[] compressedBytesLarge; - - protected AbstractDecoderTest() throws Exception { - compressedBytesSmall = compress(BYTES_SMALL); - compressedBytesLarge = compress(BYTES_LARGE); - } - - /** - * Compresses data with some external library. - */ - protected abstract byte[] compress(byte[] data) throws Exception; - - @BeforeEach - public final void initChannel() { - channel = createChannel(); - } - - protected abstract EmbeddedChannel createChannel(); - - @AfterEach - public void destroyChannel() { - if (channel != null) { - channel.finishAndReleaseAll(); - channel = null; - } - } - - public ByteBuf[] smallData() { - ByteBuf heap = Unpooled.wrappedBuffer(compressedBytesSmall); - ByteBuf direct = Unpooled.directBuffer(compressedBytesSmall.length); - direct.writeBytes(compressedBytesSmall); - return new ByteBuf[] {heap, direct}; - } - - public ByteBuf[] largeData() { - ByteBuf heap = Unpooled.wrappedBuffer(compressedBytesLarge); - ByteBuf direct = Unpooled.directBuffer(compressedBytesLarge.length); - direct.writeBytes(compressedBytesLarge); - return new ByteBuf[] {heap, direct}; - } - - @ParameterizedTest - @MethodSource("smallData") - public void testDecompressionOfSmallChunkOfData(ByteBuf data) throws Exception { - testDecompression(WRAPPED_BYTES_SMALL, data); - } - - @ParameterizedTest - @MethodSource("largeData") - public void testDecompressionOfLargeChunkOfData(ByteBuf data) throws Exception { - testDecompression(WRAPPED_BYTES_LARGE, data); - } - - @ParameterizedTest - @MethodSource("largeData") - public void testDecompressionOfBatchedFlowOfData(ByteBuf data) throws Exception { - testDecompressionOfBatchedFlow(WRAPPED_BYTES_LARGE, data); - } - - protected void testDecompression(final ByteBuf expected, final ByteBuf data) throws Exception { - assertTrue(channel.writeInbound(data)); - - ByteBuf decompressed = readDecompressed(channel); - assertEquals(expected, decompressed); - - decompressed.release(); - } - - protected void testDecompressionOfBatchedFlow(final ByteBuf expected, final ByteBuf data) throws Exception { - final int compressedLength = data.readableBytes(); - int written = 0, length = rand.nextInt(100); - while (written + length < compressedLength) { - ByteBuf compressedBuf = data.retainedSlice(written, length); - channel.writeInbound(compressedBuf); - written += length; - length = rand.nextInt(100); - } - ByteBuf compressedBuf = data.slice(written, compressedLength - written); - assertTrue(channel.writeInbound(compressedBuf.retain())); - - ByteBuf decompressedBuf = readDecompressed(channel); - assertEquals(expected, decompressedBuf); - - decompressedBuf.release(); - data.release(); - } - - protected static ByteBuf readDecompressed(final EmbeddedChannel channel) { - CompositeByteBuf decompressed = Unpooled.compositeBuffer(); - ByteBuf msg; - while ((msg = channel.readInbound()) != null) { - decompressed.addComponent(true, msg); - } - return decompressed; - } - - protected static void tryDecodeAndCatchBufLeaks(final EmbeddedChannel channel, final ByteBuf data) { - try { - channel.writeInbound(data); - } finally { - for (;;) { - ByteBuf inflated = channel.readInbound(); - if (inflated == null) { - break; - } - inflated.release(); - } - channel.finish(); - } - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/compression/AbstractEncoderTest.java b/codec/src/test/java/io/netty/handler/codec/compression/AbstractEncoderTest.java deleted file mode 100644 index 193849723a..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/compression/AbstractEncoderTest.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.CompositeByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public abstract class AbstractEncoderTest extends AbstractCompressionTest { - - protected EmbeddedChannel channel; - - /** - * Decompresses data with some external library. - */ - protected abstract ByteBuf decompress(ByteBuf compressed, int originalLength) throws Exception; - - @BeforeEach - public final void initChannel() { - channel = createChannel(); - } - - protected abstract EmbeddedChannel createChannel(); - - @AfterEach - public void destroyChannel() { - if (channel != null) { - channel.finishAndReleaseAll(); - channel = null; - } - } - - public static ByteBuf[] smallData() { - ByteBuf heap = Unpooled.wrappedBuffer(BYTES_SMALL); - ByteBuf direct = Unpooled.directBuffer(BYTES_SMALL.length); - direct.writeBytes(BYTES_SMALL); - return new ByteBuf[] {heap, direct}; - } - - public static ByteBuf[] largeData() { - ByteBuf heap = Unpooled.wrappedBuffer(BYTES_LARGE); - ByteBuf direct = Unpooled.directBuffer(BYTES_LARGE.length); - direct.writeBytes(BYTES_LARGE); - return new ByteBuf[] {heap, direct}; - } - - @ParameterizedTest - @MethodSource("smallData") - public void testCompressionOfSmallChunkOfData(ByteBuf data) throws Exception { - testCompression(data); - } - - @ParameterizedTest - @MethodSource("largeData") - public void testCompressionOfLargeChunkOfData(ByteBuf data) throws Exception { - testCompression(data); - } - - @ParameterizedTest - @MethodSource("largeData") - public void testCompressionOfBatchedFlowOfData(ByteBuf data) throws Exception { - testCompressionOfBatchedFlow(data); - } - - protected void testCompression(final ByteBuf data) throws Exception { - final int dataLength = data.readableBytes(); - assertTrue(channel.writeOutbound(data.retain())); - assertTrue(channel.finish()); - - ByteBuf decompressed = readDecompressed(dataLength); - data.readerIndex(0); - assertEquals(data, decompressed); - - decompressed.release(); - data.release(); - } - - protected void testCompressionOfBatchedFlow(final ByteBuf data) throws Exception { - final int dataLength = data.readableBytes(); - int written = 0, length = rand.nextInt(100); - while (written + length < dataLength) { - ByteBuf in = data.retainedSlice(written, length); - assertTrue(channel.writeOutbound(in)); - written += length; - length = rand.nextInt(100); - } - ByteBuf in = data.retainedSlice(written, dataLength - written); - assertTrue(channel.writeOutbound(in)); - assertTrue(channel.finish()); - - ByteBuf decompressed = readDecompressed(dataLength); - assertEquals(data, decompressed); - - decompressed.release(); - data.release(); - } - - protected ByteBuf readDecompressed(final int dataLength) throws Exception { - CompositeByteBuf compressed = Unpooled.compositeBuffer(); - ByteBuf msg; - while ((msg = channel.readOutbound()) != null) { - compressed.addComponent(true, msg); - } - return decompress(compressed, dataLength); - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/compression/AbstractIntegrationTest.java b/codec/src/test/java/io/netty/handler/codec/compression/AbstractIntegrationTest.java deleted file mode 100644 index 7cda7ab57d..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/compression/AbstractIntegrationTest.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.CompositeByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.CharsetUtil; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.internal.EmptyArrays; -import org.junit.jupiter.api.Test; - -import java.util.Arrays; -import java.util.Random; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public abstract class AbstractIntegrationTest { - - protected static final Random rand = new Random(); - - protected EmbeddedChannel encoder; - protected EmbeddedChannel decoder; - - protected abstract EmbeddedChannel createEncoder(); - protected abstract EmbeddedChannel createDecoder(); - - public void initChannels() { - encoder = createEncoder(); - decoder = createDecoder(); - } - - public void closeChannels() { - encoder.close(); - for (;;) { - Object msg = encoder.readOutbound(); - if (msg == null) { - break; - } - ReferenceCountUtil.release(msg); - } - - decoder.close(); - for (;;) { - Object msg = decoder.readInbound(); - if (msg == null) { - break; - } - ReferenceCountUtil.release(msg); - } - } - - @Test - public void testEmpty() throws Exception { - testIdentity(EmptyArrays.EMPTY_BYTES, true); - testIdentity(EmptyArrays.EMPTY_BYTES, false); - } - - @Test - public void testOneByte() throws Exception { - final byte[] data = { 'A' }; - testIdentity(data, true); - testIdentity(data, false); - } - - @Test - public void testTwoBytes() throws Exception { - final byte[] data = { 'B', 'A' }; - testIdentity(data, true); - testIdentity(data, false); - } - - @Test - public void testRegular() throws Exception { - final byte[] data = ("Netty is a NIO client server framework which enables " + - "quick and easy development of network applications such as protocol " + - "servers and clients.").getBytes(CharsetUtil.UTF_8); - testIdentity(data, true); - testIdentity(data, false); - } - - @Test - public void testLargeRandom() throws Exception { - final byte[] data = new byte[1024 * 1024]; - rand.nextBytes(data); - testIdentity(data, true); - testIdentity(data, false); - } - - @Test - public void testPartRandom() throws Exception { - final byte[] data = new byte[10240]; - rand.nextBytes(data); - for (int i = 0; i < 1024; i++) { - data[i] = 2; - } - testIdentity(data, true); - testIdentity(data, false); - } - - @Test - public void testCompressible() throws Exception { - final byte[] data = new byte[10240]; - for (int i = 0; i < data.length; i++) { - data[i] = i % 4 != 0 ? 0 : (byte) rand.nextInt(); - } - testIdentity(data, true); - testIdentity(data, false); - } - - @Test - public void testLongBlank() throws Exception { - final byte[] data = new byte[102400]; - testIdentity(data, true); - testIdentity(data, false); - } - - @Test - public void testLongSame() throws Exception { - final byte[] data = new byte[102400]; - Arrays.fill(data, (byte) 123); - testIdentity(data, true); - testIdentity(data, false); - } - - @Test - public void testSequential() throws Exception { - final byte[] data = new byte[1024]; - for (int i = 0; i < data.length; i++) { - data[i] = (byte) i; - } - testIdentity(data, true); - testIdentity(data, false); - } - - protected void testIdentity(final byte[] data, boolean heapBuffer) { - initChannels(); - final ByteBuf in = heapBuffer? Unpooled.wrappedBuffer(data) : - Unpooled.directBuffer(data.length).setBytes(0, data); - final CompositeByteBuf compressed = Unpooled.compositeBuffer(); - final CompositeByteBuf decompressed = Unpooled.compositeBuffer(); - - try { - assertTrue(encoder.writeOutbound(in.retain())); - assertTrue(encoder.finish()); - - ByteBuf msg; - while ((msg = encoder.readOutbound()) != null) { - compressed.addComponent(true, msg); - } - assertThat(compressed, is(notNullValue())); - - decoder.writeInbound(compressed.retain()); - assertFalse(compressed.isReadable()); - while ((msg = decoder.readInbound()) != null) { - decompressed.addComponent(true, msg); - } - in.readerIndex(0); - assertEquals(in, decompressed); - } finally { - compressed.release(); - decompressed.release(); - in.release(); - closeChannels(); - } - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/compression/BrotliDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/compression/BrotliDecoderTest.java deleted file mode 100644 index dfd8653821..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/compression/BrotliDecoderTest.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.compression; - -import com.aayushatharva.brotli4j.encoder.BrotliOutputStream; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.CompositeByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.internal.PlatformDependent; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.condition.DisabledIf; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.Random; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -@DisabledIf(value = "isNotSupported", disabledReason = "Brotli is not supported on this platform") -public class BrotliDecoderTest { - - private static final Random RANDOM; - private static final byte[] BYTES_SMALL = new byte[256]; - private static final byte[] BYTES_LARGE = new byte[256 * 1024]; - private static final ByteBuf WRAPPED_BYTES_SMALL; - private static final ByteBuf WRAPPED_BYTES_LARGE; - private static final byte[] COMPRESSED_BYTES_SMALL; - private static final byte[] COMPRESSED_BYTES_LARGE; - - static { - try { - Brotli.ensureAvailability(); - RANDOM = new Random(); - fillArrayWithCompressibleData(BYTES_SMALL); - fillArrayWithCompressibleData(BYTES_LARGE); - WRAPPED_BYTES_SMALL = Unpooled.wrappedBuffer(BYTES_SMALL); - WRAPPED_BYTES_LARGE = Unpooled.wrappedBuffer(BYTES_LARGE); - COMPRESSED_BYTES_SMALL = compress(BYTES_SMALL); - COMPRESSED_BYTES_LARGE = compress(BYTES_LARGE); - } catch (Throwable throwable) { - throw new ExceptionInInitializerError(throwable); - } - } - - static boolean isNotSupported() { - return PlatformDependent.isOsx() && "aarch_64".equals(PlatformDependent.normalizedArch()); - } - - private static void fillArrayWithCompressibleData(byte[] array) { - for (int i = 0; i < array.length; i++) { - array[i] = i % 4 != 0 ? 0 : (byte) RANDOM.nextInt(); - } - } - - private static byte[] compress(byte[] data) throws IOException { - ByteArrayOutputStream os = new ByteArrayOutputStream(); - BrotliOutputStream brotliOs = new BrotliOutputStream(os); - brotliOs.write(data); - brotliOs.close(); - return os.toByteArray(); - } - - private EmbeddedChannel channel; - - @BeforeEach - public void initChannel() { - channel = new EmbeddedChannel(new BrotliDecoder()); - } - - @AfterEach - public void destroyChannel() { - if (channel != null) { - channel.finishAndReleaseAll(); - channel = null; - } - } - - public static ByteBuf[] smallData() { - ByteBuf heap = Unpooled.wrappedBuffer(COMPRESSED_BYTES_SMALL); - ByteBuf direct = Unpooled.directBuffer(COMPRESSED_BYTES_SMALL.length); - direct.writeBytes(COMPRESSED_BYTES_SMALL); - return new ByteBuf[]{heap, direct}; - } - - public static ByteBuf[] largeData() { - ByteBuf heap = Unpooled.wrappedBuffer(COMPRESSED_BYTES_LARGE); - ByteBuf direct = Unpooled.directBuffer(COMPRESSED_BYTES_LARGE.length); - direct.writeBytes(COMPRESSED_BYTES_LARGE); - return new ByteBuf[]{heap, direct}; - } - - @ParameterizedTest - @MethodSource("smallData") - public void testDecompressionOfSmallChunkOfData(ByteBuf data) { - testDecompression(WRAPPED_BYTES_SMALL, data); - } - - @ParameterizedTest - @MethodSource("largeData") - public void testDecompressionOfLargeChunkOfData(ByteBuf data) { - testDecompression(WRAPPED_BYTES_LARGE, data); - } - - @ParameterizedTest - @MethodSource("largeData") - public void testDecompressionOfBatchedFlowOfData(ByteBuf data) { - testDecompressionOfBatchedFlow(WRAPPED_BYTES_LARGE, data); - } - - private void testDecompression(final ByteBuf expected, final ByteBuf data) { - assertTrue(channel.writeInbound(data)); - - ByteBuf decompressed = readDecompressed(channel); - assertEquals(expected, decompressed); - - decompressed.release(); - } - - private void testDecompressionOfBatchedFlow(final ByteBuf expected, final ByteBuf data) { - final int compressedLength = data.readableBytes(); - int written = 0, length = RANDOM.nextInt(100); - while (written + length < compressedLength) { - ByteBuf compressedBuf = data.retainedSlice(written, length); - channel.writeInbound(compressedBuf); - written += length; - length = RANDOM.nextInt(100); - } - ByteBuf compressedBuf = data.slice(written, compressedLength - written); - assertTrue(channel.writeInbound(compressedBuf.retain())); - - ByteBuf decompressedBuf = readDecompressed(channel); - assertEquals(expected, decompressedBuf); - - decompressedBuf.release(); - data.release(); - } - - private static ByteBuf readDecompressed(final EmbeddedChannel channel) { - CompositeByteBuf decompressed = Unpooled.compositeBuffer(); - ByteBuf msg; - while ((msg = channel.readInbound()) != null) { - decompressed.addComponent(true, msg); - } - return decompressed; - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/compression/BrotliEncoderTest.java b/codec/src/test/java/io/netty/handler/codec/compression/BrotliEncoderTest.java deleted file mode 100644 index 61c8df4c4a..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/compression/BrotliEncoderTest.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import com.aayushatharva.brotli4j.decoder.Decoder; -import com.aayushatharva.brotli4j.decoder.DecoderJNI; -import com.aayushatharva.brotli4j.decoder.DirectDecompress; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.CompositeByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.internal.PlatformDependent; -import org.junit.jupiter.api.condition.DisabledIf; - -@DisabledIf(value = "isNotSupported", disabledReason = "Brotli is not supported on this platform") -public class BrotliEncoderTest extends AbstractEncoderTest { - - static { - try { - Brotli.ensureAvailability(); - } catch (Throwable throwable) { - throw new ExceptionInInitializerError(throwable); - } - } - - @Override - public EmbeddedChannel createChannel() { - return new EmbeddedChannel(new BrotliEncoder()); - } - - @Override - protected ByteBuf decompress(ByteBuf compressed, int originalLength) throws Exception { - byte[] compressedArray = new byte[compressed.readableBytes()]; - compressed.readBytes(compressedArray); - compressed.release(); - - DirectDecompress decompress = Decoder.decompress(compressedArray); - if (decompress.getResultStatus() == DecoderJNI.Status.ERROR) { - throw new DecompressionException("Brotli stream corrupted"); - } - - byte[] decompressed = decompress.getDecompressedData(); - return Unpooled.wrappedBuffer(decompressed); - } - - @Override - protected ByteBuf readDecompressed(final int dataLength) throws Exception { - CompositeByteBuf decompressed = Unpooled.compositeBuffer(); - ByteBuf msg; - while ((msg = channel.readOutbound()) != null) { - if (msg.isReadable()) { - decompressed.addComponent(true, decompress(msg, -1)); - } else { - msg.release(); - } - } - return decompressed; - } - - static boolean isNotSupported() { - return PlatformDependent.isOsx() && "aarch_64".equals(PlatformDependent.normalizedArch()); - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/compression/ByteBufChecksumTest.java b/codec/src/test/java/io/netty/handler/codec/compression/ByteBufChecksumTest.java deleted file mode 100644 index bf3af6142b..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/compression/ByteBufChecksumTest.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import net.jpountz.xxhash.XXHashFactory; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.util.Random; -import java.util.zip.Adler32; -import java.util.zip.CRC32; -import java.util.zip.Checksum; - -import static io.netty.handler.codec.compression.Lz4Constants.DEFAULT_SEED; -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class ByteBufChecksumTest { - - private static final byte[] BYTE_ARRAY = new byte[1024]; - - @BeforeAll - public static void setUp() { - new Random().nextBytes(BYTE_ARRAY); - } - - @Test - public void testHeapByteBufUpdate() { - testUpdate(Unpooled.wrappedBuffer(BYTE_ARRAY)); - } - - @Test - public void testDirectByteBufUpdate() { - ByteBuf buf = Unpooled.directBuffer(BYTE_ARRAY.length); - buf.writeBytes(BYTE_ARRAY); - testUpdate(buf); - } - - private static void testUpdate(ByteBuf buf) { - try { - // all variations of xxHash32: slow and naive, optimised, wrapped optimised; - // the last two should be literally identical, but it's best to guard against - // an accidental regression in ByteBufChecksum#wrapChecksum(Checksum) - testUpdate(xxHash32(DEFAULT_SEED), ByteBufChecksum.wrapChecksum(xxHash32(DEFAULT_SEED)), buf); - testUpdate(xxHash32(DEFAULT_SEED), new Lz4XXHash32(DEFAULT_SEED), buf); - testUpdate(xxHash32(DEFAULT_SEED), ByteBufChecksum.wrapChecksum(new Lz4XXHash32(DEFAULT_SEED)), buf); - - // CRC32 and Adler32, special-cased to use ReflectiveByteBufChecksum - testUpdate(new CRC32(), ByteBufChecksum.wrapChecksum(new CRC32()), buf); - testUpdate(new Adler32(), ByteBufChecksum.wrapChecksum(new Adler32()), buf); - } finally { - buf.release(); - } - } - - private static void testUpdate(Checksum checksum, ByteBufChecksum wrapped, ByteBuf buf) { - testUpdate(checksum, wrapped, buf, 0, BYTE_ARRAY.length); - testUpdate(checksum, wrapped, buf, 0, BYTE_ARRAY.length - 1); - testUpdate(checksum, wrapped, buf, 1, BYTE_ARRAY.length - 1); - testUpdate(checksum, wrapped, buf, 1, BYTE_ARRAY.length - 2); - } - - private static void testUpdate(Checksum checksum, ByteBufChecksum wrapped, ByteBuf buf, int off, int len) { - checksum.reset(); - wrapped.reset(); - - checksum.update(BYTE_ARRAY, off, len); - wrapped.update(buf, off, len); - - assertEquals(checksum.getValue(), wrapped.getValue()); - } - - private static Checksum xxHash32(int seed) { - return XXHashFactory.fastestInstance().newStreamingHash32(seed).asChecksum(); - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/compression/Bzip2DecoderTest.java b/codec/src/test/java/io/netty/handler/codec/compression/Bzip2DecoderTest.java deleted file mode 100644 index 651d5d6938..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/compression/Bzip2DecoderTest.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream; -import org.junit.jupiter.api.Test; - -import java.io.ByteArrayOutputStream; -import java.util.Arrays; - -import static io.netty.handler.codec.compression.Bzip2Constants.*; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.fail; - -public class Bzip2DecoderTest extends AbstractDecoderTest { - - private static final byte[] DATA = { 0x42, 0x5A, 0x68, 0x37, 0x31, 0x41, 0x59, 0x26, 0x53, - 0x59, 0x77, 0x7B, (byte) 0xCA, (byte) 0xC0, 0x00, 0x00, - 0x00, 0x05, (byte) 0x80, 0x00, 0x01, 0x02, 0x00, 0x04, - 0x20, 0x20, 0x00, 0x30, (byte) 0xCD, 0x34, 0x19, (byte) 0xA6, - (byte) 0x89, (byte) 0x99, (byte) 0xC5, (byte) 0xDC, (byte) 0x91, - 0x4E, 0x14, 0x24, 0x1D, (byte) 0xDE, (byte) 0xF2, (byte) 0xB0, 0x00 }; - - public Bzip2DecoderTest() throws Exception { - } - - @Override - protected EmbeddedChannel createChannel() { - return new EmbeddedChannel(new Bzip2Decoder()); - } - - private void writeInboundDestroyAndExpectDecompressionException(ByteBuf in) { - try { - channel.writeInbound(in); - } finally { - try { - destroyChannel(); - fail(); - } catch (DecompressionException ignored) { - // expected - } - } - } - - @Test - public void testUnexpectedStreamIdentifier() { - ByteBuf in = Unpooled.buffer(); - in.writeLong(1823080128301928729L); //random value - assertThrows(DecompressionException.class, - () -> writeInboundDestroyAndExpectDecompressionException(in), "Unexpected stream identifier contents"); - } - - @Test - public void testInvalidBlockSize() { - ByteBuf in = Unpooled.buffer(); - in.writeMedium(MAGIC_NUMBER); - in.writeByte('0'); //incorrect block size - - assertThrows(DecompressionException.class, () -> channel.writeInbound(in), "block size is invalid"); - } - - @Test - public void testBadBlockHeader() { - ByteBuf in = Unpooled.buffer(); - in.writeMedium(MAGIC_NUMBER); - in.writeByte('1'); //block size - in.writeMedium(11); //incorrect block header - in.writeMedium(11); //incorrect block header - in.writeInt(11111); //block CRC - - assertThrows(DecompressionException.class, () -> channel.writeInbound(in), "bad block header"); - } - - @Test - public void testStreamCrcErrorOfEmptyBlock() { - ByteBuf in = Unpooled.buffer(); - in.writeMedium(MAGIC_NUMBER); - in.writeByte('1'); //block size - in.writeMedium(END_OF_STREAM_MAGIC_1); - in.writeMedium(END_OF_STREAM_MAGIC_2); - in.writeInt(1); //wrong storedCombinedCRC - - assertThrows(DecompressionException.class, () -> channel.writeInbound(in), "stream CRC error"); - } - - @Test - public void testStreamCrcError() { - final byte[] data = Arrays.copyOf(DATA, DATA.length); - data[41] = (byte) 0xDD; - - assertThrows(DecompressionException.class, - () -> tryDecodeAndCatchBufLeaks(channel, Unpooled.wrappedBuffer(data)), "stream CRC error"); - } - - @Test - public void testIncorrectHuffmanGroupsNumber() { - final byte[] data = Arrays.copyOf(DATA, DATA.length); - data[25] = 0x70; - - ByteBuf in = Unpooled.wrappedBuffer(data); - assertThrows(DecompressionException.class, () -> channel.writeInbound(in), "incorrect huffman groups number"); - } - - @Test - public void testIncorrectSelectorsNumber() { - final byte[] data = Arrays.copyOf(DATA, DATA.length); - data[25] = 0x2F; - - ByteBuf in = Unpooled.wrappedBuffer(data); - assertThrows(DecompressionException.class, () -> channel.writeInbound(in), "incorrect selectors number"); - } - - @Test - public void testBlockCrcError() { - final byte[] data = Arrays.copyOf(DATA, DATA.length); - data[11] = 0x77; - - ByteBuf in = Unpooled.wrappedBuffer(data); - assertThrows(DecompressionException.class, - () -> writeInboundDestroyAndExpectDecompressionException(in), "block CRC error"); - } - - @Test - public void testStartPointerInvalid() { - final byte[] data = Arrays.copyOf(DATA, DATA.length); - data[14] = (byte) 0xFF; - - ByteBuf in = Unpooled.wrappedBuffer(data); - assertThrows(DecompressionException.class, - () -> writeInboundDestroyAndExpectDecompressionException(in), "start pointer invalid"); - } - - @Override - protected byte[] compress(byte[] data) throws Exception { - ByteArrayOutputStream os = new ByteArrayOutputStream(); - BZip2CompressorOutputStream bZip2Os = new BZip2CompressorOutputStream(os, MIN_BLOCK_SIZE); - bZip2Os.write(data); - bZip2Os.close(); - - return os.toByteArray(); - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/compression/Bzip2EncoderTest.java b/codec/src/test/java/io/netty/handler/codec/compression/Bzip2EncoderTest.java deleted file mode 100644 index 8957ef6e94..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/compression/Bzip2EncoderTest.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufInputStream; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream; - -import java.io.InputStream; - -import static io.netty.handler.codec.compression.Bzip2Constants.*; -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class Bzip2EncoderTest extends AbstractEncoderTest { - - @Override - protected EmbeddedChannel createChannel() { - return new EmbeddedChannel(new Bzip2Encoder(MIN_BLOCK_SIZE)); - } - - @Override - protected ByteBuf decompress(ByteBuf compressed, int originalLength) throws Exception { - InputStream is = new ByteBufInputStream(compressed, true); - BZip2CompressorInputStream bzip2Is = null; - byte[] decompressed = new byte[originalLength]; - try { - bzip2Is = new BZip2CompressorInputStream(is); - int remaining = originalLength; - while (remaining > 0) { - int read = bzip2Is.read(decompressed, originalLength - remaining, remaining); - if (read > 0) { - remaining -= read; - } else { - break; - } - } - assertEquals(-1, bzip2Is.read()); - } finally { - if (bzip2Is != null) { - bzip2Is.close(); - } else { - is.close(); - } - } - - return Unpooled.wrappedBuffer(decompressed); - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/compression/Bzip2IntegrationTest.java b/codec/src/test/java/io/netty/handler/codec/compression/Bzip2IntegrationTest.java deleted file mode 100644 index 2b358eb9e4..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/compression/Bzip2IntegrationTest.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.channel.embedded.EmbeddedChannel; -import org.junit.jupiter.api.Test; - -public class Bzip2IntegrationTest extends AbstractIntegrationTest { - - @Override - protected EmbeddedChannel createEncoder() { - return new EmbeddedChannel(new Bzip2Encoder()); - } - - @Override - protected EmbeddedChannel createDecoder() { - return new EmbeddedChannel(new Bzip2Decoder()); - } - - @Test - public void test3Tables() throws Exception { - byte[] data = new byte[500]; - rand.nextBytes(data); - testIdentity(data, true); - testIdentity(data, false); - } - - @Test - public void test4Tables() throws Exception { - byte[] data = new byte[1100]; - rand.nextBytes(data); - testIdentity(data, true); - testIdentity(data, false); - } - - @Test - public void test5Tables() throws Exception { - byte[] data = new byte[2300]; - rand.nextBytes(data); - testIdentity(data, true); - testIdentity(data, false); - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/compression/FastLzIntegrationTest.java b/codec/src/test/java/io/netty/handler/codec/compression/FastLzIntegrationTest.java deleted file mode 100644 index 5987042953..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/compression/FastLzIntegrationTest.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.CompositeByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; - -public class FastLzIntegrationTest extends AbstractIntegrationTest { - - public static class TestWithChecksum extends AbstractIntegrationTest { - - @Override - protected EmbeddedChannel createEncoder() { - return new EmbeddedChannel(new FastLzFrameEncoder(true)); - } - - @Override - protected EmbeddedChannel createDecoder() { - return new EmbeddedChannel(new FastLzFrameDecoder(true)); - } - } - - public static class TestRandomChecksum extends AbstractIntegrationTest { - - @Override - protected EmbeddedChannel createEncoder() { - return new EmbeddedChannel(new FastLzFrameEncoder(rand.nextBoolean())); - } - - @Override - protected EmbeddedChannel createDecoder() { - return new EmbeddedChannel(new FastLzFrameDecoder(rand.nextBoolean())); - } - } - - @Override - protected EmbeddedChannel createEncoder() { - return new EmbeddedChannel(new FastLzFrameEncoder(rand.nextBoolean())); - } - - @Override - protected EmbeddedChannel createDecoder() { - return new EmbeddedChannel(new FastLzFrameDecoder(rand.nextBoolean())); - } - - @Override // test batched flow of data - protected void testIdentity(final byte[] data, boolean heapBuffer) { - initChannels(); - final ByteBuf original = heapBuffer? Unpooled.wrappedBuffer(data) : - Unpooled.directBuffer(data.length).writeBytes(data); - final CompositeByteBuf compressed = Unpooled.compositeBuffer(); - final CompositeByteBuf decompressed = Unpooled.compositeBuffer(); - - try { - int written = 0, length = rand.nextInt(100); - while (written + length < data.length) { - ByteBuf in = Unpooled.wrappedBuffer(data, written, length); - encoder.writeOutbound(in); - written += length; - length = rand.nextInt(100); - } - ByteBuf in = Unpooled.wrappedBuffer(data, written, data.length - written); - encoder.writeOutbound(in); - encoder.finish(); - - ByteBuf msg; - while ((msg = encoder.readOutbound()) != null) { - compressed.addComponent(true, msg); - } - assertThat(compressed, is(notNullValue())); - - final byte[] compressedArray = new byte[compressed.readableBytes()]; - compressed.readBytes(compressedArray); - written = 0; - length = rand.nextInt(100); - while (written + length < compressedArray.length) { - in = Unpooled.wrappedBuffer(compressedArray, written, length); - decoder.writeInbound(in); - written += length; - length = rand.nextInt(100); - } - in = Unpooled.wrappedBuffer(compressedArray, written, compressedArray.length - written); - decoder.writeInbound(in); - - assertFalse(compressed.isReadable()); - while ((msg = decoder.readInbound()) != null) { - decompressed.addComponent(true, msg); - } - assertEquals(original, decompressed); - } finally { - compressed.release(); - decompressed.release(); - original.release(); - closeChannels(); - } - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/compression/JdkZlibTest.java b/codec/src/test/java/io/netty/handler/codec/compression/JdkZlibTest.java deleted file mode 100644 index 8f6fd63d43..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/compression/JdkZlibTest.java +++ /dev/null @@ -1,676 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.buffer.AbstractByteBufAllocator; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.ByteBufInputStream; -import io.netty.buffer.Unpooled; -import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.CharsetUtil; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.internal.EmptyArrays; -import org.apache.commons.compress.utils.IOUtils; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.Arrays; -import java.util.Queue; -import java.util.Random; -import java.util.concurrent.ThreadLocalRandom; -import java.util.stream.Stream; -import java.util.zip.DeflaterOutputStream; -import java.util.zip.GZIPInputStream; -import java.util.zip.GZIPOutputStream; - -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - -public class JdkZlibTest { - private static final byte[] BYTES_SMALL = new byte[128]; - private static final byte[] BYTES_LARGE = new byte[1024 * 1024]; - private static final byte[] BYTES_LARGE2 = ("\n" + - "\n" + - "\n" + - " Apache Tomcat\n" + - "\n" + - '\n' + - "\n" + - "

It works !

\n" + - '\n' + - "

If you're seeing this page via a web browser, it means you've setup Tomcat successfully." + - " Congratulations!

\n" + - " \n" + - "

This is the default Tomcat home page." + - " It can be found on the local filesystem at: /var/lib/tomcat7/webapps/ROOT/index.html

\n" + - '\n' + - "

Tomcat7 veterans might be pleased to learn that this system instance of Tomcat is installed with" + - " CATALINA_HOME in /usr/share/tomcat7 and CATALINA_BASE in" + - " /var/lib/tomcat7, following the rules from" + - " /usr/share/doc/tomcat7-common/RUNNING.txt.gz.

\n" + - '\n' + - "

You might consider installing the following packages, if you haven't already done so:

\n" + - '\n' + - "

tomcat7-docs: This package installs a web application that allows to browse the Tomcat 7" + - " documentation locally. Once installed, you can access it by clicking here.

\n" + - '\n' + - "

tomcat7-examples: This package installs a web application that allows to access the Tomcat" + - " 7 Servlet and JSP examples. Once installed, you can access it by clicking" + - " here.

\n" + - '\n' + - "

tomcat7-admin: This package installs two web applications that can help managing this Tomcat" + - " instance. Once installed, you can access the manager webapp and" + - " the host-manager webapp.

\n" + - '\n' + - "

NOTE: For security reasons, using the manager webapp is restricted" + - " to users with role \"manager\"." + - " The host-manager webapp is restricted to users with role \"admin\". Users are " + - "defined in /etc/tomcat7/tomcat-users.xml.

\n" + - '\n' + - '\n' + - '\n' + - "").getBytes(CharsetUtil.UTF_8); - - static { - Random rand = ThreadLocalRandom.current(); - rand.nextBytes(BYTES_SMALL); - rand.nextBytes(BYTES_LARGE); - } - - enum Data { - NONE(null), - SMALL(BYTES_SMALL), - LARGE(BYTES_LARGE); - - final byte[] bytes; - - Data(byte[] bytes) { - this.bytes = bytes; - } - } - - enum BufferType { - HEAP, - DIRECT; - - ByteBuf allocate(byte[] bytes) { - switch (this) { - case HEAP: return Unpooled.wrappedBuffer(bytes); - case DIRECT: return Unpooled.directBuffer(bytes.length).writeBytes(bytes); - } - return fail("Fall-through should not be possible: " + this); - } - } - - protected ZlibDecoder createDecoder(ZlibWrapper wrapper) { - return createDecoder(wrapper, 0); - } - - protected ZlibEncoder createEncoder(ZlibWrapper wrapper, BufferType bufferType) { - return new JdkZlibEncoder(wrapper, 6, bufferType == BufferType.DIRECT); - } - - protected ZlibDecoder createDecoder(ZlibWrapper wrapper, int maxAllocation) { - return new JdkZlibDecoder(wrapper, maxAllocation); - } - - static Stream compressionConfigurations() { - Stream.Builder args = Stream.builder(); - Data[] dataVals = Data.values(); - BufferType[] bufferTypeVals = BufferType.values(); - ZlibWrapper[] zlibWrappers = ZlibWrapper.values(); - for (Data data : dataVals) { - for (BufferType inBuf : bufferTypeVals) { - for (BufferType outBuf : bufferTypeVals) { - for (ZlibWrapper inputWrapper : zlibWrappers) { - for (ZlibWrapper outputWrapper : zlibWrappers) { - args.add(Arguments.of(data, inBuf, outBuf, inputWrapper, outputWrapper)); - } - } - } - } - } - return args.build(); - } - - static Stream workingConfigurations() { - return compressionConfigurations().filter(JdkZlibTest::isWorkingConfiguration); - } - - private static boolean isWorkingConfiguration(Arguments args) { - Object[] objs = args.get(); - ZlibWrapper inWrap = (ZlibWrapper) objs[3]; - ZlibWrapper outWrap = (ZlibWrapper) objs[4]; - if (inWrap == ZlibWrapper.ZLIB_OR_NONE) { - return false; - } - if (inWrap == ZlibWrapper.GZIP || outWrap == ZlibWrapper.GZIP) { - return inWrap == outWrap; - } - if (inWrap == ZlibWrapper.NONE) { - return outWrap == ZlibWrapper.NONE || outWrap == ZlibWrapper.ZLIB_OR_NONE; - } - if (outWrap == ZlibWrapper.NONE) { - return inWrap == ZlibWrapper.NONE; - } - return true; - } - - @ParameterizedTest - @MethodSource("workingConfigurations") - void compressionInputOutput( - Data data, BufferType inBuf, BufferType outBuf, ZlibWrapper inWrap, ZlibWrapper outWrap) { - EmbeddedChannel chEncoder = new EmbeddedChannel(createEncoder(inWrap, inBuf)); - EmbeddedChannel chDecoder = new EmbeddedChannel(createDecoder(outWrap)); - chEncoder.config().setAllocator(new UnpooledByteBufAllocator(inBuf == BufferType.DIRECT)); - chDecoder.config().setAllocator(new UnpooledByteBufAllocator(outBuf == BufferType.DIRECT)); - - try { - if (data != Data.NONE) { - chEncoder.writeOutbound(inBuf.allocate(data.bytes)); - chEncoder.flush(); - - for (;;) { - ByteBuf deflatedData = chEncoder.readOutbound(); - if (deflatedData == null) { - break; - } - chDecoder.writeInbound(deflatedData); - } - - byte[] decompressed = new byte[data.bytes.length]; - int offset = 0; - for (;;) { - ByteBuf buf = chDecoder.readInbound(); - if (buf == null) { - break; - } - int length = buf.readableBytes(); - buf.readBytes(decompressed, offset, length); - offset += length; - buf.release(); - if (offset == decompressed.length) { - break; - } - } - assertArrayEquals(data.bytes, decompressed); - assertNull(chDecoder.readInbound()); - } - - // Closing an encoder channel will generate a footer. - assertTrue(chEncoder.finish()); - for (;;) { - Object msg = chEncoder.readOutbound(); - if (msg == null) { - break; - } - ReferenceCountUtil.release(msg); - } - // But, the footer will be decoded into nothing. It's only for validation. - assertFalse(chDecoder.finish()); - } finally { - dispose(chEncoder); - dispose(chDecoder); - } - } - - @Test - public void testGZIP2() throws Exception { - byte[] bytes = "message".getBytes(CharsetUtil.UTF_8); - ByteBuf data = Unpooled.wrappedBuffer(bytes); - ByteBuf deflatedData = Unpooled.wrappedBuffer(gzip(bytes)); - - EmbeddedChannel chDecoderGZip = new EmbeddedChannel(createDecoder(ZlibWrapper.GZIP)); - try { - while (deflatedData.isReadable()) { - chDecoderGZip.writeInbound(deflatedData.readRetainedSlice(1)); - } - deflatedData.release(); - assertTrue(chDecoderGZip.finish()); - ByteBuf buf = Unpooled.buffer(); - for (;;) { - ByteBuf b = chDecoderGZip.readInbound(); - if (b == null) { - break; - } - buf.writeBytes(b); - b.release(); - } - assertEquals(buf, data); - assertNull(chDecoderGZip.readInbound()); - data.release(); - buf.release(); - } finally { - dispose(chDecoderGZip); - } - } - - private void testCompress0(ZlibWrapper encoderWrapper, ZlibWrapper decoderWrapper, ByteBuf data) throws Exception { - EmbeddedChannel chEncoder = new EmbeddedChannel(createEncoder(encoderWrapper, BufferType.HEAP)); - EmbeddedChannel chDecoderZlib = new EmbeddedChannel(createDecoder(decoderWrapper)); - - try { - chEncoder.writeOutbound(data.retain()); - chEncoder.flush(); - data.readerIndex(0); - - for (;;) { - ByteBuf deflatedData = chEncoder.readOutbound(); - if (deflatedData == null) { - break; - } - chDecoderZlib.writeInbound(deflatedData); - } - - byte[] decompressed = new byte[data.readableBytes()]; - int offset = 0; - for (;;) { - ByteBuf buf = chDecoderZlib.readInbound(); - if (buf == null) { - break; - } - int length = buf.readableBytes(); - buf.readBytes(decompressed, offset, length); - offset += length; - buf.release(); - if (offset == decompressed.length) { - break; - } - } - assertEquals(data, Unpooled.wrappedBuffer(decompressed)); - assertNull(chDecoderZlib.readInbound()); - - // Closing an encoder channel will generate a footer. - assertTrue(chEncoder.finish()); - for (;;) { - Object msg = chEncoder.readOutbound(); - if (msg == null) { - break; - } - ReferenceCountUtil.release(msg); - } - // But, the footer will be decoded into nothing. It's only for validation. - assertFalse(chDecoderZlib.finish()); - - data.release(); - } finally { - dispose(chEncoder); - dispose(chDecoderZlib); - } - } - - private void testCompressNone(ZlibWrapper encoderWrapper, ZlibWrapper decoderWrapper) throws Exception { - EmbeddedChannel chEncoder = new EmbeddedChannel(createEncoder(encoderWrapper, BufferType.HEAP)); - EmbeddedChannel chDecoderZlib = new EmbeddedChannel(createDecoder(decoderWrapper)); - - try { - // Closing an encoder channel without writing anything should generate both header and footer. - assertTrue(chEncoder.finish()); - - for (;;) { - ByteBuf deflatedData = chEncoder.readOutbound(); - if (deflatedData == null) { - break; - } - chDecoderZlib.writeInbound(deflatedData); - } - - // Decoder should not generate anything at all. - boolean decoded = false; - for (;;) { - ByteBuf buf = chDecoderZlib.readInbound(); - if (buf == null) { - break; - } - - buf.release(); - decoded = true; - } - assertFalse(decoded, "should decode nothing"); - - assertFalse(chDecoderZlib.finish()); - } finally { - dispose(chEncoder); - dispose(chDecoderZlib); - } - } - - private static void dispose(EmbeddedChannel ch) { - if (ch.finish()) { - for (;;) { - Object msg = ch.readInbound(); - if (msg == null) { - break; - } - ReferenceCountUtil.release(msg); - } - for (;;) { - Object msg = ch.readOutbound(); - if (msg == null) { - break; - } - ReferenceCountUtil.release(msg); - } - } - } - - // Test for https://github.com/netty/netty/issues/2572 - private void testDecompressOnly(ZlibWrapper decoderWrapper, byte[] compressed, byte[] data) throws Exception { - EmbeddedChannel chDecoder = new EmbeddedChannel(createDecoder(decoderWrapper)); - chDecoder.writeInbound(Unpooled.copiedBuffer(compressed)); - assertTrue(chDecoder.finish()); - - ByteBuf decoded = Unpooled.buffer(data.length); - - for (;;) { - ByteBuf buf = chDecoder.readInbound(); - if (buf == null) { - break; - } - decoded.writeBytes(buf); - buf.release(); - } - assertEquals(Unpooled.copiedBuffer(data), decoded); - decoded.release(); - } - - private void testCompressSmall(ZlibWrapper encoderWrapper, ZlibWrapper decoderWrapper) throws Exception { - testCompress0(encoderWrapper, decoderWrapper, Unpooled.wrappedBuffer(BYTES_SMALL)); - testCompress0(encoderWrapper, decoderWrapper, - Unpooled.directBuffer(BYTES_SMALL.length).writeBytes(BYTES_SMALL)); - } - - private void testCompressLarge(ZlibWrapper encoderWrapper, ZlibWrapper decoderWrapper) throws Exception { - testCompress0(encoderWrapper, decoderWrapper, Unpooled.wrappedBuffer(BYTES_LARGE)); - testCompress0(encoderWrapper, decoderWrapper, - Unpooled.directBuffer(BYTES_LARGE.length).writeBytes(BYTES_LARGE)); - } - - @Test - public void testZLIB() throws Exception { - testCompressNone(ZlibWrapper.ZLIB, ZlibWrapper.ZLIB); - testCompressSmall(ZlibWrapper.ZLIB, ZlibWrapper.ZLIB); - testCompressLarge(ZlibWrapper.ZLIB, ZlibWrapper.ZLIB); - testDecompressOnly(ZlibWrapper.ZLIB, deflate(BYTES_LARGE2), BYTES_LARGE2); - } - - @Test - public void testNONE() throws Exception { - testCompressNone(ZlibWrapper.NONE, ZlibWrapper.NONE); - testCompressSmall(ZlibWrapper.NONE, ZlibWrapper.NONE); - testCompressLarge(ZlibWrapper.NONE, ZlibWrapper.NONE); - } - - @Test - public void testGZIP() throws Exception { - testCompressNone(ZlibWrapper.GZIP, ZlibWrapper.GZIP); - testCompressSmall(ZlibWrapper.GZIP, ZlibWrapper.GZIP); - testCompressLarge(ZlibWrapper.GZIP, ZlibWrapper.GZIP); - testDecompressOnly(ZlibWrapper.GZIP, gzip(BYTES_LARGE2), BYTES_LARGE2); - } - - @Test - public void testGZIPCompressOnly() throws Exception { - testGZIPCompressOnly0(null); // Do not write anything; just finish the stream. - testGZIPCompressOnly0(EmptyArrays.EMPTY_BYTES); // Write an empty array. - testGZIPCompressOnly0(BYTES_SMALL); - testGZIPCompressOnly0(BYTES_LARGE); - } - - private void testGZIPCompressOnly0(byte[] data) throws IOException { - EmbeddedChannel chEncoder = new EmbeddedChannel(createEncoder(ZlibWrapper.GZIP, BufferType.HEAP)); - if (data != null) { - chEncoder.writeOutbound(Unpooled.wrappedBuffer(data)); - } - assertTrue(chEncoder.finish()); - - ByteBuf encoded = Unpooled.buffer(); - for (;;) { - ByteBuf buf = chEncoder.readOutbound(); - if (buf == null) { - break; - } - encoded.writeBytes(buf); - buf.release(); - } - - ByteBuf decoded = Unpooled.buffer(); - GZIPInputStream stream = new GZIPInputStream(new ByteBufInputStream(encoded, true)); - try { - byte[] buf = new byte[8192]; - for (;;) { - int readBytes = stream.read(buf); - if (readBytes < 0) { - break; - } - decoded.writeBytes(buf, 0, readBytes); - } - } finally { - stream.close(); - } - - if (data != null) { - assertEquals(Unpooled.wrappedBuffer(data), decoded); - } else { - assertFalse(decoded.isReadable()); - } - - decoded.release(); - } - - @Test - public void testZLIB_OR_NONE() throws Exception { - testCompressNone(ZlibWrapper.NONE, ZlibWrapper.ZLIB_OR_NONE); - testCompressSmall(ZlibWrapper.NONE, ZlibWrapper.ZLIB_OR_NONE); - testCompressLarge(ZlibWrapper.NONE, ZlibWrapper.ZLIB_OR_NONE); - } - - @Test - public void testZLIB_OR_NONE2() throws Exception { - testCompressNone(ZlibWrapper.ZLIB, ZlibWrapper.ZLIB_OR_NONE); - testCompressSmall(ZlibWrapper.ZLIB, ZlibWrapper.ZLIB_OR_NONE); - testCompressLarge(ZlibWrapper.ZLIB, ZlibWrapper.ZLIB_OR_NONE); - } - - @Test - public void testZLIB_OR_NONE3() throws Exception { - assertThrows(DecompressionException.class, () -> testCompressNone(ZlibWrapper.GZIP, ZlibWrapper.ZLIB_OR_NONE)); - assertThrows(DecompressionException.class, () -> testCompressSmall(ZlibWrapper.GZIP, ZlibWrapper.ZLIB_OR_NONE)); - assertThrows(DecompressionException.class, () -> testCompressLarge(ZlibWrapper.GZIP, ZlibWrapper.ZLIB_OR_NONE)); - } - - @Test - public void testMaxAllocation() throws Exception { - int maxAllocation = 1024; - ZlibDecoder decoder = createDecoder(ZlibWrapper.ZLIB, maxAllocation); - EmbeddedChannel chDecoder = new EmbeddedChannel(decoder); - TestByteBufAllocator alloc = new TestByteBufAllocator(chDecoder.alloc()); - chDecoder.config().setAllocator(alloc); - - try { - chDecoder.writeInbound(Unpooled.wrappedBuffer(deflate(BYTES_LARGE))); - fail("decompressed size > maxAllocation, so should have thrown exception"); - } catch (DecompressionException e) { - assertTrue(e.getMessage().startsWith("Decompression buffer has reached maximum size")); - assertEquals(maxAllocation, alloc.getMaxAllocation()); - assertTrue(decoder.isClosed()); - assertFalse(chDecoder.finish()); - } - } - - @Test - // verifies backward compatibility - public void testConcatenatedStreamsReadFirstOnly() throws IOException { - EmbeddedChannel chDecoderGZip = new EmbeddedChannel(createDecoder(ZlibWrapper.GZIP)); - - try { - byte[] bytes = IOUtils.toByteArray(getClass().getResourceAsStream("/multiple.gz")); - - assertTrue(chDecoderGZip.writeInbound(Unpooled.copiedBuffer(bytes))); - Queue messages = chDecoderGZip.inboundMessages(); - assertEquals(1, messages.size()); - - ByteBuf msg = (ByteBuf) messages.poll(); - assertEquals("a", msg.toString(CharsetUtil.UTF_8)); - ReferenceCountUtil.release(msg); - } finally { - assertFalse(chDecoderGZip.finish()); - chDecoderGZip.close(); - } - } - - @Test - public void testConcatenatedStreamsReadFully() throws IOException { - EmbeddedChannel chDecoderGZip = new EmbeddedChannel(new JdkZlibDecoder(true)); - - try { - byte[] bytes = IOUtils.toByteArray(getClass().getResourceAsStream("/multiple.gz")); - - assertTrue(chDecoderGZip.writeInbound(Unpooled.copiedBuffer(bytes))); - Queue messages = chDecoderGZip.inboundMessages(); - assertEquals(2, messages.size()); - - for (String s : Arrays.asList("a", "b")) { - ByteBuf msg = (ByteBuf) messages.poll(); - assertEquals(s, msg.toString(CharsetUtil.UTF_8)); - ReferenceCountUtil.release(msg); - } - } finally { - assertFalse(chDecoderGZip.finish()); - chDecoderGZip.close(); - } - } - - @Test - public void testConcatenatedStreamsReadFullyWhenFragmented() throws IOException { - EmbeddedChannel chDecoderGZip = new EmbeddedChannel(new JdkZlibDecoder(true)); - - try { - byte[] bytes = IOUtils.toByteArray(getClass().getResourceAsStream("/multiple.gz")); - - // Let's feed the input byte by byte to simulate fragmentation. - ByteBuf buf = Unpooled.copiedBuffer(bytes); - boolean written = false; - while (buf.isReadable()) { - written |= chDecoderGZip.writeInbound(buf.readRetainedSlice(1)); - } - buf.release(); - - assertTrue(written); - Queue messages = chDecoderGZip.inboundMessages(); - assertEquals(2, messages.size()); - - for (String s : Arrays.asList("a", "b")) { - ByteBuf msg = (ByteBuf) messages.poll(); - assertEquals(s, msg.toString(CharsetUtil.UTF_8)); - ReferenceCountUtil.release(msg); - } - } finally { - assertFalse(chDecoderGZip.finish()); - chDecoderGZip.close(); - } - } - - @Test - public void testDecodeWithHeaderFollowingFooter() throws Exception { - byte[] bytes = new byte[1024]; - ThreadLocalRandom.current().nextBytes(bytes); - ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); - GZIPOutputStream out = new GZIPOutputStream(bytesOut); - out.write(bytes); - out.close(); - - byte[] compressed = bytesOut.toByteArray(); - ByteBuf buffer = Unpooled.buffer().writeBytes(compressed).writeBytes(compressed); - EmbeddedChannel channel = new EmbeddedChannel(new JdkZlibDecoder(ZlibWrapper.GZIP, true)); - // Write it into the Channel in a way that we were able to decompress the first data completely but not the - // whole footer. - assertTrue(channel.writeInbound(buffer.readRetainedSlice(compressed.length - 1))); - assertTrue(channel.writeInbound(buffer)); - assertTrue(channel.finish()); - - ByteBuf uncompressedBuffer = Unpooled.wrappedBuffer(bytes); - ByteBuf read = channel.readInbound(); - assertEquals(uncompressedBuffer, read); - read.release(); - - read = channel.readInbound(); - assertEquals(uncompressedBuffer, read); - read.release(); - - assertNull(channel.readInbound()); - uncompressedBuffer.release(); - } - - private static byte[] gzip(byte[] bytes) throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - GZIPOutputStream stream = new GZIPOutputStream(out); - stream.write(bytes); - stream.close(); - return out.toByteArray(); - } - - private static byte[] deflate(byte[] bytes) throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - OutputStream stream = new DeflaterOutputStream(out); - stream.write(bytes); - stream.close(); - return out.toByteArray(); - } - - private static final class TestByteBufAllocator extends AbstractByteBufAllocator { - private final ByteBufAllocator wrapped; - private int maxAllocation; - - TestByteBufAllocator(ByteBufAllocator wrapped) { - this.wrapped = wrapped; - } - - public int getMaxAllocation() { - return maxAllocation; - } - - @Override - public boolean isDirectBufferPooled() { - return wrapped.isDirectBufferPooled(); - } - - @Override - protected ByteBuf newHeapBuffer(int initialCapacity, int maxCapacity) { - maxAllocation = Math.max(maxAllocation, maxCapacity); - return wrapped.heapBuffer(initialCapacity, maxCapacity); - } - - @Override - protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) { - maxAllocation = Math.max(maxAllocation, maxCapacity); - return wrapped.directBuffer(initialCapacity, maxCapacity); - } - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/compression/LengthAwareLzfIntegrationTest.java b/codec/src/test/java/io/netty/handler/codec/compression/LengthAwareLzfIntegrationTest.java deleted file mode 100644 index 09b1313cd7..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/compression/LengthAwareLzfIntegrationTest.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.channel.embedded.EmbeddedChannel; - -import static com.ning.compress.lzf.LZFChunk.MAX_CHUNK_LEN; - -public class LengthAwareLzfIntegrationTest extends LzfIntegrationTest { - - @Override - protected EmbeddedChannel createEncoder() { - return new EmbeddedChannel(new LzfEncoder(false, MAX_CHUNK_LEN, 2 * 1024 * 1024)); - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/compression/Lz4FrameDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/compression/Lz4FrameDecoderTest.java deleted file mode 100644 index e86638d371..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/compression/Lz4FrameDecoderTest.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import net.jpountz.lz4.LZ4BlockOutputStream; -import org.junit.jupiter.api.Test; - -import java.io.ByteArrayOutputStream; -import java.util.Arrays; - -import static io.netty.handler.codec.compression.Lz4Constants.*; -import static org.junit.jupiter.api.Assertions.assertThrows; - -public class Lz4FrameDecoderTest extends AbstractDecoderTest { - - private static final byte[] DATA = { 0x4C, 0x5A, 0x34, 0x42, 0x6C, 0x6F, 0x63, 0x6B, // magic bytes - 0x16, // token - 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // compr. and decompr. length - (byte) 0x86, (byte) 0xE4, 0x79, 0x0F, // checksum - 0x4E, 0x65, 0x74, 0x74, 0x79, // data - 0x4C, 0x5A, 0x34, 0x42, 0x6C, 0x6F, 0x63, 0x6B, // magic bytes - 0x16, // token - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // last empty block - 0x00, 0x00, 0x00, 0x00 }; - - public Lz4FrameDecoderTest() throws Exception { - } - - @Override - protected EmbeddedChannel createChannel() { - return new EmbeddedChannel(new Lz4FrameDecoder(true)); - } - - @Test - public void testUnexpectedBlockIdentifier() { - final byte[] data = Arrays.copyOf(DATA, DATA.length); - data[1] = 0x00; - - ByteBuf in = Unpooled.wrappedBuffer(data); - assertThrows(DecompressionException.class, () -> channel.writeInbound(in), "unexpected block identifier"); - } - - @Test - public void testInvalidCompressedLength() { - final byte[] data = Arrays.copyOf(DATA, DATA.length); - data[12] = (byte) 0xFF; - - ByteBuf in = Unpooled.wrappedBuffer(data); - assertThrows(DecompressionException.class, () -> channel.writeInbound(in), "invalid compressedLength"); - } - - @Test - public void testInvalidDecompressedLength() { - final byte[] data = Arrays.copyOf(DATA, DATA.length); - data[16] = (byte) 0xFF; - - ByteBuf in = Unpooled.wrappedBuffer(data); - assertThrows(DecompressionException.class, () -> channel.writeInbound(in), "invalid decompressedLength"); - } - - @Test - public void testDecompressedAndCompressedLengthMismatch() { - final byte[] data = Arrays.copyOf(DATA, DATA.length); - data[13] = 0x01; - - ByteBuf in = Unpooled.wrappedBuffer(data); - assertThrows(DecompressionException.class, () -> channel.writeInbound(in), "mismatch"); - } - - @Test - public void testUnexpectedBlockType() { - final byte[] data = Arrays.copyOf(DATA, DATA.length); - data[8] = 0x36; - - ByteBuf in = Unpooled.wrappedBuffer(data); - assertThrows(DecompressionException.class, () -> channel.writeInbound(in), "unexpected blockType"); - } - - @Test - public void testMismatchingChecksum() { - final byte[] data = Arrays.copyOf(DATA, DATA.length); - data[17] = 0x01; - - ByteBuf in = Unpooled.wrappedBuffer(data); - assertThrows(DecompressionException.class, () -> channel.writeInbound(in), "mismatching checksum"); - } - - @Test - public void testChecksumErrorOfLastBlock() { - final byte[] data = Arrays.copyOf(DATA, DATA.length); - data[44] = 0x01; - - assertThrows(DecompressionException.class, - () -> tryDecodeAndCatchBufLeaks(channel, Unpooled.wrappedBuffer(data)), "checksum error"); - } - - @Override - protected byte[] compress(byte[] data) throws Exception { - ByteArrayOutputStream os = new ByteArrayOutputStream(); - int size = MAX_BLOCK_SIZE + 1; - LZ4BlockOutputStream lz4Os = new LZ4BlockOutputStream(os, - rand.nextInt(size - MIN_BLOCK_SIZE) + MIN_BLOCK_SIZE); - lz4Os.write(data); - lz4Os.close(); - - return os.toByteArray(); - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/compression/Lz4FrameEncoderTest.java b/codec/src/test/java/io/netty/handler/codec/compression/Lz4FrameEncoderTest.java deleted file mode 100644 index a51753a36b..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/compression/Lz4FrameEncoderTest.java +++ /dev/null @@ -1,305 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.bootstrap.Bootstrap; -import io.netty.bootstrap.ServerBootstrap; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.ByteBufInputStream; -import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.codec.EncoderException; -import net.jpountz.lz4.LZ4BlockInputStream; -import net.jpountz.lz4.LZ4Factory; -import net.jpountz.xxhash.XXHashFactory; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.io.InputStream; -import java.net.InetSocketAddress; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; -import java.util.zip.Checksum; - -import static io.netty.handler.codec.compression.Lz4Constants.DEFAULT_SEED; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.not; -import static org.hamcrest.core.Is.is; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.when; - -public class Lz4FrameEncoderTest extends AbstractEncoderTest { - /** - * For the purposes of this test, if we pass this (very small) size of buffer into - * {@link Lz4FrameEncoder#allocateBuffer(ChannelHandlerContext, ByteBuf, boolean)}, we should get back - * an empty buffer. - */ - private static final int NONALLOCATABLE_SIZE = 1; - - @Mock - private ChannelHandlerContext ctx; - - /** - * A {@link ByteBuf} for mocking purposes, largely because it's difficult to allocate to huge buffers. - */ - @Mock - private ByteBuf buffer; - - @BeforeEach - public void setup() { - MockitoAnnotations.initMocks(this); - when(ctx.alloc()).thenReturn(ByteBufAllocator.DEFAULT); - } - - @Override - protected EmbeddedChannel createChannel() { - return new EmbeddedChannel(new Lz4FrameEncoder()); - } - - @Override - protected ByteBuf decompress(ByteBuf compressed, int originalLength) throws Exception { - InputStream is = new ByteBufInputStream(compressed, true); - LZ4BlockInputStream lz4Is = null; - byte[] decompressed = new byte[originalLength]; - try { - lz4Is = new LZ4BlockInputStream(is); - int remaining = originalLength; - while (remaining > 0) { - int read = lz4Is.read(decompressed, originalLength - remaining, remaining); - if (read > 0) { - remaining -= read; - } else { - break; - } - } - assertEquals(-1, lz4Is.read()); - } finally { - if (lz4Is != null) { - lz4Is.close(); - } else { - is.close(); - } - } - - return Unpooled.wrappedBuffer(decompressed); - } - - @Test - public void testAllocateDirectBuffer() { - final int blockSize = 100; - testAllocateBuffer(blockSize, blockSize - 13, true); - testAllocateBuffer(blockSize, blockSize * 5, true); - testAllocateBuffer(blockSize, NONALLOCATABLE_SIZE, true); - } - - @Test - public void testAllocateHeapBuffer() { - final int blockSize = 100; - testAllocateBuffer(blockSize, blockSize - 13, false); - testAllocateBuffer(blockSize, blockSize * 5, false); - testAllocateBuffer(blockSize, NONALLOCATABLE_SIZE, false); - } - - private void testAllocateBuffer(int blockSize, int bufSize, boolean preferDirect) { - // allocate the input buffer to an arbitrary size less than the blockSize - ByteBuf in = ByteBufAllocator.DEFAULT.buffer(bufSize, bufSize); - in.writerIndex(in.capacity()); - - ByteBuf out = null; - try { - Lz4FrameEncoder encoder = newEncoder(blockSize, Lz4FrameEncoder.DEFAULT_MAX_ENCODE_SIZE); - out = encoder.allocateBuffer(ctx, in, preferDirect); - assertNotNull(out); - if (NONALLOCATABLE_SIZE == bufSize) { - assertFalse(out.isWritable()); - } else { - assertTrue(out.writableBytes() > 0); - if (!preferDirect) { - // Only check if preferDirect is not true as if a direct buffer is returned or not depends on - // if sun.misc.Unsafe is present. - assertFalse(out.isDirect()); - } - } - } finally { - in.release(); - if (out != null) { - out.release(); - } - } - } - - @Test - public void testAllocateDirectBufferExceedMaxEncodeSize() { - final int maxEncodeSize = 1024; - Lz4FrameEncoder encoder = newEncoder(Lz4Constants.DEFAULT_BLOCK_SIZE, maxEncodeSize); - int inputBufferSize = maxEncodeSize * 10; - ByteBuf buf = ByteBufAllocator.DEFAULT.buffer(inputBufferSize, inputBufferSize); - try { - buf.writerIndex(inputBufferSize); - assertThrows(EncoderException.class, () -> encoder.allocateBuffer(ctx, buf, false)); - } finally { - buf.release(); - } - } - - private Lz4FrameEncoder newEncoder(int blockSize, int maxEncodeSize) { - Checksum checksum = XXHashFactory.fastestInstance().newStreamingHash32(DEFAULT_SEED).asChecksum(); - Lz4FrameEncoder encoder = new Lz4FrameEncoder(LZ4Factory.fastestInstance(), true, - blockSize, - checksum, - maxEncodeSize); - encoder.handlerAdded(ctx); - return encoder; - } - - /** - * This test might be a invasive in terms of knowing what happens inside - * {@link Lz4FrameEncoder#allocateBuffer(ChannelHandlerContext, ByteBuf, boolean)}, but this is safest way - * of testing the overflow conditions as allocating the huge buffers fails in many CI environments. - */ - @Test - public void testAllocateOnHeapBufferOverflowsOutputSize() { - final int maxEncodeSize = Integer.MAX_VALUE; - Lz4FrameEncoder encoder = newEncoder(Lz4Constants.DEFAULT_BLOCK_SIZE, maxEncodeSize); - when(buffer.readableBytes()).thenReturn(maxEncodeSize); - buffer.writerIndex(maxEncodeSize); - assertThrows(EncoderException.class, () -> encoder.allocateBuffer(ctx, buffer, false)); - } - - @Test - public void testFlush() { - Lz4FrameEncoder encoder = new Lz4FrameEncoder(); - EmbeddedChannel channel = new EmbeddedChannel(encoder); - int size = 27; - ByteBuf buf = ByteBufAllocator.DEFAULT.buffer(size, size); - buf.writerIndex(size); - assertEquals(0, encoder.getBackingBuffer().readableBytes()); - channel.write(buf); - assertTrue(channel.outboundMessages().isEmpty()); - assertEquals(size, encoder.getBackingBuffer().readableBytes()); - channel.flush(); - assertTrue(channel.finish()); - assertTrue(channel.releaseOutbound()); - assertFalse(channel.releaseInbound()); - } - - @Test - public void testAllocatingAroundBlockSize() { - int blockSize = 100; - Lz4FrameEncoder encoder = newEncoder(blockSize, Lz4FrameEncoder.DEFAULT_MAX_ENCODE_SIZE); - EmbeddedChannel channel = new EmbeddedChannel(encoder); - - int size = blockSize - 1; - ByteBuf buf = ByteBufAllocator.DEFAULT.buffer(size, size); - buf.writerIndex(size); - assertEquals(0, encoder.getBackingBuffer().readableBytes()); - channel.write(buf); - assertEquals(size, encoder.getBackingBuffer().readableBytes()); - - int nextSize = size - 1; - buf = ByteBufAllocator.DEFAULT.buffer(nextSize, nextSize); - buf.writerIndex(nextSize); - channel.write(buf); - assertEquals(size + nextSize - blockSize, encoder.getBackingBuffer().readableBytes()); - - channel.flush(); - assertEquals(0, encoder.getBackingBuffer().readableBytes()); - assertTrue(channel.finish()); - assertTrue(channel.releaseOutbound()); - assertFalse(channel.releaseInbound()); - } - - @Test - @Timeout(value = 3000, unit = TimeUnit.MILLISECONDS) - public void writingAfterClosedChannelDoesNotNPE() throws Exception { - EventLoopGroup group = new MultithreadEventLoopGroup(2, NioHandler.newFactory()); - Channel serverChannel = null; - Channel clientChannel = null; - final CountDownLatch latch = new CountDownLatch(1); - final AtomicReference writeFailCauseRef = new AtomicReference<>(); - try { - ServerBootstrap sb = new ServerBootstrap(); - sb.group(group); - sb.channel(NioServerSocketChannel.class); - sb.childHandler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - } - }); - - Bootstrap bs = new Bootstrap(); - bs.group(group); - bs.channel(NioSocketChannel.class); - bs.handler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - ch.pipeline().addLast(new Lz4FrameEncoder()); - } - }); - - serverChannel = sb.bind(new InetSocketAddress(0)).get(); - clientChannel = bs.connect(serverChannel.localAddress()).get(); - - final Channel finalClientChannel = clientChannel; - clientChannel.executor().execute(() -> { - finalClientChannel.close(); - final int size = 27; - ByteBuf buf = ByteBufAllocator.DEFAULT.buffer(size, size); - finalClientChannel.writeAndFlush(buf.writerIndex(buf.writerIndex() + size)) - .addListener(future -> { - try { - writeFailCauseRef.set(future.cause()); - } finally { - latch.countDown(); - } - }); - }); - latch.await(); - Throwable writeFailCause = writeFailCauseRef.get(); - assertNotNull(writeFailCause); - Throwable writeFailCauseCause = writeFailCause.getCause(); - if (writeFailCauseCause != null) { - assertThat(writeFailCauseCause, is(not(instanceOf(NullPointerException.class)))); - } - } finally { - if (serverChannel != null) { - serverChannel.close(); - } - if (clientChannel != null) { - clientChannel.close(); - } - group.shutdownGracefully(); - } - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/compression/Lz4FrameIntegrationTest.java b/codec/src/test/java/io/netty/handler/codec/compression/Lz4FrameIntegrationTest.java deleted file mode 100644 index decc77913b..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/compression/Lz4FrameIntegrationTest.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.channel.embedded.EmbeddedChannel; - -public class Lz4FrameIntegrationTest extends AbstractIntegrationTest { - - @Override - protected EmbeddedChannel createEncoder() { - return new EmbeddedChannel(new Lz4FrameEncoder()); - } - - @Override - protected EmbeddedChannel createDecoder() { - return new EmbeddedChannel(new Lz4FrameDecoder()); - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/compression/LzfDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/compression/LzfDecoderTest.java deleted file mode 100644 index c5a772f3ae..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/compression/LzfDecoderTest.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import com.ning.compress.lzf.LZFEncoder; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import org.junit.jupiter.api.Test; - -import static com.ning.compress.lzf.LZFChunk.*; -import static org.junit.jupiter.api.Assertions.assertThrows; - -public class LzfDecoderTest extends AbstractDecoderTest { - - public LzfDecoderTest() throws Exception { - } - - @Override - protected EmbeddedChannel createChannel() { - return new EmbeddedChannel(new LzfDecoder()); - } - - @Test - public void testUnexpectedBlockIdentifier() { - ByteBuf in = Unpooled.buffer(); - in.writeShort(0x1234); //random value - in.writeByte(BLOCK_TYPE_NON_COMPRESSED); - in.writeShort(0); - - assertThrows(DecompressionException.class, () -> channel.writeInbound(in), "unexpected block identifier"); - } - - @Test - public void testUnknownTypeOfChunk() { - ByteBuf in = Unpooled.buffer(); - in.writeByte(BYTE_Z); - in.writeByte(BYTE_V); - in.writeByte(0xFF); //random value - in.writeInt(0); - - assertThrows(DecompressionException.class, () -> channel.writeInbound(in), "unknown type of chunk"); - } - - @Override - protected byte[] compress(byte[] data) throws Exception { - return LZFEncoder.encode(data); - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/compression/LzfEncoderTest.java b/codec/src/test/java/io/netty/handler/codec/compression/LzfEncoderTest.java deleted file mode 100644 index e006a95bc9..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/compression/LzfEncoderTest.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import com.ning.compress.lzf.LZFDecoder; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; - -public class LzfEncoderTest extends AbstractEncoderTest { - - @Override - protected EmbeddedChannel createChannel() { - return new EmbeddedChannel(new LzfEncoder()); - } - - @Override - protected ByteBuf decompress(ByteBuf compressed, int originalLength) throws Exception { - byte[] compressedArray = new byte[compressed.readableBytes()]; - compressed.readBytes(compressedArray); - compressed.release(); - - byte[] decompressed = LZFDecoder.decode(compressedArray); - return Unpooled.wrappedBuffer(decompressed); - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/compression/LzfIntegrationTest.java b/codec/src/test/java/io/netty/handler/codec/compression/LzfIntegrationTest.java deleted file mode 100644 index 23cf3baa9e..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/compression/LzfIntegrationTest.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.channel.embedded.EmbeddedChannel; - -public class LzfIntegrationTest extends AbstractIntegrationTest { - - @Override - protected EmbeddedChannel createEncoder() { - return new EmbeddedChannel(new LzfEncoder()); - } - - @Override - protected EmbeddedChannel createDecoder() { - return new EmbeddedChannel(new LzfDecoder()); - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/compression/LzmaFrameEncoderTest.java b/codec/src/test/java/io/netty/handler/codec/compression/LzmaFrameEncoderTest.java deleted file mode 100644 index 380aa358e7..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/compression/LzmaFrameEncoderTest.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufInputStream; -import io.netty.buffer.CompositeByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import lzma.sdk.lzma.Decoder; -import lzma.streams.LzmaInputStream; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class LzmaFrameEncoderTest extends AbstractEncoderTest { - - @Override - protected EmbeddedChannel createChannel() { - return new EmbeddedChannel(new LzmaFrameEncoder()); - } - - @ParameterizedTest - @MethodSource("smallData") - @Override - public void testCompressionOfBatchedFlowOfData(ByteBuf data) throws Exception { - testCompressionOfBatchedFlow(data); - } - - @Override - protected void testCompressionOfBatchedFlow(final ByteBuf data) throws Exception { - List originalLengths = new ArrayList<>(); - final int dataLength = data.readableBytes(); - int written = 0, length = rand.nextInt(50); - while (written + length < dataLength) { - ByteBuf in = data.retainedSlice(written, length); - assertTrue(channel.writeOutbound(in)); - written += length; - originalLengths.add(length); - length = rand.nextInt(50); - } - length = dataLength - written; - ByteBuf in = data.retainedSlice(written, dataLength - written); - originalLengths.add(length); - assertTrue(channel.writeOutbound(in)); - assertTrue(channel.finish()); - - CompositeByteBuf decompressed = Unpooled.compositeBuffer(); - ByteBuf msg; - int i = 0; - while ((msg = channel.readOutbound()) != null) { - ByteBuf decompressedMsg = decompress(msg, originalLengths.get(i++)); - decompressed.addComponent(true, decompressedMsg); - } - assertEquals(originalLengths.size(), i); - assertEquals(data, decompressed); - - decompressed.release(); - data.release(); - } - - @Override - protected ByteBuf decompress(ByteBuf compressed, int originalLength) throws Exception { - InputStream is = new ByteBufInputStream(compressed, true); - LzmaInputStream lzmaIs = null; - byte[] decompressed = new byte[originalLength]; - try { - lzmaIs = new LzmaInputStream(is, new Decoder()); - int remaining = originalLength; - while (remaining > 0) { - int read = lzmaIs.read(decompressed, originalLength - remaining, remaining); - if (read > 0) { - remaining -= read; - } else { - break; - } - } - assertEquals(-1, lzmaIs.read()); - } finally { - if (lzmaIs != null) { - lzmaIs.close(); - } - // LzmaInputStream does not close the stream it wraps, so we should always close. - // The close operation should be safe to call multiple times anyways so lets just call it and be safe. - // https://github.com/jponge/lzma-java/issues/14 - if (is != null) { - is.close(); - } - } - - return Unpooled.wrappedBuffer(decompressed); - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/compression/SnappyFrameDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/compression/SnappyFrameDecoderTest.java deleted file mode 100644 index cdb207d3b4..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/compression/SnappyFrameDecoderTest.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class SnappyFrameDecoderTest { - private EmbeddedChannel channel; - - @BeforeEach - public void initChannel() { - channel = new EmbeddedChannel(new SnappyFrameDecoder()); - } - - @AfterEach - public void tearDown() { - assertFalse(channel.finishAndReleaseAll()); - } - - @Test - public void testReservedUnskippableChunkTypeCausesError() { - ByteBuf in = Unpooled.wrappedBuffer(new byte[] { - 0x03, 0x01, 0x00, 0x00, 0x00 - }); - - assertThrows(DecompressionException.class, () -> channel.writeInbound(in)); - } - - @Test - public void testInvalidStreamIdentifierLength() { - ByteBuf in = Unpooled.wrappedBuffer(new byte[] { - -0x80, 0x05, 0x00, 0x00, 'n', 'e', 't', 't', 'y' - }); - - assertThrows(DecompressionException.class, () -> channel.writeInbound(in)); - } - - @Test - public void testInvalidStreamIdentifierValue() { - ByteBuf in = Unpooled.wrappedBuffer(new byte[] { - (byte) 0xff, 0x06, 0x00, 0x00, 's', 'n', 'e', 't', 't', 'y' - }); - - assertThrows(DecompressionException.class, () -> channel.writeInbound(in)); - } - - @Test - public void testReservedSkippableBeforeStreamIdentifier() { - ByteBuf in = Unpooled.wrappedBuffer(new byte[] { - -0x7f, 0x06, 0x00, 0x00, 's', 'n', 'e', 't', 't', 'y' - }); - - assertThrows(DecompressionException.class, () -> channel.writeInbound(in)); - } - - @Test - public void testUncompressedDataBeforeStreamIdentifier() { - ByteBuf in = Unpooled.wrappedBuffer(new byte[] { - 0x01, 0x05, 0x00, 0x00, 'n', 'e', 't', 't', 'y' - }); - - assertThrows(DecompressionException.class, () -> channel.writeInbound(in)); - } - - @Test - public void testCompressedDataBeforeStreamIdentifier() { - ByteBuf in = Unpooled.wrappedBuffer(new byte[] { - 0x00, 0x05, 0x00, 0x00, 'n', 'e', 't', 't', 'y' - }); - - assertThrows(DecompressionException.class, () -> channel.writeInbound(in)); - } - - @Test - public void testReservedSkippableSkipsInput() { - ByteBuf in = Unpooled.wrappedBuffer(new byte[] { - (byte) 0xff, 0x06, 0x00, 0x00, 0x73, 0x4e, 0x61, 0x50, 0x70, 0x59, - -0x7f, 0x05, 0x00, 0x00, 'n', 'e', 't', 't', 'y' - }); - - assertFalse(channel.writeInbound(in)); - assertNull(channel.readInbound()); - - assertFalse(in.isReadable()); - } - - @Test - public void testUncompressedDataAppendsToOut() { - ByteBuf in = Unpooled.wrappedBuffer(new byte[] { - (byte) 0xff, 0x06, 0x00, 0x00, 0x73, 0x4e, 0x61, 0x50, 0x70, 0x59, - 0x01, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 'n', 'e', 't', 't', 'y' - }); - - assertTrue(channel.writeInbound(in)); - - ByteBuf expected = Unpooled.wrappedBuffer(new byte[] { 'n', 'e', 't', 't', 'y' }); - ByteBuf actual = channel.readInbound(); - assertEquals(expected, actual); - - expected.release(); - actual.release(); - } - - @Test - public void testCompressedDataDecodesAndAppendsToOut() { - ByteBuf in = Unpooled.wrappedBuffer(new byte[] { - (byte) 0xff, 0x06, 0x00, 0x00, 0x73, 0x4e, 0x61, 0x50, 0x70, 0x59, - 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, // preamble length - 0x04 << 2, // literal tag + length - 0x6e, 0x65, 0x74, 0x74, 0x79 // "netty" - }); - - assertTrue(channel.writeInbound(in)); - - ByteBuf expected = Unpooled.wrappedBuffer(new byte[] { 'n', 'e', 't', 't', 'y' }); - ByteBuf actual = channel.readInbound(); - - assertEquals(expected, actual); - - expected.release(); - actual.release(); - } - - // The following two tests differ in only the checksum provided for the literal - // uncompressed string "netty" - - @Test - public void testInvalidChecksumThrowsException() { - EmbeddedChannel channel = new EmbeddedChannel(new SnappyFrameDecoder(true)); - try { - // checksum here is presented as 0 - ByteBuf in = Unpooled.wrappedBuffer(new byte[]{ - (byte) 0xff, 0x06, 0x00, 0x00, 0x73, 0x4e, 0x61, 0x50, 0x70, 0x59, - 0x01, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 'n', 'e', 't', 't', 'y' - }); - - assertThrows(DecompressionException.class, () -> channel.writeInbound(in)); - } finally { - channel.finishAndReleaseAll(); - } - } - - @Test - public void testInvalidChecksumDoesNotThrowException() { - EmbeddedChannel channel = new EmbeddedChannel(new SnappyFrameDecoder(true)); - try { - // checksum here is presented as a282986f (little endian) - ByteBuf in = Unpooled.wrappedBuffer(new byte[]{ - (byte) 0xff, 0x06, 0x00, 0x00, 0x73, 0x4e, 0x61, 0x50, 0x70, 0x59, - 0x01, 0x09, 0x00, 0x00, 0x6f, -0x68, 0x2e, -0x47, 'n', 'e', 't', 't', 'y' - }); - - assertTrue(channel.writeInbound(in)); - ByteBuf expected = Unpooled.wrappedBuffer(new byte[] { 'n', 'e', 't', 't', 'y' }); - ByteBuf actual = channel.readInbound(); - assertEquals(expected, actual); - - expected.release(); - actual.release(); - } finally { - channel.finishAndReleaseAll(); - } - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/compression/SnappyFrameEncoderTest.java b/codec/src/test/java/io/netty/handler/codec/compression/SnappyFrameEncoderTest.java deleted file mode 100644 index 8dc3642e4a..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/compression/SnappyFrameEncoderTest.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.CompositeByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.*; - -public class SnappyFrameEncoderTest { - private EmbeddedChannel channel; - - @BeforeEach - public void setUp() { - channel = new EmbeddedChannel(new SnappyFrameEncoder()); - } - - @Test - public void testSmallAmountOfDataIsUncompressed() throws Exception { - ByteBuf in = Unpooled.wrappedBuffer(new byte[] { - 'n', 'e', 't', 't', 'y' - }); - - channel.writeOutbound(in); - assertTrue(channel.finish()); - ByteBuf expected = Unpooled.wrappedBuffer(new byte[] { - (byte) 0xff, 0x06, 0x00, 0x00, 0x73, 0x4e, 0x61, 0x50, 0x70, 0x59, - 0x01, 0x09, 0x00, 0x00, 0x6f, -0x68, 0x2e, -0x47, 'n', 'e', 't', 't', 'y' - }); - ByteBuf actual = channel.readOutbound(); - assertEquals(expected, actual); - - expected.release(); - actual.release(); - } - - @Test - public void testLargeAmountOfDataIsCompressed() throws Exception { - ByteBuf in = Unpooled.wrappedBuffer(new byte[] { - 'n', 'e', 't', 't', 'y', 'n', 'e', 't', 't', 'y', - 'n', 'e', 't', 't', 'y', 'n', 'e', 't', 't', 'y' - }); - - channel.writeOutbound(in); - assertTrue(channel.finish()); - - ByteBuf expected = Unpooled.wrappedBuffer(new byte[] { - (byte) 0xff, 0x06, 0x00, 0x00, 0x73, 0x4e, 0x61, 0x50, 0x70, 0x59, - 0x00, 0x0E, 0x00, 0x00, 0x3b, 0x36, -0x7f, 0x37, - 0x14, 0x10, - 'n', 'e', 't', 't', 'y', - 0x3a, 0x05, 0x00 - }); - ByteBuf actual = channel.readOutbound(); - assertEquals(expected, actual); - - expected.release(); - actual.release(); - } - - @Test - public void testStreamStartIsOnlyWrittenOnce() throws Exception { - ByteBuf in = Unpooled.wrappedBuffer(new byte[] { - 'n', 'e', 't', 't', 'y' - }); - - channel.writeOutbound(in.retain()); - in.readerIndex(0); // rewind the buffer to write the same data - channel.writeOutbound(in); - assertTrue(channel.finish()); - - ByteBuf expected = Unpooled.wrappedBuffer(new byte[] { - (byte) 0xff, 0x06, 0x00, 0x00, 0x73, 0x4e, 0x61, 0x50, 0x70, 0x59, - 0x01, 0x09, 0x00, 0x00, 0x6f, -0x68, 0x2e, -0x47, 'n', 'e', 't', 't', 'y', - 0x01, 0x09, 0x00, 0x00, 0x6f, -0x68, 0x2e, -0x47, 'n', 'e', 't', 't', 'y', - }); - - CompositeByteBuf actual = Unpooled.compositeBuffer(); - for (;;) { - ByteBuf m = channel.readOutbound(); - if (m == null) { - break; - } - actual.addComponent(true, m); - } - assertEquals(expected, actual); - - expected.release(); - actual.release(); - } - - /** - * This test asserts that if we have a remainder after emitting a copy that - * is less than 4 bytes (ie. the minimum required for a copy), we should - * emit a literal rather than trying to see if we can emit another copy. - */ - @Test - public void testInputBufferOverseek() throws Exception { - ByteBuf in = Unpooled.wrappedBuffer(new byte[] { - 11, 0, // literal - 0, 0, 0, 0, // 1st copy - 16, 65, 96, 119, -22, 79, -43, 76, -75, -93, - 11, 104, 96, -99, 126, -98, 27, -36, 40, 117, - -65, -3, -57, -83, -58, 7, 114, -14, 68, -122, - 124, 88, 118, 54, 45, -26, 117, 13, -45, -9, - 60, -73, -53, -44, 53, 68, -77, -71, 109, 43, - -38, 59, 100, -12, -87, 44, -106, 123, -107, 38, - 13, -117, -23, -49, 29, 21, 26, 66, 1, -1, - -1, // literal - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, // 2nd copy - 66, 0, -104, -49, 16, -120, 22, 8, -52, -54, - -102, -52, -119, -124, -92, -71, 101, -120, -52, -48, - 45, -26, -24, 26, 41, -13, 36, 64, -47, 15, - -124, -7, -16, 91, 96, 0, -93, -42, 101, 20, - -74, 39, -124, 35, 43, -49, -21, -92, -20, -41, - 79, 41, 110, -105, 42, -96, 90, -9, -100, -22, - -62, 91, 2, 35, 113, 117, -71, 66, 1, // literal - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, // copy - -1, 1 // remainder - }); - - channel.writeOutbound(in); - assertTrue(channel.finish()); - ByteBuf out = channel.readOutbound(); - out.release(); - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/compression/SnappyIntegrationTest.java b/codec/src/test/java/io/netty/handler/codec/compression/SnappyIntegrationTest.java deleted file mode 100644 index 5e16178699..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/compression/SnappyIntegrationTest.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.channel.embedded.EmbeddedChannel; -import org.junit.jupiter.api.Test; - -import java.util.Random; - -public class SnappyIntegrationTest extends AbstractIntegrationTest { - - /** - * The number of random regression tests run by testRandom() runs. Whenever testRandom() finds the case that - * the snappy codec can't encode/decode, it will print the generated source code of the offending test case. - * You can always reproduce the problem using it rather than relying on testRandom(). - * - * The default is 1, but you can increase it to increase the chance of finding any unusual cases. - **/ - private static final int RANDOM_RUNS = 1; - - @Override - protected EmbeddedChannel createEncoder() { - return new EmbeddedChannel(new SnappyFrameEncoder()); - } - - @Override - protected EmbeddedChannel createDecoder() { - return new EmbeddedChannel(new SnappyFrameDecoder()); - } - - @Test - public void test1002() throws Throwable { - // Data from https://github.com/netty/netty/issues/1002 - final byte[] data = { - 11, 0, 0, 0, 0, 0, 16, 65, 96, 119, -22, 79, -43, 76, -75, -93, - 11, 104, 96, -99, 126, -98, 27, -36, 40, 117, -65, -3, -57, -83, -58, 7, - 114, -14, 68, -122, 124, 88, 118, 54, 45, -26, 117, 13, -45, -9, 60, -73, - -53, -44, 53, 68, -77, -71, 109, 43, -38, 59, 100, -12, -87, 44, -106, 123, - -107, 38, 13, -117, -23, -49, 29, 21, 26, 66, 1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 66, 0, -104, -49, - 16, -120, 22, 8, -52, -54, -102, -52, -119, -124, -92, -71, 101, -120, -52, -48, - 45, -26, -24, 26, 41, -13, 36, 64, -47, 15, -124, -7, -16, 91, 96, 0, - -93, -42, 101, 20, -74, 39, -124, 35, 43, -49, -21, -92, -20, -41, 79, 41, - 110, -105, 42, -96, 90, -9, -100, -22, -62, 91, 2, 35, 113, 117, -71, 66, - 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1 - }; - testIdentity(data, true); - } - - // These tests were found using testRandom() with large RANDOM_RUNS. - - // Tests that copies do not attempt to overrun into a previous frame chunk - @Test - public void test5323211032315942961() { - testWithSeed(5323211032315942961L); - } - - // Tests that when generating the hash lookup table for finding copies, we - // do not exceed the length of the input when there are no copies - @Test - public void test7088170877360183401() { - testWithSeed(7088170877360183401L); - } - - @Test - public void testRandom() throws Throwable { - for (int i = 0; i < RANDOM_RUNS; i++) { - long seed = rand.nextLong(); - if (seed < 0) { - // Use only positive seed to get prettier test name. :-) - continue; - } - - try { - testWithSeed(seed); - } catch (Throwable t) { - System.out.println("Failed with random seed " + seed + ". Here is a test for it:\n"); - printSeedAsTest(seed); - throw t; - } - } - } - - private void testWithSeed(long seed) { - byte[] data = new byte[16 * 1048576]; - new Random(seed).nextBytes(data); - testIdentity(data, true); - } - - private static void printSeedAsTest(long l) { - System.out.println("@Test"); - System.out.println("@Ignore"); - System.out.println("public void test" + l + "(){"); - System.out.println(" testWithSeed(" + l + "L);"); - System.out.println("}"); - } -} 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 deleted file mode 100644 index f3ba22e112..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/compression/SnappyTest.java +++ /dev/null @@ -1,338 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.CharsetUtil; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Test; - -import static io.netty.handler.codec.compression.Snappy.*; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import java.nio.CharBuffer; - -public class SnappyTest { - private final Snappy snappy = new Snappy(); - - @AfterEach - public void resetSnappy() { - snappy.reset(); - } - - @Test - public void testDecodeLiteral() throws Exception { - ByteBuf in = Unpooled.wrappedBuffer(new byte[] { - 0x05, // preamble length - 0x04 << 2, // literal tag + length - 0x6e, 0x65, 0x74, 0x74, 0x79 // "netty" - }); - ByteBuf out = Unpooled.buffer(5); - snappy.decode(in, out); - - // "netty" - ByteBuf expected = Unpooled.wrappedBuffer(new byte[] { - 0x6e, 0x65, 0x74, 0x74, 0x79 - }); - assertEquals(expected, out, "Literal was not decoded correctly"); - - in.release(); - out.release(); - expected.release(); - } - - @Test - public void testDecodeCopyWith1ByteOffset() throws Exception { - ByteBuf in = Unpooled.wrappedBuffer(new byte[] { - 0x0a, // preamble length - 0x04 << 2, // literal tag + length - 0x6e, 0x65, 0x74, 0x74, 0x79, // "netty" - 0x01 << 2 | 0x01, // copy with 1-byte offset + length - 0x05 // offset - }); - ByteBuf out = Unpooled.buffer(10); - snappy.decode(in, out); - - // "nettynetty" - we saved a whole byte :) - ByteBuf expected = Unpooled.wrappedBuffer(new byte[] { - 0x6e, 0x65, 0x74, 0x74, 0x79, 0x6e, 0x65, 0x74, 0x74, 0x79 - }); - assertEquals(expected, out, "Copy was not decoded correctly"); - - in.release(); - out.release(); - expected.release(); - } - - @Test - public void testDecodeCopyWithTinyOffset() { - ByteBuf in = Unpooled.wrappedBuffer(new byte[] { - 0x0b, // preamble length - 0x04 << 2, // literal tag + length - 0x6e, 0x65, 0x74, 0x74, 0x79, // "netty" - 0x05 << 2 | 0x01, // copy with 1-byte offset + length - 0x00 // INVALID offset (< 1) - }); - ByteBuf out = Unpooled.buffer(10); - try { - assertThrows(DecompressionException.class, () -> snappy.decode(in, out)); - } finally { - in.release(); - out.release(); - } - } - - @Test - public void testDecodeCopyWithOffsetBeforeChunk() { - ByteBuf in = Unpooled.wrappedBuffer(new byte[] { - 0x0a, // preamble length - 0x04 << 2, // literal tag + length - 0x6e, 0x65, 0x74, 0x74, 0x79, // "netty" - 0x05 << 2 | 0x01, // copy with 1-byte offset + length - 0x0b // INVALID offset (greater than chunk size) - }); - ByteBuf out = Unpooled.buffer(10); - try { - assertThrows(DecompressionException.class, () -> snappy.decode(in, out)); - } finally { - in.release(); - out.release(); - } - } - - @Test - public void testDecodeWithOverlyLongPreamble() { - ByteBuf in = Unpooled.wrappedBuffer(new byte[] { - -0x80, -0x80, -0x80, -0x80, 0x7f, // preamble length - 0x04 << 2, // literal tag + length - 0x6e, 0x65, 0x74, 0x74, 0x79, // "netty" - }); - ByteBuf out = Unpooled.buffer(10); - try { - assertThrows(DecompressionException.class, () -> snappy.decode(in, out)); - } finally { - in.release(); - out.release(); - } - } - - @Test - public void encodeShortTextIsLiteral() throws Exception { - ByteBuf in = Unpooled.wrappedBuffer(new byte[] { - 0x6e, 0x65, 0x74, 0x74, 0x79 - }); - ByteBuf out = Unpooled.buffer(7); - snappy.encode(in, out, 5); - - ByteBuf expected = Unpooled.wrappedBuffer(new byte[] { - 0x05, // preamble length - 0x04 << 2, // literal tag + length - 0x6e, 0x65, 0x74, 0x74, 0x79 // "netty" - }); - assertEquals(expected, out, "Encoded literal was invalid"); - - in.release(); - out.release(); - expected.release(); - } - - @Test - public void encodeAndDecodeLongTextUsesCopy() throws Exception { - String srcStr = "Netty has been designed carefully with the experiences " + - "earned from the implementation of a lot of protocols " + - "such as FTP, SMTP, HTTP, and various binary and " + - "text-based legacy protocols"; - ByteBuf in = Unpooled.wrappedBuffer(srcStr.getBytes("US-ASCII")); - ByteBuf out = Unpooled.buffer(180); - snappy.encode(in, out, in.readableBytes()); - - // The only compressibility in the above are the words: - // "the ", "rotocols", " of ", "TP, " and "and ". So this is a literal, - // followed by a copy followed by another literal, followed by another copy... - ByteBuf expected = Unpooled.wrappedBuffer(new byte[] { - -0x49, 0x01, // preamble length - -0x10, 0x42, // literal tag + length - - // Literal - 0x4e, 0x65, 0x74, 0x74, 0x79, 0x20, 0x68, 0x61, 0x73, 0x20, - 0x62, 0x65, 0x65, 0x6e, 0x20, 0x64, 0x65, 0x73, 0x69, 0x67, - 0x6e, 0x65, 0x64, 0x20, 0x63, 0x61, 0x72, 0x65, 0x66, 0x75, - 0x6c, 0x6c, 0x79, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, - 0x68, 0x65, 0x20, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x65, - 0x6e, 0x63, 0x65, 0x73, 0x20, 0x65, 0x61, 0x72, 0x6e, 0x65, - 0x64, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, - - // copy of "the " - 0x01, 0x1c, 0x58, - - // Next literal - 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, - 0x6c, 0x6f, 0x74, - - // copy of " of " - 0x01, 0x09, 0x60, - - // literal - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x73, 0x20, - 0x73, 0x75, 0x63, 0x68, 0x20, 0x61, 0x73, 0x20, 0x46, 0x54, - 0x50, 0x2c, 0x20, 0x53, 0x4d, - - // copy of " TP, " - 0x01, 0x06, 0x04, - - // literal - 0x48, 0x54, - - // copy of " TP, " - 0x01, 0x06, 0x44, - - // literal - 0x61, 0x6e, 0x64, 0x20, 0x76, 0x61, 0x72, 0x69, 0x6f, 0x75, - 0x73, 0x20, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, - - // copy of "and " - 0x05, 0x13, 0x48, - - // literal - 0x74, 0x65, 0x78, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65, - 0x64, 0x20, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x20, 0x70, - - // copy of "rotocols" - 0x11, 0x4c, - }); - - assertEquals(expected, out, "Encoded result was incorrect"); - - // Decode - ByteBuf outDecoded = Unpooled.buffer(); - snappy.decode(out, outDecoded); - assertEquals(CharBuffer.wrap(srcStr), - CharBuffer.wrap(outDecoded.getCharSequence(0, outDecoded.writerIndex(), CharsetUtil.US_ASCII))); - - in.release(); - out.release(); - outDecoded.release(); - } - - @Test - public void testCalculateChecksum() { - ByteBuf input = Unpooled.wrappedBuffer(new byte[] { - 'n', 'e', 't', 't', 'y' - }); - - assertEquals(maskChecksum(0xd6cb8b55L), calculateChecksum(input)); - input.release(); - } - - @Test - public void testMaskChecksum() { - ByteBuf input = Unpooled.wrappedBuffer(new byte[] { - 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, - 0x5f, 0x68, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, - 0x61, 0x74, 0x5f, - }); - assertEquals(0x44a4301f, calculateChecksum(input)); - input.release(); - } - - @Test - public void testValidateChecksumMatches() { - ByteBuf input = Unpooled.wrappedBuffer(new byte[] { - 'y', 't', 't', 'e', 'n' - }); - - validateChecksum(maskChecksum(0x2d4d3535), input); - input.release(); - } - - @Test - public void testValidateChecksumFails() { - ByteBuf input = Unpooled.wrappedBuffer(new byte[] { - 'y', 't', 't', 'e', 'n' - }); - try { - assertThrows(DecompressionException.class, () -> validateChecksum(maskChecksum(0xd6cb8b55), input)); - } finally { - input.release(); - } - } - - @Test - public void testEncodeLiteralAndDecodeLiteral() { - int[] lengths = { - 0x11, // default - 0x100, // case 60 - 0x1000, // case 61 - 0x100000, // case 62 - 0x1000001 // case 63 - }; - for (int len : lengths) { - ByteBuf in = Unpooled.wrappedBuffer(new byte[len]); - ByteBuf encoded = Unpooled.buffer(10); - ByteBuf decoded = Unpooled.buffer(10); - ByteBuf expected = Unpooled.wrappedBuffer(new byte[len]); - try { - encodeLiteral(in, encoded, len); - byte tag = encoded.readByte(); - decodeLiteral(tag, encoded, decoded); - assertEquals(expected, decoded, "Encoded or decoded literal was incorrect"); - } finally { - in.release(); - encoded.release(); - decoded.release(); - expected.release(); - } - } - } - - @Test - public void testLarge2ByteLiteralLengthAndCopyOffset() { - ByteBuf compressed = Unpooled.buffer(); - ByteBuf actualDecompressed = Unpooled.buffer(); - ByteBuf expectedDecompressed = Unpooled.buffer().writeByte(0x01).writeZero(0x8000).writeByte(0x01); - try { - // Generate a Snappy-encoded buffer that can only be decompressed correctly if - // the decoder treats 2-byte literal lengths and 2-byte copy offsets as unsigned values. - - // Write preamble, uncompressed content length (0x8002) encoded as varint. - compressed.writeByte(0x82).writeByte(0x80).writeByte(0x02); - - // Write a literal consisting of 0x01 followed by 0x8000 zeroes. - // The total length of this literal is 0x8001, which gets encoded as 0x8000 (length - 1). - // This length was selected because the encoded form is one larger than the maximum value - // representable using a signed 16-bit integer, and we want to assert the decoder is reading - // the length as an unsigned value. - compressed.writeByte(61 << 2); // tag for LITERAL with a 2-byte length - compressed.writeShortLE(0x8000); // length - 1 - compressed.writeByte(0x01).writeZero(0x8000); // literal content - - // Similarly, for a 2-byte copy operation we want to ensure the offset is treated as unsigned. - // Copy the initial 0x01 which was written 0x8001 bytes back in the stream. - compressed.writeByte(0x02); // tag for COPY with 2-byte offset, length = 1 - compressed.writeShortLE(0x8001); // offset - - snappy.decode(compressed, actualDecompressed); - assertEquals(expectedDecompressed, actualDecompressed); - } finally { - compressed.release(); - actualDecompressed.release(); - expectedDecompressed.release(); - } - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/compression/ZstdEncoderTest.java b/codec/src/test/java/io/netty/handler/codec/compression/ZstdEncoderTest.java deleted file mode 100644 index 296e3dac7e..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/compression/ZstdEncoderTest.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.compression; - -import com.github.luben.zstd.ZstdInputStream; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.ByteBufInputStream; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.embedded.EmbeddedChannel; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.io.InputStream; - - -import static org.mockito.Mockito.when; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class ZstdEncoderTest extends AbstractEncoderTest { - - @Mock - private ChannelHandlerContext ctx; - - @BeforeEach - public void setup() { - MockitoAnnotations.initMocks(this); - when(ctx.alloc()).thenReturn(ByteBufAllocator.DEFAULT); - } - - @Override - public EmbeddedChannel createChannel() { - return new EmbeddedChannel(new ZstdEncoder()); - } - - @ParameterizedTest - @MethodSource("largeData") - public void testCompressionOfLargeBatchedFlow(final ByteBuf data) throws Exception { - final int dataLength = data.readableBytes(); - int written = 0; - - ByteBuf in = data.retainedSlice(written, 65535); - assertTrue(channel.writeOutbound(in)); - - ByteBuf in2 = data.retainedSlice(65535, dataLength - 65535); - assertTrue(channel.writeOutbound(in2)); - - assertTrue(channel.finish()); - - ByteBuf decompressed = readDecompressed(dataLength); - assertEquals(data, decompressed); - - decompressed.release(); - data.release(); - } - - @ParameterizedTest - @MethodSource("smallData") - public void testCompressionOfSmallBatchedFlow(final ByteBuf data) throws Exception { - testCompressionOfBatchedFlow(data); - } - - @Override - protected ByteBuf decompress(ByteBuf compressed, int originalLength) throws Exception { - InputStream is = new ByteBufInputStream(compressed, true); - ZstdInputStream zstdIs = null; - byte[] decompressed = new byte[originalLength]; - try { - zstdIs = new ZstdInputStream(is); - int remaining = originalLength; - while (remaining > 0) { - int read = zstdIs.read(decompressed, originalLength - remaining, remaining); - if (read > 0) { - remaining -= read; - } else { - break; - } - } - assertEquals(-1, zstdIs.read()); - } finally { - if (zstdIs != null) { - zstdIs.close(); - } else { - is.close(); - } - } - - return Unpooled.wrappedBuffer(decompressed); - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/frame/DelimiterBasedFrameDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/frame/DelimiterBasedFrameDecoderTest.java deleted file mode 100644 index 6fa63eb29c..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/frame/DelimiterBasedFrameDecoderTest.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.frame; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.DecoderException; -import io.netty.handler.codec.DelimiterBasedFrameDecoder; -import io.netty.handler.codec.Delimiters; -import io.netty.handler.codec.TooLongFrameException; -import io.netty.util.CharsetUtil; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - -public class DelimiterBasedFrameDecoderTest { - - @Test - public void testFailSlowTooLongFrameRecovery() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel( - new DelimiterBasedFrameDecoder(1, true, false, Delimiters.nulDelimiter())); - - for (int i = 0; i < 2; i ++) { - ch.writeInbound(Unpooled.wrappedBuffer(new byte[] { 1, 2 })); - try { - assertTrue(ch.writeInbound(Unpooled.wrappedBuffer(new byte[] { 0 }))); - fail(DecoderException.class.getSimpleName() + " must be raised."); - } catch (TooLongFrameException e) { - // Expected - } - - ch.writeInbound(Unpooled.wrappedBuffer(new byte[] { 'A', 0 })); - ByteBuf buf = ch.readInbound(); - assertEquals("A", buf.toString(CharsetUtil.ISO_8859_1)); - - buf.release(); - } - } - - @Test - public void testFailFastTooLongFrameRecovery() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel( - new DelimiterBasedFrameDecoder(1, Delimiters.nulDelimiter())); - - for (int i = 0; i < 2; i ++) { - try { - assertTrue(ch.writeInbound(Unpooled.wrappedBuffer(new byte[] { 1, 2 }))); - fail(DecoderException.class.getSimpleName() + " must be raised."); - } catch (TooLongFrameException e) { - // Expected - } - - ch.writeInbound(Unpooled.wrappedBuffer(new byte[] { 0, 'A', 0 })); - ByteBuf buf = ch.readInbound(); - assertEquals("A", buf.toString(CharsetUtil.ISO_8859_1)); - - buf.release(); - } - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/frame/LengthFieldBasedFrameDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/frame/LengthFieldBasedFrameDecoderTest.java deleted file mode 100644 index ecdd2122ae..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/frame/LengthFieldBasedFrameDecoderTest.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.frame; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.DecoderException; -import io.netty.handler.codec.LengthFieldBasedFrameDecoder; -import io.netty.handler.codec.TooLongFrameException; -import io.netty.util.CharsetUtil; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - -public class LengthFieldBasedFrameDecoderTest { - @Test - public void testFailSlowTooLongFrameRecovery() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel( - new LengthFieldBasedFrameDecoder(5, 0, 4, 0, 4, false)); - - for (int i = 0; i < 2; i ++) { - assertFalse(ch.writeInbound(Unpooled.wrappedBuffer(new byte[] { 0, 0, 0, 2 }))); - try { - assertTrue(ch.writeInbound(Unpooled.wrappedBuffer(new byte[] { 0, 0 }))); - fail(DecoderException.class.getSimpleName() + " must be raised."); - } catch (TooLongFrameException e) { - // Expected - } - - ch.writeInbound(Unpooled.wrappedBuffer(new byte[] { 0, 0, 0, 1, 'A' })); - ByteBuf buf = ch.readInbound(); - assertEquals("A", buf.toString(CharsetUtil.ISO_8859_1)); - buf.release(); - } - } - - @Test - public void testFailFastTooLongFrameRecovery() throws Exception { - EmbeddedChannel ch = new EmbeddedChannel( - new LengthFieldBasedFrameDecoder(5, 0, 4, 0, 4)); - - for (int i = 0; i < 2; i ++) { - try { - assertTrue(ch.writeInbound(Unpooled.wrappedBuffer(new byte[] { 0, 0, 0, 2 }))); - fail(DecoderException.class.getSimpleName() + " must be raised."); - } catch (TooLongFrameException e) { - // Expected - } - - ch.writeInbound(Unpooled.wrappedBuffer(new byte[] { 0, 0, 0, 0, 0, 1, 'A' })); - ByteBuf buf = ch.readInbound(); - assertEquals("A", buf.toString(CharsetUtil.ISO_8859_1)); - buf.release(); - } - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/frame/LengthFieldPrependerTest.java b/codec/src/test/java/io/netty/handler/codec/frame/LengthFieldPrependerTest.java deleted file mode 100644 index 325f3a9e98..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/frame/LengthFieldPrependerTest.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.frame; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.EncoderException; -import io.netty.handler.codec.LengthFieldPrepender; -import io.netty.util.CharsetUtil; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static io.netty.buffer.Unpooled.*; -import java.nio.ByteOrder; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.fail; - -public class LengthFieldPrependerTest { - - private ByteBuf msg; - - @BeforeEach - public void setUp() throws Exception { - msg = copiedBuffer("A", CharsetUtil.ISO_8859_1); - } - - @Test - public void testPrependLength() throws Exception { - final EmbeddedChannel ch = new EmbeddedChannel(new LengthFieldPrepender(4)); - ch.writeOutbound(msg); - ByteBuf buf = ch.readOutbound(); - assertEquals(4, buf.readableBytes()); - assertEquals(msg.readableBytes(), buf.readInt()); - buf.release(); - - buf = ch.readOutbound(); - assertSame(buf, msg); - buf.release(); - } - - @Test - public void testPrependLengthIncludesLengthFieldLength() throws Exception { - final EmbeddedChannel ch = new EmbeddedChannel(new LengthFieldPrepender(4, true)); - ch.writeOutbound(msg); - ByteBuf buf = ch.readOutbound(); - assertEquals(4, buf.readableBytes()); - assertEquals(5, buf.readInt()); - buf.release(); - - buf = ch.readOutbound(); - assertSame(buf, msg); - buf.release(); - } - - @Test - public void testPrependAdjustedLength() throws Exception { - final EmbeddedChannel ch = new EmbeddedChannel(new LengthFieldPrepender(4, -1)); - ch.writeOutbound(msg); - ByteBuf buf = ch.readOutbound(); - assertEquals(4, buf.readableBytes()); - assertEquals(msg.readableBytes() - 1, buf.readInt()); - buf.release(); - - buf = ch.readOutbound(); - assertSame(buf, msg); - buf.release(); - } - - @Test - public void testAdjustedLengthLessThanZero() throws Exception { - final EmbeddedChannel ch = new EmbeddedChannel(new LengthFieldPrepender(4, -2)); - try { - ch.writeOutbound(msg); - fail(EncoderException.class.getSimpleName() + " must be raised."); - } catch (EncoderException e) { - // Expected - } - } - - @Test - public void testPrependLengthInLittleEndian() throws Exception { - final EmbeddedChannel ch = new EmbeddedChannel(new LengthFieldPrepender(ByteOrder.LITTLE_ENDIAN, 4, 0, false)); - ch.writeOutbound(msg); - ByteBuf buf = ch.readOutbound(); - assertEquals(4, buf.readableBytes()); - byte[] writtenBytes = new byte[buf.readableBytes()]; - buf.getBytes(0, writtenBytes); - assertEquals(1, writtenBytes[0]); - assertEquals(0, writtenBytes[1]); - assertEquals(0, writtenBytes[2]); - assertEquals(0, writtenBytes[3]); - buf.release(); - - buf = ch.readOutbound(); - assertSame(buf, msg); - buf.release(); - assertFalse(ch.finish(), "The channel must have been completely read"); - } - -} diff --git a/codec/src/test/java/io/netty/handler/codec/frame/package-info.java b/codec/src/test/java/io/netty/handler/codec/frame/package-info.java deleted file mode 100644 index d68433b136..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/frame/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * Test classes for frame based decoders - */ -package io.netty.handler.codec.frame; diff --git a/codec/src/test/java/io/netty/handler/codec/json/JsonObjectDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/json/JsonObjectDecoderTest.java deleted file mode 100644 index 96017268bc..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/json/JsonObjectDecoderTest.java +++ /dev/null @@ -1,405 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.json; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.CorruptedFrameException; -import io.netty.handler.codec.TooLongFrameException; -import io.netty.util.CharsetUtil; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class JsonObjectDecoderTest { - @Test - public void testJsonObjectOverMultipleWrites() { - EmbeddedChannel ch = new EmbeddedChannel(new JsonObjectDecoder()); - - String objectPart1 = "{ \"firstname\": \"John"; - String objectPart2 = "\" ,\n \"surname\" :"; - String objectPart3 = "\"Doe\", age:22 \n}"; - - // Test object - ch.writeInbound(Unpooled.copiedBuffer(" \n\n " + objectPart1, CharsetUtil.UTF_8)); - ch.writeInbound(Unpooled.copiedBuffer(objectPart2, CharsetUtil.UTF_8)); - ch.writeInbound(Unpooled.copiedBuffer(objectPart3 + " \n\n \n", CharsetUtil.UTF_8)); - - ByteBuf res = ch.readInbound(); - assertEquals(objectPart1 + objectPart2 + objectPart3, res.toString(CharsetUtil.UTF_8)); - res.release(); - - assertFalse(ch.finish()); - } - - @Test - public void testMultipleJsonObjectsOverMultipleWrites() { - EmbeddedChannel ch = new EmbeddedChannel(new JsonObjectDecoder()); - - String objectPart1 = "{\"name\":\"Jo"; - String objectPart2 = "hn\"}{\"name\":\"John\"}{\"name\":\"Jo"; - String objectPart3 = "hn\"}"; - - ch.writeInbound(Unpooled.copiedBuffer(objectPart1, CharsetUtil.UTF_8)); - ch.writeInbound(Unpooled.copiedBuffer(objectPart2, CharsetUtil.UTF_8)); - ch.writeInbound(Unpooled.copiedBuffer(objectPart3, CharsetUtil.UTF_8)); - - for (int i = 0; i < 3; i++) { - ByteBuf res = ch.readInbound(); - assertEquals("{\"name\":\"John\"}", res.toString(CharsetUtil.UTF_8)); - res.release(); - } - - assertFalse(ch.finish()); - } - - @Test - public void testJsonArrayOverMultipleWrites() { - EmbeddedChannel ch = new EmbeddedChannel(new JsonObjectDecoder()); - - String arrayPart1 = "[{\"test"; - String arrayPart2 = "case\" : \"\\\"}]Escaped dou\\\"ble quotes \\\" in JSON str\\\"ing\""; - String arrayPart3 = " }\n\n , "; - String arrayPart4 = "{\"testcase\" : \"Streaming string me"; - String arrayPart5 = "ssage\"} ]"; - - // Test array - ch.writeInbound(Unpooled.copiedBuffer(" " + arrayPart1, CharsetUtil.UTF_8)); - ch.writeInbound(Unpooled.copiedBuffer(arrayPart2, CharsetUtil.UTF_8)); - ch.writeInbound(Unpooled.copiedBuffer(arrayPart3, CharsetUtil.UTF_8)); - ch.writeInbound(Unpooled.copiedBuffer(arrayPart4, CharsetUtil.UTF_8)); - ch.writeInbound(Unpooled.copiedBuffer(arrayPart5 + " ", CharsetUtil.UTF_8)); - - ByteBuf res = ch.readInbound(); - assertEquals(arrayPart1 + arrayPart2 + arrayPart3 + arrayPart4 + arrayPart5, res.toString(CharsetUtil.UTF_8)); - res.release(); - - assertFalse(ch.finish()); - } - - @Test - public void testStreamJsonArrayOverMultipleWrites1() { - String[] array = new String[] { - " [{\"test", - "case\" : \"\\\"}]Escaped dou\\\"ble quotes \\\" in JSON str\\\"ing\"", - " }\n\n , ", - "{\"testcase\" : \"Streaming string me", - "ssage\"} ] " - }; - String[] result = new String[] { - "{\"testcase\" : \"\\\"}]Escaped dou\\\"ble quotes \\\" in JSON str\\\"ing\" }", - "{\"testcase\" : \"Streaming string message\"}" - }; - doTestStreamJsonArrayOverMultipleWrites(2, array, result); - } - - @Test - public void testStreamJsonArrayOverMultipleWrites2() { - String[] array = new String[] { - " [{\"test", - "case\" : \"\\\"}]Escaped dou\\\"ble quotes \\\" in JSON str\\\"ing\"", - " }\n\n , {\"test", - "case\" : \"Streaming string me", - "ssage\"} ] " - }; - String[] result = new String[] { - "{\"testcase\" : \"\\\"}]Escaped dou\\\"ble quotes \\\" in JSON str\\\"ing\" }", - "{\"testcase\" : \"Streaming string message\"}" - }; - doTestStreamJsonArrayOverMultipleWrites(2, array, result); - } - - @Test - public void testStreamJsonArrayOverMultipleWrites3() { - String[] array = new String[] { - " [{\"test", - "case\" : \"\\\"}]Escaped dou\\\"ble quotes \\\" in JSON str\\\"ing\"", - " }\n\n , [{\"test", - "case\" : \"Streaming string me", - "ssage\"}] ] " - }; - String[] result = new String[] { - "{\"testcase\" : \"\\\"}]Escaped dou\\\"ble quotes \\\" in JSON str\\\"ing\" }", - "[{\"testcase\" : \"Streaming string message\"}]" - }; - doTestStreamJsonArrayOverMultipleWrites(2, array, result); - } - - private static void doTestStreamJsonArrayOverMultipleWrites(int indexDataAvailable, - String[] array, String[] result) { - EmbeddedChannel ch = new EmbeddedChannel(new JsonObjectDecoder(true)); - - boolean dataAvailable = false; - for (String part : array) { - dataAvailable = ch.writeInbound(Unpooled.copiedBuffer(part, CharsetUtil.UTF_8)); - if (indexDataAvailable > 0) { - assertFalse(dataAvailable); - } else { - assertTrue(dataAvailable); - } - indexDataAvailable--; - } - - for (String part : result) { - ByteBuf res = ch.readInbound(); - assertEquals(part, res.toString(CharsetUtil.UTF_8)); - res.release(); - } - - assertFalse(ch.finish()); - } - - @Test - public void testSingleByteStream() { - EmbeddedChannel ch = new EmbeddedChannel(new JsonObjectDecoder()); - - String json = "{\"foo\" : {\"bar\" : [{},{}]}}"; - for (byte c : json.getBytes(CharsetUtil.UTF_8)) { - ch.writeInbound(Unpooled.copiedBuffer(new byte[] {c})); - } - - ByteBuf res = ch.readInbound(); - assertEquals(json, res.toString(CharsetUtil.UTF_8)); - res.release(); - - assertFalse(ch.finish()); - } - - @Test - public void testBackslashInString1() { - EmbeddedChannel ch = new EmbeddedChannel(new JsonObjectDecoder()); - // {"foo" : "bar\""} - String json = "{\"foo\" : \"bar\\\"\"}"; - System.out.println(json); - ch.writeInbound(Unpooled.copiedBuffer(json, CharsetUtil.UTF_8)); - - ByteBuf res = ch.readInbound(); - assertEquals(json, res.toString(CharsetUtil.UTF_8)); - res.release(); - - assertFalse(ch.finish()); - } - - @Test - public void testBackslashInString2() { - EmbeddedChannel ch = new EmbeddedChannel(new JsonObjectDecoder()); - // {"foo" : "bar\\"} - String json = "{\"foo\" : \"bar\\\\\"}"; - System.out.println(json); - ch.writeInbound(Unpooled.copiedBuffer(json, CharsetUtil.UTF_8)); - - ByteBuf res = ch.readInbound(); - assertEquals(json, res.toString(CharsetUtil.UTF_8)); - res.release(); - - assertFalse(ch.finish()); - } - - @Test - public void testBackslashInString3() { - EmbeddedChannel ch = new EmbeddedChannel(new JsonObjectDecoder()); - // {"foo" : "bar\\\""} - String json = "{\"foo\" : \"bar\\\\\\\"\"}"; - System.out.println(json); - ch.writeInbound(Unpooled.copiedBuffer(json, CharsetUtil.UTF_8)); - - ByteBuf res = ch.readInbound(); - assertEquals(json, res.toString(CharsetUtil.UTF_8)); - res.release(); - - assertFalse(ch.finish()); - } - - @Test - public void testMultipleJsonObjectsInOneWrite() { - EmbeddedChannel ch = new EmbeddedChannel(new JsonObjectDecoder()); - - String object1 = "{\"key\" : \"value1\"}", - object2 = "{\"key\" : \"value2\"}", - object3 = "{\"key\" : \"value3\"}"; - - ch.writeInbound(Unpooled.copiedBuffer(object1 + object2 + object3, CharsetUtil.UTF_8)); - - ByteBuf res = ch.readInbound(); - assertEquals(object1, res.toString(CharsetUtil.UTF_8)); - res.release(); - res = ch.readInbound(); - assertEquals(object2, res.toString(CharsetUtil.UTF_8)); - res.release(); - res = ch.readInbound(); - assertEquals(object3, res.toString(CharsetUtil.UTF_8)); - res.release(); - - assertFalse(ch.finish()); - } - - @Test - public void testNonJsonContent1() { - EmbeddedChannel ch = new EmbeddedChannel(new JsonObjectDecoder()); - try { - assertThrows(CorruptedFrameException.class, - () -> ch.writeInbound(Unpooled.copiedBuffer(" b [1,2,3]", CharsetUtil.UTF_8))); - } finally { - assertFalse(ch.finish()); - } - } - - @Test - public void testNonJsonContent2() { - EmbeddedChannel ch = new EmbeddedChannel(new JsonObjectDecoder()); - ch.writeInbound(Unpooled.copiedBuffer(" [1,2,3] ", CharsetUtil.UTF_8)); - - ByteBuf res = ch.readInbound(); - assertEquals("[1,2,3]", res.toString(CharsetUtil.UTF_8)); - res.release(); - - try { - assertThrows(CorruptedFrameException.class, - () -> ch.writeInbound(Unpooled.copiedBuffer(" a {\"key\" : 10}", CharsetUtil.UTF_8))); - } finally { - assertFalse(ch.finish()); - } - } - - @Test - public void testMaxObjectLength() { - EmbeddedChannel ch = new EmbeddedChannel(new JsonObjectDecoder(6)); - try { - assertThrows(TooLongFrameException.class, - () -> ch.writeInbound(Unpooled.copiedBuffer("[2,4,5]", CharsetUtil.UTF_8))); - } finally { - assertFalse(ch.finish()); - } - } - - @Test - public void testOneJsonObjectPerWrite() { - EmbeddedChannel ch = new EmbeddedChannel(new JsonObjectDecoder()); - - String object1 = "{\"key\" : \"value1\"}", - object2 = "{\"key\" : \"value2\"}", - object3 = "{\"key\" : \"value3\"}"; - - ch.writeInbound(Unpooled.copiedBuffer(object1, CharsetUtil.UTF_8)); - ch.writeInbound(Unpooled.copiedBuffer(object2, CharsetUtil.UTF_8)); - ch.writeInbound(Unpooled.copiedBuffer(object3, CharsetUtil.UTF_8)); - - ByteBuf res = ch.readInbound(); - assertEquals(object1, res.toString(CharsetUtil.UTF_8)); - res.release(); - res = ch.readInbound(); - assertEquals(object2, res.toString(CharsetUtil.UTF_8)); - res.release(); - res = ch.readInbound(); - assertEquals(object3, res.toString(CharsetUtil.UTF_8)); - res.release(); - - assertFalse(ch.finish()); - } - - @Test - public void testSpecialJsonCharsInString() { - EmbeddedChannel ch = new EmbeddedChannel(new JsonObjectDecoder()); - - String object = "{ \"key\" : \"[]{}}\\\"}}'}\"}"; - ch.writeInbound(Unpooled.copiedBuffer(object, CharsetUtil.UTF_8)); - - ByteBuf res = ch.readInbound(); - assertEquals(object, res.toString(CharsetUtil.UTF_8)); - res.release(); - - assertFalse(ch.finish()); - } - - @Test - public void testStreamArrayElementsSimple() { - EmbeddedChannel ch = new EmbeddedChannel(new JsonObjectDecoder(Integer.MAX_VALUE, true)); - - String array = "[ 12, \"bla\" , 13.4 \t ,{\"key0\" : [1,2], \"key1\" : 12, \"key2\" : {}} , " + - "true, false, null, [\"bla\", {}, [1,2,3]] ]"; - String object = "{\"bla\" : \"blub\"}"; - ch.writeInbound(Unpooled.copiedBuffer(array, CharsetUtil.UTF_8)); - ch.writeInbound(Unpooled.copiedBuffer(object, CharsetUtil.UTF_8)); - - ByteBuf res = ch.readInbound(); - assertEquals("12", res.toString(CharsetUtil.UTF_8)); - res.release(); - res = ch.readInbound(); - assertEquals("\"bla\"", res.toString(CharsetUtil.UTF_8)); - res.release(); - res = ch.readInbound(); - assertEquals("13.4", res.toString(CharsetUtil.UTF_8)); - res.release(); - res = ch.readInbound(); - assertEquals("{\"key0\" : [1,2], \"key1\" : 12, \"key2\" : {}}", res.toString(CharsetUtil.UTF_8)); - res.release(); - res = ch.readInbound(); - assertEquals("true", res.toString(CharsetUtil.UTF_8)); - res.release(); - res = ch.readInbound(); - assertEquals("false", res.toString(CharsetUtil.UTF_8)); - res.release(); - res = ch.readInbound(); - assertEquals("null", res.toString(CharsetUtil.UTF_8)); - res.release(); - res = ch.readInbound(); - assertEquals("[\"bla\", {}, [1,2,3]]", res.toString(CharsetUtil.UTF_8)); - res.release(); - res = ch.readInbound(); - assertEquals(object, res.toString(CharsetUtil.UTF_8)); - res.release(); - - assertFalse(ch.finish()); - } - - @Test - public void testCorruptedFrameException() { - final String part1 = "{\"a\":{\"b\":{\"c\":{ \"d\":\"27301\", \"med\":\"d\", \"path\":\"27310\"} }," + - " \"status\":\"OK\" } }{\""; - final String part2 = "a\":{\"b\":{\"c\":{\"ory\":[{\"competi\":[{\"event\":[{" + "\"externalI\":{\"external\"" + - ":[{\"id\":\"O\"} ]"; - - EmbeddedChannel ch = new EmbeddedChannel(new JsonObjectDecoder()); - - ByteBuf res; - - ch.writeInbound(Unpooled.copiedBuffer(part1, CharsetUtil.UTF_8)); - res = ch.readInbound(); - assertEquals("{\"a\":{\"b\":{\"c\":{ \"d\":\"27301\", \"med\":\"d\", \"path\":\"27310\"} }, " + - "\"status\":\"OK\" } }", res.toString(CharsetUtil.UTF_8)); - res.release(); - - ch.writeInbound(Unpooled.copiedBuffer(part2, CharsetUtil.UTF_8)); - res = ch.readInbound(); - - assertNull(res); - - ch.writeInbound(Unpooled.copiedBuffer("}}]}]}]}}}}", CharsetUtil.UTF_8)); - res = ch.readInbound(); - - assertEquals("{\"a\":{\"b\":{\"c\":{\"ory\":[{\"competi\":[{\"event\":[{" + "\"externalI\":{" + - "\"external\":[{\"id\":\"O\"} ]}}]}]}]}}}}", res.toString(CharsetUtil.UTF_8)); - res.release(); - - assertFalse(ch.finish()); - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/marshalling/AbstractCompatibleMarshallingDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/marshalling/AbstractCompatibleMarshallingDecoderTest.java deleted file mode 100644 index d019bdd076..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/marshalling/AbstractCompatibleMarshallingDecoderTest.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.marshalling; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandler; -import io.netty.channel.embedded.EmbeddedChannel; -import org.jboss.marshalling.Marshaller; -import org.jboss.marshalling.MarshallerFactory; -import org.jboss.marshalling.Marshalling; -import org.jboss.marshalling.MarshallingConfiguration; -import org.junit.jupiter.api.Test; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public abstract class AbstractCompatibleMarshallingDecoderTest extends AbstractMarshallingTest { - @SuppressWarnings("RedundantStringConstructorCall") - private final String testObject = new String("test"); - - @Test - public void testSimpleUnmarshalling() throws IOException { - MarshallerFactory marshallerFactory = createMarshallerFactory(); - MarshallingConfiguration configuration = createMarshallingConfig(); - - EmbeddedChannel ch = new EmbeddedChannel(createDecoder(Integer.MAX_VALUE)); - - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - Marshaller marshaller = marshallerFactory.createMarshaller(configuration); - marshaller.start(Marshalling.createByteOutput(bout)); - marshaller.writeObject(testObject); - marshaller.finish(); - marshaller.close(); - - byte[] testBytes = bout.toByteArray(); - - ch.writeInbound(input(testBytes)); - assertTrue(ch.finish()); - - String unmarshalled = ch.readInbound(); - - assertEquals(testObject, unmarshalled); - - assertNull(ch.readInbound()); - } - - protected ByteBuf input(byte[] input) { - return Unpooled.wrappedBuffer(input); - } - - @Test - public void testFragmentedUnmarshalling() throws IOException { - MarshallerFactory marshallerFactory = createMarshallerFactory(); - MarshallingConfiguration configuration = createMarshallingConfig(); - - EmbeddedChannel ch = new EmbeddedChannel(createDecoder(Integer.MAX_VALUE)); - - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - Marshaller marshaller = marshallerFactory.createMarshaller(configuration); - marshaller.start(Marshalling.createByteOutput(bout)); - marshaller.writeObject(testObject); - marshaller.finish(); - marshaller.close(); - - byte[] testBytes = bout.toByteArray(); - - ByteBuf buffer = input(testBytes); - ByteBuf slice = buffer.readRetainedSlice(2); - - ch.writeInbound(slice); - ch.writeInbound(buffer); - assertTrue(ch.finish()); - - String unmarshalled = ch.readInbound(); - - assertEquals(testObject, unmarshalled); - - assertNull(ch.readInbound()); - } - - @Test - public void testTooBigObject() throws IOException { - MarshallerFactory marshallerFactory = createMarshallerFactory(); - MarshallingConfiguration configuration = createMarshallingConfig(); - - ChannelHandler mDecoder = createDecoder(4); - EmbeddedChannel ch = new EmbeddedChannel(mDecoder); - - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - Marshaller marshaller = marshallerFactory.createMarshaller(configuration); - marshaller.start(Marshalling.createByteOutput(bout)); - marshaller.writeObject(testObject); - marshaller.finish(); - marshaller.close(); - - byte[] testBytes = bout.toByteArray(); - onTooBigFrame(ch, input(testBytes)); - } - - protected void onTooBigFrame(EmbeddedChannel ch, ByteBuf input) { - ch.writeInbound(input); - assertFalse(ch.isActive()); - } - - protected ChannelHandler createDecoder(int maxObjectSize) { - return new CompatibleMarshallingDecoder(createProvider(createMarshallerFactory(), - createMarshallingConfig()), maxObjectSize); - } - - protected UnmarshallerProvider createProvider(MarshallerFactory factory, MarshallingConfiguration config) { - return new DefaultUnmarshallerProvider(factory, config); - } - - protected abstract MarshallerFactory createMarshallerFactory(); - protected abstract MarshallingConfiguration createMarshallingConfig(); - -} diff --git a/codec/src/test/java/io/netty/handler/codec/marshalling/AbstractCompatibleMarshallingEncoderTest.java b/codec/src/test/java/io/netty/handler/codec/marshalling/AbstractCompatibleMarshallingEncoderTest.java deleted file mode 100644 index e2686b8cb6..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/marshalling/AbstractCompatibleMarshallingEncoderTest.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.marshalling; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandler; -import io.netty.channel.embedded.EmbeddedChannel; -import org.jboss.marshalling.MarshallerFactory; -import org.jboss.marshalling.Marshalling; -import org.jboss.marshalling.MarshallingConfiguration; -import org.jboss.marshalling.Unmarshaller; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public abstract class AbstractCompatibleMarshallingEncoderTest extends AbstractMarshallingTest { - - @Test - public void testMarshalling() throws Exception { - @SuppressWarnings("RedundantStringConstructorCall") - String testObject = new String("test"); - - final MarshallerFactory marshallerFactory = createMarshallerFactory(); - final MarshallingConfiguration configuration = createMarshallingConfig(); - - EmbeddedChannel ch = new EmbeddedChannel(createEncoder()); - - ch.writeOutbound(testObject); - assertTrue(ch.finish()); - - ByteBuf buffer = ch.readOutbound(); - - Unmarshaller unmarshaller = marshallerFactory.createUnmarshaller(configuration); - unmarshaller.start(Marshalling.createByteInput(truncate(buffer).nioBuffer())); - String read = (String) unmarshaller.readObject(); - assertEquals(testObject, read); - - assertEquals(-1, unmarshaller.read()); - - assertNull(ch.readOutbound()); - - unmarshaller.finish(); - unmarshaller.close(); - buffer.release(); - } - - protected ByteBuf truncate(ByteBuf buf) { - return buf; - } - - protected ChannelHandler createEncoder() { - return new CompatibleMarshallingEncoder(createProvider()); - } - - protected MarshallerProvider createProvider() { - return new DefaultMarshallerProvider(createMarshallerFactory(), createMarshallingConfig()); - } - - protected abstract MarshallerFactory createMarshallerFactory(); - - protected abstract MarshallingConfiguration createMarshallingConfig(); - -} diff --git a/codec/src/test/java/io/netty/handler/codec/marshalling/AbstractMarshallingTest.java b/codec/src/test/java/io/netty/handler/codec/marshalling/AbstractMarshallingTest.java deleted file mode 100644 index 5be0d78cab..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/marshalling/AbstractMarshallingTest.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.marshalling; - -import io.netty.util.internal.PlatformDependent; -import org.jboss.marshalling.Marshalling; -import org.junit.jupiter.api.BeforeAll; - -import static org.junit.jupiter.api.Assumptions.assumeTrue; - -public abstract class AbstractMarshallingTest { - - static final String SERIAL_FACTORY = "serial"; - static final String RIVER_FACTORY = "river"; - - @BeforeAll - public static void checkSupported() throws Throwable { - Throwable error = null; - try { - Marshalling.getProvidedMarshallerFactory(SERIAL_FACTORY); - } catch (Throwable cause) { - // This may fail on Java 9+ depending on which command-line arguments are used when building. - if (PlatformDependent.javaVersion() < 9) { - throw cause; - } - error = cause; - } - assumeTrue(error == null, error + " was not null"); - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/marshalling/RiverCompatibleMarshallingDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/marshalling/RiverCompatibleMarshallingDecoderTest.java deleted file mode 100644 index f5941c136c..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/marshalling/RiverCompatibleMarshallingDecoderTest.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.marshalling; - -import org.jboss.marshalling.MarshallerFactory; -import org.jboss.marshalling.Marshalling; -import org.jboss.marshalling.MarshallingConfiguration; - -public class RiverCompatibleMarshallingDecoderTest extends AbstractCompatibleMarshallingDecoderTest { - - @Override - protected MarshallerFactory createMarshallerFactory() { - return Marshalling.getProvidedMarshallerFactory(RIVER_FACTORY); - } - - @Override - protected MarshallingConfiguration createMarshallingConfig() { - // Create a configuration - final MarshallingConfiguration configuration = new MarshallingConfiguration(); - configuration.setVersion(3); - return configuration; - } - -} diff --git a/codec/src/test/java/io/netty/handler/codec/marshalling/RiverCompatibleMarshallingEncoderTest.java b/codec/src/test/java/io/netty/handler/codec/marshalling/RiverCompatibleMarshallingEncoderTest.java deleted file mode 100644 index e3c230cc45..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/marshalling/RiverCompatibleMarshallingEncoderTest.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.marshalling; - -import org.jboss.marshalling.MarshallerFactory; -import org.jboss.marshalling.Marshalling; -import org.jboss.marshalling.MarshallingConfiguration; - -public class RiverCompatibleMarshallingEncoderTest extends AbstractCompatibleMarshallingEncoderTest { - - @Override - protected MarshallerFactory createMarshallerFactory() { - return Marshalling.getProvidedMarshallerFactory(RIVER_FACTORY); - } - - @Override - protected MarshallingConfiguration createMarshallingConfig() { - // Create a configuration - final MarshallingConfiguration configuration = new MarshallingConfiguration(); - configuration.setVersion(3); - return configuration; - } - -} diff --git a/codec/src/test/java/io/netty/handler/codec/marshalling/RiverContextBoundCompatibleMarshallingDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/marshalling/RiverContextBoundCompatibleMarshallingDecoderTest.java deleted file mode 100644 index a0c37978ac..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/marshalling/RiverContextBoundCompatibleMarshallingDecoderTest.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.marshalling; - -import org.jboss.marshalling.MarshallerFactory; -import org.jboss.marshalling.MarshallingConfiguration; - -public class RiverContextBoundCompatibleMarshallingDecoderTest extends RiverCompatibleMarshallingDecoderTest { - - @Override - protected UnmarshallerProvider createProvider(MarshallerFactory factory, MarshallingConfiguration config) { - return new ContextBoundUnmarshallerProvider(factory, config); - } - -} diff --git a/codec/src/test/java/io/netty/handler/codec/marshalling/RiverContextBoundMarshallingDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/marshalling/RiverContextBoundMarshallingDecoderTest.java deleted file mode 100644 index 07891733c4..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/marshalling/RiverContextBoundMarshallingDecoderTest.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.marshalling; - -import org.jboss.marshalling.MarshallerFactory; -import org.jboss.marshalling.MarshallingConfiguration; - -public class RiverContextBoundMarshallingDecoderTest extends RiverMarshallingDecoderTest { - - @Override - protected UnmarshallerProvider createProvider(MarshallerFactory factory, MarshallingConfiguration config) { - return new ContextBoundUnmarshallerProvider(factory, config); - } - -} diff --git a/codec/src/test/java/io/netty/handler/codec/marshalling/RiverMarshallingDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/marshalling/RiverMarshallingDecoderTest.java deleted file mode 100644 index 3b488f73c5..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/marshalling/RiverMarshallingDecoderTest.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.marshalling; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandler; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.CodecException; -import io.netty.handler.codec.TooLongFrameException; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; - -public class RiverMarshallingDecoderTest extends RiverCompatibleMarshallingDecoderTest { - - @Override - protected ByteBuf input(byte[] input) { - ByteBuf length = Unpooled.buffer(4); - length.writeInt(input.length); - return Unpooled.wrappedBuffer(length, Unpooled.wrappedBuffer(input)); - } - - @Override - protected ChannelHandler createDecoder(int maxObjectSize) { - return new MarshallingDecoder(createProvider(createMarshallerFactory(), - createMarshallingConfig()), maxObjectSize); - } - - @Override - protected void onTooBigFrame(EmbeddedChannel ch, ByteBuf input) { - try { - ch.writeInbound(input); - fail(); - } catch (CodecException e) { - assertEquals(TooLongFrameException.class, e.getClass()); - } - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/marshalling/RiverMarshallingEncoderTest.java b/codec/src/test/java/io/netty/handler/codec/marshalling/RiverMarshallingEncoderTest.java deleted file mode 100644 index e7a6c40896..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/marshalling/RiverMarshallingEncoderTest.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.marshalling; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandler; - -public class RiverMarshallingEncoderTest extends RiverCompatibleMarshallingEncoderTest { - - @Override - protected ByteBuf truncate(ByteBuf buf) { - buf.readInt(); - return buf; - } - - @Override - protected ChannelHandler createEncoder() { - return new MarshallingEncoder(createProvider()); - } - -} diff --git a/codec/src/test/java/io/netty/handler/codec/marshalling/RiverThreadLocalCompatibleMarshallingDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/marshalling/RiverThreadLocalCompatibleMarshallingDecoderTest.java deleted file mode 100644 index 8e00553fe4..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/marshalling/RiverThreadLocalCompatibleMarshallingDecoderTest.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.marshalling; - -import org.jboss.marshalling.MarshallerFactory; -import org.jboss.marshalling.MarshallingConfiguration; - -public class RiverThreadLocalCompatibleMarshallingDecoderTest extends RiverCompatibleMarshallingDecoderTest { - - @Override - protected UnmarshallerProvider createProvider(MarshallerFactory factory, MarshallingConfiguration config) { - return new ThreadLocalUnmarshallerProvider(factory, config); - } - -} diff --git a/codec/src/test/java/io/netty/handler/codec/marshalling/RiverThreadLocalCompatibleMarshallingEncoderTest.java b/codec/src/test/java/io/netty/handler/codec/marshalling/RiverThreadLocalCompatibleMarshallingEncoderTest.java deleted file mode 100644 index 8b1515c776..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/marshalling/RiverThreadLocalCompatibleMarshallingEncoderTest.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.marshalling; - -public class RiverThreadLocalCompatibleMarshallingEncoderTest extends RiverCompatibleMarshallingEncoderTest { - - @Override - protected MarshallerProvider createProvider() { - return new ThreadLocalMarshallerProvider(createMarshallerFactory(), createMarshallingConfig()); - } - -} diff --git a/codec/src/test/java/io/netty/handler/codec/marshalling/RiverThreadLocalMarshallingDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/marshalling/RiverThreadLocalMarshallingDecoderTest.java deleted file mode 100644 index 8afeaafd8d..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/marshalling/RiverThreadLocalMarshallingDecoderTest.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.marshalling; - -import org.jboss.marshalling.MarshallerFactory; -import org.jboss.marshalling.MarshallingConfiguration; - -public class RiverThreadLocalMarshallingDecoderTest extends RiverMarshallingDecoderTest { - - @Override - protected UnmarshallerProvider createProvider(MarshallerFactory factory, MarshallingConfiguration config) { - return new ThreadLocalUnmarshallerProvider(factory, config); - } - -} diff --git a/codec/src/test/java/io/netty/handler/codec/marshalling/RiverThreadLocalMarshallingEncoderTest.java b/codec/src/test/java/io/netty/handler/codec/marshalling/RiverThreadLocalMarshallingEncoderTest.java deleted file mode 100644 index e4f2315883..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/marshalling/RiverThreadLocalMarshallingEncoderTest.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.marshalling; - -public class RiverThreadLocalMarshallingEncoderTest extends RiverMarshallingEncoderTest { - - @Override - protected MarshallerProvider createProvider() { - return new ThreadLocalMarshallerProvider(createMarshallerFactory(), createMarshallingConfig()); - } - -} diff --git a/codec/src/test/java/io/netty/handler/codec/marshalling/SerialCompatibleMarshallingDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/marshalling/SerialCompatibleMarshallingDecoderTest.java deleted file mode 100644 index 4f723e44a8..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/marshalling/SerialCompatibleMarshallingDecoderTest.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.marshalling; - -import org.jboss.marshalling.MarshallerFactory; -import org.jboss.marshalling.Marshalling; -import org.jboss.marshalling.MarshallingConfiguration; - -public class SerialCompatibleMarshallingDecoderTest extends AbstractCompatibleMarshallingDecoderTest { - - @Override - protected MarshallerFactory createMarshallerFactory() { - return Marshalling.getProvidedMarshallerFactory(SERIAL_FACTORY); - } - - @Override - protected MarshallingConfiguration createMarshallingConfig() { - // Create a configuration - final MarshallingConfiguration configuration = new MarshallingConfiguration(); - configuration.setVersion(5); - return configuration; - } - -} diff --git a/codec/src/test/java/io/netty/handler/codec/marshalling/SerialCompatibleMarshallingEncoderTest.java b/codec/src/test/java/io/netty/handler/codec/marshalling/SerialCompatibleMarshallingEncoderTest.java deleted file mode 100644 index 54d2899421..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/marshalling/SerialCompatibleMarshallingEncoderTest.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.marshalling; - -import org.jboss.marshalling.MarshallerFactory; -import org.jboss.marshalling.Marshalling; -import org.jboss.marshalling.MarshallingConfiguration; - -public class SerialCompatibleMarshallingEncoderTest extends AbstractCompatibleMarshallingEncoderTest { - - @Override - protected MarshallerFactory createMarshallerFactory() { - return Marshalling.getProvidedMarshallerFactory(SERIAL_FACTORY); - } - - @Override - protected MarshallingConfiguration createMarshallingConfig() { - // Create a configuration - final MarshallingConfiguration configuration = new MarshallingConfiguration(); - configuration.setVersion(5); - return configuration; - } - -} diff --git a/codec/src/test/java/io/netty/handler/codec/marshalling/SerialContextBoundCompatibleMarshallingDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/marshalling/SerialContextBoundCompatibleMarshallingDecoderTest.java deleted file mode 100644 index 032834c49f..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/marshalling/SerialContextBoundCompatibleMarshallingDecoderTest.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.marshalling; - -import org.jboss.marshalling.MarshallerFactory; -import org.jboss.marshalling.MarshallingConfiguration; - -public class SerialContextBoundCompatibleMarshallingDecoderTest extends SerialCompatibleMarshallingDecoderTest { - - @Override - protected UnmarshallerProvider createProvider(MarshallerFactory factory, MarshallingConfiguration config) { - return new ContextBoundUnmarshallerProvider(factory, config); - } - -} diff --git a/codec/src/test/java/io/netty/handler/codec/marshalling/SerialContextBoundMarshallingDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/marshalling/SerialContextBoundMarshallingDecoderTest.java deleted file mode 100644 index c5397d38ac..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/marshalling/SerialContextBoundMarshallingDecoderTest.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.marshalling; - -import org.jboss.marshalling.MarshallerFactory; -import org.jboss.marshalling.MarshallingConfiguration; - -public class SerialContextBoundMarshallingDecoderTest extends SerialMarshallingDecoderTest { - - @Override - protected UnmarshallerProvider createProvider(MarshallerFactory factory, MarshallingConfiguration config) { - return new ContextBoundUnmarshallerProvider(factory, config); - } - -} diff --git a/codec/src/test/java/io/netty/handler/codec/marshalling/SerialMarshallingDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/marshalling/SerialMarshallingDecoderTest.java deleted file mode 100644 index 5da6ecb74a..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/marshalling/SerialMarshallingDecoderTest.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.marshalling; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandler; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.CodecException; -import io.netty.handler.codec.TooLongFrameException; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; - -public class SerialMarshallingDecoderTest extends SerialCompatibleMarshallingDecoderTest { - - @Override - protected ByteBuf input(byte[] input) { - ByteBuf length = Unpooled.buffer(4); - length.writeInt(input.length); - return Unpooled.wrappedBuffer(length, Unpooled.wrappedBuffer(input)); - } - - @Override - protected ChannelHandler createDecoder(int maxObjectSize) { - return new MarshallingDecoder(createProvider(createMarshallerFactory(), - createMarshallingConfig()), maxObjectSize); - } - - @Override - protected void onTooBigFrame(EmbeddedChannel ch, ByteBuf input) { - try { - ch.writeInbound(input); - fail(); - } catch (CodecException e) { - assertEquals(TooLongFrameException.class, e.getClass()); - } - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/marshalling/SerialMarshallingEncoderTest.java b/codec/src/test/java/io/netty/handler/codec/marshalling/SerialMarshallingEncoderTest.java deleted file mode 100644 index 2a9fe92325..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/marshalling/SerialMarshallingEncoderTest.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.marshalling; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandler; - -public class SerialMarshallingEncoderTest extends SerialCompatibleMarshallingEncoderTest { - - @Override - protected ByteBuf truncate(ByteBuf buf) { - buf.readInt(); - return buf; - } - - @Override - protected ChannelHandler createEncoder() { - return new MarshallingEncoder(createProvider()); - } - -} diff --git a/codec/src/test/java/io/netty/handler/codec/marshalling/SerialThreadLocalCompatibleMarshallingDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/marshalling/SerialThreadLocalCompatibleMarshallingDecoderTest.java deleted file mode 100644 index 816f130423..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/marshalling/SerialThreadLocalCompatibleMarshallingDecoderTest.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.marshalling; - -import org.jboss.marshalling.MarshallerFactory; -import org.jboss.marshalling.MarshallingConfiguration; - -public class SerialThreadLocalCompatibleMarshallingDecoderTest extends SerialCompatibleMarshallingDecoderTest { - - @Override - protected UnmarshallerProvider createProvider(MarshallerFactory factory, MarshallingConfiguration config) { - return new ThreadLocalUnmarshallerProvider(factory, config); - } - -} diff --git a/codec/src/test/java/io/netty/handler/codec/marshalling/SerialThreadLocalCompatibleMarshallingEncoderTest.java b/codec/src/test/java/io/netty/handler/codec/marshalling/SerialThreadLocalCompatibleMarshallingEncoderTest.java deleted file mode 100644 index 8e23ba61c9..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/marshalling/SerialThreadLocalCompatibleMarshallingEncoderTest.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.marshalling; - -public class SerialThreadLocalCompatibleMarshallingEncoderTest extends SerialCompatibleMarshallingEncoderTest { - - @Override - protected MarshallerProvider createProvider() { - return new ThreadLocalMarshallerProvider(createMarshallerFactory(), createMarshallingConfig()); - } - -} diff --git a/codec/src/test/java/io/netty/handler/codec/marshalling/SerialThreadLocalMarshallingDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/marshalling/SerialThreadLocalMarshallingDecoderTest.java deleted file mode 100644 index b9fe97bf4b..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/marshalling/SerialThreadLocalMarshallingDecoderTest.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.marshalling; - -import org.jboss.marshalling.MarshallerFactory; -import org.jboss.marshalling.MarshallingConfiguration; - -public class SerialThreadLocalMarshallingDecoderTest extends SerialMarshallingDecoderTest { - - @Override - protected UnmarshallerProvider createProvider(MarshallerFactory factory, MarshallingConfiguration config) { - return new ThreadLocalUnmarshallerProvider(factory, config); - } - -} diff --git a/codec/src/test/java/io/netty/handler/codec/marshalling/SerialThreadLocalMarshallingEncoderTest.java b/codec/src/test/java/io/netty/handler/codec/marshalling/SerialThreadLocalMarshallingEncoderTest.java deleted file mode 100644 index 324b470fac..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/marshalling/SerialThreadLocalMarshallingEncoderTest.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.marshalling; - -public class SerialThreadLocalMarshallingEncoderTest extends SerialMarshallingEncoderTest { - - @Override - protected MarshallerProvider createProvider() { - return new ThreadLocalMarshallerProvider(createMarshallerFactory(), createMarshallingConfig()); - } - -} diff --git a/codec/src/test/java/io/netty/handler/codec/protobuf/ProtobufVarint32FrameDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/protobuf/ProtobufVarint32FrameDecoderTest.java deleted file mode 100644 index 7b6a3c4346..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/protobuf/ProtobufVarint32FrameDecoderTest.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.protobuf; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.embedded.EmbeddedChannel; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static io.netty.buffer.Unpooled.*; -import static org.hamcrest.core.Is.*; -import static org.hamcrest.core.IsNull.*; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class ProtobufVarint32FrameDecoderTest { - - private EmbeddedChannel ch; - - @BeforeEach - public void setUp() { - ch = new EmbeddedChannel(new ProtobufVarint32FrameDecoder()); - } - - @Test - public void testTinyDecode() { - byte[] b = { 4, 1, 1, 1, 1 }; - assertFalse(ch.writeInbound(wrappedBuffer(b, 0, 1))); - assertThat(ch.readInbound(), is(nullValue())); - assertFalse(ch.writeInbound(wrappedBuffer(b, 1, 2))); - assertThat(ch.readInbound(), is(nullValue())); - assertTrue(ch.writeInbound(wrappedBuffer(b, 3, b.length - 3))); - - ByteBuf expected = wrappedBuffer(new byte[] { 1, 1, 1, 1 }); - ByteBuf actual = ch.readInbound(); - - assertThat(expected, is(actual)); - assertFalse(ch.finish()); - - expected.release(); - actual.release(); - } - - @Test - public void testRegularDecode() { - byte[] b = new byte[2048]; - for (int i = 2; i < 2048; i ++) { - b[i] = 1; - } - b[0] = -2; - b[1] = 15; - assertFalse(ch.writeInbound(wrappedBuffer(b, 0, 1))); - assertThat(ch.readInbound(), is(nullValue())); - assertFalse(ch.writeInbound(wrappedBuffer(b, 1, 127))); - assertThat(ch.readInbound(), is(nullValue())); - assertFalse(ch.writeInbound(wrappedBuffer(b, 127, 600))); - assertThat(ch.readInbound(), is(nullValue())); - assertTrue(ch.writeInbound(wrappedBuffer(b, 727, b.length - 727))); - - ByteBuf expected = wrappedBuffer(b, 2, b.length - 2); - ByteBuf actual = ch.readInbound(); - assertThat(expected, is(actual)); - assertFalse(ch.finish()); - - expected.release(); - actual.release(); - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/protobuf/ProtobufVarint32LengthFieldPrependerTest.java b/codec/src/test/java/io/netty/handler/codec/protobuf/ProtobufVarint32LengthFieldPrependerTest.java deleted file mode 100644 index 4097e4dfc5..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/protobuf/ProtobufVarint32LengthFieldPrependerTest.java +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.protobuf; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.embedded.EmbeddedChannel; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static io.netty.buffer.Unpooled.*; -import static org.hamcrest.core.Is.*; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class ProtobufVarint32LengthFieldPrependerTest { - - private EmbeddedChannel ch; - - @BeforeEach - public void setUp() { - ch = new EmbeddedChannel(new ProtobufVarint32LengthFieldPrepender()); - } - - @Test - public void testSize1Varint() { - final int size = 1; - final int num = 10; - assertThat(ProtobufVarint32LengthFieldPrepender.computeRawVarint32Size(num), is(size)); - final byte[] buf = new byte[size + num]; - //0000 1010 - buf[0] = 0x0A; - for (int i = size; i < num + size; ++i) { - buf[i] = 1; - } - assertTrue(ch.writeOutbound(wrappedBuffer(buf, size, buf.length - size))); - - ByteBuf expected = wrappedBuffer(buf); - ByteBuf actual = ch.readOutbound(); - - assertThat(expected, is(actual)); - assertFalse(ch.finish()); - - expected.release(); - actual.release(); - } - - @Test - public void testSize2Varint() { - final int size = 2; - final int num = 266; - assertThat(ProtobufVarint32LengthFieldPrepender.computeRawVarint32Size(num), is(size)); - final byte[] buf = new byte[size + num]; - /* - 8 A 0 2 - 1000 1010 0000 0010 - 0000 1010 0000 0010 - 0000 0010 0000 1010 - 000 0010 000 1010 - - 0000 0001 0000 1010 - 0 1 0 A - 266 - */ - - buf[0] = (byte) (0x8A & 0xFF); - buf[1] = 0x02; - for (int i = size; i < num + size; ++i) { - buf[i] = 1; - } - assertTrue(ch.writeOutbound(wrappedBuffer(buf, size, buf.length - size))); - - ByteBuf expected = wrappedBuffer(buf); - ByteBuf actual = ch.readOutbound(); - - assertThat(actual, is(expected)); - assertFalse(ch.finish()); - - expected.release(); - actual.release(); - } - - @Test - public void testSize3Varint() { - final int size = 3; - final int num = 0x4000; - assertThat(ProtobufVarint32LengthFieldPrepender.computeRawVarint32Size(num), is(size)); - final byte[] buf = new byte[size + num]; - /* - 8 0 8 0 0 1 - 1000 0000 1000 0000 0000 0001 - 0000 0000 0000 0000 0000 0001 - 0000 0001 0000 0000 0000 0000 - 000 0001 000 0000 000 0000 - - 0 0000 0100 0000 0000 0000 - 0 0 4 0 0 0 - - */ - - buf[0] = (byte) (0x80 & 0xFF); - buf[1] = (byte) (0x80 & 0xFF); - buf[2] = 0x01; - for (int i = size; i < num + size; ++i) { - buf[i] = 1; - } - assertTrue(ch.writeOutbound(wrappedBuffer(buf, size, buf.length - size))); - - ByteBuf expected = wrappedBuffer(buf); - ByteBuf actual = ch.readOutbound(); - - assertThat(expected, is(actual)); - assertFalse(ch.finish()); - - expected.release(); - actual.release(); - } - - @Test - public void testSize4Varint() { - final int size = 4; - final int num = 0x200000; - assertThat(ProtobufVarint32LengthFieldPrepender.computeRawVarint32Size(num), is(size)); - final byte[] buf = new byte[size + num]; - /* - 8 0 8 0 8 0 0 1 - 1000 0000 1000 0000 1000 0000 0000 0001 - 0000 0000 0000 0000 0000 0000 0000 0001 - 0000 0001 0000 0000 0000 0000 0000 0000 - 000 0001 000 0000 000 0000 000 0000 - - 0000 0010 0000 0000 0000 0000 0000 - 0 2 0 0 0 0 0 - - */ - - buf[0] = (byte) (0x80 & 0xFF); - buf[1] = (byte) (0x80 & 0xFF); - buf[2] = (byte) (0x80 & 0xFF); - buf[3] = 0x01; - for (int i = size; i < num + size; ++i) { - buf[i] = 1; - } - assertTrue(ch.writeOutbound(wrappedBuffer(buf, size, buf.length - size))); - - ByteBuf expected = wrappedBuffer(buf); - ByteBuf actual = ch.readOutbound(); - - assertThat(actual, is(expected)); - assertFalse(ch.finish()); - - expected.release(); - actual.release(); - } - - @Test - public void testTinyEncode() { - byte[] b = { 4, 1, 1, 1, 1 }; - assertTrue(ch.writeOutbound(wrappedBuffer(b, 1, b.length - 1))); - - ByteBuf expected = wrappedBuffer(b); - ByteBuf actual = ch.readOutbound(); - - assertThat(actual, is(expected)); - assertFalse(ch.finish()); - - expected.release(); - actual.release(); - } - - @Test - public void testRegularDecode() { - byte[] b = new byte[2048]; - for (int i = 2; i < 2048; i ++) { - b[i] = 1; - } - b[0] = -2; - b[1] = 15; - assertTrue(ch.writeOutbound(wrappedBuffer(b, 2, b.length - 2))); - - ByteBuf expected = wrappedBuffer(b); - ByteBuf actual = ch.readOutbound(); - - assertThat(actual, is(expected)); - assertFalse(ch.finish()); - - expected.release(); - actual.release(); - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/serialization/CompactObjectSerializationTest.java b/codec/src/test/java/io/netty/handler/codec/serialization/CompactObjectSerializationTest.java deleted file mode 100644 index f39b7ea8e6..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/serialization/CompactObjectSerializationTest.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.serialization; - -import java.io.PipedInputStream; -import java.io.PipedOutputStream; -import java.util.List; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class CompactObjectSerializationTest { - - @Test - public void testInterfaceSerialization() throws Exception { - PipedOutputStream pipeOut = new PipedOutputStream(); - PipedInputStream pipeIn = new PipedInputStream(pipeOut); - CompactObjectOutputStream out = new CompactObjectOutputStream(pipeOut); - CompactObjectInputStream in = new CompactObjectInputStream(pipeIn, ClassResolvers.cacheDisabled(null)); - out.writeObject(List.class); - Assertions.assertSame(List.class, in.readObject()); - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/serialization/CompatibleObjectEncoderTest.java b/codec/src/test/java/io/netty/handler/codec/serialization/CompatibleObjectEncoderTest.java deleted file mode 100644 index 3b48836799..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/serialization/CompatibleObjectEncoderTest.java +++ /dev/null @@ -1,79 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.serialization; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufInputStream; -import io.netty.channel.embedded.EmbeddedChannel; -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.Serializable; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; - -public class CompatibleObjectEncoderTest { - @Test - public void testMultipleEncodeReferenceCount() throws IOException, ClassNotFoundException { - EmbeddedChannel channel = new EmbeddedChannel(new CompatibleObjectEncoder()); - testEncode(channel, new TestSerializable(6, 8)); - testEncode(channel, new TestSerializable(10, 5)); - testEncode(channel, new TestSerializable(1, 5)); - assertFalse(channel.finishAndReleaseAll()); - } - - private static void testEncode(EmbeddedChannel channel, TestSerializable original) - throws IOException, ClassNotFoundException { - channel.writeOutbound(original); - Object o = channel.readOutbound(); - ByteBuf buf = (ByteBuf) o; - ObjectInputStream ois = new ObjectInputStream(new ByteBufInputStream(buf)); - try { - assertEquals(original, ois.readObject()); - } finally { - buf.release(); - ois.close(); - } - } - - private static final class TestSerializable implements Serializable { - private static final long serialVersionUID = 2235771472534930360L; - - public final int x; - public final int y; - - TestSerializable(int x, int y) { - this.x = x; - this.y = y; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof TestSerializable)) { - return false; - } - TestSerializable rhs = (TestSerializable) o; - return x == rhs.x && y == rhs.y; - } - - @Override - public int hashCode() { - return 31 * (31 + x) + y; - } - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/string/LineEncoderTest.java b/codec/src/test/java/io/netty/handler/codec/string/LineEncoderTest.java deleted file mode 100644 index a8f2e3f2da..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/string/LineEncoderTest.java +++ /dev/null @@ -1,52 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.string; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.CharsetUtil; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class LineEncoderTest { - - @Test - public void testEncode() { - testLineEncode(LineSeparator.DEFAULT, "abc"); - testLineEncode(LineSeparator.WINDOWS, "abc"); - testLineEncode(LineSeparator.UNIX, "abc"); - } - - private static void testLineEncode(LineSeparator lineSeparator, String msg) { - EmbeddedChannel channel = new EmbeddedChannel(new LineEncoder(lineSeparator, CharsetUtil.UTF_8)); - assertTrue(channel.writeOutbound(msg)); - ByteBuf buf = channel.readOutbound(); - try { - byte[] data = new byte[buf.readableBytes()]; - buf.readBytes(data); - byte[] expected = (msg + lineSeparator.value()).getBytes(CharsetUtil.UTF_8); - assertArrayEquals(expected, data); - assertNull(channel.readOutbound()); - } finally { - buf.release(); - assertFalse(channel.finish()); - } - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/string/StringDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/string/StringDecoderTest.java deleted file mode 100644 index f0da531fb9..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/string/StringDecoderTest.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.string; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.CharsetUtil; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.assertNull; - -public class StringDecoderTest { - - @Test - public void testDecode() { - String msg = "abc123"; - ByteBuf byteBuf = Unpooled.copiedBuffer(msg, CharsetUtil.UTF_8); - EmbeddedChannel channel = new EmbeddedChannel(new StringDecoder()); - assertTrue(channel.writeInbound(byteBuf)); - String result = channel.readInbound(); - assertEquals(msg, result); - assertNull(channel.readInbound()); - assertFalse(channel.finish()); - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/string/StringEncoderTest.java b/codec/src/test/java/io/netty/handler/codec/string/StringEncoderTest.java deleted file mode 100644 index f0309b837d..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/string/StringEncoderTest.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.string; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.CharsetUtil; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertFalse; - -public class StringEncoderTest { - - @Test - public void testEncode() { - String msg = "Test"; - EmbeddedChannel channel = new EmbeddedChannel(new StringEncoder()); - Assertions.assertTrue(channel.writeOutbound(msg)); - Assertions.assertTrue(channel.finish()); - ByteBuf buf = channel.readOutbound(); - byte[] data = new byte[buf.readableBytes()]; - buf.readBytes(data); - Assertions.assertArrayEquals(msg.getBytes(CharsetUtil.UTF_8), data); - Assertions.assertNull(channel.readOutbound()); - buf.release(); - assertFalse(channel.finish()); - } -} diff --git a/codec/src/test/java/io/netty/handler/codec/xml/XmlFrameDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/xml/XmlFrameDecoderTest.java deleted file mode 100644 index 8bf8a947b3..0000000000 --- a/codec/src/test/java/io/netty/handler/codec/xml/XmlFrameDecoderTest.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec.xml; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.CorruptedFrameException; -import io.netty.handler.codec.TooLongFrameException; -import io.netty.util.CharsetUtil; -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.net.URISyntaxException; -import java.net.URL; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import static org.hamcrest.CoreMatchers.*; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; - -public class XmlFrameDecoderTest { - - private final List xmlSamples; - - public XmlFrameDecoderTest() throws IOException, URISyntaxException { - xmlSamples = Arrays.asList( - sample("01"), sample("02"), sample("03"), - sample("04"), sample("05"), sample("06") - ); - } - - @Test - public void testConstructorWithIllegalArgs01() { - assertThrows(IllegalArgumentException.class, () -> new XmlFrameDecoder(0)); - } - - @Test - public void testConstructorWithIllegalArgs02() { - assertThrows(IllegalArgumentException.class, () -> new XmlFrameDecoder(-23)); - } - - @Test - public void testDecodeWithFrameExceedingMaxLength() { - XmlFrameDecoder decoder = new XmlFrameDecoder(3); - EmbeddedChannel ch = new EmbeddedChannel(decoder); - assertThrows(TooLongFrameException.class, - () -> ch.writeInbound(Unpooled.copiedBuffer("", CharsetUtil.UTF_8))); - } - - @Test - public void testDecodeWithInvalidInput() { - XmlFrameDecoder decoder = new XmlFrameDecoder(1048576); - EmbeddedChannel ch = new EmbeddedChannel(decoder); - assertThrows(CorruptedFrameException.class, - () -> ch.writeInbound(Unpooled.copiedBuffer("invalid XML", CharsetUtil.UTF_8))); - } - - @Test - public void testDecodeWithInvalidContentBeforeXml() { - XmlFrameDecoder decoder = new XmlFrameDecoder(1048576); - EmbeddedChannel ch = new EmbeddedChannel(decoder); - assertThrows(CorruptedFrameException.class, - () -> ch.writeInbound(Unpooled.copiedBuffer("invalid XML", CharsetUtil.UTF_8))); - } - - @Test - public void testDecodeShortValidXml() { - testDecodeWithXml("", ""); - } - - @Test - public void testDecodeShortValidXmlWithLeadingWhitespace01() { - testDecodeWithXml(" ", ""); - } - - @Test - public void testDecodeShortValidXmlWithLeadingWhitespace02() { - testDecodeWithXml(" \n\r \t\t", ""); - } - - @Test - public void testDecodeShortValidXmlWithLeadingWhitespace02AndTrailingGarbage() { - testDecodeWithXml(" \n\r \t\ttrash", "", CorruptedFrameException.class); - } - - @Test - public void testDecodeInvalidXml() { - testDecodeWithXml("" + - ""; - testDecodeWithXml(xml, xml); - } - - @Test - public void testDecodeWithCDATABlockContainingNestedUnbalancedXml() { - //
isn't closed, also
should have been - final String xml = "" + - "ACME Inc.]]>" + - ""; - testDecodeWithXml(xml, xml); - } - - @Test - public void testDecodeWithMultipleMessages() { - final String input = "\n\n" + - "\n\n" + - ""; - final String frame1 = ""; - final String frame2 = "\n\n"; - final String frame3 = ""; - testDecodeWithXml(input, frame1, frame2, frame3); - } - - @Test - public void testFraming() { - testDecodeWithXml(Arrays.asList("123"), "123"); - } - - @Test - public void testDecodeWithSampleXml() { - for (final String xmlSample : xmlSamples) { - testDecodeWithXml(xmlSample, xmlSample); - } - } - - private static void testDecodeWithXml(List xmlFrames, Object... expected) { - EmbeddedChannel ch = new EmbeddedChannel(new XmlFrameDecoder(1048576)); - Exception cause = null; - try { - for (String xmlFrame : xmlFrames) { - ch.writeInbound(Unpooled.copiedBuffer(xmlFrame, CharsetUtil.UTF_8)); - } - } catch (Exception e) { - cause = e; - } - List actual = new ArrayList<>(); - for (;;) { - ByteBuf buf = ch.readInbound(); - if (buf == null) { - break; - } - actual.add(buf.toString(CharsetUtil.UTF_8)); - buf.release(); - } - - if (cause != null) { - actual.add(cause.getClass()); - } - - try { - List expectedList = new ArrayList<>(); - Collections.addAll(expectedList, expected); - assertThat(actual, is(expectedList)); - } finally { - ch.finish(); - } - } - - private static void testDecodeWithXml(String xml, Object... expected) { - testDecodeWithXml(Collections.singletonList(xml), expected); - } - - private String sample(String number) throws IOException, URISyntaxException { - String path = "io/netty/handler/codec/xml/sample-" + number + ".xml"; - URL url = getClass().getClassLoader().getResource(path); - if (url == null) { - throw new IllegalArgumentException("file not found: " + path); - } - byte[] buf = Files.readAllBytes(Paths.get(url.toURI())); - return StandardCharsets.UTF_8.decode(ByteBuffer.wrap(buf)).toString(); - } -} diff --git a/codec/src/test/resources/io/netty/handler/codec/xml/sample-01.xml b/codec/src/test/resources/io/netty/handler/codec/xml/sample-01.xml deleted file mode 100644 index 2408a6fe9d..0000000000 --- a/codec/src/test/resources/io/netty/handler/codec/xml/sample-01.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/codec/src/test/resources/io/netty/handler/codec/xml/sample-02.xml b/codec/src/test/resources/io/netty/handler/codec/xml/sample-02.xml deleted file mode 100644 index ffd40cdac7..0000000000 --- a/codec/src/test/resources/io/netty/handler/codec/xml/sample-02.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/codec/src/test/resources/io/netty/handler/codec/xml/sample-03.xml b/codec/src/test/resources/io/netty/handler/codec/xml/sample-03.xml deleted file mode 100644 index a928402300..0000000000 --- a/codec/src/test/resources/io/netty/handler/codec/xml/sample-03.xml +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/codec/src/test/resources/io/netty/handler/codec/xml/sample-04.xml b/codec/src/test/resources/io/netty/handler/codec/xml/sample-04.xml deleted file mode 100644 index 0fd0b38014..0000000000 --- a/codec/src/test/resources/io/netty/handler/codec/xml/sample-04.xml +++ /dev/null @@ -1,715 +0,0 @@ - - - - - 4.0.0 - - org.sonatype.oss - oss-parent - 7 - - - io.netty - netty-parent - pom - 4.0.14.Final-SNAPSHOT - - Netty - https://netty.io/ - - Netty is an asynchronous event-driven network application framework for - rapid development of maintainable high performance protocol servers and - clients. - - - - The Netty Project - https://netty.io/ - - - - - Apache License, Version 2.0 - https://www.apache.org/licenses/LICENSE-2.0 - - - 2008 - - - https://github.com/netty/netty - scm:git:git://github.com/netty/netty.git - scm:git:ssh://git@github.com/netty/netty.git - HEAD - - - - - netty.io - The Netty Project Contributors - netty@googlegroups.com - https://netty.io/ - The Netty Project - https://netty.io/ - - - - - UTF-8 - UTF-8 - 1.3.18.GA - - -server - -dsa -da -ea:io.netty... - -XX:+AggressiveOpts - -XX:+TieredCompilation - -XX:+UseBiasedLocking - -XX:+UseFastAccessorMethods - -XX:+UseStringCache - -XX:+OptimizeStringConcat - -XX:+HeapDumpOnOutOfMemoryError - - - - - common - buffer - codec - codec-http - codec-socks - transport - transport-rxtx - transport-sctp - transport-udt - handler - example - testsuite - microbench - all - tarball - - - - - - - org.jboss.marshalling - jboss-marshalling - ${jboss.marshalling.version} - compile - true - - - - com.google.protobuf - protobuf-java - 2.6.1 - - - com.jcraft - jzlib - 1.1.3 - - - - org.rxtx - rxtx - 2.1.7 - - - - com.barchart.udt - barchart-udt-bundle - 2.3.0 - - - - javax.servlet - servlet-api - 2.5 - - - - org.slf4j - slf4j-api - 1.7.21 - - - commons-logging - commons-logging - 1.2 - - - log4j - log4j - 1.2.17 - - - mail - javax.mail - - - jms - javax.jms - - - jmxtools - com.sun.jdmk - - - jmxri - com.sun.jmx - - - true - - - - - com.yammer.metrics - metrics-core - 2.2.0 - - - - - org.jboss.marshalling - jboss-marshalling-serial - ${jboss.marshalling.version} - test - - - org.jboss.marshalling - jboss-marshalling-river - ${jboss.marshalling.version} - test - - - - - com.google.caliper - caliper - 0.5-rc1 - test - - - - - - - - junit - junit - 4.12 - test - - - org.easymock - easymock - 3.4 - test - - - org.easymock - easymockclassextension - 3.2 - test - - - org.jmock - jmock-junit4 - 2.8.2 - test - - - ch.qos.logback - logback-classic - 1.1.7 - test - - - - - - - maven-enforcer-plugin - 1.4.1 - - - enforce-tools - - enforce - - - - - - - [1.7.0,) - - - [3.0.5,3.1) - - - - - - - - maven-compiler-plugin - 3.5.1 - - 1.7 - true - 1.6 - 1.6 - true - true - true - true - -Xlint:-options - - - 256m - 1024m - - - - maven-checkstyle-plugin - 2.12.1 - - - check-style - - check - - validate - - true - true - true - true - io/netty/checkstyle.xml - true - - - - - - ${project.groupId} - netty-build - 19 - - - - - maven-surefire-plugin - - - **/*Test*.java - **/*Benchmark*.java - - - **/Abstract* - **/TestUtil* - - random - ${test.jvm.argLine} - - - - - org.apache.felix - maven-bundle-plugin - 2.5.4 - - - generate-manifest - process-classes - - manifest - - - - ${project.groupId}.* - - sun.misc.*;resolution:=optional,* - - !* - - - - - - - maven-source-plugin - 3.0.1 - - - - attach-sources - invalid - - jar - - - - attach-sources-no-fork - package - - jar-no-fork - - - - - - maven-javadoc-plugin - 2.10.4 - - false - true - false - false - true - - - - maven-deploy-plugin - 2.8.2 - - 10 - - - - maven-release-plugin - - 2.5.3 - - false - -P release,sonatype-oss-release,full,no-osgi - true - false - netty-@{project.version} - - - - - - maven-antrun-plugin - - - - write-version-properties - initialize - - run - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Current commit: ${shortCommitHash} on ${commitDate} - - - - - - - - - - - - - - - - - - - - - - - org.apache.ant - ant - 1.9.7 - - - org.apache.ant - ant-launcher - 1.9.7 - - - ant-contrib - ant-contrib - 1.0b3 - - - ant - ant - - - - - - - - - - - - maven-surefire-plugin - 2.19.1 - - - - maven-failsafe-plugin - 2.19.1 - - - maven-clean-plugin - 3.0.0 - - - maven-resources-plugin - 3.0.1 - - - maven-jar-plugin - 3.0.2 - - - default-jar - - - - true - - true - ${project.build.outputDirectory}/META-INF/MANIFEST.MF - - - - - - - maven-dependency-plugin - 2.10 - - - maven-assembly-plugin - 2.6 - - - - maven-jxr-plugin - 2.2 - - - maven-antrun-plugin - 1.8 - - - ant-contrib - ant-contrib - 1.0b3 - - - ant - ant - - - - - - - org.codehaus.mojo - build-helper-maven-plugin - 1.10 - - - - - org.eclipse.m2e - lifecycle-mapping - 1.0.0 - - - - - - org.apache.maven.plugins - maven-antrun-plugin - [1.7,) - - run - - - - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - [1.0,) - - check - - - - - false - - - - - - org.apache.maven.plugins - maven-enforcer-plugin - [1.0,) - - enforce - - - - - false - - - - - - org.apache.maven.plugins - maven-clean-plugin - [1.0,) - - clean - - - - - false - - - - - - org.apache.felix - maven-bundle-plugin - [2.4,) - - manifest - - - - - - - - - - - - - - \ No newline at end of file diff --git a/codec/src/test/resources/io/netty/handler/codec/xml/sample-05.xml b/codec/src/test/resources/io/netty/handler/codec/xml/sample-05.xml deleted file mode 100644 index 427cbf824d..0000000000 --- a/codec/src/test/resources/io/netty/handler/codec/xml/sample-05.xml +++ /dev/null @@ -1,81 +0,0 @@ - - - - Rocket Launching - - - Configuration - - - Configuring rockets should look very familiar if you're used - to Jakarta Commons-Rocket or Commons-Space. You will first - create a normal - ContextSource - then wrap it in a - RocketContextSource - . - - - ... - - - - - - - - - - - ... - -]]> - - In a real world example you would probably configure the - rocket options and enable connection validation; the above - serves as an example to demonstrate the general idea. - - - - Validation Configuration - - - Adding validation and a few rocket configuration tweaks to - the above example is straight forward. Inject a - RocketContextValidator - and set when validation should occur and the rocket is - ready to go. - - - ... - - - - - - - - - - - - - - - - ... - -]]> - - The above example will test each - RocketContext - before it is passed to the client application and test - RocketContext - s that have been sitting idle in orbit. - - - - \ No newline at end of file diff --git a/codec/src/test/resources/io/netty/handler/codec/xml/sample-06.xml b/codec/src/test/resources/io/netty/handler/codec/xml/sample-06.xml deleted file mode 100644 index ecf5f8d7a0..0000000000 --- a/codec/src/test/resources/io/netty/handler/codec/xml/sample-06.xml +++ /dev/null @@ -1,62 +0,0 @@ - - - - Rocket Launching - - - Configuration - - - Configuring rockets should look very familiar if you're used - to Jakarta Commons-Rocket or Commons-Space. You will first - create a normal - ContextSource - then wrap it in a - RocketContextSource - . - - - ... - - - ... -]]> - - In a real world example you would probably configure the - rocket options and enable connection validation; the above - serves as an example to demonstrate the general idea. - - - - Validation Configuration - - - Adding validation and a few rocket configuration tweaks to - the above example is straight forward. Inject a - RocketContextValidator - and set when validation should occur and the rocket is - ready to go. - - - ... - - - ... - - - ... -]]> - - The above example will test each - RocketContext - before it is passed to the client application and test - RocketContext - s that have been sitting idle in orbit. - - - - \ No newline at end of file diff --git a/common/pom.xml b/common/pom.xml index 91de8cf8e8..d5d8ecae1c 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -19,7 +19,7 @@ 4.0.0 - io.netty + io.net5 netty-parent 5.0.0.Final-SNAPSHOT @@ -30,7 +30,7 @@ Netty/Common - io.netty.common + io.net5.common ${project.basedir}/src/main/templates ${project.basedir}/src/test/templates ${project.build.directory}/generated-sources/collections/java @@ -90,7 +90,7 @@ - io.netty + io.net5 netty-dev-tools test true @@ -116,7 +116,7 @@ org.jctools. - io.netty.util.internal.shaded.org.jctools. + io.net5.util.internal.shaded.org.jctools. true diff --git a/common/src/main/java/io/netty/util/AbstractConstant.java b/common/src/main/java/io/net5/util/AbstractConstant.java similarity index 98% rename from common/src/main/java/io/netty/util/AbstractConstant.java rename to common/src/main/java/io/net5/util/AbstractConstant.java index de16653cee..dfd74de4c0 100644 --- a/common/src/main/java/io/netty/util/AbstractConstant.java +++ b/common/src/main/java/io/net5/util/AbstractConstant.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util; +package io.net5.util; import java.util.concurrent.atomic.AtomicLong; diff --git a/common/src/main/java/io/netty/util/AbstractReferenceCounted.java b/common/src/main/java/io/net5/util/AbstractReferenceCounted.java similarity index 97% rename from common/src/main/java/io/netty/util/AbstractReferenceCounted.java rename to common/src/main/java/io/net5/util/AbstractReferenceCounted.java index 364b980979..5b351238df 100644 --- a/common/src/main/java/io/netty/util/AbstractReferenceCounted.java +++ b/common/src/main/java/io/net5/util/AbstractReferenceCounted.java @@ -13,11 +13,11 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util; +package io.net5.util; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; -import io.netty.util.internal.ReferenceCountUpdater; +import io.net5.util.internal.ReferenceCountUpdater; /** * Abstract base class for classes wants to implement {@link ReferenceCounted}. diff --git a/common/src/main/java/io/netty/util/AsciiString.java b/common/src/main/java/io/net5/util/AsciiString.java similarity index 99% rename from common/src/main/java/io/netty/util/AsciiString.java rename to common/src/main/java/io/net5/util/AsciiString.java index b423502b0f..c3642fef4f 100644 --- a/common/src/main/java/io/netty/util/AsciiString.java +++ b/common/src/main/java/io/net5/util/AsciiString.java @@ -13,11 +13,11 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util; +package io.net5.util; -import io.netty.util.internal.EmptyArrays; -import io.netty.util.internal.InternalThreadLocalMap; -import io.netty.util.internal.PlatformDependent; +import io.net5.util.internal.EmptyArrays; +import io.net5.util.internal.InternalThreadLocalMap; +import io.net5.util.internal.PlatformDependent; import java.nio.ByteBuffer; import java.nio.CharBuffer; @@ -29,7 +29,7 @@ import java.util.List; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; -import static io.netty.util.internal.MathUtil.isOutOfBounds; +import static io.net5.util.internal.MathUtil.isOutOfBounds; import static java.util.Objects.requireNonNull; /** diff --git a/common/src/main/java/io/netty/util/AsyncMapping.java b/common/src/main/java/io/net5/util/AsyncMapping.java similarity index 89% rename from common/src/main/java/io/netty/util/AsyncMapping.java rename to common/src/main/java/io/net5/util/AsyncMapping.java index b3114c776f..694946df79 100644 --- a/common/src/main/java/io/netty/util/AsyncMapping.java +++ b/common/src/main/java/io/net5/util/AsyncMapping.java @@ -13,10 +13,10 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util; +package io.net5.util; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; +import io.net5.util.concurrent.Future; +import io.net5.util.concurrent.Promise; public interface AsyncMapping { diff --git a/common/src/main/java/io/netty/util/Attribute.java b/common/src/main/java/io/net5/util/Attribute.java similarity index 99% rename from common/src/main/java/io/netty/util/Attribute.java rename to common/src/main/java/io/net5/util/Attribute.java index 5dc3f17470..922868ccbf 100644 --- a/common/src/main/java/io/netty/util/Attribute.java +++ b/common/src/main/java/io/net5/util/Attribute.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util; +package io.net5.util; /** * An attribute which allows to store a value reference. It may be updated atomically and so is thread-safe. diff --git a/common/src/main/java/io/netty/util/AttributeKey.java b/common/src/main/java/io/net5/util/AttributeKey.java similarity index 99% rename from common/src/main/java/io/netty/util/AttributeKey.java rename to common/src/main/java/io/net5/util/AttributeKey.java index 37f0fcf1d4..713391e25e 100644 --- a/common/src/main/java/io/netty/util/AttributeKey.java +++ b/common/src/main/java/io/net5/util/AttributeKey.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util; +package io.net5.util; /** * Key which can be used to access {@link Attribute} out of the {@link AttributeMap}. Be aware that it is not be diff --git a/common/src/main/java/io/netty/util/AttributeMap.java b/common/src/main/java/io/net5/util/AttributeMap.java similarity index 98% rename from common/src/main/java/io/netty/util/AttributeMap.java rename to common/src/main/java/io/net5/util/AttributeMap.java index 329bb2f929..5803cab485 100644 --- a/common/src/main/java/io/netty/util/AttributeMap.java +++ b/common/src/main/java/io/net5/util/AttributeMap.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util; +package io.net5.util; /** * Holds {@link Attribute}s which can be accessed via {@link AttributeKey}. diff --git a/common/src/main/java/io/netty/util/BooleanSupplier.java b/common/src/main/java/io/net5/util/BooleanSupplier.java similarity index 98% rename from common/src/main/java/io/netty/util/BooleanSupplier.java rename to common/src/main/java/io/net5/util/BooleanSupplier.java index 48ee000ed4..92f9c6e519 100644 --- a/common/src/main/java/io/netty/util/BooleanSupplier.java +++ b/common/src/main/java/io/net5/util/BooleanSupplier.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util; +package io.net5.util; /** * Represents a supplier of {@code boolean}-valued results. diff --git a/common/src/main/java/io/netty/util/ByteProcessor.java b/common/src/main/java/io/net5/util/ByteProcessor.java similarity index 93% rename from common/src/main/java/io/netty/util/ByteProcessor.java rename to common/src/main/java/io/net5/util/ByteProcessor.java index 2bc5b6f492..30ab5fbb84 100644 --- a/common/src/main/java/io/netty/util/ByteProcessor.java +++ b/common/src/main/java/io/net5/util/ByteProcessor.java @@ -12,12 +12,12 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package io.netty.util; +package io.net5.util; -import static io.netty.util.ByteProcessorUtils.CARRIAGE_RETURN; -import static io.netty.util.ByteProcessorUtils.HTAB; -import static io.netty.util.ByteProcessorUtils.LINE_FEED; -import static io.netty.util.ByteProcessorUtils.SPACE; +import static io.net5.util.ByteProcessorUtils.CARRIAGE_RETURN; +import static io.net5.util.ByteProcessorUtils.HTAB; +import static io.net5.util.ByteProcessorUtils.LINE_FEED; +import static io.net5.util.ByteProcessorUtils.SPACE; /** * Provides a mechanism to iterate over a collection of bytes. diff --git a/common/src/main/java/io/netty/util/ByteProcessorUtils.java b/common/src/main/java/io/net5/util/ByteProcessorUtils.java similarity index 97% rename from common/src/main/java/io/netty/util/ByteProcessorUtils.java rename to common/src/main/java/io/net5/util/ByteProcessorUtils.java index 073af9335e..4146554bce 100644 --- a/common/src/main/java/io/netty/util/ByteProcessorUtils.java +++ b/common/src/main/java/io/net5/util/ByteProcessorUtils.java @@ -12,7 +12,7 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package io.netty.util; +package io.net5.util; final class ByteProcessorUtils { static final byte SPACE = (byte) ' '; diff --git a/common/src/main/java/io/netty/util/CharsetUtil.java b/common/src/main/java/io/net5/util/CharsetUtil.java similarity index 98% rename from common/src/main/java/io/netty/util/CharsetUtil.java rename to common/src/main/java/io/net5/util/CharsetUtil.java index a3cb61b8a8..09bf6188e2 100644 --- a/common/src/main/java/io/netty/util/CharsetUtil.java +++ b/common/src/main/java/io/net5/util/CharsetUtil.java @@ -13,9 +13,9 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util; +package io.net5.util; -import io.netty.util.internal.InternalThreadLocalMap; +import io.net5.util.internal.InternalThreadLocalMap; import static java.util.Objects.requireNonNull; import java.nio.charset.Charset; diff --git a/common/src/main/java/io/netty/util/Constant.java b/common/src/main/java/io/net5/util/Constant.java similarity index 97% rename from common/src/main/java/io/netty/util/Constant.java rename to common/src/main/java/io/net5/util/Constant.java index 3ada9d8f09..86f5449fcc 100644 --- a/common/src/main/java/io/netty/util/Constant.java +++ b/common/src/main/java/io/net5/util/Constant.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util; +package io.net5.util; /** * A singleton which is safe to compare via the {@code ==} operator. Created and managed by {@link ConstantPool}. diff --git a/common/src/main/java/io/netty/util/ConstantPool.java b/common/src/main/java/io/net5/util/ConstantPool.java similarity index 97% rename from common/src/main/java/io/netty/util/ConstantPool.java rename to common/src/main/java/io/net5/util/ConstantPool.java index 7dee51f975..bb1c059476 100644 --- a/common/src/main/java/io/netty/util/ConstantPool.java +++ b/common/src/main/java/io/net5/util/ConstantPool.java @@ -14,9 +14,9 @@ * under the License. */ -package io.netty.util; +package io.net5.util; -import static io.netty.util.internal.ObjectUtil.checkNonEmpty; +import static io.net5.util.internal.ObjectUtil.checkNonEmpty; import static java.util.Objects.requireNonNull; import java.util.concurrent.ConcurrentHashMap; diff --git a/common/src/main/java/io/netty/util/DefaultAttributeMap.java b/common/src/main/java/io/net5/util/DefaultAttributeMap.java similarity index 99% rename from common/src/main/java/io/netty/util/DefaultAttributeMap.java rename to common/src/main/java/io/net5/util/DefaultAttributeMap.java index 8df170a8aa..47b75718fe 100644 --- a/common/src/main/java/io/netty/util/DefaultAttributeMap.java +++ b/common/src/main/java/io/net5/util/DefaultAttributeMap.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util; +package io.net5.util; import static java.util.Objects.requireNonNull; diff --git a/common/src/main/java/io/netty/util/DomainMappingBuilder.java b/common/src/main/java/io/net5/util/DomainMappingBuilder.java similarity index 99% rename from common/src/main/java/io/netty/util/DomainMappingBuilder.java rename to common/src/main/java/io/net5/util/DomainMappingBuilder.java index 03e2706dee..17d8bc493e 100644 --- a/common/src/main/java/io/netty/util/DomainMappingBuilder.java +++ b/common/src/main/java/io/net5/util/DomainMappingBuilder.java @@ -14,7 +14,7 @@ * under the License. */ -package io.netty.util; +package io.net5.util; /** * Builder for immutable {@link DomainNameMapping} instances. diff --git a/common/src/main/java/io/netty/util/DomainNameMapping.java b/common/src/main/java/io/net5/util/DomainNameMapping.java similarity index 97% rename from common/src/main/java/io/netty/util/DomainNameMapping.java rename to common/src/main/java/io/net5/util/DomainNameMapping.java index 0c108bad19..3d599fc523 100644 --- a/common/src/main/java/io/netty/util/DomainNameMapping.java +++ b/common/src/main/java/io/net5/util/DomainNameMapping.java @@ -14,9 +14,9 @@ * under the License. */ -package io.netty.util; +package io.net5.util; -import io.netty.util.internal.StringUtil; +import io.net5.util.internal.StringUtil; import java.net.IDN; import java.util.Collections; @@ -24,7 +24,7 @@ import java.util.LinkedHashMap; import java.util.Locale; import java.util.Map; -import static io.netty.util.internal.StringUtil.commonSuffixOfLength; +import static io.net5.util.internal.StringUtil.commonSuffixOfLength; import static java.util.Objects.requireNonNull; /** diff --git a/common/src/main/java/io/netty/util/DomainNameMappingBuilder.java b/common/src/main/java/io/net5/util/DomainNameMappingBuilder.java similarity index 99% rename from common/src/main/java/io/netty/util/DomainNameMappingBuilder.java rename to common/src/main/java/io/net5/util/DomainNameMappingBuilder.java index 5a10131bc4..27d648519f 100644 --- a/common/src/main/java/io/netty/util/DomainNameMappingBuilder.java +++ b/common/src/main/java/io/net5/util/DomainNameMappingBuilder.java @@ -14,7 +14,7 @@ * under the License. */ -package io.netty.util; +package io.net5.util; import java.util.Collections; import java.util.LinkedHashMap; diff --git a/common/src/main/java/io/netty/util/DomainWildcardMappingBuilder.java b/common/src/main/java/io/net5/util/DomainWildcardMappingBuilder.java similarity index 98% rename from common/src/main/java/io/netty/util/DomainWildcardMappingBuilder.java rename to common/src/main/java/io/net5/util/DomainWildcardMappingBuilder.java index a8c8ceb8f8..cd9b74b0ab 100644 --- a/common/src/main/java/io/netty/util/DomainWildcardMappingBuilder.java +++ b/common/src/main/java/io/net5/util/DomainWildcardMappingBuilder.java @@ -13,12 +13,12 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util; +package io.net5.util; import java.util.LinkedHashMap; import java.util.Map; -import static io.netty.util.internal.ObjectUtil.checkNotNull; +import static io.net5.util.internal.ObjectUtil.checkNotNull; /** * Builder that allows to build {@link Mapping}s that support diff --git a/common/src/main/java/io/netty/util/HashedWheelTimer.java b/common/src/main/java/io/net5/util/HashedWheelTimer.java similarity index 98% rename from common/src/main/java/io/netty/util/HashedWheelTimer.java rename to common/src/main/java/io/net5/util/HashedWheelTimer.java index 5317832f18..0f849c4aa2 100644 --- a/common/src/main/java/io/netty/util/HashedWheelTimer.java +++ b/common/src/main/java/io/net5/util/HashedWheelTimer.java @@ -13,11 +13,11 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util; +package io.net5.util; -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; +import io.net5.util.internal.PlatformDependent; +import io.net5.util.internal.logging.InternalLogger; +import io.net5.util.internal.logging.InternalLoggerFactory; import java.util.Collections; import java.util.HashSet; @@ -33,9 +33,9 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import java.util.concurrent.atomic.AtomicLong; -import static io.netty.util.internal.ObjectUtil.checkInRange; -import static io.netty.util.internal.ObjectUtil.checkPositive; -import static io.netty.util.internal.StringUtil.simpleClassName; +import static io.net5.util.internal.ObjectUtil.checkInRange; +import static io.net5.util.internal.ObjectUtil.checkPositive; +import static io.net5.util.internal.StringUtil.simpleClassName; import static java.util.Objects.requireNonNull; /** diff --git a/common/src/main/java/io/netty/util/HashingStrategy.java b/common/src/main/java/io/net5/util/HashingStrategy.java similarity index 99% rename from common/src/main/java/io/netty/util/HashingStrategy.java rename to common/src/main/java/io/net5/util/HashingStrategy.java index 64f0433664..a6d1103175 100644 --- a/common/src/main/java/io/netty/util/HashingStrategy.java +++ b/common/src/main/java/io/net5/util/HashingStrategy.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util; +package io.net5.util; /** * Abstraction for hash code generation and equality comparison. diff --git a/common/src/main/java/io/netty/util/IllegalReferenceCountException.java b/common/src/main/java/io/net5/util/IllegalReferenceCountException.java similarity index 98% rename from common/src/main/java/io/netty/util/IllegalReferenceCountException.java rename to common/src/main/java/io/net5/util/IllegalReferenceCountException.java index b7e5ba3095..0109e367d8 100644 --- a/common/src/main/java/io/netty/util/IllegalReferenceCountException.java +++ b/common/src/main/java/io/net5/util/IllegalReferenceCountException.java @@ -14,7 +14,7 @@ * under the License. */ -package io.netty.util; +package io.net5.util; /** * An {@link IllegalStateException} which is raised when a user attempts to access a {@link ReferenceCounted} whose diff --git a/common/src/main/java/io/netty/util/IntSupplier.java b/common/src/main/java/io/net5/util/IntSupplier.java similarity index 97% rename from common/src/main/java/io/netty/util/IntSupplier.java rename to common/src/main/java/io/net5/util/IntSupplier.java index 2840e05844..b197947e43 100644 --- a/common/src/main/java/io/netty/util/IntSupplier.java +++ b/common/src/main/java/io/net5/util/IntSupplier.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util; +package io.net5.util; /** * Represents a supplier of {@code int}-valued results. diff --git a/common/src/main/java/io/netty/util/Mapping.java b/common/src/main/java/io/net5/util/Mapping.java similarity index 97% rename from common/src/main/java/io/netty/util/Mapping.java rename to common/src/main/java/io/net5/util/Mapping.java index b57c746fde..9a639d7bb3 100644 --- a/common/src/main/java/io/netty/util/Mapping.java +++ b/common/src/main/java/io/net5/util/Mapping.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util; +package io.net5.util; /** * Maintains the mapping from the objects of one type to the objects of the other type. diff --git a/common/src/main/java/io/netty/util/NetUtil.java b/common/src/main/java/io/net5/util/NetUtil.java similarity index 98% rename from common/src/main/java/io/netty/util/NetUtil.java rename to common/src/main/java/io/net5/util/NetUtil.java index 09e333ee8d..9265e2583a 100644 --- a/common/src/main/java/io/netty/util/NetUtil.java +++ b/common/src/main/java/io/net5/util/NetUtil.java @@ -13,14 +13,14 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util; +package io.net5.util; -import io.netty.util.NetUtilInitializations.NetworkIfaceAndInetAddress; -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.SystemPropertyUtil; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; +import io.net5.util.NetUtilInitializations.NetworkIfaceAndInetAddress; +import io.net5.util.internal.PlatformDependent; +import io.net5.util.internal.StringUtil; +import io.net5.util.internal.SystemPropertyUtil; +import io.net5.util.internal.logging.InternalLogger; +import io.net5.util.internal.logging.InternalLoggerFactory; import java.io.BufferedReader; import java.io.File; @@ -37,7 +37,7 @@ import java.net.UnknownHostException; import java.security.AccessController; import java.security.PrivilegedAction; -import static io.netty.util.AsciiString.indexOf; +import static io.net5.util.AsciiString.indexOf; /** * A class that holds a number of network-related constants. @@ -169,7 +169,7 @@ public final class NetUtil { } else { // Try to get from sysctl Integer tmp = null; - if (SystemPropertyUtil.getBoolean("io.netty.net.somaxconn.trySysctl", false)) { + if (SystemPropertyUtil.getBoolean("io.net5.net.somaxconn.trySysctl", false)) { tmp = sysctlGetInt("kern.ipc.somaxconn"); if (tmp == null) { tmp = sysctlGetInt("kern.ipc.soacceptqueue"); diff --git a/common/src/main/java/io/netty/util/NetUtilInitializations.java b/common/src/main/java/io/net5/util/NetUtilInitializations.java similarity index 96% rename from common/src/main/java/io/netty/util/NetUtilInitializations.java rename to common/src/main/java/io/net5/util/NetUtilInitializations.java index 1dafab7abe..3b22809d44 100644 --- a/common/src/main/java/io/netty/util/NetUtilInitializations.java +++ b/common/src/main/java/io/net5/util/NetUtilInitializations.java @@ -13,12 +13,12 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util; +package io.net5.util; -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.SocketUtils; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; +import io.net5.util.internal.PlatformDependent; +import io.net5.util.internal.SocketUtils; +import io.net5.util.internal.logging.InternalLogger; +import io.net5.util.internal.logging.InternalLoggerFactory; import java.net.Inet4Address; import java.net.Inet6Address; diff --git a/common/src/main/java/io/netty/util/NetUtilSubstitutions.java b/common/src/main/java/io/net5/util/NetUtilSubstitutions.java similarity index 99% rename from common/src/main/java/io/netty/util/NetUtilSubstitutions.java rename to common/src/main/java/io/net5/util/NetUtilSubstitutions.java index 7894037e4e..3d80295fa1 100644 --- a/common/src/main/java/io/netty/util/NetUtilSubstitutions.java +++ b/common/src/main/java/io/net5/util/NetUtilSubstitutions.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util; +package io.net5.util; import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.annotate.InjectAccessors; diff --git a/common/src/main/java/io/netty/util/NettyRuntime.java b/common/src/main/java/io/net5/util/NettyRuntime.java similarity index 92% rename from common/src/main/java/io/netty/util/NettyRuntime.java rename to common/src/main/java/io/net5/util/NettyRuntime.java index b4044f4b2c..9f6a29ff5f 100644 --- a/common/src/main/java/io/netty/util/NettyRuntime.java +++ b/common/src/main/java/io/net5/util/NettyRuntime.java @@ -14,10 +14,10 @@ * under the License. */ -package io.netty.util; +package io.net5.util; -import io.netty.util.internal.ObjectUtil; -import io.netty.util.internal.SystemPropertyUtil; +import io.net5.util.internal.ObjectUtil; +import io.net5.util.internal.SystemPropertyUtil; import java.util.Locale; @@ -55,7 +55,7 @@ public final class NettyRuntime { /** * Get the configured number of available processors. The default is {@link Runtime#availableProcessors()}. - * This can be overridden by setting the system property "io.netty.availableProcessors" or by invoking + * This can be overridden by setting the system property "io.net5.availableProcessors" or by invoking * {@link #setAvailableProcessors(int)} before any calls to this method. * * @return the configured number of available processors @@ -65,7 +65,7 @@ public final class NettyRuntime { if (this.availableProcessors == 0) { final int availableProcessors = SystemPropertyUtil.getInt( - "io.netty.availableProcessors", + "io.net5.availableProcessors", Runtime.getRuntime().availableProcessors()); setAvailableProcessors(availableProcessors); } @@ -89,7 +89,7 @@ public final class NettyRuntime { /** * Get the configured number of available processors. The default is {@link Runtime#availableProcessors()}. This - * can be overridden by setting the system property "io.netty.availableProcessors" or by invoking + * can be overridden by setting the system property "io.net5.availableProcessors" or by invoking * {@link #setAvailableProcessors(int)} before any calls to this method. * * @return the configured number of available processors diff --git a/common/src/main/java/io/netty/util/Recycler.java b/common/src/main/java/io/net5/util/Recycler.java similarity index 93% rename from common/src/main/java/io/netty/util/Recycler.java rename to common/src/main/java/io/net5/util/Recycler.java index d6b8941660..6e9f0234e5 100644 --- a/common/src/main/java/io/netty/util/Recycler.java +++ b/common/src/main/java/io/net5/util/Recycler.java @@ -14,13 +14,13 @@ * under the License. */ -package io.netty.util; +package io.net5.util; -import io.netty.util.concurrent.FastThreadLocal; -import io.netty.util.internal.ObjectPool; -import io.netty.util.internal.SystemPropertyUtil; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; +import io.net5.util.concurrent.FastThreadLocal; +import io.net5.util.internal.ObjectPool; +import io.net5.util.internal.SystemPropertyUtil; +import io.net5.util.internal.logging.InternalLogger; +import io.net5.util.internal.logging.InternalLoggerFactory; import java.lang.ref.WeakReference; import java.util.Arrays; @@ -29,7 +29,7 @@ import java.util.WeakHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; -import static io.netty.util.internal.MathUtil.safeFindNextPositivePowerOfTwo; +import static io.net5.util.internal.MathUtil.safeFindNextPositivePowerOfTwo; import static java.lang.Math.max; import static java.lang.Math.min; @@ -59,10 +59,10 @@ public abstract class Recycler { static { // In the future, we might have different maxCapacity for different object types. - // e.g. io.netty.recycler.maxCapacity.writeTask - // io.netty.recycler.maxCapacity.outboundBuffer - int maxCapacityPerThread = SystemPropertyUtil.getInt("io.netty.recycler.maxCapacityPerThread", - SystemPropertyUtil.getInt("io.netty.recycler.maxCapacity", DEFAULT_INITIAL_MAX_CAPACITY_PER_THREAD)); + // e.g. io.net5.recycler.maxCapacity.writeTask + // io.net5.recycler.maxCapacity.outboundBuffer + int maxCapacityPerThread = SystemPropertyUtil.getInt("io.net5.recycler.maxCapacityPerThread", + SystemPropertyUtil.getInt("io.net5.recycler.maxCapacity", DEFAULT_INITIAL_MAX_CAPACITY_PER_THREAD)); if (maxCapacityPerThread < 0) { maxCapacityPerThread = DEFAULT_INITIAL_MAX_CAPACITY_PER_THREAD; } @@ -70,38 +70,38 @@ public abstract class Recycler { DEFAULT_MAX_CAPACITY_PER_THREAD = maxCapacityPerThread; MAX_SHARED_CAPACITY_FACTOR = max(2, - SystemPropertyUtil.getInt("io.netty.recycler.maxSharedCapacityFactor", + SystemPropertyUtil.getInt("io.net5.recycler.maxSharedCapacityFactor", 2)); MAX_DELAYED_QUEUES_PER_THREAD = max(0, - SystemPropertyUtil.getInt("io.netty.recycler.maxDelayedQueuesPerThread", + SystemPropertyUtil.getInt("io.net5.recycler.maxDelayedQueuesPerThread", // We use the same value as default EventLoop number NettyRuntime.availableProcessors() * 2)); LINK_CAPACITY = safeFindNextPositivePowerOfTwo( - max(SystemPropertyUtil.getInt("io.netty.recycler.linkCapacity", 16), 16)); + max(SystemPropertyUtil.getInt("io.net5.recycler.linkCapacity", 16), 16)); // By default we allow one push to a Recycler for each 8th try on handles that were never recycled before. // This should help to slowly increase the capacity of the recycler while not be too sensitive to allocation // bursts. - RATIO = max(0, SystemPropertyUtil.getInt("io.netty.recycler.ratio", 8)); - DELAYED_QUEUE_RATIO = max(0, SystemPropertyUtil.getInt("io.netty.recycler.delayedQueue.ratio", RATIO)); + RATIO = max(0, SystemPropertyUtil.getInt("io.net5.recycler.ratio", 8)); + DELAYED_QUEUE_RATIO = max(0, SystemPropertyUtil.getInt("io.net5.recycler.delayedQueue.ratio", RATIO)); INITIAL_CAPACITY = min(DEFAULT_MAX_CAPACITY_PER_THREAD, 256); if (logger.isDebugEnabled()) { if (DEFAULT_MAX_CAPACITY_PER_THREAD == 0) { - logger.debug("-Dio.netty.recycler.maxCapacityPerThread: disabled"); - logger.debug("-Dio.netty.recycler.maxSharedCapacityFactor: disabled"); - logger.debug("-Dio.netty.recycler.linkCapacity: disabled"); - logger.debug("-Dio.netty.recycler.ratio: disabled"); - logger.debug("-Dio.netty.recycler.delayedQueue.ratio: disabled"); + logger.debug("-Dio.net5.recycler.maxCapacityPerThread: disabled"); + logger.debug("-Dio.net5.recycler.maxSharedCapacityFactor: disabled"); + logger.debug("-Dio.net5.recycler.linkCapacity: disabled"); + logger.debug("-Dio.net5.recycler.ratio: disabled"); + logger.debug("-Dio.net5.recycler.delayedQueue.ratio: disabled"); } else { - logger.debug("-Dio.netty.recycler.maxCapacityPerThread: {}", DEFAULT_MAX_CAPACITY_PER_THREAD); - logger.debug("-Dio.netty.recycler.maxSharedCapacityFactor: {}", MAX_SHARED_CAPACITY_FACTOR); - logger.debug("-Dio.netty.recycler.linkCapacity: {}", LINK_CAPACITY); - logger.debug("-Dio.netty.recycler.ratio: {}", RATIO); - logger.debug("-Dio.netty.recycler.delayedQueue.ratio: {}", DELAYED_QUEUE_RATIO); + logger.debug("-Dio.net5.recycler.maxCapacityPerThread: {}", DEFAULT_MAX_CAPACITY_PER_THREAD); + logger.debug("-Dio.net5.recycler.maxSharedCapacityFactor: {}", MAX_SHARED_CAPACITY_FACTOR); + logger.debug("-Dio.net5.recycler.linkCapacity: {}", LINK_CAPACITY); + logger.debug("-Dio.net5.recycler.ratio: {}", RATIO); + logger.debug("-Dio.net5.recycler.delayedQueue.ratio: {}", DELAYED_QUEUE_RATIO); } } } diff --git a/common/src/main/java/io/netty/util/ReferenceCountUtil.java b/common/src/main/java/io/net5/util/ReferenceCountUtil.java similarity index 97% rename from common/src/main/java/io/netty/util/ReferenceCountUtil.java rename to common/src/main/java/io/net5/util/ReferenceCountUtil.java index f8fa866f70..307aaf8efb 100644 --- a/common/src/main/java/io/netty/util/ReferenceCountUtil.java +++ b/common/src/main/java/io/net5/util/ReferenceCountUtil.java @@ -13,11 +13,11 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util; +package io.net5.util; -import io.netty.util.internal.ObjectUtil; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; +import io.net5.util.internal.ObjectUtil; +import io.net5.util.internal.logging.InternalLogger; +import io.net5.util.internal.logging.InternalLoggerFactory; /** * Collection of method to handle objects that may implement {@link ReferenceCounted}. diff --git a/common/src/main/java/io/netty/util/ReferenceCounted.java b/common/src/main/java/io/net5/util/ReferenceCounted.java similarity index 99% rename from common/src/main/java/io/netty/util/ReferenceCounted.java rename to common/src/main/java/io/net5/util/ReferenceCounted.java index c510bef0c3..35ed448d1b 100644 --- a/common/src/main/java/io/netty/util/ReferenceCounted.java +++ b/common/src/main/java/io/net5/util/ReferenceCounted.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util; +package io.net5.util; /** * A reference-counted object that requires explicit deallocation. diff --git a/common/src/main/java/io/netty/util/ResourceLeakDetector.java b/common/src/main/java/io/net5/util/ResourceLeakDetector.java similarity index 97% rename from common/src/main/java/io/netty/util/ResourceLeakDetector.java rename to common/src/main/java/io/net5/util/ResourceLeakDetector.java index b15c650e8a..28feac2a1e 100644 --- a/common/src/main/java/io/netty/util/ResourceLeakDetector.java +++ b/common/src/main/java/io/net5/util/ResourceLeakDetector.java @@ -14,12 +14,12 @@ * under the License. */ -package io.netty.util; +package io.net5.util; -import io.netty.util.internal.EmptyArrays; -import io.netty.util.internal.SystemPropertyUtil; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; +import io.net5.util.internal.EmptyArrays; +import io.net5.util.internal.SystemPropertyUtil; +import io.net5.util.internal.logging.InternalLogger; +import io.net5.util.internal.logging.InternalLoggerFactory; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; @@ -35,20 +35,20 @@ import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; -import static io.netty.util.internal.StringUtil.EMPTY_STRING; -import static io.netty.util.internal.StringUtil.NEWLINE; -import static io.netty.util.internal.StringUtil.simpleClassName; +import static io.net5.util.internal.StringUtil.EMPTY_STRING; +import static io.net5.util.internal.StringUtil.NEWLINE; +import static io.net5.util.internal.StringUtil.simpleClassName; import static java.util.Objects.requireNonNull; public class ResourceLeakDetector { - private static final String PROP_LEVEL = "io.netty.leakDetection.level"; + private static final String PROP_LEVEL = "io.net5.leakDetection.level"; private static final Level DEFAULT_LEVEL = Level.SIMPLE; - private static final String PROP_TARGET_RECORDS = "io.netty.leakDetection.targetRecords"; + private static final String PROP_TARGET_RECORDS = "io.net5.leakDetection.targetRecords"; private static final int DEFAULT_TARGET_RECORDS = 4; - private static final String PROP_SAMPLING_INTERVAL = "io.netty.leakDetection.samplingInterval"; + private static final String PROP_SAMPLING_INTERVAL = "io.net5.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; diff --git a/common/src/main/java/io/netty/util/ResourceLeakDetectorFactory.java b/common/src/main/java/io/net5/util/ResourceLeakDetectorFactory.java similarity index 94% rename from common/src/main/java/io/netty/util/ResourceLeakDetectorFactory.java rename to common/src/main/java/io/net5/util/ResourceLeakDetectorFactory.java index 3069713ba4..5f13ff9eaf 100644 --- a/common/src/main/java/io/netty/util/ResourceLeakDetectorFactory.java +++ b/common/src/main/java/io/net5/util/ResourceLeakDetectorFactory.java @@ -14,15 +14,15 @@ * under the License. */ -package io.netty.util; +package io.net5.util; import static java.util.Objects.requireNonNull; -import io.netty.util.internal.ObjectUtil; -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.SystemPropertyUtil; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; +import io.net5.util.internal.ObjectUtil; +import io.net5.util.internal.PlatformDependent; +import io.net5.util.internal.SystemPropertyUtil; +import io.net5.util.internal.logging.InternalLogger; +import io.net5.util.internal.logging.InternalLoggerFactory; import java.lang.reflect.Constructor; @@ -85,9 +85,9 @@ public abstract class ResourceLeakDetectorFactory { DefaultResourceLeakDetectorFactory() { String customLeakDetector; try { - customLeakDetector = SystemPropertyUtil.get("io.netty.customResourceLeakDetector"); + customLeakDetector = SystemPropertyUtil.get("io.net5.customResourceLeakDetector"); } catch (Throwable cause) { - logger.error("Could not access System property: io.netty.customResourceLeakDetector", cause); + logger.error("Could not access System property: io.net5.customResourceLeakDetector", cause); customLeakDetector = null; } if (customLeakDetector == null) { diff --git a/common/src/main/java/io/netty/util/ResourceLeakHint.java b/common/src/main/java/io/net5/util/ResourceLeakHint.java similarity index 97% rename from common/src/main/java/io/netty/util/ResourceLeakHint.java rename to common/src/main/java/io/net5/util/ResourceLeakHint.java index 57811db5db..ed4a748a99 100644 --- a/common/src/main/java/io/netty/util/ResourceLeakHint.java +++ b/common/src/main/java/io/net5/util/ResourceLeakHint.java @@ -14,7 +14,7 @@ * under the License. */ -package io.netty.util; +package io.net5.util; /** * A hint object that provides human-readable message for easier resource leak tracking. diff --git a/common/src/main/java/io/netty/util/ResourceLeakTracker.java b/common/src/main/java/io/net5/util/ResourceLeakTracker.java similarity index 98% rename from common/src/main/java/io/netty/util/ResourceLeakTracker.java rename to common/src/main/java/io/net5/util/ResourceLeakTracker.java index eba555d17a..40612ac476 100644 --- a/common/src/main/java/io/netty/util/ResourceLeakTracker.java +++ b/common/src/main/java/io/net5/util/ResourceLeakTracker.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util; +package io.net5.util; public interface ResourceLeakTracker { diff --git a/common/src/main/java/io/netty/util/Signal.java b/common/src/main/java/io/net5/util/Signal.java similarity index 99% rename from common/src/main/java/io/netty/util/Signal.java rename to common/src/main/java/io/net5/util/Signal.java index 4372b673b7..113a8055d3 100644 --- a/common/src/main/java/io/netty/util/Signal.java +++ b/common/src/main/java/io/net5/util/Signal.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util; +package io.net5.util; /** diff --git a/common/src/main/java/io/netty/util/SuppressForbidden.java b/common/src/main/java/io/net5/util/SuppressForbidden.java similarity index 97% rename from common/src/main/java/io/netty/util/SuppressForbidden.java rename to common/src/main/java/io/net5/util/SuppressForbidden.java index 7b6629d89d..4ce5aa8e79 100644 --- a/common/src/main/java/io/netty/util/SuppressForbidden.java +++ b/common/src/main/java/io/net5/util/SuppressForbidden.java @@ -14,7 +14,7 @@ * under the License. */ -package io.netty.util; +package io.net5.util; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/common/src/main/java/io/netty/util/Timeout.java b/common/src/main/java/io/net5/util/Timeout.java similarity index 98% rename from common/src/main/java/io/netty/util/Timeout.java rename to common/src/main/java/io/net5/util/Timeout.java index 7e0df58911..a43915502c 100644 --- a/common/src/main/java/io/netty/util/Timeout.java +++ b/common/src/main/java/io/net5/util/Timeout.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util; +package io.net5.util; /** * A handle associated with a {@link TimerTask} that is returned by a diff --git a/common/src/main/java/io/netty/util/Timer.java b/common/src/main/java/io/net5/util/Timer.java similarity index 98% rename from common/src/main/java/io/netty/util/Timer.java rename to common/src/main/java/io/net5/util/Timer.java index 0a59327843..3db23f812f 100644 --- a/common/src/main/java/io/netty/util/Timer.java +++ b/common/src/main/java/io/net5/util/Timer.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util; +package io.net5.util; import java.util.Set; import java.util.concurrent.RejectedExecutionException; diff --git a/common/src/main/java/io/netty/util/TimerTask.java b/common/src/main/java/io/net5/util/TimerTask.java similarity index 97% rename from common/src/main/java/io/netty/util/TimerTask.java rename to common/src/main/java/io/net5/util/TimerTask.java index b11ef6131e..c91c0d8b99 100644 --- a/common/src/main/java/io/netty/util/TimerTask.java +++ b/common/src/main/java/io/net5/util/TimerTask.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util; +package io.net5.util; import java.util.concurrent.TimeUnit; diff --git a/common/src/main/java/io/netty/util/UncheckedBooleanSupplier.java b/common/src/main/java/io/net5/util/UncheckedBooleanSupplier.java similarity index 98% rename from common/src/main/java/io/netty/util/UncheckedBooleanSupplier.java rename to common/src/main/java/io/net5/util/UncheckedBooleanSupplier.java index 0224a65854..95238fbf7d 100644 --- a/common/src/main/java/io/netty/util/UncheckedBooleanSupplier.java +++ b/common/src/main/java/io/net5/util/UncheckedBooleanSupplier.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util; +package io.net5.util; /** * Represents a supplier of {@code boolean}-valued results which doesn't throw any checked exceptions. diff --git a/common/src/main/java/io/netty/util/Version.java b/common/src/main/java/io/net5/util/Version.java similarity index 97% rename from common/src/main/java/io/netty/util/Version.java rename to common/src/main/java/io/net5/util/Version.java index 34ace818c8..44525dedf7 100644 --- a/common/src/main/java/io/netty/util/Version.java +++ b/common/src/main/java/io/net5/util/Version.java @@ -14,9 +14,9 @@ * under the License. */ -package io.netty.util; +package io.net5.util; -import io.netty.util.internal.PlatformDependent; +import io.net5.util.internal.PlatformDependent; import java.io.InputStream; import java.net.URL; @@ -32,7 +32,7 @@ import java.util.TreeMap; /** * Retrieves the version information of available Netty artifacts. *

- * This class retrieves the version information from {@code META-INF/io.netty.versions.properties}, which is + * This class retrieves the version information from {@code META-INF/io.net5.versions.properties}, which is * generated in build time. Note that it may not be possible to retrieve the information completely, depending on * your environment, such as the specified {@link ClassLoader}, the current {@link SecurityManager}. *

@@ -69,7 +69,7 @@ public final class Version { // Collect all properties. Properties props = new Properties(); try { - Enumeration resources = classLoader.getResources("META-INF/io.netty.versions.properties"); + Enumeration resources = classLoader.getResources("META-INF/io.net5.versions.properties"); while (resources.hasMoreElements()) { URL url = resources.nextElement(); try (InputStream in = url.openStream()) { diff --git a/common/src/main/java/io/netty/util/concurrent/AbstractEventExecutor.java b/common/src/main/java/io/net5/util/concurrent/AbstractEventExecutor.java similarity index 97% rename from common/src/main/java/io/netty/util/concurrent/AbstractEventExecutor.java rename to common/src/main/java/io/net5/util/concurrent/AbstractEventExecutor.java index 1158c248d9..c892f8e102 100644 --- a/common/src/main/java/io/netty/util/concurrent/AbstractEventExecutor.java +++ b/common/src/main/java/io/net5/util/concurrent/AbstractEventExecutor.java @@ -13,10 +13,10 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.concurrent; +package io.net5.util.concurrent; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; +import io.net5.util.internal.logging.InternalLogger; +import io.net5.util.internal.logging.InternalLoggerFactory; import java.util.concurrent.Callable; import java.util.concurrent.Executors; diff --git a/common/src/main/java/io/netty/util/concurrent/AbstractScheduledEventExecutor.java b/common/src/main/java/io/net5/util/concurrent/AbstractScheduledEventExecutor.java similarity index 98% rename from common/src/main/java/io/netty/util/concurrent/AbstractScheduledEventExecutor.java rename to common/src/main/java/io/net5/util/concurrent/AbstractScheduledEventExecutor.java index e61fe326b8..e881643a82 100644 --- a/common/src/main/java/io/netty/util/concurrent/AbstractScheduledEventExecutor.java +++ b/common/src/main/java/io/net5/util/concurrent/AbstractScheduledEventExecutor.java @@ -13,11 +13,11 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.concurrent; +package io.net5.util.concurrent; -import io.netty.util.internal.DefaultPriorityQueue; -import io.netty.util.internal.PriorityQueue; -import io.netty.util.internal.PriorityQueueNode; +import io.net5.util.internal.DefaultPriorityQueue; +import io.net5.util.internal.PriorityQueue; +import io.net5.util.internal.PriorityQueueNode; import java.util.Comparator; import java.util.Queue; diff --git a/common/src/main/java/io/netty/util/concurrent/AsynchronousResult.java b/common/src/main/java/io/net5/util/concurrent/AsynchronousResult.java similarity index 99% rename from common/src/main/java/io/netty/util/concurrent/AsynchronousResult.java rename to common/src/main/java/io/net5/util/concurrent/AsynchronousResult.java index 3dd8690f32..76c4bae9b5 100644 --- a/common/src/main/java/io/netty/util/concurrent/AsynchronousResult.java +++ b/common/src/main/java/io/net5/util/concurrent/AsynchronousResult.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.concurrent; +package io.net5.util.concurrent; import java.util.concurrent.CancellationException; diff --git a/common/src/main/java/io/netty/util/concurrent/BlockingOperationException.java b/common/src/main/java/io/net5/util/concurrent/BlockingOperationException.java similarity index 97% rename from common/src/main/java/io/netty/util/concurrent/BlockingOperationException.java rename to common/src/main/java/io/net5/util/concurrent/BlockingOperationException.java index fefe46f694..935df7a594 100644 --- a/common/src/main/java/io/netty/util/concurrent/BlockingOperationException.java +++ b/common/src/main/java/io/net5/util/concurrent/BlockingOperationException.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.concurrent; +package io.net5.util.concurrent; /** * An {@link IllegalStateException} which is raised when a user performed a blocking operation diff --git a/common/src/main/java/io/netty/util/concurrent/DefaultEventExecutorGroup.java b/common/src/main/java/io/net5/util/concurrent/DefaultEventExecutorGroup.java similarity index 98% rename from common/src/main/java/io/netty/util/concurrent/DefaultEventExecutorGroup.java rename to common/src/main/java/io/net5/util/concurrent/DefaultEventExecutorGroup.java index 5966d6e2a8..9124873875 100644 --- a/common/src/main/java/io/netty/util/concurrent/DefaultEventExecutorGroup.java +++ b/common/src/main/java/io/net5/util/concurrent/DefaultEventExecutorGroup.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.concurrent; +package io.net5.util.concurrent; import java.util.concurrent.ThreadFactory; diff --git a/common/src/main/java/io/netty/util/concurrent/DefaultFutureCompletionStage.java b/common/src/main/java/io/net5/util/concurrent/DefaultFutureCompletionStage.java similarity index 99% rename from common/src/main/java/io/netty/util/concurrent/DefaultFutureCompletionStage.java rename to common/src/main/java/io/net5/util/concurrent/DefaultFutureCompletionStage.java index df37edb1e7..100461a9b4 100644 --- a/common/src/main/java/io/netty/util/concurrent/DefaultFutureCompletionStage.java +++ b/common/src/main/java/io/net5/util/concurrent/DefaultFutureCompletionStage.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.concurrent; +package io.net5.util.concurrent; import java.util.concurrent.CompletionStage; diff --git a/common/src/main/java/io/netty/util/concurrent/DefaultFutureListeners.java b/common/src/main/java/io/net5/util/concurrent/DefaultFutureListeners.java similarity index 97% rename from common/src/main/java/io/netty/util/concurrent/DefaultFutureListeners.java rename to common/src/main/java/io/net5/util/concurrent/DefaultFutureListeners.java index 482aa9c5b2..3226b524e0 100644 --- a/common/src/main/java/io/netty/util/concurrent/DefaultFutureListeners.java +++ b/common/src/main/java/io/net5/util/concurrent/DefaultFutureListeners.java @@ -13,9 +13,9 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.concurrent; +package io.net5.util.concurrent; -import io.netty.util.internal.logging.InternalLogger; +import io.net5.util.internal.logging.InternalLogger; import java.util.Arrays; import java.util.EventListener; diff --git a/common/src/main/java/io/netty/util/concurrent/DefaultPromise.java b/common/src/main/java/io/net5/util/concurrent/DefaultPromise.java similarity index 98% rename from common/src/main/java/io/netty/util/concurrent/DefaultPromise.java rename to common/src/main/java/io/net5/util/concurrent/DefaultPromise.java index a88a4f5050..ed5b510504 100644 --- a/common/src/main/java/io/netty/util/concurrent/DefaultPromise.java +++ b/common/src/main/java/io/net5/util/concurrent/DefaultPromise.java @@ -13,12 +13,12 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.concurrent; +package io.net5.util.concurrent; -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.ThrowableUtil; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; +import io.net5.util.internal.StringUtil; +import io.net5.util.internal.ThrowableUtil; +import io.net5.util.internal.logging.InternalLogger; +import io.net5.util.internal.logging.InternalLoggerFactory; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletionException; diff --git a/common/src/main/java/io/netty/util/concurrent/DefaultThreadFactory.java b/common/src/main/java/io/net5/util/concurrent/DefaultThreadFactory.java similarity index 98% rename from common/src/main/java/io/netty/util/concurrent/DefaultThreadFactory.java rename to common/src/main/java/io/net5/util/concurrent/DefaultThreadFactory.java index 26110de0f2..1f2ea12779 100644 --- a/common/src/main/java/io/netty/util/concurrent/DefaultThreadFactory.java +++ b/common/src/main/java/io/net5/util/concurrent/DefaultThreadFactory.java @@ -14,11 +14,11 @@ * under the License. */ -package io.netty.util.concurrent; +package io.net5.util.concurrent; import static java.util.Objects.requireNonNull; -import io.netty.util.internal.StringUtil; +import io.net5.util.internal.StringUtil; import java.util.Locale; import java.util.concurrent.ThreadFactory; diff --git a/common/src/main/java/io/netty/util/concurrent/EventExecutor.java b/common/src/main/java/io/net5/util/concurrent/EventExecutor.java similarity index 98% rename from common/src/main/java/io/netty/util/concurrent/EventExecutor.java rename to common/src/main/java/io/net5/util/concurrent/EventExecutor.java index 2dc02b0e48..2dfe588b0e 100644 --- a/common/src/main/java/io/netty/util/concurrent/EventExecutor.java +++ b/common/src/main/java/io/net5/util/concurrent/EventExecutor.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.concurrent; +package io.net5.util.concurrent; import java.util.Collections; import java.util.Iterator; diff --git a/common/src/main/java/io/netty/util/concurrent/EventExecutorGroup.java b/common/src/main/java/io/net5/util/concurrent/EventExecutorGroup.java similarity index 98% rename from common/src/main/java/io/netty/util/concurrent/EventExecutorGroup.java rename to common/src/main/java/io/net5/util/concurrent/EventExecutorGroup.java index e2cd2f47ac..057d8ff59b 100644 --- a/common/src/main/java/io/netty/util/concurrent/EventExecutorGroup.java +++ b/common/src/main/java/io/net5/util/concurrent/EventExecutorGroup.java @@ -13,15 +13,15 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.concurrent; +package io.net5.util.concurrent; import java.util.Iterator; import java.util.concurrent.Callable; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; -import static io.netty.util.concurrent.AbstractEventExecutor.DEFAULT_SHUTDOWN_QUIET_PERIOD; -import static io.netty.util.concurrent.AbstractEventExecutor.DEFAULT_SHUTDOWN_TIMEOUT; +import static io.net5.util.concurrent.AbstractEventExecutor.DEFAULT_SHUTDOWN_QUIET_PERIOD; +import static io.net5.util.concurrent.AbstractEventExecutor.DEFAULT_SHUTDOWN_TIMEOUT; /** * The {@link EventExecutorGroup} is responsible for providing the {@link EventExecutor}'s to use diff --git a/common/src/main/java/io/netty/util/concurrent/FastThreadLocal.java b/common/src/main/java/io/net5/util/concurrent/FastThreadLocal.java similarity index 98% rename from common/src/main/java/io/netty/util/concurrent/FastThreadLocal.java rename to common/src/main/java/io/net5/util/concurrent/FastThreadLocal.java index 9431f2635b..bc145bcd8c 100644 --- a/common/src/main/java/io/netty/util/concurrent/FastThreadLocal.java +++ b/common/src/main/java/io/net5/util/concurrent/FastThreadLocal.java @@ -13,10 +13,10 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.concurrent; +package io.net5.util.concurrent; -import io.netty.util.internal.InternalThreadLocalMap; -import io.netty.util.internal.PlatformDependent; +import io.net5.util.internal.InternalThreadLocalMap; +import io.net5.util.internal.PlatformDependent; import java.util.Collections; import java.util.IdentityHashMap; diff --git a/common/src/main/java/io/netty/util/concurrent/FastThreadLocalRunnable.java b/common/src/main/java/io/net5/util/concurrent/FastThreadLocalRunnable.java similarity index 97% rename from common/src/main/java/io/netty/util/concurrent/FastThreadLocalRunnable.java rename to common/src/main/java/io/net5/util/concurrent/FastThreadLocalRunnable.java index 44e0087cd6..1d979f23ed 100644 --- a/common/src/main/java/io/netty/util/concurrent/FastThreadLocalRunnable.java +++ b/common/src/main/java/io/net5/util/concurrent/FastThreadLocalRunnable.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.concurrent; +package io.net5.util.concurrent; import static java.util.Objects.requireNonNull; diff --git a/common/src/main/java/io/netty/util/concurrent/FastThreadLocalThread.java b/common/src/main/java/io/net5/util/concurrent/FastThreadLocalThread.java similarity index 96% rename from common/src/main/java/io/netty/util/concurrent/FastThreadLocalThread.java rename to common/src/main/java/io/net5/util/concurrent/FastThreadLocalThread.java index 3979a75960..3784c9b273 100644 --- a/common/src/main/java/io/netty/util/concurrent/FastThreadLocalThread.java +++ b/common/src/main/java/io/net5/util/concurrent/FastThreadLocalThread.java @@ -13,10 +13,10 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.concurrent; +package io.net5.util.concurrent; -import io.netty.util.internal.InternalThreadLocalMap; -import io.netty.util.internal.UnstableApi; +import io.net5.util.internal.InternalThreadLocalMap; +import io.net5.util.internal.UnstableApi; /** * A special {@link Thread} that provides fast access to {@link FastThreadLocal} variables. diff --git a/common/src/main/java/io/netty/util/concurrent/Future.java b/common/src/main/java/io/net5/util/concurrent/Future.java similarity index 96% rename from common/src/main/java/io/netty/util/concurrent/Future.java rename to common/src/main/java/io/net5/util/concurrent/Future.java index f2d80c85da..0a53cbe239 100644 --- a/common/src/main/java/io/netty/util/concurrent/Future.java +++ b/common/src/main/java/io/net5/util/concurrent/Future.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.concurrent; +package io.net5.util.concurrent; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletionException; @@ -77,16 +77,16 @@ import java.util.function.Function; * until the I/O operation is done and there's relatively expensive cost of inter-thread notification. Moreover, there's * a chance of dead-lock in a particular circumstance, which is described below. * - *

Do not call {@link #await()} inside a {@link io.netty.channel.ChannelHandler}

+ *

Do not call {@link #await()} inside a {@link io.net5.channel.ChannelHandler}

*

- * The event handler methods in {@link io.netty.channel.ChannelHandler} are usually called by an I/O thread. If {@link + * The event handler methods in {@link io.net5.channel.ChannelHandler} are usually called by an I/O thread. If {@link * #await()} is called by an event handler method, which is called by the I/O thread, the I/O operation it is waiting * for might never complete because {@link #await()} can block the I/O operation it is waiting for, which is a * dead-lock. *

  * // BAD - NEVER DO THIS
  * {@code @Override}
- * public void channelRead({@link io.netty.channel.ChannelHandlerContext} ctx, Object msg) {
+ * public void channelRead({@link io.net5.channel.ChannelHandlerContext} ctx, Object msg) {
  *     {@link Future} future = ctx.channel().close();
  *     future.awaitUninterruptibly();
  *     // Perform post-closure operation
@@ -95,7 +95,7 @@ import java.util.function.Function;
  *
  * // GOOD
  * {@code @Override}
- * public void channelRead({@link io.netty.channel.ChannelHandlerContext} ctx, Object msg) {
+ * public void channelRead({@link io.net5.channel.ChannelHandlerContext} ctx, Object msg) {
  *     {@link Future} future = ctx.channel().close();
  *     future.addListener(new {@link FutureListener}() {
  *         public void operationComplete({@link Future} future) {
@@ -118,7 +118,7 @@ import java.util.function.Function;
  * diagram above.  For example, connect timeout should be configured via a transport-specific option:
  * 
  * // BAD - NEVER DO THIS
- * {@link io.netty.bootstrap.Bootstrap} b = ...;
+ * {@link io.net5.bootstrap.Bootstrap} b = ...;
  * {@link Future} f = b.connect(...);
  * f.awaitUninterruptibly(10, TimeUnit.SECONDS);
  * if (f.isCancelled()) {
@@ -132,9 +132,9 @@ import java.util.function.Function;
  * }
  *
  * // GOOD
- * {@link io.netty.bootstrap.Bootstrap} b = ...;
+ * {@link io.net5.bootstrap.Bootstrap} b = ...;
  * // Configure the connect timeout option.
- * b.option({@link io.netty.channel.ChannelOption}.CONNECT_TIMEOUT_MILLIS, 10000);
+ * b.option({@link io.net5.channel.ChannelOption}.CONNECT_TIMEOUT_MILLIS, 10000);
  * {@link Future} f = b.connect(...);
  * f.awaitUninterruptibly();
  *
diff --git a/common/src/main/java/io/netty/util/concurrent/FutureCompletionStage.java b/common/src/main/java/io/net5/util/concurrent/FutureCompletionStage.java
similarity index 99%
rename from common/src/main/java/io/netty/util/concurrent/FutureCompletionStage.java
rename to common/src/main/java/io/net5/util/concurrent/FutureCompletionStage.java
index 8301f96361..ac7749f70f 100644
--- a/common/src/main/java/io/netty/util/concurrent/FutureCompletionStage.java
+++ b/common/src/main/java/io/net5/util/concurrent/FutureCompletionStage.java
@@ -13,9 +13,9 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.util.concurrent;
+package io.net5.util.concurrent;
 
-import io.netty.util.internal.StringUtil;
+import io.net5.util.internal.StringUtil;
 
 import java.util.Objects;
 import java.util.concurrent.CompletableFuture;
diff --git a/common/src/main/java/io/netty/util/concurrent/FutureContextListener.java b/common/src/main/java/io/net5/util/concurrent/FutureContextListener.java
similarity index 97%
rename from common/src/main/java/io/netty/util/concurrent/FutureContextListener.java
rename to common/src/main/java/io/net5/util/concurrent/FutureContextListener.java
index b8f3f2a76b..6a01597a5d 100644
--- a/common/src/main/java/io/netty/util/concurrent/FutureContextListener.java
+++ b/common/src/main/java/io/net5/util/concurrent/FutureContextListener.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.util.concurrent;
+package io.net5.util.concurrent;
 
 import java.util.EventListener;
 
diff --git a/common/src/main/java/io/netty/util/concurrent/FutureListener.java b/common/src/main/java/io/net5/util/concurrent/FutureListener.java
similarity index 97%
rename from common/src/main/java/io/netty/util/concurrent/FutureListener.java
rename to common/src/main/java/io/net5/util/concurrent/FutureListener.java
index 72080c9149..ca00317d14 100644
--- a/common/src/main/java/io/netty/util/concurrent/FutureListener.java
+++ b/common/src/main/java/io/net5/util/concurrent/FutureListener.java
@@ -14,7 +14,7 @@
  * under the License.
  */
 
-package io.netty.util.concurrent;
+package io.net5.util.concurrent;
 
 import java.util.EventListener;
 
diff --git a/common/src/main/java/io/netty/util/concurrent/Futures.java b/common/src/main/java/io/net5/util/concurrent/Futures.java
similarity index 98%
rename from common/src/main/java/io/netty/util/concurrent/Futures.java
rename to common/src/main/java/io/net5/util/concurrent/Futures.java
index 09d00c921c..78aedc5393 100644
--- a/common/src/main/java/io/netty/util/concurrent/Futures.java
+++ b/common/src/main/java/io/net5/util/concurrent/Futures.java
@@ -13,15 +13,15 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.util.concurrent;
+package io.net5.util.concurrent;
 
-import io.netty.util.internal.logging.InternalLogger;
-import io.netty.util.internal.logging.InternalLoggerFactory;
+import io.net5.util.internal.logging.InternalLogger;
+import io.net5.util.internal.logging.InternalLoggerFactory;
 
 import java.util.concurrent.Callable;
 import java.util.function.Function;
 
-import static io.netty.util.internal.PromiseNotificationUtil.tryFailure;
+import static io.net5.util.internal.PromiseNotificationUtil.tryFailure;
 import static java.util.Objects.requireNonNull;
 
 /**
diff --git a/common/src/main/java/io/netty/util/concurrent/GlobalEventExecutor.java b/common/src/main/java/io/net5/util/concurrent/GlobalEventExecutor.java
similarity index 98%
rename from common/src/main/java/io/netty/util/concurrent/GlobalEventExecutor.java
rename to common/src/main/java/io/net5/util/concurrent/GlobalEventExecutor.java
index 14b00ad5ef..f55299f6c3 100644
--- a/common/src/main/java/io/netty/util/concurrent/GlobalEventExecutor.java
+++ b/common/src/main/java/io/net5/util/concurrent/GlobalEventExecutor.java
@@ -13,13 +13,13 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.util.concurrent;
+package io.net5.util.concurrent;
 
 import static java.util.Objects.requireNonNull;
 
-import io.netty.util.internal.ThreadExecutorMap;
-import io.netty.util.internal.logging.InternalLogger;
-import io.netty.util.internal.logging.InternalLoggerFactory;
+import io.net5.util.internal.ThreadExecutorMap;
+import io.net5.util.internal.logging.InternalLogger;
+import io.net5.util.internal.logging.InternalLoggerFactory;
 
 import java.security.AccessController;
 import java.security.PrivilegedAction;
diff --git a/common/src/main/java/io/netty/util/concurrent/ImmediateEventExecutor.java b/common/src/main/java/io/net5/util/concurrent/ImmediateEventExecutor.java
similarity index 97%
rename from common/src/main/java/io/netty/util/concurrent/ImmediateEventExecutor.java
rename to common/src/main/java/io/net5/util/concurrent/ImmediateEventExecutor.java
index 32602f4867..c3a616e0ad 100644
--- a/common/src/main/java/io/netty/util/concurrent/ImmediateEventExecutor.java
+++ b/common/src/main/java/io/net5/util/concurrent/ImmediateEventExecutor.java
@@ -13,10 +13,10 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.util.concurrent;
+package io.net5.util.concurrent;
 
-import io.netty.util.internal.logging.InternalLogger;
-import io.netty.util.internal.logging.InternalLoggerFactory;
+import io.net5.util.internal.logging.InternalLogger;
+import io.net5.util.internal.logging.InternalLoggerFactory;
 
 import java.util.ArrayDeque;
 import java.util.Queue;
diff --git a/common/src/main/java/io/netty/util/concurrent/ImmediateExecutor.java b/common/src/main/java/io/net5/util/concurrent/ImmediateExecutor.java
similarity index 97%
rename from common/src/main/java/io/netty/util/concurrent/ImmediateExecutor.java
rename to common/src/main/java/io/net5/util/concurrent/ImmediateExecutor.java
index cf66d480ca..fbce516176 100644
--- a/common/src/main/java/io/netty/util/concurrent/ImmediateExecutor.java
+++ b/common/src/main/java/io/net5/util/concurrent/ImmediateExecutor.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.util.concurrent;
+package io.net5.util.concurrent;
 
 import static java.util.Objects.requireNonNull;
 
diff --git a/common/src/main/java/io/netty/util/concurrent/MultithreadEventExecutorGroup.java b/common/src/main/java/io/net5/util/concurrent/MultithreadEventExecutorGroup.java
similarity index 98%
rename from common/src/main/java/io/netty/util/concurrent/MultithreadEventExecutorGroup.java
rename to common/src/main/java/io/net5/util/concurrent/MultithreadEventExecutorGroup.java
index b22b2691b1..9abd5b92a0 100644
--- a/common/src/main/java/io/netty/util/concurrent/MultithreadEventExecutorGroup.java
+++ b/common/src/main/java/io/net5/util/concurrent/MultithreadEventExecutorGroup.java
@@ -13,9 +13,9 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.util.concurrent;
+package io.net5.util.concurrent;
 
-import io.netty.util.internal.EmptyArrays;
+import io.net5.util.internal.EmptyArrays;
 
 import java.util.Arrays;
 import java.util.Collections;
@@ -27,7 +27,7 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicLong;
 
-import static io.netty.util.internal.ObjectUtil.checkPositive;
+import static io.net5.util.internal.ObjectUtil.checkPositive;
 
 /**
  * {@link EventExecutorGroup} implementation that handles their tasks with multiple threads at
diff --git a/common/src/main/java/io/netty/util/concurrent/NonStickyEventExecutorGroup.java b/common/src/main/java/io/net5/util/concurrent/NonStickyEventExecutorGroup.java
similarity index 98%
rename from common/src/main/java/io/netty/util/concurrent/NonStickyEventExecutorGroup.java
rename to common/src/main/java/io/net5/util/concurrent/NonStickyEventExecutorGroup.java
index 318dd47c86..da8e9a7234 100644
--- a/common/src/main/java/io/netty/util/concurrent/NonStickyEventExecutorGroup.java
+++ b/common/src/main/java/io/net5/util/concurrent/NonStickyEventExecutorGroup.java
@@ -13,10 +13,10 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.util.concurrent;
+package io.net5.util.concurrent;
 
-import io.netty.util.internal.PlatformDependent;
-import io.netty.util.internal.UnstableApi;
+import io.net5.util.internal.PlatformDependent;
+import io.net5.util.internal.UnstableApi;
 
 import java.util.Iterator;
 import java.util.Queue;
@@ -25,7 +25,7 @@ import java.util.concurrent.RejectedExecutionException;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 
-import static io.netty.util.internal.ObjectUtil.checkPositive;
+import static io.net5.util.internal.ObjectUtil.checkPositive;
 import static java.util.Objects.requireNonNull;
 
 /**
diff --git a/common/src/main/java/io/netty/util/concurrent/OrderedEventExecutor.java b/common/src/main/java/io/net5/util/concurrent/OrderedEventExecutor.java
similarity index 96%
rename from common/src/main/java/io/netty/util/concurrent/OrderedEventExecutor.java
rename to common/src/main/java/io/net5/util/concurrent/OrderedEventExecutor.java
index b77a5ad0f1..b26ca4692e 100644
--- a/common/src/main/java/io/netty/util/concurrent/OrderedEventExecutor.java
+++ b/common/src/main/java/io/net5/util/concurrent/OrderedEventExecutor.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.util.concurrent;
+package io.net5.util.concurrent;
 
 /**
  * Marker interface for {@link EventExecutor}s that will process all submitted tasks in an ordered / serial fashion.
diff --git a/common/src/main/java/io/netty/util/concurrent/Promise.java b/common/src/main/java/io/net5/util/concurrent/Promise.java
similarity index 98%
rename from common/src/main/java/io/netty/util/concurrent/Promise.java
rename to common/src/main/java/io/net5/util/concurrent/Promise.java
index 3ca0fc4572..981742d560 100644
--- a/common/src/main/java/io/netty/util/concurrent/Promise.java
+++ b/common/src/main/java/io/net5/util/concurrent/Promise.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.util.concurrent;
+package io.net5.util.concurrent;
 
 /**
  * Special {@link Future} which is writable.
diff --git a/common/src/main/java/io/netty/util/concurrent/PromiseCombiner.java b/common/src/main/java/io/net5/util/concurrent/PromiseCombiner.java
similarity index 99%
rename from common/src/main/java/io/netty/util/concurrent/PromiseCombiner.java
rename to common/src/main/java/io/net5/util/concurrent/PromiseCombiner.java
index 039e28dc6d..3c0b7c2b6b 100644
--- a/common/src/main/java/io/netty/util/concurrent/PromiseCombiner.java
+++ b/common/src/main/java/io/net5/util/concurrent/PromiseCombiner.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.util.concurrent;
+package io.net5.util.concurrent;
 
 import static java.util.Objects.requireNonNull;
 
diff --git a/common/src/main/java/io/netty/util/concurrent/PromiseTask.java b/common/src/main/java/io/net5/util/concurrent/PromiseTask.java
similarity index 99%
rename from common/src/main/java/io/netty/util/concurrent/PromiseTask.java
rename to common/src/main/java/io/net5/util/concurrent/PromiseTask.java
index 1c88cd7e3d..549871b297 100644
--- a/common/src/main/java/io/netty/util/concurrent/PromiseTask.java
+++ b/common/src/main/java/io/net5/util/concurrent/PromiseTask.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.util.concurrent;
+package io.net5.util.concurrent;
 
 import java.util.concurrent.Callable;
 import java.util.concurrent.RunnableFuture;
diff --git a/common/src/main/java/io/netty/util/concurrent/RejectedExecutionHandler.java b/common/src/main/java/io/net5/util/concurrent/RejectedExecutionHandler.java
similarity index 96%
rename from common/src/main/java/io/netty/util/concurrent/RejectedExecutionHandler.java
rename to common/src/main/java/io/net5/util/concurrent/RejectedExecutionHandler.java
index bf3f7a544c..96b70502ad 100644
--- a/common/src/main/java/io/netty/util/concurrent/RejectedExecutionHandler.java
+++ b/common/src/main/java/io/net5/util/concurrent/RejectedExecutionHandler.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.util.concurrent;
+package io.net5.util.concurrent;
 
 /**
  * Similar to {@link java.util.concurrent.RejectedExecutionHandler} but specific to {@link SingleThreadEventExecutor}.
diff --git a/common/src/main/java/io/netty/util/concurrent/RejectedExecutionHandlers.java b/common/src/main/java/io/net5/util/concurrent/RejectedExecutionHandlers.java
similarity index 97%
rename from common/src/main/java/io/netty/util/concurrent/RejectedExecutionHandlers.java
rename to common/src/main/java/io/net5/util/concurrent/RejectedExecutionHandlers.java
index e640882f45..2eda71dee6 100644
--- a/common/src/main/java/io/netty/util/concurrent/RejectedExecutionHandlers.java
+++ b/common/src/main/java/io/net5/util/concurrent/RejectedExecutionHandlers.java
@@ -13,9 +13,9 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.util.concurrent;
+package io.net5.util.concurrent;
 
-import io.netty.util.internal.ObjectUtil;
+import io.net5.util.internal.ObjectUtil;
 
 import java.util.concurrent.RejectedExecutionException;
 import java.util.concurrent.TimeUnit;
diff --git a/common/src/main/java/io/netty/util/concurrent/RunnableFuture.java b/common/src/main/java/io/net5/util/concurrent/RunnableFuture.java
similarity index 97%
rename from common/src/main/java/io/netty/util/concurrent/RunnableFuture.java
rename to common/src/main/java/io/net5/util/concurrent/RunnableFuture.java
index ab829256ba..832ed18145 100644
--- a/common/src/main/java/io/netty/util/concurrent/RunnableFuture.java
+++ b/common/src/main/java/io/net5/util/concurrent/RunnableFuture.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.util.concurrent;
+package io.net5.util.concurrent;
 
 /**
  * A combination of {@link java.util.concurrent.RunnableFuture} and {@link Future}.
diff --git a/common/src/main/java/io/netty/util/concurrent/RunnableFutureAdapter.java b/common/src/main/java/io/net5/util/concurrent/RunnableFutureAdapter.java
similarity index 98%
rename from common/src/main/java/io/netty/util/concurrent/RunnableFutureAdapter.java
rename to common/src/main/java/io/net5/util/concurrent/RunnableFutureAdapter.java
index 4069d8e4cc..7561b518f0 100644
--- a/common/src/main/java/io/netty/util/concurrent/RunnableFutureAdapter.java
+++ b/common/src/main/java/io/net5/util/concurrent/RunnableFutureAdapter.java
@@ -13,9 +13,9 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.util.concurrent;
+package io.net5.util.concurrent;
 
-import io.netty.util.internal.StringUtil;
+import io.net5.util.internal.StringUtil;
 
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
diff --git a/common/src/main/java/io/netty/util/concurrent/RunnableScheduledFuture.java b/common/src/main/java/io/net5/util/concurrent/RunnableScheduledFuture.java
similarity index 98%
rename from common/src/main/java/io/netty/util/concurrent/RunnableScheduledFuture.java
rename to common/src/main/java/io/net5/util/concurrent/RunnableScheduledFuture.java
index 5376760992..0dda680aa6 100644
--- a/common/src/main/java/io/netty/util/concurrent/RunnableScheduledFuture.java
+++ b/common/src/main/java/io/net5/util/concurrent/RunnableScheduledFuture.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.util.concurrent;
+package io.net5.util.concurrent;
 
 /**
  * A combination of {@link RunnableFuture} and {@link Comparable} (sorting by their next deadline),
diff --git a/common/src/main/java/io/netty/util/concurrent/RunnableScheduledFutureAdapter.java b/common/src/main/java/io/net5/util/concurrent/RunnableScheduledFutureAdapter.java
similarity index 98%
rename from common/src/main/java/io/netty/util/concurrent/RunnableScheduledFutureAdapter.java
rename to common/src/main/java/io/net5/util/concurrent/RunnableScheduledFutureAdapter.java
index 2d1cd510f1..3b2049f5f6 100644
--- a/common/src/main/java/io/netty/util/concurrent/RunnableScheduledFutureAdapter.java
+++ b/common/src/main/java/io/net5/util/concurrent/RunnableScheduledFutureAdapter.java
@@ -14,10 +14,10 @@
  * under the License.
  */
 
-package io.netty.util.concurrent;
+package io.net5.util.concurrent;
 
-import io.netty.util.internal.DefaultPriorityQueue;
-import io.netty.util.internal.StringUtil;
+import io.net5.util.internal.DefaultPriorityQueue;
+import io.net5.util.internal.StringUtil;
 
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
diff --git a/common/src/main/java/io/netty/util/concurrent/SingleThreadEventExecutor.java b/common/src/main/java/io/net5/util/concurrent/SingleThreadEventExecutor.java
similarity index 98%
rename from common/src/main/java/io/netty/util/concurrent/SingleThreadEventExecutor.java
rename to common/src/main/java/io/net5/util/concurrent/SingleThreadEventExecutor.java
index 7ccd11b118..3f1e59d932 100644
--- a/common/src/main/java/io/netty/util/concurrent/SingleThreadEventExecutor.java
+++ b/common/src/main/java/io/net5/util/concurrent/SingleThreadEventExecutor.java
@@ -13,12 +13,12 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.util.concurrent;
+package io.net5.util.concurrent;
 
-import io.netty.util.internal.SystemPropertyUtil;
-import io.netty.util.internal.ThreadExecutorMap;
-import io.netty.util.internal.logging.InternalLogger;
-import io.netty.util.internal.logging.InternalLoggerFactory;
+import io.net5.util.internal.SystemPropertyUtil;
+import io.net5.util.internal.ThreadExecutorMap;
+import io.net5.util.internal.logging.InternalLogger;
+import io.net5.util.internal.logging.InternalLoggerFactory;
 
 import java.lang.Thread.State;
 import java.util.ArrayList;
@@ -45,7 +45,7 @@ import static java.util.Objects.requireNonNull;
 public class SingleThreadEventExecutor extends AbstractScheduledEventExecutor implements OrderedEventExecutor {
 
     protected static final int DEFAULT_MAX_PENDING_EXECUTOR_TASKS = Math.max(16,
-            SystemPropertyUtil.getInt("io.netty.eventexecutor.maxPendingTasks", Integer.MAX_VALUE));
+            SystemPropertyUtil.getInt("io.net5.eventexecutor.maxPendingTasks", Integer.MAX_VALUE));
 
     private static final InternalLogger logger =
             InternalLoggerFactory.getInstance(SingleThreadEventExecutor.class);
diff --git a/common/src/main/java/io/netty/util/concurrent/ThreadPerTaskExecutor.java b/common/src/main/java/io/net5/util/concurrent/ThreadPerTaskExecutor.java
similarity index 97%
rename from common/src/main/java/io/netty/util/concurrent/ThreadPerTaskExecutor.java
rename to common/src/main/java/io/net5/util/concurrent/ThreadPerTaskExecutor.java
index a9ca572921..46b30f9938 100644
--- a/common/src/main/java/io/netty/util/concurrent/ThreadPerTaskExecutor.java
+++ b/common/src/main/java/io/net5/util/concurrent/ThreadPerTaskExecutor.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.util.concurrent;
+package io.net5.util.concurrent;
 
 import static java.util.Objects.requireNonNull;
 
diff --git a/common/src/main/java/io/netty/util/concurrent/ThreadProperties.java b/common/src/main/java/io/net5/util/concurrent/ThreadProperties.java
similarity index 97%
rename from common/src/main/java/io/netty/util/concurrent/ThreadProperties.java
rename to common/src/main/java/io/net5/util/concurrent/ThreadProperties.java
index 650ef7e67d..6600b44cfe 100644
--- a/common/src/main/java/io/netty/util/concurrent/ThreadProperties.java
+++ b/common/src/main/java/io/net5/util/concurrent/ThreadProperties.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.util.concurrent;
+package io.net5.util.concurrent;
 
 /**
  * Expose details for a {@link Thread}.
diff --git a/common/src/main/java/io/netty/util/concurrent/UnorderedThreadPoolEventExecutor.java b/common/src/main/java/io/net5/util/concurrent/UnorderedThreadPoolEventExecutor.java
similarity index 98%
rename from common/src/main/java/io/netty/util/concurrent/UnorderedThreadPoolEventExecutor.java
rename to common/src/main/java/io/net5/util/concurrent/UnorderedThreadPoolEventExecutor.java
index 4273e06c0d..6fd9550d0c 100644
--- a/common/src/main/java/io/netty/util/concurrent/UnorderedThreadPoolEventExecutor.java
+++ b/common/src/main/java/io/net5/util/concurrent/UnorderedThreadPoolEventExecutor.java
@@ -13,10 +13,10 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.util.concurrent;
+package io.net5.util.concurrent;
 
-import io.netty.util.internal.logging.InternalLogger;
-import io.netty.util.internal.logging.InternalLoggerFactory;
+import io.net5.util.internal.logging.InternalLogger;
+import io.net5.util.internal.logging.InternalLoggerFactory;
 
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.Callable;
diff --git a/common/src/main/java/io/netty/util/concurrent/package-info.java b/common/src/main/java/io/net5/util/concurrent/package-info.java
similarity index 95%
rename from common/src/main/java/io/netty/util/concurrent/package-info.java
rename to common/src/main/java/io/net5/util/concurrent/package-info.java
index ac6077b766..60323a5515 100644
--- a/common/src/main/java/io/netty/util/concurrent/package-info.java
+++ b/common/src/main/java/io/net5/util/concurrent/package-info.java
@@ -17,4 +17,4 @@
 /**
  * Utility classes for concurrent / async tasks.
  */
-package io.netty.util.concurrent;
+package io.net5.util.concurrent;
diff --git a/common/src/main/java/io/netty/util/internal/AppendableCharSequence.java b/common/src/main/java/io/net5/util/internal/AppendableCharSequence.java
similarity index 97%
rename from common/src/main/java/io/netty/util/internal/AppendableCharSequence.java
rename to common/src/main/java/io/net5/util/internal/AppendableCharSequence.java
index c7154a317c..c581a30357 100644
--- a/common/src/main/java/io/netty/util/internal/AppendableCharSequence.java
+++ b/common/src/main/java/io/net5/util/internal/AppendableCharSequence.java
@@ -13,10 +13,10 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.util.internal;
+package io.net5.util.internal;
 
-import static io.netty.util.internal.ObjectUtil.checkPositive;
-import static io.netty.util.internal.ObjectUtil.checkNonEmpty;
+import static io.net5.util.internal.ObjectUtil.checkPositive;
+import static io.net5.util.internal.ObjectUtil.checkNonEmpty;
 
 import java.util.Arrays;
 
diff --git a/common/src/main/java/io/netty/util/internal/ClassInitializerUtil.java b/common/src/main/java/io/net5/util/internal/ClassInitializerUtil.java
similarity index 98%
rename from common/src/main/java/io/netty/util/internal/ClassInitializerUtil.java
rename to common/src/main/java/io/net5/util/internal/ClassInitializerUtil.java
index c80af45adc..7b35f09029 100644
--- a/common/src/main/java/io/netty/util/internal/ClassInitializerUtil.java
+++ b/common/src/main/java/io/net5/util/internal/ClassInitializerUtil.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.util.internal;
+package io.net5.util.internal;
 
 /**
  * Utility which ensures that classes are loaded by the {@link ClassLoader}.
diff --git a/common/src/main/java/io/netty/util/internal/Cleaner.java b/common/src/main/java/io/net5/util/internal/Cleaner.java
similarity index 96%
rename from common/src/main/java/io/netty/util/internal/Cleaner.java
rename to common/src/main/java/io/net5/util/internal/Cleaner.java
index d9dd1a8dc8..183ae1f51a 100644
--- a/common/src/main/java/io/netty/util/internal/Cleaner.java
+++ b/common/src/main/java/io/net5/util/internal/Cleaner.java
@@ -13,7 +13,7 @@
 * License for the specific language governing permissions and limitations
 * under the License.
 */
-package io.netty.util.internal;
+package io.net5.util.internal;
 
 import java.nio.ByteBuffer;
 
diff --git a/common/src/main/java/io/netty/util/internal/CleanerJava9.java b/common/src/main/java/io/net5/util/internal/CleanerJava9.java
similarity index 96%
rename from common/src/main/java/io/netty/util/internal/CleanerJava9.java
rename to common/src/main/java/io/net5/util/internal/CleanerJava9.java
index 78688db8c4..b8606b7bac 100644
--- a/common/src/main/java/io/netty/util/internal/CleanerJava9.java
+++ b/common/src/main/java/io/net5/util/internal/CleanerJava9.java
@@ -13,10 +13,10 @@
 * License for the specific language governing permissions and limitations
 * under the License.
 */
-package io.netty.util.internal;
+package io.net5.util.internal;
 
-import io.netty.util.internal.logging.InternalLogger;
-import io.netty.util.internal.logging.InternalLoggerFactory;
+import io.net5.util.internal.logging.InternalLogger;
+import io.net5.util.internal.logging.InternalLoggerFactory;
 
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
diff --git a/common/src/main/java/io/netty/util/internal/ConstantTimeUtils.java b/common/src/main/java/io/net5/util/internal/ConstantTimeUtils.java
similarity index 99%
rename from common/src/main/java/io/netty/util/internal/ConstantTimeUtils.java
rename to common/src/main/java/io/net5/util/internal/ConstantTimeUtils.java
index d328788160..f88991c1b1 100644
--- a/common/src/main/java/io/netty/util/internal/ConstantTimeUtils.java
+++ b/common/src/main/java/io/net5/util/internal/ConstantTimeUtils.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.util.internal;
+package io.net5.util.internal;
 
 public final class ConstantTimeUtils {
     private ConstantTimeUtils() { }
diff --git a/common/src/main/java/io/netty/util/internal/DefaultPriorityQueue.java b/common/src/main/java/io/net5/util/internal/DefaultPriorityQueue.java
similarity index 98%
rename from common/src/main/java/io/netty/util/internal/DefaultPriorityQueue.java
rename to common/src/main/java/io/net5/util/internal/DefaultPriorityQueue.java
index 22a346e871..ecf744855a 100644
--- a/common/src/main/java/io/netty/util/internal/DefaultPriorityQueue.java
+++ b/common/src/main/java/io/net5/util/internal/DefaultPriorityQueue.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.util.internal;
+package io.net5.util.internal;
 
 import java.util.AbstractQueue;
 import java.util.Arrays;
@@ -21,7 +21,7 @@ import java.util.Comparator;
 import java.util.Iterator;
 import java.util.NoSuchElementException;
 
-import static io.netty.util.internal.PriorityQueueNode.INDEX_NOT_IN_QUEUE;
+import static io.net5.util.internal.PriorityQueueNode.INDEX_NOT_IN_QUEUE;
 import static java.util.Objects.requireNonNull;
 
 /**
diff --git a/common/src/main/java/io/netty/util/internal/EmptyArrays.java b/common/src/main/java/io/net5/util/internal/EmptyArrays.java
similarity index 95%
rename from common/src/main/java/io/netty/util/internal/EmptyArrays.java
rename to common/src/main/java/io/net5/util/internal/EmptyArrays.java
index 0908343aa2..2afa522748 100644
--- a/common/src/main/java/io/netty/util/internal/EmptyArrays.java
+++ b/common/src/main/java/io/net5/util/internal/EmptyArrays.java
@@ -14,9 +14,9 @@
  * under the License.
  */
 
-package io.netty.util.internal;
+package io.net5.util.internal;
 
-import io.netty.util.AsciiString;
+import io.net5.util.AsciiString;
 
 import java.nio.ByteBuffer;
 import java.security.cert.Certificate;
diff --git a/common/src/main/java/io/netty/util/internal/EmptyPriorityQueue.java b/common/src/main/java/io/net5/util/internal/EmptyPriorityQueue.java
similarity index 99%
rename from common/src/main/java/io/netty/util/internal/EmptyPriorityQueue.java
rename to common/src/main/java/io/net5/util/internal/EmptyPriorityQueue.java
index fe19bd390a..a63b98724a 100644
--- a/common/src/main/java/io/netty/util/internal/EmptyPriorityQueue.java
+++ b/common/src/main/java/io/net5/util/internal/EmptyPriorityQueue.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.util.internal;
+package io.net5.util.internal;
 
 import java.util.Collection;
 import java.util.Collections;
diff --git a/common/src/main/java/io/netty/util/internal/Hidden.java b/common/src/main/java/io/net5/util/internal/Hidden.java
similarity index 70%
rename from common/src/main/java/io/netty/util/internal/Hidden.java
rename to common/src/main/java/io/net5/util/internal/Hidden.java
index e83502eb75..178893598f 100644
--- a/common/src/main/java/io/netty/util/internal/Hidden.java
+++ b/common/src/main/java/io/net5/util/internal/Hidden.java
@@ -14,9 +14,9 @@
  * under the License.
  */
 
-package io.netty.util.internal;
+package io.net5.util.internal;
 
-import io.netty.util.concurrent.FastThreadLocalThread;
+import io.net5.util.concurrent.FastThreadLocalThread;
 import reactor.blockhound.BlockHound;
 import reactor.blockhound.integration.BlockHoundIntegration;
 
@@ -38,69 +38,69 @@ class Hidden {
         @Override
         public void applyTo(BlockHound.Builder builder) {
             builder.allowBlockingCallsInside(
-                    "io.netty.channel.nio.NioEventLoop",
+                    "io.net5.channel.nio.NioEventLoop",
                     "handleLoopException"
             );
 
             builder.allowBlockingCallsInside(
-                    "io.netty.channel.kqueue.KQueueEventLoop",
+                    "io.net5.channel.kqueue.KQueueEventLoop",
                     "handleLoopException"
             );
 
             builder.allowBlockingCallsInside(
-                    "io.netty.channel.epoll.EpollEventLoop",
+                    "io.net5.channel.epoll.EpollEventLoop",
                     "handleLoopException"
             );
 
             builder.allowBlockingCallsInside(
-                    "io.netty.util.HashedWheelTimer",
+                    "io.net5.util.HashedWheelTimer",
                     "start"
             );
 
             builder.allowBlockingCallsInside(
-                    "io.netty.util.HashedWheelTimer",
+                    "io.net5.util.HashedWheelTimer",
                     "stop"
             );
 
             builder.allowBlockingCallsInside(
-                    "io.netty.util.HashedWheelTimer$Worker",
+                    "io.net5.util.HashedWheelTimer$Worker",
                     "waitForNextTick"
             );
 
             builder.allowBlockingCallsInside(
-                    "io.netty.util.concurrent.SingleThreadEventExecutor",
+                    "io.net5.util.concurrent.SingleThreadEventExecutor",
                     "confirmShutdown"
             );
-            builder.allowBlockingCallsInside("io.netty.util.concurrent.GlobalEventExecutor",
+            builder.allowBlockingCallsInside("io.net5.util.concurrent.GlobalEventExecutor",
                     "addTask");
 
-            builder.allowBlockingCallsInside("io.netty.util.concurrent.GlobalEventExecutor",
+            builder.allowBlockingCallsInside("io.net5.util.concurrent.GlobalEventExecutor",
                     "takeTask");
 
             builder.allowBlockingCallsInside(
-                    "io.netty.util.concurrent.SingleThreadEventExecutor",
+                    "io.net5.util.concurrent.SingleThreadEventExecutor",
                     "addTask");
 
             builder.allowBlockingCallsInside(
-                    "io.netty.util.concurrent.SingleThreadEventExecutor",
+                    "io.net5.util.concurrent.SingleThreadEventExecutor",
                     "takeTask");
 
             builder.allowBlockingCallsInside(
-                    "io.netty.handler.ssl.SslHandler",
+                    "io.net5.handler.ssl.SslHandler",
                     "handshake"
             );
 
             builder.allowBlockingCallsInside(
-                    "io.netty.handler.ssl.SslHandler",
+                    "io.net5.handler.ssl.SslHandler",
                     "runAllDelegatedTasks"
             );
             builder.allowBlockingCallsInside(
-                    "io.netty.handler.ssl.SslHandler",
+                    "io.net5.handler.ssl.SslHandler",
                     "runDelegatedTasks"
             );
 
             builder.allowBlockingCallsInside(
-                    "io.netty.handler.ssl.ReferenceCountedOpenSslClientContext$ExtendedTrustManagerVerifyCallback",
+                    "io.net5.handler.ssl.ReferenceCountedOpenSslClientContext$ExtendedTrustManagerVerifyCallback",
                     "verify");
 
             // Let's whitelist SSLEngineImpl.unwrap(...) for now as it may fail otherwise for TLS 1.3.
@@ -114,19 +114,19 @@ class Hidden {
                     "wrap");
 
             builder.allowBlockingCallsInside(
-                    "io.netty.resolver.dns.UnixResolverDnsServerAddressStreamProvider",
+                    "io.net5.resolver.dns.UnixResolverDnsServerAddressStreamProvider",
                     "parse");
 
             builder.allowBlockingCallsInside(
-                    "io.netty.resolver.dns.UnixResolverDnsServerAddressStreamProvider",
+                    "io.net5.resolver.dns.UnixResolverDnsServerAddressStreamProvider",
                     "parseEtcResolverSearchDomains");
 
             builder.allowBlockingCallsInside(
-                    "io.netty.resolver.dns.UnixResolverDnsServerAddressStreamProvider",
+                    "io.net5.resolver.dns.UnixResolverDnsServerAddressStreamProvider",
                     "parseEtcResolverOptions");
 
             builder.allowBlockingCallsInside(
-                    "io.netty.resolver.HostsFileEntriesProvider$ParserImpl",
+                    "io.net5.resolver.HostsFileEntriesProvider$ParserImpl",
                     "parse");
 
             builder.nonBlockingThreadPredicate(p -> thread ->
diff --git a/common/src/main/java/io/netty/util/internal/InternalThreadLocalMap.java b/common/src/main/java/io/net5/util/internal/InternalThreadLocalMap.java
similarity index 94%
rename from common/src/main/java/io/netty/util/internal/InternalThreadLocalMap.java
rename to common/src/main/java/io/net5/util/internal/InternalThreadLocalMap.java
index f137d76db1..f6cd05f887 100644
--- a/common/src/main/java/io/netty/util/internal/InternalThreadLocalMap.java
+++ b/common/src/main/java/io/net5/util/internal/InternalThreadLocalMap.java
@@ -14,12 +14,12 @@
  * under the License.
  */
 
-package io.netty.util.internal;
+package io.net5.util.internal;
 
-import io.netty.util.concurrent.FastThreadLocal;
-import io.netty.util.concurrent.FastThreadLocalThread;
-import io.netty.util.internal.logging.InternalLogger;
-import io.netty.util.internal.logging.InternalLoggerFactory;
+import io.net5.util.concurrent.FastThreadLocal;
+import io.net5.util.concurrent.FastThreadLocalThread;
+import io.net5.util.internal.logging.InternalLogger;
+import io.net5.util.internal.logging.InternalLoggerFactory;
 
 import java.nio.charset.Charset;
 import java.nio.charset.CharsetDecoder;
@@ -76,11 +76,11 @@ public final class InternalThreadLocalMap {
 
     static {
         STRING_BUILDER_INITIAL_SIZE =
-                SystemPropertyUtil.getInt("io.netty.threadLocalMap.stringBuilder.initialSize", 1024);
-        logger.debug("-Dio.netty.threadLocalMap.stringBuilder.initialSize: {}", STRING_BUILDER_INITIAL_SIZE);
+                SystemPropertyUtil.getInt("io.net5.threadLocalMap.stringBuilder.initialSize", 1024);
+        logger.debug("-Dio.net5.threadLocalMap.stringBuilder.initialSize: {}", STRING_BUILDER_INITIAL_SIZE);
 
-        STRING_BUILDER_MAX_SIZE = SystemPropertyUtil.getInt("io.netty.threadLocalMap.stringBuilder.maxSize", 1024 * 4);
-        logger.debug("-Dio.netty.threadLocalMap.stringBuilder.maxSize: {}", STRING_BUILDER_MAX_SIZE);
+        STRING_BUILDER_MAX_SIZE = SystemPropertyUtil.getInt("io.net5.threadLocalMap.stringBuilder.maxSize", 1024 * 4);
+        logger.debug("-Dio.net5.threadLocalMap.stringBuilder.maxSize: {}", STRING_BUILDER_MAX_SIZE);
     }
 
     public static InternalThreadLocalMap getIfSet() {
diff --git a/common/src/main/java/io/netty/util/internal/LongLongHashMap.java b/common/src/main/java/io/net5/util/internal/LongLongHashMap.java
similarity index 99%
rename from common/src/main/java/io/netty/util/internal/LongLongHashMap.java
rename to common/src/main/java/io/net5/util/internal/LongLongHashMap.java
index eff70406f7..690f7fada6 100644
--- a/common/src/main/java/io/netty/util/internal/LongLongHashMap.java
+++ b/common/src/main/java/io/net5/util/internal/LongLongHashMap.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.util.internal;
+package io.net5.util.internal;
 
 /**
  * Internal primitive map implementation that is specifically optimised for the runs availability map use case in
diff --git a/common/src/main/java/io/netty/util/internal/LongPriorityQueue.java b/common/src/main/java/io/net5/util/internal/LongPriorityQueue.java
similarity index 98%
rename from common/src/main/java/io/netty/util/internal/LongPriorityQueue.java
rename to common/src/main/java/io/net5/util/internal/LongPriorityQueue.java
index f08c883762..f92bd2c981 100644
--- a/common/src/main/java/io/netty/util/internal/LongPriorityQueue.java
+++ b/common/src/main/java/io/net5/util/internal/LongPriorityQueue.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.util.internal;
+package io.net5.util.internal;
 
 import java.util.Arrays;
 
diff --git a/common/src/main/java/io/netty/util/internal/MacAddressUtil.java b/common/src/main/java/io/net5/util/internal/MacAddressUtil.java
similarity index 97%
rename from common/src/main/java/io/netty/util/internal/MacAddressUtil.java
rename to common/src/main/java/io/net5/util/internal/MacAddressUtil.java
index da3fa5c6c7..0431561fab 100644
--- a/common/src/main/java/io/netty/util/internal/MacAddressUtil.java
+++ b/common/src/main/java/io/net5/util/internal/MacAddressUtil.java
@@ -14,11 +14,11 @@
  * under the License.
  */
 
-package io.netty.util.internal;
+package io.net5.util.internal;
 
-import io.netty.util.NetUtil;
-import io.netty.util.internal.logging.InternalLogger;
-import io.netty.util.internal.logging.InternalLoggerFactory;
+import io.net5.util.NetUtil;
+import io.net5.util.internal.logging.InternalLogger;
+import io.net5.util.internal.logging.InternalLoggerFactory;
 
 import java.net.InetAddress;
 import java.net.NetworkInterface;
@@ -30,7 +30,7 @@ import java.util.Map;
 import java.util.Map.Entry;
 import java.util.concurrent.ThreadLocalRandom;
 
-import static io.netty.util.internal.EmptyArrays.EMPTY_BYTES;
+import static io.net5.util.internal.EmptyArrays.EMPTY_BYTES;
 
 public final class MacAddressUtil {
     private static final InternalLogger logger = InternalLoggerFactory.getInstance(MacAddressUtil.class);
diff --git a/common/src/main/java/io/netty/util/internal/MathUtil.java b/common/src/main/java/io/net5/util/internal/MathUtil.java
similarity index 98%
rename from common/src/main/java/io/netty/util/internal/MathUtil.java
rename to common/src/main/java/io/net5/util/internal/MathUtil.java
index 7b0f687748..2a41dfaa58 100644
--- a/common/src/main/java/io/netty/util/internal/MathUtil.java
+++ b/common/src/main/java/io/net5/util/internal/MathUtil.java
@@ -12,7 +12,7 @@
  * or implied. See the License for the specific language governing permissions and limitations under
  * the License.
  */
-package io.netty.util.internal;
+package io.net5.util.internal;
 
 /**
  * Math utility methods.
diff --git a/common/src/main/java/io/netty/util/internal/NativeLibraryLoader.java b/common/src/main/java/io/net5/util/internal/NativeLibraryLoader.java
similarity index 95%
rename from common/src/main/java/io/netty/util/internal/NativeLibraryLoader.java
rename to common/src/main/java/io/net5/util/internal/NativeLibraryLoader.java
index 1236f0f339..9894ba92ef 100644
--- a/common/src/main/java/io/netty/util/internal/NativeLibraryLoader.java
+++ b/common/src/main/java/io/net5/util/internal/NativeLibraryLoader.java
@@ -13,11 +13,11 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.util.internal;
+package io.net5.util.internal;
 
-import io.netty.util.CharsetUtil;
-import io.netty.util.internal.logging.InternalLogger;
-import io.netty.util.internal.logging.InternalLoggerFactory;
+import io.net5.util.CharsetUtil;
+import io.net5.util.internal.logging.InternalLogger;
+import io.net5.util.internal.logging.InternalLoggerFactory;
 
 import java.io.ByteArrayOutputStream;
 import java.io.Closeable;
@@ -56,7 +56,7 @@ public final class NativeLibraryLoader {
             "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".getBytes(CharsetUtil.US_ASCII);
 
     static {
-        String workdir = SystemPropertyUtil.get("io.netty.native.workdir");
+        String workdir = SystemPropertyUtil.get("io.net5.native.workdir");
         if (workdir != null) {
             File f = new File(workdir);
             f.mkdirs();
@@ -68,19 +68,19 @@ public final class NativeLibraryLoader {
             }
 
             WORKDIR = f;
-            logger.debug("-Dio.netty.native.workdir: " + WORKDIR);
+            logger.debug("-Dio.net5.native.workdir: " + WORKDIR);
         } else {
             WORKDIR = PlatformDependent.tmpdir();
-            logger.debug("-Dio.netty.native.workdir: " + WORKDIR + " (io.netty.tmpdir)");
+            logger.debug("-Dio.net5.native.workdir: " + WORKDIR + " (io.net5.tmpdir)");
         }
 
         DELETE_NATIVE_LIB_AFTER_LOADING = SystemPropertyUtil.getBoolean(
-                "io.netty.native.deleteLibAfterLoading", true);
-        logger.debug("-Dio.netty.native.deleteLibAfterLoading: {}", DELETE_NATIVE_LIB_AFTER_LOADING);
+                "io.net5.native.deleteLibAfterLoading", true);
+        logger.debug("-Dio.net5.native.deleteLibAfterLoading: {}", DELETE_NATIVE_LIB_AFTER_LOADING);
 
         TRY_TO_PATCH_SHADED_ID = SystemPropertyUtil.getBoolean(
-                "io.netty.native.tryPatchShadedId", true);
-        logger.debug("-Dio.netty.native.tryPatchShadedId: {}", TRY_TO_PATCH_SHADED_ID);
+                "io.net5.native.tryPatchShadedId", true);
+        logger.debug("-Dio.net5.native.tryPatchShadedId: {}", TRY_TO_PATCH_SHADED_ID);
     }
 
     /**
@@ -203,13 +203,13 @@ public final class NativeLibraryLoader {
             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
+                    // Pass "io.net5.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 -D{}=[path] " +
                                 "to set native working directory separately.",
-                                tmpFile.getPath(), "io.netty.native.workdir");
+                                tmpFile.getPath(), "io.net5.native.workdir");
                 }
             } catch (Throwable t) {
                 suppressed.add(t);
diff --git a/common/src/main/java/io/netty/util/internal/NativeLibraryUtil.java b/common/src/main/java/io/net5/util/internal/NativeLibraryUtil.java
similarity index 98%
rename from common/src/main/java/io/netty/util/internal/NativeLibraryUtil.java
rename to common/src/main/java/io/net5/util/internal/NativeLibraryUtil.java
index 9f52c592c9..ee9a33afe7 100644
--- a/common/src/main/java/io/netty/util/internal/NativeLibraryUtil.java
+++ b/common/src/main/java/io/net5/util/internal/NativeLibraryUtil.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.util.internal;
+package io.net5.util.internal;
 
 /**
  * A Utility to Call the {@link System#load(String)} or {@link System#loadLibrary(String)}.
diff --git a/common/src/main/java/io/netty/util/internal/NoOpTypeParameterMatcher.java b/common/src/main/java/io/net5/util/internal/NoOpTypeParameterMatcher.java
similarity index 96%
rename from common/src/main/java/io/netty/util/internal/NoOpTypeParameterMatcher.java
rename to common/src/main/java/io/net5/util/internal/NoOpTypeParameterMatcher.java
index 60448480fb..233bfe39b8 100644
--- a/common/src/main/java/io/netty/util/internal/NoOpTypeParameterMatcher.java
+++ b/common/src/main/java/io/net5/util/internal/NoOpTypeParameterMatcher.java
@@ -14,7 +14,7 @@
  * under the License.
  */
 
-package io.netty.util.internal;
+package io.net5.util.internal;
 
 public final class NoOpTypeParameterMatcher extends TypeParameterMatcher {
     @Override
diff --git a/common/src/main/java/io/netty/util/internal/ObjectCleaner.java b/common/src/main/java/io/net5/util/internal/ObjectCleaner.java
similarity index 96%
rename from common/src/main/java/io/netty/util/internal/ObjectCleaner.java
rename to common/src/main/java/io/net5/util/internal/ObjectCleaner.java
index f02d685537..5d3308864e 100644
--- a/common/src/main/java/io/netty/util/internal/ObjectCleaner.java
+++ b/common/src/main/java/io/net5/util/internal/ObjectCleaner.java
@@ -13,9 +13,9 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.util.internal;
+package io.net5.util.internal;
 
-import io.netty.util.concurrent.FastThreadLocalThread;
+import io.net5.util.concurrent.FastThreadLocalThread;
 
 import java.lang.ref.ReferenceQueue;
 import java.lang.ref.WeakReference;
@@ -25,7 +25,7 @@ import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicBoolean;
 
-import static io.netty.util.internal.SystemPropertyUtil.getInt;
+import static io.net5.util.internal.SystemPropertyUtil.getInt;
 import static java.lang.Math.max;
 import static java.util.Objects.requireNonNull;
 
@@ -35,7 +35,7 @@ import static java.util.Objects.requireNonNull;
  */
 public final class ObjectCleaner {
     private static final int REFERENCE_QUEUE_POLL_TIMEOUT_MS =
-            max(500, getInt("io.netty.util.internal.ObjectCleaner.refQueuePollTimeout", 10000));
+            max(500, getInt("io.net5.util.internal.ObjectCleaner.refQueuePollTimeout", 10000));
 
     // Package-private for testing
     static final String CLEANER_THREAD_NAME = ObjectCleaner.class.getSimpleName() + "Thread";
diff --git a/common/src/main/java/io/netty/util/internal/ObjectPool.java b/common/src/main/java/io/net5/util/internal/ObjectPool.java
similarity index 97%
rename from common/src/main/java/io/netty/util/internal/ObjectPool.java
rename to common/src/main/java/io/net5/util/internal/ObjectPool.java
index 1727da20db..436988b741 100644
--- a/common/src/main/java/io/netty/util/internal/ObjectPool.java
+++ b/common/src/main/java/io/net5/util/internal/ObjectPool.java
@@ -13,9 +13,9 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.util.internal;
+package io.net5.util.internal;
 
-import io.netty.util.Recycler;
+import io.net5.util.Recycler;
 
 import java.util.Objects;
 
diff --git a/common/src/main/java/io/netty/util/internal/ObjectUtil.java b/common/src/main/java/io/net5/util/internal/ObjectUtil.java
similarity index 99%
rename from common/src/main/java/io/netty/util/internal/ObjectUtil.java
rename to common/src/main/java/io/net5/util/internal/ObjectUtil.java
index 674af247de..21efb476b2 100644
--- a/common/src/main/java/io/netty/util/internal/ObjectUtil.java
+++ b/common/src/main/java/io/net5/util/internal/ObjectUtil.java
@@ -12,7 +12,7 @@
  * or implied. See the License for the specific language governing permissions and limitations under
  * the License.
  */
-package io.netty.util.internal;
+package io.net5.util.internal;
 
 import static java.util.Objects.requireNonNull;
 
diff --git a/common/src/main/java/io/netty/util/internal/OutOfDirectMemoryError.java b/common/src/main/java/io/net5/util/internal/OutOfDirectMemoryError.java
similarity index 97%
rename from common/src/main/java/io/netty/util/internal/OutOfDirectMemoryError.java
rename to common/src/main/java/io/net5/util/internal/OutOfDirectMemoryError.java
index d9169497dd..1534a7f10a 100644
--- a/common/src/main/java/io/netty/util/internal/OutOfDirectMemoryError.java
+++ b/common/src/main/java/io/net5/util/internal/OutOfDirectMemoryError.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.util.internal;
+package io.net5.util.internal;
 
 import java.nio.ByteBuffer;
 
diff --git a/common/src/main/java/io/netty/util/internal/PendingWrite.java b/common/src/main/java/io/net5/util/internal/PendingWrite.java
similarity index 93%
rename from common/src/main/java/io/netty/util/internal/PendingWrite.java
rename to common/src/main/java/io/net5/util/internal/PendingWrite.java
index 8f67b8d45b..3073e1d00e 100644
--- a/common/src/main/java/io/netty/util/internal/PendingWrite.java
+++ b/common/src/main/java/io/net5/util/internal/PendingWrite.java
@@ -13,11 +13,11 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.util.internal;
+package io.net5.util.internal;
 
-import io.netty.util.ReferenceCountUtil;
-import io.netty.util.concurrent.Promise;
-import io.netty.util.internal.ObjectPool.Handle;
+import io.net5.util.ReferenceCountUtil;
+import io.net5.util.concurrent.Promise;
+import io.net5.util.internal.ObjectPool.Handle;
 
 /**
  * Some pending write which should be picked up later.
diff --git a/common/src/main/java/io/netty/util/internal/PlatformDependent.java b/common/src/main/java/io/net5/util/internal/PlatformDependent.java
similarity index 96%
rename from common/src/main/java/io/netty/util/internal/PlatformDependent.java
rename to common/src/main/java/io/net5/util/internal/PlatformDependent.java
index 4e33e538bf..dab95fe07a 100644
--- a/common/src/main/java/io/netty/util/internal/PlatformDependent.java
+++ b/common/src/main/java/io/net5/util/internal/PlatformDependent.java
@@ -13,11 +13,11 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package io.netty.util.internal;
+package io.net5.util.internal;
 
-import io.netty.util.CharsetUtil;
-import io.netty.util.internal.logging.InternalLogger;
-import io.netty.util.internal.logging.InternalLoggerFactory;
+import io.net5.util.CharsetUtil;
+import io.net5.util.internal.logging.InternalLogger;
+import io.net5.util.internal.logging.InternalLoggerFactory;
 import org.jctools.queues.MpscArrayQueue;
 import org.jctools.queues.MpscChunkedArrayQueue;
 import org.jctools.queues.MpscUnboundedArrayQueue;
@@ -55,11 +55,11 @@ import java.util.concurrent.atomic.AtomicLong;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import static io.netty.util.internal.PlatformDependent0.HASH_CODE_ASCII_SEED;
-import static io.netty.util.internal.PlatformDependent0.HASH_CODE_C1;
-import static io.netty.util.internal.PlatformDependent0.HASH_CODE_C2;
-import static io.netty.util.internal.PlatformDependent0.hashCodeAsciiSanitize;
-import static io.netty.util.internal.PlatformDependent0.unalignedAccess;
+import static io.net5.util.internal.PlatformDependent0.HASH_CODE_ASCII_SEED;
+import static io.net5.util.internal.PlatformDependent0.HASH_CODE_C1;
+import static io.net5.util.internal.PlatformDependent0.HASH_CODE_C2;
+import static io.net5.util.internal.PlatformDependent0.hashCodeAsciiSanitize;
+import static io.net5.util.internal.PlatformDependent0.unalignedAccess;
 import static java.lang.Math.max;
 import static java.lang.Math.min;
 
@@ -69,7 +69,7 @@ import static java.lang.Math.min;
  * {@code sun.misc.Unsafe} object.
  * 

* You can disable the use of {@code sun.misc.Unsafe} if you specify - * the system property io.netty.noUnsafe. + * the system property io.net5.noUnsafe. */ public final class PlatformDependent { @@ -131,7 +131,7 @@ public final class PlatformDependent { // * == 0 - Use cleaner, Netty will not enforce max memory, and instead will defer to JDK. // * > 0 - Don't use cleaner. This will limit Netty's total direct memory // (note: that JDK's direct memory limit is independent of this). - long maxDirectMemory = SystemPropertyUtil.getLong("io.netty.maxDirectMemory", -1); + long maxDirectMemory = SystemPropertyUtil.getLong("io.net5.maxDirectMemory", -1); if (maxDirectMemory == 0 || !hasUnsafe() || !PlatformDependent0.hasDirectBufferNoCleanerConstructor()) { USE_DIRECT_BUFFER_NO_CLEANER = false; @@ -149,14 +149,14 @@ public final class PlatformDependent { DIRECT_MEMORY_COUNTER = new AtomicLong(); } } - logger.debug("-Dio.netty.maxDirectMemory: {} bytes", maxDirectMemory); + logger.debug("-Dio.net5.maxDirectMemory: {} bytes", maxDirectMemory); DIRECT_MEMORY_LIMIT = maxDirectMemory >= 1 ? maxDirectMemory : MAX_DIRECT_MEMORY; int tryAllocateUninitializedArray = - SystemPropertyUtil.getInt("io.netty.uninitializedArrayAllocationThreshold", 1024); + SystemPropertyUtil.getInt("io.net5.uninitializedArrayAllocationThreshold", 1024); UNINITIALIZED_ARRAY_ALLOCATION_THRESHOLD = javaVersion() >= 9 && PlatformDependent0.hasAllocateArrayMethod() ? tryAllocateUninitializedArray : -1; - logger.debug("-Dio.netty.uninitializedArrayAllocationThreshold: {}", UNINITIALIZED_ARRAY_ALLOCATION_THRESHOLD); + logger.debug("-Dio.net5.uninitializedArrayAllocationThreshold: {}", UNINITIALIZED_ARRAY_ALLOCATION_THRESHOLD); MAYBE_SUPER_USER = maybeSuperUser0(); @@ -170,9 +170,9 @@ public final class PlatformDependent { // 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); + && !SystemPropertyUtil.getBoolean("io.net5.noPreferDirect", false); if (logger.isDebugEnabled()) { - logger.debug("-Dio.netty.noPreferDirect: {}", !DIRECT_BUFFER_PREFERRED); + logger.debug("-Dio.net5.noPreferDirect: {}", !DIRECT_BUFFER_PREFERRED); } /* @@ -323,7 +323,7 @@ public final class PlatformDependent { /** * Returns {@code true} if the platform has reliable low-level direct buffer access API and a user has not specified - * {@code -Dio.netty.noPreferDirect} option. + * {@code -Dio.net5.noPreferDirect} option. */ public static boolean directBufferPreferred() { return DIRECT_BUFFER_PREFERRED; @@ -1172,15 +1172,15 @@ public final class PlatformDependent { private static File tmpdir0() { File f; try { - f = toDirectory(SystemPropertyUtil.get("io.netty.tmpdir")); + f = toDirectory(SystemPropertyUtil.get("io.net5.tmpdir")); if (f != null) { - logger.debug("-Dio.netty.tmpdir: {}", f); + logger.debug("-Dio.net5.tmpdir: {}", f); return f; } f = toDirectory(SystemPropertyUtil.get("java.io.tmpdir")); if (f != null) { - logger.debug("-Dio.netty.tmpdir: {} (java.io.tmpdir)", f); + logger.debug("-Dio.net5.tmpdir: {} (java.io.tmpdir)", f); return f; } @@ -1188,7 +1188,7 @@ public final class PlatformDependent { if (isWindows()) { f = toDirectory(System.getenv("TEMP")); if (f != null) { - logger.debug("-Dio.netty.tmpdir: {} (%TEMP%)", f); + logger.debug("-Dio.net5.tmpdir: {} (%TEMP%)", f); return f; } @@ -1196,20 +1196,20 @@ public final class PlatformDependent { if (userprofile != null) { f = toDirectory(userprofile + "\\AppData\\Local\\Temp"); if (f != null) { - logger.debug("-Dio.netty.tmpdir: {} (%USERPROFILE%\\AppData\\Local\\Temp)", f); + logger.debug("-Dio.net5.tmpdir: {} (%USERPROFILE%\\AppData\\Local\\Temp)", f); return f; } f = toDirectory(userprofile + "\\Local Settings\\Temp"); if (f != null) { - logger.debug("-Dio.netty.tmpdir: {} (%USERPROFILE%\\Local Settings\\Temp)", f); + logger.debug("-Dio.net5.tmpdir: {} (%USERPROFILE%\\Local Settings\\Temp)", f); return f; } } } else { f = toDirectory(System.getenv("TMPDIR")); if (f != null) { - logger.debug("-Dio.netty.tmpdir: {} ($TMPDIR)", f); + logger.debug("-Dio.net5.tmpdir: {} ($TMPDIR)", f); return f; } } @@ -1250,21 +1250,21 @@ public final class PlatformDependent { private static int bitMode0() { // Check user-specified bit mode first. - int bitMode = SystemPropertyUtil.getInt("io.netty.bitMode", 0); + int bitMode = SystemPropertyUtil.getInt("io.net5.bitMode", 0); if (bitMode > 0) { - logger.debug("-Dio.netty.bitMode: {}", bitMode); + logger.debug("-Dio.net5.bitMode: {}", bitMode); return bitMode; } // And then the vendor specific ones which is probably most reliable. bitMode = SystemPropertyUtil.getInt("sun.arch.data.model", 0); if (bitMode > 0) { - logger.debug("-Dio.netty.bitMode: {} (sun.arch.data.model)", bitMode); + logger.debug("-Dio.net5.bitMode: {} (sun.arch.data.model)", bitMode); return bitMode; } bitMode = SystemPropertyUtil.getInt("com.ibm.vm.bitmode", 0); if (bitMode > 0) { - logger.debug("-Dio.netty.bitMode: {} (com.ibm.vm.bitmode)", bitMode); + logger.debug("-Dio.net5.bitMode: {} (com.ibm.vm.bitmode)", bitMode); return bitMode; } @@ -1277,7 +1277,7 @@ public final class PlatformDependent { } if (bitMode > 0) { - logger.debug("-Dio.netty.bitMode: {} (os.arch: {})", bitMode, arch); + logger.debug("-Dio.net5.bitMode: {} (os.arch: {})", bitMode, arch); } // Last resort: guess from VM name and then fall back to most common 64-bit mode. diff --git a/common/src/main/java/io/netty/util/internal/PlatformDependent0.java b/common/src/main/java/io/net5/util/internal/PlatformDependent0.java similarity index 98% rename from common/src/main/java/io/netty/util/internal/PlatformDependent0.java rename to common/src/main/java/io/net5/util/internal/PlatformDependent0.java index 748d674c31..bd226b827f 100644 --- a/common/src/main/java/io/netty/util/internal/PlatformDependent0.java +++ b/common/src/main/java/io/net5/util/internal/PlatformDependent0.java @@ -13,10 +13,10 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal; +package io.net5.util.internal; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; +import io.net5.util.internal.logging.InternalLogger; +import io.net5.util.internal.logging.InternalLoggerFactory; import sun.misc.Unsafe; import java.lang.invoke.MethodHandle; @@ -407,18 +407,18 @@ final class PlatformDependent0 { } private static Throwable explicitNoUnsafeCause0() { - final boolean noUnsafe = SystemPropertyUtil.getBoolean("io.netty.noUnsafe", false); - logger.debug("-Dio.netty.noUnsafe: {}", noUnsafe); + final boolean noUnsafe = SystemPropertyUtil.getBoolean("io.net5.noUnsafe", false); + logger.debug("-Dio.net5.noUnsafe: {}", noUnsafe); if (noUnsafe) { - logger.debug("sun.misc.Unsafe: unavailable (io.netty.noUnsafe)"); - return new UnsupportedOperationException("sun.misc.Unsafe: unavailable (io.netty.noUnsafe)"); + logger.debug("sun.misc.Unsafe: unavailable (io.net5.noUnsafe)"); + return new UnsupportedOperationException("sun.misc.Unsafe: unavailable (io.net5.noUnsafe)"); } - boolean tryUnsafe = SystemPropertyUtil.getBoolean("io.netty.tryUnsafe", true); + boolean tryUnsafe = SystemPropertyUtil.getBoolean("io.net5.tryUnsafe", true); if (!tryUnsafe) { - String msg = "sun.misc.Unsafe: unavailable (io.netty.tryUnsafe)"; + String msg = "sun.misc.Unsafe: unavailable (io.net5.tryUnsafe)"; logger.debug(msg); return new UnsupportedOperationException(msg); } @@ -902,7 +902,7 @@ final class PlatformDependent0 { private static boolean explicitTryReflectionSetAccessible0() { // we disable reflective access - return SystemPropertyUtil.getBoolean("io.netty.tryReflectionSetAccessible", javaVersion() < 9); + return SystemPropertyUtil.getBoolean("io.net5.tryReflectionSetAccessible", javaVersion() < 9); } static boolean isExplicitTryReflectionSetAccessible() { diff --git a/common/src/main/java/io/netty/util/internal/PriorityQueue.java b/common/src/main/java/io/net5/util/internal/PriorityQueue.java similarity index 98% rename from common/src/main/java/io/netty/util/internal/PriorityQueue.java rename to common/src/main/java/io/net5/util/internal/PriorityQueue.java index 8e9f281fc0..f8bc491ef0 100644 --- a/common/src/main/java/io/netty/util/internal/PriorityQueue.java +++ b/common/src/main/java/io/net5/util/internal/PriorityQueue.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal; +package io.net5.util.internal; import java.util.Queue; diff --git a/common/src/main/java/io/netty/util/internal/PriorityQueueNode.java b/common/src/main/java/io/net5/util/internal/PriorityQueueNode.java similarity index 98% rename from common/src/main/java/io/netty/util/internal/PriorityQueueNode.java rename to common/src/main/java/io/net5/util/internal/PriorityQueueNode.java index a8b0852ed0..94eae3fb04 100644 --- a/common/src/main/java/io/netty/util/internal/PriorityQueueNode.java +++ b/common/src/main/java/io/net5/util/internal/PriorityQueueNode.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal; +package io.net5.util.internal; /** * Provides methods for {@link DefaultPriorityQueue} to maintain internal state. These methods should generally not be diff --git a/common/src/main/java/io/netty/util/internal/PromiseNotificationUtil.java b/common/src/main/java/io/net5/util/internal/PromiseNotificationUtil.java similarity index 95% rename from common/src/main/java/io/netty/util/internal/PromiseNotificationUtil.java rename to common/src/main/java/io/net5/util/internal/PromiseNotificationUtil.java index a216f0b855..2060064f6e 100644 --- a/common/src/main/java/io/netty/util/internal/PromiseNotificationUtil.java +++ b/common/src/main/java/io/net5/util/internal/PromiseNotificationUtil.java @@ -13,10 +13,10 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal; +package io.net5.util.internal; -import io.netty.util.concurrent.Promise; -import io.netty.util.internal.logging.InternalLogger; +import io.net5.util.concurrent.Promise; +import io.net5.util.internal.logging.InternalLogger; /** * Internal utilities to notify {@link Promise}s. diff --git a/common/src/main/java/io/netty/util/internal/ReadOnlyIterator.java b/common/src/main/java/io/net5/util/internal/ReadOnlyIterator.java similarity index 97% rename from common/src/main/java/io/netty/util/internal/ReadOnlyIterator.java rename to common/src/main/java/io/net5/util/internal/ReadOnlyIterator.java index 51e5e8dbb7..7d4637e976 100644 --- a/common/src/main/java/io/netty/util/internal/ReadOnlyIterator.java +++ b/common/src/main/java/io/net5/util/internal/ReadOnlyIterator.java @@ -14,7 +14,7 @@ * under the License. */ -package io.netty.util.internal; +package io.net5.util.internal; import static java.util.Objects.requireNonNull; diff --git a/common/src/main/java/io/netty/util/internal/RecyclableArrayList.java b/common/src/main/java/io/net5/util/internal/RecyclableArrayList.java similarity index 98% rename from common/src/main/java/io/netty/util/internal/RecyclableArrayList.java rename to common/src/main/java/io/net5/util/internal/RecyclableArrayList.java index e09aeda87c..10b5948b5a 100644 --- a/common/src/main/java/io/netty/util/internal/RecyclableArrayList.java +++ b/common/src/main/java/io/net5/util/internal/RecyclableArrayList.java @@ -14,11 +14,11 @@ * under the License. */ -package io.netty.util.internal; +package io.net5.util.internal; import static java.util.Objects.requireNonNull; -import io.netty.util.internal.ObjectPool.Handle; +import io.net5.util.internal.ObjectPool.Handle; import java.util.ArrayList; import java.util.Collection; diff --git a/common/src/main/java/io/netty/util/internal/ReferenceCountUpdater.java b/common/src/main/java/io/net5/util/internal/ReferenceCountUpdater.java similarity index 97% rename from common/src/main/java/io/netty/util/internal/ReferenceCountUpdater.java rename to common/src/main/java/io/net5/util/internal/ReferenceCountUpdater.java index 93d12bb29b..519b1628e4 100644 --- a/common/src/main/java/io/netty/util/internal/ReferenceCountUpdater.java +++ b/common/src/main/java/io/net5/util/internal/ReferenceCountUpdater.java @@ -13,14 +13,14 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal; +package io.net5.util.internal; -import static io.netty.util.internal.ObjectUtil.checkPositive; +import static io.net5.util.internal.ObjectUtil.checkPositive; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; -import io.netty.util.IllegalReferenceCountException; -import io.netty.util.ReferenceCounted; +import io.net5.util.IllegalReferenceCountException; +import io.net5.util.ReferenceCounted; /** * Common logic for {@link ReferenceCounted} implementations diff --git a/common/src/main/java/io/netty/util/internal/ReflectionUtil.java b/common/src/main/java/io/net5/util/internal/ReflectionUtil.java similarity index 98% rename from common/src/main/java/io/netty/util/internal/ReflectionUtil.java rename to common/src/main/java/io/net5/util/internal/ReflectionUtil.java index 7e69a85f77..6dbb760056 100644 --- a/common/src/main/java/io/netty/util/internal/ReflectionUtil.java +++ b/common/src/main/java/io/net5/util/internal/ReflectionUtil.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal; +package io.net5.util.internal; import java.lang.reflect.AccessibleObject; diff --git a/common/src/main/java/io/netty/util/internal/ResourcesUtil.java b/common/src/main/java/io/net5/util/internal/ResourcesUtil.java similarity index 97% rename from common/src/main/java/io/netty/util/internal/ResourcesUtil.java rename to common/src/main/java/io/net5/util/internal/ResourcesUtil.java index c84df30a5c..5da45951d5 100644 --- a/common/src/main/java/io/netty/util/internal/ResourcesUtil.java +++ b/common/src/main/java/io/net5/util/internal/ResourcesUtil.java @@ -12,7 +12,7 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package io.netty.util.internal; +package io.net5.util.internal; import java.io.File; import java.io.UnsupportedEncodingException; diff --git a/common/src/main/java/io/netty/util/internal/SocketUtils.java b/common/src/main/java/io/net5/util/internal/SocketUtils.java similarity index 99% rename from common/src/main/java/io/netty/util/internal/SocketUtils.java rename to common/src/main/java/io/net5/util/internal/SocketUtils.java index 0499811478..aa0a5dcdc4 100644 --- a/common/src/main/java/io/netty/util/internal/SocketUtils.java +++ b/common/src/main/java/io/net5/util/internal/SocketUtils.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal; +package io.net5.util.internal; import java.io.IOException; import java.net.InetAddress; diff --git a/common/src/main/java/io/netty/util/internal/StringUtil.java b/common/src/main/java/io/net5/util/internal/StringUtil.java similarity index 99% rename from common/src/main/java/io/netty/util/internal/StringUtil.java rename to common/src/main/java/io/net5/util/internal/StringUtil.java index 68d4f28dfe..6d4256e483 100644 --- a/common/src/main/java/io/netty/util/internal/StringUtil.java +++ b/common/src/main/java/io/net5/util/internal/StringUtil.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal; +package io.net5.util.internal; import java.io.IOException; import java.io.UncheckedIOException; diff --git a/common/src/main/java/io/netty/util/internal/SuppressJava6Requirement.java b/common/src/main/java/io/net5/util/internal/SuppressJava6Requirement.java similarity index 97% rename from common/src/main/java/io/netty/util/internal/SuppressJava6Requirement.java rename to common/src/main/java/io/net5/util/internal/SuppressJava6Requirement.java index 353e8a11c5..6b9a1bad2a 100644 --- a/common/src/main/java/io/netty/util/internal/SuppressJava6Requirement.java +++ b/common/src/main/java/io/net5/util/internal/SuppressJava6Requirement.java @@ -14,7 +14,7 @@ * under the License. */ -package io.netty.util.internal; +package io.net5.util.internal; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/common/src/main/java/io/netty/util/internal/SystemPropertyUtil.java b/common/src/main/java/io/net5/util/internal/SystemPropertyUtil.java similarity index 96% rename from common/src/main/java/io/netty/util/internal/SystemPropertyUtil.java rename to common/src/main/java/io/net5/util/internal/SystemPropertyUtil.java index d899e34fd9..d2241700ae 100644 --- a/common/src/main/java/io/netty/util/internal/SystemPropertyUtil.java +++ b/common/src/main/java/io/net5/util/internal/SystemPropertyUtil.java @@ -13,12 +13,12 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal; +package io.net5.util.internal; -import static io.netty.util.internal.ObjectUtil.checkNonEmpty; +import static io.net5.util.internal.ObjectUtil.checkNonEmpty; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; +import io.net5.util.internal.logging.InternalLogger; +import io.net5.util.internal.logging.InternalLoggerFactory; import java.security.AccessController; import java.security.PrivilegedAction; diff --git a/common/src/main/java/io/netty/util/internal/ThreadExecutorMap.java b/common/src/main/java/io/net5/util/internal/ThreadExecutorMap.java similarity index 96% rename from common/src/main/java/io/netty/util/internal/ThreadExecutorMap.java rename to common/src/main/java/io/net5/util/internal/ThreadExecutorMap.java index 0de051349c..f5dd298c2f 100644 --- a/common/src/main/java/io/netty/util/internal/ThreadExecutorMap.java +++ b/common/src/main/java/io/net5/util/internal/ThreadExecutorMap.java @@ -13,10 +13,10 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal; +package io.net5.util.internal; -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.FastThreadLocal; +import io.net5.util.concurrent.EventExecutor; +import io.net5.util.concurrent.FastThreadLocal; import java.util.Objects; import java.util.concurrent.Executor; diff --git a/common/src/main/java/io/netty/util/internal/ThrowableUtil.java b/common/src/main/java/io/net5/util/internal/ThrowableUtil.java similarity index 98% rename from common/src/main/java/io/netty/util/internal/ThrowableUtil.java rename to common/src/main/java/io/net5/util/internal/ThrowableUtil.java index 407d6b8f57..4a038831da 100644 --- a/common/src/main/java/io/netty/util/internal/ThrowableUtil.java +++ b/common/src/main/java/io/net5/util/internal/ThrowableUtil.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal; +package io.net5.util.internal; import java.io.ByteArrayOutputStream; import java.io.IOException; diff --git a/common/src/main/java/io/netty/util/internal/TypeParameterMatcher.java b/common/src/main/java/io/net5/util/internal/TypeParameterMatcher.java similarity index 99% rename from common/src/main/java/io/netty/util/internal/TypeParameterMatcher.java rename to common/src/main/java/io/net5/util/internal/TypeParameterMatcher.java index 348702d098..d19999d217 100644 --- a/common/src/main/java/io/netty/util/internal/TypeParameterMatcher.java +++ b/common/src/main/java/io/net5/util/internal/TypeParameterMatcher.java @@ -14,7 +14,7 @@ * under the License. */ -package io.netty.util.internal; +package io.net5.util.internal; import java.lang.reflect.Array; import java.lang.reflect.GenericArrayType; diff --git a/common/src/main/java/io/netty/util/internal/UnstableApi.java b/common/src/main/java/io/net5/util/internal/UnstableApi.java similarity index 97% rename from common/src/main/java/io/netty/util/internal/UnstableApi.java rename to common/src/main/java/io/net5/util/internal/UnstableApi.java index 14056a1aa8..c02a6eaaef 100644 --- a/common/src/main/java/io/netty/util/internal/UnstableApi.java +++ b/common/src/main/java/io/net5/util/internal/UnstableApi.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal; +package io.net5.util.internal; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; diff --git a/common/src/main/java/io/netty/util/internal/logging/AbstractInternalLogger.java b/common/src/main/java/io/net5/util/internal/logging/AbstractInternalLogger.java similarity index 98% rename from common/src/main/java/io/netty/util/internal/logging/AbstractInternalLogger.java rename to common/src/main/java/io/net5/util/internal/logging/AbstractInternalLogger.java index ecae0606b7..ff5cd9839f 100644 --- a/common/src/main/java/io/netty/util/internal/logging/AbstractInternalLogger.java +++ b/common/src/main/java/io/net5/util/internal/logging/AbstractInternalLogger.java @@ -13,11 +13,11 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal.logging; +package io.net5.util.internal.logging; import static java.util.Objects.requireNonNull; -import io.netty.util.internal.StringUtil; +import io.net5.util.internal.StringUtil; import java.io.ObjectStreamException; import java.io.Serializable; diff --git a/common/src/main/java/io/netty/util/internal/logging/CommonsLogger.java b/common/src/main/java/io/net5/util/internal/logging/CommonsLogger.java similarity index 99% rename from common/src/main/java/io/netty/util/internal/logging/CommonsLogger.java rename to common/src/main/java/io/net5/util/internal/logging/CommonsLogger.java index 9407e81179..68e3d287dc 100644 --- a/common/src/main/java/io/netty/util/internal/logging/CommonsLogger.java +++ b/common/src/main/java/io/net5/util/internal/logging/CommonsLogger.java @@ -37,7 +37,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package io.netty.util.internal.logging; +package io.net5.util.internal.logging; import static java.util.Objects.requireNonNull; diff --git a/common/src/main/java/io/netty/util/internal/logging/CommonsLoggerFactory.java b/common/src/main/java/io/net5/util/internal/logging/CommonsLoggerFactory.java similarity index 97% rename from common/src/main/java/io/netty/util/internal/logging/CommonsLoggerFactory.java rename to common/src/main/java/io/net5/util/internal/logging/CommonsLoggerFactory.java index b0405c1c7b..e8a1ccd55d 100644 --- a/common/src/main/java/io/netty/util/internal/logging/CommonsLoggerFactory.java +++ b/common/src/main/java/io/net5/util/internal/logging/CommonsLoggerFactory.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal.logging; +package io.net5.util.internal.logging; import org.apache.commons.logging.LogFactory; diff --git a/common/src/main/java/io/netty/util/internal/logging/FormattingTuple.java b/common/src/main/java/io/net5/util/internal/logging/FormattingTuple.java similarity index 98% rename from common/src/main/java/io/netty/util/internal/logging/FormattingTuple.java rename to common/src/main/java/io/net5/util/internal/logging/FormattingTuple.java index bcbdb24787..995b7014d1 100644 --- a/common/src/main/java/io/netty/util/internal/logging/FormattingTuple.java +++ b/common/src/main/java/io/net5/util/internal/logging/FormattingTuple.java @@ -37,7 +37,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package io.netty.util.internal.logging; +package io.net5.util.internal.logging; /** * Holds the results of formatting done by {@link MessageFormatter}. diff --git a/common/src/main/java/io/netty/util/internal/logging/InternalLogLevel.java b/common/src/main/java/io/net5/util/internal/logging/InternalLogLevel.java similarity index 96% rename from common/src/main/java/io/netty/util/internal/logging/InternalLogLevel.java rename to common/src/main/java/io/net5/util/internal/logging/InternalLogLevel.java index d15d5261bc..ffb36dbd0d 100644 --- a/common/src/main/java/io/netty/util/internal/logging/InternalLogLevel.java +++ b/common/src/main/java/io/net5/util/internal/logging/InternalLogLevel.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal.logging; +package io.net5.util.internal.logging; /** * The log level that {@link InternalLogger} can log at. diff --git a/common/src/main/java/io/netty/util/internal/logging/InternalLogger.java b/common/src/main/java/io/net5/util/internal/logging/InternalLogger.java similarity index 99% rename from common/src/main/java/io/netty/util/internal/logging/InternalLogger.java rename to common/src/main/java/io/net5/util/internal/logging/InternalLogger.java index f05dfd649e..ccac14d524 100644 --- a/common/src/main/java/io/netty/util/internal/logging/InternalLogger.java +++ b/common/src/main/java/io/net5/util/internal/logging/InternalLogger.java @@ -37,7 +37,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package io.netty.util.internal.logging; +package io.net5.util.internal.logging; /** * Internal-use-only logger used by Netty. DO NOT diff --git a/common/src/main/java/io/netty/util/internal/logging/InternalLoggerFactory.java b/common/src/main/java/io/net5/util/internal/logging/InternalLoggerFactory.java similarity index 99% rename from common/src/main/java/io/netty/util/internal/logging/InternalLoggerFactory.java rename to common/src/main/java/io/net5/util/internal/logging/InternalLoggerFactory.java index 7da180c82c..8862d67724 100644 --- a/common/src/main/java/io/netty/util/internal/logging/InternalLoggerFactory.java +++ b/common/src/main/java/io/net5/util/internal/logging/InternalLoggerFactory.java @@ -14,7 +14,7 @@ * under the License. */ -package io.netty.util.internal.logging; +package io.net5.util.internal.logging; import static java.util.Objects.requireNonNull; diff --git a/common/src/main/java/io/netty/util/internal/logging/JdkLogger.java b/common/src/main/java/io/net5/util/internal/logging/JdkLogger.java similarity index 99% rename from common/src/main/java/io/netty/util/internal/logging/JdkLogger.java rename to common/src/main/java/io/net5/util/internal/logging/JdkLogger.java index 791c007edf..b77081ab1b 100644 --- a/common/src/main/java/io/netty/util/internal/logging/JdkLogger.java +++ b/common/src/main/java/io/net5/util/internal/logging/JdkLogger.java @@ -37,7 +37,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package io.netty.util.internal.logging; +package io.net5.util.internal.logging; import java.util.logging.Level; import java.util.logging.LogRecord; diff --git a/common/src/main/java/io/netty/util/internal/logging/JdkLoggerFactory.java b/common/src/main/java/io/net5/util/internal/logging/JdkLoggerFactory.java similarity index 96% rename from common/src/main/java/io/netty/util/internal/logging/JdkLoggerFactory.java rename to common/src/main/java/io/net5/util/internal/logging/JdkLoggerFactory.java index df2c2ed5ba..a071b4c3c8 100644 --- a/common/src/main/java/io/netty/util/internal/logging/JdkLoggerFactory.java +++ b/common/src/main/java/io/net5/util/internal/logging/JdkLoggerFactory.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal.logging; +package io.net5.util.internal.logging; import java.util.logging.Logger; diff --git a/common/src/main/java/io/netty/util/internal/logging/LocationAwareSlf4JLogger.java b/common/src/main/java/io/net5/util/internal/logging/LocationAwareSlf4JLogger.java similarity index 99% rename from common/src/main/java/io/netty/util/internal/logging/LocationAwareSlf4JLogger.java rename to common/src/main/java/io/net5/util/internal/logging/LocationAwareSlf4JLogger.java index b1838830d0..9a66cbf077 100644 --- a/common/src/main/java/io/netty/util/internal/logging/LocationAwareSlf4JLogger.java +++ b/common/src/main/java/io/net5/util/internal/logging/LocationAwareSlf4JLogger.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal.logging; +package io.net5.util.internal.logging; import org.slf4j.spi.LocationAwareLogger; diff --git a/common/src/main/java/io/netty/util/internal/logging/Log4J2Logger.java b/common/src/main/java/io/net5/util/internal/logging/Log4J2Logger.java similarity index 97% rename from common/src/main/java/io/netty/util/internal/logging/Log4J2Logger.java rename to common/src/main/java/io/net5/util/internal/logging/Log4J2Logger.java index 184a6b3aaa..6c3215fbb9 100644 --- a/common/src/main/java/io/netty/util/internal/logging/Log4J2Logger.java +++ b/common/src/main/java/io/net5/util/internal/logging/Log4J2Logger.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal.logging; +package io.net5.util.internal.logging; import org.apache.logging.log4j.Level; @@ -24,7 +24,7 @@ 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; +import static io.net5.util.internal.logging.AbstractInternalLogger.EXCEPTION_MESSAGE; class Log4J2Logger extends ExtendedLoggerWrapper implements InternalLogger { diff --git a/common/src/main/java/io/netty/util/internal/logging/Log4J2LoggerFactory.java b/common/src/main/java/io/net5/util/internal/logging/Log4J2LoggerFactory.java similarity index 96% rename from common/src/main/java/io/netty/util/internal/logging/Log4J2LoggerFactory.java rename to common/src/main/java/io/net5/util/internal/logging/Log4J2LoggerFactory.java index b84b12b252..aa09c7e293 100644 --- a/common/src/main/java/io/netty/util/internal/logging/Log4J2LoggerFactory.java +++ b/common/src/main/java/io/net5/util/internal/logging/Log4J2LoggerFactory.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal.logging; +package io.net5.util.internal.logging; import org.apache.logging.log4j.LogManager; diff --git a/common/src/main/java/io/netty/util/internal/logging/Log4JLogger.java b/common/src/main/java/io/net5/util/internal/logging/Log4JLogger.java similarity index 99% rename from common/src/main/java/io/netty/util/internal/logging/Log4JLogger.java rename to common/src/main/java/io/net5/util/internal/logging/Log4JLogger.java index 20e0ff3874..df8ff7c3fa 100644 --- a/common/src/main/java/io/netty/util/internal/logging/Log4JLogger.java +++ b/common/src/main/java/io/net5/util/internal/logging/Log4JLogger.java @@ -37,7 +37,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package io.netty.util.internal.logging; +package io.net5.util.internal.logging; import org.apache.log4j.Level; import org.apache.log4j.Logger; diff --git a/common/src/main/java/io/netty/util/internal/logging/Log4JLoggerFactory.java b/common/src/main/java/io/net5/util/internal/logging/Log4JLoggerFactory.java similarity index 96% rename from common/src/main/java/io/netty/util/internal/logging/Log4JLoggerFactory.java rename to common/src/main/java/io/net5/util/internal/logging/Log4JLoggerFactory.java index 3d56907f38..244599b573 100644 --- a/common/src/main/java/io/netty/util/internal/logging/Log4JLoggerFactory.java +++ b/common/src/main/java/io/net5/util/internal/logging/Log4JLoggerFactory.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal.logging; +package io.net5.util.internal.logging; import org.apache.log4j.Logger; diff --git a/common/src/main/java/io/netty/util/internal/logging/MessageFormatter.java b/common/src/main/java/io/net5/util/internal/logging/MessageFormatter.java similarity index 99% rename from common/src/main/java/io/netty/util/internal/logging/MessageFormatter.java rename to common/src/main/java/io/net5/util/internal/logging/MessageFormatter.java index a1746a6a87..06b9e7c676 100644 --- a/common/src/main/java/io/netty/util/internal/logging/MessageFormatter.java +++ b/common/src/main/java/io/net5/util/internal/logging/MessageFormatter.java @@ -37,7 +37,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package io.netty.util.internal.logging; +package io.net5.util.internal.logging; import java.text.MessageFormat; import java.util.HashSet; diff --git a/common/src/main/java/io/netty/util/internal/logging/Slf4JLogger.java b/common/src/main/java/io/net5/util/internal/logging/Slf4JLogger.java similarity index 99% rename from common/src/main/java/io/netty/util/internal/logging/Slf4JLogger.java rename to common/src/main/java/io/net5/util/internal/logging/Slf4JLogger.java index 43489f4c3b..9e71608bec 100644 --- a/common/src/main/java/io/netty/util/internal/logging/Slf4JLogger.java +++ b/common/src/main/java/io/net5/util/internal/logging/Slf4JLogger.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal.logging; +package io.net5.util.internal.logging; import org.slf4j.Logger; diff --git a/common/src/main/java/io/netty/util/internal/logging/Slf4JLoggerFactory.java b/common/src/main/java/io/net5/util/internal/logging/Slf4JLoggerFactory.java similarity index 97% rename from common/src/main/java/io/netty/util/internal/logging/Slf4JLoggerFactory.java rename to common/src/main/java/io/net5/util/internal/logging/Slf4JLoggerFactory.java index 512b7b5351..f1e577f6b0 100644 --- a/common/src/main/java/io/netty/util/internal/logging/Slf4JLoggerFactory.java +++ b/common/src/main/java/io/net5/util/internal/logging/Slf4JLoggerFactory.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal.logging; +package io.net5.util.internal.logging; import org.slf4j.Logger; diff --git a/common/src/main/java/io/netty/util/internal/logging/package-info.java b/common/src/main/java/io/net5/util/internal/logging/package-info.java similarity index 94% rename from common/src/main/java/io/netty/util/internal/logging/package-info.java rename to common/src/main/java/io/net5/util/internal/logging/package-info.java index 38c5c55c99..47d684642a 100644 --- a/common/src/main/java/io/netty/util/internal/logging/package-info.java +++ b/common/src/main/java/io/net5/util/internal/logging/package-info.java @@ -17,4 +17,4 @@ /** * Internal-use-only logging API which is not allowed to be used outside Netty. */ -package io.netty.util.internal.logging; +package io.net5.util.internal.logging; diff --git a/common/src/main/java/io/netty/util/internal/package-info.java b/common/src/main/java/io/net5/util/internal/package-info.java similarity index 95% rename from common/src/main/java/io/netty/util/internal/package-info.java rename to common/src/main/java/io/net5/util/internal/package-info.java index 890a99c8ce..54b43972cc 100644 --- a/common/src/main/java/io/netty/util/internal/package-info.java +++ b/common/src/main/java/io/net5/util/internal/package-info.java @@ -18,4 +18,4 @@ * Internal-use-only utilities which is not allowed to be used * outside Netty. */ -package io.netty.util.internal; +package io.net5.util.internal; diff --git a/common/src/main/java/io/netty/util/internal/svm/PlatformDependent0Substitution.java b/common/src/main/java/io/net5/util/internal/svm/PlatformDependent0Substitution.java similarity index 91% rename from common/src/main/java/io/netty/util/internal/svm/PlatformDependent0Substitution.java rename to common/src/main/java/io/net5/util/internal/svm/PlatformDependent0Substitution.java index 1a06a5518d..d63356708d 100644 --- a/common/src/main/java/io/netty/util/internal/svm/PlatformDependent0Substitution.java +++ b/common/src/main/java/io/net5/util/internal/svm/PlatformDependent0Substitution.java @@ -13,13 +13,13 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal.svm; +package io.net5.util.internal.svm; import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.annotate.RecomputeFieldValue; import com.oracle.svm.core.annotate.TargetClass; -@TargetClass(className = "io.netty.util.internal.PlatformDependent0") +@TargetClass(className = "io.net5.util.internal.PlatformDependent0") final class PlatformDependent0Substitution { private PlatformDependent0Substitution() { } diff --git a/common/src/main/java/io/netty/util/internal/svm/PlatformDependentSubstitution.java b/common/src/main/java/io/net5/util/internal/svm/PlatformDependentSubstitution.java similarity index 93% rename from common/src/main/java/io/netty/util/internal/svm/PlatformDependentSubstitution.java rename to common/src/main/java/io/net5/util/internal/svm/PlatformDependentSubstitution.java index 08932da772..672e83efbf 100644 --- a/common/src/main/java/io/netty/util/internal/svm/PlatformDependentSubstitution.java +++ b/common/src/main/java/io/net5/util/internal/svm/PlatformDependentSubstitution.java @@ -13,13 +13,13 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal.svm; +package io.net5.util.internal.svm; import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.annotate.RecomputeFieldValue; import com.oracle.svm.core.annotate.TargetClass; -@TargetClass(className = "io.netty.util.internal.PlatformDependent") +@TargetClass(className = "io.net5.util.internal.PlatformDependent") final class PlatformDependentSubstitution { private PlatformDependentSubstitution() { } diff --git a/common/src/main/java/io/netty/util/internal/svm/UnsafeRefArrayAccessSubstitution.java b/common/src/main/java/io/net5/util/internal/svm/UnsafeRefArrayAccessSubstitution.java similarity index 88% rename from common/src/main/java/io/netty/util/internal/svm/UnsafeRefArrayAccessSubstitution.java rename to common/src/main/java/io/net5/util/internal/svm/UnsafeRefArrayAccessSubstitution.java index 08f492f132..6c3b5b295c 100644 --- a/common/src/main/java/io/netty/util/internal/svm/UnsafeRefArrayAccessSubstitution.java +++ b/common/src/main/java/io/net5/util/internal/svm/UnsafeRefArrayAccessSubstitution.java @@ -13,13 +13,13 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal.svm; +package io.net5.util.internal.svm; import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.annotate.RecomputeFieldValue; import com.oracle.svm.core.annotate.TargetClass; -@TargetClass(className = "io.netty.util.internal.shaded.org.jctools.util.UnsafeRefArrayAccess") +@TargetClass(className = "io.net5.util.internal.shaded.org.jctools.util.UnsafeRefArrayAccess") final class UnsafeRefArrayAccessSubstitution { private UnsafeRefArrayAccessSubstitution() { } diff --git a/common/src/main/java/io/netty/util/internal/svm/package-info.java b/common/src/main/java/io/net5/util/internal/svm/package-info.java similarity index 95% rename from common/src/main/java/io/netty/util/internal/svm/package-info.java rename to common/src/main/java/io/net5/util/internal/svm/package-info.java index 265320a91d..7e85bd8627 100644 --- a/common/src/main/java/io/netty/util/internal/svm/package-info.java +++ b/common/src/main/java/io/net5/util/internal/svm/package-info.java @@ -18,4 +18,4 @@ * SVM substitutions for classes that will cause trouble while compiling * into native image. */ -package io.netty.util.internal.svm; +package io.net5.util.internal.svm; diff --git a/common/src/main/java/io/netty/util/package-info.java b/common/src/main/java/io/net5/util/package-info.java similarity index 96% rename from common/src/main/java/io/netty/util/package-info.java rename to common/src/main/java/io/net5/util/package-info.java index 598ea407c9..7fbd517686 100644 --- a/common/src/main/java/io/netty/util/package-info.java +++ b/common/src/main/java/io/net5/util/package-info.java @@ -17,4 +17,4 @@ /** * Utility classes used across multiple packages. */ -package io.netty.util; +package io.net5.util; diff --git a/common/src/main/resources/META-INF/native-image/io.netty/common/native-image.properties b/common/src/main/resources/META-INF/native-image/io.netty/common/native-image.properties index 4b82b03c93..275dea3ece 100644 --- a/common/src/main/resources/META-INF/native-image/io.netty/common/native-image.properties +++ b/common/src/main/resources/META-INF/native-image/io.netty/common/native-image.properties @@ -12,8 +12,8 @@ # License for the specific language governing permissions and limitations # under the License. -Args = --initialize-at-run-time=io.netty.util.AbstractReferenceCounted,io.netty.util.concurrent.GlobalEventExecutor,io.netty.util.concurrent.ImmediateEventExecutor,io.netty.util.concurrent.ScheduledFutureTask \ - --initialize-at-run-time=io.netty.util.NetUtilSubstitutions$NetUtilLocalhost4LazyHolder \ - --initialize-at-run-time=io.netty.util.NetUtilSubstitutions$NetUtilLocalhost6LazyHolder \ - --initialize-at-run-time=io.netty.util.NetUtilSubstitutions$NetUtilLocalhostLazyHolder +Args = --initialize-at-run-time=io.net5.util.AbstractReferenceCounted,io.net5.util.concurrent.GlobalEventExecutor,io.net5.util.concurrent.ImmediateEventExecutor,io.net5.util.concurrent.ScheduledFutureTask \ + --initialize-at-run-time=io.net5.util.NetUtilSubstitutions$NetUtilLocalhost4LazyHolder \ + --initialize-at-run-time=io.net5.util.NetUtilSubstitutions$NetUtilLocalhost6LazyHolder \ + --initialize-at-run-time=io.net5.util.NetUtilSubstitutions$NetUtilLocalhostLazyHolder diff --git a/common/src/main/resources/META-INF/services/reactor.blockhound.integration.BlockHoundIntegration b/common/src/main/resources/META-INF/services/reactor.blockhound.integration.BlockHoundIntegration index e33bea796c..f0c7325928 100644 --- a/common/src/main/resources/META-INF/services/reactor.blockhound.integration.BlockHoundIntegration +++ b/common/src/main/resources/META-INF/services/reactor.blockhound.integration.BlockHoundIntegration @@ -11,4 +11,4 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -io.netty.util.internal.Hidden$NettyBlockHoundIntegration \ No newline at end of file +io.net5.util.internal.Hidden$NettyBlockHoundIntegration \ No newline at end of file diff --git a/common/src/main/templates/io/netty/util/collection/KCollections.template b/common/src/main/templates/io/net5/util/collection/KCollections.template similarity index 99% rename from common/src/main/templates/io/netty/util/collection/KCollections.template rename to common/src/main/templates/io/net5/util/collection/KCollections.template index 5a7db526fe..c504f23e71 100644 --- a/common/src/main/templates/io/netty/util/collection/KCollections.template +++ b/common/src/main/templates/io/net5/util/collection/KCollections.template @@ -12,7 +12,7 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package io.netty.util.collection; +package io.net5.util.collection; import java.util.Collection; import java.util.Collections; diff --git a/common/src/main/templates/io/netty/util/collection/KObjectHashMap.template b/common/src/main/templates/io/net5/util/collection/KObjectHashMap.template similarity index 99% rename from common/src/main/templates/io/netty/util/collection/KObjectHashMap.template rename to common/src/main/templates/io/net5/util/collection/KObjectHashMap.template index a130cc87c4..6b8c8261f5 100644 --- a/common/src/main/templates/io/netty/util/collection/KObjectHashMap.template +++ b/common/src/main/templates/io/net5/util/collection/KObjectHashMap.template @@ -13,9 +13,9 @@ * the License. */ -package io.netty.util.collection; +package io.net5.util.collection; -import static io.netty.util.internal.MathUtil.safeFindNextPositivePowerOfTwo; +import static io.net5.util.internal.MathUtil.safeFindNextPositivePowerOfTwo; import java.util.AbstractCollection; import java.util.AbstractSet; diff --git a/common/src/main/templates/io/netty/util/collection/KObjectMap.template b/common/src/main/templates/io/net5/util/collection/KObjectMap.template similarity index 98% rename from common/src/main/templates/io/netty/util/collection/KObjectMap.template rename to common/src/main/templates/io/net5/util/collection/KObjectMap.template index 4af9be1958..473b22e975 100644 --- a/common/src/main/templates/io/netty/util/collection/KObjectMap.template +++ b/common/src/main/templates/io/net5/util/collection/KObjectMap.template @@ -12,7 +12,7 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package io.netty.util.collection; +package io.net5.util.collection; import java.util.Map; diff --git a/common/src/test/java/io/netty/util/AbstractReferenceCountedTest.java b/common/src/test/java/io/net5/util/AbstractReferenceCountedTest.java similarity index 99% rename from common/src/test/java/io/netty/util/AbstractReferenceCountedTest.java rename to common/src/test/java/io/net5/util/AbstractReferenceCountedTest.java index c79ef79d65..4f84f5f8b3 100644 --- a/common/src/test/java/io/netty/util/AbstractReferenceCountedTest.java +++ b/common/src/test/java/io/net5/util/AbstractReferenceCountedTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util; +package io.net5.util; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; diff --git a/common/src/test/java/io/netty/util/AsciiStringCharacterTest.java b/common/src/test/java/io/net5/util/AsciiStringCharacterTest.java similarity index 97% rename from common/src/test/java/io/netty/util/AsciiStringCharacterTest.java rename to common/src/test/java/io/net5/util/AsciiStringCharacterTest.java index 73bd7a24cc..c959a0dcbc 100644 --- a/common/src/test/java/io/netty/util/AsciiStringCharacterTest.java +++ b/common/src/test/java/io/net5/util/AsciiStringCharacterTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util; +package io.net5.util; import org.junit.jupiter.api.Test; @@ -21,14 +21,14 @@ import java.nio.CharBuffer; import java.nio.charset.Charset; import java.util.Random; -import static io.netty.util.AsciiString.contains; -import static io.netty.util.AsciiString.containsIgnoreCase; -import static io.netty.util.CharsetUtil.ISO_8859_1; -import static io.netty.util.CharsetUtil.US_ASCII; -import static io.netty.util.CharsetUtil.UTF_16; -import static io.netty.util.CharsetUtil.UTF_16BE; -import static io.netty.util.CharsetUtil.UTF_16LE; -import static io.netty.util.CharsetUtil.UTF_8; +import static io.net5.util.AsciiString.contains; +import static io.net5.util.AsciiString.containsIgnoreCase; +import static io.net5.util.CharsetUtil.ISO_8859_1; +import static io.net5.util.CharsetUtil.US_ASCII; +import static io.net5.util.CharsetUtil.UTF_16; +import static io.net5.util.CharsetUtil.UTF_16BE; +import static io.net5.util.CharsetUtil.UTF_16LE; +import static io.net5.util.CharsetUtil.UTF_8; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertArrayEquals; diff --git a/common/src/test/java/io/netty/util/AsciiStringMemoryTest.java b/common/src/test/java/io/net5/util/AsciiStringMemoryTest.java similarity index 98% rename from common/src/test/java/io/netty/util/AsciiStringMemoryTest.java rename to common/src/test/java/io/net5/util/AsciiStringMemoryTest.java index ed4d1db1d0..63400dfcb8 100644 --- a/common/src/test/java/io/netty/util/AsciiStringMemoryTest.java +++ b/common/src/test/java/io/net5/util/AsciiStringMemoryTest.java @@ -13,11 +13,11 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util; +package io.net5.util; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; -import io.netty.util.ByteProcessor.IndexOfProcessor; +import io.net5.util.ByteProcessor.IndexOfProcessor; import java.util.Random; import java.util.concurrent.atomic.AtomicReference; diff --git a/common/src/test/java/io/netty/util/AttributeKeyTest.java b/common/src/test/java/io/net5/util/AttributeKeyTest.java similarity index 98% rename from common/src/test/java/io/netty/util/AttributeKeyTest.java rename to common/src/test/java/io/net5/util/AttributeKeyTest.java index 0024671e88..2628d524b2 100644 --- a/common/src/test/java/io/netty/util/AttributeKeyTest.java +++ b/common/src/test/java/io/net5/util/AttributeKeyTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util; +package io.net5.util; import org.junit.jupiter.api.Test; diff --git a/common/src/test/java/io/netty/util/ConstantPoolTest.java b/common/src/test/java/io/net5/util/ConstantPoolTest.java similarity index 99% rename from common/src/test/java/io/netty/util/ConstantPoolTest.java rename to common/src/test/java/io/net5/util/ConstantPoolTest.java index 1bb94e4c93..e41fca1046 100644 --- a/common/src/test/java/io/netty/util/ConstantPoolTest.java +++ b/common/src/test/java/io/net5/util/ConstantPoolTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util; +package io.net5.util; import org.junit.jupiter.api.Test; diff --git a/common/src/test/java/io/netty/util/DefaultAttributeMapTest.java b/common/src/test/java/io/net5/util/DefaultAttributeMapTest.java similarity index 99% rename from common/src/test/java/io/netty/util/DefaultAttributeMapTest.java rename to common/src/test/java/io/net5/util/DefaultAttributeMapTest.java index cb70c4062b..407e195b0b 100644 --- a/common/src/test/java/io/netty/util/DefaultAttributeMapTest.java +++ b/common/src/test/java/io/net5/util/DefaultAttributeMapTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util; +package io.net5.util; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/common/src/test/java/io/netty/util/DomainNameMappingTest.java b/common/src/test/java/io/net5/util/DomainNameMappingTest.java similarity index 99% rename from common/src/test/java/io/netty/util/DomainNameMappingTest.java rename to common/src/test/java/io/net5/util/DomainNameMappingTest.java index 336d15fda0..4465b8b4f9 100644 --- a/common/src/test/java/io/netty/util/DomainNameMappingTest.java +++ b/common/src/test/java/io/net5/util/DomainNameMappingTest.java @@ -14,7 +14,7 @@ * under the License. */ -package io.netty.util; +package io.net5.util; import org.junit.jupiter.api.Test; diff --git a/common/src/test/java/io/netty/util/DomainWildcardMappingBuilderTest.java b/common/src/test/java/io/net5/util/DomainWildcardMappingBuilderTest.java similarity index 99% rename from common/src/test/java/io/netty/util/DomainWildcardMappingBuilderTest.java rename to common/src/test/java/io/net5/util/DomainWildcardMappingBuilderTest.java index 15ed44bfa1..d215a62159 100644 --- a/common/src/test/java/io/netty/util/DomainWildcardMappingBuilderTest.java +++ b/common/src/test/java/io/net5/util/DomainWildcardMappingBuilderTest.java @@ -14,7 +14,7 @@ * under the License. */ -package io.netty.util; +package io.net5.util; import org.junit.jupiter.api.Test; diff --git a/common/src/test/java/io/netty/util/HashedWheelTimerTest.java b/common/src/test/java/io/net5/util/HashedWheelTimerTest.java similarity index 99% rename from common/src/test/java/io/netty/util/HashedWheelTimerTest.java rename to common/src/test/java/io/net5/util/HashedWheelTimerTest.java index 9a840b2239..f43e79ac8e 100644 --- a/common/src/test/java/io/netty/util/HashedWheelTimerTest.java +++ b/common/src/test/java/io/net5/util/HashedWheelTimerTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util; +package io.net5.util; import org.junit.jupiter.api.Test; diff --git a/common/src/test/java/io/netty/util/NetUtilTest.java b/common/src/test/java/io/net5/util/NetUtilTest.java similarity index 99% rename from common/src/test/java/io/netty/util/NetUtilTest.java rename to common/src/test/java/io/net5/util/NetUtilTest.java index c7663c497e..50d252485c 100644 --- a/common/src/test/java/io/netty/util/NetUtilTest.java +++ b/common/src/test/java/io/net5/util/NetUtilTest.java @@ -13,9 +13,9 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util; +package io.net5.util; -import io.netty.util.internal.StringUtil; +import io.net5.util.internal.StringUtil; import org.junit.jupiter.api.Test; import java.net.Inet4Address; @@ -27,7 +27,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; -import static io.netty.util.NetUtil.*; +import static io.net5.util.NetUtil.*; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; diff --git a/common/src/test/java/io/netty/util/NettyRuntimeTests.java b/common/src/test/java/io/net5/util/NettyRuntimeTests.java similarity index 90% rename from common/src/test/java/io/netty/util/NettyRuntimeTests.java rename to common/src/test/java/io/net5/util/NettyRuntimeTests.java index 33b11a928e..92628d6a4e 100644 --- a/common/src/test/java/io/netty/util/NettyRuntimeTests.java +++ b/common/src/test/java/io/net5/util/NettyRuntimeTests.java @@ -14,9 +14,9 @@ * under the License. */ -package io.netty.util; +package io.net5.util; -import io.netty.util.internal.SystemPropertyUtil; +import io.net5.util.internal.SystemPropertyUtil; import org.junit.jupiter.api.Test; import java.util.concurrent.BrokenBarrierException; @@ -154,16 +154,16 @@ public class NettyRuntimeTests { @Test public void testGetWithSystemProperty() { - final String availableProcessorsSystemProperty = SystemPropertyUtil.get("io.netty.availableProcessors"); + final String availableProcessorsSystemProperty = SystemPropertyUtil.get("io.net5.availableProcessors"); try { - System.setProperty("io.netty.availableProcessors", "2048"); + System.setProperty("io.net5.availableProcessors", "2048"); final NettyRuntime.AvailableProcessorsHolder holder = new NettyRuntime.AvailableProcessorsHolder(); assertThat(holder.availableProcessors(), equalTo(2048)); } finally { if (availableProcessorsSystemProperty != null) { - System.setProperty("io.netty.availableProcessors", availableProcessorsSystemProperty); + System.setProperty("io.net5.availableProcessors", availableProcessorsSystemProperty); } else { - System.clearProperty("io.netty.availableProcessors"); + System.clearProperty("io.net5.availableProcessors"); } } } @@ -171,16 +171,16 @@ public class NettyRuntimeTests { @Test @SuppressForbidden(reason = "testing fallback to Runtime#availableProcessors") public void testGet() { - final String availableProcessorsSystemProperty = SystemPropertyUtil.get("io.netty.availableProcessors"); + final String availableProcessorsSystemProperty = SystemPropertyUtil.get("io.net5.availableProcessors"); try { - System.clearProperty("io.netty.availableProcessors"); + System.clearProperty("io.net5.availableProcessors"); final NettyRuntime.AvailableProcessorsHolder holder = new NettyRuntime.AvailableProcessorsHolder(); assertThat(holder.availableProcessors(), equalTo(Runtime.getRuntime().availableProcessors())); } finally { if (availableProcessorsSystemProperty != null) { - System.setProperty("io.netty.availableProcessors", availableProcessorsSystemProperty); + System.setProperty("io.net5.availableProcessors", availableProcessorsSystemProperty); } else { - System.clearProperty("io.netty.availableProcessors"); + System.clearProperty("io.net5.availableProcessors"); } } } diff --git a/common/src/test/java/io/netty/util/RecyclerTest.java b/common/src/test/java/io/net5/util/RecyclerTest.java similarity index 99% rename from common/src/test/java/io/netty/util/RecyclerTest.java rename to common/src/test/java/io/net5/util/RecyclerTest.java index 87e5b3d20b..826464eadf 100644 --- a/common/src/test/java/io/netty/util/RecyclerTest.java +++ b/common/src/test/java/io/net5/util/RecyclerTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util; +package io.net5.util; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; diff --git a/common/src/test/java/io/netty/util/ResourceLeakDetectorTest.java b/common/src/test/java/io/net5/util/ResourceLeakDetectorTest.java similarity index 99% rename from common/src/test/java/io/netty/util/ResourceLeakDetectorTest.java rename to common/src/test/java/io/net5/util/ResourceLeakDetectorTest.java index 62c09317fd..f59afed6b8 100644 --- a/common/src/test/java/io/netty/util/ResourceLeakDetectorTest.java +++ b/common/src/test/java/io/net5/util/ResourceLeakDetectorTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util; +package io.net5.util; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; diff --git a/common/src/test/java/io/netty/util/concurrent/AbstractScheduledEventExecutorTest.java b/common/src/test/java/io/net5/util/concurrent/AbstractScheduledEventExecutorTest.java similarity index 99% rename from common/src/test/java/io/netty/util/concurrent/AbstractScheduledEventExecutorTest.java rename to common/src/test/java/io/net5/util/concurrent/AbstractScheduledEventExecutorTest.java index a0ae541103..f4d0e4dbf9 100644 --- a/common/src/test/java/io/netty/util/concurrent/AbstractScheduledEventExecutorTest.java +++ b/common/src/test/java/io/net5/util/concurrent/AbstractScheduledEventExecutorTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.concurrent; +package io.net5.util.concurrent; import org.junit.jupiter.api.Test; diff --git a/common/src/test/java/io/netty/util/concurrent/DefaultFutureCompletionStageTest.java b/common/src/test/java/io/net5/util/concurrent/DefaultFutureCompletionStageTest.java similarity index 99% rename from common/src/test/java/io/netty/util/concurrent/DefaultFutureCompletionStageTest.java rename to common/src/test/java/io/net5/util/concurrent/DefaultFutureCompletionStageTest.java index d92b70f5ea..47bf6bf9c6 100644 --- a/common/src/test/java/io/netty/util/concurrent/DefaultFutureCompletionStageTest.java +++ b/common/src/test/java/io/net5/util/concurrent/DefaultFutureCompletionStageTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.concurrent; +package io.net5.util.concurrent; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; diff --git a/common/src/test/java/io/netty/util/concurrent/DefaultPromiseTest.java b/common/src/test/java/io/net5/util/concurrent/DefaultPromiseTest.java similarity index 99% rename from common/src/test/java/io/netty/util/concurrent/DefaultPromiseTest.java rename to common/src/test/java/io/net5/util/concurrent/DefaultPromiseTest.java index 74aa677e04..f7da29b3d5 100644 --- a/common/src/test/java/io/netty/util/concurrent/DefaultPromiseTest.java +++ b/common/src/test/java/io/net5/util/concurrent/DefaultPromiseTest.java @@ -14,11 +14,11 @@ * under the License. */ -package io.netty.util.concurrent; +package io.net5.util.concurrent; -import io.netty.util.Signal; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; +import io.net5.util.Signal; +import io.net5.util.internal.logging.InternalLogger; +import io.net5.util.internal.logging.InternalLoggerFactory; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; @@ -38,7 +38,7 @@ import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import static io.netty.util.concurrent.ImmediateEventExecutor.INSTANCE; +import static io.net5.util.concurrent.ImmediateEventExecutor.INSTANCE; import static java.lang.Math.max; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/common/src/test/java/io/netty/util/concurrent/DefaultThreadFactoryTest.java b/common/src/test/java/io/net5/util/concurrent/DefaultThreadFactoryTest.java similarity index 99% rename from common/src/test/java/io/netty/util/concurrent/DefaultThreadFactoryTest.java rename to common/src/test/java/io/net5/util/concurrent/DefaultThreadFactoryTest.java index 60e0744b15..a42d90248b 100644 --- a/common/src/test/java/io/netty/util/concurrent/DefaultThreadFactoryTest.java +++ b/common/src/test/java/io/net5/util/concurrent/DefaultThreadFactoryTest.java @@ -14,7 +14,7 @@ * under the License. */ -package io.netty.util.concurrent; +package io.net5.util.concurrent; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; diff --git a/common/src/test/java/io/netty/util/concurrent/FastThreadLocalTest.java b/common/src/test/java/io/net5/util/concurrent/FastThreadLocalTest.java similarity index 98% rename from common/src/test/java/io/netty/util/concurrent/FastThreadLocalTest.java rename to common/src/test/java/io/net5/util/concurrent/FastThreadLocalTest.java index e9a7710989..2c93ffc28c 100644 --- a/common/src/test/java/io/netty/util/concurrent/FastThreadLocalTest.java +++ b/common/src/test/java/io/net5/util/concurrent/FastThreadLocalTest.java @@ -14,9 +14,9 @@ * under the License. */ -package io.netty.util.concurrent; +package io.net5.util.concurrent; -import io.netty.util.internal.ObjectCleaner; +import io.net5.util.internal.ObjectCleaner; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; diff --git a/common/src/test/java/io/netty/util/concurrent/FutureCompletionStageTest.java b/common/src/test/java/io/net5/util/concurrent/FutureCompletionStageTest.java similarity index 98% rename from common/src/test/java/io/netty/util/concurrent/FutureCompletionStageTest.java rename to common/src/test/java/io/net5/util/concurrent/FutureCompletionStageTest.java index 2b4e64d8d2..10e0178053 100644 --- a/common/src/test/java/io/netty/util/concurrent/FutureCompletionStageTest.java +++ b/common/src/test/java/io/net5/util/concurrent/FutureCompletionStageTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.concurrent; +package io.net5.util.concurrent; import org.junit.jupiter.api.Test; diff --git a/common/src/test/java/io/netty/util/concurrent/FuturesTest.java b/common/src/test/java/io/net5/util/concurrent/FuturesTest.java similarity index 99% rename from common/src/test/java/io/netty/util/concurrent/FuturesTest.java rename to common/src/test/java/io/net5/util/concurrent/FuturesTest.java index 75109a9115..9a27e82035 100644 --- a/common/src/test/java/io/netty/util/concurrent/FuturesTest.java +++ b/common/src/test/java/io/net5/util/concurrent/FuturesTest.java @@ -13,14 +13,14 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.concurrent; +package io.net5.util.concurrent; import org.junit.jupiter.api.Test; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; -import static io.netty.util.concurrent.ImmediateEventExecutor.INSTANCE; +import static io.net5.util.concurrent.ImmediateEventExecutor.INSTANCE; import static java.util.concurrent.TimeUnit.SECONDS; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertFalse; diff --git a/common/src/test/java/io/netty/util/concurrent/GlobalEventExecutorTest.java b/common/src/test/java/io/net5/util/concurrent/GlobalEventExecutorTest.java similarity index 99% rename from common/src/test/java/io/netty/util/concurrent/GlobalEventExecutorTest.java rename to common/src/test/java/io/net5/util/concurrent/GlobalEventExecutorTest.java index 78550e0504..30eb44085d 100644 --- a/common/src/test/java/io/netty/util/concurrent/GlobalEventExecutorTest.java +++ b/common/src/test/java/io/net5/util/concurrent/GlobalEventExecutorTest.java @@ -14,7 +14,7 @@ * under the License. */ -package io.netty.util.concurrent; +package io.net5.util.concurrent; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/common/src/test/java/io/netty/util/concurrent/ImmediateExecutorTest.java b/common/src/test/java/io/net5/util/concurrent/ImmediateExecutorTest.java similarity index 97% rename from common/src/test/java/io/netty/util/concurrent/ImmediateExecutorTest.java rename to common/src/test/java/io/net5/util/concurrent/ImmediateExecutorTest.java index 5241262738..78d4ae324c 100644 --- a/common/src/test/java/io/netty/util/concurrent/ImmediateExecutorTest.java +++ b/common/src/test/java/io/net5/util/concurrent/ImmediateExecutorTest.java @@ -14,7 +14,7 @@ * under the License. */ -package io.netty.util.concurrent; +package io.net5.util.concurrent; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; diff --git a/common/src/test/java/io/netty/util/concurrent/NonStickyEventExecutorGroupTest.java b/common/src/test/java/io/net5/util/concurrent/NonStickyEventExecutorGroupTest.java similarity index 98% rename from common/src/test/java/io/netty/util/concurrent/NonStickyEventExecutorGroupTest.java rename to common/src/test/java/io/net5/util/concurrent/NonStickyEventExecutorGroupTest.java index 392e9a0aeb..04e00f3b20 100644 --- a/common/src/test/java/io/netty/util/concurrent/NonStickyEventExecutorGroupTest.java +++ b/common/src/test/java/io/net5/util/concurrent/NonStickyEventExecutorGroupTest.java @@ -13,9 +13,9 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.concurrent; +package io.net5.util.concurrent; -import io.netty.util.NettyRuntime; +import io.net5.util.NettyRuntime; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.params.ParameterizedTest; diff --git a/common/src/test/java/io/netty/util/concurrent/PromiseCombinerTest.java b/common/src/test/java/io/net5/util/concurrent/PromiseCombinerTest.java similarity index 97% rename from common/src/test/java/io/netty/util/concurrent/PromiseCombinerTest.java rename to common/src/test/java/io/net5/util/concurrent/PromiseCombinerTest.java index 52a701fb5b..86acf53975 100644 --- a/common/src/test/java/io/netty/util/concurrent/PromiseCombinerTest.java +++ b/common/src/test/java/io/net5/util/concurrent/PromiseCombinerTest.java @@ -13,12 +13,12 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.concurrent; +package io.net5.util.concurrent; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import static io.netty.util.concurrent.ImmediateEventExecutor.INSTANCE; +import static io.net5.util.concurrent.ImmediateEventExecutor.INSTANCE; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; diff --git a/common/src/test/java/io/netty/util/concurrent/SingleThreadEventExecutorTest.java b/common/src/test/java/io/net5/util/concurrent/SingleThreadEventExecutorTest.java similarity index 99% rename from common/src/test/java/io/netty/util/concurrent/SingleThreadEventExecutorTest.java rename to common/src/test/java/io/net5/util/concurrent/SingleThreadEventExecutorTest.java index 379990626d..4648cd6338 100644 --- a/common/src/test/java/io/netty/util/concurrent/SingleThreadEventExecutorTest.java +++ b/common/src/test/java/io/net5/util/concurrent/SingleThreadEventExecutorTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.concurrent; +package io.net5.util.concurrent; import org.junit.jupiter.api.Test; diff --git a/common/src/test/java/io/netty/util/concurrent/TestEventExecutor.java b/common/src/test/java/io/net5/util/concurrent/TestEventExecutor.java similarity index 96% rename from common/src/test/java/io/netty/util/concurrent/TestEventExecutor.java rename to common/src/test/java/io/net5/util/concurrent/TestEventExecutor.java index c33c87c5c3..dc2ae166c9 100644 --- a/common/src/test/java/io/netty/util/concurrent/TestEventExecutor.java +++ b/common/src/test/java/io/net5/util/concurrent/TestEventExecutor.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.concurrent; +package io.net5.util.concurrent; import java.util.concurrent.Executors; diff --git a/common/src/test/java/io/netty/util/concurrent/UnorderedThreadPoolEventExecutorTest.java b/common/src/test/java/io/net5/util/concurrent/UnorderedThreadPoolEventExecutorTest.java similarity index 99% rename from common/src/test/java/io/netty/util/concurrent/UnorderedThreadPoolEventExecutorTest.java rename to common/src/test/java/io/net5/util/concurrent/UnorderedThreadPoolEventExecutorTest.java index caeabdfbe5..adda32f699 100644 --- a/common/src/test/java/io/netty/util/concurrent/UnorderedThreadPoolEventExecutorTest.java +++ b/common/src/test/java/io/net5/util/concurrent/UnorderedThreadPoolEventExecutorTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.concurrent; +package io.net5.util.concurrent; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; diff --git a/common/src/test/java/io/netty/util/internal/AppendableCharSequenceTest.java b/common/src/test/java/io/net5/util/internal/AppendableCharSequenceTest.java similarity index 99% rename from common/src/test/java/io/netty/util/internal/AppendableCharSequenceTest.java rename to common/src/test/java/io/net5/util/internal/AppendableCharSequenceTest.java index b735a8a8ec..71c5ad3cb3 100644 --- a/common/src/test/java/io/netty/util/internal/AppendableCharSequenceTest.java +++ b/common/src/test/java/io/net5/util/internal/AppendableCharSequenceTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal; +package io.net5.util.internal; import org.junit.jupiter.api.Test; diff --git a/common/src/test/java/io/netty/util/internal/DefaultPriorityQueueTest.java b/common/src/test/java/io/net5/util/internal/DefaultPriorityQueueTest.java similarity index 99% rename from common/src/test/java/io/netty/util/internal/DefaultPriorityQueueTest.java rename to common/src/test/java/io/net5/util/internal/DefaultPriorityQueueTest.java index 03069958dd..623c11efe5 100644 --- a/common/src/test/java/io/netty/util/internal/DefaultPriorityQueueTest.java +++ b/common/src/test/java/io/net5/util/internal/DefaultPriorityQueueTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal; +package io.net5.util.internal; import org.junit.jupiter.api.Test; diff --git a/common/src/test/java/io/netty/util/internal/LongLongHashMapTest.java b/common/src/test/java/io/net5/util/internal/LongLongHashMapTest.java similarity index 98% rename from common/src/test/java/io/netty/util/internal/LongLongHashMapTest.java rename to common/src/test/java/io/net5/util/internal/LongLongHashMapTest.java index 261b4ef1e5..7a2b5d1889 100644 --- a/common/src/test/java/io/netty/util/internal/LongLongHashMapTest.java +++ b/common/src/test/java/io/net5/util/internal/LongLongHashMapTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal; +package io.net5.util.internal; import org.junit.jupiter.api.Test; diff --git a/common/src/test/java/io/netty/util/internal/LongPriorityQueueTest.java b/common/src/test/java/io/net5/util/internal/LongPriorityQueueTest.java similarity index 99% rename from common/src/test/java/io/netty/util/internal/LongPriorityQueueTest.java rename to common/src/test/java/io/net5/util/internal/LongPriorityQueueTest.java index c9c173962d..de518bff2b 100644 --- a/common/src/test/java/io/netty/util/internal/LongPriorityQueueTest.java +++ b/common/src/test/java/io/net5/util/internal/LongPriorityQueueTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal; +package io.net5.util.internal; import org.junit.jupiter.api.Test; diff --git a/common/src/test/java/io/netty/util/internal/MacAddressUtilTest.java b/common/src/test/java/io/net5/util/internal/MacAddressUtilTest.java similarity index 97% rename from common/src/test/java/io/netty/util/internal/MacAddressUtilTest.java rename to common/src/test/java/io/net5/util/internal/MacAddressUtilTest.java index 9925896c45..f65d1ea756 100644 --- a/common/src/test/java/io/netty/util/internal/MacAddressUtilTest.java +++ b/common/src/test/java/io/net5/util/internal/MacAddressUtilTest.java @@ -13,12 +13,12 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal; +package io.net5.util.internal; import org.junit.jupiter.api.Test; -import static io.netty.util.internal.EmptyArrays.EMPTY_BYTES; -import static io.netty.util.internal.MacAddressUtil.parseMAC; +import static io.net5.util.internal.EmptyArrays.EMPTY_BYTES; +import static io.net5.util.internal.MacAddressUtil.parseMAC; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; diff --git a/common/src/test/java/io/netty/util/internal/MathUtilTest.java b/common/src/test/java/io/net5/util/internal/MathUtilTest.java similarity index 97% rename from common/src/test/java/io/netty/util/internal/MathUtilTest.java rename to common/src/test/java/io/net5/util/internal/MathUtilTest.java index 2c1f6a3cb6..77dec85866 100644 --- a/common/src/test/java/io/netty/util/internal/MathUtilTest.java +++ b/common/src/test/java/io/net5/util/internal/MathUtilTest.java @@ -13,12 +13,12 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal; +package io.net5.util.internal; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static io.netty.util.internal.MathUtil.*; +import static io.net5.util.internal.MathUtil.*; import org.junit.jupiter.api.Test; diff --git a/common/src/test/java/io/netty/util/internal/NativeLibraryLoaderTest.java b/common/src/test/java/io/net5/util/internal/NativeLibraryLoaderTest.java similarity index 98% rename from common/src/test/java/io/netty/util/internal/NativeLibraryLoaderTest.java rename to common/src/test/java/io/net5/util/internal/NativeLibraryLoaderTest.java index 19634dda33..fc7c426fbf 100644 --- a/common/src/test/java/io/netty/util/internal/NativeLibraryLoaderTest.java +++ b/common/src/test/java/io/net5/util/internal/NativeLibraryLoaderTest.java @@ -13,9 +13,9 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal; +package io.net5.util.internal; -import io.netty.util.CharsetUtil; +import io.net5.util.CharsetUtil; import org.junit.jupiter.api.Test; import java.io.ByteArrayInputStream; diff --git a/common/src/test/java/io/netty/util/internal/ObjectCleanerTest.java b/common/src/test/java/io/net5/util/internal/ObjectCleanerTest.java similarity index 99% rename from common/src/test/java/io/netty/util/internal/ObjectCleanerTest.java rename to common/src/test/java/io/net5/util/internal/ObjectCleanerTest.java index 6b04840ed9..6d4c3b4db3 100644 --- a/common/src/test/java/io/netty/util/internal/ObjectCleanerTest.java +++ b/common/src/test/java/io/net5/util/internal/ObjectCleanerTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal; +package io.net5.util.internal; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; diff --git a/common/src/test/java/io/netty/util/internal/ObjectUtilTest.java b/common/src/test/java/io/net5/util/internal/ObjectUtilTest.java similarity index 99% rename from common/src/test/java/io/netty/util/internal/ObjectUtilTest.java rename to common/src/test/java/io/net5/util/internal/ObjectUtilTest.java index 8e0dd226f3..a26ae836fc 100644 --- a/common/src/test/java/io/netty/util/internal/ObjectUtilTest.java +++ b/common/src/test/java/io/net5/util/internal/ObjectUtilTest.java @@ -12,7 +12,7 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package io.netty.util.internal; +package io.net5.util.internal; import org.junit.jupiter.api.Test; @@ -21,7 +21,7 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** - * Testcases for io.netty.util.internal.ObjectUtil. + * Testcases for io.net5.util.internal.ObjectUtil. * * The tests for exceptions do not use a fail mimic. The tests evaluate the * presence and type, to have really regression character. diff --git a/common/src/test/java/io/netty/util/internal/PlatformDependent0Test.java b/common/src/test/java/io/net5/util/internal/PlatformDependent0Test.java similarity index 99% rename from common/src/test/java/io/netty/util/internal/PlatformDependent0Test.java rename to common/src/test/java/io/net5/util/internal/PlatformDependent0Test.java index 8899fa045f..620cda2b0a 100644 --- a/common/src/test/java/io/netty/util/internal/PlatformDependent0Test.java +++ b/common/src/test/java/io/net5/util/internal/PlatformDependent0Test.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal; +package io.net5.util.internal; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; diff --git a/common/src/test/java/io/netty/util/internal/PlatformDependentTest.java b/common/src/test/java/io/net5/util/internal/PlatformDependentTest.java similarity index 97% rename from common/src/test/java/io/netty/util/internal/PlatformDependentTest.java rename to common/src/test/java/io/net5/util/internal/PlatformDependentTest.java index 2b490b1523..bd7843b324 100644 --- a/common/src/test/java/io/netty/util/internal/PlatformDependentTest.java +++ b/common/src/test/java/io/net5/util/internal/PlatformDependentTest.java @@ -13,15 +13,15 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal; +package io.net5.util.internal; import org.junit.jupiter.api.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 io.net5.util.internal.PlatformDependent.hashCodeAscii; +import static io.net5.util.internal.PlatformDependent.hashCodeAsciiSafe; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; diff --git a/common/src/test/java/io/netty/util/internal/StringUtilTest.java b/common/src/test/java/io/net5/util/internal/StringUtilTest.java similarity index 96% rename from common/src/test/java/io/netty/util/internal/StringUtilTest.java rename to common/src/test/java/io/net5/util/internal/StringUtilTest.java index 4a45a1b4ae..ec9522e770 100644 --- a/common/src/test/java/io/netty/util/internal/StringUtilTest.java +++ b/common/src/test/java/io/net5/util/internal/StringUtilTest.java @@ -13,24 +13,24 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal; +package io.net5.util.internal; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.Collections; -import static io.netty.util.internal.StringUtil.NEWLINE; -import static io.netty.util.internal.StringUtil.commonSuffixOfLength; -import static io.netty.util.internal.StringUtil.indexOfWhiteSpace; -import static io.netty.util.internal.StringUtil.indexOfNonWhiteSpace; -import static io.netty.util.internal.StringUtil.isNullOrEmpty; -import static io.netty.util.internal.StringUtil.simpleClassName; -import static io.netty.util.internal.StringUtil.substringAfter; -import static io.netty.util.internal.StringUtil.toHexString; -import static io.netty.util.internal.StringUtil.toHexStringPadded; -import static io.netty.util.internal.StringUtil.unescapeCsv; -import static io.netty.util.internal.StringUtil.unescapeCsvFields; +import static io.net5.util.internal.StringUtil.NEWLINE; +import static io.net5.util.internal.StringUtil.commonSuffixOfLength; +import static io.net5.util.internal.StringUtil.indexOfWhiteSpace; +import static io.net5.util.internal.StringUtil.indexOfNonWhiteSpace; +import static io.net5.util.internal.StringUtil.isNullOrEmpty; +import static io.net5.util.internal.StringUtil.simpleClassName; +import static io.net5.util.internal.StringUtil.substringAfter; +import static io.net5.util.internal.StringUtil.toHexString; +import static io.net5.util.internal.StringUtil.toHexStringPadded; +import static io.net5.util.internal.StringUtil.unescapeCsv; +import static io.net5.util.internal.StringUtil.unescapeCsvFields; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertArrayEquals; diff --git a/common/src/test/java/io/netty/util/internal/SystemPropertyUtilTest.java b/common/src/test/java/io/net5/util/internal/SystemPropertyUtilTest.java similarity index 99% rename from common/src/test/java/io/netty/util/internal/SystemPropertyUtilTest.java rename to common/src/test/java/io/net5/util/internal/SystemPropertyUtilTest.java index caf06799a7..33f256fd80 100644 --- a/common/src/test/java/io/netty/util/internal/SystemPropertyUtilTest.java +++ b/common/src/test/java/io/net5/util/internal/SystemPropertyUtilTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal; +package io.net5.util.internal; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/common/src/test/java/io/netty/util/internal/ThreadExecutorMapTest.java b/common/src/test/java/io/net5/util/internal/ThreadExecutorMapTest.java similarity index 93% rename from common/src/test/java/io/netty/util/internal/ThreadExecutorMapTest.java rename to common/src/test/java/io/net5/util/internal/ThreadExecutorMapTest.java index db4a3236b9..68e53d5ce6 100644 --- a/common/src/test/java/io/netty/util/internal/ThreadExecutorMapTest.java +++ b/common/src/test/java/io/net5/util/internal/ThreadExecutorMapTest.java @@ -13,10 +13,10 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal; +package io.net5.util.internal; -import io.netty.util.concurrent.ImmediateEventExecutor; -import io.netty.util.concurrent.ImmediateExecutor; +import io.net5.util.concurrent.ImmediateEventExecutor; +import io.net5.util.concurrent.ImmediateExecutor; import org.junit.jupiter.api.Test; import java.util.concurrent.Executor; diff --git a/common/src/test/java/io/netty/util/internal/ThreadLocalRandomTest.java b/common/src/test/java/io/net5/util/internal/ThreadLocalRandomTest.java similarity index 97% rename from common/src/test/java/io/netty/util/internal/ThreadLocalRandomTest.java rename to common/src/test/java/io/net5/util/internal/ThreadLocalRandomTest.java index 0cc44ac8d6..0b0a52da15 100644 --- a/common/src/test/java/io/netty/util/internal/ThreadLocalRandomTest.java +++ b/common/src/test/java/io/net5/util/internal/ThreadLocalRandomTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal; +package io.net5.util.internal; import org.junit.jupiter.api.Test; diff --git a/common/src/test/java/io/netty/util/internal/TypeParameterMatcherTest.java b/common/src/test/java/io/net5/util/internal/TypeParameterMatcherTest.java similarity index 99% rename from common/src/test/java/io/netty/util/internal/TypeParameterMatcherTest.java rename to common/src/test/java/io/net5/util/internal/TypeParameterMatcherTest.java index 6f955fcf2e..0015bf7dfa 100644 --- a/common/src/test/java/io/netty/util/internal/TypeParameterMatcherTest.java +++ b/common/src/test/java/io/net5/util/internal/TypeParameterMatcherTest.java @@ -14,7 +14,7 @@ * under the License. */ -package io.netty.util.internal; +package io.net5.util.internal; import org.junit.jupiter.api.Test; diff --git a/common/src/test/java/io/netty/util/internal/logging/AbstractInternalLoggerTest.java b/common/src/test/java/io/net5/util/internal/logging/AbstractInternalLoggerTest.java similarity index 99% rename from common/src/test/java/io/netty/util/internal/logging/AbstractInternalLoggerTest.java rename to common/src/test/java/io/net5/util/internal/logging/AbstractInternalLoggerTest.java index ab1f455c76..8d4b8325c7 100644 --- a/common/src/test/java/io/netty/util/internal/logging/AbstractInternalLoggerTest.java +++ b/common/src/test/java/io/net5/util/internal/logging/AbstractInternalLoggerTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal.logging; +package io.net5.util.internal.logging; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; diff --git a/common/src/test/java/io/netty/util/internal/logging/CommonsLoggerFactoryTest.java b/common/src/test/java/io/net5/util/internal/logging/CommonsLoggerFactoryTest.java similarity index 96% rename from common/src/test/java/io/netty/util/internal/logging/CommonsLoggerFactoryTest.java rename to common/src/test/java/io/net5/util/internal/logging/CommonsLoggerFactoryTest.java index d7a8f6212b..1be48c860f 100644 --- a/common/src/test/java/io/netty/util/internal/logging/CommonsLoggerFactoryTest.java +++ b/common/src/test/java/io/net5/util/internal/logging/CommonsLoggerFactoryTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal.logging; +package io.net5.util.internal.logging; import org.junit.jupiter.api.Test; diff --git a/common/src/test/java/io/netty/util/internal/logging/CommonsLoggerTest.java b/common/src/test/java/io/net5/util/internal/logging/CommonsLoggerTest.java similarity index 99% rename from common/src/test/java/io/netty/util/internal/logging/CommonsLoggerTest.java rename to common/src/test/java/io/net5/util/internal/logging/CommonsLoggerTest.java index ff0027f33c..e3f2610ff8 100644 --- a/common/src/test/java/io/netty/util/internal/logging/CommonsLoggerTest.java +++ b/common/src/test/java/io/net5/util/internal/logging/CommonsLoggerTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal.logging; +package io.net5.util.internal.logging; import org.apache.commons.logging.Log; import org.junit.jupiter.api.Test; diff --git a/common/src/test/java/io/netty/util/internal/logging/InternalLoggerFactoryTest.java b/common/src/test/java/io/net5/util/internal/logging/InternalLoggerFactoryTest.java similarity index 99% rename from common/src/test/java/io/netty/util/internal/logging/InternalLoggerFactoryTest.java rename to common/src/test/java/io/net5/util/internal/logging/InternalLoggerFactoryTest.java index f2710d8bc4..818104bf46 100644 --- a/common/src/test/java/io/netty/util/internal/logging/InternalLoggerFactoryTest.java +++ b/common/src/test/java/io/net5/util/internal/logging/InternalLoggerFactoryTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal.logging; +package io.net5.util.internal.logging; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; diff --git a/common/src/test/java/io/netty/util/internal/logging/JdkLoggerFactoryTest.java b/common/src/test/java/io/net5/util/internal/logging/JdkLoggerFactoryTest.java similarity index 96% rename from common/src/test/java/io/netty/util/internal/logging/JdkLoggerFactoryTest.java rename to common/src/test/java/io/net5/util/internal/logging/JdkLoggerFactoryTest.java index 96278d213e..da7dae2e41 100644 --- a/common/src/test/java/io/netty/util/internal/logging/JdkLoggerFactoryTest.java +++ b/common/src/test/java/io/net5/util/internal/logging/JdkLoggerFactoryTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal.logging; +package io.net5.util.internal.logging; import org.junit.jupiter.api.Test; diff --git a/common/src/test/java/io/netty/util/internal/logging/Log4J2LoggerFactoryTest.java b/common/src/test/java/io/net5/util/internal/logging/Log4J2LoggerFactoryTest.java similarity index 96% rename from common/src/test/java/io/netty/util/internal/logging/Log4J2LoggerFactoryTest.java rename to common/src/test/java/io/net5/util/internal/logging/Log4J2LoggerFactoryTest.java index 118f2a5f48..74a37d9449 100644 --- a/common/src/test/java/io/netty/util/internal/logging/Log4J2LoggerFactoryTest.java +++ b/common/src/test/java/io/net5/util/internal/logging/Log4J2LoggerFactoryTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal.logging; +package io.net5.util.internal.logging; import org.junit.jupiter.api.Test; diff --git a/common/src/test/java/io/netty/util/internal/logging/Log4J2LoggerTest.java b/common/src/test/java/io/net5/util/internal/logging/Log4J2LoggerTest.java similarity index 97% rename from common/src/test/java/io/netty/util/internal/logging/Log4J2LoggerTest.java rename to common/src/test/java/io/net5/util/internal/logging/Log4J2LoggerTest.java index 879f87cd3f..518063831f 100644 --- a/common/src/test/java/io/netty/util/internal/logging/Log4J2LoggerTest.java +++ b/common/src/test/java/io/net5/util/internal/logging/Log4J2LoggerTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal.logging; +package io.net5.util.internal.logging; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -27,7 +27,7 @@ import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.spi.ExtendedLoggerWrapper; -import io.netty.util.internal.ReflectionUtil; +import io.net5.util.internal.ReflectionUtil; import org.junit.jupiter.api.Assumptions; /** diff --git a/common/src/test/java/io/netty/util/internal/logging/Log4JLoggerFactoryTest.java b/common/src/test/java/io/net5/util/internal/logging/Log4JLoggerFactoryTest.java similarity index 96% rename from common/src/test/java/io/netty/util/internal/logging/Log4JLoggerFactoryTest.java rename to common/src/test/java/io/net5/util/internal/logging/Log4JLoggerFactoryTest.java index 00db0b0cdd..060b3f6c57 100644 --- a/common/src/test/java/io/netty/util/internal/logging/Log4JLoggerFactoryTest.java +++ b/common/src/test/java/io/net5/util/internal/logging/Log4JLoggerFactoryTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal.logging; +package io.net5.util.internal.logging; import org.junit.jupiter.api.Test; diff --git a/common/src/test/java/io/netty/util/internal/logging/MessageFormatterTest.java b/common/src/test/java/io/net5/util/internal/logging/MessageFormatterTest.java similarity index 99% rename from common/src/test/java/io/netty/util/internal/logging/MessageFormatterTest.java rename to common/src/test/java/io/net5/util/internal/logging/MessageFormatterTest.java index d23131844a..597925df76 100644 --- a/common/src/test/java/io/netty/util/internal/logging/MessageFormatterTest.java +++ b/common/src/test/java/io/net5/util/internal/logging/MessageFormatterTest.java @@ -36,7 +36,7 @@ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package io.netty.util.internal.logging; +package io.net5.util.internal.logging; import org.junit.jupiter.api.Test; diff --git a/common/src/test/java/io/netty/util/internal/logging/Slf4JLoggerFactoryTest.java b/common/src/test/java/io/net5/util/internal/logging/Slf4JLoggerFactoryTest.java similarity index 99% rename from common/src/test/java/io/netty/util/internal/logging/Slf4JLoggerFactoryTest.java rename to common/src/test/java/io/net5/util/internal/logging/Slf4JLoggerFactoryTest.java index 18ec650c9c..fcc3634854 100644 --- a/common/src/test/java/io/netty/util/internal/logging/Slf4JLoggerFactoryTest.java +++ b/common/src/test/java/io/net5/util/internal/logging/Slf4JLoggerFactoryTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal.logging; +package io.net5.util.internal.logging; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; diff --git a/common/src/test/java/io/netty/util/internal/logging/Slf4JLoggerTest.java b/common/src/test/java/io/net5/util/internal/logging/Slf4JLoggerTest.java similarity index 99% rename from common/src/test/java/io/netty/util/internal/logging/Slf4JLoggerTest.java rename to common/src/test/java/io/net5/util/internal/logging/Slf4JLoggerTest.java index 259868d3cb..fe721c1980 100644 --- a/common/src/test/java/io/netty/util/internal/logging/Slf4JLoggerTest.java +++ b/common/src/test/java/io/net5/util/internal/logging/Slf4JLoggerTest.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.util.internal.logging; +package io.net5.util.internal.logging; import org.junit.jupiter.api.Test; import org.slf4j.Logger; diff --git a/common/src/test/templates/io/netty/util/collection/KObjectHashMapTest.template b/common/src/test/templates/io/net5/util/collection/KObjectHashMapTest.template similarity index 99% rename from common/src/test/templates/io/netty/util/collection/KObjectHashMapTest.template rename to common/src/test/templates/io/net5/util/collection/KObjectHashMapTest.template index cd2f254430..5d5c164011 100644 --- a/common/src/test/templates/io/netty/util/collection/KObjectHashMapTest.template +++ b/common/src/test/templates/io/net5/util/collection/KObjectHashMapTest.template @@ -12,9 +12,9 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package io.netty.util.collection; +package io.net5.util.collection; -import io.netty.util.collection.@K@ObjectMap.PrimitiveEntry; +import io.net5.util.collection.@K@ObjectMap.PrimitiveEntry; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/dev-tools/pom.xml b/dev-tools/pom.xml index 92fbccb96a..44a282a335 100644 --- a/dev-tools/pom.xml +++ b/dev-tools/pom.xml @@ -16,14 +16,15 @@ --> 4.0.0 - - org.sonatype.oss - oss-parent - 7 - - + + + mchv-snapshot + MCHV Apache Snapshot Maven Packages Distribution + https://mvn.mchv.eu/repository/mchv-snapshot + + - io.netty + io.net5 netty-dev-tools 5.0.0.Final-SNAPSHOT diff --git a/docker/Dockerfile.centos6 b/docker/Dockerfile.centos6 deleted file mode 100644 index b9323cda28..0000000000 --- a/docker/Dockerfile.centos6 +++ /dev/null @@ -1,30 +0,0 @@ -FROM centos:6.10 - -# Update as we need to use the vault now. -RUN sed -i -e 's/^mirrorlist/#mirrorlist/g' -e 's/^#baseurl=http:\/\/mirror.centos.org\/centos\/$releasever\//baseurl=https:\/\/vault.centos.org\/6.10\//g' /etc/yum.repos.d/CentOS-Base.repo - -# 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=11 -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 - -# when the JDK is GraalVM install native-image -RUN if [ -O /jdk/bin/gu ]; then /jdk/bin/gu install native-image; else echo "Not GraalVM, skip installation of native-image" ; fi diff --git a/docker/Dockerfile.cross_compile_aarch64 b/docker/Dockerfile.cross_compile_aarch64 deleted file mode 100644 index 49c1626a13..0000000000 --- a/docker/Dockerfile.cross_compile_aarch64 +++ /dev/null @@ -1,17 +0,0 @@ -FROM centos:7.6.1810 - -ARG gcc_version=10.2-2020.11 -ENV GCC_VERSION $gcc_version - -# Install requirements -RUN yum install -y wget tar git make redhat-lsb-core autoconf automake libtool glibc-devel libaio-devel openssl-devel apr-devel lksctp-tools - -# Install Java -RUN yum install -y java-11-openjdk-devel - -# Install aarch64 gcc 10.2 toolchain -RUN wget https://developer.arm.com/-/media/Files/downloads/gnu-a/$GCC_VERSION/binrel/gcc-arm-$GCC_VERSION-x86_64-aarch64-none-linux-gnu.tar.xz && \ - tar xvf gcc-arm-$GCC_VERSION-x86_64-aarch64-none-linux-gnu.tar.xz && mv gcc-arm-$GCC_VERSION-x86_64-aarch64-none-linux-gnu /opt/ -ENV PATH="/opt/gcc-arm-$GCC_VERSION-x86_64-aarch64-none-linux-gnu/bin:${PATH}" - -ENV JAVA_HOME="/usr/lib/jvm/java-11-openjdk/" diff --git a/docker/README.md b/docker/README.md deleted file mode 100644 index 4b8a2c8d3a..0000000000 --- a/docker/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# Using the docker images - -``` -cd /path/to/netty/ -``` - -## centos 6 with java 11 - -``` -docker-compose -f docker/docker-compose.yaml -f docker/docker-compose.centos-6.111.yaml run test -``` - -## aarch64 cross compile for transport-native-epoll on X86_64 - -``` -docker-compose -f docker/docker-compose.yaml run cross-compile-aarch64-build -``` -The default version of aarch64 gcc is `4.9-2016.02`. Update the parameter `gcc_version` in `docker-compose.yaml` to use a version you want. - -etc, etc diff --git a/docker/docker-compose.centos-6.111.yaml b/docker/docker-compose.centos-6.111.yaml deleted file mode 100644 index d90c42a4ae..0000000000 --- a/docker/docker-compose.centos-6.111.yaml +++ /dev/null @@ -1,36 +0,0 @@ -version: "3" - -services: - - runtime-setup: - image: netty:centos-6-1.11 - build: - args: - java_version : "adopt@1.11.0-11" - - build: - image: netty:centos-6-1.11 - - build-leak: - image: netty:centos-6-1.11 - - build-boringssl-static: - image: netty:centos-6-1.11 - - build-leak-boringssl-static: - image: netty:centos-6-1.11 - - build-unsafe-buffer: - image: netty:centos-6-1.11 - - stage-snapshot: - image: netty:centos-6-1.11 - - stage-release: - image: netty:centos-6-1.11 - - deploy: - image: netty:centos-6-1.11 - - shell: - image: netty:centos-6-1.11 diff --git a/docker/docker-compose.centos-6.116.yaml b/docker/docker-compose.centos-6.116.yaml deleted file mode 100644 index 22dafc8ff3..0000000000 --- a/docker/docker-compose.centos-6.116.yaml +++ /dev/null @@ -1,27 +0,0 @@ -version: "3" - -services: - - runtime-setup: - image: netty:centos-6-1.16 - build: - args: - java_version : "adopt@1.16.0-1" - - build: - image: netty:centos-6-1.16 - - build-leak: - image: netty:centos-6-1.16 - - build-boringssl-static: - image: netty:centos-6-1.16 - - build-leak-boringssl-static: - image: netty:centos-6-1.16 - - build-unsafe-buffer: - image: netty:centos-6-1.16 - - shell: - image: netty:centos-6-1.16 diff --git a/docker/docker-compose.centos-6.117.yaml b/docker/docker-compose.centos-6.117.yaml deleted file mode 100644 index 9950b94630..0000000000 --- a/docker/docker-compose.centos-6.117.yaml +++ /dev/null @@ -1,24 +0,0 @@ -version: "3" - -services: - - runtime-setup: - image: netty:centos-7-1.17 - build: - args: - java_version : "zulu@1.17.0-0" - - build: - image: netty:centos-7-1.17 - - build-leak: - image: netty:centos-7-1.17 - - build-boringssl-static: - image: netty:centos-7-1.17 - - build-leak-boringssl-static: - image: netty:centos-7-1.17 - - shell: - image: netty:centos-7-1.17 diff --git a/docker/docker-compose.centos-6.graalvm111.yaml b/docker/docker-compose.centos-6.graalvm111.yaml deleted file mode 100644 index 0a46fe49d8..0000000000 --- a/docker/docker-compose.centos-6.graalvm111.yaml +++ /dev/null @@ -1,27 +0,0 @@ -version: "3" - -services: - - runtime-setup: - image: netty:centos-6-1.11 - build: - args: - java_version : "graalvm-ce-java11@20.1.0" - - build: - image: netty:centos-6-1.11 - - build-leak: - image: netty:centos-6-1.11 - - build-boringssl-static: - image: netty:centos-6-1.11 - - build-leak-boringssl-static: - image: netty:centos-6-1.11 - - build-unsafe-buffer: - image: netty:centos-6-1.11 - - shell: - image: netty:centos-6-1.11 diff --git a/docker/docker-compose.centos-6.openj9111.yaml b/docker/docker-compose.centos-6.openj9111.yaml deleted file mode 100644 index 8e70868c07..0000000000 --- a/docker/docker-compose.centos-6.openj9111.yaml +++ /dev/null @@ -1,27 +0,0 @@ -version: "3" - -services: - - runtime-setup: - image: netty:centos-6-openj9-1.11 - build: - args: - java_version : "adopt-openj9@1.11.0-11" - - build: - image: netty:centos-6-openj9-1.11 - - build-leak: - image: netty:centos-6-openj9-1.11 - - build-boringssl-static: - image: netty:centos-6-openj9-1.11 - - build-leak-boringssl-static: - image: netty:centos-6-openj9-1.11 - - build-unsafe-buffer: - image: netty:centos-6-openj9-1.11 - - shell: - image: netty:centos-6-openj9-1.11 diff --git a/docker/docker-compose.centos-7.yaml b/docker/docker-compose.centos-7.yaml deleted file mode 100644 index 777c40d5aa..0000000000 --- a/docker/docker-compose.centos-7.yaml +++ /dev/null @@ -1,59 +0,0 @@ -version: "3" - -services: - - - cross-compile-aarch64-runtime-setup: - image: netty:cross_compile_aarch64 - build: - context: ../ - dockerfile: docker/Dockerfile.cross_compile_aarch64 - args: - gcc_version: "10.2-2020.11" - java_version: "adopt@1.11.0-9" - - cross-compile-aarch64-common: &cross-compile-aarch64-common - depends_on: [ cross-compile-aarch64-runtime-setup ] - image: netty:cross_compile_aarch64 - environment: - - GPG_KEYNAME - - GPG_PASSPHRASE - - GPG_PRIVATE_KEY - - MAVEN_OPTS - volumes: - - ~/.ssh:/root/.ssh - - ~/.gnupg:/root/.gnupg - - ~/.m2:/root/.m2 - - ..:/code - working_dir: /code - - cross-compile-aarch64-deploy: - <<: *cross-compile-aarch64-common - command: /bin/bash -cl "./mvnw -B -ntp -Plinux-aarch64 -pl transport-native-unix-common,transport-native-epoll -am clean deploy -DskipTests=true" - - cross-compile-aarch64-stage-snapshot: - <<: *cross-compile-aarch64-common - volumes: - - ~/.ssh:/root/.ssh - - ~/.gnupg:/root/.gnupg - - ~/.m2:/root/.m2 - - ~/local-staging:/root/local-staging - - ..:/code - command: /bin/bash -cl "./mvnw -B -ntp -Plinux-aarch64 -pl transport-native-unix-common,transport-native-epoll -am clean package org.sonatype.plugins:nexus-staging-maven-plugin:deploy -DaltStagingDirectory=/root/local-staging -DskipRemoteStaging=true -DskipTests=true" - - cross-compile-aarch64-stage-release: - <<: *cross-compile-aarch64-common - volumes: - - ~/.ssh:/root/.ssh - - ~/.m2:/root/.m2 - - ~/local-staging:/root/local-staging - - ..:/code - command: /bin/bash -cl "cat <(echo -e \"${GPG_PRIVATE_KEY}\") | gpg --batch --import && ./mvnw -B -ntp -Plinux-aarch64 -pl transport-native-unix-common,transport-native-epoll -am clean javadoc:jar package gpg:sign org.sonatype.plugins:nexus-staging-maven-plugin:deploy -DnexusUrl=https://oss.sonatype.org -DserverId=sonatype-nexus-staging -DaltStagingDirectory=/root/local-staging -DskipRemoteStaging=true -DskipTests=true -Dgpg.passphrase=${GPG_PASSPHRASE} -Dgpg.keyname=${GPG_KEYNAME}" - - cross-compile-aarch64-shell: - <<: *cross-compile-aarch64-common - entrypoint: /bin/bash - - cross-compile-aarch64-build: - <<: *cross-compile-aarch64-common - command: /bin/bash -cl "./mvnw -B -ntp -Plinux-aarch64 -pl transport-native-unix-common,transport-native-epoll -am clean package -DskipTests=true" \ No newline at end of file diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml deleted file mode 100644 index 616eeab12b..0000000000 --- a/docker/docker-compose.yaml +++ /dev/null @@ -1,71 +0,0 @@ -version: "3" - -services: - - runtime-setup: - image: netty:default - build: - context: . - dockerfile: Dockerfile.centos6 - - common: &common - image: netty:default - depends_on: [runtime-setup] - environment: - - GPG_KEYNAME - - GPG_PASSPHRASE - - GPG_PRIVATE_KEY - - MAVEN_OPTS - volumes: - - ~/.ssh:/root/.ssh - - ~/.gnupg:/root/.gnupg - - ~/.m2:/root/.m2 - - ..:/code - working_dir: /code - - build-leak: - <<: *common - command: /bin/bash -cl "./mvnw -B -ntp -Pleak clean install -Dio.netty.testsuite.badHost=netty.io -Dtcnative.classifier=linux-x86_64-fedora" - - build: - <<: *common - command: /bin/bash -cl "./mvnw -B -ntp clean install -Dio.netty.testsuite.badHost=netty.io -Dtcnative.classifier=linux-x86_64-fedora" - - deploy: - <<: *common - command: /bin/bash -cl "./mvnw -B -ntp clean deploy -DskipTests=true" - - stage-snapshot: - <<: *common - volumes: - - ~/.ssh:/root/.ssh - - ~/.gnupg:/root/.gnupg - - ~/.m2:/root/.m2 - - ~/local-staging:/root/local-staging - - ..:/code - command: /bin/bash -cl "./mvnw -B -ntp clean package org.sonatype.plugins:nexus-staging-maven-plugin:deploy -DaltStagingDirectory=/root/local-staging -DskipRemoteStaging=true -DskipTests=true" - - stage-release: - <<: *common - volumes: - - ~/.ssh:/root/.ssh - - ~/.m2:/root/.m2 - - ~/local-staging:/root/local-staging - - ..:/code - command: /bin/bash -cl "cat <(echo -e \"${GPG_PRIVATE_KEY}\") | gpg --batch --import && ./mvnw -B -ntp clean javadoc:jar package gpg:sign org.sonatype.plugins:nexus-staging-maven-plugin:deploy -DnexusUrl=https://oss.sonatype.org -DserverId=sonatype-nexus-staging -DaltStagingDirectory=/root/local-staging -DskipRemoteStaging=true -DskipTests=true -Dgpg.passphrase=${GPG_PASSPHRASE} -Dgpg.keyname=${GPG_KEYNAME} -Dtcnative.classifier=linux-x86_64-fedora" - - build-boringssl-static: - <<: *common - command: /bin/bash -cl "./mvnw -B -ntp -Pboringssl clean install -Dio.netty.testsuite.badHost=netty.io -Dxml.skip=true" - - build-leak-boringssl-static: - <<: *common - command: /bin/bash -cl "./mvnw -B -ntp -Pboringssl,leak clean install -Dio.netty.testsuite.badHost=netty.io -Dxml.skip=true" - - build-unsafe-buffer: - <<: *common - command: /bin/bash -cl "./mvnw -PunsafeBuffer clean install -Dio.netty.testsuite.badHost=netty.io -Dxml.skip=true -Dtcnative.classifier=linux-x86_64-fedora" - - shell: - <<: *common - entrypoint: /bin/bash diff --git a/example/.gitignore b/example/.gitignore deleted file mode 100644 index 02e33e7a9d..0000000000 --- a/example/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ - -# -# native udt library extract location -# - -/lib - diff --git a/example/pom.xml b/example/pom.xml deleted file mode 100644 index dc153ed1ac..0000000000 --- a/example/pom.xml +++ /dev/null @@ -1,191 +0,0 @@ - - - - - 4.0.0 - - - io.netty - netty-parent - 5.0.0.Final-SNAPSHOT - - - netty-example - jar - - Netty/Example - - - true - - - - ${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-transport-sctp - ${project.version} - - - ${project.groupId} - netty-handler-proxy - ${project.version} - - - ${project.groupId} - netty-codec-http - ${project.version} - - - ${project.groupId} - netty-codec-http2 - ${project.version} - - - ${project.groupId} - netty-codec-memcache - ${project.version} - - - ${project.groupId} - netty-codec-redis - ${project.version} - - - ${project.groupId} - netty-codec-socks - ${project.version} - - - ${project.groupId} - netty-codec-stomp - ${project.version} - - - ${project.groupId} - netty-codec-mqtt - ${project.version} - - - ${project.groupId} - netty-codec-haproxy - ${project.version} - - - ${project.groupId} - netty-codec-dns - ${project.version} - - - io.netty - netty-codec-smtp - ${project.version} - - - - com.google.protobuf - protobuf-java - - - ${project.groupId} - ${tcnative.artifactId} - ${tcnative.classifier} - - - ${conscrypt.groupId} - ${conscrypt.artifactId} - ${conscrypt.classifier} - - - org.eclipse.jetty.npn - npn-api - - - com.jcraft - jzlib - runtime - - - - ch.qos.logback - logback-classic - runtime - - - - - org.bouncycastle - bcpkix-jdk15on - - - org.bouncycastle - bcprov-jdk15on - - - - - com.sun.activation - javax.activation - - - - - - - - kr.motd.maven - exec-maven-plugin - - ${java.home}/bin/java - - ${argLine.common} - ${argLine.alpnAgent} - -classpath %classpath - ${argLine.leak} - ${argLine.coverage} - ${argLine.example} - ${exampleClass} - - - - - - - diff --git a/example/src/main/java/io/netty/example/discard/DiscardClient.java b/example/src/main/java/io/netty/example/discard/DiscardClient.java deleted file mode 100644 index 7742349867..0000000000 --- a/example/src/main/java/io/netty/example/discard/DiscardClient.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.discard; - -import io.netty.bootstrap.Bootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.util.InsecureTrustManagerFactory; - -/** - * Keeps sending random data to the specified address. - */ -public final class DiscardClient { - - static final boolean SSL = System.getProperty("ssl") != null; - static final String HOST = System.getProperty("host", "127.0.0.1"); - static final int PORT = Integer.parseInt(System.getProperty("port", "8009")); - static final int SIZE = Integer.parseInt(System.getProperty("size", "256")); - - public static void main(String[] args) throws Exception { - // Configure SSL. - final SslContext sslCtx; - if (SSL) { - sslCtx = SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE).build(); - } else { - sslCtx = null; - } - - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - Bootstrap b = new Bootstrap(); - b.group(group) - .channel(NioSocketChannel.class) - .handler(new ChannelInitializer() { - @Override - protected void initChannel(SocketChannel ch) throws Exception { - ChannelPipeline p = ch.pipeline(); - if (sslCtx != null) { - p.addLast(sslCtx.newHandler(ch.alloc(), HOST, PORT)); - } - p.addLast(new DiscardClientHandler()); - } - }); - - // Make the connection attempt. - Channel channel = b.connect(HOST, PORT).get(); - - // Wait until the connection is closed. - channel.closeFuture().sync(); - } finally { - group.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/discard/DiscardClientHandler.java b/example/src/main/java/io/netty/example/discard/DiscardClientHandler.java deleted file mode 100644 index 02cc84b51f..0000000000 --- a/example/src/main/java/io/netty/example/discard/DiscardClientHandler.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.discard; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.util.concurrent.FutureContextListener; - -/** - * Handles a client-side channel. - */ -public class DiscardClientHandler extends SimpleChannelInboundHandler { - - private ByteBuf content; - private ChannelHandlerContext ctx; - - @Override - public void channelActive(ChannelHandlerContext ctx) { - this.ctx = ctx; - - // Initialize the message. - content = ctx.alloc().directBuffer(DiscardClient.SIZE).writeZero(DiscardClient.SIZE); - - // Send the initial messages. - generateTraffic(); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) { - content.release(); - } - - @Override - public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { - // Server is supposed to send nothing, but if it sends something, discard it. - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - // Close the connection when an exception is raised. - cause.printStackTrace(); - ctx.close(); - } - - private void generateTraffic() { - // Flush the outbound buffer to the socket. - // Once flushed, generate the same amount of traffic again. - ctx.writeAndFlush(content.retainedDuplicate()).addListener(ctx.channel(), trafficGenerator); - } - - private final FutureContextListener trafficGenerator = (channel, future) -> { - if (future.isSuccess()) { - generateTraffic(); - } else { - future.cause().printStackTrace(); - channel.close(); - } - }; -} diff --git a/example/src/main/java/io/netty/example/discard/DiscardServer.java b/example/src/main/java/io/netty/example/discard/DiscardServer.java deleted file mode 100644 index 68fff242ed..0000000000 --- a/example/src/main/java/io/netty/example/discard/DiscardServer.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.discard; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.util.SelfSignedCertificate; - -/** - * Discards any incoming data. - */ -public final class DiscardServer { - - static final boolean SSL = System.getProperty("ssl") != null; - static final int PORT = Integer.parseInt(System.getProperty("port", "8009")); - - public static void main(String[] args) throws Exception { - // Configure SSL. - final SslContext sslCtx; - if (SSL) { - SelfSignedCertificate ssc = new SelfSignedCertificate(); - sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build(); - } else { - sslCtx = null; - } - - EventLoopGroup bossGroup = new MultithreadEventLoopGroup(1, NioHandler.newFactory()); - EventLoopGroup workerGroup = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - ServerBootstrap b = new ServerBootstrap(); - b.group(bossGroup, workerGroup) - .channel(NioServerSocketChannel.class) - .handler(new LoggingHandler(LogLevel.INFO)) - .childHandler(new ChannelInitializer() { - @Override - public void initChannel(SocketChannel ch) { - ChannelPipeline p = ch.pipeline(); - if (sslCtx != null) { - p.addLast(sslCtx.newHandler(ch.alloc())); - } - p.addLast(new DiscardServerHandler()); - } - }); - - // Bind and start to accept incoming connections. - Channel channel = b.bind(PORT).get(); - - // Wait until the server socket is closed. - // In this example, this does not happen, but you can do that to gracefully - // shut down your server. - channel.closeFuture().sync(); - } finally { - workerGroup.shutdownGracefully(); - bossGroup.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/discard/DiscardServerHandler.java b/example/src/main/java/io/netty/example/discard/DiscardServerHandler.java deleted file mode 100644 index 530d279b7e..0000000000 --- a/example/src/main/java/io/netty/example/discard/DiscardServerHandler.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.discard; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; - -/** - * Handles a server-side channel. - */ -public class DiscardServerHandler extends SimpleChannelInboundHandler { - - @Override - public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { - // discard - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - // Close the connection when an exception is raised. - cause.printStackTrace(); - ctx.close(); - } -} diff --git a/example/src/main/java/io/netty/example/dns/dot/DoTClient.java b/example/src/main/java/io/netty/example/dns/dot/DoTClient.java deleted file mode 100644 index 62cb95b16c..0000000000 --- a/example/src/main/java/io/netty/example/dns/dot/DoTClient.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.dns.dot; - -import io.netty.bootstrap.Bootstrap; -import io.netty.buffer.ByteBufUtil; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.codec.dns.DefaultDnsQuery; -import io.netty.handler.codec.dns.DefaultDnsQuestion; -import io.netty.handler.codec.dns.DefaultDnsResponse; -import io.netty.handler.codec.dns.DnsOpCode; -import io.netty.handler.codec.dns.DnsQuery; -import io.netty.handler.codec.dns.DnsQuestion; -import io.netty.handler.codec.dns.DnsRawRecord; -import io.netty.handler.codec.dns.DnsRecord; -import io.netty.handler.codec.dns.DnsRecordType; -import io.netty.handler.codec.dns.DnsSection; -import io.netty.handler.codec.dns.TcpDnsQueryEncoder; -import io.netty.handler.codec.dns.TcpDnsResponseDecoder; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.util.NetUtil; - -import java.util.Random; -import java.util.concurrent.TimeUnit; - -public final class DoTClient { - private static final String QUERY_DOMAIN = "www.example.com"; - private static final int DNS_SERVER_PORT = 853; - private static final String DNS_SERVER_HOST = "8.8.8.8"; - - private DoTClient() { - } - - private static void handleQueryResp(DefaultDnsResponse msg) { - if (msg.count(DnsSection.QUESTION) > 0) { - DnsQuestion question = msg.recordAt(DnsSection.QUESTION, 0); - System.out.printf("name: %s%n", question.name()); - } - for (int i = 0, count = msg.count(DnsSection.ANSWER); i < count; i++) { - DnsRecord record = msg.recordAt(DnsSection.ANSWER, i); - if (record.type() == DnsRecordType.A) { - // Just print the IP after query - DnsRawRecord raw = (DnsRawRecord) record; - System.out.println(NetUtil.bytesToIpAddress(ByteBufUtil.getBytes(raw.content()))); - } - } - } - - public static void main(String[] args) throws Exception { - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - final SslContext sslContext = SslContextBuilder.forClient() - .protocols("TLSv1.3", "TLSv1.2") - .build(); - - Bootstrap b = new Bootstrap(); - b.group(group) - .channel(NioSocketChannel.class) - .handler(new ChannelInitializer() { - @Override - protected void initChannel(SocketChannel ch) { - ChannelPipeline p = ch.pipeline(); - p.addLast(sslContext.newHandler(ch.alloc(), DNS_SERVER_HOST, DNS_SERVER_PORT)) - .addLast(new TcpDnsQueryEncoder()) - .addLast(new TcpDnsResponseDecoder()) - .addLast(new SimpleChannelInboundHandler() { - @Override - protected void messageReceived( - ChannelHandlerContext ctx, DefaultDnsResponse msg) { - try { - handleQueryResp(msg); - } finally { - ctx.close(); - } - } - }); - } - }); - final Channel ch = b.connect(DNS_SERVER_HOST, DNS_SERVER_PORT).get(); - - int randomID = new Random().nextInt(60000 - 1000) + 1000; - DnsQuery query = new DefaultDnsQuery(randomID, DnsOpCode.QUERY) - .setRecord(DnsSection.QUESTION, new DefaultDnsQuestion(QUERY_DOMAIN, DnsRecordType.A)); - ch.writeAndFlush(query).sync(); - boolean success = ch.closeFuture().await(10, TimeUnit.SECONDS); - if (!success) { - System.err.println("dns query timeout!"); - ch.close().sync(); - } - } finally { - group.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/dns/tcp/TcpDnsClient.java b/example/src/main/java/io/netty/example/dns/tcp/TcpDnsClient.java deleted file mode 100644 index 33c451761a..0000000000 --- a/example/src/main/java/io/netty/example/dns/tcp/TcpDnsClient.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.dns.tcp; - -import io.netty.bootstrap.Bootstrap; -import io.netty.buffer.ByteBufUtil; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.codec.dns.DefaultDnsQuery; -import io.netty.handler.codec.dns.DefaultDnsQuestion; -import io.netty.handler.codec.dns.DefaultDnsResponse; -import io.netty.handler.codec.dns.DnsOpCode; -import io.netty.handler.codec.dns.DnsQuery; -import io.netty.handler.codec.dns.DnsQuestion; -import io.netty.handler.codec.dns.DnsRawRecord; -import io.netty.handler.codec.dns.DnsRecord; -import io.netty.handler.codec.dns.DnsRecordType; -import io.netty.handler.codec.dns.DnsSection; -import io.netty.handler.codec.dns.TcpDnsQueryEncoder; -import io.netty.handler.codec.dns.TcpDnsResponseDecoder; -import io.netty.util.NetUtil; - -import java.util.Random; -import java.util.concurrent.TimeUnit; - -public final class TcpDnsClient { - private static final String QUERY_DOMAIN = "www.example.com"; - private static final int DNS_SERVER_PORT = 53; - private static final String DNS_SERVER_HOST = "8.8.8.8"; - - private TcpDnsClient() { - } - - private static void handleQueryResp(DefaultDnsResponse msg) { - if (msg.count(DnsSection.QUESTION) > 0) { - DnsQuestion question = msg.recordAt(DnsSection.QUESTION, 0); - System.out.printf("name: %s%n", question.name()); - } - for (int i = 0, count = msg.count(DnsSection.ANSWER); i < count; i++) { - DnsRecord record = msg.recordAt(DnsSection.ANSWER, i); - if (record.type() == DnsRecordType.A) { - // Just print the IP after query - DnsRawRecord raw = (DnsRawRecord) record; - System.out.println(NetUtil.bytesToIpAddress(ByteBufUtil.getBytes(raw.content()))); - } - } - } - - public static void main(String[] args) throws Exception { - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - Bootstrap b = new Bootstrap(); - b.group(group) - .channel(NioSocketChannel.class) - .handler(new ChannelInitializer() { - @Override - protected void initChannel(SocketChannel ch) { - ChannelPipeline p = ch.pipeline(); - p.addLast(new TcpDnsQueryEncoder()) - .addLast(new TcpDnsResponseDecoder()) - .addLast(new SimpleChannelInboundHandler() { - @Override - protected void messageReceived( - ChannelHandlerContext ctx, DefaultDnsResponse msg) { - try { - handleQueryResp(msg); - } finally { - ctx.close(); - } - } - }); - } - }); - - final Channel ch = b.connect(DNS_SERVER_HOST, DNS_SERVER_PORT).get(); - - int randomID = new Random().nextInt(60000 - 1000) + 1000; - DnsQuery query = new DefaultDnsQuery(randomID, DnsOpCode.QUERY) - .setRecord(DnsSection.QUESTION, new DefaultDnsQuestion(QUERY_DOMAIN, DnsRecordType.A)); - ch.writeAndFlush(query).sync(); - boolean success = ch.closeFuture().await(10, TimeUnit.SECONDS); - if (!success) { - System.err.println("dns query timeout!"); - ch.close().sync(); - } - } finally { - group.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/dns/tcp/TcpDnsServer.java b/example/src/main/java/io/netty/example/dns/tcp/TcpDnsServer.java deleted file mode 100644 index e32b77aa51..0000000000 --- a/example/src/main/java/io/netty/example/dns/tcp/TcpDnsServer.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.dns.tcp; - -import io.netty.bootstrap.Bootstrap; -import io.netty.bootstrap.ServerBootstrap; -import io.netty.buffer.ByteBufUtil; -import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.IoHandlerFactory; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.codec.dns.DefaultDnsQuery; -import io.netty.handler.codec.dns.DefaultDnsQuestion; -import io.netty.handler.codec.dns.DefaultDnsRawRecord; -import io.netty.handler.codec.dns.DefaultDnsResponse; -import io.netty.handler.codec.dns.DnsOpCode; -import io.netty.handler.codec.dns.DnsQuery; -import io.netty.handler.codec.dns.DnsQuestion; -import io.netty.handler.codec.dns.DnsRawRecord; -import io.netty.handler.codec.dns.DnsRecord; -import io.netty.handler.codec.dns.DnsRecordType; -import io.netty.handler.codec.dns.DnsSection; -import io.netty.handler.codec.dns.TcpDnsQueryDecoder; -import io.netty.handler.codec.dns.TcpDnsQueryEncoder; -import io.netty.handler.codec.dns.TcpDnsResponseDecoder; -import io.netty.handler.codec.dns.TcpDnsResponseEncoder; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; -import io.netty.util.NetUtil; - -import java.util.Random; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; - -public final class TcpDnsServer { - private static final String QUERY_DOMAIN = "www.example.com"; - private static final int DNS_SERVER_PORT = 53; - private static final String DNS_SERVER_HOST = "127.0.0.1"; - private static final byte[] QUERY_RESULT = {(byte) 192, (byte) 168, 1, 1}; - - public static void main(String[] args) throws Exception { - IoHandlerFactory ioHandlerFactory = NioHandler.newFactory(); - ServerBootstrap bootstrap = new ServerBootstrap() - .group(new MultithreadEventLoopGroup(1, ioHandlerFactory), - new MultithreadEventLoopGroup(ioHandlerFactory)) - .channel(NioServerSocketChannel.class) - .handler(new LoggingHandler(LogLevel.INFO)) - .childHandler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - ch.pipeline().addLast(new TcpDnsQueryDecoder(), new TcpDnsResponseEncoder(), - new SimpleChannelInboundHandler() { - @Override - protected void messageReceived(ChannelHandlerContext ctx, - DnsQuery msg) throws Exception { - DnsQuestion question = msg.recordAt(DnsSection.QUESTION); - System.out.println("Query domain: " + question); - - //always return 192.168.1.1 - ctx.writeAndFlush(newResponse(msg, question, 600, QUERY_RESULT)); - } - - private DefaultDnsResponse newResponse(DnsQuery query, - DnsQuestion question, - long ttl, byte[]... addresses) { - DefaultDnsResponse response = new DefaultDnsResponse(query.id()); - response.addRecord(DnsSection.QUESTION, question); - - for (byte[] address : addresses) { - DefaultDnsRawRecord queryAnswer = new DefaultDnsRawRecord( - question.name(), - DnsRecordType.A, ttl, Unpooled.wrappedBuffer(address)); - response.addRecord(DnsSection.ANSWER, queryAnswer); - } - return response; - } - }); - } - }); - final Channel channel = bootstrap.bind(DNS_SERVER_PORT).get(); - Executors.newSingleThreadScheduledExecutor().schedule(new Runnable() { - @Override - public void run() { - try { - clientQuery(); - channel.close(); - } catch (Exception e) { - e.printStackTrace(); - } - } - }, 1000, TimeUnit.MILLISECONDS); - channel.closeFuture().sync(); - } - - // copy from TcpDnsClient.java - private static void clientQuery() throws Exception { - MultithreadEventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - Bootstrap b = new Bootstrap(); - b.group(group) - .channel(NioSocketChannel.class) - .handler(new ChannelInitializer() { - @Override - protected void initChannel(SocketChannel ch) { - ch.pipeline().addLast(new TcpDnsQueryEncoder()) - .addLast(new TcpDnsResponseDecoder()) - .addLast(new SimpleChannelInboundHandler() { - @Override - protected void messageReceived( - ChannelHandlerContext ctx, DefaultDnsResponse msg) { - try { - handleQueryResp(msg); - } finally { - ctx.close(); - } - } - }); - } - }); - - final Channel ch = b.connect(DNS_SERVER_HOST, DNS_SERVER_PORT).get(); - - int randomID = new Random().nextInt(60000 - 1000) + 1000; - DnsQuery query = new DefaultDnsQuery(randomID, DnsOpCode.QUERY) - .setRecord(DnsSection.QUESTION, new DefaultDnsQuestion(QUERY_DOMAIN, DnsRecordType.A)); - ch.writeAndFlush(query).sync(); - boolean success = ch.closeFuture().await(10, TimeUnit.SECONDS); - if (!success) { - System.err.println("dns query timeout!"); - ch.close().sync(); - } - } finally { - group.shutdownGracefully(); - } - } - - private static void handleQueryResp(DefaultDnsResponse msg) { - if (msg.count(DnsSection.QUESTION) > 0) { - DnsQuestion question = msg.recordAt(DnsSection.QUESTION, 0); - System.out.printf("name: %s%n", question.name()); - } - for (int i = 0, count = msg.count(DnsSection.ANSWER); i < count; i++) { - DnsRecord record = msg.recordAt(DnsSection.ANSWER, i); - if (record.type() == DnsRecordType.A) { - //just print the IP after query - DnsRawRecord raw = (DnsRawRecord) record; - System.out.println(NetUtil.bytesToIpAddress(ByteBufUtil.getBytes(raw.content()))); - } - } - } -} diff --git a/example/src/main/java/io/netty/example/dns/udp/DnsClient.java b/example/src/main/java/io/netty/example/dns/udp/DnsClient.java deleted file mode 100644 index d3c56e5ca5..0000000000 --- a/example/src/main/java/io/netty/example/dns/udp/DnsClient.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.dns.udp; - -import io.netty.bootstrap.Bootstrap; -import io.netty.buffer.ByteBufUtil; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.DatagramChannel; -import io.netty.channel.socket.nio.NioDatagramChannel; -import io.netty.handler.codec.dns.DatagramDnsQuery; -import io.netty.handler.codec.dns.DatagramDnsQueryEncoder; -import io.netty.handler.codec.dns.DatagramDnsResponse; -import io.netty.handler.codec.dns.DatagramDnsResponseDecoder; -import io.netty.handler.codec.dns.DefaultDnsQuestion; -import io.netty.handler.codec.dns.DnsQuery; -import io.netty.handler.codec.dns.DnsQuestion; -import io.netty.handler.codec.dns.DnsRawRecord; -import io.netty.handler.codec.dns.DnsRecord; -import io.netty.handler.codec.dns.DnsRecordType; -import io.netty.handler.codec.dns.DnsSection; -import io.netty.util.NetUtil; - -import java.net.InetSocketAddress; -import java.util.concurrent.TimeUnit; - -public final class DnsClient { - - private static final String QUERY_DOMAIN = "www.example.com"; - private static final int DNS_SERVER_PORT = 53; - private static final String DNS_SERVER_HOST = "8.8.8.8"; - - private DnsClient() { } - - private static void handleQueryResp(DatagramDnsResponse msg) { - if (msg.count(DnsSection.QUESTION) > 0) { - DnsQuestion question = msg.recordAt(DnsSection.QUESTION, 0); - System.out.printf("name: %s%n", question.name()); - } - for (int i = 0, count = msg.count(DnsSection.ANSWER); i < count; i++) { - DnsRecord record = msg.recordAt(DnsSection.ANSWER, i); - if (record.type() == DnsRecordType.A) { - // Just print the IP after query - DnsRawRecord raw = (DnsRawRecord) record; - System.out.println(NetUtil.bytesToIpAddress(ByteBufUtil.getBytes(raw.content()))); - } - } - } - - public static void main(String[] args) throws Exception { - InetSocketAddress addr = new InetSocketAddress(DNS_SERVER_HOST, DNS_SERVER_PORT); - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - Bootstrap b = new Bootstrap(); - b.group(group) - .channel(NioDatagramChannel.class) - .handler(new ChannelInitializer() { - @Override - protected void initChannel(DatagramChannel ch) { - ChannelPipeline p = ch.pipeline(); - p.addLast(new DatagramDnsQueryEncoder()) - .addLast(new DatagramDnsResponseDecoder()) - .addLast(new SimpleChannelInboundHandler() { - @Override - protected void messageReceived(ChannelHandlerContext ctx, DatagramDnsResponse msg) { - try { - handleQueryResp(msg); - } finally { - ctx.close(); - } - } - }); - } - }); - final Channel ch = b.bind(0).get(); - DnsQuery query = new DatagramDnsQuery(null, addr, 1).setRecord( - DnsSection.QUESTION, - new DefaultDnsQuestion(QUERY_DOMAIN, DnsRecordType.A)); - ch.writeAndFlush(query).sync(); - boolean success = ch.closeFuture().await(10, TimeUnit.SECONDS); - if (!success) { - System.err.println("dns query timeout!"); - ch.close().sync(); - } - } finally { - group.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/echo/EchoClient.java b/example/src/main/java/io/netty/example/echo/EchoClient.java deleted file mode 100644 index 92d26be580..0000000000 --- a/example/src/main/java/io/netty/example/echo/EchoClient.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.echo; - -import io.netty.bootstrap.Bootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelOption; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.util.InsecureTrustManagerFactory; - -/** - * Sends one message when a connection is open and echoes back any received - * data to the server. Simply put, the echo client initiates the ping-pong - * traffic between the echo client and server by sending the first message to - * the server. - */ -public final class EchoClient { - - static final boolean SSL = System.getProperty("ssl") != null; - static final String HOST = System.getProperty("host", "127.0.0.1"); - static final int PORT = Integer.parseInt(System.getProperty("port", "8007")); - static final int SIZE = Integer.parseInt(System.getProperty("size", "256")); - - public static void main(String[] args) throws Exception { - // Configure SSL.git - final SslContext sslCtx; - if (SSL) { - sslCtx = SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE).build(); - } else { - sslCtx = null; - } - - // Configure the client. - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - Bootstrap b = new Bootstrap(); - b.group(group) - .channel(NioSocketChannel.class) - .option(ChannelOption.TCP_NODELAY, true) - .handler(new ChannelInitializer() { - @Override - public void initChannel(SocketChannel ch) throws Exception { - ChannelPipeline p = ch.pipeline(); - if (sslCtx != null) { - p.addLast(sslCtx.newHandler(ch.alloc(), HOST, PORT)); - } - //p.addLast(new LoggingHandler(LogLevel.INFO)); - p.addLast(new EchoClientHandler()); - } - }); - - // Start the client. - Channel channel = b.connect(HOST, PORT).get(); - - // Wait until the connection is closed. - channel.closeFuture().sync(); - } finally { - // Shut down the event loop to terminate all threads. - group.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/echo/EchoClientHandler.java b/example/src/main/java/io/netty/example/echo/EchoClientHandler.java deleted file mode 100644 index 72d3fa5d81..0000000000 --- a/example/src/main/java/io/netty/example/echo/EchoClientHandler.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.echo; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; - -/** - * Handler implementation for the echo client. It initiates the ping-pong - * traffic between the echo client and server by sending the first message to - * the server. - */ -public class EchoClientHandler implements ChannelHandler { - - private final ByteBuf firstMessage; - - /** - * Creates a client-side handler. - */ - public EchoClientHandler() { - firstMessage = Unpooled.buffer(EchoClient.SIZE); - for (int i = 0; i < firstMessage.capacity(); i ++) { - firstMessage.writeByte((byte) i); - } - } - - @Override - public void channelActive(ChannelHandlerContext ctx) { - ctx.writeAndFlush(firstMessage); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - ctx.write(msg); - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) { - ctx.flush(); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - // Close the connection when an exception is raised. - cause.printStackTrace(); - ctx.close(); - } -} diff --git a/example/src/main/java/io/netty/example/echo/EchoServer.java b/example/src/main/java/io/netty/example/echo/EchoServer.java deleted file mode 100644 index 6c97960828..0000000000 --- a/example/src/main/java/io/netty/example/echo/EchoServer.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.echo; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelOption; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.util.SelfSignedCertificate; - -/** - * Echoes back any received data from a client. - */ -public final class EchoServer { - - static final boolean SSL = System.getProperty("ssl") != null; - static final int PORT = Integer.parseInt(System.getProperty("port", "8007")); - - public static void main(String[] args) throws Exception { - // Configure SSL. - final SslContext sslCtx; - if (SSL) { - SelfSignedCertificate ssc = new SelfSignedCertificate(); - sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build(); - } else { - sslCtx = null; - } - - // Configure the server. - EventLoopGroup bossGroup = new MultithreadEventLoopGroup(1, NioHandler.newFactory()); - EventLoopGroup workerGroup = new MultithreadEventLoopGroup(NioHandler.newFactory()); - final EchoServerHandler serverHandler = new EchoServerHandler(); - try { - ServerBootstrap b = new ServerBootstrap(); - b.group(bossGroup, workerGroup) - .channel(NioServerSocketChannel.class) - .option(ChannelOption.SO_BACKLOG, 100) - .handler(new LoggingHandler(LogLevel.INFO)) - .childHandler(new ChannelInitializer() { - @Override - public void initChannel(SocketChannel ch) throws Exception { - ChannelPipeline p = ch.pipeline(); - if (sslCtx != null) { - p.addLast(sslCtx.newHandler(ch.alloc())); - } - //p.addLast(new LoggingHandler(LogLevel.INFO)); - p.addLast(serverHandler); - } - }); - - // Start the server. - Channel channel = b.bind(PORT).get(); - - // Wait until the server socket is closed. - channel.closeFuture().sync(); - } finally { - // Shut down all event loops to terminate all threads. - bossGroup.shutdownGracefully(); - workerGroup.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/echo/EchoServerHandler.java b/example/src/main/java/io/netty/example/echo/EchoServerHandler.java deleted file mode 100644 index da490eed59..0000000000 --- a/example/src/main/java/io/netty/example/echo/EchoServerHandler.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.echo; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; - -/** - * Handler implementation for the echo server. - */ -@Sharable -public class EchoServerHandler implements ChannelHandler { - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - ctx.write(msg); - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) { - ctx.flush(); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - // Close the connection when an exception is raised. - cause.printStackTrace(); - ctx.close(); - } -} diff --git a/example/src/main/java/io/netty/example/factorial/BigIntegerDecoder.java b/example/src/main/java/io/netty/example/factorial/BigIntegerDecoder.java deleted file mode 100644 index 9a633bbf2a..0000000000 --- a/example/src/main/java/io/netty/example/factorial/BigIntegerDecoder.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.factorial; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.ByteToMessageDecoder; -import io.netty.handler.codec.CorruptedFrameException; - -import java.math.BigInteger; - -/** - * Decodes the binary representation of a {@link BigInteger} prepended - * with a magic number ('F' or 0x46) and a 32-bit integer length prefix into a - * {@link BigInteger} instance. For example, { 'F', 0, 0, 0, 1, 42 } will be - * decoded into new BigInteger("42"). - */ -public class BigIntegerDecoder extends ByteToMessageDecoder { - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) { - // Wait until the length prefix is available. - if (in.readableBytes() < 5) { - return; - } - - int readerIndex = in.readerIndex(); - - // Check the magic number. - int magicNumber = in.readUnsignedByte(); - if (magicNumber != 'F') { - in.readerIndex(readerIndex); - throw new CorruptedFrameException("Invalid magic number: " + magicNumber); - } - - // Wait until the whole data is available. - int dataLength = in.readInt(); - if (in.readableBytes() < dataLength) { - in.readerIndex(readerIndex); - return; - } - - // Convert the received data into a new BigInteger. - byte[] decoded = new byte[dataLength]; - in.readBytes(decoded); - - ctx.fireChannelRead(new BigInteger(decoded)); - } -} diff --git a/example/src/main/java/io/netty/example/factorial/FactorialClient.java b/example/src/main/java/io/netty/example/factorial/FactorialClient.java deleted file mode 100644 index f7ee4a4243..0000000000 --- a/example/src/main/java/io/netty/example/factorial/FactorialClient.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.factorial; - -import io.netty.bootstrap.Bootstrap; -import io.netty.channel.Channel; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.util.InsecureTrustManagerFactory; - -/** - * Sends a sequence of integers to a {@link FactorialServer} to calculate - * the factorial of the specified integer. - */ -public final class FactorialClient { - - static final boolean SSL = System.getProperty("ssl") != null; - static final String HOST = System.getProperty("host", "127.0.0.1"); - static final int PORT = Integer.parseInt(System.getProperty("port", "8322")); - static final int COUNT = Integer.parseInt(System.getProperty("count", "1000")); - - public static void main(String[] args) throws Exception { - // Configure SSL. - final SslContext sslCtx; - if (SSL) { - sslCtx = SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE).build(); - } else { - sslCtx = null; - } - - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - Bootstrap b = new Bootstrap(); - b.group(group) - .channel(NioSocketChannel.class) - .handler(new FactorialClientInitializer(sslCtx)); - - // Make a new connection. - Channel channel = b.connect(HOST, PORT).get(); - - // Get the handler instance to retrieve the answer. - FactorialClientHandler handler = (FactorialClientHandler) channel.pipeline().last(); - - // Print out the answer. - System.err.format("Factorial of %,d is: %,d", COUNT, handler.getFactorial()); - } finally { - group.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/factorial/FactorialClientHandler.java b/example/src/main/java/io/netty/example/factorial/FactorialClientHandler.java deleted file mode 100644 index edcb042bea..0000000000 --- a/example/src/main/java/io/netty/example/factorial/FactorialClientHandler.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.factorial; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.FutureListener; - -import java.math.BigInteger; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; - -/** - * Handler for a client-side channel. This handler maintains stateful - * information which is specific to a certain channel using member variables. - * Therefore, an instance of this handler can cover only one channel. You have - * to create a new handler instance whenever you create a new channel and insert - * this handler to avoid a race condition. - */ -public class FactorialClientHandler extends SimpleChannelInboundHandler { - - private ChannelHandlerContext ctx; - private int receivedMessages; - private int next = 1; - final BlockingQueue answer = new LinkedBlockingQueue<>(); - - public BigInteger getFactorial() { - boolean interrupted = false; - try { - for (;;) { - try { - return answer.take(); - } catch (InterruptedException ignore) { - interrupted = true; - } - } - } finally { - if (interrupted) { - Thread.currentThread().interrupt(); - } - } - } - - @Override - public void channelActive(ChannelHandlerContext ctx) { - this.ctx = ctx; - sendNumbers(); - } - - @Override - public void messageReceived(ChannelHandlerContext ctx, final BigInteger msg) { - receivedMessages ++; - if (receivedMessages == FactorialClient.COUNT) { - // Offer the answer after closing the connection. - ctx.channel().close().addListener(future -> { - boolean offered = answer.offer(msg); - assert offered; - }); - } - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - cause.printStackTrace(); - ctx.close(); - } - - private void sendNumbers() { - // Do not send more than 4096 numbers. - Future future = null; - for (int i = 0; i < 4096 && next <= FactorialClient.COUNT; i++) { - future = ctx.write(Integer.valueOf(next)); - next++; - } - if (next <= FactorialClient.COUNT) { - assert future != null; - future.addListener(numberSender); - } - ctx.flush(); - } - - private final FutureListener numberSender = future -> { - if (future.isSuccess()) { - sendNumbers(); - } else { - future.cause().printStackTrace(); - ctx.channel().close(); - } - }; -} diff --git a/example/src/main/java/io/netty/example/factorial/FactorialClientInitializer.java b/example/src/main/java/io/netty/example/factorial/FactorialClientInitializer.java deleted file mode 100644 index da6236aefe..0000000000 --- a/example/src/main/java/io/netty/example/factorial/FactorialClientInitializer.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.factorial; - -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.socket.SocketChannel; -import io.netty.handler.codec.compression.ZlibCodecFactory; -import io.netty.handler.codec.compression.ZlibWrapper; -import io.netty.handler.ssl.SslContext; - -/** - * Creates a newly configured {@link ChannelPipeline} for a client-side channel. - */ -public class FactorialClientInitializer extends ChannelInitializer { - - private final SslContext sslCtx; - - public FactorialClientInitializer(SslContext sslCtx) { - this.sslCtx = sslCtx; - } - - @Override - public void initChannel(SocketChannel ch) { - ChannelPipeline pipeline = ch.pipeline(); - - if (sslCtx != null) { - pipeline.addLast(sslCtx.newHandler(ch.alloc(), FactorialClient.HOST, FactorialClient.PORT)); - } - - // Enable stream compression (you can remove these two if unnecessary) - pipeline.addLast(ZlibCodecFactory.newZlibEncoder(ZlibWrapper.GZIP)); - pipeline.addLast(ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP)); - - // Add the number codec first, - pipeline.addLast(new BigIntegerDecoder()); - pipeline.addLast(new NumberEncoder()); - - // and then business logic. - pipeline.addLast(new FactorialClientHandler()); - } -} diff --git a/example/src/main/java/io/netty/example/factorial/FactorialServer.java b/example/src/main/java/io/netty/example/factorial/FactorialServer.java deleted file mode 100644 index 323decbf04..0000000000 --- a/example/src/main/java/io/netty/example/factorial/FactorialServer.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.factorial; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.util.SelfSignedCertificate; - -/** - * Receives a sequence of integers from a {@link FactorialClient} to calculate - * the factorial of the specified integer. - */ -public final class FactorialServer { - - static final boolean SSL = System.getProperty("ssl") != null; - static final int PORT = Integer.parseInt(System.getProperty("port", "8322")); - - public static void main(String[] args) throws Exception { - // Configure SSL. - final SslContext sslCtx; - if (SSL) { - SelfSignedCertificate ssc = new SelfSignedCertificate(); - sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build(); - } else { - sslCtx = null; - } - - EventLoopGroup bossGroup = new MultithreadEventLoopGroup(1, NioHandler.newFactory()); - EventLoopGroup workerGroup = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - ServerBootstrap b = new ServerBootstrap(); - b.group(bossGroup, workerGroup) - .channel(NioServerSocketChannel.class) - .handler(new LoggingHandler(LogLevel.INFO)) - .childHandler(new FactorialServerInitializer(sslCtx)); - - b.bind(PORT).get().closeFuture().sync(); - } finally { - bossGroup.shutdownGracefully(); - workerGroup.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/factorial/FactorialServerHandler.java b/example/src/main/java/io/netty/example/factorial/FactorialServerHandler.java deleted file mode 100644 index 05dcc34875..0000000000 --- a/example/src/main/java/io/netty/example/factorial/FactorialServerHandler.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.factorial; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; - -import java.math.BigInteger; - -/** - * Handler for a server-side channel. This handler maintains stateful - * information which is specific to a certain channel using member variables. - * Therefore, an instance of this handler can cover only one channel. You have - * to create a new handler instance whenever you create a new channel and insert - * this handler to avoid a race condition. - */ -public class FactorialServerHandler extends SimpleChannelInboundHandler { - - private BigInteger lastMultiplier = new BigInteger("1"); - private BigInteger factorial = new BigInteger("1"); - - @Override - public void messageReceived(ChannelHandlerContext ctx, BigInteger msg) throws Exception { - // Calculate the cumulative factorial and send it to the client. - lastMultiplier = msg; - factorial = factorial.multiply(msg); - ctx.writeAndFlush(factorial); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - System.err.printf("Factorial of %,d is: %,d%n", lastMultiplier, factorial); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - cause.printStackTrace(); - ctx.close(); - } -} diff --git a/example/src/main/java/io/netty/example/factorial/FactorialServerInitializer.java b/example/src/main/java/io/netty/example/factorial/FactorialServerInitializer.java deleted file mode 100644 index e0ff81fe10..0000000000 --- a/example/src/main/java/io/netty/example/factorial/FactorialServerInitializer.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.factorial; - -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.socket.SocketChannel; -import io.netty.handler.codec.compression.ZlibCodecFactory; -import io.netty.handler.codec.compression.ZlibWrapper; -import io.netty.handler.ssl.SslContext; - -/** - * Creates a newly configured {@link ChannelPipeline} for a server-side channel. - */ -public class FactorialServerInitializer extends ChannelInitializer { - - private final SslContext sslCtx; - - public FactorialServerInitializer(SslContext sslCtx) { - this.sslCtx = sslCtx; - } - - @Override - public void initChannel(SocketChannel ch) { - ChannelPipeline pipeline = ch.pipeline(); - - if (sslCtx != null) { - pipeline.addLast(sslCtx.newHandler(ch.alloc())); - } - - // Enable stream compression (you can remove these two if unnecessary) - pipeline.addLast(ZlibCodecFactory.newZlibEncoder(ZlibWrapper.GZIP)); - pipeline.addLast(ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP)); - - // Add the number codec first, - pipeline.addLast(new BigIntegerDecoder()); - pipeline.addLast(new NumberEncoder()); - - // and then business logic. - // Please note we create a handler for every new channel - // because it has stateful properties. - pipeline.addLast(new FactorialServerHandler()); - } -} diff --git a/example/src/main/java/io/netty/example/factorial/NumberEncoder.java b/example/src/main/java/io/netty/example/factorial/NumberEncoder.java deleted file mode 100644 index 7678e2367b..0000000000 --- a/example/src/main/java/io/netty/example/factorial/NumberEncoder.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.factorial; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToByteEncoder; - -import java.math.BigInteger; - -/** - * Encodes a {@link Number} into the binary representation prepended with - * a magic number ('F' or 0x46) and a 32-bit length prefix. For example, 42 - * will be encoded to { 'F', 0, 0, 0, 1, 42 }. - */ -public class NumberEncoder extends MessageToByteEncoder { - - @Override - protected void encode(ChannelHandlerContext ctx, Number msg, ByteBuf out) { - // Convert to a BigInteger first for easier implementation. - BigInteger v; - if (msg instanceof BigInteger) { - v = (BigInteger) msg; - } else { - v = new BigInteger(String.valueOf(msg)); - } - - // Convert the number into a byte array. - byte[] data = v.toByteArray(); - int dataLength = data.length; - - // Write a message. - out.writeByte((byte) 'F'); // magic number - out.writeInt(dataLength); // data length - out.writeBytes(data); // data - } -} diff --git a/example/src/main/java/io/netty/example/file/FileServer.java b/example/src/main/java/io/netty/example/file/FileServer.java deleted file mode 100644 index 0efdbf8f09..0000000000 --- a/example/src/main/java/io/netty/example/file/FileServer.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.file; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelOption; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.codec.LineBasedFrameDecoder; -import io.netty.handler.codec.string.StringDecoder; -import io.netty.handler.codec.string.StringEncoder; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.util.SelfSignedCertificate; -import io.netty.handler.stream.ChunkedWriteHandler; -import io.netty.util.CharsetUtil; - -/** - * Server that accept the path of a file an echo back its content. - */ -public final class FileServer { - - static final boolean SSL = System.getProperty("ssl") != null; - // Use the same default port with the telnet example so that we can use the telnet client example to access it. - static final int PORT = Integer.parseInt(System.getProperty("port", SSL? "8992" : "8023")); - - public static void main(String[] args) throws Exception { - // Configure SSL. - final SslContext sslCtx; - if (SSL) { - SelfSignedCertificate ssc = new SelfSignedCertificate(); - sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build(); - } else { - sslCtx = null; - } - - // Configure the server. - EventLoopGroup bossGroup = new MultithreadEventLoopGroup(1, NioHandler.newFactory()); - EventLoopGroup workerGroup = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - ServerBootstrap b = new ServerBootstrap(); - b.group(bossGroup, workerGroup) - .channel(NioServerSocketChannel.class) - .option(ChannelOption.SO_BACKLOG, 100) - .handler(new LoggingHandler(LogLevel.INFO)) - .childHandler(new ChannelInitializer() { - @Override - public void initChannel(SocketChannel ch) throws Exception { - ChannelPipeline p = ch.pipeline(); - if (sslCtx != null) { - p.addLast(sslCtx.newHandler(ch.alloc())); - } - p.addLast( - new StringEncoder(CharsetUtil.UTF_8), - new LineBasedFrameDecoder(8192), - new StringDecoder(CharsetUtil.UTF_8), - new ChunkedWriteHandler(), - new FileServerHandler()); - } - }); - - // Start the server. - Channel channel = b.bind(PORT).get(); - - // Wait until the server socket is closed. - channel.closeFuture().sync(); - } finally { - // Shut down all event loops to terminate all threads. - bossGroup.shutdownGracefully(); - workerGroup.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/file/FileServerHandler.java b/example/src/main/java/io/netty/example/file/FileServerHandler.java deleted file mode 100644 index 6ecf728657..0000000000 --- a/example/src/main/java/io/netty/example/file/FileServerHandler.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.example.file; - -import io.netty.channel.ChannelFutureListeners; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.DefaultFileRegion; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.handler.ssl.SslHandler; -import io.netty.handler.stream.ChunkedFile; - -import java.io.RandomAccessFile; - -public class FileServerHandler extends SimpleChannelInboundHandler { - - @Override - public void channelActive(ChannelHandlerContext ctx) { - ctx.writeAndFlush("HELLO: Type the path of the file to retrieve.\n"); - } - - @Override - public void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception { - RandomAccessFile raf = null; - long length = -1; - try { - raf = new RandomAccessFile(msg, "r"); - length = raf.length(); - } catch (Exception e) { - ctx.writeAndFlush("ERR: " + e.getClass().getSimpleName() + ": " + e.getMessage() + '\n'); - return; - } finally { - if (length < 0 && raf != null) { - raf.close(); - } - } - - ctx.write("OK: " + raf.length() + '\n'); - if (ctx.pipeline().get(SslHandler.class) == null) { - // SSL not enabled - can use zero-copy file transfer. - ctx.write(new DefaultFileRegion(raf.getChannel(), 0, length)); - } else { - // SSL enabled - cannot use zero-copy file transfer. - ctx.write(new ChunkedFile(raf)); - } - ctx.writeAndFlush("\n"); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - cause.printStackTrace(); - - if (ctx.channel().isActive()) { - ctx.writeAndFlush("ERR: " + - cause.getClass().getSimpleName() + ": " + - cause.getMessage() + '\n') - .addListener(ctx, ChannelFutureListeners.CLOSE); - } - } -} - diff --git a/example/src/main/java/io/netty/example/haproxy/HAProxyClient.java b/example/src/main/java/io/netty/example/haproxy/HAProxyClient.java deleted file mode 100644 index 942f76e4c7..0000000000 --- a/example/src/main/java/io/netty/example/haproxy/HAProxyClient.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.example.haproxy; - -import io.netty.bootstrap.Bootstrap; -import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.codec.haproxy.HAProxyCommand; -import io.netty.handler.codec.haproxy.HAProxyMessage; -import io.netty.handler.codec.haproxy.HAProxyProtocolVersion; -import io.netty.handler.codec.haproxy.HAProxyProxiedProtocol; -import io.netty.util.CharsetUtil; - -import static io.netty.example.haproxy.HAProxyServer.PORT; - -public final class HAProxyClient { - - private static final String HOST = System.getProperty("host", "127.0.0.1"); - - public static void main(String[] args) throws Exception { - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - Bootstrap b = new Bootstrap(); - b.group(group) - .channel(NioSocketChannel.class) - .handler(new HAProxyHandler()); - - // Start the connection attempt. - Channel ch = b.connect(HOST, PORT).get(); - - HAProxyMessage message = new HAProxyMessage( - HAProxyProtocolVersion.V2, HAProxyCommand.PROXY, HAProxyProxiedProtocol.TCP4, - "127.0.0.1", "127.0.0.2", 8000, 9000); - - ch.writeAndFlush(message).sync(); - ch.writeAndFlush(Unpooled.copiedBuffer("Hello World!", CharsetUtil.US_ASCII)).sync(); - ch.writeAndFlush(Unpooled.copiedBuffer("Bye now!", CharsetUtil.US_ASCII)).sync(); - ch.close().sync(); - } finally { - group.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/haproxy/HAProxyHandler.java b/example/src/main/java/io/netty/example/haproxy/HAProxyHandler.java deleted file mode 100644 index cee058c117..0000000000 --- a/example/src/main/java/io/netty/example/haproxy/HAProxyHandler.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.example.haproxy; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.haproxy.HAProxyMessage; -import io.netty.handler.codec.haproxy.HAProxyMessageEncoder; -import io.netty.util.concurrent.Future; - -public class HAProxyHandler implements ChannelHandler { - - @Override - public void handlerAdded(ChannelHandlerContext ctx) throws Exception { - ctx.pipeline().addBefore(ctx.name(), null, HAProxyMessageEncoder.INSTANCE); - } - - @Override - public Future write(final ChannelHandlerContext ctx, Object msg) { - Future future = ctx.write(msg); - if (msg instanceof HAProxyMessage) { - future.addListener(fut -> { - if (fut.isSuccess()) { - ctx.pipeline().remove(HAProxyMessageEncoder.INSTANCE); - ctx.pipeline().remove(this); - } else { - ctx.close(); - } - }); - } - return future; - } -} diff --git a/example/src/main/java/io/netty/example/haproxy/HAProxyServer.java b/example/src/main/java/io/netty/example/haproxy/HAProxyServer.java deleted file mode 100644 index 5063f93fdc..0000000000 --- a/example/src/main/java/io/netty/example/haproxy/HAProxyServer.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.example.haproxy; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.codec.haproxy.HAProxyMessage; -import io.netty.handler.codec.haproxy.HAProxyMessageDecoder; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; - -public final class HAProxyServer { - - static final int PORT = Integer.parseInt(System.getProperty("port", "8080")); - - public static void main(String[] args) throws Exception { - EventLoopGroup bossGroup = new MultithreadEventLoopGroup(1, NioHandler.newFactory()); - EventLoopGroup workerGroup = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - ServerBootstrap b = new ServerBootstrap(); - b.group(bossGroup, workerGroup) - .channel(NioServerSocketChannel.class) - .handler(new LoggingHandler(LogLevel.INFO)) - .childHandler(new HAProxyServerInitializer()); - b.bind(PORT).get().closeFuture().sync(); - } finally { - bossGroup.shutdownGracefully(); - workerGroup.shutdownGracefully(); - } - } - - static class HAProxyServerInitializer extends ChannelInitializer { - @Override - public void initChannel(SocketChannel ch) throws Exception { - ch.pipeline().addLast( - new LoggingHandler(LogLevel.DEBUG), - new HAProxyMessageDecoder(), - new SimpleChannelInboundHandler() { - @Override - protected void messageReceived(ChannelHandlerContext ctx, Object msg) { - if (msg instanceof HAProxyMessage) { - System.out.println("proxy message: " + msg); - } else if (msg instanceof ByteBuf) { - System.out.println("bytebuf message: " + ByteBufUtil.prettyHexDump((ByteBuf) msg)); - } - } - }); - } - } -} diff --git a/example/src/main/java/io/netty/example/http/cors/HttpCorsServer.java b/example/src/main/java/io/netty/example/http/cors/HttpCorsServer.java deleted file mode 100644 index 6e30f65d54..0000000000 --- a/example/src/main/java/io/netty/example/http/cors/HttpCorsServer.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.http.cors; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.util.SelfSignedCertificate; - -/** - * This example server aims to demonstrate - * Cross Origin Resource Sharing (CORS) in Netty. - * It does not have a client like most of the other examples, but instead has - * a html page that is loaded to try out CORS support in a web browser. - *

- * - * CORS is configured in {@link HttpCorsServerInitializer} and by updating the config you can - * try out various combinations, like using a specific origin instead of a - * wildcard origin ('*'). - *

- * - * The file {@code src/main/resources/cors/cors.html} contains a very basic example client - * which can be used to try out different configurations. For example, you can add - * custom headers to force a CORS preflight request to make the request fail. Then - * to enable a successful request, configure the CorsHandler to allow that/those - * request headers. - * - *

Testing CORS

- * You can either load the file {@code src/main/resources/cors/cors.html} using a web server - * or load it from the file system using a web browser. - * - *

Using a web server

- * To test CORS support you can serve the file {@code src/main/resources/cors/cors.html} - * using a web server. You can then add a new host name to your systems hosts file, for - * example if you are on Linux you may update /etc/hosts to add an additional name - * for you local system: - *
- * 127.0.0.1   localhost domain1.com
- * 
- * Now, you should be able to access {@code http://domain1.com/cors.html} depending on how you - * have configured you local web server the exact url may differ. - * - *

Using a web browser

- * Open the file {@code src/main/resources/cors/cors.html} in a web browser. You should see - * loaded page and in the text area the following message: - *
- * 'CORS is not working'
- * 
- * - * If you inspect the headers being sent using your browser you'll see that the 'Origin' - * request header is {@code 'null'}. This is expected and happens when you load a file from the - * local file system. Netty can handle this by configuring the CorsHandler which is done - * in the {@link HttpCorsServerInitializer}. - * - */ -public final class HttpCorsServer { - - static final boolean SSL = System.getProperty("ssl") != null; - static final int PORT = Integer.parseInt(System.getProperty("port", SSL? "8443" : "8080")); - - public static void main(String[] args) throws Exception { - // Configure SSL. - final SslContext sslCtx; - if (SSL) { - SelfSignedCertificate ssc = new SelfSignedCertificate(); - sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build(); - } else { - sslCtx = null; - } - - EventLoopGroup bossGroup = new MultithreadEventLoopGroup(1, NioHandler.newFactory()); - EventLoopGroup workerGroup = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - ServerBootstrap b = new ServerBootstrap(); - b.group(bossGroup, workerGroup) - .channel(NioServerSocketChannel.class) - .handler(new LoggingHandler(LogLevel.INFO)) - .childHandler(new HttpCorsServerInitializer(sslCtx)); - - b.bind(PORT).get().closeFuture().sync(); - } finally { - bossGroup.shutdownGracefully(); - workerGroup.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/http/cors/HttpCorsServerInitializer.java b/example/src/main/java/io/netty/example/http/cors/HttpCorsServerInitializer.java deleted file mode 100644 index 157c71e6ca..0000000000 --- a/example/src/main/java/io/netty/example/http/cors/HttpCorsServerInitializer.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.http.cors; - -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.socket.SocketChannel; -import io.netty.handler.codec.http.HttpObjectAggregator; -import io.netty.handler.codec.http.HttpRequestDecoder; -import io.netty.handler.codec.http.HttpResponseEncoder; -import io.netty.handler.codec.http.cors.CorsConfig; -import io.netty.handler.codec.http.cors.CorsConfigBuilder; -import io.netty.handler.codec.http.cors.CorsHandler; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.stream.ChunkedWriteHandler; - -/** - * Please refer to the {@link CorsConfig} javadocs for information about all the - * configuration options available. - * - * Below are some of configuration discussed in this example: - *

Support only a specific origin

- * To support a single origin instead of the wildcard use the following: - *
- * CorsConfig corsConfig = CorsConfig.withOrigin("http://domain1.com")
- * 
- * - *

Enable loading from the file system

- * To enable the server to handle an origin specified as 'null', which happens - * when a web browser loads a file from the local file system use the following: - *
- * corsConfig.isNullOriginAllowed()
- * 
- * - *

Enable request headers

- * To enable additional request headers: - *
- * corsConfig.allowedRequestHeaders("custom-request-header")
- * 
- * - *

Expose response headers

- * By default a browser only exposes the following simple header: - *
    - *
  • Cache-Control
  • - *
  • Content-Language
  • - *
  • Content-Type
  • - *
  • Expires
  • - *
  • Last-Modified
  • - *
  • Pragma
  • - *
- * Any of the above response headers can be retrieved by: - *
- * xhr.getResponseHeader("Content-Type");
- * 
- * If you need to get access to other headers this must be enabled by the server, for example: - *
- * corsConfig.exposedHeaders("custom-response-header");
- * 
- */ -public class HttpCorsServerInitializer extends ChannelInitializer { - - private final SslContext sslCtx; - - public HttpCorsServerInitializer(SslContext sslCtx) { - this.sslCtx = sslCtx; - } - - @Override - public void initChannel(SocketChannel ch) { - CorsConfig corsConfig = CorsConfigBuilder.forAnyOrigin().allowNullOrigin().allowCredentials().build(); - ChannelPipeline pipeline = ch.pipeline(); - if (sslCtx != null) { - pipeline.addLast(sslCtx.newHandler(ch.alloc())); - } - pipeline.addLast(new HttpResponseEncoder()); - pipeline.addLast(new HttpRequestDecoder()); - pipeline.addLast(new HttpObjectAggregator(65536)); - pipeline.addLast(new ChunkedWriteHandler()); - pipeline.addLast(new CorsHandler(corsConfig)); - pipeline.addLast(new OkResponseHandler()); - } - -} diff --git a/example/src/main/java/io/netty/example/http/cors/OkResponseHandler.java b/example/src/main/java/io/netty/example/http/cors/OkResponseHandler.java deleted file mode 100644 index 4636b27625..0000000000 --- a/example/src/main/java/io/netty/example/http/cors/OkResponseHandler.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version - * 2.0 (the "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.example.http.cors; - -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelFutureListeners; -import io.netty.channel.ChannelHandlerContext; -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.HttpResponseStatus; -import io.netty.handler.codec.http.HttpVersion; - -/** - * A simple handler which will simple return a successful Http - * response for any request. - */ -public class OkResponseHandler extends SimpleChannelInboundHandler { - @Override - public void messageReceived(ChannelHandlerContext ctx, Object msg) { - final FullHttpResponse response = new DefaultFullHttpResponse( - HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.EMPTY_BUFFER); - response.headers().set("custom-response-header", "Some value"); - ctx.writeAndFlush(response).addListener(ctx, ChannelFutureListeners.CLOSE); - } -} diff --git a/example/src/main/java/io/netty/example/http/file/HttpStaticFileServer.java b/example/src/main/java/io/netty/example/http/file/HttpStaticFileServer.java deleted file mode 100644 index fb0a80d52c..0000000000 --- a/example/src/main/java/io/netty/example/http/file/HttpStaticFileServer.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.http.file; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.Channel; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.SslProvider; -import io.netty.handler.ssl.util.SelfSignedCertificate; - -public final class HttpStaticFileServer { - - static final boolean SSL = System.getProperty("ssl") != null; - static final int PORT = Integer.parseInt(System.getProperty("port", SSL? "8443" : "8080")); - - public static void main(String[] args) throws Exception { - // Configure SSL. - final SslContext sslCtx; - if (SSL) { - SelfSignedCertificate ssc = new SelfSignedCertificate(); - sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(SslProvider.JDK).build(); - } else { - sslCtx = null; - } - - EventLoopGroup bossGroup = new MultithreadEventLoopGroup(1, NioHandler.newFactory()); - EventLoopGroup workerGroup = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - ServerBootstrap b = new ServerBootstrap(); - b.group(bossGroup, workerGroup) - .channel(NioServerSocketChannel.class) - .handler(new LoggingHandler(LogLevel.INFO)) - .childHandler(new HttpStaticFileServerInitializer(sslCtx)); - - Channel ch = b.bind(PORT).get(); - - System.err.println("Open your web browser and navigate to " + - (SSL? "https" : "http") + "://127.0.0.1:" + PORT + '/'); - - ch.closeFuture().sync(); - } finally { - bossGroup.shutdownGracefully(); - workerGroup.shutdownGracefully(); - } - } -} 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 deleted file mode 100644 index 7340280d31..0000000000 --- a/example/src/main/java/io/netty/example/http/file/HttpStaticFileServerHandler.java +++ /dev/null @@ -1,410 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.http.file; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelFutureListeners; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.DefaultFileRegion; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.handler.codec.http.DefaultFullHttpResponse; -import io.netty.handler.codec.http.DefaultHttpResponse; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpChunkedInput; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpUtil; -import io.netty.handler.codec.http.LastHttpContent; -import io.netty.handler.ssl.SslHandler; -import io.netty.handler.stream.ChunkedFile; -import io.netty.util.CharsetUtil; -import io.netty.util.concurrent.Future; -import io.netty.util.internal.SystemPropertyUtil; - -import javax.activation.MimetypesFileTypeMap; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.RandomAccessFile; -import java.net.URLDecoder; -import java.nio.charset.StandardCharsets; -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.Locale; -import java.util.TimeZone; -import java.util.regex.Pattern; - -import static io.netty.handler.codec.http.HttpMethod.GET; -import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST; -import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN; -import static io.netty.handler.codec.http.HttpResponseStatus.FOUND; -import static io.netty.handler.codec.http.HttpResponseStatus.INTERNAL_SERVER_ERROR; -import static io.netty.handler.codec.http.HttpResponseStatus.METHOD_NOT_ALLOWED; -import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND; -import static io.netty.handler.codec.http.HttpResponseStatus.NOT_MODIFIED; -import static io.netty.handler.codec.http.HttpResponseStatus.OK; -import static io.netty.handler.codec.http.HttpVersion.HTTP_1_0; -import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; - -/** - * A simple handler that serves incoming HTTP requests to send their respective - * HTTP responses. It also implements {@code 'If-Modified-Since'} header to - * take advantage of browser cache, as described in - * RFC 2616. - * - *

How Browser Caching Works

- * - * Web browser caching works with HTTP headers as illustrated by the following - * sample: - *
    - *
  1. Request #1 returns the content of {@code /file1.txt}.
  2. - *
  3. Contents of {@code /file1.txt} is cached by the browser.
  4. - *
  5. Request #2 for {@code /file1.txt} does not return the contents of the - * file again. Rather, a 304 Not Modified is returned. This tells the - * browser to use the contents stored in its cache.
  6. - *
  7. The server knows the file has not been modified because the - * {@code If-Modified-Since} date is the same as the file's last - * modified date.
  8. - *
- * - *
- * Request #1 Headers
- * ===================
- * GET /file1.txt HTTP/1.1
- *
- * Response #1 Headers
- * ===================
- * HTTP/1.1 200 OK
- * Date:               Tue, 01 Mar 2011 22:44:26 GMT
- * Last-Modified:      Wed, 30 Jun 2010 21:36:48 GMT
- * Expires:            Tue, 01 Mar 2012 22:44:26 GMT
- * Cache-Control:      private, max-age=31536000
- *
- * Request #2 Headers
- * ===================
- * GET /file1.txt HTTP/1.1
- * If-Modified-Since:  Wed, 30 Jun 2010 21:36:48 GMT
- *
- * Response #2 Headers
- * ===================
- * HTTP/1.1 304 Not Modified
- * Date:               Tue, 01 Mar 2011 22:44:28 GMT
- *
- * 
- */ -public class HttpStaticFileServerHandler extends SimpleChannelInboundHandler { - - public static final String HTTP_DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz"; - public static final String HTTP_DATE_GMT_TIMEZONE = "GMT"; - public static final int HTTP_CACHE_SECONDS = 60; - - private FullHttpRequest request; - - @Override - public void messageReceived(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception { - this.request = request; - if (!request.decoderResult().isSuccess()) { - sendError(ctx, BAD_REQUEST); - return; - } - - if (!GET.equals(request.method())) { - sendError(ctx, METHOD_NOT_ALLOWED); - return; - } - - final boolean keepAlive = HttpUtil.isKeepAlive(request); - final String uri = request.uri(); - final String path = sanitizeUri(uri); - if (path == null) { - sendError(ctx, FORBIDDEN); - return; - } - - File file = new File(path); - if (file.isHidden() || !file.exists()) { - sendError(ctx, NOT_FOUND); - return; - } - - if (file.isDirectory()) { - if (uri.endsWith("/")) { - sendListing(ctx, file, uri); - } else { - sendRedirect(ctx, uri + '/'); - } - return; - } - - if (!file.isFile()) { - sendError(ctx, FORBIDDEN); - return; - } - - // Cache Validation - String ifModifiedSince = request.headers().get(HttpHeaderNames.IF_MODIFIED_SINCE); - if (ifModifiedSince != null && !ifModifiedSince.isEmpty()) { - SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US); - Date ifModifiedSinceDate = dateFormatter.parse(ifModifiedSince); - - // Only compare up to the second because the datetime format we send to the client - // does not have milliseconds - long ifModifiedSinceDateSeconds = ifModifiedSinceDate.getTime() / 1000; - long fileLastModifiedSeconds = file.lastModified() / 1000; - if (ifModifiedSinceDateSeconds == fileLastModifiedSeconds) { - sendNotModified(ctx); - return; - } - } - - RandomAccessFile raf; - try { - raf = new RandomAccessFile(file, "r"); - } catch (FileNotFoundException ignore) { - sendError(ctx, NOT_FOUND); - return; - } - long fileLength = raf.length(); - - HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK); - HttpUtil.setContentLength(response, fileLength); - setContentTypeHeader(response, file); - setDateAndCacheHeaders(response, file); - - if (!keepAlive) { - response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE); - } else if (request.protocolVersion().equals(HTTP_1_0)) { - response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE); - } - - // Write the initial line and the header. - ctx.write(response); - - // Write the content. - Future sendFileFuture; - Future lastContentFuture; - if (ctx.pipeline().get(SslHandler.class) == null) { - sendFileFuture = - ctx.write(new DefaultFileRegion(raf.getChannel(), 0, fileLength)); - // Write the end marker. - lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT); - } else { - sendFileFuture = - ctx.writeAndFlush(new HttpChunkedInput(new ChunkedFile(raf, 0, fileLength, 8192))); - // HttpChunkedInput will write the end marker (LastHttpContent) for us. - lastContentFuture = sendFileFuture; - } - - sendFileFuture.addListener(ctx.channel(), (channel, future) -> - System.err.println(channel + " Transfer complete.")); - - // Decide whether to close the connection or not. - if (!keepAlive) { - // Close the connection when the whole content is written out. - lastContentFuture.addListener(ctx, ChannelFutureListeners.CLOSE); - } - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - cause.printStackTrace(); - if (ctx.channel().isActive()) { - sendError(ctx, INTERNAL_SERVER_ERROR); - } - } - - private static final Pattern INSECURE_URI = Pattern.compile(".*[<>&\"].*"); - - private static String sanitizeUri(String uri) { - // Decode the path. - uri = URLDecoder.decode(uri, StandardCharsets.UTF_8); - - if (uri.isEmpty() || uri.charAt(0) != '/') { - return null; - } - - // Convert file separators. - uri = uri.replace('/', File.separatorChar); - - // Simplistic dumb security check. - // You will have to do something serious in the production environment. - if (uri.contains(File.separator + '.') || - uri.contains('.' + File.separator) || - uri.charAt(0) == '.' || uri.charAt(uri.length() - 1) == '.' || - INSECURE_URI.matcher(uri).matches()) { - return null; - } - - // Convert to absolute path. - return SystemPropertyUtil.get("user.dir") + File.separator + uri; - } - - private static final Pattern ALLOWED_FILE_NAME = Pattern.compile("[^-\\._]?[^<>&\\\"]*"); - - private void sendListing(ChannelHandlerContext ctx, File dir, String dirPath) { - StringBuilder buf = new StringBuilder() - .append("\r\n") - .append("") - .append("Listing of: ") - .append(dirPath) - .append("\r\n") - - .append("

Listing of: ") - .append(dirPath) - .append("

\r\n") - - .append("
    ") - .append("
  • ..
  • \r\n"); - - File[] files = dir.listFiles(); - if (files != null) { - for (File f: files) { - if (f.isHidden() || !f.canRead()) { - continue; - } - - String name = f.getName(); - if (!ALLOWED_FILE_NAME.matcher(name).matches()) { - continue; - } - - buf.append("
  • ") - .append(name) - .append("
  • \r\n"); - } - } - - buf.append("
\r\n"); - - ByteBuf buffer = ctx.alloc().buffer(buf.length()); - buffer.writeCharSequence(buf.toString(), CharsetUtil.UTF_8); - - FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, buffer); - response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html; charset=UTF-8"); - - sendAndCleanupConnection(ctx, response); - } - - private void sendRedirect(ChannelHandlerContext ctx, String newUri) { - FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, FOUND, Unpooled.EMPTY_BUFFER); - response.headers().set(HttpHeaderNames.LOCATION, newUri); - - sendAndCleanupConnection(ctx, response); - } - - private void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) { - FullHttpResponse response = new DefaultFullHttpResponse( - HTTP_1_1, status, Unpooled.copiedBuffer("Failure: " + status + "\r\n", CharsetUtil.UTF_8)); - response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8"); - - sendAndCleanupConnection(ctx, response); - } - - /** - * When file timestamp is the same as what the browser is sending up, send a "304 Not Modified" - * - * @param ctx - * Context - */ - private void sendNotModified(ChannelHandlerContext ctx) { - FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, NOT_MODIFIED, Unpooled.EMPTY_BUFFER); - setDateHeader(response); - - sendAndCleanupConnection(ctx, response); - } - - /** - * If Keep-Alive is disabled, attaches "Connection: close" header to the response - * and closes the connection after the response being sent. - */ - private void sendAndCleanupConnection(ChannelHandlerContext ctx, FullHttpResponse response) { - final FullHttpRequest request = this.request; - final boolean keepAlive = HttpUtil.isKeepAlive(request); - 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); - } else if (request.protocolVersion().equals(HTTP_1_0)) { - response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE); - } - - Future flushPromise = ctx.writeAndFlush(response); - - if (!keepAlive) { - // Close the connection as soon as the response is sent. - flushPromise.addListener(ctx, ChannelFutureListeners.CLOSE); - } - } - - /** - * Sets the Date header for the HTTP response - * - * @param response - * HTTP response - */ - private static void setDateHeader(FullHttpResponse response) { - SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US); - dateFormatter.setTimeZone(TimeZone.getTimeZone(HTTP_DATE_GMT_TIMEZONE)); - - Calendar time = new GregorianCalendar(); - response.headers().set(HttpHeaderNames.DATE, dateFormatter.format(time.getTime())); - } - - /** - * Sets the Date and Cache headers for the HTTP Response - * - * @param response - * HTTP response - * @param fileToCache - * file to extract content type - */ - private static void setDateAndCacheHeaders(HttpResponse response, File fileToCache) { - SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US); - dateFormatter.setTimeZone(TimeZone.getTimeZone(HTTP_DATE_GMT_TIMEZONE)); - - // Date header - Calendar time = new GregorianCalendar(); - response.headers().set(HttpHeaderNames.DATE, dateFormatter.format(time.getTime())); - - // Add cache headers - time.add(Calendar.SECOND, HTTP_CACHE_SECONDS); - response.headers().set(HttpHeaderNames.EXPIRES, dateFormatter.format(time.getTime())); - response.headers().set(HttpHeaderNames.CACHE_CONTROL, "private, max-age=" + HTTP_CACHE_SECONDS); - response.headers().set( - HttpHeaderNames.LAST_MODIFIED, dateFormatter.format(new Date(fileToCache.lastModified()))); - } - - /** - * Sets the content type header for the HTTP Response - * - * @param response - * HTTP response - * @param file - * file to extract content type - */ - private static void setContentTypeHeader(HttpResponse response, File file) { - MimetypesFileTypeMap mimeTypesMap = new MimetypesFileTypeMap(); - response.headers().set(HttpHeaderNames.CONTENT_TYPE, mimeTypesMap.getContentType(file.getPath())); - } -} diff --git a/example/src/main/java/io/netty/example/http/file/HttpStaticFileServerInitializer.java b/example/src/main/java/io/netty/example/http/file/HttpStaticFileServerInitializer.java deleted file mode 100644 index a5b4331e17..0000000000 --- a/example/src/main/java/io/netty/example/http/file/HttpStaticFileServerInitializer.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.http.file; - -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.socket.SocketChannel; -import io.netty.handler.codec.http.HttpObjectAggregator; -import io.netty.handler.codec.http.HttpServerCodec; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.stream.ChunkedWriteHandler; - -public class HttpStaticFileServerInitializer extends ChannelInitializer { - - private final SslContext sslCtx; - - public HttpStaticFileServerInitializer(SslContext sslCtx) { - this.sslCtx = sslCtx; - } - - @Override - public void initChannel(SocketChannel ch) { - ChannelPipeline pipeline = ch.pipeline(); - if (sslCtx != null) { - pipeline.addLast(sslCtx.newHandler(ch.alloc())); - } - pipeline.addLast(new HttpServerCodec()); - pipeline.addLast(new HttpObjectAggregator(65536)); - pipeline.addLast(new ChunkedWriteHandler()); - pipeline.addLast(new HttpStaticFileServerHandler()); - } -} diff --git a/example/src/main/java/io/netty/example/http/helloworld/HttpHelloWorldServer.java b/example/src/main/java/io/netty/example/http/helloworld/HttpHelloWorldServer.java deleted file mode 100644 index 51b19cc243..0000000000 --- a/example/src/main/java/io/netty/example/http/helloworld/HttpHelloWorldServer.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.http.helloworld; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelOption; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.util.SelfSignedCertificate; - -/** - * An HTTP server that sends back the content of the received HTTP request - * in a pretty plaintext form. - */ -public final class HttpHelloWorldServer { - - static final boolean SSL = System.getProperty("ssl") != null; - static final int PORT = Integer.parseInt(System.getProperty("port", SSL? "8443" : "8080")); - - public static void main(String[] args) throws Exception { - // Configure SSL. - final SslContext sslCtx; - if (SSL) { - SelfSignedCertificate ssc = new SelfSignedCertificate(); - sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build(); - } else { - sslCtx = null; - } - - // Configure the server. - EventLoopGroup bossGroup = new MultithreadEventLoopGroup(NioHandler.newFactory()); - EventLoopGroup workerGroup = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - ServerBootstrap b = new ServerBootstrap(); - b.option(ChannelOption.SO_BACKLOG, 1024); - b.group(bossGroup, workerGroup) - .channel(NioServerSocketChannel.class) - .handler(new LoggingHandler(LogLevel.INFO)) - .childHandler(new HttpHelloWorldServerInitializer(sslCtx)); - - Channel ch = b.bind(PORT).get(); - - System.err.println("Open your web browser and navigate to " + - (SSL? "https" : "http") + "://127.0.0.1:" + PORT + '/'); - - ch.closeFuture().sync(); - } finally { - bossGroup.shutdownGracefully(); - workerGroup.shutdownGracefully(); - } - } -} 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 deleted file mode 100644 index d0ad815031..0000000000 --- a/example/src/main/java/io/netty/example/http/helloworld/HttpHelloWorldServerHandler.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.http.helloworld; - -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelFutureListeners; -import io.netty.channel.ChannelHandlerContext; -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.HttpRequest; -import io.netty.handler.codec.http.HttpUtil; -import io.netty.util.concurrent.Future; - -import static io.netty.handler.codec.http.HttpHeaderNames.CONNECTION; -import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH; -import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE; -import static io.netty.handler.codec.http.HttpHeaderValues.CLOSE; -import static io.netty.handler.codec.http.HttpHeaderValues.KEEP_ALIVE; -import static io.netty.handler.codec.http.HttpHeaderValues.TEXT_PLAIN; -import static io.netty.handler.codec.http.HttpResponseStatus.OK; - -public class HttpHelloWorldServerHandler extends SimpleChannelInboundHandler { - private static final byte[] CONTENT = { 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd' }; - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) { - ctx.flush(); - } - - @Override - public void messageReceived(ChannelHandlerContext ctx, HttpObject msg) { - if (msg instanceof HttpRequest) { - HttpRequest req = (HttpRequest) msg; - - boolean keepAlive = HttpUtil.isKeepAlive(req); - FullHttpResponse response = new DefaultFullHttpResponse(req.protocolVersion(), OK, - Unpooled.wrappedBuffer(CONTENT)); - response.headers() - .set(CONTENT_TYPE, TEXT_PLAIN) - .setInt(CONTENT_LENGTH, response.content().readableBytes()); - - if (keepAlive) { - if (!req.protocolVersion().isKeepAliveDefault()) { - response.headers().set(CONNECTION, KEEP_ALIVE); - } - } else { - // Tell the client we're going to close the connection. - response.headers().set(CONNECTION, CLOSE); - } - - Future f = ctx.write(response); - - if (!keepAlive) { - f.addListener(ctx, ChannelFutureListeners.CLOSE); - } - } - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - cause.printStackTrace(); - ctx.close(); - } -} diff --git a/example/src/main/java/io/netty/example/http/helloworld/HttpHelloWorldServerInitializer.java b/example/src/main/java/io/netty/example/http/helloworld/HttpHelloWorldServerInitializer.java deleted file mode 100644 index b86ad46c32..0000000000 --- a/example/src/main/java/io/netty/example/http/helloworld/HttpHelloWorldServerInitializer.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.http.helloworld; - -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.socket.SocketChannel; -import io.netty.handler.codec.http.HttpServerCodec; -import io.netty.handler.codec.http.HttpServerExpectContinueHandler; -import io.netty.handler.ssl.SslContext; - -public class HttpHelloWorldServerInitializer extends ChannelInitializer { - - private final SslContext sslCtx; - - public HttpHelloWorldServerInitializer(SslContext sslCtx) { - this.sslCtx = sslCtx; - } - - @Override - public void initChannel(SocketChannel ch) { - ChannelPipeline p = ch.pipeline(); - if (sslCtx != null) { - p.addLast(sslCtx.newHandler(ch.alloc())); - } - p.addLast(new HttpServerCodec()); - p.addLast(new HttpServerExpectContinueHandler()); - p.addLast(new HttpHelloWorldServerHandler()); - } -} diff --git a/example/src/main/java/io/netty/example/http/snoop/HttpSnoopClient.java b/example/src/main/java/io/netty/example/http/snoop/HttpSnoopClient.java deleted file mode 100644 index a6a302dffa..0000000000 --- a/example/src/main/java/io/netty/example/http/snoop/HttpSnoopClient.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.http.snoop; - -import io.netty.bootstrap.Bootstrap; -import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http.cookie.ClientCookieEncoder; -import io.netty.handler.codec.http.cookie.DefaultCookie; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.util.InsecureTrustManagerFactory; - -import java.net.URI; - -/** - * A simple HTTP client that prints out the content of the HTTP response to - * {@link System#out} to test {@link HttpSnoopServer}. - */ -public final class HttpSnoopClient { - - static final String URL = System.getProperty("url", "http://127.0.0.1:8080/"); - - public static void main(String[] args) throws Exception { - URI uri = new URI(URL); - String scheme = uri.getScheme() == null? "http" : uri.getScheme(); - String host = uri.getHost() == null? "127.0.0.1" : uri.getHost(); - int port = uri.getPort(); - if (port == -1) { - if ("http".equalsIgnoreCase(scheme)) { - port = 80; - } else if ("https".equalsIgnoreCase(scheme)) { - port = 443; - } - } - - if (!"http".equalsIgnoreCase(scheme) && !"https".equalsIgnoreCase(scheme)) { - System.err.println("Only HTTP(S) is supported."); - return; - } - - // Configure SSL context if necessary. - final boolean ssl = "https".equalsIgnoreCase(scheme); - final SslContext sslCtx; - if (ssl) { - sslCtx = SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE).build(); - } else { - sslCtx = null; - } - - // Configure the client. - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - Bootstrap b = new Bootstrap(); - b.group(group) - .channel(NioSocketChannel.class) - .handler(new HttpSnoopClientInitializer(sslCtx)); - - // Make the connection attempt. - Channel ch = b.connect(host, port).get(); - - // Prepare the HTTP request. - HttpRequest request = new DefaultFullHttpRequest( - HttpVersion.HTTP_1_1, HttpMethod.GET, uri.getRawPath(), Unpooled.EMPTY_BUFFER); - request.headers().set(HttpHeaderNames.HOST, host); - request.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE); - request.headers().set(HttpHeaderNames.ACCEPT_ENCODING, HttpHeaderValues.GZIP); - - // Set some example cookies. - request.headers().set( - HttpHeaderNames.COOKIE, - ClientCookieEncoder.STRICT.encode( - new DefaultCookie("my-cookie", "foo"), - new DefaultCookie("another-cookie", "bar"))); - - // Send the HTTP request. - ch.writeAndFlush(request); - - // Wait for the server to close the connection. - ch.closeFuture().sync(); - } finally { - // Shut down executor threads to exit. - group.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/http/snoop/HttpSnoopClientHandler.java b/example/src/main/java/io/netty/example/http/snoop/HttpSnoopClientHandler.java deleted file mode 100644 index 53762f2c91..0000000000 --- a/example/src/main/java/io/netty/example/http/snoop/HttpSnoopClientHandler.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.http.snoop; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpUtil; -import io.netty.handler.codec.http.HttpObject; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.LastHttpContent; -import io.netty.util.CharsetUtil; - -public class HttpSnoopClientHandler extends SimpleChannelInboundHandler { - - @Override - public void messageReceived(ChannelHandlerContext ctx, HttpObject msg) { - if (msg instanceof HttpResponse) { - HttpResponse response = (HttpResponse) msg; - - System.err.println("STATUS: " + response.status()); - System.err.println("VERSION: " + response.protocolVersion()); - System.err.println(); - - if (!response.headers().isEmpty()) { - for (CharSequence name: response.headers().names()) { - for (CharSequence value: response.headers().getAll(name)) { - System.err.println("HEADER: " + name + " = " + value); - } - } - System.err.println(); - } - - if (HttpUtil.isTransferEncodingChunked(response)) { - System.err.println("CHUNKED CONTENT {"); - } else { - System.err.println("CONTENT {"); - } - } - if (msg instanceof HttpContent) { - HttpContent content = (HttpContent) msg; - - System.err.print(content.content().toString(CharsetUtil.UTF_8)); - System.err.flush(); - - if (content instanceof LastHttpContent) { - System.err.println("} END OF CONTENT"); - ctx.close(); - } - } - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - cause.printStackTrace(); - ctx.close(); - } -} diff --git a/example/src/main/java/io/netty/example/http/snoop/HttpSnoopClientInitializer.java b/example/src/main/java/io/netty/example/http/snoop/HttpSnoopClientInitializer.java deleted file mode 100644 index da56bf9b44..0000000000 --- a/example/src/main/java/io/netty/example/http/snoop/HttpSnoopClientInitializer.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.http.snoop; - -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.socket.SocketChannel; -import io.netty.handler.codec.http.HttpClientCodec; -import io.netty.handler.codec.http.HttpContentDecompressor; -import io.netty.handler.ssl.SslContext; - -public class HttpSnoopClientInitializer extends ChannelInitializer { - - private final SslContext sslCtx; - - public HttpSnoopClientInitializer(SslContext sslCtx) { - this.sslCtx = sslCtx; - } - - @Override - public void initChannel(SocketChannel ch) { - ChannelPipeline p = ch.pipeline(); - - // Enable HTTPS if necessary. - if (sslCtx != null) { - p.addLast(sslCtx.newHandler(ch.alloc())); - } - - p.addLast(new HttpClientCodec()); - - // Remove the following line if you don't want automatic content decompression. - p.addLast(new HttpContentDecompressor()); - - // Uncomment the following line if you don't want to handle HttpContents. - //p.addLast(new HttpObjectAggregator(1048576)); - - p.addLast(new HttpSnoopClientHandler()); - } -} diff --git a/example/src/main/java/io/netty/example/http/snoop/HttpSnoopServer.java b/example/src/main/java/io/netty/example/http/snoop/HttpSnoopServer.java deleted file mode 100644 index cf107d8452..0000000000 --- a/example/src/main/java/io/netty/example/http/snoop/HttpSnoopServer.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.http.snoop; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.Channel; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.util.SelfSignedCertificate; - -/** - * An HTTP server that sends back the content of the received HTTP request - * in a pretty plaintext form. - */ -public final class HttpSnoopServer { - - static final boolean SSL = System.getProperty("ssl") != null; - static final int PORT = Integer.parseInt(System.getProperty("port", SSL? "8443" : "8080")); - - public static void main(String[] args) throws Exception { - // Configure SSL. - final SslContext sslCtx; - if (SSL) { - SelfSignedCertificate ssc = new SelfSignedCertificate(); - sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build(); - } else { - sslCtx = null; - } - - // Configure the server. - EventLoopGroup bossGroup = new MultithreadEventLoopGroup(1, NioHandler.newFactory()); - EventLoopGroup workerGroup = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - ServerBootstrap b = new ServerBootstrap(); - b.group(bossGroup, workerGroup) - .channel(NioServerSocketChannel.class) - .handler(new LoggingHandler(LogLevel.INFO)) - .childHandler(new HttpSnoopServerInitializer(sslCtx)); - - Channel ch = b.bind(PORT).get(); - - System.err.println("Open your web browser and navigate to " + - (SSL? "https" : "http") + "://127.0.0.1:" + PORT + '/'); - - ch.closeFuture().sync(); - } finally { - bossGroup.shutdownGracefully(); - workerGroup.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/http/snoop/HttpSnoopServerHandler.java b/example/src/main/java/io/netty/example/http/snoop/HttpSnoopServerHandler.java deleted file mode 100644 index 93eef6538b..0000000000 --- a/example/src/main/java/io/netty/example/http/snoop/HttpSnoopServerHandler.java +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.http.snoop; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelFutureListeners; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.handler.codec.DecoderResult; -import io.netty.handler.codec.http.DefaultFullHttpResponse; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpUtil; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpObject; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.LastHttpContent; -import io.netty.handler.codec.http.QueryStringDecoder; -import io.netty.handler.codec.http.cookie.Cookie; -import io.netty.handler.codec.http.cookie.ServerCookieDecoder; -import io.netty.handler.codec.http.cookie.ServerCookieEncoder; -import io.netty.util.CharsetUtil; - -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import static io.netty.handler.codec.http.HttpResponseStatus.*; -import static io.netty.handler.codec.http.HttpVersion.*; - -public class HttpSnoopServerHandler extends SimpleChannelInboundHandler { - - private HttpRequest request; - /** Buffer that stores the response content */ - private final StringBuilder buf = new StringBuilder(); - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) { - ctx.flush(); - } - - @Override - protected void messageReceived(ChannelHandlerContext ctx, Object msg) { - if (msg instanceof HttpRequest) { - HttpRequest request = this.request = (HttpRequest) msg; - - if (HttpUtil.is100ContinueExpected(request)) { - send100Continue(ctx); - } - - buf.setLength(0); - buf.append("WELCOME TO THE WILD WILD WEB SERVER\r\n"); - buf.append("===================================\r\n"); - - buf.append("VERSION: ").append(request.protocolVersion()).append("\r\n"); - buf.append("HOSTNAME: ").append(request.headers().get(HttpHeaderNames.HOST, "unknown")).append("\r\n"); - buf.append("REQUEST_URI: ").append(request.uri()).append("\r\n\r\n"); - - HttpHeaders headers = request.headers(); - if (!headers.isEmpty()) { - for (Map.Entry h: headers) { - CharSequence key = h.getKey(); - CharSequence value = h.getValue(); - buf.append("HEADER: ").append(key).append(" = ").append(value).append("\r\n"); - } - buf.append("\r\n"); - } - - QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.uri()); - Map> params = queryStringDecoder.parameters(); - if (!params.isEmpty()) { - for (Entry> p: params.entrySet()) { - String key = p.getKey(); - List vals = p.getValue(); - for (String val : vals) { - buf.append("PARAM: ").append(key).append(" = ").append(val).append("\r\n"); - } - } - buf.append("\r\n"); - } - - appendDecoderResult(buf, request); - } - - if (msg instanceof HttpContent) { - HttpContent httpContent = (HttpContent) msg; - - ByteBuf content = httpContent.content(); - if (content.isReadable()) { - buf.append("CONTENT: "); - buf.append(content.toString(CharsetUtil.UTF_8)); - buf.append("\r\n"); - appendDecoderResult(buf, request); - } - - if (msg instanceof LastHttpContent) { - buf.append("END OF CONTENT\r\n"); - - LastHttpContent trailer = (LastHttpContent) msg; - if (!trailer.trailingHeaders().isEmpty()) { - buf.append("\r\n"); - for (CharSequence name: trailer.trailingHeaders().names()) { - for (CharSequence value: trailer.trailingHeaders().getAll(name)) { - buf.append("TRAILING HEADER: "); - buf.append(name).append(" = ").append(value).append("\r\n"); - } - } - buf.append("\r\n"); - } - - if (!writeResponse(trailer, ctx)) { - // If keep-alive is off, close the connection once the content is fully written. - ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ctx, ChannelFutureListeners.CLOSE); - } - } - } - } - - private static void appendDecoderResult(StringBuilder buf, HttpObject o) { - DecoderResult result = o.decoderResult(); - if (result.isSuccess()) { - return; - } - - buf.append(".. WITH DECODER FAILURE: "); - buf.append(result.cause()); - buf.append("\r\n"); - } - - private boolean writeResponse(HttpObject currentObj, ChannelHandlerContext ctx) { - // Decide whether to close the connection or not. - boolean keepAlive = HttpUtil.isKeepAlive(request); - // Build the response object. - FullHttpResponse response = new DefaultFullHttpResponse( - HTTP_1_1, currentObj.decoderResult().isSuccess()? OK : BAD_REQUEST, - Unpooled.copiedBuffer(buf.toString(), CharsetUtil.UTF_8)); - - response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8"); - - if (keepAlive) { - // Add 'Content-Length' header only for a keep-alive connection. - response.headers().setInt(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes()); - // Add keep alive header as per: - // - https://www.w3.org/Protocols/HTTP/1.1/draft-ietf-http-v11-spec-01.html#Connection - response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE); - } - - // Encode the cookie. - String cookieString = request.headers().get(HttpHeaderNames.COOKIE); - if (cookieString != null) { - Set cookies = ServerCookieDecoder.STRICT.decode(cookieString); - if (!cookies.isEmpty()) { - // Reset the cookies if necessary. - for (Cookie cookie: cookies) { - response.headers().add(HttpHeaderNames.SET_COOKIE, ServerCookieEncoder.STRICT.encode(cookie)); - } - } - } else { - // Browser sent no cookie. Add some. - response.headers().add(HttpHeaderNames.SET_COOKIE, ServerCookieEncoder.STRICT.encode("key1", "value1")); - response.headers().add(HttpHeaderNames.SET_COOKIE, ServerCookieEncoder.STRICT.encode("key2", "value2")); - } - - // Write the response. - ctx.write(response); - - return keepAlive; - } - - private static void send100Continue(ChannelHandlerContext ctx) { - FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, CONTINUE, Unpooled.EMPTY_BUFFER); - ctx.write(response); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - cause.printStackTrace(); - ctx.close(); - } -} diff --git a/example/src/main/java/io/netty/example/http/snoop/HttpSnoopServerInitializer.java b/example/src/main/java/io/netty/example/http/snoop/HttpSnoopServerInitializer.java deleted file mode 100644 index 42bad78909..0000000000 --- a/example/src/main/java/io/netty/example/http/snoop/HttpSnoopServerInitializer.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.http.snoop; - -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.socket.SocketChannel; -import io.netty.handler.codec.http.HttpRequestDecoder; -import io.netty.handler.codec.http.HttpResponseEncoder; -import io.netty.handler.ssl.SslContext; - -public class HttpSnoopServerInitializer extends ChannelInitializer { - - private final SslContext sslCtx; - - public HttpSnoopServerInitializer(SslContext sslCtx) { - this.sslCtx = sslCtx; - } - - @Override - public void initChannel(SocketChannel ch) { - ChannelPipeline p = ch.pipeline(); - if (sslCtx != null) { - p.addLast(sslCtx.newHandler(ch.alloc())); - } - p.addLast(new HttpRequestDecoder()); - // Uncomment the following line if you don't want to handle HttpChunks. - //p.addLast(new HttpObjectAggregator(1048576)); - p.addLast(new HttpResponseEncoder()); - // Remove the following line if you don't want automatic content compression. - //p.addLast(new HttpContentCompressor()); - p.addLast(new HttpSnoopServerHandler()); - } -} diff --git a/example/src/main/java/io/netty/example/http/upload/HttpUploadClient.java b/example/src/main/java/io/netty/example/http/upload/HttpUploadClient.java deleted file mode 100644 index bb0b3ca5d9..0000000000 --- a/example/src/main/java/io/netty/example/http/upload/HttpUploadClient.java +++ /dev/null @@ -1,899 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.http.upload; - -import io.netty.bootstrap.Bootstrap; -import io.netty.channel.Channel; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.codec.http.DefaultHttpRequest; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http.QueryStringEncoder; -import io.netty.handler.codec.http.cookie.ClientCookieEncoder; -import io.netty.handler.codec.http.cookie.DefaultCookie; -import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory; -import io.netty.handler.codec.http.multipart.DiskAttribute; -import io.netty.handler.codec.http.multipart.DiskFileUpload; -import io.netty.handler.codec.http.multipart.HttpDataFactory; -import io.netty.handler.codec.http.multipart.HttpPostRequestEncoder; -import io.netty.handler.codec.http.multipart.InterfaceHttpData; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.util.InsecureTrustManagerFactory; -import io.netty.util.concurrent.Future; -import io.netty.util.internal.SocketUtils; - -import java.io.File; -import java.io.FileNotFoundException; -import java.net.URI; -import java.util.List; -import java.util.Map.Entry; - -/** - * This class is meant to be run against {@link HttpUploadServer}. - */ -public final class HttpUploadClient { - - static final String BASE_URL = System.getProperty("baseUrl", "http://127.0.0.1:8080/"); - static final String FILE = System.getProperty("file", "upload.txt"); - - public static void main(String[] args) throws Exception { - String postSimple, postFile, get; - if (BASE_URL.endsWith("/")) { - postSimple = BASE_URL + "formpost"; - postFile = BASE_URL + "formpostmultipart"; - get = BASE_URL + "formget"; - } else { - postSimple = BASE_URL + "/formpost"; - postFile = BASE_URL + "/formpostmultipart"; - get = BASE_URL + "/formget"; - } - - URI uriSimple = new URI(postSimple); - String scheme = uriSimple.getScheme() == null? "http" : uriSimple.getScheme(); - String host = uriSimple.getHost() == null? "127.0.0.1" : uriSimple.getHost(); - int port = uriSimple.getPort(); - if (port == -1) { - if ("http".equalsIgnoreCase(scheme)) { - port = 80; - } else if ("https".equalsIgnoreCase(scheme)) { - port = 443; - } - } - - if (!"http".equalsIgnoreCase(scheme) && !"https".equalsIgnoreCase(scheme)) { - System.err.println("Only HTTP(S) is supported."); - return; - } - - final boolean ssl = "https".equalsIgnoreCase(scheme); - final SslContext sslCtx; - if (ssl) { - sslCtx = SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE).build(); - } else { - sslCtx = null; - } - - URI uriFile = new URI(postFile); - File file = new File(FILE); - if (!file.canRead()) { - throw new FileNotFoundException(FILE); - } - - // Configure the client. - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - - // setup the factory: here using a mixed memory/disk based on size threshold - HttpDataFactory factory = new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE); // Disk if MINSIZE exceed - - DiskFileUpload.deleteOnExitTemporaryFile = true; // should delete file on exit (in normal exit) - DiskFileUpload.baseDirectory = null; // system temp directory - DiskAttribute.deleteOnExitTemporaryFile = true; // should delete file on exit (in normal exit) - DiskAttribute.baseDirectory = null; // system temp directory - - try { - Bootstrap b = new Bootstrap(); - b.group(group).channel(NioSocketChannel.class).handler(new HttpUploadClientInitializer(sslCtx)); - - // Simple Get form: no factory used (not usable) - List> headers = formget(b, host, port, get, uriSimple); - if (headers == null) { - factory.cleanAllHttpData(); - return; - } - - // Simple Post form: factory used for big attributes - List bodylist = formpost(b, host, port, uriSimple, file, factory, headers); - if (bodylist == null) { - factory.cleanAllHttpData(); - return; - } - - // Multipart Post form: factory used - formpostmultipart(b, host, port, uriFile, factory, headers, bodylist); - } finally { - // Shut down executor threads to exit. - group.shutdownGracefully(); - - // Really clean all temporary files if they still exist - factory.cleanAllHttpData(); - } - } - - /** - * Standard usage of HTTP API in Netty without file Upload (get is not able to achieve File upload - * due to limitation on request size). - * - * @return the list of headers that will be used in every example after - **/ - private static List> formget( - Bootstrap bootstrap, String host, int port, String get, URI uriSimple) throws Exception { - // XXX /formget - // No use of HttpPostRequestEncoder since not a POST - Channel channel = bootstrap.connect(host, port).get(); - - // Prepare the HTTP request. - QueryStringEncoder encoder = new QueryStringEncoder(get); - // add Form attribute - encoder.addParam("getform", "GET"); - encoder.addParam("info", "first value"); - encoder.addParam("secondinfo", "secondvalue īŋŊīŋŊīŋŊ&"); - // not the big one since it is not compatible with GET size - // encoder.addParam("thirdinfo", textArea); - encoder.addParam("thirdinfo", "third value\r\ntest second line\r\n\r\nnew line\r\n"); - encoder.addParam("Send", "Send"); - - URI uriGet = new URI(encoder.toString()); - HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uriGet.toASCIIString()); - HttpHeaders headers = request.headers(); - headers.set(HttpHeaderNames.HOST, host); - headers.set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE); - headers.set(HttpHeaderNames.ACCEPT_ENCODING, HttpHeaderValues.GZIP + "," + HttpHeaderValues.DEFLATE); - - headers.set(HttpHeaderNames.ACCEPT_CHARSET, "ISO-8859-1,utf-8;q=0.7,*;q=0.7"); - headers.set(HttpHeaderNames.ACCEPT_LANGUAGE, "fr"); - headers.set(HttpHeaderNames.REFERER, uriSimple.toString()); - headers.set(HttpHeaderNames.USER_AGENT, "Netty Simple Http Client side"); - headers.set(HttpHeaderNames.ACCEPT, "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); - - //connection will not close but needed - // headers.set("Connection","keep-alive"); - // headers.set("Keep-Alive","300"); - - headers.set( - HttpHeaderNames.COOKIE, ClientCookieEncoder.STRICT.encode( - new DefaultCookie("my-cookie", "foo"), - new DefaultCookie("another-cookie", "bar")) - ); - - // send request - channel.writeAndFlush(request); - - // Wait for the server to close the connection. - channel.closeFuture().sync(); - - // convert headers to list - return headers.entries(); - } - - /** - * Standard post without multipart but already support on Factory (memory management) - * - * @return the list of HttpData object (attribute and file) to be reused on next post - */ - private static List formpost( - Bootstrap bootstrap, - String host, int port, URI uriSimple, File file, HttpDataFactory factory, - List> headers) throws Exception { - // XXX /formpost - // Start the connection attempt. - Future future = bootstrap.connect(SocketUtils.socketAddress(host, port)); - // Wait until the connection attempt succeeds or fails. - Channel channel = future.get(); - - // Prepare the HTTP request. - HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uriSimple.toASCIIString()); - - // Use the PostBody encoder - HttpPostRequestEncoder bodyRequestEncoder = - new HttpPostRequestEncoder(factory, request, false); // false => not multipart - - // it is legal to add directly header or cookie into the request until finalize - for (Entry entry : headers) { - request.headers().set(entry.getKey(), entry.getValue()); - } - - // add Form attribute - bodyRequestEncoder.addBodyAttribute("getform", "POST"); - bodyRequestEncoder.addBodyAttribute("info", "first value"); - bodyRequestEncoder.addBodyAttribute("secondinfo", "secondvalue īŋŊīŋŊīŋŊ&"); - bodyRequestEncoder.addBodyAttribute("thirdinfo", textArea); - bodyRequestEncoder.addBodyAttribute("fourthinfo", textAreaLong); - bodyRequestEncoder.addBodyFileUpload("myfile", file, "application/x-zip-compressed", false); - - // finalize request - request = bodyRequestEncoder.finalizeRequest(); - - // Create the bodylist to be reused on the last version with Multipart support - List bodylist = bodyRequestEncoder.getBodyListAttributes(); - - // send request - channel.write(request); - - // test if request was chunked and if so, finish the write - if (bodyRequestEncoder.isChunked()) { // could do either request.isChunked() - // either do it through ChunkedWriteHandler - channel.write(bodyRequestEncoder); - } - channel.flush(); - - // Do not clear here since we will reuse the InterfaceHttpData on the next request - // for the example (limit action on client side). Take this as a broadcast of the same - // request on both Post actions. - // - // On standard program, it is clearly recommended to clean all files after each request - // bodyRequestEncoder.cleanFiles(); - - // Wait for the server to close the connection. - channel.closeFuture().sync(); - return bodylist; - } - - /** - * Multipart example - */ - private static void formpostmultipart( - Bootstrap bootstrap, String host, int port, URI uriFile, HttpDataFactory factory, - Iterable> headers, List bodylist) throws Exception { - // XXX /formpostmultipart - // Start the connection attempt. - Future future = bootstrap.connect(SocketUtils.socketAddress(host, port)); - // Wait until the connection attempt succeeds or fails. - Channel channel = future.get(); - - // Prepare the HTTP request. - HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uriFile.toASCIIString()); - - // Use the PostBody encoder - HttpPostRequestEncoder bodyRequestEncoder = - new HttpPostRequestEncoder(factory, request, true); // true => multipart - - // it is legal to add directly header or cookie into the request until finalize - for (Entry entry : headers) { - request.headers().set(entry.getKey(), entry.getValue()); - } - - // add Form attribute from previous request in formpost() - bodyRequestEncoder.setBodyHttpDatas(bodylist); - - // finalize request - bodyRequestEncoder.finalizeRequest(); - - // send request - channel.write(request); - - // test if request was chunked and if so, finish the write - if (bodyRequestEncoder.isChunked()) { - channel.write(bodyRequestEncoder); - } - channel.flush(); - - // Now no more use of file representation (and list of HttpData) - bodyRequestEncoder.cleanFiles(); - - // Wait for the server to close the connection. - channel.closeFuture().sync(); - } - - // use to simulate a small TEXTAREA field in a form - private static final String textArea = "short text"; - // use to simulate a big TEXTAREA field in a form - private static final String textAreaLong = - "lkjlkjlKJLKJLKJLKJLJlkj lklkj\r\n\r\nLKJJJJJJJJKKKKKKKKKKKKKKK īŋŊīŋŊīŋŊīŋŊ&\r\n\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n"; -} diff --git a/example/src/main/java/io/netty/example/http/upload/HttpUploadClientHandler.java b/example/src/main/java/io/netty/example/http/upload/HttpUploadClientHandler.java deleted file mode 100644 index a95c05e5f7..0000000000 --- a/example/src/main/java/io/netty/example/http/upload/HttpUploadClientHandler.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.http.upload; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpUtil; -import io.netty.handler.codec.http.HttpObject; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.LastHttpContent; -import io.netty.util.CharsetUtil; - -/** - * Handler that just dumps the contents of the response from the server - */ -public class HttpUploadClientHandler extends SimpleChannelInboundHandler { - - private boolean readingChunks; - - @Override - public void messageReceived(ChannelHandlerContext ctx, HttpObject msg) { - if (msg instanceof HttpResponse) { - HttpResponse response = (HttpResponse) msg; - - System.err.println("STATUS: " + response.status()); - System.err.println("VERSION: " + response.protocolVersion()); - - if (!response.headers().isEmpty()) { - for (CharSequence name : response.headers().names()) { - for (CharSequence value : response.headers().getAll(name)) { - System.err.println("HEADER: " + name + " = " + value); - } - } - } - - if (response.status().code() == 200 && HttpUtil.isTransferEncodingChunked(response)) { - readingChunks = true; - System.err.println("CHUNKED CONTENT {"); - } else { - System.err.println("CONTENT {"); - } - } - if (msg instanceof HttpContent) { - HttpContent chunk = (HttpContent) msg; - System.err.println(chunk.content().toString(CharsetUtil.UTF_8)); - - if (chunk instanceof LastHttpContent) { - if (readingChunks) { - System.err.println("} END OF CHUNKED CONTENT"); - } else { - System.err.println("} END OF CONTENT"); - } - readingChunks = false; - } else { - System.err.println(chunk.content().toString(CharsetUtil.UTF_8)); - } - } - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - cause.printStackTrace(); - ctx.channel().close(); - } -} diff --git a/example/src/main/java/io/netty/example/http/upload/HttpUploadClientInitializer.java b/example/src/main/java/io/netty/example/http/upload/HttpUploadClientInitializer.java deleted file mode 100644 index a9987e8758..0000000000 --- a/example/src/main/java/io/netty/example/http/upload/HttpUploadClientInitializer.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.http.upload; - -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.socket.SocketChannel; -import io.netty.handler.codec.http.HttpClientCodec; -import io.netty.handler.codec.http.HttpContentDecompressor; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.stream.ChunkedWriteHandler; - -public class HttpUploadClientInitializer extends ChannelInitializer { - - private final SslContext sslCtx; - - public HttpUploadClientInitializer(SslContext sslCtx) { - this.sslCtx = sslCtx; - } - - @Override - public void initChannel(SocketChannel ch) { - ChannelPipeline pipeline = ch.pipeline(); - - if (sslCtx != null) { - pipeline.addLast("ssl", sslCtx.newHandler(ch.alloc())); - } - - pipeline.addLast("codec", new HttpClientCodec()); - - // Remove the following line if you don't want automatic content decompression. - pipeline.addLast("inflater", new HttpContentDecompressor()); - - // to be used since huge file transfer - pipeline.addLast("chunkedWriter", new ChunkedWriteHandler()); - - pipeline.addLast("handler", new HttpUploadClientHandler()); - } -} diff --git a/example/src/main/java/io/netty/example/http/upload/HttpUploadServer.java b/example/src/main/java/io/netty/example/http/upload/HttpUploadServer.java deleted file mode 100644 index 5e9ca08c95..0000000000 --- a/example/src/main/java/io/netty/example/http/upload/HttpUploadServer.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.http.upload; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.Channel; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.util.SelfSignedCertificate; - -/** - * An HTTP server showing how to use the HTTP multipart package for file uploads and decoding post data. - */ -public final class HttpUploadServer { - - static final boolean SSL = System.getProperty("ssl") != null; - static final int PORT = Integer.parseInt(System.getProperty("port", SSL? "8443" : "8080")); - - public static void main(String[] args) throws Exception { - // Configure SSL. - final SslContext sslCtx; - if (SSL) { - SelfSignedCertificate ssc = new SelfSignedCertificate(); - sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build(); - } else { - sslCtx = null; - } - - EventLoopGroup bossGroup = new MultithreadEventLoopGroup(1, NioHandler.newFactory()); - EventLoopGroup workerGroup = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - ServerBootstrap b = new ServerBootstrap(); - b.group(bossGroup, workerGroup); - b.channel(NioServerSocketChannel.class); - b.handler(new LoggingHandler(LogLevel.INFO)); - b.childHandler(new HttpUploadServerInitializer(sslCtx)); - - Channel ch = b.bind(PORT).get(); - - System.err.println("Open your web browser and navigate to " + - (SSL? "https" : "http") + "://127.0.0.1:" + PORT + '/'); - - ch.closeFuture().sync(); - } finally { - bossGroup.shutdownGracefully(); - workerGroup.shutdownGracefully(); - } - } -} 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 deleted file mode 100644 index e2d4b32712..0000000000 --- a/example/src/main/java/io/netty/example/http/upload/HttpUploadServerHandler.java +++ /dev/null @@ -1,453 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.http.upload; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.Channel; -import io.netty.channel.ChannelFutureListeners; -import io.netty.channel.ChannelHandlerContext; -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.HttpContent; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpObject; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpUtil; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http.LastHttpContent; -import io.netty.handler.codec.http.QueryStringDecoder; -import io.netty.handler.codec.http.cookie.Cookie; -import io.netty.handler.codec.http.cookie.ServerCookieDecoder; -import io.netty.handler.codec.http.cookie.ServerCookieEncoder; -import io.netty.handler.codec.http.multipart.Attribute; -import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory; -import io.netty.handler.codec.http.multipart.DiskAttribute; -import io.netty.handler.codec.http.multipart.DiskFileUpload; -import io.netty.handler.codec.http.multipart.FileUpload; -import io.netty.handler.codec.http.multipart.HttpData; -import io.netty.handler.codec.http.multipart.HttpDataFactory; -import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder; -import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.EndOfDataDecoderException; -import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.ErrorDataDecoderException; -import io.netty.handler.codec.http.multipart.InterfaceHttpData; -import io.netty.handler.codec.http.multipart.InterfaceHttpData.HttpDataType; -import io.netty.util.CharsetUtil; -import io.netty.util.concurrent.Future; - -import java.io.IOException; -import java.net.URI; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -import static io.netty.buffer.Unpooled.copiedBuffer; - -public class HttpUploadServerHandler extends SimpleChannelInboundHandler { - - private static final Logger logger = Logger.getLogger(HttpUploadServerHandler.class.getName()); - - private HttpRequest request; - - private HttpData partialContent; - - private final StringBuilder responseContent = new StringBuilder(); - - private static final HttpDataFactory factory = - new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE); // Disk if size exceed - - private HttpPostRequestDecoder decoder; - - static { - DiskFileUpload.deleteOnExitTemporaryFile = true; // should delete file - // on exit (in normal - // exit) - DiskFileUpload.baseDirectory = null; // system temp directory - DiskAttribute.deleteOnExitTemporaryFile = true; // should delete file on - // exit (in normal exit) - DiskAttribute.baseDirectory = null; // system temp directory - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - if (decoder != null) { - decoder.cleanFiles(); - } - } - - @Override - public void messageReceived(ChannelHandlerContext ctx, HttpObject msg) throws Exception { - if (msg instanceof HttpRequest) { - HttpRequest request = this.request = (HttpRequest) msg; - URI uri = new URI(request.uri()); - if (!uri.getPath().startsWith("/form")) { - // Write Menu - writeMenu(ctx); - return; - } - responseContent.setLength(0); - responseContent.append("WELCOME TO THE WILD WILD WEB SERVER\r\n"); - responseContent.append("===================================\r\n"); - - responseContent.append("VERSION: " + request.protocolVersion().text() + "\r\n"); - - responseContent.append("REQUEST_URI: " + request.uri() + "\r\n\r\n"); - responseContent.append("\r\n\r\n"); - - // new getMethod - for (Entry entry : request.headers()) { - responseContent.append("HEADER: " + entry.getKey() + '=' + entry.getValue() + "\r\n"); - } - responseContent.append("\r\n\r\n"); - - // new getMethod - Set cookies; - String value = request.headers().get(HttpHeaderNames.COOKIE); - if (value == null) { - cookies = Collections.emptySet(); - } else { - cookies = ServerCookieDecoder.STRICT.decode(value); - } - for (Cookie cookie : cookies) { - responseContent.append("COOKIE: " + cookie + "\r\n"); - } - responseContent.append("\r\n\r\n"); - - QueryStringDecoder decoderQuery = new QueryStringDecoder(request.uri()); - Map> uriAttributes = decoderQuery.parameters(); - for (Entry> attr: uriAttributes.entrySet()) { - for (String attrVal: attr.getValue()) { - responseContent.append("URI: " + attr.getKey() + '=' + attrVal + "\r\n"); - } - } - responseContent.append("\r\n\r\n"); - - // if GET Method: should not try to create an HttpPostRequestDecoder - if (HttpMethod.GET.equals(request.method())) { - // GET Method: should not try to create an HttpPostRequestDecoder - // So stop here - responseContent.append("\r\n\r\nEND OF GET CONTENT\r\n"); - // Not now: LastHttpContent will be sent writeResponse(ctx.channel()); - return; - } - try { - decoder = new HttpPostRequestDecoder(factory, request); - } catch (ErrorDataDecoderException e1) { - e1.printStackTrace(); - responseContent.append(e1.getMessage()); - writeResponse(ctx.channel(), true); - return; - } - - boolean readingChunks = HttpUtil.isTransferEncodingChunked(request); - responseContent.append("Is Chunked: " + readingChunks + "\r\n"); - responseContent.append("IsMultipart: " + decoder.isMultipart() + "\r\n"); - if (readingChunks) { - // Chunk version - responseContent.append("Chunks: "); - } - } - - // check if the decoder was constructed before - // if not it handles the form get - if (decoder != null) { - if (msg instanceof HttpContent) { - // New chunk is received - HttpContent chunk = (HttpContent) msg; - try { - decoder.offer(chunk); - } catch (ErrorDataDecoderException e1) { - e1.printStackTrace(); - responseContent.append(e1.getMessage()); - writeResponse(ctx.channel(), true); - return; - } - responseContent.append('o'); - // example of reading chunk by chunk (minimize memory usage due to - // Factory) - readHttpDataChunkByChunk(); - // example of reading only if at the end - if (chunk instanceof LastHttpContent) { - writeResponse(ctx.channel()); - - reset(); - } - } - } else { - writeResponse(ctx.channel()); - } - } - - private void reset() { - request = null; - - // destroy the decoder to release all resources - decoder.destroy(); - decoder = null; - } - - /** - * Example of reading request by chunk and getting values from chunk to chunk - */ - private void readHttpDataChunkByChunk() { - try { - while (decoder.hasNext()) { - InterfaceHttpData data = decoder.next(); - if (data != null) { - // check if current HttpData is a FileUpload and previously set as partial - if (partialContent == data) { - logger.info(" 100% (FinalSize: " + partialContent.length() + ")"); - partialContent = null; - } - // new value - writeHttpData(data); - } - } - // Check partial decoding for a FileUpload - InterfaceHttpData data = decoder.currentPartialHttpData(); - if (data != null) { - StringBuilder builder = new StringBuilder(); - if (partialContent == null) { - partialContent = (HttpData) data; - if (partialContent instanceof FileUpload) { - builder.append("Start FileUpload: ") - .append(((FileUpload) partialContent).getFilename()).append(" "); - } else { - builder.append("Start Attribute: ") - .append(partialContent.getName()).append(" "); - } - builder.append("(DefinedSize: ").append(partialContent.definedLength()).append(")"); - } - if (partialContent.definedLength() > 0) { - builder.append(" ").append(partialContent.length() * 100 / partialContent.definedLength()) - .append("% "); - logger.info(builder.toString()); - } else { - builder.append(" ").append(partialContent.length()).append(" "); - logger.info(builder.toString()); - } - } - } catch (EndOfDataDecoderException e1) { - // end - responseContent.append("\r\n\r\nEND OF CONTENT CHUNK BY CHUNK\r\n\r\n"); - } - } - - private void writeHttpData(InterfaceHttpData data) { - if (data.getHttpDataType() == HttpDataType.Attribute) { - Attribute attribute = (Attribute) data; - String value; - try { - value = attribute.getValue(); - } catch (IOException e1) { - // Error while reading data from File, only print name and error - e1.printStackTrace(); - responseContent.append("\r\nBODY Attribute: " + attribute.getHttpDataType().name() + ": " - + attribute.getName() + " Error while reading value: " + e1.getMessage() + "\r\n"); - return; - } - if (value.length() > 100) { - responseContent.append("\r\nBODY Attribute: " + attribute.getHttpDataType().name() + ": " - + attribute.getName() + " data too long\r\n"); - } else { - responseContent.append("\r\nBODY Attribute: " + attribute.getHttpDataType().name() + ": " - + attribute + "\r\n"); - } - } else { - responseContent.append("\r\nBODY FileUpload: " + data.getHttpDataType().name() + ": " + data - + "\r\n"); - if (data.getHttpDataType() == HttpDataType.FileUpload) { - FileUpload fileUpload = (FileUpload) data; - if (fileUpload.isCompleted()) { - if (fileUpload.length() < 10000) { - responseContent.append("\tContent of file\r\n"); - try { - responseContent.append(fileUpload.getString(fileUpload.getCharset())); - } catch (IOException e1) { - // do nothing for the example - e1.printStackTrace(); - } - responseContent.append("\r\n"); - } else { - responseContent.append("\tFile too long to be printed out:" + fileUpload.length() + "\r\n"); - } - // fileUpload.isInMemory();// tells if the file is in Memory - // or on File - // fileUpload.renameTo(dest); // enable to move into another - // File dest - // decoder.removeFileUploadFromClean(fileUpload); //remove - // the File of to delete file - } else { - responseContent.append("\tFile to be continued but should not!\r\n"); - } - } - } - } - - private void writeResponse(Channel channel) { - writeResponse(channel, false); - } - - private void writeResponse(Channel channel, boolean forceClose) { - // Convert the response content to a ChannelBuffer. - ByteBuf buf = copiedBuffer(responseContent.toString(), CharsetUtil.UTF_8); - responseContent.setLength(0); - - // Decide whether to close the connection or not. - boolean keepAlive = HttpUtil.isKeepAlive(request) && !forceClose; - - // Build the response object. - FullHttpResponse response = new DefaultFullHttpResponse( - HttpVersion.HTTP_1_1, HttpResponseStatus.OK, buf); - response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8"); - response.headers().setInt(HttpHeaderNames.CONTENT_LENGTH, buf.readableBytes()); - - if (!keepAlive) { - response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE); - } else if (request.protocolVersion().equals(HttpVersion.HTTP_1_0)) { - response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE); - } - - Set cookies; - String value = request.headers().get(HttpHeaderNames.COOKIE); - if (value == null) { - cookies = Collections.emptySet(); - } else { - cookies = ServerCookieDecoder.STRICT.decode(value); - } - if (!cookies.isEmpty()) { - // Reset the cookies if necessary. - for (Cookie cookie : cookies) { - response.headers().add(HttpHeaderNames.SET_COOKIE, ServerCookieEncoder.STRICT.encode(cookie)); - } - } - // Write the response. - Future future = channel.writeAndFlush(response); - // Close the connection after the write operation is done if necessary. - if (!keepAlive) { - future.addListener(channel, ChannelFutureListeners.CLOSE); - } - } - - private void writeMenu(ChannelHandlerContext ctx) { - // print several HTML forms - // Convert the response content to a ChannelBuffer. - responseContent.setLength(0); - - // create Pseudo Menu - responseContent.append(""); - responseContent.append(""); - responseContent.append("Netty Test Form\r\n"); - responseContent.append("\r\n"); - responseContent.append(""); - - responseContent.append(""); - responseContent.append(""); - responseContent.append(""); - responseContent.append(""); - responseContent.append("
"); - responseContent.append("

Netty Test Form

"); - responseContent.append("Choose one FORM"); - responseContent.append("
\r\n"); - - // GET - responseContent.append("
GET FORM
"); - responseContent.append("
"); - responseContent.append(""); - responseContent.append(""); - responseContent.append(""); - responseContent.append(""); - responseContent.append(""); - responseContent.append(""); - responseContent.append("
Fill with value:
Fill with value:
"); - responseContent - .append("
Fill with value:
"); - responseContent.append("
\r\n"); - responseContent.append("

"); - - // POST - responseContent.append("
POST FORM
"); - responseContent.append("
"); - responseContent.append(""); - responseContent.append(""); - responseContent.append(""); - responseContent.append(""); - responseContent.append(""); - responseContent.append(""); - responseContent.append("
Fill with value:
Fill with value:
"); - responseContent - .append("
Fill with value:
"); - responseContent.append("
Fill with file (only file name will be transmitted):
" - + ""); - responseContent.append("
\r\n"); - responseContent.append("

"); - - // POST with enctype="multipart/form-data" - responseContent.append("
POST MULTIPART FORM
"); - responseContent.append("
"); - responseContent.append(""); - responseContent.append(""); - responseContent.append(""); - responseContent.append(""); - responseContent.append(""); - responseContent.append(""); - responseContent.append("
Fill with value:
Fill with value:
"); - responseContent - .append("
Fill with value:
"); - responseContent.append("
Fill with file:
"); - responseContent.append("
\r\n"); - responseContent.append("

"); - - responseContent.append(""); - responseContent.append(""); - - ByteBuf buf = copiedBuffer(responseContent.toString(), CharsetUtil.UTF_8); - // Build the response object. - FullHttpResponse response = new DefaultFullHttpResponse( - HttpVersion.HTTP_1_1, HttpResponseStatus.OK, buf); - - response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html; charset=UTF-8"); - response.headers().setInt(HttpHeaderNames.CONTENT_LENGTH, buf.readableBytes()); - - // Decide whether to close the connection or not. - boolean keepAlive = HttpUtil.isKeepAlive(request); - if (!keepAlive) { - response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE); - } else if (request.protocolVersion().equals(HttpVersion.HTTP_1_0)) { - response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE); - } - - // Write the response. - Future future = ctx.writeAndFlush(response); - // Close the connection after the write operation is done if necessary. - if (!keepAlive) { - future.addListener(ctx, ChannelFutureListeners.CLOSE); - } - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - logger.log(Level.WARNING, responseContent.toString(), cause); - ctx.close(); - } -} diff --git a/example/src/main/java/io/netty/example/http/upload/HttpUploadServerInitializer.java b/example/src/main/java/io/netty/example/http/upload/HttpUploadServerInitializer.java deleted file mode 100644 index 9dc97a41cf..0000000000 --- a/example/src/main/java/io/netty/example/http/upload/HttpUploadServerInitializer.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.http.upload; - -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.socket.SocketChannel; -import io.netty.handler.codec.http.HttpContentCompressor; -import io.netty.handler.codec.http.HttpRequestDecoder; -import io.netty.handler.codec.http.HttpResponseEncoder; -import io.netty.handler.ssl.SslContext; - -public class HttpUploadServerInitializer extends ChannelInitializer { - - private final SslContext sslCtx; - - public HttpUploadServerInitializer(SslContext sslCtx) { - this.sslCtx = sslCtx; - } - - @Override - public void initChannel(SocketChannel ch) { - ChannelPipeline pipeline = ch.pipeline(); - - if (sslCtx != null) { - pipeline.addLast(sslCtx.newHandler(ch.alloc())); - } - - pipeline.addLast(new HttpRequestDecoder()); - pipeline.addLast(new HttpResponseEncoder()); - - // Remove the following line if you don't want automatic content compression. - pipeline.addLast(new HttpContentCompressor()); - - pipeline.addLast(new HttpUploadServerHandler()); - } -} diff --git a/example/src/main/java/io/netty/example/http/websocketx/benchmarkserver/WebSocketServer.java b/example/src/main/java/io/netty/example/http/websocketx/benchmarkserver/WebSocketServer.java deleted file mode 100644 index 6a7b2c1f7f..0000000000 --- a/example/src/main/java/io/netty/example/http/websocketx/benchmarkserver/WebSocketServer.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.http.websocketx.benchmarkserver; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.Channel; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.util.SelfSignedCertificate; - -/** - * A Benchmark application for websocket which is served at: - * - * http://localhost:8080/websocket - * - * Open your browser at http://localhost:8080/, then the benchmark page will be loaded and a Web Socket connection will - * be made automatically. - */ -public final class WebSocketServer { - - static final boolean SSL = System.getProperty("ssl") != null; - static final int PORT = Integer.parseInt(System.getProperty("port", SSL? "8443" : "8080")); - - public static void main(String[] args) throws Exception { - // Configure SSL. - final SslContext sslCtx; - if (SSL) { - SelfSignedCertificate ssc = new SelfSignedCertificate(); - sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build(); - } else { - sslCtx = null; - } - - EventLoopGroup bossGroup = new MultithreadEventLoopGroup(1, NioHandler.newFactory()); - EventLoopGroup workerGroup = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - ServerBootstrap b = new ServerBootstrap(); - b.group(bossGroup, workerGroup) - .channel(NioServerSocketChannel.class) - .childHandler(new WebSocketServerInitializer(sslCtx)); - - Channel ch = b.bind(PORT).get(); - - System.out.println("Open your web browser and navigate to " + - (SSL? "https" : "http") + "://127.0.0.1:" + PORT + '/'); - - ch.closeFuture().sync(); - } finally { - bossGroup.shutdownGracefully(); - workerGroup.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/http/websocketx/benchmarkserver/WebSocketServerBenchmarkPage.java b/example/src/main/java/io/netty/example/http/websocketx/benchmarkserver/WebSocketServerBenchmarkPage.java deleted file mode 100644 index 9941c1515c..0000000000 --- a/example/src/main/java/io/netty/example/http/websocketx/benchmarkserver/WebSocketServerBenchmarkPage.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.http.websocketx.benchmarkserver; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.CharsetUtil; - -/** - * Generates the benchmark HTML page which is served at http://localhost:8080/ - */ -public final class WebSocketServerBenchmarkPage { - - private static final String NEWLINE = "\r\n"; - - public static ByteBuf getContent(String webSocketLocation) { - return Unpooled.copiedBuffer( - "Web Socket Performance Test" + NEWLINE + - "" + NEWLINE + - "

WebSocket Performance Test

" + NEWLINE + - "" + NEWLINE + - "
" + NEWLINE + - - "
" + NEWLINE + - "Message size:" + - "
" + NEWLINE + - "Number of messages:" + - "
" + NEWLINE + - "Data Type:" + - "text" + - "binary
" + NEWLINE + - "Mode:
" + NEWLINE + - "" + - "Wait for response after each messages
" + NEWLINE + - "" + - "Send all messages and then wait for all responses
" + NEWLINE + - "Verify responded messages
" + NEWLINE + - "" + NEWLINE + - "

Output

" + NEWLINE + - "" + NEWLINE + - "
" + NEWLINE + - "" + NEWLINE + - "
" + NEWLINE + - - "" + NEWLINE + - "" + NEWLINE + - "" + NEWLINE, CharsetUtil.US_ASCII); - } - - private WebSocketServerBenchmarkPage() { - // Unused - } -} 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 deleted file mode 100644 index d92ee41128..0000000000 --- a/example/src/main/java/io/netty/example/http/websocketx/benchmarkserver/WebSocketServerHandler.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.http.websocketx.benchmarkserver; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelFutureListeners; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.handler.codec.http.DefaultFullHttpResponse; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpUtil; -import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; -import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame; -import io.netty.handler.codec.http.websocketx.PingWebSocketFrame; -import io.netty.handler.codec.http.websocketx.PongWebSocketFrame; -import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; -import io.netty.handler.codec.http.websocketx.WebSocketFrame; -import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker; -import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory; -import io.netty.util.CharsetUtil; - -import static io.netty.handler.codec.http.HttpHeaderNames.CONNECTION; -import static io.netty.handler.codec.http.HttpHeaderValues.CLOSE; -import static io.netty.handler.codec.http.HttpHeaderValues.KEEP_ALIVE; -import static io.netty.handler.codec.http.HttpMethod.GET; -import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST; -import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN; -import static io.netty.handler.codec.http.HttpResponseStatus.OK; -import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND; -import static io.netty.handler.codec.http.HttpVersion.HTTP_1_0; -import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; - -/** - * Handles handshakes and messages - */ -public class WebSocketServerHandler extends SimpleChannelInboundHandler { - - private static final String WEBSOCKET_PATH = "/websocket"; - - private WebSocketServerHandshaker handshaker; - - @Override - public void messageReceived(ChannelHandlerContext ctx, Object msg) { - if (msg instanceof FullHttpRequest) { - handleHttpRequest(ctx, (FullHttpRequest) msg); - } else if (msg instanceof WebSocketFrame) { - handleWebSocketFrame(ctx, (WebSocketFrame) msg); - } - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) { - ctx.flush(); - } - - private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) { - // Handle a bad request. - if (!req.decoderResult().isSuccess()) { - sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, BAD_REQUEST, Unpooled.EMPTY_BUFFER)); - return; - } - - // Allow only GET methods. - if (!GET.equals(req.method())) { - sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN, Unpooled.EMPTY_BUFFER)); - return; - } - - // Send the demo page and favicon.ico - if ("/".equals(req.uri())) { - ByteBuf content = WebSocketServerBenchmarkPage.getContent(getWebSocketLocation(req)); - FullHttpResponse res = new DefaultFullHttpResponse(HTTP_1_1, OK, content); - - res.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html; charset=UTF-8"); - HttpUtil.setContentLength(res, content.readableBytes()); - - sendHttpResponse(ctx, req, res); - return; - } - if ("/favicon.ico".equals(req.uri())) { - FullHttpResponse res = new DefaultFullHttpResponse(HTTP_1_1, NOT_FOUND, Unpooled.EMPTY_BUFFER); - sendHttpResponse(ctx, req, res); - return; - } - - // Handshake - WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory( - getWebSocketLocation(req), null, true, 5 * 1024 * 1024); - handshaker = wsFactory.newHandshaker(req); - if (handshaker == null) { - WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel()); - } else { - handshaker.handshake(ctx.channel(), req); - } - } - - private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) { - - // Check for closing frame - if (frame instanceof CloseWebSocketFrame) { - handshaker.close(ctx, (CloseWebSocketFrame) frame.retain()); - return; - } - if (frame instanceof PingWebSocketFrame) { - ctx.write(new PongWebSocketFrame(frame.content().retain())); - return; - } - if (frame instanceof TextWebSocketFrame) { - // Echo the frame - ctx.write(frame.retain()); - return; - } - if (frame instanceof BinaryWebSocketFrame) { - // Echo the frame - ctx.write(frame.retain()); - } - } - - private static void sendHttpResponse( - ChannelHandlerContext ctx, FullHttpRequest req, FullHttpResponse res) { - // Generate an error page if response getStatus code is not OK (200). - if (res.status().code() != 200) { - ByteBuf buf = Unpooled.copiedBuffer(res.status().toString(), CharsetUtil.UTF_8); - res.content().writeBytes(buf); - buf.release(); - HttpUtil.setContentLength(res, res.content().readableBytes()); - } - - // Send the response and close the connection if necessary. - if (!HttpUtil.isKeepAlive(req) || res.status().code() != 200) { - // Tell the client we're going to close the connection. - res.headers().set(CONNECTION, CLOSE); - ctx.writeAndFlush(res).addListener(ctx, ChannelFutureListeners.CLOSE); - } else { - if (req.protocolVersion().equals(HTTP_1_0)) { - res.headers().set(CONNECTION, KEEP_ALIVE); - } - ctx.writeAndFlush(res); - } - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - cause.printStackTrace(); - ctx.close(); - } - - private static String getWebSocketLocation(FullHttpRequest req) { - String location = req.headers().get(HttpHeaderNames.HOST) + WEBSOCKET_PATH; - if (WebSocketServer.SSL) { - return "wss://" + location; - } else { - return "ws://" + location; - } - } -} diff --git a/example/src/main/java/io/netty/example/http/websocketx/benchmarkserver/WebSocketServerInitializer.java b/example/src/main/java/io/netty/example/http/websocketx/benchmarkserver/WebSocketServerInitializer.java deleted file mode 100644 index 329e545077..0000000000 --- a/example/src/main/java/io/netty/example/http/websocketx/benchmarkserver/WebSocketServerInitializer.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.http.websocketx.benchmarkserver; - -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.socket.SocketChannel; -import io.netty.handler.codec.http.HttpObjectAggregator; -import io.netty.handler.codec.http.HttpServerCodec; -import io.netty.handler.ssl.SslContext; - -/** - */ -public class WebSocketServerInitializer extends ChannelInitializer { - - private final SslContext sslCtx; - - public WebSocketServerInitializer(SslContext sslCtx) { - this.sslCtx = sslCtx; - } - - @Override - public void initChannel(SocketChannel ch) throws Exception { - ChannelPipeline pipeline = ch.pipeline(); - if (sslCtx != null) { - pipeline.addLast(sslCtx.newHandler(ch.alloc())); - } - pipeline.addLast(new HttpServerCodec()); - pipeline.addLast(new HttpObjectAggregator(65536)); - pipeline.addLast(new WebSocketServerHandler()); - } -} diff --git a/example/src/main/java/io/netty/example/http/websocketx/benchmarkserver/package-info.java b/example/src/main/java/io/netty/example/http/websocketx/benchmarkserver/package-info.java deleted file mode 100644 index fcd1245a5e..0000000000 --- a/example/src/main/java/io/netty/example/http/websocketx/benchmarkserver/package-info.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - *

This package contains a benchmark application for websockets. - *

The websocket server will accept websocket upgrade requests and simply echo - * all incoming frames. - *

The supplied index page contains a benchmark client that runs in a browser. - *

Once started, you can start benchmarking by by navigating - * to http://localhost:8080/ with your browser. - *

You can also test it with a web socket client. Send web socket traffic to - * ws://localhost:8080/websocket. - */ -package io.netty.example.http.websocketx.benchmarkserver; diff --git a/example/src/main/java/io/netty/example/http/websocketx/client/WebSocketClient.java b/example/src/main/java/io/netty/example/http/websocketx/client/WebSocketClient.java deleted file mode 100644 index d547c88e2d..0000000000 --- a/example/src/main/java/io/netty/example/http/websocketx/client/WebSocketClient.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.http.websocketx.client; - -import io.netty.bootstrap.Bootstrap; -import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.codec.http.DefaultHttpHeaders; -import io.netty.handler.codec.http.HttpClientCodec; -import io.netty.handler.codec.http.HttpObjectAggregator; -import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame; -import io.netty.handler.codec.http.websocketx.PingWebSocketFrame; -import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; -import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory; -import io.netty.handler.codec.http.websocketx.WebSocketFrame; -import io.netty.handler.codec.http.websocketx.WebSocketVersion; -import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketClientCompressionHandler; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.util.InsecureTrustManagerFactory; - -import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.net.URI; - -/** - * This is an example of a WebSocket client. - *

- * In order to run this example you need a compatible WebSocket server. - * Therefore you can either start the WebSocket server from the examples - * by running {@link io.netty.example.http.websocketx.server.WebSocketServer} - * or connect to an existing WebSocket server such as - * ws://echo.websocket.org. - *

- * The client will attempt to connect to the URI passed to it as the first argument. - * You don't have to specify any arguments if you want to connect to the example WebSocket server, - * as this is the default. - */ -public final class WebSocketClient { - - static final String URL = System.getProperty("url", "ws://127.0.0.1:8080/websocket"); - - public static void main(String[] args) throws Exception { - URI uri = new URI(URL); - String scheme = uri.getScheme() == null? "ws" : uri.getScheme(); - final String host = uri.getHost() == null? "127.0.0.1" : uri.getHost(); - final int port; - if (uri.getPort() == -1) { - if ("ws".equalsIgnoreCase(scheme)) { - port = 80; - } else if ("wss".equalsIgnoreCase(scheme)) { - port = 443; - } else { - port = -1; - } - } else { - port = uri.getPort(); - } - - if (!"ws".equalsIgnoreCase(scheme) && !"wss".equalsIgnoreCase(scheme)) { - System.err.println("Only WS(S) is supported."); - return; - } - - final boolean ssl = "wss".equalsIgnoreCase(scheme); - final SslContext sslCtx; - if (ssl) { - sslCtx = SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE).build(); - } else { - sslCtx = null; - } - - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - // Connect with V13 (RFC 6455 aka HyBi-17). You can change it to V08 or V00. - // If you change it to V00, ping is not supported and remember to change - // HttpResponseDecoder to WebSocketHttpResponseDecoder in the pipeline. - final WebSocketClientHandler handler = - new WebSocketClientHandler( - WebSocketClientHandshakerFactory.newHandshaker( - uri, WebSocketVersion.V13, null, true, new DefaultHttpHeaders())); - - Bootstrap b = new Bootstrap(); - b.group(group) - .channel(NioSocketChannel.class) - .handler(new ChannelInitializer() { - @Override - protected void initChannel(SocketChannel ch) { - ChannelPipeline p = ch.pipeline(); - if (sslCtx != null) { - p.addLast(sslCtx.newHandler(ch.alloc(), host, port)); - } - p.addLast( - new HttpClientCodec(), - new HttpObjectAggregator(8192), - WebSocketClientCompressionHandler.INSTANCE, - handler); - } - }); - - Channel ch = b.connect(uri.getHost(), port).get(); - handler.handshakeFuture().sync(); - - BufferedReader console = new BufferedReader(new InputStreamReader(System.in)); - while (true) { - String msg = console.readLine(); - if (msg == null) { - break; - } else if ("bye".equalsIgnoreCase(msg)) { - ch.writeAndFlush(new CloseWebSocketFrame()); - ch.closeFuture().sync(); - break; - } else if ("ping".equalsIgnoreCase(msg)) { - WebSocketFrame frame = new PingWebSocketFrame(Unpooled.wrappedBuffer(new byte[] { 8, 1, 8, 1 })); - ch.writeAndFlush(frame); - } else { - WebSocketFrame frame = new TextWebSocketFrame(msg); - ch.writeAndFlush(frame); - } - } - } finally { - group.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/http/websocketx/client/WebSocketClientHandler.java b/example/src/main/java/io/netty/example/http/websocketx/client/WebSocketClientHandler.java deleted file mode 100644 index 868b7062b7..0000000000 --- a/example/src/main/java/io/netty/example/http/websocketx/client/WebSocketClientHandler.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -//The MIT License -// -//Copyright (c) 2009 Carl BystrÅĄm -// -//Permission is hereby granted, free of charge, to any person obtaining a copy -//of this software and associated documentation files (the "Software"), to deal -//in the Software without restriction, including without limitation the rights -//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -//copies of the Software, and to permit persons to whom the Software is -//furnished to do so, subject to the following conditions: -// -//The above copyright notice and this permission notice shall be included in -//all copies or substantial portions of the Software. -// -//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -//THE SOFTWARE. - -package io.netty.example.http.websocketx.client; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame; -import io.netty.handler.codec.http.websocketx.PongWebSocketFrame; -import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; -import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker; -import io.netty.handler.codec.http.websocketx.WebSocketFrame; -import io.netty.handler.codec.http.websocketx.WebSocketHandshakeException; -import io.netty.util.CharsetUtil; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; - -public class WebSocketClientHandler extends SimpleChannelInboundHandler { - - private final WebSocketClientHandshaker handshaker; - private Promise handshakeFuture; - - public WebSocketClientHandler(WebSocketClientHandshaker handshaker) { - this.handshaker = handshaker; - } - - public Future handshakeFuture() { - return handshakeFuture.asFuture(); - } - - @Override - public void handlerAdded(ChannelHandlerContext ctx) { - handshakeFuture = ctx.newPromise(); - } - - @Override - public void channelActive(ChannelHandlerContext ctx) { - handshaker.handshake(ctx.channel()); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) { - System.out.println("WebSocket Client disconnected!"); - } - - @Override - public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { - Channel ch = ctx.channel(); - if (!handshaker.isHandshakeComplete()) { - try { - handshaker.finishHandshake(ch, (FullHttpResponse) msg); - System.out.println("WebSocket Client connected!"); - handshakeFuture.setSuccess(null); - } catch (WebSocketHandshakeException e) { - System.out.println("WebSocket Client failed to connect"); - handshakeFuture.setFailure(e); - } - return; - } - - if (msg instanceof FullHttpResponse) { - FullHttpResponse response = (FullHttpResponse) msg; - throw new IllegalStateException( - "Unexpected FullHttpResponse (getStatus=" + response.status() + - ", content=" + response.content().toString(CharsetUtil.UTF_8) + ')'); - } - - WebSocketFrame frame = (WebSocketFrame) msg; - if (frame instanceof TextWebSocketFrame) { - TextWebSocketFrame textFrame = (TextWebSocketFrame) frame; - System.out.println("WebSocket Client received message: " + textFrame.text()); - } else if (frame instanceof PongWebSocketFrame) { - System.out.println("WebSocket Client received pong"); - } else if (frame instanceof CloseWebSocketFrame) { - System.out.println("WebSocket Client received closing"); - ch.close(); - } - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - cause.printStackTrace(); - if (!handshakeFuture.isDone()) { - handshakeFuture.setFailure(cause); - } - ctx.close(); - } -} 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 deleted file mode 100644 index 3813d26988..0000000000 --- a/example/src/main/java/io/netty/example/http/websocketx/server/WebSocketFrameHandler.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.http.websocketx.server; - -import java.util.Locale; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; -import io.netty.handler.codec.http.websocketx.WebSocketFrame; - -/** - * Echoes uppercase content of text frames. - */ -public class WebSocketFrameHandler extends SimpleChannelInboundHandler { - - @Override - protected void messageReceived(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception { - // ping and pong frames already handled - - if (frame instanceof TextWebSocketFrame) { - // Send the uppercase string back. - String request = ((TextWebSocketFrame) frame).text(); - ctx.channel().writeAndFlush(new TextWebSocketFrame(request.toUpperCase(Locale.US))); - } else { - String message = "unsupported frame type: " + frame.getClass().getName(); - throw new UnsupportedOperationException(message); - } - } -} 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 deleted file mode 100644 index 56f0e94ca3..0000000000 --- a/example/src/main/java/io/netty/example/http/websocketx/server/WebSocketIndexPageHandler.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.http.websocketx.server; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelFutureListeners; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.handler.codec.http.DefaultFullHttpResponse; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpUtil; -import io.netty.handler.ssl.SslHandler; -import io.netty.util.CharsetUtil; - -import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE; -import static io.netty.handler.codec.http.HttpHeaderNames.CONNECTION; -import static io.netty.handler.codec.http.HttpHeaderValues.CLOSE; -import static io.netty.handler.codec.http.HttpHeaderValues.KEEP_ALIVE; -import static io.netty.handler.codec.http.HttpMethod.GET; -import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST; -import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN; -import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND; -import static io.netty.handler.codec.http.HttpResponseStatus.OK; -import static io.netty.handler.codec.http.HttpVersion.HTTP_1_0; -import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; - -/** - * Outputs index page content. - */ -public class WebSocketIndexPageHandler extends SimpleChannelInboundHandler { - - private final String websocketPath; - - public WebSocketIndexPageHandler(String websocketPath) { - this.websocketPath = websocketPath; - } - - @Override - protected void messageReceived(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception { - // Handle a bad request. - if (!req.decoderResult().isSuccess()) { - sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, BAD_REQUEST, Unpooled.EMPTY_BUFFER)); - return; - } - - // Allow only GET methods. - if (!GET.equals(req.method())) { - sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN, Unpooled.EMPTY_BUFFER)); - return; - } - - // Send the index page - if ("/".equals(req.uri()) || "/index.html".equals(req.uri())) { - String webSocketLocation = getWebSocketLocation(ctx.pipeline(), req, websocketPath); - ByteBuf content = WebSocketServerIndexPage.getContent(webSocketLocation); - FullHttpResponse res = new DefaultFullHttpResponse(HTTP_1_1, OK, content); - - res.headers().set(CONTENT_TYPE, "text/html; charset=UTF-8"); - HttpUtil.setContentLength(res, content.readableBytes()); - - sendHttpResponse(ctx, req, res); - } else { - sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, NOT_FOUND, Unpooled.EMPTY_BUFFER)); - } - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - cause.printStackTrace(); - ctx.close(); - } - - private static void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req, FullHttpResponse res) { - // Generate an error page if response getStatus code is not OK (200). - if (res.status().code() != 200) { - ByteBuf buf = Unpooled.copiedBuffer(res.status().toString(), CharsetUtil.UTF_8); - res.content().writeBytes(buf); - buf.release(); - HttpUtil.setContentLength(res, res.content().readableBytes()); - } - - // Send the response and close the connection if necessary. - if (!HttpUtil.isKeepAlive(req) || res.status().code() != 200) { - // Tell the client we're going to close the connection. - res.headers().set(CONNECTION, CLOSE); - ctx.writeAndFlush(res).addListener(ctx, ChannelFutureListeners.CLOSE); - } else { - if (req.protocolVersion().equals(HTTP_1_0)) { - res.headers().set(CONNECTION, KEEP_ALIVE); - } - ctx.writeAndFlush(res); - } - } - - private static String getWebSocketLocation(ChannelPipeline cp, HttpRequest req, String path) { - String protocol = "ws"; - if (cp.get(SslHandler.class) != null) { - // SSL in use so use Secure WebSockets - protocol = "wss"; - } - return protocol + "://" + req.headers().get(HttpHeaderNames.HOST) + path; - } -} diff --git a/example/src/main/java/io/netty/example/http/websocketx/server/WebSocketServer.java b/example/src/main/java/io/netty/example/http/websocketx/server/WebSocketServer.java deleted file mode 100644 index 5f79190426..0000000000 --- a/example/src/main/java/io/netty/example/http/websocketx/server/WebSocketServer.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.http.websocketx.server; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.Channel; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.util.SelfSignedCertificate; - -/** - * An HTTP server which serves Web Socket requests at: - * - * http://localhost:8080/websocket - * - * Open your browser at http://localhost:8080/, then the demo page will be loaded - * and a Web Socket connection will be made automatically. - * - * This server illustrates support for the different web socket specification versions and will work with: - * - *
    - *
  • Safari 5+ (draft-ietf-hybi-thewebsocketprotocol-00) - *
  • Chrome 6-13 (draft-ietf-hybi-thewebsocketprotocol-00) - *
  • Chrome 14+ (draft-ietf-hybi-thewebsocketprotocol-10) - *
  • Chrome 16+ (RFC 6455 aka draft-ietf-hybi-thewebsocketprotocol-17) - *
  • Firefox 7+ (draft-ietf-hybi-thewebsocketprotocol-10) - *
  • Firefox 11+ (RFC 6455 aka draft-ietf-hybi-thewebsocketprotocol-17) - *
- */ -public final class WebSocketServer { - - static final boolean SSL = System.getProperty("ssl") != null; - static final int PORT = Integer.parseInt(System.getProperty("port", SSL? "8443" : "8080")); - - public static void main(String[] args) throws Exception { - // Configure SSL. - final SslContext sslCtx; - if (SSL) { - SelfSignedCertificate ssc = new SelfSignedCertificate(); - sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build(); - } else { - sslCtx = null; - } - - EventLoopGroup bossGroup = new MultithreadEventLoopGroup(1, NioHandler.newFactory()); - EventLoopGroup workerGroup = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - ServerBootstrap b = new ServerBootstrap(); - b.group(bossGroup, workerGroup) - .channel(NioServerSocketChannel.class) - .handler(new LoggingHandler(LogLevel.INFO)) - .childHandler(new WebSocketServerInitializer(sslCtx)); - - Channel ch = b.bind(PORT).get(); - - System.out.println("Open your web browser and navigate to " + - (SSL? "https" : "http") + "://127.0.0.1:" + PORT + '/'); - - ch.closeFuture().sync(); - } finally { - bossGroup.shutdownGracefully(); - workerGroup.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/http/websocketx/server/WebSocketServerIndexPage.java b/example/src/main/java/io/netty/example/http/websocketx/server/WebSocketServerIndexPage.java deleted file mode 100644 index 1a345ec531..0000000000 --- a/example/src/main/java/io/netty/example/http/websocketx/server/WebSocketServerIndexPage.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.http.websocketx.server; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.CharsetUtil; - -/** - * Generates the demo HTML page which is served at http://localhost:8080/ - */ -public final class WebSocketServerIndexPage { - - private static final String NEWLINE = "\r\n"; - - public static ByteBuf getContent(String webSocketLocation) { - return Unpooled.copiedBuffer( - "Web Socket Test" + NEWLINE + - "" + NEWLINE + - "" + NEWLINE + - "
" + NEWLINE + - "" + - "" + NEWLINE + - "

Output

" + NEWLINE + - "" + NEWLINE + - "
" + NEWLINE + - "" + NEWLINE + - "" + NEWLINE, CharsetUtil.US_ASCII); - } - - private WebSocketServerIndexPage() { - // Unused - } -} diff --git a/example/src/main/java/io/netty/example/http/websocketx/server/WebSocketServerInitializer.java b/example/src/main/java/io/netty/example/http/websocketx/server/WebSocketServerInitializer.java deleted file mode 100644 index 6f1e9dfed4..0000000000 --- a/example/src/main/java/io/netty/example/http/websocketx/server/WebSocketServerInitializer.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.http.websocketx.server; - -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.socket.SocketChannel; -import io.netty.handler.codec.http.HttpObjectAggregator; -import io.netty.handler.codec.http.HttpServerCodec; -import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; -import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketServerCompressionHandler; -import io.netty.handler.ssl.SslContext; - -/** - */ -public class WebSocketServerInitializer extends ChannelInitializer { - - private static final String WEBSOCKET_PATH = "/websocket"; - - private final SslContext sslCtx; - - public WebSocketServerInitializer(SslContext sslCtx) { - this.sslCtx = sslCtx; - } - - @Override - public void initChannel(SocketChannel ch) throws Exception { - ChannelPipeline pipeline = ch.pipeline(); - if (sslCtx != null) { - pipeline.addLast(sslCtx.newHandler(ch.alloc())); - } - pipeline.addLast(new HttpServerCodec()); - pipeline.addLast(new HttpObjectAggregator(65536)); - pipeline.addLast(new WebSocketServerCompressionHandler()); - pipeline.addLast(new WebSocketServerProtocolHandler(WEBSOCKET_PATH, null, true)); - pipeline.addLast(new WebSocketIndexPageHandler(WEBSOCKET_PATH)); - pipeline.addLast(new WebSocketFrameHandler()); - } -} diff --git a/example/src/main/java/io/netty/example/http/websocketx/server/package-info.java b/example/src/main/java/io/netty/example/http/websocketx/server/package-info.java deleted file mode 100644 index 75fe77750e..0000000000 --- a/example/src/main/java/io/netty/example/http/websocketx/server/package-info.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - *

This package contains an example web socket web server. - *

The web server only handles text, ping and closing frames. For text frames, - * it echoes the received text in upper case. - *

Once started, you can test the web server against your browser by navigating - * to http://localhost:8080/ - *

You can also test it with a web socket client. Send web socket traffic to - * ws://localhost:8080/websocket. - */ -package io.netty.example.http.websocketx.server; diff --git a/example/src/main/java/io/netty/example/http2/Http2ExampleUtil.java b/example/src/main/java/io/netty/example/http2/Http2ExampleUtil.java deleted file mode 100644 index 1a0d436871..0000000000 --- a/example/src/main/java/io/netty/example/http2/Http2ExampleUtil.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.example.http2; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.handler.codec.http.QueryStringDecoder; - -import java.io.IOException; -import java.io.InputStream; -import java.util.List; - -/** - * Utility methods used by the example client and server. - */ -public final class Http2ExampleUtil { - - /** - * Response header sent in response to the http->http2 cleartext upgrade request. - */ - public static final String UPGRADE_RESPONSE_HEADER = "http-to-http2-upgrade"; - - /** - * Size of the block to be read from the input stream. - */ - private static final int BLOCK_SIZE = 1024; - - private Http2ExampleUtil() { } - - /** - * @param string the string to be converted to an integer. - * @param defaultValue the default value - * @return the integer value of a string or the default value, if the string is either null or empty. - */ - public static int toInt(String string, int defaultValue) { - if (string != null && !string.isEmpty()) { - return Integer.parseInt(string); - } - return defaultValue; - } - - /** - * Reads an InputStream into a byte array. - * @param input the InputStream. - * @return a byte array representation of the InputStream. - * @throws IOException if an I/O exception of some sort happens while reading the InputStream. - */ - public static ByteBuf toByteBuf(InputStream input) throws IOException { - ByteBuf buf = Unpooled.buffer(); - int n = 0; - do { - n = buf.writeBytes(input, BLOCK_SIZE); - } while (n > 0); - return buf; - } - - /** - * @param query the decoder of query string - * @param key the key to lookup - * @return the first occurrence of that key in the string parameters - */ - public static String firstValue(QueryStringDecoder query, String key) { - requireNonNull(query, "Query can't be null!"); - List values = query.parameters().get(key); - if (values == null || values.isEmpty()) { - return null; - } - return values.get(0); - } -} diff --git a/example/src/main/java/io/netty/example/http2/helloworld/client/Http2Client.java b/example/src/main/java/io/netty/example/http2/helloworld/client/Http2Client.java deleted file mode 100644 index 2fb5028532..0000000000 --- a/example/src/main/java/io/netty/example/http2/helloworld/client/Http2Client.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.example.http2.helloworld.client; - -import io.netty.bootstrap.Bootstrap; -import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.netty.channel.ChannelOption; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpScheme; -import io.netty.handler.codec.http2.Http2SecurityUtil; -import io.netty.handler.codec.http2.HttpConversionUtil; -import io.netty.handler.ssl.ApplicationProtocolConfig; -import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol; -import io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior; -import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior; -import io.netty.handler.ssl.ApplicationProtocolNames; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.SslProvider; -import io.netty.handler.ssl.SupportedCipherSuiteFilter; -import io.netty.handler.ssl.util.InsecureTrustManagerFactory; -import io.netty.util.AsciiString; -import io.netty.util.CharsetUtil; - -import java.util.concurrent.TimeUnit; - -import static io.netty.buffer.Unpooled.wrappedBuffer; -import static io.netty.handler.codec.http.HttpMethod.GET; -import static io.netty.handler.codec.http.HttpMethod.POST; -import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; -import static io.netty.handler.ssl.SslProvider.JDK; -import static io.netty.handler.ssl.SslProvider.OPENSSL; -import static io.netty.handler.ssl.SslProvider.isAlpnSupported; - -/** - * An HTTP2 client that allows you to send HTTP2 frames to a server using HTTP1-style approaches - * (via {@link io.netty.handler.codec.http2.InboundHttp2ToHttpAdapter}). Inbound and outbound - * frames are logged. - * When run from the command-line, sends a single HEADERS frame to the server and gets back - * a "Hello World" response. - * See the ./http2/helloworld/frame/client/ example for a HTTP2 client example which does not use - * HTTP1-style objects and patterns. - */ -public final class Http2Client { - - static final boolean SSL = System.getProperty("ssl") != null; - static final String HOST = System.getProperty("host", "127.0.0.1"); - static final int PORT = Integer.parseInt(System.getProperty("port", SSL? "8443" : "8080")); - static final String URL = System.getProperty("url", "/whatever"); - static final String URL2 = System.getProperty("url2"); - static final String URL2DATA = System.getProperty("url2data", "test data!"); - - public static void main(String[] args) throws Exception { - // Configure SSL. - final SslContext sslCtx; - if (SSL) { - SslProvider provider = isAlpnSupported(OPENSSL) ? OPENSSL : JDK; - sslCtx = SslContextBuilder.forClient() - .sslProvider(provider) - /* NOTE: the cipher filter may not include all ciphers required by the HTTP/2 specification. - * Please refer to the HTTP/2 specification for cipher requirements. */ - .ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE) - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .applicationProtocolConfig(new ApplicationProtocolConfig( - Protocol.ALPN, - // NO_ADVERTISE is currently the only mode supported by both OpenSsl and JDK providers. - SelectorFailureBehavior.NO_ADVERTISE, - // ACCEPT is currently the only mode supported by both OpenSsl and JDK providers. - SelectedListenerFailureBehavior.ACCEPT, - ApplicationProtocolNames.HTTP_2, - ApplicationProtocolNames.HTTP_1_1)) - .build(); - } else { - sslCtx = null; - } - - EventLoopGroup workerGroup = new MultithreadEventLoopGroup(NioHandler.newFactory()); - Http2ClientInitializer initializer = new Http2ClientInitializer(sslCtx, Integer.MAX_VALUE); - - try { - // Configure the client. - Bootstrap b = new Bootstrap(); - b.group(workerGroup); - b.channel(NioSocketChannel.class); - b.option(ChannelOption.SO_KEEPALIVE, true); - b.remoteAddress(HOST, PORT); - b.handler(initializer); - - // Start the client. - Channel channel = b.connect().get(); - System.out.println("Connected to [" + HOST + ':' + PORT + ']'); - - // Wait for the HTTP/2 upgrade to occur. - Http2SettingsHandler http2SettingsHandler = initializer.settingsHandler(); - http2SettingsHandler.awaitSettings(5, TimeUnit.SECONDS); - - HttpResponseHandler responseHandler = initializer.responseHandler(); - int streamId = 3; - HttpScheme scheme = SSL ? HttpScheme.HTTPS : HttpScheme.HTTP; - AsciiString hostName = new AsciiString(HOST + ':' + PORT); - System.err.println("Sending request(s)..."); - if (URL != null) { - // Create a simple GET request. - FullHttpRequest request = new DefaultFullHttpRequest(HTTP_1_1, GET, URL, Unpooled.EMPTY_BUFFER); - request.headers().add(HttpHeaderNames.HOST, hostName); - request.headers().add(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), scheme.name()); - request.headers().add(HttpHeaderNames.ACCEPT_ENCODING, HttpHeaderValues.GZIP); - request.headers().add(HttpHeaderNames.ACCEPT_ENCODING, HttpHeaderValues.DEFLATE); - responseHandler.put(streamId, channel.write(request), channel.newPromise()); - streamId += 2; - } - if (URL2 != null) { - // Create a simple POST request with a body. - FullHttpRequest request = new DefaultFullHttpRequest(HTTP_1_1, POST, URL2, - wrappedBuffer(URL2DATA.getBytes(CharsetUtil.UTF_8))); - request.headers().add(HttpHeaderNames.HOST, hostName); - request.headers().add(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), scheme.name()); - request.headers().add(HttpHeaderNames.ACCEPT_ENCODING, HttpHeaderValues.GZIP); - request.headers().add(HttpHeaderNames.ACCEPT_ENCODING, HttpHeaderValues.DEFLATE); - responseHandler.put(streamId, channel.write(request), channel.newPromise()); - } - channel.flush(); - responseHandler.awaitResponses(5, TimeUnit.SECONDS); - System.out.println("Finished HTTP/2 request(s)"); - - // Wait until the connection is closed. - channel.close().syncUninterruptibly(); - } finally { - workerGroup.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/http2/helloworld/client/Http2ClientInitializer.java b/example/src/main/java/io/netty/example/http2/helloworld/client/Http2ClientInitializer.java deleted file mode 100644 index c71e6c6d69..0000000000 --- a/example/src/main/java/io/netty/example/http2/helloworld/client/Http2ClientInitializer.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.example.http2.helloworld.client; - -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.socket.SocketChannel; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.HttpClientCodec; -import io.netty.handler.codec.http.HttpClientUpgradeHandler; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http2.DefaultHttp2Connection; -import io.netty.handler.codec.http2.DelegatingDecompressorFrameListener; -import io.netty.handler.codec.http2.Http2ClientUpgradeCodec; -import io.netty.handler.codec.http2.Http2Connection; -import io.netty.handler.codec.http2.Http2FrameLogger; -import io.netty.handler.codec.http2.HttpToHttp2ConnectionHandler; -import io.netty.handler.codec.http2.HttpToHttp2ConnectionHandlerBuilder; -import io.netty.handler.codec.http2.InboundHttp2ToHttpAdapterBuilder; -import io.netty.handler.ssl.ApplicationProtocolNames; -import io.netty.handler.ssl.ApplicationProtocolNegotiationHandler; -import io.netty.handler.ssl.SslContext; - -import java.net.InetSocketAddress; - -import static io.netty.handler.logging.LogLevel.INFO; - -/** - * Configures the client pipeline to support HTTP/2 frames. - */ -public class Http2ClientInitializer extends ChannelInitializer { - private static final Http2FrameLogger logger = new Http2FrameLogger(INFO, Http2ClientInitializer.class); - - private final SslContext sslCtx; - private final int maxContentLength; - private HttpToHttp2ConnectionHandler connectionHandler; - private HttpResponseHandler responseHandler; - private Http2SettingsHandler settingsHandler; - - public Http2ClientInitializer(SslContext sslCtx, int maxContentLength) { - this.sslCtx = sslCtx; - this.maxContentLength = maxContentLength; - } - - @Override - public void initChannel(SocketChannel ch) throws Exception { - final Http2Connection connection = new DefaultHttp2Connection(false); - connectionHandler = new HttpToHttp2ConnectionHandlerBuilder() - .frameListener(new DelegatingDecompressorFrameListener( - connection, - new InboundHttp2ToHttpAdapterBuilder(connection) - .maxContentLength(maxContentLength) - .propagateSettings(true) - .build())) - .frameLogger(logger) - .connection(connection) - .build(); - responseHandler = new HttpResponseHandler(); - settingsHandler = new Http2SettingsHandler(ch.newPromise()); - if (sslCtx != null) { - configureSsl(ch); - } else { - configureClearText(ch); - } - } - - public HttpResponseHandler responseHandler() { - return responseHandler; - } - - public Http2SettingsHandler settingsHandler() { - return settingsHandler; - } - - protected void configureEndOfPipeline(ChannelPipeline pipeline) { - pipeline.addLast(settingsHandler, responseHandler); - } - - /** - * Configure the pipeline for TLS NPN negotiation to HTTP/2. - */ - private void configureSsl(SocketChannel ch) { - ChannelPipeline pipeline = ch.pipeline(); - // Specify Host in SSLContext New Handler to add TLS SNI Extension - pipeline.addLast(sslCtx.newHandler(ch.alloc(), Http2Client.HOST, Http2Client.PORT)); - // We must wait for the handshake to finish and the protocol to be negotiated before configuring - // the HTTP/2 components of the pipeline. - pipeline.addLast(new ApplicationProtocolNegotiationHandler("") { - @Override - protected void configurePipeline(ChannelHandlerContext ctx, String protocol) { - if (ApplicationProtocolNames.HTTP_2.equals(protocol)) { - ChannelPipeline p = ctx.pipeline(); - p.addLast(connectionHandler); - configureEndOfPipeline(p); - return; - } - ctx.close(); - throw new IllegalStateException("unknown protocol: " + protocol); - } - }); - } - - /** - * Configure the pipeline for a cleartext upgrade from HTTP to HTTP/2. - */ - private void configureClearText(SocketChannel ch) { - HttpClientCodec sourceCodec = new HttpClientCodec(); - Http2ClientUpgradeCodec upgradeCodec = new Http2ClientUpgradeCodec(connectionHandler); - HttpClientUpgradeHandler upgradeHandler = new HttpClientUpgradeHandler(sourceCodec, upgradeCodec, 65536); - - ch.pipeline().addLast(sourceCodec, - upgradeHandler, - new UpgradeRequestHandler(), - new UserEventLogger()); - } - - /** - * A handler that triggers the cleartext upgrade to HTTP/2 by sending an initial HTTP request. - */ - private final class UpgradeRequestHandler implements ChannelHandler { - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - DefaultFullHttpRequest upgradeRequest = - new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/", Unpooled.EMPTY_BUFFER); - - // Set HOST header as the remote peer may require it. - InetSocketAddress remote = (InetSocketAddress) ctx.channel().remoteAddress(); - String hostString = remote.getHostString(); - if (hostString == null) { - hostString = remote.getAddress().getHostAddress(); - } - upgradeRequest.headers().set(HttpHeaderNames.HOST, hostString + ':' + remote.getPort()); - - ctx.writeAndFlush(upgradeRequest); - - ctx.fireChannelActive(); - - // Done with this handler, remove it from the pipeline. - ctx.pipeline().remove(this); - - configureEndOfPipeline(ctx.pipeline()); - } - } - - /** - * Class that logs any User Events triggered on this channel. - */ - private static class UserEventLogger implements ChannelHandler { - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - System.out.println("User Event Triggered: " + evt); - ctx.fireUserEventTriggered(evt); - } - } -} diff --git a/example/src/main/java/io/netty/example/http2/helloworld/client/Http2SettingsHandler.java b/example/src/main/java/io/netty/example/http2/helloworld/client/Http2SettingsHandler.java deleted file mode 100644 index 8127884446..0000000000 --- a/example/src/main/java/io/netty/example/http2/helloworld/client/Http2SettingsHandler.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.example.http2.helloworld.client; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.handler.codec.http2.Http2Settings; -import io.netty.util.concurrent.Promise; - -import java.util.concurrent.TimeUnit; - -/** - * Reads the first {@link Http2Settings} object and notifies a {@link Promise} - */ -public class Http2SettingsHandler extends SimpleChannelInboundHandler { - private final Promise promise; - - /** - * Create new instance - * - * @param promise Promise object used to notify when first settings are received - */ - public Http2SettingsHandler(Promise promise) { - this.promise = promise; - } - - /** - * Wait for this handler to be added after the upgrade to HTTP/2, and for initial preface - * handshake to complete. - * - * @param timeout Time to wait - * @param unit {@link TimeUnit} for {@code timeout} - * @throws Exception if timeout or other failure occurs - */ - public void awaitSettings(long timeout, TimeUnit unit) throws Exception { - if (!promise.asFuture().awaitUninterruptibly(timeout, unit)) { - throw new IllegalStateException("Timed out waiting for settings"); - } - if (promise.isFailed()) { - throw new RuntimeException(promise.cause()); - } - } - - @Override - protected void messageReceived(ChannelHandlerContext ctx, Http2Settings msg) throws Exception { - promise.setSuccess(null); - - // Only care about the first settings message - ctx.pipeline().remove(this); - } -} diff --git a/example/src/main/java/io/netty/example/http2/helloworld/client/HttpResponseHandler.java b/example/src/main/java/io/netty/example/http2/helloworld/client/HttpResponseHandler.java deleted file mode 100644 index 0200358053..0000000000 --- a/example/src/main/java/io/netty/example/http2/helloworld/client/HttpResponseHandler.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.example.http2.helloworld.client; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http2.HttpConversionUtil; -import io.netty.util.CharsetUtil; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; - -import java.util.AbstractMap.SimpleEntry; -import java.util.Iterator; -import java.util.Map; -import java.util.Map.Entry; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; - -/** - * Process {@link FullHttpResponse} translated from HTTP/2 frames - */ -public class HttpResponseHandler extends SimpleChannelInboundHandler { - - private final Map, Promise>> streamidPromiseMap; - - public HttpResponseHandler() { - // Use a concurrent map because we add and iterate from the main thread (just for the purposes of the example), - // but Netty also does a get on the map when messages are received in a EventLoop thread. - streamidPromiseMap = new ConcurrentHashMap<>(); - } - - /** - * Create an association between an anticipated response stream id and a {@link Promise} - * - * @param streamId The stream for which a response is expected - * @param writeFuture A future that represent the request write operation - * @param promise The promise object that will be used to wait/notify events - * @return The previous object associated with {@code streamId} - * @see HttpResponseHandler#awaitResponses(long, TimeUnit) - */ - public Entry, Promise> put(int streamId, Future writeFuture, Promise promise) { - return streamidPromiseMap.put(streamId, new SimpleEntry<>(writeFuture, promise)); - } - - /** - * Wait (sequentially) for a time duration for each anticipated response - * - * @param timeout Value of time to wait for each response - * @param unit Units associated with {@code timeout} - * @see HttpResponseHandler#put(int, Future, Promise) - */ - public void awaitResponses(long timeout, TimeUnit unit) { - Iterator, Promise>>> itr = streamidPromiseMap.entrySet().iterator(); - while (itr.hasNext()) { - Entry, Promise>> entry = itr.next(); - Future writeFuture = entry.getValue().getKey(); - if (!writeFuture.awaitUninterruptibly(timeout, unit)) { - throw new IllegalStateException("Timed out waiting to write for stream id " + entry.getKey()); - } - if (writeFuture.isFailed()) { - throw new RuntimeException(writeFuture.cause()); - } - Promise promise = entry.getValue().getValue(); - if (!promise.asFuture().awaitUninterruptibly(timeout, unit)) { - throw new IllegalStateException("Timed out waiting for response on stream id " + entry.getKey()); - } - if (promise.isFailed()) { - throw new RuntimeException(promise.cause()); - } - System.out.println("---Stream id: " + entry.getKey() + " received---"); - itr.remove(); - } - } - - @Override - protected void messageReceived(ChannelHandlerContext ctx, FullHttpResponse msg) throws Exception { - Integer streamId = msg.headers().getInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text()); - if (streamId == null) { - System.err.println("HttpResponseHandler unexpected message received: " + msg); - return; - } - - Entry, Promise> entry = streamidPromiseMap.get(streamId); - if (entry == null) { - System.err.println("Message received for unknown stream id " + streamId); - } else { - // Do stuff with the message (for now just print it) - ByteBuf content = msg.content(); - if (content.isReadable()) { - int contentLength = content.readableBytes(); - byte[] arr = new byte[contentLength]; - content.readBytes(arr); - System.out.println(new String(arr, 0, contentLength, CharsetUtil.UTF_8)); - } - - entry.getValue().setSuccess(null); - } - } -} diff --git a/example/src/main/java/io/netty/example/http2/helloworld/frame/client/Http2ClientFrameInitializer.java b/example/src/main/java/io/netty/example/http2/helloworld/frame/client/Http2ClientFrameInitializer.java deleted file mode 100644 index c50d1338ab..0000000000 --- a/example/src/main/java/io/netty/example/http2/helloworld/frame/client/Http2ClientFrameInitializer.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.example.http2.helloworld.frame.client; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.handler.codec.http2.Http2FrameCodec; -import io.netty.handler.codec.http2.Http2FrameCodecBuilder; -import io.netty.handler.codec.http2.Http2MultiplexHandler; -import io.netty.handler.codec.http2.Http2Settings; -import io.netty.handler.ssl.SslContext; - -/** - * Configures client pipeline to support HTTP/2 frames via {@link Http2FrameCodec} and {@link Http2MultiplexHandler}. - */ -public final class Http2ClientFrameInitializer extends ChannelInitializer { - - private final SslContext sslCtx; - - public Http2ClientFrameInitializer(SslContext sslCtx) { - this.sslCtx = sslCtx; - } - - @Override - protected void initChannel(Channel ch) throws Exception { - // ensure that our 'trust all' SSL handler is the first in the pipeline if SSL is enabled. - if (sslCtx != null) { - ch.pipeline().addFirst(sslCtx.newHandler(ch.alloc())); - } - - final Http2FrameCodec http2FrameCodec = Http2FrameCodecBuilder.forClient() - .initialSettings(Http2Settings.defaultSettings()) // this is the default, but shows it can be changed. - .build(); - ch.pipeline().addLast(http2FrameCodec); - ch.pipeline().addLast(new Http2MultiplexHandler(new SimpleChannelInboundHandler() { - - @Override - protected void messageReceived(ChannelHandlerContext ctx, Object msg) { - // NOOP (this is the handler for 'inbound' streams, which is not relevant in this example) - } - })); - } - -} diff --git a/example/src/main/java/io/netty/example/http2/helloworld/frame/client/Http2ClientStreamFrameResponseHandler.java b/example/src/main/java/io/netty/example/http2/helloworld/frame/client/Http2ClientStreamFrameResponseHandler.java deleted file mode 100644 index bb56683810..0000000000 --- a/example/src/main/java/io/netty/example/http2/helloworld/frame/client/Http2ClientStreamFrameResponseHandler.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.example.http2.helloworld.frame.client; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.handler.codec.http2.Http2DataFrame; -import io.netty.handler.codec.http2.Http2HeadersFrame; -import io.netty.handler.codec.http2.Http2StreamFrame; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -/** - * Handles HTTP/2 stream frame responses. This is a useful approach if you specifically want to check - * the main HTTP/2 response DATA/HEADERs, but in this example it's used purely to see whether - * our request (for a specific stream id) has had a final response (for that same stream id). - */ -public final class Http2ClientStreamFrameResponseHandler extends SimpleChannelInboundHandler { - - private final CountDownLatch latch = new CountDownLatch(1); - - @Override - protected void messageReceived(ChannelHandlerContext ctx, Http2StreamFrame msg) { - System.out.println("Received HTTP/2 'stream' frame: " + msg); - - // isEndStream() is not from a common interface, so we currently must check both - if (msg instanceof Http2DataFrame && ((Http2DataFrame) msg).isEndStream()) { - latch.countDown(); - } else if (msg instanceof Http2HeadersFrame && ((Http2HeadersFrame) msg).isEndStream()) { - latch.countDown(); - } - } - - /** - * Waits for the latch to be decremented (i.e. for an end of stream message to be received), or for - * the latch to expire after 5 seconds. - * @return true if a successful HTTP/2 end of stream message was received. - */ - public boolean responseSuccessfullyCompleted() { - try { - return latch.await(5, TimeUnit.SECONDS); - } catch (InterruptedException ie) { - System.err.println("Latch exception: " + ie.getMessage()); - return false; - } - } - -} diff --git a/example/src/main/java/io/netty/example/http2/helloworld/frame/client/Http2FrameClient.java b/example/src/main/java/io/netty/example/http2/helloworld/frame/client/Http2FrameClient.java deleted file mode 100644 index d0cbc90c16..0000000000 --- a/example/src/main/java/io/netty/example/http2/helloworld/frame/client/Http2FrameClient.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.example.http2.helloworld.frame.client; - -import io.netty.bootstrap.Bootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelOption; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.codec.http2.DefaultHttp2Headers; -import io.netty.handler.codec.http2.DefaultHttp2HeadersFrame; -import io.netty.handler.codec.http2.Http2HeadersFrame; -import io.netty.handler.codec.http2.Http2SecurityUtil; -import io.netty.handler.codec.http2.Http2StreamChannel; -import io.netty.handler.codec.http2.Http2StreamChannelBootstrap; -import io.netty.handler.ssl.ApplicationProtocolConfig; -import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol; -import io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior; -import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior; -import io.netty.handler.ssl.ApplicationProtocolNames; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.SslProvider; -import io.netty.handler.ssl.SupportedCipherSuiteFilter; -import io.netty.handler.ssl.util.InsecureTrustManagerFactory; - -/** - * An HTTP2 client that allows you to send HTTP2 frames to a server using the newer HTTP2 - * approach (via {@link io.netty.handler.codec.http2.Http2FrameCodec}). - * When run from the command-line, sends a single HEADERS frame (with prior knowledge) to - * the server configured at host:port/path. - * You should include {@link io.netty.handler.codec.http2.Http2ClientUpgradeCodec} if the - * HTTP/2 server you are hitting doesn't support h2c/prior knowledge. - */ -public final class Http2FrameClient { - - static final boolean SSL = System.getProperty("ssl") != null; - static final String HOST = System.getProperty("host", "127.0.0.1"); - static final int PORT = Integer.parseInt(System.getProperty("port", SSL? "8443" : "8080")); - static final String PATH = System.getProperty("path", "/"); - - private Http2FrameClient() { - } - - public static void main(String[] args) throws Exception { - final EventLoopGroup clientWorkerGroup = new MultithreadEventLoopGroup(NioHandler.newFactory()); - - // Configure SSL. - final SslContext sslCtx; - if (SSL) { - final SslProvider provider = - SslProvider.isAlpnSupported(SslProvider.OPENSSL)? SslProvider.OPENSSL : SslProvider.JDK; - sslCtx = SslContextBuilder.forClient() - .sslProvider(provider) - .ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE) - // you probably won't want to use this in production, but it is fine for this example: - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .applicationProtocolConfig(new ApplicationProtocolConfig( - Protocol.ALPN, - SelectorFailureBehavior.NO_ADVERTISE, - SelectedListenerFailureBehavior.ACCEPT, - ApplicationProtocolNames.HTTP_2, - ApplicationProtocolNames.HTTP_1_1)) - .build(); - } else { - sslCtx = null; - } - - try { - final Bootstrap b = new Bootstrap(); - b.group(clientWorkerGroup); - b.channel(NioSocketChannel.class); - b.option(ChannelOption.SO_KEEPALIVE, true); - b.remoteAddress(HOST, PORT); - b.handler(new Http2ClientFrameInitializer(sslCtx)); - - // Start the client. - final Channel channel = b.connect().get(); - System.out.println("Connected to [" + HOST + ':' + PORT + ']'); - - final Http2ClientStreamFrameResponseHandler streamFrameResponseHandler = - new Http2ClientStreamFrameResponseHandler(); - - final Http2StreamChannelBootstrap streamChannelBootstrap = new Http2StreamChannelBootstrap(channel); - final Http2StreamChannel streamChannel = streamChannelBootstrap.open().syncUninterruptibly().getNow(); - streamChannel.pipeline().addLast(streamFrameResponseHandler); - - // Send request (a HTTP/2 HEADERS frame - with ':method = GET' in this case) - final DefaultHttp2Headers headers = new DefaultHttp2Headers(); - headers.method("GET"); - headers.path(PATH); - headers.scheme(SSL? "https" : "http"); - final Http2HeadersFrame headersFrame = new DefaultHttp2HeadersFrame(headers, true); - streamChannel.writeAndFlush(headersFrame); - System.out.println("Sent HTTP/2 GET request to " + PATH); - - // Wait for the responses (or for the latch to expire), then clean up the connections - if (!streamFrameResponseHandler.responseSuccessfullyCompleted()) { - System.err.println("Did not get HTTP/2 response in expected time."); - } - - System.out.println("Finished HTTP/2 request, will close the connection."); - - // Wait until the connection is closed. - channel.close().syncUninterruptibly(); - } finally { - clientWorkerGroup.shutdownGracefully(); - } - } - -} diff --git a/example/src/main/java/io/netty/example/http2/helloworld/frame/server/HelloWorldHttp2Handler.java b/example/src/main/java/io/netty/example/http2/helloworld/frame/server/HelloWorldHttp2Handler.java deleted file mode 100644 index 62ecb17953..0000000000 --- a/example/src/main/java/io/netty/example/http2/helloworld/frame/server/HelloWorldHttp2Handler.java +++ /dev/null @@ -1,109 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.example.http2.helloworld.frame.server; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http2.DefaultHttp2DataFrame; -import io.netty.handler.codec.http2.DefaultHttp2Headers; -import io.netty.handler.codec.http2.DefaultHttp2HeadersFrame; -import io.netty.handler.codec.http2.DefaultHttp2WindowUpdateFrame; -import io.netty.handler.codec.http2.Http2DataFrame; -import io.netty.handler.codec.http2.Http2FrameStream; -import io.netty.handler.codec.http2.Http2Headers; -import io.netty.handler.codec.http2.Http2HeadersFrame; -import io.netty.util.CharsetUtil; - -import static io.netty.buffer.Unpooled.copiedBuffer; -import static io.netty.buffer.Unpooled.unreleasableBuffer; -import static io.netty.handler.codec.http.HttpResponseStatus.OK; - -/** - * A simple handler that responds with the message "Hello World!". - * - *

This example is making use of the "frame codec" http2 API. This API is very experimental and incomplete. - */ -@Sharable -public class HelloWorldHttp2Handler implements ChannelHandler { - - static final ByteBuf RESPONSE_BYTES = unreleasableBuffer(copiedBuffer("Hello World", CharsetUtil.UTF_8)); - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - ctx.fireExceptionCaught(cause); - cause.printStackTrace(); - ctx.close(); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - if (msg instanceof Http2HeadersFrame) { - onHeadersRead(ctx, (Http2HeadersFrame) msg); - } else if (msg instanceof Http2DataFrame) { - onDataRead(ctx, (Http2DataFrame) msg); - } else { - ctx.fireChannelRead(msg); - } - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { - ctx.flush(); - } - - /** - * If receive a frame with end-of-stream set, send a pre-canned response. - */ - private static void onDataRead(ChannelHandlerContext ctx, Http2DataFrame data) throws Exception { - Http2FrameStream stream = data.stream(); - - if (data.isEndStream()) { - sendResponse(ctx, stream, data.content()); - } else { - // We do not send back the response to the remote-peer, so we need to release it. - data.release(); - } - - // Update the flowcontroller - ctx.write(new DefaultHttp2WindowUpdateFrame(data.initialFlowControlledBytes()).stream(stream)); - } - - /** - * If receive a frame with end-of-stream set, send a pre-canned response. - */ - private static void onHeadersRead(ChannelHandlerContext ctx, Http2HeadersFrame headers) - throws Exception { - if (headers.isEndStream()) { - ByteBuf content = ctx.alloc().buffer(); - content.writeBytes(RESPONSE_BYTES.duplicate()); - ByteBufUtil.writeAscii(content, " - via HTTP/2"); - sendResponse(ctx, headers.stream(), content); - } - } - - /** - * Sends a "Hello World" DATA frame to the client. - */ - private static void sendResponse(ChannelHandlerContext ctx, Http2FrameStream stream, ByteBuf payload) { - // Send a frame for the response status - Http2Headers headers = new DefaultHttp2Headers().status(OK.codeAsText()); - ctx.write(new DefaultHttp2HeadersFrame(headers).stream(stream)); - ctx.write(new DefaultHttp2DataFrame(payload, true).stream(stream)); - } -} diff --git a/example/src/main/java/io/netty/example/http2/helloworld/frame/server/Http2OrHttpHandler.java b/example/src/main/java/io/netty/example/http2/helloworld/frame/server/Http2OrHttpHandler.java deleted file mode 100644 index 143c618754..0000000000 --- a/example/src/main/java/io/netty/example/http2/helloworld/frame/server/Http2OrHttpHandler.java +++ /dev/null @@ -1,53 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.example.http2.helloworld.frame.server; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.example.http2.helloworld.server.HelloWorldHttp1Handler; -import io.netty.handler.codec.http.HttpObjectAggregator; -import io.netty.handler.codec.http.HttpServerCodec; -import io.netty.handler.codec.http2.Http2FrameCodecBuilder; -import io.netty.handler.ssl.ApplicationProtocolNames; -import io.netty.handler.ssl.ApplicationProtocolNegotiationHandler; - -/** - * Negotiates with the browser if HTTP2 or HTTP is going to be used. Once decided, the Netty - * pipeline is setup with the correct handlers for the selected protocol. - */ -public class Http2OrHttpHandler extends ApplicationProtocolNegotiationHandler { - - private static final int MAX_CONTENT_LENGTH = 1024 * 100; - - protected Http2OrHttpHandler() { - super(ApplicationProtocolNames.HTTP_1_1); - } - - @Override - protected void configurePipeline(ChannelHandlerContext ctx, String protocol) throws Exception { - if (ApplicationProtocolNames.HTTP_2.equals(protocol)) { - ctx.pipeline().addLast(Http2FrameCodecBuilder.forServer().build(), new HelloWorldHttp2Handler()); - return; - } - - if (ApplicationProtocolNames.HTTP_1_1.equals(protocol)) { - ctx.pipeline().addLast(new HttpServerCodec(), - new HttpObjectAggregator(MAX_CONTENT_LENGTH), - new HelloWorldHttp1Handler("ALPN Negotiation")); - return; - } - - throw new IllegalStateException("unknown protocol: " + protocol); - } -} diff --git a/example/src/main/java/io/netty/example/http2/helloworld/frame/server/Http2Server.java b/example/src/main/java/io/netty/example/http2/helloworld/frame/server/Http2Server.java deleted file mode 100644 index 284c25db97..0000000000 --- a/example/src/main/java/io/netty/example/http2/helloworld/frame/server/Http2Server.java +++ /dev/null @@ -1,98 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.example.http2.helloworld.frame.server; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelOption; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.codec.http2.Http2SecurityUtil; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; -import io.netty.handler.ssl.ApplicationProtocolConfig; -import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol; -import io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior; -import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior; -import io.netty.handler.ssl.ApplicationProtocolNames; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.SslProvider; -import io.netty.handler.ssl.SupportedCipherSuiteFilter; -import io.netty.handler.ssl.util.SelfSignedCertificate; - -import static io.netty.handler.ssl.SslProvider.isAlpnSupported; - -/** - * An HTTP/2 Server that responds to requests with a Hello World. Once started, you can test the - * server with the example client. - * - *

This example is making use of the "multiplexing" http2 API, where streams are mapped to child - * Channels. This API is very experimental and incomplete. - */ -public final class Http2Server { - - static final boolean SSL = System.getProperty("ssl") != null; - - static final int PORT = Integer.parseInt(System.getProperty("port", SSL? "8443" : "8080")); - - public static void main(String[] args) throws Exception { - // Configure SSL. - final SslContext sslCtx; - if (SSL) { - SslProvider provider = isAlpnSupported(SslProvider.OPENSSL) ? SslProvider.OPENSSL : SslProvider.JDK; - SelfSignedCertificate ssc = new SelfSignedCertificate(); - sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(provider) - /* NOTE: the cipher filter may not include all ciphers required by the HTTP/2 specification. - * Please refer to the HTTP/2 specification for cipher requirements. */ - .ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE) - .applicationProtocolConfig(new ApplicationProtocolConfig( - Protocol.ALPN, - // NO_ADVERTISE is currently the only mode supported by both OpenSsl and JDK providers. - SelectorFailureBehavior.NO_ADVERTISE, - // ACCEPT is currently the only mode supported by both OpenSsl and JDK providers. - SelectedListenerFailureBehavior.ACCEPT, - ApplicationProtocolNames.HTTP_2, - ApplicationProtocolNames.HTTP_1_1)) - .build(); - } else { - sslCtx = null; - } - // Configure the server. - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - ServerBootstrap b = new ServerBootstrap(); - b.option(ChannelOption.SO_BACKLOG, 1024); - b.group(group) - .channel(NioServerSocketChannel.class) - .handler(new LoggingHandler(LogLevel.INFO)) - .childHandler(new Http2ServerInitializer(sslCtx)); - - Channel ch = b.bind(PORT).get(); - - System.err.println("Open your HTTP/2-enabled web browser and navigate to " + - (SSL? "https" : "http") + "://127.0.0.1:" + PORT + '/'); - - ch.closeFuture().sync(); - } finally { - group.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/http2/helloworld/frame/server/Http2ServerInitializer.java b/example/src/main/java/io/netty/example/http2/helloworld/frame/server/Http2ServerInitializer.java deleted file mode 100644 index b0df3bc54d..0000000000 --- a/example/src/main/java/io/netty/example/http2/helloworld/frame/server/Http2ServerInitializer.java +++ /dev/null @@ -1,118 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.example.http2.helloworld.frame.server; - -import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.channel.socket.SocketChannel; -import io.netty.example.http2.helloworld.server.HelloWorldHttp1Handler; -import io.netty.handler.codec.http.HttpMessage; -import io.netty.handler.codec.http.HttpObjectAggregator; -import io.netty.handler.codec.http.HttpServerCodec; -import io.netty.handler.codec.http.HttpServerUpgradeHandler; -import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeCodecFactory; -import io.netty.handler.codec.http2.Http2CodecUtil; -import io.netty.handler.codec.http2.Http2FrameCodecBuilder; -import io.netty.handler.codec.http2.Http2ServerUpgradeCodec; -import io.netty.handler.ssl.SslContext; -import io.netty.util.AsciiString; -import io.netty.util.ReferenceCountUtil; - -/** - * Sets up the Netty pipeline for the example server. Depending on the endpoint config, sets up the - * pipeline for NPN or cleartext HTTP upgrade to HTTP/2. - */ -public class Http2ServerInitializer extends ChannelInitializer { - - private static final UpgradeCodecFactory upgradeCodecFactory = protocol -> { - if (AsciiString.contentEquals(Http2CodecUtil.HTTP_UPGRADE_PROTOCOL_NAME, protocol)) { - return new Http2ServerUpgradeCodec( - Http2FrameCodecBuilder.forServer().build(), new HelloWorldHttp2Handler()); - } else { - return null; - } - }; - - private final SslContext sslCtx; - private final int maxHttpContentLength; - - public Http2ServerInitializer(SslContext sslCtx) { - this(sslCtx, 16 * 1024); - } - - public Http2ServerInitializer(SslContext sslCtx, int maxHttpContentLength) { - this.sslCtx = sslCtx; - this.maxHttpContentLength = checkPositiveOrZero(maxHttpContentLength, "maxHttpContentLength"); - } - - @Override - public void initChannel(SocketChannel ch) { - if (sslCtx != null) { - configureSsl(ch); - } else { - configureClearText(ch); - } - } - - /** - * Configure the pipeline for TLS NPN negotiation to HTTP/2. - */ - private void configureSsl(SocketChannel ch) { - ch.pipeline().addLast(sslCtx.newHandler(ch.alloc()), new Http2OrHttpHandler()); - } - - /** - * Configure the pipeline for a cleartext upgrade from HTTP to HTTP/2.0 - */ - private void configureClearText(SocketChannel ch) { - final ChannelPipeline p = ch.pipeline(); - final HttpServerCodec sourceCodec = new HttpServerCodec(); - - p.addLast(sourceCodec); - p.addLast(new HttpServerUpgradeHandler(sourceCodec, upgradeCodecFactory)); - p.addLast(new SimpleChannelInboundHandler() { - @Override - protected void messageReceived(ChannelHandlerContext ctx, HttpMessage msg) throws Exception { - // If this handler is hit then no upgrade has been attempted and the client is just talking HTTP. - System.err.println("Directly talking: " + msg.protocolVersion() + " (no upgrade was attempted)"); - ChannelPipeline pipeline = ctx.pipeline(); - pipeline.addAfter(ctx.name(), null, new HelloWorldHttp1Handler("Direct. No Upgrade Attempted.")); - pipeline.addAfter(ctx.name(), null, new HttpObjectAggregator(maxHttpContentLength)); - ctx.fireChannelRead(ReferenceCountUtil.retain(msg)); - pipeline.remove(this); - } - }); - - p.addLast(new UserEventLogger()); - } - - /** - * Class that logs any User Events triggered on this channel. - */ - private static class UserEventLogger implements ChannelHandler { - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { - System.out.println("User Event Triggered: " + evt); - ctx.fireUserEventTriggered(evt); - } - } -} diff --git a/example/src/main/java/io/netty/example/http2/helloworld/multiplex/server/HelloWorldHttp2Handler.java b/example/src/main/java/io/netty/example/http2/helloworld/multiplex/server/HelloWorldHttp2Handler.java deleted file mode 100644 index 212a73bf46..0000000000 --- a/example/src/main/java/io/netty/example/http2/helloworld/multiplex/server/HelloWorldHttp2Handler.java +++ /dev/null @@ -1,102 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.example.http2.helloworld.multiplex.server; - -import static io.netty.buffer.Unpooled.copiedBuffer; -import static io.netty.buffer.Unpooled.unreleasableBuffer; -import static io.netty.handler.codec.http.HttpResponseStatus.OK; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http2.DefaultHttp2DataFrame; -import io.netty.handler.codec.http2.DefaultHttp2Headers; -import io.netty.handler.codec.http2.DefaultHttp2HeadersFrame; -import io.netty.handler.codec.http2.Http2DataFrame; -import io.netty.handler.codec.http2.Http2Headers; -import io.netty.handler.codec.http2.Http2HeadersFrame; -import io.netty.util.CharsetUtil; - -/** - * A simple handler that responds with the message "Hello World!". - * - *

This example is making use of the "multiplexing" http2 API, where streams are mapped to child - * Channels. This API is very experimental and incomplete. - */ -@Sharable -public class HelloWorldHttp2Handler implements ChannelHandler { - - static final ByteBuf RESPONSE_BYTES = unreleasableBuffer(copiedBuffer("Hello World", CharsetUtil.UTF_8)); - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - ctx.fireExceptionCaught(cause); - cause.printStackTrace(); - ctx.close(); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - if (msg instanceof Http2HeadersFrame) { - onHeadersRead(ctx, (Http2HeadersFrame) msg); - } else if (msg instanceof Http2DataFrame) { - onDataRead(ctx, (Http2DataFrame) msg); - } else { - ctx.fireChannelRead(msg); - } - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { - ctx.flush(); - } - - /** - * If receive a frame with end-of-stream set, send a pre-canned response. - */ - private static void onDataRead(ChannelHandlerContext ctx, Http2DataFrame data) throws Exception { - if (data.isEndStream()) { - sendResponse(ctx, data.content()); - } else { - // We do not send back the response to the remote-peer, so we need to release it. - data.release(); - } - } - - /** - * If receive a frame with end-of-stream set, send a pre-canned response. - */ - private static void onHeadersRead(ChannelHandlerContext ctx, Http2HeadersFrame headers) - throws Exception { - if (headers.isEndStream()) { - ByteBuf content = ctx.alloc().buffer(); - content.writeBytes(RESPONSE_BYTES.duplicate()); - ByteBufUtil.writeAscii(content, " - via HTTP/2"); - sendResponse(ctx, content); - } - } - - /** - * Sends a "Hello World" DATA frame to the client. - */ - private static void sendResponse(ChannelHandlerContext ctx, ByteBuf payload) { - // Send a frame for the response status - Http2Headers headers = new DefaultHttp2Headers().status(OK.codeAsText()); - ctx.write(new DefaultHttp2HeadersFrame(headers)); - ctx.write(new DefaultHttp2DataFrame(payload, true)); - } -} diff --git a/example/src/main/java/io/netty/example/http2/helloworld/multiplex/server/Http2OrHttpHandler.java b/example/src/main/java/io/netty/example/http2/helloworld/multiplex/server/Http2OrHttpHandler.java deleted file mode 100644 index eb175423e3..0000000000 --- a/example/src/main/java/io/netty/example/http2/helloworld/multiplex/server/Http2OrHttpHandler.java +++ /dev/null @@ -1,55 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.example.http2.helloworld.multiplex.server; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.example.http2.helloworld.server.HelloWorldHttp1Handler; -import io.netty.handler.codec.http.HttpObjectAggregator; -import io.netty.handler.codec.http.HttpServerCodec; -import io.netty.handler.codec.http2.Http2FrameCodecBuilder; -import io.netty.handler.codec.http2.Http2MultiplexHandler; -import io.netty.handler.ssl.ApplicationProtocolNames; -import io.netty.handler.ssl.ApplicationProtocolNegotiationHandler; - -/** - * Negotiates with the browser if HTTP2 or HTTP is going to be used. Once decided, the Netty - * pipeline is setup with the correct handlers for the selected protocol. - */ -public class Http2OrHttpHandler extends ApplicationProtocolNegotiationHandler { - - private static final int MAX_CONTENT_LENGTH = 1024 * 100; - - protected Http2OrHttpHandler() { - super(ApplicationProtocolNames.HTTP_1_1); - } - - @Override - protected void configurePipeline(ChannelHandlerContext ctx, String protocol) throws Exception { - if (ApplicationProtocolNames.HTTP_2.equals(protocol)) { - ctx.pipeline().addLast(Http2FrameCodecBuilder.forServer().build()); - ctx.pipeline().addLast(new Http2MultiplexHandler(new HelloWorldHttp2Handler())); - return; - } - - if (ApplicationProtocolNames.HTTP_1_1.equals(protocol)) { - ctx.pipeline().addLast(new HttpServerCodec(), - new HttpObjectAggregator(MAX_CONTENT_LENGTH), - new HelloWorldHttp1Handler("ALPN Negotiation")); - return; - } - - throw new IllegalStateException("unknown protocol: " + protocol); - } -} diff --git a/example/src/main/java/io/netty/example/http2/helloworld/multiplex/server/Http2Server.java b/example/src/main/java/io/netty/example/http2/helloworld/multiplex/server/Http2Server.java deleted file mode 100644 index 94c9760fdc..0000000000 --- a/example/src/main/java/io/netty/example/http2/helloworld/multiplex/server/Http2Server.java +++ /dev/null @@ -1,100 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.example.http2.helloworld.multiplex.server; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelOption; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.codec.http2.Http2SecurityUtil; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; -import io.netty.handler.ssl.ApplicationProtocolConfig; -import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol; -import io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior; -import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior; -import io.netty.handler.ssl.ApplicationProtocolNames; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.SslProvider; -import io.netty.handler.ssl.SupportedCipherSuiteFilter; -import io.netty.handler.ssl.util.SelfSignedCertificate; - -import static io.netty.handler.ssl.SslProvider.JDK; -import static io.netty.handler.ssl.SslProvider.OPENSSL; -import static io.netty.handler.ssl.SslProvider.isAlpnSupported; - -/** - * An HTTP/2 Server that responds to requests with a Hello World. Once started, you can test the - * server with the example client. - * - *

This example is making use of the "multiplexing" http2 API, where streams are mapped to child - * Channels. This API is very experimental and incomplete. - */ -public final class Http2Server { - - static final boolean SSL = System.getProperty("ssl") != null; - - static final int PORT = Integer.parseInt(System.getProperty("port", SSL? "8443" : "8080")); - - public static void main(String[] args) throws Exception { - // Configure SSL. - final SslContext sslCtx; - if (SSL) { - SslProvider provider = isAlpnSupported(OPENSSL) ? OPENSSL : JDK; - SelfSignedCertificate ssc = new SelfSignedCertificate(); - sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(provider) - /* NOTE: the cipher filter may not include all ciphers required by the HTTP/2 specification. - * Please refer to the HTTP/2 specification for cipher requirements. */ - .ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE) - .applicationProtocolConfig(new ApplicationProtocolConfig( - Protocol.ALPN, - // NO_ADVERTISE is currently the only mode supported by both OpenSsl and JDK providers. - SelectorFailureBehavior.NO_ADVERTISE, - // ACCEPT is currently the only mode supported by both OpenSsl and JDK providers. - SelectedListenerFailureBehavior.ACCEPT, - ApplicationProtocolNames.HTTP_2, - ApplicationProtocolNames.HTTP_1_1)) - .build(); - } else { - sslCtx = null; - } - // Configure the server. - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - ServerBootstrap b = new ServerBootstrap(); - b.option(ChannelOption.SO_BACKLOG, 1024); - b.group(group) - .channel(NioServerSocketChannel.class) - .handler(new LoggingHandler(LogLevel.INFO)) - .childHandler(new Http2ServerInitializer(sslCtx)); - - Channel ch = b.bind(PORT).get(); - - System.err.println("Open your HTTP/2-enabled web browser and navigate to " + - (SSL? "https" : "http") + "://127.0.0.1:" + PORT + '/'); - - ch.closeFuture().sync(); - } finally { - group.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/http2/helloworld/multiplex/server/Http2ServerInitializer.java b/example/src/main/java/io/netty/example/http2/helloworld/multiplex/server/Http2ServerInitializer.java deleted file mode 100644 index ab89d7673c..0000000000 --- a/example/src/main/java/io/netty/example/http2/helloworld/multiplex/server/Http2ServerInitializer.java +++ /dev/null @@ -1,120 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.example.http2.helloworld.multiplex.server; - -import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.channel.socket.SocketChannel; -import io.netty.example.http2.helloworld.server.HelloWorldHttp1Handler; -import io.netty.handler.codec.http.HttpMessage; -import io.netty.handler.codec.http.HttpObjectAggregator; -import io.netty.handler.codec.http.HttpServerCodec; -import io.netty.handler.codec.http.HttpServerUpgradeHandler; -import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeCodecFactory; -import io.netty.handler.codec.http2.Http2FrameCodecBuilder; -import io.netty.handler.codec.http2.Http2CodecUtil; -import io.netty.handler.codec.http2.Http2MultiplexHandler; -import io.netty.handler.codec.http2.Http2ServerUpgradeCodec; -import io.netty.handler.ssl.SslContext; -import io.netty.util.AsciiString; -import io.netty.util.ReferenceCountUtil; - -/** - * Sets up the Netty pipeline for the example server. Depending on the endpoint config, sets up the - * pipeline for NPN or cleartext HTTP upgrade to HTTP/2. - */ -public class Http2ServerInitializer extends ChannelInitializer { - - private static final UpgradeCodecFactory upgradeCodecFactory = protocol -> { - if (AsciiString.contentEquals(Http2CodecUtil.HTTP_UPGRADE_PROTOCOL_NAME, protocol)) { - return new Http2ServerUpgradeCodec( - Http2FrameCodecBuilder.forServer().build(), - new Http2MultiplexHandler(new HelloWorldHttp2Handler())); - } else { - return null; - } - }; - - private final SslContext sslCtx; - private final int maxHttpContentLength; - - public Http2ServerInitializer(SslContext sslCtx) { - this(sslCtx, 16 * 1024); - } - - public Http2ServerInitializer(SslContext sslCtx, int maxHttpContentLength) { - this.sslCtx = sslCtx; - this.maxHttpContentLength = checkPositiveOrZero(maxHttpContentLength, "maxHttpContentLength"); - } - - @Override - public void initChannel(SocketChannel ch) { - if (sslCtx != null) { - configureSsl(ch); - } else { - configureClearText(ch); - } - } - - /** - * Configure the pipeline for TLS NPN negotiation to HTTP/2. - */ - private void configureSsl(SocketChannel ch) { - ch.pipeline().addLast(sslCtx.newHandler(ch.alloc()), new Http2OrHttpHandler()); - } - - /** - * Configure the pipeline for a cleartext upgrade from HTTP to HTTP/2.0 - */ - private void configureClearText(SocketChannel ch) { - final ChannelPipeline p = ch.pipeline(); - final HttpServerCodec sourceCodec = new HttpServerCodec(); - - p.addLast(sourceCodec); - p.addLast(new HttpServerUpgradeHandler(sourceCodec, upgradeCodecFactory)); - p.addLast(new SimpleChannelInboundHandler() { - @Override - protected void messageReceived(ChannelHandlerContext ctx, HttpMessage msg) throws Exception { - // If this handler is hit then no upgrade has been attempted and the client is just talking HTTP. - System.err.println("Directly talking: " + msg.protocolVersion() + " (no upgrade was attempted)"); - ChannelPipeline pipeline = ctx.pipeline(); - pipeline.addAfter(ctx.name(), null, new HelloWorldHttp1Handler("Direct. No Upgrade Attempted.")); - pipeline.addAfter(ctx.name(), null, new HttpObjectAggregator(maxHttpContentLength)); - ctx.fireChannelRead(ReferenceCountUtil.retain(msg)); - pipeline.remove(this); - } - }); - - p.addLast(new UserEventLogger()); - } - - /** - * Class that logs any User Events triggered on this channel. - */ - private static class UserEventLogger implements ChannelHandler { - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { - System.out.println("User Event Triggered: " + evt); - ctx.fireUserEventTriggered(evt); - } - } -} diff --git a/example/src/main/java/io/netty/example/http2/helloworld/server/HelloWorldHttp1Handler.java b/example/src/main/java/io/netty/example/http2/helloworld/server/HelloWorldHttp1Handler.java deleted file mode 100644 index e69abe6dc0..0000000000 --- a/example/src/main/java/io/netty/example/http2/helloworld/server/HelloWorldHttp1Handler.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.http2.helloworld.server; - -import static io.netty.handler.codec.http.HttpHeaderNames.CONNECTION; -import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH; -import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE; -import static io.netty.handler.codec.http.HttpHeaderValues.CLOSE; -import static io.netty.handler.codec.http.HttpHeaderValues.KEEP_ALIVE; -import static io.netty.handler.codec.http.HttpResponseStatus.CONTINUE; -import static io.netty.handler.codec.http.HttpResponseStatus.OK; -import static io.netty.handler.codec.http.HttpVersion.HTTP_1_0; -import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelFutureListeners; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.handler.codec.http.DefaultFullHttpResponse; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpUtil; - -/** - * HTTP handler that responds with a "Hello World" - */ -public class HelloWorldHttp1Handler extends SimpleChannelInboundHandler { - private final String establishApproach; - - public HelloWorldHttp1Handler(String establishApproach) { - this.establishApproach = requireNonNull(establishApproach, "establishApproach"); - } - - @Override - public void messageReceived(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception { - if (HttpUtil.is100ContinueExpected(req)) { - ctx.write(new DefaultFullHttpResponse(HTTP_1_1, CONTINUE, Unpooled.EMPTY_BUFFER)); - } - boolean keepAlive = HttpUtil.isKeepAlive(req); - - ByteBuf content = ctx.alloc().buffer(); - content.writeBytes(HelloWorldHttp2Handler.RESPONSE_BYTES.duplicate()); - ByteBufUtil.writeAscii(content, " - via " + req.protocolVersion() + " (" + establishApproach + ")"); - - FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, content); - response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8"); - response.headers().setInt(CONTENT_LENGTH, response.content().readableBytes()); - - if (keepAlive) { - if (req.protocolVersion().equals(HTTP_1_0)) { - response.headers().set(CONNECTION, KEEP_ALIVE); - } - ctx.write(response); - } else { - // Tell the client we're going to close the connection. - response.headers().set(CONNECTION, CLOSE); - ctx.write(response).addListener(ctx, ChannelFutureListeners.CLOSE); - } - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { - ctx.flush(); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - cause.printStackTrace(); - ctx.close(); - } -} diff --git a/example/src/main/java/io/netty/example/http2/helloworld/server/HelloWorldHttp2Handler.java b/example/src/main/java/io/netty/example/http2/helloworld/server/HelloWorldHttp2Handler.java deleted file mode 100644 index b63a70be82..0000000000 --- a/example/src/main/java/io/netty/example/http2/helloworld/server/HelloWorldHttp2Handler.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.example.http2.helloworld.server; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpScheme; -import io.netty.handler.codec.http.HttpServerUpgradeHandler; -import io.netty.handler.codec.http2.DefaultHttp2Headers; -import io.netty.handler.codec.http2.Http2ConnectionDecoder; -import io.netty.handler.codec.http2.Http2ConnectionEncoder; -import io.netty.handler.codec.http2.Http2ConnectionHandler; -import io.netty.handler.codec.http2.Http2Flags; -import io.netty.handler.codec.http2.Http2FrameListener; -import io.netty.handler.codec.http2.Http2Headers; -import io.netty.handler.codec.http2.Http2Settings; -import io.netty.util.CharsetUtil; - -import static io.netty.buffer.Unpooled.copiedBuffer; -import static io.netty.buffer.Unpooled.unreleasableBuffer; -import static io.netty.handler.codec.http.HttpResponseStatus.OK; - -/** - * A simple handler that responds with the message "Hello World!". - */ -public final class HelloWorldHttp2Handler extends Http2ConnectionHandler implements Http2FrameListener { - - static final ByteBuf RESPONSE_BYTES = unreleasableBuffer(copiedBuffer("Hello World", CharsetUtil.UTF_8)); - - HelloWorldHttp2Handler(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder, - Http2Settings initialSettings) { - super(decoder, encoder, initialSettings); - } - - private static Http2Headers http1HeadersToHttp2Headers(FullHttpRequest request) { - CharSequence host = request.headers().get(HttpHeaderNames.HOST); - Http2Headers http2Headers = new DefaultHttp2Headers() - .method(HttpMethod.GET.asciiName()) - .path(request.uri()) - .scheme(HttpScheme.HTTP.name()); - if (host != null) { - http2Headers.authority(host); - } - return http2Headers; - } - - /** - * Handles the cleartext HTTP upgrade event. If an upgrade occurred, sends a simple response via HTTP/2 - * on stream 1 (the stream specifically reserved for cleartext HTTP upgrade). - */ - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (evt instanceof HttpServerUpgradeHandler.UpgradeEvent) { - HttpServerUpgradeHandler.UpgradeEvent upgradeEvent = - (HttpServerUpgradeHandler.UpgradeEvent) evt; - onHeadersRead(ctx, 1, http1HeadersToHttp2Headers(upgradeEvent.upgradeRequest()), 0 , true); - } - super.userEventTriggered(ctx, evt); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - super.exceptionCaught(ctx, cause); - cause.printStackTrace(); - ctx.close(); - } - - /** - * Sends a "Hello World" DATA frame to the client. - */ - private void sendResponse(ChannelHandlerContext ctx, int streamId, ByteBuf payload) { - // Send a frame for the response status - Http2Headers headers = new DefaultHttp2Headers().status(OK.codeAsText()); - encoder().writeHeaders(ctx, streamId, headers, 0, false); - encoder().writeData(ctx, streamId, payload, 0, true); - - // no need to call flush as channelReadComplete(...) will take care of it. - } - - @Override - public int onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding, boolean endOfStream) { - int processed = data.readableBytes() + padding; - if (endOfStream) { - sendResponse(ctx, streamId, data.retain()); - } - return processed; - } - - @Override - public void onHeadersRead(ChannelHandlerContext ctx, int streamId, - Http2Headers headers, int padding, boolean endOfStream) { - if (endOfStream) { - ByteBuf content = ctx.alloc().buffer(); - content.writeBytes(RESPONSE_BYTES.duplicate()); - ByteBufUtil.writeAscii(content, " - via HTTP/2"); - sendResponse(ctx, streamId, content); - } - } - - @Override - public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int streamDependency, - short weight, boolean exclusive, int padding, boolean endOfStream) { - onHeadersRead(ctx, streamId, headers, padding, endOfStream); - } - - @Override - public void onPriorityRead(ChannelHandlerContext ctx, int streamId, int streamDependency, - short weight, boolean exclusive) { - } - - @Override - public void onRstStreamRead(ChannelHandlerContext ctx, int streamId, long errorCode) { - } - - @Override - public void onSettingsAckRead(ChannelHandlerContext ctx) { - } - - @Override - public void onSettingsRead(ChannelHandlerContext ctx, Http2Settings settings) { - } - - @Override - public void onPingRead(ChannelHandlerContext ctx, long data) { - } - - @Override - public void onPingAckRead(ChannelHandlerContext ctx, long data) { - } - - @Override - public void onPushPromiseRead(ChannelHandlerContext ctx, int streamId, int promisedStreamId, - Http2Headers headers, int padding) { - } - - @Override - public void onGoAwayRead(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData) { - } - - @Override - public void onWindowUpdateRead(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement) { - } - - @Override - public void onUnknownFrame(ChannelHandlerContext ctx, byte frameType, int streamId, - Http2Flags flags, ByteBuf payload) { - } -} diff --git a/example/src/main/java/io/netty/example/http2/helloworld/server/HelloWorldHttp2HandlerBuilder.java b/example/src/main/java/io/netty/example/http2/helloworld/server/HelloWorldHttp2HandlerBuilder.java deleted file mode 100644 index 10c9bc4929..0000000000 --- a/example/src/main/java/io/netty/example/http2/helloworld/server/HelloWorldHttp2HandlerBuilder.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.example.http2.helloworld.server; - -import io.netty.handler.codec.http2.AbstractHttp2ConnectionHandlerBuilder; -import io.netty.handler.codec.http2.Http2ConnectionDecoder; -import io.netty.handler.codec.http2.Http2ConnectionEncoder; -import io.netty.handler.codec.http2.Http2FrameLogger; -import io.netty.handler.codec.http2.Http2Settings; - -import static io.netty.handler.logging.LogLevel.INFO; - -public final class HelloWorldHttp2HandlerBuilder - extends AbstractHttp2ConnectionHandlerBuilder { - - private static final Http2FrameLogger logger = new Http2FrameLogger(INFO, HelloWorldHttp2Handler.class); - - public HelloWorldHttp2HandlerBuilder() { - frameLogger(logger); - } - - @Override - public HelloWorldHttp2Handler build() { - return super.build(); - } - - @Override - protected HelloWorldHttp2Handler build(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder, - Http2Settings initialSettings) { - HelloWorldHttp2Handler handler = new HelloWorldHttp2Handler(decoder, encoder, initialSettings); - frameListener(handler); - return handler; - } -} diff --git a/example/src/main/java/io/netty/example/http2/helloworld/server/Http2OrHttpHandler.java b/example/src/main/java/io/netty/example/http2/helloworld/server/Http2OrHttpHandler.java deleted file mode 100644 index f5c7712b01..0000000000 --- a/example/src/main/java/io/netty/example/http2/helloworld/server/Http2OrHttpHandler.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.example.http2.helloworld.server; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http.HttpObjectAggregator; -import io.netty.handler.codec.http.HttpServerCodec; -import io.netty.handler.ssl.ApplicationProtocolNames; -import io.netty.handler.ssl.ApplicationProtocolNegotiationHandler; - -/** - * Negotiates with the browser if HTTP2 or HTTP is going to be used. Once decided, the Netty - * pipeline is setup with the correct handlers for the selected protocol. - */ -public class Http2OrHttpHandler extends ApplicationProtocolNegotiationHandler { - - private static final int MAX_CONTENT_LENGTH = 1024 * 100; - - protected Http2OrHttpHandler() { - super(ApplicationProtocolNames.HTTP_1_1); - } - - @Override - protected void configurePipeline(ChannelHandlerContext ctx, String protocol) throws Exception { - if (ApplicationProtocolNames.HTTP_2.equals(protocol)) { - ctx.pipeline().addLast(new HelloWorldHttp2HandlerBuilder().build()); - return; - } - - if (ApplicationProtocolNames.HTTP_1_1.equals(protocol)) { - ctx.pipeline().addLast(new HttpServerCodec(), - new HttpObjectAggregator(MAX_CONTENT_LENGTH), - new HelloWorldHttp1Handler("ALPN Negotiation")); - return; - } - - throw new IllegalStateException("unknown protocol: " + protocol); - } -} diff --git a/example/src/main/java/io/netty/example/http2/helloworld/server/Http2Server.java b/example/src/main/java/io/netty/example/http2/helloworld/server/Http2Server.java deleted file mode 100644 index 24f0ffc920..0000000000 --- a/example/src/main/java/io/netty/example/http2/helloworld/server/Http2Server.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.example.http2.helloworld.server; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelOption; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.codec.http2.Http2SecurityUtil; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; -import io.netty.handler.ssl.ApplicationProtocolConfig; -import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol; -import io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior; -import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior; -import io.netty.handler.ssl.ApplicationProtocolNames; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.SslProvider; -import io.netty.handler.ssl.SupportedCipherSuiteFilter; -import io.netty.handler.ssl.util.SelfSignedCertificate; - -import static io.netty.handler.ssl.SslProvider.isAlpnSupported; - -/** - * An HTTP/2 Server that responds to requests with a Hello World. Once started, you can test the - * server with the example client. - */ -public final class Http2Server { - - static final boolean SSL = System.getProperty("ssl") != null; - - static final int PORT = Integer.parseInt(System.getProperty("port", SSL? "8443" : "8080")); - - public static void main(String[] args) throws Exception { - // Configure SSL. - final SslContext sslCtx; - if (SSL) { - SslProvider provider = isAlpnSupported(SslProvider.OPENSSL) ? SslProvider.OPENSSL : SslProvider.JDK; - SelfSignedCertificate ssc = new SelfSignedCertificate(); - sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(provider) - /* NOTE: the cipher filter may not include all ciphers required by the HTTP/2 specification. - * Please refer to the HTTP/2 specification for cipher requirements. */ - .ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE) - .applicationProtocolConfig(new ApplicationProtocolConfig( - Protocol.ALPN, - // NO_ADVERTISE is currently the only mode supported by both OpenSsl and JDK providers. - SelectorFailureBehavior.NO_ADVERTISE, - // ACCEPT is currently the only mode supported by both OpenSsl and JDK providers. - SelectedListenerFailureBehavior.ACCEPT, - ApplicationProtocolNames.HTTP_2, - ApplicationProtocolNames.HTTP_1_1)) - .build(); - } else { - sslCtx = null; - } - // Configure the server. - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - ServerBootstrap b = new ServerBootstrap(); - b.option(ChannelOption.SO_BACKLOG, 1024); - b.group(group) - .channel(NioServerSocketChannel.class) - .handler(new LoggingHandler(LogLevel.INFO)) - .childHandler(new Http2ServerInitializer(sslCtx)); - - Channel ch = b.bind(PORT).get(); - - System.err.println("Open your HTTP/2-enabled web browser and navigate to " + - (SSL? "https" : "http") + "://127.0.0.1:" + PORT + '/'); - - ch.closeFuture().sync(); - } finally { - group.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/http2/helloworld/server/Http2ServerInitializer.java b/example/src/main/java/io/netty/example/http2/helloworld/server/Http2ServerInitializer.java deleted file mode 100644 index b322d083e3..0000000000 --- a/example/src/main/java/io/netty/example/http2/helloworld/server/Http2ServerInitializer.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.example.http2.helloworld.server; - -import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.channel.socket.SocketChannel; -import io.netty.handler.codec.http.HttpMessage; -import io.netty.handler.codec.http.HttpObjectAggregator; -import io.netty.handler.codec.http.HttpServerCodec; -import io.netty.handler.codec.http.HttpServerUpgradeHandler; -import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeCodecFactory; -import io.netty.handler.codec.http2.CleartextHttp2ServerUpgradeHandler; -import io.netty.handler.codec.http2.Http2CodecUtil; -import io.netty.handler.codec.http2.Http2ServerUpgradeCodec; -import io.netty.handler.ssl.SslContext; -import io.netty.util.AsciiString; -import io.netty.util.ReferenceCountUtil; - -/** - * Sets up the Netty pipeline for the example server. Depending on the endpoint config, sets up the - * pipeline for NPN or cleartext HTTP upgrade to HTTP/2. - */ -public class Http2ServerInitializer extends ChannelInitializer { - - private static final UpgradeCodecFactory upgradeCodecFactory = protocol -> { - if (AsciiString.contentEquals(Http2CodecUtil.HTTP_UPGRADE_PROTOCOL_NAME, protocol)) { - return new Http2ServerUpgradeCodec(new HelloWorldHttp2HandlerBuilder().build()); - } else { - return null; - } - }; - - private final SslContext sslCtx; - private final int maxHttpContentLength; - - public Http2ServerInitializer(SslContext sslCtx) { - this(sslCtx, 16 * 1024); - } - - public Http2ServerInitializer(SslContext sslCtx, int maxHttpContentLength) { - this.sslCtx = sslCtx; - this.maxHttpContentLength = checkPositiveOrZero(maxHttpContentLength, "maxHttpContentLength"); - } - - @Override - public void initChannel(SocketChannel ch) { - if (sslCtx != null) { - configureSsl(ch); - } else { - configureClearText(ch); - } - } - - /** - * Configure the pipeline for TLS NPN negotiation to HTTP/2. - */ - private void configureSsl(SocketChannel ch) { - ch.pipeline().addLast(sslCtx.newHandler(ch.alloc()), new Http2OrHttpHandler()); - } - - /** - * Configure the pipeline for a cleartext upgrade from HTTP to HTTP/2.0 - */ - private void configureClearText(SocketChannel ch) { - final ChannelPipeline p = ch.pipeline(); - final HttpServerCodec sourceCodec = new HttpServerCodec(); - final HttpServerUpgradeHandler upgradeHandler = new HttpServerUpgradeHandler(sourceCodec, upgradeCodecFactory); - final CleartextHttp2ServerUpgradeHandler cleartextHttp2ServerUpgradeHandler = - new CleartextHttp2ServerUpgradeHandler(sourceCodec, upgradeHandler, - new HelloWorldHttp2HandlerBuilder().build()); - - p.addLast(cleartextHttp2ServerUpgradeHandler); - p.addLast(new SimpleChannelInboundHandler() { - @Override - protected void messageReceived(ChannelHandlerContext ctx, HttpMessage msg) throws Exception { - // If this handler is hit then no upgrade has been attempted and the client is just talking HTTP. - System.err.println("Directly talking: " + msg.protocolVersion() + " (no upgrade was attempted)"); - ChannelPipeline pipeline = ctx.pipeline(); - pipeline.addAfter(ctx.name(), null, new HelloWorldHttp1Handler("Direct. No Upgrade Attempted.")); - pipeline.addAfter(ctx.name(), null, new HttpObjectAggregator(maxHttpContentLength)); - ctx.fireChannelRead(ReferenceCountUtil.retain(msg)); - pipeline.remove(this); - } - }); - - p.addLast(new UserEventLogger()); - } - - /** - * Class that logs any User Events triggered on this channel. - */ - private static class UserEventLogger implements ChannelHandler { - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { - System.out.println("User Event Triggered: " + evt); - ctx.fireUserEventTriggered(evt); - } - } -} diff --git a/example/src/main/java/io/netty/example/http2/tiles/FallbackRequestHandler.java b/example/src/main/java/io/netty/example/http2/tiles/FallbackRequestHandler.java deleted file mode 100644 index 7b4876f4c9..0000000000 --- a/example/src/main/java/io/netty/example/http2/tiles/FallbackRequestHandler.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.example.http2.tiles; - -import static io.netty.buffer.Unpooled.EMPTY_BUFFER; -import static io.netty.buffer.Unpooled.copiedBuffer; -import static io.netty.buffer.Unpooled.unreleasableBuffer; -import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH; -import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE; -import static io.netty.handler.codec.http.HttpResponseStatus.CONTINUE; -import static io.netty.handler.codec.http.HttpResponseStatus.OK; -import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; -import static io.netty.util.CharsetUtil.UTF_8; -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelFutureListeners; -import io.netty.channel.ChannelHandlerContext; -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.HttpUtil; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http2.Http2CodecUtil; - -/** - * Handles the exceptional case where HTTP 1.x was negotiated under TLS. - */ -public final class FallbackRequestHandler extends SimpleChannelInboundHandler { - - private static final ByteBuf response = unreleasableBuffer(copiedBuffer("" - + "

To view the example you need a browser that supports HTTP/2 (" - + Http2CodecUtil.TLS_UPGRADE_PROTOCOL_NAME - + ")

", UTF_8)).asReadOnly(); - - @Override - protected void messageReceived(ChannelHandlerContext ctx, HttpRequest req) throws Exception { - if (HttpUtil.is100ContinueExpected(req)) { - ctx.write(new DefaultFullHttpResponse(HTTP_1_1, CONTINUE, EMPTY_BUFFER)); - } - - ByteBuf content = ctx.alloc().buffer(); - content.writeBytes(response.duplicate()); - - FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, content); - response.headers().set(CONTENT_TYPE, "text/html; charset=UTF-8"); - response.headers().setInt(CONTENT_LENGTH, response.content().readableBytes()); - - ctx.write(response).addListener(ctx, ChannelFutureListeners.CLOSE); - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { - ctx.flush(); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - cause.printStackTrace(); - ctx.close(); - } -} diff --git a/example/src/main/java/io/netty/example/http2/tiles/Html.java b/example/src/main/java/io/netty/example/http2/tiles/Html.java deleted file mode 100644 index 48192e2d85..0000000000 --- a/example/src/main/java/io/netty/example/http2/tiles/Html.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.example.http2.tiles; - -import static io.netty.util.CharsetUtil.UTF_8; - -import java.util.Random; - -/** - * Static and dynamically generated HTML for the example. - */ -public final class Html { - - public static final String IP = System.getProperty("ip", "127.0.0.1"); - - public static final byte[] FOOTER = "".getBytes(UTF_8); - - public static final byte[] HEADER = ("Netty HTTP/2 Example" - + "" - + "" - + "A grid of 200 tiled images is shown below. Compare:" - + "

[HTTP/2, 0 latency] [HTTP/1, 0 latency]
" + "[HTTP/2, 30ms latency] [HTTP/1, 30ms latency]
" + "[HTTP/2, 200ms latency] [HTTP/1, 200ms latency]
" + "[HTTP/2, 1s latency] [HTTP/1, " + "1s latency]
").getBytes(UTF_8); - - private static final int IMAGES_X_AXIS = 20; - - private static final int IMAGES_Y_AXIS = 10; - - private Html() { - } - - private static String url(int port) { - return IP + ":" + port + "/http2"; - } - - public static byte[] body(int latency) { - int r = Math.abs(new Random().nextInt()); - // The string to be built contains 13192 fixed characters plus the variable latency and random cache-bust. - int numberOfCharacters = 13192 + stringLength(latency) + stringLength(r); - StringBuilder sb = new StringBuilder(numberOfCharacters).append("

"); - for (int y = 0; y < IMAGES_Y_AXIS; y++) { - for (int x = 0; x < IMAGES_X_AXIS; x++) { - sb.append(""); - } - sb.append("
\r\n"); - } - sb.append("
"); - return sb.toString().getBytes(UTF_8); - } - - private static int stringLength(int value) { - return Integer.toString(value).length() * IMAGES_X_AXIS * IMAGES_Y_AXIS; - } -} diff --git a/example/src/main/java/io/netty/example/http2/tiles/Http1RequestHandler.java b/example/src/main/java/io/netty/example/http2/tiles/Http1RequestHandler.java deleted file mode 100644 index cb3882923e..0000000000 --- a/example/src/main/java/io/netty/example/http2/tiles/Http1RequestHandler.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.example.http2.tiles; - -import static io.netty.handler.codec.http.HttpHeaderNames.CONNECTION; -import static io.netty.handler.codec.http.HttpHeaderValues.CLOSE; -import static io.netty.handler.codec.http.HttpHeaderValues.KEEP_ALIVE; -import static io.netty.handler.codec.http.HttpUtil.isKeepAlive; -import static io.netty.handler.codec.http.HttpResponseStatus.CONTINUE; -import static io.netty.handler.codec.http.HttpVersion.HTTP_1_0; -import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; - -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelFutureListeners; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http.DefaultFullHttpResponse; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpUtil; - -import java.util.concurrent.TimeUnit; - -/** - * Handles the requests for the tiled image using HTTP 1.x as a protocol. - */ -public final class Http1RequestHandler extends Http2RequestHandler { - - @Override - protected void messageReceived(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception { - if (HttpUtil.is100ContinueExpected(request)) { - ctx.write(new DefaultFullHttpResponse(HTTP_1_1, CONTINUE, Unpooled.EMPTY_BUFFER)); - } - super.messageReceived(ctx, request); - } - - @Override - protected void sendResponse(final ChannelHandlerContext ctx, String streamId, int latency, - final FullHttpResponse response, final FullHttpRequest request) { - HttpUtil.setContentLength(response, response.content().readableBytes()); - ctx.executor().schedule(() -> { - if (isKeepAlive(request)) { - if (request.protocolVersion().equals(HTTP_1_0)) { - response.headers().set(CONNECTION, KEEP_ALIVE); - } - ctx.writeAndFlush(response); - } else { - // Tell the client we're going to close the connection. - response.headers().set(CONNECTION, CLOSE); - ctx.writeAndFlush(response).addListener(ctx, ChannelFutureListeners.CLOSE); - } - }, latency, TimeUnit.MILLISECONDS); - } -} diff --git a/example/src/main/java/io/netty/example/http2/tiles/Http2OrHttpHandler.java b/example/src/main/java/io/netty/example/http2/tiles/Http2OrHttpHandler.java deleted file mode 100644 index 47983582c4..0000000000 --- a/example/src/main/java/io/netty/example/http2/tiles/Http2OrHttpHandler.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.example.http2.tiles; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http.HttpObjectAggregator; -import io.netty.handler.codec.http.HttpServerCodec; -import io.netty.handler.codec.http2.DefaultHttp2Connection; -import io.netty.handler.codec.http2.HttpToHttp2ConnectionHandlerBuilder; -import io.netty.handler.codec.http2.InboundHttp2ToHttpAdapter; -import io.netty.handler.codec.http2.InboundHttp2ToHttpAdapterBuilder; -import io.netty.handler.ssl.ApplicationProtocolNames; -import io.netty.handler.ssl.ApplicationProtocolNegotiationHandler; - -/** - * Used during protocol negotiation, the main function of this handler is to - * return the HTTP/1.1 or HTTP/2 handler once the protocol has been negotiated. - */ -public class Http2OrHttpHandler extends ApplicationProtocolNegotiationHandler { - - private static final int MAX_CONTENT_LENGTH = 1024 * 100; - - protected Http2OrHttpHandler() { - super(ApplicationProtocolNames.HTTP_1_1); - } - - @Override - protected void configurePipeline(ChannelHandlerContext ctx, String protocol) throws Exception { - if (ApplicationProtocolNames.HTTP_2.equals(protocol)) { - configureHttp2(ctx); - return; - } - - if (ApplicationProtocolNames.HTTP_1_1.equals(protocol)) { - configureHttp1(ctx); - return; - } - - throw new IllegalStateException("unknown protocol: " + protocol); - } - - private static void configureHttp2(ChannelHandlerContext ctx) { - DefaultHttp2Connection connection = new DefaultHttp2Connection(true); - InboundHttp2ToHttpAdapter listener = new InboundHttp2ToHttpAdapterBuilder(connection) - .propagateSettings(true).validateHttpHeaders(false) - .maxContentLength(MAX_CONTENT_LENGTH).build(); - - ctx.pipeline().addLast(new HttpToHttp2ConnectionHandlerBuilder() - .frameListener(listener) - // .frameLogger(TilesHttp2ToHttpHandler.logger) - .connection(connection).build()); - - ctx.pipeline().addLast(new Http2RequestHandler()); - } - - private static void configureHttp1(ChannelHandlerContext ctx) throws Exception { - ctx.pipeline().addLast(new HttpServerCodec(), - new HttpObjectAggregator(MAX_CONTENT_LENGTH), - new FallbackRequestHandler()); - } -} diff --git a/example/src/main/java/io/netty/example/http2/tiles/Http2RequestHandler.java b/example/src/main/java/io/netty/example/http2/tiles/Http2RequestHandler.java deleted file mode 100644 index ce08e68470..0000000000 --- a/example/src/main/java/io/netty/example/http2/tiles/Http2RequestHandler.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.example.http2.tiles; - -import static io.netty.buffer.Unpooled.EMPTY_BUFFER; -import static io.netty.example.http2.Http2ExampleUtil.firstValue; -import static io.netty.example.http2.Http2ExampleUtil.toInt; -import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE; -import static io.netty.handler.codec.http.HttpUtil.setContentLength; -import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST; -import static io.netty.handler.codec.http.HttpResponseStatus.OK; -import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; -import static java.lang.Integer.parseInt; -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.handler.codec.http.DefaultFullHttpResponse; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.QueryStringDecoder; -import io.netty.handler.codec.http2.HttpConversionUtil; -import io.netty.handler.codec.http2.InboundHttp2ToHttpAdapter; - -import java.util.concurrent.TimeUnit; - -/** - * Handles all the requests for data. It receives a {@link FullHttpRequest}, - * which has been converted by a {@link InboundHttp2ToHttpAdapter} before it - * arrived here. For further details, check {@link Http2OrHttpHandler} where the - * pipeline is setup. - */ -public class Http2RequestHandler extends SimpleChannelInboundHandler { - - private static final String LATENCY_FIELD_NAME = "latency"; - private static final int MIN_LATENCY = 0; - private static final int MAX_LATENCY = 1000; - private static final String IMAGE_COORDINATE_Y = "y"; - private static final String IMAGE_COORDINATE_X = "x"; - - @Override - protected void messageReceived(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception { - QueryStringDecoder queryString = new QueryStringDecoder(request.uri()); - String streamId = streamId(request); - int latency = toInt(firstValue(queryString, LATENCY_FIELD_NAME), 0); - if (latency < MIN_LATENCY || latency > MAX_LATENCY) { - sendBadRequest(ctx, streamId); - return; - } - String x = firstValue(queryString, IMAGE_COORDINATE_X); - String y = firstValue(queryString, IMAGE_COORDINATE_Y); - if (x == null || y == null) { - handlePage(ctx, streamId, latency, request); - } else { - handleImage(x, y, ctx, streamId, latency, request); - } - } - - private static void sendBadRequest(ChannelHandlerContext ctx, String streamId) { - FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, BAD_REQUEST, EMPTY_BUFFER); - streamId(response, streamId); - ctx.writeAndFlush(response); - } - - private void handleImage(String x, String y, ChannelHandlerContext ctx, String streamId, int latency, - FullHttpRequest request) { - ByteBuf image = ImageCache.INSTANCE.image(parseInt(x), parseInt(y)); - FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, image.duplicate()); - response.headers().set(CONTENT_TYPE, "image/jpeg"); - sendResponse(ctx, streamId, latency, response, request); - } - - private void handlePage(ChannelHandlerContext ctx, String streamId, int latency, FullHttpRequest request) { - byte[] body = Html.body(latency); - ByteBuf content = ctx.alloc().buffer(Html.HEADER.length + body.length + Html.FOOTER.length); - content.writeBytes(Html.HEADER); - content.writeBytes(body); - content.writeBytes(Html.FOOTER); - FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, content); - response.headers().set(CONTENT_TYPE, "text/html; charset=UTF-8"); - sendResponse(ctx, streamId, latency, response, request); - } - - protected void sendResponse(final ChannelHandlerContext ctx, String streamId, int latency, - final FullHttpResponse response, final FullHttpRequest request) { - setContentLength(response, response.content().readableBytes()); - streamId(response, streamId); - ctx.executor().schedule(() -> { - ctx.writeAndFlush(response); - }, latency, TimeUnit.MILLISECONDS); - } - - private static String streamId(FullHttpRequest request) { - return request.headers().get(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text()); - } - - private static void streamId(FullHttpResponse response, String streamId) { - response.headers().set(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), streamId); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - cause.printStackTrace(); - ctx.close(); - } -} diff --git a/example/src/main/java/io/netty/example/http2/tiles/Http2Server.java b/example/src/main/java/io/netty/example/http2/tiles/Http2Server.java deleted file mode 100644 index 7b6214f999..0000000000 --- a/example/src/main/java/io/netty/example/http2/tiles/Http2Server.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.example.http2.tiles; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelOption; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.ssl.ApplicationProtocolConfig; -import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol; -import io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior; -import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior; -import io.netty.handler.ssl.ApplicationProtocolNames; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.SupportedCipherSuiteFilter; -import io.netty.handler.ssl.util.SelfSignedCertificate; -import io.netty.util.concurrent.Future; - -import javax.net.ssl.SSLException; -import java.security.cert.CertificateException; - -import static io.netty.handler.codec.http2.Http2SecurityUtil.CIPHERS; - -/** - * Demonstrates an Http2 server using Netty to display a bunch of images and - * simulate latency. It is a Netty version of the - * Go lang HTTP2 tiles demo. - */ -public class Http2Server { - - public static final int PORT = Integer.parseInt(System.getProperty("http2-port", "8443")); - - private final EventLoopGroup group; - - public Http2Server(EventLoopGroup eventLoopGroup) { - group = eventLoopGroup; - } - - public Future start() throws Exception { - final SslContext sslCtx = configureTLS(); - ServerBootstrap b = new ServerBootstrap(); - b.option(ChannelOption.SO_BACKLOG, 1024); - b.group(group).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer() { - @Override - protected void initChannel(SocketChannel ch) throws Exception { - ch.pipeline().addLast(sslCtx.newHandler(ch.alloc()), new Http2OrHttpHandler()); - } - }); - - Channel ch = b.bind(PORT).get(); - return ch.closeFuture(); - } - - private static SslContext configureTLS() throws CertificateException, SSLException { - SelfSignedCertificate ssc = new SelfSignedCertificate(); - ApplicationProtocolConfig apn = new ApplicationProtocolConfig( - Protocol.ALPN, - // NO_ADVERTISE is currently the only mode supported by both OpenSsl and JDK providers. - SelectorFailureBehavior.NO_ADVERTISE, - // ACCEPT is currently the only mode supported by both OpenSsl and JDK providers. - SelectedListenerFailureBehavior.ACCEPT, - ApplicationProtocolNames.HTTP_2, - ApplicationProtocolNames.HTTP_1_1); - - return SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey(), null) - .ciphers(CIPHERS, SupportedCipherSuiteFilter.INSTANCE) - .applicationProtocolConfig(apn).build(); - } -} diff --git a/example/src/main/java/io/netty/example/http2/tiles/HttpServer.java b/example/src/main/java/io/netty/example/http2/tiles/HttpServer.java deleted file mode 100644 index 042ff8104a..0000000000 --- a/example/src/main/java/io/netty/example/http2/tiles/HttpServer.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.example.http2.tiles; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelOption; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.codec.http.HttpObjectAggregator; -import io.netty.handler.codec.http.HttpRequestDecoder; -import io.netty.handler.codec.http.HttpResponseEncoder; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; -import io.netty.util.concurrent.Future; - -/** - * Demonstrates an http server using Netty to display a bunch of images, simulate - * latency and compare it against the http2 implementation. - */ -public final class HttpServer { - - public static final int PORT = Integer.parseInt(System.getProperty("http-port", "8080")); - private static final int MAX_CONTENT_LENGTH = 1024 * 100; - - private final EventLoopGroup group; - - public HttpServer(EventLoopGroup eventLoopGroup) { - group = eventLoopGroup; - } - - public Future start() throws Exception { - ServerBootstrap b = new ServerBootstrap(); - b.option(ChannelOption.SO_BACKLOG, 1024); - - b.group(group).channel(NioServerSocketChannel.class).handler(new LoggingHandler(LogLevel.INFO)) - .childHandler(new ChannelInitializer() { - @Override - protected void initChannel(SocketChannel ch) throws Exception { - ch.pipeline().addLast(new HttpRequestDecoder(), - new HttpResponseEncoder(), - new HttpObjectAggregator(MAX_CONTENT_LENGTH), - new Http1RequestHandler()); - } - }); - - Channel ch = b.bind(PORT).get(); - return ch.closeFuture(); - } -} diff --git a/example/src/main/java/io/netty/example/http2/tiles/ImageCache.java b/example/src/main/java/io/netty/example/http2/tiles/ImageCache.java deleted file mode 100644 index 66507da5a7..0000000000 --- a/example/src/main/java/io/netty/example/http2/tiles/ImageCache.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.example.http2.tiles; - -import static io.netty.buffer.Unpooled.unreleasableBuffer; -import static io.netty.example.http2.Http2ExampleUtil.toByteBuf; -import io.netty.buffer.ByteBuf; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -/** - * Caches the images to avoid reading them every time from the disk. - */ -public final class ImageCache { - - public static ImageCache INSTANCE = new ImageCache(); - - private final Map imageBank = new HashMap<>(200); - - private ImageCache() { - init(); - } - - public static String name(int x, int y) { - return "tile-" + y + "-" + x + ".jpeg"; - } - - public ByteBuf image(int x, int y) { - return imageBank.get(name(x, y)); - } - - private void init() { - for (int y = 0; y < 10; y++) { - for (int x = 0; x < 20; x++) { - try { - String name = name(x, y); - ByteBuf fileBytes = unreleasableBuffer(toByteBuf(getClass() - .getResourceAsStream(name)).asReadOnly()); - imageBank.put(name, fileBytes); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - } -} diff --git a/example/src/main/java/io/netty/example/http2/tiles/Launcher.java b/example/src/main/java/io/netty/example/http2/tiles/Launcher.java deleted file mode 100644 index b9a20c41cd..0000000000 --- a/example/src/main/java/io/netty/example/http2/tiles/Launcher.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.example.http2.tiles; - -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; - -/** - *

- * Launches both Http and Http2 servers using Netty to display a set of images - * and simulate latency. It is a Netty version of the Go lang HTTP2 tiles - * demo. - *

- *

- * Please note that if you intent to use the JDK provider for SSL, you MUST use JDK 1.8. - * Previous JDK versions don't have any cipher suite that is suitable for use with HTTP/2. - * The associated ALPN library for your JDK version can be found here: - * https://eclipse.org/jetty/documentation/current/alpn-chapter.html#alpn-versions. - * Alternatively, you can use the OpenSsl provider. Please make sure that you run OpenSsl - * version 1.0.2 or greater. - *

- */ -public final class Launcher { - - public static void main(String[] args) { - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - Http2Server http2 = new Http2Server(group); - HttpServer http = new HttpServer(group); - try { - http2.start(); - System.err.println("Open your web browser and navigate to " + "http://" + Html.IP + ":" + HttpServer.PORT); - http.start().sync(); - } catch (Exception e) { - e.printStackTrace(); - } finally { - group.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/localecho/LocalEcho.java b/example/src/main/java/io/netty/example/localecho/LocalEcho.java deleted file mode 100644 index df9245a8ed..0000000000 --- a/example/src/main/java/io/netty/example/localecho/LocalEcho.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.localecho; - -import io.netty.bootstrap.Bootstrap; -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.local.LocalAddress; -import io.netty.channel.local.LocalChannel; -import io.netty.channel.local.LocalHandler; -import io.netty.channel.local.LocalServerChannel; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; -import io.netty.util.concurrent.Future; - -import java.io.BufferedReader; -import java.io.InputStreamReader; - -public final class LocalEcho { - - static final String PORT = System.getProperty("port", "test_port"); - - public static void main(String[] args) throws Exception { - // Address to bind on / connect to. - final LocalAddress addr = new LocalAddress(PORT); - - EventLoopGroup serverGroup = new MultithreadEventLoopGroup(LocalHandler.newFactory()); - EventLoopGroup clientGroup = new MultithreadEventLoopGroup(LocalHandler.newFactory()); - try { - // Note that we can use any event loop to ensure certain local channels - // are handled by the same event loop thread which drives a certain socket channel - // to reduce the communication latency between socket channels and local channels. - ServerBootstrap sb = new ServerBootstrap(); - sb.group(serverGroup) - .channel(LocalServerChannel.class) - .handler(new ChannelInitializer() { - @Override - public void initChannel(LocalServerChannel ch) throws Exception { - ch.pipeline().addLast(new LoggingHandler(LogLevel.INFO)); - } - }) - .childHandler(new ChannelInitializer() { - @Override - public void initChannel(LocalChannel ch) throws Exception { - ch.pipeline().addLast( - new LoggingHandler(LogLevel.INFO), - new LocalEchoServerHandler()); - } - }); - - Bootstrap cb = new Bootstrap(); - cb.group(clientGroup) - .channel(LocalChannel.class) - .handler(new ChannelInitializer() { - @Override - public void initChannel(LocalChannel ch) throws Exception { - ch.pipeline().addLast( - new LoggingHandler(LogLevel.INFO), - new LocalEchoClientHandler()); - } - }); - - // Start the server. - sb.bind(addr).sync(); - - // Start the client. - Channel ch = cb.connect(addr).get(); - - // Read commands from the stdin. - System.out.println("Enter text (quit to end)"); - Future lastWriteFuture = null; - BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); - for (;;) { - String line = in.readLine(); - if (line == null || "quit".equalsIgnoreCase(line)) { - break; - } - - // Sends the received line to the server. - lastWriteFuture = ch.writeAndFlush(line); - } - - // Wait until all messages are flushed before closing the channel. - if (lastWriteFuture != null) { - lastWriteFuture.awaitUninterruptibly(); - } - } finally { - serverGroup.shutdownGracefully(); - clientGroup.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/localecho/LocalEchoClientHandler.java b/example/src/main/java/io/netty/example/localecho/LocalEchoClientHandler.java deleted file mode 100644 index 566fb1be75..0000000000 --- a/example/src/main/java/io/netty/example/localecho/LocalEchoClientHandler.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.localecho; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; - -public class LocalEchoClientHandler extends SimpleChannelInboundHandler { - - @Override - public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { - // Print as received - System.out.println(msg); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - cause.printStackTrace(); - ctx.close(); - } -} diff --git a/example/src/main/java/io/netty/example/localecho/LocalEchoServerHandler.java b/example/src/main/java/io/netty/example/localecho/LocalEchoServerHandler.java deleted file mode 100644 index bc4c01f50c..0000000000 --- a/example/src/main/java/io/netty/example/localecho/LocalEchoServerHandler.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.localecho; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; - -public class LocalEchoServerHandler implements ChannelHandler { - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - // Write back as received - ctx.write(msg); - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) { - ctx.flush(); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - cause.printStackTrace(); - ctx.close(); - } -} diff --git a/example/src/main/java/io/netty/example/memcache/binary/MemcacheClient.java b/example/src/main/java/io/netty/example/memcache/binary/MemcacheClient.java deleted file mode 100644 index 58462ac663..0000000000 --- a/example/src/main/java/io/netty/example/memcache/binary/MemcacheClient.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.memcache.binary; - -import io.netty.bootstrap.Bootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.codec.memcache.binary.BinaryMemcacheClientCodec; -import io.netty.handler.codec.memcache.binary.BinaryMemcacheObjectAggregator; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.util.InsecureTrustManagerFactory; -import io.netty.util.concurrent.Future; - -import java.io.BufferedReader; -import java.io.InputStreamReader; - -/** - * Simple memcache client that demonstrates get and set commands against a memcache server. - */ -public final class MemcacheClient { - - static final boolean SSL = System.getProperty("ssl") != null; - static final String HOST = System.getProperty("host", "127.0.0.1"); - static final int PORT = Integer.parseInt(System.getProperty("port", "11211")); - - public static void main(String[] args) throws Exception { - // Configure SSL. - final SslContext sslCtx; - if (SSL) { - sslCtx = SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE).build(); - } else { - sslCtx = null; - } - - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - Bootstrap b = new Bootstrap(); - b.group(group) - .channel(NioSocketChannel.class) - .handler(new ChannelInitializer() { - @Override - protected void initChannel(SocketChannel ch) throws Exception { - ChannelPipeline p = ch.pipeline(); - if (sslCtx != null) { - p.addLast(sslCtx.newHandler(ch.alloc(), HOST, PORT)); - } - p.addLast(new BinaryMemcacheClientCodec()); - p.addLast(new BinaryMemcacheObjectAggregator(Integer.MAX_VALUE)); - p.addLast(new MemcacheClientHandler()); - } - }); - - // Start the connection attempt. - Channel ch = b.connect(HOST, PORT).get(); - - // Read commands from the stdin. - System.out.println("Enter commands (quit to end)"); - System.out.println("get "); - System.out.println("set "); - Future lastWriteFuture = null; - BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); - for (;;) { - String line = in.readLine(); - if (line == null) { - break; - } - if ("quit".equalsIgnoreCase(line)) { - ch.close().sync(); - break; - } - // Sends the received line to the server. - lastWriteFuture = ch.writeAndFlush(line); - } - - // Wait until all messages are flushed before closing the channel. - if (lastWriteFuture != null) { - lastWriteFuture.sync(); - } - } finally { - group.shutdownGracefully(); - } - } -} 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 deleted file mode 100644 index af4d1bf0e6..0000000000 --- a/example/src/main/java/io/netty/example/memcache/binary/MemcacheClientHandler.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.memcache.binary; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.memcache.binary.BinaryMemcacheOpcodes; -import io.netty.handler.codec.memcache.binary.BinaryMemcacheRequest; -import io.netty.handler.codec.memcache.binary.DefaultBinaryMemcacheRequest; -import io.netty.handler.codec.memcache.binary.DefaultFullBinaryMemcacheRequest; -import io.netty.handler.codec.memcache.binary.FullBinaryMemcacheResponse; -import io.netty.util.CharsetUtil; -import io.netty.util.concurrent.Future; -import io.netty.util.ReferenceCountUtil; - -public class MemcacheClientHandler implements ChannelHandler { - - /** - * Transforms basic string requests to binary memcache requests - */ - @Override - public Future write(ChannelHandlerContext ctx, Object msg) { - String command = (String) msg; - if (command.startsWith("get ")) { - String keyString = command.substring("get ".length()); - ByteBuf key = Unpooled.wrappedBuffer(keyString.getBytes(CharsetUtil.UTF_8)); - - BinaryMemcacheRequest req = new DefaultBinaryMemcacheRequest(key); - req.setOpcode(BinaryMemcacheOpcodes.GET); - - return ctx.write(req); - } - if (command.startsWith("set ")) { - String[] parts = command.split(" ", 3); - if (parts.length < 3) { - throw new IllegalArgumentException("Malformed Command: " + command); - } - String keyString = parts[1]; - String value = parts[2]; - - ByteBuf key = Unpooled.wrappedBuffer(keyString.getBytes(CharsetUtil.UTF_8)); - ByteBuf content = Unpooled.wrappedBuffer(value.getBytes(CharsetUtil.UTF_8)); - ByteBuf extras = ctx.alloc().buffer(8); - extras.writeZero(8); - - BinaryMemcacheRequest req = new DefaultFullBinaryMemcacheRequest(key, extras, content); - req.setOpcode(BinaryMemcacheOpcodes.SET); - - return ctx.write(req); - } else { - IllegalStateException ex = new IllegalStateException("Unknown Message: " + msg); - ReferenceCountUtil.release(msg); - return ctx.newFailedFuture(ex); - } - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - FullBinaryMemcacheResponse res = (FullBinaryMemcacheResponse) msg; - System.out.println(res.content().toString(CharsetUtil.UTF_8)); - res.release(); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - cause.printStackTrace(); - ctx.close(); - } -} diff --git a/example/src/main/java/io/netty/example/mqtt/heartBeat/MqttHeartBeatBroker.java b/example/src/main/java/io/netty/example/mqtt/heartBeat/MqttHeartBeatBroker.java deleted file mode 100644 index 49003fea99..0000000000 --- a/example/src/main/java/io/netty/example/mqtt/heartBeat/MqttHeartBeatBroker.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.mqtt.heartBeat; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelOption; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.codec.mqtt.MqttDecoder; -import io.netty.handler.codec.mqtt.MqttEncoder; -import io.netty.handler.timeout.IdleStateHandler; - -import java.util.concurrent.TimeUnit; - -public final class MqttHeartBeatBroker { - - private MqttHeartBeatBroker() { - } - - public static void main(String[] args) throws Exception { - EventLoopGroup bossGroup = new MultithreadEventLoopGroup(1, NioHandler.newFactory()); - EventLoopGroup workerGroup = new MultithreadEventLoopGroup(NioHandler.newFactory()); - - try { - ServerBootstrap b = new ServerBootstrap(); - b.group(bossGroup, workerGroup); - b.option(ChannelOption.SO_BACKLOG, 1024); - b.channel(NioServerSocketChannel.class); - b.childHandler(new ChannelInitializer() { - @Override - protected void initChannel(SocketChannel ch) throws Exception { - ch.pipeline().addLast("encoder", MqttEncoder.INSTANCE); - ch.pipeline().addLast("decoder", new MqttDecoder()); - ch.pipeline().addLast("heartBeatHandler", new IdleStateHandler(45, 0, 0, TimeUnit.SECONDS)); - ch.pipeline().addLast("handler", MqttHeartBeatBrokerHandler.INSTANCE); - } - }); - - Channel channel = b.bind(1883).get(); - System.out.println("Broker initiated..."); - - channel.closeFuture().sync(); - } finally { - workerGroup.shutdownGracefully(); - bossGroup.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/mqtt/heartBeat/MqttHeartBeatBrokerHandler.java b/example/src/main/java/io/netty/example/mqtt/heartBeat/MqttHeartBeatBrokerHandler.java deleted file mode 100644 index 096b76d56a..0000000000 --- a/example/src/main/java/io/netty/example/mqtt/heartBeat/MqttHeartBeatBrokerHandler.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.mqtt.heartBeat; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.mqtt.MqttConnAckMessage; -import io.netty.handler.codec.mqtt.MqttConnAckVariableHeader; -import io.netty.handler.codec.mqtt.MqttConnectReturnCode; -import io.netty.handler.codec.mqtt.MqttFixedHeader; -import io.netty.handler.codec.mqtt.MqttMessage; -import io.netty.handler.codec.mqtt.MqttMessageType; -import io.netty.handler.codec.mqtt.MqttQoS; -import io.netty.handler.timeout.IdleState; -import io.netty.handler.timeout.IdleStateEvent; -import io.netty.util.ReferenceCountUtil; - -@Sharable -public final class MqttHeartBeatBrokerHandler implements ChannelHandler { - - public static final MqttHeartBeatBrokerHandler INSTANCE = new MqttHeartBeatBrokerHandler(); - - private MqttHeartBeatBrokerHandler() { - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - MqttMessage mqttMessage = (MqttMessage) msg; - System.out.println("Received MQTT message: " + mqttMessage); - switch (mqttMessage.fixedHeader().messageType()) { - case CONNECT: - MqttFixedHeader connackFixedHeader = - new MqttFixedHeader(MqttMessageType.CONNACK, false, MqttQoS.AT_MOST_ONCE, false, 0); - MqttConnAckVariableHeader mqttConnAckVariableHeader = - new MqttConnAckVariableHeader(MqttConnectReturnCode.CONNECTION_ACCEPTED, false); - MqttConnAckMessage connack = new MqttConnAckMessage(connackFixedHeader, mqttConnAckVariableHeader); - ctx.writeAndFlush(connack); - break; - case PINGREQ: - MqttFixedHeader pingreqFixedHeader = new MqttFixedHeader(MqttMessageType.PINGRESP, false, - MqttQoS.AT_MOST_ONCE, false, 0); - MqttMessage pingResp = new MqttMessage(pingreqFixedHeader); - ctx.writeAndFlush(pingResp); - break; - case DISCONNECT: - ctx.close(); - break; - default: - System.out.println("Unexpected message type: " + mqttMessage.fixedHeader().messageType()); - ReferenceCountUtil.release(msg); - ctx.close(); - } - } - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { - System.out.println("Channel heartBeat lost"); - if (evt instanceof IdleStateEvent && IdleState.READER_IDLE == ((IdleStateEvent) evt).state()) { - ctx.close(); - } - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - cause.printStackTrace(); - ctx.close(); - } -} diff --git a/example/src/main/java/io/netty/example/mqtt/heartBeat/MqttHeartBeatClient.java b/example/src/main/java/io/netty/example/mqtt/heartBeat/MqttHeartBeatClient.java deleted file mode 100644 index 58979f12c7..0000000000 --- a/example/src/main/java/io/netty/example/mqtt/heartBeat/MqttHeartBeatClient.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.mqtt.heartBeat; - -import io.netty.bootstrap.Bootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.codec.mqtt.MqttDecoder; -import io.netty.handler.codec.mqtt.MqttEncoder; -import io.netty.handler.timeout.IdleStateHandler; - -import java.util.concurrent.TimeUnit; - -public final class MqttHeartBeatClient { - private MqttHeartBeatClient() { - } - - private static final String HOST = System.getProperty("host", "127.0.0.1"); - private static final int PORT = Integer.parseInt(System.getProperty("port", "1883")); - private static final String CLIENT_ID = System.getProperty("clientId", "guestClient"); - private static final String USER_NAME = System.getProperty("userName", "guest"); - private static final String PASSWORD = System.getProperty("password", "guest"); - - public static void main(String[] args) throws Exception { - EventLoopGroup workerGroup = new MultithreadEventLoopGroup(NioHandler.newFactory()); - - try { - Bootstrap b = new Bootstrap(); - b.group(workerGroup); - b.channel(NioSocketChannel.class); - b.handler(new ChannelInitializer() { - @Override - protected void initChannel(SocketChannel ch) throws Exception { - ch.pipeline().addLast("encoder", MqttEncoder.INSTANCE); - ch.pipeline().addLast("decoder", new MqttDecoder()); - ch.pipeline().addLast("heartBeatHandler", new IdleStateHandler(0, 20, 0, TimeUnit.SECONDS)); - ch.pipeline().addLast("handler", new MqttHeartBeatClientHandler(CLIENT_ID, USER_NAME, PASSWORD)); - } - }); - - Channel channel = b.connect(HOST, PORT).get(); - System.out.println("Client connected"); - channel.closeFuture().sync(); - } finally { - workerGroup.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/mqtt/heartBeat/MqttHeartBeatClientHandler.java b/example/src/main/java/io/netty/example/mqtt/heartBeat/MqttHeartBeatClientHandler.java deleted file mode 100644 index cee0895ec6..0000000000 --- a/example/src/main/java/io/netty/example/mqtt/heartBeat/MqttHeartBeatClientHandler.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.mqtt.heartBeat; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.mqtt.MqttConnectMessage; -import io.netty.handler.codec.mqtt.MqttConnectPayload; -import io.netty.handler.codec.mqtt.MqttConnectVariableHeader; -import io.netty.handler.codec.mqtt.MqttFixedHeader; -import io.netty.handler.codec.mqtt.MqttMessage; -import io.netty.handler.codec.mqtt.MqttMessageType; -import io.netty.handler.codec.mqtt.MqttQoS; -import io.netty.handler.codec.mqtt.MqttProperties; -import io.netty.handler.timeout.IdleStateEvent; -import io.netty.util.ReferenceCountUtil; - -public class MqttHeartBeatClientHandler implements ChannelHandler { - - private static final String PROTOCOL_NAME_MQTT_3_1_1 = "MQTT"; - private static final int PROTOCOL_VERSION_MQTT_3_1_1 = 4; - - private final String clientId; - private final String userName; - private final byte[] password; - - public MqttHeartBeatClientHandler(String clientId, String userName, String password) { - this.clientId = clientId; - this.userName = userName; - this.password = password.getBytes(); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - // discard all messages - ReferenceCountUtil.release(msg); - } - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - MqttFixedHeader connectFixedHeader = - new MqttFixedHeader(MqttMessageType.CONNECT, false, MqttQoS.AT_MOST_ONCE, false, 0); - MqttConnectVariableHeader connectVariableHeader = - new MqttConnectVariableHeader(PROTOCOL_NAME_MQTT_3_1_1, PROTOCOL_VERSION_MQTT_3_1_1, true, true, false, - 0, false, false, 20, MqttProperties.NO_PROPERTIES); - MqttConnectPayload connectPayload = new MqttConnectPayload(clientId, - MqttProperties.NO_PROPERTIES, - null, - null, - userName, - password); - MqttConnectMessage connectMessage = - new MqttConnectMessage(connectFixedHeader, connectVariableHeader, connectPayload); - ctx.writeAndFlush(connectMessage); - System.out.println("Sent CONNECT"); - } - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (evt instanceof IdleStateEvent) { - MqttFixedHeader pingreqFixedHeader = - new MqttFixedHeader(MqttMessageType.PINGREQ, false, MqttQoS.AT_MOST_ONCE, false, 0); - MqttMessage pingreqMessage = new MqttMessage(pingreqFixedHeader); - ctx.writeAndFlush(pingreqMessage); - System.out.println("Sent PINGREQ"); - } else { - ctx.fireUserEventTriggered(evt); - } - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - cause.printStackTrace(); - ctx.close(); - } -} diff --git a/example/src/main/java/io/netty/example/objectecho/ObjectEchoClient.java b/example/src/main/java/io/netty/example/objectecho/ObjectEchoClient.java deleted file mode 100644 index dea12fcfa9..0000000000 --- a/example/src/main/java/io/netty/example/objectecho/ObjectEchoClient.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.objectecho; - -import io.netty.bootstrap.Bootstrap; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.example.echo.EchoClient; -import io.netty.handler.codec.serialization.ClassResolvers; -import io.netty.handler.codec.serialization.ObjectDecoder; -import io.netty.handler.codec.serialization.ObjectEncoder; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.util.InsecureTrustManagerFactory; - -/** - * Modification of {@link EchoClient} which utilizes Java object serialization. - */ -public final class ObjectEchoClient { - - static final boolean SSL = System.getProperty("ssl") != null; - static final String HOST = System.getProperty("host", "127.0.0.1"); - static final int PORT = Integer.parseInt(System.getProperty("port", "8007")); - static final int SIZE = Integer.parseInt(System.getProperty("size", "256")); - - public static void main(String[] args) throws Exception { - // Configure SSL. - final SslContext sslCtx; - if (SSL) { - sslCtx = SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE).build(); - } else { - sslCtx = null; - } - - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - Bootstrap b = new Bootstrap(); - b.group(group) - .channel(NioSocketChannel.class) - .handler(new ChannelInitializer() { - @Override - public void initChannel(SocketChannel ch) throws Exception { - ChannelPipeline p = ch.pipeline(); - if (sslCtx != null) { - p.addLast(sslCtx.newHandler(ch.alloc(), HOST, PORT)); - } - p.addLast( - new ObjectEncoder(), - new ObjectDecoder(ClassResolvers.cacheDisabled(null)), - new ObjectEchoClientHandler()); - } - }); - - // Start the connection attempt. - b.connect(HOST, PORT).get().closeFuture().sync(); - } finally { - group.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/objectecho/ObjectEchoClientHandler.java b/example/src/main/java/io/netty/example/objectecho/ObjectEchoClientHandler.java deleted file mode 100644 index 6c19093dc0..0000000000 --- a/example/src/main/java/io/netty/example/objectecho/ObjectEchoClientHandler.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.objectecho; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.concurrent.Future; - -import java.util.ArrayList; -import java.util.List; - -import static io.netty.channel.ChannelFutureListeners.FIRE_EXCEPTION_ON_FAILURE; - -/** - * Handler implementation for the object echo client. It initiates the - * ping-pong traffic between the object echo client and server by sending the - * first message to the server. - */ -public class ObjectEchoClientHandler implements ChannelHandler { - - private final List firstMessage; - - /** - * Creates a client-side handler. - */ - public ObjectEchoClientHandler() { - firstMessage = new ArrayList<>(ObjectEchoClient.SIZE); - for (int i = 0; i < ObjectEchoClient.SIZE; i ++) { - firstMessage.add(Integer.valueOf(i)); - } - } - - @Override - public void channelActive(ChannelHandlerContext ctx) { - // Send the first message if this handler is a client-side handler. - Future future = ctx.writeAndFlush(firstMessage); - future.addListener(ctx.channel(), FIRE_EXCEPTION_ON_FAILURE); // Let object serialisation exceptions propagate. - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - // Echo back the received object to the server. - ctx.write(msg); - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) { - ctx.flush(); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - cause.printStackTrace(); - ctx.close(); - } -} diff --git a/example/src/main/java/io/netty/example/objectecho/ObjectEchoServer.java b/example/src/main/java/io/netty/example/objectecho/ObjectEchoServer.java deleted file mode 100644 index 883ce7272b..0000000000 --- a/example/src/main/java/io/netty/example/objectecho/ObjectEchoServer.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.objectecho; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.example.echo.EchoServer; -import io.netty.handler.codec.serialization.ClassResolvers; -import io.netty.handler.codec.serialization.ObjectDecoder; -import io.netty.handler.codec.serialization.ObjectEncoder; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.util.SelfSignedCertificate; - -/** - * Modification of {@link EchoServer} which utilizes Java object serialization. - */ -public final class ObjectEchoServer { - - static final boolean SSL = System.getProperty("ssl") != null; - static final int PORT = Integer.parseInt(System.getProperty("port", "8007")); - - public static void main(String[] args) throws Exception { - // Configure SSL. - final SslContext sslCtx; - if (SSL) { - SelfSignedCertificate ssc = new SelfSignedCertificate(); - sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build(); - } else { - sslCtx = null; - } - - EventLoopGroup bossGroup = new MultithreadEventLoopGroup(1, NioHandler.newFactory()); - EventLoopGroup workerGroup = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - ServerBootstrap b = new ServerBootstrap(); - b.group(bossGroup, workerGroup) - .channel(NioServerSocketChannel.class) - .handler(new LoggingHandler(LogLevel.INFO)) - .childHandler(new ChannelInitializer() { - @Override - public void initChannel(SocketChannel ch) throws Exception { - ChannelPipeline p = ch.pipeline(); - if (sslCtx != null) { - p.addLast(sslCtx.newHandler(ch.alloc())); - } - p.addLast( - new ObjectEncoder(), - new ObjectDecoder(ClassResolvers.cacheDisabled(null)), - new ObjectEchoServerHandler()); - } - }); - - // Bind and start to accept incoming connections. - b.bind(PORT).get().closeFuture().sync(); - } finally { - bossGroup.shutdownGracefully(); - workerGroup.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/objectecho/ObjectEchoServerHandler.java b/example/src/main/java/io/netty/example/objectecho/ObjectEchoServerHandler.java deleted file mode 100644 index e58ea7aaec..0000000000 --- a/example/src/main/java/io/netty/example/objectecho/ObjectEchoServerHandler.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.objectecho; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; - -/** - * Handles both client-side and server-side handler depending on which - * constructor was called. - */ -public class ObjectEchoServerHandler implements ChannelHandler { - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - // Echo back the received object to the client. - ctx.write(msg); - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) { - ctx.flush(); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - cause.printStackTrace(); - ctx.close(); - } -} diff --git a/example/src/main/java/io/netty/example/ocsp/Digester.java b/example/src/main/java/io/netty/example/ocsp/Digester.java deleted file mode 100644 index 31db49b393..0000000000 --- a/example/src/main/java/io/netty/example/ocsp/Digester.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.example.ocsp; - -import java.io.OutputStream; - -import org.bouncycastle.asn1.ASN1ObjectIdentifier; -import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers; -import org.bouncycastle.asn1.x509.AlgorithmIdentifier; -import org.bouncycastle.cert.ocsp.OCSPReqBuilder; -import org.bouncycastle.crypto.Digest; -import org.bouncycastle.crypto.digests.SHA1Digest; -import org.bouncycastle.crypto.digests.SHA256Digest; -import org.bouncycastle.crypto.io.DigestOutputStream; -import org.bouncycastle.operator.DigestCalculator; - -/** - * BC's {@link OCSPReqBuilder} needs a {@link DigestCalculator} but BC doesn't - * provide any public implementations of that interface. That's why we need to - * write our own. There's a default SHA-1 implementation and one for SHA-256. - * Which one to use will depend on the Certificate Authority (CA). - */ -public final class Digester implements DigestCalculator { - - public static DigestCalculator sha1() { - Digest digest = new SHA1Digest(); - AlgorithmIdentifier algId = new AlgorithmIdentifier( - OIWObjectIdentifiers.idSHA1); - - return new Digester(digest, algId); - } - - public static DigestCalculator sha256() { - Digest digest = new SHA256Digest(); - - // The OID for SHA-256: http://www.oid-info.com/get/2.16.840.1.101.3.4.2.1 - ASN1ObjectIdentifier oid = new ASN1ObjectIdentifier( - "2.16.840.1.101.3.4.2.1").intern(); - AlgorithmIdentifier algId = new AlgorithmIdentifier(oid); - - return new Digester(digest, algId); - } - - private final DigestOutputStream dos; - - private final AlgorithmIdentifier algId; - - private Digester(Digest digest, AlgorithmIdentifier algId) { - this.dos = new DigestOutputStream(digest); - this.algId = algId; - } - - @Override - public AlgorithmIdentifier getAlgorithmIdentifier() { - return algId; - } - - @Override - public OutputStream getOutputStream() { - return dos; - } - - @Override - public byte[] getDigest() { - return dos.getDigest(); - } -} diff --git a/example/src/main/java/io/netty/example/ocsp/OcspClientExample.java b/example/src/main/java/io/netty/example/ocsp/OcspClientExample.java deleted file mode 100644 index 9f7063fbc7..0000000000 --- a/example/src/main/java/io/netty/example/ocsp/OcspClientExample.java +++ /dev/null @@ -1,245 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.example.ocsp; - -import io.netty.bootstrap.Bootstrap; -import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.netty.channel.ChannelFutureListeners; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelOption; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -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.HttpMethod; -import io.netty.handler.codec.http.HttpObjectAggregator; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.ssl.OpenSsl; -import io.netty.handler.ssl.ReferenceCountedOpenSslContext; -import io.netty.handler.ssl.ReferenceCountedOpenSslEngine; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.SslHandler; -import io.netty.handler.ssl.SslProvider; -import io.netty.handler.ssl.ocsp.OcspClientHandler; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.concurrent.Promise; -import org.bouncycastle.asn1.ocsp.OCSPResponseStatus; -import org.bouncycastle.cert.ocsp.BasicOCSPResp; -import org.bouncycastle.cert.ocsp.CertificateStatus; -import org.bouncycastle.cert.ocsp.OCSPResp; -import org.bouncycastle.cert.ocsp.SingleResp; - -import javax.net.ssl.SSLSession; -import javax.security.cert.X509Certificate; -import java.math.BigInteger; - -/** - * This is a very simple example for an HTTPS client that uses OCSP stapling. - * The client connects to an HTTPS server that has OCSP stapling enabled and - * then uses BC to parse and validate it. - */ -public class OcspClientExample { - public static void main(String[] args) throws Exception { - if (!OpenSsl.isAvailable()) { - throw new IllegalStateException("OpenSSL is not available!"); - } - - if (!OpenSsl.isOcspSupported()) { - throw new IllegalStateException("OCSP is not supported!"); - } - - // Using Wikipedia as an example. I'd rather use Netty's own website - // but the server (Cloudflare) doesn't support OCSP stapling. A few - // other examples could be Microsoft or Squarespace. Use OpenSSL's - // CLI client to assess if a server supports OCSP stapling. E.g.: - // - // openssl s_client -tlsextdebug -status -connect www.squarespace.com:443 - // - String host = "www.wikipedia.org"; - - ReferenceCountedOpenSslContext context - = (ReferenceCountedOpenSslContext) SslContextBuilder.forClient() - .sslProvider(SslProvider.OPENSSL) - .enableOcsp(true) - .build(); - - try { - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - Promise promise = group.next().newPromise(); - - Bootstrap bootstrap = new Bootstrap() - .channel(NioSocketChannel.class) - .group(group) - .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5 * 1000) - .handler(newClientHandler(context, host, promise)); - - Channel channel = bootstrap.connect(host, 443).get(); - - try { - FullHttpResponse response = promise.asFuture().get(); - ReferenceCountUtil.release(response); - } finally { - channel.close(); - } - } finally { - group.shutdownGracefully(); - } - } finally { - context.release(); - } - } - - private static ChannelInitializer newClientHandler(final ReferenceCountedOpenSslContext context, - final String host, final Promise promise) { - - return new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - SslHandler sslHandler = context.newHandler(ch.alloc()); - ReferenceCountedOpenSslEngine engine - = (ReferenceCountedOpenSslEngine) sslHandler.engine(); - - ChannelPipeline pipeline = ch.pipeline(); - pipeline.addLast(sslHandler); - pipeline.addLast(new ExampleOcspClientHandler(engine)); - - pipeline.addLast(new HttpClientCodec()); - pipeline.addLast(new HttpObjectAggregator(1024 * 1024)); - pipeline.addLast(new HttpClientHandler(host, promise)); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - if (!promise.isDone()) { - promise.tryFailure(new IllegalStateException("Connection closed and Promise was not done.")); - } - ctx.fireChannelInactive(); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - if (!promise.tryFailure(cause)) { - ctx.fireExceptionCaught(cause); - } - } - }; - } - - private static class HttpClientHandler implements ChannelHandler { - - private final String host; - - private final Promise promise; - - HttpClientHandler(String host, Promise promise) { - this.host = host; - this.promise = promise; - } - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - FullHttpRequest request = new DefaultFullHttpRequest( - HttpVersion.HTTP_1_1, HttpMethod.GET, "/", Unpooled.EMPTY_BUFFER); - request.headers().set(HttpHeaderNames.HOST, host); - request.headers().set(HttpHeaderNames.USER_AGENT, "netty-ocsp-example/1.0"); - - ctx.writeAndFlush(request).addListener(ctx.channel(), ChannelFutureListeners.FIRE_EXCEPTION_ON_FAILURE); - - ctx.fireChannelActive(); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - if (!promise.isDone()) { - promise.tryFailure(new IllegalStateException("Connection closed and Promise was not done.")); - } - ctx.fireChannelInactive(); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - if (msg instanceof FullHttpResponse) { - if (!promise.trySuccess((FullHttpResponse) msg)) { - ReferenceCountUtil.release(msg); - } - return; - } - - ctx.fireChannelRead(msg); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - if (!promise.tryFailure(cause)) { - ctx.fireExceptionCaught(cause); - } - } - } - - private static class ExampleOcspClientHandler extends OcspClientHandler { - - ExampleOcspClientHandler(ReferenceCountedOpenSslEngine engine) { - super(engine); - } - - @Override - protected boolean verify(ChannelHandlerContext ctx, ReferenceCountedOpenSslEngine engine) throws Exception { - byte[] staple = engine.getOcspResponse(); - if (staple == null) { - throw new IllegalStateException("Server didn't provide an OCSP staple!"); - } - - OCSPResp response = new OCSPResp(staple); - if (response.getStatus() != OCSPResponseStatus.SUCCESSFUL) { - return false; - } - - SSLSession session = engine.getSession(); - X509Certificate[] chain = session.getPeerCertificateChain(); - BigInteger certSerial = chain[0].getSerialNumber(); - - BasicOCSPResp basicResponse = (BasicOCSPResp) response.getResponseObject(); - SingleResp first = basicResponse.getResponses()[0]; - - // ATTENTION: CertificateStatus.GOOD is actually a null value! Do not use - // equals() or you'll NPE! - CertificateStatus status = first.getCertStatus(); - BigInteger ocspSerial = first.getCertID().getSerialNumber(); - String message = new StringBuilder() - .append("OCSP status of ").append(ctx.channel().remoteAddress()) - .append("\n Status: ").append(status == CertificateStatus.GOOD ? "Good" : status) - .append("\n This Update: ").append(first.getThisUpdate()) - .append("\n Next Update: ").append(first.getNextUpdate()) - .append("\n Cert Serial: ").append(certSerial) - .append("\n OCSP Serial: ").append(ocspSerial) - .toString(); - System.out.println(message); - - return status == CertificateStatus.GOOD && certSerial.equals(ocspSerial); - } - } -} diff --git a/example/src/main/java/io/netty/example/ocsp/OcspRequestBuilder.java b/example/src/main/java/io/netty/example/ocsp/OcspRequestBuilder.java deleted file mode 100644 index a5f91b76af..0000000000 --- a/example/src/main/java/io/netty/example/ocsp/OcspRequestBuilder.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.example.ocsp; - -import static java.util.Objects.requireNonNull; - -import java.io.IOException; -import java.math.BigInteger; -import java.security.SecureRandom; -import java.security.cert.CertificateEncodingException; -import java.security.cert.X509Certificate; - -import org.bouncycastle.asn1.DEROctetString; -import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers; -import org.bouncycastle.asn1.x509.Extension; -import org.bouncycastle.asn1.x509.Extensions; -import org.bouncycastle.cert.X509CertificateHolder; -import org.bouncycastle.cert.ocsp.CertificateID; -import org.bouncycastle.cert.ocsp.OCSPException; -import org.bouncycastle.cert.ocsp.OCSPReq; -import org.bouncycastle.cert.ocsp.OCSPReqBuilder; -import org.bouncycastle.operator.DigestCalculator; - -/** - * This is a simplified version of BC's own {@link OCSPReqBuilder}. - * - * @see OCSPReqBuilder - */ -public class OcspRequestBuilder { - - private static final SecureRandom GENERATOR = new SecureRandom(); - - private SecureRandom generator = GENERATOR; - - private DigestCalculator calculator = Digester.sha1(); - - private X509Certificate certificate; - - private X509Certificate issuer; - - public OcspRequestBuilder generator(SecureRandom generator) { - this.generator = generator; - return this; - } - - public OcspRequestBuilder calculator(DigestCalculator calculator) { - this.calculator = calculator; - return this; - } - - public OcspRequestBuilder certificate(X509Certificate certificate) { - this.certificate = certificate; - return this; - } - - public OcspRequestBuilder issuer(X509Certificate issuer) { - this.issuer = issuer; - return this; - } - - /** - * ATTENTION: The returned {@link OCSPReq} is not re-usable/cacheable! It contains a one-time nonce - * and CA's will (should) reject subsequent requests that have the same nonce value. - */ - public OCSPReq build() throws OCSPException, IOException, CertificateEncodingException { - SecureRandom generator = requireNonNull(this.generator, "generator"); - DigestCalculator calculator = requireNonNull(this.calculator, "calculator"); - X509Certificate certificate = requireNonNull(this.certificate, "certificate"); - X509Certificate issuer = requireNonNull(this.issuer, "issuer"); - - BigInteger serial = certificate.getSerialNumber(); - - CertificateID certId = new CertificateID(calculator, - new X509CertificateHolder(issuer.getEncoded()), serial); - - OCSPReqBuilder builder = new OCSPReqBuilder(); - builder.addRequest(certId); - - byte[] nonce = new byte[8]; - generator.nextBytes(nonce); - - Extension[] extensions = new Extension[] { - new Extension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce, false, - new DEROctetString(nonce)) }; - - builder.setRequestExtensions(new Extensions(extensions)); - - return builder.build(); - } -} diff --git a/example/src/main/java/io/netty/example/ocsp/OcspServerExample.java b/example/src/main/java/io/netty/example/ocsp/OcspServerExample.java deleted file mode 100644 index f1687f72e1..0000000000 --- a/example/src/main/java/io/netty/example/ocsp/OcspServerExample.java +++ /dev/null @@ -1,206 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.example.ocsp; - -import java.io.BufferedReader; -import java.io.FileNotFoundException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.math.BigInteger; -import java.net.URI; -import java.security.PrivateKey; -import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import org.bouncycastle.asn1.ocsp.OCSPResponseStatus; -import org.bouncycastle.cert.X509CertificateHolder; -import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; -import org.bouncycastle.cert.ocsp.BasicOCSPResp; -import org.bouncycastle.cert.ocsp.CertificateStatus; -import org.bouncycastle.cert.ocsp.OCSPReq; -import org.bouncycastle.cert.ocsp.OCSPResp; -import org.bouncycastle.cert.ocsp.SingleResp; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.openssl.PEMParser; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.handler.ssl.OpenSsl; -import io.netty.handler.ssl.ReferenceCountedOpenSslContext; -import io.netty.handler.ssl.ReferenceCountedOpenSslEngine; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.SslHandler; -import io.netty.handler.ssl.SslProvider; -import io.netty.util.CharsetUtil; - -/** - * ATTENTION: This is an incomplete example! In order to provide a fully functional - * end-to-end example we'd need an X.509 certificate and the matching PrivateKey. - */ -@SuppressWarnings("unused") -public class OcspServerExample { - public static void main(String[] args) throws Exception { - // We assume there's a private key. - PrivateKey privateKey = null; - - // Step 1: Load the certificate chain for netty.io. We'll need the certificate - // and the issuer's certificate and we don't need any of the intermediate certs. - // The array is assumed to be a certain order to keep things simple. - X509Certificate[] keyCertChain = parseCertificates(OcspServerExample.class, "netty_io_chain.pem"); - - X509Certificate certificate = keyCertChain[0]; - X509Certificate issuer = keyCertChain[keyCertChain.length - 1]; - - // Step 2: We need the URL of the CA's OCSP responder server. It's somewhere encoded - // into the certificate! Notice that it's an HTTP URL. - URI uri = OcspUtils.ocspUri(certificate); - System.out.println("OCSP Responder URI: " + uri); - - if (uri == null) { - throw new IllegalStateException("The CA/certificate doesn't have an OCSP responder"); - } - - // Step 3: Construct the OCSP request - OCSPReq request = new OcspRequestBuilder() - .certificate(certificate) - .issuer(issuer) - .build(); - - // Step 4: Do the request to the CA's OCSP responder - OCSPResp response = OcspUtils.request(uri, request, 5L, TimeUnit.SECONDS); - if (response.getStatus() != OCSPResponseStatus.SUCCESSFUL) { - throw new IllegalStateException("response-status=" + response.getStatus()); - } - - // Step 5: Is my certificate any good or has the CA revoked it? - BasicOCSPResp basicResponse = (BasicOCSPResp) response.getResponseObject(); - SingleResp first = basicResponse.getResponses()[0]; - - CertificateStatus status = first.getCertStatus(); - System.out.println("Status: " + (status == CertificateStatus.GOOD ? "Good" : status)); - System.out.println("This Update: " + first.getThisUpdate()); - System.out.println("Next Update: " + first.getNextUpdate()); - - if (status != null) { - throw new IllegalStateException("certificate-status=" + status); - } - - BigInteger certSerial = certificate.getSerialNumber(); - BigInteger ocspSerial = first.getCertID().getSerialNumber(); - if (!certSerial.equals(ocspSerial)) { - throw new IllegalStateException("Bad Serials=" + certSerial + " vs. " + ocspSerial); - } - - // Step 6: Cache the OCSP response and use it as long as it's not - // expired. The exact semantics are beyond the scope of this example. - - if (!OpenSsl.isAvailable()) { - throw new IllegalStateException("OpenSSL is not available!"); - } - - if (!OpenSsl.isOcspSupported()) { - throw new IllegalStateException("OCSP is not supported!"); - } - - if (privateKey == null) { - throw new IllegalStateException("Because we don't have a PrivateKey we can't continue past this point."); - } - - ReferenceCountedOpenSslContext context - = (ReferenceCountedOpenSslContext) SslContextBuilder.forServer(privateKey, keyCertChain) - .sslProvider(SslProvider.OPENSSL) - .enableOcsp(true) - .build(); - - try { - ServerBootstrap bootstrap = new ServerBootstrap() - .childHandler(newServerHandler(context, response)); - - // so on and so forth... - } finally { - context.release(); - } - } - - private static ChannelInitializer newServerHandler(final ReferenceCountedOpenSslContext context, - final OCSPResp response) { - return new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - SslHandler sslHandler = context.newHandler(ch.alloc()); - - if (response != null) { - ReferenceCountedOpenSslEngine engine - = (ReferenceCountedOpenSslEngine) sslHandler.engine(); - - engine.setOcspResponse(response.getEncoded()); - } - - ChannelPipeline pipeline = ch.pipeline(); - pipeline.addLast(sslHandler); - - // so on and so forth... - } - }; - } - - private static X509Certificate[] parseCertificates(Class clazz, String name) throws Exception { - InputStream in = clazz.getResourceAsStream(name); - if (in == null) { - throw new FileNotFoundException("clazz=" + clazz + ", name=" + name); - } - - try { - try (BufferedReader reader = new BufferedReader(new InputStreamReader(in, CharsetUtil.US_ASCII))) { - return parseCertificates(reader); - } - } finally { - in.close(); - } - } - - private static X509Certificate[] parseCertificates(Reader reader) throws Exception { - - JcaX509CertificateConverter converter = new JcaX509CertificateConverter() - .setProvider(new BouncyCastleProvider()); - - List dst = new ArrayList<>(); - - PEMParser parser = new PEMParser(reader); - try { - X509CertificateHolder holder = null; - - while ((holder = (X509CertificateHolder) parser.readObject()) != null) { - X509Certificate certificate = converter.getCertificate(holder); - if (certificate == null) { - continue; - } - - dst.add(certificate); - } - } finally { - parser.close(); - } - - return dst.toArray(new X509Certificate[0]); - } -} diff --git a/example/src/main/java/io/netty/example/ocsp/OcspUtils.java b/example/src/main/java/io/netty/example/ocsp/OcspUtils.java deleted file mode 100644 index 5134254abc..0000000000 --- a/example/src/main/java/io/netty/example/ocsp/OcspUtils.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.example.ocsp; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.HttpURLConnection; -import java.net.URI; -import java.net.URL; -import java.security.cert.X509Certificate; -import java.util.concurrent.TimeUnit; - -import javax.net.ssl.HttpsURLConnection; - -import org.bouncycastle.asn1.ASN1Encodable; -import org.bouncycastle.asn1.ASN1ObjectIdentifier; -import org.bouncycastle.asn1.ASN1Primitive; -import org.bouncycastle.asn1.BERTags; -import org.bouncycastle.asn1.DERTaggedObject; -import org.bouncycastle.asn1.DLSequence; -import org.bouncycastle.asn1.x509.Extension; -import org.bouncycastle.cert.ocsp.OCSPReq; -import org.bouncycastle.cert.ocsp.OCSPResp; -import org.bouncycastle.x509.extension.X509ExtensionUtil; - -import io.netty.util.CharsetUtil; - -public final class OcspUtils { - /** - * The OID for OCSP responder URLs. - * - * https://www.alvestrand.no/objectid/1.3.6.1.5.5.7.48.1.html - */ - private static final ASN1ObjectIdentifier OCSP_RESPONDER_OID - = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.48.1").intern(); - - private static final String OCSP_REQUEST_TYPE = "application/ocsp-request"; - - private static final String OCSP_RESPONSE_TYPE = "application/ocsp-response"; - - private OcspUtils() { - } - - /** - * Returns the OCSP responder {@link URI} or {@code null} if it doesn't have one. - */ - public static URI ocspUri(X509Certificate certificate) throws IOException { - byte[] value = certificate.getExtensionValue(Extension.authorityInfoAccess.getId()); - if (value == null) { - return null; - } - - ASN1Primitive authorityInfoAccess = X509ExtensionUtil.fromExtensionValue(value); - if (!(authorityInfoAccess instanceof DLSequence)) { - return null; - } - - DLSequence aiaSequence = (DLSequence) authorityInfoAccess; - DERTaggedObject taggedObject = findObject(aiaSequence, OCSP_RESPONDER_OID, DERTaggedObject.class); - if (taggedObject == null) { - return null; - } - - if (taggedObject.getTagNo() != BERTags.OBJECT_IDENTIFIER) { - return null; - } - - byte[] encoded = taggedObject.getEncoded(); - int length = (int) encoded[1] & 0xFF; - String uri = new String(encoded, 2, length, CharsetUtil.UTF_8); - return URI.create(uri); - } - - private static T findObject(DLSequence sequence, ASN1ObjectIdentifier oid, Class type) { - for (ASN1Encodable element : sequence) { - if (!(element instanceof DLSequence)) { - continue; - } - - DLSequence subSequence = (DLSequence) element; - if (subSequence.size() != 2) { - continue; - } - - ASN1Encodable key = subSequence.getObjectAt(0); - ASN1Encodable value = subSequence.getObjectAt(1); - - if (key.equals(oid) && type.isInstance(value)) { - return type.cast(value); - } - } - - return null; - } - - /** - * TODO: This is a very crude and non-scalable HTTP client to fetch the OCSP response from the - * CA's OCSP responder server. It's meant to demonstrate the basic building blocks on how to - * interact with the responder server and you should consider using Netty's HTTP client instead. - */ - public static OCSPResp request(URI uri, OCSPReq request, long timeout, TimeUnit unit) throws IOException { - byte[] encoded = request.getEncoded(); - - URL url = uri.toURL(); - HttpURLConnection connection = (HttpURLConnection) url.openConnection(); - try { - connection.setConnectTimeout((int) unit.toMillis(timeout)); - connection.setReadTimeout((int) unit.toMillis(timeout)); - connection.setDoOutput(true); - connection.setDoInput(true); - connection.setRequestMethod("POST"); - connection.setRequestProperty("host", uri.getHost()); - connection.setRequestProperty("content-type", OCSP_REQUEST_TYPE); - connection.setRequestProperty("accept", OCSP_RESPONSE_TYPE); - connection.setRequestProperty("content-length", String.valueOf(encoded.length)); - - try (OutputStream out = connection.getOutputStream()) { - out.write(encoded); - out.flush(); - - try (InputStream in = connection.getInputStream()) { - int code = connection.getResponseCode(); - if (code != HttpsURLConnection.HTTP_OK) { - throw new IOException("Unexpected status-code=" + code); - } - - String contentType = connection.getContentType(); - if (!contentType.equalsIgnoreCase(OCSP_RESPONSE_TYPE)) { - throw new IOException("Unexpected content-type=" + contentType); - } - - int contentLength = connection.getContentLength(); - if (contentLength == -1) { - // Probably a terrible idea! - contentLength = Integer.MAX_VALUE; - } - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try { - byte[] buffer = new byte[8192]; - int length = -1; - - while ((length = in.read(buffer)) != -1) { - baos.write(buffer, 0, length); - - if (baos.size() >= contentLength) { - break; - } - } - } finally { - baos.close(); - } - return new OCSPResp(baos.toByteArray()); - } - } - } finally { - connection.disconnect(); - } - } -} diff --git a/example/src/main/java/io/netty/example/portunification/PortUnificationServer.java b/example/src/main/java/io/netty/example/portunification/PortUnificationServer.java deleted file mode 100644 index d24bafe157..0000000000 --- a/example/src/main/java/io/netty/example/portunification/PortUnificationServer.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.portunification; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.util.SelfSignedCertificate; - -/** - * Serves two protocols (HTTP and Factorial) using only one port, enabling - * either SSL or GZIP dynamically on demand. - *

- * Because SSL and GZIP are enabled on demand, 5 combinations per protocol - * are possible: none, SSL only, GZIP only, SSL + GZIP, and GZIP + SSL. - */ -public final class PortUnificationServer { - - static final int PORT = Integer.parseInt(System.getProperty("port", "8080")); - - public static void main(String[] args) throws Exception { - // Configure SSL context - SelfSignedCertificate ssc = new SelfSignedCertificate(); - final SslContext sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .build(); - - EventLoopGroup bossGroup = new MultithreadEventLoopGroup(1, NioHandler.newFactory()); - EventLoopGroup workerGroup = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - ServerBootstrap b = new ServerBootstrap(); - b.group(bossGroup, workerGroup) - .channel(NioServerSocketChannel.class) - .handler(new LoggingHandler(LogLevel.INFO)) - .childHandler(new ChannelInitializer() { - @Override - public void initChannel(SocketChannel ch) throws Exception { - ch.pipeline().addLast(new PortUnificationServerHandler(sslCtx)); - } - }); - - // Bind and start to accept incoming connections. - b.bind(PORT).get().closeFuture().sync(); - } finally { - bossGroup.shutdownGracefully(); - workerGroup.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/portunification/PortUnificationServerHandler.java b/example/src/main/java/io/netty/example/portunification/PortUnificationServerHandler.java deleted file mode 100644 index bf1138ea81..0000000000 --- a/example/src/main/java/io/netty/example/portunification/PortUnificationServerHandler.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.portunification; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.example.factorial.BigIntegerDecoder; -import io.netty.example.factorial.FactorialServerHandler; -import io.netty.example.factorial.NumberEncoder; -import io.netty.example.http.snoop.HttpSnoopServerHandler; -import io.netty.handler.codec.ByteToMessageDecoder; -import io.netty.handler.codec.compression.ZlibCodecFactory; -import io.netty.handler.codec.compression.ZlibWrapper; -import io.netty.handler.codec.http.HttpContentCompressor; -import io.netty.handler.codec.http.HttpRequestDecoder; -import io.netty.handler.codec.http.HttpResponseEncoder; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslHandler; - -/** - * Manipulates the current pipeline dynamically to switch protocols or enable - * SSL or GZIP. - */ -public class PortUnificationServerHandler extends ByteToMessageDecoder { - - private final SslContext sslCtx; - private final boolean detectSsl; - private final boolean detectGzip; - - public PortUnificationServerHandler(SslContext sslCtx) { - this(sslCtx, true, true); - } - - private PortUnificationServerHandler(SslContext sslCtx, boolean detectSsl, boolean detectGzip) { - this.sslCtx = sslCtx; - this.detectSsl = detectSsl; - this.detectGzip = detectGzip; - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - // Will use the first five bytes to detect a protocol. - if (in.readableBytes() < 5) { - return; - } - - if (isSsl(in)) { - enableSsl(ctx); - } else { - final int magic1 = in.getUnsignedByte(in.readerIndex()); - final int magic2 = in.getUnsignedByte(in.readerIndex() + 1); - if (isGzip(magic1, magic2)) { - enableGzip(ctx); - } else if (isHttp(magic1, magic2)) { - switchToHttp(ctx); - } else if (isFactorial(magic1)) { - switchToFactorial(ctx); - } else { - // Unknown protocol; discard everything and close the connection. - in.clear(); - ctx.close(); - } - } - } - - private boolean isSsl(ByteBuf buf) { - if (detectSsl) { - return SslHandler.isEncrypted(buf); - } - return false; - } - - private boolean isGzip(int magic1, int magic2) { - if (detectGzip) { - return magic1 == 31 && magic2 == 139; - } - return false; - } - - private static boolean isHttp(int magic1, int magic2) { - return - magic1 == 'G' && magic2 == 'E' || // GET - magic1 == 'P' && magic2 == 'O' || // POST - magic1 == 'P' && magic2 == 'U' || // PUT - magic1 == 'H' && magic2 == 'E' || // HEAD - magic1 == 'O' && magic2 == 'P' || // OPTIONS - magic1 == 'P' && magic2 == 'A' || // PATCH - magic1 == 'D' && magic2 == 'E' || // DELETE - magic1 == 'T' && magic2 == 'R' || // TRACE - magic1 == 'C' && magic2 == 'O'; // CONNECT - } - - private static boolean isFactorial(int magic1) { - return magic1 == 'F'; - } - - private void enableSsl(ChannelHandlerContext ctx) { - ChannelPipeline p = ctx.pipeline(); - p.addLast("ssl", sslCtx.newHandler(ctx.alloc())); - p.addLast("unificationA", new PortUnificationServerHandler(sslCtx, false, detectGzip)); - p.remove(this); - } - - private void enableGzip(ChannelHandlerContext ctx) { - ChannelPipeline p = ctx.pipeline(); - p.addLast("gzipdeflater", ZlibCodecFactory.newZlibEncoder(ZlibWrapper.GZIP)); - p.addLast("gzipinflater", ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP)); - p.addLast("unificationB", new PortUnificationServerHandler(sslCtx, detectSsl, false)); - p.remove(this); - } - - private void switchToHttp(ChannelHandlerContext ctx) { - ChannelPipeline p = ctx.pipeline(); - p.addLast("decoder", new HttpRequestDecoder()); - p.addLast("encoder", new HttpResponseEncoder()); - p.addLast("deflater", new HttpContentCompressor()); - p.addLast("handler", new HttpSnoopServerHandler()); - p.remove(this); - } - - private void switchToFactorial(ChannelHandlerContext ctx) { - ChannelPipeline p = ctx.pipeline(); - p.addLast("decoder", new BigIntegerDecoder()); - p.addLast("encoder", new NumberEncoder()); - p.addLast("handler", new FactorialServerHandler()); - p.remove(this); - } -} diff --git a/example/src/main/java/io/netty/example/proxy/HexDumpProxy.java b/example/src/main/java/io/netty/example/proxy/HexDumpProxy.java deleted file mode 100644 index 0642c323b9..0000000000 --- a/example/src/main/java/io/netty/example/proxy/HexDumpProxy.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.proxy; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.ChannelOption; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; - -public final class HexDumpProxy { - - static final int LOCAL_PORT = Integer.parseInt(System.getProperty("localPort", "8443")); - static final String REMOTE_HOST = System.getProperty("remoteHost", "www.google.com"); - static final int REMOTE_PORT = Integer.parseInt(System.getProperty("remotePort", "443")); - - public static void main(String[] args) throws Exception { - System.err.println("Proxying *:" + LOCAL_PORT + " to " + REMOTE_HOST + ':' + REMOTE_PORT + " ..."); - - // Configure the bootstrap. - EventLoopGroup bossGroup = new MultithreadEventLoopGroup(1, NioHandler.newFactory()); - EventLoopGroup workerGroup = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - ServerBootstrap b = new ServerBootstrap(); - b.group(bossGroup, workerGroup) - .channel(NioServerSocketChannel.class) - .handler(new LoggingHandler(LogLevel.INFO)) - .childHandler(new HexDumpProxyInitializer(REMOTE_HOST, REMOTE_PORT)) - .childOption(ChannelOption.AUTO_READ, false) - .bind(LOCAL_PORT).get().closeFuture().sync(); - } finally { - bossGroup.shutdownGracefully(); - workerGroup.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/proxy/HexDumpProxyBackendHandler.java b/example/src/main/java/io/netty/example/proxy/HexDumpProxyBackendHandler.java deleted file mode 100644 index fdef8c38f1..0000000000 --- a/example/src/main/java/io/netty/example/proxy/HexDumpProxyBackendHandler.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.proxy; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; - -public class HexDumpProxyBackendHandler implements ChannelHandler { - - private final Channel inboundChannel; - - public HexDumpProxyBackendHandler(Channel inboundChannel) { - this.inboundChannel = inboundChannel; - } - - @Override - public void channelActive(ChannelHandlerContext ctx) { - ctx.read(); - } - - @Override - public void channelRead(final ChannelHandlerContext ctx, Object msg) { - inboundChannel.writeAndFlush(msg).addListener(inboundChannel, (inbound, future) -> { - if (future.isSuccess()) { - ctx.channel().read(); - } else { - inbound.close(); - } - }); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) { - HexDumpProxyFrontendHandler.closeOnFlush(inboundChannel); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - cause.printStackTrace(); - HexDumpProxyFrontendHandler.closeOnFlush(ctx.channel()); - } -} diff --git a/example/src/main/java/io/netty/example/proxy/HexDumpProxyFrontendHandler.java b/example/src/main/java/io/netty/example/proxy/HexDumpProxyFrontendHandler.java deleted file mode 100644 index a6a2920917..0000000000 --- a/example/src/main/java/io/netty/example/proxy/HexDumpProxyFrontendHandler.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.proxy; - -import io.netty.bootstrap.Bootstrap; -import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.netty.channel.ChannelFutureListeners; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelOption; - -public class HexDumpProxyFrontendHandler implements ChannelHandler { - - private final String remoteHost; - private final int remotePort; - - // As we use inboundChannel.eventLoop() when building the Bootstrap this does not need to be volatile as - // the outboundChannel will use the same EventLoop (and therefore Thread) as the inboundChannel. - private Channel outboundChannel; - - public HexDumpProxyFrontendHandler(String remoteHost, int remotePort) { - this.remoteHost = remoteHost; - this.remotePort = remotePort; - } - - @Override - public void channelActive(ChannelHandlerContext ctx) { - final Channel inboundChannel = ctx.channel(); - - // Start the connection attempt. - Bootstrap b = new Bootstrap(); - b.group(inboundChannel.executor()) - .channel(ctx.channel().getClass()) - .handler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - outboundChannel = ch; - ch.pipeline().addLast(new HexDumpProxyBackendHandler(inboundChannel)); - } - }) - .option(ChannelOption.AUTO_READ, false) - .connect(remoteHost, remotePort) - .addListener(future -> { - if (future.isSuccess()) { - // connection complete start to read first data - inboundChannel.read(); - } else { - // Close the connection if the connection attempt has failed. - inboundChannel.close(); - } - }); - } - - @Override - public void channelRead(final ChannelHandlerContext ctx, Object msg) { - if (outboundChannel.isActive()) { - outboundChannel.writeAndFlush(msg).addListener(outboundChannel, (outbound, future) -> { - if (future.isSuccess()) { - // was able to flush out data, start to read the next chunk - ctx.channel().read(); - } else { - outbound.close(); - } - }); - } - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) { - if (outboundChannel != null) { - closeOnFlush(outboundChannel); - } - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - cause.printStackTrace(); - closeOnFlush(ctx.channel()); - } - - /** - * Closes the specified channel after all queued write requests are flushed. - */ - static void closeOnFlush(Channel ch) { - if (ch.isActive()) { - ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ch, ChannelFutureListeners.CLOSE); - } - } -} diff --git a/example/src/main/java/io/netty/example/proxy/HexDumpProxyInitializer.java b/example/src/main/java/io/netty/example/proxy/HexDumpProxyInitializer.java deleted file mode 100644 index d15cf37d12..0000000000 --- a/example/src/main/java/io/netty/example/proxy/HexDumpProxyInitializer.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.proxy; - -import io.netty.channel.ChannelInitializer; -import io.netty.channel.socket.SocketChannel; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; - -public class HexDumpProxyInitializer extends ChannelInitializer { - - private final String remoteHost; - private final int remotePort; - - public HexDumpProxyInitializer(String remoteHost, int remotePort) { - this.remoteHost = remoteHost; - this.remotePort = remotePort; - } - - @Override - public void initChannel(SocketChannel ch) { - ch.pipeline().addLast( - new LoggingHandler(LogLevel.INFO), - new HexDumpProxyFrontendHandler(remoteHost, remotePort)); - } -} diff --git a/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentClient.java b/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentClient.java deleted file mode 100644 index 6a63e2e881..0000000000 --- a/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentClient.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.qotm; - -import io.netty.bootstrap.Bootstrap; -import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.netty.channel.ChannelOption; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.DatagramPacket; -import io.netty.channel.socket.nio.NioDatagramChannel; -import io.netty.util.CharsetUtil; -import io.netty.util.internal.SocketUtils; - -/** - * A UDP broadcast client that asks for a quote of the moment (QOTM) to {@link QuoteOfTheMomentServer}. - * - * Inspired by the official - * Java tutorial. - */ -public final class QuoteOfTheMomentClient { - - static final int PORT = Integer.parseInt(System.getProperty("port", "7686")); - - public static void main(String[] args) throws Exception { - - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - Bootstrap b = new Bootstrap(); - b.group(group) - .channel(NioDatagramChannel.class) - .option(ChannelOption.SO_BROADCAST, true) - .handler(new QuoteOfTheMomentClientHandler()); - - Channel ch = b.bind(0).get(); - - // Broadcast the QOTM request to port 8080. - ch.writeAndFlush(new DatagramPacket( - Unpooled.copiedBuffer("QOTM?", CharsetUtil.UTF_8), - SocketUtils.socketAddress("255.255.255.255", PORT))).sync(); - - // QuoteOfTheMomentClientHandler will close the DatagramChannel when a - // response is received. If the channel is not closed within 5 seconds, - // print an error message and quit. - if (!ch.closeFuture().await(5000)) { - System.err.println("QOTM request timed out."); - } - } finally { - group.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentClientHandler.java b/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentClientHandler.java deleted file mode 100644 index d2826ad539..0000000000 --- a/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentClientHandler.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.qotm; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.channel.socket.DatagramPacket; -import io.netty.util.CharsetUtil; - -public class QuoteOfTheMomentClientHandler extends SimpleChannelInboundHandler { - - @Override - public void messageReceived(ChannelHandlerContext ctx, DatagramPacket msg) throws Exception { - String response = msg.content().toString(CharsetUtil.UTF_8); - if (response.startsWith("QOTM: ")) { - System.out.println("Quote of the Moment: " + response.substring(6)); - ctx.close(); - } - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - cause.printStackTrace(); - ctx.close(); - } -} diff --git a/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentServer.java b/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentServer.java deleted file mode 100644 index dba7b3219f..0000000000 --- a/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentServer.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.qotm; - -import io.netty.bootstrap.Bootstrap; -import io.netty.channel.ChannelOption; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioDatagramChannel; - -/** - * A UDP server that responds to the QOTM (quote of the moment) request to a {@link QuoteOfTheMomentClient}. - * - * Inspired by the official - * Java tutorial. - */ -public final class QuoteOfTheMomentServer { - - private static final int PORT = Integer.parseInt(System.getProperty("port", "7686")); - - public static void main(String[] args) throws Exception { - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - Bootstrap b = new Bootstrap(); - b.group(group) - .channel(NioDatagramChannel.class) - .option(ChannelOption.SO_BROADCAST, true) - .handler(new QuoteOfTheMomentServerHandler()); - - b.bind(PORT).get().closeFuture().await(); - } finally { - group.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentServerHandler.java b/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentServerHandler.java deleted file mode 100644 index 666fbb61c1..0000000000 --- a/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentServerHandler.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.qotm; - -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.channel.socket.DatagramPacket; -import io.netty.util.CharsetUtil; - -import java.util.Random; - -public class QuoteOfTheMomentServerHandler extends SimpleChannelInboundHandler { - - private static final Random random = new Random(); - - // Quotes from Mohandas K. Gandhi: - private static final String[] quotes = { - "Where there is love there is life.", - "First they ignore you, then they laugh at you, then they fight you, then you win.", - "Be the change you want to see in the world.", - "The weak can never forgive. Forgiveness is the attribute of the strong.", - }; - - private static String nextQuote() { - int quoteId; - synchronized (random) { - quoteId = random.nextInt(quotes.length); - } - return quotes[quoteId]; - } - - @Override - public void messageReceived(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception { - System.err.println(packet); - if ("QOTM?".equals(packet.content().toString(CharsetUtil.UTF_8))) { - ctx.write(new DatagramPacket( - Unpooled.copiedBuffer("QOTM: " + nextQuote(), CharsetUtil.UTF_8), packet.sender())); - } - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) { - ctx.flush(); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - cause.printStackTrace(); - // We don't close the channel because we can keep serving requests. - } -} diff --git a/example/src/main/java/io/netty/example/redis/RedisClient.java b/example/src/main/java/io/netty/example/redis/RedisClient.java deleted file mode 100644 index ad6b20084b..0000000000 --- a/example/src/main/java/io/netty/example/redis/RedisClient.java +++ /dev/null @@ -1,96 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.example.redis; - -import io.netty.bootstrap.Bootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.codec.redis.RedisArrayAggregator; -import io.netty.handler.codec.redis.RedisBulkStringAggregator; -import io.netty.handler.codec.redis.RedisDecoder; -import io.netty.handler.codec.redis.RedisEncoder; -import io.netty.util.concurrent.Future; - -import java.io.BufferedReader; -import java.io.InputStreamReader; - -/** - * Simple Redis client that demonstrates Redis commands against a Redis server. - */ -public class RedisClient { - private static final String HOST = System.getProperty("host", "127.0.0.1"); - private static final int PORT = Integer.parseInt(System.getProperty("port", "6379")); - - public static void main(String[] args) throws Exception { - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - Bootstrap b = new Bootstrap(); - b.group(group) - .channel(NioSocketChannel.class) - .handler(new ChannelInitializer() { - @Override - protected void initChannel(SocketChannel ch) throws Exception { - ChannelPipeline p = ch.pipeline(); - p.addLast(new RedisDecoder()); - p.addLast(new RedisBulkStringAggregator()); - p.addLast(new RedisArrayAggregator()); - p.addLast(new RedisEncoder()); - p.addLast(new RedisClientHandler()); - } - }); - - // Start the connection attempt. - Channel ch = b.connect(HOST, PORT).get(); - - // Read commands from the stdin. - System.out.println("Enter Redis commands (quit to end)"); - Future lastWriteFuture = null; - BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); - for (;;) { - final String input = in.readLine(); - final String line = input != null ? input.trim() : null; - if (line == null || "quit".equalsIgnoreCase(line)) { // EOF or "quit" - ch.close().sync(); - break; - } - if (line.isEmpty()) { // skip `enter` or `enter` with spaces. - continue; - } - // Sends the received line to the server. - lastWriteFuture = ch.writeAndFlush(line); - lastWriteFuture.addListener(future -> { - if (future.isFailed()) { - System.err.print("write failed: "); - future.cause().printStackTrace(System.err); - } - }); - } - - // Wait until all messages are flushed before closing the channel. - if (lastWriteFuture != null) { - lastWriteFuture.sync(); - } - } finally { - group.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/redis/RedisClientHandler.java b/example/src/main/java/io/netty/example/redis/RedisClientHandler.java deleted file mode 100644 index c298631cd7..0000000000 --- a/example/src/main/java/io/netty/example/redis/RedisClientHandler.java +++ /dev/null @@ -1,90 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.example.redis; - -import io.netty.buffer.ByteBufUtil; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.CodecException; -import io.netty.handler.codec.redis.ArrayRedisMessage; -import io.netty.handler.codec.redis.ErrorRedisMessage; -import io.netty.handler.codec.redis.FullBulkStringRedisMessage; -import io.netty.handler.codec.redis.IntegerRedisMessage; -import io.netty.handler.codec.redis.RedisMessage; -import io.netty.handler.codec.redis.SimpleStringRedisMessage; -import io.netty.util.CharsetUtil; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.concurrent.Future; - -import java.util.ArrayList; -import java.util.List; - -/** - * An example Redis client handler. This handler read input from STDIN and write output to STDOUT. - */ -public class RedisClientHandler implements ChannelHandler { - - @Override - public Future write(ChannelHandlerContext ctx, Object msg) { - String[] commands = ((String) msg).split("\\s+"); - List children = new ArrayList<>(commands.length); - for (String cmdString : commands) { - children.add(new FullBulkStringRedisMessage(ByteBufUtil.writeUtf8(ctx.alloc(), cmdString))); - } - RedisMessage request = new ArrayRedisMessage(children); - return ctx.write(request); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - RedisMessage redisMessage = (RedisMessage) msg; - printAggregatedRedisResponse(redisMessage); - ReferenceCountUtil.release(redisMessage); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - System.err.print("exceptionCaught: "); - cause.printStackTrace(System.err); - ctx.close(); - } - - private static void printAggregatedRedisResponse(RedisMessage msg) { - if (msg instanceof SimpleStringRedisMessage) { - System.out.println(((SimpleStringRedisMessage) msg).content()); - } else if (msg instanceof ErrorRedisMessage) { - System.out.println(((ErrorRedisMessage) msg).content()); - } else if (msg instanceof IntegerRedisMessage) { - System.out.println(((IntegerRedisMessage) msg).value()); - } else if (msg instanceof FullBulkStringRedisMessage) { - System.out.println(getString((FullBulkStringRedisMessage) msg)); - } else if (msg instanceof ArrayRedisMessage) { - for (RedisMessage child : ((ArrayRedisMessage) msg).children()) { - printAggregatedRedisResponse(child); - } - } else { - throw new CodecException("unknown message type: " + msg); - } - } - - private static String getString(FullBulkStringRedisMessage msg) { - if (msg.isNull()) { - return "(null)"; - } - return msg.content().toString(CharsetUtil.UTF_8); - } -} diff --git a/example/src/main/java/io/netty/example/sctp/SctpEchoClient.java b/example/src/main/java/io/netty/example/sctp/SctpEchoClient.java deleted file mode 100644 index f2df3fc148..0000000000 --- a/example/src/main/java/io/netty/example/sctp/SctpEchoClient.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.sctp; - -import io.netty.bootstrap.Bootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.sctp.SctpChannel; -import io.netty.channel.sctp.SctpChannelOption; -import io.netty.channel.sctp.nio.NioSctpChannel; - -/** - * Sends one message when a connection is open and echoes back any received - * data to the server over SCTP connection. - * - * Simply put, the echo client initiates the ping-pong - * traffic between the echo client and server by sending the first message to - * the server. - */ -public final class SctpEchoClient { - - static final String HOST = System.getProperty("host", "127.0.0.1"); - static final int PORT = Integer.parseInt(System.getProperty("port", "8007")); - static final int SIZE = Integer.parseInt(System.getProperty("size", "256")); - - public static void main(String[] args) throws Exception { - // Configure the client. - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - Bootstrap b = new Bootstrap(); - b.group(group) - .channel(NioSctpChannel.class) - .option(SctpChannelOption.SCTP_NODELAY, true) - .handler(new ChannelInitializer() { - @Override - public void initChannel(SctpChannel ch) throws Exception { - ch.pipeline().addLast( - //new LoggingHandler(LogLevel.INFO), - new SctpEchoClientHandler()); - } - }); - - // Start the client. - Channel channel = b.connect(HOST, PORT).get(); - - // Wait until the connection is closed. - channel.closeFuture().sync(); - } finally { - // Shut down the event loop to terminate all threads. - group.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/sctp/SctpEchoClientHandler.java b/example/src/main/java/io/netty/example/sctp/SctpEchoClientHandler.java deleted file mode 100644 index c8db7062e1..0000000000 --- a/example/src/main/java/io/netty/example/sctp/SctpEchoClientHandler.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.sctp; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.sctp.SctpMessage; - -/** - * Handler implementation for the SCTP echo client. It initiates the ping-pong - * traffic between the echo client and server by sending the first message to - * the server. - */ -public class SctpEchoClientHandler implements ChannelHandler { - - private final ByteBuf firstMessage; - - /** - * Creates a client-side handler. - */ - public SctpEchoClientHandler() { - firstMessage = Unpooled.buffer(SctpEchoClient.SIZE); - for (int i = 0; i < firstMessage.capacity(); i++) { - firstMessage.writeByte((byte) i); - } - } - - @Override - public void channelActive(ChannelHandlerContext ctx) { - ctx.writeAndFlush(new SctpMessage(0, 0, firstMessage)); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - ctx.write(msg); - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) { - ctx.flush(); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - // Close the connection when an exception is raised. - cause.printStackTrace(); - ctx.close(); - } -} diff --git a/example/src/main/java/io/netty/example/sctp/SctpEchoServer.java b/example/src/main/java/io/netty/example/sctp/SctpEchoServer.java deleted file mode 100644 index 1997a12043..0000000000 --- a/example/src/main/java/io/netty/example/sctp/SctpEchoServer.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.sctp; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelOption; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.sctp.SctpChannel; -import io.netty.channel.sctp.nio.NioSctpServerChannel; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; - -/** - * Echoes back any received data from a SCTP client. - */ -public final class SctpEchoServer { - - static final int PORT = Integer.parseInt(System.getProperty("port", "8007")); - - public static void main(String[] args) throws Exception { - // Configure the server. - EventLoopGroup bossGroup = new MultithreadEventLoopGroup(1, NioHandler.newFactory()); - EventLoopGroup workerGroup = new MultithreadEventLoopGroup(NioHandler.newFactory()); - final SctpEchoServerHandler serverHandler = new SctpEchoServerHandler(); - try { - ServerBootstrap b = new ServerBootstrap(); - b.group(bossGroup, workerGroup) - .channel(NioSctpServerChannel.class) - .option(ChannelOption.SO_BACKLOG, 100) - .handler(new LoggingHandler(LogLevel.INFO)) - .childHandler(new ChannelInitializer() { - @Override - public void initChannel(SctpChannel ch) throws Exception { - ch.pipeline().addLast( - //new LoggingHandler(LogLevel.INFO), - serverHandler); - } - }); - - // Start the server. - Channel channel = b.bind(PORT).get(); - - // Wait until the server socket is closed. - channel.closeFuture().sync(); - } finally { - // Shut down all event loops to terminate all threads. - bossGroup.shutdownGracefully(); - workerGroup.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/sctp/SctpEchoServerHandler.java b/example/src/main/java/io/netty/example/sctp/SctpEchoServerHandler.java deleted file mode 100644 index 65951072a7..0000000000 --- a/example/src/main/java/io/netty/example/sctp/SctpEchoServerHandler.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.sctp; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; - -/** - * Handler implementation for the SCTP echo server. - */ -@Sharable -public class SctpEchoServerHandler implements ChannelHandler { - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - ctx.write(msg); - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) { - ctx.flush(); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - // Close the connection when an exception is raised. - cause.printStackTrace(); - ctx.close(); - } -} diff --git a/example/src/main/java/io/netty/example/sctp/multihoming/SctpMultiHomingEchoClient.java b/example/src/main/java/io/netty/example/sctp/multihoming/SctpMultiHomingEchoClient.java deleted file mode 100644 index 655aaf234a..0000000000 --- a/example/src/main/java/io/netty/example/sctp/multihoming/SctpMultiHomingEchoClient.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.sctp.multihoming; - -import io.netty.bootstrap.Bootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.sctp.SctpChannel; -import io.netty.channel.sctp.SctpChannelOption; -import io.netty.channel.sctp.nio.NioSctpChannel; -import io.netty.example.sctp.SctpEchoClientHandler; -import io.netty.util.concurrent.Future; -import io.netty.util.internal.SocketUtils; - -import java.net.InetAddress; -import java.net.InetSocketAddress; - -/** - * SCTP Echo Client with multi-homing support. - */ -public final class SctpMultiHomingEchoClient { - - private static final String CLIENT_PRIMARY_HOST = System.getProperty("host.primary", "127.0.0.1"); - private static final String CLIENT_SECONDARY_HOST = System.getProperty("host.secondary", "127.0.0.2"); - - private static final int CLIENT_PORT = Integer.parseInt(System.getProperty("port.local", "8008")); - - private static final String SERVER_REMOTE_HOST = System.getProperty("host.remote", "127.0.0.1"); - private static final int SERVER_REMOTE_PORT = Integer.parseInt(System.getProperty("port.remote", "8007")); - - public static void main(String[] args) throws Exception { - // Configure the client. - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - Bootstrap b = new Bootstrap(); - b.group(group) - .channel(NioSctpChannel.class) - .option(SctpChannelOption.SCTP_NODELAY, true) - .handler(new ChannelInitializer() { - @Override - public void initChannel(SctpChannel ch) throws Exception { - ch.pipeline().addLast( -// new LoggingHandler(LogLevel.INFO), - new SctpEchoClientHandler()); - } - }); - - InetSocketAddress localAddress = SocketUtils.socketAddress(CLIENT_PRIMARY_HOST, CLIENT_PORT); - InetAddress localSecondaryAddress = SocketUtils.addressByName(CLIENT_SECONDARY_HOST); - - InetSocketAddress remoteAddress = SocketUtils.socketAddress(SERVER_REMOTE_HOST, SERVER_REMOTE_PORT); - - // Bind the client channel. - Future bindFuture = b.bind(localAddress); - - // Get the underlying sctp channel - SctpChannel channel = (SctpChannel) bindFuture.get(); - - // Bind the secondary address. - // Please note that, bindAddress in the client channel should be done before connecting if you have not - // enable Dynamic Address Configuration. See net.sctp.addip_enable kernel param - channel.bindAddress(localSecondaryAddress).sync(); - - // Finish connect - Future connectFuture = channel.connect(remoteAddress).sync(); - - // Wait until the connection is closed. - channel.closeFuture().sync(); - } finally { - // Shut down the event loop to terminate all threads. - group.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/sctp/multihoming/SctpMultiHomingEchoServer.java b/example/src/main/java/io/netty/example/sctp/multihoming/SctpMultiHomingEchoServer.java deleted file mode 100644 index 147c1f682a..0000000000 --- a/example/src/main/java/io/netty/example/sctp/multihoming/SctpMultiHomingEchoServer.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.sctp.multihoming; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelOption; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.sctp.SctpChannel; -import io.netty.channel.sctp.SctpServerChannel; -import io.netty.channel.sctp.nio.NioSctpServerChannel; -import io.netty.example.sctp.SctpEchoServerHandler; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; -import io.netty.util.concurrent.Future; -import io.netty.util.internal.SocketUtils; - -import java.net.InetAddress; -import java.net.InetSocketAddress; - -/** - * SCTP Echo Server with multi-homing support. - */ -public final class SctpMultiHomingEchoServer { - - private static final String SERVER_PRIMARY_HOST = System.getProperty("host.primary", "127.0.0.1"); - private static final String SERVER_SECONDARY_HOST = System.getProperty("host.secondary", "127.0.0.2"); - - private static final int SERVER_PORT = Integer.parseInt(System.getProperty("port", "8007")); - - public static void main(String[] args) throws Exception { - // Configure the server. - EventLoopGroup bossGroup = new MultithreadEventLoopGroup(1, NioHandler.newFactory()); - EventLoopGroup workerGroup = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - ServerBootstrap b = new ServerBootstrap(); - b.group(bossGroup, workerGroup) - .channel(NioSctpServerChannel.class) - .option(ChannelOption.SO_BACKLOG, 100) - .handler(new LoggingHandler(LogLevel.INFO)) - .childHandler(new ChannelInitializer() { - @Override - public void initChannel(SctpChannel ch) throws Exception { - ch.pipeline().addLast( -// new LoggingHandler(LogLevel.INFO), - new SctpEchoServerHandler()); - } - }); - - InetSocketAddress localAddress = SocketUtils.socketAddress(SERVER_PRIMARY_HOST, SERVER_PORT); - InetAddress localSecondaryAddress = SocketUtils.addressByName(SERVER_SECONDARY_HOST); - - // Bind the server to primary address. - Future bindFuture = b.bind(localAddress); - - //Get the underlying sctp channel - SctpServerChannel channel = (SctpServerChannel) bindFuture.get(); - - //Bind the secondary address - channel.bindAddress(localSecondaryAddress).sync(); - - // Wait until the connection is closed. - channel.closeFuture().sync(); - } finally { - // Shut down all event loops to terminate all threads. - bossGroup.shutdownGracefully(); - workerGroup.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/securechat/SecureChatClient.java b/example/src/main/java/io/netty/example/securechat/SecureChatClient.java deleted file mode 100644 index fd2583082b..0000000000 --- a/example/src/main/java/io/netty/example/securechat/SecureChatClient.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.securechat; - -import io.netty.bootstrap.Bootstrap; -import io.netty.channel.Channel; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.example.telnet.TelnetClient; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.util.InsecureTrustManagerFactory; -import io.netty.util.concurrent.Future; - -import java.io.BufferedReader; -import java.io.InputStreamReader; - -/** - * Simple SSL chat client modified from {@link TelnetClient}. - */ -public final class SecureChatClient { - - static final String HOST = System.getProperty("host", "127.0.0.1"); - static final int PORT = Integer.parseInt(System.getProperty("port", "8992")); - - public static void main(String[] args) throws Exception { - // Configure SSL. - final SslContext sslCtx = SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE).build(); - - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - Bootstrap b = new Bootstrap(); - b.group(group) - .channel(NioSocketChannel.class) - .handler(new SecureChatClientInitializer(sslCtx)); - - // Start the connection attempt. - Channel ch = b.connect(HOST, PORT).get(); - - // Read commands from the stdin. - Future lastWriteFuture = null; - BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); - for (;;) { - String line = in.readLine(); - if (line == null) { - break; - } - - // Sends the received line to the server. - lastWriteFuture = ch.writeAndFlush(line + "\r\n"); - - // If user typed the 'bye' command, wait until the server closes - // the connection. - if ("bye".equalsIgnoreCase(line)) { - ch.closeFuture().sync(); - break; - } - } - - // Wait until all messages are flushed before closing the channel. - if (lastWriteFuture != null) { - lastWriteFuture.sync(); - } - } finally { - // The connection is closed automatically on shutdown. - group.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/securechat/SecureChatClientHandler.java b/example/src/main/java/io/netty/example/securechat/SecureChatClientHandler.java deleted file mode 100644 index 1bd86e11d6..0000000000 --- a/example/src/main/java/io/netty/example/securechat/SecureChatClientHandler.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.securechat; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; - -/** - * Handles a client-side channel. - */ -public class SecureChatClientHandler extends SimpleChannelInboundHandler { - - @Override - public void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception { - System.err.println(msg); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - cause.printStackTrace(); - ctx.close(); - } -} diff --git a/example/src/main/java/io/netty/example/securechat/SecureChatClientInitializer.java b/example/src/main/java/io/netty/example/securechat/SecureChatClientInitializer.java deleted file mode 100644 index 8e85c95dc9..0000000000 --- a/example/src/main/java/io/netty/example/securechat/SecureChatClientInitializer.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.securechat; - -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.socket.SocketChannel; -import io.netty.handler.codec.DelimiterBasedFrameDecoder; -import io.netty.handler.codec.Delimiters; -import io.netty.handler.codec.string.StringDecoder; -import io.netty.handler.codec.string.StringEncoder; -import io.netty.handler.ssl.SslContext; - -/** - * Creates a newly configured {@link ChannelPipeline} for a new channel. - */ -public class SecureChatClientInitializer extends ChannelInitializer { - - private final SslContext sslCtx; - - public SecureChatClientInitializer(SslContext sslCtx) { - this.sslCtx = sslCtx; - } - - @Override - public void initChannel(SocketChannel ch) throws Exception { - ChannelPipeline pipeline = ch.pipeline(); - - // Add SSL handler first to encrypt and decrypt everything. - // In this example, we use a bogus certificate in the server side - // and accept any invalid certificates in the client side. - // You will need something more complicated to identify both - // and server in the real world. - pipeline.addLast(sslCtx.newHandler(ch.alloc(), SecureChatClient.HOST, SecureChatClient.PORT)); - - // On top of the SSL handler, add the text line codec. - pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter())); - pipeline.addLast(new StringDecoder()); - pipeline.addLast(new StringEncoder()); - - // and then business logic. - pipeline.addLast(new SecureChatClientHandler()); - } -} diff --git a/example/src/main/java/io/netty/example/securechat/SecureChatServer.java b/example/src/main/java/io/netty/example/securechat/SecureChatServer.java deleted file mode 100644 index 8f898379e0..0000000000 --- a/example/src/main/java/io/netty/example/securechat/SecureChatServer.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.securechat; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.example.telnet.TelnetServer; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.util.SelfSignedCertificate; - -/** - * Simple SSL chat server modified from {@link TelnetServer}. - */ -public final class SecureChatServer { - - static final int PORT = Integer.parseInt(System.getProperty("port", "8992")); - - public static void main(String[] args) throws Exception { - SelfSignedCertificate ssc = new SelfSignedCertificate(); - SslContext sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .build(); - - EventLoopGroup bossGroup = new MultithreadEventLoopGroup(1, NioHandler.newFactory()); - EventLoopGroup workerGroup = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - ServerBootstrap b = new ServerBootstrap(); - b.group(bossGroup, workerGroup) - .channel(NioServerSocketChannel.class) - .handler(new LoggingHandler(LogLevel.INFO)) - .childHandler(new SecureChatServerInitializer(sslCtx)); - - b.bind(PORT).get().closeFuture().sync(); - } finally { - bossGroup.shutdownGracefully(); - workerGroup.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/securechat/SecureChatServerHandler.java b/example/src/main/java/io/netty/example/securechat/SecureChatServerHandler.java deleted file mode 100644 index 4c0236325d..0000000000 --- a/example/src/main/java/io/netty/example/securechat/SecureChatServerHandler.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.securechat; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.channel.group.ChannelGroup; -import io.netty.channel.group.DefaultChannelGroup; -import io.netty.handler.ssl.SslHandler; -import io.netty.util.concurrent.GlobalEventExecutor; - -import java.net.InetAddress; - -/** - * Handles a server-side channel. - */ -public class SecureChatServerHandler extends SimpleChannelInboundHandler { - - static final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); - - @Override - public void channelActive(final ChannelHandlerContext ctx) { - // Once session is secured, send a greeting and register the channel to the global channel - // list so the channel received the messages from others. - ctx.pipeline().get(SslHandler.class).handshakeFuture().addListener(future -> { - ctx.writeAndFlush( - "Welcome to " + InetAddress.getLocalHost().getHostName() + " secure chat service!\n"); - ctx.writeAndFlush( - "Your session is protected by " + - ctx.pipeline().get(SslHandler.class).engine().getSession().getCipherSuite() + - " cipher suite.\n"); - - channels.add(ctx.channel()); - }); - } - - @Override - public void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception { - // Send the received message to all channels but the current one. - for (Channel c: channels) { - if (c != ctx.channel()) { - c.writeAndFlush("[" + ctx.channel().remoteAddress() + "] " + msg + '\n'); - } else { - c.writeAndFlush("[you] " + msg + '\n'); - } - } - - // Close the connection if the client has sent 'bye'. - if ("bye".equals(msg.toLowerCase())) { - ctx.close(); - } - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - cause.printStackTrace(); - ctx.close(); - } -} diff --git a/example/src/main/java/io/netty/example/securechat/SecureChatServerInitializer.java b/example/src/main/java/io/netty/example/securechat/SecureChatServerInitializer.java deleted file mode 100644 index ae3848772f..0000000000 --- a/example/src/main/java/io/netty/example/securechat/SecureChatServerInitializer.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.securechat; - -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.socket.SocketChannel; -import io.netty.handler.codec.DelimiterBasedFrameDecoder; -import io.netty.handler.codec.Delimiters; -import io.netty.handler.codec.string.StringDecoder; -import io.netty.handler.codec.string.StringEncoder; -import io.netty.handler.ssl.SslContext; - -/** - * Creates a newly configured {@link ChannelPipeline} for a new channel. - */ -public class SecureChatServerInitializer extends ChannelInitializer { - - private final SslContext sslCtx; - - public SecureChatServerInitializer(SslContext sslCtx) { - this.sslCtx = sslCtx; - } - - @Override - public void initChannel(SocketChannel ch) throws Exception { - ChannelPipeline pipeline = ch.pipeline(); - - // Add SSL handler first to encrypt and decrypt everything. - // In this example, we use a bogus certificate in the server side - // and accept any invalid certificates in the client side. - // You will need something more complicated to identify both - // and server in the real world. - pipeline.addLast(sslCtx.newHandler(ch.alloc())); - - // On top of the SSL handler, add the text line codec. - pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter())); - pipeline.addLast(new StringDecoder()); - pipeline.addLast(new StringEncoder()); - - // and then business logic. - pipeline.addLast(new SecureChatServerHandler()); - } -} diff --git a/example/src/main/java/io/netty/example/smtp/SmtpClient.java b/example/src/main/java/io/netty/example/smtp/SmtpClient.java deleted file mode 100644 index bb9a4f6d3f..0000000000 --- a/example/src/main/java/io/netty/example/smtp/SmtpClient.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.example.smtp; - -import io.netty.bootstrap.Bootstrap; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.codec.smtp.DefaultSmtpRequest; -import io.netty.handler.codec.smtp.SmtpCommand; -import io.netty.handler.codec.smtp.SmtpRequest; -import io.netty.handler.codec.smtp.SmtpRequestEncoder; -import io.netty.handler.codec.smtp.SmtpResponse; -import io.netty.handler.codec.smtp.SmtpResponseDecoder; -import io.netty.util.CharsetUtil; -import io.netty.util.concurrent.Future; - -import java.util.Base64; - -import static io.netty.handler.codec.smtp.SmtpCommand.AUTH; -import static io.netty.handler.codec.smtp.SmtpCommand.DATA; -import static io.netty.handler.codec.smtp.SmtpCommand.EHLO; -import static io.netty.handler.codec.smtp.SmtpCommand.EMPTY; -import static io.netty.handler.codec.smtp.SmtpCommand.MAIL; -import static io.netty.handler.codec.smtp.SmtpCommand.QUIT; -import static io.netty.handler.codec.smtp.SmtpCommand.RCPT; - -/** - * A simple smtp client - */ -public class SmtpClient { - - private static final String HOST = System.getProperty("host", "127.0.0.1"); - private static final int PORT = Integer.parseInt(System.getProperty("port", "25")); - - private static final String USERNAME = "sample-user@test.com"; - private static final String PASSWORD = "sample-password"; - - private static final String SENDER = "sample-sender@test.com"; - private static final String RECEIVER = "sample-receiver@test.com"; - - private static final String content = "From: " + SENDER + "\r\n" + - "To: " + RECEIVER + "\r\n" + - "Subject: test\r\n" + - "\r\n" + - "This is a test message.\r\n" + - ".\r\n"; - - public static void main(String[] args) throws Exception { - SmtpClientHandler handler = new SmtpClientHandler(); - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - Bootstrap b = new Bootstrap(); - b.group(group) - .channel(NioSocketChannel.class) - .handler(new ChannelInitializer() { - @Override - protected void initChannel(SocketChannel ch) throws Exception { - ChannelPipeline p = ch.pipeline(); - p.addLast(new SmtpRequestEncoder()); - p.addLast(new SmtpResponseDecoder(Integer.MAX_VALUE)); - p.addLast(handler); - } - }); - - ByteBuf mailContent = Unpooled.wrappedBuffer(content.getBytes(CharsetUtil.UTF_8)); - - // Start send smtp command. - Future f = handler.createResponseFuture(group.next()) - .thenCompose(v -> handler.send(req(EHLO, "localhost"))) - .thenCompose(r -> handler.send(req(AUTH, "login"))) - .thenCompose(r -> handler.send(req(EMPTY, encode(USERNAME)))) - .thenCompose(r -> handler.send(req(EMPTY, encode(PASSWORD)))) - .thenCompose(r -> handler.send(req(MAIL, String.format("FROM:<%s>", SENDER)))) - .thenCompose(r -> handler.send(req(RCPT, String.format("TO:<%s>", RECEIVER)))) - .thenCompose(r -> handler.send(req(DATA))) - .thenCompose(r -> handler.sendMailContent(mailContent)) - .thenCompose(r -> handler.send(req(QUIT))) - .future(); - - // Start connect. - Channel ch = b.connect(HOST, PORT).get(); - f.await(); - ch.close(); - } finally { - group.shutdownGracefully(); - } - } - - private static String encode(String msg) { - return Base64.getEncoder().encodeToString(msg.getBytes(CharsetUtil.UTF_8)); - } - - private static SmtpRequest req(SmtpCommand command, CharSequence... arguments) { - return new DefaultSmtpRequest(command, arguments); - } -} diff --git a/example/src/main/java/io/netty/example/smtp/SmtpClientHandler.java b/example/src/main/java/io/netty/example/smtp/SmtpClientHandler.java deleted file mode 100644 index 8c75ab3d17..0000000000 --- a/example/src/main/java/io/netty/example/smtp/SmtpClientHandler.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.example.smtp; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.handler.codec.smtp.DefaultLastSmtpContent; -import io.netty.handler.codec.smtp.SmtpRequest; -import io.netty.handler.codec.smtp.SmtpResponse; -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.FutureCompletionStage; -import io.netty.util.concurrent.Promise; - -import java.util.concurrent.atomic.AtomicReference; - -/** - * An example smtp client handler - */ -public class SmtpClientHandler extends SimpleChannelInboundHandler { - - private ChannelHandlerContext ctx; - private final AtomicReference> responseFuture = new AtomicReference<>(); - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - this.ctx = ctx; - } - - @Override - protected void messageReceived(ChannelHandlerContext ctx, SmtpResponse response) throws Exception { - Promise promise = responseFuture.get(); - if (promise == null) { - throw new RuntimeException("Unexpected response received: " + response); - } else { - if (response.code() >= 500) { - throw new RuntimeException("receive an error: " + response); - } - printResponse(response); - promise.setSuccess(response); - responseFuture.set(null); - } - } - - private static void printResponse(SmtpResponse response) { - System.out.println(response); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - cause.printStackTrace(); - responseFuture.get().setFailure(cause); - ctx.close(); - } - - public FutureCompletionStage send(SmtpRequest request) { - FutureCompletionStage future = createResponseFuture(ctx.executor()); - ctx.writeAndFlush(request); - return future; - } - - public FutureCompletionStage sendMailContent(ByteBuf content) { - FutureCompletionStage future = createResponseFuture(ctx.executor()); - ctx.writeAndFlush(new DefaultLastSmtpContent(content)); - return future; - } - - public FutureCompletionStage createResponseFuture(EventExecutor executor) { - Promise promise = executor.newPromise(); - while (!responseFuture.compareAndSet(null, promise)) { - Promise previousFuture = responseFuture.get(); - if (previousFuture != null) { - throw new RuntimeException("Still waiting for the past response"); - } - } - return promise.asFuture().asStage(); - } -} diff --git a/example/src/main/java/io/netty/example/socksproxy/DirectClientHandler.java b/example/src/main/java/io/netty/example/socksproxy/DirectClientHandler.java deleted file mode 100644 index 1a7c2713ff..0000000000 --- a/example/src/main/java/io/netty/example/socksproxy/DirectClientHandler.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.socksproxy; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.concurrent.Promise; - -public final class DirectClientHandler implements ChannelHandler { - - private final Promise promise; - - public DirectClientHandler(Promise promise) { - this.promise = promise; - } - - @Override - public void channelActive(ChannelHandlerContext ctx) { - ctx.pipeline().remove(this); - promise.setSuccess(ctx.channel()); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable throwable) { - promise.setFailure(throwable); - } -} diff --git a/example/src/main/java/io/netty/example/socksproxy/RelayHandler.java b/example/src/main/java/io/netty/example/socksproxy/RelayHandler.java deleted file mode 100644 index 953b85c034..0000000000 --- a/example/src/main/java/io/netty/example/socksproxy/RelayHandler.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.socksproxy; - -import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.ReferenceCountUtil; - -public final class RelayHandler implements ChannelHandler { - - private final Channel relayChannel; - - public RelayHandler(Channel relayChannel) { - this.relayChannel = relayChannel; - } - - @Override - public void channelActive(ChannelHandlerContext ctx) { - ctx.writeAndFlush(Unpooled.EMPTY_BUFFER); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - if (relayChannel.isActive()) { - relayChannel.writeAndFlush(msg); - } else { - ReferenceCountUtil.release(msg); - } - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) { - if (relayChannel.isActive()) { - SocksServerUtils.closeOnFlush(relayChannel); - } - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - cause.printStackTrace(); - ctx.close(); - } -} diff --git a/example/src/main/java/io/netty/example/socksproxy/SocksServer.java b/example/src/main/java/io/netty/example/socksproxy/SocksServer.java deleted file mode 100644 index 4e45cb913b..0000000000 --- a/example/src/main/java/io/netty/example/socksproxy/SocksServer.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.socksproxy; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; - -public final class SocksServer { - - static final int PORT = Integer.parseInt(System.getProperty("port", "1080")); - - public static void main(String[] args) throws Exception { - EventLoopGroup bossGroup = new MultithreadEventLoopGroup(1, NioHandler.newFactory()); - EventLoopGroup workerGroup = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - ServerBootstrap b = new ServerBootstrap(); - b.group(bossGroup, workerGroup) - .channel(NioServerSocketChannel.class) - .handler(new LoggingHandler(LogLevel.INFO)) - .childHandler(new SocksServerInitializer()); - b.bind(PORT).get().closeFuture().sync(); - } finally { - bossGroup.shutdownGracefully(); - workerGroup.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/socksproxy/SocksServerConnectHandler.java b/example/src/main/java/io/netty/example/socksproxy/SocksServerConnectHandler.java deleted file mode 100644 index ab2973224b..0000000000 --- a/example/src/main/java/io/netty/example/socksproxy/SocksServerConnectHandler.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.socksproxy; - -import io.netty.bootstrap.Bootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelOption; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.codec.socksx.SocksMessage; -import io.netty.handler.codec.socksx.v4.DefaultSocks4CommandResponse; -import io.netty.handler.codec.socksx.v4.Socks4CommandRequest; -import io.netty.handler.codec.socksx.v4.Socks4CommandStatus; -import io.netty.handler.codec.socksx.v5.DefaultSocks5CommandResponse; -import io.netty.handler.codec.socksx.v5.Socks5CommandRequest; -import io.netty.handler.codec.socksx.v5.Socks5CommandStatus; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; - -@ChannelHandler.Sharable -public final class SocksServerConnectHandler extends SimpleChannelInboundHandler { - - private final Bootstrap b = new Bootstrap(); - - @Override - public void messageReceived(final ChannelHandlerContext ctx, final SocksMessage message) throws Exception { - if (message instanceof Socks4CommandRequest) { - final Socks4CommandRequest request = (Socks4CommandRequest) message; - Promise promise = ctx.executor().newPromise(); - promise.asFuture().addListener(future -> { - final Channel outboundChannel = future.getNow(); - if (future.isSuccess()) { - Future responseFuture = ctx.channel().writeAndFlush( - new DefaultSocks4CommandResponse(Socks4CommandStatus.SUCCESS)); - - responseFuture.addListener(fut -> { - ctx.pipeline().remove(this); - outboundChannel.pipeline().addLast(new RelayHandler(ctx.channel())); - ctx.pipeline().addLast(new RelayHandler(outboundChannel)); - }); - } else { - ctx.channel().writeAndFlush( - new DefaultSocks4CommandResponse(Socks4CommandStatus.REJECTED_OR_FAILED)); - SocksServerUtils.closeOnFlush(ctx.channel()); - } - }); - - final Channel inboundChannel = ctx.channel(); - b.group(inboundChannel.executor()) - .channel(NioSocketChannel.class) - .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000) - .option(ChannelOption.SO_KEEPALIVE, true) - .handler(new DirectClientHandler(promise)); - - b.connect(request.dstAddr(), request.dstPort()).addListener(future -> { - if (future.isSuccess()) { - // Connection established use handler provided results - } else { - // Close the connection if the connection attempt has failed. - ctx.channel().writeAndFlush( - new DefaultSocks4CommandResponse(Socks4CommandStatus.REJECTED_OR_FAILED) - ); - SocksServerUtils.closeOnFlush(ctx.channel()); - } - }); - } else if (message instanceof Socks5CommandRequest) { - final Socks5CommandRequest request = (Socks5CommandRequest) message; - Promise promise = ctx.executor().newPromise(); - promise.asFuture().addListener(future -> { - final Channel outboundChannel = future.getNow(); - if (future.isSuccess()) { - Future responseFuture = - ctx.channel().writeAndFlush(new DefaultSocks5CommandResponse( - Socks5CommandStatus.SUCCESS, - request.dstAddrType(), - request.dstAddr(), - request.dstPort())); - - responseFuture.addListener(fut -> { - ctx.pipeline().remove(this); - outboundChannel.pipeline().addLast(new RelayHandler(ctx.channel())); - ctx.pipeline().addLast(new RelayHandler(outboundChannel)); - }); - } else { - ctx.channel().writeAndFlush(new DefaultSocks5CommandResponse( - Socks5CommandStatus.FAILURE, request.dstAddrType())); - SocksServerUtils.closeOnFlush(ctx.channel()); - } - }); - - final Channel inboundChannel = ctx.channel(); - b.group(inboundChannel.executor()) - .channel(NioSocketChannel.class) - .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000) - .option(ChannelOption.SO_KEEPALIVE, true) - .handler(new DirectClientHandler(promise)); - - b.connect(request.dstAddr(), request.dstPort()).addListener(future -> { - if (future.isSuccess()) { - // Connection established use handler provided results - } else { - // Close the connection if the connection attempt has failed. - ctx.channel().writeAndFlush( - new DefaultSocks5CommandResponse(Socks5CommandStatus.FAILURE, request.dstAddrType())); - SocksServerUtils.closeOnFlush(ctx.channel()); - } - }); - } else { - ctx.close(); - } - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - SocksServerUtils.closeOnFlush(ctx.channel()); - } -} diff --git a/example/src/main/java/io/netty/example/socksproxy/SocksServerHandler.java b/example/src/main/java/io/netty/example/socksproxy/SocksServerHandler.java deleted file mode 100644 index b49b8dddfb..0000000000 --- a/example/src/main/java/io/netty/example/socksproxy/SocksServerHandler.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.socksproxy; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.handler.codec.socksx.SocksMessage; -import io.netty.handler.codec.socksx.v4.Socks4CommandRequest; -import io.netty.handler.codec.socksx.v4.Socks4CommandType; -import io.netty.handler.codec.socksx.v5.DefaultSocks5InitialResponse; -import io.netty.handler.codec.socksx.v5.DefaultSocks5PasswordAuthResponse; -import io.netty.handler.codec.socksx.v5.Socks5AuthMethod; -import io.netty.handler.codec.socksx.v5.Socks5InitialRequest; -import io.netty.handler.codec.socksx.v5.Socks5CommandRequest; -import io.netty.handler.codec.socksx.v5.Socks5CommandRequestDecoder; -import io.netty.handler.codec.socksx.v5.Socks5CommandType; -import io.netty.handler.codec.socksx.v5.Socks5PasswordAuthRequest; -import io.netty.handler.codec.socksx.v5.Socks5PasswordAuthStatus; - -@ChannelHandler.Sharable -public final class SocksServerHandler extends SimpleChannelInboundHandler { - - public static final SocksServerHandler INSTANCE = new SocksServerHandler(); - - private SocksServerHandler() { } - - @Override - public void messageReceived(ChannelHandlerContext ctx, SocksMessage socksRequest) throws Exception { - switch (socksRequest.version()) { - case SOCKS4a: - Socks4CommandRequest socksV4CmdRequest = (Socks4CommandRequest) socksRequest; - if (socksV4CmdRequest.type() == Socks4CommandType.CONNECT) { - ctx.pipeline().addLast(new SocksServerConnectHandler()); - ctx.fireChannelRead(socksRequest); - ctx.pipeline().remove(this); - } else { - ctx.close(); - } - break; - case SOCKS5: - if (socksRequest instanceof Socks5InitialRequest) { - // auth support example - //ctx.pipeline().addFirst(new Socks5PasswordAuthRequestDecoder()); - //ctx.write(new DefaultSocks5AuthMethodResponse(Socks5AuthMethod.PASSWORD)); - ctx.pipeline().addFirst(new Socks5CommandRequestDecoder()); - ctx.write(new DefaultSocks5InitialResponse(Socks5AuthMethod.NO_AUTH)); - } else if (socksRequest instanceof Socks5PasswordAuthRequest) { - ctx.pipeline().addFirst(new Socks5CommandRequestDecoder()); - ctx.write(new DefaultSocks5PasswordAuthResponse(Socks5PasswordAuthStatus.SUCCESS)); - } else if (socksRequest instanceof Socks5CommandRequest) { - Socks5CommandRequest socks5CmdRequest = (Socks5CommandRequest) socksRequest; - if (socks5CmdRequest.type() == Socks5CommandType.CONNECT) { - ctx.pipeline().addLast(new SocksServerConnectHandler()); - ctx.fireChannelRead(socksRequest); - ctx.pipeline().remove(this); - } else { - ctx.close(); - } - } else { - ctx.close(); - } - break; - case UNKNOWN: - ctx.close(); - break; - } - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) { - ctx.flush(); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable throwable) { - throwable.printStackTrace(); - SocksServerUtils.closeOnFlush(ctx.channel()); - } -} diff --git a/example/src/main/java/io/netty/example/socksproxy/SocksServerInitializer.java b/example/src/main/java/io/netty/example/socksproxy/SocksServerInitializer.java deleted file mode 100644 index cafc6e0c6a..0000000000 --- a/example/src/main/java/io/netty/example/socksproxy/SocksServerInitializer.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.socksproxy; - -import io.netty.channel.ChannelInitializer; -import io.netty.channel.socket.SocketChannel; -import io.netty.handler.codec.socksx.SocksPortUnificationServerHandler; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; - -public final class SocksServerInitializer extends ChannelInitializer { - @Override - public void initChannel(SocketChannel ch) throws Exception { - ch.pipeline().addLast( - new LoggingHandler(LogLevel.DEBUG), - new SocksPortUnificationServerHandler(), - SocksServerHandler.INSTANCE); - } -} diff --git a/example/src/main/java/io/netty/example/socksproxy/SocksServerUtils.java b/example/src/main/java/io/netty/example/socksproxy/SocksServerUtils.java deleted file mode 100644 index f3d08a6ace..0000000000 --- a/example/src/main/java/io/netty/example/socksproxy/SocksServerUtils.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.socksproxy; - -import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.netty.channel.ChannelFutureListeners; - -public final class SocksServerUtils { - - /** - * Closes the specified channel after all queued write requests are flushed. - */ - public static void closeOnFlush(Channel ch) { - if (ch.isActive()) { - ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ch, ChannelFutureListeners.CLOSE); - } - } - - private SocksServerUtils() { } -} diff --git a/example/src/main/java/io/netty/example/stomp/StompClient.java b/example/src/main/java/io/netty/example/stomp/StompClient.java deleted file mode 100644 index 37752d246d..0000000000 --- a/example/src/main/java/io/netty/example/stomp/StompClient.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.stomp; - -import io.netty.bootstrap.Bootstrap; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.codec.stomp.StompSubframeAggregator; -import io.netty.handler.codec.stomp.StompSubframeDecoder; -import io.netty.handler.codec.stomp.StompSubframeEncoder; - - -/** - * very simple stomp client implementation example, requires running stomp server to actually work - * uses default username/password and destination values from hornetq message broker - */ -public final class StompClient { - - static final String HOST = System.getProperty("host", "127.0.0.1"); - static final int PORT = Integer.parseInt(System.getProperty("port", "61613")); - static final String LOGIN = System.getProperty("login", "guest"); - static final String PASSCODE = System.getProperty("passcode", "guest"); - static final String TOPIC = System.getProperty("topic", "jms.topic.exampleTopic"); - - public static void main(String[] args) throws Exception { - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - Bootstrap b = new Bootstrap(); - b.group(group).channel(NioSocketChannel.class); - b.handler(new ChannelInitializer() { - @Override - protected void initChannel(SocketChannel ch) throws Exception { - ChannelPipeline pipeline = ch.pipeline(); - pipeline.addLast("decoder", new StompSubframeDecoder()); - pipeline.addLast("encoder", new StompSubframeEncoder()); - pipeline.addLast("aggregator", new StompSubframeAggregator(1048576)); - pipeline.addLast("handler", new StompClientHandler()); - } - }); - - b.connect(HOST, PORT).get().closeFuture().sync(); - } finally { - group.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/stomp/StompClientHandler.java b/example/src/main/java/io/netty/example/stomp/StompClientHandler.java deleted file mode 100644 index b7668c6c80..0000000000 --- a/example/src/main/java/io/netty/example/stomp/StompClientHandler.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.stomp; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.handler.codec.stomp.DefaultStompFrame; -import io.netty.handler.codec.stomp.StompCommand; -import io.netty.handler.codec.stomp.StompFrame; -import io.netty.handler.codec.stomp.StompHeaders; - -/** - * STOMP client inbound handler implementation, which just passes received messages to listener - */ -public class StompClientHandler extends SimpleChannelInboundHandler { - - private enum ClientState { - AUTHENTICATING, - AUTHENTICATED, - SUBSCRIBED, - DISCONNECTING - } - - private ClientState state; - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - state = ClientState.AUTHENTICATING; - StompFrame connFrame = new DefaultStompFrame(StompCommand.CONNECT); - connFrame.headers().set(StompHeaders.ACCEPT_VERSION, "1.2"); - connFrame.headers().set(StompHeaders.HOST, StompClient.HOST); - connFrame.headers().set(StompHeaders.LOGIN, StompClient.LOGIN); - connFrame.headers().set(StompHeaders.PASSCODE, StompClient.PASSCODE); - ctx.writeAndFlush(connFrame); - } - - @Override - protected void messageReceived(ChannelHandlerContext ctx, StompFrame frame) throws Exception { - String subscrReceiptId = "001"; - String disconReceiptId = "002"; - switch (frame.command()) { - case CONNECTED: - StompFrame subscribeFrame = new DefaultStompFrame(StompCommand.SUBSCRIBE); - subscribeFrame.headers().set(StompHeaders.DESTINATION, StompClient.TOPIC); - subscribeFrame.headers().set(StompHeaders.RECEIPT, subscrReceiptId); - subscribeFrame.headers().set(StompHeaders.ID, "1"); - System.out.println("connected, sending subscribe frame: " + subscribeFrame); - state = ClientState.AUTHENTICATED; - ctx.writeAndFlush(subscribeFrame); - break; - case RECEIPT: - String receiptHeader = frame.headers().getAsString(StompHeaders.RECEIPT_ID); - if (state == ClientState.AUTHENTICATED && receiptHeader.equals(subscrReceiptId)) { - StompFrame msgFrame = new DefaultStompFrame(StompCommand.SEND); - msgFrame.headers().set(StompHeaders.DESTINATION, StompClient.TOPIC); - msgFrame.content().writeBytes("some payload".getBytes()); - System.out.println("subscribed, sending message frame: " + msgFrame); - state = ClientState.SUBSCRIBED; - ctx.writeAndFlush(msgFrame); - } else if (state == ClientState.DISCONNECTING && receiptHeader.equals(disconReceiptId)) { - System.out.println("disconnected"); - ctx.close(); - } else { - throw new IllegalStateException("received: " + frame + ", while internal state is " + state); - } - break; - case MESSAGE: - if (state == ClientState.SUBSCRIBED) { - System.out.println("received frame: " + frame); - StompFrame disconnFrame = new DefaultStompFrame(StompCommand.DISCONNECT); - disconnFrame.headers().set(StompHeaders.RECEIPT, disconReceiptId); - System.out.println("sending disconnect frame: " + disconnFrame); - state = ClientState.DISCONNECTING; - ctx.writeAndFlush(disconnFrame); - } - break; - default: - break; - } - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - cause.printStackTrace(); - ctx.close(); - } -} diff --git a/example/src/main/java/io/netty/example/stomp/websocket/StompChatHandler.java b/example/src/main/java/io/netty/example/stomp/websocket/StompChatHandler.java deleted file mode 100644 index 59b7a2b235..0000000000 --- a/example/src/main/java/io/netty/example/stomp/websocket/StompChatHandler.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.stomp.websocket; - -import io.netty.channel.ChannelFutureListeners; -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.handler.codec.DecoderResult; -import io.netty.handler.codec.stomp.DefaultStompFrame; -import io.netty.handler.codec.stomp.StompCommand; -import io.netty.handler.codec.stomp.StompFrame; -import io.netty.util.CharsetUtil; - -import java.util.HashSet; -import java.util.Iterator; -import java.util.Map.Entry; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -import static io.netty.handler.codec.stomp.StompHeaders.ACCEPT_VERSION; -import static io.netty.handler.codec.stomp.StompHeaders.CONTENT_LENGTH; -import static io.netty.handler.codec.stomp.StompHeaders.CONTENT_TYPE; -import static io.netty.handler.codec.stomp.StompHeaders.DESTINATION; -import static io.netty.handler.codec.stomp.StompHeaders.HEART_BEAT; -import static io.netty.handler.codec.stomp.StompHeaders.ID; -import static io.netty.handler.codec.stomp.StompHeaders.MESSAGE; -import static io.netty.handler.codec.stomp.StompHeaders.MESSAGE_ID; -import static io.netty.handler.codec.stomp.StompHeaders.RECEIPT; -import static io.netty.handler.codec.stomp.StompHeaders.RECEIPT_ID; -import static io.netty.handler.codec.stomp.StompHeaders.SERVER; -import static io.netty.handler.codec.stomp.StompHeaders.SUBSCRIPTION; -import static io.netty.handler.codec.stomp.StompHeaders.VERSION; - -@Sharable -public class StompChatHandler extends SimpleChannelInboundHandler { - - private final ConcurrentMap> chatDestinations = - new ConcurrentHashMap>(); - - @Override - protected void messageReceived(ChannelHandlerContext ctx, StompFrame inboundFrame) throws Exception { - DecoderResult decoderResult = inboundFrame.decoderResult(); - if (decoderResult.isFailure()) { - sendErrorFrame("rejected frame", decoderResult.toString(), ctx); - return; - } - - switch (inboundFrame.command()) { - case STOMP: - case CONNECT: - onConnect(ctx, inboundFrame); - break; - case SUBSCRIBE: - onSubscribe(ctx, inboundFrame); - break; - case SEND: - onSend(ctx, inboundFrame); - break; - case UNSUBSCRIBE: - onUnsubscribe(ctx, inboundFrame); - break; - case DISCONNECT: - onDisconnect(ctx, inboundFrame); - break; - default: - sendErrorFrame("unsupported command", - "Received unsupported command " + inboundFrame.command(), ctx); - } - } - - private void onSubscribe(ChannelHandlerContext ctx, StompFrame inboundFrame) { - String destination = inboundFrame.headers().getAsString(DESTINATION); - String subscriptionId = inboundFrame.headers().getAsString(ID); - - if (destination == null || subscriptionId == null) { - sendErrorFrame("missed header", "Required 'destination' or 'id' header missed", ctx); - return; - } - - Set subscriptions = chatDestinations.get(destination); - if (subscriptions == null) { - subscriptions = new HashSet(); - Set previousSubscriptions = chatDestinations.putIfAbsent(destination, subscriptions); - if (previousSubscriptions != null) { - subscriptions = previousSubscriptions; - } - } - - final StompSubscription subscription = new StompSubscription(subscriptionId, destination, ctx.channel()); - if (subscriptions.contains(subscription)) { - sendErrorFrame("duplicate subscription", - "Received duplicate subscription id=" + subscriptionId, ctx); - return; - } - - subscriptions.add(subscription); - ctx.channel().closeFuture().addListener(future -> { - chatDestinations.get(subscription.destination()).remove(subscription); - }); - - String receiptId = inboundFrame.headers().getAsString(RECEIPT); - if (receiptId != null) { - StompFrame receiptFrame = new DefaultStompFrame(StompCommand.RECEIPT); - receiptFrame.headers().set(RECEIPT_ID, receiptId); - ctx.writeAndFlush(receiptFrame); - } - } - - private void onSend(ChannelHandlerContext ctx, StompFrame inboundFrame) { - String destination = inboundFrame.headers().getAsString(DESTINATION); - if (destination == null) { - sendErrorFrame("missed header", "required 'destination' header missed", ctx); - return; - } - - Set subscriptions = chatDestinations.get(destination); - for (StompSubscription subscription : subscriptions) { - subscription.channel().writeAndFlush(transformToMessage(inboundFrame, subscription)); - } - } - - private void onUnsubscribe(ChannelHandlerContext ctx, StompFrame inboundFrame) { - String subscriptionId = inboundFrame.headers().getAsString(SUBSCRIPTION); - for (Entry> entry : chatDestinations.entrySet()) { - Iterator iterator = entry.getValue().iterator(); - while (iterator.hasNext()) { - StompSubscription subscription = iterator.next(); - if (subscription.id().equals(subscriptionId) && subscription.channel().equals(ctx.channel())) { - iterator.remove(); - return; - } - } - } - } - - private static void onConnect(ChannelHandlerContext ctx, StompFrame inboundFrame) { - String acceptVersions = inboundFrame.headers().getAsString(ACCEPT_VERSION); - StompVersion handshakeAcceptVersion = ctx.channel().attr(StompVersion.CHANNEL_ATTRIBUTE_KEY).get(); - if (acceptVersions == null || !acceptVersions.contains(handshakeAcceptVersion.version())) { - sendErrorFrame("invalid version", - "Received invalid version, expected " + handshakeAcceptVersion.version(), ctx); - return; - } - - StompFrame connectedFrame = new DefaultStompFrame(StompCommand.CONNECTED); - connectedFrame.headers() - .set(VERSION, handshakeAcceptVersion.version()) - .set(SERVER, "Netty-Server") - .set(HEART_BEAT, "0,0"); - ctx.writeAndFlush(connectedFrame); - } - - private static void onDisconnect(ChannelHandlerContext ctx, StompFrame inboundFrame) { - String receiptId = inboundFrame.headers().getAsString(RECEIPT); - if (receiptId == null) { - ctx.close(); - return; - } - - StompFrame receiptFrame = new DefaultStompFrame(StompCommand.RECEIPT); - receiptFrame.headers().set(RECEIPT_ID, receiptId); - ctx.writeAndFlush(receiptFrame).addListener(ctx, ChannelFutureListeners.CLOSE); - } - - private static void sendErrorFrame(String message, String description, ChannelHandlerContext ctx) { - StompFrame errorFrame = new DefaultStompFrame(StompCommand.ERROR); - errorFrame.headers().set(MESSAGE, message); - - if (description != null) { - errorFrame.content().writeCharSequence(description, CharsetUtil.UTF_8); - } - - ctx.writeAndFlush(errorFrame).addListener(ctx, ChannelFutureListeners.CLOSE); - } - - private static StompFrame transformToMessage(StompFrame sendFrame, StompSubscription subscription) { - StompFrame messageFrame = new DefaultStompFrame(StompCommand.MESSAGE, sendFrame.content().retainedDuplicate()); - String id = UUID.randomUUID().toString(); - messageFrame.headers() - .set(MESSAGE_ID, id) - .set(SUBSCRIPTION, subscription.id()) - .set(CONTENT_LENGTH, Integer.toString(messageFrame.content().readableBytes())); - - CharSequence contentType = sendFrame.headers().get(CONTENT_TYPE); - if (contentType != null) { - messageFrame.headers().set(CONTENT_TYPE, contentType); - } - - return messageFrame; - } -} diff --git a/example/src/main/java/io/netty/example/stomp/websocket/StompSubscription.java b/example/src/main/java/io/netty/example/stomp/websocket/StompSubscription.java deleted file mode 100644 index c3b57cafb2..0000000000 --- a/example/src/main/java/io/netty/example/stomp/websocket/StompSubscription.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.stomp.websocket; - -import io.netty.channel.Channel; -import io.netty.util.internal.ObjectUtil; - -public final class StompSubscription { - - private final String id; - private final String destination; - private final Channel channel; - - public StompSubscription(String id, String destination, Channel channel) { - this.id = ObjectUtil.checkNotNull(id, "id"); - this.destination = ObjectUtil.checkNotNull(destination, "destination"); - this.channel = ObjectUtil.checkNotNull(channel, "channel"); - } - - public String id() { - return id; - } - - public String destination() { - return destination; - } - - public Channel channel() { - return channel; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - - if (obj == null || getClass() != obj.getClass()) { - return false; - } - - StompSubscription that = (StompSubscription) obj; - - if (!id.equals(that.id)) { - return false; - } - - if (!destination.equals(that.destination)) { - return false; - } - - return channel.equals(that.channel); - } - - @Override - public int hashCode() { - int result = id.hashCode(); - result = 31 * result + destination.hashCode(); - result = 31 * result + channel.hashCode(); - return result; - } -} diff --git a/example/src/main/java/io/netty/example/stomp/websocket/StompVersion.java b/example/src/main/java/io/netty/example/stomp/websocket/StompVersion.java deleted file mode 100644 index aaeceb20d3..0000000000 --- a/example/src/main/java/io/netty/example/stomp/websocket/StompVersion.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.stomp.websocket; - -import io.netty.util.AttributeKey; -import io.netty.util.internal.StringUtil; - -import java.util.ArrayList; -import java.util.List; - -public enum StompVersion { - - STOMP_V11("1.1", "v11.stomp"), - - STOMP_V12("1.2", "v12.stomp"); - - public static final AttributeKey CHANNEL_ATTRIBUTE_KEY = AttributeKey.valueOf("stomp_version"); - public static final String SUB_PROTOCOLS; - - static { - List subProtocols = new ArrayList(values().length); - for (StompVersion stompVersion : values()) { - subProtocols.add(stompVersion.subProtocol); - } - - SUB_PROTOCOLS = StringUtil.join(",", subProtocols).toString(); - } - - private final String version; - private final String subProtocol; - - StompVersion(String version, String subProtocol) { - this.version = version; - this.subProtocol = subProtocol; - } - - public String version() { - return version; - } - - public String subProtocol() { - return subProtocol; - } - - public static StompVersion findBySubProtocol(String subProtocol) { - if (subProtocol != null) { - for (StompVersion stompVersion : values()) { - if (stompVersion.subProtocol().equals(subProtocol)) { - return stompVersion; - } - } - } - - throw new IllegalArgumentException("Not found StompVersion for '" + subProtocol + "'"); - } -} diff --git a/example/src/main/java/io/netty/example/stomp/websocket/StompWebSocketChatServer.java b/example/src/main/java/io/netty/example/stomp/websocket/StompWebSocketChatServer.java deleted file mode 100644 index d1046c0c8a..0000000000 --- a/example/src/main/java/io/netty/example/stomp/websocket/StompWebSocketChatServer.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.stomp.websocket; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioServerSocketChannel; - -public class StompWebSocketChatServer { - - static final int PORT = Integer.parseInt(System.getProperty("port", "8080")); - - public void start(final int port) throws Exception { - MultithreadEventLoopGroup boosGroup = new MultithreadEventLoopGroup(1, NioHandler.newFactory()); - MultithreadEventLoopGroup workerGroup = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - ServerBootstrap bootstrap = new ServerBootstrap() - .group(boosGroup, workerGroup) - .channel(NioServerSocketChannel.class) - .childHandler(new StompWebSocketChatServerInitializer("/chat")); - bootstrap.bind(port).addListener(future -> { - if (future.isSuccess()) { - System.out.println("Open your web browser and navigate to http://127.0.0.1:" + PORT + '/'); - } else { - System.out.println("Cannot start server, follows exception " + future.cause()); - } - }).get().closeFuture().sync(); - } finally { - boosGroup.shutdownGracefully(); - workerGroup.shutdownGracefully(); - } - } - - public static void main(String[] args) throws Exception { - new StompWebSocketChatServer().start(PORT); - } -} diff --git a/example/src/main/java/io/netty/example/stomp/websocket/StompWebSocketChatServerInitializer.java b/example/src/main/java/io/netty/example/stomp/websocket/StompWebSocketChatServerInitializer.java deleted file mode 100644 index d5f494780c..0000000000 --- a/example/src/main/java/io/netty/example/stomp/websocket/StompWebSocketChatServerInitializer.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.stomp.websocket; - -import io.netty.channel.ChannelInitializer; -import io.netty.channel.socket.SocketChannel; -import io.netty.handler.codec.http.HttpObjectAggregator; -import io.netty.handler.codec.http.HttpServerCodec; -import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; -import io.netty.util.internal.ObjectUtil; - -public class StompWebSocketChatServerInitializer extends ChannelInitializer { - - private final String chatPath; - private final StompWebSocketProtocolCodec stompWebSocketProtocolCodec; - - public StompWebSocketChatServerInitializer(String chatPath) { - this.chatPath = ObjectUtil.checkNotNull(chatPath, "chatPath"); - stompWebSocketProtocolCodec = new StompWebSocketProtocolCodec(); - } - - @Override - protected void initChannel(SocketChannel channel) throws Exception { - channel.pipeline() - .addLast(new HttpServerCodec()) - .addLast(new HttpObjectAggregator(65536)) - .addLast(StompWebSocketClientPageHandler.INSTANCE) - .addLast(new WebSocketServerProtocolHandler(chatPath, StompVersion.SUB_PROTOCOLS)) - .addLast(stompWebSocketProtocolCodec); - } -} diff --git a/example/src/main/java/io/netty/example/stomp/websocket/StompWebSocketClientPageHandler.java b/example/src/main/java/io/netty/example/stomp/websocket/StompWebSocketClientPageHandler.java deleted file mode 100644 index e1b633b85f..0000000000 --- a/example/src/main/java/io/netty/example/stomp/websocket/StompWebSocketClientPageHandler.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.stomp.websocket; - -import io.netty.channel.ChannelFutureListeners; -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.DefaultFileRegion; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.handler.codec.http.DefaultFullHttpResponse; -import io.netty.handler.codec.http.DefaultHttpResponse; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.HttpUtil; -import io.netty.handler.codec.http.LastHttpContent; -import io.netty.util.CharsetUtil; - -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.net.URL; - -import static io.netty.handler.codec.http.HttpHeaderNames.*; -import static io.netty.handler.codec.http.HttpHeaderValues.KEEP_ALIVE; -import static io.netty.handler.codec.http.HttpHeaderValues.*; -import static io.netty.handler.codec.http.HttpResponseStatus.*; -import static io.netty.handler.codec.http.HttpVersion.*; - -@Sharable -public final class StompWebSocketClientPageHandler extends SimpleChannelInboundHandler { - - static final StompWebSocketClientPageHandler INSTANCE = new StompWebSocketClientPageHandler(); - - private StompWebSocketClientPageHandler() { - } - - @Override - protected void messageReceived(ChannelHandlerContext ctx, FullHttpRequest request) { - if (request.headers().contains(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET, true)) { - ctx.fireChannelRead(request.retain()); - return; - } - - if (request.decoderResult().isFailure()) { - FullHttpResponse badRequest = new DefaultFullHttpResponse(request.protocolVersion(), BAD_REQUEST); - sendResponse(badRequest, ctx, true); - return; - } - - if (!sendResource(request, ctx)) { - FullHttpResponse notFound = new DefaultFullHttpResponse(request.protocolVersion(), NOT_FOUND); - notFound.headers().set(CONTENT_TYPE, TEXT_PLAIN); - String payload = "Requested resource " + request.uri() + " not found"; - notFound.content().writeCharSequence(payload, CharsetUtil.UTF_8); - HttpUtil.setContentLength(notFound, notFound.content().readableBytes()); - sendResponse(notFound, ctx, true); - } - } - - private static boolean sendResource(FullHttpRequest request, ChannelHandlerContext ctx) { - if (request.uri().isEmpty() || !request.uri().startsWith("/")) { - return false; - } - - String requestResource = request.uri().substring(1); - if (requestResource.isEmpty()) { - requestResource = "index.html"; - } - - URL resourceUrl = INSTANCE.getClass().getResource(requestResource); - if (resourceUrl == null) { - return false; - } - - RandomAccessFile raf = null; - long fileLength = -1L; - try { - raf = new RandomAccessFile(resourceUrl.getFile(), "r"); - fileLength = raf.length(); - } catch (FileNotFoundException fne) { - System.out.println("File not found " + fne.getMessage()); - return false; - } catch (IOException io) { - System.out.println("Cannot read file length " + io.getMessage()); - return false; - } finally { - if (fileLength < 0 && raf != null) { - try { - raf.close(); - } catch (IOException io) { - // Nothing to do - } - } - } - - HttpResponse response = new DefaultHttpResponse(request.protocolVersion(), OK); - HttpUtil.setContentLength(response, fileLength); - - String contentType = "application/octet-stream"; - if (requestResource.endsWith("html")) { - contentType = "text/html; charset=UTF-8"; - } else if (requestResource.endsWith("css")) { - contentType = "text/css; charset=UTF-8"; - } else if (requestResource.endsWith("js")) { - contentType = "application/javascript"; - } - - response.headers().set(CONTENT_TYPE, contentType); - sendResponse(response, ctx, false); - ctx.write(new DefaultFileRegion(raf.getChannel(), 0, fileLength)); - ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT); - return true; - } - - private static void sendResponse(HttpResponse response, ChannelHandlerContext ctx, boolean autoFlush) { - if (HttpUtil.isKeepAlive(response)) { - if (response.protocolVersion().equals(HTTP_1_0)) { - response.headers().set(CONNECTION, KEEP_ALIVE); - } - ctx.write(response); - } else { - response.headers().set(CONNECTION, CLOSE); - ctx.write(response).addListener(ctx, ChannelFutureListeners.CLOSE); - } - - if (autoFlush) { - ctx.flush(); - } - } -} diff --git a/example/src/main/java/io/netty/example/stomp/websocket/StompWebSocketFrameEncoder.java b/example/src/main/java/io/netty/example/stomp/websocket/StompWebSocketFrameEncoder.java deleted file mode 100644 index 141b57a487..0000000000 --- a/example/src/main/java/io/netty/example/stomp/websocket/StompWebSocketFrameEncoder.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.stomp.websocket; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; -import io.netty.handler.codec.http.websocketx.ContinuationWebSocketFrame; -import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; -import io.netty.handler.codec.http.websocketx.WebSocketFrame; -import io.netty.handler.codec.stomp.LastStompContentSubframe; -import io.netty.handler.codec.stomp.StompContentSubframe; -import io.netty.handler.codec.stomp.StompFrame; -import io.netty.handler.codec.stomp.StompHeaders; -import io.netty.handler.codec.stomp.StompHeadersSubframe; -import io.netty.handler.codec.stomp.StompSubframe; -import io.netty.handler.codec.stomp.StompSubframeEncoder; - -import java.util.List; - -public class StompWebSocketFrameEncoder extends StompSubframeEncoder { - - @Override - public void encode(ChannelHandlerContext ctx, StompSubframe msg, List out) throws Exception { - super.encode(ctx, msg, out); - } - - @Override - protected WebSocketFrame convertFullFrame(StompFrame original, ByteBuf encoded) { - if (isTextFrame(original)) { - return new TextWebSocketFrame(encoded); - } - - return new BinaryWebSocketFrame(encoded); - } - - @Override - protected WebSocketFrame convertHeadersSubFrame(StompHeadersSubframe original, ByteBuf encoded) { - if (isTextFrame(original)) { - return new TextWebSocketFrame(false, 0, encoded); - } - - return new BinaryWebSocketFrame(false, 0, encoded); - } - - @Override - protected WebSocketFrame convertContentSubFrame(StompContentSubframe original, ByteBuf encoded) { - if (original instanceof LastStompContentSubframe) { - return new ContinuationWebSocketFrame(true, 0, encoded); - } - - return new ContinuationWebSocketFrame(false, 0, encoded); - } - - private static boolean isTextFrame(StompHeadersSubframe headersSubframe) { - String contentType = headersSubframe.headers().getAsString(StompHeaders.CONTENT_TYPE); - return contentType != null && (contentType.startsWith("text") || contentType.startsWith("application/json")); - } -} diff --git a/example/src/main/java/io/netty/example/stomp/websocket/StompWebSocketProtocolCodec.java b/example/src/main/java/io/netty/example/stomp/websocket/StompWebSocketProtocolCodec.java deleted file mode 100644 index dad60c0b01..0000000000 --- a/example/src/main/java/io/netty/example/stomp/websocket/StompWebSocketProtocolCodec.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.stomp.websocket; - -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToMessageCodec; -import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; -import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; -import io.netty.handler.codec.http.websocketx.WebSocketFrame; -import io.netty.handler.codec.http.websocketx.WebSocketFrameAggregator; -import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; -import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler.HandshakeComplete; -import io.netty.handler.codec.stomp.StompSubframe; -import io.netty.handler.codec.stomp.StompSubframeAggregator; -import io.netty.handler.codec.stomp.StompSubframeDecoder; - -import java.util.List; - -@Sharable -public class StompWebSocketProtocolCodec extends MessageToMessageCodec { - - private final StompChatHandler stompChatHandler = new StompChatHandler(); - private final StompWebSocketFrameEncoder stompWebSocketFrameEncoder = new StompWebSocketFrameEncoder(); - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (evt instanceof WebSocketServerProtocolHandler.HandshakeComplete) { - StompVersion stompVersion = StompVersion.findBySubProtocol(((HandshakeComplete) evt).selectedSubprotocol()); - ctx.channel().attr(StompVersion.CHANNEL_ATTRIBUTE_KEY).set(stompVersion); - ctx.pipeline() - .addLast(new WebSocketFrameAggregator(65536)) - .addLast(new StompSubframeDecoder()) - .addLast(new StompSubframeAggregator(65536)) - .addLast(stompChatHandler) - .remove(StompWebSocketClientPageHandler.INSTANCE); - } else { - super.userEventTriggered(ctx, evt); - } - } - - @Override - protected void encode(ChannelHandlerContext ctx, StompSubframe stompFrame, List out) throws Exception { - stompWebSocketFrameEncoder.encode(ctx, stompFrame, out); - } - - @Override - protected void decode(ChannelHandlerContext ctx, WebSocketFrame webSocketFrame) { - if (webSocketFrame instanceof TextWebSocketFrame || webSocketFrame instanceof BinaryWebSocketFrame) { - ctx.fireChannelRead(webSocketFrame.content().retain()); - } else { - ctx.close(); - } - } -} diff --git a/example/src/main/java/io/netty/example/telnet/TelnetClient.java b/example/src/main/java/io/netty/example/telnet/TelnetClient.java deleted file mode 100644 index 793b78ebbe..0000000000 --- a/example/src/main/java/io/netty/example/telnet/TelnetClient.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.telnet; - -import io.netty.bootstrap.Bootstrap; -import io.netty.channel.Channel; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.util.InsecureTrustManagerFactory; -import io.netty.util.concurrent.Future; - -import java.io.BufferedReader; -import java.io.InputStreamReader; - -/** - * Simplistic telnet client. - */ -public final class TelnetClient { - - static final boolean SSL = System.getProperty("ssl") != null; - static final String HOST = System.getProperty("host", "127.0.0.1"); - static final int PORT = Integer.parseInt(System.getProperty("port", SSL? "8992" : "8023")); - - public static void main(String[] args) throws Exception { - // Configure SSL. - final SslContext sslCtx; - if (SSL) { - sslCtx = SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE).build(); - } else { - sslCtx = null; - } - - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - Bootstrap b = new Bootstrap(); - b.group(group) - .channel(NioSocketChannel.class) - .handler(new TelnetClientInitializer(sslCtx)); - - // Start the connection attempt. - Channel ch = b.connect(HOST, PORT).get(); - - // Read commands from the stdin. - Future lastWriteFuture = null; - BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); - for (;;) { - String line = in.readLine(); - if (line == null) { - break; - } - - // Sends the received line to the server. - lastWriteFuture = ch.writeAndFlush(line + "\r\n"); - - // If user typed the 'bye' command, wait until the server closes - // the connection. - if ("bye".equalsIgnoreCase(line)) { - ch.closeFuture().sync(); - break; - } - } - - // Wait until all messages are flushed before closing the channel. - if (lastWriteFuture != null) { - lastWriteFuture.sync(); - } - } finally { - group.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/telnet/TelnetClientHandler.java b/example/src/main/java/io/netty/example/telnet/TelnetClientHandler.java deleted file mode 100644 index c1c723385a..0000000000 --- a/example/src/main/java/io/netty/example/telnet/TelnetClientHandler.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.telnet; - -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; - -/** - * Handles a client-side channel. - */ -@Sharable -public class TelnetClientHandler extends SimpleChannelInboundHandler { - - @Override - protected void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception { - System.err.println(msg); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - cause.printStackTrace(); - ctx.close(); - } -} diff --git a/example/src/main/java/io/netty/example/telnet/TelnetClientInitializer.java b/example/src/main/java/io/netty/example/telnet/TelnetClientInitializer.java deleted file mode 100644 index 67ddee7cd8..0000000000 --- a/example/src/main/java/io/netty/example/telnet/TelnetClientInitializer.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.telnet; - -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.socket.SocketChannel; -import io.netty.handler.codec.DelimiterBasedFrameDecoder; -import io.netty.handler.codec.Delimiters; -import io.netty.handler.codec.string.StringDecoder; -import io.netty.handler.codec.string.StringEncoder; -import io.netty.handler.ssl.SslContext; - -/** - * Creates a newly configured {@link ChannelPipeline} for a new channel. - */ -public class TelnetClientInitializer extends ChannelInitializer { - - private static final StringDecoder DECODER = new StringDecoder(); - private static final StringEncoder ENCODER = new StringEncoder(); - - private static final TelnetClientHandler CLIENT_HANDLER = new TelnetClientHandler(); - - private final SslContext sslCtx; - - public TelnetClientInitializer(SslContext sslCtx) { - this.sslCtx = sslCtx; - } - - @Override - public void initChannel(SocketChannel ch) { - ChannelPipeline pipeline = ch.pipeline(); - - if (sslCtx != null) { - pipeline.addLast(sslCtx.newHandler(ch.alloc(), TelnetClient.HOST, TelnetClient.PORT)); - } - - // Add the text line codec combination first, - pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter())); - pipeline.addLast(DECODER); - pipeline.addLast(ENCODER); - - // and then business logic. - pipeline.addLast(CLIENT_HANDLER); - } -} diff --git a/example/src/main/java/io/netty/example/telnet/TelnetServer.java b/example/src/main/java/io/netty/example/telnet/TelnetServer.java deleted file mode 100644 index 16fae056e2..0000000000 --- a/example/src/main/java/io/netty/example/telnet/TelnetServer.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.telnet; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.util.SelfSignedCertificate; - -/** - * Simplistic telnet server. - */ -public final class TelnetServer { - - static final boolean SSL = System.getProperty("ssl") != null; - static final int PORT = Integer.parseInt(System.getProperty("port", SSL? "8992" : "8023")); - - public static void main(String[] args) throws Exception { - // Configure SSL. - final SslContext sslCtx; - if (SSL) { - SelfSignedCertificate ssc = new SelfSignedCertificate(); - sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build(); - } else { - sslCtx = null; - } - - EventLoopGroup bossGroup = new MultithreadEventLoopGroup(1, NioHandler.newFactory()); - EventLoopGroup workerGroup = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - ServerBootstrap b = new ServerBootstrap(); - b.group(bossGroup, workerGroup) - .channel(NioServerSocketChannel.class) - .handler(new LoggingHandler(LogLevel.INFO)) - .childHandler(new TelnetServerInitializer(sslCtx)); - - b.bind(PORT).get().closeFuture().sync(); - } finally { - bossGroup.shutdownGracefully(); - workerGroup.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/telnet/TelnetServerHandler.java b/example/src/main/java/io/netty/example/telnet/TelnetServerHandler.java deleted file mode 100644 index 81c3f8da9d..0000000000 --- a/example/src/main/java/io/netty/example/telnet/TelnetServerHandler.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.telnet; - -import io.netty.channel.ChannelFutureListeners; -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.util.concurrent.Future; - -import java.net.InetAddress; -import java.util.Date; - -/** - * Handles a server-side channel. - */ -@Sharable -public class TelnetServerHandler extends SimpleChannelInboundHandler { - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - // Send greeting for a new connection. - ctx.write("Welcome to " + InetAddress.getLocalHost().getHostName() + "!\r\n"); - ctx.write("It is " + new Date() + " now.\r\n"); - ctx.flush(); - } - - @Override - public void messageReceived(ChannelHandlerContext ctx, String request) throws Exception { - // Generate and write a response. - String response; - boolean close = false; - if (request.isEmpty()) { - response = "Please type something.\r\n"; - } else if ("bye".equals(request.toLowerCase())) { - response = "Have a good day!\r\n"; - close = true; - } else { - response = "Did you say '" + request + "'?\r\n"; - } - - // We do not need to write a ChannelBuffer here. - // We know the encoder inserted at TelnetPipelineFactory will do the conversion. - Future future = ctx.write(response); - - // Close the connection after sending 'Have a good day!' - // if the client has sent 'bye'. - if (close) { - future.addListener(ctx, ChannelFutureListeners.CLOSE); - } - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) { - ctx.flush(); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - cause.printStackTrace(); - ctx.close(); - } -} diff --git a/example/src/main/java/io/netty/example/telnet/TelnetServerInitializer.java b/example/src/main/java/io/netty/example/telnet/TelnetServerInitializer.java deleted file mode 100644 index 01e51ee2aa..0000000000 --- a/example/src/main/java/io/netty/example/telnet/TelnetServerInitializer.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.telnet; - -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.socket.SocketChannel; -import io.netty.handler.codec.DelimiterBasedFrameDecoder; -import io.netty.handler.codec.Delimiters; -import io.netty.handler.codec.string.StringDecoder; -import io.netty.handler.codec.string.StringEncoder; -import io.netty.handler.ssl.SslContext; - -/** - * Creates a newly configured {@link ChannelPipeline} for a new channel. - */ -public class TelnetServerInitializer extends ChannelInitializer { - - private static final StringDecoder DECODER = new StringDecoder(); - private static final StringEncoder ENCODER = new StringEncoder(); - - private static final TelnetServerHandler SERVER_HANDLER = new TelnetServerHandler(); - - private final SslContext sslCtx; - - public TelnetServerInitializer(SslContext sslCtx) { - this.sslCtx = sslCtx; - } - - @Override - public void initChannel(SocketChannel ch) throws Exception { - ChannelPipeline pipeline = ch.pipeline(); - - if (sslCtx != null) { - pipeline.addLast(sslCtx.newHandler(ch.alloc())); - } - - // Add the text line codec combination first, - pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter())); - // the encoder and decoder are static as these are sharable - pipeline.addLast(DECODER); - pipeline.addLast(ENCODER); - - // and then business logic. - pipeline.addLast(SERVER_HANDLER); - } -} diff --git a/example/src/main/java/io/netty/example/uptime/UptimeClient.java b/example/src/main/java/io/netty/example/uptime/UptimeClient.java deleted file mode 100644 index f6d1fd5e6f..0000000000 --- a/example/src/main/java/io/netty/example/uptime/UptimeClient.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.uptime; - -import io.netty.bootstrap.Bootstrap; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.timeout.IdleStateHandler; - - -/** - * Connects to a server periodically to measure and print the uptime of the - * server. This example demonstrates how to implement reliable reconnection - * mechanism in Netty. - */ -public final class UptimeClient { - - static final String HOST = System.getProperty("host", "127.0.0.1"); - static final int PORT = Integer.parseInt(System.getProperty("port", "8080")); - // Sleep 5 seconds before a reconnection attempt. - static final int RECONNECT_DELAY = Integer.parseInt(System.getProperty("reconnectDelay", "5")); - // Reconnect when the server sends nothing for 10 seconds. - private static final int READ_TIMEOUT = Integer.parseInt(System.getProperty("readTimeout", "10")); - - private static final UptimeClientHandler handler = new UptimeClientHandler(); - private static final Bootstrap bs = new Bootstrap(); - - public static void main(String[] args) throws Exception { - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - bs.group(group) - .channel(NioSocketChannel.class) - .remoteAddress(HOST, PORT) - .handler(new ChannelInitializer() { - @Override - protected void initChannel(SocketChannel ch) throws Exception { - ch.pipeline().addLast(new IdleStateHandler(READ_TIMEOUT, 0, 0), handler); - } - }); - bs.connect(); - } - - static void connect() { - bs.connect().addListener(future -> { - if (future.cause() != null) { - handler.startTime = -1; - handler.println("Failed to connect: " + future.cause()); - } - }); - } -} diff --git a/example/src/main/java/io/netty/example/uptime/UptimeClientHandler.java b/example/src/main/java/io/netty/example/uptime/UptimeClientHandler.java deleted file mode 100644 index 6e3869b2fb..0000000000 --- a/example/src/main/java/io/netty/example/uptime/UptimeClientHandler.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.uptime; - -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.handler.timeout.IdleState; -import io.netty.handler.timeout.IdleStateEvent; - -import java.util.concurrent.TimeUnit; - -/** - * Keep reconnecting to the server while printing out the current uptime and - * connection attempt getStatus. - */ -@Sharable -public class UptimeClientHandler extends SimpleChannelInboundHandler { - - long startTime = -1; - - @Override - public void channelActive(ChannelHandlerContext ctx) { - if (startTime < 0) { - startTime = System.currentTimeMillis(); - } - println("Connected to: " + ctx.channel().remoteAddress()); - } - - @Override - public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { - // Discard received data - } - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { - if (!(evt instanceof IdleStateEvent)) { - return; - } - - IdleStateEvent e = (IdleStateEvent) evt; - if (e.state() == IdleState.READER_IDLE) { - // The connection was OK but there was no traffic for last period. - println("Disconnecting due to no inbound traffic"); - ctx.close(); - } - } - - @Override - public void channelInactive(final ChannelHandlerContext ctx) { - println("Disconnected from: " + ctx.channel().remoteAddress()); - } - - @Override - public void channelUnregistered(final ChannelHandlerContext ctx) throws Exception { - println("Sleeping for: " + UptimeClient.RECONNECT_DELAY + 's'); - - ctx.channel().executor().schedule(() -> { - println("Reconnecting to: " + UptimeClient.HOST + ':' + UptimeClient.PORT); - UptimeClient.connect(); - }, UptimeClient.RECONNECT_DELAY, TimeUnit.SECONDS); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - cause.printStackTrace(); - ctx.close(); - } - - void println(String msg) { - if (startTime < 0) { - System.err.format("[SERVER IS DOWN] %s%n", msg); - } else { - System.err.format("[UPTIME: %5ds] %s%n", (System.currentTimeMillis() - startTime) / 1000, msg); - } - } -} diff --git a/example/src/main/java/io/netty/example/uptime/UptimeServer.java b/example/src/main/java/io/netty/example/uptime/UptimeServer.java deleted file mode 100644 index c6d2655bb1..0000000000 --- a/example/src/main/java/io/netty/example/uptime/UptimeServer.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.uptime; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; - -/** - * Uptime server is served as a connection server. - * So it simply discards all message received. - */ -public final class UptimeServer { - private static final int PORT = Integer.parseInt(System.getProperty("port", "8080")); - private static final UptimeServerHandler handler = new UptimeServerHandler(); - - private UptimeServer() { - } - - public static void main(String[] args) throws Exception { - - EventLoopGroup bossGroup = new MultithreadEventLoopGroup(1, NioHandler.newFactory()); - EventLoopGroup workerGroup = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - ServerBootstrap b = new ServerBootstrap(); - b.group(bossGroup, workerGroup) - .channel(NioServerSocketChannel.class) - .handler(new LoggingHandler(LogLevel.INFO)) - .childHandler(new ChannelInitializer() { - @Override - public void initChannel(SocketChannel ch) { - ch.pipeline().addLast(handler); - } - }); - - // Bind and start to accept incoming connections. - Channel channel = b.bind(PORT).get(); - - // Wait until the server socket is closed. - // In this example, this does not happen, but you can do that to gracefully - // shut down your server. - channel.closeFuture().sync(); - } finally { - workerGroup.shutdownGracefully(); - bossGroup.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/uptime/UptimeServerHandler.java b/example/src/main/java/io/netty/example/uptime/UptimeServerHandler.java deleted file mode 100644 index 8893701ca5..0000000000 --- a/example/src/main/java/io/netty/example/uptime/UptimeServerHandler.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.uptime; - -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; - -@Sharable -public class UptimeServerHandler extends SimpleChannelInboundHandler { - @Override - public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { - // discard - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - // Close the connection when an exception is raised. - cause.printStackTrace(); - ctx.close(); - } -} diff --git a/example/src/main/java/io/netty/example/worldclock/WorldClockClient.java b/example/src/main/java/io/netty/example/worldclock/WorldClockClient.java deleted file mode 100644 index 39984ab3bf..0000000000 --- a/example/src/main/java/io/netty/example/worldclock/WorldClockClient.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.worldclock; - -import io.netty.bootstrap.Bootstrap; -import io.netty.channel.Channel; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.util.InsecureTrustManagerFactory; - -import java.util.Arrays; -import java.util.List; - -/** - * Sends a list of continent/city pairs to a {@link WorldClockServer} to - * get the local times of the specified cities. - */ -public final class WorldClockClient { - - static final boolean SSL = System.getProperty("ssl") != null; - static final String HOST = System.getProperty("host", "127.0.0.1"); - static final int PORT = Integer.parseInt(System.getProperty("port", "8463")); - static final List CITIES = Arrays.asList(System.getProperty( - "cities", "Asia/Seoul,Europe/Berlin,America/Los_Angeles").split(",")); - - public static void main(String[] args) throws Exception { - // Configure SSL. - final SslContext sslCtx; - if (SSL) { - sslCtx = SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE).build(); - } else { - sslCtx = null; - } - - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - Bootstrap b = new Bootstrap(); - b.group(group) - .channel(NioSocketChannel.class) - .handler(new WorldClockClientInitializer(sslCtx)); - - // Make a new connection. - Channel ch = b.connect(HOST, PORT).get(); - - // Get the handler instance to initiate the request. - WorldClockClientHandler handler = ch.pipeline().get(WorldClockClientHandler.class); - - // Request and get the response. - List response = handler.getLocalTimes(CITIES); - - // Close the connection. - ch.close(); - - // Print the response at last but not least. - for (int i = 0; i < CITIES.size(); i ++) { - System.out.format("%28s: %s%n", CITIES.get(i), response.get(i)); - } - } finally { - group.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/worldclock/WorldClockClientHandler.java b/example/src/main/java/io/netty/example/worldclock/WorldClockClientHandler.java deleted file mode 100644 index ff3555361e..0000000000 --- a/example/src/main/java/io/netty/example/worldclock/WorldClockClientHandler.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.worldclock; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.example.worldclock.WorldClockProtocol.Continent; -import io.netty.example.worldclock.WorldClockProtocol.LocalTime; -import io.netty.example.worldclock.WorldClockProtocol.LocalTimes; -import io.netty.example.worldclock.WorldClockProtocol.Location; -import io.netty.example.worldclock.WorldClockProtocol.Locations; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Formatter; -import java.util.List; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.regex.Pattern; - -public class WorldClockClientHandler extends SimpleChannelInboundHandler { - - private static final Pattern DELIM = Pattern.compile("/"); - - // Stateful properties - private volatile Channel channel; - private final BlockingQueue answer = new LinkedBlockingQueue<>(); - - public WorldClockClientHandler() { - super(false); - } - - public List getLocalTimes(Collection cities) { - Locations.Builder builder = Locations.newBuilder(); - - for (String c: cities) { - String[] components = DELIM.split(c); - builder.addLocation(Location.newBuilder(). - setContinent(Continent.valueOf(components[0].toUpperCase())). - setCity(components[1]).build()); - } - - channel.writeAndFlush(builder.build()); - - LocalTimes localTimes; - boolean interrupted = false; - for (;;) { - try { - localTimes = answer.take(); - break; - } catch (InterruptedException ignore) { - interrupted = true; - } - } - - if (interrupted) { - Thread.currentThread().interrupt(); - } - - List result = new ArrayList<>(); - for (LocalTime lt: localTimes.getLocalTimeList()) { - result.add( - new Formatter().format( - "%4d-%02d-%02d %02d:%02d:%02d %s", - lt.getYear(), - lt.getMonth(), - lt.getDayOfMonth(), - lt.getHour(), - lt.getMinute(), - lt.getSecond(), - lt.getDayOfWeek().name()).toString()); - } - - return result; - } - - @Override - public void channelRegistered(ChannelHandlerContext ctx) { - channel = ctx.channel(); - } - - @Override - public void messageReceived(ChannelHandlerContext ctx, LocalTimes times) throws Exception { - answer.add(times); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - cause.printStackTrace(); - ctx.close(); - } -} diff --git a/example/src/main/java/io/netty/example/worldclock/WorldClockClientInitializer.java b/example/src/main/java/io/netty/example/worldclock/WorldClockClientInitializer.java deleted file mode 100644 index 641ae69eb9..0000000000 --- a/example/src/main/java/io/netty/example/worldclock/WorldClockClientInitializer.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.worldclock; - -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.socket.SocketChannel; -import io.netty.handler.codec.protobuf.ProtobufDecoder; -import io.netty.handler.codec.protobuf.ProtobufEncoder; -import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder; -import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender; -import io.netty.handler.ssl.SslContext; - -public class WorldClockClientInitializer extends ChannelInitializer { - - private final SslContext sslCtx; - - public WorldClockClientInitializer(SslContext sslCtx) { - this.sslCtx = sslCtx; - } - - @Override - public void initChannel(SocketChannel ch) { - ChannelPipeline p = ch.pipeline(); - if (sslCtx != null) { - p.addLast(sslCtx.newHandler(ch.alloc(), WorldClockClient.HOST, WorldClockClient.PORT)); - } - - p.addLast(new ProtobufVarint32FrameDecoder()); - p.addLast(new ProtobufDecoder(WorldClockProtocol.LocalTimes.getDefaultInstance())); - - p.addLast(new ProtobufVarint32LengthFieldPrepender()); - p.addLast(new ProtobufEncoder()); - - p.addLast(new WorldClockClientHandler()); - } -} diff --git a/example/src/main/java/io/netty/example/worldclock/WorldClockProtocol.java b/example/src/main/java/io/netty/example/worldclock/WorldClockProtocol.java deleted file mode 100644 index 331eefc2d4..0000000000 --- a/example/src/main/java/io/netty/example/worldclock/WorldClockProtocol.java +++ /dev/null @@ -1,3423 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -// Generated by the protocol buffer compiler. DO NOT EDIT! -// source: src/main/java/io/netty/example/worldclock/WorldClockProtocol.proto - -package io.netty.example.worldclock; - -import static io.netty.util.internal.ObjectUtil.checkNotNull; - -@SuppressWarnings("all") -public final class WorldClockProtocol { - private WorldClockProtocol() {} - public static void registerAllExtensions( - com.google.protobuf.ExtensionRegistry registry) { - } - /** - * Protobuf enum {@code io.netty.example.worldclock.Continent} - */ - public enum Continent - implements com.google.protobuf.ProtocolMessageEnum { - /** - * AFRICA = 0; - */ - AFRICA(0, 0), - /** - * AMERICA = 1; - */ - AMERICA(1, 1), - /** - * ANTARCTICA = 2; - */ - ANTARCTICA(2, 2), - /** - * ARCTIC = 3; - */ - ARCTIC(3, 3), - /** - * ASIA = 4; - */ - ASIA(4, 4), - /** - * ATLANTIC = 5; - */ - ATLANTIC(5, 5), - /** - * AUSTRALIA = 6; - */ - AUSTRALIA(6, 6), - /** - * EUROPE = 7; - */ - EUROPE(7, 7), - /** - * INDIAN = 8; - */ - INDIAN(8, 8), - /** - * MIDEAST = 9; - */ - MIDEAST(9, 9), - /** - * PACIFIC = 10; - */ - PACIFIC(10, 10), - ; - - /** - * AFRICA = 0; - */ - public static final int AFRICA_VALUE = 0; - /** - * AMERICA = 1; - */ - public static final int AMERICA_VALUE = 1; - /** - * ANTARCTICA = 2; - */ - public static final int ANTARCTICA_VALUE = 2; - /** - * ARCTIC = 3; - */ - public static final int ARCTIC_VALUE = 3; - /** - * ASIA = 4; - */ - public static final int ASIA_VALUE = 4; - /** - * ATLANTIC = 5; - */ - public static final int ATLANTIC_VALUE = 5; - /** - * AUSTRALIA = 6; - */ - public static final int AUSTRALIA_VALUE = 6; - /** - * EUROPE = 7; - */ - public static final int EUROPE_VALUE = 7; - /** - * INDIAN = 8; - */ - public static final int INDIAN_VALUE = 8; - /** - * MIDEAST = 9; - */ - public static final int MIDEAST_VALUE = 9; - /** - * PACIFIC = 10; - */ - public static final int PACIFIC_VALUE = 10; - - - @Override - public final int getNumber() { return value; } - - public static Continent valueOf(int value) { - switch (value) { - case 0: return AFRICA; - case 1: return AMERICA; - case 2: return ANTARCTICA; - case 3: return ARCTIC; - case 4: return ASIA; - case 5: return ATLANTIC; - case 6: return AUSTRALIA; - case 7: return EUROPE; - case 8: return INDIAN; - case 9: return MIDEAST; - case 10: return PACIFIC; - default: return null; - } - } - - public static com.google.protobuf.Internal.EnumLiteMap - internalGetValueMap() { - return internalValueMap; - } - private static com.google.protobuf.Internal.EnumLiteMap - internalValueMap = - new com.google.protobuf.Internal.EnumLiteMap() { - @Override - public Continent findValueByNumber(int number) { - return Continent.valueOf(number); - } - }; - - @Override - public final com.google.protobuf.Descriptors.EnumValueDescriptor - getValueDescriptor() { - return getDescriptor().getValues().get(index); - } - @Override - public final com.google.protobuf.Descriptors.EnumDescriptor - getDescriptorForType() { - return getDescriptor(); - } - public static final com.google.protobuf.Descriptors.EnumDescriptor - getDescriptor() { - return io.netty.example.worldclock.WorldClockProtocol.getDescriptor().getEnumTypes().get(0); - } - - private static final Continent[] VALUES = values(); - - public static Continent valueOf( - com.google.protobuf.Descriptors.EnumValueDescriptor desc) { - if (desc.getType() != getDescriptor()) { - throw new java.lang.IllegalArgumentException( - "EnumValueDescriptor is not for this type."); - } - return VALUES[desc.getIndex()]; - } - - private final int index; - private final int value; - - private Continent(int index, int value) { - this.index = index; - this.value = value; - } - - // @@protoc_insertion_point(enum_scope:io.netty.example.worldclock.Continent) - } - - /** - * Protobuf enum {@code io.netty.example.worldclock.DayOfWeek} - */ - public enum DayOfWeek - implements com.google.protobuf.ProtocolMessageEnum { - /** - * SUNDAY = 1; - */ - SUNDAY(0, 1), - /** - * MONDAY = 2; - */ - MONDAY(1, 2), - /** - * TUESDAY = 3; - */ - TUESDAY(2, 3), - /** - * WEDNESDAY = 4; - */ - WEDNESDAY(3, 4), - /** - * THURSDAY = 5; - */ - THURSDAY(4, 5), - /** - * FRIDAY = 6; - */ - FRIDAY(5, 6), - /** - * SATURDAY = 7; - */ - SATURDAY(6, 7), - ; - - /** - * SUNDAY = 1; - */ - public static final int SUNDAY_VALUE = 1; - /** - * MONDAY = 2; - */ - public static final int MONDAY_VALUE = 2; - /** - * TUESDAY = 3; - */ - public static final int TUESDAY_VALUE = 3; - /** - * WEDNESDAY = 4; - */ - public static final int WEDNESDAY_VALUE = 4; - /** - * THURSDAY = 5; - */ - public static final int THURSDAY_VALUE = 5; - /** - * FRIDAY = 6; - */ - public static final int FRIDAY_VALUE = 6; - /** - * SATURDAY = 7; - */ - public static final int SATURDAY_VALUE = 7; - - - @Override - public final int getNumber() { return value; } - - public static DayOfWeek valueOf(int value) { - switch (value) { - case 1: return SUNDAY; - case 2: return MONDAY; - case 3: return TUESDAY; - case 4: return WEDNESDAY; - case 5: return THURSDAY; - case 6: return FRIDAY; - case 7: return SATURDAY; - default: return null; - } - } - - public static com.google.protobuf.Internal.EnumLiteMap - internalGetValueMap() { - return internalValueMap; - } - private static com.google.protobuf.Internal.EnumLiteMap - internalValueMap = - new com.google.protobuf.Internal.EnumLiteMap() { - @Override - public DayOfWeek findValueByNumber(int number) { - return DayOfWeek.valueOf(number); - } - }; - - @Override - public final com.google.protobuf.Descriptors.EnumValueDescriptor - getValueDescriptor() { - return getDescriptor().getValues().get(index); - } - @Override - public final com.google.protobuf.Descriptors.EnumDescriptor - getDescriptorForType() { - return getDescriptor(); - } - public static final com.google.protobuf.Descriptors.EnumDescriptor - getDescriptor() { - return io.netty.example.worldclock.WorldClockProtocol.getDescriptor().getEnumTypes().get(1); - } - - private static final DayOfWeek[] VALUES = values(); - - public static DayOfWeek valueOf( - com.google.protobuf.Descriptors.EnumValueDescriptor desc) { - if (desc.getType() != getDescriptor()) { - throw new java.lang.IllegalArgumentException( - "EnumValueDescriptor is not for this type."); - } - return VALUES[desc.getIndex()]; - } - - private final int index; - private final int value; - - private DayOfWeek(int index, int value) { - this.index = index; - this.value = value; - } - - // @@protoc_insertion_point(enum_scope:io.netty.example.worldclock.DayOfWeek) - } - - public interface LocationOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // required .io.netty.example.worldclock.Continent continent = 1; - /** - * required .io.netty.example.worldclock.Continent continent = 1; - */ - boolean hasContinent(); - /** - * required .io.netty.example.worldclock.Continent continent = 1; - */ - io.netty.example.worldclock.WorldClockProtocol.Continent getContinent(); - - // required string city = 2; - /** - * required string city = 2; - */ - boolean hasCity(); - /** - * required string city = 2; - */ - java.lang.String getCity(); - /** - * required string city = 2; - */ - com.google.protobuf.ByteString - getCityBytes(); - } - /** - * Protobuf type {@code io.netty.example.worldclock.Location} - */ - public static final class Location extends - com.google.protobuf.GeneratedMessage - implements LocationOrBuilder { - // Use Location.newBuilder() to construct. - private Location(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private Location(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final Location defaultInstance; - public static Location getDefaultInstance() { - return defaultInstance; - } - - @Override - public Location getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private Location( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 8: { - int rawValue = input.readEnum(); - io.netty.example.worldclock.WorldClockProtocol.Continent value = io.netty.example.worldclock.WorldClockProtocol.Continent.valueOf(rawValue); - if (value == null) { - unknownFields.mergeVarintField(1, rawValue); - } else { - bitField0_ |= 0x00000001; - continent_ = value; - } - break; - } - case 18: { - bitField0_ |= 0x00000002; - city_ = input.readBytes(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return io.netty.example.worldclock.WorldClockProtocol.internal_static_io_netty_example_worldclock_Location_descriptor; - } - - @Override - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return io.netty.example.worldclock.WorldClockProtocol.internal_static_io_netty_example_worldclock_Location_fieldAccessorTable - .ensureFieldAccessorsInitialized( - io.netty.example.worldclock.WorldClockProtocol.Location.class, io.netty.example.worldclock.WorldClockProtocol.Location.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - @Override - public Location parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new Location(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // required .io.netty.example.worldclock.Continent continent = 1; - public static final int CONTINENT_FIELD_NUMBER = 1; - private io.netty.example.worldclock.WorldClockProtocol.Continent continent_; - /** - * required .io.netty.example.worldclock.Continent continent = 1; - */ - @Override - public boolean hasContinent() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * required .io.netty.example.worldclock.Continent continent = 1; - */ - @Override - public io.netty.example.worldclock.WorldClockProtocol.Continent getContinent() { - return continent_; - } - - // required string city = 2; - public static final int CITY_FIELD_NUMBER = 2; - private java.lang.Object city_; - /** - * required string city = 2; - */ - @Override - public boolean hasCity() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * required string city = 2; - */ - @Override - public java.lang.String getCity() { - java.lang.Object ref = city_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - city_ = s; - } - return s; - } - } - /** - * required string city = 2; - */ - @Override - public com.google.protobuf.ByteString - getCityBytes() { - java.lang.Object ref = city_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - city_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - private void initFields() { - continent_ = io.netty.example.worldclock.WorldClockProtocol.Continent.AFRICA; - city_ = ""; - } - private byte memoizedIsInitialized = -1; - @Override - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - if (!hasContinent()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasCity()) { - memoizedIsInitialized = 0; - return false; - } - memoizedIsInitialized = 1; - return true; - } - - @Override - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeEnum(1, continent_.getNumber()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, getCityBytes()); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - @Override - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeEnumSize(1, continent_.getNumber()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, getCityBytes()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static io.netty.example.worldclock.WorldClockProtocol.Location parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static io.netty.example.worldclock.WorldClockProtocol.Location parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static io.netty.example.worldclock.WorldClockProtocol.Location parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static io.netty.example.worldclock.WorldClockProtocol.Location parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static io.netty.example.worldclock.WorldClockProtocol.Location parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static io.netty.example.worldclock.WorldClockProtocol.Location parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static io.netty.example.worldclock.WorldClockProtocol.Location parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static io.netty.example.worldclock.WorldClockProtocol.Location parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static io.netty.example.worldclock.WorldClockProtocol.Location parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static io.netty.example.worldclock.WorldClockProtocol.Location parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - @Override - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(io.netty.example.worldclock.WorldClockProtocol.Location prototype) { - return newBuilder().mergeFrom(prototype); - } - @Override - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code io.netty.example.worldclock.Location} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements io.netty.example.worldclock.WorldClockProtocol.LocationOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return io.netty.example.worldclock.WorldClockProtocol.internal_static_io_netty_example_worldclock_Location_descriptor; - } - - @Override - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return io.netty.example.worldclock.WorldClockProtocol.internal_static_io_netty_example_worldclock_Location_fieldAccessorTable - .ensureFieldAccessorsInitialized( - io.netty.example.worldclock.WorldClockProtocol.Location.class, io.netty.example.worldclock.WorldClockProtocol.Location.Builder.class); - } - - // Construct using io.netty.example.worldclock.WorldClockProtocol.Location.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - @Override - public Builder clear() { - super.clear(); - continent_ = io.netty.example.worldclock.WorldClockProtocol.Continent.AFRICA; - bitField0_ = (bitField0_ & ~0x00000001); - city_ = ""; - bitField0_ = (bitField0_ & ~0x00000002); - return this; - } - - @Override - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - @Override - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return io.netty.example.worldclock.WorldClockProtocol.internal_static_io_netty_example_worldclock_Location_descriptor; - } - - @Override - public io.netty.example.worldclock.WorldClockProtocol.Location getDefaultInstanceForType() { - return io.netty.example.worldclock.WorldClockProtocol.Location.getDefaultInstance(); - } - - @Override - public io.netty.example.worldclock.WorldClockProtocol.Location build() { - io.netty.example.worldclock.WorldClockProtocol.Location result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - @Override - public io.netty.example.worldclock.WorldClockProtocol.Location buildPartial() { - io.netty.example.worldclock.WorldClockProtocol.Location result = new io.netty.example.worldclock.WorldClockProtocol.Location(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.continent_ = continent_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.city_ = city_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - @Override - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof io.netty.example.worldclock.WorldClockProtocol.Location) { - return mergeFrom((io.netty.example.worldclock.WorldClockProtocol.Location)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(io.netty.example.worldclock.WorldClockProtocol.Location other) { - if (other == io.netty.example.worldclock.WorldClockProtocol.Location.getDefaultInstance()) return this; - if (other.hasContinent()) { - setContinent(other.getContinent()); - } - if (other.hasCity()) { - bitField0_ |= 0x00000002; - city_ = other.city_; - onChanged(); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - @Override - public final boolean isInitialized() { - if (!hasContinent()) { - - return false; - } - if (!hasCity()) { - - return false; - } - return true; - } - - @Override - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - io.netty.example.worldclock.WorldClockProtocol.Location parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (io.netty.example.worldclock.WorldClockProtocol.Location) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // required .io.netty.example.worldclock.Continent continent = 1; - private io.netty.example.worldclock.WorldClockProtocol.Continent continent_ = io.netty.example.worldclock.WorldClockProtocol.Continent.AFRICA; - /** - * required .io.netty.example.worldclock.Continent continent = 1; - */ - @Override - public boolean hasContinent() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * required .io.netty.example.worldclock.Continent continent = 1; - */ - @Override - public io.netty.example.worldclock.WorldClockProtocol.Continent getContinent() { - return continent_; - } - /** - * required .io.netty.example.worldclock.Continent continent = 1; - */ - public Builder setContinent(io.netty.example.worldclock.WorldClockProtocol.Continent value) { - checkNotNull(value, "value"); - bitField0_ |= 0x00000001; - continent_ = value; - onChanged(); - return this; - } - /** - * required .io.netty.example.worldclock.Continent continent = 1; - */ - public Builder clearContinent() { - bitField0_ = (bitField0_ & ~0x00000001); - continent_ = io.netty.example.worldclock.WorldClockProtocol.Continent.AFRICA; - onChanged(); - return this; - } - - // required string city = 2; - private java.lang.Object city_ = ""; - /** - * required string city = 2; - */ - @Override - public boolean hasCity() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * required string city = 2; - */ - @Override - public java.lang.String getCity() { - java.lang.Object ref = city_; - if (!(ref instanceof java.lang.String)) { - java.lang.String s = ((com.google.protobuf.ByteString) ref) - .toStringUtf8(); - city_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - * required string city = 2; - */ - @Override - public com.google.protobuf.ByteString - getCityBytes() { - java.lang.Object ref = city_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - city_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * required string city = 2; - */ - public Builder setCity( - java.lang.String value) { - checkNotNull(value, "value"); - bitField0_ |= 0x00000002; - city_ = value; - onChanged(); - return this; - } - /** - * required string city = 2; - */ - public Builder clearCity() { - bitField0_ = (bitField0_ & ~0x00000002); - city_ = getDefaultInstance().getCity(); - onChanged(); - return this; - } - /** - * required string city = 2; - */ - public Builder setCityBytes( - com.google.protobuf.ByteString value) { - checkNotNull(value, "value"); - bitField0_ |= 0x00000002; - city_ = value; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:io.netty.example.worldclock.Location) - } - - static { - defaultInstance = new Location(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:io.netty.example.worldclock.Location) - } - - public interface LocationsOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // repeated .io.netty.example.worldclock.Location location = 1; - /** - * repeated .io.netty.example.worldclock.Location location = 1; - */ - java.util.List - getLocationList(); - /** - * repeated .io.netty.example.worldclock.Location location = 1; - */ - io.netty.example.worldclock.WorldClockProtocol.Location getLocation(int index); - /** - * repeated .io.netty.example.worldclock.Location location = 1; - */ - int getLocationCount(); - /** - * repeated .io.netty.example.worldclock.Location location = 1; - */ - java.util.List - getLocationOrBuilderList(); - /** - * repeated .io.netty.example.worldclock.Location location = 1; - */ - io.netty.example.worldclock.WorldClockProtocol.LocationOrBuilder getLocationOrBuilder( - int index); - } - /** - * Protobuf type {@code io.netty.example.worldclock.Locations} - */ - public static final class Locations extends - com.google.protobuf.GeneratedMessage - implements LocationsOrBuilder { - // Use Locations.newBuilder() to construct. - private Locations(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private Locations(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final Locations defaultInstance; - public static Locations getDefaultInstance() { - return defaultInstance; - } - - @Override - public Locations getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private Locations( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - if (!((mutable_bitField0_ & 0x00000001) == 0x00000001)) { - location_ = new java.util.ArrayList(); - mutable_bitField0_ |= 0x00000001; - } - location_.add(input.readMessage(io.netty.example.worldclock.WorldClockProtocol.Location.PARSER, extensionRegistry)); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - if (((mutable_bitField0_ & 0x00000001) == 0x00000001)) { - location_ = java.util.Collections.unmodifiableList(location_); - } - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return io.netty.example.worldclock.WorldClockProtocol.internal_static_io_netty_example_worldclock_Locations_descriptor; - } - - @Override - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return io.netty.example.worldclock.WorldClockProtocol.internal_static_io_netty_example_worldclock_Locations_fieldAccessorTable - .ensureFieldAccessorsInitialized( - io.netty.example.worldclock.WorldClockProtocol.Locations.class, io.netty.example.worldclock.WorldClockProtocol.Locations.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - @Override - public Locations parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new Locations(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - // repeated .io.netty.example.worldclock.Location location = 1; - public static final int LOCATION_FIELD_NUMBER = 1; - private java.util.List location_; - /** - * repeated .io.netty.example.worldclock.Location location = 1; - */ - @Override - public java.util.List getLocationList() { - return location_; - } - /** - * repeated .io.netty.example.worldclock.Location location = 1; - */ - @Override - public java.util.List - getLocationOrBuilderList() { - return location_; - } - /** - * repeated .io.netty.example.worldclock.Location location = 1; - */ - @Override - public int getLocationCount() { - return location_.size(); - } - /** - * repeated .io.netty.example.worldclock.Location location = 1; - */ - @Override - public io.netty.example.worldclock.WorldClockProtocol.Location getLocation(int index) { - return location_.get(index); - } - /** - * repeated .io.netty.example.worldclock.Location location = 1; - */ - @Override - public io.netty.example.worldclock.WorldClockProtocol.LocationOrBuilder getLocationOrBuilder( - int index) { - return location_.get(index); - } - - private void initFields() { - location_ = java.util.Collections.emptyList(); - } - private byte memoizedIsInitialized = -1; - @Override - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - for (int i = 0; i < getLocationCount(); i++) { - if (!getLocation(i).isInitialized()) { - memoizedIsInitialized = 0; - return false; - } - } - memoizedIsInitialized = 1; - return true; - } - - @Override - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - for (int i = 0; i < location_.size(); i++) { - output.writeMessage(1, location_.get(i)); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - @Override - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - for (int i = 0; i < location_.size(); i++) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(1, location_.get(i)); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static io.netty.example.worldclock.WorldClockProtocol.Locations parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static io.netty.example.worldclock.WorldClockProtocol.Locations parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static io.netty.example.worldclock.WorldClockProtocol.Locations parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static io.netty.example.worldclock.WorldClockProtocol.Locations parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static io.netty.example.worldclock.WorldClockProtocol.Locations parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static io.netty.example.worldclock.WorldClockProtocol.Locations parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static io.netty.example.worldclock.WorldClockProtocol.Locations parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static io.netty.example.worldclock.WorldClockProtocol.Locations parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static io.netty.example.worldclock.WorldClockProtocol.Locations parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static io.netty.example.worldclock.WorldClockProtocol.Locations parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - @Override - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(io.netty.example.worldclock.WorldClockProtocol.Locations prototype) { - return newBuilder().mergeFrom(prototype); - } - @Override - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code io.netty.example.worldclock.Locations} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements io.netty.example.worldclock.WorldClockProtocol.LocationsOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return io.netty.example.worldclock.WorldClockProtocol.internal_static_io_netty_example_worldclock_Locations_descriptor; - } - - @Override - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return io.netty.example.worldclock.WorldClockProtocol.internal_static_io_netty_example_worldclock_Locations_fieldAccessorTable - .ensureFieldAccessorsInitialized( - io.netty.example.worldclock.WorldClockProtocol.Locations.class, io.netty.example.worldclock.WorldClockProtocol.Locations.Builder.class); - } - - // Construct using io.netty.example.worldclock.WorldClockProtocol.Locations.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - getLocationFieldBuilder(); - } - } - private static Builder create() { - return new Builder(); - } - - @Override - public Builder clear() { - super.clear(); - if (locationBuilder_ == null) { - location_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000001); - } else { - locationBuilder_.clear(); - } - return this; - } - - @Override - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - @Override - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return io.netty.example.worldclock.WorldClockProtocol.internal_static_io_netty_example_worldclock_Locations_descriptor; - } - - @Override - public io.netty.example.worldclock.WorldClockProtocol.Locations getDefaultInstanceForType() { - return io.netty.example.worldclock.WorldClockProtocol.Locations.getDefaultInstance(); - } - - @Override - public io.netty.example.worldclock.WorldClockProtocol.Locations build() { - io.netty.example.worldclock.WorldClockProtocol.Locations result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - @Override - public io.netty.example.worldclock.WorldClockProtocol.Locations buildPartial() { - io.netty.example.worldclock.WorldClockProtocol.Locations result = new io.netty.example.worldclock.WorldClockProtocol.Locations(this); - int from_bitField0_ = bitField0_; - if (locationBuilder_ == null) { - if (((bitField0_ & 0x00000001) == 0x00000001)) { - location_ = java.util.Collections.unmodifiableList(location_); - bitField0_ = (bitField0_ & ~0x00000001); - } - result.location_ = location_; - } else { - result.location_ = locationBuilder_.build(); - } - onBuilt(); - return result; - } - - @Override - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof io.netty.example.worldclock.WorldClockProtocol.Locations) { - return mergeFrom((io.netty.example.worldclock.WorldClockProtocol.Locations)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(io.netty.example.worldclock.WorldClockProtocol.Locations other) { - if (other == io.netty.example.worldclock.WorldClockProtocol.Locations.getDefaultInstance()) return this; - if (locationBuilder_ == null) { - if (!other.location_.isEmpty()) { - if (location_.isEmpty()) { - location_ = other.location_; - bitField0_ = (bitField0_ & ~0x00000001); - } else { - ensureLocationIsMutable(); - location_.addAll(other.location_); - } - onChanged(); - } - } else { - if (!other.location_.isEmpty()) { - if (locationBuilder_.isEmpty()) { - locationBuilder_.dispose(); - locationBuilder_ = null; - location_ = other.location_; - bitField0_ = (bitField0_ & ~0x00000001); - locationBuilder_ = - com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? - getLocationFieldBuilder() : null; - } else { - locationBuilder_.addAllMessages(other.location_); - } - } - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - @Override - public final boolean isInitialized() { - for (int i = 0; i < getLocationCount(); i++) { - if (!getLocation(i).isInitialized()) { - - return false; - } - } - return true; - } - - @Override - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - io.netty.example.worldclock.WorldClockProtocol.Locations parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (io.netty.example.worldclock.WorldClockProtocol.Locations) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // repeated .io.netty.example.worldclock.Location location = 1; - private java.util.List location_ = - java.util.Collections.emptyList(); - private void ensureLocationIsMutable() { - if (!((bitField0_ & 0x00000001) == 0x00000001)) { - location_ = new java.util.ArrayList(location_); - bitField0_ |= 0x00000001; - } - } - - private com.google.protobuf.RepeatedFieldBuilder< - io.netty.example.worldclock.WorldClockProtocol.Location, io.netty.example.worldclock.WorldClockProtocol.Location.Builder, io.netty.example.worldclock.WorldClockProtocol.LocationOrBuilder> locationBuilder_; - - /** - * repeated .io.netty.example.worldclock.Location location = 1; - */ - @Override - public java.util.List getLocationList() { - if (locationBuilder_ == null) { - return java.util.Collections.unmodifiableList(location_); - } else { - return locationBuilder_.getMessageList(); - } - } - /** - * repeated .io.netty.example.worldclock.Location location = 1; - */ - @Override - public int getLocationCount() { - if (locationBuilder_ == null) { - return location_.size(); - } else { - return locationBuilder_.getCount(); - } - } - /** - * repeated .io.netty.example.worldclock.Location location = 1; - */ - @Override - public io.netty.example.worldclock.WorldClockProtocol.Location getLocation(int index) { - if (locationBuilder_ == null) { - return location_.get(index); - } else { - return locationBuilder_.getMessage(index); - } - } - /** - * repeated .io.netty.example.worldclock.Location location = 1; - */ - public Builder setLocation( - int index, io.netty.example.worldclock.WorldClockProtocol.Location value) { - if (locationBuilder_ == null) { - checkNotNull(value, "value"); - ensureLocationIsMutable(); - location_.set(index, value); - onChanged(); - } else { - locationBuilder_.setMessage(index, value); - } - return this; - } - /** - * repeated .io.netty.example.worldclock.Location location = 1; - */ - public Builder setLocation( - int index, io.netty.example.worldclock.WorldClockProtocol.Location.Builder builderForValue) { - if (locationBuilder_ == null) { - ensureLocationIsMutable(); - location_.set(index, builderForValue.build()); - onChanged(); - } else { - locationBuilder_.setMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .io.netty.example.worldclock.Location location = 1; - */ - public Builder addLocation(io.netty.example.worldclock.WorldClockProtocol.Location value) { - if (locationBuilder_ == null) { - checkNotNull(value, "value"); - ensureLocationIsMutable(); - location_.add(value); - onChanged(); - } else { - locationBuilder_.addMessage(value); - } - return this; - } - /** - * repeated .io.netty.example.worldclock.Location location = 1; - */ - public Builder addLocation( - int index, io.netty.example.worldclock.WorldClockProtocol.Location value) { - if (locationBuilder_ == null) { - checkNotNull(value, "value"); - ensureLocationIsMutable(); - location_.add(index, value); - onChanged(); - } else { - locationBuilder_.addMessage(index, value); - } - return this; - } - /** - * repeated .io.netty.example.worldclock.Location location = 1; - */ - public Builder addLocation( - io.netty.example.worldclock.WorldClockProtocol.Location.Builder builderForValue) { - if (locationBuilder_ == null) { - ensureLocationIsMutable(); - location_.add(builderForValue.build()); - onChanged(); - } else { - locationBuilder_.addMessage(builderForValue.build()); - } - return this; - } - /** - * repeated .io.netty.example.worldclock.Location location = 1; - */ - public Builder addLocation( - int index, io.netty.example.worldclock.WorldClockProtocol.Location.Builder builderForValue) { - if (locationBuilder_ == null) { - ensureLocationIsMutable(); - location_.add(index, builderForValue.build()); - onChanged(); - } else { - locationBuilder_.addMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .io.netty.example.worldclock.Location location = 1; - */ - public Builder addAllLocation( - java.lang.Iterable values) { - if (locationBuilder_ == null) { - ensureLocationIsMutable(); - super.addAll(values, location_); - onChanged(); - } else { - locationBuilder_.addAllMessages(values); - } - return this; - } - /** - * repeated .io.netty.example.worldclock.Location location = 1; - */ - public Builder clearLocation() { - if (locationBuilder_ == null) { - location_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000001); - onChanged(); - } else { - locationBuilder_.clear(); - } - return this; - } - /** - * repeated .io.netty.example.worldclock.Location location = 1; - */ - public Builder removeLocation(int index) { - if (locationBuilder_ == null) { - ensureLocationIsMutable(); - location_.remove(index); - onChanged(); - } else { - locationBuilder_.remove(index); - } - return this; - } - /** - * repeated .io.netty.example.worldclock.Location location = 1; - */ - public io.netty.example.worldclock.WorldClockProtocol.Location.Builder getLocationBuilder( - int index) { - return getLocationFieldBuilder().getBuilder(index); - } - /** - * repeated .io.netty.example.worldclock.Location location = 1; - */ - @Override - public io.netty.example.worldclock.WorldClockProtocol.LocationOrBuilder getLocationOrBuilder( - int index) { - if (locationBuilder_ == null) { - return location_.get(index); } else { - return locationBuilder_.getMessageOrBuilder(index); - } - } - /** - * repeated .io.netty.example.worldclock.Location location = 1; - */ - @Override - public java.util.List - getLocationOrBuilderList() { - if (locationBuilder_ != null) { - return locationBuilder_.getMessageOrBuilderList(); - } else { - return java.util.Collections.unmodifiableList(location_); - } - } - /** - * repeated .io.netty.example.worldclock.Location location = 1; - */ - public io.netty.example.worldclock.WorldClockProtocol.Location.Builder addLocationBuilder() { - return getLocationFieldBuilder().addBuilder( - io.netty.example.worldclock.WorldClockProtocol.Location.getDefaultInstance()); - } - /** - * repeated .io.netty.example.worldclock.Location location = 1; - */ - public io.netty.example.worldclock.WorldClockProtocol.Location.Builder addLocationBuilder( - int index) { - return getLocationFieldBuilder().addBuilder( - index, io.netty.example.worldclock.WorldClockProtocol.Location.getDefaultInstance()); - } - /** - * repeated .io.netty.example.worldclock.Location location = 1; - */ - public java.util.List - getLocationBuilderList() { - return getLocationFieldBuilder().getBuilderList(); - } - private com.google.protobuf.RepeatedFieldBuilder< - io.netty.example.worldclock.WorldClockProtocol.Location, io.netty.example.worldclock.WorldClockProtocol.Location.Builder, io.netty.example.worldclock.WorldClockProtocol.LocationOrBuilder> - getLocationFieldBuilder() { - if (locationBuilder_ == null) { - locationBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< - io.netty.example.worldclock.WorldClockProtocol.Location, io.netty.example.worldclock.WorldClockProtocol.Location.Builder, io.netty.example.worldclock.WorldClockProtocol.LocationOrBuilder>( - location_, - ((bitField0_ & 0x00000001) == 0x00000001), - getParentForChildren(), - isClean()); - location_ = null; - } - return locationBuilder_; - } - - // @@protoc_insertion_point(builder_scope:io.netty.example.worldclock.Locations) - } - - static { - defaultInstance = new Locations(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:io.netty.example.worldclock.Locations) - } - - public interface LocalTimeOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // required uint32 year = 1; - /** - * required uint32 year = 1; - */ - boolean hasYear(); - /** - * required uint32 year = 1; - */ - int getYear(); - - // required uint32 month = 2; - /** - * required uint32 month = 2; - */ - boolean hasMonth(); - /** - * required uint32 month = 2; - */ - int getMonth(); - - // required uint32 dayOfMonth = 4; - /** - * required uint32 dayOfMonth = 4; - */ - boolean hasDayOfMonth(); - /** - * required uint32 dayOfMonth = 4; - */ - int getDayOfMonth(); - - // required .io.netty.example.worldclock.DayOfWeek dayOfWeek = 5; - /** - * required .io.netty.example.worldclock.DayOfWeek dayOfWeek = 5; - */ - boolean hasDayOfWeek(); - /** - * required .io.netty.example.worldclock.DayOfWeek dayOfWeek = 5; - */ - io.netty.example.worldclock.WorldClockProtocol.DayOfWeek getDayOfWeek(); - - // required uint32 hour = 6; - /** - * required uint32 hour = 6; - */ - boolean hasHour(); - /** - * required uint32 hour = 6; - */ - int getHour(); - - // required uint32 minute = 7; - /** - * required uint32 minute = 7; - */ - boolean hasMinute(); - /** - * required uint32 minute = 7; - */ - int getMinute(); - - // required uint32 second = 8; - /** - * required uint32 second = 8; - */ - boolean hasSecond(); - /** - * required uint32 second = 8; - */ - int getSecond(); - } - /** - * Protobuf type {@code io.netty.example.worldclock.LocalTime} - */ - public static final class LocalTime extends - com.google.protobuf.GeneratedMessage - implements LocalTimeOrBuilder { - // Use LocalTime.newBuilder() to construct. - private LocalTime(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private LocalTime(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final LocalTime defaultInstance; - public static LocalTime getDefaultInstance() { - return defaultInstance; - } - - @Override - public LocalTime getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private LocalTime( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - year_ = input.readUInt32(); - break; - } - case 16: { - bitField0_ |= 0x00000002; - month_ = input.readUInt32(); - break; - } - case 32: { - bitField0_ |= 0x00000004; - dayOfMonth_ = input.readUInt32(); - break; - } - case 40: { - int rawValue = input.readEnum(); - io.netty.example.worldclock.WorldClockProtocol.DayOfWeek value = io.netty.example.worldclock.WorldClockProtocol.DayOfWeek.valueOf(rawValue); - if (value == null) { - unknownFields.mergeVarintField(5, rawValue); - } else { - bitField0_ |= 0x00000008; - dayOfWeek_ = value; - } - break; - } - case 48: { - bitField0_ |= 0x00000010; - hour_ = input.readUInt32(); - break; - } - case 56: { - bitField0_ |= 0x00000020; - minute_ = input.readUInt32(); - break; - } - case 64: { - bitField0_ |= 0x00000040; - second_ = input.readUInt32(); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return io.netty.example.worldclock.WorldClockProtocol.internal_static_io_netty_example_worldclock_LocalTime_descriptor; - } - - @Override - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return io.netty.example.worldclock.WorldClockProtocol.internal_static_io_netty_example_worldclock_LocalTime_fieldAccessorTable - .ensureFieldAccessorsInitialized( - io.netty.example.worldclock.WorldClockProtocol.LocalTime.class, io.netty.example.worldclock.WorldClockProtocol.LocalTime.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - @Override - public LocalTime parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new LocalTime(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - private int bitField0_; - // required uint32 year = 1; - public static final int YEAR_FIELD_NUMBER = 1; - private int year_; - /** - * required uint32 year = 1; - */ - @Override - public boolean hasYear() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * required uint32 year = 1; - */ - @Override - public int getYear() { - return year_; - } - - // required uint32 month = 2; - public static final int MONTH_FIELD_NUMBER = 2; - private int month_; - /** - * required uint32 month = 2; - */ - @Override - public boolean hasMonth() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * required uint32 month = 2; - */ - @Override - public int getMonth() { - return month_; - } - - // required uint32 dayOfMonth = 4; - public static final int DAYOFMONTH_FIELD_NUMBER = 4; - private int dayOfMonth_; - /** - * required uint32 dayOfMonth = 4; - */ - @Override - public boolean hasDayOfMonth() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * required uint32 dayOfMonth = 4; - */ - @Override - public int getDayOfMonth() { - return dayOfMonth_; - } - - // required .io.netty.example.worldclock.DayOfWeek dayOfWeek = 5; - public static final int DAYOFWEEK_FIELD_NUMBER = 5; - private io.netty.example.worldclock.WorldClockProtocol.DayOfWeek dayOfWeek_; - /** - * required .io.netty.example.worldclock.DayOfWeek dayOfWeek = 5; - */ - @Override - public boolean hasDayOfWeek() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * required .io.netty.example.worldclock.DayOfWeek dayOfWeek = 5; - */ - @Override - public io.netty.example.worldclock.WorldClockProtocol.DayOfWeek getDayOfWeek() { - return dayOfWeek_; - } - - // required uint32 hour = 6; - public static final int HOUR_FIELD_NUMBER = 6; - private int hour_; - /** - * required uint32 hour = 6; - */ - @Override - public boolean hasHour() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - /** - * required uint32 hour = 6; - */ - @Override - public int getHour() { - return hour_; - } - - // required uint32 minute = 7; - public static final int MINUTE_FIELD_NUMBER = 7; - private int minute_; - /** - * required uint32 minute = 7; - */ - @Override - public boolean hasMinute() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - /** - * required uint32 minute = 7; - */ - @Override - public int getMinute() { - return minute_; - } - - // required uint32 second = 8; - public static final int SECOND_FIELD_NUMBER = 8; - private int second_; - /** - * required uint32 second = 8; - */ - @Override - public boolean hasSecond() { - return ((bitField0_ & 0x00000040) == 0x00000040); - } - /** - * required uint32 second = 8; - */ - @Override - public int getSecond() { - return second_; - } - - private void initFields() { - year_ = 0; - month_ = 0; - dayOfMonth_ = 0; - dayOfWeek_ = io.netty.example.worldclock.WorldClockProtocol.DayOfWeek.SUNDAY; - hour_ = 0; - minute_ = 0; - second_ = 0; - } - private byte memoizedIsInitialized = -1; - @Override - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - if (!hasYear()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasMonth()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasDayOfMonth()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasDayOfWeek()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasHour()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasMinute()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasSecond()) { - memoizedIsInitialized = 0; - return false; - } - memoizedIsInitialized = 1; - return true; - } - - @Override - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt32(1, year_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeUInt32(2, month_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeUInt32(4, dayOfMonth_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeEnum(5, dayOfWeek_.getNumber()); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - output.writeUInt32(6, hour_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - output.writeUInt32(7, minute_); - } - if (((bitField0_ & 0x00000040) == 0x00000040)) { - output.writeUInt32(8, second_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - @Override - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(1, year_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(2, month_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(4, dayOfMonth_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeEnumSize(5, dayOfWeek_.getNumber()); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(6, hour_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(7, minute_); - } - if (((bitField0_ & 0x00000040) == 0x00000040)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(8, second_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static io.netty.example.worldclock.WorldClockProtocol.LocalTime parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static io.netty.example.worldclock.WorldClockProtocol.LocalTime parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static io.netty.example.worldclock.WorldClockProtocol.LocalTime parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static io.netty.example.worldclock.WorldClockProtocol.LocalTime parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static io.netty.example.worldclock.WorldClockProtocol.LocalTime parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static io.netty.example.worldclock.WorldClockProtocol.LocalTime parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static io.netty.example.worldclock.WorldClockProtocol.LocalTime parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static io.netty.example.worldclock.WorldClockProtocol.LocalTime parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static io.netty.example.worldclock.WorldClockProtocol.LocalTime parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static io.netty.example.worldclock.WorldClockProtocol.LocalTime parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - @Override - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(io.netty.example.worldclock.WorldClockProtocol.LocalTime prototype) { - return newBuilder().mergeFrom(prototype); - } - @Override - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code io.netty.example.worldclock.LocalTime} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements io.netty.example.worldclock.WorldClockProtocol.LocalTimeOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return io.netty.example.worldclock.WorldClockProtocol.internal_static_io_netty_example_worldclock_LocalTime_descriptor; - } - - @Override - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return io.netty.example.worldclock.WorldClockProtocol.internal_static_io_netty_example_worldclock_LocalTime_fieldAccessorTable - .ensureFieldAccessorsInitialized( - io.netty.example.worldclock.WorldClockProtocol.LocalTime.class, io.netty.example.worldclock.WorldClockProtocol.LocalTime.Builder.class); - } - - // Construct using io.netty.example.worldclock.WorldClockProtocol.LocalTime.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - @Override - public Builder clear() { - super.clear(); - year_ = 0; - bitField0_ = (bitField0_ & ~0x00000001); - month_ = 0; - bitField0_ = (bitField0_ & ~0x00000002); - dayOfMonth_ = 0; - bitField0_ = (bitField0_ & ~0x00000004); - dayOfWeek_ = io.netty.example.worldclock.WorldClockProtocol.DayOfWeek.SUNDAY; - bitField0_ = (bitField0_ & ~0x00000008); - hour_ = 0; - bitField0_ = (bitField0_ & ~0x00000010); - minute_ = 0; - bitField0_ = (bitField0_ & ~0x00000020); - second_ = 0; - bitField0_ = (bitField0_ & ~0x00000040); - return this; - } - - @Override - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - @Override - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return io.netty.example.worldclock.WorldClockProtocol.internal_static_io_netty_example_worldclock_LocalTime_descriptor; - } - - @Override - public io.netty.example.worldclock.WorldClockProtocol.LocalTime getDefaultInstanceForType() { - return io.netty.example.worldclock.WorldClockProtocol.LocalTime.getDefaultInstance(); - } - - @Override - public io.netty.example.worldclock.WorldClockProtocol.LocalTime build() { - io.netty.example.worldclock.WorldClockProtocol.LocalTime result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - @Override - public io.netty.example.worldclock.WorldClockProtocol.LocalTime buildPartial() { - io.netty.example.worldclock.WorldClockProtocol.LocalTime result = new io.netty.example.worldclock.WorldClockProtocol.LocalTime(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.year_ = year_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.month_ = month_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.dayOfMonth_ = dayOfMonth_; - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000008; - } - result.dayOfWeek_ = dayOfWeek_; - if (((from_bitField0_ & 0x00000010) == 0x00000010)) { - to_bitField0_ |= 0x00000010; - } - result.hour_ = hour_; - if (((from_bitField0_ & 0x00000020) == 0x00000020)) { - to_bitField0_ |= 0x00000020; - } - result.minute_ = minute_; - if (((from_bitField0_ & 0x00000040) == 0x00000040)) { - to_bitField0_ |= 0x00000040; - } - result.second_ = second_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - @Override - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof io.netty.example.worldclock.WorldClockProtocol.LocalTime) { - return mergeFrom((io.netty.example.worldclock.WorldClockProtocol.LocalTime)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(io.netty.example.worldclock.WorldClockProtocol.LocalTime other) { - if (other == io.netty.example.worldclock.WorldClockProtocol.LocalTime.getDefaultInstance()) return this; - if (other.hasYear()) { - setYear(other.getYear()); - } - if (other.hasMonth()) { - setMonth(other.getMonth()); - } - if (other.hasDayOfMonth()) { - setDayOfMonth(other.getDayOfMonth()); - } - if (other.hasDayOfWeek()) { - setDayOfWeek(other.getDayOfWeek()); - } - if (other.hasHour()) { - setHour(other.getHour()); - } - if (other.hasMinute()) { - setMinute(other.getMinute()); - } - if (other.hasSecond()) { - setSecond(other.getSecond()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - @Override - public final boolean isInitialized() { - if (!hasYear()) { - - return false; - } - if (!hasMonth()) { - - return false; - } - if (!hasDayOfMonth()) { - - return false; - } - if (!hasDayOfWeek()) { - - return false; - } - if (!hasHour()) { - - return false; - } - if (!hasMinute()) { - - return false; - } - if (!hasSecond()) { - - return false; - } - return true; - } - - @Override - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - io.netty.example.worldclock.WorldClockProtocol.LocalTime parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (io.netty.example.worldclock.WorldClockProtocol.LocalTime) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // required uint32 year = 1; - private int year_ ; - /** - * required uint32 year = 1; - */ - @Override - public boolean hasYear() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - /** - * required uint32 year = 1; - */ - @Override - public int getYear() { - return year_; - } - /** - * required uint32 year = 1; - */ - public Builder setYear(int value) { - bitField0_ |= 0x00000001; - year_ = value; - onChanged(); - return this; - } - /** - * required uint32 year = 1; - */ - public Builder clearYear() { - bitField0_ = (bitField0_ & ~0x00000001); - year_ = 0; - onChanged(); - return this; - } - - // required uint32 month = 2; - private int month_ ; - /** - * required uint32 month = 2; - */ - @Override - public boolean hasMonth() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - /** - * required uint32 month = 2; - */ - @Override - public int getMonth() { - return month_; - } - /** - * required uint32 month = 2; - */ - public Builder setMonth(int value) { - bitField0_ |= 0x00000002; - month_ = value; - onChanged(); - return this; - } - /** - * required uint32 month = 2; - */ - public Builder clearMonth() { - bitField0_ = (bitField0_ & ~0x00000002); - month_ = 0; - onChanged(); - return this; - } - - // required uint32 dayOfMonth = 4; - private int dayOfMonth_ ; - /** - * required uint32 dayOfMonth = 4; - */ - @Override - public boolean hasDayOfMonth() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - /** - * required uint32 dayOfMonth = 4; - */ - @Override - public int getDayOfMonth() { - return dayOfMonth_; - } - /** - * required uint32 dayOfMonth = 4; - */ - public Builder setDayOfMonth(int value) { - bitField0_ |= 0x00000004; - dayOfMonth_ = value; - onChanged(); - return this; - } - /** - * required uint32 dayOfMonth = 4; - */ - public Builder clearDayOfMonth() { - bitField0_ = (bitField0_ & ~0x00000004); - dayOfMonth_ = 0; - onChanged(); - return this; - } - - // required .io.netty.example.worldclock.DayOfWeek dayOfWeek = 5; - private io.netty.example.worldclock.WorldClockProtocol.DayOfWeek dayOfWeek_ = io.netty.example.worldclock.WorldClockProtocol.DayOfWeek.SUNDAY; - /** - * required .io.netty.example.worldclock.DayOfWeek dayOfWeek = 5; - */ - @Override - public boolean hasDayOfWeek() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - /** - * required .io.netty.example.worldclock.DayOfWeek dayOfWeek = 5; - */ - @Override - public io.netty.example.worldclock.WorldClockProtocol.DayOfWeek getDayOfWeek() { - return dayOfWeek_; - } - /** - * required .io.netty.example.worldclock.DayOfWeek dayOfWeek = 5; - */ - public Builder setDayOfWeek(io.netty.example.worldclock.WorldClockProtocol.DayOfWeek value) { - checkNotNull(value, "value"); - bitField0_ |= 0x00000008; - dayOfWeek_ = value; - onChanged(); - return this; - } - /** - * required .io.netty.example.worldclock.DayOfWeek dayOfWeek = 5; - */ - public Builder clearDayOfWeek() { - bitField0_ = (bitField0_ & ~0x00000008); - dayOfWeek_ = io.netty.example.worldclock.WorldClockProtocol.DayOfWeek.SUNDAY; - onChanged(); - return this; - } - - // required uint32 hour = 6; - private int hour_ ; - /** - * required uint32 hour = 6; - */ - @Override - public boolean hasHour() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - /** - * required uint32 hour = 6; - */ - @Override - public int getHour() { - return hour_; - } - /** - * required uint32 hour = 6; - */ - public Builder setHour(int value) { - bitField0_ |= 0x00000010; - hour_ = value; - onChanged(); - return this; - } - /** - * required uint32 hour = 6; - */ - public Builder clearHour() { - bitField0_ = (bitField0_ & ~0x00000010); - hour_ = 0; - onChanged(); - return this; - } - - // required uint32 minute = 7; - private int minute_ ; - /** - * required uint32 minute = 7; - */ - @Override - public boolean hasMinute() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - /** - * required uint32 minute = 7; - */ - @Override - public int getMinute() { - return minute_; - } - /** - * required uint32 minute = 7; - */ - public Builder setMinute(int value) { - bitField0_ |= 0x00000020; - minute_ = value; - onChanged(); - return this; - } - /** - * required uint32 minute = 7; - */ - public Builder clearMinute() { - bitField0_ = (bitField0_ & ~0x00000020); - minute_ = 0; - onChanged(); - return this; - } - - // required uint32 second = 8; - private int second_ ; - /** - * required uint32 second = 8; - */ - @Override - public boolean hasSecond() { - return ((bitField0_ & 0x00000040) == 0x00000040); - } - /** - * required uint32 second = 8; - */ - @Override - public int getSecond() { - return second_; - } - /** - * required uint32 second = 8; - */ - public Builder setSecond(int value) { - bitField0_ |= 0x00000040; - second_ = value; - onChanged(); - return this; - } - /** - * required uint32 second = 8; - */ - public Builder clearSecond() { - bitField0_ = (bitField0_ & ~0x00000040); - second_ = 0; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:io.netty.example.worldclock.LocalTime) - } - - static { - defaultInstance = new LocalTime(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:io.netty.example.worldclock.LocalTime) - } - - public interface LocalTimesOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // repeated .io.netty.example.worldclock.LocalTime localTime = 1; - /** - * repeated .io.netty.example.worldclock.LocalTime localTime = 1; - */ - java.util.List - getLocalTimeList(); - /** - * repeated .io.netty.example.worldclock.LocalTime localTime = 1; - */ - io.netty.example.worldclock.WorldClockProtocol.LocalTime getLocalTime(int index); - /** - * repeated .io.netty.example.worldclock.LocalTime localTime = 1; - */ - int getLocalTimeCount(); - /** - * repeated .io.netty.example.worldclock.LocalTime localTime = 1; - */ - java.util.List - getLocalTimeOrBuilderList(); - /** - * repeated .io.netty.example.worldclock.LocalTime localTime = 1; - */ - io.netty.example.worldclock.WorldClockProtocol.LocalTimeOrBuilder getLocalTimeOrBuilder( - int index); - } - /** - * Protobuf type {@code io.netty.example.worldclock.LocalTimes} - */ - public static final class LocalTimes extends - com.google.protobuf.GeneratedMessage - implements LocalTimesOrBuilder { - // Use LocalTimes.newBuilder() to construct. - private LocalTimes(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - this.unknownFields = builder.getUnknownFields(); - } - private LocalTimes(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } - - private static final LocalTimes defaultInstance; - public static LocalTimes getDefaultInstance() { - return defaultInstance; - } - - @Override - public LocalTimes getDefaultInstanceForType() { - return defaultInstance; - } - - private final com.google.protobuf.UnknownFieldSet unknownFields; - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private LocalTimes( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - initFields(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - done = true; - } - break; - } - case 10: { - if (!((mutable_bitField0_ & 0x00000001) == 0x00000001)) { - localTime_ = new java.util.ArrayList(); - mutable_bitField0_ |= 0x00000001; - } - localTime_.add(input.readMessage(io.netty.example.worldclock.WorldClockProtocol.LocalTime.PARSER, extensionRegistry)); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e.getMessage()).setUnfinishedMessage(this); - } finally { - if (((mutable_bitField0_ & 0x00000001) == 0x00000001)) { - localTime_ = java.util.Collections.unmodifiableList(localTime_); - } - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return io.netty.example.worldclock.WorldClockProtocol.internal_static_io_netty_example_worldclock_LocalTimes_descriptor; - } - - @Override - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return io.netty.example.worldclock.WorldClockProtocol.internal_static_io_netty_example_worldclock_LocalTimes_fieldAccessorTable - .ensureFieldAccessorsInitialized( - io.netty.example.worldclock.WorldClockProtocol.LocalTimes.class, io.netty.example.worldclock.WorldClockProtocol.LocalTimes.Builder.class); - } - - public static com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - @Override - public LocalTimes parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new LocalTimes(input, extensionRegistry); - } - }; - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - // repeated .io.netty.example.worldclock.LocalTime localTime = 1; - public static final int LOCALTIME_FIELD_NUMBER = 1; - private java.util.List localTime_; - /** - * repeated .io.netty.example.worldclock.LocalTime localTime = 1; - */ - @Override - public java.util.List getLocalTimeList() { - return localTime_; - } - /** - * repeated .io.netty.example.worldclock.LocalTime localTime = 1; - */ - @Override - public java.util.List - getLocalTimeOrBuilderList() { - return localTime_; - } - /** - * repeated .io.netty.example.worldclock.LocalTime localTime = 1; - */ - @Override - public int getLocalTimeCount() { - return localTime_.size(); - } - /** - * repeated .io.netty.example.worldclock.LocalTime localTime = 1; - */ - @Override - public io.netty.example.worldclock.WorldClockProtocol.LocalTime getLocalTime(int index) { - return localTime_.get(index); - } - /** - * repeated .io.netty.example.worldclock.LocalTime localTime = 1; - */ - @Override - public io.netty.example.worldclock.WorldClockProtocol.LocalTimeOrBuilder getLocalTimeOrBuilder( - int index) { - return localTime_.get(index); - } - - private void initFields() { - localTime_ = java.util.Collections.emptyList(); - } - private byte memoizedIsInitialized = -1; - @Override - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - for (int i = 0; i < getLocalTimeCount(); i++) { - if (!getLocalTime(i).isInitialized()) { - memoizedIsInitialized = 0; - return false; - } - } - memoizedIsInitialized = 1; - return true; - } - - @Override - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - for (int i = 0; i < localTime_.size(); i++) { - output.writeMessage(1, localTime_.get(i)); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - @Override - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - for (int i = 0; i < localTime_.size(); i++) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(1, localTime_.get(i)); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static io.netty.example.worldclock.WorldClockProtocol.LocalTimes parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static io.netty.example.worldclock.WorldClockProtocol.LocalTimes parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static io.netty.example.worldclock.WorldClockProtocol.LocalTimes parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static io.netty.example.worldclock.WorldClockProtocol.LocalTimes parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static io.netty.example.worldclock.WorldClockProtocol.LocalTimes parseFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static io.netty.example.worldclock.WorldClockProtocol.LocalTimes parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - public static io.netty.example.worldclock.WorldClockProtocol.LocalTimes parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input); - } - public static io.netty.example.worldclock.WorldClockProtocol.LocalTimes parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseDelimitedFrom(input, extensionRegistry); - } - public static io.netty.example.worldclock.WorldClockProtocol.LocalTimes parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return PARSER.parseFrom(input); - } - public static io.netty.example.worldclock.WorldClockProtocol.LocalTimes parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return PARSER.parseFrom(input, extensionRegistry); - } - - public static Builder newBuilder() { return Builder.create(); } - @Override - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(io.netty.example.worldclock.WorldClockProtocol.LocalTimes prototype) { - return newBuilder().mergeFrom(prototype); - } - @Override - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code io.netty.example.worldclock.LocalTimes} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements io.netty.example.worldclock.WorldClockProtocol.LocalTimesOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return io.netty.example.worldclock.WorldClockProtocol.internal_static_io_netty_example_worldclock_LocalTimes_descriptor; - } - - @Override - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return io.netty.example.worldclock.WorldClockProtocol.internal_static_io_netty_example_worldclock_LocalTimes_fieldAccessorTable - .ensureFieldAccessorsInitialized( - io.netty.example.worldclock.WorldClockProtocol.LocalTimes.class, io.netty.example.worldclock.WorldClockProtocol.LocalTimes.Builder.class); - } - - // Construct using io.netty.example.worldclock.WorldClockProtocol.LocalTimes.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - getLocalTimeFieldBuilder(); - } - } - private static Builder create() { - return new Builder(); - } - - @Override - public Builder clear() { - super.clear(); - if (localTimeBuilder_ == null) { - localTime_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000001); - } else { - localTimeBuilder_.clear(); - } - return this; - } - - @Override - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - @Override - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return io.netty.example.worldclock.WorldClockProtocol.internal_static_io_netty_example_worldclock_LocalTimes_descriptor; - } - - @Override - public io.netty.example.worldclock.WorldClockProtocol.LocalTimes getDefaultInstanceForType() { - return io.netty.example.worldclock.WorldClockProtocol.LocalTimes.getDefaultInstance(); - } - - @Override - public io.netty.example.worldclock.WorldClockProtocol.LocalTimes build() { - io.netty.example.worldclock.WorldClockProtocol.LocalTimes result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - @Override - public io.netty.example.worldclock.WorldClockProtocol.LocalTimes buildPartial() { - io.netty.example.worldclock.WorldClockProtocol.LocalTimes result = new io.netty.example.worldclock.WorldClockProtocol.LocalTimes(this); - int from_bitField0_ = bitField0_; - if (localTimeBuilder_ == null) { - if (((bitField0_ & 0x00000001) == 0x00000001)) { - localTime_ = java.util.Collections.unmodifiableList(localTime_); - bitField0_ = (bitField0_ & ~0x00000001); - } - result.localTime_ = localTime_; - } else { - result.localTime_ = localTimeBuilder_.build(); - } - onBuilt(); - return result; - } - - @Override - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof io.netty.example.worldclock.WorldClockProtocol.LocalTimes) { - return mergeFrom((io.netty.example.worldclock.WorldClockProtocol.LocalTimes)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(io.netty.example.worldclock.WorldClockProtocol.LocalTimes other) { - if (other == io.netty.example.worldclock.WorldClockProtocol.LocalTimes.getDefaultInstance()) return this; - if (localTimeBuilder_ == null) { - if (!other.localTime_.isEmpty()) { - if (localTime_.isEmpty()) { - localTime_ = other.localTime_; - bitField0_ = (bitField0_ & ~0x00000001); - } else { - ensureLocalTimeIsMutable(); - localTime_.addAll(other.localTime_); - } - onChanged(); - } - } else { - if (!other.localTime_.isEmpty()) { - if (localTimeBuilder_.isEmpty()) { - localTimeBuilder_.dispose(); - localTimeBuilder_ = null; - localTime_ = other.localTime_; - bitField0_ = (bitField0_ & ~0x00000001); - localTimeBuilder_ = - com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? - getLocalTimeFieldBuilder() : null; - } else { - localTimeBuilder_.addAllMessages(other.localTime_); - } - } - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - @Override - public final boolean isInitialized() { - for (int i = 0; i < getLocalTimeCount(); i++) { - if (!getLocalTime(i).isInitialized()) { - - return false; - } - } - return true; - } - - @Override - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - io.netty.example.worldclock.WorldClockProtocol.LocalTimes parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (io.netty.example.worldclock.WorldClockProtocol.LocalTimes) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - private int bitField0_; - - // repeated .io.netty.example.worldclock.LocalTime localTime = 1; - private java.util.List localTime_ = - java.util.Collections.emptyList(); - private void ensureLocalTimeIsMutable() { - if (!((bitField0_ & 0x00000001) == 0x00000001)) { - localTime_ = new java.util.ArrayList(localTime_); - bitField0_ |= 0x00000001; - } - } - - private com.google.protobuf.RepeatedFieldBuilder< - io.netty.example.worldclock.WorldClockProtocol.LocalTime, io.netty.example.worldclock.WorldClockProtocol.LocalTime.Builder, io.netty.example.worldclock.WorldClockProtocol.LocalTimeOrBuilder> localTimeBuilder_; - - /** - * repeated .io.netty.example.worldclock.LocalTime localTime = 1; - */ - @Override - public java.util.List getLocalTimeList() { - if (localTimeBuilder_ == null) { - return java.util.Collections.unmodifiableList(localTime_); - } else { - return localTimeBuilder_.getMessageList(); - } - } - /** - * repeated .io.netty.example.worldclock.LocalTime localTime = 1; - */ - @Override - public int getLocalTimeCount() { - if (localTimeBuilder_ == null) { - return localTime_.size(); - } else { - return localTimeBuilder_.getCount(); - } - } - /** - * repeated .io.netty.example.worldclock.LocalTime localTime = 1; - */ - @Override - public io.netty.example.worldclock.WorldClockProtocol.LocalTime getLocalTime(int index) { - if (localTimeBuilder_ == null) { - return localTime_.get(index); - } else { - return localTimeBuilder_.getMessage(index); - } - } - /** - * repeated .io.netty.example.worldclock.LocalTime localTime = 1; - */ - public Builder setLocalTime( - int index, io.netty.example.worldclock.WorldClockProtocol.LocalTime value) { - if (localTimeBuilder_ == null) { - checkNotNull(value, "value"); - ensureLocalTimeIsMutable(); - localTime_.set(index, value); - onChanged(); - } else { - localTimeBuilder_.setMessage(index, value); - } - return this; - } - /** - * repeated .io.netty.example.worldclock.LocalTime localTime = 1; - */ - public Builder setLocalTime( - int index, io.netty.example.worldclock.WorldClockProtocol.LocalTime.Builder builderForValue) { - if (localTimeBuilder_ == null) { - ensureLocalTimeIsMutable(); - localTime_.set(index, builderForValue.build()); - onChanged(); - } else { - localTimeBuilder_.setMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .io.netty.example.worldclock.LocalTime localTime = 1; - */ - public Builder addLocalTime(io.netty.example.worldclock.WorldClockProtocol.LocalTime value) { - if (localTimeBuilder_ == null) { - checkNotNull(value, "value"); - ensureLocalTimeIsMutable(); - localTime_.add(value); - onChanged(); - } else { - localTimeBuilder_.addMessage(value); - } - return this; - } - /** - * repeated .io.netty.example.worldclock.LocalTime localTime = 1; - */ - public Builder addLocalTime( - int index, io.netty.example.worldclock.WorldClockProtocol.LocalTime value) { - if (localTimeBuilder_ == null) { - checkNotNull(value, "value"); - ensureLocalTimeIsMutable(); - localTime_.add(index, value); - onChanged(); - } else { - localTimeBuilder_.addMessage(index, value); - } - return this; - } - /** - * repeated .io.netty.example.worldclock.LocalTime localTime = 1; - */ - public Builder addLocalTime( - io.netty.example.worldclock.WorldClockProtocol.LocalTime.Builder builderForValue) { - if (localTimeBuilder_ == null) { - ensureLocalTimeIsMutable(); - localTime_.add(builderForValue.build()); - onChanged(); - } else { - localTimeBuilder_.addMessage(builderForValue.build()); - } - return this; - } - /** - * repeated .io.netty.example.worldclock.LocalTime localTime = 1; - */ - public Builder addLocalTime( - int index, io.netty.example.worldclock.WorldClockProtocol.LocalTime.Builder builderForValue) { - if (localTimeBuilder_ == null) { - ensureLocalTimeIsMutable(); - localTime_.add(index, builderForValue.build()); - onChanged(); - } else { - localTimeBuilder_.addMessage(index, builderForValue.build()); - } - return this; - } - /** - * repeated .io.netty.example.worldclock.LocalTime localTime = 1; - */ - public Builder addAllLocalTime( - java.lang.Iterable values) { - if (localTimeBuilder_ == null) { - ensureLocalTimeIsMutable(); - super.addAll(values, localTime_); - onChanged(); - } else { - localTimeBuilder_.addAllMessages(values); - } - return this; - } - /** - * repeated .io.netty.example.worldclock.LocalTime localTime = 1; - */ - public Builder clearLocalTime() { - if (localTimeBuilder_ == null) { - localTime_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000001); - onChanged(); - } else { - localTimeBuilder_.clear(); - } - return this; - } - /** - * repeated .io.netty.example.worldclock.LocalTime localTime = 1; - */ - public Builder removeLocalTime(int index) { - if (localTimeBuilder_ == null) { - ensureLocalTimeIsMutable(); - localTime_.remove(index); - onChanged(); - } else { - localTimeBuilder_.remove(index); - } - return this; - } - /** - * repeated .io.netty.example.worldclock.LocalTime localTime = 1; - */ - public io.netty.example.worldclock.WorldClockProtocol.LocalTime.Builder getLocalTimeBuilder( - int index) { - return getLocalTimeFieldBuilder().getBuilder(index); - } - /** - * repeated .io.netty.example.worldclock.LocalTime localTime = 1; - */ - @Override - public io.netty.example.worldclock.WorldClockProtocol.LocalTimeOrBuilder getLocalTimeOrBuilder( - int index) { - if (localTimeBuilder_ == null) { - return localTime_.get(index); } else { - return localTimeBuilder_.getMessageOrBuilder(index); - } - } - /** - * repeated .io.netty.example.worldclock.LocalTime localTime = 1; - */ - @Override - public java.util.List - getLocalTimeOrBuilderList() { - if (localTimeBuilder_ != null) { - return localTimeBuilder_.getMessageOrBuilderList(); - } else { - return java.util.Collections.unmodifiableList(localTime_); - } - } - /** - * repeated .io.netty.example.worldclock.LocalTime localTime = 1; - */ - public io.netty.example.worldclock.WorldClockProtocol.LocalTime.Builder addLocalTimeBuilder() { - return getLocalTimeFieldBuilder().addBuilder( - io.netty.example.worldclock.WorldClockProtocol.LocalTime.getDefaultInstance()); - } - /** - * repeated .io.netty.example.worldclock.LocalTime localTime = 1; - */ - public io.netty.example.worldclock.WorldClockProtocol.LocalTime.Builder addLocalTimeBuilder( - int index) { - return getLocalTimeFieldBuilder().addBuilder( - index, io.netty.example.worldclock.WorldClockProtocol.LocalTime.getDefaultInstance()); - } - /** - * repeated .io.netty.example.worldclock.LocalTime localTime = 1; - */ - public java.util.List - getLocalTimeBuilderList() { - return getLocalTimeFieldBuilder().getBuilderList(); - } - private com.google.protobuf.RepeatedFieldBuilder< - io.netty.example.worldclock.WorldClockProtocol.LocalTime, io.netty.example.worldclock.WorldClockProtocol.LocalTime.Builder, io.netty.example.worldclock.WorldClockProtocol.LocalTimeOrBuilder> - getLocalTimeFieldBuilder() { - if (localTimeBuilder_ == null) { - localTimeBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< - io.netty.example.worldclock.WorldClockProtocol.LocalTime, io.netty.example.worldclock.WorldClockProtocol.LocalTime.Builder, io.netty.example.worldclock.WorldClockProtocol.LocalTimeOrBuilder>( - localTime_, - ((bitField0_ & 0x00000001) == 0x00000001), - getParentForChildren(), - isClean()); - localTime_ = null; - } - return localTimeBuilder_; - } - - // @@protoc_insertion_point(builder_scope:io.netty.example.worldclock.LocalTimes) - } - - static { - defaultInstance = new LocalTimes(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:io.netty.example.worldclock.LocalTimes) - } - - private static com.google.protobuf.Descriptors.Descriptor - internal_static_io_netty_example_worldclock_Location_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_io_netty_example_worldclock_Location_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_io_netty_example_worldclock_Locations_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_io_netty_example_worldclock_Locations_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_io_netty_example_worldclock_LocalTime_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_io_netty_example_worldclock_LocalTime_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_io_netty_example_worldclock_LocalTimes_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_io_netty_example_worldclock_LocalTimes_fieldAccessorTable; - - public static com.google.protobuf.Descriptors.FileDescriptor - getDescriptor() { - return descriptor; - } - private static com.google.protobuf.Descriptors.FileDescriptor - descriptor; - static { - java.lang.String[] descriptorData = { - "\nBsrc/main/java/io/netty/example/worldcl" + - "ock/WorldClockProtocol.proto\022\033io.netty.e" + - "xample.worldclock\"S\n\010Location\0229\n\tcontine" + - "nt\030\001 \002(\0162&.io.netty.example.worldclock.C" + - "ontinent\022\014\n\004city\030\002 \002(\t\"D\n\tLocations\0227\n\010l" + - "ocation\030\001 \003(\0132%.io.netty.example.worldcl" + - "ock.Location\"\245\001\n\tLocalTime\022\014\n\004year\030\001 \002(\r" + - "\022\r\n\005month\030\002 \002(\r\022\022\n\ndayOfMonth\030\004 \002(\r\0229\n\td" + - "ayOfWeek\030\005 \002(\0162&.io.netty.example.worldc" + - "lock.DayOfWeek\022\014\n\004hour\030\006 \002(\r\022\016\n\006minute\030\007", - " \002(\r\022\016\n\006second\030\010 \002(\r\"G\n\nLocalTimes\0229\n\tlo" + - "calTime\030\001 \003(\0132&.io.netty.example.worldcl" + - "ock.LocalTime*\231\001\n\tContinent\022\n\n\006AFRICA\020\000\022" + - "\013\n\007AMERICA\020\001\022\016\n\nANTARCTICA\020\002\022\n\n\006ARCTIC\020\003" + - "\022\010\n\004ASIA\020\004\022\014\n\010ATLANTIC\020\005\022\r\n\tAUSTRALIA\020\006\022" + - "\n\n\006EUROPE\020\007\022\n\n\006INDIAN\020\010\022\013\n\007MIDEAST\020\t\022\013\n\007" + - "PACIFIC\020\n*g\n\tDayOfWeek\022\n\n\006SUNDAY\020\001\022\n\n\006MO" + - "NDAY\020\002\022\013\n\007TUESDAY\020\003\022\r\n\tWEDNESDAY\020\004\022\014\n\010TH" + - "URSDAY\020\005\022\n\n\006FRIDAY\020\006\022\014\n\010SATURDAY\020\007B\002H\001" - }; - com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = - new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { - @Override - public com.google.protobuf.ExtensionRegistry assignDescriptors( - com.google.protobuf.Descriptors.FileDescriptor root) { - descriptor = root; - internal_static_io_netty_example_worldclock_Location_descriptor = - getDescriptor().getMessageTypes().get(0); - internal_static_io_netty_example_worldclock_Location_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_io_netty_example_worldclock_Location_descriptor, - new java.lang.String[] { "Continent", "City", }); - internal_static_io_netty_example_worldclock_Locations_descriptor = - getDescriptor().getMessageTypes().get(1); - internal_static_io_netty_example_worldclock_Locations_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_io_netty_example_worldclock_Locations_descriptor, - new java.lang.String[] { "Location", }); - internal_static_io_netty_example_worldclock_LocalTime_descriptor = - getDescriptor().getMessageTypes().get(2); - internal_static_io_netty_example_worldclock_LocalTime_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_io_netty_example_worldclock_LocalTime_descriptor, - new java.lang.String[] { "Year", "Month", "DayOfMonth", "DayOfWeek", "Hour", "Minute", "Second", }); - internal_static_io_netty_example_worldclock_LocalTimes_descriptor = - getDescriptor().getMessageTypes().get(3); - internal_static_io_netty_example_worldclock_LocalTimes_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_io_netty_example_worldclock_LocalTimes_descriptor, - new java.lang.String[] { "LocalTime", }); - return null; - } - }; - com.google.protobuf.Descriptors.FileDescriptor - .internalBuildGeneratedFileFrom(descriptorData, - new com.google.protobuf.Descriptors.FileDescriptor[] { - }, assigner); - } - - // @@protoc_insertion_point(outer_class_scope) -} diff --git a/example/src/main/java/io/netty/example/worldclock/WorldClockProtocol.proto b/example/src/main/java/io/netty/example/worldclock/WorldClockProtocol.proto deleted file mode 100644 index 9e7a845fe9..0000000000 --- a/example/src/main/java/io/netty/example/worldclock/WorldClockProtocol.proto +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.worldclock; - -// How to generate -// =============== -// $ cd example -// $ protoc src/main/java/io/netty/example/worldclock/WorldClockProtocol.proto \ -// --java_out=src/main/java -// -// Add @SuppressWarnings("all") to the generated code not to pollute IDE task list. - -option optimize_for = SPEED; - -enum Continent { - AFRICA = 0; - AMERICA = 1; - ANTARCTICA = 2; - ARCTIC = 3; - ASIA = 4; - ATLANTIC = 5; - AUSTRALIA = 6; - EUROPE = 7; - INDIAN = 8; - MIDEAST = 9; - PACIFIC = 10; -} - -message Location { - required Continent continent = 1; - required string city = 2; -} - -message Locations { - repeated Location location = 1; -} - -enum DayOfWeek { - SUNDAY = 1; - MONDAY = 2; - TUESDAY = 3; - WEDNESDAY = 4; - THURSDAY = 5; - FRIDAY = 6; - SATURDAY = 7; -} - -message LocalTime { - required uint32 year = 1; - required uint32 month = 2; - required uint32 dayOfMonth = 4; - required DayOfWeek dayOfWeek = 5; - required uint32 hour = 6; - required uint32 minute = 7; - required uint32 second = 8; -} - -message LocalTimes { - repeated LocalTime localTime = 1; -} diff --git a/example/src/main/java/io/netty/example/worldclock/WorldClockServer.java b/example/src/main/java/io/netty/example/worldclock/WorldClockServer.java deleted file mode 100644 index 79579d4dec..0000000000 --- a/example/src/main/java/io/netty/example/worldclock/WorldClockServer.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.worldclock; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.util.SelfSignedCertificate; - -/** - * Receives a list of continent/city pairs from a {@link WorldClockClient} to - * get the local times of the specified cities. - */ -public final class WorldClockServer { - - static final boolean SSL = System.getProperty("ssl") != null; - static final int PORT = Integer.parseInt(System.getProperty("port", "8463")); - - public static void main(String[] args) throws Exception { - // Configure SSL. - final SslContext sslCtx; - if (SSL) { - SelfSignedCertificate ssc = new SelfSignedCertificate(); - sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build(); - } else { - sslCtx = null; - } - - EventLoopGroup bossGroup = new MultithreadEventLoopGroup(1, NioHandler.newFactory()); - EventLoopGroup workerGroup = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - ServerBootstrap b = new ServerBootstrap(); - b.group(bossGroup, workerGroup) - .channel(NioServerSocketChannel.class) - .handler(new LoggingHandler(LogLevel.INFO)) - .childHandler(new WorldClockServerInitializer(sslCtx)); - - b.bind(PORT).get().closeFuture().sync(); - } finally { - bossGroup.shutdownGracefully(); - workerGroup.shutdownGracefully(); - } - } -} diff --git a/example/src/main/java/io/netty/example/worldclock/WorldClockServerHandler.java b/example/src/main/java/io/netty/example/worldclock/WorldClockServerHandler.java deleted file mode 100644 index b3bb1dd2d1..0000000000 --- a/example/src/main/java/io/netty/example/worldclock/WorldClockServerHandler.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.worldclock; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.example.worldclock.WorldClockProtocol.Continent; -import io.netty.example.worldclock.WorldClockProtocol.DayOfWeek; -import io.netty.example.worldclock.WorldClockProtocol.LocalTime; -import io.netty.example.worldclock.WorldClockProtocol.LocalTimes; -import io.netty.example.worldclock.WorldClockProtocol.Location; -import io.netty.example.worldclock.WorldClockProtocol.Locations; - -import java.util.Calendar; -import java.util.TimeZone; - -import static java.util.Calendar.*; - -public class WorldClockServerHandler extends SimpleChannelInboundHandler { - - @Override - public void messageReceived(ChannelHandlerContext ctx, Locations locations) throws Exception { - long currentTime = System.currentTimeMillis(); - - LocalTimes.Builder builder = LocalTimes.newBuilder(); - for (Location l: locations.getLocationList()) { - TimeZone tz = TimeZone.getTimeZone( - toString(l.getContinent()) + '/' + l.getCity()); - Calendar calendar = getInstance(tz); - calendar.setTimeInMillis(currentTime); - - builder.addLocalTime(LocalTime.newBuilder(). - setYear(calendar.get(YEAR)). - setMonth(calendar.get(MONTH) + 1). - setDayOfMonth(calendar.get(DAY_OF_MONTH)). - setDayOfWeek(DayOfWeek.valueOf(calendar.get(DAY_OF_WEEK))). - setHour(calendar.get(HOUR_OF_DAY)). - setMinute(calendar.get(MINUTE)). - setSecond(calendar.get(SECOND)).build()); - } - - ctx.write(builder.build()); - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) { - ctx.flush(); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - cause.printStackTrace(); - ctx.close(); - } - - private static String toString(Continent c) { - return c.name().charAt(0) + c.name().toLowerCase().substring(1); - } -} diff --git a/example/src/main/java/io/netty/example/worldclock/WorldClockServerInitializer.java b/example/src/main/java/io/netty/example/worldclock/WorldClockServerInitializer.java deleted file mode 100644 index 77bfda0a19..0000000000 --- a/example/src/main/java/io/netty/example/worldclock/WorldClockServerInitializer.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.worldclock; - -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.socket.SocketChannel; -import io.netty.handler.codec.protobuf.ProtobufDecoder; -import io.netty.handler.codec.protobuf.ProtobufEncoder; -import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder; -import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender; -import io.netty.handler.ssl.SslContext; - -public class WorldClockServerInitializer extends ChannelInitializer { - - private final SslContext sslCtx; - - public WorldClockServerInitializer(SslContext sslCtx) { - this.sslCtx = sslCtx; - } - - @Override - public void initChannel(SocketChannel ch) throws Exception { - ChannelPipeline p = ch.pipeline(); - if (sslCtx != null) { - p.addLast(sslCtx.newHandler(ch.alloc())); - } - - p.addLast(new ProtobufVarint32FrameDecoder()); - p.addLast(new ProtobufDecoder(WorldClockProtocol.Locations.getDefaultInstance())); - - p.addLast(new ProtobufVarint32LengthFieldPrepender()); - p.addLast(new ProtobufEncoder()); - - p.addLast(new WorldClockServerHandler()); - } -} diff --git a/example/src/main/resources/cors/cors.html b/example/src/main/resources/cors/cors.html deleted file mode 100644 index 4f96ded7bf..0000000000 --- a/example/src/main/resources/cors/cors.html +++ /dev/null @@ -1,42 +0,0 @@ - - - - - Cross Origin Resource Sharing (CORS) Example - - - -

Response from Server

- - - - diff --git a/example/src/main/resources/cors/css/cors.css b/example/src/main/resources/cors/css/cors.css deleted file mode 100644 index 3bb211e8c7..0000000000 --- a/example/src/main/resources/cors/css/cors.css +++ /dev/null @@ -1,4 +0,0 @@ -textarea { - width: 200px; - height: 50px; -} diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-0-0.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-0-0.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-0-0.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-0-0.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-0-1.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-0-1.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-0-1.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-0-1.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-0-10.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-0-10.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-0-10.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-0-10.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-0-11.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-0-11.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-0-11.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-0-11.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-0-12.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-0-12.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-0-12.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-0-12.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-0-13.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-0-13.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-0-13.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-0-13.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-0-14.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-0-14.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-0-14.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-0-14.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-0-15.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-0-15.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-0-15.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-0-15.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-0-16.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-0-16.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-0-16.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-0-16.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-0-17.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-0-17.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-0-17.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-0-17.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-0-18.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-0-18.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-0-18.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-0-18.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-0-19.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-0-19.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-0-19.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-0-19.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-0-2.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-0-2.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-0-2.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-0-2.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-0-3.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-0-3.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-0-3.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-0-3.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-0-4.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-0-4.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-0-4.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-0-4.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-0-5.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-0-5.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-0-5.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-0-5.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-0-6.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-0-6.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-0-6.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-0-6.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-0-7.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-0-7.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-0-7.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-0-7.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-0-8.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-0-8.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-0-8.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-0-8.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-0-9.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-0-9.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-0-9.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-0-9.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-1-0.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-1-0.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-1-0.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-1-0.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-1-1.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-1-1.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-1-1.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-1-1.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-1-10.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-1-10.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-1-10.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-1-10.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-1-11.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-1-11.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-1-11.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-1-11.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-1-12.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-1-12.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-1-12.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-1-12.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-1-13.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-1-13.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-1-13.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-1-13.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-1-14.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-1-14.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-1-14.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-1-14.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-1-15.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-1-15.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-1-15.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-1-15.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-1-16.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-1-16.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-1-16.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-1-16.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-1-17.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-1-17.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-1-17.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-1-17.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-1-18.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-1-18.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-1-18.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-1-18.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-1-19.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-1-19.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-1-19.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-1-19.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-1-2.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-1-2.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-1-2.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-1-2.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-1-3.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-1-3.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-1-3.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-1-3.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-1-4.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-1-4.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-1-4.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-1-4.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-1-5.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-1-5.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-1-5.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-1-5.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-1-6.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-1-6.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-1-6.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-1-6.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-1-7.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-1-7.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-1-7.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-1-7.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-1-8.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-1-8.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-1-8.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-1-8.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-1-9.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-1-9.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-1-9.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-1-9.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-2-0.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-2-0.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-2-0.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-2-0.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-2-1.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-2-1.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-2-1.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-2-1.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-2-10.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-2-10.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-2-10.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-2-10.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-2-11.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-2-11.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-2-11.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-2-11.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-2-12.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-2-12.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-2-12.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-2-12.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-2-13.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-2-13.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-2-13.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-2-13.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-2-14.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-2-14.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-2-14.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-2-14.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-2-15.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-2-15.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-2-15.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-2-15.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-2-16.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-2-16.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-2-16.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-2-16.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-2-17.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-2-17.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-2-17.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-2-17.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-2-18.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-2-18.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-2-18.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-2-18.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-2-19.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-2-19.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-2-19.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-2-19.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-2-2.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-2-2.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-2-2.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-2-2.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-2-3.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-2-3.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-2-3.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-2-3.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-2-4.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-2-4.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-2-4.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-2-4.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-2-5.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-2-5.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-2-5.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-2-5.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-2-6.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-2-6.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-2-6.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-2-6.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-2-7.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-2-7.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-2-7.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-2-7.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-2-8.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-2-8.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-2-8.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-2-8.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-2-9.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-2-9.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-2-9.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-2-9.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-3-0.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-3-0.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-3-0.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-3-0.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-3-1.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-3-1.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-3-1.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-3-1.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-3-10.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-3-10.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-3-10.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-3-10.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-3-11.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-3-11.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-3-11.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-3-11.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-3-12.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-3-12.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-3-12.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-3-12.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-3-13.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-3-13.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-3-13.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-3-13.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-3-14.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-3-14.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-3-14.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-3-14.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-3-15.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-3-15.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-3-15.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-3-15.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-3-16.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-3-16.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-3-16.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-3-16.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-3-17.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-3-17.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-3-17.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-3-17.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-3-18.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-3-18.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-3-18.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-3-18.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-3-19.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-3-19.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-3-19.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-3-19.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-3-2.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-3-2.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-3-2.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-3-2.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-3-3.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-3-3.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-3-3.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-3-3.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-3-4.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-3-4.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-3-4.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-3-4.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-3-5.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-3-5.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-3-5.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-3-5.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-3-6.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-3-6.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-3-6.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-3-6.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-3-7.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-3-7.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-3-7.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-3-7.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-3-8.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-3-8.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-3-8.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-3-8.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-3-9.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-3-9.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-3-9.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-3-9.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-4-0.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-4-0.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-4-0.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-4-0.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-4-1.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-4-1.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-4-1.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-4-1.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-4-10.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-4-10.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-4-10.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-4-10.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-4-11.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-4-11.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-4-11.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-4-11.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-4-12.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-4-12.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-4-12.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-4-12.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-4-13.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-4-13.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-4-13.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-4-13.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-4-14.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-4-14.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-4-14.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-4-14.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-4-15.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-4-15.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-4-15.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-4-15.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-4-16.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-4-16.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-4-16.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-4-16.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-4-17.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-4-17.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-4-17.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-4-17.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-4-18.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-4-18.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-4-18.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-4-18.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-4-19.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-4-19.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-4-19.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-4-19.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-4-2.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-4-2.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-4-2.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-4-2.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-4-3.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-4-3.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-4-3.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-4-3.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-4-4.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-4-4.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-4-4.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-4-4.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-4-5.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-4-5.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-4-5.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-4-5.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-4-6.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-4-6.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-4-6.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-4-6.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-4-7.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-4-7.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-4-7.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-4-7.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-4-8.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-4-8.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-4-8.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-4-8.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-4-9.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-4-9.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-4-9.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-4-9.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-5-0.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-5-0.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-5-0.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-5-0.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-5-1.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-5-1.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-5-1.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-5-1.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-5-10.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-5-10.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-5-10.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-5-10.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-5-11.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-5-11.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-5-11.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-5-11.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-5-12.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-5-12.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-5-12.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-5-12.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-5-13.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-5-13.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-5-13.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-5-13.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-5-14.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-5-14.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-5-14.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-5-14.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-5-15.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-5-15.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-5-15.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-5-15.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-5-16.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-5-16.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-5-16.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-5-16.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-5-17.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-5-17.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-5-17.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-5-17.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-5-18.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-5-18.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-5-18.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-5-18.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-5-19.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-5-19.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-5-19.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-5-19.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-5-2.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-5-2.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-5-2.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-5-2.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-5-3.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-5-3.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-5-3.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-5-3.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-5-4.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-5-4.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-5-4.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-5-4.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-5-5.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-5-5.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-5-5.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-5-5.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-5-6.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-5-6.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-5-6.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-5-6.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-5-7.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-5-7.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-5-7.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-5-7.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-5-8.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-5-8.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-5-8.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-5-8.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-5-9.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-5-9.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-5-9.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-5-9.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-6-0.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-6-0.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-6-0.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-6-0.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-6-1.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-6-1.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-6-1.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-6-1.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-6-10.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-6-10.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-6-10.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-6-10.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-6-11.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-6-11.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-6-11.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-6-11.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-6-12.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-6-12.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-6-12.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-6-12.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-6-13.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-6-13.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-6-13.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-6-13.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-6-14.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-6-14.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-6-14.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-6-14.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-6-15.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-6-15.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-6-15.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-6-15.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-6-16.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-6-16.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-6-16.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-6-16.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-6-17.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-6-17.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-6-17.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-6-17.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-6-18.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-6-18.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-6-18.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-6-18.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-6-19.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-6-19.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-6-19.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-6-19.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-6-2.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-6-2.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-6-2.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-6-2.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-6-3.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-6-3.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-6-3.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-6-3.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-6-4.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-6-4.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-6-4.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-6-4.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-6-5.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-6-5.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-6-5.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-6-5.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-6-6.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-6-6.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-6-6.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-6-6.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-6-7.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-6-7.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-6-7.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-6-7.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-6-8.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-6-8.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-6-8.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-6-8.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-6-9.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-6-9.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-6-9.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-6-9.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-7-0.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-7-0.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-7-0.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-7-0.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-7-1.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-7-1.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-7-1.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-7-1.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-7-10.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-7-10.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-7-10.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-7-10.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-7-11.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-7-11.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-7-11.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-7-11.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-7-12.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-7-12.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-7-12.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-7-12.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-7-13.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-7-13.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-7-13.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-7-13.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-7-14.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-7-14.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-7-14.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-7-14.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-7-15.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-7-15.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-7-15.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-7-15.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-7-16.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-7-16.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-7-16.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-7-16.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-7-17.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-7-17.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-7-17.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-7-17.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-7-18.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-7-18.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-7-18.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-7-18.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-7-19.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-7-19.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-7-19.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-7-19.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-7-2.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-7-2.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-7-2.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-7-2.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-7-3.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-7-3.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-7-3.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-7-3.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-7-4.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-7-4.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-7-4.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-7-4.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-7-5.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-7-5.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-7-5.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-7-5.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-7-6.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-7-6.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-7-6.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-7-6.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-7-7.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-7-7.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-7-7.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-7-7.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-7-8.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-7-8.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-7-8.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-7-8.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-7-9.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-7-9.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-7-9.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-7-9.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-8-0.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-8-0.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-8-0.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-8-0.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-8-1.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-8-1.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-8-1.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-8-1.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-8-10.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-8-10.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-8-10.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-8-10.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-8-11.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-8-11.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-8-11.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-8-11.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-8-12.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-8-12.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-8-12.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-8-12.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-8-13.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-8-13.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-8-13.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-8-13.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-8-14.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-8-14.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-8-14.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-8-14.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-8-15.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-8-15.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-8-15.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-8-15.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-8-16.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-8-16.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-8-16.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-8-16.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-8-17.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-8-17.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-8-17.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-8-17.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-8-18.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-8-18.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-8-18.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-8-18.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-8-19.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-8-19.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-8-19.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-8-19.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-8-2.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-8-2.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-8-2.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-8-2.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-8-3.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-8-3.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-8-3.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-8-3.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-8-4.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-8-4.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-8-4.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-8-4.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-8-5.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-8-5.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-8-5.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-8-5.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-8-6.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-8-6.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-8-6.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-8-6.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-8-7.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-8-7.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-8-7.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-8-7.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-8-8.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-8-8.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-8-8.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-8-8.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-8-9.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-8-9.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-8-9.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-8-9.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-9-0.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-9-0.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-9-0.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-9-0.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-9-1.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-9-1.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-9-1.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-9-1.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-9-10.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-9-10.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-9-10.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-9-10.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-9-11.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-9-11.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-9-11.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-9-11.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-9-12.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-9-12.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-9-12.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-9-12.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-9-13.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-9-13.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-9-13.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-9-13.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-9-14.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-9-14.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-9-14.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-9-14.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-9-15.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-9-15.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-9-15.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-9-15.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-9-16.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-9-16.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-9-16.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-9-16.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-9-17.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-9-17.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-9-17.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-9-17.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-9-18.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-9-18.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-9-18.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-9-18.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-9-19.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-9-19.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-9-19.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-9-19.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-9-2.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-9-2.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-9-2.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-9-2.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-9-3.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-9-3.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-9-3.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-9-3.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-9-4.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-9-4.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-9-4.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-9-4.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-9-5.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-9-5.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-9-5.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-9-5.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-9-6.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-9-6.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-9-6.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-9-6.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-9-7.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-9-7.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-9-7.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-9-7.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-9-8.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-9-8.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-9-8.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-9-8.jpeg diff --git a/example/src/main/resources/io/netty/example/http2/tiles/tile-9-9.jpeg b/example/src/main/resources/io/net5/example/http2/tiles/tile-9-9.jpeg similarity index 100% rename from example/src/main/resources/io/netty/example/http2/tiles/tile-9-9.jpeg rename to example/src/main/resources/io/net5/example/http2/tiles/tile-9-9.jpeg diff --git a/example/src/main/resources/io/netty/example/ocsp/README.txt b/example/src/main/resources/io/netty/example/ocsp/README.txt deleted file mode 100644 index 2139ceed7b..0000000000 --- a/example/src/main/resources/io/netty/example/ocsp/README.txt +++ /dev/null @@ -1,8 +0,0 @@ -The netty_io_chain.pem file is the cert chain of . The file -was created using the browser's export functionality for certs. The cert was -issued by COMODO (via Cloudflare) and its purpose is to demonstrate how to -extract the CA's OCSP responder server URL from the certificate and then -interact with the responder server to check the revocation status of the -certificate. The cert will at some point expire or get revoked. It's probably -a good idea to save it once that happens as it's an excellent example to -demonstrate negative responses from the CA. \ No newline at end of file diff --git a/example/src/main/resources/io/netty/example/ocsp/netty_io_chain.pem b/example/src/main/resources/io/netty/example/ocsp/netty_io_chain.pem deleted file mode 100644 index 9471a3d95c..0000000000 --- a/example/src/main/resources/io/netty/example/ocsp/netty_io_chain.pem +++ /dev/null @@ -1,63 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIHIDCCBsagAwIBAgIQWBZ3c+IkpJfV6xSQfc79MzAKBggqhkjOPQQDAjCBkjEL -MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE -BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxODA2BgNVBAMT -L0NPTU9ETyBFQ0MgRG9tYWluIFZhbGlkYXRpb24gU2VjdXJlIFNlcnZlciBDQSAy -MB4XDTE3MDIyMTAwMDAwMFoXDTE3MDgwNjIzNTk1OVowazEhMB8GA1UECxMYRG9t -YWluIENvbnRyb2wgVmFsaWRhdGVkMSEwHwYDVQQLExhQb3NpdGl2ZVNTTCBNdWx0 -aS1Eb21haW4xIzAhBgNVBAMTGnNuaTQ5NjI5LmNsb3VkZmxhcmVzc2wuY29tMFkw -EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEbBzbKRFsB8VD5/T1ruGNByqTP3b+7zRN -TJ0uaaHb5BzDd7+bdfYeQLfxbIe9yIX/InRWxcPrxZxNFe0pe26zWaOCBSIwggUe -MB8GA1UdIwQYMBaAFEAJYWfwvINxT94SCCxv1NQrdj2WMB0GA1UdDgQWBBQB5zit -yT/cG3KZA9q/xPDNgwVu4DAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAd -BgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwTwYDVR0gBEgwRjA6BgsrBgEE -AbIxAQICBzArMCkGCCsGAQUFBwIBFh1odHRwczovL3NlY3VyZS5jb21vZG8uY29t -L0NQUzAIBgZngQwBAgEwVgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDovL2NybC5jb21v -ZG9jYTQuY29tL0NPTU9ET0VDQ0RvbWFpblZhbGlkYXRpb25TZWN1cmVTZXJ2ZXJD -QTIuY3JsMIGIBggrBgEFBQcBAQR8MHowUQYIKwYBBQUHMAKGRWh0dHA6Ly9jcnQu -Y29tb2RvY2E0LmNvbS9DT01PRE9FQ0NEb21haW5WYWxpZGF0aW9uU2VjdXJlU2Vy -dmVyQ0EyLmNydDAlBggrBgEFBQcwAYYZaHR0cDovL29jc3AuY29tb2RvY2E0LmNv -bTCCA2kGA1UdEQSCA2AwggNcghpzbmk0OTYyOS5jbG91ZGZsYXJlc3NsLmNvbYIQ -Ki4wOTMzMTk0NDIzLmNvbYISKi5hbGlhbW90ZWwuY29tLnR3ghMqLmFuaW1hbGNv -bGxlZ2Uub3JnghEqLmFyZG9yLWRjLmNvbS50d4IOKi5hc2lhbmlhZ2EubXmCDCou -YmlsbG93cy50d4ISKi5jYW5keXNvdXJjZXMuY29tgiIqLmNvbmNyZXRlY29tbWFu -ZG9zY29uc3RydWN0aW9uLmNhghYqLmZsYXRsaW5lc2VjdXJpdHkuY29tgggqLmcy -Yy50d4IOKi5nbGluZS5jb20udHeCEioubGVtaXJhYmVhdXR5LmNvbYIWKi5saWJl -cnR5ZGVzaWduLmNvbS50d4ISKi5saWJlcnR5ZGVzaWduLnR3ghIqLmxpYmVydHlv -Y2Vhbi5jb22CEioubGl1aC1qaWFuLmNvbS50d4IQKi5samhzYWx1bW5pLm9yZ4Ia -Ki5sb21hbmthbS13aW5nY2h1bi5vcmcudHeCCioubmV0dHkuaW+CDCoub3BsaWZ0 -LmNvbYITKi5zb3V0aGVybmJlbGxzLm5ldIIZKi50ZW5yeW8tY2xlYW5yb29tLmNv -bS50d4IOMDkzMzE5NDQyMy5jb22CEGFsaWFtb3RlbC5jb20udHeCEWFuaW1hbGNv -bGxlZ2Uub3Jngg9hcmRvci1kYy5jb20udHeCDGFzaWFuaWFnYS5teYIKYmlsbG93 -cy50d4IQY2FuZHlzb3VyY2VzLmNvbYIgY29uY3JldGVjb21tYW5kb3Njb25zdHJ1 -Y3Rpb24uY2GCFGZsYXRsaW5lc2VjdXJpdHkuY29tggZnMmMudHeCDGdsaW5lLmNv -bS50d4IQbGVtaXJhYmVhdXR5LmNvbYIUbGliZXJ0eWRlc2lnbi5jb20udHeCEGxp -YmVydHlkZXNpZ24udHeCEGxpYmVydHlvY2Vhbi5jb22CEGxpdWgtamlhbi5jb20u -dHeCDmxqaHNhbHVtbmkub3Jnghhsb21hbmthbS13aW5nY2h1bi5vcmcudHeCCG5l -dHR5LmlvggpvcGxpZnQuY29tghFzb3V0aGVybmJlbGxzLm5ldIIXdGVucnlvLWNs -ZWFucm9vbS5jb20udHcwCgYIKoZIzj0EAwIDSAAwRQIgCqAcYW//4ucQu4WiCM/l -bsaz7ZIJb6vKxyMoWRoCuJkCIQDEIK02bfsKicBK9DJGCq7j6TkmIwwvnRuuRA7W -08APyA== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDnzCCAyWgAwIBAgIQWyXOaQfEJlVm0zkMmalUrTAKBggqhkjOPQQDAzCBhTEL -MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE -BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT -IkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTQwOTI1MDAw -MDAwWhcNMjkwOTI0MjM1OTU5WjCBkjELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdy -ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09N -T0RPIENBIExpbWl0ZWQxODA2BgNVBAMTL0NPTU9ETyBFQ0MgRG9tYWluIFZhbGlk -YXRpb24gU2VjdXJlIFNlcnZlciBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD -QgAEAjgZgTrJaYRwWQKOqIofMN+83gP8eR06JSxrQSEYgur5PkrkM8wSzypD/A7y -ZADA4SVQgiTNtkk4DyVHkUikraOCAWYwggFiMB8GA1UdIwQYMBaAFHVxpxlIGbyd -nepBR9+UxEh3mdN5MB0GA1UdDgQWBBRACWFn8LyDcU/eEggsb9TUK3Y9ljAOBgNV -HQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHSUEFjAUBggrBgEF -BQcDAQYIKwYBBQUHAwIwGwYDVR0gBBQwEjAGBgRVHSAAMAgGBmeBDAECATBMBgNV -HR8ERTBDMEGgP6A9hjtodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9FQ0ND -ZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDByBggrBgEFBQcBAQRmMGQwOwYIKwYB -BQUHMAKGL2h0dHA6Ly9jcnQuY29tb2RvY2EuY29tL0NPTU9ET0VDQ0FkZFRydXN0 -Q0EuY3J0MCUGCCsGAQUFBzABhhlodHRwOi8vb2NzcC5jb21vZG9jYTQuY29tMAoG -CCqGSM49BAMDA2gAMGUCMQCsaEclgBNPE1bAojcJl1pQxOfttGHLKIoKETKm4nHf -EQGJbwd6IGZrGNC5LkP3Um8CMBKFfI4TZpIEuppFCZRKMGHRSdxv6+ctyYnPHmp8 -7IXOMCVZuoFwNLg0f+cB0eLLUg== ------END CERTIFICATE----- diff --git a/example/src/main/resources/io/netty/example/stomp/websocket/css/bootstrap.min.css b/example/src/main/resources/io/netty/example/stomp/websocket/css/bootstrap.min.css deleted file mode 100644 index 5e1aee0948..0000000000 --- a/example/src/main/resources/io/netty/example/stomp/websocket/css/bootstrap.min.css +++ /dev/null @@ -1,610 +0,0 @@ -article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block;} -audio,canvas,video{display:inline-block;*display:inline;*zoom:1;} -audio:not([controls]){display:none;} -html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;} -a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;} -a:hover,a:active{outline:0;} -sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline;} -sup{top:-0.5em;} -sub{bottom:-0.25em;} -img{max-width:100%;height:auto;border:0;-ms-interpolation-mode:bicubic;} -button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle;} -button,input{*overflow:visible;line-height:normal;} -button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0;} -button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;} -input[type="search"]{-webkit-appearance:textfield;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;} -input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none;} -textarea{overflow:auto;vertical-align:top;} -body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;color:#333333;background-color:#ffffff;} -a{color:#0088cc;text-decoration:none;} -a:hover{color:#005580;text-decoration:underline;} -.row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";} -.row:after{clear:both;} -[class*="span"]{float:left;margin-left:20px;} -.span1{width:60px;} -.span2{width:140px;} -.span3{width:220px;} -.span4{width:300px;} -.span5{width:380px;} -.span6{width:460px;} -.span7{width:540px;} -.span8{width:620px;} -.span9{width:700px;} -.span10{width:780px;} -.span11{width:860px;} -.span12,.container{width:940px;} -.offset1{margin-left:100px;} -.offset2{margin-left:180px;} -.offset3{margin-left:260px;} -.offset4{margin-left:340px;} -.offset5{margin-left:420px;} -.offset6{margin-left:500px;} -.offset7{margin-left:580px;} -.offset8{margin-left:660px;} -.offset9{margin-left:740px;} -.offset10{margin-left:820px;} -.offset11{margin-left:900px;} -.row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";} -.row-fluid:after{clear:both;} -.row-fluid>[class*="span"]{float:left;margin-left:2.127659574%;} -.row-fluid>[class*="span"]:first-child{margin-left:0;} -.row-fluid .span1{width:6.382978723%;} -.row-fluid .span2{width:14.89361702%;} -.row-fluid .span3{width:23.404255317%;} -.row-fluid .span4{width:31.914893614%;} -.row-fluid .span5{width:40.425531911%;} -.row-fluid .span6{width:48.93617020799999%;} -.row-fluid .span7{width:57.446808505%;} -.row-fluid .span8{width:65.95744680199999%;} -.row-fluid .span9{width:74.468085099%;} -.row-fluid .span10{width:82.97872339599999%;} -.row-fluid .span11{width:91.489361693%;} -.row-fluid .span12{width:99.99999998999999%;} -.container{width:940px;margin-left:auto;margin-right:auto;*zoom:1;}.container:before,.container:after{display:table;content:"";} -.container:after{clear:both;} -.container-fluid{padding-left:20px;padding-right:20px;*zoom:1;}.container-fluid:before,.container-fluid:after{display:table;content:"";} -.container-fluid:after{clear:both;} -p{margin:0 0 9px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;}p small{font-size:11px;color:#999999;} -.lead{margin-bottom:18px;font-size:20px;font-weight:200;line-height:27px;} -h1,h2,h3,h4,h5,h6{margin:0;font-weight:bold;color:#333333;text-rendering:optimizelegibility;}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;color:#999999;} -h1{font-size:30px;line-height:36px;}h1 small{font-size:18px;} -h2{font-size:24px;line-height:36px;}h2 small{font-size:18px;} -h3{line-height:27px;font-size:18px;}h3 small{font-size:14px;} -h4,h5,h6{line-height:18px;} -h4{font-size:14px;}h4 small{font-size:12px;} -h5{font-size:12px;} -h6{font-size:11px;color:#999999;text-transform:uppercase;} -.page-header{padding-bottom:17px;margin:18px 0;border-bottom:1px solid #eeeeee;} -.page-header h1{line-height:1;} -ul,ol{padding:0;margin:0 0 9px 25px;} -ul ul,ul ol,ol ol,ol ul{margin-bottom:0;} -ul{list-style:disc;} -ol{list-style:decimal;} -li{line-height:18px;} -ul.unstyled{margin-left:0;list-style:none;} -dl{margin-bottom:18px;} -dt,dd{line-height:18px;} -dt{font-weight:bold;} -dd{margin-left:9px;} -hr{margin:18px 0;border:0;border-top:1px solid #e5e5e5;border-bottom:1px solid #ffffff;} -strong{font-weight:bold;} -em{font-style:italic;} -.muted{color:#999999;} -abbr{font-size:90%;text-transform:uppercase;border-bottom:1px dotted #ddd;cursor:help;} -blockquote{padding:0 0 0 15px;margin:0 0 18px;border-left:5px solid #eeeeee;}blockquote p{margin-bottom:0;font-size:16px;font-weight:300;line-height:22.5px;} -blockquote small{display:block;line-height:18px;color:#999999;}blockquote small:before{content:'\2014 \00A0';} -blockquote.pull-right{float:right;padding-left:0;padding-right:15px;border-left:0;border-right:5px solid #eeeeee;}blockquote.pull-right p,blockquote.pull-right small{text-align:right;} -q:before,q:after,blockquote:before,blockquote:after{content:"";} -address{display:block;margin-bottom:18px;line-height:18px;font-style:normal;} -small{font-size:100%;} -cite{font-style:normal;} -code,pre{padding:0 3px 2px;font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;color:#333333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} -code{padding:3px 4px;color:#d14;background-color:#f7f7f9;border:1px solid #e1e1e8;} -pre{display:block;padding:8.5px;margin:0 0 9px;font-size:12px;line-height:18px;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;white-space:pre;white-space:pre-wrap;word-break:break-all;}pre.prettyprint{margin-bottom:18px;} -pre code{padding:0;background-color:transparent;} -form{margin:0 0 18px;} -fieldset{padding:0;margin:0;border:0;} -legend{display:block;width:100%;padding:0;margin-bottom:27px;font-size:19.5px;line-height:36px;color:#333333;border:0;border-bottom:1px solid #eee;} -label,input,button,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:18px;} -label{display:block;margin-bottom:5px;color:#333333;} -input,textarea,select,.uneditable-input{display:inline-block;width:210px;height:18px;padding:4px;margin-bottom:9px;font-size:13px;line-height:18px;color:#555555;border:1px solid #ccc;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} -.uneditable-textarea{width:auto;height:auto;} -label input,label textarea,label select{display:block;} -input[type="image"],input[type="checkbox"],input[type="radio"]{width:auto;height:auto;padding:0;margin:3px 0;*margin-top:0;line-height:normal;border:0;cursor:pointer;border-radius:0 \0/;} -input[type="file"]{padding:initial;line-height:initial;border:initial;background-color:#ffffff;background-color:initial;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} -input[type="button"],input[type="reset"],input[type="submit"]{width:auto;height:auto;} -select,input[type="file"]{height:28px;*margin-top:4px;line-height:28px;} -select{width:220px;background-color:#ffffff;} -select[multiple],select[size]{height:auto;} -input[type="image"]{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} -textarea{height:auto;} -input[type="hidden"]{display:none;} -.radio,.checkbox{padding-left:18px;} -.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-18px;} -.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px;} -.radio.inline,.checkbox.inline{display:inline-block;margin-bottom:0;vertical-align:middle;} -.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px;} -.controls>.radio.inline:first-child,.controls>.checkbox.inline:first-child{padding-top:0;} -input,textarea{-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-webkit-transition:border linear 0.2s,box-shadow linear 0.2s;-moz-transition:border linear 0.2s,box-shadow linear 0.2s;-ms-transition:border linear 0.2s,box-shadow linear 0.2s;-o-transition:border linear 0.2s,box-shadow linear 0.2s;transition:border linear 0.2s,box-shadow linear 0.2s;} -input:focus,textarea:focus{border-color:rgba(82, 168, 236, 0.8);-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);outline:0;outline:thin dotted \9;} -input[type="file"]:focus,input[type="checkbox"]:focus,select:focus{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;} -.input-mini{width:60px;} -.input-small{width:90px;} -.input-medium{width:150px;} -.input-large{width:210px;} -.input-xlarge{width:270px;} -.input-xxlarge{width:530px;} -input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{float:none;margin-left:0;} -input.span1,textarea.span1,.uneditable-input.span1{width:50px;} -input.span2,textarea.span2,.uneditable-input.span2{width:130px;} -input.span3,textarea.span3,.uneditable-input.span3{width:210px;} -input.span4,textarea.span4,.uneditable-input.span4{width:290px;} -input.span5,textarea.span5,.uneditable-input.span5{width:370px;} -input.span6,textarea.span6,.uneditable-input.span6{width:450px;} -input.span7,textarea.span7,.uneditable-input.span7{width:530px;} -input.span8,textarea.span8,.uneditable-input.span8{width:610px;} -input.span9,textarea.span9,.uneditable-input.span9{width:690px;} -input.span10,textarea.span10,.uneditable-input.span10{width:770px;} -input.span11,textarea.span11,.uneditable-input.span11{width:850px;} -input.span12,textarea.span12,.uneditable-input.span12{width:930px;} -input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{background-color:#f5f5f5;border-color:#ddd;cursor:not-allowed;} -.control-group.warning>label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#c09853;} -.control-group.warning input,.control-group.warning select,.control-group.warning textarea{color:#c09853;border-color:#c09853;}.control-group.warning input:focus,.control-group.warning select:focus,.control-group.warning textarea:focus{border-color:#a47e3c;-webkit-box-shadow:0 0 6px #dbc59e;-moz-box-shadow:0 0 6px #dbc59e;box-shadow:0 0 6px #dbc59e;} -.control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853;} -.control-group.error>label,.control-group.error .help-block,.control-group.error .help-inline{color:#b94a48;} -.control-group.error input,.control-group.error select,.control-group.error textarea{color:#b94a48;border-color:#b94a48;}.control-group.error input:focus,.control-group.error select:focus,.control-group.error textarea:focus{border-color:#953b39;-webkit-box-shadow:0 0 6px #d59392;-moz-box-shadow:0 0 6px #d59392;box-shadow:0 0 6px #d59392;} -.control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48;} -.control-group.success>label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847;} -.control-group.success input,.control-group.success select,.control-group.success textarea{color:#468847;border-color:#468847;}.control-group.success input:focus,.control-group.success select:focus,.control-group.success textarea:focus{border-color:#356635;-webkit-box-shadow:0 0 6px #7aba7b;-moz-box-shadow:0 0 6px #7aba7b;box-shadow:0 0 6px #7aba7b;} -.control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847;} -input:focus:required:invalid,textarea:focus:required:invalid,select:focus:required:invalid{color:#b94a48;border-color:#ee5f5b;}input:focus:required:invalid:focus,textarea:focus:required:invalid:focus,select:focus:required:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7;} -.form-actions{padding:17px 20px 18px;margin-top:18px;margin-bottom:18px;background-color:#f5f5f5;border-top:1px solid #ddd;} -.uneditable-input{display:block;background-color:#ffffff;border-color:#eee;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);cursor:not-allowed;} -:-moz-placeholder{color:#999999;} -::-webkit-input-placeholder{color:#999999;} -.help-block{margin-top:5px;margin-bottom:0;color:#999999;} -.help-inline{display:inline-block;*display:inline;*zoom:1;margin-bottom:9px;vertical-align:middle;padding-left:5px;} -.input-prepend,.input-append{margin-bottom:5px;*zoom:1;}.input-prepend:before,.input-append:before,.input-prepend:after,.input-append:after{display:table;content:"";} -.input-prepend:after,.input-append:after{clear:both;} -.input-prepend input,.input-append input,.input-prepend .uneditable-input,.input-append .uneditable-input{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}.input-prepend input:focus,.input-append input:focus,.input-prepend .uneditable-input:focus,.input-append .uneditable-input:focus{position:relative;z-index:2;} -.input-prepend .uneditable-input,.input-append .uneditable-input{border-left-color:#ccc;} -.input-prepend .add-on,.input-append .add-on{float:left;display:block;width:auto;min-width:16px;height:18px;margin-right:-1px;padding:4px 5px;font-weight:normal;line-height:18px;color:#999999;text-align:center;text-shadow:0 1px 0 #ffffff;background-color:#f5f5f5;border:1px solid #ccc;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;} -.input-prepend .active,.input-append .active{background-color:#a9dba9;border-color:#46a546;} -.input-prepend .add-on{*margin-top:1px;} -.input-append input,.input-append .uneditable-input{float:left;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;} -.input-append .uneditable-input{border-right-color:#ccc;} -.input-append .add-on{margin-right:0;margin-left:-1px;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;} -.input-append input:first-child{*margin-left:-160px;}.input-append input:first-child+.add-on{*margin-left:-21px;} -.search-query{padding-left:14px;padding-right:14px;margin-bottom:0;-webkit-border-radius:14px;-moz-border-radius:14px;border-radius:14px;} -.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input{display:inline-block;margin-bottom:0;} -.form-search label,.form-inline label,.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{display:inline-block;} -.form-search .input-append .add-on,.form-inline .input-prepend .add-on,.form-search .input-append .add-on,.form-inline .input-prepend .add-on{vertical-align:middle;} -.control-group{margin-bottom:9px;} -.form-horizontal legend+.control-group{margin-top:18px;-webkit-margin-top-collapse:separate;} -.form-horizontal .control-group{margin-bottom:18px;*zoom:1;}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;content:"";} -.form-horizontal .control-group:after{clear:both;} -.form-horizontal .control-group>label{float:left;width:140px;padding-top:5px;text-align:right;} -.form-horizontal .controls{margin-left:160px;} -.form-horizontal .form-actions{padding-left:160px;} -table{max-width:100%;border-collapse:collapse;border-spacing:0;} -.table{width:100%;margin-bottom:18px;}.table th,.table td{padding:8px;line-height:18px;text-align:left;border-top:1px solid #ddd;} -.table th{font-weight:bold;vertical-align:bottom;} -.table td{vertical-align:top;} -.table thead:first-child tr th,.table thead:first-child tr td{border-top:0;} -.table tbody+tbody{border-top:2px solid #ddd;} -.table-condensed th,.table-condensed td{padding:4px 5px;} -.table-bordered{border:1px solid #ddd;border-collapse:separate;*border-collapse:collapsed;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.table-bordered th+th,.table-bordered td+td,.table-bordered th+td,.table-bordered td+th{border-left:1px solid #ddd;} -.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0;} -.table-bordered thead:first-child tr:first-child th:first-child,.table-bordered tbody:first-child tr:first-child td:first-child{-webkit-border-radius:4px 0 0 0;-moz-border-radius:4px 0 0 0;border-radius:4px 0 0 0;} -.table-bordered thead:first-child tr:first-child th:last-child,.table-bordered tbody:first-child tr:first-child td:last-child{-webkit-border-radius:0 4px 0 0;-moz-border-radius:0 4px 0 0;border-radius:0 4px 0 0;} -.table-bordered thead:last-child tr:last-child th:first-child,.table-bordered tbody:last-child tr:last-child td:first-child{-webkit-border-radius:0 0 0 4px;-moz-border-radius:0 0 0 4px;border-radius:0 0 0 4px;} -.table-bordered thead:last-child tr:last-child th:last-child,.table-bordered tbody:last-child tr:last-child td:last-child{-webkit-border-radius:0 0 4px 0;-moz-border-radius:0 0 4px 0;border-radius:0 0 4px 0;} -.table-striped tbody tr:nth-child(odd) td,.table-striped tbody tr:nth-child(odd) th{background-color:#f9f9f9;} -table .span1{float:none;width:44px;margin-left:0;} -table .span2{float:none;width:124px;margin-left:0;} -table .span3{float:none;width:204px;margin-left:0;} -table .span4{float:none;width:284px;margin-left:0;} -table .span5{float:none;width:364px;margin-left:0;} -table .span6{float:none;width:444px;margin-left:0;} -table .span7{float:none;width:524px;margin-left:0;} -table .span8{float:none;width:604px;margin-left:0;} -table .span9{float:none;width:684px;margin-left:0;} -table .span10{float:none;width:764px;margin-left:0;} -table .span11{float:none;width:844px;margin-left:0;} -table .span12{float:none;width:924px;margin-left:0;} -[class^="icon-"]{display:inline-block;width:14px;height:14px;vertical-align:text-top;background-image:url(../img/glyphicons-halflings.png);background-position:14px 14px;background-repeat:no-repeat;*margin-right:.3em;}[class^="icon-"]:last-child{*margin-left:0;} -.icon-white{background-image:url(../img/glyphicons-halflings-white.png);} -.icon-glass{background-position:0 0;} -.icon-music{background-position:-24px 0;} -.icon-search{background-position:-48px 0;} -.icon-envelope{background-position:-72px 0;} -.icon-heart{background-position:-96px 0;} -.icon-star{background-position:-120px 0;} -.icon-star-empty{background-position:-144px 0;} -.icon-user{background-position:-168px 0;} -.icon-film{background-position:-192px 0;} -.icon-th-large{background-position:-216px 0;} -.icon-th{background-position:-240px 0;} -.icon-th-list{background-position:-264px 0;} -.icon-ok{background-position:-288px 0;} -.icon-remove{background-position:-312px 0;} -.icon-zoom-in{background-position:-336px 0;} -.icon-zoom-out{background-position:-360px 0;} -.icon-off{background-position:-384px 0;} -.icon-signal{background-position:-408px 0;} -.icon-cog{background-position:-432px 0;} -.icon-trash{background-position:-456px 0;} -.icon-home{background-position:0 -24px;} -.icon-file{background-position:-24px -24px;} -.icon-time{background-position:-48px -24px;} -.icon-road{background-position:-72px -24px;} -.icon-download-alt{background-position:-96px -24px;} -.icon-download{background-position:-120px -24px;} -.icon-upload{background-position:-144px -24px;} -.icon-inbox{background-position:-168px -24px;} -.icon-play-circle{background-position:-192px -24px;} -.icon-repeat{background-position:-216px -24px;} -.icon-refresh{background-position:-240px -24px;} -.icon-list-alt{background-position:-264px -24px;} -.icon-lock{background-position:-287px -24px;} -.icon-flag{background-position:-312px -24px;} -.icon-headphones{background-position:-336px -24px;} -.icon-volume-off{background-position:-360px -24px;} -.icon-volume-down{background-position:-384px -24px;} -.icon-volume-up{background-position:-408px -24px;} -.icon-qrcode{background-position:-432px -24px;} -.icon-barcode{background-position:-456px -24px;} -.icon-tag{background-position:0 -48px;} -.icon-tags{background-position:-25px -48px;} -.icon-book{background-position:-48px -48px;} -.icon-bookmark{background-position:-72px -48px;} -.icon-print{background-position:-96px -48px;} -.icon-camera{background-position:-120px -48px;} -.icon-font{background-position:-144px -48px;} -.icon-bold{background-position:-167px -48px;} -.icon-italic{background-position:-192px -48px;} -.icon-text-height{background-position:-216px -48px;} -.icon-text-width{background-position:-240px -48px;} -.icon-align-left{background-position:-264px -48px;} -.icon-align-center{background-position:-288px -48px;} -.icon-align-right{background-position:-312px -48px;} -.icon-align-justify{background-position:-336px -48px;} -.icon-list{background-position:-360px -48px;} -.icon-indent-left{background-position:-384px -48px;} -.icon-indent-right{background-position:-408px -48px;} -.icon-facetime-video{background-position:-432px -48px;} -.icon-picture{background-position:-456px -48px;} -.icon-pencil{background-position:0 -72px;} -.icon-map-marker{background-position:-24px -72px;} -.icon-adjust{background-position:-48px -72px;} -.icon-tint{background-position:-72px -72px;} -.icon-edit{background-position:-96px -72px;} -.icon-share{background-position:-120px -72px;} -.icon-check{background-position:-144px -72px;} -.icon-move{background-position:-168px -72px;} -.icon-step-backward{background-position:-192px -72px;} -.icon-fast-backward{background-position:-216px -72px;} -.icon-backward{background-position:-240px -72px;} -.icon-play{background-position:-264px -72px;} -.icon-pause{background-position:-288px -72px;} -.icon-stop{background-position:-312px -72px;} -.icon-forward{background-position:-336px -72px;} -.icon-fast-forward{background-position:-360px -72px;} -.icon-step-forward{background-position:-384px -72px;} -.icon-eject{background-position:-408px -72px;} -.icon-chevron-left{background-position:-432px -72px;} -.icon-chevron-right{background-position:-456px -72px;} -.icon-plus-sign{background-position:0 -96px;} -.icon-minus-sign{background-position:-24px -96px;} -.icon-remove-sign{background-position:-48px -96px;} -.icon-ok-sign{background-position:-72px -96px;} -.icon-question-sign{background-position:-96px -96px;} -.icon-info-sign{background-position:-120px -96px;} -.icon-screenshot{background-position:-144px -96px;} -.icon-remove-circle{background-position:-168px -96px;} -.icon-ok-circle{background-position:-192px -96px;} -.icon-ban-circle{background-position:-216px -96px;} -.icon-arrow-left{background-position:-240px -96px;} -.icon-arrow-right{background-position:-264px -96px;} -.icon-arrow-up{background-position:-289px -96px;} -.icon-arrow-down{background-position:-312px -96px;} -.icon-share-alt{background-position:-336px -96px;} -.icon-resize-full{background-position:-360px -96px;} -.icon-resize-small{background-position:-384px -96px;} -.icon-plus{background-position:-408px -96px;} -.icon-minus{background-position:-433px -96px;} -.icon-asterisk{background-position:-456px -96px;} -.icon-exclamation-sign{background-position:0 -120px;} -.icon-gift{background-position:-24px -120px;} -.icon-leaf{background-position:-48px -120px;} -.icon-fire{background-position:-72px -120px;} -.icon-eye-open{background-position:-96px -120px;} -.icon-eye-close{background-position:-120px -120px;} -.icon-warning-sign{background-position:-144px -120px;} -.icon-plane{background-position:-168px -120px;} -.icon-calendar{background-position:-192px -120px;} -.icon-random{background-position:-216px -120px;} -.icon-comment{background-position:-240px -120px;} -.icon-magnet{background-position:-264px -120px;} -.icon-chevron-up{background-position:-288px -120px;} -.icon-chevron-down{background-position:-313px -119px;} -.icon-retweet{background-position:-336px -120px;} -.icon-shopping-cart{background-position:-360px -120px;} -.icon-folder-close{background-position:-384px -120px;} -.icon-folder-open{background-position:-408px -120px;} -.icon-resize-vertical{background-position:-432px -119px;} -.icon-resize-horizontal{background-position:-456px -118px;} -.dropdown{position:relative;} -.dropdown-toggle{*margin-bottom:-3px;} -.dropdown-toggle:active,.open .dropdown-toggle{outline:0;} -.caret{display:inline-block;width:0;height:0;text-indent:-99999px;*text-indent:0;vertical-align:top;border-left:4px solid transparent;border-right:4px solid transparent;border-top:4px solid #000000;opacity:0.3;filter:alpha(opacity=30);content:"\2193";} -.dropdown .caret{margin-top:8px;margin-left:2px;} -.dropdown:hover .caret,.open.dropdown .caret{opacity:1;filter:alpha(opacity=100);} -.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;float:left;display:none;min-width:160px;max-width:220px;_width:160px;padding:4px 0;margin:0;list-style:none;background-color:#ffffff;border-color:#ccc;border-color:rgba(0, 0, 0, 0.2);border-style:solid;border-width:1px;-webkit-border-radius:0 0 5px 5px;-moz-border-radius:0 0 5px 5px;border-radius:0 0 5px 5px;-webkit-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box;*border-right-width:2px;*border-bottom-width:2px;}.dropdown-menu.bottom-up{top:auto;bottom:100%;margin-bottom:2px;} -.dropdown-menu .divider{height:1px;margin:5px 1px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #ffffff;*width:100%;*margin:-5px 0 5px;} -.dropdown-menu a{display:block;padding:3px 15px;clear:both;font-weight:normal;line-height:18px;color:#555555;white-space:nowrap;} -.dropdown-menu li>a:hover,.dropdown-menu .active>a,.dropdown-menu .active>a:hover{color:#ffffff;text-decoration:none;background-color:#0088cc;} -.dropdown.open{*z-index:1000;}.dropdown.open .dropdown-toggle{color:#ffffff;background:#ccc;background:rgba(0, 0, 0, 0.3);} -.dropdown.open .dropdown-menu{display:block;} -.typeahead{margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} -.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #eee;border:1px solid rgba(0, 0, 0, 0.05);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);}.well blockquote{border-color:#ddd;border-color:rgba(0, 0, 0, 0.15);} -.fade{-webkit-transition:opacity 0.15s linear;-moz-transition:opacity 0.15s linear;-ms-transition:opacity 0.15s linear;-o-transition:opacity 0.15s linear;transition:opacity 0.15s linear;opacity:0;}.fade.in{opacity:1;} -.collapse{-webkit-transition:height 0.35s ease;-moz-transition:height 0.35s ease;-ms-transition:height 0.35s ease;-o-transition:height 0.35s ease;transition:height 0.35s ease;position:relative;overflow:hidden;height:0;}.collapse.in{height:auto;} -.close{float:right;font-size:20px;font-weight:bold;line-height:18px;color:#000000;text-shadow:0 1px 0 #ffffff;opacity:0.2;filter:alpha(opacity=20);}.close:hover{color:#000000;text-decoration:none;opacity:0.4;filter:alpha(opacity=40);cursor:pointer;} -.btn{display:inline-block;padding:4px 10px 4px;font-size:13px;line-height:18px;color:#333333;text-align:center;text-shadow:0 1px 1px rgba(255, 255, 255, 0.75);background-color:#fafafa;background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), color-stop(25%, #ffffff), to(#e6e6e6));background-image:-webkit-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-image:-moz-linear-gradient(top, #ffffff, #ffffff 25%, #e6e6e6);background-image:-ms-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-image:-o-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-image:linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);border:1px solid #ccc;border-bottom-color:#bbb;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);cursor:pointer;*margin-left:.3em;}.btn:first-child{*margin-left:0;} -.btn:hover{color:#333333;text-decoration:none;background-color:#e6e6e6;background-position:0 -15px;-webkit-transition:background-position 0.1s linear;-moz-transition:background-position 0.1s linear;-ms-transition:background-position 0.1s linear;-o-transition:background-position 0.1s linear;transition:background-position 0.1s linear;} -.btn:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;} -.btn.active,.btn:active{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);background-color:#e6e6e6;background-color:#d9d9d9 \9;color:rgba(0, 0, 0, 0.5);outline:0;} -.btn.disabled,.btn[disabled]{cursor:default;background-image:none;background-color:#e6e6e6;opacity:0.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} -.btn-large{padding:9px 14px;font-size:15px;line-height:normal;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;} -.btn-large .icon{margin-top:1px;} -.btn-small{padding:5px 9px;font-size:11px;line-height:16px;} -.btn-small .icon{margin-top:-1px;} -.btn-primary,.btn-primary:hover,.btn-warning,.btn-warning:hover,.btn-danger,.btn-danger:hover,.btn-success,.btn-success:hover,.btn-info,.btn-info:hover{text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);color:#ffffff;} -.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active{color:rgba(255, 255, 255, 0.75);} -.btn-primary{background-color:#006dcc;background-image:-moz-linear-gradient(top, #0088cc, #0044cc);background-image:-ms-linear-gradient(top, #0088cc, #0044cc);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));background-image:-webkit-linear-gradient(top, #0088cc, #0044cc);background-image:-o-linear-gradient(top, #0088cc, #0044cc);background-image:linear-gradient(top, #0088cc, #0044cc);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0);border-color:#0044cc #0044cc #002a80;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-primary:hover,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{background-color:#0044cc;} -.btn-primary:active,.btn-primary.active{background-color:#003399 \9;} -.btn-warning{background-color:#faa732;background-image:-moz-linear-gradient(top, #fbb450, #f89406);background-image:-ms-linear-gradient(top, #fbb450, #f89406);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));background-image:-webkit-linear-gradient(top, #fbb450, #f89406);background-image:-o-linear-gradient(top, #fbb450, #f89406);background-image:linear-gradient(top, #fbb450, #f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fbb450', endColorstr='#f89406', GradientType=0);border-color:#f89406 #f89406 #ad6704;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-warning:hover,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{background-color:#f89406;} -.btn-warning:active,.btn-warning.active{background-color:#c67605 \9;} -.btn-danger{background-color:#da4f49;background-image:-moz-linear-gradient(top, #ee5f5b, #bd362f);background-image:-ms-linear-gradient(top, #ee5f5b, #bd362f);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f));background-image:-webkit-linear-gradient(top, #ee5f5b, #bd362f);background-image:-o-linear-gradient(top, #ee5f5b, #bd362f);background-image:linear-gradient(top, #ee5f5b, #bd362f);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#bd362f', GradientType=0);border-color:#bd362f #bd362f #802420;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-danger:hover,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{background-color:#bd362f;} -.btn-danger:active,.btn-danger.active{background-color:#942a25 \9;} -.btn-success{background-color:#5bb75b;background-image:-moz-linear-gradient(top, #62c462, #51a351);background-image:-ms-linear-gradient(top, #62c462, #51a351);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));background-image:-webkit-linear-gradient(top, #62c462, #51a351);background-image:-o-linear-gradient(top, #62c462, #51a351);background-image:linear-gradient(top, #62c462, #51a351);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#51a351', GradientType=0);border-color:#51a351 #51a351 #387038;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-success:hover,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{background-color:#51a351;} -.btn-success:active,.btn-success.active{background-color:#408140 \9;} -.btn-info{background-color:#49afcd;background-image:-moz-linear-gradient(top, #5bc0de, #2f96b4);background-image:-ms-linear-gradient(top, #5bc0de, #2f96b4);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4));background-image:-webkit-linear-gradient(top, #5bc0de, #2f96b4);background-image:-o-linear-gradient(top, #5bc0de, #2f96b4);background-image:linear-gradient(top, #5bc0de, #2f96b4);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#2f96b4', GradientType=0);border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-info:hover,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{background-color:#2f96b4;} -.btn-info:active,.btn-info.active{background-color:#24748c \9;} -button.btn,input[type="submit"].btn{*padding-top:2px;*padding-bottom:2px;}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0;} -button.btn.large,input[type="submit"].btn.large{*padding-top:7px;*padding-bottom:7px;} -button.btn.small,input[type="submit"].btn.small{*padding-top:3px;*padding-bottom:3px;} -.btn-group{position:relative;*zoom:1;*margin-left:.3em;}.btn-group:before,.btn-group:after{display:table;content:"";} -.btn-group:after{clear:both;} -.btn-group:first-child{*margin-left:0;} -.btn-group+.btn-group{margin-left:5px;} -.btn-toolbar{margin-top:9px;margin-bottom:9px;}.btn-toolbar .btn-group{display:inline-block;*display:inline;*zoom:1;} -.btn-group .btn{position:relative;float:left;margin-left:-1px;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;} -.btn-group .btn:first-child{margin-left:0;-webkit-border-top-left-radius:4px;-moz-border-radius-topleft:4px;border-top-left-radius:4px;-webkit-border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;border-bottom-left-radius:4px;} -.btn-group .btn:last-child,.btn-group .dropdown-toggle{-webkit-border-top-right-radius:4px;-moz-border-radius-topright:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;border-bottom-right-radius:4px;} -.btn-group .btn.large:first-child{margin-left:0;-webkit-border-top-left-radius:6px;-moz-border-radius-topleft:6px;border-top-left-radius:6px;-webkit-border-bottom-left-radius:6px;-moz-border-radius-bottomleft:6px;border-bottom-left-radius:6px;} -.btn-group .btn.large:last-child,.btn-group .large.dropdown-toggle{-webkit-border-top-right-radius:6px;-moz-border-radius-topright:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;-moz-border-radius-bottomright:6px;border-bottom-right-radius:6px;} -.btn-group .btn:hover,.btn-group .btn:focus,.btn-group .btn:active,.btn-group .btn.active{z-index:2;} -.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0;} -.btn-group .dropdown-toggle{padding-left:8px;padding-right:8px;-webkit-box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125),inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125),inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125),inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);*padding-top:5px;*padding-bottom:5px;} -.btn-group.open{*z-index:1000;}.btn-group.open .dropdown-menu{display:block;margin-top:1px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;} -.btn-group.open .dropdown-toggle{background-image:none;-webkit-box-shadow:inset 0 1px 6px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 6px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 6px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);} -.btn .caret{margin-top:7px;margin-left:0;} -.btn:hover .caret,.open.btn-group .caret{opacity:1;filter:alpha(opacity=100);} -.btn-primary .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret{border-top-color:#ffffff;opacity:0.75;filter:alpha(opacity=75);} -.btn-small .caret{margin-top:4px;} -.alert{padding:8px 35px 8px 14px;margin-bottom:18px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} -.alert,.alert-heading{color:#c09853;} -.alert .close{position:relative;top:-2px;right:-21px;line-height:18px;} -.alert-success{background-color:#dff0d8;border-color:#d6e9c6;} -.alert-success,.alert-success .alert-heading{color:#468847;} -.alert-danger,.alert-error{background-color:#f2dede;border-color:#eed3d7;} -.alert-danger,.alert-error,.alert-danger .alert-heading,.alert-error .alert-heading{color:#b94a48;} -.alert-info{background-color:#d9edf7;border-color:#bce8f1;} -.alert-info,.alert-info .alert-heading{color:#3a87ad;} -.alert-block{padding-top:14px;padding-bottom:14px;} -.alert-block>p,.alert-block>ul{margin-bottom:0;} -.alert-block p+p{margin-top:5px;} -.nav{margin-left:0;margin-bottom:18px;list-style:none;} -.nav>li>a{display:block;} -.nav>li>a:hover{text-decoration:none;background-color:#eeeeee;} -.nav-list{padding-left:14px;padding-right:14px;margin-bottom:0;} -.nav-list>li>a,.nav-list .nav-header{display:block;padding:3px 15px;margin-left:-15px;margin-right:-15px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);} -.nav-list .nav-header{font-size:11px;font-weight:bold;line-height:18px;color:#999999;text-transform:uppercase;} -.nav-list>li+.nav-header{margin-top:9px;} -.nav-list .active>a{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.2);background-color:#0088cc;} -.nav-list .icon{margin-right:2px;} -.nav-tabs,.nav-pills{*zoom:1;}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;content:"";} -.nav-tabs:after,.nav-pills:after{clear:both;} -.nav-tabs>li,.nav-pills>li{float:left;} -.nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px;} -.nav-tabs{border-bottom:1px solid #ddd;} -.nav-tabs>li{margin-bottom:-1px;} -.nav-tabs>li>a{padding-top:9px;padding-bottom:9px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;}.nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #dddddd;} -.nav-tabs>.active>a,.nav-tabs>.active>a:hover{color:#555555;background-color:#ffffff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default;} -.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;} -.nav-pills .active>a,.nav-pills .active>a:hover{color:#ffffff;background-color:#0088cc;} -.nav-stacked>li{float:none;} -.nav-stacked>li>a{margin-right:0;} -.nav-tabs.nav-stacked{border-bottom:0;} -.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;} -.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;} -.nav-tabs.nav-stacked>li:last-child>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;} -.nav-tabs.nav-stacked>li>a:hover{border-color:#ddd;z-index:2;} -.nav-pills.nav-stacked>li>a{margin-bottom:3px;} -.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px;} -.nav-tabs .dropdown-menu,.nav-pills .dropdown-menu{margin-top:1px;border-width:1px;} -.nav-pills .dropdown-menu{-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} -.nav-tabs .dropdown-toggle .caret,.nav-pills .dropdown-toggle .caret{border-top-color:#0088cc;margin-top:6px;} -.nav-tabs .dropdown-toggle:hover .caret,.nav-pills .dropdown-toggle:hover .caret{border-top-color:#005580;} -.nav-tabs .active .dropdown-toggle .caret,.nav-pills .active .dropdown-toggle .caret{border-top-color:#333333;} -.nav>.dropdown.active>a:hover{color:#000000;cursor:pointer;} -.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>.open.active>a:hover{color:#ffffff;background-color:#999999;border-color:#999999;} -.nav .open .caret,.nav .open.active .caret,.nav .open a:hover .caret{border-top-color:#ffffff;opacity:1;filter:alpha(opacity=100);} -.tabs-stacked .open>a:hover{border-color:#999999;} -.tabbable{*zoom:1;}.tabbable:before,.tabbable:after{display:table;content:"";} -.tabbable:after{clear:both;} -.tabs-below .nav-tabs,.tabs-right .nav-tabs,.tabs-left .nav-tabs{border-bottom:0;} -.tab-content>.tab-pane,.pill-content>.pill-pane{display:none;} -.tab-content>.active,.pill-content>.active{display:block;} -.tabs-below .nav-tabs{border-top:1px solid #ddd;} -.tabs-below .nav-tabs>li{margin-top:-1px;margin-bottom:0;} -.tabs-below .nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;}.tabs-below .nav-tabs>li>a:hover{border-bottom-color:transparent;border-top-color:#ddd;} -.tabs-below .nav-tabs .active>a,.tabs-below .nav-tabs .active>a:hover{border-color:transparent #ddd #ddd #ddd;} -.tabs-left .nav-tabs>li,.tabs-right .nav-tabs>li{float:none;} -.tabs-left .nav-tabs>li>a,.tabs-right .nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px;} -.tabs-left .nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd;} -.tabs-left .nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px;} -.tabs-left .nav-tabs>li>a:hover{border-color:#eeeeee #dddddd #eeeeee #eeeeee;} -.tabs-left .nav-tabs .active>a,.tabs-left .nav-tabs .active>a:hover{border-color:#ddd transparent #ddd #ddd;*border-right-color:#ffffff;} -.tabs-right .nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd;} -.tabs-right .nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0;} -.tabs-right .nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #eeeeee #dddddd;} -.tabs-right .nav-tabs .active>a,.tabs-right .nav-tabs .active>a:hover{border-color:#ddd #ddd #ddd transparent;*border-left-color:#ffffff;} -.navbar{overflow:visible;margin-bottom:18px;} -.navbar-inner{padding-left:20px;padding-right:20px;background-color:#2c2c2c;background-image:-moz-linear-gradient(top, #333333, #222222);background-image:-ms-linear-gradient(top, #333333, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222));background-image:-webkit-linear-gradient(top, #333333, #222222);background-image:-o-linear-gradient(top, #333333, #222222);background-image:linear-gradient(top, #333333, #222222);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);} -.btn-navbar{display:none;float:right;padding:7px 10px;margin-left:5px;margin-right:5px;background-color:#2c2c2c;background-image:-moz-linear-gradient(top, #333333, #222222);background-image:-ms-linear-gradient(top, #333333, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222));background-image:-webkit-linear-gradient(top, #333333, #222222);background-image:-o-linear-gradient(top, #333333, #222222);background-image:linear-gradient(top, #333333, #222222);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);border-color:#222222 #222222 #000000;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);}.btn-navbar:hover,.btn-navbar:active,.btn-navbar.active,.btn-navbar.disabled,.btn-navbar[disabled]{background-color:#222222;} -.btn-navbar:active,.btn-navbar.active{background-color:#080808 \9;} -.btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);-moz-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);} -.btn-navbar .icon-bar+.icon-bar{margin-top:3px;} -.nav-collapse.collapse{height:auto;} -.navbar .brand:hover{text-decoration:none;} -.navbar .brand{float:left;display:block;padding:8px 20px 12px;margin-left:-20px;font-size:20px;font-weight:200;line-height:1;color:#ffffff;} -.navbar .navbar-text{margin-bottom:0;line-height:40px;color:#999999;}.navbar .navbar-text a:hover{color:#ffffff;background-color:transparent;} -.navbar .btn,.navbar .btn-group{margin-top:5px;} -.navbar .btn-group .btn{margin-top:0;} -.navbar-form{margin-bottom:0;*zoom:1;}.navbar-form:before,.navbar-form:after{display:table;content:"";} -.navbar-form:after{clear:both;} -.navbar-form input,.navbar-form select{display:inline-block;margin-top:5px;margin-bottom:0;} -.navbar-form .radio,.navbar-form .checkbox{margin-top:5px;} -.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px;} -.navbar-search{position:relative;float:left;margin-top:6px;margin-bottom:0;}.navbar-search .search-query{padding:4px 9px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;color:#ffffff;color:rgba(255, 255, 255, 0.75);background:#666;background:rgba(255, 255, 255, 0.3);border:1px solid #111;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15);-webkit-transition:none;-moz-transition:none;-ms-transition:none;-o-transition:none;transition:none;}.navbar-search .search-query :-moz-placeholder{color:#eeeeee;} -.navbar-search .search-query::-webkit-input-placeholder{color:#eeeeee;} -.navbar-search .search-query:hover{color:#ffffff;background-color:#999999;background-color:rgba(255, 255, 255, 0.5);} -.navbar-search .search-query:focus,.navbar-search .search-query.focused{padding:5px 10px;color:#333333;text-shadow:0 1px 0 #ffffff;background-color:#ffffff;border:0;-webkit-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);-moz-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);box-shadow:0 0 3px rgba(0, 0, 0, 0.15);outline:0;} -.navbar-fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030;} -.navbar-fixed-top .navbar-inner{padding-left:0;padding-right:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;} -.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0;} -.navbar .nav.pull-right{float:right;} -.navbar .nav>li{display:block;float:left;} -.navbar .nav>li>a{float:none;padding:10px 10px 11px;line-height:19px;color:#999999;text-decoration:none;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);} -.navbar .nav>li>a:hover{background-color:transparent;color:#ffffff;text-decoration:none;} -.navbar .nav .active>a,.navbar .nav .active>a:hover{color:#ffffff;text-decoration:none;background-color:#222222;background-color:rgba(0, 0, 0, 0.5);} -.navbar .divider-vertical{height:40px;width:1px;margin:0 9px;overflow:hidden;background-color:#222222;border-right:1px solid #333333;} -.navbar .nav.pull-right{margin-left:10px;margin-right:0;} -.navbar .dropdown-menu{margin-top:1px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.navbar .dropdown-menu:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-bottom-color:rgba(0, 0, 0, 0.2);position:absolute;top:-7px;left:9px;} -.navbar .dropdown-menu:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #ffffff;position:absolute;top:-6px;left:10px;} -.navbar .nav .dropdown-toggle .caret,.navbar .nav .open.dropdown .caret{border-top-color:#ffffff;} -.navbar .nav .active .caret{opacity:1;filter:alpha(opacity=100);} -.navbar .nav .open>.dropdown-toggle,.navbar .nav .active>.dropdown-toggle,.navbar .nav .open.active>.dropdown-toggle{background-color:transparent;} -.navbar .nav .active>.dropdown-toggle:hover{color:#ffffff;} -.navbar .nav.pull-right .dropdown-menu{left:auto;right:0;}.navbar .nav.pull-right .dropdown-menu:before{left:auto;right:12px;} -.navbar .nav.pull-right .dropdown-menu:after{left:auto;right:13px;} -.breadcrumb{padding:7px 14px;margin:0 0 18px;background-color:#fbfbfb;background-image:-moz-linear-gradient(top, #ffffff, #f5f5f5);background-image:-ms-linear-gradient(top, #ffffff, #f5f5f5);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f5f5f5));background-image:-webkit-linear-gradient(top, #ffffff, #f5f5f5);background-image:-o-linear-gradient(top, #ffffff, #f5f5f5);background-image:linear-gradient(top, #ffffff, #f5f5f5);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f5f5f5', GradientType=0);border:1px solid #ddd;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;}.breadcrumb li{display:inline;text-shadow:0 1px 0 #ffffff;} -.breadcrumb .divider{padding:0 5px;color:#999999;} -.breadcrumb .active a{color:#333333;} -.pagination{height:36px;margin:18px 0;} -.pagination ul{display:inline-block;*display:inline;*zoom:1;margin-left:0;margin-bottom:0;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);} -.pagination li{display:inline;} -.pagination a{float:left;padding:0 14px;line-height:34px;text-decoration:none;border:1px solid #ddd;border-left-width:0;} -.pagination a:hover,.pagination .active a{background-color:#f5f5f5;} -.pagination .active a{color:#999999;cursor:default;} -.pagination .disabled a,.pagination .disabled a:hover{color:#999999;background-color:transparent;cursor:default;} -.pagination li:first-child a{border-left-width:1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;} -.pagination li:last-child a{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;} -.pagination-centered{text-align:center;} -.pagination-right{text-align:right;} -.pager{margin-left:0;margin-bottom:18px;list-style:none;text-align:center;*zoom:1;}.pager:before,.pager:after{display:table;content:"";} -.pager:after{clear:both;} -.pager li{display:inline;} -.pager a{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px;} -.pager a:hover{text-decoration:none;background-color:#f5f5f5;} -.pager .next a{float:right;} -.pager .previous a{float:left;} -.modal-open .dropdown-menu{z-index:2050;} -.modal-open .dropdown.open{*z-index:2050;} -.modal-open .popover{z-index:2060;} -.modal-open .tooltip{z-index:2070;} -.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000000;}.modal-backdrop.fade{opacity:0;} -.modal-backdrop,.modal-backdrop.fade.in{opacity:0.8;filter:alpha(opacity=80);} -.modal{position:fixed;top:50%;left:50%;z-index:1050;max-height:500px;overflow:auto;width:560px;margin:-250px 0 0 -280px;background-color:#ffffff;border:1px solid #999;border:1px solid rgba(0, 0, 0, 0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.modal.fade{-webkit-transition:opacity .3s linear, top .3s ease-out;-moz-transition:opacity .3s linear, top .3s ease-out;-ms-transition:opacity .3s linear, top .3s ease-out;-o-transition:opacity .3s linear, top .3s ease-out;transition:opacity .3s linear, top .3s ease-out;top:-25%;} -.modal.fade.in{top:50%;} -.modal-header{padding:9px 15px;border-bottom:1px solid #eee;}.modal-header .close{margin-top:2px;} -.modal-body{padding:15px;} -.modal-footer{padding:14px 15px 15px;margin-bottom:0;background-color:#f5f5f5;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;*zoom:1;}.modal-footer:before,.modal-footer:after{display:table;content:"";} -.modal-footer:after{clear:both;} -.modal-footer .btn{float:right;margin-left:5px;margin-bottom:0;} -.tooltip{position:absolute;z-index:1020;display:block;visibility:visible;padding:5px;font-size:11px;opacity:0;filter:alpha(opacity=0);}.tooltip.in{opacity:0.8;filter:alpha(opacity=80);} -.tooltip.top{margin-top:-2px;} -.tooltip.right{margin-left:2px;} -.tooltip.bottom{margin-top:2px;} -.tooltip.left{margin-left:-2px;} -.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;} -.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000000;} -.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:5px solid #000000;} -.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-right:5px solid #000000;} -.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;text-decoration:none;background-color:#000000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} -.tooltip-arrow{position:absolute;width:0;height:0;} -.popover{position:absolute;top:0;left:0;z-index:1010;display:none;padding:5px;}.popover.top{margin-top:-5px;} -.popover.right{margin-left:5px;} -.popover.bottom{margin-top:5px;} -.popover.left{margin-left:-5px;} -.popover.top .arrow{bottom:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;} -.popover.right .arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-right:5px solid #000000;} -.popover.bottom .arrow{top:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:5px solid #000000;} -.popover.left .arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000000;} -.popover .arrow{position:absolute;width:0;height:0;} -.popover-inner{padding:3px;width:280px;overflow:hidden;background:#000000;background:rgba(0, 0, 0, 0.8);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);} -.popover-title{padding:9px 15px;line-height:1;background-color:#f5f5f5;border-bottom:1px solid #eee;-webkit-border-radius:3px 3px 0 0;-moz-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0;} -.popover-content{padding:14px;background-color:#ffffff;-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.popover-content p,.popover-content ul,.popover-content ol{margin-bottom:0;} -.thumbnails{margin-left:-20px;list-style:none;*zoom:1;}.thumbnails:before,.thumbnails:after{display:table;content:"";} -.thumbnails:after{clear:both;} -.thumbnails>li{float:left;margin:0 0 18px 20px;} -.thumbnail{display:block;padding:4px;line-height:1;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);} -a.thumbnail:hover{border-color:#0088cc;-webkit-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);-moz-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);} -.thumbnail>img{display:block;max-width:100%;margin-left:auto;margin-right:auto;} -.thumbnail .caption{padding:9px;} -.label{padding:1px 3px 2px;font-size:9.75px;font-weight:bold;color:#ffffff;text-transform:uppercase;background-color:#999999;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} -.label-important{background-color:#b94a48;} -.label-warning{background-color:#f89406;} -.label-success{background-color:#468847;} -.label-info{background-color:#3a87ad;} -@-webkit-keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}@-moz-keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}@keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}.progress{overflow:hidden;height:18px;margin-bottom:18px;background-color:#f7f7f7;background-image:-moz-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-ms-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9));background-image:-webkit-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-o-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:linear-gradient(top, #f5f5f5, #f9f9f9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#f5f5f5', endColorstr='#f9f9f9', GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} -.progress .bar{width:0%;height:18px;color:#ffffff;font-size:12px;text-align:center;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top, #149bdf, #0480be);background-image:-ms-linear-gradient(top, #149bdf, #0480be);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be));background-image:-webkit-linear-gradient(top, #149bdf, #0480be);background-image:-o-linear-gradient(top, #149bdf, #0480be);background-image:linear-gradient(top, #149bdf, #0480be);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#149bdf', endColorstr='#0480be', GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width 0.6s ease;-moz-transition:width 0.6s ease;-ms-transition:width 0.6s ease;-o-transition:width 0.6s ease;transition:width 0.6s ease;} -.progress-striped .bar{background-color:#62c462;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px;} -.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite;} -.progress-danger .bar{background-color:#dd514c;background-image:-moz-linear-gradient(top, #ee5f5b, #c43c35);background-image:-ms-linear-gradient(top, #ee5f5b, #c43c35);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35));background-image:-webkit-linear-gradient(top, #ee5f5b, #c43c35);background-image:-o-linear-gradient(top, #ee5f5b, #c43c35);background-image:linear-gradient(top, #ee5f5b, #c43c35);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#c43c35', GradientType=0);} -.progress-danger.progress-striped .bar{background-color:#ee5f5b;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);} -.progress-success .bar{background-color:#5eb95e;background-image:-moz-linear-gradient(top, #62c462, #57a957);background-image:-ms-linear-gradient(top, #62c462, #57a957);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957));background-image:-webkit-linear-gradient(top, #62c462, #57a957);background-image:-o-linear-gradient(top, #62c462, #57a957);background-image:linear-gradient(top, #62c462, #57a957);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0);} -.progress-success.progress-striped .bar{background-color:#62c462;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);} -.progress-info .bar{background-color:#4bb1cf;background-image:-moz-linear-gradient(top, #5bc0de, #339bb9);background-image:-ms-linear-gradient(top, #5bc0de, #339bb9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9));background-image:-webkit-linear-gradient(top, #5bc0de, #339bb9);background-image:-o-linear-gradient(top, #5bc0de, #339bb9);background-image:linear-gradient(top, #5bc0de, #339bb9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0);} -.progress-info.progress-striped .bar{background-color:#5bc0de;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);} -.accordion{margin-bottom:18px;} -.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} -.accordion-heading{border-bottom:0;} -.accordion-heading .accordion-toggle{display:block;padding:8px 15px;} -.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5;} -.carousel{position:relative;margin-bottom:18px;line-height:1;} -.carousel-inner{overflow:hidden;width:100%;position:relative;} -.carousel .item{display:none;position:relative;-webkit-transition:0.6s ease-in-out left;-moz-transition:0.6s ease-in-out left;-ms-transition:0.6s ease-in-out left;-o-transition:0.6s ease-in-out left;transition:0.6s ease-in-out left;} -.carousel .item>img{display:block;line-height:1;} -.carousel .active,.carousel .next,.carousel .prev{display:block;} -.carousel .active{left:0;} -.carousel .next,.carousel .prev{position:absolute;top:0;width:100%;} -.carousel .next{left:100%;} -.carousel .prev{left:-100%;} -.carousel .next.left,.carousel .prev.right{left:0;} -.carousel .active.left{left:-100%;} -.carousel .active.right{left:100%;} -.carousel-control{position:absolute;top:40%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#ffffff;text-align:center;background:#222222;border:3px solid #ffffff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:0.5;filter:alpha(opacity=50);}.carousel-control.right{left:auto;right:15px;} -.carousel-control:hover{color:#ffffff;text-decoration:none;opacity:0.9;filter:alpha(opacity=90);} -.carousel-caption{position:absolute;left:0;right:0;bottom:0;padding:10px 15px 5px;background:#333333;background:rgba(0, 0, 0, 0.75);} -.carousel-caption h4,.carousel-caption p{color:#ffffff;} -.hero-unit{padding:60px;margin-bottom:30px;background-color:#f5f5f5;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;letter-spacing:-1px;} -.hero-unit p{font-size:18px;font-weight:200;line-height:27px;} -.pull-right{float:right;} -.pull-left{float:left;} -.hide{display:none;} -.show{display:block;} -.invisible{visibility:hidden;} diff --git a/example/src/main/resources/io/netty/example/stomp/websocket/css/bootstrap.min.responsive.css b/example/src/main/resources/io/netty/example/stomp/websocket/css/bootstrap.min.responsive.css deleted file mode 100644 index dd95d9d78f..0000000000 --- a/example/src/main/resources/io/netty/example/stomp/websocket/css/bootstrap.min.responsive.css +++ /dev/null @@ -1,2 +0,0 @@ -.hidden{display:none;visibility:hidden;} -@media (max-width:480px){.nav-collapse{-webkit-transform:translate3d(0, 0, 0);} .page-header h1 small{display:block;line-height:18px;} input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;} .input-prepend input[class*="span"],.input-append input[class*="span"]{width:auto;} input[type="checkbox"],input[type="radio"]{border:1px solid #ccc;} .form-horizontal .control-group>label{float:none;width:auto;padding-top:0;text-align:left;} .form-horizontal .controls{margin-left:0;} .form-horizontal .control-list{padding-top:0;} .form-horizontal .form-actions{padding-left:10px;padding-right:10px;} .modal{position:absolute;top:10px;left:10px;right:10px;width:auto;margin:0;}.modal.fade.in{top:auto;} .modal-header .close{padding:10px;margin:-10px;} .carousel-caption{position:static;}}@media (max-width:768px){.container{width:auto;padding:0 20px;} .row-fluid{width:100%;} .row{margin-left:0;} .row>[class*="span"],.row-fluid>[class*="span"]{float:none;display:block;width:auto;margin:0;}}@media (min-width:768px) and (max-width:980px){.row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";} .row:after{clear:both;} [class*="span"]{float:left;margin-left:20px;} .span1{width:42px;} .span2{width:104px;} .span3{width:166px;} .span4{width:228px;} .span5{width:290px;} .span6{width:352px;} .span7{width:414px;} .span8{width:476px;} .span9{width:538px;} .span10{width:600px;} .span11{width:662px;} .span12,.container{width:724px;} .offset1{margin-left:82px;} .offset2{margin-left:144px;} .offset3{margin-left:206px;} .offset4{margin-left:268px;} .offset5{margin-left:330px;} .offset6{margin-left:392px;} .offset7{margin-left:454px;} .offset8{margin-left:516px;} .offset9{margin-left:578px;} .offset10{margin-left:640px;} .offset11{margin-left:702px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";} .row-fluid:after{clear:both;} .row-fluid>[class*="span"]{float:left;margin-left:2.762430939%;} .row-fluid>[class*="span"]:first-child{margin-left:0;} .row-fluid .span1{width:5.801104972%;} .row-fluid .span2{width:14.364640883%;} .row-fluid .span3{width:22.928176794%;} .row-fluid .span4{width:31.491712705%;} .row-fluid .span5{width:40.055248616%;} .row-fluid .span6{width:48.618784527%;} .row-fluid .span7{width:57.182320438000005%;} .row-fluid .span8{width:65.74585634900001%;} .row-fluid .span9{width:74.30939226%;} .row-fluid .span10{width:82.87292817100001%;} .row-fluid .span11{width:91.436464082%;} .row-fluid .span12{width:99.999999993%;} input.span1,textarea.span1,.uneditable-input.span1{width:32px;} input.span2,textarea.span2,.uneditable-input.span2{width:94px;} input.span3,textarea.span3,.uneditable-input.span3{width:156px;} input.span4,textarea.span4,.uneditable-input.span4{width:218px;} input.span5,textarea.span5,.uneditable-input.span5{width:280px;} input.span6,textarea.span6,.uneditable-input.span6{width:342px;} input.span7,textarea.span7,.uneditable-input.span7{width:404px;} input.span8,textarea.span8,.uneditable-input.span8{width:466px;} input.span9,textarea.span9,.uneditable-input.span9{width:528px;} input.span10,textarea.span10,.uneditable-input.span10{width:590px;} input.span11,textarea.span11,.uneditable-input.span11{width:652px;} input.span12,textarea.span12,.uneditable-input.span12{width:714px;}}@media (max-width:980px){body{padding-top:0;} .navbar-fixed-top{position:static;margin-bottom:18px;} .navbar-fixed-top .navbar-inner{padding:5px;} .navbar .container{width:auto;padding:0;} .navbar .brand{padding-left:10px;padding-right:10px;margin:0 0 0 -5px;} .navbar .nav-collapse{clear:left;} .navbar .nav{float:none;margin:0 0 9px;} .navbar .nav>li{float:none;} .navbar .nav>li>a{margin-bottom:2px;} .navbar .nav>.divider-vertical{display:none;} .navbar .nav>li>a,.navbar .dropdown-menu a{padding:6px 15px;font-weight:bold;color:#999999;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} .navbar .dropdown-menu li+li a{margin-bottom:2px;} .navbar .nav>li>a:hover,.navbar .dropdown-menu a:hover{background-color:#222222;} .navbar .dropdown-menu{position:static;top:auto;left:auto;float:none;display:block;max-width:none;margin:0 15px;padding:0;background-color:transparent;border:none;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} .navbar .dropdown-menu:before,.navbar .dropdown-menu:after{display:none;} .navbar .dropdown-menu .divider{display:none;} .navbar-form,.navbar-search{float:none;padding:9px 15px;margin:9px 0;border-top:1px solid #222222;border-bottom:1px solid #222222;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.1);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.1);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.1);} .navbar .nav.pull-right{float:none;margin-left:0;} .navbar-static .navbar-inner{padding-left:10px;padding-right:10px;} .btn-navbar{display:block;} .nav-collapse{overflow:hidden;height:0;}}@media (min-width:980px){.nav-collapse.collapse{height:auto !important;}}@media (min-width:1200px){.row{margin-left:-30px;*zoom:1;}.row:before,.row:after{display:table;content:"";} .row:after{clear:both;} [class*="span"]{float:left;margin-left:30px;} .span1{width:70px;} .span2{width:170px;} .span3{width:270px;} .span4{width:370px;} .span5{width:470px;} .span6{width:570px;} .span7{width:670px;} .span8{width:770px;} .span9{width:870px;} .span10{width:970px;} .span11{width:1070px;} .span12,.container{width:1170px;} .offset1{margin-left:130px;} .offset2{margin-left:230px;} .offset3{margin-left:330px;} .offset4{margin-left:430px;} .offset5{margin-left:530px;} .offset6{margin-left:630px;} .offset7{margin-left:730px;} .offset8{margin-left:830px;} .offset9{margin-left:930px;} .offset10{margin-left:1030px;} .offset11{margin-left:1130px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";} .row-fluid:after{clear:both;} .row-fluid>[class*="span"]{float:left;margin-left:2.564102564%;} .row-fluid>[class*="span"]:first-child{margin-left:0;} .row-fluid .span1{width:5.982905983%;} .row-fluid .span2{width:14.529914530000001%;} .row-fluid .span3{width:23.076923077%;} .row-fluid .span4{width:31.623931624%;} .row-fluid .span5{width:40.170940171000005%;} .row-fluid .span6{width:48.717948718%;} .row-fluid .span7{width:57.264957265%;} .row-fluid .span8{width:65.81196581200001%;} .row-fluid .span9{width:74.358974359%;} .row-fluid .span10{width:82.905982906%;} .row-fluid .span11{width:91.45299145300001%;} .row-fluid .span12{width:100%;} input.span1,textarea.span1,.uneditable-input.span1{width:60px;} input.span2,textarea.span2,.uneditable-input.span2{width:160px;} input.span3,textarea.span3,.uneditable-input.span3{width:260px;} input.span4,textarea.span4,.uneditable-input.span4{width:360px;} input.span5,textarea.span5,.uneditable-input.span5{width:460px;} input.span6,textarea.span6,.uneditable-input.span6{width:560px;} input.span7,textarea.span7,.uneditable-input.span7{width:660px;} input.span8,textarea.span8,.uneditable-input.span8{width:760px;} input.span9,textarea.span9,.uneditable-input.span9{width:860px;} input.span10,textarea.span10,.uneditable-input.span10{width:960px;} input.span11,textarea.span11,.uneditable-input.span11{width:1060px;} input.span12,textarea.span12,.uneditable-input.span12{width:1160px;} .thumbnails{margin-left:-30px;} .thumbnails>li{margin-left:30px;}} diff --git a/example/src/main/resources/io/netty/example/stomp/websocket/index.html b/example/src/main/resources/io/netty/example/stomp/websocket/index.html deleted file mode 100644 index 88ba5fbe7d..0000000000 --- a/example/src/main/resources/io/netty/example/stomp/websocket/index.html +++ /dev/null @@ -1,146 +0,0 @@ - - - - - Chat Example Using STOMP Over WebSockets - - - - - - - - - -
-
-
-
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
-
-
-
- -
-
- -

-    
-
-
- - - - - - - - \ No newline at end of file diff --git a/example/src/main/resources/io/netty/example/stomp/websocket/stomp.js b/example/src/main/resources/io/netty/example/stomp/websocket/stomp.js deleted file mode 100644 index 0e6aa2af5e..0000000000 --- a/example/src/main/resources/io/netty/example/stomp/websocket/stomp.js +++ /dev/null @@ -1,498 +0,0 @@ -/* - Stomp Over WebSocket http://www.jmesnil.net/stomp-websocket/doc/ | Apache License V2.0 - Copyright (C) 2010-2013 [Jeff Mesnil](http://jmesnil.net/) - Copyright (C) 2012 [FuseSource, Inc.](https://fusesource.com) - */ -(function () { - var Byte, Client, Frame, Stomp, - __hasProp = {}.hasOwnProperty, - __slice = [].slice; - - Byte = { - LF: '\x0A', - NULL: '\x00' - }; - - Frame = (function () { - var unmarshallSingle; - - function Frame(command, headers, body) { - this.command = command; - this.headers = headers != null ? headers : {}; - this.body = body != null ? body : ''; - } - - Frame.prototype.toString = function () { - var lines, name, skipContentLength, value, _ref; - lines = [this.command]; - skipContentLength = this.headers['content-length'] === false; - if (skipContentLength) { - delete this.headers['content-length']; - } - _ref = this.headers; - for (name in _ref) { - if (!__hasProp.call(_ref, name)) continue; - value = _ref[name]; - lines.push("" + name + ":" + value); - } - if (this.body && !skipContentLength) { - lines.push("content-length:" + (Frame.sizeOfUTF8(this.body))); - } - lines.push(Byte.LF + this.body); - return lines.join(Byte.LF); - }; - - Frame.sizeOfUTF8 = function (s) { - if (s) { - return encodeURI(s).match(/%..|./g).length; - } else { - return 0; - } - }; - - unmarshallSingle = function (data) { - var body, chr, command, divider, headerLines, headers, i, idx, len, line, start, trim, _i, _j, _len, _ref, _ref1; - divider = data.search(RegExp("" + Byte.LF + Byte.LF)); - headerLines = data.substring(0, divider).split(Byte.LF); - command = headerLines.shift(); - headers = {}; - trim = function (str) { - return str.replace(/^\s+|\s+$/g, ''); - }; - _ref = headerLines.reverse(); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - line = _ref[_i]; - idx = line.indexOf(':'); - headers[trim(line.substring(0, idx))] = trim(line.substring(idx + 1)); - } - body = ''; - start = divider + 2; - if (headers['content-length']) { - len = parseInt(headers['content-length']); - body = ('' + data).substring(start, start + len); - } else { - chr = null; - for (i = _j = start, _ref1 = data.length; start <= _ref1 ? _j < _ref1 : _j > _ref1; i = start <= _ref1 ? ++_j : --_j) { - chr = data.charAt(i); - if (chr === Byte.NULL) { - break; - } - body += chr; - } - } - return new Frame(command, headers, body); - }; - - Frame.unmarshall = function (datas) { - var frame, frames, last_frame, r; - frames = datas.split(RegExp("" + Byte.NULL + Byte.LF + "*")); - r = { - frames: [], - partial: '' - }; - r.frames = (function () { - var _i, _len, _ref, _results; - _ref = frames.slice(0, -1); - _results = []; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - frame = _ref[_i]; - _results.push(unmarshallSingle(frame)); - } - return _results; - })(); - last_frame = frames.slice(-1)[0]; - if (last_frame === Byte.LF || (last_frame.search(RegExp("" + Byte.NULL + Byte.LF + "*$"))) !== -1) { - r.frames.push(unmarshallSingle(last_frame)); - } else { - r.partial = last_frame; - } - return r; - }; - - Frame.marshall = function (command, headers, body) { - var frame; - frame = new Frame(command, headers, body); - return frame.toString() + Byte.NULL; - }; - - return Frame; - - })(); - - Client = (function () { - var now; - - function Client(ws) { - this.ws = ws; - this.ws.binaryType = "arraybuffer"; - this.counter = 0; - this.connected = false; - this.heartbeat = { - outgoing: 10000, - incoming: 10000 - }; - this.maxWebSocketFrameSize = 16 * 1024; - this.subscriptions = {}; - this.partialData = ''; - } - - Client.prototype.debug = function (message) { - var _ref; - return typeof window !== "undefined" && window !== null ? (_ref = window.console) != null ? _ref.log(message) : void 0 : void 0; - }; - - now = function () { - if (Date.now) { - return Date.now(); - } else { - return new Date().valueOf; - } - }; - - Client.prototype._transmit = function (command, headers, body) { - var out; - out = Frame.marshall(command, headers, body); - if (typeof this.debug === "function") { - this.debug(">>> " + out); - } - while (true) { - if (out.length > this.maxWebSocketFrameSize) { - this.ws.send(out.substring(0, this.maxWebSocketFrameSize)); - out = out.substring(this.maxWebSocketFrameSize); - if (typeof this.debug === "function") { - this.debug("remaining = " + out.length); - } - } else { - return this.ws.send(out); - } - } - }; - - Client.prototype._setupHeartbeat = function (headers) { - var serverIncoming, serverOutgoing, ttl, v, _ref, _ref1; - if ((_ref = headers.version) !== Stomp.VERSIONS.V1_1 && _ref !== Stomp.VERSIONS.V1_2) { - return; - } - _ref1 = (function () { - var _i, _len, _ref1, _results; - _ref1 = headers['heart-beat'].split(","); - _results = []; - for (_i = 0, _len = _ref1.length; _i < _len; _i++) { - v = _ref1[_i]; - _results.push(parseInt(v)); - } - return _results; - })(), serverOutgoing = _ref1[0], serverIncoming = _ref1[1]; - if (!(this.heartbeat.outgoing === 0 || serverIncoming === 0)) { - ttl = Math.max(this.heartbeat.outgoing, serverIncoming); - if (typeof this.debug === "function") { - this.debug("send PING every " + ttl + "ms"); - } - this.pinger = Stomp.setInterval(ttl, (function (_this) { - return function () { - _this.ws.send(Byte.LF); - return typeof _this.debug === "function" ? _this.debug(">>> PING") : void 0; - }; - })(this)); - } - if (!(this.heartbeat.incoming === 0 || serverOutgoing === 0)) { - ttl = Math.max(this.heartbeat.incoming, serverOutgoing); - if (typeof this.debug === "function") { - this.debug("check PONG every " + ttl + "ms"); - } - return this.ponger = Stomp.setInterval(ttl, (function (_this) { - return function () { - var delta; - delta = now() - _this.serverActivity; - if (delta > ttl * 2) { - if (typeof _this.debug === "function") { - _this.debug("did not receive server activity for the last " + delta + "ms"); - } - return _this.ws.close(); - } - }; - })(this)); - } - }; - - Client.prototype._parseConnect = function () { - var args, connectCallback, errorCallback, headers; - args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; - headers = {}; - switch (args.length) { - case 2: - headers = args[0], connectCallback = args[1]; - break; - case 3: - if (args[1] instanceof Function) { - headers = args[0], connectCallback = args[1], errorCallback = args[2]; - } else { - headers.login = args[0], headers.passcode = args[1], connectCallback = args[2]; - } - break; - case 4: - headers.login = args[0], headers.passcode = args[1], connectCallback = args[2], errorCallback = args[3]; - break; - default: - headers.login = args[0], headers.passcode = args[1], connectCallback = args[2], errorCallback = args[3], headers.host = args[4]; - } - return [headers, connectCallback, errorCallback]; - }; - - Client.prototype.connect = function () { - var args, errorCallback, headers, out; - args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; - out = this._parseConnect.apply(this, args); - headers = out[0], this.connectCallback = out[1], errorCallback = out[2]; - if (typeof this.debug === "function") { - this.debug("Opening Web Socket..."); - } - this.ws.onmessage = (function (_this) { - return function (evt) { - var arr, c, client, data, frame, messageID, onreceive, subscription, unmarshalledData, _i, _len, _ref, - _results; - data = typeof ArrayBuffer !== 'undefined' && evt.data instanceof ArrayBuffer ? (arr = new Uint8Array(evt.data), typeof _this.debug === "function" ? _this.debug("--- got data length: " + arr.length) : void 0, ((function () { - var _i, _len, _results; - _results = []; - for (_i = 0, _len = arr.length; _i < _len; _i++) { - c = arr[_i]; - _results.push(String.fromCharCode(c)); - } - return _results; - })()).join('')) : evt.data; - _this.serverActivity = now(); - if (data === Byte.LF) { - if (typeof _this.debug === "function") { - _this.debug("<<< PONG"); - } - return; - } - if (typeof _this.debug === "function") { - _this.debug("<<< " + data); - } - unmarshalledData = Frame.unmarshall(_this.partialData + data); - _this.partialData = unmarshalledData.partial; - _ref = unmarshalledData.frames; - _results = []; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - frame = _ref[_i]; - switch (frame.command) { - case "CONNECTED": - if (typeof _this.debug === "function") { - _this.debug("connected to server " + frame.headers.server); - } - _this.connected = true; - _this._setupHeartbeat(frame.headers); - _results.push(typeof _this.connectCallback === "function" ? _this.connectCallback(frame) : void 0); - break; - case "MESSAGE": - subscription = frame.headers.subscription; - onreceive = _this.subscriptions[subscription] || _this.onreceive; - if (onreceive) { - client = _this; - messageID = frame.headers["message-id"]; - frame.ack = function (headers) { - if (headers == null) { - headers = {}; - } - return client.ack(messageID, subscription, headers); - }; - frame.nack = function (headers) { - if (headers == null) { - headers = {}; - } - return client.nack(messageID, subscription, headers); - }; - _results.push(onreceive(frame)); - } else { - _results.push(typeof _this.debug === "function" ? _this.debug("Unhandled received MESSAGE: " + frame) : void 0); - } - break; - case "RECEIPT": - _results.push(typeof _this.onreceipt === "function" ? _this.onreceipt(frame) : void 0); - break; - case "ERROR": - _results.push(typeof errorCallback === "function" ? errorCallback(frame) : void 0); - break; - default: - _results.push(typeof _this.debug === "function" ? _this.debug("Unhandled frame: " + frame) : void 0); - } - } - return _results; - }; - })(this); - this.ws.onclose = (function (_this) { - return function () { - var msg; - msg = "Whoops! Lost connection to " + _this.ws.url; - if (typeof _this.debug === "function") { - _this.debug(msg); - } - _this._cleanUp(); - return typeof errorCallback === "function" ? errorCallback(msg) : void 0; - }; - })(this); - return this.ws.onopen = (function (_this) { - return function () { - if (typeof _this.debug === "function") { - _this.debug('Web Socket Opened...'); - } - headers["accept-version"] = Stomp.VERSIONS.supportedVersions(); - headers["heart-beat"] = [_this.heartbeat.outgoing, _this.heartbeat.incoming].join(','); - return _this._transmit("CONNECT", headers); - }; - })(this); - }; - - Client.prototype.disconnect = function (disconnectCallback, headers) { - if (headers == null) { - headers = {}; - } - this._transmit("DISCONNECT", headers); - this.ws.onclose = null; - this.ws.close(); - this._cleanUp(); - return typeof disconnectCallback === "function" ? disconnectCallback() : void 0; - }; - - Client.prototype._cleanUp = function () { - this.connected = false; - if (this.pinger) { - Stomp.clearInterval(this.pinger); - } - if (this.ponger) { - return Stomp.clearInterval(this.ponger); - } - }; - - Client.prototype.send = function (destination, headers, body) { - if (headers == null) { - headers = {}; - } - if (body == null) { - body = ''; - } - headers.destination = destination; - return this._transmit("SEND", headers, body); - }; - - Client.prototype.subscribe = function (destination, callback, headers) { - var client; - if (headers == null) { - headers = {}; - } - if (!headers.id) { - headers.id = "sub-" + this.counter++; - } - headers.destination = destination; - this.subscriptions[headers.id] = callback; - this._transmit("SUBSCRIBE", headers); - client = this; - return { - id: headers.id, - unsubscribe: function () { - return client.unsubscribe(headers.id); - } - }; - }; - - Client.prototype.unsubscribe = function (id) { - delete this.subscriptions[id]; - return this._transmit("UNSUBSCRIBE", { - id: id - }); - }; - - Client.prototype.begin = function (transaction) { - var client, txid; - txid = transaction || "tx-" + this.counter++; - this._transmit("BEGIN", { - transaction: txid - }); - client = this; - return { - id: txid, - commit: function () { - return client.commit(txid); - }, - abort: function () { - return client.abort(txid); - } - }; - }; - - Client.prototype.commit = function (transaction) { - return this._transmit("COMMIT", { - transaction: transaction - }); - }; - - Client.prototype.abort = function (transaction) { - return this._transmit("ABORT", { - transaction: transaction - }); - }; - - Client.prototype.ack = function (messageID, subscription, headers) { - if (headers == null) { - headers = {}; - } - headers["message-id"] = messageID; - headers.subscription = subscription; - return this._transmit("ACK", headers); - }; - - Client.prototype.nack = function (messageID, subscription, headers) { - if (headers == null) { - headers = {}; - } - headers["message-id"] = messageID; - headers.subscription = subscription; - return this._transmit("NACK", headers); - }; - - return Client; - - })(); - - Stomp = { - VERSIONS: { - V1_0: '1.0', - V1_1: '1.1', - V1_2: '1.2', - supportedVersions: function () { - return '1.1,1.0'; - } - }, - client: function (url, protocols) { - var klass, ws; - if (protocols == null) { - protocols = ['v10.stomp', 'v11.stomp']; - } - klass = Stomp.WebSocketClass || WebSocket; - ws = new klass(url, protocols); - return new Client(ws); - }, - over: function (ws) { - return new Client(ws); - }, - Frame: Frame - }; - - if (typeof exports !== "undefined" && exports !== null) { - exports.Stomp = Stomp; - } - - if (typeof window !== "undefined" && window !== null) { - Stomp.setInterval = function (interval, f) { - return window.setInterval(f, interval); - }; - Stomp.clearInterval = function (id) { - return window.clearInterval(id); - }; - window.Stomp = Stomp; - } else if (!exports) { - self.Stomp = Stomp; - } - -}).call(this); diff --git a/example/src/main/resources/logback.xml b/example/src/main/resources/logback.xml deleted file mode 100644 index ea5238ed68..0000000000 --- a/example/src/main/resources/logback.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - - - - - - - diff --git a/handler-proxy/pom.xml b/handler-proxy/pom.xml deleted file mode 100644 index 8479f287a9..0000000000 --- a/handler-proxy/pom.xml +++ /dev/null @@ -1,89 +0,0 @@ - - - - - 4.0.0 - - io.netty - netty-parent - 5.0.0.Final-SNAPSHOT - - - netty-handler-proxy - jar - - - io.netty.handler.proxy - - - 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 - ${project.version} - - - ${project.groupId} - netty-codec-http - ${project.version} - - - - ${project.groupId} - netty-handler - ${project.version} - test - - - org.mockito - mockito-core - - - org.bouncycastle - bcpkix-jdk15on - test - - - org.bouncycastle - bcprov-jdk15on - test - - - - 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 deleted file mode 100644 index f5130abad6..0000000000 --- a/handler-proxy/src/main/java/io/netty/handler/proxy/HttpProxyHandler.java +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.proxy; - -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.FullHttpRequest; -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.HttpMethod; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpUtil; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http.LastHttpContent; -import io.netty.util.AsciiString; -import io.netty.util.concurrent.Future; - -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; -import java.util.Base64; - -import static java.util.Objects.requireNonNull; - -public final class HttpProxyHandler extends ProxyHandler { - - private static final String PROTOCOL = "http"; - private static final String AUTH_BASIC = "basic"; - private static final byte[] BASIC_BYTES = "Basic ".getBytes(StandardCharsets.UTF_8); - - // Wrapper for the HttpClientCodec to prevent it to be removed by other handlers by mistake (for example the - // WebSocket*Handshaker. - // - // See: - // - https://github.com/netty/netty/issues/5201 - // - https://github.com/netty/netty/issues/5070 - private final HttpClientCodecWrapper codecWrapper = new HttpClientCodecWrapper(); - 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 inboundHeaders; - - public HttpProxyHandler(SocketAddress proxyAddress) { - this(proxyAddress, null); - } - - public HttpProxyHandler(SocketAddress proxyAddress, HttpHeaders headers) { - this(proxyAddress, headers, false); - } - - public HttpProxyHandler(SocketAddress proxyAddress, - HttpHeaders headers, - boolean ignoreDefaultPortsInConnectHostHeader) { - super(proxyAddress); - username = null; - password = null; - authorization = null; - outboundHeaders = headers; - this.ignoreDefaultPortsInConnectHostHeader = ignoreDefaultPortsInConnectHostHeader; - } - - public HttpProxyHandler(SocketAddress proxyAddress, String username, String password) { - this(proxyAddress, username, password, null); - } - - public HttpProxyHandler(SocketAddress proxyAddress, String username, String password, - HttpHeaders headers) { - this(proxyAddress, username, password, headers, false); - } - - public HttpProxyHandler(SocketAddress proxyAddress, - String username, - String password, - HttpHeaders headers, - boolean ignoreDefaultPortsInConnectHostHeader) { - super(proxyAddress); - requireNonNull(username, "username"); - requireNonNull(password, "password"); - this.username = username; - this.password = password; - - byte[] authzBase64 = Base64.getEncoder().encode( - (username + ':' + password).getBytes(StandardCharsets.UTF_8)); - byte[] authzHeader = Arrays.copyOf(BASIC_BYTES, 6 + authzBase64.length); - System.arraycopy(authzBase64, 0, authzHeader, 6, authzBase64.length); - - authorization = new AsciiString(authzHeader, /*copy=*/ false); - - outboundHeaders = headers; - this.ignoreDefaultPortsInConnectHostHeader = ignoreDefaultPortsInConnectHostHeader; - } - - @Override - public String protocol() { - return PROTOCOL; - } - - @Override - public String authScheme() { - return authorization != null? AUTH_BASIC : AUTH_NONE; - } - - public String username() { - return username; - } - - public String password() { - return password; - } - - @Override - protected void addCodec(ChannelHandlerContext ctx) throws Exception { - ChannelPipeline p = ctx.pipeline(); - String name = ctx.name(); - p.addBefore(name, null, codecWrapper); - } - - @Override - protected void removeEncoder(ChannelHandlerContext ctx) throws Exception { - codecWrapper.codec.removeOutboundHandler(); - } - - @Override - protected void removeDecoder(ChannelHandlerContext ctx) throws Exception { - codecWrapper.codec.removeInboundHandler(); - } - - @Override - protected Object newInitialMessage(ChannelHandlerContext ctx) throws Exception { - InetSocketAddress raddr = destinationAddress(); - - String hostString = HttpUtil.formatHostnameForHttp(raddr); - int port = raddr.getPort(); - String url = hostString + ':' + port; - String hostHeader = (ignoreDefaultPortsInConnectHostHeader && (port == 80 || port == 443)) ? - hostString : - url; - - FullHttpRequest req = new DefaultFullHttpRequest( - HttpVersion.HTTP_1_1, HttpMethod.CONNECT, - url, - Unpooled.EMPTY_BUFFER, false); - - req.headers().set(HttpHeaderNames.HOST, hostHeader); - - if (authorization != null) { - req.headers().set(HttpHeaderNames.PROXY_AUTHORIZATION, authorization); - } - - if (outboundHeaders != null) { - req.headers().add(outboundHeaders); - } - - return req; - } - - @Override - 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); - } - HttpResponse res = (HttpResponse) response; - status = res.status(); - inboundHeaders = res.headers(); - } - - boolean finished = response instanceof LastHttpContent; - if (finished) { - if (status == null) { - throw new HttpProxyConnectException(exceptionMessage("missing response"), inboundHeaders); - } - if (status.code() != 200) { - 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; - } - } - - private static final class HttpClientCodecWrapper implements ChannelHandler { - final HttpClientCodec codec = new HttpClientCodec(); - - @Override - public void handlerAdded(ChannelHandlerContext ctx) throws Exception { - codec.handlerAdded(ctx); - } - - @Override - public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { - codec.handlerRemoved(ctx); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - codec.exceptionCaught(ctx, cause); - } - - @Override - public void channelRegistered(ChannelHandlerContext ctx) throws Exception { - codec.channelRegistered(ctx); - } - - @Override - public void channelUnregistered(ChannelHandlerContext ctx) throws Exception { - codec.channelUnregistered(ctx); - } - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - codec.channelActive(ctx); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - codec.channelInactive(ctx); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - codec.channelRead(ctx, msg); - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { - codec.channelReadComplete(ctx); - } - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - codec.userEventTriggered(ctx, evt); - } - - @Override - public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception { - codec.channelWritabilityChanged(ctx); - } - - @Override - public Future bind(ChannelHandlerContext ctx, SocketAddress localAddress) { - return codec.bind(ctx, localAddress); - } - - @Override - public Future connect( - ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress) { - return codec.connect(ctx, remoteAddress, localAddress); - } - - @Override - public Future disconnect(ChannelHandlerContext ctx) { - return codec.disconnect(ctx); - } - - @Override - public Future close(ChannelHandlerContext ctx) { - return codec.close(ctx); - } - - @Override - public Future deregister(ChannelHandlerContext ctx) { - return codec.deregister(ctx); - } - - @Override - public void read(ChannelHandlerContext ctx) { - codec.read(ctx); - } - - @Override - public Future write(ChannelHandlerContext ctx, Object msg) { - return codec.write(ctx, msg); - } - - @Override - public void flush(ChannelHandlerContext ctx) { - codec.flush(ctx); - } - } -} diff --git a/handler-proxy/src/main/java/io/netty/handler/proxy/ProxyConnectException.java b/handler-proxy/src/main/java/io/netty/handler/proxy/ProxyConnectException.java deleted file mode 100644 index dd0ef7b2a9..0000000000 --- a/handler-proxy/src/main/java/io/netty/handler/proxy/ProxyConnectException.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.proxy; - -import java.net.ConnectException; - -public class ProxyConnectException extends ConnectException { - private static final long serialVersionUID = 5211364632246265538L; - - public ProxyConnectException() { } - - public ProxyConnectException(String msg) { - super(msg); - } - - public ProxyConnectException(Throwable cause) { - initCause(cause); - } - - public ProxyConnectException(String msg, Throwable cause) { - super(msg); - initCause(cause); - } -} diff --git a/handler-proxy/src/main/java/io/netty/handler/proxy/ProxyConnectionEvent.java b/handler-proxy/src/main/java/io/netty/handler/proxy/ProxyConnectionEvent.java deleted file mode 100644 index baf68818cf..0000000000 --- a/handler-proxy/src/main/java/io/netty/handler/proxy/ProxyConnectionEvent.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.proxy; - -import static java.util.Objects.requireNonNull; - -import io.netty.util.internal.StringUtil; - -import java.net.SocketAddress; - -public final class ProxyConnectionEvent { - - private final String protocol; - private final String authScheme; - private final SocketAddress proxyAddress; - private final SocketAddress destinationAddress; - private String strVal; - - /** - * Creates a new event that indicates a successful connection attempt to the destination address. - */ - public ProxyConnectionEvent( - String protocol, String authScheme, SocketAddress proxyAddress, SocketAddress destinationAddress) { - requireNonNull(protocol, "protocol"); - requireNonNull(authScheme, "authScheme"); - requireNonNull(proxyAddress, "proxyAddress"); - requireNonNull(destinationAddress, "destinationAddress"); - - this.protocol = protocol; - this.authScheme = authScheme; - this.proxyAddress = proxyAddress; - this.destinationAddress = destinationAddress; - } - - /** - * Returns the name of the proxy protocol in use. - */ - public String protocol() { - return protocol; - } - - /** - * Returns the name of the authentication scheme in use. - */ - public String authScheme() { - return authScheme; - } - - /** - * Returns the address of the proxy server. - */ - @SuppressWarnings("unchecked") - public T proxyAddress() { - return (T) proxyAddress; - } - - /** - * Returns the address of the destination. - */ - @SuppressWarnings("unchecked") - public T destinationAddress() { - return (T) destinationAddress; - } - - @Override - public String toString() { - if (strVal != null) { - return strVal; - } - - StringBuilder buf = new StringBuilder(128) - .append(StringUtil.simpleClassName(this)) - .append('(') - .append(protocol) - .append(", ") - .append(authScheme) - .append(", ") - .append(proxyAddress) - .append(" => ") - .append(destinationAddress) - .append(')'); - - return strVal = buf.toString(); - } -} diff --git a/handler-proxy/src/main/java/io/netty/handler/proxy/ProxyHandler.java b/handler-proxy/src/main/java/io/netty/handler/proxy/ProxyHandler.java deleted file mode 100644 index 82f2fc7676..0000000000 --- a/handler-proxy/src/main/java/io/netty/handler/proxy/ProxyHandler.java +++ /dev/null @@ -1,456 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.proxy; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.PendingWriteQueue; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.concurrent.DefaultPromise; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.FutureListener; -import io.netty.util.concurrent.ImmediateEventExecutor; -import io.netty.util.concurrent.Promise; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.net.SocketAddress; -import java.nio.channels.ConnectionPendingException; -import java.util.concurrent.TimeUnit; - -import static java.util.Objects.requireNonNull; - -public abstract class ProxyHandler implements ChannelHandler { - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(ProxyHandler.class); - - /** - * The default connect timeout: 10 seconds. - */ - private static final long DEFAULT_CONNECT_TIMEOUT_MILLIS = 10000; - - /** - * A string that signifies 'no authentication' or 'anonymous'. - */ - static final String AUTH_NONE = "none"; - - private final SocketAddress proxyAddress; - private volatile SocketAddress destinationAddress; - private volatile long connectTimeoutMillis = DEFAULT_CONNECT_TIMEOUT_MILLIS; - - private volatile ChannelHandlerContext ctx; - private PendingWriteQueue pendingWrites; - private boolean finished; - private boolean suppressChannelReadComplete; - private boolean flushedPrematurely; - private final Promise connectPromise = new LazyPromise(); - private Future connectTimeoutFuture; - private final FutureListener writeListener = future -> { - if (future.isFailed()) { - setConnectFailure(future.cause()); - } - }; - - protected ProxyHandler(SocketAddress proxyAddress) { - requireNonNull(proxyAddress, "proxyAddress"); - this.proxyAddress = proxyAddress; - } - - /** - * Returns the name of the proxy protocol in use. - */ - public abstract String protocol(); - - /** - * Returns the name of the authentication scheme in use. - */ - public abstract String authScheme(); - - /** - * Returns the address of the proxy server. - */ - @SuppressWarnings("unchecked") - public final T proxyAddress() { - return (T) proxyAddress; - } - - /** - * Returns the address of the destination to connect to via the proxy server. - */ - @SuppressWarnings("unchecked") - public final T destinationAddress() { - return (T) destinationAddress; - } - - /** - * Returns {@code true} if and only if the connection to the destination has been established successfully. - */ - public final boolean isConnected() { - return connectPromise.isSuccess(); - } - - /** - * Returns a {@link Future} that is notified when the connection to the destination has been established - * or the connection attempt has failed. - */ - public final Future connectFuture() { - return connectPromise.asFuture(); - } - - /** - * Returns the connect timeout in millis. If the connection attempt to the destination does not finish within - * the timeout, the connection attempt will be failed. - */ - public final long connectTimeoutMillis() { - return connectTimeoutMillis; - } - - /** - * Sets the connect timeout in millis. If the connection attempt to the destination does not finish within - * the timeout, the connection attempt will be failed. - */ - public final void setConnectTimeoutMillis(long connectTimeoutMillis) { - if (connectTimeoutMillis <= 0) { - connectTimeoutMillis = 0; - } - - this.connectTimeoutMillis = connectTimeoutMillis; - } - - @Override - public final void handlerAdded(ChannelHandlerContext ctx) throws Exception { - this.ctx = ctx; - addCodec(ctx); - - if (ctx.channel().isActive()) { - // channelActive() event has been fired already, which means this.channelActive() will - // not be invoked. We have to initialize here instead. - sendInitialMessage(ctx); - } else { - // channelActive() event has not been fired yet. this.channelOpen() will be invoked - // and initialization will occur there. - } - } - - /** - * Adds the codec handlers required to communicate with the proxy server. - */ - protected abstract void addCodec(ChannelHandlerContext ctx) throws Exception; - - /** - * Removes the encoders added in {@link #addCodec(ChannelHandlerContext)}. - */ - protected abstract void removeEncoder(ChannelHandlerContext ctx) throws Exception; - - /** - * Removes the decoders added in {@link #addCodec(ChannelHandlerContext)}. - */ - protected abstract void removeDecoder(ChannelHandlerContext ctx) throws Exception; - - @Override - public final Future connect( - ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress) { - if (destinationAddress != null) { - return ctx.newFailedFuture(new ConnectionPendingException()); - } - - destinationAddress = remoteAddress; - return ctx.connect(proxyAddress, localAddress); - } - - @Override - public final void channelActive(ChannelHandlerContext ctx) throws Exception { - sendInitialMessage(ctx); - ctx.fireChannelActive(); - } - - /** - * Sends the initial message to be sent to the proxy server. This method also starts a timeout task which marks - * the {@link #connectPromise} as failure if the connection attempt does not success within the timeout. - */ - private void sendInitialMessage(final ChannelHandlerContext ctx) throws Exception { - final long connectTimeoutMillis = this.connectTimeoutMillis; - if (connectTimeoutMillis > 0) { - connectTimeoutFuture = ctx.executor().schedule(() -> { - if (!connectPromise.isDone()) { - setConnectFailure(new ProxyConnectException(exceptionMessage("timeout"))); - } - }, connectTimeoutMillis, TimeUnit.MILLISECONDS); - } - - final Object initialMessage = newInitialMessage(ctx); - if (initialMessage != null) { - sendToProxyServer(initialMessage); - } - - readIfNeeded(ctx); - } - - /** - * Returns a new message that is sent at first time when the connection to the proxy server has been established. - * - * @return the initial message, or {@code null} if the proxy server is expected to send the first message instead - */ - protected abstract Object newInitialMessage(ChannelHandlerContext ctx) throws Exception; - - /** - * Sends the specified message to the proxy server. Use this method to send a response to the proxy server in - * {@link #handleResponse(ChannelHandlerContext, Object)}. - */ - protected final void sendToProxyServer(Object msg) { - ctx.writeAndFlush(msg).addListener(writeListener); - } - - @Override - public final void channelInactive(ChannelHandlerContext ctx) throws Exception { - if (finished) { - ctx.fireChannelInactive(); - } else { - // Disconnected before connected to the destination. - setConnectFailure(new ProxyConnectException(exceptionMessage("disconnected"))); - } - } - - @Override - public final void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - if (finished) { - ctx.fireExceptionCaught(cause); - } else { - // Exception was raised before the connection attempt is finished. - setConnectFailure(cause); - } - } - - @Override - public final void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - if (finished) { - // Received a message after the connection has been established; pass through. - suppressChannelReadComplete = false; - ctx.fireChannelRead(msg); - } else { - suppressChannelReadComplete = true; - Throwable cause = null; - try { - boolean done = handleResponse(ctx, msg); - if (done) { - setConnectSuccess(); - } - } catch (Throwable t) { - cause = t; - } finally { - ReferenceCountUtil.release(msg); - if (cause != null) { - setConnectFailure(cause); - } - } - } - } - - /** - * Handles the message received from the proxy server. - * - * @return {@code true} if the connection to the destination has been established, - * {@code false} if the connection to the destination has not been established and more messages are - * expected from the proxy server - */ - protected abstract boolean handleResponse(ChannelHandlerContext ctx, Object response) throws Exception; - - private void setConnectSuccess() { - finished = true; - cancelConnectTimeoutFuture(); - - if (!connectPromise.isDone()) { - boolean removedCodec = true; - - removedCodec &= safeRemoveEncoder(); - - ctx.fireUserEventTriggered( - new ProxyConnectionEvent(protocol(), authScheme(), proxyAddress, destinationAddress)); - - removedCodec &= safeRemoveDecoder(); - - if (removedCodec) { - writePendingWrites(); - - if (flushedPrematurely) { - ctx.flush(); - } - connectPromise.trySuccess(ctx.channel()); - } else { - // We are at inconsistent state because we failed to remove all codec handlers. - Exception cause = new ProxyConnectException( - "failed to remove all codec handlers added by the proxy handler; bug?"); - failPendingWritesAndClose(cause); - } - } - } - - private boolean safeRemoveDecoder() { - try { - removeDecoder(ctx); - return true; - } catch (Exception e) { - logger.warn("Failed to remove proxy decoders:", e); - } - - return false; - } - - private boolean safeRemoveEncoder() { - try { - removeEncoder(ctx); - return true; - } catch (Exception e) { - logger.warn("Failed to remove proxy encoders:", e); - } - - return false; - } - - private void setConnectFailure(Throwable cause) { - finished = true; - cancelConnectTimeoutFuture(); - - if (!connectPromise.isDone()) { - - if (!(cause instanceof ProxyConnectException)) { - cause = new ProxyConnectException( - exceptionMessage(cause.toString()), cause); - } - - safeRemoveDecoder(); - safeRemoveEncoder(); - failPendingWritesAndClose(cause); - } - } - - private void failPendingWritesAndClose(Throwable cause) { - failPendingWrites(cause); - connectPromise.tryFailure(cause); - ctx.fireExceptionCaught(cause); - ctx.close(); - } - - private void cancelConnectTimeoutFuture() { - if (connectTimeoutFuture != null) { - connectTimeoutFuture.cancel(); - connectTimeoutFuture = null; - } - } - - /** - * Decorates the specified exception message with the common information such as the current protocol, - * authentication scheme, proxy address, and destination address. - */ - protected final String exceptionMessage(String msg) { - if (msg == null) { - msg = ""; - } - - StringBuilder buf = new StringBuilder(128 + msg.length()) - .append(protocol()) - .append(", ") - .append(authScheme()) - .append(", ") - .append(proxyAddress) - .append(" => ") - .append(destinationAddress); - if (!msg.isEmpty()) { - buf.append(", ").append(msg); - } - - return buf.toString(); - } - - @Override - public final void channelReadComplete(ChannelHandlerContext ctx) throws Exception { - if (suppressChannelReadComplete) { - suppressChannelReadComplete = false; - - readIfNeeded(ctx); - } else { - ctx.fireChannelReadComplete(); - } - } - - @Override - public final Future write(ChannelHandlerContext ctx, Object msg) { - if (finished) { - writePendingWrites(); - return ctx.write(msg); - } - Promise promise = ctx.newPromise(); - addPendingWrite(ctx, msg, promise); - return promise.asFuture(); - } - - @Override - public final void flush(ChannelHandlerContext ctx) { - if (finished) { - writePendingWrites(); - ctx.flush(); - } else { - flushedPrematurely = true; - } - } - - private static void readIfNeeded(ChannelHandlerContext ctx) { - if (!ctx.channel().config().isAutoRead()) { - ctx.read(); - } - } - - private void writePendingWrites() { - if (pendingWrites != null) { - PendingWriteQueue queue = pendingWrites; - pendingWrites = null; - queue.removeAndWriteAll(); - } - } - - private void failPendingWrites(Throwable cause) { - if (pendingWrites != null) { - pendingWrites.removeAndFailAll(cause); - pendingWrites = null; - } - } - - private void addPendingWrite(ChannelHandlerContext ctx, Object msg, Promise promise) { - PendingWriteQueue pendingWrites = this.pendingWrites; - if (pendingWrites == null) { - this.pendingWrites = pendingWrites = new PendingWriteQueue(ctx); - } - pendingWrites.add(msg, promise); - } - - private final class LazyPromise extends DefaultPromise { - - LazyPromise() { - super(ImmediateEventExecutor.INSTANCE); - } - - @Override - protected void checkDeadLock() { - if (ctx == null) { - // If ctx is null the handlerAdded(...) callback was not called yet. - return; - } - checkDeadLock(ctx.executor()); - } - } -} diff --git a/handler-proxy/src/main/java/io/netty/handler/proxy/Socks4ProxyHandler.java b/handler-proxy/src/main/java/io/netty/handler/proxy/Socks4ProxyHandler.java deleted file mode 100644 index 5dca360d01..0000000000 --- a/handler-proxy/src/main/java/io/netty/handler/proxy/Socks4ProxyHandler.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.proxy; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.handler.codec.socksx.v4.DefaultSocks4CommandRequest; -import io.netty.handler.codec.socksx.v4.Socks4ClientDecoder; -import io.netty.handler.codec.socksx.v4.Socks4ClientEncoder; -import io.netty.handler.codec.socksx.v4.Socks4CommandResponse; -import io.netty.handler.codec.socksx.v4.Socks4CommandStatus; -import io.netty.handler.codec.socksx.v4.Socks4CommandType; - -import java.net.InetSocketAddress; -import java.net.SocketAddress; - -public final class Socks4ProxyHandler extends ProxyHandler { - - private static final String PROTOCOL = "socks4"; - private static final String AUTH_USERNAME = "username"; - - private final String username; - - private String decoderName; - private String encoderName; - - public Socks4ProxyHandler(SocketAddress proxyAddress) { - this(proxyAddress, null); - } - - public Socks4ProxyHandler(SocketAddress proxyAddress, String username) { - super(proxyAddress); - if (username != null && username.isEmpty()) { - username = null; - } - this.username = username; - } - - @Override - public String protocol() { - return PROTOCOL; - } - - @Override - public String authScheme() { - return username != null? AUTH_USERNAME : AUTH_NONE; - } - - public String username() { - return username; - } - - @Override - protected void addCodec(ChannelHandlerContext ctx) throws Exception { - ChannelPipeline p = ctx.pipeline(); - String name = ctx.name(); - - Socks4ClientDecoder decoder = new Socks4ClientDecoder(); - p.addBefore(name, null, decoder); - - decoderName = p.context(decoder).name(); - encoderName = decoderName + ".encoder"; - - p.addBefore(name, encoderName, Socks4ClientEncoder.INSTANCE); - } - - @Override - protected void removeEncoder(ChannelHandlerContext ctx) throws Exception { - ChannelPipeline p = ctx.pipeline(); - p.remove(encoderName); - } - - @Override - protected void removeDecoder(ChannelHandlerContext ctx) throws Exception { - ChannelPipeline p = ctx.pipeline(); - p.remove(decoderName); - } - - @Override - protected Object newInitialMessage(ChannelHandlerContext ctx) throws Exception { - InetSocketAddress raddr = destinationAddress(); - String rhost; - if (raddr.isUnresolved()) { - rhost = raddr.getHostString(); - } else { - rhost = raddr.getAddress().getHostAddress(); - } - return new DefaultSocks4CommandRequest( - Socks4CommandType.CONNECT, rhost, raddr.getPort(), username != null? username : ""); - } - - @Override - protected boolean handleResponse(ChannelHandlerContext ctx, Object response) throws Exception { - final Socks4CommandResponse res = (Socks4CommandResponse) response; - final Socks4CommandStatus status = res.status(); - if (status == Socks4CommandStatus.SUCCESS) { - return true; - } - - throw new ProxyConnectException(exceptionMessage("status: " + status)); - } -} diff --git a/handler-proxy/src/main/java/io/netty/handler/proxy/Socks5ProxyHandler.java b/handler-proxy/src/main/java/io/netty/handler/proxy/Socks5ProxyHandler.java deleted file mode 100644 index 1d6a03a6f8..0000000000 --- a/handler-proxy/src/main/java/io/netty/handler/proxy/Socks5ProxyHandler.java +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.proxy; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.handler.codec.socksx.v5.DefaultSocks5InitialRequest; -import io.netty.handler.codec.socksx.v5.DefaultSocks5CommandRequest; -import io.netty.handler.codec.socksx.v5.DefaultSocks5PasswordAuthRequest; -import io.netty.handler.codec.socksx.v5.Socks5AddressType; -import io.netty.handler.codec.socksx.v5.Socks5AuthMethod; -import io.netty.handler.codec.socksx.v5.Socks5InitialRequest; -import io.netty.handler.codec.socksx.v5.Socks5InitialResponse; -import io.netty.handler.codec.socksx.v5.Socks5InitialResponseDecoder; -import io.netty.handler.codec.socksx.v5.Socks5ClientEncoder; -import io.netty.handler.codec.socksx.v5.Socks5CommandResponse; -import io.netty.handler.codec.socksx.v5.Socks5CommandResponseDecoder; -import io.netty.handler.codec.socksx.v5.Socks5CommandStatus; -import io.netty.handler.codec.socksx.v5.Socks5CommandType; -import io.netty.handler.codec.socksx.v5.Socks5PasswordAuthResponse; -import io.netty.handler.codec.socksx.v5.Socks5PasswordAuthResponseDecoder; -import io.netty.handler.codec.socksx.v5.Socks5PasswordAuthStatus; -import io.netty.util.NetUtil; -import io.netty.util.internal.StringUtil; - -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.util.Arrays; -import java.util.Collections; - -public final class Socks5ProxyHandler extends ProxyHandler { - - private static final String PROTOCOL = "socks5"; - private static final String AUTH_PASSWORD = "password"; - - private static final Socks5InitialRequest INIT_REQUEST_NO_AUTH = - new DefaultSocks5InitialRequest(Collections.singletonList(Socks5AuthMethod.NO_AUTH)); - - private static final Socks5InitialRequest INIT_REQUEST_PASSWORD = - new DefaultSocks5InitialRequest(Arrays.asList(Socks5AuthMethod.NO_AUTH, Socks5AuthMethod.PASSWORD)); - - private final String username; - private final String password; - - private String decoderName; - private String encoderName; - - public Socks5ProxyHandler(SocketAddress proxyAddress) { - this(proxyAddress, null, null); - } - - public Socks5ProxyHandler(SocketAddress proxyAddress, String username, String password) { - super(proxyAddress); - if (username != null && username.isEmpty()) { - username = null; - } - if (password != null && password.isEmpty()) { - password = null; - } - this.username = username; - this.password = password; - } - - @Override - public String protocol() { - return PROTOCOL; - } - - @Override - public String authScheme() { - return socksAuthMethod() == Socks5AuthMethod.PASSWORD? AUTH_PASSWORD : AUTH_NONE; - } - - public String username() { - return username; - } - - public String password() { - return password; - } - - @Override - protected void addCodec(ChannelHandlerContext ctx) throws Exception { - ChannelPipeline p = ctx.pipeline(); - String name = ctx.name(); - - Socks5InitialResponseDecoder decoder = new Socks5InitialResponseDecoder(); - p.addBefore(name, null, decoder); - - decoderName = p.context(decoder).name(); - encoderName = decoderName + ".encoder"; - - p.addBefore(name, encoderName, Socks5ClientEncoder.DEFAULT); - } - - @Override - protected void removeEncoder(ChannelHandlerContext ctx) throws Exception { - ctx.pipeline().remove(encoderName); - } - - @Override - protected void removeDecoder(ChannelHandlerContext ctx) throws Exception { - ChannelPipeline p = ctx.pipeline(); - if (p.context(decoderName) != null) { - p.remove(decoderName); - } - } - - @Override - protected Object newInitialMessage(ChannelHandlerContext ctx) throws Exception { - return socksAuthMethod() == Socks5AuthMethod.PASSWORD? INIT_REQUEST_PASSWORD : INIT_REQUEST_NO_AUTH; - } - - @Override - protected boolean handleResponse(ChannelHandlerContext ctx, Object response) throws Exception { - if (response instanceof Socks5InitialResponse) { - Socks5InitialResponse res = (Socks5InitialResponse) response; - Socks5AuthMethod authMethod = socksAuthMethod(); - - if (res.authMethod() != Socks5AuthMethod.NO_AUTH && res.authMethod() != authMethod) { - // Server did not allow unauthenticated access nor accept the requested authentication scheme. - throw new ProxyConnectException(exceptionMessage("unexpected authMethod: " + res.authMethod())); - } - - if (authMethod == Socks5AuthMethod.NO_AUTH) { - sendConnectCommand(ctx); - } else if (authMethod == Socks5AuthMethod.PASSWORD) { - // In case of password authentication, send an authentication request. - ctx.pipeline().replace(decoderName, decoderName, new Socks5PasswordAuthResponseDecoder()); - sendToProxyServer(new DefaultSocks5PasswordAuthRequest( - username != null? username : "", password != null? password : "")); - } else { - // Should never reach here. - throw new Error(); - } - - return false; - } - - if (response instanceof Socks5PasswordAuthResponse) { - // Received an authentication response from the server. - Socks5PasswordAuthResponse res = (Socks5PasswordAuthResponse) response; - if (res.status() != Socks5PasswordAuthStatus.SUCCESS) { - throw new ProxyConnectException(exceptionMessage("authStatus: " + res.status())); - } - - sendConnectCommand(ctx); - return false; - } - - // This should be the last message from the server. - Socks5CommandResponse res = (Socks5CommandResponse) response; - if (res.status() != Socks5CommandStatus.SUCCESS) { - throw new ProxyConnectException(exceptionMessage("status: " + res.status())); - } - - return true; - } - - private Socks5AuthMethod socksAuthMethod() { - Socks5AuthMethod authMethod; - if (username == null && password == null) { - authMethod = Socks5AuthMethod.NO_AUTH; - } else { - authMethod = Socks5AuthMethod.PASSWORD; - } - return authMethod; - } - - private void sendConnectCommand(ChannelHandlerContext ctx) throws Exception { - InetSocketAddress raddr = destinationAddress(); - Socks5AddressType addrType; - String rhost; - if (raddr.isUnresolved()) { - addrType = Socks5AddressType.DOMAIN; - rhost = raddr.getHostString(); - } else { - rhost = raddr.getAddress().getHostAddress(); - if (NetUtil.isValidIpV4Address(rhost)) { - addrType = Socks5AddressType.IPv4; - } else if (NetUtil.isValidIpV6Address(rhost)) { - addrType = Socks5AddressType.IPv6; - } else { - throw new ProxyConnectException( - exceptionMessage("unknown address type: " + StringUtil.simpleClassName(rhost))); - } - } - - ctx.pipeline().replace(decoderName, decoderName, new Socks5CommandResponseDecoder()); - sendToProxyServer(new DefaultSocks5CommandRequest(Socks5CommandType.CONNECT, addrType, rhost, raddr.getPort())); - } -} diff --git a/handler-proxy/src/main/java/io/netty/handler/proxy/package-info.java b/handler-proxy/src/main/java/io/netty/handler/proxy/package-info.java deleted file mode 100644 index d327d971c1..0000000000 --- a/handler-proxy/src/main/java/io/netty/handler/proxy/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -/** - * Adds support for client connections via proxy protocols such as - * SOCKS and - * HTTP CONNECT tunneling - */ -package io.netty.handler.proxy; 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 deleted file mode 100644 index 2d27eb924d..0000000000 --- a/handler-proxy/src/test/java/io/netty/handler/proxy/HttpProxyHandlerTest.java +++ /dev/null @@ -1,296 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.proxy; - -import io.netty.bootstrap.Bootstrap; -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.channel.local.LocalAddress; -import io.netty.channel.local.LocalChannel; -import io.netty.channel.local.LocalHandler; -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.HttpClientCodec; -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 io.netty.util.concurrent.Future; -import org.junit.jupiter.api.Test; - -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.util.concurrent.atomic.AtomicReference; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.isNull; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.same; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -public class HttpProxyHandlerTest { - - @Test - public void testHostname() throws Exception { - InetSocketAddress socketAddress = new InetSocketAddress(InetAddress.getByName("localhost"), 8080); - testInitialMessage( - socketAddress, - "localhost:8080", - "localhost:8080", - null, - true); - } - - @Test - public void testHostnameUnresolved() throws Exception { - InetSocketAddress socketAddress = InetSocketAddress.createUnresolved("localhost", 8080); - testInitialMessage( - socketAddress, - "localhost:8080", - "localhost:8080", - null, - true); - } - - @Test - public void testHostHeaderWithHttpDefaultPort() throws Exception { - InetSocketAddress socketAddress = new InetSocketAddress(InetAddress.getByName("localhost"), 80); - testInitialMessage(socketAddress, - "localhost:80", - "localhost:80", null, - false); - } - - @Test - public void testHostHeaderWithHttpDefaultPortIgnored() throws Exception { - InetSocketAddress socketAddress = InetSocketAddress.createUnresolved("localhost", 80); - testInitialMessage( - socketAddress, - "localhost:80", - "localhost", - null, - true); - } - - @Test - public void testHostHeaderWithHttpsDefaultPort() throws Exception { - InetSocketAddress socketAddress = new InetSocketAddress(InetAddress.getByName("localhost"), 443); - testInitialMessage( - socketAddress, - "localhost:443", - "localhost:443", - null, - false); - } - - @Test - public void testHostHeaderWithHttpsDefaultPortIgnored() throws Exception { - InetSocketAddress socketAddress = InetSocketAddress.createUnresolved("localhost", 443); - testInitialMessage( - socketAddress, - "localhost:443", - "localhost", - null, - true); - } - - @Test - public void testIpv6() throws Exception { - InetSocketAddress socketAddress = new InetSocketAddress(InetAddress.getByName("::1"), 8080); - testInitialMessage( - socketAddress, - "[::1]:8080", - "[::1]:8080", - null, - true); - } - - @Test - public void testIpv6Unresolved() throws Exception { - InetSocketAddress socketAddress = InetSocketAddress.createUnresolved("::1", 8080); - testInitialMessage( - socketAddress, - "[::1]:8080", - "[::1]:8080", - null, - true); - } - - @Test - public void testIpv4() throws Exception { - InetSocketAddress socketAddress = new InetSocketAddress(InetAddress.getByName("10.0.0.1"), 8080); - testInitialMessage(socketAddress, - "10.0.0.1:8080", - "10.0.0.1:8080", - null, - true); - } - - @Test - public void testIpv4Unresolved() throws Exception { - InetSocketAddress socketAddress = InetSocketAddress.createUnresolved("10.0.0.1", 8080); - testInitialMessage( - socketAddress, - "10.0.0.1:8080", - "10.0.0.1:8080", - null, - true); - } - - @Test - public void testCustomHeaders() throws Exception { - InetSocketAddress socketAddress = InetSocketAddress.createUnresolved("10.0.0.1", 8080); - testInitialMessage( - socketAddress, - "10.0.0.1:8080", - "10.0.0.1:8080", - new DefaultHttpHeaders() - .add("CUSTOM_HEADER", "CUSTOM_VALUE1") - .add("CUSTOM_HEADER", "CUSTOM_VALUE2"), - true); - } - - @Test - public void testExceptionDuringConnect() throws Exception { - EventLoopGroup group = null; - Channel serverChannel = null; - Channel clientChannel = null; - try { - group = new MultithreadEventLoopGroup(1, LocalHandler.newFactory()); - final LocalAddress addr = new LocalAddress("a"); - final AtomicReference exception = new AtomicReference(); - Future sf = - new ServerBootstrap().channel(LocalServerChannel.class).group(group).childHandler( - new ChannelInitializer() { - - @Override - protected void initChannel(Channel ch) { - ch.pipeline().addFirst(new HttpResponseEncoder()); - ch.pipeline().addFirst(new ChannelHandler() { - @Override - public void channelActive(ChannelHandlerContext ctx) { - DefaultFullHttpResponse response = new DefaultFullHttpResponse( - HttpVersion.HTTP_1_1, - HttpResponseStatus.BAD_GATEWAY); - response.headers().add("name", "value"); - response.headers().add(HttpHeaderNames.CONTENT_LENGTH, "0"); - ctx.writeAndFlush(response); - } - }); - } - }).bind(addr); - serverChannel = sf.get(); - Future 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 ChannelHandler() { - @Override - public void exceptionCaught(ChannelHandlerContext ctx, - Throwable cause) { - exception.set(cause); - } - }); - } - }).connect(new InetSocketAddress("localhost", 1234)); - clientChannel = cf.get(); - 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(); - } - } - } - - @SuppressWarnings("unchecked") - private static void testInitialMessage(InetSocketAddress socketAddress, - String expectedUrl, - String expectedHostHeader, - HttpHeaders headers, - boolean ignoreDefaultPortsInConnectHostHeader) throws Exception { - InetSocketAddress proxyAddress = new InetSocketAddress(NetUtil.LOCALHOST, 8080); - - Future future = mock(Future.class); - - ChannelHandlerContext ctx = mock(ChannelHandlerContext.class); - when(ctx.connect(same(proxyAddress), isNull(InetSocketAddress.class))).thenReturn(future); - - HttpProxyHandler handler = new HttpProxyHandler( - new InetSocketAddress(NetUtil.LOCALHOST, 8080), - headers, - ignoreDefaultPortsInConnectHostHeader); - handler.connect(ctx, socketAddress, null); - - FullHttpRequest request = (FullHttpRequest) handler.newInitialMessage(ctx); - try { - assertEquals(HttpVersion.HTTP_1_1, request.protocolVersion()); - assertEquals(expectedUrl, request.uri()); - HttpHeaders actualHeaders = request.headers(); - assertEquals(expectedHostHeader, actualHeaders.get(HttpHeaderNames.HOST)); - - if (headers != null) { - // The actual request header is a strict superset of the custom header - for (String name : headers.names()) { - assertEquals(headers.getAll(name), actualHeaders.getAll(name)); - } - } - } finally { - request.release(); - } - verify(ctx).connect(proxyAddress, null); - } - - @Test - public void testHttpClientCodecIsInvisible() { - EmbeddedChannel channel = new EmbeddedChannel(new HttpProxyHandler( - new InetSocketAddress(NetUtil.LOCALHOST, 8080))) { - @Override - public boolean isActive() { - // We want to simulate that the Channel did not become active yet. - return false; - } - }; - assertNotNull(channel.pipeline().get(HttpProxyHandler.class)); - assertNull(channel.pipeline().get(HttpClientCodec.class)); - } -} diff --git a/handler-proxy/src/test/java/io/netty/handler/proxy/HttpProxyServer.java b/handler-proxy/src/test/java/io/netty/handler/proxy/HttpProxyServer.java deleted file mode 100644 index f15a7be0f8..0000000000 --- a/handler-proxy/src/test/java/io/netty/handler/proxy/HttpProxyServer.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.proxy; - -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.socket.SocketChannel; -import io.netty.handler.codec.LineBasedFrameDecoder; -import io.netty.handler.codec.http.DefaultFullHttpResponse; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpObjectAggregator; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpServerCodec; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.util.internal.SocketUtils; - -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.nio.charset.StandardCharsets; -import java.util.Base64; - -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; - -final class HttpProxyServer extends ProxyServer { - - HttpProxyServer(boolean useSsl, TestMode testMode, InetSocketAddress destination) { - super(useSsl, testMode, destination); - } - - HttpProxyServer( - boolean useSsl, TestMode testMode, InetSocketAddress destination, String username, String password) { - super(useSsl, testMode, destination, username, password); - } - - @Override - protected void configure(SocketChannel ch) throws Exception { - ChannelPipeline p = ch.pipeline(); - switch (testMode) { - case INTERMEDIARY: - p.addLast(new HttpServerCodec()); - p.addLast(new HttpObjectAggregator(1)); - p.addLast(new HttpIntermediaryHandler()); - break; - case TERMINAL: - p.addLast(new HttpServerCodec()); - p.addLast(new HttpObjectAggregator(1)); - p.addLast(new HttpTerminalHandler()); - break; - case UNRESPONSIVE: - p.addLast(UnresponsiveHandler.INSTANCE); - break; - } - } - - private boolean authenticate(ChannelHandlerContext ctx, FullHttpRequest req) { - assertThat(req.method(), is(HttpMethod.CONNECT)); - - if (testMode != TestMode.INTERMEDIARY) { - ctx.pipeline().addBefore(ctx.name(), "lineDecoder", new LineBasedFrameDecoder(64, false, true)); - } - - ctx.pipeline().remove(HttpObjectAggregator.class); - ctx.pipeline().get(HttpServerCodec.class).removeInboundHandler(); - - boolean authzSuccess = false; - if (username != null) { - CharSequence authz = req.headers().get(HttpHeaderNames.PROXY_AUTHORIZATION); - if (authz != null) { - String[] authzParts = authz.toString().split(" ", 2); - byte[] authzCreds = Base64.getDecoder().decode(authzParts[1]); - - String expectedAuthz = username + ':' + password; - authzSuccess = "Basic".equals(authzParts[0]) && - expectedAuthz.equals(new String(authzCreds, StandardCharsets.UTF_8)); - } - } else { - authzSuccess = true; - } - - return authzSuccess; - } - - private final class HttpIntermediaryHandler extends IntermediaryHandler { - - private SocketAddress intermediaryDestination; - - @Override - protected boolean handleProxyProtocol(ChannelHandlerContext ctx, Object msg) throws Exception { - FullHttpRequest req = (FullHttpRequest) msg; - FullHttpResponse res; - if (!authenticate(ctx, req)) { - res = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.UNAUTHORIZED); - res.headers().set(HttpHeaderNames.CONTENT_LENGTH, 0); - } else { - res = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - String uri = req.uri(); - int lastColonPos = uri.lastIndexOf(':'); - assertThat(lastColonPos, is(greaterThan(0))); - intermediaryDestination = SocketUtils.socketAddress( - uri.substring(0, lastColonPos), Integer.parseInt(uri.substring(lastColonPos + 1))); - } - - ctx.write(res); - ctx.pipeline().get(HttpServerCodec.class).removeOutboundHandler(); - return true; - } - - @Override - protected SocketAddress intermediaryDestination() { - return intermediaryDestination; - } - } - - private final class HttpTerminalHandler extends TerminalHandler { - - @Override - protected boolean handleProxyProtocol(ChannelHandlerContext ctx, Object msg) throws Exception { - FullHttpRequest req = (FullHttpRequest) msg; - FullHttpResponse res; - boolean sendGreeting = false; - - if (!authenticate(ctx, req)) { - res = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.UNAUTHORIZED); - res.headers().set(HttpHeaderNames.CONTENT_LENGTH, 0); - } else if (!req.uri().equals(destination.getHostString() + ':' + destination.getPort())) { - res = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.FORBIDDEN); - res.headers().set(HttpHeaderNames.CONTENT_LENGTH, 0); - } else { - res = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - sendGreeting = true; - } - - ctx.write(res); - ctx.pipeline().get(HttpServerCodec.class).removeOutboundHandler(); - - if (sendGreeting) { - ctx.write(Unpooled.copiedBuffer("0\n", StandardCharsets.US_ASCII)); - } - - return true; - } - } -} 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 deleted file mode 100644 index 7e089839df..0000000000 --- a/handler-proxy/src/test/java/io/netty/handler/proxy/ProxyHandlerTest.java +++ /dev/null @@ -1,755 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.proxy; - -import io.netty.bootstrap.Bootstrap; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.PooledByteBufAllocator; -import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelOption; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.SocketChannel; -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; -import io.netty.util.CharsetUtil; -import io.netty.util.concurrent.DefaultThreadFactory; -import io.netty.util.concurrent.Future; -import io.netty.util.internal.EmptyArrays; -import io.netty.util.internal.SocketUtils; -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -import java.net.InetSocketAddress; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Queue; -import java.util.Random; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.fail; - -public class ProxyHandlerTest { - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(ProxyHandlerTest.class); - - private static final InetSocketAddress DESTINATION = InetSocketAddress.createUnresolved("destination.com", 42); - private static final InetSocketAddress BAD_DESTINATION = SocketUtils.socketAddress("1.2.3.4", 5); - private static final String USERNAME = "testUser"; - private static final String PASSWORD = "testPassword"; - private static final String BAD_USERNAME = "badUser"; - private static final String BAD_PASSWORD = "badPassword"; - - static final EventLoopGroup group = new MultithreadEventLoopGroup(3, - new DefaultThreadFactory("proxy", true), NioHandler.newFactory()); - - static final SslContext serverSslCtx; - static final SslContext clientSslCtx; - - static { - SslContext sctx; - SslContext cctx; - try { - SelfSignedCertificate ssc = new SelfSignedCertificate(); - sctx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build(); - cctx = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build(); - } catch (Exception e) { - throw new Error(e); - } - serverSslCtx = sctx; - clientSslCtx = cctx; - } - - static final ProxyServer deadHttpProxy = new HttpProxyServer(false, TestMode.UNRESPONSIVE, null); - static final ProxyServer interHttpProxy = new HttpProxyServer(false, TestMode.INTERMEDIARY, null); - static final ProxyServer anonHttpProxy = new HttpProxyServer(false, TestMode.TERMINAL, DESTINATION); - static final ProxyServer httpProxy = - new HttpProxyServer(false, TestMode.TERMINAL, DESTINATION, USERNAME, PASSWORD); - - static final ProxyServer deadHttpsProxy = new HttpProxyServer(true, TestMode.UNRESPONSIVE, null); - static final ProxyServer interHttpsProxy = new HttpProxyServer(true, TestMode.INTERMEDIARY, null); - static final ProxyServer anonHttpsProxy = new HttpProxyServer(true, TestMode.TERMINAL, DESTINATION); - static final ProxyServer httpsProxy = - new HttpProxyServer(true, TestMode.TERMINAL, DESTINATION, USERNAME, PASSWORD); - - static final ProxyServer deadSocks4Proxy = new Socks4ProxyServer(false, TestMode.UNRESPONSIVE, null); - static final ProxyServer interSocks4Proxy = new Socks4ProxyServer(false, TestMode.INTERMEDIARY, null); - static final ProxyServer anonSocks4Proxy = new Socks4ProxyServer(false, TestMode.TERMINAL, DESTINATION); - static final ProxyServer socks4Proxy = new Socks4ProxyServer(false, TestMode.TERMINAL, DESTINATION, USERNAME); - - static final ProxyServer deadSocks5Proxy = new Socks5ProxyServer(false, TestMode.UNRESPONSIVE, null); - static final ProxyServer interSocks5Proxy = new Socks5ProxyServer(false, TestMode.INTERMEDIARY, null); - static final ProxyServer anonSocks5Proxy = new Socks5ProxyServer(false, TestMode.TERMINAL, DESTINATION); - static final ProxyServer socks5Proxy = - new Socks5ProxyServer(false, TestMode.TERMINAL, DESTINATION, USERNAME, PASSWORD); - - private static final Collection allProxies = Arrays.asList( - deadHttpProxy, interHttpProxy, anonHttpProxy, httpProxy, - deadHttpsProxy, interHttpsProxy, anonHttpsProxy, httpsProxy, - deadSocks4Proxy, interSocks4Proxy, anonSocks4Proxy, socks4Proxy, - 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; - - public static List testItems() { - - List items = Arrays.asList( - - // HTTP ------------------------------------------------------- - - new SuccessTestItem( - "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( - "Anonymous HTTP proxy: rejected connection", - BAD_DESTINATION, "status: 403", - new HttpProxyHandler(anonHttpProxy.address())), - - new FailureTestItem( - "HTTP proxy: rejected anonymous connection", - DESTINATION, "status: 401", - new HttpProxyHandler(httpProxy.address())), - - new SuccessTestItem( - "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( - "HTTP proxy: rejected connection", - BAD_DESTINATION, "status: 403", - new HttpProxyHandler(httpProxy.address(), USERNAME, PASSWORD)), - - new FailureTestItem( - "HTTP proxy: authentication failure", - DESTINATION, "status: 401", - new HttpProxyHandler(httpProxy.address(), BAD_USERNAME, BAD_PASSWORD)), - - new TimeoutTestItem( - "HTTP proxy: timeout", - new HttpProxyHandler(deadHttpProxy.address())), - - // HTTPS ------------------------------------------------------ - - new SuccessTestItem( - "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())), - - new FailureTestItem( - "Anonymous HTTPS proxy: rejected connection", - BAD_DESTINATION, "status: 403", - clientSslCtx.newHandler(PooledByteBufAllocator.DEFAULT), - new HttpProxyHandler(anonHttpsProxy.address())), - - new FailureTestItem( - "HTTPS proxy: rejected anonymous connection", - DESTINATION, "status: 401", - clientSslCtx.newHandler(PooledByteBufAllocator.DEFAULT), - new HttpProxyHandler(httpsProxy.address())), - - new SuccessTestItem( - "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)), - - new FailureTestItem( - "HTTPS proxy: rejected connection", - BAD_DESTINATION, "status: 403", - clientSslCtx.newHandler(PooledByteBufAllocator.DEFAULT), - new HttpProxyHandler(httpsProxy.address(), USERNAME, PASSWORD)), - - new FailureTestItem( - "HTTPS proxy: authentication failure", - DESTINATION, "status: 401", - clientSslCtx.newHandler(PooledByteBufAllocator.DEFAULT), - new HttpProxyHandler(httpsProxy.address(), BAD_USERNAME, BAD_PASSWORD)), - - new TimeoutTestItem( - "HTTPS proxy: timeout", - clientSslCtx.newHandler(PooledByteBufAllocator.DEFAULT), - new HttpProxyHandler(deadHttpsProxy.address())), - - // SOCKS4 ----------------------------------------------------- - - new SuccessTestItem( - "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( - "Anonymous SOCKS4: rejected connection", - BAD_DESTINATION, "status: REJECTED_OR_FAILED", - new Socks4ProxyHandler(anonSocks4Proxy.address())), - - new FailureTestItem( - "SOCKS4: rejected anonymous connection", - DESTINATION, "status: IDENTD_AUTH_FAILURE", - new Socks4ProxyHandler(socks4Proxy.address())), - - new SuccessTestItem( - "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( - "SOCKS4: rejected connection", - BAD_DESTINATION, "status: REJECTED_OR_FAILED", - new Socks4ProxyHandler(socks4Proxy.address(), USERNAME)), - - new FailureTestItem( - "SOCKS4: authentication failure", - DESTINATION, "status: IDENTD_AUTH_FAILURE", - new Socks4ProxyHandler(socks4Proxy.address(), BAD_USERNAME)), - - new TimeoutTestItem( - "SOCKS4: timeout", - new Socks4ProxyHandler(deadSocks4Proxy.address())), - - // SOCKS5 ----------------------------------------------------- - - new SuccessTestItem( - "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( - "Anonymous SOCKS5: rejected connection", - BAD_DESTINATION, "status: FORBIDDEN", - new Socks5ProxyHandler(anonSocks5Proxy.address())), - - new FailureTestItem( - "SOCKS5: rejected anonymous connection", - DESTINATION, "unexpected authMethod: PASSWORD", - new Socks5ProxyHandler(socks5Proxy.address())), - - new SuccessTestItem( - "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( - "SOCKS5: rejected connection", - BAD_DESTINATION, "status: FORBIDDEN", - new Socks5ProxyHandler(socks5Proxy.address(), USERNAME, PASSWORD)), - - new FailureTestItem( - "SOCKS5: authentication failure", - DESTINATION, "authStatus: FAILURE", - new Socks5ProxyHandler(socks5Proxy.address(), BAD_USERNAME, BAD_PASSWORD)), - - new TimeoutTestItem( - "SOCKS5: timeout", - new Socks5ProxyHandler(deadSocks5Proxy.address())), - - // HTTP + HTTPS + SOCKS4 + SOCKS5 - - new SuccessTestItem( - "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), - new HttpProxyHandler(interHttpsProxy.address()), // HTTPS - new HttpProxyHandler(interHttpProxy.address()), // HTTP - new HttpProxyHandler(anonHttpProxy.address())), - - // (HTTP + HTTPS + SOCKS4 + SOCKS5) * 2 - - new SuccessTestItem( - "Double-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 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( - "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. - List params = new ArrayList<>(items.size()); - for (Object i: items) { - params.add(new Object[] { i }); - } - - // Randomize the execution order to increase the possibility of exposing failure dependencies. - long seed = reproducibleSeed == 0L? System.currentTimeMillis() : reproducibleSeed; - logger.debug("Seed used: {}\n", seed); - Collections.shuffle(params, new Random(seed)); - - return params; - } - - @AfterAll - public static void stopServers() { - for (ProxyServer p: allProxies) { - p.stop(); - } - } - - @BeforeEach - public void clearServerExceptions() throws Exception { - for (ProxyServer p: allProxies) { - p.clearExceptions(); - } - } - - @ParameterizedTest(name = "{index}: {0}") - @MethodSource("testItems") - public void test(TestItem testItem) throws Exception { - testItem.test(); - } - - @AfterEach - public void checkServerExceptions() throws Exception { - for (ProxyServer p: allProxies) { - p.checkExceptions(); - } - } - - private static final class SuccessTestHandler extends SimpleChannelInboundHandler { - - final Queue received = new LinkedBlockingQueue<>(); - final Queue exceptions = new LinkedBlockingQueue<>(); - volatile int eventCount; - - private static void readIfNeeded(ChannelHandlerContext ctx) { - if (!ctx.channel().config().isAutoRead()) { - ctx.read(); - } - } - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - ctx.writeAndFlush(Unpooled.copiedBuffer("A\n", CharsetUtil.US_ASCII)); - readIfNeeded(ctx); - } - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (evt instanceof ProxyConnectionEvent) { - eventCount ++; - - if (eventCount == 1) { - // Note that ProxyConnectionEvent can be triggered multiple times when there are multiple - // ProxyHandlers in the pipeline. Therefore, we send the 'B' message only on the first event. - ctx.writeAndFlush(Unpooled.copiedBuffer("B\n", CharsetUtil.US_ASCII)); - } - readIfNeeded(ctx); - } - } - - @Override - protected void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { - String str = ((ByteBuf) msg).toString(CharsetUtil.US_ASCII); - received.add(str); - if ("2".equals(str)) { - ctx.writeAndFlush(Unpooled.copiedBuffer("C\n", CharsetUtil.US_ASCII)); - } - readIfNeeded(ctx); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - exceptions.add(cause); - ctx.close(); - } - } - - private static final class FailureTestHandler extends SimpleChannelInboundHandler { - - final Queue exceptions = new LinkedBlockingQueue<>(); - - /** - * A latch that counts down when: - * - a pending write attempt in {@link #channelActive(ChannelHandlerContext)} finishes, or - * - the channel is closed. - * By waiting until the latch goes down to 0, we can make sure all assertion failures related with all write - * attempts have been recorded. - */ - final CountDownLatch latch = new CountDownLatch(2); - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - ctx.writeAndFlush(Unpooled.copiedBuffer("A\n", CharsetUtil.US_ASCII)).addListener( - future -> { - latch.countDown(); - if (!(future.cause() instanceof ProxyConnectException)) { - exceptions.add(new AssertionError( - "Unexpected failure cause for initial write: " + future.cause())); - } - }); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - latch.countDown(); - } - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (evt instanceof ProxyConnectionEvent) { - fail("Unexpected event: " + evt); - } - } - - @Override - protected void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { - fail("Unexpected message: " + msg); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - exceptions.add(cause); - ctx.close(); - } - } - - private abstract static class TestItem { - final String name; - final InetSocketAddress destination; - final ChannelHandler[] clientHandlers; - - protected TestItem(String name, InetSocketAddress destination, ChannelHandler... clientHandlers) { - this.name = name; - this.destination = destination; - this.clientHandlers = clientHandlers; - } - - abstract void test() throws Exception; - - protected void assertProxyHandlers(boolean success) { - for (ChannelHandler h: clientHandlers) { - if (h instanceof ProxyHandler) { - ProxyHandler ph = (ProxyHandler) h; - String type = StringUtil.simpleClassName(ph); - Future f = ph.connectFuture(); - if (!f.isDone()) { - logger.warn("{}: not done", type); - } else if (f.isSuccess()) { - if (success) { - logger.debug("{}: success", type); - } else { - logger.warn("{}: success", type); - } - } else { - if (success) { - logger.warn("{}: failure", type, f.cause()); - } else { - logger.debug("{}: failure", type, f.cause()); - } - } - } - } - - for (ChannelHandler h: clientHandlers) { - if (h instanceof ProxyHandler) { - ProxyHandler ph = (ProxyHandler) h; - assertThat(ph.connectFuture().isDone(), is(true)); - assertThat(ph.connectFuture().isSuccess(), is(success)); - } - } - } - - @Override - public String toString() { - return name; - } - } - - private static final class SuccessTestItem extends TestItem { - - private final int expectedEventCount; - // 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) { - if (h instanceof ProxyHandler) { - expectedEventCount++; - } - } - - this.expectedEventCount = expectedEventCount; - this.autoRead = autoRead; - } - - @Override - protected void test() throws Exception { - final SuccessTestHandler testHandler = new SuccessTestHandler(); - Bootstrap b = new Bootstrap(); - b.group(group); - b.channel(NioSocketChannel.class); - b.option(ChannelOption.AUTO_READ, autoRead); - b.resolver(NoopAddressResolverGroup.INSTANCE); - b.handler(new ChannelInitializer() { - @Override - protected void initChannel(SocketChannel ch) throws Exception { - ChannelPipeline p = ch.pipeline(); - p.addLast(clientHandlers); - p.addLast(new LineBasedFrameDecoder(64)); - p.addLast(testHandler); - } - }); - - Channel channel = b.connect(destination).get(); - boolean finished = channel.closeFuture().await(10, TimeUnit.SECONDS); - - logger.debug("Received messages: {}", testHandler.received); - - if (testHandler.exceptions.isEmpty()) { - logger.debug("No recorded exceptions on the client side."); - } else { - for (Throwable t : testHandler.exceptions) { - logger.debug("Recorded exception on the client side: {}", t); - } - } - - assertProxyHandlers(true); - - assertThat(testHandler.received.toArray(), is(new Object[] { "0", "1", "2", "3" })); - assertThat(testHandler.exceptions.toArray(), is(EmptyArrays.EMPTY_OBJECTS)); - assertThat(testHandler.eventCount, is(expectedEventCount)); - assertThat(finished, is(true)); - } - } - - private static final class FailureTestItem extends TestItem { - - private final String expectedMessage; - - FailureTestItem( - String name, InetSocketAddress destination, String expectedMessage, ChannelHandler... clientHandlers) { - super(name, destination, clientHandlers); - this.expectedMessage = expectedMessage; - } - - @Override - protected void test() throws Exception { - final FailureTestHandler testHandler = new FailureTestHandler(); - Bootstrap b = new Bootstrap(); - b.group(group); - b.channel(NioSocketChannel.class); - b.resolver(NoopAddressResolverGroup.INSTANCE); - b.handler(new ChannelInitializer() { - @Override - protected void initChannel(SocketChannel ch) throws Exception { - ChannelPipeline p = ch.pipeline(); - p.addLast(clientHandlers); - p.addLast(new LineBasedFrameDecoder(64)); - p.addLast(testHandler); - } - }); - - Channel channel = b.connect(destination).get(); - boolean finished = channel.closeFuture().await(10, TimeUnit.SECONDS); - finished &= testHandler.latch.await(10, TimeUnit.SECONDS); - - logger.debug("Recorded exceptions: {}", testHandler.exceptions); - - assertProxyHandlers(false); - - assertThat(testHandler.exceptions.size(), is(1)); - Throwable e = testHandler.exceptions.poll(); - assertThat(e, is(instanceOf(ProxyConnectException.class))); - assertThat(String.valueOf(e), containsString(expectedMessage)); - assertThat(finished, is(true)); - } - } - - private static final class TimeoutTestItem extends TestItem { - - TimeoutTestItem(String name, ChannelHandler... clientHandlers) { - super(name, null, clientHandlers); - } - - @Override - protected void test() throws Exception { - final long TIMEOUT = 2000; - for (ChannelHandler h: clientHandlers) { - if (h instanceof ProxyHandler) { - ((ProxyHandler) h).setConnectTimeoutMillis(TIMEOUT); - } - } - - final FailureTestHandler testHandler = new FailureTestHandler(); - Bootstrap b = new Bootstrap(); - b.group(group); - b.channel(NioSocketChannel.class); - b.resolver(NoopAddressResolverGroup.INSTANCE); - b.handler(new ChannelInitializer() { - @Override - protected void initChannel(SocketChannel ch) throws Exception { - ChannelPipeline p = ch.pipeline(); - p.addLast(clientHandlers); - p.addLast(new LineBasedFrameDecoder(64)); - p.addLast(testHandler); - } - }); - - Channel channel = b.connect(DESTINATION).get(); - Future cf = channel.closeFuture(); - boolean finished = cf.await(TIMEOUT * 2, TimeUnit.MILLISECONDS); - finished &= testHandler.latch.await(TIMEOUT * 2, TimeUnit.MILLISECONDS); - - logger.debug("Recorded exceptions: {}", testHandler.exceptions); - - assertProxyHandlers(false); - - assertThat(testHandler.exceptions.size(), is(1)); - Throwable e = testHandler.exceptions.poll(); - assertThat(e, is(instanceOf(ProxyConnectException.class))); - assertThat(String.valueOf(e), containsString("timeout")); - assertThat(finished, is(true)); - } - } -} diff --git a/handler-proxy/src/test/java/io/netty/handler/proxy/ProxyServer.java b/handler-proxy/src/test/java/io/netty/handler/proxy/ProxyServer.java deleted file mode 100644 index 68038cce33..0000000000 --- a/handler-proxy/src/test/java/io/netty/handler/proxy/ProxyServer.java +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.proxy; - -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.ChannelFutureListeners; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoop; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.channel.socket.ServerSocketChannel; -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.ReferenceCountUtil; -import io.netty.util.concurrent.Future; -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.util.ArrayDeque; -import java.util.Queue; -import java.util.concurrent.LinkedBlockingQueue; - -abstract class ProxyServer { - - protected final InternalLogger logger = InternalLoggerFactory.getInstance(getClass()); - - private final ServerSocketChannel ch; - private final Queue recordedExceptions = new LinkedBlockingQueue<>(); - protected final TestMode testMode; - protected final String username; - protected final String password; - protected final InetSocketAddress destination; - - /** - * Starts a new proxy server with disabled authentication for testing purpose. - * - * @param useSsl {@code true} if and only if implicit SSL is enabled - * @param testMode the test mode - * @param destination the expected destination. If the client requests proxying to a different destination, this - * server will reject the connection request. - */ - protected ProxyServer(boolean useSsl, TestMode testMode, InetSocketAddress destination) { - this(useSsl, testMode, destination, null, null); - } - - /** - * Starts a new proxy server with disabled authentication for testing purpose. - * - * @param useSsl {@code true} if and only if implicit SSL is enabled - * @param testMode the test mode - * @param username the expected username. If the client tries to authenticate with a different username, this server - * will fail the authentication request. - * @param password the expected password. If the client tries to authenticate with a different password, this server - * will fail the authentication request. - * @param destination the expected destination. If the client requests proxying to a different destination, this - * server will reject the connection request. - */ - protected ProxyServer( - final boolean useSsl, TestMode testMode, - InetSocketAddress destination, String username, String password) { - - this.testMode = testMode; - this.destination = destination; - this.username = username; - this.password = password; - - ServerBootstrap b = new ServerBootstrap(); - b.channel(NioServerSocketChannel.class); - b.group(ProxyHandlerTest.group); - b.childHandler(new ChannelInitializer() { - @Override - protected void initChannel(SocketChannel ch) throws Exception { - ChannelPipeline p = ch.pipeline(); - if (useSsl) { - p.addLast(ProxyHandlerTest.serverSslCtx.newHandler(ch.alloc())); - } - - configure(ch); - } - }); - - ch = (ServerSocketChannel) b.bind(NetUtil.LOCALHOST, 0).syncUninterruptibly().getNow(); - } - - public final InetSocketAddress address() { - return new InetSocketAddress(NetUtil.LOCALHOST, ch.localAddress().getPort()); - } - - protected abstract void configure(SocketChannel ch) throws Exception; - - final void recordException(Throwable t) { - logger.warn("Unexpected exception from proxy server:", t); - recordedExceptions.add(t); - } - - /** - * Clears all recorded exceptions. - */ - public final void clearExceptions() { - recordedExceptions.clear(); - } - - /** - * Logs all recorded exceptions and raises the last one so that the caller can fail. - */ - public final void checkExceptions() { - Throwable t; - for (;;) { - t = recordedExceptions.poll(); - if (t == null) { - break; - } - - logger.warn("Unexpected exception:", t); - } - - if (t != null) { - PlatformDependent.throwException(t); - } - } - - public final void stop() { - ch.close(); - } - - protected abstract class IntermediaryHandler extends SimpleChannelInboundHandler { - - private final Queue received = new ArrayDeque<>(); - - private boolean finished; - private Channel backend; - - @Override - protected final void messageReceived(final ChannelHandlerContext ctx, Object msg) throws Exception { - if (finished) { - received.add(ReferenceCountUtil.retain(msg)); - flush(); - return; - } - - boolean finished = handleProxyProtocol(ctx, msg); - if (finished) { - this.finished = true; - Future f = connectToDestination(ctx.channel().executor(), new BackendHandler(ctx)); - f.addListener(future -> { - if (future.isFailed()) { - recordException(future.cause()); - ctx.close(); - } else { - backend = future.getNow(); - flush(); - } - }); - } - } - - private void flush() { - if (backend != null) { - boolean wrote = false; - for (;;) { - Object msg = received.poll(); - if (msg == null) { - break; - } - backend.write(msg); - wrote = true; - } - - if (wrote) { - backend.flush(); - } - } - } - - protected abstract boolean handleProxyProtocol(ChannelHandlerContext ctx, Object msg) throws Exception; - - protected abstract SocketAddress intermediaryDestination(); - - private Future connectToDestination(EventLoop loop, ChannelHandler handler) { - Bootstrap b = new Bootstrap(); - b.channel(NioSocketChannel.class); - b.group(loop); - b.handler(handler); - return b.connect(intermediaryDestination()); - } - - @Override - public final void channelReadComplete(ChannelHandlerContext ctx) throws Exception { - ctx.flush(); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - if (backend != null) { - backend.close(); - } - } - - @Override - public final void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - recordException(cause); - ctx.close(); - } - - private final class BackendHandler implements ChannelHandler { - - private final ChannelHandlerContext frontend; - - BackendHandler(ChannelHandlerContext frontend) { - this.frontend = frontend; - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - frontend.write(msg); - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { - frontend.flush(); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - frontend.close(); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - recordException(cause); - ctx.close(); - } - } - } - - protected abstract class TerminalHandler extends SimpleChannelInboundHandler { - - private boolean finished; - - @Override - protected final void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { - if (finished) { - String str = ((ByteBuf) msg).toString(CharsetUtil.US_ASCII); - if ("A\n".equals(str)) { - ctx.write(Unpooled.copiedBuffer("1\n", CharsetUtil.US_ASCII)); - } else if ("B\n".equals(str)) { - ctx.write(Unpooled.copiedBuffer("2\n", CharsetUtil.US_ASCII)); - } else if ("C\n".equals(str)) { - ctx.write(Unpooled.copiedBuffer("3\n", CharsetUtil.US_ASCII)) - .addListener(ctx, ChannelFutureListeners.CLOSE); - } else { - throw new IllegalStateException("unexpected message: " + str); - } - return; - } - - boolean finished = handleProxyProtocol(ctx, msg); - if (finished) { - this.finished = finished; - } - } - - protected abstract boolean handleProxyProtocol(ChannelHandlerContext ctx, Object msg) throws Exception; - - @Override - public final void channelReadComplete(ChannelHandlerContext ctx) throws Exception { - ctx.flush(); - } - - @Override - public final void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - recordException(cause); - ctx.close(); - } - } -} diff --git a/handler-proxy/src/test/java/io/netty/handler/proxy/Socks4ProxyServer.java b/handler-proxy/src/test/java/io/netty/handler/proxy/Socks4ProxyServer.java deleted file mode 100644 index 8bfd235364..0000000000 --- a/handler-proxy/src/test/java/io/netty/handler/proxy/Socks4ProxyServer.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.proxy; - -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.socket.SocketChannel; -import io.netty.handler.codec.LineBasedFrameDecoder; -import io.netty.handler.codec.socksx.v4.DefaultSocks4CommandResponse; -import io.netty.handler.codec.socksx.v4.Socks4CommandRequest; -import io.netty.handler.codec.socksx.v4.Socks4CommandResponse; -import io.netty.handler.codec.socksx.v4.Socks4CommandStatus; -import io.netty.handler.codec.socksx.v4.Socks4CommandType; -import io.netty.handler.codec.socksx.v4.Socks4ServerDecoder; -import io.netty.handler.codec.socksx.v4.Socks4ServerEncoder; -import io.netty.util.CharsetUtil; -import io.netty.util.internal.SocketUtils; - -import java.net.InetSocketAddress; -import java.net.SocketAddress; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; - -final class Socks4ProxyServer extends ProxyServer { - - Socks4ProxyServer(boolean useSsl, TestMode testMode, InetSocketAddress destination) { - super(useSsl, testMode, destination); - } - - Socks4ProxyServer(boolean useSsl, TestMode testMode, InetSocketAddress destination, String username) { - super(useSsl, testMode, destination, username, null); - } - - @Override - protected void configure(SocketChannel ch) throws Exception { - ChannelPipeline p = ch.pipeline(); - switch (testMode) { - case INTERMEDIARY: - p.addLast(new Socks4ServerDecoder()); - p.addLast(Socks4ServerEncoder.INSTANCE); - p.addLast(new Socks4IntermediaryHandler()); - break; - case TERMINAL: - p.addLast(new Socks4ServerDecoder()); - p.addLast(Socks4ServerEncoder.INSTANCE); - p.addLast(new Socks4TerminalHandler()); - break; - case UNRESPONSIVE: - p.addLast(UnresponsiveHandler.INSTANCE); - break; - } - } - - private boolean authenticate(ChannelHandlerContext ctx, Socks4CommandRequest req) { - assertThat(req.type(), is(Socks4CommandType.CONNECT)); - - if (testMode != TestMode.INTERMEDIARY) { - ctx.pipeline().addBefore(ctx.name(), "lineDecoder", new LineBasedFrameDecoder(64, false, true)); - } - - boolean authzSuccess; - if (username != null) { - authzSuccess = username.equals(req.userId()); - } else { - authzSuccess = true; - } - return authzSuccess; - } - - private final class Socks4IntermediaryHandler extends IntermediaryHandler { - - private SocketAddress intermediaryDestination; - - @Override - protected boolean handleProxyProtocol(ChannelHandlerContext ctx, Object msg) throws Exception { - Socks4CommandRequest req = (Socks4CommandRequest) msg; - Socks4CommandResponse res; - - if (!authenticate(ctx, req)) { - res = new DefaultSocks4CommandResponse(Socks4CommandStatus.IDENTD_AUTH_FAILURE); - } else { - res = new DefaultSocks4CommandResponse(Socks4CommandStatus.SUCCESS); - intermediaryDestination = SocketUtils.socketAddress(req.dstAddr(), req.dstPort()); - } - - ctx.write(res); - - ctx.pipeline().remove(Socks4ServerDecoder.class); - ctx.pipeline().remove(Socks4ServerEncoder.class); - - return true; - } - - @Override - protected SocketAddress intermediaryDestination() { - return intermediaryDestination; - } - } - - private final class Socks4TerminalHandler extends TerminalHandler { - @Override - protected boolean handleProxyProtocol(ChannelHandlerContext ctx, Object msg) throws Exception { - Socks4CommandRequest req = (Socks4CommandRequest) msg; - boolean authzSuccess = authenticate(ctx, req); - - Socks4CommandResponse res; - boolean sendGreeting = false; - if (!authzSuccess) { - res = new DefaultSocks4CommandResponse(Socks4CommandStatus.IDENTD_AUTH_FAILURE); - } else if (!req.dstAddr().equals(destination.getHostString()) || - req.dstPort() != destination.getPort()) { - res = new DefaultSocks4CommandResponse(Socks4CommandStatus.REJECTED_OR_FAILED); - } else { - res = new DefaultSocks4CommandResponse(Socks4CommandStatus.SUCCESS); - sendGreeting = true; - } - - ctx.write(res); - - ctx.pipeline().remove(Socks4ServerDecoder.class); - ctx.pipeline().remove(Socks4ServerEncoder.class); - - if (sendGreeting) { - ctx.write(Unpooled.copiedBuffer("0\n", CharsetUtil.US_ASCII)); - } - - return true; - } - } -} diff --git a/handler-proxy/src/test/java/io/netty/handler/proxy/Socks5ProxyServer.java b/handler-proxy/src/test/java/io/netty/handler/proxy/Socks5ProxyServer.java deleted file mode 100644 index abb849915c..0000000000 --- a/handler-proxy/src/test/java/io/netty/handler/proxy/Socks5ProxyServer.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.proxy; - -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.socket.SocketChannel; -import io.netty.handler.codec.LineBasedFrameDecoder; -import io.netty.handler.codec.socksx.v5.DefaultSocks5CommandResponse; -import io.netty.handler.codec.socksx.v5.DefaultSocks5InitialResponse; -import io.netty.handler.codec.socksx.v5.DefaultSocks5PasswordAuthResponse; -import io.netty.handler.codec.socksx.v5.Socks5AddressType; -import io.netty.handler.codec.socksx.v5.Socks5AuthMethod; -import io.netty.handler.codec.socksx.v5.Socks5CommandRequest; -import io.netty.handler.codec.socksx.v5.Socks5CommandRequestDecoder; -import io.netty.handler.codec.socksx.v5.Socks5CommandResponse; -import io.netty.handler.codec.socksx.v5.Socks5CommandStatus; -import io.netty.handler.codec.socksx.v5.Socks5CommandType; -import io.netty.handler.codec.socksx.v5.Socks5InitialRequest; -import io.netty.handler.codec.socksx.v5.Socks5InitialRequestDecoder; -import io.netty.handler.codec.socksx.v5.Socks5PasswordAuthRequest; -import io.netty.handler.codec.socksx.v5.Socks5PasswordAuthRequestDecoder; -import io.netty.handler.codec.socksx.v5.Socks5PasswordAuthStatus; -import io.netty.handler.codec.socksx.v5.Socks5ServerEncoder; -import io.netty.util.CharsetUtil; -import io.netty.util.internal.SocketUtils; - -import java.net.InetSocketAddress; -import java.net.SocketAddress; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; - -final class Socks5ProxyServer extends ProxyServer { - - private static final String ENCODER = "encoder"; - private static final String DECODER = "decoder"; - - Socks5ProxyServer(boolean useSsl, TestMode testMode, InetSocketAddress destination) { - super(useSsl, testMode, destination); - } - - Socks5ProxyServer( - boolean useSsl, TestMode testMode, InetSocketAddress destination, String username, String password) { - super(useSsl, testMode, destination, username, password); - } - - @Override - protected void configure(SocketChannel ch) throws Exception { - ChannelPipeline p = ch.pipeline(); - switch (testMode) { - case INTERMEDIARY: - p.addLast(DECODER, new Socks5InitialRequestDecoder()); - p.addLast(ENCODER, Socks5ServerEncoder.DEFAULT); - p.addLast(new Socks5IntermediaryHandler()); - break; - case TERMINAL: - p.addLast(DECODER, new Socks5InitialRequestDecoder()); - p.addLast(ENCODER, Socks5ServerEncoder.DEFAULT); - p.addLast(new Socks5TerminalHandler()); - break; - case UNRESPONSIVE: - p.addLast(UnresponsiveHandler.INSTANCE); - break; - } - } - - boolean authenticate(ChannelHandlerContext ctx, Object msg) { - if (username == null) { - ctx.pipeline().replace(DECODER, DECODER, new Socks5CommandRequestDecoder()); - ctx.write(new DefaultSocks5InitialResponse(Socks5AuthMethod.NO_AUTH)); - return true; - } - - if (msg instanceof Socks5InitialRequest) { - ctx.pipeline().replace(DECODER, DECODER, new Socks5PasswordAuthRequestDecoder()); - ctx.write(new DefaultSocks5InitialResponse(Socks5AuthMethod.PASSWORD)); - return false; - } - - Socks5PasswordAuthRequest req = (Socks5PasswordAuthRequest) msg; - if (req.username().equals(username) && req.password().equals(password)) { - ctx.pipeline().replace(DECODER, DECODER, new Socks5CommandRequestDecoder()); - ctx.write(new DefaultSocks5PasswordAuthResponse(Socks5PasswordAuthStatus.SUCCESS)); - return true; - } - - ctx.pipeline().replace(DECODER, DECODER, new Socks5PasswordAuthRequestDecoder()); - ctx.write(new DefaultSocks5PasswordAuthResponse(Socks5PasswordAuthStatus.FAILURE)); - return false; - } - - private final class Socks5IntermediaryHandler extends IntermediaryHandler { - - private boolean authenticated; - private SocketAddress intermediaryDestination; - - @Override - protected boolean handleProxyProtocol(ChannelHandlerContext ctx, Object msg) throws Exception { - if (!authenticated) { - authenticated = authenticate(ctx, msg); - return false; - } - - Socks5CommandRequest req = (Socks5CommandRequest) msg; - assertThat(req.type(), is(Socks5CommandType.CONNECT)); - - Socks5CommandResponse res = - new DefaultSocks5CommandResponse(Socks5CommandStatus.SUCCESS, Socks5AddressType.IPv4); - intermediaryDestination = SocketUtils.socketAddress(req.dstAddr(), req.dstPort()); - - ctx.write(res); - - ctx.pipeline().remove(ENCODER); - ctx.pipeline().remove(DECODER); - - return true; - } - - @Override - protected SocketAddress intermediaryDestination() { - return intermediaryDestination; - } - } - - private final class Socks5TerminalHandler extends TerminalHandler { - - private boolean authenticated; - - @Override - protected boolean handleProxyProtocol(ChannelHandlerContext ctx, Object msg) throws Exception { - if (!authenticated) { - authenticated = authenticate(ctx, msg); - return false; - } - - Socks5CommandRequest req = (Socks5CommandRequest) msg; - assertThat(req.type(), is(Socks5CommandType.CONNECT)); - - ctx.pipeline().addBefore(ctx.name(), "lineDecoder", new LineBasedFrameDecoder(64, false, true)); - - Socks5CommandResponse res; - boolean sendGreeting = false; - if (!req.dstAddr().equals(destination.getHostString()) || - req.dstPort() != destination.getPort()) { - res = new DefaultSocks5CommandResponse(Socks5CommandStatus.FORBIDDEN, Socks5AddressType.IPv4); - } else { - res = new DefaultSocks5CommandResponse(Socks5CommandStatus.SUCCESS, Socks5AddressType.IPv4); - sendGreeting = true; - } - - ctx.write(res); - - ctx.pipeline().remove(ENCODER); - ctx.pipeline().remove(DECODER); - - if (sendGreeting) { - ctx.write(Unpooled.copiedBuffer("0\n", CharsetUtil.US_ASCII)); - } - - return true; - } - } -} diff --git a/handler-proxy/src/test/java/io/netty/handler/proxy/TestMode.java b/handler-proxy/src/test/java/io/netty/handler/proxy/TestMode.java deleted file mode 100644 index 2ce15bef3f..0000000000 --- a/handler-proxy/src/test/java/io/netty/handler/proxy/TestMode.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.proxy; - -enum TestMode { - INTERMEDIARY, - TERMINAL, - UNRESPONSIVE, -} diff --git a/handler-proxy/src/test/java/io/netty/handler/proxy/UnresponsiveHandler.java b/handler-proxy/src/test/java/io/netty/handler/proxy/UnresponsiveHandler.java deleted file mode 100644 index 4600d2ee99..0000000000 --- a/handler-proxy/src/test/java/io/netty/handler/proxy/UnresponsiveHandler.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.proxy; - -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; - -@Sharable -final class UnresponsiveHandler extends SimpleChannelInboundHandler { - - static final UnresponsiveHandler INSTANCE = new UnresponsiveHandler(); - - private UnresponsiveHandler() { } - - @Override - protected void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { - // Ignore - } -} diff --git a/handler/pom.xml b/handler/pom.xml deleted file mode 100644 index 1cd551b1b7..0000000000 --- a/handler/pom.xml +++ /dev/null @@ -1,102 +0,0 @@ - - - - - 4.0.0 - - io.netty - netty-parent - 5.0.0.Final-SNAPSHOT - - - netty-handler - jar - - - io.netty.handler - - - Netty/Handler - - - - ${project.groupId} - netty-common - ${project.version} - - - ${project.groupId} - netty-resolver - ${project.version} - - - ${project.groupId} - netty-buffer - ${project.version} - - - ${project.groupId} - netty-transport - ${project.version} - - - ${project.groupId} - netty-codec - ${project.version} - - - ${project.groupId} - ${tcnative.artifactId} - ${tcnative.classifier} - true - - - org.bouncycastle - bcpkix-jdk15on - true - - - org.eclipse.jetty.npn - npn-api - true - - - org.eclipse.jetty.alpn - alpn-api - true - - - ${conscrypt.groupId} - ${conscrypt.artifactId} - ${conscrypt.classifier} - true - - - org.mockito - mockito-core - - - - software.amazon.cryptools - AmazonCorrettoCryptoProvider - 1.1.0 - linux-x86_64 - test - - - - diff --git a/handler/src/main/java/io/netty/handler/ssl/ApplicationProtocolNames.java b/handler/src/main/java/io/net5/handler/ssl/ApplicationProtocolNames.java similarity index 97% rename from handler/src/main/java/io/netty/handler/ssl/ApplicationProtocolNames.java rename to handler/src/main/java/io/net5/handler/ssl/ApplicationProtocolNames.java index 306a7dff35..9394875ade 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ApplicationProtocolNames.java +++ b/handler/src/main/java/io/net5/handler/ssl/ApplicationProtocolNames.java @@ -14,7 +14,7 @@ * under the License. */ -package io.netty.handler.ssl; +package io.net5.handler.ssl; /** * Provides a set of protocol names used in ALPN and NPN. diff --git a/handler/src/main/java/io/net5/handler/ssl/SslHandler.java b/handler/src/main/java/io/net5/handler/ssl/SslHandler.java new file mode 100644 index 0000000000..409526f2fc --- /dev/null +++ b/handler/src/main/java/io/net5/handler/ssl/SslHandler.java @@ -0,0 +1,165 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.net5.handler.ssl; + +import io.net5.buffer.ByteBuf; +import io.net5.buffer.ByteBufAllocator; +import io.net5.buffer.ByteBufConvertible; +import io.net5.buffer.ByteBufUtil; +import io.net5.buffer.CompositeByteBuf; +import io.net5.buffer.Unpooled; +import io.net5.channel.AbstractCoalescingBufferQueue; +import io.net5.channel.Channel; +import io.net5.channel.ChannelConfig; +import io.net5.channel.ChannelException; +import io.net5.channel.ChannelHandler; +import io.net5.channel.ChannelHandlerContext; +import io.net5.channel.ChannelOption; +import io.net5.channel.ChannelOutboundBuffer; +import io.net5.channel.ChannelPipeline; +import io.net5.handler.codec.ByteToMessageDecoder; +import io.net5.handler.codec.DecoderException; +import io.net5.handler.codec.UnsupportedMessageTypeException; +import io.net5.util.ReferenceCountUtil; +import io.net5.util.concurrent.DefaultPromise; +import io.net5.util.concurrent.EventExecutor; +import io.net5.util.concurrent.Future; +import io.net5.util.concurrent.ImmediateEventExecutor; +import io.net5.util.concurrent.ImmediateExecutor; +import io.net5.util.concurrent.Promise; +import io.net5.util.internal.PlatformDependent; +import io.net5.util.internal.UnstableApi; +import io.net5.util.internal.logging.InternalLogger; +import io.net5.util.internal.logging.InternalLoggerFactory; + +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLEngineResult; +import javax.net.ssl.SSLEngineResult.HandshakeStatus; +import javax.net.ssl.SSLEngineResult.Status; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLHandshakeException; +import javax.net.ssl.SSLSession; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.DatagramChannel; +import java.nio.channels.SocketChannel; +import java.util.concurrent.Executor; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; + +import static io.net5.buffer.ByteBufUtil.ensureWritableSuccess; +import static io.net5.util.internal.ObjectUtil.checkPositiveOrZero; +import static java.util.Objects.requireNonNull; + +/** + * Adds SSL + * · TLS and StartTLS support to a {@link Channel}. Please refer + * to the "SecureChat" example in the distribution or the web + * site for the detailed usage. + * + *

Beginning the handshake

+ *

+ * Beside using the handshake {@link Future} to get notified about the completion of the handshake it's + * also possible to detect it by implement the + * {@link ChannelHandler#userEventTriggered(ChannelHandlerContext, Object)} + * method and check for a SslHandshakeCompletionEvent. + * + *

Handshake

+ *

+ * The handshake will be automatically issued for you once the {@link Channel} is active and + * {@link SSLEngine#getUseClientMode()} returns {@code true}. + * So no need to bother with it by your self. + * + *

Closing the session

+ *

+ * To close the SSL session, the 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, + * it is not reusable, and consequently you should create a new + * {@link SslHandler} with a new {@link SSLEngine} as explained in the + * following section. + * + *

Restarting the session

+ *

+ * To restart the SSL session, you must remove the existing closed + * {@link SslHandler} from the {@link ChannelPipeline}, insert a new + * {@link SslHandler} with a new {@link SSLEngine} into the pipeline, + * and start the handshake process as described in the first section. + * + *

Implementing StartTLS

+ *

+ * StartTLS is the + * communication pattern that secures the wire in the middle of the plaintext + * connection. Please note that it is different from SSL · TLS, that + * secures the wire from the beginning of the connection. Typically, StartTLS + * is composed of three steps: + *

    + *
  1. Client sends a StartTLS request to server.
  2. + *
  3. Server sends a StartTLS response to client.
  4. + *
  5. Client begins SSL handshake.
  6. + *
+ * If you implement a server, you need to: + *
    + *
  1. create a new {@link SslHandler} instance with {@code startTls} flag set + * to {@code true},
  2. + *
  3. insert the {@link SslHandler} to the {@link ChannelPipeline}, and
  4. + *
  5. write a StartTLS response.
  6. + *
+ * Please note that you must insert {@link SslHandler} before sending + * the StartTLS response. Otherwise the client can send begin SSL handshake + * before {@link SslHandler} is inserted to the {@link ChannelPipeline}, causing + * data corruption. + *

+ * The client-side implementation is much simpler. + *

    + *
  1. Write a StartTLS request,
  2. + *
  3. wait for the StartTLS response,
  4. + *
  5. create a new {@link SslHandler} instance with {@code startTls} flag set + * to {@code false},
  6. + *
  7. insert the {@link SslHandler} to the {@link ChannelPipeline}, and
  8. + *
  9. Initiate SSL handshake.
  10. + *
+ * + *

Known issues

+ *

+ * Because of a known issue with the current implementation of the SslEngine that comes + * with Java it may be possible that you see blocked IO-Threads while a full GC is done. + *

+ * So if you are affected you can workaround this problem by adjust the cache settings + * like shown below: + * + *

+ *     SslContext context = ...;
+ *     context.getServerSessionContext().setSessionCacheSize(someSaneSize);
+ *     context.getServerSessionContext().setSessionTime(someSameTimeout);
+ * 
+ *

+ * What values to use here depends on the nature of your application and should be set + * based on monitoring and debugging of it. + * For more details see + * #832 in our issue tracker. + */ +public class SslHandler extends ByteToMessageDecoder { + + @Override + protected void decode(ChannelHandlerContext ctx, ByteBuf in) { + throw new UnsupportedOperationException(); + } +} diff --git a/handler/src/main/java/io/netty/handler/ssl/package-info.java b/handler/src/main/java/io/net5/handler/ssl/package-info.java similarity index 96% rename from handler/src/main/java/io/netty/handler/ssl/package-info.java rename to handler/src/main/java/io/net5/handler/ssl/package-info.java index 583c63bd61..054d6f5c35 100644 --- a/handler/src/main/java/io/netty/handler/ssl/package-info.java +++ b/handler/src/main/java/io/net5/handler/ssl/package-info.java @@ -18,4 +18,4 @@ * SSL · * TLS implementation based on {@link javax.net.ssl.SSLEngine} */ -package io.netty.handler.ssl; +package io.net5.handler.ssl; diff --git a/handler/src/main/java/io/netty/handler/address/DynamicAddressConnectHandler.java b/handler/src/main/java/io/netty/handler/address/DynamicAddressConnectHandler.java deleted file mode 100644 index 33e9e785fb..0000000000 --- a/handler/src/main/java/io/netty/handler/address/DynamicAddressConnectHandler.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.address; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.concurrent.Future; -import java.net.NetworkInterface; -import java.net.SocketAddress; - -/** - * {@link ChannelHandler} implementation which allows to dynamically replace the used - * {@code remoteAddress} and / or {@code localAddress} when making a connection attempt. - *

- * This can be useful to for example bind to a specific {@link NetworkInterface} based on - * the {@code remoteAddress}. - */ -public abstract class DynamicAddressConnectHandler implements ChannelHandler { - - @Override - public final Future connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, - SocketAddress localAddress) { - final SocketAddress remote; - final SocketAddress local; - try { - remote = remoteAddress(remoteAddress, localAddress); - local = localAddress(remoteAddress, localAddress); - } catch (Exception e) { - return ctx.newFailedFuture(e); - } - return ctx.connect(remote, local).addListener(future -> { - if (future.isSuccess()) { - // We only remove this handler from the pipeline once the connect was successful as otherwise - // the user may try to connect again. - ctx.pipeline().remove(this); - } - }); - } - - /** - * Returns the local {@link SocketAddress} to use for - * {@link ChannelHandlerContext#connect(SocketAddress, SocketAddress)} based on the original {@code remoteAddress} - * and {@code localAddress}. - * By default, this method returns the given {@code localAddress}. - */ - protected SocketAddress localAddress( - @SuppressWarnings("unused") SocketAddress remoteAddress, SocketAddress localAddress) throws Exception { - return localAddress; - } - - /** - * Returns the remote {@link SocketAddress} to use for - * {@link ChannelHandlerContext#connect(SocketAddress, SocketAddress)} based on the original {@code remoteAddress} - * and {@code localAddress}. - * By default, this method returns the given {@code remoteAddress}. - */ - protected SocketAddress remoteAddress( - SocketAddress remoteAddress, @SuppressWarnings("unused") SocketAddress localAddress) throws Exception { - return remoteAddress; - } -} diff --git a/handler/src/main/java/io/netty/handler/address/ResolveAddressHandler.java b/handler/src/main/java/io/netty/handler/address/ResolveAddressHandler.java deleted file mode 100644 index 12b01f14d5..0000000000 --- a/handler/src/main/java/io/netty/handler/address/ResolveAddressHandler.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.address; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; -import io.netty.resolver.AddressResolver; -import io.netty.resolver.AddressResolverGroup; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.FutureListener; -import io.netty.util.concurrent.Promise; - -import java.net.SocketAddress; -import java.util.Objects; - -/** - * {@link ChannelHandler} which will resolve the {@link SocketAddress} that is passed to - * {@link #connect(ChannelHandlerContext, SocketAddress, SocketAddress)} if it is not already resolved - * and the {@link AddressResolver} supports the type of {@link SocketAddress}. - */ -@Sharable -public class ResolveAddressHandler implements ChannelHandler { - - private final AddressResolverGroup resolverGroup; - - public ResolveAddressHandler(AddressResolverGroup resolverGroup) { - this.resolverGroup = Objects.requireNonNull(resolverGroup, "resolverGroup"); - } - - @Override - public Future connect(final ChannelHandlerContext ctx, SocketAddress remoteAddress, - final SocketAddress localAddress) { - AddressResolver resolver = resolverGroup.getResolver(ctx.executor()); - if (resolver.isSupported(remoteAddress) && !resolver.isResolved(remoteAddress)) { - Promise promise = ctx.newPromise(); - resolver.resolve(remoteAddress).addListener((FutureListener) future -> { - Throwable cause = future.cause(); - if (cause != null) { - promise.setFailure(cause); - } else { - ctx.connect(future.getNow(), localAddress).cascadeTo(promise); - } - ctx.pipeline().remove(this); - }); - return promise.asFuture(); - } else { - Future f = ctx.connect(remoteAddress, localAddress); - ctx.pipeline().remove(this); - return f; - } - } -} diff --git a/handler/src/main/java/io/netty/handler/address/package-info.java b/handler/src/main/java/io/netty/handler/address/package-info.java deleted file mode 100644 index 1f83996aa0..0000000000 --- a/handler/src/main/java/io/netty/handler/address/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * Package to dynamically replace local / remote {@link java.net.SocketAddress}. - */ -package io.netty.handler.address; diff --git a/handler/src/main/java/io/netty/handler/flow/FlowControlHandler.java b/handler/src/main/java/io/netty/handler/flow/FlowControlHandler.java deleted file mode 100644 index bdb16867cf..0000000000 --- a/handler/src/main/java/io/netty/handler/flow/FlowControlHandler.java +++ /dev/null @@ -1,246 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.flow; - -import java.util.ArrayDeque; -import java.util.Queue; - -import io.netty.channel.ChannelConfig; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.ByteToMessageDecoder; -import io.netty.handler.codec.MessageToByteEncoder; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.internal.ObjectPool; -import io.netty.util.internal.ObjectPool.Handle; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -/** - * The {@link FlowControlHandler} ensures that only one message per {@code read()} is sent downstream. - * - * Classes such as {@link ByteToMessageDecoder} or {@link MessageToByteEncoder} are free to emit as - * many events as they like for any given input. A channel's auto reading configuration doesn't usually - * apply in these scenarios. This is causing problems in downstream {@link ChannelHandler}s that would - * like to hold subsequent events while they're processing one event. It's a common problem with the - * {@code HttpObjectDecoder} that will very often fire an {@code HttpRequest} that is immediately followed - * by a {@code LastHttpContent} event. - * - *

{@code
- * ChannelPipeline pipeline = ...;
- *
- * pipeline.addLast(new HttpServerCodec());
- * pipeline.addLast(new FlowControlHandler());
- *
- * pipeline.addLast(new MyExampleHandler());
- *
- * class MyExampleHandler extends ChannelInboundHandlerAdapter {
- *   @Override
- *   public void channelRead(ChannelHandlerContext ctx, Object msg) {
- *     if (msg instanceof HttpRequest) {
- *       ctx.channel().config().setAutoRead(false);
- *
- *       // The FlowControlHandler will hold any subsequent events that
- *       // were emitted by HttpObjectDecoder until auto reading is turned
- *       // back on or Channel#read() is being called.
- *     }
- *   }
- * }
- * }
- * - * @see ChannelConfig#setAutoRead(boolean) - */ -public class FlowControlHandler implements ChannelHandler { - private static final InternalLogger logger = InternalLoggerFactory.getInstance(FlowControlHandler.class); - - private final boolean releaseMessages; - - private RecyclableArrayDeque queue; - - private ChannelConfig config; - - private boolean shouldConsume; - - public FlowControlHandler() { - this(true); - } - - public FlowControlHandler(boolean releaseMessages) { - this.releaseMessages = releaseMessages; - } - - /** - * Determine if the underlying {@link Queue} is empty. This method exists for - * testing, debugging and inspection purposes and it is not Thread safe! - */ - boolean isQueueEmpty() { - return queue == null || queue.isEmpty(); - } - - /** - * Releases all messages and destroys the {@link Queue}. - */ - private void destroy() { - if (queue != null) { - - if (!queue.isEmpty()) { - logger.trace("Non-empty queue: {}", queue); - - if (releaseMessages) { - Object msg; - while ((msg = queue.poll()) != null) { - ReferenceCountUtil.safeRelease(msg); - } - } - } - - queue.recycle(); - queue = null; - } - } - - @Override - public void handlerAdded(ChannelHandlerContext ctx) throws Exception { - config = ctx.channel().config(); - } - - @Override - public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { - if (!isQueueEmpty()) { - dequeue(ctx, queue.size()); - } - destroy(); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - destroy(); - ctx.fireChannelInactive(); - } - - @Override - public void read(ChannelHandlerContext ctx) { - if (dequeue(ctx, 1) == 0) { - // It seems no messages were consumed. We need to read() some - // messages from upstream and once one arrives it need to be - // relayed to downstream to keep the flow going. - shouldConsume = true; - ctx.read(); - } - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - if (queue == null) { - queue = RecyclableArrayDeque.newInstance(); - } - - queue.offer(msg); - - // We just received one message. Do we need to relay it regardless - // of the auto reading configuration? The answer is yes if this - // method was called as a result of a prior read() call. - int minConsume = shouldConsume ? 1 : 0; - shouldConsume = false; - - dequeue(ctx, minConsume); - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { - if (isQueueEmpty()) { - ctx.fireChannelReadComplete(); - } else { - // Don't relay completion events from upstream as they - // make no sense in this context. See dequeue() where - // a new set of completion events is being produced. - } - } - - /** - * Dequeues one or many (or none) messages depending on the channel's auto - * reading state and returns the number of messages that were consumed from - * the internal queue. - * - * The {@code minConsume} argument is used to force {@code dequeue()} into - * consuming that number of messages regardless of the channel's auto - * reading configuration. - * - * @see #read(ChannelHandlerContext) - * @see #channelRead(ChannelHandlerContext, Object) - */ - private int dequeue(ChannelHandlerContext ctx, int minConsume) { - int consumed = 0; - - // fireChannelRead(...) may call ctx.read() and so this method may reentrance. Because of this we need to - // check if queue was set to null in the meantime and if so break the loop. - while (queue != null && (consumed < minConsume || config.isAutoRead())) { - Object msg = queue.poll(); - if (msg == null) { - break; - } - - ++consumed; - ctx.fireChannelRead(msg); - } - - // We're firing a completion event every time one (or more) - // messages were consumed and the queue ended up being drained - // to an empty state. - if (queue != null && queue.isEmpty()) { - queue.recycle(); - queue = null; - - if (consumed > 0) { - ctx.fireChannelReadComplete(); - } - } - - return consumed; - } - - /** - * A recyclable {@link ArrayDeque}. - */ - private static final class RecyclableArrayDeque extends ArrayDeque { - - private static final long serialVersionUID = 0L; - - /** - * A value of {@code 2} should be a good choice for most scenarios. - */ - private static final int DEFAULT_NUM_ELEMENTS = 2; - - private static final ObjectPool RECYCLER = ObjectPool.newPool( - handle -> new RecyclableArrayDeque(DEFAULT_NUM_ELEMENTS, handle)); - - public static RecyclableArrayDeque newInstance() { - return RECYCLER.get(); - } - - private final Handle handle; - - private RecyclableArrayDeque(int numElements, Handle handle) { - super(numElements); - this.handle = handle; - } - - public void recycle() { - clear(); - handle.recycle(this); - } - } -} diff --git a/handler/src/main/java/io/netty/handler/flow/package-info.java b/handler/src/main/java/io/netty/handler/flow/package-info.java deleted file mode 100644 index 285901fc64..0000000000 --- a/handler/src/main/java/io/netty/handler/flow/package-info.java +++ /dev/null @@ -1,20 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * Package to control the flow of messages. - */ -package io.netty.handler.flow; diff --git a/handler/src/main/java/io/netty/handler/flush/FlushConsolidationHandler.java b/handler/src/main/java/io/netty/handler/flush/FlushConsolidationHandler.java deleted file mode 100644 index aaea42c3a8..0000000000 --- a/handler/src/main/java/io/netty/handler/flush/FlushConsolidationHandler.java +++ /dev/null @@ -1,212 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.flush; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelOutboundInvoker; -import io.netty.channel.ChannelPipeline; -import io.netty.util.concurrent.Future; -import io.netty.util.internal.ObjectUtil; - -/** - * {@link ChannelHandler} which consolidates {@link Channel#flush()} / {@link ChannelHandlerContext#flush()} - * operations (which also includes - * {@link Channel#writeAndFlush(Object)} and - * {@link ChannelOutboundInvoker#writeAndFlush(Object)}. - *

- * Flush operations are generally speaking expensive as these may trigger a syscall on the transport level. Thus it is - * in most cases (where write latency can be traded with throughput) a good idea to try to minimize flush operations - * as much as possible. - *

- * If a read loop is currently ongoing, {@link ChannelHandler#flush(ChannelHandlerContext)} will not be passed on to - * the next {@link ChannelHandler} in the {@link ChannelPipeline}, as it will pick up any pending flushes when - * {@link #channelReadComplete(ChannelHandlerContext)} is triggered. - * If no read loop is ongoing, the behavior depends on the {@code consolidateWhenNoReadInProgress} constructor argument: - *

    - *
  • if {@code false}, flushes are passed on to the next handler directly;
  • - *
  • if {@code true}, the invocation of the next handler is submitted as a separate task on the event loop. Under - * 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 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. - *

- * The {@link FlushConsolidationHandler} should be put as first {@link ChannelHandler} in the - * {@link ChannelPipeline} to have the best effect. - */ -public class FlushConsolidationHandler implements ChannelHandler { - private final int explicitFlushAfterFlushes; - private final boolean consolidateWhenNoReadInProgress; - private final Runnable flushTask; - private int flushPendingCount; - private boolean readInProgress; - private ChannelHandlerContext ctx; - private Future nextScheduledFlush; - - /** - * 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(DEFAULT_EXPLICIT_FLUSH_AFTER_FLUSHES, false); - } - - /** - * Create new instance which doesn't consolidate flushes when no read is in progress. - * - * @param explicitFlushAfterFlushes the number of flushes after which an explicit flush will be done. - */ - public FlushConsolidationHandler(int explicitFlushAfterFlushes) { - this(explicitFlushAfterFlushes, false); - } - - /** - * Create new instance. - * - * @param explicitFlushAfterFlushes the number of flushes after which an explicit flush will be done. - * @param consolidateWhenNoReadInProgress whether to consolidate flushes even when no read loop is currently - * ongoing. - */ - public FlushConsolidationHandler(int explicitFlushAfterFlushes, boolean consolidateWhenNoReadInProgress) { - this.explicitFlushAfterFlushes = - ObjectUtil.checkPositive(explicitFlushAfterFlushes, "explicitFlushAfterFlushes"); - this.consolidateWhenNoReadInProgress = consolidateWhenNoReadInProgress; - flushTask = consolidateWhenNoReadInProgress ? - () -> { - if (flushPendingCount > 0 && !readInProgress) { - flushPendingCount = 0; - nextScheduledFlush = null; - ctx.flush(); - } // else we'll flush when the read completes - } - : null; - } - - @Override - public void handlerAdded(ChannelHandlerContext ctx) throws Exception { - this.ctx = ctx; - } - - @Override - public void flush(ChannelHandlerContext ctx) { - if (readInProgress) { - // If there is still a read in progress we are sure we will see a channelReadComplete(...) call. Thus - // we only need to flush if we reach the explicitFlushAfterFlushes limit. - if (++flushPendingCount == explicitFlushAfterFlushes) { - flushNow(ctx); - } - } else if (consolidateWhenNoReadInProgress) { - // Flush immediately if we reach the threshold, otherwise schedule - if (++flushPendingCount == explicitFlushAfterFlushes) { - flushNow(ctx); - } else { - scheduleFlush(ctx); - } - } else { - // Always flush directly - flushNow(ctx); - } - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { - // This may be the last event in the read loop, so flush now! - resetReadAndFlushIfNeeded(ctx); - ctx.fireChannelReadComplete(); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - readInProgress = true; - ctx.fireChannelRead(msg); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - // To ensure we not miss to flush anything, do it now. - resetReadAndFlushIfNeeded(ctx); - ctx.fireExceptionCaught(cause); - } - - @Override - public Future disconnect(ChannelHandlerContext ctx) { - // Try to flush one last time if flushes are pending before disconnect the channel. - resetReadAndFlushIfNeeded(ctx); - return ctx.disconnect(); - } - - @Override - public Future close(ChannelHandlerContext ctx) { - // Try to flush one last time if flushes are pending before close the channel. - resetReadAndFlushIfNeeded(ctx); - return ctx.close(); - } - - @Override - public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception { - if (!ctx.channel().isWritable()) { - // The writability of the channel changed to false, so flush all consolidated flushes now to free up memory. - flushIfNeeded(ctx); - } - ctx.fireChannelWritabilityChanged(); - } - - @Override - public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { - flushIfNeeded(ctx); - } - - private void resetReadAndFlushIfNeeded(ChannelHandlerContext ctx) { - readInProgress = false; - flushIfNeeded(ctx); - } - - private void flushIfNeeded(ChannelHandlerContext ctx) { - if (flushPendingCount > 0) { - flushNow(ctx); - } - } - - private void flushNow(ChannelHandlerContext ctx) { - cancelScheduledFlush(); - flushPendingCount = 0; - ctx.flush(); - } - - private void scheduleFlush(final ChannelHandlerContext ctx) { - if (nextScheduledFlush == null) { - // Run as soon as possible, but still yield to give a chance for additional writes to enqueue. - nextScheduledFlush = ctx.channel().executor().submit(flushTask); - } - } - - private void cancelScheduledFlush() { - if (nextScheduledFlush != null) { - nextScheduledFlush.cancel(); - nextScheduledFlush = null; - } - } -} diff --git a/handler/src/main/java/io/netty/handler/flush/package-info.java b/handler/src/main/java/io/netty/handler/flush/package-info.java deleted file mode 100644 index 8dc2070f18..0000000000 --- a/handler/src/main/java/io/netty/handler/flush/package-info.java +++ /dev/null @@ -1,20 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * Package to control flush behavior. - */ -package io.netty.handler.flush; diff --git a/handler/src/main/java/io/netty/handler/ipfilter/AbstractRemoteAddressFilter.java b/handler/src/main/java/io/netty/handler/ipfilter/AbstractRemoteAddressFilter.java deleted file mode 100644 index 4c0eb0aae2..0000000000 --- a/handler/src/main/java/io/netty/handler/ipfilter/AbstractRemoteAddressFilter.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ipfilter; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelFutureListeners; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.concurrent.Future; - -import java.net.SocketAddress; - -/** - * This class provides the functionality to either accept or reject new {@link Channel}s - * based on their IP address. - *

- * You should inherit from this class if you would like to implement your own IP-based filter. Basically you have to - * implement {@link #accept(ChannelHandlerContext, SocketAddress)} to decided whether you want to accept or reject - * a connection from the remote address. - *

- * Furthermore overriding {@link #channelRejected(ChannelHandlerContext, SocketAddress)} gives you the - * flexibility to respond to rejected (denied) connections. If you do not want to send a response, just have it return - * null. Take a look at {@link RuleBasedIpFilter} for details. - */ -public abstract class AbstractRemoteAddressFilter implements ChannelHandler { - - @Override - public void channelRegistered(ChannelHandlerContext ctx) throws Exception { - handleNewChannel(ctx, true); - } - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - if (!handleNewChannel(ctx, false)) { - throw new IllegalStateException("cannot determine to accept or reject a channel: " + ctx.channel()); - } - } - - private boolean handleNewChannel(ChannelHandlerContext ctx, boolean register) throws Exception { - @SuppressWarnings("unchecked") - T remoteAddress = (T) ctx.channel().remoteAddress(); - boolean remove = false; - try { - // If the remote address is not available yet, defer the decision. - if (remoteAddress == null) { - return false; - } - - if (accept(ctx, remoteAddress)) { - channelAccepted(ctx, remoteAddress); - remove = true; - } else { - Future rejectedFuture = channelRejected(ctx, remoteAddress); - if (rejectedFuture != null && !rejectedFuture.isDone()) { - rejectedFuture.addListener(ctx, ChannelFutureListeners.CLOSE); - } else { - ctx.close(); - } - } - return true; - } finally { - if (!ctx.isRemoved()) { - if (register) { - ctx.fireChannelRegistered(); - } else { - ctx.fireChannelActive(); - } - if (remove) { - // No need to keep this handler in the pipeline anymore because the decision is going to be made - // now. Also, this will prevent the subsequent events from being handled by this handler. - ctx.pipeline().remove(this); - } - } - } - } - - /** - * This method is called immediately after a {@link io.netty.channel.Channel} gets registered. - * - * @return Return true if connections from this IP address and port should be accepted. False otherwise. - */ - protected abstract boolean accept(ChannelHandlerContext ctx, T remoteAddress) throws Exception; - - /** - * This method is called if {@code remoteAddress} gets accepted by - * {@link #accept(ChannelHandlerContext, SocketAddress)}. You should override it if you would like to handle - * (e.g. respond to) accepted addresses. - */ - @SuppressWarnings("UnusedParameters") - protected void channelAccepted(ChannelHandlerContext ctx, T remoteAddress) { } - - /** - * This method is called if {@code remoteAddress} gets rejected by - * {@link #accept(ChannelHandlerContext, SocketAddress)}. You should override it if you would like to handle - * (e.g. respond to) rejected addresses. - * - * @return A {@link Future} if you perform I/O operations, so that - * the {@link Channel} can be closed once it completes. Null otherwise. - */ - @SuppressWarnings("UnusedParameters") - protected Future channelRejected(ChannelHandlerContext ctx, T remoteAddress) { - return null; - } -} diff --git a/handler/src/main/java/io/netty/handler/ipfilter/IpFilterRule.java b/handler/src/main/java/io/netty/handler/ipfilter/IpFilterRule.java deleted file mode 100644 index 51b201368d..0000000000 --- a/handler/src/main/java/io/netty/handler/ipfilter/IpFilterRule.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ipfilter; - -import java.net.InetSocketAddress; - -/** - * Implement this interface to create new rules. - */ -public interface IpFilterRule { - /** - * @return This method should return true if remoteAddress is valid according to your criteria. False otherwise. - */ - boolean matches(InetSocketAddress remoteAddress); - - /** - * @return This method should return {@link IpFilterRuleType#ACCEPT} if all - * {@link IpFilterRule#matches(InetSocketAddress)} for which {@link #matches(InetSocketAddress)} - * returns true should the accepted. If you want to exclude all of those IP addresses then - * {@link IpFilterRuleType#REJECT} should be returned. - */ - IpFilterRuleType ruleType(); -} diff --git a/handler/src/main/java/io/netty/handler/ipfilter/IpFilterRuleType.java b/handler/src/main/java/io/netty/handler/ipfilter/IpFilterRuleType.java deleted file mode 100644 index 64ce9beb1b..0000000000 --- a/handler/src/main/java/io/netty/handler/ipfilter/IpFilterRuleType.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ipfilter; - -/** - * Used in {@link IpFilterRule} to decide if a matching IP Address should be allowed or denied to connect. - */ -public enum IpFilterRuleType { - ACCEPT, - REJECT -} diff --git a/handler/src/main/java/io/netty/handler/ipfilter/IpSubnetFilter.java b/handler/src/main/java/io/netty/handler/ipfilter/IpSubnetFilter.java deleted file mode 100644 index 15d19df9b7..0000000000 --- a/handler/src/main/java/io/netty/handler/ipfilter/IpSubnetFilter.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ipfilter; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.internal.ObjectUtil; - -import java.net.Inet4Address; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; - -/** - *

- * This class allows one to filter new {@link Channel}s based on the - * {@link IpSubnetFilter}s passed to its constructor. If no rules are provided, all connections - * will be accepted since {@code acceptIfNotFound} is {@code true} by default. - *

- * - *

- * If you would like to explicitly take action on rejected {@link Channel}s, you should override - * {@link AbstractRemoteAddressFilter#channelRejected(ChannelHandlerContext, SocketAddress)}. - *

- * - *

- * Few Points to keep in mind: - *

    - *
  1. Since {@link IpSubnetFilter} uses Binary search algorithm, it's a good - * idea to insert IP addresses in incremental order.
  2. - *
  3. Remove any over-lapping CIDR.
  4. - *
- *

- * - */ -@Sharable -public class IpSubnetFilter extends AbstractRemoteAddressFilter { - - private final boolean acceptIfNotFound; - private final List ipv4Rules; - private final List ipv6Rules; - private final IpFilterRuleType ipFilterRuleTypeIPv4; - private final IpFilterRuleType ipFilterRuleTypeIPv6; - - /** - *

Create new {@link IpSubnetFilter} Instance with specified {@link IpSubnetFilterRule} as array.

- *

{@code acceptIfNotFound} is set to {@code true}.

- * - * @param rules {@link IpSubnetFilterRule} as an array - */ - public IpSubnetFilter(IpSubnetFilterRule... rules) { - this(true, Arrays.asList(ObjectUtil.checkNotNull(rules, "rules"))); - } - - /** - *

Create new {@link IpSubnetFilter} Instance with specified {@link IpSubnetFilterRule} as array - * and specify if we'll accept a connection if we don't find it in the rule(s).

- * - * @param acceptIfNotFound {@code true} if we'll accept connection if not found in rule(s). - * @param rules {@link IpSubnetFilterRule} as an array - */ - public IpSubnetFilter(boolean acceptIfNotFound, IpSubnetFilterRule... rules) { - this(acceptIfNotFound, Arrays.asList(ObjectUtil.checkNotNull(rules, "rules"))); - } - - /** - *

Create new {@link IpSubnetFilter} Instance with specified {@link IpSubnetFilterRule} as {@link List}.

- *

{@code acceptIfNotFound} is set to {@code true}.

- * - * @param rules {@link IpSubnetFilterRule} as a {@link List} - */ - public IpSubnetFilter(List rules) { - this(true, rules); - } - - /** - *

Create new {@link IpSubnetFilter} Instance with specified {@link IpSubnetFilterRule} as {@link List} - * and specify if we'll accept a connection if we don't find it in the rule(s).

- * - * @param acceptIfNotFound {@code true} if we'll accept connection if not found in rule(s). - * @param rules {@link IpSubnetFilterRule} as a {@link List} - */ - public IpSubnetFilter(boolean acceptIfNotFound, List rules) { - ObjectUtil.checkNotNull(rules, "rules"); - this.acceptIfNotFound = acceptIfNotFound; - - int numAcceptIPv4 = 0; - int numRejectIPv4 = 0; - int numAcceptIPv6 = 0; - int numRejectIPv6 = 0; - - List unsortedIPv4Rules = new ArrayList(); - List unsortedIPv6Rules = new ArrayList(); - - // Iterate over rules and check for `null` rule. - for (IpSubnetFilterRule ipSubnetFilterRule : rules) { - ObjectUtil.checkNotNull(ipSubnetFilterRule, "rule"); - - if (ipSubnetFilterRule.getFilterRule() instanceof IpSubnetFilterRule.Ip4SubnetFilterRule) { - unsortedIPv4Rules.add(ipSubnetFilterRule); - - if (ipSubnetFilterRule.ruleType() == IpFilterRuleType.ACCEPT) { - numAcceptIPv4++; - } else { - numRejectIPv4++; - } - } else { - unsortedIPv6Rules.add(ipSubnetFilterRule); - - if (ipSubnetFilterRule.ruleType() == IpFilterRuleType.ACCEPT) { - numAcceptIPv6++; - } else { - numRejectIPv6++; - } - } - } - - /* - * If Number of ACCEPT rule is 0 and number of REJECT rules is more than 0, - * then all rules are of "REJECT" type. - * - * In this case, we'll set `ipFilterRuleTypeIPv4` to `IpFilterRuleType.REJECT`. - * - * If Number of ACCEPT rules are more than 0 and number of REJECT rules is 0, - * then all rules are of "ACCEPT" type. - * - * In this case, we'll set `ipFilterRuleTypeIPv4` to `IpFilterRuleType.ACCEPT`. - */ - if (numAcceptIPv4 == 0 && numRejectIPv4 > 0) { - ipFilterRuleTypeIPv4 = IpFilterRuleType.REJECT; - } else if (numAcceptIPv4 > 0 && numRejectIPv4 == 0) { - ipFilterRuleTypeIPv4 = IpFilterRuleType.ACCEPT; - } else { - ipFilterRuleTypeIPv4 = null; - } - - if (numAcceptIPv6 == 0 && numRejectIPv6 > 0) { - ipFilterRuleTypeIPv6 = IpFilterRuleType.REJECT; - } else if (numAcceptIPv6 > 0 && numRejectIPv6 == 0) { - ipFilterRuleTypeIPv6 = IpFilterRuleType.ACCEPT; - } else { - ipFilterRuleTypeIPv6 = null; - } - - this.ipv4Rules = sortAndFilter(unsortedIPv4Rules); - this.ipv6Rules = sortAndFilter(unsortedIPv6Rules); - } - - @Override - protected boolean accept(ChannelHandlerContext ctx, InetSocketAddress remoteAddress) { - if (remoteAddress.getAddress() instanceof Inet4Address) { - int indexOf = Collections.binarySearch(ipv4Rules, remoteAddress, IpSubnetFilterRuleComparator.INSTANCE); - if (indexOf >= 0) { - if (ipFilterRuleTypeIPv4 == null) { - return ipv4Rules.get(indexOf).ruleType() == IpFilterRuleType.ACCEPT; - } else { - return ipFilterRuleTypeIPv4 == IpFilterRuleType.ACCEPT; - } - } - } else { - int indexOf = Collections.binarySearch(ipv6Rules, remoteAddress, IpSubnetFilterRuleComparator.INSTANCE); - if (indexOf >= 0) { - if (ipFilterRuleTypeIPv6 == null) { - return ipv6Rules.get(indexOf).ruleType() == IpFilterRuleType.ACCEPT; - } else { - return ipFilterRuleTypeIPv6 == IpFilterRuleType.ACCEPT; - } - } - } - - return acceptIfNotFound; - } - - /** - *
    - *
  1. Sort the list
  2. - *
  3. Remove over-lapping subnet
  4. - *
  5. Sort the list again
  6. - *
- */ - @SuppressWarnings("ConstantConditions") - private static List sortAndFilter(List rules) { - Collections.sort(rules); - Iterator iterator = rules.iterator(); - List toKeep = new ArrayList(); - - IpSubnetFilterRule parentRule = iterator.hasNext() ? iterator.next() : null; - if (parentRule != null) { - toKeep.add(parentRule); - } - - while (iterator.hasNext()) { - - // Grab a potential child rule. - IpSubnetFilterRule childRule = iterator.next(); - - // If parentRule matches childRule, then there's no need to keep the child rule. - // Otherwise, the rules are distinct and we need both. - if (!parentRule.matches(new InetSocketAddress(childRule.getIpAddress(), 1))) { - toKeep.add(childRule); - // Then we'll keep the child rule around as the parent for the next round. - parentRule = childRule; - } - } - - return toKeep; - } -} diff --git a/handler/src/main/java/io/netty/handler/ipfilter/IpSubnetFilterRule.java b/handler/src/main/java/io/netty/handler/ipfilter/IpSubnetFilterRule.java deleted file mode 100644 index defc90d2d9..0000000000 --- a/handler/src/main/java/io/netty/handler/ipfilter/IpSubnetFilterRule.java +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ipfilter; - -import static java.util.Objects.requireNonNull; - -import io.netty.util.NetUtil; -import io.netty.util.internal.SocketUtils; - -import java.math.BigInteger; -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.UnknownHostException; - -/** - * Use this class to create rules for {@link RuleBasedIpFilter} that group IP addresses into subnets. - * Supports both, IPv4 and IPv6. - */ -public final class IpSubnetFilterRule implements IpFilterRule, Comparable { - - private final IpFilterRule filterRule; - private final String ipAddress; - - public IpSubnetFilterRule(String ipAddress, int cidrPrefix, IpFilterRuleType ruleType) { - try { - this.ipAddress = ipAddress; - filterRule = selectFilterRule(SocketUtils.addressByName(ipAddress), cidrPrefix, ruleType); - } catch (UnknownHostException e) { - throw new IllegalArgumentException("ipAddress", e); - } - } - - public IpSubnetFilterRule(InetAddress ipAddress, int cidrPrefix, IpFilterRuleType ruleType) { - this.ipAddress = ipAddress.getHostAddress(); - filterRule = selectFilterRule(ipAddress, cidrPrefix, ruleType); - } - - private static IpFilterRule selectFilterRule(InetAddress ipAddress, int cidrPrefix, IpFilterRuleType ruleType) { - requireNonNull(ipAddress, "ipAddress"); - requireNonNull(ruleType, "ruleType"); - - if (ipAddress instanceof Inet4Address) { - return new Ip4SubnetFilterRule((Inet4Address) ipAddress, cidrPrefix, ruleType); - } else if (ipAddress instanceof Inet6Address) { - return new Ip6SubnetFilterRule((Inet6Address) ipAddress, cidrPrefix, ruleType); - } else { - throw new IllegalArgumentException("Only IPv4 and IPv6 addresses are supported"); - } - } - - @Override - public boolean matches(InetSocketAddress remoteAddress) { - return filterRule.matches(remoteAddress); - } - - @Override - public IpFilterRuleType ruleType() { - return filterRule.ruleType(); - } - - /** - * Get IP Address of this rule - */ - String getIpAddress() { - return ipAddress; - } - - /** - * {@link Ip4SubnetFilterRule} or {@link Ip6SubnetFilterRule} - */ - IpFilterRule getFilterRule() { - return filterRule; - } - - @Override - public int compareTo(IpSubnetFilterRule ipSubnetFilterRule) { - if (filterRule instanceof Ip4SubnetFilterRule) { - return compareInt(((Ip4SubnetFilterRule) filterRule).networkAddress, - ((Ip4SubnetFilterRule) ipSubnetFilterRule.filterRule).networkAddress); - } else { - return ((Ip6SubnetFilterRule) filterRule).networkAddress - .compareTo(((Ip6SubnetFilterRule) ipSubnetFilterRule.filterRule).networkAddress); - } - } - - /** - * It'll compare IP address with {@link Ip4SubnetFilterRule#networkAddress} or - * {@link Ip6SubnetFilterRule#networkAddress}. - * - * @param inetSocketAddress {@link InetSocketAddress} to match - * @return 0 if IP Address match else difference index. - */ - int compareTo(InetSocketAddress inetSocketAddress) { - if (filterRule instanceof Ip4SubnetFilterRule) { - Ip4SubnetFilterRule ip4SubnetFilterRule = (Ip4SubnetFilterRule) filterRule; - return compareInt(ip4SubnetFilterRule.networkAddress, NetUtil.ipv4AddressToInt((Inet4Address) - inetSocketAddress.getAddress()) & ip4SubnetFilterRule.subnetMask); - } else { - Ip6SubnetFilterRule ip6SubnetFilterRule = (Ip6SubnetFilterRule) filterRule; - return ip6SubnetFilterRule.networkAddress - .compareTo(Ip6SubnetFilterRule.ipToInt((Inet6Address) inetSocketAddress.getAddress()) - .and(ip6SubnetFilterRule.networkAddress)); - } - } - - /** - * Equivalent to {@link Integer#compare(int, int)} - */ - private static int compareInt(int x, int y) { - return (x < y) ? -1 : ((x == y) ? 0 : 1); - } - - static final class Ip4SubnetFilterRule implements IpFilterRule { - - private final int networkAddress; - private final int subnetMask; - private final IpFilterRuleType ruleType; - - private Ip4SubnetFilterRule(Inet4Address ipAddress, int cidrPrefix, IpFilterRuleType ruleType) { - if (cidrPrefix < 0 || cidrPrefix > 32) { - throw new IllegalArgumentException(String.format("IPv4 requires the subnet prefix to be in range of " - + "[0,32]. The prefix was: %d", cidrPrefix)); - } - - subnetMask = prefixToSubnetMask(cidrPrefix); - networkAddress = NetUtil.ipv4AddressToInt(ipAddress) & subnetMask; - this.ruleType = ruleType; - } - - @Override - public boolean matches(InetSocketAddress remoteAddress) { - final InetAddress inetAddress = remoteAddress.getAddress(); - if (inetAddress instanceof Inet4Address) { - int ipAddress = NetUtil.ipv4AddressToInt((Inet4Address) inetAddress); - return (ipAddress & subnetMask) == networkAddress; - } - return false; - } - - @Override - public IpFilterRuleType ruleType() { - return ruleType; - } - - private static int prefixToSubnetMask(int cidrPrefix) { - /* - * Perform the shift on a long and downcast it to int afterwards. - * This is necessary to handle a cidrPrefix of zero correctly. - * The left shift operator on an int only uses the five least - * significant bits of the right-hand operand. Thus -1 << 32 evaluates - * to -1 instead of 0. The left shift operator applied on a long - * uses the six least significant bits. - * - * Also see https://github.com/netty/netty/issues/2767 - */ - return (int) (-1L << 32 - cidrPrefix); - } - } - - static final class Ip6SubnetFilterRule implements IpFilterRule { - - private static final BigInteger MINUS_ONE = BigInteger.valueOf(-1); - - private final BigInteger networkAddress; - private final BigInteger subnetMask; - private final IpFilterRuleType ruleType; - - private Ip6SubnetFilterRule(Inet6Address ipAddress, int cidrPrefix, IpFilterRuleType ruleType) { - if (cidrPrefix < 0 || cidrPrefix > 128) { - throw new IllegalArgumentException(String.format("IPv6 requires the subnet prefix to be in range of " - + "[0,128]. The prefix was: %d", cidrPrefix)); - } - - subnetMask = prefixToSubnetMask(cidrPrefix); - networkAddress = ipToInt(ipAddress).and(subnetMask); - this.ruleType = ruleType; - } - - @Override - public boolean matches(InetSocketAddress remoteAddress) { - final InetAddress inetAddress = remoteAddress.getAddress(); - if (inetAddress instanceof Inet6Address) { - BigInteger ipAddress = ipToInt((Inet6Address) inetAddress); - return ipAddress.and(subnetMask).equals(subnetMask) || ipAddress.and(subnetMask).equals(networkAddress); - } - return false; - } - - @Override - public IpFilterRuleType ruleType() { - return ruleType; - } - - private static BigInteger ipToInt(Inet6Address ipAddress) { - byte[] octets = ipAddress.getAddress(); - assert octets.length == 16; - - return new BigInteger(octets); - } - - private static BigInteger prefixToSubnetMask(int cidrPrefix) { - return MINUS_ONE.shiftLeft(128 - cidrPrefix); - } - } -} diff --git a/handler/src/main/java/io/netty/handler/ipfilter/IpSubnetFilterRuleComparator.java b/handler/src/main/java/io/netty/handler/ipfilter/IpSubnetFilterRuleComparator.java deleted file mode 100644 index 35bf2bf3b2..0000000000 --- a/handler/src/main/java/io/netty/handler/ipfilter/IpSubnetFilterRuleComparator.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ipfilter; - -import java.net.InetSocketAddress; -import java.util.Comparator; - -/** - * This comparator is only used for searching. - */ -final class IpSubnetFilterRuleComparator implements Comparator { - - static final IpSubnetFilterRuleComparator INSTANCE = new IpSubnetFilterRuleComparator(); - - private IpSubnetFilterRuleComparator() { - // Prevent outside initialization - } - - @Override - public int compare(Object o1, Object o2) { - return ((IpSubnetFilterRule) o1).compareTo((InetSocketAddress) o2); - } -} diff --git a/handler/src/main/java/io/netty/handler/ipfilter/RuleBasedIpFilter.java b/handler/src/main/java/io/netty/handler/ipfilter/RuleBasedIpFilter.java deleted file mode 100644 index 35a281a41a..0000000000 --- a/handler/src/main/java/io/netty/handler/ipfilter/RuleBasedIpFilter.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ipfilter; - -import static java.util.Objects.requireNonNull; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; - -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.util.ArrayList; -import java.util.List; - -/** - *

- * This class allows one to filter new {@link Channel}s based on the - * {@link IpFilterRule}s passed to its constructor. If no rules are provided, all connections - * will be accepted. - *

- * - *

- * If you would like to explicitly take action on rejected {@link Channel}s, you should override - * {@link AbstractRemoteAddressFilter#channelRejected(ChannelHandlerContext, SocketAddress)}. - *

- * - *

Consider using {@link IpSubnetFilter} for better performance while not as - * general purpose as this filter.

- */ -@Sharable -public class RuleBasedIpFilter extends AbstractRemoteAddressFilter { - - private final boolean acceptIfNotFound; - private final List rules; - - /** - *

Create new Instance of {@link RuleBasedIpFilter} and filter incoming connections - * based on their IP address and {@code rules} applied.

- * - *

{@code acceptIfNotFound} is set to {@code true}.

- * - * @param rules An array of {@link IpFilterRule} containing all rules. - */ - public RuleBasedIpFilter(IpFilterRule... rules) { - this(true, rules); - } - - /** - * Create new Instance of {@link RuleBasedIpFilter} and filter incoming connections - * based on their IP address and {@code rules} applied. - * - * @param acceptIfNotFound If {@code true} then accept connection from IP Address if it - * doesn't match any rule. - * @param rules An array of {@link IpFilterRule} containing all rules. - */ - public RuleBasedIpFilter(boolean acceptIfNotFound, IpFilterRule... rules) { - requireNonNull(rules, "rules"); - - this.acceptIfNotFound = acceptIfNotFound; - this.rules = new ArrayList<>(rules.length); - - for (IpFilterRule rule : rules) { - if (rule != null) { - this.rules.add(rule); - } - } - } - - @Override - protected boolean accept(ChannelHandlerContext ctx, InetSocketAddress remoteAddress) throws Exception { - for (IpFilterRule rule : rules) { - if (rule.matches(remoteAddress)) { - return rule.ruleType() == IpFilterRuleType.ACCEPT; - } - } - - return acceptIfNotFound; - } -} diff --git a/handler/src/main/java/io/netty/handler/ipfilter/UniqueIpFilter.java b/handler/src/main/java/io/netty/handler/ipfilter/UniqueIpFilter.java deleted file mode 100644 index 851438bb5f..0000000000 --- a/handler/src/main/java/io/netty/handler/ipfilter/UniqueIpFilter.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ipfilter; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; - -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -/** - * This class allows one to ensure that at all times for every IP address there is at most one - * {@link Channel} connected to the server. - */ -@ChannelHandler.Sharable -public class UniqueIpFilter extends AbstractRemoteAddressFilter { - - private final Set connected = ConcurrentHashMap.newKeySet(); - - @Override - protected boolean accept(ChannelHandlerContext ctx, InetSocketAddress remoteAddress) { - final InetAddress remoteIp = remoteAddress.getAddress(); - if (!connected.add(remoteIp)) { - return false; - } else { - ctx.channel().closeFuture().addListener(future -> connected.remove(remoteIp)); - return true; - } - } -} diff --git a/handler/src/main/java/io/netty/handler/ipfilter/package-info.java b/handler/src/main/java/io/netty/handler/ipfilter/package-info.java deleted file mode 100644 index 9c1fde3250..0000000000 --- a/handler/src/main/java/io/netty/handler/ipfilter/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * Package to filter IP addresses (allow/deny). - */ -package io.netty.handler.ipfilter; diff --git a/handler/src/main/java/io/netty/handler/logging/ByteBufFormat.java b/handler/src/main/java/io/netty/handler/logging/ByteBufFormat.java deleted file mode 100644 index 11950c8793..0000000000 --- a/handler/src/main/java/io/netty/handler/logging/ByteBufFormat.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.logging; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufHolder; -import io.netty.buffer.ByteBufUtil; - -/** - * Used to control the format and verbosity of logging for {@link ByteBuf}s and {@link ByteBufHolder}s. - * - * @see LoggingHandler - */ -public enum ByteBufFormat { - /** - * {@link ByteBuf}s will be logged in a simple format, with no hex dump included. - */ - SIMPLE, - /** - * {@link ByteBuf}s will be logged using {@link ByteBufUtil#appendPrettyHexDump(StringBuilder, ByteBuf)}. - */ - HEX_DUMP -} diff --git a/handler/src/main/java/io/netty/handler/logging/LogLevel.java b/handler/src/main/java/io/netty/handler/logging/LogLevel.java deleted file mode 100644 index 7e59e60b3a..0000000000 --- a/handler/src/main/java/io/netty/handler/logging/LogLevel.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.logging; - -import io.netty.util.internal.logging.InternalLogLevel; - -/** - * Maps the regular {@link LogLevel}s with the {@link InternalLogLevel} ones. - */ -public enum LogLevel { - TRACE(InternalLogLevel.TRACE), - DEBUG(InternalLogLevel.DEBUG), - INFO(InternalLogLevel.INFO), - WARN(InternalLogLevel.WARN), - ERROR(InternalLogLevel.ERROR); - - private final InternalLogLevel internalLevel; - - LogLevel(InternalLogLevel internalLevel) { - this.internalLevel = internalLevel; - } - - /** - * For internal use only. - * - *

Converts the specified {@link LogLevel} to its {@link InternalLogLevel} variant. - * - * @return the converted level. - */ - public InternalLogLevel toInternalLevel() { - return internalLevel; - } -} diff --git a/handler/src/main/java/io/netty/handler/logging/LoggingHandler.java b/handler/src/main/java/io/netty/handler/logging/LoggingHandler.java deleted file mode 100644 index 1613f333da..0000000000 --- a/handler/src/main/java/io/netty/handler/logging/LoggingHandler.java +++ /dev/null @@ -1,426 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.logging; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufConvertible; -import io.netty.buffer.ByteBufHolder; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.concurrent.Future; -import io.netty.util.internal.logging.InternalLogLevel; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.net.SocketAddress; - -import static io.netty.buffer.ByteBufUtil.appendPrettyHexDump; -import static io.netty.util.internal.StringUtil.NEWLINE; -import static java.util.Objects.requireNonNull; - -/** - * A {@link ChannelHandler} that logs all events using a logging framework. - * By default, all events are logged at DEBUG level and full hex dumps are recorded for ByteBufs. - */ -@Sharable -@SuppressWarnings({ "StringBufferReplaceableByString" }) -public class LoggingHandler implements ChannelHandler { - - private static final LogLevel DEFAULT_LEVEL = LogLevel.DEBUG; - - protected final InternalLogger logger; - protected final InternalLogLevel internalLevel; - - private final LogLevel level; - private final ByteBufFormat byteBufFormat; - - /** - * Creates a new instance whose logger name is the fully qualified class - * name of the instance with hex dump enabled. - */ - public LoggingHandler() { - this(DEFAULT_LEVEL); - } - /** - * Creates a new instance whose logger name is the fully qualified class - * name of the instance. - * - * @param format Format of ByteBuf dumping - */ - public LoggingHandler(ByteBufFormat format) { - this(DEFAULT_LEVEL, format); - } - - /** - * Creates a new instance whose logger name is the fully qualified class - * name of the instance. - * - * @param level the log level - */ - public LoggingHandler(LogLevel level) { - this(level, ByteBufFormat.HEX_DUMP); - } - - /** - * Creates a new instance whose logger name is the fully qualified class - * name of the instance. - * - * @param level the log level - * @param byteBufFormat the ByteBuf format - */ - public LoggingHandler(LogLevel level, ByteBufFormat byteBufFormat) { - this.level = requireNonNull(level, "level"); - this.byteBufFormat = requireNonNull(byteBufFormat, "byteBufFormat"); - logger = InternalLoggerFactory.getInstance(getClass()); - internalLevel = level.toInternalLevel(); - } - - /** - * Creates a new instance with the specified logger name and with hex dump - * enabled. - * - * @param clazz the class type to generate the logger for - */ - public LoggingHandler(Class clazz) { - this(clazz, DEFAULT_LEVEL); - } - - /** - * Creates a new instance with the specified logger name. - * - * @param clazz the class type to generate the logger for - * @param level the log level - */ - public LoggingHandler(Class clazz, LogLevel level) { - this(clazz, level, ByteBufFormat.HEX_DUMP); - } - - /** - * Creates a new instance with the specified logger name. - * - * @param clazz the class type to generate the logger for - * @param level the log level - * @param byteBufFormat the ByteBuf format - */ - public LoggingHandler(Class clazz, LogLevel level, ByteBufFormat byteBufFormat) { - requireNonNull(clazz, "clazz"); - this.level = requireNonNull(level, "level"); - this.byteBufFormat = requireNonNull(byteBufFormat, "byteBufFormat"); - logger = InternalLoggerFactory.getInstance(clazz); - internalLevel = level.toInternalLevel(); - } - - /** - * Creates a new instance with the specified logger name using the default log level. - * - * @param name the name of the class to use for the logger - */ - public LoggingHandler(String name) { - this(name, DEFAULT_LEVEL); - } - - /** - * Creates a new instance with the specified logger name. - * - * @param name the name of the class to use for the logger - * @param level the log level - */ - public LoggingHandler(String name, LogLevel level) { - this(name, level, ByteBufFormat.HEX_DUMP); - } - - /** - * Creates a new instance with the specified logger name. - * - * @param name the name of the class to use for the logger - * @param level the log level - * @param byteBufFormat the ByteBuf format - */ - public LoggingHandler(String name, LogLevel level, ByteBufFormat byteBufFormat) { - requireNonNull(name, "name"); - - this.level = requireNonNull(level, "level"); - this.byteBufFormat = requireNonNull(byteBufFormat, "byteBufFormat"); - logger = InternalLoggerFactory.getInstance(name); - internalLevel = level.toInternalLevel(); - } - - /** - * Returns the {@link LogLevel} that this handler uses to log - */ - public LogLevel level() { - return level; - } - - /** - * Returns the {@link ByteBufFormat} that this handler uses to log - */ - public ByteBufFormat byteBufFormat() { - return byteBufFormat; - } - - @Override - public void channelRegistered(ChannelHandlerContext ctx) throws Exception { - if (logger.isEnabled(internalLevel)) { - logger.log(internalLevel, format(ctx, "REGISTERED")); - } - ctx.fireChannelRegistered(); - } - - @Override - public void channelUnregistered(ChannelHandlerContext ctx) throws Exception { - if (logger.isEnabled(internalLevel)) { - logger.log(internalLevel, format(ctx, "UNREGISTERED")); - } - ctx.fireChannelUnregistered(); - } - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - if (logger.isEnabled(internalLevel)) { - logger.log(internalLevel, format(ctx, "ACTIVE")); - } - ctx.fireChannelActive(); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - if (logger.isEnabled(internalLevel)) { - logger.log(internalLevel, format(ctx, "INACTIVE")); - } - ctx.fireChannelInactive(); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - if (logger.isEnabled(internalLevel)) { - logger.log(internalLevel, format(ctx, "EXCEPTION", cause), cause); - } - ctx.fireExceptionCaught(cause); - } - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (logger.isEnabled(internalLevel)) { - logger.log(internalLevel, format(ctx, "USER_EVENT", evt)); - } - ctx.fireUserEventTriggered(evt); - } - - @Override - public Future bind(ChannelHandlerContext ctx, SocketAddress localAddress) { - if (logger.isEnabled(internalLevel)) { - logger.log(internalLevel, format(ctx, "BIND", localAddress)); - } - return ctx.bind(localAddress); - } - - @Override - public Future connect( - ChannelHandlerContext ctx, - SocketAddress remoteAddress, SocketAddress localAddress) { - if (logger.isEnabled(internalLevel)) { - logger.log(internalLevel, format(ctx, "CONNECT", remoteAddress, localAddress)); - } - return ctx.connect(remoteAddress, localAddress); - } - - @Override - public Future disconnect(ChannelHandlerContext ctx) { - if (logger.isEnabled(internalLevel)) { - logger.log(internalLevel, format(ctx, "DISCONNECT")); - } - return ctx.disconnect(); - } - - @Override - public Future close(ChannelHandlerContext ctx) { - if (logger.isEnabled(internalLevel)) { - logger.log(internalLevel, format(ctx, "CLOSE")); - } - return ctx.close(); - } - - @Override - public Future deregister(ChannelHandlerContext ctx) { - if (logger.isEnabled(internalLevel)) { - logger.log(internalLevel, format(ctx, "DEREGISTER")); - } - return ctx.deregister(); - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { - if (logger.isEnabled(internalLevel)) { - logger.log(internalLevel, format(ctx, "READ COMPLETE")); - } - ctx.fireChannelReadComplete(); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - if (logger.isEnabled(internalLevel)) { - logger.log(internalLevel, format(ctx, "READ", msg)); - } - ctx.fireChannelRead(msg); - } - - @Override - public Future write(ChannelHandlerContext ctx, Object msg) { - if (logger.isEnabled(internalLevel)) { - logger.log(internalLevel, format(ctx, "WRITE", msg)); - } - return ctx.write(msg); - } - - @Override - public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception { - if (logger.isEnabled(internalLevel)) { - logger.log(internalLevel, format(ctx, "WRITABILITY CHANGED")); - } - ctx.fireChannelWritabilityChanged(); - } - - @Override - public void flush(ChannelHandlerContext ctx) { - if (logger.isEnabled(internalLevel)) { - logger.log(internalLevel, format(ctx, "FLUSH")); - } - ctx.flush(); - } - - /** - * Formats an event and returns the formatted message. - * - * @param eventName the name of the event - */ - protected String format(ChannelHandlerContext ctx, String eventName) { - String chStr = ctx.channel().toString(); - return new StringBuilder(chStr.length() + 1 + eventName.length()) - .append(chStr) - .append(' ') - .append(eventName) - .toString(); - } - - /** - * Formats an event and returns the formatted message. - * - * @param eventName the name of the event - * @param arg the argument of the event - */ - protected String format(ChannelHandlerContext ctx, String eventName, Object arg) { - if (arg instanceof ByteBufConvertible) { - return formatByteBuf(ctx, eventName, ((ByteBufConvertible) arg).asByteBuf()); - } else if (arg instanceof ByteBufHolder) { - return formatByteBufHolder(ctx, eventName, (ByteBufHolder) arg); - } else { - return formatSimple(ctx, eventName, arg); - } - } - - /** - * Formats an event and returns the formatted message. This method is currently only used for formatting - * {@link ChannelHandler#connect(ChannelHandlerContext, SocketAddress, SocketAddress)}. - * - * @param eventName the name of the event - * @param firstArg the first argument of the event - * @param secondArg the second argument of the event - */ - protected String format(ChannelHandlerContext ctx, String eventName, Object firstArg, Object secondArg) { - if (secondArg == null) { - return formatSimple(ctx, eventName, firstArg); - } - - String chStr = ctx.channel().toString(); - String arg1Str = String.valueOf(firstArg); - String arg2Str = secondArg.toString(); - StringBuilder buf = new StringBuilder( - chStr.length() + 1 + eventName.length() + 2 + arg1Str.length() + 2 + arg2Str.length()); - buf.append(chStr).append(' ').append(eventName).append(": ").append(arg1Str).append(", ").append(arg2Str); - return buf.toString(); - } - - /** - * Generates the default log message of the specified event whose argument is a {@link ByteBuf}. - */ - private String formatByteBuf(ChannelHandlerContext ctx, String eventName, ByteBuf msg) { - String chStr = ctx.channel().toString(); - int length = msg.readableBytes(); - if (length == 0) { - StringBuilder buf = new StringBuilder(chStr.length() + 1 + eventName.length() + 4); - buf.append(chStr).append(' ').append(eventName).append(": 0B"); - return buf.toString(); - } else { - int outputLength = chStr.length() + 1 + eventName.length() + 2 + 10 + 1; - if (byteBufFormat == ByteBufFormat.HEX_DUMP) { - int rows = length / 16 + (length % 15 == 0? 0 : 1) + 4; - int hexDumpLength = 2 + rows * 80; - outputLength += hexDumpLength; - } - StringBuilder buf = new StringBuilder(outputLength); - buf.append(chStr).append(' ').append(eventName).append(": ").append(length).append('B'); - if (byteBufFormat == ByteBufFormat.HEX_DUMP) { - buf.append(NEWLINE); - appendPrettyHexDump(buf, msg); - } - - return buf.toString(); - } - } - - /** - * Generates the default log message of the specified event whose argument is a {@link ByteBufHolder}. - */ - private String formatByteBufHolder(ChannelHandlerContext ctx, String eventName, ByteBufHolder msg) { - String chStr = ctx.channel().toString(); - String msgStr = msg.toString(); - ByteBuf content = msg.content(); - int length = content.readableBytes(); - if (length == 0) { - StringBuilder buf = new StringBuilder(chStr.length() + 1 + eventName.length() + 2 + msgStr.length() + 4); - buf.append(chStr).append(' ').append(eventName).append(", ").append(msgStr).append(", 0B"); - return buf.toString(); - } else { - int outputLength = chStr.length() + 1 + eventName.length() + 2 + msgStr.length() + 2 + 10 + 1; - if (byteBufFormat == ByteBufFormat.HEX_DUMP) { - int rows = length / 16 + (length % 15 == 0? 0 : 1) + 4; - int hexDumpLength = 2 + rows * 80; - outputLength += hexDumpLength; - } - StringBuilder buf = new StringBuilder(outputLength); - buf.append(chStr).append(' ').append(eventName).append(": ") - .append(msgStr).append(", ").append(length).append('B'); - if (byteBufFormat == ByteBufFormat.HEX_DUMP) { - buf.append(NEWLINE); - appendPrettyHexDump(buf, content); - } - - return buf.toString(); - } - } - - /** - * Generates the default log message of the specified event whose argument is an arbitrary object. - */ - private static String formatSimple(ChannelHandlerContext ctx, String eventName, Object msg) { - String chStr = ctx.channel().toString(); - String msgStr = String.valueOf(msg); - StringBuilder buf = new StringBuilder(chStr.length() + 1 + eventName.length() + 2 + msgStr.length()); - return buf.append(chStr).append(' ').append(eventName).append(": ").append(msgStr).toString(); - } -} diff --git a/handler/src/main/java/io/netty/handler/logging/package-info.java b/handler/src/main/java/io/netty/handler/logging/package-info.java deleted file mode 100644 index 04db33cdbd..0000000000 --- a/handler/src/main/java/io/netty/handler/logging/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * Logs the I/O events for debugging purpose. - */ -package io.netty.handler.logging; diff --git a/handler/src/main/java/io/netty/handler/pcap/EthernetPacket.java b/handler/src/main/java/io/netty/handler/pcap/EthernetPacket.java deleted file mode 100644 index 8e5cb8e251..0000000000 --- a/handler/src/main/java/io/netty/handler/pcap/EthernetPacket.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.pcap; - -import io.netty.buffer.ByteBuf; - -final class EthernetPacket { - - /** - * MAC Address: 00:00:5E:00:53:00 - */ - private static final byte[] DUMMY_SOURCE_MAC_ADDRESS = new byte[]{0, 0, 94, 0, 83, 0}; - - /** - * MAC Address: 00:00:5E:00:53:FF - */ - private static final byte[] DUMMY_DESTINATION_MAC_ADDRESS = new byte[]{0, 0, 94, 0, 83, -1}; - - /** - * IPv4 - */ - private static final int V4 = 0x0800; - - /** - * IPv6 - */ - private static final int V6 = 0x86dd; - - private EthernetPacket() { - // Prevent outside initialization - } - - /** - * Write IPv4 Ethernet Packet. It uses a dummy MAC address for both source and destination. - * - * @param byteBuf ByteBuf where Ethernet Packet data will be set - * @param payload Payload of IPv4 - */ - static void writeIPv4(ByteBuf byteBuf, ByteBuf payload) { - EthernetPacket.writePacket(byteBuf, payload, DUMMY_SOURCE_MAC_ADDRESS, DUMMY_DESTINATION_MAC_ADDRESS, V4); - } - - /** - * Write IPv6 Ethernet Packet. It uses a dummy MAC address for both source and destination. - * - * @param byteBuf ByteBuf where Ethernet Packet data will be set - * @param payload Payload of IPv6 - */ - static void writeIPv6(ByteBuf byteBuf, ByteBuf payload) { - EthernetPacket.writePacket(byteBuf, payload, DUMMY_SOURCE_MAC_ADDRESS, DUMMY_DESTINATION_MAC_ADDRESS, V6); - } - - /** - * Write IPv6 Ethernet Packet - * - * @param byteBuf ByteBuf where Ethernet Packet data will be set - * @param payload Payload of IPv6 - * @param srcAddress Source MAC Address - * @param dstAddress Destination MAC Address - * @param type Type of Frame - */ - private static void writePacket(ByteBuf byteBuf, ByteBuf payload, byte[] srcAddress, byte[] dstAddress, int type) { - byteBuf.writeBytes(dstAddress); // Destination MAC Address - byteBuf.writeBytes(srcAddress); // Source MAC Address - byteBuf.writeShort(type); // Frame Type (IPv4 or IPv6) - byteBuf.writeBytes(payload); // Payload of L3 - } -} diff --git a/handler/src/main/java/io/netty/handler/pcap/IPPacket.java b/handler/src/main/java/io/netty/handler/pcap/IPPacket.java deleted file mode 100644 index 11e8bf31b8..0000000000 --- a/handler/src/main/java/io/netty/handler/pcap/IPPacket.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.pcap; - -import io.netty.buffer.ByteBuf; - -final class IPPacket { - - private static final byte MAX_TTL = (byte) 255; - private static final short V4_HEADER_SIZE = 20; - private static final byte TCP = 6 & 0xff; - private static final byte UDP = 17 & 0xff; - - /** - * Version + Traffic class + Flow label - */ - private static final int IPV6_VERSION_TRAFFIC_FLOW = 60000000; - - private IPPacket() { - // Prevent outside initialization - } - - /** - * Write IPv4 Packet for UDP Packet - * - * @param byteBuf ByteBuf where IP Packet data will be set - * @param payload Payload of UDP - * @param srcAddress Source IPv4 Address - * @param dstAddress Destination IPv4 Address - */ - static void writeUDPv4(ByteBuf byteBuf, ByteBuf payload, int srcAddress, int dstAddress) { - writePacketv4(byteBuf, payload, UDP, srcAddress, dstAddress); - } - - /** - * Write IPv6 Packet for UDP Packet - * - * @param byteBuf ByteBuf where IP Packet data will be set - * @param payload Payload of UDP - * @param srcAddress Source IPv6 Address - * @param dstAddress Destination IPv6 Address - */ - static void writeUDPv6(ByteBuf byteBuf, ByteBuf payload, byte[] srcAddress, byte[] dstAddress) { - writePacketv6(byteBuf, payload, UDP, srcAddress, dstAddress); - } - - /** - * Write IPv4 Packet for TCP Packet - * - * @param byteBuf ByteBuf where IP Packet data will be set - * @param payload Payload of TCP - * @param srcAddress Source IPv4 Address - * @param dstAddress Destination IPv4 Address - */ - static void writeTCPv4(ByteBuf byteBuf, ByteBuf payload, int srcAddress, int dstAddress) { - writePacketv4(byteBuf, payload, TCP, srcAddress, dstAddress); - } - - /** - * Write IPv6 Packet for TCP Packet - * - * @param byteBuf ByteBuf where IP Packet data will be set - * @param payload Payload of TCP - * @param srcAddress Source IPv6 Address - * @param dstAddress Destination IPv6 Address - */ - static void writeTCPv6(ByteBuf byteBuf, ByteBuf payload, byte[] srcAddress, byte[] dstAddress) { - writePacketv6(byteBuf, payload, TCP, srcAddress, dstAddress); - } - - private static void writePacketv4(ByteBuf byteBuf, ByteBuf payload, int protocol, int srcAddress, - int dstAddress) { - - byteBuf.writeByte(0x45); // Version + IHL - byteBuf.writeByte(0x00); // DSCP - byteBuf.writeShort(V4_HEADER_SIZE + payload.readableBytes()); // Length - byteBuf.writeShort(0x0000); // Identification - byteBuf.writeShort(0x0000); // Fragment - byteBuf.writeByte(MAX_TTL); // TTL - byteBuf.writeByte(protocol); // Protocol - byteBuf.writeShort(0); // Checksum - byteBuf.writeInt(srcAddress); // Source IPv4 Address - byteBuf.writeInt(dstAddress); // Destination IPv4 Address - byteBuf.writeBytes(payload); // Payload of L4 - } - - private static void writePacketv6(ByteBuf byteBuf, ByteBuf payload, int protocol, byte[] srcAddress, - byte[] dstAddress) { - - byteBuf.writeInt(IPV6_VERSION_TRAFFIC_FLOW); // Version + Traffic class + Flow label - byteBuf.writeShort(payload.readableBytes()); // Payload length - byteBuf.writeByte(protocol & 0xff); // Next header - byteBuf.writeByte(MAX_TTL); // Hop limit - byteBuf.writeBytes(srcAddress); // Source IPv6 Address - byteBuf.writeBytes(dstAddress); // Destination IPv6 Address - byteBuf.writeBytes(payload); // Payload of L4 - } -} diff --git a/handler/src/main/java/io/netty/handler/pcap/PcapHeaders.java b/handler/src/main/java/io/netty/handler/pcap/PcapHeaders.java deleted file mode 100644 index 1a8cce2a07..0000000000 --- a/handler/src/main/java/io/netty/handler/pcap/PcapHeaders.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.pcap; - -import io.netty.buffer.ByteBuf; - -final class PcapHeaders { - - /** - * Pcap Global Header built from: - *

    - *
  1. magic_number
  2. - *
  3. version_major
  4. - *
  5. version_minor
  6. - *
  7. thiszone
  8. - *
  9. sigfigs
  10. - *
  11. snaplen
  12. - *
  13. network
  14. - *
- */ - private static final byte[] GLOBAL_HEADER = new byte[]{-95, -78, -61, -44, 0, 2, 0, 4, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, 0, 0, 0, 1}; - - private PcapHeaders() { - // Prevent outside initialization - } - - /** - * Write Pcap Global Header - * - * @param byteBuf byteBuf ByteBuf where we'll write header data - */ - public static void writeGlobalHeader(ByteBuf byteBuf) { - byteBuf.writeBytes(GLOBAL_HEADER); - } - - /** - * Write Pcap Packet Header - * - * @param byteBuf ByteBuf where we'll write header data - * @param ts_sec timestamp seconds - * @param ts_usec timestamp microseconds - * @param incl_len number of octets of packet saved in file - * @param orig_len actual length of packet - */ - static void writePacketHeader(ByteBuf byteBuf, int ts_sec, int ts_usec, int incl_len, int orig_len) { - byteBuf.writeInt(ts_sec); - byteBuf.writeInt(ts_usec); - byteBuf.writeInt(incl_len); - byteBuf.writeInt(orig_len); - } -} diff --git a/handler/src/main/java/io/netty/handler/pcap/PcapWriteHandler.java b/handler/src/main/java/io/netty/handler/pcap/PcapWriteHandler.java deleted file mode 100644 index 9e83a41cd0..0000000000 --- a/handler/src/main/java/io/netty/handler/pcap/PcapWriteHandler.java +++ /dev/null @@ -1,555 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.pcap; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.ByteBufConvertible; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ServerChannel; -import io.netty.channel.socket.DatagramChannel; -import io.netty.channel.socket.DatagramPacket; -import io.netty.channel.socket.ServerSocketChannel; -import io.netty.channel.socket.SocketChannel; -import io.netty.util.NetUtil; -import io.netty.util.concurrent.Future; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.io.Closeable; -import java.io.IOException; -import java.io.OutputStream; -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.InetSocketAddress; -import java.util.Objects; - -/** - *

{@link PcapWriteHandler} captures {@link ByteBuf} from {@link SocketChannel} / {@link ServerChannel} - * or {@link DatagramPacket} and writes it into Pcap {@link OutputStream}.

- * - *

- * Things to keep in mind when using {@link PcapWriteHandler} with TCP: - * - *

    - *
  • Whenever {@link ChannelHandler#channelActive(ChannelHandlerContext)} is called, - * a fake TCP 3-way handshake (SYN, SYN+ACK, ACK) is simulated as new connection in Pcap.
  • - * - *
  • Whenever {@link ChannelHandler#handlerRemoved(ChannelHandlerContext)} is called, - * a fake TCP 3-way handshake (FIN+ACK, FIN+ACK, ACK) is simulated as connection shutdown in Pcap.
  • - * - *
  • Whenever {@link ChannelHandler#exceptionCaught(ChannelHandlerContext, Throwable)} - * is called, a fake TCP RST is sent to simulate connection Reset in Pcap.
  • - * - *
  • ACK is sent each time data is send / received.
  • - * - *
  • Zero Length Data Packets can cause TCP Double ACK error in Wireshark. To tackle this, - * set {@code captureZeroByte} to {@code false}.
  • - *
- *

- */ -public final class PcapWriteHandler implements ChannelHandler, Closeable { - - /** - * Logger for logging events - */ - private final InternalLogger logger = InternalLoggerFactory.getInstance(PcapWriteHandler.class); - - /** - * {@link PcapWriter} Instance - */ - private PcapWriter pCapWriter; - - /** - * {@link OutputStream} where we'll write Pcap data. - */ - private final OutputStream outputStream; - - /** - * {@code true} if we want to capture packets with zero bytes else {@code false}. - */ - private final boolean captureZeroByte; - - /** - * {@code true} if we want to write Pcap Global Header on initialization of - * {@link PcapWriter} else {@code false}. - */ - private final boolean writePcapGlobalHeader; - - /** - * TCP Sender Segment Number. - * It'll start with 1 and keep incrementing with number of bytes read/sent. - */ - private int sendSegmentNumber = 1; - - /** - * TCP Receiver Segment Number. - * It'll start with 1 and keep incrementing with number of bytes read/sent. - */ - private int receiveSegmentNumber = 1; - - /** - * Source Address - */ - private InetSocketAddress srcAddr; - - /** - * Destination Address - */ - private InetSocketAddress dstAddr; - - /** - * Set to {@code true} if {@link #close()} is called and we should stop writing Pcap. - */ - private boolean isClosed; - - /** - * Create new {@link PcapWriteHandler} Instance. - * {@code captureZeroByte} is set to {@code false} and - * {@code writePcapGlobalHeader} is set to {@code true}. - * - * @param outputStream OutputStream where Pcap data will be written. Call {@link #close()} to close this - * OutputStream. - * @throws NullPointerException If {@link OutputStream} is {@code null} then we'll throw an - * {@link NullPointerException} - */ - public PcapWriteHandler(OutputStream outputStream) { - this(outputStream, false, true); - } - - /** - * Create new {@link PcapWriteHandler} Instance - * - * @param outputStream OutputStream where Pcap data will be written. Call {@link #close()} to close this - * OutputStream. - * @param captureZeroByte Set to {@code true} to enable capturing packets with empty (0 bytes) payload. - * Otherwise, if set to {@code false}, empty packets will be filtered out. - * @param writePcapGlobalHeader Set to {@code true} to write Pcap Global Header on initialization. - * Otherwise, if set to {@code false}, Pcap Global Header will not be written - * on initialization. This could when writing Pcap data on a existing file where - * Pcap Global Header is already present. - * @throws NullPointerException If {@link OutputStream} is {@code null} then we'll throw an - * {@link NullPointerException} - */ - public PcapWriteHandler(OutputStream outputStream, boolean captureZeroByte, boolean writePcapGlobalHeader) { - this.outputStream = Objects.requireNonNull(outputStream, "OutputStream"); - this.captureZeroByte = captureZeroByte; - this.writePcapGlobalHeader = writePcapGlobalHeader; - } - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - - ByteBufAllocator byteBufAllocator = ctx.alloc(); - - /* - * If `writePcapGlobalHeader` is `true`, we'll write Pcap Global Header. - */ - if (writePcapGlobalHeader) { - - ByteBuf byteBuf = byteBufAllocator.buffer(); - try { - pCapWriter = new PcapWriter(outputStream, byteBuf); - } catch (IOException ex) { - ctx.channel().close(); - ctx.fireExceptionCaught(ex); - logger.error("Caught Exception While Initializing PcapWriter, Closing Channel.", ex); - } finally { - byteBuf.release(); - } - } else { - pCapWriter = new PcapWriter(outputStream); - } - - // If Channel belongs to `SocketChannel` then we're handling TCP. - if (ctx.channel() instanceof SocketChannel) { - - // Capture correct `localAddress` and `remoteAddress` - if (ctx.channel().parent() instanceof ServerSocketChannel) { - srcAddr = (InetSocketAddress) ctx.channel().remoteAddress(); - dstAddr = (InetSocketAddress) ctx.channel().localAddress(); - } else { - srcAddr = (InetSocketAddress) ctx.channel().localAddress(); - dstAddr = (InetSocketAddress) ctx.channel().remoteAddress(); - } - - logger.debug("Initiating Fake TCP 3-Way Handshake"); - - ByteBuf tcpBuf = byteBufAllocator.buffer(); - - try { - // Write SYN with Normal Source and Destination Address - TCPPacket.writePacket(tcpBuf, null, 0, 0, srcAddr.getPort(), dstAddr.getPort(), TCPPacket.TCPFlag.SYN); - completeTCPWrite(srcAddr, dstAddr, tcpBuf, byteBufAllocator, ctx); - - // Write SYN+ACK with Reversed Source and Destination Address - TCPPacket.writePacket(tcpBuf, null, 0, 1, dstAddr.getPort(), srcAddr.getPort(), TCPPacket.TCPFlag.SYN, - TCPPacket.TCPFlag.ACK); - completeTCPWrite(dstAddr, srcAddr, tcpBuf, byteBufAllocator, ctx); - - // Write ACK with Normal Source and Destination Address - TCPPacket.writePacket(tcpBuf, null, 1, 1, srcAddr.getPort(), dstAddr.getPort(), TCPPacket.TCPFlag.ACK); - completeTCPWrite(srcAddr, dstAddr, tcpBuf, byteBufAllocator, ctx); - } finally { - tcpBuf.release(); - } - - logger.debug("Finished Fake TCP 3-Way Handshake"); - } else if (ctx.channel() instanceof DatagramChannel) { - DatagramChannel datagramChannel = (DatagramChannel) ctx.channel(); - - // If `DatagramChannel` is connected then we can get - // `localAddress` and `remoteAddress` from Channel. - if (datagramChannel.isConnected()) { - srcAddr = (InetSocketAddress) ctx.channel().localAddress(); - dstAddr = (InetSocketAddress) ctx.channel().remoteAddress(); - } - } - - ctx.fireChannelActive(); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - if (!isClosed) { - if (ctx.channel() instanceof SocketChannel) { - handleTCP(ctx, msg, false); - } else if (ctx.channel() instanceof DatagramChannel) { - handleUDP(ctx, msg); - } else { - logger.debug("Discarding Pcap Write for Unknown Channel Type: {}", ctx.channel()); - } - } - ctx.fireChannelRead(msg); - } - - @Override - public Future write(ChannelHandlerContext ctx, Object msg) { - if (!isClosed) { - if (ctx.channel() instanceof SocketChannel) { - handleTCP(ctx, msg, true); - } else if (ctx.channel() instanceof DatagramChannel) { - handleUDP(ctx, msg); - } else { - logger.debug("Discarding Pcap Write for Unknown Channel Type: {}", ctx.channel()); - } - } - return ctx.write(msg); - } - - /** - * Handle TCP L4 - * - * @param ctx {@link ChannelHandlerContext} for {@link ByteBuf} allocation and - * {@code fireExceptionCaught} - * @param msg {@link Object} must be {@link ByteBuf} else it'll be discarded - * @param isWriteOperation Set {@code true} if we have to process packet when packets are being sent out - * else set {@code false} - */ - private void handleTCP(ChannelHandlerContext ctx, Object msg, boolean isWriteOperation) { - if (msg instanceof ByteBufConvertible) { - - // If bytes are 0 and `captureZeroByte` is false, we won't capture this. - if (((ByteBufConvertible) msg).asByteBuf().readableBytes() == 0 && !captureZeroByte) { - logger.debug("Discarding Zero Byte TCP Packet. isWriteOperation {}", isWriteOperation); - return; - } - - ByteBufAllocator byteBufAllocator = ctx.alloc(); - ByteBuf packet = ((ByteBufConvertible) msg).asByteBuf().duplicate(); - ByteBuf tcpBuf = byteBufAllocator.buffer(); - int bytes = packet.readableBytes(); - - try { - if (isWriteOperation) { - TCPPacket.writePacket(tcpBuf, packet, sendSegmentNumber, receiveSegmentNumber, srcAddr.getPort(), - dstAddr.getPort(), TCPPacket.TCPFlag.ACK); - completeTCPWrite(srcAddr, dstAddr, tcpBuf, byteBufAllocator, ctx); - logTCP(true, bytes, sendSegmentNumber, receiveSegmentNumber, srcAddr, dstAddr, false); - - sendSegmentNumber += bytes; - - TCPPacket.writePacket(tcpBuf, null, receiveSegmentNumber, sendSegmentNumber, dstAddr.getPort(), - srcAddr.getPort(), TCPPacket.TCPFlag.ACK); - completeTCPWrite(dstAddr, srcAddr, tcpBuf, byteBufAllocator, ctx); - logTCP(true, bytes, sendSegmentNumber, receiveSegmentNumber, dstAddr, srcAddr, true); - } else { - TCPPacket.writePacket(tcpBuf, packet, receiveSegmentNumber, sendSegmentNumber, dstAddr.getPort(), - srcAddr.getPort(), TCPPacket.TCPFlag.ACK); - completeTCPWrite(dstAddr, srcAddr, tcpBuf, byteBufAllocator, ctx); - logTCP(false, bytes, receiveSegmentNumber, sendSegmentNumber, dstAddr, srcAddr, false); - - receiveSegmentNumber += bytes; - - TCPPacket.writePacket(tcpBuf, null, sendSegmentNumber, receiveSegmentNumber, srcAddr.getPort(), - dstAddr.getPort(), TCPPacket.TCPFlag.ACK); - completeTCPWrite(srcAddr, dstAddr, tcpBuf, byteBufAllocator, ctx); - logTCP(false, bytes, sendSegmentNumber, receiveSegmentNumber, srcAddr, dstAddr, true); - } - } finally { - tcpBuf.release(); - } - } else { - logger.debug("Discarding Pcap Write for TCP Object: {}", msg); - } - } - - /** - * Write TCP/IP L3 and L2 here. - * - * @param srcAddr {@link InetSocketAddress} Source Address of this Packet - * @param dstAddr {@link InetSocketAddress} Destination Address of this Packet - * @param tcpBuf {@link ByteBuf} containing TCP L4 Data - * @param byteBufAllocator {@link ByteBufAllocator} for allocating bytes for TCP/IP L3 and L2 data. - * @param ctx {@link ChannelHandlerContext} for {@code fireExceptionCaught} - */ - private void completeTCPWrite(InetSocketAddress srcAddr, InetSocketAddress dstAddr, ByteBuf tcpBuf, - ByteBufAllocator byteBufAllocator, ChannelHandlerContext ctx) { - - ByteBuf ipBuf = byteBufAllocator.buffer(); - ByteBuf ethernetBuf = byteBufAllocator.buffer(); - ByteBuf pcap = byteBufAllocator.buffer(); - - try { - if (srcAddr.getAddress() instanceof Inet4Address && dstAddr.getAddress() instanceof Inet4Address) { - IPPacket.writeTCPv4(ipBuf, tcpBuf, - NetUtil.ipv4AddressToInt((Inet4Address) srcAddr.getAddress()), - NetUtil.ipv4AddressToInt((Inet4Address) dstAddr.getAddress())); - - EthernetPacket.writeIPv4(ethernetBuf, ipBuf); - } else if (srcAddr.getAddress() instanceof Inet6Address && dstAddr.getAddress() instanceof Inet6Address) { - IPPacket.writeTCPv6(ipBuf, tcpBuf, - srcAddr.getAddress().getAddress(), - dstAddr.getAddress().getAddress()); - - EthernetPacket.writeIPv6(ethernetBuf, ipBuf); - } else { - logger.error("Source and Destination IP Address versions are not same. Source Address: {}, " + - "Destination Address: {}", srcAddr.getAddress(), dstAddr.getAddress()); - return; - } - - // Write Packet into Pcap - pCapWriter.writePacket(pcap, ethernetBuf); - } catch (IOException ex) { - logger.error("Caught Exception While Writing Packet into Pcap", ex); - ctx.fireExceptionCaught(ex); - } finally { - ipBuf.release(); - ethernetBuf.release(); - pcap.release(); - } - } - - /** - * Logger for TCP - */ - private void logTCP(boolean isWriteOperation, int bytes, int sendSegmentNumber, int receiveSegmentNumber, - InetSocketAddress srcAddr, InetSocketAddress dstAddr, boolean ackOnly) { - // If `ackOnly` is `true` when we don't need to write any data so we'll not - // log number of bytes being written and mark the operation as "TCP ACK". - if (logger.isDebugEnabled()) { - if (ackOnly) { - logger.debug("Writing TCP ACK, isWriteOperation {}, Segment Number {}, Ack Number {}, Src Addr {}, " - + "Dst Addr {}", isWriteOperation, sendSegmentNumber, receiveSegmentNumber, dstAddr, srcAddr); - } else { - logger.debug("Writing TCP Data of {} Bytes, isWriteOperation {}, Segment Number {}, Ack Number {}, " + - "Src Addr {}, Dst Addr {}", bytes, isWriteOperation, sendSegmentNumber, - receiveSegmentNumber, srcAddr, dstAddr); - } - } - } - - /** - * Handle UDP l4 - * - * @param ctx {@link ChannelHandlerContext} for {@code localAddress} / {@code remoteAddress}, - * {@link ByteBuf} allocation and {@code fireExceptionCaught} - * @param msg {@link DatagramPacket} or {@link DatagramChannel} - */ - private void handleUDP(ChannelHandlerContext ctx, Object msg) { - ByteBuf udpBuf = ctx.alloc().buffer(); - - try { - if (msg instanceof DatagramPacket) { - - // If bytes are 0 and `captureZeroByte` is false, we won't capture this. - if (((DatagramPacket) msg).content().readableBytes() == 0 && !captureZeroByte) { - logger.debug("Discarding Zero Byte UDP Packet"); - return; - } - - DatagramPacket datagramPacket = ((DatagramPacket) msg).duplicate(); - InetSocketAddress srcAddr = datagramPacket.sender(); - InetSocketAddress dstAddr = datagramPacket.recipient(); - - // If `datagramPacket.sender()` is `null` then DatagramPacket is initialized - // `sender` (local) address. In this case, we'll get source address from Channel. - if (srcAddr == null) { - srcAddr = (InetSocketAddress) ctx.channel().localAddress(); - } - - logger.debug("Writing UDP Data of {} Bytes, Src Addr {}, Dst Addr {}", - datagramPacket.content().readableBytes(), srcAddr, dstAddr); - - UDPPacket.writePacket(udpBuf, datagramPacket.content(), srcAddr.getPort(), dstAddr.getPort()); - completeUDPWrite(srcAddr, dstAddr, udpBuf, ctx.alloc(), ctx); - } else if (msg instanceof ByteBufConvertible && ((DatagramChannel) ctx.channel()).isConnected()) { - - // If bytes are 0 and `captureZeroByte` is false, we won't capture this. - if (((ByteBufConvertible) msg).asByteBuf().readableBytes() == 0 && !captureZeroByte) { - logger.debug("Discarding Zero Byte UDP Packet"); - return; - } - - ByteBuf byteBuf = ((ByteBufConvertible) msg).asByteBuf().duplicate(); - - logger.debug("Writing UDP Data of {} Bytes, Src Addr {}, Dst Addr {}", - byteBuf.readableBytes(), srcAddr, dstAddr); - - UDPPacket.writePacket(udpBuf, byteBuf, srcAddr.getPort(), dstAddr.getPort()); - completeUDPWrite(srcAddr, dstAddr, udpBuf, ctx.alloc(), ctx); - } else { - logger.debug("Discarding Pcap Write for UDP Object: {}", msg); - } - } finally { - udpBuf.release(); - } - } - - /** - * Write UDP/IP L3 and L2 here. - * - * @param srcAddr {@link InetSocketAddress} Source Address of this Packet - * @param dstAddr {@link InetSocketAddress} Destination Address of this Packet - * @param udpBuf {@link ByteBuf} containing UDP L4 Data - * @param byteBufAllocator {@link ByteBufAllocator} for allocating bytes for UDP/IP L3 and L2 data. - * @param ctx {@link ChannelHandlerContext} for {@code fireExceptionCaught} - */ - private void completeUDPWrite(InetSocketAddress srcAddr, InetSocketAddress dstAddr, ByteBuf udpBuf, - ByteBufAllocator byteBufAllocator, ChannelHandlerContext ctx) { - - ByteBuf ipBuf = byteBufAllocator.buffer(); - ByteBuf ethernetBuf = byteBufAllocator.buffer(); - ByteBuf pcap = byteBufAllocator.buffer(); - - try { - if (srcAddr.getAddress() instanceof Inet4Address && dstAddr.getAddress() instanceof Inet4Address) { - IPPacket.writeUDPv4(ipBuf, udpBuf, - NetUtil.ipv4AddressToInt((Inet4Address) srcAddr.getAddress()), - NetUtil.ipv4AddressToInt((Inet4Address) dstAddr.getAddress())); - - EthernetPacket.writeIPv4(ethernetBuf, ipBuf); - } else if (srcAddr.getAddress() instanceof Inet6Address && dstAddr.getAddress() instanceof Inet6Address) { - IPPacket.writeUDPv6(ipBuf, udpBuf, - srcAddr.getAddress().getAddress(), - dstAddr.getAddress().getAddress()); - - EthernetPacket.writeIPv6(ethernetBuf, ipBuf); - } else { - logger.error("Source and Destination IP Address versions are not same. Source Address: {}, " + - "Destination Address: {}", srcAddr.getAddress(), dstAddr.getAddress()); - return; - } - - // Write Packet into Pcap - pCapWriter.writePacket(pcap, ethernetBuf); - } catch (IOException ex) { - logger.error("Caught Exception While Writing Packet into Pcap", ex); - ctx.fireExceptionCaught(ex); - } finally { - ipBuf.release(); - ethernetBuf.release(); - pcap.release(); - } - } - - @Override - public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { - - // If `isTCP` is true, then we'll simulate a `FIN` flow. - if (ctx.channel() instanceof SocketChannel) { - logger.debug("Starting Fake TCP FIN+ACK Flow to close connection"); - - ByteBufAllocator byteBufAllocator = ctx.alloc(); - ByteBuf tcpBuf = byteBufAllocator.buffer(); - - try { - // Write FIN+ACK with Normal Source and Destination Address - TCPPacket.writePacket(tcpBuf, null, sendSegmentNumber, receiveSegmentNumber, srcAddr.getPort(), - dstAddr.getPort(), TCPPacket.TCPFlag.FIN, TCPPacket.TCPFlag.ACK); - completeTCPWrite(srcAddr, dstAddr, tcpBuf, byteBufAllocator, ctx); - - // Write FIN+ACK with Reversed Source and Destination Address - TCPPacket.writePacket(tcpBuf, null, receiveSegmentNumber, sendSegmentNumber, dstAddr.getPort(), - srcAddr.getPort(), TCPPacket.TCPFlag.FIN, TCPPacket.TCPFlag.ACK); - completeTCPWrite(dstAddr, srcAddr, tcpBuf, byteBufAllocator, ctx); - - // Write ACK with Normal Source and Destination Address - TCPPacket.writePacket(tcpBuf, null, sendSegmentNumber + 1, receiveSegmentNumber + 1, - srcAddr.getPort(), dstAddr.getPort(), TCPPacket.TCPFlag.ACK); - completeTCPWrite(srcAddr, dstAddr, tcpBuf, byteBufAllocator, ctx); - } finally { - tcpBuf.release(); - } - - logger.debug("Finished Fake TCP FIN+ACK Flow to close connection"); - } - - close(); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - - if (ctx.channel() instanceof SocketChannel) { - ByteBuf tcpBuf = ctx.alloc().buffer(); - - try { - // Write RST with Normal Source and Destination Address - TCPPacket.writePacket(tcpBuf, null, sendSegmentNumber, receiveSegmentNumber, srcAddr.getPort(), - dstAddr.getPort(), TCPPacket.TCPFlag.RST, TCPPacket.TCPFlag.ACK); - completeTCPWrite(srcAddr, dstAddr, tcpBuf, ctx.alloc(), ctx); - } finally { - tcpBuf.release(); - } - - logger.debug("Sent Fake TCP RST to close connection"); - } - - close(); - ctx.fireExceptionCaught(cause); - } - - /** - *

Close {@code PcapWriter} and {@link OutputStream}.

- *

Note: Calling this method does not close {@link PcapWriteHandler}. - * Only Pcap Writes are closed.

- * - * @throws IOException If {@link OutputStream#close()} throws an exception - */ - @Override - public void close() throws IOException { - if (isClosed) { - logger.debug("PcapWriterHandler is already closed"); - } else { - isClosed = true; - pCapWriter.close(); - logger.debug("PcapWriterHandler is now closed"); - } - } -} diff --git a/handler/src/main/java/io/netty/handler/pcap/PcapWriter.java b/handler/src/main/java/io/netty/handler/pcap/PcapWriter.java deleted file mode 100644 index f01e3425df..0000000000 --- a/handler/src/main/java/io/netty/handler/pcap/PcapWriter.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.pcap; - -import io.netty.buffer.ByteBuf; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.io.Closeable; -import java.io.IOException; -import java.io.OutputStream; - -final class PcapWriter implements Closeable { - - /** - * Logger - */ - private static final InternalLogger logger = InternalLoggerFactory.getInstance(PcapWriter.class); - - /** - * {@link OutputStream} where we'll write Pcap data. - */ - private final OutputStream outputStream; - - /** - * Set to {@code true} if {@link #outputStream} is closed. - */ - private boolean isClosed; - - /** - * This uses {@link OutputStream} for writing Pcap. - * Pcap Global Header is not written on construction. - */ - PcapWriter(OutputStream outputStream) { - this.outputStream = outputStream; - } - - /** - * This uses {@link OutputStream} for writing Pcap. - * Pcap Global Header is also written on construction. - * - * @throws IOException If {@link OutputStream#write(byte[])} throws an exception - */ - PcapWriter(OutputStream outputStream, ByteBuf byteBuf) throws IOException { - this.outputStream = outputStream; - - PcapHeaders.writeGlobalHeader(byteBuf); - byteBuf.readBytes(outputStream, byteBuf.readableBytes()); - } - - /** - * Write Packet in Pcap OutputStream. - * - * @param packetHeaderBuf Packer Header {@link ByteBuf} - * @param packet Packet - * @throws IOException If {@link OutputStream#write(byte[])} throws an exception - */ - void writePacket(ByteBuf packetHeaderBuf, ByteBuf packet) throws IOException { - if (isClosed) { - logger.debug("Pcap Write attempted on closed PcapWriter"); - } - - long timestamp = System.currentTimeMillis(); - - PcapHeaders.writePacketHeader( - packetHeaderBuf, - (int) (timestamp / 1000L), - (int) (timestamp % 1000L * 1000L), - packet.readableBytes(), - packet.readableBytes() - ); - - packetHeaderBuf.readBytes(outputStream, packetHeaderBuf.readableBytes()); - packet.readBytes(outputStream, packet.readableBytes()); - } - - @Override - public void close() throws IOException { - if (isClosed) { - logger.debug("PcapWriter is already closed"); - } else { - isClosed = true; - outputStream.flush(); - outputStream.close(); - logger.debug("PcapWriter is now closed"); - } - } -} diff --git a/handler/src/main/java/io/netty/handler/pcap/TCPPacket.java b/handler/src/main/java/io/netty/handler/pcap/TCPPacket.java deleted file mode 100644 index cd74ea0061..0000000000 --- a/handler/src/main/java/io/netty/handler/pcap/TCPPacket.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.pcap; - -import io.netty.buffer.ByteBuf; - -final class TCPPacket { - - /** - * Data Offset + Reserved Bits. - */ - private static final short OFFSET = 0x5000; - - private TCPPacket() { - // Prevent outside initialization - } - - /** - * Write TCP Packet - * - * @param byteBuf ByteBuf where Packet data will be set - * @param payload Payload of this Packet - * @param srcPort Source Port - * @param dstPort Destination Port - */ - static void writePacket(ByteBuf byteBuf, ByteBuf payload, int segmentNumber, int ackNumber, int srcPort, - int dstPort, TCPFlag... tcpFlags) { - - byteBuf.writeShort(srcPort); // Source Port - byteBuf.writeShort(dstPort); // Destination Port - byteBuf.writeInt(segmentNumber); // Segment Number - byteBuf.writeInt(ackNumber); // Acknowledgment Number - byteBuf.writeShort(OFFSET | TCPFlag.getFlag(tcpFlags)); // Flags - byteBuf.writeShort(65535); // Window Size - byteBuf.writeShort(0x0001); // Checksum - byteBuf.writeShort(0); // Urgent Pointer - - if (payload != null) { - byteBuf.writeBytes(payload); // Payload of Data - } - } - - enum TCPFlag { - FIN(1), - SYN(1 << 1), - RST(1 << 2), - PSH(1 << 3), - ACK(1 << 4), - URG(1 << 5), - ECE(1 << 6), - CWR(1 << 7); - - private final int value; - - TCPFlag(int value) { - this.value = value; - } - - static int getFlag(TCPFlag... tcpFlags) { - int flags = 0; - - for (TCPFlag tcpFlag : tcpFlags) { - flags |= tcpFlag.value; - } - - return flags; - } - } -} diff --git a/handler/src/main/java/io/netty/handler/pcap/UDPPacket.java b/handler/src/main/java/io/netty/handler/pcap/UDPPacket.java deleted file mode 100644 index c59abf7b21..0000000000 --- a/handler/src/main/java/io/netty/handler/pcap/UDPPacket.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.pcap; - -import io.netty.buffer.ByteBuf; - -final class UDPPacket { - - private static final short UDP_HEADER_SIZE = 8; - - private UDPPacket() { - // Prevent outside initialization - } - - /** - * Write UDP Packet - * - * @param byteBuf ByteBuf where Packet data will be set - * @param payload Payload of this Packet - * @param srcPort Source Port - * @param dstPort Destination Port - */ - static void writePacket(ByteBuf byteBuf, ByteBuf payload, int srcPort, int dstPort) { - byteBuf.writeShort(srcPort); // Source Port - byteBuf.writeShort(dstPort); // Destination Port - byteBuf.writeShort(UDP_HEADER_SIZE + payload.readableBytes()); // UDP Header Length + Payload Length - byteBuf.writeShort(0x0001); // Checksum - byteBuf.writeBytes(payload); // Payload of Data - } -} diff --git a/handler/src/main/java/io/netty/handler/pcap/package-info.java b/handler/src/main/java/io/netty/handler/pcap/package-info.java deleted file mode 100644 index e990b223f8..0000000000 --- a/handler/src/main/java/io/netty/handler/pcap/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * Capture data and write into Pcap format which helps in troubleshooting. - */ -package io.netty.handler.pcap; diff --git a/handler/src/main/java/io/netty/handler/ssl/AbstractSniHandler.java b/handler/src/main/java/io/netty/handler/ssl/AbstractSniHandler.java deleted file mode 100644 index a37f6742d1..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/AbstractSniHandler.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.CharsetUtil; -import io.netty.util.concurrent.Future; - -import java.util.Locale; - -/** - *

Enables SNI - * (Server Name Indication) extension for server side SSL. For clients - * support SNI, the server could have multiple host name bound on a single IP. - * The client will send host name in the handshake data so server could decide - * which certificate to choose for the host name.

- */ -public abstract class AbstractSniHandler extends SslClientHelloHandler { - - private static String extractSniHostname(ByteBuf in) { - // See https://tools.ietf.org/html/rfc5246#section-7.4.1.2 - // - // Decode the ssl client hello packet. - // - // struct { - // ProtocolVersion client_version; - // Random random; - // SessionID session_id; - // CipherSuite cipher_suites<2..2^16-2>; - // CompressionMethod compression_methods<1..2^8-1>; - // select (extensions_present) { - // case false: - // struct {}; - // case true: - // Extension extensions<0..2^16-1>; - // }; - // } ClientHello; - // - - // We have to skip bytes until SessionID (which sum to 34 bytes in this case). - int offset = in.readerIndex(); - int endOffset = in.writerIndex(); - offset += 34; - - if (endOffset - offset >= 6) { - final int sessionIdLength = in.getUnsignedByte(offset); - offset += sessionIdLength + 1; - - final int cipherSuitesLength = in.getUnsignedShort(offset); - offset += cipherSuitesLength + 2; - - final int compressionMethodLength = in.getUnsignedByte(offset); - offset += compressionMethodLength + 1; - - final int extensionsLength = in.getUnsignedShort(offset); - offset += 2; - final int extensionsLimit = offset + extensionsLength; - - // Extensions should never exceed the record boundary. - if (extensionsLimit <= endOffset) { - while (extensionsLimit - offset >= 4) { - final int extensionType = in.getUnsignedShort(offset); - offset += 2; - - final int extensionLength = in.getUnsignedShort(offset); - offset += 2; - - if (extensionsLimit - offset < extensionLength) { - break; - } - - // SNI - // See https://tools.ietf.org/html/rfc6066#page-6 - if (extensionType == 0) { - offset += 2; - if (extensionsLimit - offset < 3) { - break; - } - - final int serverNameType = in.getUnsignedByte(offset); - offset++; - - if (serverNameType == 0) { - final int serverNameLength = in.getUnsignedShort(offset); - offset += 2; - - if (extensionsLimit - offset < serverNameLength) { - break; - } - - final String hostname = in.toString(offset, serverNameLength, CharsetUtil.US_ASCII); - return hostname.toLowerCase(Locale.US); - } else { - // invalid enum value - break; - } - } - - offset += extensionLength; - } - } - } - return null; - } - - private String hostname; - - @Override - protected Future lookup(ChannelHandlerContext ctx, ByteBuf clientHello) throws Exception { - hostname = clientHello == null ? null : extractSniHostname(clientHello); - - return lookup(ctx, hostname); - } - - @Override - protected void onLookupComplete(ChannelHandlerContext ctx, Future future) throws Exception { - try { - onLookupComplete(ctx, hostname, future); - } finally { - fireSniCompletionEvent( - // If this handler was removed as part of onLookupComplete(...) we should fire the - // event from the beginning of the pipeline as otherwise this will fail. - ctx.isRemoved() ? ctx.pipeline().firstContext() : ctx, hostname, future); - } - } - - /** - * Kicks off a lookup for the given SNI value and returns a {@link Future} which in turn will - * notify the {@link #onLookupComplete(ChannelHandlerContext, String, Future)} on completion. - * - * @see #onLookupComplete(ChannelHandlerContext, String, Future) - */ - protected abstract Future lookup(ChannelHandlerContext ctx, String hostname) throws Exception; - - /** - * Called upon completion of the {@link #lookup(ChannelHandlerContext, String)} {@link Future}. - * - * @see #lookup(ChannelHandlerContext, String) - */ - protected abstract void onLookupComplete(ChannelHandlerContext ctx, - String hostname, Future future) throws Exception; - - private static void fireSniCompletionEvent(ChannelHandlerContext ctx, String hostname, Future future) { - Throwable cause = future.cause(); - if (cause == null) { - ctx.fireUserEventTriggered(new SniCompletionEvent(hostname)); - } else { - ctx.fireUserEventTriggered(new SniCompletionEvent(hostname, cause)); - } - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/ApplicationProtocolAccessor.java b/handler/src/main/java/io/netty/handler/ssl/ApplicationProtocolAccessor.java deleted file mode 100644 index 489d59d4d3..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/ApplicationProtocolAccessor.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.ssl; - -/** - * Provides a way to get the application-level protocol name from ALPN or NPN. - */ -interface ApplicationProtocolAccessor { - /** - * Returns the name of the negotiated application-level protocol. - * - * @return the application-level protocol name or - * {@code null} if the negotiation failed or the client does not have ALPN/NPN extension - */ - String getNegotiatedApplicationProtocol(); -} diff --git a/handler/src/main/java/io/netty/handler/ssl/ApplicationProtocolConfig.java b/handler/src/main/java/io/netty/handler/ssl/ApplicationProtocolConfig.java deleted file mode 100644 index 6c1075f118..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/ApplicationProtocolConfig.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import java.util.Collections; -import java.util.List; - -import javax.net.ssl.SSLEngine; - -import static io.netty.handler.ssl.ApplicationProtocolUtil.toList; -import static io.netty.util.internal.ObjectUtil.checkNonEmpty; -import static java.util.Objects.requireNonNull; - -/** - * Provides an {@link SSLEngine} agnostic way to configure a {@link ApplicationProtocolNegotiator}. - */ -public final class ApplicationProtocolConfig { - - /** - * The configuration that disables application protocol negotiation. - */ - public static final ApplicationProtocolConfig DISABLED = new ApplicationProtocolConfig(); - - private final List supportedProtocols; - private final Protocol protocol; - private final SelectorFailureBehavior selectorBehavior; - private final SelectedListenerFailureBehavior selectedBehavior; - - /** - * Create a new instance. - * @param protocol The application protocol functionality to use. - * @param selectorBehavior How the peer selecting the protocol should behave. - * @param selectedBehavior How the peer being notified of the selected protocol should behave. - * @param supportedProtocols The order of iteration determines the preference of support for protocols. - */ - public ApplicationProtocolConfig(Protocol protocol, SelectorFailureBehavior selectorBehavior, - SelectedListenerFailureBehavior selectedBehavior, Iterable supportedProtocols) { - this(protocol, selectorBehavior, selectedBehavior, toList(supportedProtocols)); - } - - /** - * Create a new instance. - * @param protocol The application protocol functionality to use. - * @param selectorBehavior How the peer selecting the protocol should behave. - * @param selectedBehavior How the peer being notified of the selected protocol should behave. - * @param supportedProtocols The order of iteration determines the preference of support for protocols. - */ - public ApplicationProtocolConfig(Protocol protocol, SelectorFailureBehavior selectorBehavior, - SelectedListenerFailureBehavior selectedBehavior, String... supportedProtocols) { - this(protocol, selectorBehavior, selectedBehavior, toList(supportedProtocols)); - } - - /** - * Create a new instance. - * @param protocol The application protocol functionality to use. - * @param selectorBehavior How the peer selecting the protocol should behave. - * @param selectedBehavior How the peer being notified of the selected protocol should behave. - * @param supportedProtocols The order of iteration determines the preference of support for protocols. - */ - private ApplicationProtocolConfig( - Protocol protocol, SelectorFailureBehavior selectorBehavior, - SelectedListenerFailureBehavior selectedBehavior, List supportedProtocols) { - this.supportedProtocols = Collections.unmodifiableList( - requireNonNull(supportedProtocols, "supportedProtocols")); - this.protocol = requireNonNull(protocol, "protocol"); - this.selectorBehavior = requireNonNull(selectorBehavior, "selectorBehavior"); - this.selectedBehavior = requireNonNull(selectedBehavior, "selectedBehavior"); - - if (protocol == Protocol.NONE) { - throw new IllegalArgumentException("protocol (" + Protocol.NONE + ") must not be " + Protocol.NONE + '.'); - } - checkNonEmpty(supportedProtocols, "supportedProtocols"); - } - - /** - * A special constructor that is used to instantiate {@link #DISABLED}. - */ - private ApplicationProtocolConfig() { - supportedProtocols = Collections.emptyList(); - protocol = Protocol.NONE; - selectorBehavior = SelectorFailureBehavior.CHOOSE_MY_LAST_PROTOCOL; - selectedBehavior = SelectedListenerFailureBehavior.ACCEPT; - } - - /** - * Defines which application level protocol negotiation to use. - */ - public enum Protocol { - NONE, NPN, ALPN, NPN_AND_ALPN - } - - /** - * Defines the most common behaviors for the peer that selects the application protocol. - */ - public enum SelectorFailureBehavior { - /** - * If the peer who selects the application protocol doesn't find a match this will result in the failing the - * handshake with a fatal alert. - *

- * For example in the case of ALPN this will result in a - * no_application_protocol(120) alert. - */ - FATAL_ALERT, - /** - * If the peer who selects the application protocol doesn't find a match it will pretend no to support - * the TLS extension by not advertising support for the TLS extension in the handshake. This is used in cases - * where a "best effort" is desired to talk even if there is no matching protocol. - */ - NO_ADVERTISE, - /** - * If the peer who selects the application protocol doesn't find a match it will just select the last protocol - * it advertised support for. This is used in cases where a "best effort" is desired to talk even if there - * is no matching protocol, and the assumption is the "most general" fallback protocol is typically listed last. - *

- * This may be illegal for some RFCs but was - * observed behavior by some SSL implementations, and is supported for flexibility/compatibility. - */ - CHOOSE_MY_LAST_PROTOCOL - } - - /** - * Defines the most common behaviors for the peer which is notified of the selected protocol. - */ - public enum SelectedListenerFailureBehavior { - /** - * If the peer who is notified what protocol was selected determines the selection was not matched, or the peer - * didn't advertise support for the TLS extension then the handshake will continue and the application protocol - * is assumed to be accepted. - */ - ACCEPT, - /** - * If the peer who is notified what protocol was selected determines the selection was not matched, or the peer - * didn't advertise support for the TLS extension then the handshake will be failed with a fatal alert. - */ - FATAL_ALERT, - /** - * If the peer who is notified what protocol was selected determines the selection was not matched, or the peer - * didn't advertise support for the TLS extension then the handshake will continue assuming the last protocol - * supported by this peer is used. This is used in cases where a "best effort" is desired to talk even if there - * is no matching protocol, and the assumption is the "most general" fallback protocol is typically listed last. - */ - CHOOSE_MY_LAST_PROTOCOL - } - - /** - * The application level protocols supported. - */ - public List supportedProtocols() { - return supportedProtocols; - } - - /** - * Get which application level protocol negotiation to use. - */ - public Protocol protocol() { - return protocol; - } - - /** - * Get the desired behavior for the peer who selects the application protocol. - */ - public SelectorFailureBehavior selectorFailureBehavior() { - return selectorBehavior; - } - - /** - * Get the desired behavior for the peer who is notified of the selected protocol. - */ - public SelectedListenerFailureBehavior selectedListenerFailureBehavior() { - return selectedBehavior; - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/ApplicationProtocolNegotiationHandler.java b/handler/src/main/java/io/netty/handler/ssl/ApplicationProtocolNegotiationHandler.java deleted file mode 100644 index 55e6df7a06..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/ApplicationProtocolNegotiationHandler.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import static java.util.Objects.requireNonNull; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.socket.ChannelInputShutdownEvent; -import io.netty.handler.codec.DecoderException; -import io.netty.util.internal.RecyclableArrayList; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import javax.net.ssl.SSLException; - -/** - * Configures a {@link ChannelPipeline} depending on the application-level protocol negotiation result of - * {@link SslHandler}. For example, you could configure your HTTP pipeline depending on the result of ALPN: - *

- * public class MyInitializer extends {@link ChannelInitializer}<{@link Channel}> {
- *     private final {@link SslContext} sslCtx;
- *
- *     public MyInitializer({@link SslContext} sslCtx) {
- *         this.sslCtx = sslCtx;
- *     }
- *
- *     protected void initChannel({@link Channel} ch) {
- *         {@link ChannelPipeline} p = ch.pipeline();
- *         p.addLast(sslCtx.newHandler(...)); // Adds {@link SslHandler}
- *         p.addLast(new MyNegotiationHandler());
- *     }
- * }
- *
- * public class MyNegotiationHandler extends {@link ApplicationProtocolNegotiationHandler} {
- *     public MyNegotiationHandler() {
- *         super({@link ApplicationProtocolNames}.HTTP_1_1);
- *     }
- *
- *     protected void configurePipeline({@link ChannelHandlerContext} ctx, String protocol) {
- *         if ({@link ApplicationProtocolNames}.HTTP_2.equals(protocol) {
- *             configureHttp2(ctx);
- *         } else if ({@link ApplicationProtocolNames}.HTTP_1_1.equals(protocol)) {
- *             configureHttp1(ctx);
- *         } else {
- *             throw new IllegalStateException("unknown protocol: " + protocol);
- *         }
- *     }
- * }
- * 
- */ -public abstract class ApplicationProtocolNegotiationHandler implements ChannelHandler { - - private static final InternalLogger logger = - InternalLoggerFactory.getInstance(ApplicationProtocolNegotiationHandler.class); - - private final String fallbackProtocol; - private final RecyclableArrayList bufferedMessages = RecyclableArrayList.newInstance(); - private ChannelHandlerContext ctx; - private boolean sslHandlerChecked; - - /** - * Creates a new instance with the specified fallback protocol name. - * - * @param fallbackProtocol the name of the protocol to use when - * ALPN/NPN negotiation fails or the client does not support ALPN/NPN - */ - protected ApplicationProtocolNegotiationHandler(String fallbackProtocol) { - this.fallbackProtocol = requireNonNull(fallbackProtocol, "fallbackProtocol"); - } - - @Override - public void handlerAdded(ChannelHandlerContext ctx) throws Exception { - this.ctx = ctx; - } - - @Override - public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { - fireBufferedMessages(); - bufferedMessages.recycle(); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - // Let's buffer all data until this handler will be removed from the pipeline. - bufferedMessages.add(msg); - if (!sslHandlerChecked) { - sslHandlerChecked = true; - if (ctx.pipeline().get(SslHandler.class) == null) { - // Just remove ourself if there is no SslHandler in the pipeline and so we would otherwise - // buffer forever. - removeSelfIfPresent(ctx); - } - } - } - - /** - * Process all backlog into pipeline from List. - */ - private void fireBufferedMessages() { - if (!bufferedMessages.isEmpty()) { - for (int i = 0; i < bufferedMessages.size(); i++) { - ctx.fireChannelRead(bufferedMessages.get(i)); - } - ctx.fireChannelReadComplete(); - bufferedMessages.clear(); - } - } - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (evt instanceof SslHandshakeCompletionEvent) { - // Let's first fire the event before we try to modify the pipeline. - ctx.fireUserEventTriggered(evt); - - SslHandshakeCompletionEvent handshakeEvent = (SslHandshakeCompletionEvent) evt; - try { - if (handshakeEvent.isSuccess()) { - SslHandler sslHandler = ctx.pipeline().get(SslHandler.class); - if (sslHandler == null) { - throw new IllegalStateException("cannot find an SslHandler in the pipeline (required for " - + "application-level protocol negotiation)"); - } - String protocol = sslHandler.applicationProtocol(); - configurePipeline(ctx, protocol != null ? protocol : fallbackProtocol); - } else { - // if the event is not produced because of an successful handshake we will receive the same - // exception in exceptionCaught(...) and handle it there. This will allow us more fine-grained - // control over which exception we propagate down the ChannelPipeline. - // - // See https://github.com/netty/netty/issues/10342 - } - } catch (Throwable cause) { - exceptionCaught(ctx, cause); - } finally { - // Handshake failures are handled in exceptionCaught(...). - if (handshakeEvent.isSuccess()) { - removeSelfIfPresent(ctx); - } - } - } else { - if (evt instanceof ChannelInputShutdownEvent) { - fireBufferedMessages(); - } - - ctx.fireUserEventTriggered(evt); - } - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - fireBufferedMessages(); - ctx.fireChannelInactive(); - } - - private void removeSelfIfPresent(ChannelHandlerContext ctx) { - ChannelPipeline pipeline = ctx.pipeline(); - if (pipeline.context(this) != null) { - pipeline.remove(this); - } - } - - /** - * Invoked on successful initial SSL/TLS handshake. Implement this method to configure your pipeline - * for the negotiated application-level protocol. - * - * @param protocol the name of the negotiated application-level protocol, or - * the fallback protocol name specified in the constructor call if negotiation failed or the client - * isn't aware of ALPN/NPN extension - */ - protected abstract void configurePipeline(ChannelHandlerContext ctx, String protocol) throws Exception; - - /** - * Invoked on failed initial SSL/TLS handshake. - */ - protected void handshakeFailure(ChannelHandlerContext ctx, Throwable cause) throws Exception { - logger.warn("{} TLS handshake failed:", ctx.channel(), cause); - ctx.close(); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - Throwable wrapped; - if (cause instanceof DecoderException && ((wrapped = cause.getCause()) instanceof SSLException)) { - try { - handshakeFailure(ctx, wrapped); - return; - } finally { - removeSelfIfPresent(ctx); - } - } - logger.warn("{} Failed to select the application-level protocol:", ctx.channel(), cause); - ctx.fireExceptionCaught(cause); - ctx.close(); - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/ApplicationProtocolNegotiator.java b/handler/src/main/java/io/netty/handler/ssl/ApplicationProtocolNegotiator.java deleted file mode 100644 index 2d5820de79..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/ApplicationProtocolNegotiator.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import java.util.List; - -/** - * Interface to support Application Protocol Negotiation. - *

- * Default implementations are provided for: - *

- * - * @deprecated use {@link ApplicationProtocolConfig} - */ -@SuppressWarnings("deprecation") -public interface ApplicationProtocolNegotiator { - /** - * Get the collection of application protocols supported by this application (in preference order). - */ - List protocols(); -} diff --git a/handler/src/main/java/io/netty/handler/ssl/ApplicationProtocolUtil.java b/handler/src/main/java/io/netty/handler/ssl/ApplicationProtocolUtil.java deleted file mode 100644 index fb610a2012..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/ApplicationProtocolUtil.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import static io.netty.util.internal.ObjectUtil.checkNonEmpty; - -import java.util.ArrayList; -import java.util.List; - -/** - * Utility class for application protocol common operations. - */ -final class ApplicationProtocolUtil { - private static final int DEFAULT_LIST_SIZE = 2; - - private ApplicationProtocolUtil() { - } - - static List toList(Iterable protocols) { - return toList(DEFAULT_LIST_SIZE, protocols); - } - - static List toList(int initialListSize, Iterable protocols) { - if (protocols == null) { - return null; - } - - List result = new ArrayList<>(initialListSize); - for (String p : protocols) { - result.add(checkNonEmpty(p, "p")); - } - - return checkNonEmpty(result, "result"); - } - - static List toList(String... protocols) { - return toList(DEFAULT_LIST_SIZE, protocols); - } - - static List toList(int initialListSize, String... protocols) { - if (protocols == null) { - return null; - } - - List result = new ArrayList<>(initialListSize); - for (String p : protocols) { - result.add(checkNonEmpty(p, "p")); - } - - return checkNonEmpty(result, "result"); - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/AsyncRunnable.java b/handler/src/main/java/io/netty/handler/ssl/AsyncRunnable.java deleted file mode 100644 index 6fb620e227..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/AsyncRunnable.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -interface AsyncRunnable extends Runnable { - void run(Runnable completionCallback); -} diff --git a/handler/src/main/java/io/netty/handler/ssl/BouncyCastle.java b/handler/src/main/java/io/netty/handler/ssl/BouncyCastle.java deleted file mode 100644 index a90bdd19ec..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/BouncyCastle.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import javax.net.ssl.SSLEngine; - -/** - * Contains methods that can be used to detect if BouncyCastle is usable. - */ -final class BouncyCastle { - - private static final boolean BOUNCY_CASTLE_ON_CLASSPATH; - - static { - boolean bcOnClasspath = false; - try { - Class.forName("org.bouncycastle.jsse.provider.BouncyCastleJsseProvider"); - bcOnClasspath = true; - } catch (Throwable ignore) { - // ignore - } - BOUNCY_CASTLE_ON_CLASSPATH = bcOnClasspath; - } - - /** - * Indicates whether or not BouncyCastle is available on the current system. - */ - static boolean isAvailable() { - return BOUNCY_CASTLE_ON_CLASSPATH; - } - - /** - * Indicates whether or not BouncyCastle is the underlying SSLEngine. - */ - static boolean isInUse(SSLEngine engine) { - return engine.getClass().getPackage().getName().startsWith("org.bouncycastle.jsse.provider"); - } - - private BouncyCastle() { - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/BouncyCastleAlpnSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/BouncyCastleAlpnSslEngine.java deleted file mode 100644 index 35f3ee8669..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/BouncyCastleAlpnSslEngine.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import javax.net.ssl.SSLEngine; -import java.util.List; -import java.util.function.BiConsumer; -import java.util.function.BiFunction; - -final class BouncyCastleAlpnSslEngine extends JdkAlpnSslEngine { - - BouncyCastleAlpnSslEngine(SSLEngine engine, - @SuppressWarnings("deprecation") JdkApplicationProtocolNegotiator applicationNegotiator, - boolean isServer) { - super(engine, applicationNegotiator, isServer, - new BiConsumer() { - @Override - public void accept(SSLEngine e, AlpnSelector s) { - BouncyCastleAlpnSslUtils.setHandshakeApplicationProtocolSelector(e, s); - } - }, - new BiConsumer>() { - @Override - public void accept(SSLEngine e, List p) { - BouncyCastleAlpnSslUtils.setApplicationProtocols(e, p); - } - }); - } - - public String getApplicationProtocol() { - return BouncyCastleAlpnSslUtils.getApplicationProtocol(getWrappedEngine()); - } - - public String getHandshakeApplicationProtocol() { - return BouncyCastleAlpnSslUtils.getHandshakeApplicationProtocol(getWrappedEngine()); - } - - public void setHandshakeApplicationProtocolSelector(BiFunction, String> selector) { - BouncyCastleAlpnSslUtils.setHandshakeApplicationProtocolSelector(getWrappedEngine(), selector); - } - - public BiFunction, String> getHandshakeApplicationProtocolSelector() { - return BouncyCastleAlpnSslUtils.getHandshakeApplicationProtocolSelector(getWrappedEngine()); - } - -} diff --git a/handler/src/main/java/io/netty/handler/ssl/BouncyCastleAlpnSslUtils.java b/handler/src/main/java/io/netty/handler/ssl/BouncyCastleAlpnSslUtils.java deleted file mode 100644 index 902e65bea2..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/BouncyCastleAlpnSslUtils.java +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - - -import io.netty.util.internal.EmptyArrays; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLParameters; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.security.AccessController; -import java.security.PrivilegedExceptionAction; -import java.util.List; -import java.util.function.BiFunction; - -import static io.netty.handler.ssl.SslUtils.getSSLContext; - -final class BouncyCastleAlpnSslUtils { - private static final InternalLogger logger = InternalLoggerFactory.getInstance(BouncyCastleAlpnSslUtils.class); - private static final Class BC_SSL_PARAMETERS; - private static final Method SET_PARAMETERS; - private static final Method SET_APPLICATION_PROTOCOLS; - private static final Method GET_APPLICATION_PROTOCOL; - private static final Method GET_HANDSHAKE_APPLICATION_PROTOCOL; - private static final Method SET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR; - private static final Method GET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR; - private static final Class BC_APPLICATION_PROTOCOL_SELECTOR; - private static final Method BC_APPLICATION_PROTOCOL_SELECTOR_SELECT; - - static { - Class bcSslEngine; - Class bcSslParameters; - Method setParameters; - Method setApplicationProtocols; - Method getApplicationProtocol; - Method getHandshakeApplicationProtocol; - Method setHandshakeApplicationProtocolSelector; - Method getHandshakeApplicationProtocolSelector; - Method bcApplicationProtocolSelectorSelect; - Class bcApplicationProtocolSelector; - - try { - bcSslEngine = Class.forName("org.bouncycastle.jsse.BCSSLEngine"); - final Class testBCSslEngine = bcSslEngine; - - bcSslParameters = Class.forName("org.bouncycastle.jsse.BCSSLParameters"); - Object bcSslParametersInstance = bcSslParameters.newInstance(); - final Class testBCSslParameters = bcSslParameters; - - bcApplicationProtocolSelector = - Class.forName("org.bouncycastle.jsse.BCApplicationProtocolSelector"); - - final Class testBCApplicationProtocolSelector = bcApplicationProtocolSelector; - - bcApplicationProtocolSelectorSelect = AccessController.doPrivileged( - new PrivilegedExceptionAction() { - @Override - public Method run() throws Exception { - return testBCApplicationProtocolSelector.getMethod("select", Object.class, List.class); - } - }); - - SSLContext context = getSSLContext("BCJSSE"); - SSLEngine engine = context.createSSLEngine(); - setParameters = AccessController.doPrivileged(new PrivilegedExceptionAction() { - @Override - public Method run() throws Exception { - return testBCSslEngine.getMethod("setParameters", testBCSslParameters); - } - }); - setParameters.invoke(engine, bcSslParametersInstance); - - setApplicationProtocols = AccessController.doPrivileged(new PrivilegedExceptionAction() { - @Override - public Method run() throws Exception { - return testBCSslParameters.getMethod("setApplicationProtocols", String[].class); - } - }); - setApplicationProtocols.invoke(bcSslParametersInstance, new Object[]{EmptyArrays.EMPTY_STRINGS}); - - getApplicationProtocol = AccessController.doPrivileged(new PrivilegedExceptionAction() { - @Override - public Method run() throws Exception { - return testBCSslEngine.getMethod("getApplicationProtocol"); - } - }); - getApplicationProtocol.invoke(engine); - - getHandshakeApplicationProtocol = AccessController.doPrivileged(new PrivilegedExceptionAction() { - @Override - public Method run() throws Exception { - return testBCSslEngine.getMethod("getHandshakeApplicationProtocol"); - } - }); - getHandshakeApplicationProtocol.invoke(engine); - - setHandshakeApplicationProtocolSelector = - AccessController.doPrivileged(new PrivilegedExceptionAction() { - @Override - public Method run() throws Exception { - return testBCSslEngine.getMethod("setBCHandshakeApplicationProtocolSelector", - testBCApplicationProtocolSelector); - } - }); - - getHandshakeApplicationProtocolSelector = - AccessController.doPrivileged(new PrivilegedExceptionAction() { - @Override - public Method run() throws Exception { - return testBCSslEngine.getMethod("getBCHandshakeApplicationProtocolSelector"); - } - }); - getHandshakeApplicationProtocolSelector.invoke(engine); - - } catch (Throwable t) { - logger.error("Unable to initialize BouncyCastleAlpnSslUtils.", t); - bcSslParameters = null; - setParameters = null; - setApplicationProtocols = null; - getApplicationProtocol = null; - getHandshakeApplicationProtocol = null; - setHandshakeApplicationProtocolSelector = null; - getHandshakeApplicationProtocolSelector = null; - bcApplicationProtocolSelectorSelect = null; - bcApplicationProtocolSelector = null; - } - BC_SSL_PARAMETERS = bcSslParameters; - SET_PARAMETERS = setParameters; - SET_APPLICATION_PROTOCOLS = setApplicationProtocols; - GET_APPLICATION_PROTOCOL = getApplicationProtocol; - GET_HANDSHAKE_APPLICATION_PROTOCOL = getHandshakeApplicationProtocol; - SET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR = setHandshakeApplicationProtocolSelector; - GET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR = getHandshakeApplicationProtocolSelector; - BC_APPLICATION_PROTOCOL_SELECTOR_SELECT = bcApplicationProtocolSelectorSelect; - BC_APPLICATION_PROTOCOL_SELECTOR = bcApplicationProtocolSelector; - } - - private BouncyCastleAlpnSslUtils() { - } - - static String getApplicationProtocol(SSLEngine sslEngine) { - try { - return (String) GET_APPLICATION_PROTOCOL.invoke(sslEngine); - } catch (UnsupportedOperationException ex) { - throw ex; - } catch (Exception ex) { - throw new IllegalStateException(ex); - } - } - - static void setApplicationProtocols(SSLEngine engine, List supportedProtocols) { - SSLParameters parameters = engine.getSSLParameters(); - - String[] protocolArray = supportedProtocols.toArray(EmptyArrays.EMPTY_STRINGS); - try { - Object bcSslParameters = BC_SSL_PARAMETERS.newInstance(); - SET_APPLICATION_PROTOCOLS.invoke(bcSslParameters, new Object[]{protocolArray}); - SET_PARAMETERS.invoke(engine, bcSslParameters); - } catch (UnsupportedOperationException ex) { - throw ex; - } catch (Exception ex) { - throw new IllegalStateException(ex); - } - engine.setSSLParameters(parameters); - } - - static String getHandshakeApplicationProtocol(SSLEngine sslEngine) { - try { - return (String) GET_HANDSHAKE_APPLICATION_PROTOCOL.invoke(sslEngine); - } catch (UnsupportedOperationException ex) { - throw ex; - } catch (Exception ex) { - throw new IllegalStateException(ex); - } - } - - static void setHandshakeApplicationProtocolSelector( - SSLEngine engine, final BiFunction, String> selector) { - try { - Object selectorProxyInstance = Proxy.newProxyInstance( - BouncyCastleAlpnSslUtils.class.getClassLoader(), - new Class[]{BC_APPLICATION_PROTOCOL_SELECTOR}, - new InvocationHandler() { - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - if (method.getName().equals("select")) { - try { - return selector.apply((SSLEngine) args[0], (List) args[1]); - } catch (ClassCastException e) { - throw new RuntimeException("BCApplicationProtocolSelector select method " + - "parameter of invalid type.", e); - } - } else { - throw new UnsupportedOperationException(String.format("Method '%s' not supported.", - method.getName())); - } - } - }); - - SET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR.invoke(engine, selectorProxyInstance); - } catch (UnsupportedOperationException ex) { - throw ex; - } catch (Exception ex) { - throw new IllegalStateException(ex); - } - } - - @SuppressWarnings("unchecked") - static BiFunction, String> getHandshakeApplicationProtocolSelector(SSLEngine engine) { - try { - final Object selector = GET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR.invoke(engine); - return new BiFunction, String>() { - - @Override - public String apply(SSLEngine sslEngine, List strings) { - try { - return (String) BC_APPLICATION_PROTOCOL_SELECTOR_SELECT.invoke(selector, sslEngine, - strings); - } catch (Exception e) { - throw new RuntimeException("Could not call getHandshakeApplicationProtocolSelector", e); - } - } - }; - - } catch (UnsupportedOperationException ex) { - throw ex; - } catch (Exception ex) { - throw new IllegalStateException(ex); - } - } - -} diff --git a/handler/src/main/java/io/netty/handler/ssl/CipherSuiteConverter.java b/handler/src/main/java/io/netty/handler/ssl/CipherSuiteConverter.java deleted file mode 100644 index 1ccd7e5a00..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/CipherSuiteConverter.java +++ /dev/null @@ -1,496 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.ssl; - -import io.netty.util.internal.UnstableApi; -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.ConcurrentHashMap; -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. - * - * @see Wikipedia page about cipher suite - */ -@UnstableApi -public final class CipherSuiteConverter { - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(CipherSuiteConverter.class); - - /** - * A_B_WITH_C_D, where: - * - * A - TLS or SSL (protocol) - * B - handshake algorithm (key exchange and authentication algorithms to be precise) - * C - bulk cipher - * D - HMAC algorithm - * - * This regular expression assumes that: - * - * 1) A is always TLS or SSL, and - * 2) D is always a single word. - */ - private static final Pattern JAVA_CIPHERSUITE_PATTERN = - Pattern.compile("^(?:TLS|SSL)_((?:(?!_WITH_).)+)_WITH_(.*)_(.*)$"); - - /** - * A-B-C, where: - * - * A - handshake algorithm (key exchange and authentication algorithms to be precise) - * B - bulk cipher - * C - HMAC algorithm - * - * This regular expression assumes that: - * - * 1) A has some deterministic pattern as shown below, and - * 2) C is always a single word - */ - private static final Pattern OPENSSL_CIPHERSUITE_PATTERN = - // Be very careful not to break the indentation while editing. - Pattern.compile( - "^(?:(" + // BEGIN handshake algorithm - "(?:(?:EXP-)?" + - "(?:" + - "(?:DHE|EDH|ECDH|ECDHE|SRP|RSA)-(?:DSS|RSA|ECDSA|PSK)|" + - "(?:ADH|AECDH|KRB5|PSK|SRP)" + - ')' + - ")|" + - "EXP" + - ")-)?" + // END handshake algorithm - "(.*)-(.*)$"); - - private static final Pattern JAVA_AES_CBC_PATTERN = Pattern.compile("^(AES)_([0-9]+)_CBC$"); - private static final Pattern JAVA_AES_PATTERN = Pattern.compile("^(AES)_([0-9]+)_(.*)$"); - private static final Pattern OPENSSL_AES_CBC_PATTERN = Pattern.compile("^(AES)([0-9]+)$"); - private static final Pattern OPENSSL_AES_PATTERN = Pattern.compile("^(AES)([0-9]+)-(.*)$"); - - /** - * Java-to-OpenSSL cipher suite conversion map - * Note that the Java cipher suite has the protocol prefix (TLS_, SSL_) - */ - private static final ConcurrentMap j2o = new ConcurrentHashMap<>(); - - /** - * OpenSSL-to-Java cipher suite conversion map. - * Note that one OpenSSL cipher suite can be converted to more than one Java cipher suites because - * a Java cipher suite has the protocol name prefix (TLS_, SSL_) - */ - private static final ConcurrentMap> o2j = new ConcurrentHashMap<>(); - - 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. - */ - static void clearCache() { - j2o.clear(); - o2j.clear(); - } - - /** - * Tests if the specified key-value pair has been cached in Java-to-OpenSSL cache. - */ - static boolean isJ2OCached(String key, String value) { - return value.equals(j2o.get(key)); - } - - /** - * Tests if the specified key-value pair has been cached in OpenSSL-to-Java cache. - */ - static boolean isO2JCached(String key, String protocol, String value) { - Map p2j = o2j.get(key); - if (p2j == null) { - return false; - } else { - return value.equals(p2j.get(protocol)); - } - } - - /** - * Converts the specified Java cipher suite to its corresponding OpenSSL cipher suite name. - * - * @return {@code null} if the conversion has failed - */ - public static String toOpenSsl(String javaCipherSuite, boolean boringSSL) { - String converted = j2o.get(javaCipherSuite); - if (converted != null) { - return converted; - } - return cacheFromJava(javaCipherSuite, boringSSL); - } - - 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; - } - - // Cache the mapping. - j2o.putIfAbsent(javaCipherSuite, openSslCipherSuite); - - // Cache the reverse mapping after stripping the protocol prefix (TLS_ or SSL_) - final String javaCipherSuiteSuffix = javaCipherSuite.substring(4); - Map p2j = new HashMap<>(4); - p2j.put("", javaCipherSuiteSuffix); - p2j.put("SSL", "SSL_" + javaCipherSuiteSuffix); - p2j.put("TLS", "TLS_" + javaCipherSuiteSuffix); - o2j.put(openSslCipherSuite, p2j); - - logger.debug("Cipher suite mapping: {} => {}", javaCipherSuite, openSslCipherSuite); - - return openSslCipherSuite; - } - - 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; - } - - String handshakeAlgo = toOpenSslHandshakeAlgo(m.group(1)); - String bulkCipher = toOpenSslBulkCipher(m.group(2)); - String hmacAlgo = toOpenSslHmacAlgo(m.group(3)); - if (handshakeAlgo.isEmpty()) { - return bulkCipher + '-' + hmacAlgo; - } else if (bulkCipher.contains("CHACHA20")) { - return handshakeAlgo + '-' + bulkCipher; - } else { - return handshakeAlgo + '-' + bulkCipher + '-' + hmacAlgo; - } - } - - private static String toOpenSslHandshakeAlgo(String handshakeAlgo) { - final boolean export = handshakeAlgo.endsWith("_EXPORT"); - if (export) { - handshakeAlgo = handshakeAlgo.substring(0, handshakeAlgo.length() - 7); - } - - if ("RSA".equals(handshakeAlgo)) { - handshakeAlgo = ""; - } else if (handshakeAlgo.endsWith("_anon")) { - handshakeAlgo = 'A' + handshakeAlgo.substring(0, handshakeAlgo.length() - 5); - } - - if (export) { - if (handshakeAlgo.isEmpty()) { - handshakeAlgo = "EXP"; - } else { - handshakeAlgo = "EXP-" + handshakeAlgo; - } - } - - return handshakeAlgo.replace('_', '-'); - } - - private static String toOpenSslBulkCipher(String bulkCipher) { - if (bulkCipher.startsWith("AES_")) { - Matcher m = JAVA_AES_CBC_PATTERN.matcher(bulkCipher); - if (m.matches()) { - return m.replaceFirst("$1$2"); - } - - m = JAVA_AES_PATTERN.matcher(bulkCipher); - if (m.matches()) { - return m.replaceFirst("$1$2-$3"); - } - } - - if ("3DES_EDE_CBC".equals(bulkCipher)) { - return "DES-CBC3"; - } - - if ("RC4_128".equals(bulkCipher) || "RC4_40".equals(bulkCipher)) { - return "RC4"; - } - - if ("DES40_CBC".equals(bulkCipher) || "DES_CBC_40".equals(bulkCipher)) { - return "DES-CBC"; - } - - if ("RC2_CBC_40".equals(bulkCipher)) { - return "RC2-CBC"; - } - - return bulkCipher.replace('_', '-'); - } - - private static String toOpenSslHmacAlgo(String hmacAlgo) { - // Java and OpenSSL use the same algorithm names for: - // - // * SHA - // * SHA256 - // * MD5 - // - return hmacAlgo; - } - - /** - * Convert from OpenSSL cipher suite name convention to java cipher suite name convention. - * @param openSslCipherSuite An OpenSSL cipher suite name. - * @param protocol The cryptographic protocol (i.e. SSL, TLS, ...). - * @return The translated cipher suite name according to java conventions. This will not be {@code null}. - */ - public static String toJava(String openSslCipherSuite, String protocol) { - Map p2j = o2j.get(openSslCipherSuite); - if (p2j == null) { - p2j = cacheFromOpenSsl(openSslCipherSuite); - // This may happen if this method is queried when OpenSSL doesn't yet have a cipher setup. It will return - // "(NONE)" in this case. - if (p2j == null) { - return null; - } - } - - String javaCipherSuite = p2j.get(protocol); - if (javaCipherSuite == null) { - String cipher = p2j.get(""); - if (cipher == null) { - return null; - } - javaCipherSuite = protocol + '_' + cipher; - } - - return javaCipherSuite; - } - - private static Map cacheFromOpenSsl(String openSslCipherSuite) { - Map converted = o2jTls13.get(openSslCipherSuite); - if (converted != null) { - return converted; - } - - String javaCipherSuiteSuffix = toJavaUncached0(openSslCipherSuite, false); - if (javaCipherSuiteSuffix == null) { - return null; - } - - final String javaCipherSuiteSsl = "SSL_" + javaCipherSuiteSuffix; - final String javaCipherSuiteTls = "TLS_" + javaCipherSuiteSuffix; - - // Cache the mapping. - final Map p2j = new HashMap<>(4); - p2j.put("", javaCipherSuiteSuffix); - p2j.put("SSL", javaCipherSuiteSsl); - p2j.put("TLS", javaCipherSuiteTls); - o2j.putIfAbsent(openSslCipherSuite, p2j); - - // Cache the reverse mapping after adding the protocol prefix (TLS_ or SSL_) - j2o.putIfAbsent(javaCipherSuiteTls, openSslCipherSuite); - j2o.putIfAbsent(javaCipherSuiteSsl, openSslCipherSuite); - - logger.debug("Cipher suite mapping: {} => {}", javaCipherSuiteTls, openSslCipherSuite); - logger.debug("Cipher suite mapping: {} => {}", javaCipherSuiteSsl, openSslCipherSuite); - - return p2j; - } - - 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; - } - - String handshakeAlgo = m.group(1); - final boolean export; - if (handshakeAlgo == null) { - handshakeAlgo = ""; - export = false; - } else if (handshakeAlgo.startsWith("EXP-")) { - handshakeAlgo = handshakeAlgo.substring(4); - export = true; - } else if ("EXP".equals(handshakeAlgo)) { - handshakeAlgo = ""; - export = true; - } else { - export = false; - } - - handshakeAlgo = toJavaHandshakeAlgo(handshakeAlgo, export); - String bulkCipher = toJavaBulkCipher(m.group(2), export); - String hmacAlgo = toJavaHmacAlgo(m.group(3)); - - String javaCipherSuite = handshakeAlgo + "_WITH_" + bulkCipher + '_' + hmacAlgo; - // For historical reasons the CHACHA20 ciphers do not follow OpenSSL's custom naming convention and omits the - // HMAC algorithm portion of the name. There is currently no way to derive this information because it is - // omitted from the OpenSSL cipher name, but they currently all use SHA256 for HMAC [1]. - // [1] https://www.openssl.org/docs/man1.1.0/apps/ciphers.html - return bulkCipher.contains("CHACHA20") ? javaCipherSuite + "_SHA256" : javaCipherSuite; - } - - private static String toJavaHandshakeAlgo(String handshakeAlgo, boolean export) { - if (handshakeAlgo.isEmpty()) { - handshakeAlgo = "RSA"; - } else if ("ADH".equals(handshakeAlgo)) { - handshakeAlgo = "DH_anon"; - } else if ("AECDH".equals(handshakeAlgo)) { - handshakeAlgo = "ECDH_anon"; - } - - handshakeAlgo = handshakeAlgo.replace('-', '_'); - if (export) { - return handshakeAlgo + "_EXPORT"; - } else { - return handshakeAlgo; - } - } - - private static String toJavaBulkCipher(String bulkCipher, boolean export) { - if (bulkCipher.startsWith("AES")) { - Matcher m = OPENSSL_AES_CBC_PATTERN.matcher(bulkCipher); - if (m.matches()) { - return m.replaceFirst("$1_$2_CBC"); - } - - m = OPENSSL_AES_PATTERN.matcher(bulkCipher); - if (m.matches()) { - return m.replaceFirst("$1_$2_$3"); - } - } - - if ("DES-CBC3".equals(bulkCipher)) { - return "3DES_EDE_CBC"; - } - - if ("RC4".equals(bulkCipher)) { - if (export) { - return "RC4_40"; - } else { - return "RC4_128"; - } - } - - if ("DES-CBC".equals(bulkCipher)) { - if (export) { - return "DES_CBC_40"; - } else { - return "DES_CBC"; - } - } - - if ("RC2-CBC".equals(bulkCipher)) { - if (export) { - return "RC2_CBC_40"; - } else { - return "RC2_CBC"; - } - } - - return bulkCipher.replace('-', '_'); - } - - private static String toJavaHmacAlgo(String hmacAlgo) { - // Java and OpenSSL use the same algorithm names for: - // - // * SHA - // * SHA256 - // * MD5 - // - 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, boolean boringSSL) { - for (String c: cipherSuites) { - if (c == null) { - break; - } - - String converted = toOpenSsl(c, boringSSL); - if (converted == null) { - converted = c; - } - - if (!OpenSsl.isCipherSuiteAvailable(converted)) { - throw new IllegalArgumentException("unsupported cipher suite: " + c + '(' + converted + ')'); - } - - if (SslUtils.isTLSv13Cipher(converted) || SslUtils.isTLSv13Cipher(c)) { - 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/CipherSuiteFilter.java b/handler/src/main/java/io/netty/handler/ssl/CipherSuiteFilter.java deleted file mode 100644 index 5e47182320..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/CipherSuiteFilter.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import javax.net.ssl.SSLEngine; -import java.util.List; -import java.util.Set; - -/** - * Provides a means to filter the supplied cipher suite based upon the supported and default cipher suites. - */ -public interface CipherSuiteFilter { - /** - * Filter the requested {@code ciphers} based upon other cipher characteristics. - * @param ciphers The requested ciphers - * @param defaultCiphers The default recommended ciphers for the current {@link SSLEngine} as determined by Netty - * @param supportedCiphers The supported ciphers for the current {@link SSLEngine} - * @return The filter list of ciphers. Must not return {@code null}. - */ - String[] filterCipherSuites(Iterable ciphers, List defaultCiphers, Set supportedCiphers); -} diff --git a/handler/src/main/java/io/netty/handler/ssl/Ciphers.java b/handler/src/main/java/io/netty/handler/ssl/Ciphers.java deleted file mode 100644 index a615007c79..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/Ciphers.java +++ /dev/null @@ -1,754 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -/** - * Cipher suites - */ -public final class Ciphers { - - /** - * TLS_AES_256_GCM_SHA384 - */ - public static final String TLS_AES_256_GCM_SHA384 = "TLS_AES_256_GCM_SHA384"; - - /** - * TLS_CHACHA20_POLY1305_SHA256 - */ - public static final String TLS_CHACHA20_POLY1305_SHA256 = "TLS_CHACHA20_POLY1305_SHA256"; - - /** - * TLS_AES_128_GCM_SHA256 - */ - public static final String TLS_AES_128_GCM_SHA256 = "TLS_AES_128_GCM_SHA256"; - - /** - * TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 - */ - public static final String TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"; - - /** - * TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 - */ - public static final String TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"; - - /** - * TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 - */ - public static final String TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 = "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384"; - - /** - * TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 - */ - public static final String TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384"; - - /** - * TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 - */ - public static final String TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = - "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256"; - - /** - * TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 - */ - public static final String TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = - "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256"; - - /** - * TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 - */ - public static final String TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256"; - - /** - * TLS_ECDHE_ECDSA_WITH_AES_256_CBC_CCM8 - */ - public static final String TLS_ECDHE_ECDSA_WITH_AES_256_CBC_CCM8 = "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_CCM8"; - - /** - * TLS_ECDHE_ECDSA_WITH_AES_256_CBC_CCM - */ - public static final String TLS_ECDHE_ECDSA_WITH_AES_256_CBC_CCM = "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_CCM"; - - /** - * TLS_DHE_RSA_WITH_AES_256_CBC_CCM8 - */ - public static final String TLS_DHE_RSA_WITH_AES_256_CBC_CCM8 = "TLS_DHE_RSA_WITH_AES_256_CBC_CCM8"; - - /** - * TLS_DHE_RSA_WITH_AES_256_CBC_CCM - */ - public static final String TLS_DHE_RSA_WITH_AES_256_CBC_CCM = "TLS_DHE_RSA_WITH_AES_256_CBC_CCM"; - - /** - * TLS_ECDHE_ECDSA_WITH_ARIA256_GCM_SHA384 - */ - public static final String TLS_ECDHE_ECDSA_WITH_ARIA256_GCM_SHA384 = "TLS_ECDHE_ECDSA_WITH_ARIA256_GCM_SHA384"; - - /** - * TLS_RSA_WITH_ECDHE_ARIA256_GCM_SHA384 - */ - public static final String TLS_RSA_WITH_ECDHE_ARIA256_GCM_SHA384 = "TLS_RSA_WITH_ECDHE_ARIA256_GCM_SHA384"; - - /** - * TLS_DHE_DSS_WITH_ARIA256_GCM_SHA384 - */ - public static final String TLS_DHE_DSS_WITH_ARIA256_GCM_SHA384 = "TLS_DHE_DSS_WITH_ARIA256_GCM_SHA384"; - - /** - * TLS_DHE_RSA_WITH_ARIA256_GCM_SHA384 - */ - public static final String TLS_DHE_RSA_WITH_ARIA256_GCM_SHA384 = "TLS_DHE_RSA_WITH_ARIA256_GCM_SHA384"; - - /** - * TLS_DH_anon_WITH_AES_256_GCM_SHA384 - */ - public static final String TLS_DH_anon_WITH_AES_256_GCM_SHA384 = "TLS_DH_anon_WITH_AES_256_GCM_SHA384"; - - /** - * TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 - */ - public static final String TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"; - - /** - * TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 - */ - public static final String TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"; - - /** - * TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 - */ - public static final String TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 = "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256"; - - /** - * TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 - */ - public static final String TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256"; - - /** - * TLS_ECDHE_ECDSA_WITH_AES_128_CBC_CCM8 - */ - public static final String TLS_ECDHE_ECDSA_WITH_AES_128_CBC_CCM8 = "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_CCM8"; - - /** - * TLS_ECDHE_ECDSA_WITH_AES_128_CBC_CCM - */ - public static final String TLS_ECDHE_ECDSA_WITH_AES_128_CBC_CCM = "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_CCM"; - - /** - * TLS_DHE_RSA_WITH_AES_128_CBC_CCM8 - */ - public static final String TLS_DHE_RSA_WITH_AES_128_CBC_CCM8 = "TLS_DHE_RSA_WITH_AES_128_CBC_CCM8"; - - /** - * TLS_DHE_RSA_WITH_AES_128_CBC_CCM - */ - public static final String TLS_DHE_RSA_WITH_AES_128_CBC_CCM = "TLS_DHE_RSA_WITH_AES_128_CBC_CCM"; - - /** - * TLS_ECDHE_ECDSA_WITH_ARIA128_GCM_SHA256 - */ - public static final String TLS_ECDHE_ECDSA_WITH_ARIA128_GCM_SHA256 = "TLS_ECDHE_ECDSA_WITH_ARIA128_GCM_SHA256"; - - /** - * TLS_RSA_WITH_ECDHE_ARIA128_GCM_SHA256 - */ - public static final String TLS_RSA_WITH_ECDHE_ARIA128_GCM_SHA256 = "TLS_RSA_WITH_ECDHE_ARIA128_GCM_SHA256"; - - /** - * TLS_DHE_DSS_WITH_ARIA128_GCM_SHA256 - */ - public static final String TLS_DHE_DSS_WITH_ARIA128_GCM_SHA256 = "TLS_DHE_DSS_WITH_ARIA128_GCM_SHA256"; - - /** - * TLS_DHE_RSA_WITH_ARIA128_GCM_SHA256 - */ - public static final String TLS_DHE_RSA_WITH_ARIA128_GCM_SHA256 = "TLS_DHE_RSA_WITH_ARIA128_GCM_SHA256"; - - /** - * TLS_DH_anon_WITH_AES_128_GCM_SHA256 - */ - public static final String TLS_DH_anon_WITH_AES_128_GCM_SHA256 = "TLS_DH_anon_WITH_AES_128_GCM_SHA256"; - - /** - * TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 - */ - public static final String TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384"; - - /** - * TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 - */ - public static final String TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 = "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384"; - - /** - * TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 - */ - public static final String TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256"; - - /** - * TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 - */ - public static final String TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256"; - - /** - * TLS_ECDHE_ECDSA_WITH_CAMELLIA256_SHA384 - */ - public static final String TLS_ECDHE_ECDSA_WITH_CAMELLIA256_SHA384 = "TLS_ECDHE_ECDSA_WITH_CAMELLIA256_SHA384"; - - /** - * TLS_ECDHE_RSA_WITH_CAMELLIA256_SHA384 - */ - public static final String TLS_ECDHE_RSA_WITH_CAMELLIA256_SHA384 = "TLS_ECDHE_RSA_WITH_CAMELLIA256_SHA384"; - - /** - * TLS_DHE_RSA_WITH_CAMELLIA256_SHA256 - */ - public static final String TLS_DHE_RSA_WITH_CAMELLIA256_SHA256 = "TLS_DHE_RSA_WITH_CAMELLIA256_SHA256"; - - /** - * TLS_DHE_DSS_WITH_CAMELLIA256_SHA256 - */ - public static final String TLS_DHE_DSS_WITH_CAMELLIA256_SHA256 = "TLS_DHE_DSS_WITH_CAMELLIA256_SHA256"; - - /** - * TLS_DH_anon_WITH_AES_256_CBC_SHA256 - */ - public static final String TLS_DH_anon_WITH_AES_256_CBC_SHA256 = "TLS_DH_anon_WITH_AES_256_CBC_SHA256"; - - /** - * TLS_DH_anon_WITH_CAMELLIA256_SHA256 - */ - public static final String TLS_DH_anon_WITH_CAMELLIA256_SHA256 = "TLS_DH_anon_WITH_CAMELLIA256_SHA256"; - - /** - * TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 - */ - public static final String TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256"; - - /** - * TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 - */ - public static final String TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"; - - /** - * TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 - */ - public static final String TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256"; - - /** - * TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 - */ - public static final String TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256"; - - /** - * TLS_ECDHE_ECDSA_WITH_CAMELLIA128_SHA256 - */ - public static final String TLS_ECDHE_ECDSA_WITH_CAMELLIA128_SHA256 = "TLS_ECDHE_ECDSA_WITH_CAMELLIA128_SHA256"; - - /** - * TLS_ECDHE_RSA_WITH_CAMELLIA128_SHA256 - */ - public static final String TLS_ECDHE_RSA_WITH_CAMELLIA128_SHA256 = "TLS_ECDHE_RSA_WITH_CAMELLIA128_SHA256"; - - /** - * TLS_DHE_RSA_WITH_CAMELLIA128_SHA256 - */ - public static final String TLS_DHE_RSA_WITH_CAMELLIA128_SHA256 = "TLS_DHE_RSA_WITH_CAMELLIA128_SHA256"; - - /** - * TLS_DHE_DSS_WITH_CAMELLIA128_SHA256 - */ - public static final String TLS_DHE_DSS_WITH_CAMELLIA128_SHA256 = "TLS_DHE_DSS_WITH_CAMELLIA128_SHA256"; - - /** - * TLS_DH_anon_WITH_AES_128_CBC_SHA256 - */ - public static final String TLS_DH_anon_WITH_AES_128_CBC_SHA256 = "TLS_DH_anon_WITH_AES_128_CBC_SHA256"; - - /** - * TLS_DH_anon_WITH_CAMELLIA128_SHA256 - */ - public static final String TLS_DH_anon_WITH_CAMELLIA128_SHA256 = "TLS_DH_anon_WITH_CAMELLIA128_SHA256"; - - /** - * TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA - */ - public static final String TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA"; - - /** - * TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA - */ - public static final String TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA"; - - /** - * TLS_DHE_RSA_WITH_AES_256_CBC_SHA - */ - public static final String TLS_DHE_RSA_WITH_AES_256_CBC_SHA = "TLS_DHE_RSA_WITH_AES_256_CBC_SHA"; - - /** - * TLS_DHE_DSS_WITH_AES_256_CBC_SHA - */ - public static final String TLS_DHE_DSS_WITH_AES_256_CBC_SHA = "TLS_DHE_DSS_WITH_AES_256_CBC_SHA"; - - /** - * TLS_DHE_RSA_WITH_CAMELLIA256_SHA - */ - public static final String TLS_DHE_RSA_WITH_CAMELLIA256_SHA = "TLS_DHE_RSA_WITH_CAMELLIA256_SHA"; - - /** - * TLS_DHE_DSS_WITH_CAMELLIA256_SHA - */ - public static final String TLS_DHE_DSS_WITH_CAMELLIA256_SHA = "TLS_DHE_DSS_WITH_CAMELLIA256_SHA"; - - /** - * TLS_ECDH_anon_WITH_AES_256_CBC_SHA - */ - public static final String TLS_ECDH_anon_WITH_AES_256_CBC_SHA = "TLS_ECDH_anon_WITH_AES_256_CBC_SHA"; - - /** - * TLS_DH_anon_WITH_AES_256_CBC_SHA - */ - public static final String TLS_DH_anon_WITH_AES_256_CBC_SHA = "TLS_DH_anon_WITH_AES_256_CBC_SHA"; - - /** - * TLS_DH_anon_WITH_CAMELLIA256_SHA - */ - public static final String TLS_DH_anon_WITH_CAMELLIA256_SHA = "TLS_DH_anon_WITH_CAMELLIA256_SHA"; - - /** - * TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA - */ - public static final String TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA"; - - /** - * TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA - */ - public static final String TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"; - - /** - * TLS_DHE_RSA_WITH_AES_128_CBC_SHA - */ - public static final String TLS_DHE_RSA_WITH_AES_128_CBC_SHA = "TLS_DHE_RSA_WITH_AES_128_CBC_SHA"; - - /** - * TLS_DHE_DSS_WITH_AES_128_CBC_SHA - */ - public static final String TLS_DHE_DSS_WITH_AES_128_CBC_SHA = "TLS_DHE_DSS_WITH_AES_128_CBC_SHA"; - - /** - * TLS_DHE_RSA_WITH_SEED_SHA - */ - public static final String TLS_DHE_RSA_WITH_SEED_SHA = "TLS_DHE_RSA_WITH_SEED_SHA"; - - /** - * TLS_DHE_DSS_WITH_SEED_SHA - */ - public static final String TLS_DHE_DSS_WITH_SEED_SHA = "TLS_DHE_DSS_WITH_SEED_SHA"; - - /** - * TLS_DHE_RSA_WITH_CAMELLIA128_SHA - */ - public static final String TLS_DHE_RSA_WITH_CAMELLIA128_SHA = "TLS_DHE_RSA_WITH_CAMELLIA128_SHA"; - - /** - * TLS_DHE_DSS_WITH_CAMELLIA128_SHA - */ - public static final String TLS_DHE_DSS_WITH_CAMELLIA128_SHA = "TLS_DHE_DSS_WITH_CAMELLIA128_SHA"; - - /** - * TLS_ECDH_anon_WITH_AES_128_CBC_SHA - */ - public static final String TLS_ECDH_anon_WITH_AES_128_CBC_SHA = "TLS_ECDH_anon_WITH_AES_128_CBC_SHA"; - - /** - * TLS_DH_anon_WITH_AES_128_CBC_SHA - */ - public static final String TLS_DH_anon_WITH_AES_128_CBC_SHA = "TLS_DH_anon_WITH_AES_128_CBC_SHA"; - - /** - * TLS_DH_anon_WITH_SEED_SHA - */ - public static final String TLS_DH_anon_WITH_SEED_SHA = "TLS_DH_anon_WITH_SEED_SHA"; - - /** - * TLS_DH_anon_WITH_CAMELLIA128_SHA - */ - public static final String TLS_DH_anon_WITH_CAMELLIA128_SHA = "TLS_DH_anon_WITH_CAMELLIA128_SHA"; - - /** - * TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 - */ - public static final String TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 = "TLS_RSA_PSK_WITH_AES_256_GCM_SHA384"; - - /** - * TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 - */ - public static final String TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 = "TLS_DHE_PSK_WITH_AES_256_GCM_SHA384"; - - /** - * TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256 - */ - public static final String TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256 = "TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256"; - - /** - * TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256 - */ - public static final String TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256 = "TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256"; - - /** - * TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 - */ - public static final String TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 = - "TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256"; - - /** - * TLS_DHE_PSK_WITH_AES_256_CBC_CCM8 - */ - public static final String TLS_DHE_PSK_WITH_AES_256_CBC_CCM8 = "TLS_DHE_PSK_WITH_AES_256_CBC_CCM8"; - - /** - * TLS_DHE_PSK_WITH_AES_256_CBC_CCM - */ - public static final String TLS_DHE_PSK_WITH_AES_256_CBC_CCM = "TLS_DHE_PSK_WITH_AES_256_CBC_CCM"; - - /** - * TLS_RSA_PSK_WITH_ARIA256_GCM_SHA384 - */ - public static final String TLS_RSA_PSK_WITH_ARIA256_GCM_SHA384 = "TLS_RSA_PSK_WITH_ARIA256_GCM_SHA384"; - - /** - * TLS_DHE_PSK_WITH_ARIA256_GCM_SHA384 - */ - public static final String TLS_DHE_PSK_WITH_ARIA256_GCM_SHA384 = "TLS_DHE_PSK_WITH_ARIA256_GCM_SHA384"; - - /** - * TLS_RSA_WITH_AES_256_GCM_SHA384 - */ - public static final String TLS_RSA_WITH_AES_256_GCM_SHA384 = "TLS_RSA_WITH_AES_256_GCM_SHA384"; - - /** - * TLS_RSA_WITH_AES_256_CBC_CCM8 - */ - public static final String TLS_RSA_WITH_AES_256_CBC_CCM8 = "TLS_RSA_WITH_AES_256_CBC_CCM8"; - - /** - * TLS_RSA_WITH_AES_256_CBC_CCM - */ - public static final String TLS_RSA_WITH_AES_256_CBC_CCM = "TLS_RSA_WITH_AES_256_CBC_CCM"; - - /** - * TLS_RSA_WITH_ARIA256_GCM_SHA384 - */ - public static final String TLS_RSA_WITH_ARIA256_GCM_SHA384 = "TLS_RSA_WITH_ARIA256_GCM_SHA384"; - - /** - * TLS_PSK_WITH_AES_256_GCM_SHA384 - */ - public static final String TLS_PSK_WITH_AES_256_GCM_SHA384 = "TLS_PSK_WITH_AES_256_GCM_SHA384"; - - /** - * TLS_PSK_WITH_CHACHA20_POLY1305_SHA256 - */ - public static final String TLS_PSK_WITH_CHACHA20_POLY1305_SHA256 = "TLS_PSK_WITH_CHACHA20_POLY1305_SHA256"; - - /** - * TLS_PSK_WITH_AES_256_CBC_CCM8 - */ - public static final String TLS_PSK_WITH_AES_256_CBC_CCM8 = "TLS_PSK_WITH_AES_256_CBC_CCM8"; - - /** - * TLS_PSK_WITH_AES_256_CBC_CCM - */ - public static final String TLS_PSK_WITH_AES_256_CBC_CCM = "TLS_PSK_WITH_AES_256_CBC_CCM"; - - /** - * TLS_PSK_WITH_ARIA256_GCM_SHA384 - */ - public static final String TLS_PSK_WITH_ARIA256_GCM_SHA384 = "TLS_PSK_WITH_ARIA256_GCM_SHA384"; - - /** - * TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 - */ - public static final String TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 = "TLS_RSA_PSK_WITH_AES_128_GCM_SHA256"; - - /** - * TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 - */ - public static final String TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 = "TLS_DHE_PSK_WITH_AES_128_GCM_SHA256"; - - /** - * TLS_DHE_PSK_WITH_AES_128_CBC_CCM8 - */ - public static final String TLS_DHE_PSK_WITH_AES_128_CBC_CCM8 = "TLS_DHE_PSK_WITH_AES_128_CBC_CCM8"; - - /** - * TLS_DHE_PSK_WITH_AES_128_CBC_CCM - */ - public static final String TLS_DHE_PSK_WITH_AES_128_CBC_CCM = "TLS_DHE_PSK_WITH_AES_128_CBC_CCM"; - - /** - * TLS_RSA_PSK_WITH_ARIA128_GCM_SHA256 - */ - public static final String TLS_RSA_PSK_WITH_ARIA128_GCM_SHA256 = "TLS_RSA_PSK_WITH_ARIA128_GCM_SHA256"; - - /** - * TLS_DHE_PSK_WITH_ARIA128_GCM_SHA256 - */ - public static final String TLS_DHE_PSK_WITH_ARIA128_GCM_SHA256 = "TLS_DHE_PSK_WITH_ARIA128_GCM_SHA256"; - - /** - * TLS_RSA_WITH_AES_128_GCM_SHA256 - */ - public static final String TLS_RSA_WITH_AES_128_GCM_SHA256 = "TLS_RSA_WITH_AES_128_GCM_SHA256"; - - /** - * TLS_RSA_WITH_AES_128_CBC_CCM8 - */ - public static final String TLS_RSA_WITH_AES_128_CBC_CCM8 = "TLS_RSA_WITH_AES_128_CBC_CCM8"; - - /** - * TLS_RSA_WITH_AES_128_CBC_CCM - */ - public static final String TLS_RSA_WITH_AES_128_CBC_CCM = "TLS_RSA_WITH_AES_128_CBC_CCM"; - - /** - * TLS_RSA_WITH_ARIA128_GCM_SHA256 - */ - public static final String TLS_RSA_WITH_ARIA128_GCM_SHA256 = "TLS_RSA_WITH_ARIA128_GCM_SHA256"; - - /** - * TLS_PSK_WITH_AES_128_GCM_SHA256 - */ - public static final String TLS_PSK_WITH_AES_128_GCM_SHA256 = "TLS_PSK_WITH_AES_128_GCM_SHA256"; - - /** - * TLS_PSK_WITH_AES_128_CBC_CCM8 - */ - public static final String TLS_PSK_WITH_AES_128_CBC_CCM8 = "TLS_PSK_WITH_AES_128_CBC_CCM8"; - - /** - * TLS_PSK_WITH_AES_128_CBC_CCM - */ - public static final String TLS_PSK_WITH_AES_128_CBC_CCM = "TLS_PSK_WITH_AES_128_CBC_CCM"; - - /** - * TLS_PSK_WITH_ARIA128_GCM_SHA256 - */ - public static final String TLS_PSK_WITH_ARIA128_GCM_SHA256 = "TLS_PSK_WITH_ARIA128_GCM_SHA256"; - - /** - * TLS_RSA_WITH_AES_256_CBC_SHA256 - */ - public static final String TLS_RSA_WITH_AES_256_CBC_SHA256 = "TLS_RSA_WITH_AES_256_CBC_SHA256"; - - /** - * TLS_RSA_WITH_CAMELLIA256_SHA256 - */ - public static final String TLS_RSA_WITH_CAMELLIA256_SHA256 = "TLS_RSA_WITH_CAMELLIA256_SHA256"; - - /** - * TLS_RSA_WITH_AES_128_CBC_SHA256 - */ - public static final String TLS_RSA_WITH_AES_128_CBC_SHA256 = "TLS_RSA_WITH_AES_128_CBC_SHA256"; - - /** - * TLS_RSA_WITH_CAMELLIA128_SHA256 - */ - public static final String TLS_RSA_WITH_CAMELLIA128_SHA256 = "TLS_RSA_WITH_CAMELLIA128_SHA256"; - - /** - * TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 - */ - public static final String TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 = "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384"; - - /** - * TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA - */ - public static final String TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA = "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA"; - - /** - * TLS_SRP_DSS_WITH_AES_256_CBC_SHA - */ - public static final String TLS_SRP_DSS_WITH_AES_256_CBC_SHA = "TLS_SRP_DSS_WITH_AES_256_CBC_SHA"; - - /** - * TLS_SRP_RSA_WITH_AES_256_CBC_SHA - */ - public static final String TLS_SRP_RSA_WITH_AES_256_CBC_SHA = "TLS_SRP_RSA_WITH_AES_256_CBC_SHA"; - - /** - * TLS_SRP_WITH_AES_256_CBC_SHA - */ - public static final String TLS_SRP_WITH_AES_256_CBC_SHA = "TLS_SRP_WITH_AES_256_CBC_SHA"; - - /** - * TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 - */ - public static final String TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 = "TLS_RSA_PSK_WITH_AES_256_CBC_SHA384"; - - /** - * TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 - */ - public static final String TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 = "TLS_DHE_PSK_WITH_AES_256_CBC_SHA384"; - - /** - * TLS_RSA_PSK_WITH_AES_256_CBC_SHA - */ - public static final String TLS_RSA_PSK_WITH_AES_256_CBC_SHA = "TLS_RSA_PSK_WITH_AES_256_CBC_SHA"; - - /** - * TLS_DHE_PSK_WITH_AES_256_CBC_SHA - */ - public static final String TLS_DHE_PSK_WITH_AES_256_CBC_SHA = "TLS_DHE_PSK_WITH_AES_256_CBC_SHA"; - - /** - * TLS_ECDHE_PSK_WITH_CAMELLIA256_SHA384 - */ - public static final String TLS_ECDHE_PSK_WITH_CAMELLIA256_SHA384 = "TLS_ECDHE_PSK_WITH_CAMELLIA256_SHA384"; - - /** - * TLS_RSA_PSK_WITH_CAMELLIA256_SHA384 - */ - public static final String TLS_RSA_PSK_WITH_CAMELLIA256_SHA384 = "TLS_RSA_PSK_WITH_CAMELLIA256_SHA384"; - - /** - * TLS_DHE_PSK_WITH_CAMELLIA256_SHA384 - */ - public static final String TLS_DHE_PSK_WITH_CAMELLIA256_SHA384 = "TLS_DHE_PSK_WITH_CAMELLIA256_SHA384"; - - /** - * TLS_RSA_WITH_AES_256_CBC_SHA - */ - public static final String TLS_RSA_WITH_AES_256_CBC_SHA = "TLS_RSA_WITH_AES_256_CBC_SHA"; - - /** - * TLS_RSA_WITH_CAMELLIA256_SHA - */ - public static final String TLS_RSA_WITH_CAMELLIA256_SHA = "TLS_RSA_WITH_CAMELLIA256_SHA"; - - /** - * TLS_PSK_WITH_AES_256_CBC_SHA384 - */ - public static final String TLS_PSK_WITH_AES_256_CBC_SHA384 = "TLS_PSK_WITH_AES_256_CBC_SHA384"; - - /** - * TLS_PSK_WITH_AES_256_CBC_SHA - */ - public static final String TLS_PSK_WITH_AES_256_CBC_SHA = "TLS_PSK_WITH_AES_256_CBC_SHA"; - - /** - * TLS_PSK_WITH_CAMELLIA256_SHA384 - */ - public static final String TLS_PSK_WITH_CAMELLIA256_SHA384 = "TLS_PSK_WITH_CAMELLIA256_SHA384"; - - /** - * TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 - */ - public static final String TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 = "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256"; - - /** - * TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA - */ - public static final String TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA = "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA"; - - /** - * TLS_SRP_DSS_WITH_AES_128_CBC_SHA - */ - public static final String TLS_SRP_DSS_WITH_AES_128_CBC_SHA = "TLS_SRP_DSS_WITH_AES_128_CBC_SHA"; - - /** - * TLS_SRP_RSA_WITH_AES_128_CBC_SHA - */ - public static final String TLS_SRP_RSA_WITH_AES_128_CBC_SHA = "TLS_SRP_RSA_WITH_AES_128_CBC_SHA"; - - /** - * TLS_SRP_WITH_AES_128_CBC_SHA - */ - public static final String TLS_SRP_WITH_AES_128_CBC_SHA = "TLS_SRP_WITH_AES_128_CBC_SHA"; - - /** - * TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 - */ - public static final String TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 = "TLS_RSA_PSK_WITH_AES_128_CBC_SHA256"; - - /** - * TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 - */ - public static final String TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 = "TLS_DHE_PSK_WITH_AES_128_CBC_SHA256"; - - /** - * TLS_RSA_PSK_WITH_AES_128_CBC_SHA - */ - public static final String TLS_RSA_PSK_WITH_AES_128_CBC_SHA = "TLS_RSA_PSK_WITH_AES_128_CBC_SHA"; - - /** - * TLS_DHE_PSK_WITH_AES_128_CBC_SHA - */ - public static final String TLS_DHE_PSK_WITH_AES_128_CBC_SHA = "TLS_DHE_PSK_WITH_AES_128_CBC_SHA"; - - /** - * TLS_ECDHE_PSK_WITH_CAMELLIA128_SHA256 - */ - public static final String TLS_ECDHE_PSK_WITH_CAMELLIA128_SHA256 = "TLS_ECDHE_PSK_WITH_CAMELLIA128_SHA256"; - - /** - * TLS_RSA_PSK_WITH_CAMELLIA128_SHA256 - */ - public static final String TLS_RSA_PSK_WITH_CAMELLIA128_SHA256 = "TLS_RSA_PSK_WITH_CAMELLIA128_SHA256"; - - /** - * TLS_DHE_PSK_WITH_CAMELLIA128_SHA256 - */ - public static final String TLS_DHE_PSK_WITH_CAMELLIA128_SHA256 = "TLS_DHE_PSK_WITH_CAMELLIA128_SHA256"; - - /** - * TLS_RSA_WITH_AES_128_CBC_SHA - */ - public static final String TLS_RSA_WITH_AES_128_CBC_SHA = "TLS_RSA_WITH_AES_128_CBC_SHA"; - - /** - * TLS_RSA_WITH_SEED_SHA - */ - public static final String TLS_RSA_WITH_SEED_SHA = "TLS_RSA_WITH_SEED_SHA"; - - /** - * TLS_RSA_WITH_CAMELLIA128_SHA - */ - public static final String TLS_RSA_WITH_CAMELLIA128_SHA = "TLS_RSA_WITH_CAMELLIA128_SHA"; - - /** - * TLS_RSA_WITH_IDEA_CBC_SHA - */ - public static final String TLS_RSA_WITH_IDEA_CBC_SHA = "TLS_RSA_WITH_IDEA_CBC_SHA"; - - /** - * TLS_PSK_WITH_AES_128_CBC_SHA256 - */ - public static final String TLS_PSK_WITH_AES_128_CBC_SHA256 = "TLS_PSK_WITH_AES_128_CBC_SHA256"; - - /** - * TLS_PSK_WITH_AES_128_CBC_SHA - */ - public static final String TLS_PSK_WITH_AES_128_CBC_SHA = "TLS_PSK_WITH_AES_128_CBC_SHA"; - - /** - * TLS_PSK_WITH_CAMELLIA128_SHA256 - */ - public static final String TLS_PSK_WITH_CAMELLIA128_SHA256 = "TLS_PSK_WITH_CAMELLIA128_SHA256"; - - private Ciphers() { - // Prevent outside initialization - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/ClientAuth.java b/handler/src/main/java/io/netty/handler/ssl/ClientAuth.java deleted file mode 100644 index 82696d9275..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/ClientAuth.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.ssl; - -/** - * Indicates the state of the {@link javax.net.ssl.SSLEngine} with respect to client authentication. - * This configuration item really only applies when building the server-side {@link SslContext}. - */ -public enum ClientAuth { - /** - * Indicates that the {@link javax.net.ssl.SSLEngine} will not request client authentication. - */ - NONE, - - /** - * Indicates that the {@link javax.net.ssl.SSLEngine} will request client authentication. - */ - OPTIONAL, - - /** - * Indicates that the {@link javax.net.ssl.SSLEngine} will *require* client authentication. - */ - REQUIRE -} diff --git a/handler/src/main/java/io/netty/handler/ssl/Conscrypt.java b/handler/src/main/java/io/netty/handler/ssl/Conscrypt.java deleted file mode 100644 index da7c8a25c3..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/Conscrypt.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.util.internal.PlatformDependent; - -import javax.net.ssl.SSLEngine; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; - -/** - * Contains methods that can be used to detect if conscrypt is usable. - */ -final class Conscrypt { - // This class exists to avoid loading other conscrypt related classes using features only available in JDK8+, - // because we need to maintain JDK6+ runtime compatibility. - - private static final MethodHandle IS_CONSCRYPT_SSLENGINE; - - static { - MethodHandle isConscryptSSLEngine = null; - - if ((PlatformDependent.javaVersion() >= 8 && - // Only works on Java14 and earlier for now - // See https://github.com/google/conscrypt/issues/838 - PlatformDependent.javaVersion() < 15) || PlatformDependent.isAndroid()) { - try { - MethodHandles.Lookup lookup = MethodHandles.lookup(); - Class providerClass = Class.forName("org.conscrypt.OpenSSLProvider", true, - PlatformDependent.getClassLoader(ConscryptAlpnSslEngine.class)); - lookup.findConstructor(providerClass, MethodType.methodType(void.class)).invoke(); - - Class conscryptClass = Class.forName("org.conscrypt.Conscrypt", true, - PlatformDependent.getClassLoader(ConscryptAlpnSslEngine.class)); - isConscryptSSLEngine = lookup.findStatic(conscryptClass, "isConscrypt", - MethodType.methodType(boolean.class, SSLEngine.class)); - } catch (Throwable ignore) { - // ignore - } - } - IS_CONSCRYPT_SSLENGINE = isConscryptSSLEngine; - } - - /** - * Indicates whether or not conscrypt is available on the current system. - */ - static boolean isAvailable() { - return IS_CONSCRYPT_SSLENGINE != null; - } - - /** - * Returns {@code true} if the passed in {@link SSLEngine} is handled by Conscrypt, {@code false} otherwise. - */ - static boolean isEngineSupported(SSLEngine engine) { - try { - return IS_CONSCRYPT_SSLENGINE != null && (boolean) IS_CONSCRYPT_SSLENGINE.invokeExact(engine); - } catch (IllegalAccessException ignore) { - return false; - } catch (Throwable ex) { - throw new RuntimeException(ex); - } - } - - private Conscrypt() { } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/ConscryptAlpnSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/ConscryptAlpnSslEngine.java deleted file mode 100644 index 5ddffc50c6..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/ConscryptAlpnSslEngine.java +++ /dev/null @@ -1,196 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import static io.netty.handler.ssl.SslUtils.toSSLHandshakeException; -import static java.lang.Math.min; -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.handler.ssl.JdkApplicationProtocolNegotiator.ProtocolSelectionListener; -import io.netty.handler.ssl.JdkApplicationProtocolNegotiator.ProtocolSelector; -import java.nio.ByteBuffer; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.List; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLEngineResult; -import javax.net.ssl.SSLException; - -import io.netty.util.internal.SystemPropertyUtil; -import org.conscrypt.AllocatedBuffer; -import org.conscrypt.BufferAllocator; -import org.conscrypt.Conscrypt; -import org.conscrypt.HandshakeListener; - -/** - * A {@link JdkSslEngine} that uses the Conscrypt provider or SSL with ALPN. - */ -abstract class ConscryptAlpnSslEngine extends JdkSslEngine { - private static final boolean USE_BUFFER_ALLOCATOR = SystemPropertyUtil.getBoolean( - "io.netty.handler.ssl.conscrypt.useBufferAllocator", true); - - static ConscryptAlpnSslEngine newClientEngine(SSLEngine engine, ByteBufAllocator alloc, - JdkApplicationProtocolNegotiator applicationNegotiator) { - return new ClientEngine(engine, alloc, applicationNegotiator); - } - - static ConscryptAlpnSslEngine newServerEngine(SSLEngine engine, ByteBufAllocator alloc, - JdkApplicationProtocolNegotiator applicationNegotiator) { - return new ServerEngine(engine, alloc, applicationNegotiator); - } - - private ConscryptAlpnSslEngine(SSLEngine engine, ByteBufAllocator alloc, List protocols) { - super(engine); - - // Configure the Conscrypt engine to use Netty's buffer allocator. This is a trade-off of memory vs - // performance. - // - // If no allocator is provided, the engine will internally allocate a direct buffer of max packet size in - // order to optimize JNI calls (this happens the first time it is provided a non-direct buffer from the - // application). - // - // Alternatively, if an allocator is provided, no internal buffer will be created and direct buffers will be - // retrieved from the allocator on-demand. - if (USE_BUFFER_ALLOCATOR) { - Conscrypt.setBufferAllocator(engine, new BufferAllocatorAdapter(alloc)); - } - - // Set the list of supported ALPN protocols on the engine. - Conscrypt.setApplicationProtocols(engine, protocols.toArray(new String[0])); - } - - /** - * Calculates the maximum size of the encrypted output buffer required to wrap the given plaintext bytes. Assumes - * as a worst case that there is one TLS record per buffer. - * - * @param plaintextBytes the number of plaintext bytes to be wrapped. - * @param numBuffers the number of buffers that the plaintext bytes are spread across. - * @return the maximum size of the encrypted output buffer required for the wrap operation. - */ - final int calculateOutNetBufSize(int plaintextBytes, int numBuffers) { - // Assuming a max of one frame per component in a composite buffer. - long maxOverhead = (long) Conscrypt.maxSealOverhead(getWrappedEngine()) * numBuffers; - // TODO(nmittler): update this to use MAX_ENCRYPTED_PACKET_LENGTH instead of Integer.MAX_VALUE - return (int) min(Integer.MAX_VALUE, plaintextBytes + maxOverhead); - } - - final SSLEngineResult unwrap(ByteBuffer[] srcs, ByteBuffer[] dests) throws SSLException { - return Conscrypt.unwrap(getWrappedEngine(), srcs, dests); - } - - private static final class ClientEngine extends ConscryptAlpnSslEngine { - private final ProtocolSelectionListener protocolListener; - - ClientEngine(SSLEngine engine, ByteBufAllocator alloc, - JdkApplicationProtocolNegotiator applicationNegotiator) { - super(engine, alloc, applicationNegotiator.protocols()); - // Register for completion of the handshake. - Conscrypt.setHandshakeListener(engine, new HandshakeListener() { - @Override - public void onHandshakeFinished() throws SSLException { - selectProtocol(); - } - }); - - protocolListener = requireNonNull(applicationNegotiator - .protocolListenerFactory().newListener(this, applicationNegotiator.protocols()), - "protocolListener"); - } - - private void selectProtocol() throws SSLException { - String protocol = Conscrypt.getApplicationProtocol(getWrappedEngine()); - try { - protocolListener.selected(protocol); - } catch (Throwable e) { - throw toSSLHandshakeException(e); - } - } - } - - private static final class ServerEngine extends ConscryptAlpnSslEngine { - private final ProtocolSelector protocolSelector; - - ServerEngine(SSLEngine engine, ByteBufAllocator alloc, - JdkApplicationProtocolNegotiator applicationNegotiator) { - super(engine, alloc, applicationNegotiator.protocols()); - - // Register for completion of the handshake. - Conscrypt.setHandshakeListener(engine, new HandshakeListener() { - @Override - public void onHandshakeFinished() throws SSLException { - selectProtocol(); - } - }); - - protocolSelector = requireNonNull(applicationNegotiator.protocolSelectorFactory() - .newSelector(this, - new LinkedHashSet<>(applicationNegotiator.protocols())), - "protocolSelector"); - } - - private void selectProtocol() throws SSLException { - try { - String protocol = Conscrypt.getApplicationProtocol(getWrappedEngine()); - protocolSelector.select(protocol != null ? Collections.singletonList(protocol) - : Collections.emptyList()); - } catch (Throwable e) { - throw toSSLHandshakeException(e); - } - } - } - - private static final class BufferAllocatorAdapter extends BufferAllocator { - private final ByteBufAllocator alloc; - - BufferAllocatorAdapter(ByteBufAllocator alloc) { - this.alloc = alloc; - } - - @Override - public AllocatedBuffer allocateDirectBuffer(int capacity) { - return new BufferAdapter(alloc.directBuffer(capacity)); - } - } - - private static final class BufferAdapter extends AllocatedBuffer { - private final ByteBuf nettyBuffer; - private final ByteBuffer buffer; - - BufferAdapter(ByteBuf nettyBuffer) { - this.nettyBuffer = nettyBuffer; - buffer = nettyBuffer.nioBuffer(0, nettyBuffer.capacity()); - } - - @Override - public ByteBuffer nioBuffer() { - return buffer; - } - - @Override - public AllocatedBuffer retain() { - nettyBuffer.retain(); - return this; - } - - @Override - public AllocatedBuffer release() { - nettyBuffer.release(); - return this; - } - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/DefaultOpenSslKeyMaterial.java b/handler/src/main/java/io/netty/handler/ssl/DefaultOpenSslKeyMaterial.java deleted file mode 100644 index 6673044eb0..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/DefaultOpenSslKeyMaterial.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.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 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, 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) { - 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/DelegatingSslContext.java b/handler/src/main/java/io/netty/handler/ssl/DelegatingSslContext.java deleted file mode 100644 index 5430acc817..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/DelegatingSslContext.java +++ /dev/null @@ -1,123 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBufAllocator; - -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. - */ -public abstract class DelegatingSslContext extends SslContext { - - private final SslContext ctx; - - protected DelegatingSslContext(SslContext ctx) { - this.ctx = requireNonNull(ctx, "ctx"); - } - - @Override - public final boolean isClient() { - return ctx.isClient(); - } - - @Override - public final List cipherSuites() { - return ctx.cipherSuites(); - } - - @Override - public final long sessionCacheSize() { - return ctx.sessionCacheSize(); - } - - @Override - public final long sessionTimeout() { - return ctx.sessionTimeout(); - } - - @Override - public final ApplicationProtocolNegotiator applicationProtocolNegotiator() { - return ctx.applicationProtocolNegotiator(); - } - - @Override - public final SSLEngine newEngine(ByteBufAllocator alloc) { - SSLEngine engine = ctx.newEngine(alloc); - initEngine(engine); - return engine; - } - - @Override - public final SSLEngine newEngine(ByteBufAllocator alloc, String peerHost, int peerPort) { - SSLEngine engine = ctx.newEngine(alloc, peerHost, peerPort); - initEngine(engine); - return engine; - } - - @Override - protected final SslHandler newHandler(ByteBufAllocator alloc, boolean startTls) { - SslHandler handler = ctx.newHandler(alloc, startTls); - initHandler(handler); - return handler; - } - - @Override - protected final SslHandler newHandler(ByteBufAllocator alloc, String peerHost, int peerPort, boolean startTls) { - SslHandler handler = ctx.newHandler(alloc, peerHost, peerPort, startTls); - initHandler(handler); - 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(); - } - - /** - * Init the {@link SSLEngine}. - */ - protected abstract void initEngine(SSLEngine engine); - - /** - * Init the {@link SslHandler}. This will by default call {@link #initEngine(SSLEngine)}, sub-classes may override - * this. - */ - protected void initHandler(SslHandler handler) { - initEngine(handler.engine()); - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/ExtendedOpenSslSession.java b/handler/src/main/java/io/netty/handler/ssl/ExtendedOpenSslSession.java deleted file mode 100644 index 20c4601e93..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/ExtendedOpenSslSession.java +++ /dev/null @@ -1,239 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.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.SSLSessionBindingEvent; -import javax.net.ssl.SSLSessionBindingListener; -import javax.security.cert.X509Certificate; -import java.security.Principal; -import java.security.cert.Certificate; -import java.util.Collections; -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", - "RSASSA-PSS", - }; - - private final OpenSslSession wrapped; - - ExtendedOpenSslSession(OpenSslSession wrapped) { - this.wrapped = wrapped; - } - - // Use rawtypes an unchecked override to be able to also work on java7. - @Override - @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 OpenSslSessionId sessionId() { - return wrapped.sessionId(); - } - - @Override - public void setSessionId(OpenSslSessionId id) { - wrapped.setSessionId(id); - } - - @Override - public final void setLocalCertificate(Certificate[] localCertificate) { - wrapped.setLocalCertificate(localCertificate); - } - - @Override - public String[] getPeerSupportedSignatureAlgorithms() { - return EmptyArrays.EMPTY_STRINGS; - } - - @Override - public final void tryExpandApplicationBufferSize(int packetLengthDataOnly) { - wrapped.tryExpandApplicationBufferSize(packetLengthDataOnly); - } - - @Override - public final String[] getLocalSupportedSignatureAlgorithms() { - return LOCAL_SUPPORTED_SIGNATURE_ALGORITHMS.clone(); - } - - @Override - public final byte[] getId() { - return wrapped.getId(); - } - - @Override - public final OpenSslSessionContext getSessionContext() { - return wrapped.getSessionContext(); - } - - @Override - public final long getCreationTime() { - return wrapped.getCreationTime(); - } - - @Override - public final long getLastAccessedTime() { - return wrapped.getLastAccessedTime(); - } - - @Override - public final void invalidate() { - wrapped.invalidate(); - } - - @Override - public final boolean isValid() { - return wrapped.isValid(); - } - - @Override - public final void putValue(String name, Object value) { - if (value instanceof SSLSessionBindingListener) { - // Decorate the value if needed so we submit the correct SSLSession instance - value = new SSLSessionBindingListenerDecorator((SSLSessionBindingListener) value); - } - wrapped.putValue(name, value); - } - - @Override - public final Object getValue(String s) { - Object value = wrapped.getValue(s); - if (value instanceof SSLSessionBindingListenerDecorator) { - // Unwrap as needed so we return the original value - return ((SSLSessionBindingListenerDecorator) value).delegate; - } - return value; - } - - @Override - public final void removeValue(String s) { - wrapped.removeValue(s); - } - - @Override - public final String[] getValueNames() { - return wrapped.getValueNames(); - } - - @Override - public final Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException { - return wrapped.getPeerCertificates(); - } - - @Override - public final Certificate[] getLocalCertificates() { - return wrapped.getLocalCertificates(); - } - - @Override - public final X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException { - return wrapped.getPeerCertificateChain(); - } - - @Override - public final Principal getPeerPrincipal() throws SSLPeerUnverifiedException { - return wrapped.getPeerPrincipal(); - } - - @Override - public final Principal getLocalPrincipal() { - return wrapped.getLocalPrincipal(); - } - - @Override - public final String getCipherSuite() { - return wrapped.getCipherSuite(); - } - - @Override - public String getProtocol() { - return wrapped.getProtocol(); - } - - @Override - public final String getPeerHost() { - return wrapped.getPeerHost(); - } - - @Override - public final int getPeerPort() { - return wrapped.getPeerPort(); - } - - @Override - public final int getPacketBufferSize() { - return wrapped.getPacketBufferSize(); - } - - @Override - public final int getApplicationBufferSize() { - return wrapped.getApplicationBufferSize(); - } - - private final class SSLSessionBindingListenerDecorator implements SSLSessionBindingListener { - - final SSLSessionBindingListener delegate; - - SSLSessionBindingListenerDecorator(SSLSessionBindingListener delegate) { - this.delegate = delegate; - } - - @Override - public void valueBound(SSLSessionBindingEvent event) { - delegate.valueBound(new SSLSessionBindingEvent(ExtendedOpenSslSession.this, event.getName())); - } - - @Override - public void valueUnbound(SSLSessionBindingEvent event) { - delegate.valueUnbound(new SSLSessionBindingEvent(ExtendedOpenSslSession.this, event.getName())); - } - } - - @Override - public void handshakeFinished(byte[] id, String cipher, String protocol, byte[] peerCertificate, - byte[][] peerCertificateChain, long creationTime, long timeout) throws SSLException { - wrapped.handshakeFinished(id, cipher, protocol, peerCertificate, peerCertificateChain, creationTime, timeout); - } - - @Override - public String toString() { - return "ExtendedOpenSslSession{" + - "wrapped=" + wrapped + - '}'; - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/GroupsConverter.java b/handler/src/main/java/io/netty/handler/ssl/GroupsConverter.java deleted file mode 100644 index 28b332998c..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/GroupsConverter.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -/** - * Convert java naming to OpenSSL naming if possible and if not return the original name. - */ -final class GroupsConverter { - - private static final Map mappings; - - static { - // See https://tools.ietf.org/search/rfc4492#appendix-A and https://www.java.com/en/configure_crypto.html - Map map = new HashMap(); - map.put("secp224r1", "P-224"); - map.put("prime256v1", "P-256"); - map.put("secp256r1", "P-256"); - map.put("secp384r1", "P-384"); - map.put("secp521r1", "P-521"); - map.put("x25519", "X25519"); - mappings = Collections.unmodifiableMap(map); - } - - static String toOpenSsl(String key) { - String mapping = mappings.get(key); - if (mapping == null) { - return key; - } - return mapping; - } - - private GroupsConverter() { } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/IdentityCipherSuiteFilter.java b/handler/src/main/java/io/netty/handler/ssl/IdentityCipherSuiteFilter.java deleted file mode 100644 index ea5372e3e0..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/IdentityCipherSuiteFilter.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - -/** - * This class will not do any filtering of ciphers suites. - */ -public final class IdentityCipherSuiteFilter implements CipherSuiteFilter { - - /** - * Defaults to default ciphers when provided ciphers are null - */ - public static final IdentityCipherSuiteFilter INSTANCE = new IdentityCipherSuiteFilter(true); - - /** - * Defaults to supported ciphers when provided ciphers are null - */ - public static final IdentityCipherSuiteFilter INSTANCE_DEFAULTING_TO_SUPPORTED_CIPHERS = - new IdentityCipherSuiteFilter(false); - - private final boolean defaultToDefaultCiphers; - - private IdentityCipherSuiteFilter(boolean defaultToDefaultCiphers) { - this.defaultToDefaultCiphers = defaultToDefaultCiphers; - } - - @Override - public String[] filterCipherSuites(Iterable ciphers, List defaultCiphers, - Set supportedCiphers) { - if (ciphers == null) { - return defaultToDefaultCiphers ? - defaultCiphers.toArray(new String[0]) : - supportedCiphers.toArray(new String[0]); - } else { - List newCiphers = new ArrayList<>(supportedCiphers.size()); - for (String c : ciphers) { - if (c == null) { - break; - } - newCiphers.add(c); - } - return newCiphers.toArray(new String[0]); - } - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/Java7SslParametersUtils.java b/handler/src/main/java/io/netty/handler/ssl/Java7SslParametersUtils.java deleted file mode 100644 index 41a2401445..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/Java7SslParametersUtils.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import javax.net.ssl.SSLParameters; -import java.security.AlgorithmConstraints; - -final class Java7SslParametersUtils { - - private Java7SslParametersUtils() { - // Utility - } - - /** - * Utility method that is used by {@link OpenSslEngine} and so allow use not not have any reference to - * {@link AlgorithmConstraints} in the code. This helps us to not get into trouble when using it in java - * version < 7 and especially when using on android. - */ - static void setAlgorithmConstraints(SSLParameters sslParameters, Object algorithmConstraints) { - sslParameters.setAlgorithmConstraints((AlgorithmConstraints) algorithmConstraints); - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/Java8SslUtils.java b/handler/src/main/java/io/netty/handler/ssl/Java8SslUtils.java deleted file mode 100644 index 953e448b17..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/Java8SslUtils.java +++ /dev/null @@ -1,101 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import javax.net.ssl.SNIHostName; -import javax.net.ssl.SNIMatcher; -import javax.net.ssl.SNIServerName; -import javax.net.ssl.SSLParameters; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; - -final class Java8SslUtils { - - private Java8SslUtils() { } - - static List getSniHostNames(SSLParameters sslParameters) { - List names = sslParameters.getServerNames(); - if (names == null || names.isEmpty()) { - return Collections.emptyList(); - } - List strings = new ArrayList<>(names.size()); - - for (SNIServerName serverName : names) { - if (serverName instanceof SNIHostName) { - strings.add(((SNIHostName) serverName).getAsciiName()); - } else { - throw new IllegalArgumentException("Only " + SNIHostName.class.getName() - + " instances are supported, but found: " + serverName); - } - } - return strings; - } - - 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)); - } - 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(); - } - - static void setUseCipherSuitesOrder(SSLParameters sslParameters, boolean useOrder) { - sslParameters.setUseCipherSuitesOrder(useOrder); - } - - @SuppressWarnings("unchecked") - static void setSNIMatchers(SSLParameters sslParameters, Collection matchers) { - sslParameters.setSNIMatchers((Collection) matchers); - } - - @SuppressWarnings("unchecked") - static boolean checkSniHostnameMatch(Collection matchers, byte[] hostname) { - if (matchers != null && !matchers.isEmpty()) { - SNIHostName name = new SNIHostName(hostname); - Iterator matcherIt = (Iterator) matchers.iterator(); - while (matcherIt.hasNext()) { - SNIMatcher matcher = matcherIt.next(); - // type 0 is for hostname - if (matcher.getType() == 0 && matcher.matches(name)) { - return true; - } - } - return false; - } - return true; - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/JdkAlpnApplicationProtocolNegotiator.java b/handler/src/main/java/io/netty/handler/ssl/JdkAlpnApplicationProtocolNegotiator.java deleted file mode 100644 index 9eb8f15d14..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/JdkAlpnApplicationProtocolNegotiator.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.buffer.ByteBufAllocator; - -import javax.net.ssl.SSLEngine; - -/** - * The {@link JdkApplicationProtocolNegotiator} to use if you need ALPN and are using {@link SslProvider#JDK}. - * - * @deprecated use {@link ApplicationProtocolConfig}. - */ -@Deprecated -public final class JdkAlpnApplicationProtocolNegotiator extends JdkBaseApplicationProtocolNegotiator { - private static final boolean AVAILABLE = Conscrypt.isAvailable() || - JdkAlpnSslUtils.supportsAlpn() || - JettyAlpnSslEngine.isAvailable() || - BouncyCastle.isAvailable(); - - private static final SslEngineWrapperFactory ALPN_WRAPPER = AVAILABLE ? new AlpnWrapper() : new FailureWrapper(); - - /** - * Create a new instance. - * @param protocols The order of iteration determines the preference of support for protocols. - */ - public JdkAlpnApplicationProtocolNegotiator(Iterable protocols) { - this(false, protocols); - } - - /** - * Create a new instance. - * @param protocols The order of iteration determines the preference of support for protocols. - */ - public JdkAlpnApplicationProtocolNegotiator(String... protocols) { - this(false, protocols); - } - - /** - * Create a new instance. - * @param failIfNoCommonProtocols Fail with a fatal alert if not common protocols are detected. - * @param protocols The order of iteration determines the preference of support for protocols. - */ - public JdkAlpnApplicationProtocolNegotiator(boolean failIfNoCommonProtocols, Iterable protocols) { - this(failIfNoCommonProtocols, failIfNoCommonProtocols, protocols); - } - - /** - * Create a new instance. - * @param failIfNoCommonProtocols Fail with a fatal alert if not common protocols are detected. - * @param protocols The order of iteration determines the preference of support for protocols. - */ - public JdkAlpnApplicationProtocolNegotiator(boolean failIfNoCommonProtocols, String... protocols) { - this(failIfNoCommonProtocols, failIfNoCommonProtocols, protocols); - } - - /** - * Create a new instance. - * @param clientFailIfNoCommonProtocols Client side fail with a fatal alert if not common protocols are detected. - * @param serverFailIfNoCommonProtocols Server side fail with a fatal alert if not common protocols are detected. - * @param protocols The order of iteration determines the preference of support for protocols. - */ - public JdkAlpnApplicationProtocolNegotiator(boolean clientFailIfNoCommonProtocols, - boolean serverFailIfNoCommonProtocols, Iterable protocols) { - this(serverFailIfNoCommonProtocols ? FAIL_SELECTOR_FACTORY : NO_FAIL_SELECTOR_FACTORY, - clientFailIfNoCommonProtocols ? FAIL_SELECTION_LISTENER_FACTORY : NO_FAIL_SELECTION_LISTENER_FACTORY, - protocols); - } - - /** - * Create a new instance. - * @param clientFailIfNoCommonProtocols Client side fail with a fatal alert if not common protocols are detected. - * @param serverFailIfNoCommonProtocols Server side fail with a fatal alert if not common protocols are detected. - * @param protocols The order of iteration determines the preference of support for protocols. - */ - public JdkAlpnApplicationProtocolNegotiator(boolean clientFailIfNoCommonProtocols, - boolean serverFailIfNoCommonProtocols, String... protocols) { - this(serverFailIfNoCommonProtocols ? FAIL_SELECTOR_FACTORY : NO_FAIL_SELECTOR_FACTORY, - clientFailIfNoCommonProtocols ? FAIL_SELECTION_LISTENER_FACTORY : NO_FAIL_SELECTION_LISTENER_FACTORY, - protocols); - } - - /** - * Create a new instance. - * @param selectorFactory The factory which provides classes responsible for selecting the protocol. - * @param listenerFactory The factory which provides to be notified of which protocol was selected. - * @param protocols The order of iteration determines the preference of support for protocols. - */ - public JdkAlpnApplicationProtocolNegotiator(ProtocolSelectorFactory selectorFactory, - ProtocolSelectionListenerFactory listenerFactory, Iterable protocols) { - super(ALPN_WRAPPER, selectorFactory, listenerFactory, protocols); - } - - /** - * Create a new instance. - * @param selectorFactory The factory which provides classes responsible for selecting the protocol. - * @param listenerFactory The factory which provides to be notified of which protocol was selected. - * @param protocols The order of iteration determines the preference of support for protocols. - */ - public JdkAlpnApplicationProtocolNegotiator(ProtocolSelectorFactory selectorFactory, - ProtocolSelectionListenerFactory listenerFactory, String... protocols) { - super(ALPN_WRAPPER, selectorFactory, listenerFactory, protocols); - } - - private static final class FailureWrapper extends AllocatorAwareSslEngineWrapperFactory { - @Override - public SSLEngine wrapSslEngine(SSLEngine engine, ByteBufAllocator alloc, - JdkApplicationProtocolNegotiator applicationNegotiator, boolean isServer) { - throw new RuntimeException("ALPN unsupported. Is your classpath configured correctly?" - + " For Conscrypt, add the appropriate Conscrypt JAR to classpath and set the security provider." - + " For Jetty-ALPN, see " - + "https://www.eclipse.org/jetty/documentation/current/alpn-chapter.html#alpn-starting"); - } - } - - private static final class AlpnWrapper extends AllocatorAwareSslEngineWrapperFactory { - @Override - public SSLEngine wrapSslEngine(SSLEngine engine, ByteBufAllocator alloc, - JdkApplicationProtocolNegotiator applicationNegotiator, boolean isServer) { - if (Conscrypt.isEngineSupported(engine)) { - return isServer ? ConscryptAlpnSslEngine.newServerEngine(engine, alloc, applicationNegotiator) - : ConscryptAlpnSslEngine.newClientEngine(engine, alloc, applicationNegotiator); - } - if (BouncyCastle.isInUse(engine)) { - return new BouncyCastleAlpnSslEngine(engine, applicationNegotiator, isServer); - } - // ALPN support was recently backported to Java8 as - // https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8230977. - // Because of this lets not do a Java version runtime check but just depend on if the required methods are - // present - if (JdkAlpnSslUtils.supportsAlpn()) { - return new JdkAlpnSslEngine(engine, applicationNegotiator, isServer); - } - if (JettyAlpnSslEngine.isAvailable()) { - return isServer ? JettyAlpnSslEngine.newServerEngine(engine, applicationNegotiator) - : JettyAlpnSslEngine.newClientEngine(engine, applicationNegotiator); - } - throw new UnsupportedOperationException("ALPN not supported. Unable to wrap SSLEngine of type '" - + engine.getClass().getName() + "')"); - } - } - - static boolean isAlpnSupported() { - return AVAILABLE; - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/JdkAlpnSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/JdkAlpnSslEngine.java deleted file mode 100644 index 6368193287..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/JdkAlpnSslEngine.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.util.internal.StringUtil; - -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLEngineResult; -import javax.net.ssl.SSLException; - -import java.nio.ByteBuffer; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.function.BiConsumer; -import java.util.function.BiFunction; - -import static io.netty.handler.ssl.SslUtils.toSSLHandshakeException; -import static io.netty.handler.ssl.JdkApplicationProtocolNegotiator.ProtocolSelectionListener; -import static io.netty.handler.ssl.JdkApplicationProtocolNegotiator.ProtocolSelector; - -class JdkAlpnSslEngine extends JdkSslEngine { - private final ProtocolSelectionListener selectionListener; - private final AlpnSelector alpnSelector; - - final class AlpnSelector implements BiFunction, String> { - private final ProtocolSelector selector; - private boolean called; - - AlpnSelector(ProtocolSelector selector) { - this.selector = selector; - } - - @Override - public String apply(SSLEngine sslEngine, List strings) { - assert !called; - called = true; - - try { - String selected = selector.select(strings); - return selected == null ? StringUtil.EMPTY_STRING : selected; - } catch (Exception cause) { - // Returning null means we want to fail the handshake. - // - // See https://download.java.net/java/jdk9/docs/api/javax/net/ssl/ - // SSLEngine.html#setHandshakeApplicationProtocolSelector-java.util.function.BiFunction- - return null; - } - } - - void checkUnsupported() { - if (called) { - // ALPN message was received by peer and so apply(...) was called. - // See: - // https://hg.openjdk.java.net/jdk9/dev/jdk/file/65464a307408/src/ - // java.base/share/classes/sun/security/ssl/ServerHandshaker.java#l933 - return; - } - String protocol = getApplicationProtocol(); - assert protocol != null; - - if (protocol.isEmpty()) { - // ALPN is not supported - selector.unsupported(); - } - } - } - - JdkAlpnSslEngine(SSLEngine engine, - @SuppressWarnings("deprecation") JdkApplicationProtocolNegotiator applicationNegotiator, - boolean isServer, BiConsumer setHandshakeApplicationProtocolSelector, - BiConsumer> setApplicationProtocols) { - super(engine); - if (isServer) { - selectionListener = null; - alpnSelector = new AlpnSelector(applicationNegotiator.protocolSelectorFactory(). - newSelector(this, new LinkedHashSet<>(applicationNegotiator.protocols()))); - setHandshakeApplicationProtocolSelector.accept(engine, alpnSelector); - } else { - selectionListener = applicationNegotiator.protocolListenerFactory() - .newListener(this, applicationNegotiator.protocols()); - alpnSelector = null; - setApplicationProtocols.accept(engine, applicationNegotiator.protocols()); - } - } - - JdkAlpnSslEngine(SSLEngine engine, - @SuppressWarnings("deprecation") JdkApplicationProtocolNegotiator applicationNegotiator, - boolean isServer) { - this(engine, applicationNegotiator, isServer, - new BiConsumer() { - @Override - public void accept(SSLEngine e, AlpnSelector s) { - JdkAlpnSslUtils.setHandshakeApplicationProtocolSelector(e, s); - } - }, - new BiConsumer>() { - @Override - public void accept(SSLEngine e, List p) { - JdkAlpnSslUtils.setApplicationProtocols(e, p); - } - }); - } - - private SSLEngineResult verifyProtocolSelection(SSLEngineResult result) throws SSLException { - if (result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.FINISHED) { - if (alpnSelector == null) { - // This means we are using client-side and - try { - String protocol = getApplicationProtocol(); - assert protocol != null; - if (protocol.isEmpty()) { - // If empty the server did not announce ALPN: - // See: - // https://hg.openjdk.java.net/jdk9/dev/jdk/file/65464a307408/src/java.base/ - // share/classes/sun/security/ssl/ClientHandshaker.java#l741 - selectionListener.unsupported(); - } else { - selectionListener.selected(protocol); - } - } catch (Throwable e) { - throw toSSLHandshakeException(e); - } - } else { - assert selectionListener == null; - alpnSelector.checkUnsupported(); - } - } - return result; - } - - @Override - public SSLEngineResult wrap(ByteBuffer src, ByteBuffer dst) throws SSLException { - return verifyProtocolSelection(super.wrap(src, dst)); - } - - @Override - public SSLEngineResult wrap(ByteBuffer[] srcs, ByteBuffer dst) throws SSLException { - return verifyProtocolSelection(super.wrap(srcs, dst)); - } - - @Override - public SSLEngineResult wrap(ByteBuffer[] srcs, int offset, int len, ByteBuffer dst) throws SSLException { - return verifyProtocolSelection(super.wrap(srcs, offset, len, dst)); - } - - @Override - public SSLEngineResult unwrap(ByteBuffer src, ByteBuffer dst) throws SSLException { - return verifyProtocolSelection(super.unwrap(src, dst)); - } - - @Override - public SSLEngineResult unwrap(ByteBuffer src, ByteBuffer[] dsts) throws SSLException { - return verifyProtocolSelection(super.unwrap(src, dsts)); - } - - @Override - public SSLEngineResult unwrap(ByteBuffer src, ByteBuffer[] dst, int offset, int len) throws SSLException { - return verifyProtocolSelection(super.unwrap(src, dst, offset, len)); - } - - @Override - void setNegotiatedApplicationProtocol(String applicationProtocol) { - // Do nothing as this is handled internally by the Java8u251+ implementation of SSLEngine. - } - - @Override - public String getNegotiatedApplicationProtocol() { - String protocol = getApplicationProtocol(); - if (protocol != null) { - return protocol.isEmpty() ? null : protocol; - } - return null; - } - - // These methods will override the methods defined by Java 8u251 and later. As we may compile with an earlier - // java8 version we don't use @Override annotations here. - public String getApplicationProtocol() { - return JdkAlpnSslUtils.getApplicationProtocol(getWrappedEngine()); - } - - public String getHandshakeApplicationProtocol() { - return JdkAlpnSslUtils.getHandshakeApplicationProtocol(getWrappedEngine()); - } - - public void setHandshakeApplicationProtocolSelector(BiFunction, String> selector) { - JdkAlpnSslUtils.setHandshakeApplicationProtocolSelector(getWrappedEngine(), selector); - } - - public BiFunction, String> getHandshakeApplicationProtocolSelector() { - return JdkAlpnSslUtils.getHandshakeApplicationProtocolSelector(getWrappedEngine()); - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/JdkAlpnSslUtils.java b/handler/src/main/java/io/netty/handler/ssl/JdkAlpnSslUtils.java deleted file mode 100644 index 5bcab1b204..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/JdkAlpnSslUtils.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - - -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLParameters; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; -import java.security.AccessController; -import java.security.PrivilegedExceptionAction; -import java.util.List; -import java.util.function.BiFunction; - -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; - -final class JdkAlpnSslUtils { - private static final InternalLogger logger = InternalLoggerFactory.getInstance(JdkAlpnSslUtils.class); - private static final MethodHandle SET_APPLICATION_PROTOCOLS; - private static final MethodHandle GET_APPLICATION_PROTOCOL; - private static final MethodHandle GET_HANDSHAKE_APPLICATION_PROTOCOL; - private static final MethodHandle SET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR; - private static final MethodHandle GET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR; - - static { - MethodHandle getHandshakeApplicationProtocol; - MethodHandle getApplicationProtocol; - MethodHandle setApplicationProtocols; - MethodHandle setHandshakeApplicationProtocolSelector; - MethodHandle getHandshakeApplicationProtocolSelector; - - try { - SSLContext context = SSLContext.getInstance(JdkSslContext.PROTOCOL); - context.init(null, null, null); - SSLEngine engine = context.createSSLEngine(); - MethodHandles.Lookup lookup = MethodHandles.lookup(); - getHandshakeApplicationProtocol = - AccessController.doPrivileged((PrivilegedExceptionAction) () -> - lookup.findVirtual(SSLEngine.class, "getHandshakeApplicationProtocol", - MethodType.methodType(String.class))); - // Invoke and specify the return type so the compiler doesnt try to use void - String getHandshakeApplicationProtocolRes = (String) getHandshakeApplicationProtocol.invokeExact(engine); - - getApplicationProtocol = AccessController.doPrivileged((PrivilegedExceptionAction) () -> - lookup.findVirtual(SSLEngine.class, "getApplicationProtocol", - MethodType.methodType(String.class))); - // Invoke and specify the return type so the compiler doesnt try to use void - String getApplicationProtocolRes = (String) getApplicationProtocol.invokeExact(engine); - - setApplicationProtocols = AccessController.doPrivileged((PrivilegedExceptionAction) () -> - lookup.findVirtual(SSLParameters.class, "setApplicationProtocols", - MethodType.methodType(void.class, String[].class))); - setApplicationProtocols.invokeExact(engine.getSSLParameters(), EmptyArrays.EMPTY_STRINGS); - - setHandshakeApplicationProtocolSelector = - AccessController.doPrivileged((PrivilegedExceptionAction) () -> - lookup.findVirtual(SSLEngine.class, "setHandshakeApplicationProtocolSelector", - MethodType.methodType(void.class, BiFunction.class))); - setHandshakeApplicationProtocolSelector.invokeExact(engine, - (BiFunction, String>) (sslEngine, strings) -> null); - - getHandshakeApplicationProtocolSelector = - AccessController.doPrivileged((PrivilegedExceptionAction) () -> - lookup.findVirtual(SSLEngine.class, "getHandshakeApplicationProtocolSelector", - MethodType.methodType(BiFunction.class))); - // Invoke and specify the return type so the compiler doesnt try to use void - @SuppressWarnings("unchecked") - BiFunction, String> getHandshakeApplicationProtocolSelectorRes = - (BiFunction, String>) - getHandshakeApplicationProtocolSelector.invokeExact(engine); - } catch (Throwable t) { - int version = PlatformDependent.javaVersion(); - if (version >= 9) { - // We only log when run on java9+ as this is expected on some earlier java8 versions - logger.error("Unable to initialize JdkAlpnSslUtils, but the detected java version was: {}", version, t); - } - getHandshakeApplicationProtocol = null; - getApplicationProtocol = null; - setApplicationProtocols = null; - setHandshakeApplicationProtocolSelector = null; - getHandshakeApplicationProtocolSelector = null; - } - GET_HANDSHAKE_APPLICATION_PROTOCOL = getHandshakeApplicationProtocol; - GET_APPLICATION_PROTOCOL = getApplicationProtocol; - SET_APPLICATION_PROTOCOLS = setApplicationProtocols; - SET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR = setHandshakeApplicationProtocolSelector; - GET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR = getHandshakeApplicationProtocolSelector; - } - - private JdkAlpnSslUtils() { - } - - static boolean supportsAlpn() { - return GET_APPLICATION_PROTOCOL != null; - } - - static String getApplicationProtocol(SSLEngine sslEngine) { - try { - return (String) GET_APPLICATION_PROTOCOL.invokeExact(sslEngine); - } catch (UnsupportedOperationException ex) { - throw ex; - } catch (Throwable ex) { - throw new IllegalStateException(ex); - } - } - - static String getHandshakeApplicationProtocol(SSLEngine sslEngine) { - try { - return (String) GET_HANDSHAKE_APPLICATION_PROTOCOL.invokeExact(sslEngine); - } catch (UnsupportedOperationException ex) { - throw ex; - } catch (Throwable ex) { - throw new IllegalStateException(ex); - } - } - - static void setApplicationProtocols(SSLEngine engine, List supportedProtocols) { - SSLParameters parameters = engine.getSSLParameters(); - - String[] protocolArray = supportedProtocols.toArray(EmptyArrays.EMPTY_STRINGS); - try { - SET_APPLICATION_PROTOCOLS.invokeExact(parameters, protocolArray); - } catch (UnsupportedOperationException ex) { - throw ex; - } catch (Throwable ex) { - throw new IllegalStateException(ex); - } - engine.setSSLParameters(parameters); - } - - static void setHandshakeApplicationProtocolSelector( - SSLEngine engine, BiFunction, String> selector) { - try { - SET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR.invokeExact(engine, selector); - } catch (UnsupportedOperationException ex) { - throw ex; - } catch (Throwable ex) { - throw new IllegalStateException(ex); - } - } - - @SuppressWarnings("unchecked") - static BiFunction, String> getHandshakeApplicationProtocolSelector(SSLEngine engine) { - try { - return (BiFunction, String>) - GET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR.invokeExact(engine); - } catch (UnsupportedOperationException ex) { - throw ex; - } catch (Throwable ex) { - throw new IllegalStateException(ex); - } - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/JdkApplicationProtocolNegotiator.java b/handler/src/main/java/io/netty/handler/ssl/JdkApplicationProtocolNegotiator.java deleted file mode 100644 index 14dec628bc..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/JdkApplicationProtocolNegotiator.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.buffer.ByteBufAllocator; -import javax.net.ssl.SSLEngine; -import java.util.List; -import java.util.Set; - -/** - * JDK extension methods to support {@link ApplicationProtocolNegotiator} - * - * @deprecated use {@link ApplicationProtocolConfig} - */ -@Deprecated -public interface JdkApplicationProtocolNegotiator extends ApplicationProtocolNegotiator { - /** - * Abstract factory pattern for wrapping an {@link SSLEngine} object. This is useful for NPN/APLN JDK support. - */ - interface SslEngineWrapperFactory { - /** - * Abstract factory pattern for wrapping an {@link SSLEngine} object. This is useful for NPN/APLN support. - * - * @param engine The engine to wrap. - * @param applicationNegotiator The application level protocol negotiator - * @param isServer
    - *
  • {@code true} if the engine is for server side of connections
  • - *
  • {@code false} if the engine is for client side of connections
  • - *
- * @return The resulting wrapped engine. This may just be {@code engine}. - */ - SSLEngine wrapSslEngine( - SSLEngine engine, JdkApplicationProtocolNegotiator applicationNegotiator, boolean isServer); - } - - abstract class AllocatorAwareSslEngineWrapperFactory implements SslEngineWrapperFactory { - - @Override - public final SSLEngine wrapSslEngine(SSLEngine engine, - JdkApplicationProtocolNegotiator applicationNegotiator, boolean isServer) { - return wrapSslEngine(engine, ByteBufAllocator.DEFAULT, applicationNegotiator, isServer); - } - - /** - * Abstract factory pattern for wrapping an {@link SSLEngine} object. This is useful for NPN/APLN support. - * - * @param engine The engine to wrap. - * @param alloc the buffer allocator. - * @param applicationNegotiator The application level protocol negotiator - * @param isServer
    - *
  • {@code true} if the engine is for server side of connections
  • - *
  • {@code false} if the engine is for client side of connections
  • - *
- * @return The resulting wrapped engine. This may just be {@code engine}. - */ - abstract SSLEngine wrapSslEngine(SSLEngine engine, ByteBufAllocator alloc, - JdkApplicationProtocolNegotiator applicationNegotiator, boolean isServer); - } - - /** - * Interface to define the role of an application protocol selector in the SSL handshake process. Either - * {@link ProtocolSelector#unsupported()} OR {@link ProtocolSelector#select(List)} will be called for each SSL - * handshake. - */ - interface ProtocolSelector { - /** - * Callback invoked to let the application know that the peer does not support this - * {@link ApplicationProtocolNegotiator}. - */ - void unsupported(); - - /** - * Callback invoked to select the application level protocol from the {@code protocols} provided. - * - * @param protocols the protocols sent by the protocol advertiser - * @return the protocol selected by this {@link ProtocolSelector}. A {@code null} value will indicate the no - * protocols were selected but the handshake should not fail. The decision to fail the handshake is left to the - * other end negotiating the SSL handshake. - * @throws Exception If the {@code protocols} provide warrant failing the SSL handshake with a fatal alert. - */ - String select(List protocols) throws Exception; - } - - /** - * A listener to be notified by which protocol was select by its peer. Either the - * {@link ProtocolSelectionListener#unsupported()} OR the {@link ProtocolSelectionListener#selected(String)} method - * will be called for each SSL handshake. - */ - interface ProtocolSelectionListener { - /** - * Callback invoked to let the application know that the peer does not support this - * {@link ApplicationProtocolNegotiator}. - */ - void unsupported(); - - /** - * Callback invoked to let this application know the protocol chosen by the peer. - * - * @param protocol the protocol selected by the peer. May be {@code null} or empty as supported by the - * application negotiation protocol. - * @throws Exception This may be thrown if the selected protocol is not acceptable and the desired behavior is - * to fail the handshake with a fatal alert. - */ - void selected(String protocol) throws Exception; - } - - /** - * Factory interface for {@link ProtocolSelector} objects. - */ - interface ProtocolSelectorFactory { - /** - * Generate a new instance of {@link ProtocolSelector}. - * @param engine The {@link SSLEngine} that the returned {@link ProtocolSelector} will be used to create an - * instance for. - * @param supportedProtocols The protocols that are supported. - * @return A new instance of {@link ProtocolSelector}. - */ - ProtocolSelector newSelector(SSLEngine engine, Set supportedProtocols); - } - - /** - * Factory interface for {@link ProtocolSelectionListener} objects. - */ - interface ProtocolSelectionListenerFactory { - /** - * Generate a new instance of {@link ProtocolSelectionListener}. - * @param engine The {@link SSLEngine} that the returned {@link ProtocolSelectionListener} will be used to - * create an instance for. - * @param supportedProtocols The protocols that are supported in preference order. - * @return A new instance of {@link ProtocolSelectionListener}. - */ - ProtocolSelectionListener newListener(SSLEngine engine, List supportedProtocols); - } - - /** - * Get the {@link SslEngineWrapperFactory}. - */ - SslEngineWrapperFactory wrapperFactory(); - - /** - * Get the {@link ProtocolSelectorFactory}. - */ - ProtocolSelectorFactory protocolSelectorFactory(); - - /** - * Get the {@link ProtocolSelectionListenerFactory}. - */ - ProtocolSelectionListenerFactory protocolListenerFactory(); -} diff --git a/handler/src/main/java/io/netty/handler/ssl/JdkBaseApplicationProtocolNegotiator.java b/handler/src/main/java/io/netty/handler/ssl/JdkBaseApplicationProtocolNegotiator.java deleted file mode 100644 index 303184ff61..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/JdkBaseApplicationProtocolNegotiator.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import static io.netty.handler.ssl.ApplicationProtocolUtil.toList; -import static java.util.Objects.requireNonNull; - -import java.util.Collections; -import java.util.List; -import java.util.Set; - -import javax.net.ssl.SSLHandshakeException; - -/** - * Common base class for {@link JdkApplicationProtocolNegotiator} classes to inherit from. - */ -class JdkBaseApplicationProtocolNegotiator implements JdkApplicationProtocolNegotiator { - private final List protocols; - private final ProtocolSelectorFactory selectorFactory; - private final ProtocolSelectionListenerFactory listenerFactory; - private final SslEngineWrapperFactory wrapperFactory; - - /** - * Create a new instance. - * @param wrapperFactory Determines which application protocol will be used by wrapping the SSLEngine in use. - * @param selectorFactory How the peer selecting the protocol should behave. - * @param listenerFactory How the peer being notified of the selected protocol should behave. - * @param protocols The order of iteration determines the preference of support for protocols. - */ - JdkBaseApplicationProtocolNegotiator(SslEngineWrapperFactory wrapperFactory, - ProtocolSelectorFactory selectorFactory, ProtocolSelectionListenerFactory listenerFactory, - Iterable protocols) { - this(wrapperFactory, selectorFactory, listenerFactory, toList(protocols)); - } - - /** - * Create a new instance. - * @param wrapperFactory Determines which application protocol will be used by wrapping the SSLEngine in use. - * @param selectorFactory How the peer selecting the protocol should behave. - * @param listenerFactory How the peer being notified of the selected protocol should behave. - * @param protocols The order of iteration determines the preference of support for protocols. - */ - JdkBaseApplicationProtocolNegotiator(SslEngineWrapperFactory wrapperFactory, - ProtocolSelectorFactory selectorFactory, ProtocolSelectionListenerFactory listenerFactory, - String... protocols) { - this(wrapperFactory, selectorFactory, listenerFactory, toList(protocols)); - } - - /** - * Create a new instance. - * @param wrapperFactory Determines which application protocol will be used by wrapping the SSLEngine in use. - * @param selectorFactory How the peer selecting the protocol should behave. - * @param listenerFactory How the peer being notified of the selected protocol should behave. - * @param protocols The order of iteration determines the preference of support for protocols. - */ - private JdkBaseApplicationProtocolNegotiator(SslEngineWrapperFactory wrapperFactory, - ProtocolSelectorFactory selectorFactory, ProtocolSelectionListenerFactory listenerFactory, - List protocols) { - this.wrapperFactory = requireNonNull(wrapperFactory, "wrapperFactory"); - this.selectorFactory = requireNonNull(selectorFactory, "selectorFactory"); - this.listenerFactory = requireNonNull(listenerFactory, "listenerFactory"); - this.protocols = Collections.unmodifiableList(requireNonNull(protocols, "protocols")); - } - - @Override - public List protocols() { - return protocols; - } - - @Override - public ProtocolSelectorFactory protocolSelectorFactory() { - return selectorFactory; - } - - @Override - public ProtocolSelectionListenerFactory protocolListenerFactory() { - return listenerFactory; - } - - @Override - public SslEngineWrapperFactory wrapperFactory() { - return wrapperFactory; - } - - static final ProtocolSelectorFactory FAIL_SELECTOR_FACTORY = (engine, supportedProtocols) -> - new FailProtocolSelector((JdkSslEngine) engine, supportedProtocols); - - static final ProtocolSelectorFactory NO_FAIL_SELECTOR_FACTORY = (engine, supportedProtocols) -> - new NoFailProtocolSelector((JdkSslEngine) engine, supportedProtocols); - - static final ProtocolSelectionListenerFactory FAIL_SELECTION_LISTENER_FACTORY = (engine, supportedProtocols) -> - new FailProtocolSelectionListener((JdkSslEngine) engine, supportedProtocols); - - static final ProtocolSelectionListenerFactory NO_FAIL_SELECTION_LISTENER_FACTORY = (engine, supportedProtocols) -> - new NoFailProtocolSelectionListener((JdkSslEngine) engine, supportedProtocols); - - static class NoFailProtocolSelector implements ProtocolSelector { - private final JdkSslEngine engineWrapper; - private final Set supportedProtocols; - - NoFailProtocolSelector(JdkSslEngine engineWrapper, Set supportedProtocols) { - this.engineWrapper = engineWrapper; - this.supportedProtocols = supportedProtocols; - } - - @Override - public void unsupported() { - engineWrapper.setNegotiatedApplicationProtocol(null); - } - - @Override - public String select(List protocols) throws Exception { - for (String p : supportedProtocols) { - if (protocols.contains(p)) { - engineWrapper.setNegotiatedApplicationProtocol(p); - return p; - } - } - return noSelectMatchFound(); - } - - public String noSelectMatchFound() throws Exception { - engineWrapper.setNegotiatedApplicationProtocol(null); - return null; - } - } - - private static final class FailProtocolSelector extends NoFailProtocolSelector { - FailProtocolSelector(JdkSslEngine engineWrapper, Set supportedProtocols) { - super(engineWrapper, supportedProtocols); - } - - @Override - public String noSelectMatchFound() throws Exception { - throw new SSLHandshakeException("Selected protocol is not supported"); - } - } - - private static class NoFailProtocolSelectionListener implements ProtocolSelectionListener { - private final JdkSslEngine engineWrapper; - private final List supportedProtocols; - - NoFailProtocolSelectionListener(JdkSslEngine engineWrapper, List supportedProtocols) { - this.engineWrapper = engineWrapper; - this.supportedProtocols = supportedProtocols; - } - - @Override - public void unsupported() { - engineWrapper.setNegotiatedApplicationProtocol(null); - } - - @Override - public void selected(String protocol) throws Exception { - if (supportedProtocols.contains(protocol)) { - engineWrapper.setNegotiatedApplicationProtocol(protocol); - } else { - noSelectedMatchFound(protocol); - } - } - - protected void noSelectedMatchFound(String protocol) throws Exception { - // Will never be called. - } - } - - private static final class FailProtocolSelectionListener extends NoFailProtocolSelectionListener { - FailProtocolSelectionListener(JdkSslEngine engineWrapper, List supportedProtocols) { - super(engineWrapper, supportedProtocols); - } - - @Override - protected void noSelectedMatchFound(String protocol) throws Exception { - throw new SSLHandshakeException("No compatible protocols found"); - } - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/JdkDefaultApplicationProtocolNegotiator.java b/handler/src/main/java/io/netty/handler/ssl/JdkDefaultApplicationProtocolNegotiator.java deleted file mode 100644 index 170c2870cd..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/JdkDefaultApplicationProtocolNegotiator.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import java.util.Collections; -import java.util.List; - -/** - * The {@link JdkApplicationProtocolNegotiator} to use if you do not care about NPN or ALPN and are using - * {@link SslProvider#JDK}. - */ -final class JdkDefaultApplicationProtocolNegotiator implements JdkApplicationProtocolNegotiator { - public static final JdkDefaultApplicationProtocolNegotiator INSTANCE = - new JdkDefaultApplicationProtocolNegotiator(); - private static final SslEngineWrapperFactory DEFAULT_SSL_ENGINE_WRAPPER_FACTORY = - (engine, applicationNegotiator, isServer) -> engine; - - private JdkDefaultApplicationProtocolNegotiator() { - } - - @Override - public SslEngineWrapperFactory wrapperFactory() { - return DEFAULT_SSL_ENGINE_WRAPPER_FACTORY; - } - - @Override - public ProtocolSelectorFactory protocolSelectorFactory() { - throw new UnsupportedOperationException("Application protocol negotiation unsupported"); - } - - @Override - public ProtocolSelectionListenerFactory protocolListenerFactory() { - throw new UnsupportedOperationException("Application protocol negotiation unsupported"); - } - - @Override - public List protocols() { - return Collections.emptyList(); - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/JdkNpnApplicationProtocolNegotiator.java b/handler/src/main/java/io/netty/handler/ssl/JdkNpnApplicationProtocolNegotiator.java deleted file mode 100644 index cbaeb0c462..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/JdkNpnApplicationProtocolNegotiator.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import javax.net.ssl.SSLEngine; - -/** - * The {@link JdkApplicationProtocolNegotiator} to use if you need NPN and are using {@link SslProvider#JDK}. - * - * @deprecated use {@link ApplicationProtocolConfig}. - */ -@Deprecated -public final class JdkNpnApplicationProtocolNegotiator extends JdkBaseApplicationProtocolNegotiator { - private static final SslEngineWrapperFactory NPN_WRAPPER = new SslEngineWrapperFactory() { - { - if (!JettyNpnSslEngine.isAvailable()) { - throw new RuntimeException("NPN unsupported. Is your classpath configured correctly?" - + " See https://wiki.eclipse.org/Jetty/Feature/NPN"); - } - } - - @Override - public SSLEngine wrapSslEngine(SSLEngine engine, - JdkApplicationProtocolNegotiator applicationNegotiator, boolean isServer) { - return new JettyNpnSslEngine(engine, applicationNegotiator, isServer); - } - }; - - /** - * Create a new instance. - * @param protocols The order of iteration determines the preference of support for protocols. - */ - public JdkNpnApplicationProtocolNegotiator(Iterable protocols) { - this(false, protocols); - } - - /** - * Create a new instance. - * @param protocols The order of iteration determines the preference of support for protocols. - */ - public JdkNpnApplicationProtocolNegotiator(String... protocols) { - this(false, protocols); - } - - /** - * Create a new instance. - * @param failIfNoCommonProtocols Fail with a fatal alert if not common protocols are detected. - * @param protocols The order of iteration determines the preference of support for protocols. - */ - public JdkNpnApplicationProtocolNegotiator(boolean failIfNoCommonProtocols, Iterable protocols) { - this(failIfNoCommonProtocols, failIfNoCommonProtocols, protocols); - } - - /** - * Create a new instance. - * @param failIfNoCommonProtocols Fail with a fatal alert if not common protocols are detected. - * @param protocols The order of iteration determines the preference of support for protocols. - */ - public JdkNpnApplicationProtocolNegotiator(boolean failIfNoCommonProtocols, String... protocols) { - this(failIfNoCommonProtocols, failIfNoCommonProtocols, protocols); - } - - /** - * Create a new instance. - * @param clientFailIfNoCommonProtocols Client side fail with a fatal alert if not common protocols are detected. - * @param serverFailIfNoCommonProtocols Server side fail with a fatal alert if not common protocols are detected. - * @param protocols The order of iteration determines the preference of support for protocols. - */ - public JdkNpnApplicationProtocolNegotiator(boolean clientFailIfNoCommonProtocols, - boolean serverFailIfNoCommonProtocols, Iterable protocols) { - this(clientFailIfNoCommonProtocols ? FAIL_SELECTOR_FACTORY : NO_FAIL_SELECTOR_FACTORY, - serverFailIfNoCommonProtocols ? FAIL_SELECTION_LISTENER_FACTORY : NO_FAIL_SELECTION_LISTENER_FACTORY, - protocols); - } - - /** - * Create a new instance. - * @param clientFailIfNoCommonProtocols Client side fail with a fatal alert if not common protocols are detected. - * @param serverFailIfNoCommonProtocols Server side fail with a fatal alert if not common protocols are detected. - * @param protocols The order of iteration determines the preference of support for protocols. - */ - public JdkNpnApplicationProtocolNegotiator(boolean clientFailIfNoCommonProtocols, - boolean serverFailIfNoCommonProtocols, String... protocols) { - this(clientFailIfNoCommonProtocols ? FAIL_SELECTOR_FACTORY : NO_FAIL_SELECTOR_FACTORY, - serverFailIfNoCommonProtocols ? FAIL_SELECTION_LISTENER_FACTORY : NO_FAIL_SELECTION_LISTENER_FACTORY, - protocols); - } - - /** - * Create a new instance. - * @param selectorFactory The factory which provides classes responsible for selecting the protocol. - * @param listenerFactory The factory which provides to be notified of which protocol was selected. - * @param protocols The order of iteration determines the preference of support for protocols. - */ - public JdkNpnApplicationProtocolNegotiator(ProtocolSelectorFactory selectorFactory, - ProtocolSelectionListenerFactory listenerFactory, Iterable protocols) { - super(NPN_WRAPPER, selectorFactory, listenerFactory, protocols); - } - - /** - * Create a new instance. - * @param selectorFactory The factory which provides classes responsible for selecting the protocol. - * @param listenerFactory The factory which provides to be notified of which protocol was selected. - * @param protocols The order of iteration determines the preference of support for protocols. - */ - public JdkNpnApplicationProtocolNegotiator(ProtocolSelectorFactory selectorFactory, - ProtocolSelectionListenerFactory listenerFactory, String... protocols) { - super(NPN_WRAPPER, selectorFactory, listenerFactory, protocols); - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/JdkSslClientContext.java b/handler/src/main/java/io/netty/handler/ssl/JdkSslClientContext.java deleted file mode 100644 index a44a452c4c..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/JdkSslClientContext.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.ssl; - -import java.io.File; -import java.security.KeyStore; -import java.security.PrivateKey; -import java.security.Provider; -import java.security.cert.X509Certificate; - -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLException; -import javax.net.ssl.SSLSessionContext; -import javax.net.ssl.TrustManagerFactory; - -/** - * A client-side {@link SslContext} which uses JDK's SSL/TLS implementation. - */ -final class JdkSslClientContext extends JdkSslContext { - - // FIXME test only - JdkSslClientContext(Provider provider, - File trustCertCollectionFile, - TrustManagerFactory trustManagerFactory, - Iterable ciphers, - CipherSuiteFilter cipherFilter, - JdkApplicationProtocolNegotiator apn, - long sessionCacheSize, - long sessionTimeout) - throws SSLException { - super(newSSLContext(provider, toX509CertificatesInternal(trustCertCollectionFile), - trustManagerFactory, null, null, - null, null, sessionCacheSize, sessionTimeout, KeyStore.getDefaultType()), true, - ciphers, cipherFilter, apn, ClientAuth.NONE, null, false); - } - - JdkSslClientContext(Provider sslContextProvider, - X509Certificate[] trustCertCollection, - TrustManagerFactory trustManagerFactory, - X509Certificate[] keyCertChain, - PrivateKey key, - String keyPassword, - KeyManagerFactory keyManagerFactory, - Iterable ciphers, - CipherSuiteFilter cipherFilter, - ApplicationProtocolConfig apn, - String[] protocols, - long sessionCacheSize, - long sessionTimeout, - String keyStore) - throws SSLException { - super(newSSLContext(sslContextProvider, trustCertCollection, trustManagerFactory, - keyCertChain, key, keyPassword, keyManagerFactory, sessionCacheSize, sessionTimeout, keyStore), - true, ciphers, cipherFilter, toNegotiator(apn, false), ClientAuth.NONE, protocols, false); - } - - private static SSLContext newSSLContext(Provider sslContextProvider, - X509Certificate[] trustCertCollection, - TrustManagerFactory trustManagerFactory, X509Certificate[] keyCertChain, - PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory, - long sessionCacheSize, long sessionTimeout, - String keyStore) throws SSLException { - try { - if (trustCertCollection != null) { - trustManagerFactory = buildTrustManagerFactory(trustCertCollection, trustManagerFactory, keyStore); - } - if (keyCertChain != null) { - keyManagerFactory = buildKeyManagerFactory(keyCertChain, null, - key, keyPassword, keyManagerFactory, keyStore); - } - SSLContext ctx = sslContextProvider == null ? SSLContext.getInstance(PROTOCOL) - : SSLContext.getInstance(PROTOCOL, sslContextProvider); - ctx.init(keyManagerFactory == null ? null : keyManagerFactory.getKeyManagers(), - trustManagerFactory == null ? null : trustManagerFactory.getTrustManagers(), - null); - - SSLSessionContext sessCtx = ctx.getClientSessionContext(); - if (sessionCacheSize > 0) { - sessCtx.setSessionCacheSize((int) Math.min(sessionCacheSize, Integer.MAX_VALUE)); - } - if (sessionTimeout > 0) { - sessCtx.setSessionTimeout((int) Math.min(sessionTimeout, Integer.MAX_VALUE)); - } - return ctx; - } catch (Exception e) { - if (e instanceof SSLException) { - throw (SSLException) e; - } - throw new SSLException("failed to initialize the client-side SSL context", e); - } - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/JdkSslContext.java b/handler/src/main/java/io/netty/handler/ssl/JdkSslContext.java deleted file mode 100644 index 64cb8c79b9..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/JdkSslContext.java +++ /dev/null @@ -1,515 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.ssl; - -import io.netty.buffer.ByteBufAllocator; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.internal.EmptyArrays; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.io.File; -import java.io.IOException; -import java.security.InvalidAlgorithmParameterException; -import java.security.KeyException; -import java.security.KeyStore; -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; -import java.security.spec.InvalidKeySpecException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; - -import javax.crypto.NoSuchPaddingException; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLSessionContext; - -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 java.util.Objects.requireNonNull; - -/** - * An {@link SslContext} which uses JDK's SSL/TLS implementation. - */ -public class JdkSslContext extends SslContext { - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(JdkSslContext.class); - - 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; - try { - context = SSLContext.getInstance(PROTOCOL); - context.init(null, null, null); - } catch (Exception e) { - throw new Error("failed to initialize the default SSL context", e); - } - - DEFAULT_PROVIDER = context.getProvider(); - - SSLEngine engine = context.createSSLEngine(); - DEFAULT_PROTOCOLS = defaultProtocols(context, 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(SSLContext context, SSLEngine engine) { - // Choose the sensible default list of protocols that respects JDK flags, eg. jdk.tls.client.protocols - final String[] supportedProtocols = context.getDefaultSSLParameters().getProtocols(); - Set supportedProtocolsSet = new HashSet<>(supportedProtocols.length); - Collections.addAll(supportedProtocolsSet, supportedProtocols); - List protocols = new ArrayList<>(); - addIfSupported( - supportedProtocolsSet, protocols, - SslProtocols.TLS_v1_3, SslProtocols.TLS_v1_2, - SslProtocols.TLS_v1_1, SslProtocols.TLS_v1); - - if (!protocols.isEmpty()) { - return protocols.toArray(EmptyArrays.EMPTY_STRINGS); - } - return engine.getEnabledProtocols(); - } - - private static Set supportedCiphers(SSLEngine engine) { - // Choose the sensible default list of cipher suites. - final String[] supportedCiphers = engine.getSupportedCipherSuites(); - Set supportedCiphersSet = new LinkedHashSet<>(supportedCiphers.length); - for (int i = 0; i < supportedCiphers.length; ++i) { - String supportedCipher = supportedCiphers[i]; - 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. - // See the IBM forum discussion [3] and issue on IBM's JVM [4] for more details. - //[1] https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#ciphersuites - //[2] https://www.ibm.com/support/knowledgecenter/en/SSYKE2_8.0.0/com.ibm.java.security.component.80.doc/ - // security-component/jsse2Docs/ciphersuites.html - //[3] https://www.ibm.com/developerworks/community/forums/html/topic?id=9b5a56a9-fa46-4031-b33b-df91e28d77c2 - //[4] https://www.ibm.com/developerworks/rfe/execute?use_case=viewRfe&CR_ID=71770 - if (supportedCipher.startsWith("SSL_")) { - final String tlsPrefixedCipherName = "TLS_" + supportedCipher.substring("SSL_".length()); - try { - engine.setEnabledCipherSuites(new String[]{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(supportedCiphers, ciphers, DEFAULT_CIPHER_SUITES); - useFallbackCiphersIfDefaultIsEmpty(ciphers, engine.getEnabledCipherSuites()); - return ciphers; - } - - private static boolean isTlsV13Supported(String[] protocols) { - for (String protocol: protocols) { - if (SslProtocols.TLS_v1_3.equals(protocol)) { - return true; - } - } - return false; - } - - private final String[] protocols; - private final String[] cipherSuites; - private final List unmodifiableCipherSuites; - @SuppressWarnings("deprecation") - private final JdkApplicationProtocolNegotiator apn; - private final ClientAuth clientAuth; - private final SSLContext sslContext; - private final boolean isClient; - - /** - * 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 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, - JdkDefaultApplicationProtocolNegotiator.INSTANCE, 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}. - * @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, 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") - JdkSslContext(SSLContext sslContext, boolean isClient, Iterable ciphers, CipherSuiteFilter cipherFilter, - JdkApplicationProtocolNegotiator apn, ClientAuth clientAuth, String[] protocols, boolean startTls) { - super(startTls); - this.apn = requireNonNull(apn, "apn"); - this.clientAuth = requireNonNull(clientAuth, "clientAuth"); - this.sslContext = requireNonNull(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(sslContext, 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 = requireNonNull(cipherFilter, "cipherFilter").filterCipherSuites( - ciphers, defaultCiphers, supportedCiphers); - - unmodifiableCipherSuites = Collections.unmodifiableList(Arrays.asList(cipherSuites)); - this.isClient = isClient; - } - - /** - * Returns the JDK {@link SSLContext} object held by this context. - */ - public final SSLContext context() { - return sslContext; - } - - @Override - public final boolean isClient() { - return isClient; - } - - /** - * Returns the JDK {@link SSLSessionContext} object held by this context. - */ - @Override - public final SSLSessionContext sessionContext() { - if (isServer()) { - return context().getServerSessionContext(); - } else { - return context().getClientSessionContext(); - } - } - - @Override - public final List cipherSuites() { - return unmodifiableCipherSuites; - } - - @Override - public final SSLEngine newEngine(ByteBufAllocator alloc) { - return configureAndWrapEngine(context().createSSLEngine(), alloc); - } - - @Override - public final SSLEngine newEngine(ByteBufAllocator alloc, String peerHost, int peerPort) { - return configureAndWrapEngine(context().createSSLEngine(peerHost, peerPort), alloc); - } - - @SuppressWarnings("deprecation") - private SSLEngine configureAndWrapEngine(SSLEngine engine, ByteBufAllocator alloc) { - engine.setEnabledCipherSuites(cipherSuites); - engine.setEnabledProtocols(protocols); - engine.setUseClientMode(isClient()); - if (isServer()) { - switch (clientAuth) { - case OPTIONAL: - engine.setWantClientAuth(true); - break; - case REQUIRE: - engine.setNeedClientAuth(true); - break; - case NONE: - break; // exhaustive cases - default: - throw new Error("Unknown auth " + clientAuth); - } - } - JdkApplicationProtocolNegotiator.SslEngineWrapperFactory factory = apn.wrapperFactory(); - if (factory instanceof JdkApplicationProtocolNegotiator.AllocatorAwareSslEngineWrapperFactory) { - return ((JdkApplicationProtocolNegotiator.AllocatorAwareSslEngineWrapperFactory) factory) - .wrapSslEngine(engine, alloc, apn, isServer()); - } - return factory.wrapSslEngine(engine, apn, isServer()); - } - - @Override - public final JdkApplicationProtocolNegotiator applicationProtocolNegotiator() { - return apn; - } - - /** - * Translate a {@link ApplicationProtocolConfig} object to a {@link JdkApplicationProtocolNegotiator} object. - * @param config The configuration which defines the translation - * @param isServer {@code true} if a server {@code false} otherwise. - * @return The results of the translation - */ - @SuppressWarnings("deprecation") - static JdkApplicationProtocolNegotiator toNegotiator(ApplicationProtocolConfig config, boolean isServer) { - if (config == null) { - return JdkDefaultApplicationProtocolNegotiator.INSTANCE; - } - - switch(config.protocol()) { - case NONE: - return JdkDefaultApplicationProtocolNegotiator.INSTANCE; - case ALPN: - if (isServer) { - switch(config.selectorFailureBehavior()) { - case FATAL_ALERT: - return new JdkAlpnApplicationProtocolNegotiator(true, config.supportedProtocols()); - case NO_ADVERTISE: - return new JdkAlpnApplicationProtocolNegotiator(false, config.supportedProtocols()); - default: - throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ") - .append(config.selectorFailureBehavior()).append(" failure behavior").toString()); - } - } else { - switch(config.selectedListenerFailureBehavior()) { - case ACCEPT: - return new JdkAlpnApplicationProtocolNegotiator(false, config.supportedProtocols()); - case FATAL_ALERT: - return new JdkAlpnApplicationProtocolNegotiator(true, config.supportedProtocols()); - default: - throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ") - .append(config.selectedListenerFailureBehavior()).append(" failure behavior").toString()); - } - } - case NPN: - if (isServer) { - switch(config.selectedListenerFailureBehavior()) { - case ACCEPT: - return new JdkNpnApplicationProtocolNegotiator(false, config.supportedProtocols()); - case FATAL_ALERT: - return new JdkNpnApplicationProtocolNegotiator(true, config.supportedProtocols()); - default: - throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ") - .append(config.selectedListenerFailureBehavior()).append(" failure behavior").toString()); - } - } else { - switch(config.selectorFailureBehavior()) { - case FATAL_ALERT: - return new JdkNpnApplicationProtocolNegotiator(true, config.supportedProtocols()); - case NO_ADVERTISE: - return new JdkNpnApplicationProtocolNegotiator(false, config.supportedProtocols()); - default: - throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ") - .append(config.selectorFailureBehavior()).append(" failure behavior").toString()); - } - } - default: - throw new UnsupportedOperationException(new StringBuilder("JDK provider does not support ") - .append(config.protocol()).append(" protocol").toString()); - } - } - - /** - * Build a {@link KeyManagerFactory} based upon a key file, key file password, and a certificate chain. - * @param certChainFile an X.509 certificate chain file in PEM format - * @param keyFile a PKCS#8 private key file in PEM format - * @param keyPassword the password of the {@code keyFile}. - * {@code null} if it's not password-protected. - * @param kmf The existing {@link KeyManagerFactory} that will be used if not {@code null} - * @param keyStore the {@link KeyStore} that should be used in the {@link KeyManagerFactory} - * @return A {@link KeyManagerFactory} based upon a key file, key file password, and a certificate chain. - */ - static KeyManagerFactory buildKeyManagerFactory(File certChainFile, File keyFile, String keyPassword, - KeyManagerFactory kmf, String keyStore) - throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException, - NoSuchPaddingException, InvalidKeySpecException, InvalidAlgorithmParameterException, - CertificateException, KeyException, IOException { - String algorithm = Security.getProperty("ssl.KeyManagerFactory.algorithm"); - if (algorithm == null) { - algorithm = "SunX509"; - } - return buildKeyManagerFactory(certChainFile, algorithm, keyFile, keyPassword, kmf, keyStore); - } - - /** - * Build a {@link KeyManagerFactory} based upon a key file, key file password, and a certificate chain. - * @param certChainFile an X.509 certificate chain file in PEM format - * @param keyFile a PKCS#8 private key file in PEM format - * @param keyPassword the password of the {@code keyFile}. - * {@code null} if it's not password-protected. - * @param kmf The existing {@link KeyManagerFactory} that will be used if not {@code null} - * @return A {@link KeyManagerFactory} based upon a key file, key file password, and a certificate chain. - * @deprecated will be removed. - */ - @Deprecated - protected static KeyManagerFactory buildKeyManagerFactory(File certChainFile, File keyFile, String keyPassword, - KeyManagerFactory kmf) - throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException, - NoSuchPaddingException, InvalidKeySpecException, InvalidAlgorithmParameterException, - CertificateException, KeyException, IOException { - return buildKeyManagerFactory(certChainFile, keyFile, keyPassword, kmf, KeyStore.getDefaultType()); - } - - /** - * Build a {@link KeyManagerFactory} based upon a key algorithm, key file, key file password, - * and a certificate chain. - * @param certChainFile an X.509 certificate chain file in PEM format - * @param keyAlgorithm the standard name of the requested algorithm. See the Java Secure Socket Extension - * Reference Guide for information about standard algorithm names. - * @param keyFile a PKCS#8 private key file in PEM format - * @param keyPassword the password of the {@code keyFile}. - * {@code null} if it's not password-protected. - * @param kmf The existing {@link KeyManagerFactory} that will be used if not {@code null} - * @param keyStore the {@link KeyStore} that should be used in the {@link KeyManagerFactory} - * @return A {@link KeyManagerFactory} based upon a key algorithm, key file, key file password, - * and a certificate chain. - */ - static KeyManagerFactory buildKeyManagerFactory(File certChainFile, - String keyAlgorithm, File keyFile, String keyPassword, KeyManagerFactory kmf, - String keyStore) - throws KeyStoreException, NoSuchAlgorithmException, NoSuchPaddingException, - InvalidKeySpecException, InvalidAlgorithmParameterException, IOException, - CertificateException, KeyException, UnrecoverableKeyException { - return buildKeyManagerFactory(toX509Certificates(certChainFile), keyAlgorithm, - toPrivateKey(keyFile, keyPassword), keyPassword, kmf, keyStore); - } - - /** - * Build a {@link KeyManagerFactory} based upon a key algorithm, key file, key file password, - * and a certificate chain. - * @param certChainFile an buildKeyManagerFactory X.509 certificate chain file in PEM format - * @param keyAlgorithm the standard name of the requested algorithm. See the Java Secure Socket Extension - * Reference Guide for information about standard algorithm names. - * @param keyFile a PKCS#8 private key file in PEM format - * @param keyPassword the password of the {@code keyFile}. - * {@code null} if it's not password-protected. - * @param kmf The existing {@link KeyManagerFactory} that will be used if not {@code null} - * @return A {@link KeyManagerFactory} based upon a key algorithm, key file, key file password, - * and a certificate chain. - * @deprecated will be removed. - */ - @Deprecated - protected static KeyManagerFactory buildKeyManagerFactory(File certChainFile, - String keyAlgorithm, File keyFile, - String keyPassword, KeyManagerFactory kmf) - throws KeyStoreException, NoSuchAlgorithmException, NoSuchPaddingException, - InvalidKeySpecException, InvalidAlgorithmParameterException, IOException, - CertificateException, KeyException, UnrecoverableKeyException { - return buildKeyManagerFactory(toX509Certificates(certChainFile), keyAlgorithm, - toPrivateKey(keyFile, keyPassword), keyPassword, kmf, KeyStore.getDefaultType()); - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/JdkSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/JdkSslEngine.java deleted file mode 100644 index c29cf40a68..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/JdkSslEngine.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import java.nio.ByteBuffer; - -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.SSLParameters; -import javax.net.ssl.SSLSession; - -class JdkSslEngine extends SSLEngine implements ApplicationProtocolAccessor { - private final SSLEngine engine; - private volatile String applicationProtocol; - - JdkSslEngine(SSLEngine engine) { - this.engine = engine; - } - - @Override - public String getNegotiatedApplicationProtocol() { - return applicationProtocol; - } - - void setNegotiatedApplicationProtocol(String applicationProtocol) { - this.applicationProtocol = applicationProtocol; - } - - @Override - public SSLSession getSession() { - return engine.getSession(); - } - - public SSLEngine getWrappedEngine() { - return engine; - } - - @Override - public void closeInbound() throws SSLException { - engine.closeInbound(); - } - - @Override - public void closeOutbound() { - engine.closeOutbound(); - } - - @Override - public String getPeerHost() { - return engine.getPeerHost(); - } - - @Override - public int getPeerPort() { - return engine.getPeerPort(); - } - - @Override - public SSLEngineResult wrap(ByteBuffer byteBuffer, ByteBuffer byteBuffer2) throws SSLException { - return engine.wrap(byteBuffer, byteBuffer2); - } - - @Override - public SSLEngineResult wrap(ByteBuffer[] byteBuffers, ByteBuffer byteBuffer) throws SSLException { - return engine.wrap(byteBuffers, byteBuffer); - } - - @Override - public SSLEngineResult wrap(ByteBuffer[] byteBuffers, int i, int i2, ByteBuffer byteBuffer) throws SSLException { - return engine.wrap(byteBuffers, i, i2, byteBuffer); - } - - @Override - public SSLEngineResult unwrap(ByteBuffer byteBuffer, ByteBuffer byteBuffer2) throws SSLException { - return engine.unwrap(byteBuffer, byteBuffer2); - } - - @Override - public SSLEngineResult unwrap(ByteBuffer byteBuffer, ByteBuffer[] byteBuffers) throws SSLException { - return engine.unwrap(byteBuffer, byteBuffers); - } - - @Override - public SSLEngineResult unwrap(ByteBuffer byteBuffer, ByteBuffer[] byteBuffers, int i, int i2) throws SSLException { - return engine.unwrap(byteBuffer, byteBuffers, i, i2); - } - - @Override - public Runnable getDelegatedTask() { - return engine.getDelegatedTask(); - } - - @Override - public boolean isInboundDone() { - return engine.isInboundDone(); - } - - @Override - public boolean isOutboundDone() { - return engine.isOutboundDone(); - } - - @Override - public String[] getSupportedCipherSuites() { - return engine.getSupportedCipherSuites(); - } - - @Override - public String[] getEnabledCipherSuites() { - return engine.getEnabledCipherSuites(); - } - - @Override - public void setEnabledCipherSuites(String[] strings) { - engine.setEnabledCipherSuites(strings); - } - - @Override - public String[] getSupportedProtocols() { - return engine.getSupportedProtocols(); - } - - @Override - public String[] getEnabledProtocols() { - return engine.getEnabledProtocols(); - } - - @Override - public void setEnabledProtocols(String[] strings) { - engine.setEnabledProtocols(strings); - } - - @Override - public SSLSession getHandshakeSession() { - return engine.getHandshakeSession(); - } - - @Override - public void beginHandshake() throws SSLException { - engine.beginHandshake(); - } - - @Override - public HandshakeStatus getHandshakeStatus() { - return engine.getHandshakeStatus(); - } - - @Override - public void setUseClientMode(boolean b) { - engine.setUseClientMode(b); - } - - @Override - public boolean getUseClientMode() { - return engine.getUseClientMode(); - } - - @Override - public void setNeedClientAuth(boolean b) { - engine.setNeedClientAuth(b); - } - - @Override - public boolean getNeedClientAuth() { - return engine.getNeedClientAuth(); - } - - @Override - public void setWantClientAuth(boolean b) { - engine.setWantClientAuth(b); - } - - @Override - public boolean getWantClientAuth() { - return engine.getWantClientAuth(); - } - - @Override - public void setEnableSessionCreation(boolean b) { - engine.setEnableSessionCreation(b); - } - - @Override - public boolean getEnableSessionCreation() { - return engine.getEnableSessionCreation(); - } - - @Override - public SSLParameters getSSLParameters() { - return engine.getSSLParameters(); - } - - @Override - public void setSSLParameters(SSLParameters sslParameters) { - engine.setSSLParameters(sslParameters); - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/JdkSslServerContext.java b/handler/src/main/java/io/netty/handler/ssl/JdkSslServerContext.java deleted file mode 100644 index b3b03ea1e6..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/JdkSslServerContext.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.ssl; - -import java.io.File; -import java.security.KeyStore; -import java.security.PrivateKey; -import java.security.Provider; -import java.security.cert.X509Certificate; - -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLException; -import javax.net.ssl.SSLSessionContext; -import javax.net.ssl.TrustManagerFactory; - -/** - * A server-side {@link SslContext} which uses JDK's SSL/TLS implementation. - */ -final class JdkSslServerContext extends JdkSslContext { - - // FIXME test only - JdkSslServerContext(Provider provider, - File certChainFile, - File keyFile, - String keyPassword, - Iterable ciphers, - CipherSuiteFilter cipherFilter, - JdkApplicationProtocolNegotiator apn, - long sessionCacheSize, - long sessionTimeout) - throws SSLException { - super(newSSLContext(provider, null, null, - toX509CertificatesInternal(certChainFile), toPrivateKeyInternal(keyFile, keyPassword), - keyPassword, null, sessionCacheSize, sessionTimeout, KeyStore.getDefaultType()), false, - ciphers, cipherFilter, apn, ClientAuth.NONE, null, false); - } - - JdkSslServerContext(Provider provider, - X509Certificate[] trustCertCollection, - TrustManagerFactory trustManagerFactory, - X509Certificate[] keyCertChain, - PrivateKey key, - String keyPassword, - KeyManagerFactory keyManagerFactory, - Iterable ciphers, - CipherSuiteFilter cipherFilter, - ApplicationProtocolConfig apn, - long sessionCacheSize, - long sessionTimeout, - ClientAuth clientAuth, - String[] protocols, - boolean startTls, - String keyStore) - throws SSLException { - super(newSSLContext(provider, trustCertCollection, trustManagerFactory, keyCertChain, key, - keyPassword, keyManagerFactory, sessionCacheSize, sessionTimeout, keyStore), false, - ciphers, cipherFilter, toNegotiator(apn, true), clientAuth, protocols, startTls); - } - - private static SSLContext newSSLContext(Provider sslContextProvider, X509Certificate[] trustCertCollection, - TrustManagerFactory trustManagerFactory, X509Certificate[] keyCertChain, - PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory, - long sessionCacheSize, long sessionTimeout, String keyStore) - throws SSLException { - if (key == null && keyManagerFactory == null) { - throw new NullPointerException("key, keyManagerFactory"); - } - - try { - if (trustCertCollection != null) { - trustManagerFactory = buildTrustManagerFactory(trustCertCollection, trustManagerFactory, keyStore); - } - if (key != null) { - keyManagerFactory = buildKeyManagerFactory(keyCertChain, null, - key, keyPassword, keyManagerFactory, null); - } - - // Initialize the SSLContext to work with our key managers. - SSLContext ctx = sslContextProvider == null ? SSLContext.getInstance(PROTOCOL) - : SSLContext.getInstance(PROTOCOL, sslContextProvider); - ctx.init(keyManagerFactory.getKeyManagers(), - trustManagerFactory == null ? null : trustManagerFactory.getTrustManagers(), - null); - - SSLSessionContext sessCtx = ctx.getServerSessionContext(); - if (sessionCacheSize > 0) { - sessCtx.setSessionCacheSize((int) Math.min(sessionCacheSize, Integer.MAX_VALUE)); - } - if (sessionTimeout > 0) { - sessCtx.setSessionTimeout((int) Math.min(sessionTimeout, Integer.MAX_VALUE)); - } - return ctx; - } catch (Exception e) { - if (e instanceof SSLException) { - throw (SSLException) e; - } - throw new SSLException("failed to initialize the server-side SSL context", e); - } - } - -} diff --git a/handler/src/main/java/io/netty/handler/ssl/JettyAlpnSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/JettyAlpnSslEngine.java deleted file mode 100644 index 28ed6597cb..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/JettyAlpnSslEngine.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import static io.netty.handler.ssl.SslUtils.toSSLHandshakeException; -import static java.util.Objects.requireNonNull; - -import io.netty.handler.ssl.JdkApplicationProtocolNegotiator.ProtocolSelectionListener; -import io.netty.handler.ssl.JdkApplicationProtocolNegotiator.ProtocolSelector; - -import java.util.LinkedHashSet; -import java.util.List; - -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLException; - -import io.netty.util.internal.PlatformDependent; -import org.eclipse.jetty.alpn.ALPN; - -abstract class JettyAlpnSslEngine extends JdkSslEngine { - private static final boolean available = initAvailable(); - - static boolean isAvailable() { - return available; - } - - private static boolean initAvailable() { - if (PlatformDependent.javaVersion() <= 8) { - try { - // Always use bootstrap class loader. - Class.forName("sun.security.ssl.ALPNExtension", true, null); - return true; - } catch (Throwable ignore) { - // alpn-boot was not loaded. - } - } - return false; - } - - static JettyAlpnSslEngine newClientEngine(SSLEngine engine, - JdkApplicationProtocolNegotiator applicationNegotiator) { - return new ClientEngine(engine, applicationNegotiator); - } - - static JettyAlpnSslEngine newServerEngine(SSLEngine engine, - JdkApplicationProtocolNegotiator applicationNegotiator) { - return new ServerEngine(engine, applicationNegotiator); - } - - private JettyAlpnSslEngine(SSLEngine engine) { - super(engine); - } - - private static final class ClientEngine extends JettyAlpnSslEngine { - ClientEngine(SSLEngine engine, final JdkApplicationProtocolNegotiator applicationNegotiator) { - super(engine); - requireNonNull(applicationNegotiator, "applicationNegotiator"); - final ProtocolSelectionListener protocolListener = requireNonNull(applicationNegotiator - .protocolListenerFactory().newListener(this, applicationNegotiator.protocols()), - "protocolListener"); - ALPN.put(engine, new ALPN.ClientProvider() { - @Override - public List protocols() { - return applicationNegotiator.protocols(); - } - - @Override - public void selected(String protocol) throws SSLException { - try { - protocolListener.selected(protocol); - } catch (Throwable t) { - throw toSSLHandshakeException(t); - } - } - - @Override - public void unsupported() { - protocolListener.unsupported(); - } - }); - } - - @Override - public void closeInbound() throws SSLException { - try { - ALPN.remove(getWrappedEngine()); - } finally { - super.closeInbound(); - } - } - - @Override - public void closeOutbound() { - try { - ALPN.remove(getWrappedEngine()); - } finally { - super.closeOutbound(); - } - } - } - - private static final class ServerEngine extends JettyAlpnSslEngine { - ServerEngine(SSLEngine engine, final JdkApplicationProtocolNegotiator applicationNegotiator) { - super(engine); - requireNonNull(applicationNegotiator, "applicationNegotiator"); - final ProtocolSelector protocolSelector = requireNonNull(applicationNegotiator.protocolSelectorFactory() - .newSelector(this, new LinkedHashSet<>(applicationNegotiator.protocols())), - "protocolSelector"); - ALPN.put(engine, new ALPN.ServerProvider() { - @Override - public String select(List protocols) throws SSLException { - try { - return protocolSelector.select(protocols); - } catch (Throwable t) { - throw toSSLHandshakeException(t); - } - } - - @Override - public void unsupported() { - protocolSelector.unsupported(); - } - }); - } - - @Override - public void closeInbound() throws SSLException { - try { - ALPN.remove(getWrappedEngine()); - } finally { - super.closeInbound(); - } - } - - @Override - public void closeOutbound() { - try { - ALPN.remove(getWrappedEngine()); - } finally { - super.closeOutbound(); - } - } - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/JettyNpnSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/JettyNpnSslEngine.java deleted file mode 100644 index 14dc31790f..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/JettyNpnSslEngine.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.ssl; - -import io.netty.handler.ssl.JdkApplicationProtocolNegotiator.ProtocolSelectionListener; -import io.netty.handler.ssl.JdkApplicationProtocolNegotiator.ProtocolSelector; -import io.netty.util.internal.PlatformDependent; -import org.eclipse.jetty.npn.NextProtoNego; -import org.eclipse.jetty.npn.NextProtoNego.ClientProvider; -import org.eclipse.jetty.npn.NextProtoNego.ServerProvider; - -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLException; -import java.util.LinkedHashSet; -import java.util.List; - -import static java.util.Objects.requireNonNull; - -final class JettyNpnSslEngine extends JdkSslEngine { - private static boolean available; - - static boolean isAvailable() { - updateAvailability(); - return available; - } - - private static void updateAvailability() { - if (available) { - return; - } - try { - // Always use bootstrap class loader. - Class.forName("sun.security.ssl.NextProtoNegoExtension", true, null); - available = true; - } catch (Exception ignore) { - // npn-boot was not loaded. - } - } - - JettyNpnSslEngine(SSLEngine engine, final JdkApplicationProtocolNegotiator applicationNegotiator, boolean server) { - super(engine); - requireNonNull(applicationNegotiator, "applicationNegotiator"); - - if (server) { - final ProtocolSelectionListener protocolListener = requireNonNull(applicationNegotiator - .protocolListenerFactory().newListener(this, applicationNegotiator.protocols()), - "protocolListener"); - NextProtoNego.put(engine, new ServerProvider() { - @Override - public void unsupported() { - protocolListener.unsupported(); - } - - @Override - public List protocols() { - return applicationNegotiator.protocols(); - } - - @Override - public void protocolSelected(String protocol) { - try { - protocolListener.selected(protocol); - } catch (Throwable t) { - PlatformDependent.throwException(t); - } - } - }); - } else { - final ProtocolSelector protocolSelector = requireNonNull(applicationNegotiator.protocolSelectorFactory() - .newSelector(this, new LinkedHashSet<>(applicationNegotiator.protocols())), - "protocolSelector"); - NextProtoNego.put(engine, new ClientProvider() { - @Override - public boolean supports() { - return true; - } - - @Override - public void unsupported() { - protocolSelector.unsupported(); - } - - @Override - public String selectProtocol(List protocols) { - try { - return protocolSelector.select(protocols); - } catch (Throwable t) { - PlatformDependent.throwException(t); - return null; - } - } - }); - } - } - - @Override - public void closeInbound() throws SSLException { - NextProtoNego.remove(getWrappedEngine()); - super.closeInbound(); - } - - @Override - public void closeOutbound() { - NextProtoNego.remove(getWrappedEngine()); - super.closeOutbound(); - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/NotSslRecordException.java b/handler/src/main/java/io/netty/handler/ssl/NotSslRecordException.java deleted file mode 100644 index 4e74a61667..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/NotSslRecordException.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import javax.net.ssl.SSLException; - -/** - * Special {@link SSLException} which will get thrown if a packet is - * received that not looks like a TLS/SSL record. A user can check for - * this {@link NotSslRecordException} and so detect if one peer tries to - * use secure and the other plain connection. - * - * - */ -public class NotSslRecordException extends SSLException { - - private static final long serialVersionUID = -4316784434770656841L; - - public NotSslRecordException() { - super(""); - } - - public NotSslRecordException(String message) { - super(message); - } - - public NotSslRecordException(Throwable cause) { - super(cause); - } - - public NotSslRecordException(String message, Throwable cause) { - super(message, cause); - } - -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java b/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java deleted file mode 100644 index 1a55926382..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java +++ /dev/null @@ -1,696 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.ssl; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty.internal.tcnative.Buffer; -import io.netty.internal.tcnative.Library; -import io.netty.internal.tcnative.SSL; -import io.netty.internal.tcnative.SSLContext; -import io.netty.util.CharsetUtil; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.ReferenceCounted; -import io.netty.util.internal.EmptyArrays; -import io.netty.util.internal.NativeLibraryLoader; -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.SystemPropertyUtil; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.io.ByteArrayInputStream; -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.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; - -import static io.netty.handler.ssl.SslUtils.*; - -/** - * Tells if {@code netty-tcnative} and its OpenSSL support - * are available. - */ -public final class OpenSsl { - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(OpenSsl.class); - private static final Throwable UNAVAILABILITY_CAUSE; - - static final List DEFAULT_CIPHERS; - static final Set AVAILABLE_CIPHER_SUITES; - private static final Set AVAILABLE_OPENSSL_CIPHER_SUITES; - private static final Set AVAILABLE_JAVA_CIPHER_SUITES; - private static final boolean SUPPORTS_KEYMANAGER_FACTORY; - private static final boolean SUPPORTS_OCSP; - private static final boolean TLSV13_SUPPORTED; - private static final boolean IS_BORINGSSL; - static final Set SUPPORTED_PROTOCOLS_SET; - static final String[] EXTRA_SUPPORTED_TLS_1_3_CIPHERS; - static final String EXTRA_SUPPORTED_TLS_1_3_CIPHERS_STRING; - static final String[] NAMED_GROUPS; - - // Use default that is supported in java 11 and earlier and also in OpenSSL / BoringSSL. - // See https://github.com/netty/netty-tcnative/issues/567 - // See https://www.java.com/en/configure_crypto.html for ordering - private static final String[] DEFAULT_NAMED_GROUPS = { "x25519", "secp256r1", "secp384r1", "secp521r1" }; - - // self-signed certificate for netty.io and the matching private-key - private static final String CERT = "-----BEGIN CERTIFICATE-----\n" + - "MIICrjCCAZagAwIBAgIIdSvQPv1QAZQwDQYJKoZIhvcNAQELBQAwFjEUMBIGA1UEAxMLZXhhbXBs\n" + - "ZS5jb20wIBcNMTgwNDA2MjIwNjU5WhgPOTk5OTEyMzEyMzU5NTlaMBYxFDASBgNVBAMTC2V4YW1w\n" + - "bGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAggbWsmDQ6zNzRZ5AW8E3eoGl\n" + - "qWvOBDb5Fs1oBRrVQHuYmVAoaqwDzXYJ0LOwa293AgWEQ1jpcbZ2hpoYQzqEZBTLnFhMrhRFlH6K\n" + - "bJND8Y33kZ/iSVBBDuGbdSbJShlM+4WwQ9IAso4MZ4vW3S1iv5fGGpLgbtXRmBf/RU8omN0Gijlv\n" + - "WlLWHWijLN8xQtySFuBQ7ssW8RcKAary3pUm6UUQB+Co6lnfti0Tzag8PgjhAJq2Z3wbsGRnP2YS\n" + - "vYoaK6qzmHXRYlp/PxrjBAZAmkLJs4YTm/XFF+fkeYx4i9zqHbyone5yerRibsHaXZWLnUL+rFoe\n" + - "MdKvr0VS3sGmhQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQADQi441pKmXf9FvUV5EHU4v8nJT9Iq\n" + - "yqwsKwXnr7AsUlDGHBD7jGrjAXnG5rGxuNKBQ35wRxJATKrUtyaquFUL6H8O6aGQehiFTk6zmPbe\n" + - "12Gu44vqqTgIUxnv3JQJiox8S2hMxsSddpeCmSdvmalvD6WG4NthH6B9ZaBEiep1+0s0RUaBYn73\n" + - "I7CCUaAtbjfR6pcJjrFk5ei7uwdQZFSJtkP2z8r7zfeANJddAKFlkaMWn7u+OIVuB4XPooWicObk\n" + - "NAHFtP65bocUYnDpTVdiyvn8DdqyZ/EO8n1bBKBzuSLplk2msW4pdgaFgY7Vw/0wzcFXfUXmL1uy\n" + - "G8sQD/wx\n" + - "-----END CERTIFICATE-----"; - - private static final String KEY = "-----BEGIN PRIVATE KEY-----\n" + - "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCCBtayYNDrM3NFnkBbwTd6gaWp\n" + - "a84ENvkWzWgFGtVAe5iZUChqrAPNdgnQs7Brb3cCBYRDWOlxtnaGmhhDOoRkFMucWEyuFEWUfops\n" + - "k0PxjfeRn+JJUEEO4Zt1JslKGUz7hbBD0gCyjgxni9bdLWK/l8YakuBu1dGYF/9FTyiY3QaKOW9a\n" + - "UtYdaKMs3zFC3JIW4FDuyxbxFwoBqvLelSbpRRAH4KjqWd+2LRPNqDw+COEAmrZnfBuwZGc/ZhK9\n" + - "ihorqrOYddFiWn8/GuMEBkCaQsmzhhOb9cUX5+R5jHiL3OodvKid7nJ6tGJuwdpdlYudQv6sWh4x\n" + - "0q+vRVLewaaFAgMBAAECggEAP8tPJvFtTxhNJAkCloHz0D0vpDHqQBMgntlkgayqmBqLwhyb18pR\n" + - "i0qwgh7HHc7wWqOOQuSqlEnrWRrdcI6TSe8R/sErzfTQNoznKWIPYcI/hskk4sdnQ//Yn9/Jvnsv\n" + - "U/BBjOTJxtD+sQbhAl80JcA3R+5sArURQkfzzHOL/YMqzAsn5hTzp7HZCxUqBk3KaHRxV7NefeOE\n" + - "xlZuWSmxYWfbFIs4kx19/1t7h8CHQWezw+G60G2VBtSBBxDnhBWvqG6R/wpzJ3nEhPLLY9T+XIHe\n" + - "ipzdMOOOUZorfIg7M+pyYPji+ZIZxIpY5OjrOzXHciAjRtr5Y7l99K1CG1LguQKBgQDrQfIMxxtZ\n" + - "vxU/1cRmUV9l7pt5bjV5R6byXq178LxPKVYNjdZ840Q0/OpZEVqaT1xKVi35ohP1QfNjxPLlHD+K\n" + - "iDAR9z6zkwjIrbwPCnb5kuXy4lpwPcmmmkva25fI7qlpHtbcuQdoBdCfr/KkKaUCMPyY89LCXgEw\n" + - "5KTDj64UywKBgQCNfbO+eZLGzhiHhtNJurresCsIGWlInv322gL8CSfBMYl6eNfUTZvUDdFhPISL\n" + - "UljKWzXDrjw0ujFSPR0XhUGtiq89H+HUTuPPYv25gVXO+HTgBFZEPl4PpA+BUsSVZy0NddneyqLk\n" + - "42Wey9omY9Q8WsdNQS5cbUvy0uG6WFoX7wKBgQDZ1jpW8pa0x2bZsQsm4vo+3G5CRnZlUp+XlWt2\n" + - "dDcp5dC0xD1zbs1dc0NcLeGDOTDv9FSl7hok42iHXXq8AygjEm/QcuwwQ1nC2HxmQP5holAiUs4D\n" + - "WHM8PWs3wFYPzE459EBoKTxeaeP/uWAn+he8q7d5uWvSZlEcANs/6e77eQKBgD21Ar0hfFfj7mK8\n" + - "9E0FeRZBsqK3omkfnhcYgZC11Xa2SgT1yvs2Va2n0RcdM5kncr3eBZav2GYOhhAdwyBM55XuE/sO\n" + - "eokDVutNeuZ6d5fqV96TRaRBpvgfTvvRwxZ9hvKF4Vz+9wfn/JvCwANaKmegF6ejs7pvmF3whq2k\n" + - "drZVAoGAX5YxQ5XMTD0QbMAl7/6qp6S58xNoVdfCkmkj1ZLKaHKIjS/benkKGlySVQVPexPfnkZx\n" + - "p/Vv9yyphBoudiTBS9Uog66ueLYZqpgxlM/6OhYg86Gm3U2ycvMxYjBM1NFiyze21AqAhI+HX+Ot\n" + - "mraV2/guSgDgZAhukRZzeQ2RucI=\n" + - "-----END PRIVATE KEY-----"; - - static { - Throwable cause = null; - - if (SystemPropertyUtil.getBoolean("io.netty.handler.ssl.noOpenSsl", false)) { - cause = new UnsupportedOperationException( - "OpenSSL was explicit disabled with -Dio.netty.handler.ssl.noOpenSsl=true"); - - logger.debug( - "netty-tcnative explicit disabled; " + - OpenSslEngine.class.getSimpleName() + " will be unavailable.", cause); - } else { - // Test if netty-tcnative is in the classpath first. - try { - Class.forName("io.netty.internal.tcnative.SSLContext", false, - PlatformDependent.getClassLoader(OpenSsl.class)); - } catch (ClassNotFoundException t) { - cause = t; - logger.debug( - "netty-tcnative not in the classpath; " + - OpenSslEngine.class.getSimpleName() + " will be unavailable."); - } - - // If in the classpath, try to load the native library and initialize netty-tcnative. - if (cause == null) { - try { - // The JNI library was not already loaded. Load it now. - loadTcNative(); - } catch (Throwable t) { - cause = t; - logger.debug( - "Failed to load netty-tcnative; " + - OpenSslEngine.class.getSimpleName() + " will be unavailable, unless the " + - "application has already loaded the symbols by some other means. " + - "See https://netty.io/wiki/forked-tomcat-native.html for more information.", t); - } - - try { - 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 - // means. - cause = null; - } catch (Throwable t) { - if (cause == null) { - cause = t; - } - logger.debug( - "Failed to initialize netty-tcnative; " + - OpenSslEngine.class.getSimpleName() + " will be unavailable. " + - "See https://netty.io/wiki/forked-tomcat-native.html for more information.", t); - } - } - } - - UNAVAILABILITY_CAUSE = cause; - - if (cause == null) { - logger.debug("netty-tcnative using native library: {}", SSL.versionString()); - - final List defaultCiphers = new ArrayList<>(); - final Set availableOpenSslCipherSuites = new LinkedHashSet<>(128); - boolean supportsKeyManagerFactory = false; - boolean useKeyManagerFactory = false; - boolean tlsv13Supported = false; - String[] namedGroups = DEFAULT_NAMED_GROUPS; - String[] defaultConvertedNamedGroups = new String[namedGroups.length]; - for (int i = 0; i < namedGroups.length; i++) { - defaultConvertedNamedGroups[i] = GroupsConverter.toOpenSsl(namedGroups[i]); - } - - IS_BORINGSSL = "BoringSSL".equals(versionString()); - if (IS_BORINGSSL) { - EXTRA_SUPPORTED_TLS_1_3_CIPHERS = new String [] { "TLS_AES_128_GCM_SHA256", - "TLS_AES_256_GCM_SHA384" , - "TLS_CHACHA20_POLY1305_SHA256" }; - - StringBuilder ciphersBuilder = new StringBuilder(128); - for (String cipher: EXTRA_SUPPORTED_TLS_1_3_CIPHERS) { - ciphersBuilder.append(cipher).append(":"); - } - ciphersBuilder.setLength(ciphersBuilder.length() - 1); - EXTRA_SUPPORTED_TLS_1_3_CIPHERS_STRING = ciphersBuilder.toString(); - } else { - EXTRA_SUPPORTED_TLS_1_3_CIPHERS = EmptyArrays.EMPTY_STRINGS; - EXTRA_SUPPORTED_TLS_1_3_CIPHERS_STRING = StringUtil.EMPTY_STRING; - } - - try { - final long sslCtx = SSLContext.make(SSL.SSL_PROTOCOL_ALL, SSL.SSL_MODE_SERVER); - long certBio = 0; - long keyBio = 0; - long cert = 0; - long key = 0; - try { - // As we delegate to the KeyManager / TrustManager of the JDK we need to ensure it can actually - // handle TLSv13 as otherwise we may see runtime exceptions - if (SslProvider.isTlsv13Supported(SslProvider.JDK)) { - try { - 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; - } - } - - 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) || - // Filter out TLSv1.3 ciphers if not supported. - !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, EXTRA_SUPPORTED_TLS_1_3_CIPHERS); - Collections.addAll(availableOpenSslCipherSuites, - "AEAD-AES128-GCM-SHA256", - "AEAD-AES256-GCM-SHA384", - "AEAD-CHACHA20-POLY1305-SHA256"); - } - - PemEncoded privateKey = PemPrivateKey.valueOf(KEY.getBytes(CharsetUtil.US_ASCII)); - try { - // Let's check if we can set a callback, which may not work if the used OpenSSL version - // is to old. - SSLContext.setCertificateCallback(sslCtx, null); - - X509Certificate certificate = selfSignedCertificate(); - certBio = ReferenceCountedOpenSslContext.toBIO(ByteBufAllocator.DEFAULT, certificate); - cert = SSL.parseX509Chain(certBio); - - keyBio = ReferenceCountedOpenSslContext.toBIO( - UnpooledByteBufAllocator.DEFAULT, privateKey.retain()); - key = SSL.parsePrivateKey(keyBio, null); - - SSL.setKeyMaterial(ssl, cert, key); - supportsKeyManagerFactory = true; - } catch (Error ignore) { - logger.debug("KeyManagerFactory not supported."); - } finally { - privateKey.release(); - } - } finally { - SSL.freeSSL(ssl); - if (certBio != 0) { - SSL.freeBIO(certBio); - } - if (keyBio != 0) { - SSL.freeBIO(keyBio); - } - if (cert != 0) { - SSL.freeX509Chain(cert); - } - if (key != 0) { - SSL.freePrivateKey(key); - } - } - - String groups = SystemPropertyUtil.get("jdk.tls.namedGroups", null); - if (groups != null) { - String[] nGroups = groups.split(","); - Set supportedNamedGroups = new LinkedHashSet(nGroups.length); - Set supportedConvertedNamedGroups = new LinkedHashSet(nGroups.length); - - Set unsupportedNamedGroups = new LinkedHashSet(); - for (String namedGroup : nGroups) { - String converted = GroupsConverter.toOpenSsl(namedGroup); - if (SSLContext.setCurvesList(sslCtx, converted)) { - supportedConvertedNamedGroups.add(converted); - supportedNamedGroups.add(namedGroup); - } else { - unsupportedNamedGroups.add(namedGroup); - } - } - - if (supportedNamedGroups.isEmpty()) { - namedGroups = defaultConvertedNamedGroups; - logger.info("All configured namedGroups are not supported: {}. Use default: {}.", - Arrays.toString(unsupportedNamedGroups.toArray(EmptyArrays.EMPTY_STRINGS)), - Arrays.toString(DEFAULT_NAMED_GROUPS)); - } else { - String[] groupArray = supportedNamedGroups.toArray(EmptyArrays.EMPTY_STRINGS); - if (unsupportedNamedGroups.isEmpty()) { - logger.info("Using configured namedGroups -D 'jdk.tls.namedGroup': {} ", - Arrays.toString(groupArray)); - } else { - logger.info("Using supported configured namedGroups: {}. Unsupported namedGroups: {}. ", - Arrays.toString(groupArray), - Arrays.toString(unsupportedNamedGroups.toArray(EmptyArrays.EMPTY_STRINGS))); - } - namedGroups = supportedConvertedNamedGroups.toArray(EmptyArrays.EMPTY_STRINGS); - } - } else { - namedGroups = defaultConvertedNamedGroups; - } - } finally { - SSLContext.free(sslCtx); - } - } catch (Exception e) { - logger.warn("Failed to get the list of available OpenSSL cipher suites.", e); - } - NAMED_GROUPS = namedGroups; - AVAILABLE_OPENSSL_CIPHER_SUITES = Collections.unmodifiableSet(availableOpenSslCipherSuites); - final Set availableJavaCipherSuites = new LinkedHashSet<>( - AVAILABLE_OPENSSL_CIPHER_SUITES.size() * 2); - for (String cipher: AVAILABLE_OPENSSL_CIPHER_SUITES) { - // Included converted but also openssl cipher name - if (!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); - addIfSupported(availableJavaCipherSuites, defaultCiphers, TLSV13_CIPHER_SUITES); - // Also handle the extra supported ciphers as these will contain some more stuff on BoringSSL. - addIfSupported(availableJavaCipherSuites, defaultCiphers, EXTRA_SUPPORTED_TLS_1_3_CIPHERS); - - useFallbackCiphersIfDefaultIsEmpty(defaultCiphers, availableJavaCipherSuites); - DEFAULT_CIPHERS = Collections.unmodifiableList(defaultCiphers); - - AVAILABLE_JAVA_CIPHER_SUITES = Collections.unmodifiableSet(availableJavaCipherSuites); - - final Set availableCipherSuites = new LinkedHashSet<>( - AVAILABLE_OPENSSL_CIPHER_SUITES.size() + AVAILABLE_JAVA_CIPHER_SUITES.size()); - availableCipherSuites.addAll(AVAILABLE_OPENSSL_CIPHER_SUITES); - availableCipherSuites.addAll(AVAILABLE_JAVA_CIPHER_SUITES); - - AVAILABLE_CIPHER_SUITES = availableCipherSuites; - SUPPORTS_KEYMANAGER_FACTORY = supportsKeyManagerFactory; - - Set protocols = new LinkedHashSet<>(6); - // Seems like there is no way to explicitly disable SSLv2Hello in openssl so it is always enabled - protocols.add(SslProtocols.SSL_v2_HELLO); - if (doesSupportProtocol(SSL.SSL_PROTOCOL_SSLV2, SSL.SSL_OP_NO_SSLv2)) { - protocols.add(SslProtocols.SSL_v2); - } - if (doesSupportProtocol(SSL.SSL_PROTOCOL_SSLV3, SSL.SSL_OP_NO_SSLv3)) { - protocols.add(SslProtocols.SSL_v3); - } - if (doesSupportProtocol(SSL.SSL_PROTOCOL_TLSV1, SSL.SSL_OP_NO_TLSv1)) { - protocols.add(SslProtocols.TLS_v1); - } - if (doesSupportProtocol(SSL.SSL_PROTOCOL_TLSV1_1, SSL.SSL_OP_NO_TLSv1_1)) { - protocols.add(SslProtocols.TLS_v1_1); - } - if (doesSupportProtocol(SSL.SSL_PROTOCOL_TLSV1_2, SSL.SSL_OP_NO_TLSv1_2)) { - protocols.add(SslProtocols.TLS_v1_2); - } - - // This is only supported by java8u272 and later. - if (tlsv13Supported && doesSupportProtocol(SSL.SSL_PROTOCOL_TLSV1_3, SSL.SSL_OP_NO_TLSv1_3)) { - protocols.add(SslProtocols.TLS_v1_3); - TLSV13_SUPPORTED = true; - } else { - TLSV13_SUPPORTED = false; - } - - SUPPORTED_PROTOCOLS_SET = Collections.unmodifiableSet(protocols); - SUPPORTS_OCSP = doesSupportOcsp(); - - if (logger.isDebugEnabled()) { - logger.debug("Supported protocols (OpenSSL): {} ", SUPPORTED_PROTOCOLS_SET); - logger.debug("Default cipher suites (OpenSSL): {}", DEFAULT_CIPHERS); - } - } else { - DEFAULT_CIPHERS = Collections.emptyList(); - AVAILABLE_OPENSSL_CIPHER_SUITES = Collections.emptySet(); - AVAILABLE_JAVA_CIPHER_SUITES = Collections.emptySet(); - AVAILABLE_CIPHER_SUITES = Collections.emptySet(); - SUPPORTS_KEYMANAGER_FACTORY = false; - SUPPORTED_PROTOCOLS_SET = Collections.emptySet(); - SUPPORTS_OCSP = false; - TLSV13_SUPPORTED = false; - IS_BORINGSSL = false; - EXTRA_SUPPORTED_TLS_1_3_CIPHERS = EmptyArrays.EMPTY_STRINGS; - EXTRA_SUPPORTED_TLS_1_3_CIPHERS_STRING = StringUtil.EMPTY_STRING; - NAMED_GROUPS = DEFAULT_NAMED_GROUPS; - } - } - - static String checkTls13Ciphers(InternalLogger logger, String ciphers) { - if (IS_BORINGSSL && !ciphers.isEmpty()) { - assert EXTRA_SUPPORTED_TLS_1_3_CIPHERS.length > 0; - Set boringsslTlsv13Ciphers = new HashSet(EXTRA_SUPPORTED_TLS_1_3_CIPHERS.length); - Collections.addAll(boringsslTlsv13Ciphers, EXTRA_SUPPORTED_TLS_1_3_CIPHERS); - boolean ciphersNotMatch = false; - for (String cipher: ciphers.split(":")) { - if (boringsslTlsv13Ciphers.isEmpty()) { - ciphersNotMatch = true; - break; - } - if (!boringsslTlsv13Ciphers.remove(cipher) && - !boringsslTlsv13Ciphers.remove(CipherSuiteConverter.toJava(cipher, "TLS"))) { - ciphersNotMatch = true; - break; - } - } - - // Also check if there are ciphers left. - ciphersNotMatch |= !boringsslTlsv13Ciphers.isEmpty(); - - if (ciphersNotMatch) { - if (logger.isInfoEnabled()) { - StringBuilder javaCiphers = new StringBuilder(128); - for (String cipher : ciphers.split(":")) { - javaCiphers.append(CipherSuiteConverter.toJava(cipher, "TLS")).append(":"); - } - javaCiphers.setLength(javaCiphers.length() - 1); - logger.info( - "BoringSSL doesn't allow to enable or disable TLSv1.3 ciphers explicitly." + - " Provided TLSv1.3 ciphers: '{}', default TLSv1.3 ciphers that will be used: '{}'.", - javaCiphers, EXTRA_SUPPORTED_TLS_1_3_CIPHERS_STRING); - } - return EXTRA_SUPPORTED_TLS_1_3_CIPHERS_STRING; - } - } - return ciphers; - } - - static boolean isSessionCacheSupported() { - return version() >= 0x10100000L; - } - - /** - * Returns a self-signed {@link X509Certificate} for {@code netty.io}. - */ - static X509Certificate selfSignedCertificate() throws CertificateException { - return (X509Certificate) SslContext.X509_CERT_FACTORY.generateCertificate( - new ByteArrayInputStream(CERT.getBytes(CharsetUtil.US_ASCII)) - ); - } - - private static boolean doesSupportOcsp() { - boolean supportsOcsp = false; - if (version() >= 0x10002000L) { - long sslCtx = -1; - try { - sslCtx = SSLContext.make(SSL.SSL_PROTOCOL_TLSV1_2, SSL.SSL_MODE_SERVER); - SSLContext.enableOcsp(sslCtx, false); - supportsOcsp = true; - } catch (Exception ignore) { - // ignore - } finally { - if (sslCtx != -1) { - SSLContext.free(sslCtx); - } - } - } - return supportsOcsp; - } - 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); - return true; - } catch (Exception ignore) { - return false; - } finally { - if (sslCtx != -1) { - SSLContext.free(sslCtx); - } - } - } - - /** - * Returns {@code true} if and only if - * {@code netty-tcnative} and its OpenSSL support - * are available. - */ - public static boolean isAvailable() { - return UNAVAILABILITY_CAUSE == null; - } - - /** - * Returns {@code true} if the used version of openssl supports - * ALPN. - * - * @deprecated use {@link SslProvider#isAlpnSupported(SslProvider)} with {@link SslProvider#OPENSSL}. - */ - @Deprecated - public static boolean isAlpnSupported() { - return version() >= 0x10002000L; - } - - /** - * Returns {@code true} if the used version of OpenSSL supports OCSP stapling. - */ - public static boolean isOcspSupported() { - return SUPPORTS_OCSP; - } - - /** - * Returns the version of the used available OpenSSL library or {@code -1} if {@link #isAvailable()} - * returns {@code false}. - */ - public static int version() { - return isAvailable() ? SSL.version() : -1; - } - - /** - * Returns the version string of the used available OpenSSL library or {@code null} if {@link #isAvailable()} - * returns {@code false}. - */ - public static String versionString() { - return isAvailable() ? SSL.versionString() : null; - } - - /** - * Ensure that {@code netty-tcnative} and - * its OpenSSL support are available. - * - * @throws UnsatisfiedLinkError if unavailable - */ - public static void ensureAvailability() { - if (UNAVAILABILITY_CAUSE != null) { - throw (Error) new UnsatisfiedLinkError( - "failed to load the required native library").initCause(UNAVAILABILITY_CAUSE); - } - } - - /** - * Returns the cause of unavailability of - * {@code netty-tcnative} and its OpenSSL support. - * - * @return the cause if unavailable. {@code null} if available. - */ - public static Throwable unavailabilityCause() { - return UNAVAILABILITY_CAUSE; - } - - /** - * @deprecated use {@link #availableOpenSslCipherSuites()} - */ - @Deprecated - public static Set availableCipherSuites() { - return availableOpenSslCipherSuites(); - } - - /** - * Returns all the available OpenSSL cipher suites. - * Please note that the returned array may include the cipher suites that are insecure or non-functional. - */ - public static Set availableOpenSslCipherSuites() { - return AVAILABLE_OPENSSL_CIPHER_SUITES; - } - - /** - * Returns all the available cipher suites (Java-style). - * Please note that the returned array may include the cipher suites that are insecure or non-functional. - */ - public static Set availableJavaCipherSuites() { - return AVAILABLE_JAVA_CIPHER_SUITES; - } - - /** - * Returns {@code true} if and only if the specified cipher suite is available in OpenSSL. - * Both Java-style cipher suite and OpenSSL-style cipher suite are accepted. - */ - public static boolean isCipherSuiteAvailable(String cipherSuite) { - String converted = CipherSuiteConverter.toOpenSsl(cipherSuite, IS_BORINGSSL); - if (converted != null) { - cipherSuite = converted; - } - return AVAILABLE_OPENSSL_CIPHER_SUITES.contains(cipherSuite); - } - - /** - * Returns {@code true} if {@link javax.net.ssl.KeyManagerFactory} is supported when using OpenSSL. - */ - public static boolean supportsKeyManagerFactory() { - return SUPPORTS_KEYMANAGER_FACTORY; - } - - /** - * Always returns {@code true} if {@link #isAvailable()} returns {@code true}. - * - * @deprecated Will be removed because hostname validation is always done by a - * {@link javax.net.ssl.TrustManager} implementation. - */ - @Deprecated - public static boolean supportsHostnameValidation() { - return isAvailable(); - } - - static long memoryAddress(ByteBuf buf) { - assert buf.isDirect(); - return buf.hasMemoryAddress() ? buf.memoryAddress() : - // Use internalNioBuffer to reduce object creation. - Buffer.address(buf.internalNioBuffer(0, buf.readableBytes())); - } - - private OpenSsl() { } - - private static void loadTcNative() throws Exception { - String os = PlatformDependent.normalizedOs(); - String arch = PlatformDependent.normalizedArch(); - - Set libNames = new LinkedHashSet<>(5); - String staticLibName = "netty_tcnative"; - - // First, try loading the platform-specific library. Platform-specific - // libraries will be available if using a tcnative uber jar. - if ("linux".equals(os)) { - Set classifiers = PlatformDependent.normalizedLinuxClassifiers(); - for (String classifier : classifiers) { - libNames.add(staticLibName + "_" + os + '_' + arch + "_" + classifier); - } - // generic arch-dependent library - libNames.add(staticLibName + "_" + os + '_' + arch); - - // Fedora SSL lib so naming (libssl.so.10 vs libssl.so.1.0.0). - // note: should already be included from the classifiers but if not, we use this as an - // additional fallback option here - libNames.add(staticLibName + "_" + os + '_' + arch + "_fedora"); - } else { - libNames.add(staticLibName + "_" + os + '_' + arch); - } - libNames.add(staticLibName + "_" + arch); - libNames.add(staticLibName); - - NativeLibraryLoader.loadFirstAvailable(PlatformDependent.getClassLoader(SSLContext.class), - libNames.toArray(new String[0])); - } - - private static boolean initializeTcNative(String engine) throws Exception { - return Library.initialize("provided", engine); - } - - static void releaseIfNeeded(ReferenceCounted counted) { - if (counted.refCnt() > 0) { - ReferenceCountUtil.safeRelease(counted); - } - } - - static boolean isTlsv13Supported() { - return TLSV13_SUPPORTED; - } - - static boolean isBoringSSL() { - return IS_BORINGSSL; - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslApplicationProtocolNegotiator.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslApplicationProtocolNegotiator.java deleted file mode 100644 index 98349ff82a..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslApplicationProtocolNegotiator.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -/** - * OpenSSL version of {@link ApplicationProtocolNegotiator}. - * - * @deprecated use {@link ApplicationProtocolConfig} - */ -@Deprecated -public interface OpenSslApplicationProtocolNegotiator extends ApplicationProtocolNegotiator { - - /** - * Returns the {@link ApplicationProtocolConfig.Protocol} which should be used. - */ - ApplicationProtocolConfig.Protocol protocol(); - - /** - * Get the desired behavior for the peer who selects the application protocol. - */ - ApplicationProtocolConfig.SelectorFailureBehavior selectorFailureBehavior(); - - /** - * Get the desired behavior for the peer who is notified of the selected protocol. - */ - ApplicationProtocolConfig.SelectedListenerFailureBehavior selectedListenerFailureBehavior(); -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslAsyncPrivateKeyMethod.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslAsyncPrivateKeyMethod.java deleted file mode 100644 index 27edaa629f..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslAsyncPrivateKeyMethod.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.internal.tcnative.SSLPrivateKeyMethod; -import io.netty.util.concurrent.Future; - -import javax.net.ssl.SSLEngine; - -public interface OpenSslAsyncPrivateKeyMethod { - int SSL_SIGN_RSA_PKCS1_SHA1 = SSLPrivateKeyMethod.SSL_SIGN_RSA_PKCS1_SHA1; - int SSL_SIGN_RSA_PKCS1_SHA256 = SSLPrivateKeyMethod.SSL_SIGN_RSA_PKCS1_SHA256; - int SSL_SIGN_RSA_PKCS1_SHA384 = SSLPrivateKeyMethod.SSL_SIGN_RSA_PKCS1_SHA384; - int SSL_SIGN_RSA_PKCS1_SHA512 = SSLPrivateKeyMethod.SSL_SIGN_RSA_PKCS1_SHA512; - int SSL_SIGN_ECDSA_SHA1 = SSLPrivateKeyMethod.SSL_SIGN_ECDSA_SHA1; - int SSL_SIGN_ECDSA_SECP256R1_SHA256 = SSLPrivateKeyMethod.SSL_SIGN_ECDSA_SECP256R1_SHA256; - int SSL_SIGN_ECDSA_SECP384R1_SHA384 = SSLPrivateKeyMethod.SSL_SIGN_ECDSA_SECP384R1_SHA384; - int SSL_SIGN_ECDSA_SECP521R1_SHA512 = SSLPrivateKeyMethod.SSL_SIGN_ECDSA_SECP521R1_SHA512; - int SSL_SIGN_RSA_PSS_RSAE_SHA256 = SSLPrivateKeyMethod.SSL_SIGN_RSA_PSS_RSAE_SHA256; - int SSL_SIGN_RSA_PSS_RSAE_SHA384 = SSLPrivateKeyMethod.SSL_SIGN_RSA_PSS_RSAE_SHA384; - int SSL_SIGN_RSA_PSS_RSAE_SHA512 = SSLPrivateKeyMethod.SSL_SIGN_RSA_PSS_RSAE_SHA512; - int SSL_SIGN_ED25519 = SSLPrivateKeyMethod.SSL_SIGN_ED25519; - int SSL_SIGN_RSA_PKCS1_MD5_SHA1 = SSLPrivateKeyMethod.SSL_SIGN_RSA_PKCS1_MD5_SHA1; - - /** - * Signs the input with the given key and notifies the returned {@link Future} with the signed bytes. - * - * @param engine the {@link SSLEngine} - * @param signatureAlgorithm the algorithm to use for signing - * @param input the digest itself - * @return the {@link Future} that will be notified with the signed data - * (must not be {@code null}) when the operation completes. - */ - Future sign(SSLEngine engine, int signatureAlgorithm, byte[] input); - - /** - * Decrypts the input with the given key and notifies the returned {@link Future} with the decrypted bytes. - * - * @param engine the {@link SSLEngine} - * @param input the input which should be decrypted - * @return the {@link Future} that will be notified with the decrypted data - * (must not be {@code null}) when the operation completes. - */ - Future decrypt(SSLEngine engine, byte[] input); -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslCachingKeyMaterialProvider.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslCachingKeyMaterialProvider.java deleted file mode 100644 index f6fe8857f5..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslCachingKeyMaterialProvider.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.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 int maxCachedEntries; - private volatile boolean full; - private final ConcurrentMap cache = new ConcurrentHashMap<>(); - - OpenSslCachingKeyMaterialProvider(X509KeyManager keyManager, String password, int maxCachedEntries) { - super(keyManager, password); - this.maxCachedEntries = maxCachedEntries; - } - - @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; - } - - if (full) { - return material; - } - if (cache.size() > maxCachedEntries) { - full = true; - // Do not cache... - return material; - } - 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 deleted file mode 100644 index 7f644e2151..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslCachingX509KeyManagerFactory.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -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.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 { - - private final int maxCachedEntries; - - public OpenSslCachingX509KeyManagerFactory(final KeyManagerFactory factory) { - this(factory, 1024); - } - - public OpenSslCachingX509KeyManagerFactory(final KeyManagerFactory factory, int maxCachedEntries) { - 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()); - this.maxCachedEntries = ObjectUtil.checkPositive(maxCachedEntries, "maxCachedEntries"); - } - - OpenSslKeyMaterialProvider newProvider(String password) { - X509KeyManager keyManager = ReferenceCountedOpenSslContext.chooseX509KeyManager(getKeyManagers()); - if ("sun.security.ssl.X509KeyManagerImpl".equals(keyManager.getClass().getName())) { - // Don't do caching if X509KeyManagerImpl is used as the returned aliases are not stable and will change - // between invocations. - return new OpenSslKeyMaterialProvider(keyManager, password); - } - return new OpenSslCachingKeyMaterialProvider( - ReferenceCountedOpenSslContext.chooseX509KeyManager(getKeyManagers()), password, maxCachedEntries); - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslCertificateException.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslCertificateException.java deleted file mode 100644 index 39fddf2664..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslCertificateException.java +++ /dev/null @@ -1,81 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.internal.tcnative.CertificateVerifier; - -import java.security.cert.CertificateException; - -/** - * A special {@link CertificateException} which allows to specify which error code is included in the - * SSL Record. This only work when {@link SslProvider#OPENSSL} or {@link SslProvider#OPENSSL_REFCNT} is used. - */ -public final class OpenSslCertificateException extends CertificateException { - private static final long serialVersionUID = 5542675253797129798L; - - private final int errorCode; - - /** - * Construct a new exception with the - * error code. - */ - public OpenSslCertificateException(int errorCode) { - this((String) null, errorCode); - } - - /** - * Construct a new exception with the msg and - * error code . - */ - public OpenSslCertificateException(String msg, int errorCode) { - super(msg); - this.errorCode = checkErrorCode(errorCode); - } - - /** - * Construct a new exception with the msg, cause and - * error code . - */ - public OpenSslCertificateException(String message, Throwable cause, int errorCode) { - super(message, cause); - this.errorCode = checkErrorCode(errorCode); - } - - /** - * Construct a new exception with the cause and - * error code . - */ - public OpenSslCertificateException(Throwable cause, int errorCode) { - this(null, cause, errorCode); - } - - /** - * Return the error code to use. - */ - public int errorCode() { - return errorCode; - } - - private static int checkErrorCode(int 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."); - } - return errorCode; - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslClientContext.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslClientContext.java deleted file mode 100644 index dbd1b98b62..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslClientContext.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.internal.tcnative.SSL; - -import java.security.PrivateKey; -import java.security.cert.X509Certificate; -import java.util.Map; - -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLException; -import javax.net.ssl.TrustManagerFactory; - -import static io.netty.handler.ssl.ReferenceCountedOpenSslClientContext.newSessionContext; - -/** - * A client-side {@link SslContext} which uses OpenSSL's SSL/TLS implementation. - *

This class will use a finalizer to ensure native resources are automatically cleaned up. To avoid finalizers - * and manually release the native memory see {@link ReferenceCountedOpenSslClientContext}. - */ -final class OpenSslClientContext extends OpenSslContext { - private final OpenSslSessionContext sessionContext; - - OpenSslClientContext(X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory, - X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, - KeyManagerFactory keyManagerFactory, Iterable ciphers, - CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, String[] protocols, - long sessionCacheSize, long sessionTimeout, boolean enableOcsp, String keyStore, - Map.Entry, Object>... options) - throws SSLException { - super(ciphers, cipherFilter, apn, SSL.SSL_MODE_CLIENT, keyCertChain, - ClientAuth.NONE, protocols, false, enableOcsp, options); - boolean success = false; - try { - OpenSslKeyMaterialProvider.validateKeyMaterialSupported(keyCertChain, key, keyPassword); - sessionContext = newSessionContext(this, ctx, engineMap, trustCertCollection, trustManagerFactory, - keyCertChain, key, keyPassword, keyManagerFactory, keyStore, - sessionCacheSize, sessionTimeout); - success = true; - } finally { - if (!success) { - release(); - } - } - } - - @Override - public OpenSslSessionContext sessionContext() { - return sessionContext; - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslClientSessionCache.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslClientSessionCache.java deleted file mode 100644 index f0c6daf4f6..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslClientSessionCache.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.internal.tcnative.SSL; -import io.netty.util.AsciiString; - -import java.util.HashMap; -import java.util.Map; - -/** - * {@link OpenSslSessionCache} that is used by the client-side. - */ -final class OpenSslClientSessionCache extends OpenSslSessionCache { - // TODO: Should we support to have a List of OpenSslSessions for a Host/Port key and so be able to - // support sessions for different protocols / ciphers to the same remote peer ? - private final Map sessions = new HashMap(); - - OpenSslClientSessionCache(OpenSslEngineMap engineMap) { - super(engineMap); - } - - @Override - protected boolean sessionCreated(NativeSslSession session) { - assert Thread.holdsLock(this); - HostPort hostPort = keyFor(session.getPeerHost(), session.getPeerPort()); - if (hostPort == null || sessions.containsKey(hostPort)) { - return false; - } - sessions.put(hostPort, session); - return true; - } - - @Override - protected void sessionRemoved(NativeSslSession session) { - assert Thread.holdsLock(this); - HostPort hostPort = keyFor(session.getPeerHost(), session.getPeerPort()); - if (hostPort == null) { - return; - } - sessions.remove(hostPort); - } - - @Override - void setSession(long ssl, String host, int port) { - HostPort hostPort = keyFor(host, port); - if (hostPort == null) { - return; - } - final NativeSslSession session; - final boolean reused; - synchronized (this) { - session = sessions.get(hostPort); - if (session == null) { - return; - } - if (!session.isValid()) { - removeSessionWithId(session.sessionId()); - return; - } - // Try to set the session, if true is returned OpenSSL incremented the reference count - // of the underlying SSL_SESSION*. - reused = SSL.setSession(ssl, session.session()); - } - - if (reused) { - if (session.shouldBeSingleUse()) { - // Should only be used once - session.invalidate(); - } - session.updateLastAccessedTime(); - } - } - - private static HostPort keyFor(String host, int port) { - if (host == null && port < 1) { - return null; - } - return new HostPort(host, port); - } - - @Override - synchronized void clear() { - super.clear(); - sessions.clear(); - } - - /** - * Host / Port tuple used to find a {@link OpenSslSession} in the cache. - */ - private static final class HostPort { - private final int hash; - private final String host; - private final int port; - - HostPort(String host, int port) { - this.host = host; - this.port = port; - // Calculate a hashCode that does ignore case. - this.hash = 31 * AsciiString.hashCode(host) + port; - } - - @Override - public int hashCode() { - return hash; - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof HostPort)) { - return false; - } - HostPort other = (HostPort) obj; - return port == other.port && host.equalsIgnoreCase(other.host); - } - - @Override - public String toString() { - return "HostPort{" + - "host='" + host + '\'' + - ", port=" + port + - '}'; - } - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslContext.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslContext.java deleted file mode 100644 index e50108cf35..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslContext.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.buffer.ByteBufAllocator; - -import java.security.cert.Certificate; -import java.util.Map; - -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLException; - -/** - * This class will use a finalizer to ensure native resources are automatically cleaned up. To avoid finalizers - * and manually release the native memory see {@link ReferenceCountedOpenSslContext}. - */ -public abstract class OpenSslContext extends ReferenceCountedOpenSslContext { - OpenSslContext(Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apnCfg, - int mode, Certificate[] keyCertChain, - ClientAuth clientAuth, String[] protocols, boolean startTls, boolean enableOcsp, - Map.Entry, Object>... options) - throws SSLException { - super(ciphers, cipherFilter, toNegotiator(apnCfg), mode, keyCertChain, - clientAuth, protocols, startTls, enableOcsp, false, options); - } - - OpenSslContext(Iterable ciphers, CipherSuiteFilter cipherFilter, OpenSslApplicationProtocolNegotiator apn, - int mode, Certificate[] keyCertChain, - ClientAuth clientAuth, String[] protocols, boolean startTls, boolean enableOcsp, - Map.Entry, Object>... options) - throws SSLException { - super(ciphers, cipherFilter, apn, mode, keyCertChain, - clientAuth, protocols, startTls, enableOcsp, false, options); - } - - @Override - final SSLEngine newEngine0(ByteBufAllocator alloc, String peerHost, int peerPort, boolean jdkCompatibilityMode) { - return new OpenSslEngine(this, alloc, peerHost, peerPort, jdkCompatibilityMode); - } - - @Override - @SuppressWarnings("FinalizeDeclaration") - protected final void finalize() throws Throwable { - super.finalize(); - OpenSsl.releaseIfNeeded(this); - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslContextOption.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslContextOption.java deleted file mode 100644 index cf7d11c09c..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslContextOption.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -/** - * {@link SslContextOption}s that are specific to the {@link SslProvider#OPENSSL} / {@link SslProvider#OPENSSL_REFCNT}. - * - * @param the type of the value. - */ -public final class OpenSslContextOption extends SslContextOption { - - private OpenSslContextOption(String name) { - super(name); - } - - /** - * If enabled heavy-operations may be offloaded from the {@link io.netty.channel.EventLoop} if possible. - */ - public static final OpenSslContextOption USE_TASKS = - new OpenSslContextOption("USE_TASKS"); - /** - * If enabled TLS false start will be enabled if supported. - * When TLS false start is enabled the flow of {@link SslHandshakeCompletionEvent}s may be different compared when, - * not enabled. - * - * This is currently only supported when {@code BoringSSL} and ALPN is used. - */ - public static final OpenSslContextOption TLS_FALSE_START = - new OpenSslContextOption("TLS_FALSE_START"); - - /** - * Set the {@link OpenSslPrivateKeyMethod} to use. This allows to offload private-key operations - * if needed. - * - * This is currently only supported when {@code BoringSSL} is used. - */ - public static final OpenSslContextOption PRIVATE_KEY_METHOD = - new OpenSslContextOption("PRIVATE_KEY_METHOD"); - - /** - * Set the {@link OpenSslAsyncPrivateKeyMethod} to use. This allows to offload private-key operations - * if needed. - * - * This is currently only supported when {@code BoringSSL} is used. - */ - public static final OpenSslContextOption ASYNC_PRIVATE_KEY_METHOD = - new OpenSslContextOption("ASYNC_PRIVATE_KEY_METHOD"); -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslDefaultApplicationProtocolNegotiator.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslDefaultApplicationProtocolNegotiator.java deleted file mode 100644 index e1aba6fd6a..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslDefaultApplicationProtocolNegotiator.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import java.util.List; - -import static java.util.Objects.requireNonNull; - -/** - * OpenSSL {@link ApplicationProtocolNegotiator} for ALPN and NPN. - * - * @deprecated use {@link ApplicationProtocolConfig}. - */ -@Deprecated -public final class OpenSslDefaultApplicationProtocolNegotiator implements OpenSslApplicationProtocolNegotiator { - private final ApplicationProtocolConfig config; - public OpenSslDefaultApplicationProtocolNegotiator(ApplicationProtocolConfig config) { - this.config = requireNonNull(config, "config"); - } - - @Override - public List protocols() { - return config.supportedProtocols(); - } - - @Override - public ApplicationProtocolConfig.Protocol protocol() { - return config.protocol(); - } - - @Override - public ApplicationProtocolConfig.SelectorFailureBehavior selectorFailureBehavior() { - return config.selectorFailureBehavior(); - } - - @Override - public ApplicationProtocolConfig.SelectedListenerFailureBehavior selectedListenerFailureBehavior() { - return config.selectedListenerFailureBehavior(); - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslEngine.java deleted file mode 100644 index a5464adb87..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslEngine.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.buffer.ByteBufAllocator; - -import javax.net.ssl.SSLEngine; - -/** - * Implements a {@link SSLEngine} using - * OpenSSL BIO abstractions. - *

- * This class will use a finalizer to ensure native resources are automatically cleaned up. To avoid finalizers - * and manually release the native memory see {@link ReferenceCountedOpenSslEngine}. - */ -public final class OpenSslEngine extends ReferenceCountedOpenSslEngine { - OpenSslEngine(OpenSslContext context, ByteBufAllocator alloc, String peerHost, int peerPort, - boolean jdkCompatibilityMode) { - super(context, alloc, peerHost, peerPort, jdkCompatibilityMode, false); - } - - @Override - @SuppressWarnings("FinalizeDeclaration") - protected void finalize() throws Throwable { - super.finalize(); - OpenSsl.releaseIfNeeded(this); - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslEngineMap.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslEngineMap.java deleted file mode 100644 index 68e2df5713..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslEngineMap.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -interface OpenSslEngineMap { - - /** - * Remove the {@link OpenSslEngine} with the given {@code ssl} address and - * return it. - */ - ReferenceCountedOpenSslEngine remove(long ssl); - - /** - * Add a {@link OpenSslEngine} to this {@link OpenSslEngineMap}. - */ - void add(ReferenceCountedOpenSslEngine engine); - - /** - * Get the {@link OpenSslEngine} for the given {@code ssl} address. - */ - ReferenceCountedOpenSslEngine get(long ssl); -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterial.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterial.java deleted file mode 100644 index 88131e4cbc..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterial.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -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. - */ - long certificateChainAddress(); - - /** - * Returns the pointer to the {@code EVP_PKEY}. - */ - long privateKeyAddress(); - - @Override - OpenSslKeyMaterial retain(); - - @Override - OpenSslKeyMaterial retain(int increment); - - @Override - OpenSslKeyMaterial touch(); - - @Override - OpenSslKeyMaterial touch(Object hint); - - @Override - boolean release(); - - @Override - boolean release(int 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 deleted file mode 100644 index 0b585ee993..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialManager.java +++ /dev/null @@ -1,138 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import javax.net.ssl.SSLException; -import javax.net.ssl.SSLHandshakeException; -import javax.net.ssl.X509ExtendedKeyManager; -import javax.net.ssl.X509KeyManager; -import javax.security.auth.x500.X500Principal; -import java.security.PrivateKey; -import java.security.cert.X509Certificate; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - - -/** - * Manages key material for {@link OpenSslEngine}s and so set the right {@link PrivateKey}s and - * {@link X509Certificate}s. - */ -final class OpenSslKeyMaterialManager { - - // Code in this class is inspired by code of conscrypts: - // - https://android.googlesource.com/platform/external/ - // conscrypt/+/master/src/main/java/org/conscrypt/OpenSSLEngineImpl.java - // - https://android.googlesource.com/platform/external/ - // conscrypt/+/master/src/main/java/org/conscrypt/SSLParametersImpl.java - // - static final String KEY_TYPE_RSA = "RSA"; - static final String KEY_TYPE_DH_RSA = "DH_RSA"; - static final String KEY_TYPE_EC = "EC"; - static final String KEY_TYPE_EC_EC = "EC_EC"; - static final String KEY_TYPE_EC_RSA = "EC_RSA"; - - // key type mappings for types. - private static final Map KEY_TYPES = new HashMap<>(); - static { - KEY_TYPES.put("RSA", KEY_TYPE_RSA); - KEY_TYPES.put("DHE_RSA", KEY_TYPE_RSA); - KEY_TYPES.put("ECDHE_RSA", KEY_TYPE_RSA); - KEY_TYPES.put("ECDHE_ECDSA", KEY_TYPE_EC); - KEY_TYPES.put("ECDH_RSA", KEY_TYPE_EC_RSA); - KEY_TYPES.put("ECDH_ECDSA", KEY_TYPE_EC_EC); - KEY_TYPES.put("DH_RSA", KEY_TYPE_DH_RSA); - } - - private final OpenSslKeyMaterialProvider provider; - - OpenSslKeyMaterialManager(OpenSslKeyMaterialProvider provider) { - this.provider = provider; - } - - void setKeyMaterialServerSide(ReferenceCountedOpenSslEngine engine) throws SSLException { - String[] authMethods = engine.authMethods(); - if (authMethods.length == 0) { - throw new SSLHandshakeException("Unable to find key material"); - } - - // authMethods may contain duplicates or may result in the same type - // but call chooseServerAlias(...) may be expensive. So let's ensure - // we filter out duplicates. - Set typeSet = new HashSet<>(KEY_TYPES.size()); - for (String authMethod : authMethods) { - String type = KEY_TYPES.get(authMethod); - if (type != null && typeSet.add(type)) { - String alias = chooseServerAlias(engine, type); - if (alias != null) { - // We found a match... let's set the key material and return. - setKeyMaterial(engine, alias); - return; - } - } - } - throw new SSLHandshakeException("Unable to find key material for auth method(s): " - + Arrays.toString(authMethods)); - } - - void setKeyMaterialClientSide(ReferenceCountedOpenSslEngine engine, String[] keyTypes, - X500Principal[] issuer) throws SSLException { - String alias = chooseClientAlias(engine, keyTypes, issuer); - // Only try to set the keymaterial if we have a match. This is also consistent with what OpenJDK does: - // https://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 { - OpenSslKeyMaterial keyMaterial = null; - try { - keyMaterial = provider.chooseKeyMaterial(engine.alloc, alias); - if (keyMaterial == null) { - return; - } - engine.setKeyMaterial(keyMaterial); - } catch (SSLException e) { - throw e; - } catch (Exception e) { - throw new SSLException(e); - } finally { - if (keyMaterial != null) { - keyMaterial.release(); - } - } - } - private String chooseClientAlias(ReferenceCountedOpenSslEngine engine, - String[] keyTypes, X500Principal[] issuer) { - X509KeyManager manager = provider.keyManager(); - if (manager instanceof X509ExtendedKeyManager) { - return ((X509ExtendedKeyManager) manager).chooseEngineClientAlias(keyTypes, issuer, engine); - } - return manager.chooseClientAlias(keyTypes, issuer, 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 deleted file mode 100644 index adf545fb61..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslKeyMaterialProvider.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty.internal.tcnative.SSL; - -import javax.net.ssl.SSLException; -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; - } - - static void validateKeyMaterialSupported(X509Certificate[] keyCertChain, PrivateKey key, String keyPassword) - throws SSLException { - validateSupported(keyCertChain); - validateSupported(key, keyPassword); - } - - private static void validateSupported(PrivateKey key, String password) throws SSLException { - if (key == null) { - return; - } - - long pkeyBio = 0; - long pkey = 0; - - try { - pkeyBio = toBIO(UnpooledByteBufAllocator.DEFAULT, key); - pkey = SSL.parsePrivateKey(pkeyBio, password); - } catch (Exception e) { - throw new SSLException("PrivateKey type not supported " + key.getFormat(), e); - } finally { - SSL.freeBIO(pkeyBio); - if (pkey != 0) { - SSL.freePrivateKey(pkey); - } - } - } - - private static void validateSupported(X509Certificate[] certificates) throws SSLException { - if (certificates == null || certificates.length == 0) { - return; - } - - long chainBio = 0; - long chain = 0; - PemEncoded encoded = null; - try { - encoded = PemX509Certificate.toPEM(UnpooledByteBufAllocator.DEFAULT, true, certificates); - chainBio = toBIO(UnpooledByteBufAllocator.DEFAULT, encoded.retain()); - chain = SSL.parseX509Chain(chainBio); - } catch (Exception e) { - throw new SSLException("Certificate type not supported", e); - } finally { - SSL.freeBIO(chainBio); - if (chain != 0) { - SSL.freeX509Chain(chain); - } - if (encoded != null) { - encoded.release(); - } - } - } - - /** - * 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()); - chain = SSL.parseX509Chain(chainBio); - - OpenSslKeyMaterial keyMaterial; - if (key instanceof OpenSslPrivateKey) { - keyMaterial = ((OpenSslPrivateKey) key).newKeyMaterial(chain, certificates); - } else { - pkeyBio = toBIO(allocator, key); - pkey = key == null ? 0 : SSL.parsePrivateKey(pkeyBio, password); - keyMaterial = new DefaultOpenSslKeyMaterial(chain, pkey, certificates); - } - - // 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/OpenSslNpnApplicationProtocolNegotiator.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslNpnApplicationProtocolNegotiator.java deleted file mode 100644 index a7884aebb7..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslNpnApplicationProtocolNegotiator.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import static io.netty.handler.ssl.ApplicationProtocolUtil.toList; -import static java.util.Objects.requireNonNull; - -import java.util.List; - -/** - * OpenSSL {@link ApplicationProtocolNegotiator} for NPN. - * - * @deprecated use {@link ApplicationProtocolConfig} - */ -@Deprecated -public final class OpenSslNpnApplicationProtocolNegotiator implements OpenSslApplicationProtocolNegotiator { - private final List protocols; - - public OpenSslNpnApplicationProtocolNegotiator(Iterable protocols) { - this.protocols = requireNonNull(toList(protocols), "protocols"); - } - - public OpenSslNpnApplicationProtocolNegotiator(String... protocols) { - this.protocols = requireNonNull(toList(protocols), "protocols"); - } - - @Override - public ApplicationProtocolConfig.Protocol protocol() { - return ApplicationProtocolConfig.Protocol.NPN; - } - - @Override - public List protocols() { - return protocols; - } - - @Override - public ApplicationProtocolConfig.SelectorFailureBehavior selectorFailureBehavior() { - return ApplicationProtocolConfig.SelectorFailureBehavior.CHOOSE_MY_LAST_PROTOCOL; - } - - @Override - public ApplicationProtocolConfig.SelectedListenerFailureBehavior selectedListenerFailureBehavior() { - return ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT; - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslPrivateKey.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslPrivateKey.java deleted file mode 100644 index fb6caed2fe..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslPrivateKey.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -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 { - - private long privateKeyAddress; - - OpenSslPrivateKey(long privateKeyAddress) { - this.privateKeyAddress = privateKeyAddress; - } - - @Override - public String getAlgorithm() { - return "unknown"; - } - - @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; - } - - private 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() - */ - @Override - 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() - */ - @Override - public boolean isDestroyed() { - return refCnt() == 0; - } - - /** - * Create a new {@link OpenSslKeyMaterial} which uses the private key that is held by {@link OpenSslPrivateKey}. - * - * When the material is created we increment the reference count of the enclosing {@link OpenSslPrivateKey} and - * decrement it again when the reference count of the {@link OpenSslKeyMaterial} reaches {@code 0}. - */ - OpenSslKeyMaterial newKeyMaterial(long certificateChain, X509Certificate[] chain) { - return new OpenSslPrivateKeyMaterial(certificateChain, chain); - } - - // Package-private for unit-test only - final class OpenSslPrivateKeyMaterial extends AbstractReferenceCounted implements OpenSslKeyMaterial { - - // Package-private for unit-test only - long certificateChain; - private final X509Certificate[] x509CertificateChain; - - OpenSslPrivateKeyMaterial(long certificateChain, X509Certificate[] x509CertificateChain) { - this.certificateChain = certificateChain; - this.x509CertificateChain = x509CertificateChain == null ? - EmptyArrays.EMPTY_X509_CERTIFICATES : x509CertificateChain; - OpenSslPrivateKey.this.retain(); - } - - @Override - public X509Certificate[] certificateChain() { - return x509CertificateChain.clone(); - } - - @Override - public long certificateChainAddress() { - if (refCnt() <= 0) { - throw new IllegalReferenceCountException(); - } - return certificateChain; - } - - @Override - public long privateKeyAddress() { - if (refCnt() <= 0) { - throw new IllegalReferenceCountException(); - } - return OpenSslPrivateKey.this.privateKeyAddress(); - } - - @Override - public OpenSslKeyMaterial touch(Object hint) { - OpenSslPrivateKey.this.touch(hint); - return this; - } - - @Override - public OpenSslKeyMaterial retain() { - super.retain(); - return this; - } - - @Override - public OpenSslKeyMaterial retain(int increment) { - super.retain(increment); - return this; - } - - @Override - public OpenSslKeyMaterial touch() { - OpenSslPrivateKey.this.touch(); - return this; - } - - @Override - protected void deallocate() { - releaseChain(); - OpenSslPrivateKey.this.release(); - } - - private void releaseChain() { - SSL.freeX509Chain(certificateChain); - certificateChain = 0; - } - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslPrivateKeyMethod.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslPrivateKeyMethod.java deleted file mode 100644 index 84c8229624..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslPrivateKeyMethod.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.internal.tcnative.SSLPrivateKeyMethod; -import io.netty.util.internal.UnstableApi; - -import javax.net.ssl.SSLEngine; - -/** - * Allow to customize private key signing / decrypting (when using RSA). Only supported when using BoringSSL atm. - */ -@UnstableApi -public interface OpenSslPrivateKeyMethod { - int SSL_SIGN_RSA_PKCS1_SHA1 = SSLPrivateKeyMethod.SSL_SIGN_RSA_PKCS1_SHA1; - int SSL_SIGN_RSA_PKCS1_SHA256 = SSLPrivateKeyMethod.SSL_SIGN_RSA_PKCS1_SHA256; - int SSL_SIGN_RSA_PKCS1_SHA384 = SSLPrivateKeyMethod.SSL_SIGN_RSA_PKCS1_SHA384; - int SSL_SIGN_RSA_PKCS1_SHA512 = SSLPrivateKeyMethod.SSL_SIGN_RSA_PKCS1_SHA512; - int SSL_SIGN_ECDSA_SHA1 = SSLPrivateKeyMethod.SSL_SIGN_ECDSA_SHA1; - int SSL_SIGN_ECDSA_SECP256R1_SHA256 = SSLPrivateKeyMethod.SSL_SIGN_ECDSA_SECP256R1_SHA256; - int SSL_SIGN_ECDSA_SECP384R1_SHA384 = SSLPrivateKeyMethod.SSL_SIGN_ECDSA_SECP384R1_SHA384; - int SSL_SIGN_ECDSA_SECP521R1_SHA512 = SSLPrivateKeyMethod.SSL_SIGN_ECDSA_SECP521R1_SHA512; - int SSL_SIGN_RSA_PSS_RSAE_SHA256 = SSLPrivateKeyMethod.SSL_SIGN_RSA_PSS_RSAE_SHA256; - int SSL_SIGN_RSA_PSS_RSAE_SHA384 = SSLPrivateKeyMethod.SSL_SIGN_RSA_PSS_RSAE_SHA384; - int SSL_SIGN_RSA_PSS_RSAE_SHA512 = SSLPrivateKeyMethod.SSL_SIGN_RSA_PSS_RSAE_SHA512; - int SSL_SIGN_ED25519 = SSLPrivateKeyMethod.SSL_SIGN_ED25519; - int SSL_SIGN_RSA_PKCS1_MD5_SHA1 = SSLPrivateKeyMethod.SSL_SIGN_RSA_PKCS1_MD5_SHA1; - - /** - * Signs the input with the given key and returns the signed bytes. - * - * @param engine the {@link SSLEngine} - * @param signatureAlgorithm the algorithm to use for signing - * @param input the digest itself - * @return the signed data (must not be {@code null}) - * @throws Exception thrown if an error is encountered during the signing - */ - byte[] sign(SSLEngine engine, int signatureAlgorithm, byte[] input) throws Exception; - - /** - * Decrypts the input with the given key and returns the decrypted bytes. - * - * @param engine the {@link SSLEngine} - * @param input the input which should be decrypted - * @return the decrypted data (must not be {@code null}) - * @throws Exception thrown if an error is encountered during the decrypting - */ - byte[] decrypt(SSLEngine engine, byte[] input) throws Exception; -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslServerContext.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslServerContext.java deleted file mode 100644 index a5d9d1155f..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslServerContext.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.internal.tcnative.SSL; - -import java.security.PrivateKey; -import java.security.cert.X509Certificate; -import java.util.Map; - -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLException; -import javax.net.ssl.TrustManagerFactory; - -import static io.netty.handler.ssl.ReferenceCountedOpenSslServerContext.newSessionContext; - -/** - * A server-side {@link SslContext} which uses OpenSSL's SSL/TLS implementation. - *

This class will use a finalizer to ensure native resources are automatically cleaned up. To avoid finalizers - * and manually release the native memory see {@link ReferenceCountedOpenSslServerContext}. - */ -final class OpenSslServerContext extends OpenSslContext { - private final OpenSslServerSessionContext sessionContext; - - OpenSslServerContext( - X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory, - X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory, - Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, - long sessionCacheSize, long sessionTimeout, ClientAuth clientAuth, String[] protocols, boolean startTls, - boolean enableOcsp, String keyStore, Map.Entry, Object>... options) - throws SSLException { - this(trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory, ciphers, - cipherFilter, toNegotiator(apn), sessionCacheSize, sessionTimeout, clientAuth, protocols, startTls, - enableOcsp, keyStore, options); - } - - @SuppressWarnings("deprecation") - private OpenSslServerContext( - X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory, - X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory, - Iterable ciphers, CipherSuiteFilter cipherFilter, OpenSslApplicationProtocolNegotiator apn, - long sessionCacheSize, long sessionTimeout, ClientAuth clientAuth, String[] protocols, boolean startTls, - boolean enableOcsp, String keyStore, Map.Entry, Object>... options) - throws SSLException { - super(ciphers, cipherFilter, apn, SSL.SSL_MODE_SERVER, keyCertChain, - clientAuth, protocols, startTls, enableOcsp, options); - // Create a new SSL_CTX and configure it. - boolean success = false; - try { - OpenSslKeyMaterialProvider.validateKeyMaterialSupported(keyCertChain, key, keyPassword); - sessionContext = newSessionContext(this, ctx, engineMap, trustCertCollection, trustManagerFactory, - keyCertChain, key, keyPassword, keyManagerFactory, keyStore, - sessionCacheSize, sessionTimeout); - success = true; - } finally { - if (!success) { - release(); - } - } - } - - @Override - public OpenSslServerSessionContext sessionContext() { - return sessionContext; - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslServerSessionContext.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslServerSessionContext.java deleted file mode 100644 index eba161f361..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslServerSessionContext.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.internal.tcnative.SSL; -import io.netty.internal.tcnative.SSLContext; - -import java.util.concurrent.locks.Lock; - - -/** - * {@link OpenSslSessionContext} implementation which offers extra methods which are only useful for the server-side. - */ -public final class OpenSslServerSessionContext extends OpenSslSessionContext { - OpenSslServerSessionContext(ReferenceCountedOpenSslContext context, OpenSslKeyMaterialProvider provider) { - super(context, provider, SSL.SSL_SESS_CACHE_SERVER, new OpenSslSessionCache(context.engineMap)); - } - - /** - * Set the context within which session be reused (server side only) - * See - * man SSL_CTX_set_session_id_context - * - * @param sidCtx can be any kind of binary data, it is therefore possible to use e.g. the name - * of the application and/or the hostname and/or service name - * @return {@code true} if success, {@code false} otherwise. - */ - public boolean setSessionIdContext(byte[] sidCtx) { - Lock writerLock = context.ctxLock.writeLock(); - writerLock.lock(); - try { - return SSLContext.setSessionIdContext(context.ctx, sidCtx); - } finally { - writerLock.unlock(); - } - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslSession.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslSession.java deleted file mode 100644 index 4e6ef35d23..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslSession.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.util.ReferenceCounted; - -import javax.net.ssl.SSLException; -import javax.net.ssl.SSLSession; -import java.security.cert.Certificate; - -/** - * {@link SSLSession} that is specific to our native implementation and {@link ReferenceCounted} to track native - * resources. - */ -interface OpenSslSession extends SSLSession { - - /** - * Return the {@link OpenSslSessionId} that can be used to identify this session. - */ - OpenSslSessionId sessionId(); - - /** - * Set the local certificate chain that is used. It is not expected that this array will be changed at all - * and so its ok to not copy the array. - */ - void setLocalCertificate(Certificate[] localCertificate); - - /** - * Set the {@link OpenSslSessionId} for the {@link OpenSslSession}. - */ - void setSessionId(OpenSslSessionId id); - - @Override - OpenSslSessionContext getSessionContext(); - - /** - * 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); - - /** - * Called once the handshake has completed. - */ - void handshakeFinished(byte[] id, String cipher, String protocol, byte[] peerCertificate, - byte[][] peerCertificateChain, long creationTime, long timeout) throws SSLException; -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslSessionCache.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslSessionCache.java deleted file mode 100644 index 10d02577cd..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslSessionCache.java +++ /dev/null @@ -1,492 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.internal.tcnative.SSLSession; -import io.netty.internal.tcnative.SSLSessionCache; -import io.netty.util.ResourceLeakDetector; -import io.netty.util.ResourceLeakDetectorFactory; -import io.netty.util.ResourceLeakTracker; -import io.netty.util.internal.EmptyArrays; -import io.netty.util.internal.SystemPropertyUtil; - -import javax.security.cert.X509Certificate; -import java.security.Principal; -import java.security.cert.Certificate; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * {@link SSLSessionCache} implementation for our native SSL implementation. - */ -class OpenSslSessionCache implements SSLSessionCache { - private static final OpenSslSession[] EMPTY_SESSIONS = new OpenSslSession[0]; - - private static final int DEFAULT_CACHE_SIZE; - static { - // Respect the same system property as the JDK implementation to make it easy to switch between implementations. - int cacheSize = SystemPropertyUtil.getInt("javax.net.ssl.sessionCacheSize", 20480); - if (cacheSize >= 0) { - DEFAULT_CACHE_SIZE = cacheSize; - } else { - DEFAULT_CACHE_SIZE = 20480; - } - } - private final OpenSslEngineMap engineMap; - - private final Map sessions = - new LinkedHashMap() { - - private static final long serialVersionUID = -7773696788135734448L; - - @Override - protected boolean removeEldestEntry(Map.Entry eldest) { - int maxSize = maximumCacheSize.get(); - if (maxSize >= 0 && size() > maxSize) { - removeSessionWithId(eldest.getKey()); - } - // We always need to return false as we modify the map directly. - return false; - } - }; - - private final AtomicInteger maximumCacheSize = new AtomicInteger(DEFAULT_CACHE_SIZE); - - // Let's use the same default value as OpenSSL does. - // See https://www.openssl.org/docs/man1.1.1/man3/SSL_get_default_timeout.html - private final AtomicInteger sessionTimeout = new AtomicInteger(300); - private int sessionCounter; - - OpenSslSessionCache(OpenSslEngineMap engineMap) { - this.engineMap = engineMap; - } - - final void setSessionTimeout(int seconds) { - int oldTimeout = sessionTimeout.getAndSet(seconds); - if (oldTimeout > seconds) { - // Drain the whole cache as this way we can use the ordering of the LinkedHashMap to detect early - // if there are any other sessions left that are invalid. - clear(); - } - } - - final int getSessionTimeout() { - return sessionTimeout.get(); - } - - /** - * Called once a new {@link OpenSslSession} was created. - * - * @param session the new session. - * @return {@code true} if the session should be cached, {@code false} otherwise. - */ - protected boolean sessionCreated(NativeSslSession session) { - return true; - } - - /** - * Called once an {@link OpenSslSession} was removed from the cache. - * - * @param session the session to remove. - */ - protected void sessionRemoved(NativeSslSession session) { } - - final void setSessionCacheSize(int size) { - long oldSize = maximumCacheSize.getAndSet(size); - if (oldSize > size || size == 0) { - // Just keep it simple for now and drain the whole cache. - clear(); - } - } - - final int getSessionCacheSize() { - return maximumCacheSize.get(); - } - - private void expungeInvalidSessions() { - if (sessions.isEmpty()) { - return; - } - long now = System.currentTimeMillis(); - Iterator> iterator = sessions.entrySet().iterator(); - while (iterator.hasNext()) { - NativeSslSession session = iterator.next().getValue(); - // As we use a LinkedHashMap we can break the while loop as soon as we find a valid session. - // This is true as we always drain the cache as soon as we change the timeout to a smaller value as - // it was set before. This way its true that the insertation order matches the timeout order. - if (session.isValid(now)) { - break; - } - iterator.remove(); - - notifyRemovalAndFree(session); - } - } - - @Override - public final boolean sessionCreated(long ssl, long sslSession) { - ReferenceCountedOpenSslEngine engine = engineMap.get(ssl); - if (engine == null) { - // We couldn't find the engine itself. - return false; - } - NativeSslSession session = new NativeSslSession(sslSession, engine.getPeerHost(), engine.getPeerPort(), - getSessionTimeout() * 1000L); - engine.setSessionId(session.sessionId()); - synchronized (this) { - // Mimic what OpenSSL is doing and expunge every 255 new sessions - // See https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_flush_sessions.html - if (++sessionCounter == 255) { - sessionCounter = 0; - expungeInvalidSessions(); - } - - if (!sessionCreated(session)) { - // Should not be cached, return false. In this case we also need to call close() to ensure we - // close the ResourceLeakTracker. - session.close(); - return false; - } - - final NativeSslSession old = sessions.put(session.sessionId(), session); - if (old != null) { - notifyRemovalAndFree(old); - } - } - return true; - } - - @Override - public final long getSession(long ssl, byte[] sessionId) { - OpenSslSessionId id = new OpenSslSessionId(sessionId); - final NativeSslSession session; - synchronized (this) { - session = sessions.get(id); - if (session == null) { - return -1; - } - - // If the session is not valid anymore we should remove it from the cache and just signal back - // that we couldn't find a session that is re-usable. - if (!session.isValid() || - // This needs to happen in the synchronized block so we ensure we never destroy it before we - // incremented the reference count. If we cant increment the reference count there is something - // wrong. In this case just remove the session from the cache and signal back that we couldn't - // find a session for re-use. - !session.upRef()) { - // Remove the session from the cache. This will also take care of calling SSL_SESSION_free(...) - removeSessionWithId(session.sessionId()); - return -1; - } - - // At this point we already incremented the reference count via SSL_SESSION_up_ref(...). - if (session.shouldBeSingleUse()) { - // Should only be used once. In this case invalidate the session which will also ensure we remove it - // from the cache and call SSL_SESSION_free(...). - removeSessionWithId(session.sessionId()); - } - } - session.updateLastAccessedTime(); - return session.session(); - } - - void setSession(long ssl, String host, int port) { - // Do nothing by default as this needs special handling for the client side. - } - - /** - * Remove the session with the given id from the cache - */ - final synchronized void removeSessionWithId(OpenSslSessionId id) { - NativeSslSession sslSession = sessions.remove(id); - if (sslSession != null) { - notifyRemovalAndFree(sslSession); - } - } - - /** - * Returns {@code true} if there is a session for the given id in the cache. - */ - final synchronized boolean containsSessionWithId(OpenSslSessionId id) { - return sessions.containsKey(id); - } - - private void notifyRemovalAndFree(NativeSslSession session) { - sessionRemoved(session); - session.free(); - } - - /** - * Return the {@link OpenSslSession} which is cached for the given id. - */ - final synchronized OpenSslSession getSession(OpenSslSessionId id) { - NativeSslSession session = sessions.get(id); - if (session != null && !session.isValid()) { - // The session is not valid anymore, let's remove it and just signal back that there is no session - // with the given ID in the cache anymore. This also takes care of calling SSL_SESSION_free(...) - removeSessionWithId(session.sessionId()); - return null; - } - return session; - } - - /** - * Returns a snapshot of the session ids of the current valid sessions. - */ - final List getIds() { - final OpenSslSession[] sessionsArray; - synchronized (this) { - sessionsArray = sessions.values().toArray(EMPTY_SESSIONS); - } - List ids = new ArrayList(sessionsArray.length); - for (OpenSslSession session: sessionsArray) { - if (session.isValid()) { - ids.add(session.sessionId()); - } - } - return ids; - } - - /** - * Clear the cache and free all cached SSL_SESSION*. - */ - synchronized void clear() { - Iterator> iterator = sessions.entrySet().iterator(); - while (iterator.hasNext()) { - NativeSslSession session = iterator.next().getValue(); - iterator.remove(); - - // Notify about removal. This also takes care of calling SSL_SESSION_free(...). - notifyRemovalAndFree(session); - } - } - - /** - * {@link OpenSslSession} implementation which wraps the native SSL_SESSION* while in cache. - */ - static final class NativeSslSession implements OpenSslSession { - static final ResourceLeakDetector LEAK_DETECTOR = ResourceLeakDetectorFactory.instance() - .newResourceLeakDetector(NativeSslSession.class); - private final ResourceLeakTracker leakTracker; - private final long session; - private final String peerHost; - private final int peerPort; - private final OpenSslSessionId id; - private final long timeout; - private final long creationTime = System.currentTimeMillis(); - private volatile long lastAccessedTime = creationTime; - private volatile boolean valid = true; - private boolean freed; - - NativeSslSession(long session, String peerHost, int peerPort, long timeout) { - this.session = session; - this.peerHost = peerHost; - this.peerPort = peerPort; - this.timeout = timeout; - this.id = new OpenSslSessionId(io.netty.internal.tcnative.SSLSession.getSessionId(session)); - leakTracker = LEAK_DETECTOR.track(this); - } - - @Override - public void setSessionId(OpenSslSessionId id) { - throw new UnsupportedOperationException(); - } - - boolean shouldBeSingleUse() { - assert !freed; - return SSLSession.shouldBeSingleUse(session); - } - - long session() { - assert !freed; - return session; - } - - boolean upRef() { - assert !freed; - return SSLSession.upRef(session); - } - - synchronized void free() { - close(); - SSLSession.free(session); - } - - void close() { - assert !freed; - freed = true; - invalidate(); - if (leakTracker != null) { - leakTracker.close(this); - } - } - - @Override - public OpenSslSessionId sessionId() { - return id; - } - - boolean isValid(long now) { - return creationTime + timeout >= now && valid; - } - - @Override - public void setLocalCertificate(Certificate[] localCertificate) { - throw new UnsupportedOperationException(); - } - - @Override - public OpenSslSessionContext getSessionContext() { - return null; - } - - @Override - public void tryExpandApplicationBufferSize(int packetLengthDataOnly) { - throw new UnsupportedOperationException(); - } - - @Override - public void handshakeFinished(byte[] id, String cipher, String protocol, byte[] peerCertificate, - byte[][] peerCertificateChain, long creationTime, long timeout) { - throw new UnsupportedOperationException(); - } - - @Override - public byte[] getId() { - return id.cloneBytes(); - } - - @Override - public long getCreationTime() { - return creationTime; - } - - void updateLastAccessedTime() { - lastAccessedTime = System.currentTimeMillis(); - } - - @Override - public long getLastAccessedTime() { - return lastAccessedTime; - } - - @Override - public void invalidate() { - valid = false; - } - - @Override - public boolean isValid() { - return isValid(System.currentTimeMillis()); - } - - @Override - public void putValue(String name, Object value) { - throw new UnsupportedOperationException(); - } - - @Override - public Object getValue(String name) { - return null; - } - - @Override - public void removeValue(String name) { - // NOOP - } - - @Override - public String[] getValueNames() { - return EmptyArrays.EMPTY_STRINGS; - } - - @Override - public Certificate[] getPeerCertificates() { - throw new UnsupportedOperationException(); - } - - @Override - public Certificate[] getLocalCertificates() { - throw new UnsupportedOperationException(); - } - - @Override - public X509Certificate[] getPeerCertificateChain() { - throw new UnsupportedOperationException(); - } - - @Override - public Principal getPeerPrincipal() { - throw new UnsupportedOperationException(); - } - - @Override - public Principal getLocalPrincipal() { - throw new UnsupportedOperationException(); - } - - @Override - public String getCipherSuite() { - return null; - } - - @Override - public String getProtocol() { - return null; - } - - @Override - public String getPeerHost() { - return peerHost; - } - - @Override - public int getPeerPort() { - return peerPort; - } - - @Override - public int getPacketBufferSize() { - return ReferenceCountedOpenSslEngine.MAX_RECORD_SIZE; - } - - @Override - public int getApplicationBufferSize() { - return ReferenceCountedOpenSslEngine.MAX_PLAINTEXT_LENGTH; - } - - @Override - public int hashCode() { - return id.hashCode(); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof OpenSslSession)) { - return false; - } - OpenSslSession session1 = (OpenSslSession) o; - return id.equals(session1.sessionId()); - } - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslSessionContext.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslSessionContext.java deleted file mode 100644 index a1c3aaa349..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslSessionContext.java +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import static java.util.Objects.requireNonNull; - -import io.netty.internal.tcnative.SSL; -import io.netty.internal.tcnative.SSLContext; -import io.netty.internal.tcnative.SessionTicketKey; -import io.netty.util.internal.ObjectUtil; - -import javax.net.ssl.SSLSession; -import javax.net.ssl.SSLSessionContext; -import java.util.Arrays; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.concurrent.locks.Lock; - -/** - * OpenSSL specific {@link SSLSessionContext} implementation. - */ -public abstract class OpenSslSessionContext implements SSLSessionContext { - - 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; - - private final OpenSslSessionCache sessionCache; - private final long mask; - - // 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, OpenSslKeyMaterialProvider provider, long mask, - OpenSslSessionCache cache) { - this.context = context; - this.provider = provider; - this.mask = mask; - stats = new OpenSslSessionStats(context); - sessionCache = cache; - SSLContext.setSSLSessionCache(context.ctx, cache); - } - - final boolean useKeyManager() { - return provider != null; - } - - @Override - public void setSessionCacheSize(int size) { - ObjectUtil.checkPositiveOrZero(size, "size"); - sessionCache.setSessionCacheSize(size); - } - - @Override - public int getSessionCacheSize() { - return sessionCache.getSessionCacheSize(); - } - - @Override - public void setSessionTimeout(int seconds) { - ObjectUtil.checkPositiveOrZero(seconds, "seconds"); - - Lock writerLock = context.ctxLock.writeLock(); - writerLock.lock(); - try { - SSLContext.setSessionCacheTimeout(context.ctx, seconds); - sessionCache.setSessionTimeout(seconds); - } finally { - writerLock.unlock(); - } - } - - @Override - public int getSessionTimeout() { - return sessionCache.getSessionTimeout(); - } - - @Override - public SSLSession getSession(byte[] bytes) { - return sessionCache.getSession(new OpenSslSessionId(bytes)); - } - - @Override - public Enumeration getIds() { - return new Enumeration() { - private final Iterator ids = sessionCache.getIds().iterator(); - @Override - public boolean hasMoreElements() { - return ids.hasNext(); - } - - @Override - public byte[] nextElement() { - return ids.next().cloneBytes(); - } - }; - } - - /** - * Sets the SSL session ticket keys of this context. - * @deprecated use {@link #setTicketKeys(OpenSslSessionTicketKey...)}. - */ - @Deprecated - public void setTicketKeys(byte[] keys) { - if (keys.length % SessionTicketKey.TICKET_KEY_SIZE != 0) { - throw new IllegalArgumentException("keys.length % " + SessionTicketKey.TICKET_KEY_SIZE + " != 0"); - } - SessionTicketKey[] tickets = new SessionTicketKey[keys.length / SessionTicketKey.TICKET_KEY_SIZE]; - for (int i = 0, a = 0; i < tickets.length; i++) { - byte[] name = Arrays.copyOfRange(keys, a, SessionTicketKey.NAME_SIZE); - a += SessionTicketKey.NAME_SIZE; - byte[] hmacKey = Arrays.copyOfRange(keys, a, SessionTicketKey.HMAC_KEY_SIZE); - i += SessionTicketKey.HMAC_KEY_SIZE; - byte[] aesKey = Arrays.copyOfRange(keys, a, SessionTicketKey.AES_KEY_SIZE); - a += SessionTicketKey.AES_KEY_SIZE; - tickets[i] = new SessionTicketKey(name, hmacKey, aesKey); - } - Lock writerLock = context.ctxLock.writeLock(); - writerLock.lock(); - try { - SSLContext.clearOptions(context.ctx, SSL.SSL_OP_NO_TICKET); - SSLContext.setSessionTicketKeys(context.ctx, tickets); - } finally { - writerLock.unlock(); - } - } - - /** - * Sets the SSL session ticket keys of this context. Depending on the underlying native library you may omit the - * argument or pass an empty array and so let the native library handle the key generation and rotating for you. - * If this is supported by the underlying native library should be checked in this case. For example - * BoringSSL is known to support this. - */ - public void setTicketKeys(OpenSslSessionTicketKey... keys) { - requireNonNull(keys, "keys"); - SessionTicketKey[] ticketKeys = new SessionTicketKey[keys.length]; - for (int i = 0; i < ticketKeys.length; i++) { - ticketKeys[i] = keys[i].key; - } - Lock writerLock = context.ctxLock.writeLock(); - writerLock.lock(); - try { - SSLContext.clearOptions(context.ctx, SSL.SSL_OP_NO_TICKET); - if (ticketKeys.length > 0) { - SSLContext.setSessionTicketKeys(context.ctx, ticketKeys); - } - } finally { - writerLock.unlock(); - } - } - - /** - * Enable or disable caching of SSL sessions. - */ - public void setSessionCacheEnabled(boolean enabled) { - long mode = enabled ? mask | SSL.SSL_SESS_CACHE_NO_INTERNAL_LOOKUP | - SSL.SSL_SESS_CACHE_NO_INTERNAL_STORE : SSL.SSL_SESS_CACHE_OFF; - Lock writerLock = context.ctxLock.writeLock(); - writerLock.lock(); - try { - SSLContext.setSessionCacheMode(context.ctx, mode); - if (!enabled) { - sessionCache.clear(); - } - } finally { - writerLock.unlock(); - } - } - - /** - * Return {@code true} if caching of SSL sessions is enabled, {@code false} otherwise. - */ - public boolean isSessionCacheEnabled() { - Lock readerLock = context.ctxLock.readLock(); - readerLock.lock(); - try { - return (SSLContext.getSessionCacheMode(context.ctx) & mask) != 0; - } finally { - readerLock.unlock(); - } - } - - /** - * Returns the stats of this context. - */ - public OpenSslSessionStats stats() { - return stats; - } - - /** - * Remove the given {@link OpenSslSession} from the cache, and so not re-use it for new connections. - */ - final void removeFromCache(OpenSslSessionId id) { - sessionCache.removeSessionWithId(id); - } - - final boolean isInCache(OpenSslSessionId id) { - return sessionCache.containsSessionWithId(id); - } - - void setSessionFromCache(String host, int port, long ssl) { - sessionCache.setSession(ssl, host, port); - } - - final void destroy() { - if (provider != null) { - provider.destroy(); - } - sessionCache.clear(); - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslSessionId.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslSessionId.java deleted file mode 100644 index 76941f74ae..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslSessionId.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.util.internal.EmptyArrays; - -import java.util.Arrays; - -/** - * Represent the session ID used by an {@link OpenSslSession}. - */ -final class OpenSslSessionId { - - private final byte[] id; - private final int hashCode; - - static final OpenSslSessionId NULL_ID = new OpenSslSessionId(EmptyArrays.EMPTY_BYTES); - - OpenSslSessionId(byte[] id) { - // We take ownership if the byte[] and so there is no need to clone it. - this.id = id; - // cache the hashCode as the byte[] array will never change - this.hashCode = Arrays.hashCode(id); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof OpenSslSessionId)) { - return false; - } - - return Arrays.equals(id, ((OpenSslSessionId) o).id); - } - - @Override - public String toString() { - return "OpenSslSessionId{" + - "id=" + Arrays.toString(id) + - '}'; - } - - @Override - public int hashCode() { - return hashCode; - } - - byte[] cloneBytes() { - return id.clone(); - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslSessionStats.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslSessionStats.java deleted file mode 100644 index 85a0ee8b04..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslSessionStats.java +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.ssl; - -import io.netty.internal.tcnative.SSLContext; - -import java.util.concurrent.locks.Lock; - -/** - * Stats exposed by an OpenSSL session context. - * - * @see SSL_CTX_sess_number - */ -public final class OpenSslSessionStats { - - private 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. - OpenSslSessionStats(ReferenceCountedOpenSslContext context) { - this.context = context; - } - - /** - * Returns the current number of sessions in the internal session cache. - */ - public long number() { - Lock readerLock = context.ctxLock.readLock(); - readerLock.lock(); - try { - return SSLContext.sessionNumber(context.ctx); - } finally { - readerLock.unlock(); - } - } - - /** - * Returns the number of started SSL/TLS handshakes in client mode. - */ - public long connect() { - Lock readerLock = context.ctxLock.readLock(); - readerLock.lock(); - try { - return SSLContext.sessionConnect(context.ctx); - } finally { - readerLock.unlock(); - } - } - - /** - * Returns the number of successfully established SSL/TLS sessions in client mode. - */ - public long connectGood() { - Lock readerLock = context.ctxLock.readLock(); - readerLock.lock(); - try { - return SSLContext.sessionConnectGood(context.ctx); - } finally { - readerLock.unlock(); - } - } - - /** - * Returns the number of start renegotiations in client mode. - */ - public long connectRenegotiate() { - Lock readerLock = context.ctxLock.readLock(); - readerLock.lock(); - try { - return SSLContext.sessionConnectRenegotiate(context.ctx); - } finally { - readerLock.unlock(); - } - } - - /** - * Returns the number of started SSL/TLS handshakes in server mode. - */ - public long accept() { - Lock readerLock = context.ctxLock.readLock(); - readerLock.lock(); - try { - return SSLContext.sessionAccept(context.ctx); - } finally { - readerLock.unlock(); - } - } - - /** - * Returns the number of successfully established SSL/TLS sessions in server mode. - */ - public long acceptGood() { - Lock readerLock = context.ctxLock.readLock(); - readerLock.lock(); - try { - return SSLContext.sessionAcceptGood(context.ctx); - } finally { - readerLock.unlock(); - } - } - - /** - * Returns the number of start renegotiations in server mode. - */ - public long acceptRenegotiate() { - Lock readerLock = context.ctxLock.readLock(); - readerLock.lock(); - try { - return SSLContext.sessionAcceptRenegotiate(context.ctx); - } finally { - readerLock.unlock(); - } - } - - /** - * Returns the number of successfully reused sessions. In client mode, a session set with {@code SSL_set_session} - * successfully reused is counted as a hit. In server mode, a session successfully retrieved from internal or - * external cache is counted as a hit. - */ - public long hits() { - Lock readerLock = context.ctxLock.readLock(); - readerLock.lock(); - try { - return SSLContext.sessionHits(context.ctx); - } finally { - readerLock.unlock(); - } - } - - /** - * Returns the number of successfully retrieved sessions from the external session cache in server mode. - */ - public long cbHits() { - Lock readerLock = context.ctxLock.readLock(); - readerLock.lock(); - try { - return SSLContext.sessionCbHits(context.ctx); - } finally { - readerLock.unlock(); - } - } - - /** - * Returns the number of sessions proposed by clients that were not found in the internal session cache - * in server mode. - */ - public long misses() { - Lock readerLock = context.ctxLock.readLock(); - readerLock.lock(); - try { - return SSLContext.sessionMisses(context.ctx); - } finally { - readerLock.unlock(); - } - } - - /** - * Returns the number of sessions proposed by clients and either found in the internal or external session cache - * in server mode, but that were invalid due to timeout. These sessions are not included in the {@link #hits()} - * count. - */ - public long timeouts() { - Lock readerLock = context.ctxLock.readLock(); - readerLock.lock(); - try { - return SSLContext.sessionTimeouts(context.ctx); - } finally { - readerLock.unlock(); - } - } - - /** - * Returns the number of sessions that were removed because the maximum session cache size was exceeded. - */ - public long cacheFull() { - Lock readerLock = context.ctxLock.readLock(); - readerLock.lock(); - try { - return SSLContext.sessionCacheFull(context.ctx); - } finally { - readerLock.unlock(); - } - } - - /** - * Returns the number of times a client presented a ticket that did not match any key in the list. - */ - public long ticketKeyFail() { - Lock readerLock = context.ctxLock.readLock(); - readerLock.lock(); - try { - return SSLContext.sessionTicketKeyFail(context.ctx); - } finally { - readerLock.unlock(); - } - } - - /** - * Returns the number of times a client did not present a ticket and we issued a new one - */ - public long ticketKeyNew() { - Lock readerLock = context.ctxLock.readLock(); - readerLock.lock(); - try { - return SSLContext.sessionTicketKeyNew(context.ctx); - } finally { - readerLock.unlock(); - } - } - - /** - * Returns the number of times a client presented a ticket derived from an older key, - * and we upgraded to the primary key. - */ - public long ticketKeyRenew() { - Lock readerLock = context.ctxLock.readLock(); - readerLock.lock(); - try { - return SSLContext.sessionTicketKeyRenew(context.ctx); - } finally { - readerLock.unlock(); - } - } - - /** - * Returns the number of times a client presented a ticket derived from the primary key. - */ - public long ticketKeyResume() { - Lock readerLock = context.ctxLock.readLock(); - readerLock.lock(); - try { - return SSLContext.sessionTicketKeyResume(context.ctx); - } finally { - readerLock.unlock(); - } - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslSessionTicketKey.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslSessionTicketKey.java deleted file mode 100644 index 175a37e5fa..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslSessionTicketKey.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.internal.tcnative.SessionTicketKey; - -/** - * Session Ticket Key - */ -public final class OpenSslSessionTicketKey { - - /** - * Size of session ticket key name - */ - public static final int NAME_SIZE = SessionTicketKey.NAME_SIZE; - /** - * Size of session ticket key HMAC key - */ - public static final int HMAC_KEY_SIZE = SessionTicketKey.HMAC_KEY_SIZE; - /** - * Size of session ticket key AES key - */ - public static final int AES_KEY_SIZE = SessionTicketKey.AES_KEY_SIZE; - /** - * Size of session ticker key - */ - public static final int TICKET_KEY_SIZE = SessionTicketKey.TICKET_KEY_SIZE; - - final SessionTicketKey key; - - /** - * Construct a OpenSslSessionTicketKey. - * - * @param name the name of the session ticket key - * @param hmacKey the HMAC key of the session ticket key - * @param aesKey the AES key of the session ticket key - */ - public OpenSslSessionTicketKey(byte[] name, byte[] hmacKey, byte[] aesKey) { - key = new SessionTicketKey(name.clone(), hmacKey.clone(), aesKey.clone()); - } - - /** - * Get name. - * @return the name of the session ticket key - */ - public byte[] name() { - return key.getName().clone(); - } - - /** - * Get HMAC key. - * @return the HMAC key of the session ticket key - */ - public byte[] hmacKey() { - return key.getHmacKey().clone(); - } - - /** - * Get AES Key. - * @return the AES key of the session ticket key - */ - public byte[] aesKey() { - return key.getAesKey().clone(); - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslX509KeyManagerFactory.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslX509KeyManagerFactory.java deleted file mode 100644 index 5d5fef891d..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslX509KeyManagerFactory.java +++ /dev/null @@ -1,416 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import static java.util.Objects.requireNonNull; -import static io.netty.util.internal.ObjectUtil.checkNonEmpty; -import static io.netty.util.internal.ObjectUtil.checkNotNull; - -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty.internal.tcnative.SSL; -import io.netty.util.ReferenceCountUtil; - -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.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; - -/** - * 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 = requireNonNull(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(); - } - } - checkNonEmpty(materialMap, "materialMap"); - } - - @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(); - } - throw (Exception) value; - } - - @Override - void destroy() { - for (Object material: materialMap.values()) { - ReferenceCountUtil.release(material); - } - materialMap.clear(); - } - } - } - } - - /** - * 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 { - checkNotNull(certificateChain, "certificateChain"); - KeyStore store = new OpenSslKeyStore(certificateChain.clone(), false); - store.load(null, null); - OpenSslX509KeyManagerFactory factory = new OpenSslX509KeyManagerFactory(); - factory.init(store, password == null ? null : password.toCharArray()); - return factory; - } - - /** - * See {@link OpenSslX509KeyManagerFactory#newEngineBased(X509Certificate[], String)}. - */ - public static OpenSslX509KeyManagerFactory newKeyless(File chain) - throws CertificateException, IOException, - KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException { - return newKeyless(SslContext.toX509Certificates(chain)); - } - - /** - * See {@link OpenSslX509KeyManagerFactory#newEngineBased(X509Certificate[], String)}. - */ - public static OpenSslX509KeyManagerFactory newKeyless(InputStream chain) - throws CertificateException, IOException, - KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException { - return newKeyless(SslContext.toX509Certificates(chain)); - } - - /** - * Returns a new initialized {@link OpenSslX509KeyManagerFactory} which will provide its private key by using the - * {@link OpenSslPrivateKeyMethod}. - */ - public static OpenSslX509KeyManagerFactory newKeyless(X509Certificate... certificateChain) - throws CertificateException, IOException, - KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException { - checkNotNull(certificateChain, "certificateChain"); - KeyStore store = new OpenSslKeyStore(certificateChain.clone(), true); - store.load(null, null); - OpenSslX509KeyManagerFactory factory = new OpenSslX509KeyManagerFactory(); - factory.init(store, null); - return factory; - } - - private static final class OpenSslKeyStore extends KeyStore { - private OpenSslKeyStore(final X509Certificate[] certificateChain, final boolean keyless) { - super(new KeyStoreSpi() { - - private final Date creationDate = new Date(); - - @Override - public Key engineGetKey(String alias, char[] password) throws UnrecoverableKeyException { - if (engineContainsAlias(alias)) { - final long privateKeyAddress; - if (keyless) { - privateKeyAddress = 0; - } else { - try { - privateKeyAddress = 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 new OpenSslPrivateKey(privateKeyAddress); - } - 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/OpenSslX509TrustManagerWrapper.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslX509TrustManagerWrapper.java deleted file mode 100644 index 02c87a38cf..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslX509TrustManagerWrapper.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.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.NoSuchProviderException; -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 = manager -> 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: - // - https://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 { - throw new CertificateException(); - } - - @Override - public void checkServerTrusted(X509Certificate[] x509Certificates, String s) - throws CertificateException { - throw new 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((PrivilegedAction) () -> { - 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 | 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, NoSuchProviderException { - // As this depends on the implementation detail we should explicit select the correct provider. - // See https://github.com/netty/netty/issues/10374 - return SSLContext.getInstance("TLS", "SunJSSE"); - } - - 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 | KeyManagementException | NoSuchProviderException e) { - // This should never happen as we did the same in the static block - // before. - PlatformDependent.throwException(e); - } - } - return manager; - } - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OptionalSslHandler.java b/handler/src/main/java/io/netty/handler/ssl/OptionalSslHandler.java deleted file mode 100644 index 9fc9f2ab81..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/OptionalSslHandler.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.ByteToMessageDecoder; -import io.netty.util.ReferenceCountUtil; - -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLParameters; - -/** - * {@link OptionalSslHandler} is a utility decoder to support both SSL and non-SSL handlers - * based on the first message received. - */ -public class OptionalSslHandler extends ByteToMessageDecoder { - - private final SslContext sslContext; - - public OptionalSslHandler(SslContext sslContext) { - this.sslContext = requireNonNull(sslContext, "sslContext"); - } - - @Override - protected void decode(ChannelHandlerContext context, ByteBuf in) throws Exception { - if (in.readableBytes() < SslUtils.SSL_RECORD_HEADER_LENGTH) { - return; - } - if (SslHandler.isEncrypted(in)) { - handleSsl(context); - } else { - handleNonSsl(context); - } - } - - private void handleSsl(ChannelHandlerContext context) { - SslHandler sslHandler = null; - try { - sslHandler = newSslHandler(context, sslContext); - context.pipeline().replace(this, newSslHandlerName(), sslHandler); - sslHandler = null; - } finally { - // Since the SslHandler was not inserted into the pipeline the ownership of the SSLEngine was not - // transferred to the SslHandler. - if (sslHandler != null) { - ReferenceCountUtil.safeRelease(sslHandler.engine()); - } - } - } - - private void handleNonSsl(ChannelHandlerContext context) { - ChannelHandler handler = newNonSslHandler(context); - if (handler != null) { - context.pipeline().replace(this, newNonSslHandlerName(), handler); - } else { - context.pipeline().remove(this); - } - } - - /** - * Optionally specify the SSL handler name, this method may return {@code null}. - * @return the name of the SSL handler. - */ - protected String newSslHandlerName() { - return null; - } - - /** - * Override to configure the SslHandler eg. {@link SSLParameters#setEndpointIdentificationAlgorithm(String)}. - * The hostname and port is not known by this method so servers may want to override this method and use the - * {@link SslContext#newHandler(ByteBufAllocator, String, int)} variant. - * - * @param context the {@link ChannelHandlerContext} to use. - * @param sslContext the {@link SSLContext} to use. - * @return the {@link SslHandler} which will replace the {@link OptionalSslHandler} in the pipeline if the - * traffic is SSL. - */ - protected SslHandler newSslHandler(ChannelHandlerContext context, SslContext sslContext) { - return sslContext.newHandler(context.alloc()); - } - - /** - * Optionally specify the non-SSL handler name, this method may return {@code null}. - * @return the name of the non-SSL handler. - */ - protected String newNonSslHandlerName() { - return null; - } - - /** - * Override to configure the ChannelHandler. - * @param context the {@link ChannelHandlerContext} to use. - * @return the {@link ChannelHandler} which will replace the {@link OptionalSslHandler} in the pipeline - * or {@code null} to simply remove the {@link OptionalSslHandler} if the traffic is non-SSL. - */ - protected ChannelHandler newNonSslHandler(ChannelHandlerContext context) { - return null; - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/PemEncoded.java b/handler/src/main/java/io/netty/handler/ssl/PemEncoded.java deleted file mode 100644 index 634d9e2e25..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/PemEncoded.java +++ /dev/null @@ -1,55 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufHolder; - -/** - * A marker interface for PEM encoded values. - */ -interface PemEncoded extends ByteBufHolder { - - /** - * Returns {@code true} if the PEM encoded value is considered - * sensitive information such as a private key. - */ - boolean isSensitive(); - - @Override - PemEncoded copy(); - - @Override - PemEncoded duplicate(); - - @Override - PemEncoded retainedDuplicate(); - - @Override - PemEncoded replace(ByteBuf content); - - @Override - PemEncoded retain(); - - @Override - PemEncoded retain(int increment); - - @Override - PemEncoded touch(); - - @Override - PemEncoded touch(Object hint); -} diff --git a/handler/src/main/java/io/netty/handler/ssl/PemPrivateKey.java b/handler/src/main/java/io/netty/handler/ssl/PemPrivateKey.java deleted file mode 100644 index 2e82ef33b9..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/PemPrivateKey.java +++ /dev/null @@ -1,231 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import static java.util.Objects.requireNonNull; - -import java.security.PrivateKey; - -import javax.security.auth.Destroyable; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.Unpooled; -import io.netty.util.AbstractReferenceCounted; -import io.netty.util.CharsetUtil; -import io.netty.util.IllegalReferenceCountException; - -/** - * This is a special purpose implementation of a {@link PrivateKey} which allows the - * user to pass PEM/PKCS#8 encoded key material straight into {@link OpenSslContext} - * without having to parse and re-encode bytes in Java land. - * - * All methods other than what's implemented in {@link PemEncoded} and {@link Destroyable} - * throw {@link UnsupportedOperationException}s. - * - * @see PemEncoded - * @see OpenSslContext - * @see #valueOf(byte[]) - * @see #valueOf(ByteBuf) - */ -public final class PemPrivateKey extends AbstractReferenceCounted implements PrivateKey, PemEncoded { - private static final long serialVersionUID = 7978017465645018936L; - - private static final byte[] BEGIN_PRIVATE_KEY = "-----BEGIN PRIVATE KEY-----\n".getBytes(CharsetUtil.US_ASCII); - private static final byte[] END_PRIVATE_KEY = "\n-----END PRIVATE KEY-----\n".getBytes(CharsetUtil.US_ASCII); - - private static final String PKCS8_FORMAT = "PKCS#8"; - - /** - * Creates a {@link PemEncoded} value from the {@link PrivateKey}. - */ - static PemEncoded toPEM(ByteBufAllocator allocator, boolean useDirect, PrivateKey key) { - // We can take a shortcut if the private key happens to be already - // PEM/PKCS#8 encoded. This is the ideal case and reason why all - // this exists. It allows the user to pass pre-encoded bytes straight - // into OpenSSL without having to do any of the extra work. - if (key instanceof PemEncoded) { - return ((PemEncoded) key).retain(); - } - - byte[] bytes = key.getEncoded(); - if (bytes == null) { - throw new IllegalArgumentException(key.getClass().getName() + " does not support encoding"); - } - - return toPEM(allocator, useDirect, bytes); - } - - static PemEncoded toPEM(ByteBufAllocator allocator, boolean useDirect, byte[] bytes) { - ByteBuf encoded = Unpooled.wrappedBuffer(bytes); - try { - ByteBuf base64 = SslUtils.toBase64(allocator, encoded); - try { - int size = BEGIN_PRIVATE_KEY.length + base64.readableBytes() + END_PRIVATE_KEY.length; - - boolean success = false; - final ByteBuf pem = useDirect ? allocator.directBuffer(size) : allocator.buffer(size); - try { - pem.writeBytes(BEGIN_PRIVATE_KEY); - pem.writeBytes(base64); - pem.writeBytes(END_PRIVATE_KEY); - - PemValue value = new PemValue(pem, true); - success = true; - return value; - } finally { - // Make sure we never leak that PEM ByteBuf if there's an Exception. - if (!success) { - SslUtils.zerooutAndRelease(pem); - } - } - } finally { - SslUtils.zerooutAndRelease(base64); - } - } finally { - SslUtils.zerooutAndRelease(encoded); - } - } - - /** - * Creates a {@link PemPrivateKey} from raw {@code byte[]}. - * - * ATTENTION: It's assumed that the given argument is a PEM/PKCS#8 encoded value. - * No input validation is performed to validate it. - */ - public static PemPrivateKey valueOf(byte[] key) { - return valueOf(Unpooled.wrappedBuffer(key)); - } - - /** - * Creates a {@link PemPrivateKey} from raw {@code ByteBuf}. - * - * ATTENTION: It's assumed that the given argument is a PEM/PKCS#8 encoded value. - * No input validation is performed to validate it. - */ - public static PemPrivateKey valueOf(ByteBuf key) { - return new PemPrivateKey(key); - } - - private final ByteBuf content; - - private PemPrivateKey(ByteBuf content) { - this.content = requireNonNull(content, "content"); - } - - @Override - public boolean isSensitive() { - return true; - } - - @Override - public ByteBuf content() { - int count = refCnt(); - if (count <= 0) { - throw new IllegalReferenceCountException(count); - } - - return content; - } - - @Override - public PemPrivateKey copy() { - return replace(content.copy()); - } - - @Override - public PemPrivateKey duplicate() { - return replace(content.duplicate()); - } - - @Override - public PemPrivateKey retainedDuplicate() { - return replace(content.retainedDuplicate()); - } - - @Override - public PemPrivateKey replace(ByteBuf content) { - return new PemPrivateKey(content); - } - - @Override - public PemPrivateKey touch() { - content.touch(); - return this; - } - - @Override - public PemPrivateKey touch(Object hint) { - content.touch(hint); - return this; - } - - @Override - public PemPrivateKey retain() { - return (PemPrivateKey) super.retain(); - } - - @Override - public PemPrivateKey retain(int increment) { - return (PemPrivateKey) super.retain(increment); - } - - @Override - protected void deallocate() { - // Private Keys are sensitive. We need to zero the bytes - // before we're releasing the underlying ByteBuf - SslUtils.zerooutAndRelease(content); - } - - @Override - public byte[] getEncoded() { - throw new UnsupportedOperationException(); - } - - @Override - public String getAlgorithm() { - throw new UnsupportedOperationException(); - } - - @Override - public String getFormat() { - return PKCS8_FORMAT; - } - - /** - * 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() - */ - @Override - 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() - */ - @Override - public boolean isDestroyed() { - return refCnt() == 0; - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/PemReader.java b/handler/src/main/java/io/netty/handler/ssl/PemReader.java deleted file mode 100644 index 131f12ddbf..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/PemReader.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.ssl; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.handler.codec.base64.Base64; -import io.netty.util.CharsetUtil; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.security.KeyException; -import java.security.KeyStore; -import java.security.cert.CertificateException; -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Reads a PEM file and converts it into a list of DERs so that they are imported into a {@link KeyStore} easily. - */ -final class PemReader { - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(PemReader.class); - - private static final Pattern CERT_PATTERN = Pattern.compile( - "-+BEGIN\\s+.*CERTIFICATE[^-]*-+(?:\\s|\\r|\\n)+" + // Header - "([a-z0-9+/=\\r\\n]+)" + // Base64 text - "-+END\\s+.*CERTIFICATE[^-]*-+", // Footer - Pattern.CASE_INSENSITIVE); - private static final Pattern KEY_PATTERN = Pattern.compile( - "-+BEGIN\\s+.*PRIVATE\\s+KEY[^-]*-+(?:\\s|\\r|\\n)+" + // Header - "([a-z0-9+/=\\r\\n]+)" + // Base64 text - "-+END\\s+.*PRIVATE\\s+KEY[^-]*-+", // Footer - Pattern.CASE_INSENSITIVE); - - static ByteBuf[] readCertificates(File file) throws CertificateException { - try { - InputStream in = new FileInputStream(file); - - try { - return readCertificates(in); - } finally { - safeClose(in); - } - } catch (FileNotFoundException e) { - throw new CertificateException("could not find certificate file: " + file); - } - } - - static ByteBuf[] readCertificates(InputStream in) throws CertificateException { - String content; - try { - content = readContent(in); - } catch (IOException e) { - throw new CertificateException("failed to read certificate input stream", e); - } - - List certs = new ArrayList<>(); - Matcher m = CERT_PATTERN.matcher(content); - int start = 0; - while (m.find(start)) { - - ByteBuf base64 = Unpooled.copiedBuffer(m.group(1), CharsetUtil.US_ASCII); - ByteBuf der = Base64.decode(base64); - base64.release(); - certs.add(der); - - start = m.end(); - } - - if (certs.isEmpty()) { - throw new CertificateException("found no certificates in input stream"); - } - - return certs.toArray(new ByteBuf[0]); - } - - static ByteBuf readPrivateKey(File file) throws KeyException { - try { - InputStream in = new FileInputStream(file); - - try { - return readPrivateKey(in); - } finally { - safeClose(in); - } - } catch (FileNotFoundException e) { - throw new KeyException("could not find key file: " + file); - } - } - - static ByteBuf readPrivateKey(InputStream in) throws KeyException { - String content; - try { - content = readContent(in); - } catch (IOException e) { - throw new KeyException("failed to read key input stream", e); - } - - Matcher m = KEY_PATTERN.matcher(content); - if (!m.find()) { - throw new KeyException("could not find a PKCS #8 private key in input stream" + - " (see https://netty.io/wiki/sslcontextbuilder-and-private-key.html for more information)"); - } - - ByteBuf base64 = Unpooled.copiedBuffer(m.group(1), CharsetUtil.US_ASCII); - ByteBuf der = Base64.decode(base64); - base64.release(); - return der; - } - - private static String readContent(InputStream in) throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try { - byte[] buf = new byte[8192]; - for (;;) { - int ret = in.read(buf); - if (ret < 0) { - break; - } - out.write(buf, 0, ret); - } - return out.toString(CharsetUtil.US_ASCII.name()); - } finally { - safeClose(out); - } - } - - private static void safeClose(InputStream in) { - try { - in.close(); - } catch (IOException e) { - logger.warn("Failed to close a stream.", e); - } - } - - private static void safeClose(OutputStream out) { - try { - out.close(); - } catch (IOException e) { - logger.warn("Failed to close a stream.", e); - } - } - - private PemReader() { } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/PemValue.java b/handler/src/main/java/io/netty/handler/ssl/PemValue.java deleted file mode 100644 index 4f10b8e3be..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/PemValue.java +++ /dev/null @@ -1,106 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.util.AbstractReferenceCounted; -import io.netty.util.IllegalReferenceCountException; - -/** - * A PEM encoded value. - * - * @see PemEncoded - * @see PemPrivateKey#toPEM(ByteBufAllocator, boolean, java.security.PrivateKey) - * @see PemX509Certificate#toPEM(ByteBufAllocator, boolean, java.security.cert.X509Certificate[]) - */ -class PemValue extends AbstractReferenceCounted implements PemEncoded { - - private final ByteBuf content; - - private final boolean sensitive; - - PemValue(ByteBuf content, boolean sensitive) { - this.content = requireNonNull(content, "content"); - this.sensitive = sensitive; - } - - @Override - public boolean isSensitive() { - return sensitive; - } - - @Override - public ByteBuf content() { - int count = refCnt(); - if (count <= 0) { - throw new IllegalReferenceCountException(count); - } - - return content; - } - - @Override - public PemValue copy() { - return replace(content.copy()); - } - - @Override - public PemValue duplicate() { - return replace(content.duplicate()); - } - - @Override - public PemValue retainedDuplicate() { - return replace(content.retainedDuplicate()); - } - - @Override - public PemValue replace(ByteBuf content) { - return new PemValue(content, sensitive); - } - - @Override - public PemValue touch() { - return (PemValue) super.touch(); - } - - @Override - public PemValue touch(Object hint) { - content.touch(hint); - return this; - } - - @Override - public PemValue retain() { - return (PemValue) super.retain(); - } - - @Override - public PemValue retain(int increment) { - return (PemValue) super.retain(increment); - } - - @Override - protected void deallocate() { - if (sensitive) { - SslUtils.zeroout(content); - } - content.release(); - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/PemX509Certificate.java b/handler/src/main/java/io/netty/handler/ssl/PemX509Certificate.java deleted file mode 100644 index f728211d8f..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/PemX509Certificate.java +++ /dev/null @@ -1,402 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import static java.util.Objects.requireNonNull; -import static io.netty.util.internal.ObjectUtil.checkNonEmpty; - -import java.math.BigInteger; -import java.security.Principal; -import java.security.PublicKey; -import java.security.cert.CertificateEncodingException; -import java.security.cert.X509Certificate; -import java.util.Arrays; -import java.util.Date; -import java.util.Set; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.Unpooled; -import io.netty.util.CharsetUtil; -import io.netty.util.IllegalReferenceCountException; - -/** - * This is a special purpose implementation of a {@link X509Certificate} which allows - * the user to pass PEM/PKCS#8 encoded data straight into {@link OpenSslContext} without - * having to parse and re-encode bytes in Java land. - * - * All methods other than what's implemented in {@link PemEncoded}'s throw - * {@link UnsupportedOperationException}s. - * - * @see PemEncoded - * @see OpenSslContext - * @see #valueOf(byte[]) - * @see #valueOf(ByteBuf) - */ -public final class PemX509Certificate extends X509Certificate implements PemEncoded { - - private static final byte[] BEGIN_CERT = "-----BEGIN CERTIFICATE-----\n".getBytes(CharsetUtil.US_ASCII); - private static final byte[] END_CERT = "\n-----END CERTIFICATE-----\n".getBytes(CharsetUtil.US_ASCII); - - /** - * Creates a {@link PemEncoded} value from the {@link X509Certificate}s. - */ - static PemEncoded toPEM(ByteBufAllocator allocator, boolean useDirect, - X509Certificate... chain) throws CertificateEncodingException { - - checkNonEmpty(chain, "chain"); - - // We can take a shortcut if there is only one certificate and - // it already happens to be a PemEncoded instance. This is the - // ideal case and reason why all this exists. It allows the user - // to pass pre-encoded bytes straight into OpenSSL without having - // to do any of the extra work. - if (chain.length == 1) { - X509Certificate first = chain[0]; - if (first instanceof PemEncoded) { - return ((PemEncoded) first).retain(); - } - } - - boolean success = false; - ByteBuf pem = null; - try { - for (X509Certificate cert : chain) { - - if (cert == null) { - throw new IllegalArgumentException("Null element in chain: " + Arrays.toString(chain)); - } - - if (cert instanceof PemEncoded) { - pem = append(allocator, useDirect, (PemEncoded) cert, chain.length, pem); - } else { - pem = append(allocator, useDirect, cert, chain.length, pem); - } - } - - PemValue value = new PemValue(pem, false); - success = true; - return value; - } finally { - // Make sure we never leak the PEM's ByteBuf in the event of an Exception - if (!success && pem != null) { - pem.release(); - } - } - } - - /** - * Appends the {@link PemEncoded} value to the {@link ByteBuf} (last arg) and returns it. - * If the {@link ByteBuf} didn't exist yet it'll create it using the {@link ByteBufAllocator}. - */ - private static ByteBuf append(ByteBufAllocator allocator, boolean useDirect, - PemEncoded encoded, int count, ByteBuf pem) { - - ByteBuf content = encoded.content(); - - if (pem == null) { - // see the other append() method - pem = newBuffer(allocator, useDirect, content.readableBytes() * count); - } - - pem.writeBytes(content.slice()); - return pem; - } - - /** - * Appends the {@link X509Certificate} value to the {@link ByteBuf} (last arg) and returns it. - * If the {@link ByteBuf} didn't exist yet it'll create it using the {@link ByteBufAllocator}. - */ - private static ByteBuf append(ByteBufAllocator allocator, boolean useDirect, - X509Certificate cert, int count, ByteBuf pem) throws CertificateEncodingException { - - ByteBuf encoded = Unpooled.wrappedBuffer(cert.getEncoded()); - try { - ByteBuf base64 = SslUtils.toBase64(allocator, encoded); - try { - if (pem == null) { - // We try to approximate the buffer's initial size. The sizes of - // certificates can vary a lot so it'll be off a bit depending - // on the number of elements in the array (count argument). - pem = newBuffer(allocator, useDirect, - (BEGIN_CERT.length + base64.readableBytes() + END_CERT.length) * count); - } - - pem.writeBytes(BEGIN_CERT); - pem.writeBytes(base64); - pem.writeBytes(END_CERT); - } finally { - base64.release(); - } - } finally { - encoded.release(); - } - - return pem; - } - - private static ByteBuf newBuffer(ByteBufAllocator allocator, boolean useDirect, int initialCapacity) { - return useDirect ? allocator.directBuffer(initialCapacity) : allocator.buffer(initialCapacity); - } - - /** - * Creates a {@link PemX509Certificate} from raw {@code byte[]}. - * - * ATTENTION: It's assumed that the given argument is a PEM/PKCS#8 encoded value. - * No input validation is performed to validate it. - */ - public static PemX509Certificate valueOf(byte[] key) { - return valueOf(Unpooled.wrappedBuffer(key)); - } - - /** - * Creates a {@link PemX509Certificate} from raw {@code ByteBuf}. - * - * ATTENTION: It's assumed that the given argument is a PEM/PKCS#8 encoded value. - * No input validation is performed to validate it. - */ - public static PemX509Certificate valueOf(ByteBuf key) { - return new PemX509Certificate(key); - } - - private final ByteBuf content; - - private PemX509Certificate(ByteBuf content) { - this.content = requireNonNull(content, "content"); - } - - @Override - public boolean isSensitive() { - // There is no sensitive information in a X509 Certificate - return false; - } - - @Override - public int refCnt() { - return content.refCnt(); - } - - @Override - public ByteBuf content() { - int count = refCnt(); - if (count <= 0) { - throw new IllegalReferenceCountException(count); - } - - return content; - } - - @Override - public PemX509Certificate copy() { - return replace(content.copy()); - } - - @Override - public PemX509Certificate duplicate() { - return replace(content.duplicate()); - } - - @Override - public PemX509Certificate retainedDuplicate() { - return replace(content.retainedDuplicate()); - } - - @Override - public PemX509Certificate replace(ByteBuf content) { - return new PemX509Certificate(content); - } - - @Override - public PemX509Certificate retain() { - content.retain(); - return this; - } - - @Override - public PemX509Certificate retain(int increment) { - content.retain(increment); - return this; - } - - @Override - public PemX509Certificate touch() { - content.touch(); - return this; - } - - @Override - public PemX509Certificate touch(Object hint) { - content.touch(hint); - return this; - } - - @Override - public boolean release() { - return content.release(); - } - - @Override - public boolean release(int decrement) { - return content.release(decrement); - } - - @Override - public byte[] getEncoded() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean hasUnsupportedCriticalExtension() { - throw new UnsupportedOperationException(); - } - - @Override - public Set getCriticalExtensionOIDs() { - throw new UnsupportedOperationException(); - } - - @Override - public Set getNonCriticalExtensionOIDs() { - throw new UnsupportedOperationException(); - } - - @Override - public byte[] getExtensionValue(String oid) { - throw new UnsupportedOperationException(); - } - - @Override - public void checkValidity() { - throw new UnsupportedOperationException(); - } - - @Override - public void checkValidity(Date date) { - throw new UnsupportedOperationException(); - } - - @Override - public int getVersion() { - throw new UnsupportedOperationException(); - } - - @Override - public BigInteger getSerialNumber() { - throw new UnsupportedOperationException(); - } - - @Override - public Principal getIssuerDN() { - throw new UnsupportedOperationException(); - } - - @Override - public Principal getSubjectDN() { - throw new UnsupportedOperationException(); - } - - @Override - public Date getNotBefore() { - throw new UnsupportedOperationException(); - } - - @Override - public Date getNotAfter() { - throw new UnsupportedOperationException(); - } - - @Override - public byte[] getTBSCertificate() { - throw new UnsupportedOperationException(); - } - - @Override - public byte[] getSignature() { - throw new UnsupportedOperationException(); - } - - @Override - public String getSigAlgName() { - throw new UnsupportedOperationException(); - } - - @Override - public String getSigAlgOID() { - throw new UnsupportedOperationException(); - } - - @Override - public byte[] getSigAlgParams() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean[] getIssuerUniqueID() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean[] getSubjectUniqueID() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean[] getKeyUsage() { - throw new UnsupportedOperationException(); - } - - @Override - public int getBasicConstraints() { - throw new UnsupportedOperationException(); - } - - @Override - public void verify(PublicKey key) { - throw new UnsupportedOperationException(); - } - - @Override - public void verify(PublicKey key, String sigProvider) { - throw new UnsupportedOperationException(); - } - - @Override - public PublicKey getPublicKey() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean equals(Object o) { - if (o == this) { - return true; - } else if (!(o instanceof PemX509Certificate)) { - return false; - } - - PemX509Certificate other = (PemX509Certificate) o; - return content.equals(other.content); - } - - @Override - public int hashCode() { - return content.hashCode(); - } - - @Override - public String toString() { - return content.toString(CharsetUtil.UTF_8); - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/PseudoRandomFunction.java b/handler/src/main/java/io/netty/handler/ssl/PseudoRandomFunction.java deleted file mode 100644 index 0471fbde02..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/PseudoRandomFunction.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; - -import io.netty.util.internal.EmptyArrays; - -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; -import java.security.GeneralSecurityException; -import java.util.Arrays; - -/** - * This pseudorandom function (PRF) takes as input a secret, a seed, and - * an identifying label and produces an output of arbitrary length. - * - * This is used by the TLS RFC to construct/deconstruct an array of bytes into - * composite secrets. - * - * {@link rfc5246} - */ -final class PseudoRandomFunction { - - /** - * Constructor never to be called. - */ - private PseudoRandomFunction() { - } - - /** - * Use a single hash function to expand a secret and seed into an - * arbitrary quantity of output. - * - * P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) + - * HMAC_hash(secret, A(2) + seed) + - * HMAC_hash(secret, A(3) + seed) + ... - * where + indicates concatenation. - * A() is defined as: - * A(0) = seed - * A(i) = HMAC_hash(secret, A(i-1)) - * @param secret The starting secret to use for expansion - * @param label An ascii string without a length byte or trailing null character. - * @param seed The seed of the hash - * @param length The number of bytes to return - * @param algo the hmac algorithm to use - * @return The expanded secrets - * @throws IllegalArgumentException if the algo could not be found. - */ - static byte[] hash(byte[] secret, byte[] label, byte[] seed, int length, String algo) { - checkPositiveOrZero(length, "length"); - try { - Mac hmac = Mac.getInstance(algo); - hmac.init(new SecretKeySpec(secret, algo)); - /* - * P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) + - * HMAC_hash(secret, A(2) + seed) + HMAC_hash(secret, A(3) + seed) + ... - * where + indicates concatenation. A() is defined as: A(0) = seed, A(i) - * = HMAC_hash(secret, A(i-1)) - */ - - int iterations = (int) Math.ceil(length / (double) hmac.getMacLength()); - byte[] expansion = EmptyArrays.EMPTY_BYTES; - byte[] data = concat(label, seed); - byte[] A = data; - for (int i = 0; i < iterations; i++) { - A = hmac.doFinal(A); - expansion = concat(expansion, hmac.doFinal(concat(A, data))); - } - return Arrays.copyOf(expansion, length); - } catch (GeneralSecurityException e) { - throw new IllegalArgumentException("Could not find algo: " + algo, e); - } - } - - private static byte[] concat(byte[] first, byte[] second) { - byte[] result = Arrays.copyOf(first, first.length + second.length); - System.arraycopy(second, 0, result, first.length, second.length); - return result; - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java deleted file mode 100644 index c0825882d0..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java +++ /dev/null @@ -1,312 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.internal.tcnative.CertificateCallback; -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.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Set; - -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLException; -import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509ExtendedTrustManager; -import javax.net.ssl.X509TrustManager; -import javax.security.auth.x500.X500Principal; - -/** - * A client-side {@link SslContext} which uses OpenSSL's SSL/TLS implementation. - *

Instances of this class must be {@link #release() released} or else native memory will leak! - * - *

Instances of this class must not be released before any {@link ReferenceCountedOpenSslEngine} - * which depends upon the instance of this class is released. Otherwise if any method of - * {@link ReferenceCountedOpenSslEngine} is called which uses this class's JNI resources the JVM may crash. - */ -public final class ReferenceCountedOpenSslClientContext extends ReferenceCountedOpenSslContext { - - 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, - X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, - KeyManagerFactory keyManagerFactory, Iterable ciphers, - CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, - String[] protocols, long sessionCacheSize, long sessionTimeout, - boolean enableOcsp, String keyStore, - Map.Entry, Object>... options) throws SSLException { - super(ciphers, cipherFilter, toNegotiator(apn), SSL.SSL_MODE_CLIENT, keyCertChain, - ClientAuth.NONE, protocols, false, enableOcsp, true, options); - boolean success = false; - try { - sessionContext = newSessionContext(this, ctx, engineMap, trustCertCollection, trustManagerFactory, - keyCertChain, key, keyPassword, keyManagerFactory, keyStore, - sessionCacheSize, sessionTimeout); - success = true; - } finally { - if (!success) { - release(); - } - } - } - - @Override - public OpenSslSessionContext sessionContext() { - return sessionContext; - } - - static OpenSslSessionContext newSessionContext(ReferenceCountedOpenSslContext thiz, long ctx, - OpenSslEngineMap engineMap, - X509Certificate[] trustCertCollection, - TrustManagerFactory trustManagerFactory, - X509Certificate[] keyCertChain, PrivateKey key, - String keyPassword, KeyManagerFactory keyManagerFactory, - String keyStore, long sessionCacheSize, long sessionTimeout) - throws SSLException { - if (key == null && keyCertChain != null || key != null && keyCertChain == null) { - throw new IllegalArgumentException( - "Either both keyCertChain and key needs to be null or none of them"); - } - OpenSslKeyMaterialProvider keyMaterialProvider = null; - try { - try { - if (!OpenSsl.supportsKeyManagerFactory()) { - 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) { - char[] keyPasswordChars = keyStorePassword(keyPassword); - KeyStore ks = buildKeyStore(keyCertChain, key, keyPasswordChars, keyStore); - 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); - } - - if (keyMaterialProvider != null) { - OpenSslKeyMaterialManager materialManager = new OpenSslKeyMaterialManager(keyMaterialProvider); - SSLContext.setCertificateCallback(ctx, new OpenSslClientCertificateCallback( - engineMap, materialManager)); - } - } - } catch (Exception e) { - throw new SSLException("failed to set certificate and key", e); - } - - // On the client side we always need to use SSL_CVERIFY_OPTIONAL (which will translate to SSL_VERIFY_PEER) - // to ensure that when the TrustManager throws we will produce the correct alert back to the server. - // - // See: - // - https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_set_verify.html - // - https://github.com/netty/netty/issues/8942 - SSLContext.setVerify(ctx, SSL.SSL_CVERIFY_OPTIONAL, VERIFY_DEPTH); - - try { - if (trustCertCollection != null) { - trustManagerFactory = buildTrustManagerFactory(trustCertCollection, trustManagerFactory, keyStore); - } 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 - - // 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); - context.setSessionCacheEnabled(CLIENT_ENABLE_SESSION_CACHE); - if (sessionCacheSize > 0) { - context.setSessionCacheSize((int) Math.min(sessionCacheSize, Integer.MAX_VALUE)); - } - if (sessionTimeout > 0) { - context.setSessionTimeout((int) Math.min(sessionTimeout, Integer.MAX_VALUE)); - } - - if (CLIENT_ENABLE_SESSION_TICKET) { - context.setTicketKeys(); - } - - keyMaterialProvider = null; - return context; - } finally { - if (keyMaterialProvider != null) { - keyMaterialProvider.destroy(); - } - } - } - - static final class OpenSslClientSessionContext extends OpenSslSessionContext { - OpenSslClientSessionContext(ReferenceCountedOpenSslContext context, OpenSslKeyMaterialProvider provider) { - super(context, provider, SSL.SSL_SESS_CACHE_CLIENT, new OpenSslClientSessionCache(context.engineMap)); - } - } - - private static final class TrustManagerVerifyCallback extends AbstractCertificateVerifier { - private final X509TrustManager manager; - - TrustManagerVerifyCallback(OpenSslEngineMap engineMap, X509TrustManager manager) { - super(engineMap); - this.manager = manager; - } - - @Override - void verify(ReferenceCountedOpenSslEngine engine, X509Certificate[] peerCerts, String auth) - throws Exception { - manager.checkServerTrusted(peerCerts, auth); - } - } - - private static final class ExtendedTrustManagerVerifyCallback extends AbstractCertificateVerifier { - private final X509ExtendedTrustManager manager; - - ExtendedTrustManagerVerifyCallback(OpenSslEngineMap engineMap, X509ExtendedTrustManager manager) { - super(engineMap); - this.manager = manager; - } - - @Override - void verify(ReferenceCountedOpenSslEngine engine, X509Certificate[] peerCerts, String auth) - throws Exception { - manager.checkServerTrusted(peerCerts, auth, engine); - } - } - - private static final class OpenSslClientCertificateCallback implements CertificateCallback { - private final OpenSslEngineMap engineMap; - private final OpenSslKeyMaterialManager keyManagerHolder; - - OpenSslClientCertificateCallback(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); - // May be null if it was destroyed in the meantime. - if (engine == null) { - return; - } - try { - final Set keyTypesSet = supportedClientKeyTypes(keyTypeBytes); - final String[] keyTypes = keyTypesSet.toArray(new String[0]); - final X500Principal[] issuers; - if (asn1DerEncodedPrincipals == null) { - issuers = null; - } else { - issuers = new X500Principal[asn1DerEncodedPrincipals.length]; - for (int i = 0; i < asn1DerEncodedPrincipals.length; i++) { - issuers[i] = new X500Principal(asn1DerEncodedPrincipals[i]); - } - } - keyManagerHolder.setKeyMaterialClientSide(engine, keyTypes, issuers); - } catch (Throwable cause) { - engine.initHandshakeException(cause); - if (cause instanceof Exception) { - throw (Exception) cause; - } - throw new SSLException(cause); - } - } - - /** - * Gets the supported key types for client certificates. - * - * @param clientCertificateTypes {@code ClientCertificateType} values provided by the server. - * See https://www.ietf.org/assignments/tls-parameters/tls-parameters.xml. - * @return supported key types that can be used in {@code X509KeyManager.chooseClientAlias} and - * {@code X509ExtendedKeyManager.chooseEngineClientAlias}. - */ - private static Set supportedClientKeyTypes(byte[] clientCertificateTypes) { - if (clientCertificateTypes == null) { - // Try all of the supported key types. - return SUPPORTED_KEY_TYPES; - } - Set result = new HashSet<>(clientCertificateTypes.length); - for (byte keyTypeCode : clientCertificateTypes) { - String keyType = clientKeyType(keyTypeCode); - if (keyType == null) { - // Unsupported client key type -- ignore - continue; - } - result.add(keyType); - } - return result; - } - - private static String clientKeyType(byte clientCertificateType) { - // See also https://www.ietf.org/assignments/tls-parameters/tls-parameters.xml - switch (clientCertificateType) { - case CertificateCallback.TLS_CT_RSA_SIGN: - return OpenSslKeyMaterialManager.KEY_TYPE_RSA; // RFC rsa_sign - case CertificateCallback.TLS_CT_RSA_FIXED_DH: - return OpenSslKeyMaterialManager.KEY_TYPE_DH_RSA; // RFC rsa_fixed_dh - case CertificateCallback.TLS_CT_ECDSA_SIGN: - return OpenSslKeyMaterialManager.KEY_TYPE_EC; // RFC ecdsa_sign - case CertificateCallback.TLS_CT_RSA_FIXED_ECDH: - return OpenSslKeyMaterialManager.KEY_TYPE_EC_RSA; // RFC rsa_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 deleted file mode 100644 index e77715c71f..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslContext.java +++ /dev/null @@ -1,1072 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.handler.ssl.util.LazyX509Certificate; -import io.netty.internal.tcnative.AsyncSSLPrivateKeyMethod; -import io.netty.internal.tcnative.CertificateVerifier; -import io.netty.internal.tcnative.ResultCallback; -import io.netty.internal.tcnative.SSL; -import io.netty.internal.tcnative.SSLContext; -import io.netty.internal.tcnative.SSLPrivateKeyMethod; -import io.netty.util.AbstractReferenceCounted; -import io.netty.util.ReferenceCounted; -import io.netty.util.ResourceLeakDetector; -import io.netty.util.ResourceLeakDetectorFactory; -import io.netty.util.ResourceLeakTracker; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.FutureListener; -import io.netty.util.internal.EmptyArrays; -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.SystemPropertyUtil; -import io.netty.util.internal.UnstableApi; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -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.X509ExtendedTrustManager; -import javax.net.ssl.X509KeyManager; -import javax.net.ssl.X509TrustManager; -import java.security.PrivateKey; -import java.security.SignatureException; -import java.security.cert.CertPathValidatorException; -import java.security.cert.Certificate; -import java.security.cert.CertificateExpiredException; -import java.security.cert.CertificateNotYetValidException; -import java.security.cert.CertificateRevokedException; -import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Executor; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; - -import static io.netty.handler.ssl.OpenSsl.DEFAULT_CIPHERS; -import static io.netty.handler.ssl.OpenSsl.availableJavaCipherSuites; -import static io.netty.util.internal.ObjectUtil.checkNonEmpty; -import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; -import static java.util.Objects.requireNonNull; - -/** - * An implementation of {@link SslContext} which works with libraries that support the - * OpenSsl C library API. - *

Instances of this class must be {@link #release() released} or else native memory will leak! - * - *

Instances of this class must not be released before any {@link ReferenceCountedOpenSslEngine} - * which depends upon the instance of this class is released. Otherwise if any method of - * {@link ReferenceCountedOpenSslEngine} is called which uses this class's JNI resources the JVM may crash. - */ -public abstract class ReferenceCountedOpenSslContext extends SslContext implements ReferenceCounted { - private static final InternalLogger logger = - InternalLoggerFactory.getInstance(ReferenceCountedOpenSslContext.class); - - private static final int DEFAULT_BIO_NON_APPLICATION_BUFFER_SIZE = Math.max(1, - SystemPropertyUtil.getInt("io.netty.handler.ssl.openssl.bioNonApplicationBufferSize", - 2048)); - // Let's use tasks by default but still allow the user to disable it via system property just in case. - static final boolean USE_TASKS = - SystemPropertyUtil.getBoolean("io.netty.handler.ssl.openssl.useTasks", true); - private static final Integer DH_KEY_LENGTH; - private static final ResourceLeakDetector leakDetector = - ResourceLeakDetectorFactory.instance().newResourceLeakDetector(ReferenceCountedOpenSslContext.class); - - // TODO: Maybe make configurable ? - protected static final int VERIFY_DEPTH = 10; - - static final boolean CLIENT_ENABLE_SESSION_TICKET = - SystemPropertyUtil.getBoolean("jdk.tls.client.enableSessionTicketExtension", false); - - static final boolean CLIENT_ENABLE_SESSION_TICKET_TLSV13 = - SystemPropertyUtil.getBoolean("jdk.tls.client.enableSessionTicketExtension", true); - - static final boolean SERVER_ENABLE_SESSION_TICKET = - SystemPropertyUtil.getBoolean("jdk.tls.server.enableSessionTicketExtension", false); - - static final boolean SERVER_ENABLE_SESSION_TICKET_TLSV13 = - SystemPropertyUtil.getBoolean("jdk.tls.server.enableSessionTicketExtension", true); - - static final boolean SERVER_ENABLE_SESSION_CACHE = - SystemPropertyUtil.getBoolean("io.netty.handler.ssl.openssl.sessionCacheServer", true); - // session caching is disabled by default on the client side due a JDK bug: - // https://mail.openjdk.java.net/pipermail/security-dev/2021-March/024758.html - static final boolean CLIENT_ENABLE_SESSION_CACHE = - SystemPropertyUtil.getBoolean("io.netty.handler.ssl.openssl.sessionCacheClient", false); - - /** - * The OpenSSL SSL_CTX object. - * - * {@link #ctxLock} must be hold while using ctx! - */ - protected long ctx; - private final List unmodifiableCiphers; - private final OpenSslApplicationProtocolNegotiator apn; - private final int mode; - - // Reference Counting - private final ResourceLeakTracker leak; - private final AbstractReferenceCounted refCnt = new AbstractReferenceCounted() { - @Override - public ReferenceCounted touch(Object hint) { - if (leak != null) { - leak.record(hint); - } - - return ReferenceCountedOpenSslContext.this; - } - - @Override - protected void deallocate() { - destroy(); - if (leak != null) { - boolean closed = leak.close(ReferenceCountedOpenSslContext.this); - assert closed; - } - } - }; - - final Certificate[] keyCertChain; - final ClientAuth clientAuth; - final String[] protocols; - final boolean enableOcsp; - final OpenSslEngineMap engineMap = new DefaultOpenSslEngineMap(); - final ReadWriteLock ctxLock = new ReentrantReadWriteLock(); - - private volatile int bioNonApplicationBufferSize = DEFAULT_BIO_NON_APPLICATION_BUFFER_SIZE; - - @SuppressWarnings("deprecation") - static final OpenSslApplicationProtocolNegotiator NONE_PROTOCOL_NEGOTIATOR = - new OpenSslApplicationProtocolNegotiator() { - @Override - public ApplicationProtocolConfig.Protocol protocol() { - return ApplicationProtocolConfig.Protocol.NONE; - } - - @Override - public List protocols() { - return Collections.emptyList(); - } - - @Override - public ApplicationProtocolConfig.SelectorFailureBehavior selectorFailureBehavior() { - return ApplicationProtocolConfig.SelectorFailureBehavior.CHOOSE_MY_LAST_PROTOCOL; - } - - @Override - public ApplicationProtocolConfig.SelectedListenerFailureBehavior selectedListenerFailureBehavior() { - return ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT; - } - }; - - static { - Integer dhLen = null; - - try { - String dhKeySize = SystemPropertyUtil.get("jdk.tls.ephemeralDHKeySize"); - if (dhKeySize != null) { - try { - dhLen = Integer.valueOf(dhKeySize); - } catch (NumberFormatException e) { - logger.debug("ReferenceCountedOpenSslContext supports -Djdk.tls.ephemeralDHKeySize={int}, but got: " - + dhKeySize); - } - } - } catch (Throwable ignore) { - // ignore - } - DH_KEY_LENGTH = dhLen; - } - - final boolean tlsFalseStart; - - ReferenceCountedOpenSslContext(Iterable ciphers, CipherSuiteFilter cipherFilter, - OpenSslApplicationProtocolNegotiator apn, int mode, Certificate[] keyCertChain, - ClientAuth clientAuth, String[] protocols, boolean startTls, boolean enableOcsp, - boolean leakDetection, Map.Entry, Object>... ctxOptions) - throws SSLException { - super(startTls); - - OpenSsl.ensureAvailability(); - - if (enableOcsp && !OpenSsl.isOcspSupported()) { - throw new IllegalStateException("OCSP is not supported."); - } - - if (mode != SSL.SSL_MODE_SERVER && mode != SSL.SSL_MODE_CLIENT) { - throw new IllegalArgumentException("mode most be either SSL.SSL_MODE_SERVER or SSL.SSL_MODE_CLIENT"); - } - - boolean tlsFalseStart = false; - boolean useTasks = USE_TASKS; - OpenSslPrivateKeyMethod privateKeyMethod = null; - OpenSslAsyncPrivateKeyMethod asyncPrivateKeyMethod = null; - - if (ctxOptions != null) { - for (Map.Entry, Object> ctxOpt : ctxOptions) { - SslContextOption option = ctxOpt.getKey(); - - if (option == OpenSslContextOption.TLS_FALSE_START) { - tlsFalseStart = (Boolean) ctxOpt.getValue(); - } else if (option == OpenSslContextOption.USE_TASKS) { - useTasks = (Boolean) ctxOpt.getValue(); - } else if (option == OpenSslContextOption.PRIVATE_KEY_METHOD) { - privateKeyMethod = (OpenSslPrivateKeyMethod) ctxOpt.getValue(); - } else if (option == OpenSslContextOption.ASYNC_PRIVATE_KEY_METHOD) { - asyncPrivateKeyMethod = (OpenSslAsyncPrivateKeyMethod) ctxOpt.getValue(); - } else { - logger.debug("Skipping unsupported " + SslContextOption.class.getSimpleName() - + ": " + ctxOpt.getKey()); - } - } - } - if (privateKeyMethod != null && asyncPrivateKeyMethod != null) { - throw new IllegalArgumentException("You can either only use " - + OpenSslAsyncPrivateKeyMethod.class.getSimpleName() + " or " - + OpenSslPrivateKeyMethod.class.getSimpleName()); - } - - this.tlsFalseStart = tlsFalseStart; - - leak = leakDetection ? leakDetector.track(this) : null; - this.mode = mode; - this.clientAuth = isServer() ? requireNonNull(clientAuth, "clientAuth") : ClientAuth.NONE; - this.protocols = protocols; - this.enableOcsp = enableOcsp; - - this.keyCertChain = keyCertChain == null ? null : keyCertChain.clone(); - - String[] suites = requireNonNull(cipherFilter, "cipherFilter").filterCipherSuites( - ciphers, DEFAULT_CIPHERS, availableJavaCipherSuites()); - // Filter out duplicates. - LinkedHashSet suitesSet = new LinkedHashSet<>(suites.length); - Collections.addAll(suitesSet, suites); - unmodifiableCiphers = new ArrayList<>(suitesSet); - - this.apn = requireNonNull(apn, "apn"); - - // Create a new SSL_CTX and configure it. - boolean success = false; - try { - boolean tlsv13Supported = OpenSsl.isTlsv13Supported(); - - try { - int protocolOpts = SSL.SSL_PROTOCOL_SSLV3 | SSL.SSL_PROTOCOL_TLSV1 | - SSL.SSL_PROTOCOL_TLSV1_1 | SSL.SSL_PROTOCOL_TLSV1_2; - if (tlsv13Supported) { - protocolOpts |= SSL.SSL_PROTOCOL_TLSV1_3; - } - ctx = SSLContext.make(protocolOpts, mode); - } catch (Exception e) { - throw new SSLException("failed to create an SSL_CTX", e); - } - - 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, OpenSsl.isBoringSSL()); - - // Set non TLSv1.3 ciphers. - SSLContext.setCipherSuite(ctx, cipherBuilder.toString(), false); - if (tlsv13Supported) { - // Set TLSv1.3 ciphers. - SSLContext.setCipherSuite(ctx, - OpenSsl.checkTls13Ciphers(logger, 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 and TLSv1.1 by default as these are not considered secure anymore - // and the JDK is doing the same: - // https://www.oracle.com/java/technologies/javase/8u291-relnotes.html - SSL.SSL_OP_NO_TLSv1 | - SSL.SSL_OP_NO_TLSv1_1 | - - SSL.SSL_OP_CIPHER_SERVER_PREFERENCE | - - // 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(...). - // See https://github.com/netty/netty-tcnative/issues/100 - SSLContext.setMode(ctx, SSLContext.getMode(ctx) | SSL.SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); - - if (DH_KEY_LENGTH != null) { - SSLContext.setTmpDHLength(ctx, DH_KEY_LENGTH); - } - - List nextProtoList = apn.protocols(); - /* Set next protocols for next protocol negotiation extension, if specified */ - if (!nextProtoList.isEmpty()) { - String[] appProtocols = nextProtoList.toArray(EmptyArrays.EMPTY_STRINGS); - int selectorBehavior = opensslSelectorFailureBehavior(apn.selectorFailureBehavior()); - - switch (apn.protocol()) { - case NPN: - SSLContext.setNpnProtos(ctx, appProtocols, selectorBehavior); - break; - case ALPN: - SSLContext.setAlpnProtos(ctx, appProtocols, selectorBehavior); - break; - case NPN_AND_ALPN: - SSLContext.setNpnProtos(ctx, appProtocols, selectorBehavior); - SSLContext.setAlpnProtos(ctx, appProtocols, selectorBehavior); - break; - default: - throw new Error(); - } - } - - if (enableOcsp) { - SSLContext.enableOcsp(ctx, isClient()); - } - - SSLContext.setUseTasks(ctx, useTasks); - if (privateKeyMethod != null) { - SSLContext.setPrivateKeyMethod(ctx, new PrivateKeyMethod(engineMap, privateKeyMethod)); - } - if (asyncPrivateKeyMethod != null) { - SSLContext.setPrivateKeyMethod(ctx, new AsyncPrivateKeyMethod(engineMap, asyncPrivateKeyMethod)); - } - // Set the curves. - SSLContext.setCurvesList(ctx, OpenSsl.NAMED_GROUPS); - success = true; - } finally { - if (!success) { - release(); - } - } - } - - private static int opensslSelectorFailureBehavior(ApplicationProtocolConfig.SelectorFailureBehavior behavior) { - switch (behavior) { - case NO_ADVERTISE: - return SSL.SSL_SELECTOR_FAILURE_NO_ADVERTISE; - case CHOOSE_MY_LAST_PROTOCOL: - return SSL.SSL_SELECTOR_FAILURE_CHOOSE_MY_LAST_PROTOCOL; - default: - throw new Error(); - } - } - - @Override - public final List cipherSuites() { - return unmodifiableCiphers; - } - - @Override - public ApplicationProtocolNegotiator applicationProtocolNegotiator() { - return apn; - } - - @Override - public final boolean isClient() { - return mode == SSL.SSL_MODE_CLIENT; - } - - @Override - public final SSLEngine newEngine(ByteBufAllocator alloc, String peerHost, int peerPort) { - return newEngine0(alloc, peerHost, peerPort, true); - } - - @Override - protected final SslHandler newHandler(ByteBufAllocator alloc, boolean startTls) { - return new SslHandler(newEngine0(alloc, null, -1, false), startTls); - } - - @Override - protected final SslHandler newHandler(ByteBufAllocator alloc, String peerHost, int peerPort, boolean startTls) { - 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); - } - - /** - * Returns a new server-side {@link SSLEngine} with the current configuration. - */ - @Override - public final SSLEngine newEngine(ByteBufAllocator alloc) { - return newEngine(alloc, null, -1); - } - - /** - * Returns the pointer to the {@code SSL_CTX} object for this {@link ReferenceCountedOpenSslContext}. - * Be aware that it is freed as soon as the {@link #finalize()} method is called. - * At this point {@code 0} will be returned. - * - * @deprecated this method is considered unsafe as the returned pointer may be released later. Dont use it! - */ - @Deprecated - public final long context() { - return sslCtxPointer(); - } - - /** - * Returns the stats of this context. - * - * @deprecated use {@link #sessionContext#stats()} - */ - @Deprecated - public final OpenSslSessionStats stats() { - return sessionContext().stats(); - } - - /** - * {@deprecated Renegotiation is not supported} - * Specify if remote initiated renegotiation is supported or not. If not supported and the remote side tries - * to initiate a renegotiation a {@link SSLHandshakeException} will be thrown during decoding. - */ - @Deprecated - public void setRejectRemoteInitiatedRenegotiation(boolean rejectRemoteInitiatedRenegotiation) { - if (!rejectRemoteInitiatedRenegotiation) { - throw new UnsupportedOperationException("Renegotiation is not supported"); - } - } - - /** - * {@deprecated Renegotiation is not supported} - * @return {@code true} because renegotiation is not supported. - */ - @Deprecated - public boolean getRejectRemoteInitiatedRenegotiation() { - return true; - } - - /** - * Set the size of the buffer used by the BIO for non-application based writes - * (e.g. handshake, renegotiation, etc...). - */ - public void setBioNonApplicationBufferSize(int bioNonApplicationBufferSize) { - this.bioNonApplicationBufferSize = - checkPositiveOrZero(bioNonApplicationBufferSize, "bioNonApplicationBufferSize"); - } - - /** - * Returns the size of the buffer used by the BIO for non-application based writes - */ - public int getBioNonApplicationBufferSize() { - return bioNonApplicationBufferSize; - } - - /** - * Sets the SSL session ticket keys of this context. - * - * @deprecated use {@link OpenSslSessionContext#setTicketKeys(byte[])} - */ - @Deprecated - public final void setTicketKeys(byte[] keys) { - sessionContext().setTicketKeys(keys); - } - - @Override - public abstract OpenSslSessionContext sessionContext(); - - /** - * Returns the pointer to the {@code SSL_CTX} object for this {@link ReferenceCountedOpenSslContext}. - * Be aware that it is freed as soon as the {@link #release()} method is called. - * At this point {@code 0} will be returned. - * - * @deprecated this method is considered unsafe as the returned pointer may be released later. Dont use it! - */ - @Deprecated - public final long sslCtxPointer() { - Lock readerLock = ctxLock.readLock(); - readerLock.lock(); - try { - return SSLContext.getSslCtx(ctx); - } finally { - readerLock.unlock(); - } - } - - /** - * Set the {@link OpenSslPrivateKeyMethod} to use. This allows to offload private-key operations - * if needed. - * - * This method is currently only supported when {@code BoringSSL} is used. - * - * @param method method to use. - * @deprecated use {@link SslContextBuilder#option(SslContextOption, Object)} with - * {@link OpenSslContextOption#PRIVATE_KEY_METHOD}. - */ - @Deprecated - @UnstableApi - public final void setPrivateKeyMethod(OpenSslPrivateKeyMethod method) { - requireNonNull(method, "method"); - Lock writerLock = ctxLock.writeLock(); - writerLock.lock(); - try { - SSLContext.setPrivateKeyMethod(ctx, new PrivateKeyMethod(engineMap, method)); - } finally { - writerLock.unlock(); - } - } - - /** - * @deprecated use {@link SslContextBuilder#option(SslContextOption, Object)} with - * {@link OpenSslContextOption#USE_TASKS}. - */ - @Deprecated - public final void setUseTasks(boolean useTasks) { - Lock writerLock = ctxLock.writeLock(); - writerLock.lock(); - try { - SSLContext.setUseTasks(ctx, useTasks); - } finally { - writerLock.unlock(); - } - } - - // IMPORTANT: This method must only be called from either the constructor or the finalizer as a user MUST never - // get access to an OpenSslSessionContext after this method was called to prevent the user from - // producing a segfault. - private void destroy() { - Lock writerLock = ctxLock.writeLock(); - writerLock.lock(); - try { - if (ctx != 0) { - if (enableOcsp) { - SSLContext.disableOcsp(ctx); - } - - SSLContext.free(ctx); - ctx = 0; - - OpenSslSessionContext context = sessionContext(); - if (context != null) { - context.destroy(); - } - } - } finally { - writerLock.unlock(); - } - } - - protected static X509Certificate[] certificates(byte[][] chain) { - X509Certificate[] peerCerts = new X509Certificate[chain.length]; - for (int i = 0; i < peerCerts.length; i++) { - peerCerts[i] = new LazyX509Certificate(chain[i]); - } - return peerCerts; - } - - protected static X509TrustManager chooseTrustManager(TrustManager[] managers) { - for (TrustManager m : managers) { - if (m instanceof X509TrustManager) { - return OpenSslX509TrustManagerWrapper.wrapIfNeeded((X509TrustManager) m); - } - } - throw new IllegalStateException("no X509TrustManager found"); - } - - protected static X509KeyManager chooseX509KeyManager(KeyManager[] kms) { - for (KeyManager km : kms) { - if (km instanceof X509KeyManager) { - return (X509KeyManager) km; - } - } - throw new IllegalStateException("no X509KeyManager found"); - } - - /** - * Translate a {@link ApplicationProtocolConfig} object to a - * {@link OpenSslApplicationProtocolNegotiator} object. - * - * @param config The configuration which defines the translation - * @return The results of the translation - */ - @SuppressWarnings("deprecation") - static OpenSslApplicationProtocolNegotiator toNegotiator(ApplicationProtocolConfig config) { - if (config == null) { - return NONE_PROTOCOL_NEGOTIATOR; - } - - switch (config.protocol()) { - case NONE: - return NONE_PROTOCOL_NEGOTIATOR; - case ALPN: - case NPN: - case NPN_AND_ALPN: - switch (config.selectedListenerFailureBehavior()) { - case CHOOSE_MY_LAST_PROTOCOL: - case ACCEPT: - switch (config.selectorFailureBehavior()) { - case CHOOSE_MY_LAST_PROTOCOL: - case NO_ADVERTISE: - return new OpenSslDefaultApplicationProtocolNegotiator( - config); - default: - throw new UnsupportedOperationException( - new StringBuilder("OpenSSL provider does not support ") - .append(config.selectorFailureBehavior()) - .append(" behavior").toString()); - } - default: - throw new UnsupportedOperationException( - new StringBuilder("OpenSSL provider does not support ") - .append(config.selectedListenerFailureBehavior()) - .append(" behavior").toString()); - } - default: - throw new Error(); - } - } - - static boolean useExtendedTrustManager(X509TrustManager trustManager) { - return trustManager instanceof X509ExtendedTrustManager; - } - - @Override - public final int refCnt() { - return refCnt.refCnt(); - } - - @Override - public final ReferenceCounted retain() { - refCnt.retain(); - return this; - } - - @Override - public final ReferenceCounted retain(int increment) { - refCnt.retain(increment); - return this; - } - - @Override - public final ReferenceCounted touch() { - refCnt.touch(); - return this; - } - - @Override - public final ReferenceCounted touch(Object hint) { - refCnt.touch(hint); - return this; - } - - @Override - public final boolean release() { - return refCnt.release(); - } - - @Override - public final boolean release(int decrement) { - return refCnt.release(decrement); - } - - abstract static class AbstractCertificateVerifier extends CertificateVerifier { - private final OpenSslEngineMap engineMap; - - AbstractCertificateVerifier(OpenSslEngineMap engineMap) { - this.engineMap = engineMap; - } - - @Override - public final int verify(long ssl, byte[][] chain, String auth) { - final ReferenceCountedOpenSslEngine engine = engineMap.get(ssl); - if (engine == null) { - // May be null if it was destroyed in the meantime. - return CertificateVerifier.X509_V_ERR_UNSPECIFIED; - } - X509Certificate[] peerCerts = certificates(chain); - try { - verify(engine, peerCerts, auth); - return CertificateVerifier.X509_V_OK; - } catch (Throwable cause) { - logger.debug("verification of certificate failed", cause); - engine.initHandshakeException(cause); - - // Try to extract the correct error code that should be used. - if (cause instanceof OpenSslCertificateException) { - // This will never return a negative error code as its validated when constructing the - // OpenSslCertificateException. - return ((OpenSslCertificateException) cause).errorCode(); - } - if (cause instanceof CertificateExpiredException) { - return CertificateVerifier.X509_V_ERR_CERT_HAS_EXPIRED; - } - if (cause instanceof CertificateNotYetValidException) { - return CertificateVerifier.X509_V_ERR_CERT_NOT_YET_VALID; - } - if (cause instanceof CertificateRevokedException) { - return CertificateVerifier.X509_V_ERR_CERT_REVOKED; - } - - // The X509TrustManagerImpl uses a Validator which wraps a CertPathValidatorException into - // an CertificateException. So we need to handle the wrapped CertPathValidatorException to be - // able to send the correct alert. - Throwable wrapped = cause.getCause(); - while (wrapped != null) { - if (wrapped instanceof CertPathValidatorException) { - CertPathValidatorException ex = (CertPathValidatorException) wrapped; - CertPathValidatorException.Reason reason = ex.getReason(); - if (reason == CertPathValidatorException.BasicReason.EXPIRED) { - return CertificateVerifier.X509_V_ERR_CERT_HAS_EXPIRED; - } - if (reason == CertPathValidatorException.BasicReason.NOT_YET_VALID) { - return CertificateVerifier.X509_V_ERR_CERT_NOT_YET_VALID; - } - if (reason == CertPathValidatorException.BasicReason.REVOKED) { - return CertificateVerifier.X509_V_ERR_CERT_REVOKED; - } - } - wrapped = wrapped.getCause(); - } - - // Could not detect a specific error code to use, so fallback to a default code. - return CertificateVerifier.X509_V_ERR_UNSPECIFIED; - } - } - - abstract void verify(ReferenceCountedOpenSslEngine engine, X509Certificate[] peerCerts, - String auth) throws Exception; - } - - private static final class DefaultOpenSslEngineMap implements OpenSslEngineMap { - private final Map engines = new ConcurrentHashMap<>(); - - @Override - public ReferenceCountedOpenSslEngine remove(long ssl) { - return engines.remove(ssl); - } - - @Override - public void add(ReferenceCountedOpenSslEngine engine) { - engines.put(engine.sslPointer(), engine); - } - - @Override - public ReferenceCountedOpenSslEngine get(long ssl) { - return engines.get(ssl); - } - } - - static void setKeyMaterial(long ctx, X509Certificate[] keyCertChain, PrivateKey key, String keyPassword) - throws SSLException { - /* Load the certificate file and private key. */ - long keyBio = 0; - long keyCertChainBio = 0; - long keyCertChainBio2 = 0; - PemEncoded encoded = null; - try { - // Only encode one time - encoded = PemX509Certificate.toPEM(ByteBufAllocator.DEFAULT, true, keyCertChain); - keyCertChainBio = toBIO(ByteBufAllocator.DEFAULT, encoded.retain()); - keyCertChainBio2 = toBIO(ByteBufAllocator.DEFAULT, encoded.retain()); - - if (key != null) { - keyBio = toBIO(ByteBufAllocator.DEFAULT, key); - } - - SSLContext.setCertificateBio( - ctx, keyCertChainBio, keyBio, - keyPassword == null ? StringUtil.EMPTY_STRING : keyPassword); - // We may have more then one cert in the chain so add all of them now. - SSLContext.setCertificateChainBio(ctx, keyCertChainBio2, true); - } catch (SSLException e) { - throw e; - } catch (Exception e) { - throw new SSLException("failed to set certificate and key", e); - } finally { - freeBio(keyBio); - freeBio(keyCertChainBio); - freeBio(keyCertChainBio2); - if (encoded != null) { - encoded.release(); - } - } - } - - static void freeBio(long bio) { - if (bio != 0) { - SSL.freeBIO(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(ByteBufAllocator allocator, PrivateKey key) throws Exception { - if (key == null) { - return 0; - } - - PemEncoded pem = PemPrivateKey.toPEM(allocator, true, key); - try { - return toBIO(allocator, pem.retain()); - } finally { - pem.release(); - } - } - - /** - * 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(ByteBufAllocator allocator, X509Certificate... certChain) throws Exception { - if (certChain == null) { - return 0; - } - - checkNonEmpty(certChain, "certChain"); - - PemEncoded pem = PemX509Certificate.toPEM(allocator, true, certChain); - try { - return toBIO(allocator, pem.retain()); - } finally { - pem.release(); - } - } - - static long toBIO(ByteBufAllocator allocator, PemEncoded pem) throws Exception { - try { - // We can turn direct buffers straight into BIOs. No need to - // make a yet another copy. - ByteBuf content = pem.content(); - - if (content.isDirect()) { - return newBIO(content.retainedSlice()); - } - - ByteBuf buffer = allocator.directBuffer(content.readableBytes()); - try { - buffer.writeBytes(content, content.readerIndex(), content.readableBytes()); - return newBIO(buffer.retainedSlice()); - } finally { - try { - // If the contents of the ByteBuf is sensitive (e.g. a PrivateKey) we - // need to zero out the bytes of the copy before we're releasing it. - if (pem.isSensitive()) { - SslUtils.zeroout(buffer); - } - } finally { - buffer.release(); - } - } - } finally { - pem.release(); - } - } - - private static long newBIO(ByteBuf buffer) throws Exception { - try { - long bio = SSL.newMemBIO(); - int readable = buffer.readableBytes(); - if (SSL.bioWrite(bio, OpenSsl.memoryAddress(buffer) + buffer.readerIndex(), readable) != readable) { - SSL.freeBIO(bio); - throw new IllegalStateException("Could not write data to memory BIO"); - } - return bio; - } finally { - 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) { - if (factory instanceof OpenSslX509KeyManagerFactory) { - return ((OpenSslX509KeyManagerFactory) factory).newProvider(); - } - - if (factory instanceof OpenSslCachingX509KeyManagerFactory) { - // The user explicit used OpenSslCachingX509KeyManagerFactory which signals us that its fine to cache. - return ((OpenSslCachingX509KeyManagerFactory) factory).newProvider(password); - } - // We can not be sure if the material may change at runtime so we will not cache it. - return new OpenSslKeyMaterialProvider(chooseX509KeyManager(factory.getKeyManagers()), password); - } - - private static final class PrivateKeyMethod implements SSLPrivateKeyMethod { - - private final OpenSslEngineMap engineMap; - private final OpenSslPrivateKeyMethod keyMethod; - PrivateKeyMethod(OpenSslEngineMap engineMap, OpenSslPrivateKeyMethod keyMethod) { - this.engineMap = engineMap; - this.keyMethod = keyMethod; - } - - private ReferenceCountedOpenSslEngine retrieveEngine(long ssl) throws SSLException { - ReferenceCountedOpenSslEngine engine = engineMap.get(ssl); - if (engine == null) { - throw new SSLException("Could not find a " + - StringUtil.simpleClassName(ReferenceCountedOpenSslEngine.class) + " for sslPointer " + ssl); - } - return engine; - } - - @Override - public byte[] sign(long ssl, int signatureAlgorithm, byte[] digest) throws Exception { - ReferenceCountedOpenSslEngine engine = retrieveEngine(ssl); - try { - return verifyResult(keyMethod.sign(engine, signatureAlgorithm, digest)); - } catch (Exception e) { - engine.initHandshakeException(e); - throw e; - } - } - - @Override - public byte[] decrypt(long ssl, byte[] input) throws Exception { - ReferenceCountedOpenSslEngine engine = retrieveEngine(ssl); - try { - return verifyResult(keyMethod.decrypt(engine, input)); - } catch (Exception e) { - engine.initHandshakeException(e); - throw e; - } - } - } - - private static final class AsyncPrivateKeyMethod implements AsyncSSLPrivateKeyMethod { - - private final OpenSslEngineMap engineMap; - private final OpenSslAsyncPrivateKeyMethod keyMethod; - - AsyncPrivateKeyMethod(OpenSslEngineMap engineMap, OpenSslAsyncPrivateKeyMethod keyMethod) { - this.engineMap = engineMap; - this.keyMethod = keyMethod; - } - - private ReferenceCountedOpenSslEngine retrieveEngine(long ssl) throws SSLException { - ReferenceCountedOpenSslEngine engine = engineMap.get(ssl); - if (engine == null) { - throw new SSLException("Could not find a " + - StringUtil.simpleClassName(ReferenceCountedOpenSslEngine.class) + " for sslPointer " + ssl); - } - return engine; - } - - @Override - public void sign(long ssl, int signatureAlgorithm, byte[] bytes, ResultCallback resultCallback) { - try { - ReferenceCountedOpenSslEngine engine = retrieveEngine(ssl); - keyMethod.sign(engine, signatureAlgorithm, bytes) - .addListener(new ResultCallbackListener(engine, ssl, resultCallback)); - } catch (SSLException e) { - resultCallback.onError(ssl, e); - } - } - - @Override - public void decrypt(long ssl, byte[] bytes, ResultCallback resultCallback) { - try { - ReferenceCountedOpenSslEngine engine = retrieveEngine(ssl); - keyMethod.decrypt(engine, bytes) - .addListener(new ResultCallbackListener(engine, ssl, resultCallback)); - } catch (SSLException e) { - resultCallback.onError(ssl, e); - } - } - - private static final class ResultCallbackListener implements FutureListener { - private final ReferenceCountedOpenSslEngine engine; - private final long ssl; - private final ResultCallback resultCallback; - - ResultCallbackListener(ReferenceCountedOpenSslEngine engine, long ssl, - ResultCallback resultCallback) { - this.engine = engine; - this.ssl = ssl; - this.resultCallback = resultCallback; - } - - @Override - public void operationComplete(Future future) { - Throwable cause = future.cause(); - if (cause == null) { - try { - byte[] result = verifyResult(future.getNow()); - resultCallback.onSuccess(ssl, result); - return; - } catch (SignatureException e) { - cause = e; - engine.initHandshakeException(e); - } - } - resultCallback.onError(ssl, cause); - } - } - } - - private static byte[] verifyResult(byte[] result) throws SignatureException { - if (result == null) { - throw new SignatureException(); - } - return result; - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java deleted file mode 100644 index d81c36468d..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java +++ /dev/null @@ -1,2635 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.handler.ssl.util.LazyJavaxX509Certificate; -import io.netty.handler.ssl.util.LazyX509Certificate; -import io.netty.internal.tcnative.AsyncTask; -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; -import io.netty.util.ResourceLeakTracker; -import io.netty.util.internal.EmptyArrays; -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; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import javax.crypto.spec.SecretKeySpec; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLEngineResult; -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.SSLSessionBindingEvent; -import javax.net.ssl.SSLSessionBindingListener; -import javax.security.cert.X509Certificate; -import java.nio.ByteBuffer; -import java.nio.ReadOnlyBufferException; -import java.security.Principal; -import java.security.cert.Certificate; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.locks.Lock; - -import static io.netty.handler.ssl.OpenSsl.memoryAddress; -import static io.netty.handler.ssl.SslUtils.SSL_RECORD_HEADER_LENGTH; -import static io.netty.util.internal.ObjectUtil.checkNotNullArrayParam; -import static io.netty.util.internal.ObjectUtil.checkNotNullWithIAE; -import static java.lang.Integer.MAX_VALUE; -import static java.lang.Math.min; -import static java.util.Objects.requireNonNull; -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; -import static javax.net.ssl.SSLEngineResult.Status.BUFFER_OVERFLOW; -import static javax.net.ssl.SSLEngineResult.Status.BUFFER_UNDERFLOW; -import static javax.net.ssl.SSLEngineResult.Status.CLOSED; -import static javax.net.ssl.SSLEngineResult.Status.OK; - -/** - * Implements a {@link SSLEngine} using - * OpenSSL BIO abstractions. - *

Instances of this class must be {@link #release() released} or else native memory will leak! - * - *

Instances of this class must be released before the {@link ReferenceCountedOpenSslContext} - * the instance depends upon are released. Otherwise if any method of this class is called which uses the - * the {@link ReferenceCountedOpenSslContext} JNI resources the JVM may crash. - */ -public class ReferenceCountedOpenSslEngine extends SSLEngine implements ReferenceCounted, ApplicationProtocolAccessor { - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(ReferenceCountedOpenSslEngine.class); - - private static final ResourceLeakDetector leakDetector = - ResourceLeakDetectorFactory.instance().newResourceLeakDetector(ReferenceCountedOpenSslEngine.class); - private static final int OPENSSL_OP_NO_PROTOCOL_INDEX_SSLV2 = 0; - private static final int OPENSSL_OP_NO_PROTOCOL_INDEX_SSLV3 = 1; - 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_3 - }; - - /** - * Depends upon tcnative ... only use if tcnative is available! - */ - static final int MAX_PLAINTEXT_LENGTH = SSL.SSL_MAX_PLAINTEXT_LENGTH; - /** - * Depends upon tcnative ... only use if tcnative is available! - */ - static final int MAX_RECORD_SIZE = SSL.SSL_MAX_RECORD_LENGTH; - - 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); - private static final SSLEngineResult NEED_WRAP_CLOSED = new SSLEngineResult(CLOSED, NEED_WRAP, 0, 0); - private static final SSLEngineResult CLOSED_NOT_HANDSHAKING = new SSLEngineResult(CLOSED, NOT_HANDSHAKING, 0, 0); - - // OpenSSL state - private long ssl; - private long networkBIO; - - private enum HandshakeState { - /** - * Not started yet. - */ - NOT_STARTED, - /** - * Started via unwrap/wrap. - */ - STARTED_IMPLICITLY, - /** - * Started via {@link #beginHandshake()}. - */ - STARTED_EXPLICITLY, - /** - * Handshake is finished. - */ - FINISHED - } - - private HandshakeState handshakeState = HandshakeState.NOT_STARTED; - private boolean receivedShutdown; - private volatile boolean destroyed; - private volatile String applicationProtocol; - private volatile boolean needTask; - private String[] explicitlyEnabledProtocols; - private boolean sessionSet; - - // Reference Counting - private final ResourceLeakTracker leak; - private final AbstractReferenceCounted refCnt = new AbstractReferenceCounted() { - @Override - public ReferenceCounted touch(Object hint) { - if (leak != null) { - leak.record(hint); - } - - return ReferenceCountedOpenSslEngine.this; - } - - @Override - protected void deallocate() { - shutdown(); - if (leak != null) { - boolean closed = leak.close(ReferenceCountedOpenSslEngine.this); - assert closed; - } - parentContext.release(); - } - }; - - private volatile ClientAuth clientAuth = ClientAuth.NONE; - - // Updated once a new handshake is started and so the SSLSession reused. - private volatile long lastAccessed = -1; - - private String endPointIdentificationAlgorithm; - // Store as object as AlgorithmConstraints only exists since java 7. - private Object algorithmConstraints; - private List sniHostNames; - - // Mark as volatile as accessed by checkSniHostnameMatch(...) and also not specify the SNIMatcher type to allow us - // using it with java7. - private volatile Collection matchers; - - // SSL Engine status variables - private boolean isInboundDone; - private boolean outboundClosed; - - final boolean jdkCompatibilityMode; - private final boolean clientMode; - final ByteBufAllocator alloc; - private final OpenSslEngineMap engineMap; - private final OpenSslApplicationProtocolNegotiator apn; - private final ReferenceCountedOpenSslContext parentContext; - private final OpenSslSession session; - private final ByteBuffer[] singleSrcBuffer = new ByteBuffer[1]; - private final ByteBuffer[] singleDstBuffer = new ByteBuffer[1]; - private final boolean enableOcsp; - private int maxWrapOverhead; - private int maxWrapBufferSize; - private Throwable pendingException; - - /** - * Create a new instance. - * @param context Reference count release responsibility is not transferred! The callee still owns this object. - * @param alloc The allocator to use. - * @param peerHost The peer host name. - * @param peerPort The peer port. - * @param jdkCompatibilityMode {@code true} to behave like described in - * https://docs.oracle.com/javase/7/docs/api/javax/net/ssl/SSLEngine.html. - * {@code false} allows for partial and/or multiple packets to be process in a single - * wrap or unwrap call. - * @param leakDetection {@code true} to enable leak detection of this object. - */ - ReferenceCountedOpenSslEngine(ReferenceCountedOpenSslContext context, final ByteBufAllocator alloc, String peerHost, - int peerPort, boolean jdkCompatibilityMode, boolean leakDetection) { - super(peerHost, peerPort); - OpenSsl.ensureAvailability(); - this.alloc = requireNonNull(alloc, "alloc"); - apn = (OpenSslApplicationProtocolNegotiator) context.applicationProtocolNegotiator(); - clientMode = context.isClient(); - session = new ExtendedOpenSslSession(new DefaultOpenSslSession(context.sessionContext())) { - private String[] peerSupportedSignatureAlgorithms; - private List requestedServerNames; - - @Override - public List getRequestedServerNames() { - 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 { - Set algorithmList = new LinkedHashSet<>(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 - 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); - } - }; - engineMap = context.engineMap; - enableOcsp = context.enableOcsp; - if (!context.sessionContext().useKeyManager()) { - session.setLocalCertificate(context.keyCertChain); - } - - this.jdkCompatibilityMode = jdkCompatibilityMode; - Lock readerLock = context.ctxLock.readLock(); - readerLock.lock(); - final long finalSsl; - try { - finalSsl = SSL.newSSL(context.ctx, !context.isClient()); - } finally { - readerLock.unlock(); - } - synchronized (this) { - ssl = finalSsl; - try { - networkBIO = SSL.bioNewByteBuffer(ssl, context.getBioNonApplicationBufferSize()); - - // Set the client auth mode, this needs to be done via setClientAuth(...) method so we actually call the - // needed JNI methods. - setClientAuth(clientMode ? ClientAuth.NONE : context.clientAuth); - - if (context.protocols != null) { - setEnabledProtocols0(context.protocols, true); - } else { - this.explicitlyEnabledProtocols = getEnabledProtocols(); - } - - // Use SNI if peerHost was specified and a valid hostname - // See https://github.com/netty/netty/issues/4746 - if (clientMode && SslUtils.isValidHostNameForSNI(peerHost)) { - SSL.setTlsExtHostName(ssl, peerHost); - sniHostNames = Collections.singletonList(peerHost); - } - - if (enableOcsp) { - SSL.enableOcsp(ssl); - } - - if (!jdkCompatibilityMode) { - SSL.setMode(ssl, SSL.getMode(ssl) | SSL.SSL_MODE_ENABLE_PARTIAL_WRITE); - } - - if (isProtocolEnabled(SSL.getOptions(ssl), SSL.SSL_OP_NO_TLSv1_3, SslProtocols.TLS_v1_3)) { - final boolean enableTickets = clientMode ? - ReferenceCountedOpenSslContext.CLIENT_ENABLE_SESSION_TICKET_TLSV13 : - ReferenceCountedOpenSslContext.SERVER_ENABLE_SESSION_TICKET_TLSV13; - if (enableTickets) { - // We should enable session tickets for stateless resumption when TLSv1.3 is enabled. This - // is also done by OpenJDK and without this session resumption does not work at all with - // BoringSSL when TLSv1.3 is used as BoringSSL only supports stateless resumption with TLSv1.3: - // - // See: - // - https://bugs.openjdk.java.net/browse/JDK-8223922 - // - https://boringssl.googlesource.com/boringssl/+/refs/heads/master/ssl/tls13_server.cc#104 - SSL.clearOptions(ssl, SSL.SSL_OP_NO_TICKET); - } - } - - if (OpenSsl.isBoringSSL() && clientMode) { - // If in client-mode and BoringSSL let's allow to renegotiate once as the server may use this - // for client auth. - // - // See https://github.com/netty/netty/issues/11529 - try { - SSL.setRenegotiateMode(ssl, SSL.SSL_RENEGOTIATE_ONCE); - } catch (Exception e) { - throw new IllegalStateException(e); - } - } - // setMode may impact the overhead. - calculateMaxWrapOverhead(); - } catch (Throwable cause) { - // 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. - shutdown(); - throw cause; - } - } - - // Now that everything looks good and we're going to successfully return the - // object so we need to retain a reference to the parent context. - parentContext = context; - parentContext.retain(); - - // Only create the leak after everything else was executed and so ensure we don't produce a false-positive for - // the ResourceLeakDetector. - leak = leakDetection ? leakDetector.track(this) : null; - } - - final synchronized String[] authMethods() { - if (isDestroyed()) { - return EmptyArrays.EMPTY_STRINGS; - } - return SSL.authenticationMethods(ssl); - } - - final boolean setKeyMaterial(OpenSslKeyMaterial keyMaterial) throws Exception { - synchronized (this) { - if (isDestroyed()) { - return false; - } - SSL.setKeyMaterial(ssl, keyMaterial.certificateChainAddress(), keyMaterial.privateKeyAddress()); - } - session.setLocalCertificate(keyMaterial.certificateChain()); - return true; - } - - final synchronized SecretKeySpec masterKey() { - if (isDestroyed()) { - return null; - } - return new SecretKeySpec(SSL.getMasterKey(ssl), "AES"); - } - - synchronized boolean isSessionReused() { - if (isDestroyed()) { - return false; - } - return SSL.isSessionReused(ssl); - } - - /** - * Sets the OCSP response. - */ - @UnstableApi - public void setOcspResponse(byte[] response) { - if (!enableOcsp) { - throw new IllegalStateException("OCSP stapling is not enabled"); - } - - if (clientMode) { - throw new IllegalStateException("Not a server SSLEngine"); - } - - synchronized (this) { - if (!isDestroyed()) { - SSL.setOcspResponse(ssl, response); - } - } - } - - /** - * Returns the OCSP response or {@code null} if the server didn't provide a stapled OCSP response. - */ - @UnstableApi - public byte[] getOcspResponse() { - if (!enableOcsp) { - throw new IllegalStateException("OCSP stapling is not enabled"); - } - - if (!clientMode) { - throw new IllegalStateException("Not a client SSLEngine"); - } - - synchronized (this) { - if (isDestroyed()) { - return EmptyArrays.EMPTY_BYTES; - } - return SSL.getOcspResponse(ssl); - } - } - - @Override - public final int refCnt() { - return refCnt.refCnt(); - } - - @Override - public final ReferenceCounted retain() { - refCnt.retain(); - return this; - } - - @Override - public final ReferenceCounted retain(int increment) { - refCnt.retain(increment); - return this; - } - - @Override - public final ReferenceCounted touch() { - refCnt.touch(); - return this; - } - - @Override - public final ReferenceCounted touch(Object hint) { - refCnt.touch(hint); - return this; - } - - @Override - public final boolean release() { - return refCnt.release(); - } - - @Override - public final boolean release(int decrement) { - return refCnt.release(decrement); - } - - // These method will override the method defined by Java 8u251 and later. As we may compile with an earlier - // java8 version we don't use @Override annotations here. - public String getApplicationProtocol() { - return applicationProtocol; - } - - // These method will override the method defined by Java 8u251 and later. As we may compile with an earlier - // java8 version we don't use @Override annotations here. - public String getHandshakeApplicationProtocol() { - return applicationProtocol; - } - - @Override - public final synchronized SSLSession getHandshakeSession() { - // Javadocs state return value should be: - // null if this instance is not currently handshaking, or if the current handshake has not - // progressed far enough to create a basic SSLSession. Otherwise, this method returns the - // SSLSession currently being negotiated. - switch(handshakeState) { - case NOT_STARTED: - case FINISHED: - return null; - default: - return session; - } - } - - /** - * Returns the pointer to the {@code SSL} object for this {@link ReferenceCountedOpenSslEngine}. - * Be aware that it is freed as soon as the {@link #release()} or {@link #shutdown()} methods are called. - * At this point {@code 0} will be returned. - */ - public final synchronized long sslPointer() { - return ssl; - } - - /** - * Destroys this engine. - */ - public final synchronized void shutdown() { - if (!destroyed) { - destroyed = true; - engineMap.remove(ssl); - SSL.freeSSL(ssl); - ssl = networkBIO = 0; - - isInboundDone = outboundClosed = true; - } - - // On shutdown clear all errors - SSL.clearError(); - } - - /** - * Write plaintext data to the OpenSSL internal BIO - * - * Calling this function with src.remaining == 0 is undefined. - */ - private int writePlaintextData(final ByteBuffer src, int len) { - final int pos = src.position(); - final int limit = src.limit(); - final int sslWrote; - - if (src.isDirect()) { - sslWrote = SSL.writeToSSL(ssl, bufferAddress(src) + pos, len); - if (sslWrote > 0) { - src.position(pos + sslWrote); - } - } else { - ByteBuf buf = alloc.directBuffer(len); - try { - src.limit(pos + len); - - buf.setBytes(0, src); - src.limit(limit); - - sslWrote = SSL.writeToSSL(ssl, memoryAddress(buf), len); - if (sslWrote > 0) { - src.position(pos + sslWrote); - } else { - src.position(pos); - } - } finally { - buf.release(); - } - } - return sslWrote; - } - - /** - * Write encrypted data to the OpenSSL network BIO. - */ - private ByteBuf writeEncryptedData(final ByteBuffer src, int len) throws SSLException { - final int pos = src.position(); - if (src.isDirect()) { - SSL.bioSetByteBuffer(networkBIO, bufferAddress(src) + pos, len, false); - } else { - final ByteBuf buf = alloc.directBuffer(len); - try { - final int limit = src.limit(); - src.limit(pos + len); - buf.writeBytes(src); - // Restore the original position and limit because we don't want to consume from `src`. - src.position(pos); - src.limit(limit); - - SSL.bioSetByteBuffer(networkBIO, memoryAddress(buf), len, false); - return buf; - } catch (Throwable cause) { - buf.release(); - throw cause; - } - } - return null; - } - - /** - * Read plaintext data from the OpenSSL internal BIO - */ - private int readPlaintextData(final ByteBuffer dst) throws SSLException { - final int sslRead; - final int pos = dst.position(); - if (dst.isDirect()) { - sslRead = SSL.readFromSSL(ssl, bufferAddress(dst) + pos, dst.limit() - pos); - if (sslRead > 0) { - dst.position(pos + sslRead); - } - } else { - final int limit = dst.limit(); - final int len = min(maxEncryptedPacketLength0(), limit - pos); - final ByteBuf buf = alloc.directBuffer(len); - try { - sslRead = SSL.readFromSSL(ssl, memoryAddress(buf), len); - if (sslRead > 0) { - dst.limit(pos + sslRead); - buf.getBytes(buf.readerIndex(), dst); - dst.limit(limit); - } - } finally { - buf.release(); - } - } - - return sslRead; - } - - /** - * Visible only for testing! - */ - final synchronized int maxWrapOverhead() { - return maxWrapOverhead; - } - - /** - * Visible only for testing! - */ - final synchronized int maxEncryptedPacketLength() { - return maxEncryptedPacketLength0(); - } - - /** - * This method is intentionally not synchronized, only use if you know you are in the EventLoop - * thread and visibility on {@link #maxWrapOverhead} is achieved via other synchronized blocks. - */ - final int maxEncryptedPacketLength0() { - return maxWrapOverhead + MAX_PLAINTEXT_LENGTH; - } - - /** - * This method is intentionally not synchronized, only use if you know you are in the EventLoop - * thread and visibility on {@link #maxWrapBufferSize} and {@link #maxWrapOverhead} is achieved - * via other synchronized blocks. - */ - final int calculateMaxLengthForWrap(int plaintextLength, int numComponents) { - return (int) min(maxWrapBufferSize, plaintextLength + (long) maxWrapOverhead * numComponents); - } - - final synchronized int sslPending() { - return sslPending0(); - } - - /** - * It is assumed this method is called in a synchronized block (or the constructor)! - */ - private void calculateMaxWrapOverhead() { - maxWrapOverhead = SSL.getMaxWrapOverhead(ssl); - - // maxWrapBufferSize must be set after maxWrapOverhead because there is a dependency on this value. - // If jdkCompatibility mode is off we allow enough space to encrypt 16 buffers at a time. This could be - // configurable in the future if necessary. - maxWrapBufferSize = jdkCompatibilityMode ? maxEncryptedPacketLength0() : maxEncryptedPacketLength0() << 4; - } - - private int sslPending0() { - // OpenSSL has a limitation where if you call SSL_pending before the handshake is complete OpenSSL will throw a - // "called a function you should not call" error. Using the TLS_method instead of SSLv23_method may solve this - // issue but this API is only available in 1.1.0+ [1]. - // [1] https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_new.html - return handshakeState != HandshakeState.FINISHED ? 0 : SSL.sslPending(ssl); - } - - private boolean isBytesAvailableEnoughForWrap(int bytesAvailable, int plaintextLength, int numComponents) { - return bytesAvailable - (long) maxWrapOverhead * numComponents >= plaintextLength; - } - - @Override - public final SSLEngineResult wrap( - final ByteBuffer[] srcs, int offset, final int length, final ByteBuffer dst) throws SSLException { - // Throw required runtime exceptions - checkNotNullWithIAE(srcs, "srcs"); - checkNotNullWithIAE(dst, "dst"); - - if (offset >= srcs.length || offset + length > srcs.length) { - throw new IndexOutOfBoundsException( - "offset: " + offset + ", length: " + length + - " (expected: offset <= offset + length <= srcs.length (" + srcs.length + "))"); - } - - if (dst.isReadOnly()) { - throw new ReadOnlyBufferException(); - } - - synchronized (this) { - if (isOutboundDone()) { - // All drained in the outbound buffer - return isInboundDone() || isDestroyed() ? CLOSED_NOT_HANDSHAKING : NEED_UNWRAP_CLOSED; - } - - int bytesProduced = 0; - ByteBuf bioReadCopyBuf = null; - try { - // Setup the BIO buffer so that we directly write the encryption results into dst. - if (dst.isDirect()) { - SSL.bioSetByteBuffer(networkBIO, bufferAddress(dst) + dst.position(), dst.remaining(), - true); - } else { - bioReadCopyBuf = alloc.directBuffer(dst.remaining()); - SSL.bioSetByteBuffer(networkBIO, memoryAddress(bioReadCopyBuf), bioReadCopyBuf.writableBytes(), - true); - } - - int bioLengthBefore = SSL.bioLengthByteBuffer(networkBIO); - - // 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); - if (bytesProduced <= 0) { - return newResultMayFinishHandshake(NOT_HANDSHAKING, 0, 0); - } - // It is possible when the outbound was closed there was not enough room in the non-application - // buffers to hold the close_notify. We should keep trying to close until we consume all the data - // OpenSSL can give us. - if (!doSSLShutdown()) { - return newResultMayFinishHandshake(NOT_HANDSHAKING, 0, bytesProduced); - } - bytesProduced = bioLengthBefore - SSL.bioLengthByteBuffer(networkBIO); - return newResultMayFinishHandshake(NEED_WRAP, 0, bytesProduced); - } - - // Flush any data that may be implicitly generated by OpenSSL (handshake, close, etc..). - SSLEngineResult.HandshakeStatus status = NOT_HANDSHAKING; - // Prepare OpenSSL to work in server mode and receive handshake - if (handshakeState != HandshakeState.FINISHED) { - if (handshakeState != HandshakeState.STARTED_EXPLICITLY) { - // Update accepted so we know we triggered the handshake via wrap - handshakeState = HandshakeState.STARTED_IMPLICITLY; - } - - // Flush any data that may have been written implicitly during the handshake by OpenSSL. - bytesProduced = SSL.bioFlushByteBuffer(networkBIO); - - if (pendingException != null) { - // TODO(scott): It is possible that when the handshake failed there was not enough room in the - // non-application buffers to hold the alert. We should get all the data before progressing on. - // However I'm not aware of a way to do this with the OpenSSL APIs. - // See https://github.com/netty/netty/issues/6385. - - // We produced / consumed some data during the handshake, signal back to the caller. - // If there is a handshake exception and we have produced data, we should send the data before - // we allow handshake() to throw the handshake exception. - // - // When the user calls wrap() again we will propagate the handshake error back to the user as - // soon as there is no more data to was produced (as part of an alert etc). - if (bytesProduced > 0) { - return newResult(NEED_WRAP, 0, bytesProduced); - } - // Nothing was produced see if there is a handshakeException that needs to be propagated - // to the caller by calling handshakeException() which will return the right HandshakeStatus - // if it can "recover" from the exception for now. - return newResult(handshakeException(), 0, 0); - } - - status = handshake(); - - // Handshake may have generated more data, for example if the internal SSL buffer is small - // 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 - // SSL buffers. - return newResult(mayFinishHandshake(status != FINISHED ? - bytesProduced == bioLengthBefore ? NEED_WRAP : - getHandshakeStatus(SSL.bioLengthNonApplication(networkBIO)) : FINISHED), - 0, bytesProduced); - } - - if (status == NEED_UNWRAP) { - // Signal if the outbound is done or not. - return isOutboundDone() ? NEED_UNWRAP_CLOSED : NEED_UNWRAP_OK; - } - - // Explicit use outboundClosed and not outboundClosed() as we want to drain any bytes that are - // still present. - if (outboundClosed) { - bytesProduced = SSL.bioFlushByteBuffer(networkBIO); - return newResultMayFinishHandshake(status, 0, bytesProduced); - } - } - - final int endOffset = offset + length; - if (jdkCompatibilityMode) { - int srcsLen = 0; - for (int i = offset; i < endOffset; ++i) { - final ByteBuffer src = srcs[i]; - if (src == null) { - throw new IllegalArgumentException("srcs[" + i + "] is null"); - } - if (srcsLen == MAX_PLAINTEXT_LENGTH) { - continue; - } - - srcsLen += src.remaining(); - if (srcsLen > MAX_PLAINTEXT_LENGTH || srcsLen < 0) { - // If srcLen > MAX_PLAINTEXT_LENGTH or secLen < 0 just set it to MAX_PLAINTEXT_LENGTH. - // This also help us to guard against overflow. - // We not break out here as we still need to check for null entries in srcs[]. - srcsLen = MAX_PLAINTEXT_LENGTH; - } - } - - // jdkCompatibilityMode will only produce a single TLS packet, and we don't aggregate src buffers, - // so we always fix the number of buffers to 1 when checking if the dst buffer is large enough. - if (!isBytesAvailableEnoughForWrap(dst.remaining(), srcsLen, 1)) { - return new SSLEngineResult(BUFFER_OVERFLOW, getHandshakeStatus(), 0, 0); - } - } - - // There was no pending data in the network BIO -- encrypt any application data - int bytesConsumed = 0; - assert bytesProduced == 0; - - // Flush any data that may have been written implicitly by OpenSSL in case a shutdown/alert occurs. - bytesProduced = SSL.bioFlushByteBuffer(networkBIO); - - if (bytesProduced > 0) { - return newResultMayFinishHandshake(status, bytesConsumed, bytesProduced); - } - // There was a pending exception that we just delayed because there was something to produce left. - // Throw it now and shutdown the engine. - if (pendingException != null) { - Throwable error = pendingException; - pendingException = null; - shutdown(); - // Throw a new exception wrapping the pending exception, so the stacktrace is meaningful and - // contains all the details. - throw new SSLException(error); - } - - for (; offset < endOffset; ++offset) { - final ByteBuffer src = srcs[offset]; - final int remaining = src.remaining(); - if (remaining == 0) { - continue; - } - - final int bytesWritten; - if (jdkCompatibilityMode) { - // Write plaintext application data to the SSL engine. We don't have to worry about checking - // if there is enough space if jdkCompatibilityMode because we only wrap at most - // MAX_PLAINTEXT_LENGTH and we loop over the input before hand and check if there is space. - bytesWritten = writePlaintextData(src, min(remaining, MAX_PLAINTEXT_LENGTH - bytesConsumed)); - } else { - // OpenSSL's SSL_write keeps state between calls. We should make sure the amount we attempt to - // write is guaranteed to succeed so we don't have to worry about keeping state consistent - // between calls. - final int availableCapacityForWrap = dst.remaining() - bytesProduced - maxWrapOverhead; - if (availableCapacityForWrap <= 0) { - return new SSLEngineResult(BUFFER_OVERFLOW, getHandshakeStatus(), bytesConsumed, - bytesProduced); - } - bytesWritten = writePlaintextData(src, min(remaining, availableCapacityForWrap)); - } - - // Determine how much encrypted data was generated. - // - // Even if SSL_write doesn't consume any application data it is possible that OpenSSL will - // produce non-application data into the BIO. For example session tickets.... - // See https://github.com/netty/netty/issues/10041 - final int pendingNow = SSL.bioLengthByteBuffer(networkBIO); - bytesProduced += bioLengthBefore - pendingNow; - bioLengthBefore = pendingNow; - - if (bytesWritten > 0) { - bytesConsumed += bytesWritten; - - if (jdkCompatibilityMode || bytesProduced == dst.remaining()) { - return newResultMayFinishHandshake(status, bytesConsumed, bytesProduced); - } - } else { - int sslError = SSL.getError(ssl, bytesWritten); - if (sslError == SSL.SSL_ERROR_ZERO_RETURN) { - // This means the connection was shutdown correctly, close inbound and outbound - if (!receivedShutdown) { - closeAll(); - - bytesProduced += bioLengthBefore - SSL.bioLengthByteBuffer(networkBIO); - - // 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 SSL buffers. - SSLEngineResult.HandshakeStatus hs = mayFinishHandshake( - status != FINISHED ? bytesProduced == dst.remaining() ? NEED_WRAP - : getHandshakeStatus(SSL.bioLengthNonApplication(networkBIO)) - : FINISHED); - return newResult(hs, bytesConsumed, bytesProduced); - } - - return newResult(NOT_HANDSHAKING, bytesConsumed, bytesProduced); - } else if (sslError == SSL.SSL_ERROR_WANT_READ) { - // If there is no pending data to read from BIO we should go back to event loop and try - // to read more data [1]. It is also possible that event loop will detect the socket has - // been closed. [1] https://www.openssl.org/docs/manmaster/ssl/SSL_write.html - return newResult(NEED_UNWRAP, bytesConsumed, bytesProduced); - } else if (sslError == SSL.SSL_ERROR_WANT_WRITE) { - // SSL_ERROR_WANT_WRITE typically means that the underlying transport is not writable - // and we should set the "want write" flag on the selector and try again when the - // underlying transport is writable [1]. However we are not directly writing to the - // underlying transport and instead writing to a BIO buffer. The OpenSsl documentation - // says we should do the following [1]: - // - // "When using a buffering BIO, like a BIO pair, data must be written into or retrieved - // out of the BIO before being able to continue." - // - // In practice this means the destination buffer doesn't have enough space for OpenSSL - // to write encrypted data to. This is an OVERFLOW condition. - // [1] https://www.openssl.org/docs/manmaster/ssl/SSL_write.html - if (bytesProduced > 0) { - // If we produced something we should report this back and let the user call - // wrap again. - return newResult(NEED_WRAP, bytesConsumed, bytesProduced); - } - return newResult(BUFFER_OVERFLOW, status, bytesConsumed, bytesProduced); - } else if (sslError == SSL.SSL_ERROR_WANT_X509_LOOKUP || - sslError == SSL.SSL_ERROR_WANT_CERTIFICATE_VERIFY || - sslError == SSL.SSL_ERROR_WANT_PRIVATE_KEY_OPERATION) { - - return newResult(NEED_TASK, bytesConsumed, bytesProduced); - } else { - // Everything else is considered as error - throw shutdownWithError("SSL_write", sslError); - } - } - } - return newResultMayFinishHandshake(status, bytesConsumed, bytesProduced); - } finally { - SSL.bioClearByteBuffer(networkBIO); - if (bioReadCopyBuf == null) { - dst.position(dst.position() + bytesProduced); - } else { - assert bioReadCopyBuf.readableBytes() <= dst.remaining() : "The destination buffer " + dst + - " didn't have enough remaining space to hold the encrypted content in " + bioReadCopyBuf; - dst.put(bioReadCopyBuf.internalNioBuffer(bioReadCopyBuf.readerIndex(), bytesProduced)); - bioReadCopyBuf.release(); - } - } - } - } - - private SSLEngineResult newResult(SSLEngineResult.HandshakeStatus hs, int bytesConsumed, int bytesProduced) { - return newResult(OK, hs, bytesConsumed, bytesProduced); - } - - private SSLEngineResult newResult(SSLEngineResult.Status status, SSLEngineResult.HandshakeStatus hs, - int bytesConsumed, int bytesProduced) { - // If isOutboundDone, then the data from the network BIO - // was the close_notify message and all was consumed we are not required to wait - // for the receipt the peer's close_notify message -- shutdown. - if (isOutboundDone()) { - if (isInboundDone()) { - // If the inbound was done as well, we need to ensure we return NOT_HANDSHAKING to signal we are done. - hs = NOT_HANDSHAKING; - - // As the inbound and the outbound is done we can shutdown the engine now. - shutdown(); - } - 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); - } - - private SSLEngineResult newResultMayFinishHandshake(SSLEngineResult.HandshakeStatus hs, - int bytesConsumed, int bytesProduced) throws SSLException { - return newResult(mayFinishHandshake(hs, bytesConsumed, bytesProduced), bytesConsumed, bytesProduced); - } - - private SSLEngineResult newResultMayFinishHandshake(SSLEngineResult.Status status, - SSLEngineResult.HandshakeStatus hs, - int bytesConsumed, int bytesProduced) throws SSLException { - return newResult(status, mayFinishHandshake(hs, bytesConsumed, bytesProduced), bytesConsumed, bytesProduced); - } - - /** - * Log the error, shutdown the engine and throw an exception. - */ - private SSLException shutdownWithError(String operations, int sslError) { - return shutdownWithError(operations, sslError, SSL.getLastErrorNumber()); - } - - 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, error, errorString); - } - - // There was an internal error -- shutdown - shutdown(); - if (handshakeState == HandshakeState.FINISHED) { - return new SSLException(errorString); - } - - SSLHandshakeException exception = new SSLHandshakeException(errorString); - // If we have a handshakeException stored already we should include it as well to help the user debug things. - if (pendingException != null) { - exception.initCause(pendingException); - pendingException = null; - } - return exception; - } - - private SSLEngineResult handleUnwrapException(int bytesConsumed, int bytesProduced, SSLException e) - throws SSLException { - int lastError = SSL.getLastErrorNumber(); - if (lastError != 0) { - return sslReadErrorResult(SSL.SSL_ERROR_SSL, lastError, bytesConsumed, - bytesProduced); - } - throw e; - } - - public final SSLEngineResult unwrap( - final ByteBuffer[] srcs, int srcsOffset, final int srcsLength, - final ByteBuffer[] dsts, int dstsOffset, final int dstsLength) throws SSLException { - - // Throw required runtime exceptions - checkNotNullWithIAE(srcs, "srcs"); - if (srcsOffset >= srcs.length - || srcsOffset + srcsLength > srcs.length) { - throw new IndexOutOfBoundsException( - "offset: " + srcsOffset + ", length: " + srcsLength + - " (expected: offset <= offset + length <= srcs.length (" + srcs.length + "))"); - } - checkNotNullWithIAE(dsts, "dsts"); - if (dstsOffset >= dsts.length || dstsOffset + dstsLength > dsts.length) { - throw new IndexOutOfBoundsException( - "offset: " + dstsOffset + ", length: " + dstsLength + - " (expected: offset <= offset + length <= dsts.length (" + dsts.length + "))"); - } - long capacity = 0; - final int dstsEndOffset = dstsOffset + dstsLength; - for (int i = dstsOffset; i < dstsEndOffset; i ++) { - ByteBuffer dst = checkNotNullArrayParam(dsts[i], i, "dsts"); - if (dst.isReadOnly()) { - throw new ReadOnlyBufferException(); - } - capacity += dst.remaining(); - } - - final int srcsEndOffset = srcsOffset + srcsLength; - long len = 0; - for (int i = srcsOffset; i < srcsEndOffset; i++) { - ByteBuffer src = checkNotNullArrayParam(srcs[i], i, "srcs"); - len += src.remaining(); - } - - synchronized (this) { - if (isInboundDone()) { - return isOutboundDone() || isDestroyed() ? CLOSED_NOT_HANDSHAKING : NEED_WRAP_CLOSED; - } - - SSLEngineResult.HandshakeStatus status = NOT_HANDSHAKING; - // Prepare OpenSSL to work in server mode and receive handshake - if (handshakeState != HandshakeState.FINISHED) { - if (handshakeState != HandshakeState.STARTED_EXPLICITLY) { - // Update accepted so we know we triggered the handshake via wrap - handshakeState = HandshakeState.STARTED_IMPLICITLY; - } - - status = handshake(); - - if (status == NEED_TASK) { - return newResult(status, 0, 0); - } - - if (status == NEED_WRAP) { - return NEED_WRAP_OK; - } - // Check if the inbound is considered to be closed if so let us try to wrap again. - if (isInboundDone) { - return NEED_WRAP_CLOSED; - } - } - - int sslPending = sslPending0(); - int packetLength; - // The JDK implies that only a single SSL packet should be processed per unwrap call [1]. If we are in - // JDK compatibility mode then we should honor this, but if not we just wrap as much as possible. If there - // are multiple records or partial records this may reduce thrashing events through the pipeline. - // [1] https://docs.oracle.com/javase/7/docs/api/javax/net/ssl/SSLEngine.html - if (jdkCompatibilityMode) { - if (len < SSL_RECORD_HEADER_LENGTH) { - return newResultMayFinishHandshake(BUFFER_UNDERFLOW, status, 0, 0); - } - - packetLength = SslUtils.getEncryptedPacketLength(srcs, srcsOffset); - if (packetLength == SslUtils.NOT_ENCRYPTED) { - throw new NotSslRecordException("not an SSL/TLS record"); - } - - final int packetLengthDataOnly = packetLength - SSL_RECORD_HEADER_LENGTH; - if (packetLengthDataOnly > capacity) { - // Not enough space in the destination buffer so signal the caller that the buffer needs to be - // increased. - if (packetLengthDataOnly > MAX_RECORD_SIZE) { - // The packet length MUST NOT exceed 2^14 [1]. However we do accommodate more data to support - // legacy use cases which may violate this condition (e.g. OpenJDK's SslEngineImpl). If the max - // length is exceeded we fail fast here to avoid an infinite loop due to the fact that we - // won't allocate a buffer large enough. - // [1] https://tools.ietf.org/html/rfc5246#section-6.2.1 - throw new SSLException("Illegal packet length: " + packetLengthDataOnly + " > " + - session.getApplicationBufferSize()); - } else { - session.tryExpandApplicationBufferSize(packetLengthDataOnly); - } - return newResultMayFinishHandshake(BUFFER_OVERFLOW, status, 0, 0); - } - - if (len < packetLength) { - // We either don't have enough data to read the packet length or not enough for reading the whole - // packet. - return newResultMayFinishHandshake(BUFFER_UNDERFLOW, status, 0, 0); - } - } else if (len == 0 && sslPending <= 0) { - return newResultMayFinishHandshake(BUFFER_UNDERFLOW, status, 0, 0); - } else if (capacity == 0) { - return newResultMayFinishHandshake(BUFFER_OVERFLOW, status, 0, 0); - } else { - packetLength = (int) min(MAX_VALUE, len); - } - - // This must always be the case when we reached here as if not we returned BUFFER_UNDERFLOW. - assert srcsOffset < srcsEndOffset; - - // This must always be the case if we reached here. - assert capacity > 0; - - // Number of produced bytes - int bytesProduced = 0; - int bytesConsumed = 0; - try { - srcLoop: - for (;;) { - ByteBuffer src = srcs[srcsOffset]; - int remaining = src.remaining(); - final ByteBuf bioWriteCopyBuf; - int pendingEncryptedBytes; - if (remaining == 0) { - if (sslPending <= 0) { - // We must skip empty buffers as BIO_write will return 0 if asked to write something - // with length 0. - if (++srcsOffset >= srcsEndOffset) { - break; - } - continue; - } else { - bioWriteCopyBuf = null; - pendingEncryptedBytes = SSL.bioLengthByteBuffer(networkBIO); - } - } else { - // Write more encrypted data into the BIO. Ensure we only read one packet at a time as - // stated in the SSLEngine javadocs. - pendingEncryptedBytes = min(packetLength, remaining); - try { - bioWriteCopyBuf = writeEncryptedData(src, pendingEncryptedBytes); - } catch (SSLException e) { - // Ensure we correctly handle the error stack. - return handleUnwrapException(bytesConsumed, bytesProduced, e); - } - } - try { - for (;;) { - ByteBuffer dst = dsts[dstsOffset]; - if (!dst.hasRemaining()) { - // No space left in the destination buffer, skip it. - if (++dstsOffset >= dstsEndOffset) { - break srcLoop; - } - continue; - } - - int bytesRead; - try { - bytesRead = readPlaintextData(dst); - } catch (SSLException e) { - // Ensure we correctly handle the error stack. - return handleUnwrapException(bytesConsumed, bytesProduced, e); - } - // We are directly using the ByteBuffer memory for the write, and so we only know what has - // been consumed after we let SSL decrypt the data. At this point we should update the - // number of bytes consumed, update the ByteBuffer position, and release temp ByteBuf. - int localBytesConsumed = pendingEncryptedBytes - SSL.bioLengthByteBuffer(networkBIO); - bytesConsumed += localBytesConsumed; - packetLength -= localBytesConsumed; - pendingEncryptedBytes -= localBytesConsumed; - src.position(src.position() + localBytesConsumed); - - if (bytesRead > 0) { - bytesProduced += bytesRead; - - if (!dst.hasRemaining()) { - sslPending = sslPending0(); - // Move to the next dst buffer as this one is full. - if (++dstsOffset >= dstsEndOffset) { - return sslPending > 0 ? - newResult(BUFFER_OVERFLOW, status, bytesConsumed, bytesProduced) : - newResultMayFinishHandshake(isInboundDone() ? CLOSED : OK, status, - bytesConsumed, bytesProduced); - } - } else if (packetLength == 0 || jdkCompatibilityMode) { - // We either consumed all data or we are in jdkCompatibilityMode and have consumed - // a single TLS packet and should stop consuming until this method is called again. - break srcLoop; - } - } else { - int sslError = SSL.getError(ssl, bytesRead); - if (sslError == SSL.SSL_ERROR_WANT_READ || sslError == SSL.SSL_ERROR_WANT_WRITE) { - // break to the outer loop as we want to read more data which means we need to - // write more to the BIO. - break; - } else if (sslError == SSL.SSL_ERROR_ZERO_RETURN) { - // This means the connection was shutdown correctly, close inbound and outbound - if (!receivedShutdown) { - closeAll(); - } - return newResultMayFinishHandshake(isInboundDone() ? CLOSED : OK, status, - bytesConsumed, bytesProduced); - } else if (sslError == SSL.SSL_ERROR_WANT_X509_LOOKUP || - sslError == SSL.SSL_ERROR_WANT_CERTIFICATE_VERIFY || - sslError == SSL.SSL_ERROR_WANT_PRIVATE_KEY_OPERATION) { - return newResult(isInboundDone() ? CLOSED : OK, - NEED_TASK, bytesConsumed, bytesProduced); - } else { - return sslReadErrorResult(sslError, SSL.getLastErrorNumber(), bytesConsumed, - bytesProduced); - } - } - } - - if (++srcsOffset >= srcsEndOffset) { - break; - } - } finally { - if (bioWriteCopyBuf != null) { - bioWriteCopyBuf.release(); - } - } - } - } finally { - SSL.bioClearByteBuffer(networkBIO); - rejectRemoteInitiatedRenegotiation(); - } - - // Check to see if we received a close_notify message from the peer. - if (!receivedShutdown && (SSL.getShutdown(ssl) & SSL.SSL_RECEIVED_SHUTDOWN) == SSL.SSL_RECEIVED_SHUTDOWN) { - closeAll(); - } - - return newResultMayFinishHandshake(isInboundDone() ? CLOSED : OK, status, bytesConsumed, bytesProduced); - } - } - - private boolean needWrapAgain(int stackError) { - // 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. - // See https://github.com/netty/netty/issues/3900 - if (SSL.bioLengthNonApplication(networkBIO) > 0) { - // 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. - String message = SSL.getErrorString(stackError); - SSLException exception = handshakeState == HandshakeState.FINISHED ? - new SSLException(message) : new SSLHandshakeException(message); - if (pendingException == null) { - pendingException = exception; - } else { - pendingException.addSuppressed(exception); - } - // 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 true; - } - return false; - } - - private SSLEngineResult sslReadErrorResult(int error, int stackError, int bytesConsumed, int bytesProduced) - throws SSLException { - if (needWrapAgain(stackError)) { - // There is something that needs to be send to the remote peer before we can teardown. - // This is most likely some alert. - return new SSLEngineResult(OK, NEED_WRAP, bytesConsumed, bytesProduced); - } - throw shutdownWithError("SSL_read", error, stackError); - } - - private void closeAll() throws SSLException { - receivedShutdown = true; - closeOutbound(); - closeInbound(); - } - - 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() && (!clientMode && SSL.getHandshakeCount(ssl) > 1 || - // Let's allow to renegotiate once for client auth. - clientMode && SSL.getHandshakeCount(ssl) > 2) && - // 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. - !SslProtocols.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(); - throw new SSLHandshakeException("remote-initiated renegotiation not allowed"); - } - } - - public final SSLEngineResult unwrap(final ByteBuffer[] srcs, final ByteBuffer[] dsts) throws SSLException { - return unwrap(srcs, 0, srcs.length, dsts, 0, dsts.length); - } - - private ByteBuffer[] singleSrcBuffer(ByteBuffer src) { - singleSrcBuffer[0] = src; - return singleSrcBuffer; - } - - private void resetSingleSrcBuffer() { - singleSrcBuffer[0] = null; - } - - private ByteBuffer[] singleDstBuffer(ByteBuffer src) { - singleDstBuffer[0] = src; - return singleDstBuffer; - } - - private void resetSingleDstBuffer() { - singleDstBuffer[0] = null; - } - - @Override - public final synchronized SSLEngineResult unwrap( - final ByteBuffer src, final ByteBuffer[] dsts, final int offset, final int length) throws SSLException { - try { - return unwrap(singleSrcBuffer(src), 0, 1, dsts, offset, length); - } finally { - resetSingleSrcBuffer(); - } - } - - @Override - public final synchronized SSLEngineResult wrap(ByteBuffer src, ByteBuffer dst) throws SSLException { - try { - return wrap(singleSrcBuffer(src), dst); - } finally { - resetSingleSrcBuffer(); - } - } - - @Override - public final synchronized SSLEngineResult unwrap(ByteBuffer src, ByteBuffer dst) throws SSLException { - try { - return unwrap(singleSrcBuffer(src), singleDstBuffer(dst)); - } finally { - resetSingleSrcBuffer(); - resetSingleDstBuffer(); - } - } - - @Override - public final synchronized SSLEngineResult unwrap(ByteBuffer src, ByteBuffer[] dsts) throws SSLException { - try { - return unwrap(singleSrcBuffer(src), dsts); - } finally { - resetSingleSrcBuffer(); - } - } - - private class TaskDecorator implements Runnable { - protected final R task; - TaskDecorator(R task) { - this.task = task; - } - - @Override - public void run() { - if (isDestroyed()) { - // The engine was destroyed in the meantime, just return. - return; - } - try { - task.run(); - } finally { - // The task was run, reset needTask to false so getHandshakeStatus() returns the correct value. - needTask = false; - } - } - } - - private final class AsyncTaskDecorator extends TaskDecorator implements AsyncRunnable { - AsyncTaskDecorator(AsyncTask task) { - super(task); - } - - @Override - public void run(Runnable runnable) { - if (isDestroyed()) { - // The engine was destroyed in the meantime, just return. - runnable.run(); - return; - } - task.runAsync(() -> { - // The task was run, reset needTask to false so getHandshakeStatus() returns the correct value. - // This needs to be done before we run the completion runnable, since that might - // query the handshake status. - needTask = false; - runnable.run(); - }); - } - } - - @Override - public final synchronized Runnable getDelegatedTask() { - if (isDestroyed()) { - return null; - } - final Runnable task = SSL.getTask(ssl); - if (task == null) { - return null; - } - if (task instanceof AsyncTask) { - return new AsyncTaskDecorator((AsyncTask) task); - } - return new TaskDecorator<>(task); - } - - @Override - public final synchronized void closeInbound() throws SSLException { - if (isInboundDone) { - return; - } - - isInboundDone = true; - - if (isOutboundDone()) { - // Only call shutdown if there is no outbound data pending. - // See https://github.com/netty/netty/issues/6167 - shutdown(); - } - - if (handshakeState != HandshakeState.NOT_STARTED && !receivedShutdown) { - throw new SSLException( - "Inbound closed before receiving peer's close_notify: possible truncation attack?"); - } - } - - @Override - public final synchronized boolean isInboundDone() { - return isInboundDone; - } - - @Override - public final synchronized void closeOutbound() { - if (outboundClosed) { - return; - } - - outboundClosed = true; - - if (handshakeState != HandshakeState.NOT_STARTED && !isDestroyed()) { - int mode = SSL.getShutdown(ssl); - if ((mode & SSL.SSL_SENT_SHUTDOWN) != SSL.SSL_SENT_SHUTDOWN) { - doSSLShutdown(); - } - } else { - // engine closing before initial handshake - shutdown(); - } - } - - /** - * Attempt to call {@link SSL#shutdownSSL(long)}. - * @return {@code false} if the call to {@link SSL#shutdownSSL(long)} was not attempted or returned an error. - */ - private boolean doSSLShutdown() { - if (SSL.isInInit(ssl) != 0) { - // Only try to call SSL_shutdown if we are not in the init state anymore. - // Otherwise we will see 'error:140E0197:SSL routines:SSL_shutdown:shutdown while in init' in our logs. - // - // See also https://hg.nginx.org/nginx/rev/062c189fee20 - return false; - } - int err = SSL.shutdownSSL(ssl); - if (err < 0) { - int sslErr = SSL.getError(ssl, err); - if (sslErr == SSL.SSL_ERROR_SYSCALL || sslErr == SSL.SSL_ERROR_SSL) { - if (logger.isDebugEnabled()) { - int error = SSL.getLastErrorNumber(); - logger.debug("SSL_shutdown failed: OpenSSL error: {} {}", error, SSL.getErrorString(error)); - } - // There was an internal error -- shutdown - shutdown(); - return false; - } - SSL.clearError(); - } - return true; - } - - @Override - public final synchronized boolean isOutboundDone() { - // Check if there is anything left in the outbound buffer. - // We need to ensure we only call SSL.pendingWrittenBytesInBIO(...) if the engine was not destroyed yet. - return outboundClosed && (networkBIO == 0 || SSL.bioLengthNonApplication(networkBIO) == 0); - } - - @Override - public final String[] getSupportedCipherSuites() { - return OpenSsl.AVAILABLE_CIPHER_SUITES.toArray(new String[0]); - } - - @Override - public final String[] getEnabledCipherSuites() { - final String[] extraCiphers; - final String[] enabled; - final boolean tls13Enabled; - synchronized (this) { - if (!isDestroyed()) { - enabled = SSL.getCiphers(ssl); - int opts = SSL.getOptions(ssl); - if (isProtocolEnabled(opts, SSL.SSL_OP_NO_TLSv1_3, SslProtocols.TLS_v1_3)) { - extraCiphers = OpenSsl.EXTRA_SUPPORTED_TLS_1_3_CIPHERS; - tls13Enabled = true; - } else { - extraCiphers = EmptyArrays.EMPTY_STRINGS; - tls13Enabled = false; - } - } else { - return EmptyArrays.EMPTY_STRINGS; - } - } - if (enabled == null) { - return EmptyArrays.EMPTY_STRINGS; - } else { - Set enabledSet = new LinkedHashSet<>(enabled.length + extraCiphers.length); - synchronized (this) { - for (int i = 0; i < enabled.length; i++) { - String mapped = toJavaCipherSuite(enabled[i]); - final String cipher = mapped == null ? enabled[i] : mapped; - if ((!tls13Enabled || !OpenSsl.isTlsv13Supported()) && SslUtils.isTLSv13Cipher(cipher)) { - continue; - } - enabledSet.add(cipher); - } - Collections.addAll(enabledSet, extraCiphers); - } - return enabledSet.toArray(new String[0]); - } - } - - @Override - public final void setEnabledCipherSuites(String[] cipherSuites) { - requireNonNull(cipherSuites, "cipherSuites"); - - final StringBuilder buf = new StringBuilder(); - final StringBuilder bufTLSv13 = new StringBuilder(); - - CipherSuiteConverter.convertToCipherStrings(Arrays.asList(cipherSuites), buf, bufTLSv13, OpenSsl.isBoringSSL()); - 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()) { - try { - // Set non TLSv1.3 ciphers. - SSL.setCipherSuites(ssl, cipherSuiteSpec, false); - if (OpenSsl.isTlsv13Supported()) { - // Set TLSv1.3 ciphers. - SSL.setCipherSuites(ssl, OpenSsl.checkTls13Ciphers(logger, cipherSuiteSpecTLSv13), true); - } - - // We also need to update the enabled protocols to ensure we disable the protocol if there are - // no compatible ciphers left. - Set protocols = new HashSet(explicitlyEnabledProtocols.length); - Collections.addAll(protocols, explicitlyEnabledProtocols); - - // We have no ciphers that are compatible with none-TLSv1.3, let us explicit disable all other - // protocols. - if (cipherSuiteSpec.isEmpty()) { - protocols.remove(SslProtocols.TLS_v1); - protocols.remove(SslProtocols.TLS_v1_1); - protocols.remove(SslProtocols.TLS_v1_2); - protocols.remove(SslProtocols.SSL_v3); - protocols.remove(SslProtocols.SSL_v2); - protocols.remove(SslProtocols.SSL_v2_HELLO); - } - // We have no ciphers that are compatible with TLSv1.3, let us explicit disable it. - if (cipherSuiteSpecTLSv13.isEmpty()) { - protocols.remove(SslProtocols.TLS_v1_3); - } - // Update the protocols but not cache the value. We only cache when we call it from the user - // code or when we construct the engine. - setEnabledProtocols0(protocols.toArray(EmptyArrays.EMPTY_STRINGS), false); - } catch (Exception e) { - throw new IllegalStateException("failed to enable cipher suites: " + cipherSuiteSpec, e); - } - } else { - throw new IllegalStateException("failed to enable cipher suites: " + cipherSuiteSpec); - } - } - } - - @Override - public final String[] getSupportedProtocols() { - return OpenSsl.SUPPORTED_PROTOCOLS_SET.toArray(new String[0]); - } - - @Override - public final String[] getEnabledProtocols() { - List enabled = new ArrayList<>(6); - // Seems like there is no way to explicit disable SSLv2Hello in openssl so it is always enabled - enabled.add(SslProtocols.SSL_v2_HELLO); - - int opts; - synchronized (this) { - if (!isDestroyed()) { - opts = SSL.getOptions(ssl); - } else { - return enabled.toArray(new String[0]); - } - } - if (isProtocolEnabled(opts, SSL.SSL_OP_NO_TLSv1, SslProtocols.TLS_v1)) { - enabled.add(SslProtocols.TLS_v1); - } - if (isProtocolEnabled(opts, SSL.SSL_OP_NO_TLSv1_1, SslProtocols.TLS_v1_1)) { - enabled.add(SslProtocols.TLS_v1_1); - } - if (isProtocolEnabled(opts, SSL.SSL_OP_NO_TLSv1_2, SslProtocols.TLS_v1_2)) { - enabled.add(SslProtocols.TLS_v1_2); - } - if (isProtocolEnabled(opts, SSL.SSL_OP_NO_TLSv1_3, SslProtocols.TLS_v1_3)) { - enabled.add(SslProtocols.TLS_v1_3); - } - if (isProtocolEnabled(opts, SSL.SSL_OP_NO_SSLv2, SslProtocols.SSL_v2)) { - enabled.add(SslProtocols.SSL_v2); - } - if (isProtocolEnabled(opts, SSL.SSL_OP_NO_SSLv3, SslProtocols.SSL_v3)) { - enabled.add(SslProtocols.SSL_v3); - } - return enabled.toArray(new String[0]); - } - - private static boolean isProtocolEnabled(int opts, int disableMask, String protocolString) { - // We also need to check if the actual protocolString is supported as depending on the openssl API - // implementations it may use a disableMask of 0 (BoringSSL is doing this for example). - return (opts & disableMask) == 0 && OpenSsl.SUPPORTED_PROTOCOLS_SET.contains(protocolString); - } - - /** - * {@inheritDoc} - * TLS doesn't support a way to advertise non-contiguous versions from the client's perspective, and the client - * just advertises the max supported version. The TLS protocol also doesn't support all different combinations of - * discrete protocols, and instead assumes contiguous ranges. OpenSSL has some unexpected behavior - * (e.g. handshake failures) if non-contiguous protocols are used even where there is a compatible set of protocols - * and ciphers. For these reasons this method will determine the minimum protocol and the maximum protocol and - * enabled a contiguous range from [min protocol, max protocol] in OpenSSL. - */ - @Override - public final void setEnabledProtocols(String[] protocols) { - setEnabledProtocols0(protocols, true); - } - - private void setEnabledProtocols0(String[] protocols, boolean cache) { - // This is correct from the API docs - checkNotNullWithIAE(protocols, "protocols"); - int minProtocolIndex = OPENSSL_OP_NO_PROTOCOLS.length; - int maxProtocolIndex = 0; - for (String p: protocols) { - if (!OpenSsl.SUPPORTED_PROTOCOLS_SET.contains(p)) { - throw new IllegalArgumentException("Protocol " + p + " is not supported."); - } - switch (p) { - case SslProtocols.SSL_v2: - if (minProtocolIndex > OPENSSL_OP_NO_PROTOCOL_INDEX_SSLV2) { - minProtocolIndex = OPENSSL_OP_NO_PROTOCOL_INDEX_SSLV2; - } - if (maxProtocolIndex < OPENSSL_OP_NO_PROTOCOL_INDEX_SSLV2) { // lgtm[java/constant-comparison] - maxProtocolIndex = OPENSSL_OP_NO_PROTOCOL_INDEX_SSLV2; - } - break; - case SslProtocols.SSL_v3: - if (minProtocolIndex > OPENSSL_OP_NO_PROTOCOL_INDEX_SSLV3) { - minProtocolIndex = OPENSSL_OP_NO_PROTOCOL_INDEX_SSLV3; - } - if (maxProtocolIndex < OPENSSL_OP_NO_PROTOCOL_INDEX_SSLV3) { - maxProtocolIndex = OPENSSL_OP_NO_PROTOCOL_INDEX_SSLV3; - } - break; - case SslProtocols.TLS_v1: - if (minProtocolIndex > OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1) { - minProtocolIndex = OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1; - } - if (maxProtocolIndex < OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1) { - maxProtocolIndex = OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1; - } - break; - case SslProtocols.TLS_v1_1: - if (minProtocolIndex > OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_1) { - minProtocolIndex = OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_1; - } - if (maxProtocolIndex < OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_1) { - maxProtocolIndex = OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_1; - } - break; - case SslProtocols.TLS_v1_2: - if (minProtocolIndex > OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_2) { - minProtocolIndex = OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_2; - } - if (maxProtocolIndex < OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_2) { - maxProtocolIndex = OPENSSL_OP_NO_PROTOCOL_INDEX_TLSv1_2; - } - break; - case SslProtocols.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; - } - break; - } - } - synchronized (this) { - if (cache) { - this.explicitlyEnabledProtocols = protocols; - } - 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_3); - - int opts = 0; - for (int i = 0; i < minProtocolIndex; ++i) { - opts |= OPENSSL_OP_NO_PROTOCOLS[i]; - } - assert maxProtocolIndex != MAX_VALUE; - for (int i = maxProtocolIndex + 1; i < OPENSSL_OP_NO_PROTOCOLS.length; ++i) { - opts |= OPENSSL_OP_NO_PROTOCOLS[i]; - } - - // Disable protocols we do not want - SSL.setOptions(ssl, opts); - } else { - throw new IllegalStateException("failed to enable protocols: " + Arrays.asList(protocols)); - } - } - } - - @Override - public final SSLSession getSession() { - return session; - } - - @Override - public final synchronized void beginHandshake() throws SSLException { - switch (handshakeState) { - case STARTED_IMPLICITLY: - checkEngineClosed(); - - // A user did not start handshake by calling this method by him/herself, - // but handshake has been started already by wrap() or unwrap() implicitly. - // Because it's the user's first time to call this method, it is unfair to - // raise an exception. From the user's standpoint, he or she never asked - // for renegotiation. - - handshakeState = HandshakeState.STARTED_EXPLICITLY; // Next time this method is invoked by the user, - calculateMaxWrapOverhead(); - // we should raise an exception. - break; - case STARTED_EXPLICITLY: - // Nothing to do as the handshake is not done yet. - break; - case FINISHED: - throw new SSLException("renegotiation unsupported"); - case NOT_STARTED: - handshakeState = HandshakeState.STARTED_EXPLICITLY; - if (handshake() == NEED_TASK) { - // Set needTask to true so getHandshakeStatus() will return the correct value. - needTask = true; - } - calculateMaxWrapOverhead(); - break; - default: - throw new Error(); - } - } - - private void checkEngineClosed() throws SSLException { - if (isDestroyed()) { - throw new SSLException("engine closed"); - } - } - - private static SSLEngineResult.HandshakeStatus pendingStatus(int pendingStatus) { - // Depending on if there is something left in the BIO we need to WRAP or UNWRAP - return pendingStatus > 0 ? NEED_WRAP : NEED_UNWRAP; - } - - private static boolean isEmpty(Object[] arr) { - return arr == null || arr.length == 0; - } - - private static boolean isEmpty(byte[] cert) { - return cert == null || cert.length == 0; - } - - private SSLEngineResult.HandshakeStatus handshakeException() throws SSLException { - if (SSL.bioLengthNonApplication(networkBIO) > 0) { - // There is something pending, we need to consume it first via a WRAP so we don't loose anything. - return NEED_WRAP; - } - - Throwable exception = pendingException; - assert exception != null; - pendingException = null; - shutdown(); - if (exception instanceof SSLHandshakeException) { - throw (SSLHandshakeException) exception; - } - SSLHandshakeException e = new SSLHandshakeException("General OpenSslEngine problem"); - e.initCause(exception); - throw e; - } - - /** - * Should be called if the handshake will be failed due a callback that throws an exception. - * This cause will then be used to give more details as part of the {@link SSLHandshakeException}. - */ - final void initHandshakeException(Throwable cause) { - if (pendingException == null) { - pendingException = cause; - } else { - pendingException.addSuppressed(cause); - } - } - - private SSLEngineResult.HandshakeStatus handshake() throws SSLException { - if (needTask) { - return NEED_TASK; - } - if (handshakeState == HandshakeState.FINISHED) { - return FINISHED; - } - - checkEngineClosed(); - - if (pendingException != null) { - // Let's call SSL.doHandshake(...) again in case there is some async operation pending that would fill the - // outbound buffer. - if (SSL.doHandshake(ssl) <= 0) { - // Clear any error that was put on the stack by the handshake - SSL.clearError(); - } - return handshakeException(); - } - - // Adding the OpenSslEngine to the OpenSslEngineMap so it can be used in the AbstractCertificateVerifier. - engineMap.add(this); - - if (!sessionSet) { - parentContext.sessionContext().setSessionFromCache(getPeerHost(), getPeerPort(), ssl); - sessionSet = true; - } - - if (lastAccessed == -1) { - lastAccessed = System.currentTimeMillis(); - } - - int code = SSL.doHandshake(ssl); - if (code <= 0) { - int sslError = SSL.getError(ssl, code); - if (sslError == SSL.SSL_ERROR_WANT_READ || sslError == SSL.SSL_ERROR_WANT_WRITE) { - return pendingStatus(SSL.bioLengthNonApplication(networkBIO)); - } - - if (sslError == SSL.SSL_ERROR_WANT_X509_LOOKUP || - sslError == SSL.SSL_ERROR_WANT_CERTIFICATE_VERIFY || - sslError == SSL.SSL_ERROR_WANT_PRIVATE_KEY_OPERATION) { - return NEED_TASK; - } - - if (needWrapAgain(SSL.getLastErrorNumber())) { - // There is something that needs to be send to the remote peer before we can teardown. - // This is most likely some alert. - return NEED_WRAP; - } - // Check if we have a pending exception that was created during the handshake and if so throw it after - // shutdown the connection. - if (pendingException != null) { - return handshakeException(); - } - - // 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(SSL.getSessionId(ssl), SSL.getCipherForSSL(ssl), SSL.getVersion(ssl), - SSL.getPeerCertificate(ssl), SSL.getPeerCertChain(ssl), - SSL.getTime(ssl) * 1000L, parentContext.sessionTimeout() * 1000L); - selectApplicationProtocol(); - return FINISHED; - } - - private SSLEngineResult.HandshakeStatus mayFinishHandshake( - SSLEngineResult.HandshakeStatus hs, int bytesConsumed, int bytesProduced) throws SSLException { - return hs == NEED_UNWRAP && bytesProduced > 0 || hs == NEED_WRAP && bytesConsumed > 0 ? - handshake() : mayFinishHandshake(hs != FINISHED ? getHandshakeStatus() : FINISHED); - } - - private SSLEngineResult.HandshakeStatus mayFinishHandshake(SSLEngineResult.HandshakeStatus status) - throws SSLException { - if (status == NOT_HANDSHAKING) { - if (handshakeState != HandshakeState.FINISHED) { - // If the status was NOT_HANDSHAKING and we not finished the handshake we need to call - // SSL_do_handshake() again - return handshake(); - } - if (!isDestroyed() && SSL.bioLengthNonApplication(networkBIO) > 0) { - // We have something left that needs to be wrapped. - return NEED_WRAP; - } - } - return status; - } - - @Override - public final synchronized SSLEngineResult.HandshakeStatus getHandshakeStatus() { - // Check if we are in the initial handshake phase or shutdown phase - 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 - if (needPendingStatus()) { - if (needTask) { - // There is a task outstanding - return NEED_TASK; - } - return pendingStatus(pending); - } - return NOT_HANDSHAKING; - } - - private boolean needPendingStatus() { - return handshakeState != HandshakeState.NOT_STARTED && !isDestroyed() - && (handshakeState != HandshakeState.FINISHED || isInboundDone() || isOutboundDone()); - } - - /** - * Converts the specified OpenSSL cipher suite to the Java cipher suite. - */ - private String toJavaCipherSuite(String openSslCipherSuite) { - if (openSslCipherSuite == null) { - return null; - } - - String version = SSL.getVersion(ssl); - String prefix = toJavaCipherSuitePrefix(version); - return CipherSuiteConverter.toJava(openSslCipherSuite, prefix); - } - - /** - * Converts the protocol version string returned by {@link SSL#getVersion(long)} to protocol family string. - */ - private static String toJavaCipherSuitePrefix(String protocolVersion) { - final char c; - if (protocolVersion == null || protocolVersion.isEmpty()) { - c = 0; - } else { - c = protocolVersion.charAt(0); - } - - switch (c) { - case 'T': - return "TLS"; - case 'S': - return "SSL"; - default: - return "UNKNOWN"; - } - } - - @Override - public final void setUseClientMode(boolean clientMode) { - if (clientMode != this.clientMode) { - throw new UnsupportedOperationException(); - } - } - - @Override - public final boolean getUseClientMode() { - return clientMode; - } - - @Override - public final void setNeedClientAuth(boolean b) { - setClientAuth(b ? ClientAuth.REQUIRE : ClientAuth.NONE); - } - - @Override - public final boolean getNeedClientAuth() { - return clientAuth == ClientAuth.REQUIRE; - } - - @Override - public final void setWantClientAuth(boolean b) { - setClientAuth(b ? ClientAuth.OPTIONAL : ClientAuth.NONE); - } - - @Override - public final boolean getWantClientAuth() { - return clientAuth == ClientAuth.OPTIONAL; - } - - /** - * See SSL_set_verify and - * {@link SSL#setVerify(long, int, int)}. - */ - @UnstableApi - public final synchronized void setVerify(int verifyMode, int depth) { - if (!isDestroyed()) { - SSL.setVerify(ssl, verifyMode, depth); - } - } - - private void setClientAuth(ClientAuth mode) { - if (clientMode) { - return; - } - synchronized (this) { - if (clientAuth == mode) { - // No need to issue any JNI calls if the mode is the same - return; - } - if (!isDestroyed()) { - switch (mode) { - case NONE: - SSL.setVerify(ssl, SSL.SSL_CVERIFY_NONE, ReferenceCountedOpenSslContext.VERIFY_DEPTH); - break; - case REQUIRE: - SSL.setVerify(ssl, SSL.SSL_CVERIFY_REQUIRED, ReferenceCountedOpenSslContext.VERIFY_DEPTH); - break; - case OPTIONAL: - SSL.setVerify(ssl, SSL.SSL_CVERIFY_OPTIONAL, ReferenceCountedOpenSslContext.VERIFY_DEPTH); - break; - default: - throw new Error(mode.toString()); - } - } - clientAuth = mode; - } - } - - @Override - public final void setEnableSessionCreation(boolean b) { - if (b) { - throw new UnsupportedOperationException(); - } - } - - @Override - public final boolean getEnableSessionCreation() { - return false; - } - - @Override - public final synchronized SSLParameters getSSLParameters() { - SSLParameters sslParameters = super.getSSLParameters(); - - int version = PlatformDependent.javaVersion(); - if (version >= 7) { - sslParameters.setEndpointIdentificationAlgorithm(endPointIdentificationAlgorithm); - Java7SslParametersUtils.setAlgorithmConstraints(sslParameters, algorithmConstraints); - if (version >= 8) { - if (sniHostNames != null) { - Java8SslUtils.setSniHostNames(sslParameters, sniHostNames); - } - if (!isDestroyed()) { - Java8SslUtils.setUseCipherSuitesOrder( - sslParameters, (SSL.getOptions(ssl) & SSL.SSL_OP_CIPHER_SERVER_PREFERENCE) != 0); - } - - Java8SslUtils.setSNIMatchers(sslParameters, matchers); - } - } - return sslParameters; - } - - @Override - public final synchronized void setSSLParameters(SSLParameters sslParameters) { - int version = PlatformDependent.javaVersion(); - if (version >= 7) { - if (sslParameters.getAlgorithmConstraints() != null) { - throw new IllegalArgumentException("AlgorithmConstraints are not supported."); - } - - boolean isDestroyed = isDestroyed(); - if (version >= 8) { - if (!isDestroyed) { - if (clientMode) { - final List sniHostNames = Java8SslUtils.getSniHostNames(sslParameters); - for (String name: sniHostNames) { - SSL.setTlsExtHostName(ssl, name); - } - this.sniHostNames = sniHostNames; - } - if (Java8SslUtils.getUseCipherSuitesOrder(sslParameters)) { - SSL.setOptions(ssl, SSL.SSL_OP_CIPHER_SERVER_PREFERENCE); - } else { - SSL.clearOptions(ssl, SSL.SSL_OP_CIPHER_SERVER_PREFERENCE); - } - } - matchers = sslParameters.getSNIMatchers(); - } - - final String endPointIdentificationAlgorithm = sslParameters.getEndpointIdentificationAlgorithm(); - if (!isDestroyed) { - // 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 && isEndPointVerificationEnabled(endPointIdentificationAlgorithm)) { - SSL.setVerify(ssl, SSL.SSL_CVERIFY_REQUIRED, -1); - } - } - this.endPointIdentificationAlgorithm = endPointIdentificationAlgorithm; - algorithmConstraints = sslParameters.getAlgorithmConstraints(); - } - super.setSSLParameters(sslParameters); - } - - private static boolean isEndPointVerificationEnabled(String endPointIdentificationAlgorithm) { - return endPointIdentificationAlgorithm != null && !endPointIdentificationAlgorithm.isEmpty(); - } - - private boolean isDestroyed() { - return destroyed; - } - - final boolean checkSniHostnameMatch(byte[] hostname) { - return Java8SslUtils.checkSniHostnameMatch(matchers, hostname); - } - - @Override - public String getNegotiatedApplicationProtocol() { - return applicationProtocol; - } - - private static long bufferAddress(ByteBuffer b) { - assert b.isDirect(); - if (PlatformDependent.hasUnsafe()) { - return PlatformDependent.directBufferAddress(b); - } - return Buffer.address(b); - } - - /** - * Select the application protocol used. - */ - private void selectApplicationProtocol() throws SSLException { - ApplicationProtocolConfig.SelectedListenerFailureBehavior behavior = apn.selectedListenerFailureBehavior(); - List protocols = apn.protocols(); - String applicationProtocol; - switch (apn.protocol()) { - case NONE: - break; - // We always need to check for applicationProtocol == null as the remote peer may not support - // the TLS extension or may have returned an empty selection. - case ALPN: - applicationProtocol = SSL.getAlpnSelected(ssl); - if (applicationProtocol != null) { - ReferenceCountedOpenSslEngine.this.applicationProtocol = selectApplicationProtocol( - protocols, behavior, applicationProtocol); - } - break; - case NPN: - applicationProtocol = SSL.getNextProtoNegotiated(ssl); - if (applicationProtocol != null) { - ReferenceCountedOpenSslEngine.this.applicationProtocol = selectApplicationProtocol( - protocols, behavior, applicationProtocol); - } - break; - case NPN_AND_ALPN: - applicationProtocol = SSL.getAlpnSelected(ssl); - if (applicationProtocol == null) { - applicationProtocol = SSL.getNextProtoNegotiated(ssl); - } - if (applicationProtocol != null) { - ReferenceCountedOpenSslEngine.this.applicationProtocol = selectApplicationProtocol( - protocols, behavior, applicationProtocol); - } - break; - default: - throw new Error(); - } - } - - private String selectApplicationProtocol(List protocols, - ApplicationProtocolConfig.SelectedListenerFailureBehavior behavior, - String applicationProtocol) throws SSLException { - if (behavior == ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT) { - return applicationProtocol; - } else { - int size = protocols.size(); - assert size > 0; - if (protocols.contains(applicationProtocol)) { - return applicationProtocol; - } else { - if (behavior == ApplicationProtocolConfig.SelectedListenerFailureBehavior.CHOOSE_MY_LAST_PROTOCOL) { - return protocols.get(size - 1); - } else { - throw new SSLException("unknown protocol " + applicationProtocol); - } - } - } - } - - final void setSessionId(OpenSslSessionId id) { - session.setSessionId(id); - } - - private final class DefaultOpenSslSession implements OpenSslSession { - private final OpenSslSessionContext sessionContext; - - // These are guarded by synchronized(OpenSslEngine.this) as handshakeFinished() may be triggered by any - // thread. - private X509Certificate[] x509PeerCerts; - private Certificate[] peerCerts; - - private boolean valid = true; - private String protocol; - private String cipher; - private OpenSslSessionId id = OpenSslSessionId.NULL_ID; - private volatile long creationTime; - private volatile int applicationBufferSize = MAX_PLAINTEXT_LENGTH; - private volatile Certificate[] localCertificateChain; - // lazy init for memory reasons - private Map values; - - DefaultOpenSslSession(OpenSslSessionContext sessionContext) { - this.sessionContext = sessionContext; - } - - private SSLSessionBindingEvent newSSLSessionBindingEvent(String name) { - return new SSLSessionBindingEvent(session, name); - } - - @Override - public void setSessionId(OpenSslSessionId sessionId) { - synchronized (ReferenceCountedOpenSslEngine.this) { - if (this.id == OpenSslSessionId.NULL_ID) { - this.id = sessionId; - creationTime = System.currentTimeMillis(); - } - } - } - - @Override - public OpenSslSessionId sessionId() { - synchronized (ReferenceCountedOpenSslEngine.this) { - if (this.id == OpenSslSessionId.NULL_ID && !isDestroyed()) { - byte[] sessionId = SSL.getSessionId(ssl); - if (sessionId != null) { - id = new OpenSslSessionId(sessionId); - } - } - - return id; - } - } - - @Override - public void setLocalCertificate(Certificate[] localCertificate) { - this.localCertificateChain = localCertificate; - } - - @Override - public byte[] getId() { - return sessionId().cloneBytes(); - } - - @Override - public OpenSslSessionContext getSessionContext() { - return sessionContext; - } - - @Override - public long getCreationTime() { - synchronized (ReferenceCountedOpenSslEngine.this) { - return creationTime; - } - } - - @Override - public long getLastAccessedTime() { - long lastAccessed = ReferenceCountedOpenSslEngine.this.lastAccessed; - // if lastAccessed is -1 we will just return the creation time as the handshake was not started yet. - return lastAccessed == -1 ? getCreationTime() : lastAccessed; - } - - @Override - public void invalidate() { - synchronized (ReferenceCountedOpenSslEngine.this) { - valid = false; - sessionContext.removeFromCache(id); - } - } - - @Override - public boolean isValid() { - synchronized (ReferenceCountedOpenSslEngine.this) { - return valid || sessionContext.isInCache(id); - } - } - - @Override - public void putValue(String name, Object value) { - requireNonNull(name, "name"); - requireNonNull(value, "value"); - - 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); - } - - if (value instanceof SSLSessionBindingListener) { - // Use newSSLSessionBindingEvent so we always use the wrapper if needed. - ((SSLSessionBindingListener) value).valueBound(newSSLSessionBindingEvent(name)); - } - notifyUnbound(old, name); - } - - @Override - public Object getValue(String name) { - requireNonNull(name, "name"); - synchronized (this) { - if (values == null) { - return null; - } - return values.get(name); - } - } - - @Override - public void removeValue(String name) { - requireNonNull(name, "name"); - - final Object old; - synchronized (this) { - Map values = this.values; - if (values == null) { - return; - } - old = values.remove(name); - } - - notifyUnbound(old, name); - } - - @Override - public String[] getValueNames() { - synchronized (this) { - Map values = this.values; - if (values == null || values.isEmpty()) { - return EmptyArrays.EMPTY_STRINGS; - } - return values.keySet().toArray(new String[0]); - } - } - - private void notifyUnbound(Object value, String name) { - if (value instanceof SSLSessionBindingListener) { - // Use newSSLSessionBindingEvent so we always use the wrapper if needed. - ((SSLSessionBindingListener) value).valueUnbound(newSSLSessionBindingEvent(name)); - } - } - - /** - * Finish the handshake and so init everything in the {@link OpenSslSession} that should be accessible by - * the user. - */ - @Override - public void handshakeFinished(byte[] id, String cipher, String protocol, byte[] peerCertificate, - byte[][] peerCertificateChain, long creationTime, long timeout) - throws SSLException { - synchronized (ReferenceCountedOpenSslEngine.this) { - if (!isDestroyed()) { - this.creationTime = creationTime; - if (this.id == OpenSslSessionId.NULL_ID) { - this.id = id == null ? OpenSslSessionId.NULL_ID : new OpenSslSessionId(id); - } - this.cipher = toJavaCipherSuite(cipher); - this.protocol = protocol; - - if (clientMode) { - if (isEmpty(peerCertificateChain)) { - peerCerts = EmptyArrays.EMPTY_CERTIFICATES; - x509PeerCerts = EmptyArrays.EMPTY_JAVAX_X509_CERTIFICATES; - } else { - peerCerts = new Certificate[peerCertificateChain.length]; - x509PeerCerts = new X509Certificate[peerCertificateChain.length]; - initCerts(peerCertificateChain, 0); - } - } else { - // if used on the server side SSL_get_peer_cert_chain(...) will not include the remote peer - // certificate. We use SSL_get_peer_certificate to get it in this case and add it to our - // array later. - // - // See https://www.openssl.org/docs/ssl/SSL_get_peer_cert_chain.html - if (isEmpty(peerCertificate)) { - peerCerts = EmptyArrays.EMPTY_CERTIFICATES; - x509PeerCerts = EmptyArrays.EMPTY_JAVAX_X509_CERTIFICATES; - } else { - if (isEmpty(peerCertificateChain)) { - peerCerts = new Certificate[] {new LazyX509Certificate(peerCertificate)}; - x509PeerCerts = new X509Certificate[] {new LazyJavaxX509Certificate(peerCertificate)}; - } else { - peerCerts = new Certificate[peerCertificateChain.length + 1]; - x509PeerCerts = new X509Certificate[peerCertificateChain.length + 1]; - peerCerts[0] = new LazyX509Certificate(peerCertificate); - x509PeerCerts[0] = new LazyJavaxX509Certificate(peerCertificate); - initCerts(peerCertificateChain, 1); - } - } - } - - calculateMaxWrapOverhead(); - - handshakeState = HandshakeState.FINISHED; - } else { - throw new SSLException("Already closed"); - } - } - } - - private void initCerts(byte[][] chain, int startPos) { - for (int i = 0; i < chain.length; i++) { - int certPos = startPos + i; - peerCerts[certPos] = new LazyX509Certificate(chain[i]); - x509PeerCerts[certPos] = new LazyJavaxX509Certificate(chain[i]); - } - } - - @Override - public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException { - synchronized (ReferenceCountedOpenSslEngine.this) { - if (isEmpty(peerCerts)) { - throw new SSLPeerUnverifiedException("peer not verified"); - } - return peerCerts.clone(); - } - } - - @Override - public Certificate[] getLocalCertificates() { - Certificate[] localCerts = this.localCertificateChain; - if (localCerts == null) { - return null; - } - return localCerts.clone(); - } - - @Override - public X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException { - synchronized (ReferenceCountedOpenSslEngine.this) { - if (isEmpty(x509PeerCerts)) { - throw new SSLPeerUnverifiedException("peer not verified"); - } - return x509PeerCerts.clone(); - } - } - - @Override - public Principal getPeerPrincipal() throws SSLPeerUnverifiedException { - Certificate[] peer = getPeerCertificates(); - // No need for null or length > 0 is needed as this is done in getPeerCertificates() - // already. - return ((java.security.cert.X509Certificate) peer[0]).getSubjectX500Principal(); - } - - @Override - public Principal getLocalPrincipal() { - Certificate[] local = this.localCertificateChain; - if (local == null || local.length == 0) { - return null; - } - return ((java.security.cert.X509Certificate) local[0]).getIssuerX500Principal(); - } - - @Override - public String getCipherSuite() { - synchronized (ReferenceCountedOpenSslEngine.this) { - if (cipher == null) { - return SslUtils.INVALID_CIPHER; - } - return cipher; - } - } - - @Override - public String getProtocol() { - String protocol = this.protocol; - if (protocol == null) { - synchronized (ReferenceCountedOpenSslEngine.this) { - if (!isDestroyed()) { - protocol = SSL.getVersion(ssl); - } else { - protocol = StringUtil.EMPTY_STRING; - } - } - } - return protocol; - } - - @Override - public String getPeerHost() { - return ReferenceCountedOpenSslEngine.this.getPeerHost(); - } - - @Override - public int getPeerPort() { - return ReferenceCountedOpenSslEngine.this.getPeerPort(); - } - - @Override - public int getPacketBufferSize() { - return maxEncryptedPacketLength(); - } - - @Override - public int getApplicationBufferSize() { - return applicationBufferSize; - } - - @Override - public void tryExpandApplicationBufferSize(int packetLengthDataOnly) { - if (packetLengthDataOnly > MAX_PLAINTEXT_LENGTH && applicationBufferSize != MAX_RECORD_SIZE) { - applicationBufferSize = MAX_RECORD_SIZE; - } - } - - @Override - public String toString() { - return "DefaultOpenSslSession{" + - "sessionContext=" + sessionContext + - ", id=" + id + - '}'; - } - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java deleted file mode 100644 index 3667e74e52..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java +++ /dev/null @@ -1,287 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.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; -import io.netty.util.CharsetUtil; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.security.KeyStore; -import java.security.PrivateKey; -import java.security.cert.X509Certificate; -import java.util.Map; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLException; -import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509ExtendedTrustManager; -import javax.net.ssl.X509TrustManager; - -import static java.util.Objects.requireNonNull; - -/** - * A server-side {@link SslContext} which uses OpenSSL's SSL/TLS implementation. - *

Instances of this class must be {@link #release() released} or else native memory will leak! - * - *

Instances of this class must not be released before any {@link ReferenceCountedOpenSslEngine} - * which depends upon the instance of this class is released. Otherwise if any method of - * {@link ReferenceCountedOpenSslEngine} is called which uses this class's JNI resources the JVM may crash. - */ -public final class ReferenceCountedOpenSslServerContext extends ReferenceCountedOpenSslContext { - private static final InternalLogger logger = - InternalLoggerFactory.getInstance(ReferenceCountedOpenSslServerContext.class); - private static final byte[] ID = {'n', 'e', 't', 't', 'y'}; - private final OpenSslServerSessionContext sessionContext; - - ReferenceCountedOpenSslServerContext( - X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory, - X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory, - Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, - long sessionCacheSize, long sessionTimeout, ClientAuth clientAuth, String[] protocols, boolean startTls, - boolean enableOcsp, String keyStore, Map.Entry, Object>... options) - throws SSLException { - this(trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory, ciphers, - cipherFilter, toNegotiator(apn), sessionCacheSize, sessionTimeout, clientAuth, protocols, startTls, - enableOcsp, keyStore, options); - } - - ReferenceCountedOpenSslServerContext( - X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory, - X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory, - Iterable ciphers, CipherSuiteFilter cipherFilter, OpenSslApplicationProtocolNegotiator apn, - long sessionCacheSize, long sessionTimeout, ClientAuth clientAuth, String[] protocols, boolean startTls, - boolean enableOcsp, String keyStore, Map.Entry, Object>... options) - throws SSLException { - super(ciphers, cipherFilter, apn, SSL.SSL_MODE_SERVER, keyCertChain, - clientAuth, protocols, startTls, enableOcsp, true, options); - // Create a new SSL_CTX and configure it. - boolean success = false; - try { - sessionContext = newSessionContext(this, ctx, engineMap, trustCertCollection, trustManagerFactory, - keyCertChain, key, keyPassword, keyManagerFactory, keyStore, - sessionCacheSize, sessionTimeout); - if (SERVER_ENABLE_SESSION_TICKET) { - sessionContext.setTicketKeys(); - } - success = true; - } finally { - if (!success) { - release(); - } - } - } - - @Override - public OpenSslServerSessionContext sessionContext() { - return sessionContext; - } - - static OpenSslServerSessionContext newSessionContext(ReferenceCountedOpenSslContext thiz, long ctx, - OpenSslEngineMap engineMap, - X509Certificate[] trustCertCollection, - TrustManagerFactory trustManagerFactory, - X509Certificate[] keyCertChain, PrivateKey key, - String keyPassword, KeyManagerFactory keyManagerFactory, - String keyStore, long sessionCacheSize, long sessionTimeout) - throws SSLException { - OpenSslKeyMaterialProvider keyMaterialProvider = null; - try { - try { - SSLContext.setVerify(ctx, SSL.SSL_CVERIFY_NONE, VERIFY_DEPTH); - if (!OpenSsl.supportsKeyManagerFactory()) { - if (keyManagerFactory != null) { - throw new IllegalArgumentException( - "KeyManagerFactory not supported"); - } - requireNonNull(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) { - char[] keyPasswordChars = keyStorePassword(keyPassword); - KeyStore ks = buildKeyStore(keyCertChain, key, keyPasswordChars, keyStore); - if (ks.aliases().hasMoreElements()) { - keyManagerFactory = new OpenSslX509KeyManagerFactory(); - } else { - keyManagerFactory = new OpenSslCachingX509KeyManagerFactory( - KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())); - } - keyManagerFactory.init(ks, keyPasswordChars); - } - keyMaterialProvider = providerFor(keyManagerFactory, keyPassword); - - SSLContext.setCertificateCallback(ctx, new OpenSslServerCertificateCallback( - engineMap, new OpenSslKeyMaterialManager(keyMaterialProvider))); - } - } catch (Exception e) { - throw new SSLException("failed to set certificate and key", e); - } - try { - if (trustCertCollection != null) { - trustManagerFactory = buildTrustManagerFactory(trustCertCollection, trustManagerFactory, keyStore); - } 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 - - // 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); - } - } finally { - freeBio(bio); - } - } - - // 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); - } - - OpenSslServerSessionContext sessionContext = new OpenSslServerSessionContext(thiz, keyMaterialProvider); - sessionContext.setSessionIdContext(ID); - // Enable session caching by default - sessionContext.setSessionCacheEnabled(SERVER_ENABLE_SESSION_CACHE); - if (sessionCacheSize > 0) { - sessionContext.setSessionCacheSize((int) Math.min(sessionCacheSize, Integer.MAX_VALUE)); - } - if (sessionTimeout > 0) { - sessionContext.setSessionTimeout((int) Math.min(sessionTimeout, Integer.MAX_VALUE)); - } - - keyMaterialProvider = null; - - return sessionContext; - } finally { - if (keyMaterialProvider != null) { - keyMaterialProvider.destroy(); - } - } - } - - 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); - if (engine == null) { - // Maybe null if destroyed in the meantime. - return; - } - 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) { - engine.initHandshakeException(cause); - - if (cause instanceof Exception) { - throw (Exception) cause; - } - throw new SSLException(cause); - } - } - } - - private static final class TrustManagerVerifyCallback extends AbstractCertificateVerifier { - private final X509TrustManager manager; - - TrustManagerVerifyCallback(OpenSslEngineMap engineMap, X509TrustManager manager) { - super(engineMap); - this.manager = manager; - } - - @Override - void verify(ReferenceCountedOpenSslEngine engine, X509Certificate[] peerCerts, String auth) - throws Exception { - manager.checkClientTrusted(peerCerts, auth); - } - } - - private static final class ExtendedTrustManagerVerifyCallback extends AbstractCertificateVerifier { - private final X509ExtendedTrustManager manager; - - ExtendedTrustManagerVerifyCallback(OpenSslEngineMap engineMap, X509ExtendedTrustManager manager) { - super(engineMap); - this.manager = manager; - } - - @Override - void verify(ReferenceCountedOpenSslEngine engine, X509Certificate[] peerCerts, String auth) - throws Exception { - manager.checkClientTrusted(peerCerts, auth, engine); - } - } - - private static final class OpenSslSniHostnameMatcher implements SniHostNameMatcher { - private final OpenSslEngineMap engineMap; - - OpenSslSniHostnameMatcher(OpenSslEngineMap engineMap) { - this.engineMap = engineMap; - } - - @Override - public boolean match(long ssl, String hostname) { - ReferenceCountedOpenSslEngine engine = engineMap.get(ssl); - if (engine != null) { - // 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/main/java/io/netty/handler/ssl/SignatureAlgorithmConverter.java b/handler/src/main/java/io/netty/handler/ssl/SignatureAlgorithmConverter.java deleted file mode 100644 index 362a9de372..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/SignatureAlgorithmConverter.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.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 - // - // BoringSSL uses a different format: - // https://github.com/google/boringssl/blob/8525ff3/ssl/ssl_privkey.cc#L436 - // - private static final Pattern PATTERN = Pattern.compile( - // 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, - * 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 group1 = matcher.group(1); - if (group1 != null) { - return group1.toUpperCase(Locale.ROOT) + "with" + matcher.group(2).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/main/java/io/netty/handler/ssl/SniCompletionEvent.java b/handler/src/main/java/io/netty/handler/ssl/SniCompletionEvent.java deleted file mode 100644 index e15ba7284d..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/SniCompletionEvent.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.util.internal.UnstableApi; - -/** - * Event that is fired once we did a selection of a {@link SslContext} based on the {@code SNI hostname}, - * which may be because it was successful or there was an error. - */ -@UnstableApi -public final class SniCompletionEvent extends SslCompletionEvent { - private final String hostname; - - SniCompletionEvent(String hostname) { - this.hostname = hostname; - } - - SniCompletionEvent(String hostname, Throwable cause) { - super(cause); - this.hostname = hostname; - } - - SniCompletionEvent(Throwable cause) { - this(null, cause); - } - - /** - * Returns the SNI hostname send by the client if we were able to parse it, {@code null} otherwise. - */ - public String hostname() { - return hostname; - } - - @Override - public String toString() { - final Throwable cause = cause(); - return cause == null ? getClass().getSimpleName() + "(SUCCESS='" + hostname + "'\")": - getClass().getSimpleName() + '(' + cause + ')'; - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/SniHandler.java b/handler/src/main/java/io/netty/handler/ssl/SniHandler.java deleted file mode 100644 index 80f5a80a94..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/SniHandler.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.buffer.ByteBufAllocator; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.DecoderException; -import io.netty.util.AsyncMapping; -import io.netty.util.DomainNameMapping; -import io.netty.util.Mapping; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; - -import static java.util.Objects.requireNonNull; - -/** - *

Enables SNI - * (Server Name Indication) extension for server side SSL. For clients - * support SNI, the server could have multiple host name bound on a single IP. - * The client will send host name in the handshake data so server could decide - * which certificate to choose for the host name.

- */ -public class SniHandler extends AbstractSniHandler { - private static final Selection EMPTY_SELECTION = new Selection(null, null); - - protected final AsyncMapping mapping; - - private volatile Selection selection = EMPTY_SELECTION; - - /** - * Creates a SNI detection handler with configured {@link SslContext} - * maintained by {@link Mapping} - * - * @param mapping the mapping of domain name to {@link SslContext} - */ - public SniHandler(Mapping mapping) { - this(new AsyncMappingAdapter(mapping)); - } - - /** - * Creates a SNI detection handler with configured {@link SslContext} - * maintained by {@link DomainNameMapping} - * - * @param mapping the mapping of domain name to {@link SslContext} - */ - public SniHandler(DomainNameMapping mapping) { - this((Mapping) mapping); - } - - /** - * Creates a SNI detection handler with configured {@link SslContext} - * maintained by {@link AsyncMapping} - * - * @param mapping the mapping of domain name to {@link SslContext} - */ - @SuppressWarnings("unchecked") - public SniHandler(AsyncMapping mapping) { - this.mapping = (AsyncMapping) requireNonNull(mapping, "mapping"); - } - - /** - * @return the selected hostname - */ - public String hostname() { - return selection.hostname; - } - - /** - * @return the selected {@link SslContext} - */ - public SslContext sslContext() { - return selection.context; - } - - /** - * The default implementation will simply call {@link AsyncMapping#map(Object, Promise)} but - * users can override this method to implement custom behavior. - * - * @see AsyncMapping#map(Object, Promise) - */ - @Override - protected Future lookup(ChannelHandlerContext ctx, String hostname) throws Exception { - return mapping.map(hostname, ctx.executor().newPromise()); - } - - @Override - protected final void onLookupComplete(ChannelHandlerContext ctx, - String hostname, Future future) throws Exception { - if (future.isFailed()) { - final Throwable cause = future.cause(); - if (cause instanceof Error) { - throw (Error) cause; - } - throw new DecoderException("failed to get the SslContext for " + hostname, cause); - } - - SslContext sslContext = future.getNow(); - selection = new Selection(sslContext, hostname); - try { - replaceHandler(ctx, hostname, sslContext); - } catch (Throwable cause) { - selection = EMPTY_SELECTION; - throw cause; - } - } - - /** - * The default implementation of this method will simply replace {@code this} {@link SniHandler} - * instance with a {@link SslHandler}. Users may override this method to implement custom behavior. - * - * Please be aware that this method may get called after a client has already disconnected and - * custom implementations must take it into consideration when overriding this method. - * - * It's also possible for the hostname argument to be {@code null}. - */ - protected void replaceHandler(ChannelHandlerContext ctx, String hostname, SslContext sslContext) throws Exception { - SslHandler sslHandler = null; - try { - sslHandler = newSslHandler(sslContext, ctx.alloc()); - ctx.pipeline().replace(this, SslHandler.class.getName(), sslHandler); - sslHandler = null; - } finally { - // Since the SslHandler was not inserted into the pipeline the ownership of the SSLEngine was not - // transferred to the SslHandler. - // See https://github.com/netty/netty/issues/5678 - if (sslHandler != null) { - ReferenceCountUtil.safeRelease(sslHandler.engine()); - } - } - } - - /** - * 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; - - private AsyncMappingAdapter(Mapping mapping) { - this.mapping = requireNonNull(mapping, "mapping"); - } - - @Override - public Future map(String input, Promise promise) { - final SslContext context; - try { - context = mapping.map(input); - } catch (Throwable cause) { - return promise.setFailure(cause).asFuture(); - } - return promise.setSuccess(context).asFuture(); - } - } - - private static final class Selection { - final SslContext context; - final String hostname; - - Selection(SslContext context, String hostname) { - this.context = context; - this.hostname = hostname; - } - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/SslClientHelloHandler.java b/handler/src/main/java/io/netty/handler/ssl/SslClientHelloHandler.java deleted file mode 100644 index 26458c5b50..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/SslClientHelloHandler.java +++ /dev/null @@ -1,267 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.ByteToMessageDecoder; -import io.netty.handler.codec.DecoderException; -import io.netty.util.concurrent.Future; -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -/** - * {@link ByteToMessageDecoder} which allows to be notified once a full {@code ClientHello} was received. - */ -public abstract class SslClientHelloHandler extends ByteToMessageDecoder { - - private static final InternalLogger logger = - InternalLoggerFactory.getInstance(SslClientHelloHandler.class); - - private boolean handshakeFailed; - private boolean suppressRead; - private boolean readPending; - private ByteBuf handshakeBuffer; - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - if (!suppressRead && !handshakeFailed) { - try { - int readerIndex = in.readerIndex(); - int readableBytes = in.readableBytes(); - int handshakeLength = -1; - - // Check if we have enough data to determine the record type and length. - while (readableBytes >= SslUtils.SSL_RECORD_HEADER_LENGTH) { - final int contentType = in.getUnsignedByte(readerIndex); - switch (contentType) { - case SslUtils.SSL_CONTENT_TYPE_CHANGE_CIPHER_SPEC: - // fall-through - case SslUtils.SSL_CONTENT_TYPE_ALERT: - final int len = SslUtils.getEncryptedPacketLength(in, readerIndex); - - // Not an SSL/TLS packet - if (len == SslUtils.NOT_ENCRYPTED) { - handshakeFailed = true; - NotSslRecordException e = new NotSslRecordException( - "not an SSL/TLS record: " + ByteBufUtil.hexDump(in)); - in.skipBytes(in.readableBytes()); - ctx.fireUserEventTriggered(new SniCompletionEvent(e)); - ctx.fireUserEventTriggered(new SslHandshakeCompletionEvent(e)); - throw e; - } - if (len == SslUtils.NOT_ENOUGH_DATA) { - // Not enough data - return; - } - // No ClientHello - select(ctx, null); - return; - case SslUtils.SSL_CONTENT_TYPE_HANDSHAKE: - final int majorVersion = in.getUnsignedByte(readerIndex + 1); - // SSLv3 or TLS - if (majorVersion == 3) { - int packetLength = in.getUnsignedShort(readerIndex + 3) + - SslUtils.SSL_RECORD_HEADER_LENGTH; - - if (readableBytes < packetLength) { - // client hello incomplete; try again to decode once more data is ready. - return; - } else if (packetLength == SslUtils.SSL_RECORD_HEADER_LENGTH) { - select(ctx, null); - return; - } - - final int endOffset = readerIndex + packetLength; - - // Let's check if we already parsed the handshake length or not. - if (handshakeLength == -1) { - if (readerIndex + 4 > endOffset) { - // Need more data to read HandshakeType and handshakeLength (4 bytes) - return; - } - - final int handshakeType = in.getUnsignedByte(readerIndex + - SslUtils.SSL_RECORD_HEADER_LENGTH); - - // Check if this is a clientHello(1) - // See https://tools.ietf.org/html/rfc5246#section-7.4 - if (handshakeType != 1) { - select(ctx, null); - return; - } - - // Read the length of the handshake as it may arrive in fragments - // See https://tools.ietf.org/html/rfc5246#section-7.4 - handshakeLength = in.getUnsignedMedium(readerIndex + - SslUtils.SSL_RECORD_HEADER_LENGTH + 1); - - // Consume handshakeType and handshakeLength (this sums up as 4 bytes) - readerIndex += 4; - packetLength -= 4; - - if (handshakeLength + 4 + SslUtils.SSL_RECORD_HEADER_LENGTH <= packetLength) { - // We have everything we need in one packet. - // Skip the record header - readerIndex += SslUtils.SSL_RECORD_HEADER_LENGTH; - select(ctx, in.retainedSlice(readerIndex, handshakeLength)); - return; - } else { - if (handshakeBuffer == null) { - handshakeBuffer = ctx.alloc().buffer(handshakeLength); - } else { - // Clear the buffer so we can aggregate into it again. - handshakeBuffer.clear(); - } - } - } - - // Combine the encapsulated data in one buffer but not include the SSL_RECORD_HEADER - handshakeBuffer.writeBytes(in, readerIndex + SslUtils.SSL_RECORD_HEADER_LENGTH, - packetLength - SslUtils.SSL_RECORD_HEADER_LENGTH); - readerIndex += packetLength; - readableBytes -= packetLength; - if (handshakeLength <= handshakeBuffer.readableBytes()) { - ByteBuf clientHello = handshakeBuffer.setIndex(0, handshakeLength); - handshakeBuffer = null; - - select(ctx, clientHello); - return; - } - break; - } - // fall-through - default: - // not tls, ssl or application data - select(ctx, null); - return; - } - } - } catch (NotSslRecordException e) { - // Just rethrow as in this case we also closed the channel and this is consistent with SslHandler. - throw e; - } catch (Exception e) { - // unexpected encoding, ignore sni and use default - if (logger.isDebugEnabled()) { - logger.debug("Unexpected client hello packet: " + ByteBufUtil.hexDump(in), e); - } - select(ctx, null); - } - } - } - - private void releaseHandshakeBuffer() { - releaseIfNotNull(handshakeBuffer); - handshakeBuffer = null; - } - - private static void releaseIfNotNull(ByteBuf buffer) { - if (buffer != null) { - buffer.release(); - } - } - - private void select(final ChannelHandlerContext ctx, ByteBuf clientHello) { - final Future future; - try { - future = lookup(ctx, clientHello); - if (future.isDone()) { - onLookupComplete(ctx, future); - } else { - suppressRead = true; - final ByteBuf finalClientHello = clientHello; - future.addListener(f -> { - releaseIfNotNull(finalClientHello); - try { - suppressRead = false; - try { - onLookupComplete(ctx, f); - } catch (DecoderException err) { - ctx.fireExceptionCaught(err); - } catch (Exception cause) { - ctx.fireExceptionCaught(new DecoderException(cause)); - } catch (Throwable cause) { - ctx.fireExceptionCaught(cause); - } - } finally { - if (readPending) { - readPending = false; - ctx.read(); - } - } - }); - - // Ownership was transferred to the FutureListener. - clientHello = null; - } - } catch (Throwable cause) { - PlatformDependent.throwException(cause); - } finally { - releaseIfNotNull(clientHello); - } - } - - @Override - protected void handlerRemoved0(ChannelHandlerContext ctx) throws Exception { - releaseHandshakeBuffer(); - - super.handlerRemoved0(ctx); - } - - /** - * Kicks off a lookup for the given {@code ClientHello} and returns a {@link Future} which in turn will - * notify the {@link #onLookupComplete(ChannelHandlerContext, Future)} on completion. - * - * See https://tools.ietf.org/html/rfc5246#section-7.4.1.2 - * - *
-     * struct {
-     *    ProtocolVersion client_version;
-     *    Random random;
-     *    SessionID session_id;
-     *    CipherSuite cipher_suites<2..2^16-2>;
-     *    CompressionMethod compression_methods<1..2^8-1>;
-     *    select (extensions_present) {
-     *        case false:
-     *            struct {};
-     *        case true:
-     *            Extension extensions<0..2^16-1>;
-     *    };
-     * } ClientHello;
-     * 
- * - * @see #onLookupComplete(ChannelHandlerContext, Future) - */ - protected abstract Future lookup(ChannelHandlerContext ctx, ByteBuf clientHello) throws Exception; - - /** - * Called upon completion of the {@link #lookup(ChannelHandlerContext, ByteBuf)} {@link Future}. - * - * @see #lookup(ChannelHandlerContext, ByteBuf) - */ - protected abstract void onLookupComplete(ChannelHandlerContext ctx, Future future) throws Exception; - - @Override - public void read(ChannelHandlerContext ctx) { - if (suppressRead) { - readPending = true; - } else { - ctx.read(); - } - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/SslCloseCompletionEvent.java b/handler/src/main/java/io/netty/handler/ssl/SslCloseCompletionEvent.java deleted file mode 100644 index 679e3462e9..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/SslCloseCompletionEvent.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -/** - * Event that is fired once the close_notify was received or if an failure happens before it was received. - */ -public final class SslCloseCompletionEvent extends SslCompletionEvent { - - public static final SslCloseCompletionEvent SUCCESS = new SslCloseCompletionEvent(); - - /** - * Creates a new event that indicates a successful receiving of close_notify. - */ - private SslCloseCompletionEvent() { } - - /** - * Creates a new event that indicates an close_notify was not received because of an previous error. - * Use {@link #SUCCESS} to indicate a success. - */ - public SslCloseCompletionEvent(Throwable cause) { - super(cause); - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/SslClosedEngineException.java b/handler/src/main/java/io/netty/handler/ssl/SslClosedEngineException.java deleted file mode 100644 index 0ef9c0f5cd..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/SslClosedEngineException.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import javax.net.ssl.SSLException; - -/** - * {@link SSLException} which signals that the exception was caused by an {@link javax.net.ssl.SSLEngine} which was - * closed already. - */ -public final class SslClosedEngineException extends SSLException { - - private static final long serialVersionUID = -5204207600474401904L; - - public SslClosedEngineException(String reason) { - super(reason); - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/SslCompletionEvent.java b/handler/src/main/java/io/netty/handler/ssl/SslCompletionEvent.java deleted file mode 100644 index abc439df41..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/SslCompletionEvent.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import static java.util.Objects.requireNonNull; - -public abstract class SslCompletionEvent { - - private final Throwable cause; - - SslCompletionEvent() { - cause = null; - } - - SslCompletionEvent(Throwable cause) { - this.cause = requireNonNull(cause, "cause"); - } - - /** - * Return {@code true} if the completion was successful - */ - public final boolean isSuccess() { - return cause == null; - } - - /** - * Return the {@link Throwable} if {@link #isSuccess()} returns {@code false} - * and so the completion failed. - */ - public final Throwable cause() { - return cause; - } - - @Override - public String toString() { - final Throwable cause = cause(); - return cause == null? getClass().getSimpleName() + "(SUCCESS)" : - getClass().getSimpleName() + '(' + cause + ')'; - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/SslContext.java b/handler/src/main/java/io/netty/handler/ssl/SslContext.java deleted file mode 100644 index ff93d4011c..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/SslContext.java +++ /dev/null @@ -1,1303 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.ssl; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.ByteBufInputStream; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol; -import io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior; -import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior; -import io.netty.util.AttributeMap; -import io.netty.util.DefaultAttributeMap; -import io.netty.util.internal.EmptyArrays; - -import java.security.Provider; -import javax.net.ssl.KeyManager; -import javax.net.ssl.KeyManagerFactory; -import javax.crypto.Cipher; -import javax.crypto.EncryptedPrivateKeyInfo; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.SecretKey; -import javax.crypto.SecretKeyFactory; -import javax.crypto.spec.PBEKeySpec; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLException; -import javax.net.ssl.SSLSessionContext; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.KeyException; -import java.security.KeyFactory; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.UnrecoverableKeyException; -import java.security.cert.CertificateException; -import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.PKCS8EncodedKeySpec; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Executor; - -/** - * A secure socket protocol implementation which acts as a factory for {@link SSLEngine} and {@link SslHandler}. - * Internally, it is implemented via JDK's {@link SSLContext} or OpenSSL's {@code SSL_CTX}. - * - *

Making your server support SSL/TLS

- *
- * // In your {@link ChannelInitializer}:
- * {@link ChannelPipeline} p = channel.pipeline();
- * {@link SslContext} sslCtx = {@link SslContextBuilder#forServer(File, File) SslContextBuilder.forServer(...)}.build();
- * p.addLast("ssl", {@link #newHandler(ByteBufAllocator) sslCtx.newHandler(channel.alloc())});
- * ...
- * 
- * - *

Making your client support SSL/TLS

- *
- * // In your {@link ChannelInitializer}:
- * {@link ChannelPipeline} p = channel.pipeline();
- * {@link SslContext} sslCtx = {@link SslContextBuilder#forClient() SslContextBuilder.forClient()}.build();
- * p.addLast("ssl", {@link #newHandler(ByteBufAllocator, String, int) sslCtx.newHandler(channel.alloc(), host, port)});
- * ...
- * 
- */ -public abstract class SslContext { - static final String ALIAS = "key"; - - static final CertificateFactory X509_CERT_FACTORY; - static { - try { - X509_CERT_FACTORY = CertificateFactory.getInstance("X.509"); - } catch (CertificateException e) { - throw new IllegalStateException("unable to instance X.509 CertificateFactory", e); - } - } - - private final boolean startTls; - private final AttributeMap attributes = new DefaultAttributeMap(); - - /** - * Returns the default server-side implementation provider currently in use. - * - * @return {@link SslProvider#OPENSSL} if OpenSSL is available. {@link SslProvider#JDK} otherwise. - */ - public static SslProvider defaultServerProvider() { - return defaultProvider(); - } - - /** - * Returns the default client-side implementation provider currently in use. - * - * @return {@link SslProvider#OPENSSL} if OpenSSL is available. {@link SslProvider#JDK} otherwise. - */ - public static SslProvider defaultClientProvider() { - return defaultProvider(); - } - - private static SslProvider defaultProvider() { - if (OpenSsl.isAvailable()) { - return SslProvider.OPENSSL; - } else { - return SslProvider.JDK; - } - } - - /** - * Creates a new server-side {@link SslContext}. - * - * @param certChainFile an X.509 certificate chain file in PEM format - * @param keyFile a PKCS#8 private key file in PEM format - * @return a new server-side {@link SslContext} - * @deprecated Replaced by {@link SslContextBuilder} - */ - @Deprecated - public static SslContext newServerContext(File certChainFile, File keyFile) throws SSLException { - return newServerContext(certChainFile, keyFile, null); - } - - /** - * Creates a new server-side {@link SslContext}. - * - * @param certChainFile an X.509 certificate chain file in PEM format - * @param keyFile a PKCS#8 private key file in PEM format - * @param keyPassword the password of the {@code keyFile}. - * {@code null} if it's not password-protected. - * @return a new server-side {@link SslContext} - * @deprecated Replaced by {@link SslContextBuilder} - */ - @Deprecated - public static SslContext newServerContext( - File certChainFile, File keyFile, String keyPassword) throws SSLException { - return newServerContext(null, certChainFile, keyFile, keyPassword); - } - - /** - * Creates a new server-side {@link SslContext}. - * - * @param certChainFile an X.509 certificate chain file in PEM format - * @param keyFile a PKCS#8 private key file in PEM format - * @param keyPassword the password of the {@code keyFile}. - * {@code null} if it's not password-protected. - * @param ciphers the cipher suites to enable, in the order of preference. - * {@code null} to use the default cipher suites. - * @param nextProtocols the application layer protocols to accept, in the order of preference. - * {@code null} to disable TLS NPN/ALPN extension. - * @param sessionCacheSize the size of the cache used for storing SSL session objects. - * {@code 0} to use the default value. - * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. - * {@code 0} to use the default value. - * @return a new server-side {@link SslContext} - * @deprecated Replaced by {@link SslContextBuilder} - */ - @Deprecated - public static SslContext newServerContext( - File certChainFile, File keyFile, String keyPassword, - Iterable ciphers, Iterable nextProtocols, - long sessionCacheSize, long sessionTimeout) throws SSLException { - - return newServerContext( - null, certChainFile, keyFile, keyPassword, - ciphers, nextProtocols, sessionCacheSize, sessionTimeout); - } - - /** - * Creates a new server-side {@link SslContext}. - * - * @param certChainFile an X.509 certificate chain file in PEM format - * @param keyFile a PKCS#8 private key file in PEM format - * @param keyPassword the password of the {@code keyFile}. - * {@code null} if it's not password-protected. - * @param ciphers the cipher suites to enable, in the order of preference. - * {@code null} to use the default cipher suites. - * @param cipherFilter a filter to apply over the supplied list of ciphers - * @param apn Provides a means to configure parameters related to application protocol negotiation. - * @param sessionCacheSize the size of the cache used for storing SSL session objects. - * {@code 0} to use the default value. - * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. - * {@code 0} to use the default value. - * @return a new server-side {@link SslContext} - * @deprecated Replaced by {@link SslContextBuilder} - */ - @Deprecated - public static SslContext newServerContext( - File certChainFile, File keyFile, String keyPassword, - Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, - long sessionCacheSize, long sessionTimeout) throws SSLException { - return newServerContext( - null, certChainFile, keyFile, keyPassword, - ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout); - } - - /** - * Creates a new server-side {@link SslContext}. - * - * @param provider the {@link SslContext} implementation to use. - * {@code null} to use the current default one. - * @param certChainFile an X.509 certificate chain file in PEM format - * @param keyFile a PKCS#8 private key file in PEM format - * @return a new server-side {@link SslContext} - * @deprecated Replaced by {@link SslContextBuilder} - */ - @Deprecated - public static SslContext newServerContext( - SslProvider provider, File certChainFile, File keyFile) throws SSLException { - return newServerContext(provider, certChainFile, keyFile, null); - } - - /** - * Creates a new server-side {@link SslContext}. - * - * @param provider the {@link SslContext} implementation to use. - * {@code null} to use the current default one. - * @param certChainFile an X.509 certificate chain file in PEM format - * @param keyFile a PKCS#8 private key file in PEM format - * @param keyPassword the password of the {@code keyFile}. - * {@code null} if it's not password-protected. - * @return a new server-side {@link SslContext} - * @deprecated Replaced by {@link SslContextBuilder} - */ - @Deprecated - public static SslContext newServerContext( - SslProvider provider, File certChainFile, File keyFile, String keyPassword) throws SSLException { - return newServerContext(provider, certChainFile, keyFile, keyPassword, null, IdentityCipherSuiteFilter.INSTANCE, - null, 0, 0); - } - - /** - * Creates a new server-side {@link SslContext}. - * - * @param provider the {@link SslContext} implementation to use. - * {@code null} to use the current default one. - * @param certChainFile an X.509 certificate chain file in PEM format - * @param keyFile a PKCS#8 private key file in PEM format - * @param keyPassword the password of the {@code keyFile}. - * {@code null} if it's not password-protected. - * @param ciphers the cipher suites to enable, in the order of preference. - * {@code null} to use the default cipher suites. - * @param nextProtocols the application layer protocols to accept, in the order of preference. - * {@code null} to disable TLS NPN/ALPN extension. - * @param sessionCacheSize the size of the cache used for storing SSL session objects. - * {@code 0} to use the default value. - * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. - * {@code 0} to use the default value. - * @return a new server-side {@link SslContext} - * @deprecated Replaced by {@link SslContextBuilder} - */ - @Deprecated - public static SslContext newServerContext( - SslProvider provider, - File certChainFile, File keyFile, String keyPassword, - Iterable ciphers, Iterable nextProtocols, - long sessionCacheSize, long sessionTimeout) throws SSLException { - return newServerContext(provider, certChainFile, keyFile, keyPassword, - ciphers, IdentityCipherSuiteFilter.INSTANCE, - toApplicationProtocolConfig(nextProtocols), sessionCacheSize, sessionTimeout); - } - - /** - * Creates a new server-side {@link SslContext}. - * - * @param provider the {@link SslContext} implementation to use. - * {@code null} to use the current default one. - * @param certChainFile an X.509 certificate chain file in PEM format - * @param keyFile a PKCS#8 private key file in PEM format - * @param keyPassword the password of the {@code keyFile}. - * {@code null} if it's not password-protected. - * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s - * that verifies the certificates sent from servers. - * {@code null} to use the default. - * @param ciphers the cipher suites to enable, in the order of preference. - * {@code null} to use the default cipher suites. - * @param nextProtocols the application layer protocols to accept, in the order of preference. - * {@code null} to disable TLS NPN/ALPN extension. - * @param sessionCacheSize the size of the cache used for storing SSL session objects. - * {@code 0} to use the default value. - * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. - * {@code 0} to use the default value. - * @return a new server-side {@link SslContext} - * @deprecated Replaced by {@link SslContextBuilder} - */ - @Deprecated - public static SslContext newServerContext( - SslProvider provider, - File certChainFile, File keyFile, String keyPassword, TrustManagerFactory trustManagerFactory, - Iterable ciphers, Iterable nextProtocols, - long sessionCacheSize, long sessionTimeout) throws SSLException { - - return newServerContext( - provider, null, trustManagerFactory, certChainFile, keyFile, keyPassword, - null, ciphers, IdentityCipherSuiteFilter.INSTANCE, - toApplicationProtocolConfig(nextProtocols), sessionCacheSize, sessionTimeout); - } - - /** - * Creates a new server-side {@link SslContext}. - * - * @param provider the {@link SslContext} implementation to use. - * {@code null} to use the current default one. - * @param certChainFile an X.509 certificate chain file in PEM format - * @param keyFile a PKCS#8 private key file in PEM format - * @param keyPassword the password of the {@code keyFile}. - * {@code null} if it's not password-protected. - * @param ciphers the cipher suites to enable, in the order of preference. - * {@code null} to use the default cipher suites. - * @param cipherFilter a filter to apply over the supplied list of ciphers - * Only required if {@code provider} is {@link SslProvider#JDK} - * @param apn Provides a means to configure parameters related to application protocol negotiation. - * @param sessionCacheSize the size of the cache used for storing SSL session objects. - * {@code 0} to use the default value. - * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. - * {@code 0} to use the default value. - * @return a new server-side {@link SslContext} - * @deprecated Replaced by {@link SslContextBuilder} - */ - @Deprecated - public static SslContext newServerContext(SslProvider provider, - File certChainFile, File keyFile, String keyPassword, - Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, - long sessionCacheSize, long sessionTimeout) throws SSLException { - return newServerContext(provider, null, null, certChainFile, keyFile, keyPassword, null, - ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, KeyStore.getDefaultType()); - } - - /** - * Creates a new server-side {@link SslContext}. - * @param provider the {@link SslContext} implementation to use. - * {@code null} to use the current default one. - * @param trustCertCollectionFile an X.509 certificate collection file in PEM format. - * This provides the certificate collection used for mutual authentication. - * {@code null} to use the system default - * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s - * that verifies the certificates sent from clients. - * {@code null} to use the default or the results of parsing - * {@code trustCertCollectionFile}. - * This parameter is ignored if {@code provider} is not {@link SslProvider#JDK}. - * @param keyCertChainFile an X.509 certificate chain file in PEM format - * @param keyFile a PKCS#8 private key file in PEM format - * @param keyPassword the password of the {@code keyFile}. - * {@code null} if it's not password-protected. - * @param keyManagerFactory the {@link KeyManagerFactory} that provides the {@link KeyManager}s - * that is used to encrypt data being sent to clients. - * {@code null} to use the default or the results of parsing - * {@code keyCertChainFile} and {@code keyFile}. - * This parameter is ignored if {@code provider} is not {@link SslProvider#JDK}. - * @param ciphers the cipher suites to enable, in the order of preference. - * {@code null} to use the default cipher suites. - * @param cipherFilter a filter to apply over the supplied list of ciphers - * Only required if {@code provider} is {@link SslProvider#JDK} - * @param apn Provides a means to configure parameters related to application protocol negotiation. - * @param sessionCacheSize the size of the cache used for storing SSL session objects. - * {@code 0} to use the default value. - * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. - * {@code 0} to use the default value. - * @return a new server-side {@link SslContext} - * @deprecated Replaced by {@link SslContextBuilder} - */ - @Deprecated - public static SslContext newServerContext( - SslProvider provider, - File trustCertCollectionFile, TrustManagerFactory trustManagerFactory, - File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory, - Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, - long sessionCacheSize, long sessionTimeout) throws SSLException { - return newServerContext(provider, trustCertCollectionFile, trustManagerFactory, keyCertChainFile, - keyFile, keyPassword, keyManagerFactory, ciphers, cipherFilter, apn, - sessionCacheSize, sessionTimeout, KeyStore.getDefaultType()); - } - - /** - * Creates a new server-side {@link SslContext}. - * @param provider the {@link SslContext} implementation to use. - * {@code null} to use the current default one. - * @param trustCertCollectionFile an X.509 certificate collection file in PEM format. - * This provides the certificate collection used for mutual authentication. - * {@code null} to use the system default - * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s - * that verifies the certificates sent from clients. - * {@code null} to use the default or the results of parsing - * {@code trustCertCollectionFile}. - * This parameter is ignored if {@code provider} is not {@link SslProvider#JDK}. - * @param keyCertChainFile an X.509 certificate chain file in PEM format - * @param keyFile a PKCS#8 private key file in PEM format - * @param keyPassword the password of the {@code keyFile}. - * {@code null} if it's not password-protected. - * @param keyManagerFactory the {@link KeyManagerFactory} that provides the {@link KeyManager}s - * that is used to encrypt data being sent to clients. - * {@code null} to use the default or the results of parsing - * {@code keyCertChainFile} and {@code keyFile}. - * This parameter is ignored if {@code provider} is not {@link SslProvider#JDK}. - * @param ciphers the cipher suites to enable, in the order of preference. - * {@code null} to use the default cipher suites. - * @param cipherFilter a filter to apply over the supplied list of ciphers - * Only required if {@code provider} is {@link SslProvider#JDK} - * @param apn Provides a means to configure parameters related to application protocol negotiation. - * @param sessionCacheSize the size of the cache used for storing SSL session objects. - * {@code 0} to use the default value. - * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. - * {@code 0} to use the default value. - * @param keyStore the keystore type that should be used - * @return a new server-side {@link SslContext} - */ - static SslContext newServerContext( - SslProvider provider, - File trustCertCollectionFile, TrustManagerFactory trustManagerFactory, - File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory, - Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, - long sessionCacheSize, long sessionTimeout, String keyStore) throws SSLException { - try { - return newServerContextInternal(provider, null, toX509Certificates(trustCertCollectionFile), - trustManagerFactory, toX509Certificates(keyCertChainFile), - toPrivateKey(keyFile, keyPassword), - keyPassword, keyManagerFactory, ciphers, cipherFilter, apn, - sessionCacheSize, sessionTimeout, ClientAuth.NONE, null, - false, false, keyStore); - } catch (Exception e) { - if (e instanceof SSLException) { - throw (SSLException) e; - } - throw new SSLException("failed to initialize the server-side SSL context", e); - } - } - - static SslContext newServerContextInternal( - SslProvider provider, - Provider sslContextProvider, - X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory, - X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory, - Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, - long sessionCacheSize, long sessionTimeout, ClientAuth clientAuth, String[] protocols, boolean startTls, - boolean enableOcsp, String keyStoreType, Map.Entry, Object>... ctxOptions) - throws SSLException { - - if (provider == null) { - provider = defaultServerProvider(); - } - - switch (provider) { - case JDK: - if (enableOcsp) { - throw new IllegalArgumentException("OCSP is not supported with this SslProvider: " + provider); - } - return new JdkSslServerContext(sslContextProvider, - trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword, - keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, - clientAuth, protocols, startTls, keyStoreType); - case OPENSSL: - verifyNullSslContextProvider(provider, sslContextProvider); - return new OpenSslServerContext( - trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword, - keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, - clientAuth, protocols, startTls, enableOcsp, keyStoreType, ctxOptions); - case OPENSSL_REFCNT: - verifyNullSslContextProvider(provider, sslContextProvider); - return new ReferenceCountedOpenSslServerContext( - trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword, - keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, - clientAuth, protocols, startTls, enableOcsp, keyStoreType, ctxOptions); - default: - throw new Error(provider.toString()); - } - } - - private static void verifyNullSslContextProvider(SslProvider provider, Provider sslContextProvider) { - if (sslContextProvider != null) { - throw new IllegalArgumentException("Java Security Provider unsupported for SslProvider: " + provider); - } - } - - /** - * Creates a new client-side {@link SslContext}. - * - * @return a new client-side {@link SslContext} - * @deprecated Replaced by {@link SslContextBuilder} - */ - @Deprecated - public static SslContext newClientContext() throws SSLException { - return newClientContext(null, null, null); - } - - /** - * Creates a new client-side {@link SslContext}. - * - * @param certChainFile an X.509 certificate chain file in PEM format - * - * @return a new client-side {@link SslContext} - * @deprecated Replaced by {@link SslContextBuilder} - */ - @Deprecated - public static SslContext newClientContext(File certChainFile) throws SSLException { - return newClientContext(null, certChainFile); - } - - /** - * Creates a new client-side {@link SslContext}. - * - * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s - * that verifies the certificates sent from servers. - * {@code null} to use the default. - * - * @return a new client-side {@link SslContext} - * @deprecated Replaced by {@link SslContextBuilder} - */ - @Deprecated - public static SslContext newClientContext(TrustManagerFactory trustManagerFactory) throws SSLException { - return newClientContext(null, null, trustManagerFactory); - } - - /** - * Creates a new client-side {@link SslContext}. - * - * @param certChainFile an X.509 certificate chain file in PEM format. - * {@code null} to use the system default - * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s - * that verifies the certificates sent from servers. - * {@code null} to use the default. - * - * @return a new client-side {@link SslContext} - * @deprecated Replaced by {@link SslContextBuilder} - */ - @Deprecated - public static SslContext newClientContext( - File certChainFile, TrustManagerFactory trustManagerFactory) throws SSLException { - return newClientContext(null, certChainFile, trustManagerFactory); - } - - /** - * Creates a new client-side {@link SslContext}. - * - * @param certChainFile an X.509 certificate chain file in PEM format. - * {@code null} to use the system default - * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s - * that verifies the certificates sent from servers. - * {@code null} to use the default. - * @param ciphers the cipher suites to enable, in the order of preference. - * {@code null} to use the default cipher suites. - * @param nextProtocols the application layer protocols to accept, in the order of preference. - * {@code null} to disable TLS NPN/ALPN extension. - * @param sessionCacheSize the size of the cache used for storing SSL session objects. - * {@code 0} to use the default value. - * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. - * {@code 0} to use the default value. - * - * @return a new client-side {@link SslContext} - * @deprecated Replaced by {@link SslContextBuilder} - */ - @Deprecated - public static SslContext newClientContext( - File certChainFile, TrustManagerFactory trustManagerFactory, - Iterable ciphers, Iterable nextProtocols, - long sessionCacheSize, long sessionTimeout) throws SSLException { - return newClientContext( - null, certChainFile, trustManagerFactory, - ciphers, nextProtocols, sessionCacheSize, sessionTimeout); - } - - /** - * Creates a new client-side {@link SslContext}. - * - * @param certChainFile an X.509 certificate chain file in PEM format. - * {@code null} to use the system default - * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s - * that verifies the certificates sent from servers. - * {@code null} to use the default. - * @param ciphers the cipher suites to enable, in the order of preference. - * {@code null} to use the default cipher suites. - * @param cipherFilter a filter to apply over the supplied list of ciphers - * @param apn Provides a means to configure parameters related to application protocol negotiation. - * @param sessionCacheSize the size of the cache used for storing SSL session objects. - * {@code 0} to use the default value. - * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. - * {@code 0} to use the default value. - * - * @return a new client-side {@link SslContext} - * @deprecated Replaced by {@link SslContextBuilder} - */ - @Deprecated - public static SslContext newClientContext( - File certChainFile, TrustManagerFactory trustManagerFactory, - Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, - long sessionCacheSize, long sessionTimeout) throws SSLException { - return newClientContext( - null, certChainFile, trustManagerFactory, - ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout); - } - - /** - * Creates a new client-side {@link SslContext}. - * - * @param provider the {@link SslContext} implementation to use. - * {@code null} to use the current default one. - * - * @return a new client-side {@link SslContext} - * @deprecated Replaced by {@link SslContextBuilder} - */ - @Deprecated - public static SslContext newClientContext(SslProvider provider) throws SSLException { - return newClientContext(provider, null, null); - } - - /** - * Creates a new client-side {@link SslContext}. - * - * @param provider the {@link SslContext} implementation to use. - * {@code null} to use the current default one. - * @param certChainFile an X.509 certificate chain file in PEM format. - * {@code null} to use the system default - * - * @return a new client-side {@link SslContext} - * @deprecated Replaced by {@link SslContextBuilder} - */ - @Deprecated - public static SslContext newClientContext(SslProvider provider, File certChainFile) throws SSLException { - return newClientContext(provider, certChainFile, null); - } - - /** - * Creates a new client-side {@link SslContext}. - * - * @param provider the {@link SslContext} implementation to use. - * {@code null} to use the current default one. - * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s - * that verifies the certificates sent from servers. - * {@code null} to use the default. - * - * @return a new client-side {@link SslContext} - * @deprecated Replaced by {@link SslContextBuilder} - */ - @Deprecated - public static SslContext newClientContext( - SslProvider provider, TrustManagerFactory trustManagerFactory) throws SSLException { - return newClientContext(provider, null, trustManagerFactory); - } - - /** - * Creates a new client-side {@link SslContext}. - * - * @param provider the {@link SslContext} implementation to use. - * {@code null} to use the current default one. - * @param certChainFile an X.509 certificate chain file in PEM format. - * {@code null} to use the system default - * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s - * that verifies the certificates sent from servers. - * {@code null} to use the default. - * - * @return a new client-side {@link SslContext} - * @deprecated Replaced by {@link SslContextBuilder} - */ - @Deprecated - public static SslContext newClientContext( - SslProvider provider, File certChainFile, TrustManagerFactory trustManagerFactory) throws SSLException { - return newClientContext(provider, certChainFile, trustManagerFactory, null, IdentityCipherSuiteFilter.INSTANCE, - null, 0, 0); - } - - /** - * Creates a new client-side {@link SslContext}. - * - * @param provider the {@link SslContext} implementation to use. - * {@code null} to use the current default one. - * @param certChainFile an X.509 certificate chain file in PEM format. - * {@code null} to use the system default - * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s - * that verifies the certificates sent from servers. - * {@code null} to use the default. - * @param ciphers the cipher suites to enable, in the order of preference. - * {@code null} to use the default cipher suites. - * @param nextProtocols the application layer protocols to accept, in the order of preference. - * {@code null} to disable TLS NPN/ALPN extension. - * @param sessionCacheSize the size of the cache used for storing SSL session objects. - * {@code 0} to use the default value. - * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. - * {@code 0} to use the default value. - * - * @return a new client-side {@link SslContext} - * @deprecated Replaced by {@link SslContextBuilder} - */ - @Deprecated - public static SslContext newClientContext( - SslProvider provider, - File certChainFile, TrustManagerFactory trustManagerFactory, - Iterable ciphers, Iterable nextProtocols, - long sessionCacheSize, long sessionTimeout) throws SSLException { - return newClientContext( - provider, certChainFile, trustManagerFactory, null, null, null, null, - ciphers, IdentityCipherSuiteFilter.INSTANCE, - toApplicationProtocolConfig(nextProtocols), sessionCacheSize, sessionTimeout); - } - - /** - * Creates a new client-side {@link SslContext}. - * - * @param provider the {@link SslContext} implementation to use. - * {@code null} to use the current default one. - * @param certChainFile an X.509 certificate chain file in PEM format. - * {@code null} to use the system default - * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s - * that verifies the certificates sent from servers. - * {@code null} to use the default. - * @param ciphers the cipher suites to enable, in the order of preference. - * {@code null} to use the default cipher suites. - * @param cipherFilter a filter to apply over the supplied list of ciphers - * @param apn Provides a means to configure parameters related to application protocol negotiation. - * @param sessionCacheSize the size of the cache used for storing SSL session objects. - * {@code 0} to use the default value. - * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. - * {@code 0} to use the default value. - * - * @return a new client-side {@link SslContext} - * @deprecated Replaced by {@link SslContextBuilder} - */ - @Deprecated - public static SslContext newClientContext( - SslProvider provider, - File certChainFile, TrustManagerFactory trustManagerFactory, - Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, - long sessionCacheSize, long sessionTimeout) throws SSLException { - - return newClientContext( - provider, certChainFile, trustManagerFactory, null, null, null, null, - ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout); - } - - /** - * Creates a new client-side {@link SslContext}. - * @param provider the {@link SslContext} implementation to use. - * {@code null} to use the current default one. - * @param trustCertCollectionFile an X.509 certificate collection file in PEM format. - * {@code null} to use the system default - * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s - * that verifies the certificates sent from servers. - * {@code null} to use the default or the results of parsing - * {@code trustCertCollectionFile}. - * This parameter is ignored if {@code provider} is not {@link SslProvider#JDK}. - * @param keyCertChainFile an X.509 certificate chain file in PEM format. - * This provides the public key for mutual authentication. - * {@code null} to use the system default - * @param keyFile a PKCS#8 private key file in PEM format. - * This provides the private key for mutual authentication. - * {@code null} for no mutual authentication. - * @param keyPassword the password of the {@code keyFile}. - * {@code null} if it's not password-protected. - * Ignored if {@code keyFile} is {@code null}. - * @param keyManagerFactory the {@link KeyManagerFactory} that provides the {@link KeyManager}s - * that is used to encrypt data being sent to servers. - * {@code null} to use the default or the results of parsing - * {@code keyCertChainFile} and {@code keyFile}. - * This parameter is ignored if {@code provider} is not {@link SslProvider#JDK}. - * @param ciphers the cipher suites to enable, in the order of preference. - * {@code null} to use the default cipher suites. - * @param cipherFilter a filter to apply over the supplied list of ciphers - * @param apn Provides a means to configure parameters related to application protocol negotiation. - * @param sessionCacheSize the size of the cache used for storing SSL session objects. - * {@code 0} to use the default value. - * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. - * {@code 0} to use the default value. - * - * @return a new client-side {@link SslContext} - * @deprecated Replaced by {@link SslContextBuilder} - */ - @Deprecated - public static SslContext newClientContext( - SslProvider provider, - File trustCertCollectionFile, TrustManagerFactory trustManagerFactory, - File keyCertChainFile, File keyFile, String keyPassword, - KeyManagerFactory keyManagerFactory, - Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, - long sessionCacheSize, long sessionTimeout) throws SSLException { - try { - return newClientContextInternal(provider, null, - toX509Certificates(trustCertCollectionFile), trustManagerFactory, - toX509Certificates(keyCertChainFile), toPrivateKey(keyFile, keyPassword), - keyPassword, keyManagerFactory, ciphers, cipherFilter, - apn, null, sessionCacheSize, sessionTimeout, false, - KeyStore.getDefaultType()); - } catch (Exception e) { - if (e instanceof SSLException) { - throw (SSLException) e; - } - throw new SSLException("failed to initialize the client-side SSL context", e); - } - } - - static SslContext newClientContextInternal( - SslProvider provider, - Provider sslContextProvider, - X509Certificate[] trustCert, TrustManagerFactory trustManagerFactory, - X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory, - Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, String[] protocols, - long sessionCacheSize, long sessionTimeout, boolean enableOcsp, String keyStoreType, - Map.Entry, Object>... options) throws SSLException { - if (provider == null) { - provider = defaultClientProvider(); - } - switch (provider) { - case JDK: - if (enableOcsp) { - throw new IllegalArgumentException("OCSP is not supported with this SslProvider: " + provider); - } - return new JdkSslClientContext(sslContextProvider, - trustCert, trustManagerFactory, keyCertChain, key, keyPassword, - keyManagerFactory, ciphers, cipherFilter, apn, protocols, sessionCacheSize, - sessionTimeout, keyStoreType); - case OPENSSL: - verifyNullSslContextProvider(provider, sslContextProvider); - OpenSsl.ensureAvailability(); - return new OpenSslClientContext( - trustCert, trustManagerFactory, keyCertChain, key, keyPassword, - keyManagerFactory, ciphers, cipherFilter, apn, protocols, sessionCacheSize, sessionTimeout, - enableOcsp, keyStoreType, options); - case OPENSSL_REFCNT: - verifyNullSslContextProvider(provider, sslContextProvider); - OpenSsl.ensureAvailability(); - return new ReferenceCountedOpenSslClientContext( - trustCert, trustManagerFactory, keyCertChain, key, keyPassword, - keyManagerFactory, ciphers, cipherFilter, apn, protocols, sessionCacheSize, sessionTimeout, - enableOcsp, keyStoreType, options); - default: - throw new Error(provider.toString()); - } - } - - static ApplicationProtocolConfig toApplicationProtocolConfig(Iterable nextProtocols) { - ApplicationProtocolConfig apn; - if (nextProtocols == null) { - apn = ApplicationProtocolConfig.DISABLED; - } else { - apn = new ApplicationProtocolConfig( - Protocol.NPN_AND_ALPN, SelectorFailureBehavior.CHOOSE_MY_LAST_PROTOCOL, - SelectedListenerFailureBehavior.ACCEPT, nextProtocols); - } - return apn; - } - - /** - * Creates a new instance (startTls set to {@code false}). - */ - protected SslContext() { - this(false); - } - - /** - * Creates a new instance. - */ - protected SslContext(boolean startTls) { - this.startTls = startTls; - } - - /** - * Returns the {@link AttributeMap} that belongs to this {@link SslContext} . - */ - public final AttributeMap attributes() { - return attributes; - } - - /** - * Returns {@code true} if and only if this context is for server-side. - */ - public final boolean isServer() { - return !isClient(); - } - - /** - * Returns the {@code true} if and only if this context is for client-side. - */ - public abstract boolean isClient(); - - /** - * Returns the list of enabled cipher suites, in the order of preference. - */ - public abstract List cipherSuites(); - - /** - * Returns the size of the cache used for storing SSL session objects. - */ - public long sessionCacheSize() { - return sessionContext().getSessionCacheSize(); - } - - /** - * Returns the timeout for the cached SSL session objects, in seconds. - */ - public long sessionTimeout() { - return sessionContext().getSessionTimeout(); - } - - /** - * @deprecated Use {@link #applicationProtocolNegotiator()} instead. - */ - @Deprecated - public final List nextProtocols() { - return applicationProtocolNegotiator().protocols(); - } - - /** - * Returns the object responsible for negotiating application layer protocols for the TLS NPN/ALPN extensions. - */ - public abstract ApplicationProtocolNegotiator applicationProtocolNegotiator(); - - /** - * Creates a new {@link SSLEngine}. - *

If {@link SslProvider#OPENSSL_REFCNT} is used then the object must be released. One way to do this is to - * wrap in a {@link SslHandler} and insert it into a pipeline. See {@link #newHandler(ByteBufAllocator)}. - * @return a new {@link SSLEngine} - */ - public abstract SSLEngine newEngine(ByteBufAllocator alloc); - - /** - * Creates a new {@link SSLEngine} using advisory peer information. - *

If {@link SslProvider#OPENSSL_REFCNT} is used then the object must be released. One way to do this is to - * wrap in a {@link SslHandler} and insert it into a pipeline. - * See {@link #newHandler(ByteBufAllocator, String, int)}. - * @param peerHost the non-authoritative name of the host - * @param peerPort the non-authoritative port - * - * @return a new {@link SSLEngine} - */ - public abstract SSLEngine newEngine(ByteBufAllocator alloc, String peerHost, int peerPort); - - /** - * Returns the {@link SSLSessionContext} object held by this context. - */ - 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 - * that is wrapped. If the returned {@link SslHandler} is not inserted into a pipeline then you may leak native - * memory! - *

Beware: the underlying generated {@link SSLEngine} won't have - * hostname verification enabled by default. - * If you create {@link SslHandler} for the client side and want proper security, we advice that you configure - * the {@link SSLEngine} (see {@link javax.net.ssl.SSLParameters#setEndpointIdentificationAlgorithm(String)}): - *

-     * SSLEngine sslEngine = sslHandler.engine();
-     * SSLParameters sslParameters = sslEngine.getSSLParameters();
-     * // only available since Java 7
-     * sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
-     * sslEngine.setSSLParameters(sslParameters);
-     * 
- *

- * The underlying {@link SSLEngine} may not follow the restrictions imposed by the - * 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 SslHandler newHandler(ByteBufAllocator alloc, Executor delegatedTaskExecutor) { - return newHandler(alloc, startTls, delegatedTaskExecutor); - } - - /** - * Create a new SslHandler. - * @see #newHandler(ByteBufAllocator, String, int, boolean, Executor) - */ - 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); - } - - /** - * Creates a new {@link SslHandler} with advisory peer information. - *

If {@link SslProvider#OPENSSL_REFCNT} is used then the returned {@link SslHandler} will release the engine - * that is wrapped. If the returned {@link SslHandler} is not inserted into a pipeline then you may leak native - * memory! - *

Beware: the underlying generated {@link SSLEngine} won't have - * hostname verification enabled by default. - * If you create {@link SslHandler} for the client side and want proper security, we advice that you configure - * the {@link SSLEngine} (see {@link javax.net.ssl.SSLParameters#setEndpointIdentificationAlgorithm(String)}): - *

-     * SSLEngine sslEngine = sslHandler.engine();
-     * SSLParameters sslParameters = sslEngine.getSSLParameters();
-     * // only available since Java 7
-     * sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
-     * sslEngine.setSSLParameters(sslParameters);
-     * 
- *

- * The underlying {@link SSLEngine} may not follow the restrictions imposed by the - * 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 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 SslHandler newHandler(ByteBufAllocator alloc, String peerHost, int peerPort, - Executor delegatedTaskExecutor) { - return newHandler(alloc, peerHost, peerPort, startTls, delegatedTaskExecutor); - } - - protected SslHandler newHandler(ByteBufAllocator alloc, String peerHost, int peerPort, boolean startTls, - Executor delegatedTaskExecutor) { - return new SslHandler(newEngine(alloc, peerHost, peerPort), startTls, delegatedTaskExecutor); - } - - /** - * Generates a key specification for an (encrypted) private key. - * - * @param password characters, if {@code null} an unencrypted key is assumed - * @param key bytes of the DER encoded private key - * - * @return a key specification - * - * @throws IOException if parsing {@code key} fails - * @throws NoSuchAlgorithmException if the algorithm used to encrypt {@code key} is unknown - * @throws NoSuchPaddingException if the padding scheme specified in the decryption algorithm is unknown - * @throws InvalidKeySpecException if the decryption key based on {@code password} cannot be generated - * @throws InvalidKeyException if the decryption key based on {@code password} cannot be used to decrypt - * {@code key} - * @throws InvalidAlgorithmParameterException if decryption algorithm parameters are somehow faulty - */ - @Deprecated - protected static PKCS8EncodedKeySpec generateKeySpec(char[] password, byte[] key) - throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException, - InvalidKeyException, InvalidAlgorithmParameterException { - - if (password == null) { - return new PKCS8EncodedKeySpec(key); - } - - EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = new EncryptedPrivateKeyInfo(key); - SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(encryptedPrivateKeyInfo.getAlgName()); - PBEKeySpec pbeKeySpec = new PBEKeySpec(password); - SecretKey pbeKey = keyFactory.generateSecret(pbeKeySpec); - - Cipher cipher = Cipher.getInstance(encryptedPrivateKeyInfo.getAlgName()); - cipher.init(Cipher.DECRYPT_MODE, pbeKey, encryptedPrivateKeyInfo.getAlgParameters()); - - return encryptedPrivateKeyInfo.getKeySpec(cipher); - } - - /** - * Generates a new {@link KeyStore}. - * - * @param certChain an X.509 certificate chain - * @param key a PKCS#8 private key - * @param keyPasswordChars the password of the {@code keyFile}. - * {@code null} if it's not password-protected. - * @param keyStoreType The KeyStore Type you want to use - * @return generated {@link KeyStore}. - */ - protected static KeyStore buildKeyStore(X509Certificate[] certChain, PrivateKey key, - char[] keyPasswordChars, String keyStoreType) - throws KeyStoreException, NoSuchAlgorithmException, - CertificateException, IOException { - if (keyStoreType == null) { - keyStoreType = KeyStore.getDefaultType(); - } - KeyStore ks = KeyStore.getInstance(keyStoreType); - ks.load(null, null); - ks.setKeyEntry(ALIAS, key, keyPasswordChars, certChain); - return ks; - } - - protected static PrivateKey toPrivateKey(File keyFile, String keyPassword) throws NoSuchAlgorithmException, - NoSuchPaddingException, InvalidKeySpecException, - InvalidAlgorithmParameterException, - KeyException, IOException { - if (keyFile == null) { - return null; - } - return getPrivateKeyFromByteBuffer(PemReader.readPrivateKey(keyFile), keyPassword); - } - - protected static PrivateKey toPrivateKey(InputStream keyInputStream, String keyPassword) - throws NoSuchAlgorithmException, - NoSuchPaddingException, InvalidKeySpecException, - InvalidAlgorithmParameterException, - KeyException, IOException { - if (keyInputStream == null) { - return null; - } - return getPrivateKeyFromByteBuffer(PemReader.readPrivateKey(keyInputStream), keyPassword); - } - - private static PrivateKey getPrivateKeyFromByteBuffer(ByteBuf encodedKeyBuf, String keyPassword) - throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException, - InvalidAlgorithmParameterException, KeyException, IOException { - - byte[] encodedKey = new byte[encodedKeyBuf.readableBytes()]; - encodedKeyBuf.readBytes(encodedKey).release(); - - PKCS8EncodedKeySpec encodedKeySpec = generateKeySpec( - keyPassword == null ? null : keyPassword.toCharArray(), encodedKey); - try { - return KeyFactory.getInstance("RSA").generatePrivate(encodedKeySpec); - } catch (InvalidKeySpecException ignore) { - try { - return KeyFactory.getInstance("DSA").generatePrivate(encodedKeySpec); - } catch (InvalidKeySpecException ignore2) { - try { - return KeyFactory.getInstance("EC").generatePrivate(encodedKeySpec); - } catch (InvalidKeySpecException e) { - throw new InvalidKeySpecException("Neither RSA, DSA nor EC worked", e); - } - } - } - } - - /** - * Build a {@link TrustManagerFactory} from a certificate chain file. - * @param certChainFile The certificate file to build from. - * @param trustManagerFactory The existing {@link TrustManagerFactory} that will be used if not {@code null}. - * @return A {@link TrustManagerFactory} which contains the certificates in {@code certChainFile} - */ - @Deprecated - protected static TrustManagerFactory buildTrustManagerFactory( - File certChainFile, TrustManagerFactory trustManagerFactory) - throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException { - return buildTrustManagerFactory(certChainFile, trustManagerFactory, null); - } - - /** - * Build a {@link TrustManagerFactory} from a certificate chain file. - * @param certChainFile The certificate file to build from. - * @param trustManagerFactory The existing {@link TrustManagerFactory} that will be used if not {@code null}. - * @param keyType The KeyStore Type you want to use - * @return A {@link TrustManagerFactory} which contains the certificates in {@code certChainFile} - */ - protected static TrustManagerFactory buildTrustManagerFactory( - File certChainFile, TrustManagerFactory trustManagerFactory, String keyType) - throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException { - X509Certificate[] x509Certs = toX509Certificates(certChainFile); - - return buildTrustManagerFactory(x509Certs, trustManagerFactory, keyType); - } - - protected static X509Certificate[] toX509Certificates(File file) throws CertificateException { - if (file == null) { - return null; - } - return getCertificatesFromBuffers(PemReader.readCertificates(file)); - } - - protected static X509Certificate[] toX509Certificates(InputStream in) throws CertificateException { - if (in == null) { - return null; - } - return getCertificatesFromBuffers(PemReader.readCertificates(in)); - } - - private static X509Certificate[] getCertificatesFromBuffers(ByteBuf[] certs) throws CertificateException { - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - X509Certificate[] x509Certs = new X509Certificate[certs.length]; - - try { - for (int i = 0; i < certs.length; i++) { - InputStream is = new ByteBufInputStream(certs[i], false); - try { - x509Certs[i] = (X509Certificate) cf.generateCertificate(is); - } finally { - try { - is.close(); - } catch (IOException e) { - // This is not expected to happen, but re-throw in case it does. - throw new RuntimeException(e); - } - } - } - } finally { - for (ByteBuf buf: certs) { - buf.release(); - } - } - return x509Certs; - } - - protected static TrustManagerFactory buildTrustManagerFactory( - X509Certificate[] certCollection, TrustManagerFactory trustManagerFactory, String keyStoreType) - throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException { - if (keyStoreType == null) { - keyStoreType = KeyStore.getDefaultType(); - } - final KeyStore ks = KeyStore.getInstance(keyStoreType); - ks.load(null, null); - - int i = 1; - for (X509Certificate cert: certCollection) { - String alias = Integer.toString(i); - ks.setCertificateEntry(alias, cert); - i++; - } - - // Set up trust manager factory to use our key store. - if (trustManagerFactory == null) { - trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); - } - trustManagerFactory.init(ks); - - return trustManagerFactory; - } - - static PrivateKey toPrivateKeyInternal(File keyFile, String keyPassword) throws SSLException { - try { - return toPrivateKey(keyFile, keyPassword); - } catch (Exception e) { - throw new SSLException(e); - } - } - - static X509Certificate[] toX509CertificatesInternal(File file) throws SSLException { - try { - return toX509Certificates(file); - } catch (CertificateException e) { - throw new SSLException(e); - } - } - - protected static KeyManagerFactory buildKeyManagerFactory(X509Certificate[] certChainFile, - String keyAlgorithm, PrivateKey key, - String keyPassword, KeyManagerFactory kmf, - String keyStore) - throws KeyStoreException, NoSuchAlgorithmException, IOException, - CertificateException, UnrecoverableKeyException { - if (keyAlgorithm == null) { - keyAlgorithm = KeyManagerFactory.getDefaultAlgorithm(); - } - char[] keyPasswordChars = keyStorePassword(keyPassword); - KeyStore ks = buildKeyStore(certChainFile, key, keyPasswordChars, keyStore); - 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) { - if (keyAlgorithm == null) { - keyAlgorithm = KeyManagerFactory.getDefaultAlgorithm(); - } - kmf = KeyManagerFactory.getInstance(keyAlgorithm); - } - kmf.init(ks, keyPasswordChars); - - 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 deleted file mode 100644 index 6add78a84d..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/SslContextBuilder.java +++ /dev/null @@ -1,629 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.ssl; - -import io.netty.handler.ssl.util.KeyManagerFactoryWrapper; -import io.netty.handler.ssl.util.TrustManagerFactoryWrapper; -import io.netty.util.internal.UnstableApi; - -import javax.net.ssl.KeyManager; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLException; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import java.io.File; -import java.io.InputStream; -import java.security.KeyStore; -import java.security.PrivateKey; -import java.security.Provider; -import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static io.netty.util.internal.EmptyArrays.EMPTY_STRINGS; -import static io.netty.util.internal.EmptyArrays.EMPTY_X509_CERTIFICATES; -import static io.netty.util.internal.ObjectUtil.checkNotNullWithIAE; -import static io.netty.util.internal.ObjectUtil.checkNonEmpty; -import static java.util.Objects.requireNonNull; - -/** - * Builder for configuring a new SslContext for creation. - */ -public final class SslContextBuilder { - @SuppressWarnings("rawtypes") - private static final Map.Entry[] EMPTY_ENTRIES = new Map.Entry[0]; - - /** - * Creates a builder for new client-side {@link SslContext}. - */ - public static SslContextBuilder forClient() { - return new SslContextBuilder(false); - } - - /** - * Creates a builder for new server-side {@link SslContext}. - * - * @param keyCertChainFile an X.509 certificate chain file in PEM format - * @param keyFile a PKCS#8 private key file in PEM format - * @see #keyManager(File, File) - */ - public static SslContextBuilder forServer(File keyCertChainFile, File keyFile) { - return new SslContextBuilder(true).keyManager(keyCertChainFile, keyFile); - } - - /** - * Creates a builder for new server-side {@link SslContext}. - * - * @param keyCertChainInputStream an input stream for an X.509 certificate chain in PEM format. The caller is - * responsible for calling {@link InputStream#close()} after {@link #build()} - * has been called. - * @param keyInputStream an input stream for a PKCS#8 private key in PEM format. The caller is - * responsible for calling {@link InputStream#close()} after {@link #build()} - * has been called. - * - * @see #keyManager(InputStream, InputStream) - */ - public static SslContextBuilder forServer(InputStream keyCertChainInputStream, InputStream keyInputStream) { - return new SslContextBuilder(true).keyManager(keyCertChainInputStream, keyInputStream); - } - - /** - * Creates a builder for new server-side {@link SslContext}. - * - * @param key a PKCS#8 private key - * @param keyCertChain the X.509 certificate chain - * @see #keyManager(PrivateKey, X509Certificate[]) - */ - public static SslContextBuilder forServer(PrivateKey key, X509Certificate... keyCertChain) { - return new SslContextBuilder(true).keyManager(key, keyCertChain); - } - - /** - * Creates a builder for new server-side {@link SslContext}. - * - * @param key a PKCS#8 private key - * @param keyCertChain the X.509 certificate chain - * @see #keyManager(PrivateKey, X509Certificate[]) - */ - public static SslContextBuilder forServer(PrivateKey key, Iterable keyCertChain) { - return forServer(key, toArray(keyCertChain, EMPTY_X509_CERTIFICATES)); - } - - /** - * Creates a builder for new server-side {@link SslContext}. - * - * @param keyCertChainFile an X.509 certificate chain file in PEM format - * @param keyFile a PKCS#8 private key file in PEM format - * @param keyPassword the password of the {@code keyFile}, or {@code null} if it's not - * password-protected - * @see #keyManager(File, File, String) - */ - public static SslContextBuilder forServer( - File keyCertChainFile, File keyFile, String keyPassword) { - return new SslContextBuilder(true).keyManager(keyCertChainFile, keyFile, keyPassword); - } - - /** - * Creates a builder for new server-side {@link SslContext}. - * - * @param keyCertChainInputStream an input stream for an X.509 certificate chain in PEM format. The caller is - * responsible for calling {@link InputStream#close()} after {@link #build()} - * has been called. - * @param keyInputStream an input stream for a PKCS#8 private key in PEM format. The caller is - * responsible for calling {@link InputStream#close()} after {@link #build()} - * has been called. - * @param keyPassword the password of the {@code keyFile}, or {@code null} if it's not - * password-protected - * @see #keyManager(InputStream, InputStream, String) - */ - public static SslContextBuilder forServer( - InputStream keyCertChainInputStream, InputStream keyInputStream, String keyPassword) { - return new SslContextBuilder(true).keyManager(keyCertChainInputStream, keyInputStream, keyPassword); - } - - /** - * Creates a builder for new server-side {@link SslContext}. - * - * @param key a PKCS#8 private key - * @param keyCertChain the X.509 certificate chain - * @param keyPassword the password of the {@code keyFile}, or {@code null} if it's not - * password-protected - * @see #keyManager(File, File, String) - */ - public static SslContextBuilder forServer( - PrivateKey key, String keyPassword, X509Certificate... keyCertChain) { - return new SslContextBuilder(true).keyManager(key, keyPassword, keyCertChain); - } - - /** - * Creates a builder for new server-side {@link SslContext}. - * - * @param key a PKCS#8 private key - * @param keyCertChain the X.509 certificate chain - * @param keyPassword the password of the {@code keyFile}, or {@code null} if it's not - * password-protected - * @see #keyManager(File, File, String) - */ - public static SslContextBuilder forServer( - PrivateKey key, String keyPassword, Iterable keyCertChain) { - return forServer(key, keyPassword, toArray(keyCertChain, EMPTY_X509_CERTIFICATES)); - } - - /** - * Creates a builder for new server-side {@link SslContext}. - * - * If you use {@link SslProvider#OPENSSL} or {@link SslProvider#OPENSSL_REFCNT} consider using - * {@link OpenSslX509KeyManagerFactory} or {@link OpenSslCachingX509KeyManagerFactory}. - * - * @param keyManagerFactory non-{@code null} factory for server's private key - * @see #keyManager(KeyManagerFactory) - */ - public static SslContextBuilder forServer(KeyManagerFactory keyManagerFactory) { - return new SslContextBuilder(true).keyManager(keyManagerFactory); - } - - /** - * Creates a builder for new server-side {@link SslContext} with {@link KeyManager}. - * - * @param keyManager non-{@code null} KeyManager for server's private key - */ - public static SslContextBuilder forServer(KeyManager keyManager) { - return new SslContextBuilder(true).keyManager(keyManager); - } - - private final boolean forServer; - private SslProvider provider; - private Provider sslContextProvider; - private X509Certificate[] trustCertCollection; - private TrustManagerFactory trustManagerFactory; - private X509Certificate[] keyCertChain; - private PrivateKey key; - private String keyPassword; - private KeyManagerFactory keyManagerFactory; - private Iterable ciphers; - private CipherSuiteFilter cipherFilter = IdentityCipherSuiteFilter.INSTANCE; - private ApplicationProtocolConfig apn; - private long sessionCacheSize; - private long sessionTimeout; - private ClientAuth clientAuth = ClientAuth.NONE; - private String[] protocols; - private boolean startTls; - private boolean enableOcsp; - private String keyStoreType = KeyStore.getDefaultType(); - private final Map, Object> options = new HashMap, Object>(); - - private SslContextBuilder(boolean forServer) { - this.forServer = forServer; - } - - /** - * Configure a {@link SslContextOption}. - */ - public SslContextBuilder option(SslContextOption option, T value) { - if (value == null) { - options.remove(option); - } else { - options.put(option, value); - } - return this; - } - - /** - * The {@link SslContext} implementation to use. {@code null} uses the default one. - */ - public SslContextBuilder sslProvider(SslProvider provider) { - this.provider = provider; - return this; - } - - /** - * Sets the {@link KeyStore} type that should be used. {@code null} uses the default one. - */ - public SslContextBuilder keyStoreType(String keyStoreType) { - this.keyStoreType = keyStoreType; - return this; - } - - /** - * The SSLContext {@link Provider} to use. {@code null} uses the default one. This is only - * used with {@link SslProvider#JDK}. - */ - public SslContextBuilder sslContextProvider(Provider sslContextProvider) { - this.sslContextProvider = sslContextProvider; - return this; - } - - /** - * Trusted certificates for verifying the remote endpoint's certificate. The file should - * contain an X.509 certificate collection in PEM format. {@code null} uses the system default. - */ - public SslContextBuilder trustManager(File trustCertCollectionFile) { - try { - return trustManager(SslContext.toX509Certificates(trustCertCollectionFile)); - } catch (Exception e) { - throw new IllegalArgumentException("File does not contain valid certificates: " - + trustCertCollectionFile, e); - } - } - - /** - * Trusted certificates for verifying the remote endpoint's certificate. The input stream should - * contain an X.509 certificate collection in PEM format. {@code null} uses the system default. - * - * The caller is responsible for calling {@link InputStream#close()} after {@link #build()} has been called. - */ - public SslContextBuilder trustManager(InputStream trustCertCollectionInputStream) { - try { - return trustManager(SslContext.toX509Certificates(trustCertCollectionInputStream)); - } catch (Exception e) { - throw new IllegalArgumentException("Input stream does not contain valid certificates.", e); - } - } - - /** - * Trusted certificates for verifying the remote endpoint's certificate, {@code null} uses the system default. - */ - public SslContextBuilder trustManager(X509Certificate... trustCertCollection) { - this.trustCertCollection = trustCertCollection != null ? trustCertCollection.clone() : null; - trustManagerFactory = null; - return this; - } - - /** - * Trusted certificates for verifying the remote endpoint's certificate, {@code null} uses the system default. - */ - public SslContextBuilder trustManager(Iterable trustCertCollection) { - return trustManager(toArray(trustCertCollection, EMPTY_X509_CERTIFICATES)); - } - - /** - * Trusted manager for verifying the remote endpoint's certificate. {@code null} uses the system default. - */ - public SslContextBuilder trustManager(TrustManagerFactory trustManagerFactory) { - trustCertCollection = null; - this.trustManagerFactory = trustManagerFactory; - return this; - } - - /** - * A single trusted manager for verifying the remote endpoint's certificate. - * This is helpful when custom implementation of {@link TrustManager} is needed. - * Internally, a simple wrapper of {@link TrustManagerFactory} that only produces this - * specified {@link TrustManager} will be created, thus all the requirements specified in - * {@link #trustManager(TrustManagerFactory trustManagerFactory)} also apply here. - */ - public SslContextBuilder trustManager(TrustManager trustManager) { - this.trustManagerFactory = new TrustManagerFactoryWrapper(trustManager); - trustCertCollection = null; - return this; - } - - /** - * Identifying certificate for this host. {@code keyCertChainFile} and {@code keyFile} may - * be {@code null} for client contexts, which disables mutual authentication. - * - * @param keyCertChainFile an X.509 certificate chain file in PEM format - * @param keyFile a PKCS#8 private key file in PEM format - */ - public SslContextBuilder keyManager(File keyCertChainFile, File keyFile) { - return keyManager(keyCertChainFile, keyFile, null); - } - - /** - * Identifying certificate for this host. {@code keyCertChainInputStream} and {@code keyInputStream} may - * be {@code null} for client contexts, which disables mutual authentication. - * - * @param keyCertChainInputStream an input stream for an X.509 certificate chain in PEM format. The caller is - * responsible for calling {@link InputStream#close()} after {@link #build()} - * has been called. - * @param keyInputStream an input stream for a PKCS#8 private key in PEM format. The caller is - * responsible for calling {@link InputStream#close()} after {@link #build()} - * has been called. - */ - public SslContextBuilder keyManager(InputStream keyCertChainInputStream, InputStream keyInputStream) { - return keyManager(keyCertChainInputStream, keyInputStream, null); - } - - /** - * Identifying certificate for this host. {@code keyCertChain} and {@code key} may - * be {@code null} for client contexts, which disables mutual authentication. - * - * @param key a PKCS#8 private key - * @param keyCertChain an X.509 certificate chain - */ - public SslContextBuilder keyManager(PrivateKey key, X509Certificate... keyCertChain) { - return keyManager(key, null, keyCertChain); - } - - /** - * Identifying certificate for this host. {@code keyCertChain} and {@code key} may - * be {@code null} for client contexts, which disables mutual authentication. - * - * @param key a PKCS#8 private key - * @param keyCertChain an X.509 certificate chain - */ - public SslContextBuilder keyManager(PrivateKey key, Iterable keyCertChain) { - return keyManager(key, toArray(keyCertChain, EMPTY_X509_CERTIFICATES)); - } - - /** - * Identifying certificate for this host. {@code keyCertChainFile} and {@code keyFile} may - * be {@code null} for client contexts, which disables mutual authentication. - * - * @param keyCertChainFile an X.509 certificate chain file in PEM format - * @param keyFile a PKCS#8 private key file in PEM format - * @param keyPassword the password of the {@code keyFile}, or {@code null} if it's not - * password-protected - */ - public SslContextBuilder keyManager(File keyCertChainFile, File keyFile, String keyPassword) { - X509Certificate[] keyCertChain; - PrivateKey key; - try { - keyCertChain = SslContext.toX509Certificates(keyCertChainFile); - } catch (Exception e) { - throw new IllegalArgumentException("File does not contain valid certificates: " + keyCertChainFile, e); - } - try { - key = SslContext.toPrivateKey(keyFile, keyPassword); - } catch (Exception e) { - throw new IllegalArgumentException("File does not contain valid private key: " + keyFile, e); - } - return keyManager(key, keyPassword, keyCertChain); - } - - /** - * Identifying certificate for this host. {@code keyCertChainInputStream} and {@code keyInputStream} may - * be {@code null} for client contexts, which disables mutual authentication. - * - * @param keyCertChainInputStream an input stream for an X.509 certificate chain in PEM format. The caller is - * responsible for calling {@link InputStream#close()} after {@link #build()} - * has been called. - * @param keyInputStream an input stream for a PKCS#8 private key in PEM format. The caller is - * responsible for calling {@link InputStream#close()} after {@link #build()} - * has been called. - * @param keyPassword the password of the {@code keyInputStream}, or {@code null} if it's not - * password-protected - */ - public SslContextBuilder keyManager(InputStream keyCertChainInputStream, InputStream keyInputStream, - String keyPassword) { - X509Certificate[] keyCertChain; - PrivateKey key; - try { - keyCertChain = SslContext.toX509Certificates(keyCertChainInputStream); - } catch (Exception e) { - throw new IllegalArgumentException("Input stream not contain valid certificates.", e); - } - try { - key = SslContext.toPrivateKey(keyInputStream, keyPassword); - } catch (Exception e) { - throw new IllegalArgumentException("Input stream does not contain valid private key.", e); - } - return keyManager(key, keyPassword, keyCertChain); - } - - /** - * Identifying certificate for this host. {@code keyCertChain} and {@code key} may - * be {@code null} for client contexts, which disables mutual authentication. - * - * @param key a PKCS#8 private key file - * @param keyPassword the password of the {@code key}, or {@code null} if it's not - * password-protected - * @param keyCertChain an X.509 certificate chain - */ - public SslContextBuilder keyManager(PrivateKey key, String keyPassword, X509Certificate... keyCertChain) { - if (forServer) { - checkNonEmpty(keyCertChain, "keyCertChain"); // lgtm[java/dereferenced-value-may-be-null] - requireNonNull(key, "key required for servers"); - } - if (keyCertChain == null || keyCertChain.length == 0) { - this.keyCertChain = null; - } else { - for (X509Certificate cert: keyCertChain) { - checkNotNullWithIAE(cert, "cert"); - } - this.keyCertChain = keyCertChain.clone(); - } - this.key = key; - this.keyPassword = keyPassword; - keyManagerFactory = null; - return this; - } - - /** - * Identifying certificate for this host. {@code keyCertChain} and {@code key} may - * be {@code null} for client contexts, which disables mutual authentication. - * - * @param key a PKCS#8 private key file - * @param keyPassword the password of the {@code key}, or {@code null} if it's not - * password-protected - * @param keyCertChain an X.509 certificate chain - */ - public SslContextBuilder keyManager(PrivateKey key, String keyPassword, - Iterable keyCertChain) { - return keyManager(key, keyPassword, toArray(keyCertChain, EMPTY_X509_CERTIFICATES)); - } - - /** - * Identifying manager for this host. {@code keyManagerFactory} may be {@code null} for - * client contexts, which disables mutual authentication. Using a {@link KeyManagerFactory} - * is only supported for {@link SslProvider#JDK} or {@link SslProvider#OPENSSL} / {@link SslProvider#OPENSSL_REFCNT} - * 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 OpenSslX509KeyManagerFactory} or {@link OpenSslCachingX509KeyManagerFactory}. - */ - public SslContextBuilder keyManager(KeyManagerFactory keyManagerFactory) { - if (forServer) { - requireNonNull(keyManagerFactory, "keyManagerFactory required for servers"); - } - keyCertChain = null; - key = null; - keyPassword = null; - this.keyManagerFactory = keyManagerFactory; - return this; - } - - /** - * A single key manager managing the identity information of this host. - * This is helpful when custom implementation of {@link KeyManager} is needed. - * Internally, a wrapper of {@link KeyManagerFactory} that only produces this specified - * {@link KeyManager} will be created, thus all the requirements specified in - * {@link #keyManager(KeyManagerFactory keyManagerFactory)} also apply here. - */ - public SslContextBuilder keyManager(KeyManager keyManager) { - if (forServer) { - requireNonNull(keyManager, "keyManager required for servers"); - } - if (keyManager != null) { - this.keyManagerFactory = new KeyManagerFactoryWrapper(keyManager); - } else { - this.keyManagerFactory = null; - } - keyCertChain = null; - key = null; - keyPassword = null; - return this; - } - - /** - * The cipher suites to enable, in the order of preference. {@code null} to use default - * cipher suites. - */ - public SslContextBuilder ciphers(Iterable ciphers) { - return ciphers(ciphers, IdentityCipherSuiteFilter.INSTANCE); - } - - /** - * The cipher suites to enable, in the order of preference. {@code cipherFilter} will be - * applied to the ciphers before use. If {@code ciphers} is {@code null}, then the default - * cipher suites will be used. - */ - public SslContextBuilder ciphers(Iterable ciphers, CipherSuiteFilter cipherFilter) { - requireNonNull(cipherFilter, "cipherFilter"); - this.ciphers = ciphers; - this.cipherFilter = cipherFilter; - return this; - } - - /** - * Application protocol negotiation configuration. {@code null} disables support. - */ - public SslContextBuilder applicationProtocolConfig(ApplicationProtocolConfig apn) { - this.apn = apn; - return this; - } - - /** - * Set the size of the cache used for storing SSL session objects. {@code 0} to use the - * default value. - */ - public SslContextBuilder sessionCacheSize(long sessionCacheSize) { - this.sessionCacheSize = sessionCacheSize; - return this; - } - - /** - * Set the timeout for the cached SSL session objects, in seconds. {@code 0} to use the - * default value. - */ - public SslContextBuilder sessionTimeout(long sessionTimeout) { - this.sessionTimeout = sessionTimeout; - return this; - } - - /** - * Sets the client authentication mode. - */ - public SslContextBuilder clientAuth(ClientAuth clientAuth) { - this.clientAuth = requireNonNull(clientAuth, "clientAuth"); - return this; - } - - /** - * The TLS protocol versions to enable. - * @param protocols The protocols to enable, or {@code null} to enable the default protocols. - * @see SSLEngine#setEnabledCipherSuites(String[]) - */ - public SslContextBuilder protocols(String... protocols) { - this.protocols = protocols == null ? null : protocols.clone(); - return this; - } - - /** - * The TLS protocol versions to enable. - * @param protocols The protocols to enable, or {@code null} to enable the default protocols. - * @see SSLEngine#setEnabledCipherSuites(String[]) - */ - public SslContextBuilder protocols(Iterable protocols) { - return protocols(toArray(protocols, EMPTY_STRINGS)); - } - - /** - * {@code true} if the first write request shouldn't be encrypted. - */ - public SslContextBuilder startTls(boolean startTls) { - this.startTls = startTls; - return this; - } - - /** - * Enables OCSP stapling. Please note that not all {@link SslProvider} implementations support OCSP - * stapling and an exception will be thrown upon {@link #build()}. - * - * @see OpenSsl#isOcspSupported() - */ - @UnstableApi - public SslContextBuilder enableOcsp(boolean enableOcsp) { - this.enableOcsp = enableOcsp; - return this; - } - - /** - * Create new {@code SslContext} instance with configured settings. - *

If {@link #sslProvider(SslProvider)} is set to {@link SslProvider#OPENSSL_REFCNT} then the caller is - * responsible for releasing this object, or else native memory may leak. - */ - public SslContext build() throws SSLException { - if (forServer) { - return SslContext.newServerContextInternal(provider, sslContextProvider, trustCertCollection, - trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory, - ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, clientAuth, protocols, startTls, - enableOcsp, keyStoreType, toArray(options.entrySet(), EMPTY_ENTRIES)); - } else { - return SslContext.newClientContextInternal(provider, sslContextProvider, trustCertCollection, - trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory, - ciphers, cipherFilter, apn, protocols, sessionCacheSize, sessionTimeout, enableOcsp, keyStoreType, - toArray(options.entrySet(), EMPTY_ENTRIES)); - } - } - - private static T[] toArray(Iterable iterable, T[] prototype) { - if (iterable == null) { - return null; - } - final List list = new ArrayList<>(); - for (T element : iterable) { - list.add(element); - } - return list.toArray(prototype); - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/SslContextOption.java b/handler/src/main/java/io/netty/handler/ssl/SslContextOption.java deleted file mode 100644 index 36491d5086..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/SslContextOption.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.util.AbstractConstant; -import io.netty.util.ConstantPool; -import io.netty.util.internal.ObjectUtil; - - -/** - * A {@link SslContextOption} allows to configure a {@link SslContext} in a type-safe - * way. Which {@link SslContextOption} is supported depends on the actual implementation - * of {@link SslContext} and may depend on the nature of the SSL implementation it belongs - * to. - * - * @param the type of the value which is valid for the {@link SslContextOption} - */ -public class SslContextOption extends AbstractConstant> { - - private static final ConstantPool> pool = new ConstantPool>() { - @Override - protected SslContextOption newConstant(int id, String name) { - return new SslContextOption(id, name); - } - }; - - /** - * Returns the {@link SslContextOption} of the specified name. - */ - @SuppressWarnings("unchecked") - public static SslContextOption valueOf(String name) { - return (SslContextOption) pool.valueOf(name); - } - - /** - * Shortcut of {@link #valueOf(String) valueOf(firstNameComponent.getName() + "#" + secondNameComponent)}. - */ - @SuppressWarnings("unchecked") - public static SslContextOption valueOf(Class firstNameComponent, String secondNameComponent) { - return (SslContextOption) pool.valueOf(firstNameComponent, secondNameComponent); - } - - /** - * Returns {@code true} if a {@link SslContextOption} exists for the given {@code name}. - */ - public static boolean exists(String name) { - return pool.exists(name); - } - - /** - * Creates a new {@link SslContextOption} with the specified unique {@code name}. - */ - private SslContextOption(int id, String name) { - super(id, name); - } - - /** - * Should be used by sub-classes. - * - * @param name the name of the option - */ - protected SslContextOption(String name) { - this(pool.nextId(), name); - } - - /** - * Validate the value which is set for the {@link SslContextOption}. Sub-classes - * may override this for special checks. - */ - public void validate(T value) { - ObjectUtil.checkNotNull(value, "value"); - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java deleted file mode 100644 index 35551c3b73..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java +++ /dev/null @@ -1,2269 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.ByteBufConvertible; -import io.netty.buffer.ByteBufUtil; -import io.netty.buffer.CompositeByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.AbstractCoalescingBufferQueue; -import io.netty.channel.Channel; -import io.netty.channel.ChannelConfig; -import io.netty.channel.ChannelException; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelOption; -import io.netty.channel.ChannelOutboundBuffer; -import io.netty.channel.ChannelPipeline; -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.concurrent.DefaultPromise; -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.ImmediateEventExecutor; -import io.netty.util.concurrent.ImmediateExecutor; -import io.netty.util.concurrent.Promise; -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; - -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLEngineResult; -import javax.net.ssl.SSLEngineResult.HandshakeStatus; -import javax.net.ssl.SSLEngineResult.Status; -import javax.net.ssl.SSLException; -import javax.net.ssl.SSLHandshakeException; -import javax.net.ssl.SSLSession; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.channels.ClosedChannelException; -import java.nio.channels.DatagramChannel; -import java.nio.channels.SocketChannel; -import java.util.concurrent.Executor; -import java.util.concurrent.RejectedExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.regex.Pattern; - -import static io.netty.buffer.ByteBufUtil.ensureWritableSuccess; -import static io.netty.handler.ssl.SslUtils.getEncryptedPacketLength; -import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; -import static java.util.Objects.requireNonNull; - -/** - * Adds SSL - * · TLS and StartTLS support to a {@link Channel}. Please refer - * to the "SecureChat" example in the distribution or the web - * site for the detailed usage. - * - *

Beginning the handshake

- *

- * Beside using the handshake {@link Future} to get notified about the completion of the handshake it's - * also possible to detect it by implement the - * {@link ChannelHandler#userEventTriggered(ChannelHandlerContext, Object)} - * method and check for a {@link SslHandshakeCompletionEvent}. - * - *

Handshake

- *

- * The handshake will be automatically issued for you once the {@link Channel} is active and - * {@link SSLEngine#getUseClientMode()} returns {@code true}. - * So no need to bother with it by your self. - * - *

Closing the session

- *

- * 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, - * it is not reusable, and consequently you should create a new - * {@link SslHandler} with a new {@link SSLEngine} as explained in the - * following section. - * - *

Restarting the session

- *

- * To restart the SSL session, you must remove the existing closed - * {@link SslHandler} from the {@link ChannelPipeline}, insert a new - * {@link SslHandler} with a new {@link SSLEngine} into the pipeline, - * and start the handshake process as described in the first section. - * - *

Implementing StartTLS

- *

- * StartTLS is the - * communication pattern that secures the wire in the middle of the plaintext - * connection. Please note that it is different from SSL · TLS, that - * secures the wire from the beginning of the connection. Typically, StartTLS - * is composed of three steps: - *

    - *
  1. Client sends a StartTLS request to server.
  2. - *
  3. Server sends a StartTLS response to client.
  4. - *
  5. Client begins SSL handshake.
  6. - *
- * If you implement a server, you need to: - *
    - *
  1. create a new {@link SslHandler} instance with {@code startTls} flag set - * to {@code true},
  2. - *
  3. insert the {@link SslHandler} to the {@link ChannelPipeline}, and
  4. - *
  5. write a StartTLS response.
  6. - *
- * Please note that you must insert {@link SslHandler} before sending - * the StartTLS response. Otherwise the client can send begin SSL handshake - * before {@link SslHandler} is inserted to the {@link ChannelPipeline}, causing - * data corruption. - *

- * The client-side implementation is much simpler. - *

    - *
  1. Write a StartTLS request,
  2. - *
  3. wait for the StartTLS response,
  4. - *
  5. create a new {@link SslHandler} instance with {@code startTls} flag set - * to {@code false},
  6. - *
  7. insert the {@link SslHandler} to the {@link ChannelPipeline}, and
  8. - *
  9. Initiate SSL handshake.
  10. - *
- * - *

Known issues

- *

- * Because of a known issue with the current implementation of the SslEngine that comes - * with Java it may be possible that you see blocked IO-Threads while a full GC is done. - *

- * So if you are affected you can workaround this problem by adjust the cache settings - * like shown below: - * - *

- *     SslContext context = ...;
- *     context.getServerSessionContext().setSessionCacheSize(someSaneSize);
- *     context.getServerSessionContext().setSessionTime(someSameTimeout);
- * 
- *

- * What values to use here depends on the nature of your application and should be set - * based on monitoring and debugging of it. - * For more details see - * #832 in our issue tracker. - */ -public class SslHandler extends ByteToMessageDecoder { - private static final InternalLogger logger = - InternalLoggerFactory.getInstance(SslHandler.class); - private static final Pattern IGNORABLE_CLASS_IN_STACK = Pattern.compile( - "^.*(?:Socket|Datagram|Sctp|Udt)Channel.*$"); - private static final Pattern IGNORABLE_ERROR_MESSAGE = Pattern.compile( - "^.*(?:connection.*(?:reset|closed|abort|broken)|broken.*pipe).*$", Pattern.CASE_INSENSITIVE); - private static final int STATE_SENT_FIRST_MESSAGE = 1; - private static final int STATE_FLUSHED_BEFORE_HANDSHAKE = 1 << 1; - private static final int STATE_READ_DURING_HANDSHAKE = 1 << 2; - private static final int STATE_HANDSHAKE_STARTED = 1 << 3; - /** - * Set by wrap*() methods when something is produced. - * {@link #channelReadComplete(ChannelHandlerContext)} will check this flag, clear it, and call ctx.flush(). - */ - private static final int STATE_NEEDS_FLUSH = 1 << 4; - private static final int STATE_OUTBOUND_CLOSED = 1 << 5; - private static final int STATE_CLOSE_NOTIFY = 1 << 6; - private static final int STATE_PROCESS_TASK = 1 << 7; - /** - * This flag is used to determine if we need to call {@link ChannelHandlerContext#read()} to consume more data - * when {@link ChannelConfig#isAutoRead()} is {@code false}. - */ - private static final int STATE_FIRE_CHANNEL_READ = 1 << 8; - private static final int STATE_UNWRAP_REENTRY = 1 << 9; - - /** - * 2^14 which is the maximum sized plaintext chunk - * allowed by the TLS RFC. - */ - private static final int MAX_PLAINTEXT_LENGTH = 16 * 1024; - - private enum SslEngineType { - TCNATIVE(true, COMPOSITE_CUMULATOR) { - @Override - SSLEngineResult unwrap(SslHandler handler, ByteBuf in, int len, ByteBuf out) throws SSLException { - int nioBufferCount = in.nioBufferCount(); - int writerIndex = out.writerIndex(); - final SSLEngineResult result; - if (nioBufferCount > 1) { - /* - * If {@link OpenSslEngine} is in use, - * we can use a special {@link OpenSslEngine#unwrap(ByteBuffer[], ByteBuffer[])} method - * that accepts multiple {@link ByteBuffer}s without additional memory copies. - */ - ReferenceCountedOpenSslEngine opensslEngine = (ReferenceCountedOpenSslEngine) handler.engine; - try { - handler.singleBuffer[0] = toByteBuffer(out, writerIndex, out.writableBytes()); - result = opensslEngine.unwrap(in.nioBuffers(in.readerIndex(), len), handler.singleBuffer); - } finally { - handler.singleBuffer[0] = null; - } - } else { - result = handler.engine.unwrap(toByteBuffer(in, in.readerIndex(), len), - toByteBuffer(out, writerIndex, out.writableBytes())); - } - out.writerIndex(writerIndex + result.bytesProduced()); - return result; - } - - @Override - ByteBuf allocateWrapBuffer(SslHandler handler, ByteBufAllocator allocator, - int pendingBytes, int numComponents) { - return allocator.directBuffer(((ReferenceCountedOpenSslEngine) handler.engine) - .calculateMaxLengthForWrap(pendingBytes, numComponents)); - } - - @Override - int calculatePendingData(SslHandler handler, int guess) { - int sslPending = ((ReferenceCountedOpenSslEngine) handler.engine).sslPending(); - return sslPending > 0 ? sslPending : guess; - } - - @Override - boolean jdkCompatibilityMode(SSLEngine engine) { - return ((ReferenceCountedOpenSslEngine) engine).jdkCompatibilityMode; - } - }, - CONSCRYPT(true, COMPOSITE_CUMULATOR) { - @Override - SSLEngineResult unwrap(SslHandler handler, ByteBuf in, int len, ByteBuf out) throws SSLException { - int nioBufferCount = in.nioBufferCount(); - int writerIndex = out.writerIndex(); - final SSLEngineResult result; - if (nioBufferCount > 1) { - /* - * Use a special unwrap method without additional memory copies. - */ - try { - handler.singleBuffer[0] = toByteBuffer(out, writerIndex, out.writableBytes()); - result = ((ConscryptAlpnSslEngine) handler.engine).unwrap( - in.nioBuffers(in.readerIndex(), len), - handler.singleBuffer); - } finally { - handler.singleBuffer[0] = null; - } - } else { - result = handler.engine.unwrap(toByteBuffer(in, in.readerIndex(), len), - toByteBuffer(out, writerIndex, out.writableBytes())); - } - out.writerIndex(writerIndex + result.bytesProduced()); - return result; - } - - @Override - ByteBuf allocateWrapBuffer(SslHandler handler, ByteBufAllocator allocator, - int pendingBytes, int numComponents) { - return allocator.directBuffer( - ((ConscryptAlpnSslEngine) handler.engine).calculateOutNetBufSize(pendingBytes, numComponents)); - } - - @Override - int calculatePendingData(SslHandler handler, int guess) { - return guess; - } - - @Override - boolean jdkCompatibilityMode(SSLEngine engine) { - return true; - } - }, - JDK(false, MERGE_CUMULATOR) { - @Override - SSLEngineResult unwrap(SslHandler handler, ByteBuf in, int len, ByteBuf out) throws SSLException { - int writerIndex = out.writerIndex(); - ByteBuffer inNioBuffer = toByteBuffer(in, in.readerIndex(), len); - int position = inNioBuffer.position(); - final SSLEngineResult result = handler.engine.unwrap(inNioBuffer, - toByteBuffer(out, writerIndex, out.writableBytes())); - out.writerIndex(writerIndex + result.bytesProduced()); - - // This is a workaround for a bug in Android 5.0. Android 5.0 does not correctly update the - // SSLEngineResult.bytesConsumed() in some cases and just return 0. - // - // See: - // - https://android-review.googlesource.com/c/platform/external/conscrypt/+/122080 - // - https://github.com/netty/netty/issues/7758 - if (result.bytesConsumed() == 0) { - int consumed = inNioBuffer.position() - position; - if (consumed != result.bytesConsumed()) { - // Create a new SSLEngineResult with the correct bytesConsumed(). - return new SSLEngineResult( - result.getStatus(), result.getHandshakeStatus(), consumed, result.bytesProduced()); - } - } - return result; - } - - @Override - ByteBuf allocateWrapBuffer(SslHandler handler, ByteBufAllocator allocator, - int pendingBytes, int numComponents) { - // As for the JDK SSLEngine we always need to allocate buffers of the size required by the SSLEngine - // (normally ~16KB). This is required even if the amount of data to encrypt is very small. Use heap - // buffers to reduce the native memory usage. - // - // Beside this the JDK SSLEngine also (as of today) will do an extra heap to direct buffer copy - // if a direct buffer is used as its internals operate on byte[]. - return allocator.heapBuffer(handler.engine.getSession().getPacketBufferSize()); - } - - @Override - int calculatePendingData(SslHandler handler, int guess) { - return guess; - } - - @Override - boolean jdkCompatibilityMode(SSLEngine engine) { - return true; - } - }; - - static SslEngineType forEngine(SSLEngine engine) { - return engine instanceof ReferenceCountedOpenSslEngine ? TCNATIVE : - engine instanceof ConscryptAlpnSslEngine ? CONSCRYPT : JDK; - } - - SslEngineType(boolean wantsDirectBuffer, Cumulator cumulator) { - this.wantsDirectBuffer = wantsDirectBuffer; - this.cumulator = cumulator; - } - - abstract SSLEngineResult unwrap(SslHandler handler, ByteBuf in, int len, ByteBuf out) throws SSLException; - - abstract int calculatePendingData(SslHandler handler, int guess); - - abstract boolean jdkCompatibilityMode(SSLEngine engine); - - abstract ByteBuf allocateWrapBuffer(SslHandler handler, ByteBufAllocator allocator, - int pendingBytes, int numComponents); - - // BEGIN Platform-dependent flags - - /** - * {@code true} if and only if {@link SSLEngine} expects a direct buffer and so if a heap buffer - * is given will make an extra memory copy. - */ - final boolean wantsDirectBuffer; - - // END Platform-dependent flags - - /** - * When using JDK {@link SSLEngine}, we use {@link #MERGE_CUMULATOR} because it works only with - * one {@link ByteBuffer}. - * - * When using {@link OpenSslEngine}, we can use {@link #COMPOSITE_CUMULATOR} because it has - * {@link OpenSslEngine#unwrap(ByteBuffer[], ByteBuffer[])} which works with multiple {@link ByteBuffer}s - * and which does not need to do extra memory copies. - */ - final Cumulator cumulator; - } - - private volatile ChannelHandlerContext ctx; - private final SSLEngine engine; - private final SslEngineType engineType; - private final Executor delegatedTaskExecutor; - private final boolean jdkCompatibilityMode; - - /** - * Used if {@link SSLEngine#wrap(ByteBuffer[], ByteBuffer)} and {@link SSLEngine#unwrap(ByteBuffer, ByteBuffer[])} - * should be called with a {@link ByteBuf} that is only backed by one {@link ByteBuffer} to reduce the object - * creation. - */ - private final ByteBuffer[] singleBuffer = new ByteBuffer[1]; - - private final boolean startTls; - - private final SslTasksRunner sslTaskRunnerForUnwrap = new SslTasksRunner(true); - private final SslTasksRunner sslTaskRunner = new SslTasksRunner(false); - - private SslHandlerCoalescingBufferQueue pendingUnencryptedWrites; - private Promise handshakePromise = new LazyPromise(); - private final Promise sslClosePromise = new LazyPromise(); - - private int packetLength; - private short state; - - private volatile long handshakeTimeoutMillis = 10000; - private volatile long closeNotifyFlushTimeoutMillis = 3000; - private volatile long closeNotifyReadTimeoutMillis; - volatile int wrapDataSize = MAX_PLAINTEXT_LENGTH; - - /** - * Creates a new instance which runs all delegated tasks directly on the {@link EventExecutor}. - * - * @param engine the {@link SSLEngine} this handler will use - */ - public SslHandler(SSLEngine engine) { - this(engine, false); - } - - /** - * 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} - */ - public SslHandler(SSLEngine engine, boolean startTls) { - this(engine, startTls, ImmediateExecutor.INSTANCE); - } - - /** - * 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()}. - */ - public SslHandler(SSLEngine engine, Executor delegatedTaskExecutor) { - this(engine, false, delegatedTaskExecutor); - } - - /** - * 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()}. - */ - public SslHandler(SSLEngine engine, boolean startTls, Executor delegatedTaskExecutor) { - requireNonNull(engine, "engine"); - requireNonNull(delegatedTaskExecutor, "delegatedTaskExecutor"); - this.engine = engine; - engineType = SslEngineType.forEngine(engine); - this.delegatedTaskExecutor = delegatedTaskExecutor; - this.startTls = startTls; - jdkCompatibilityMode = engineType.jdkCompatibilityMode(engine); - setCumulator(engineType.cumulator); - } - - public long getHandshakeTimeoutMillis() { - return handshakeTimeoutMillis; - } - - public void setHandshakeTimeout(long handshakeTimeout, TimeUnit unit) { - requireNonNull(unit, "unit"); - setHandshakeTimeoutMillis(unit.toMillis(handshakeTimeout)); - } - - public void setHandshakeTimeoutMillis(long handshakeTimeoutMillis) { - this.handshakeTimeoutMillis = checkPositiveOrZero(handshakeTimeoutMillis, "handshakeTimeoutMillis"); - } - - /** - * Sets the number of bytes to pass to each {@link SSLEngine#wrap(ByteBuffer[], int, int, ByteBuffer)} call. - *

- * This value will partition data which is passed to write - * {@link #write(ChannelHandlerContext, Object)}. The partitioning will work as follows: - *

    - *
  • If {@code wrapDataSize <= 0} then we will write each data chunk as is.
  • - *
  • If {@code wrapDataSize > data size} then we will attempt to aggregate multiple data chunks together.
  • - *
  • If {@code wrapDataSize > data size} Else if {@code wrapDataSize <= data size} then we will divide the data - * into chunks of {@code wrapDataSize} when writing.
  • - *
- *

- * If the {@link SSLEngine} doesn't support a gather wrap operation (e.g. {@link SslProvider#OPENSSL}) then - * aggregating data before wrapping can help reduce the ratio between TLS overhead vs data payload which will lead - * to better goodput. Writing fixed chunks of data can also help target the underlying transport's (e.g. TCP) - * frame size. Under lossy/congested network conditions this may help the peer get full TLS packets earlier and - * be able to do work sooner, as opposed to waiting for the all the pieces of the TLS packet to arrive. - * @param wrapDataSize the number of bytes which will be passed to each - * {@link SSLEngine#wrap(ByteBuffer[], int, int, ByteBuffer)} call. - */ - @UnstableApi - public final void setWrapDataSize(int wrapDataSize) { - this.wrapDataSize = wrapDataSize; - } - - /** - * @deprecated use {@link #getCloseNotifyFlushTimeoutMillis()} - */ - @Deprecated - public long getCloseNotifyTimeoutMillis() { - return getCloseNotifyFlushTimeoutMillis(); - } - - /** - * @deprecated use {@link #setCloseNotifyFlushTimeout(long, TimeUnit)} - */ - @Deprecated - public void setCloseNotifyTimeout(long closeNotifyTimeout, TimeUnit unit) { - setCloseNotifyFlushTimeout(closeNotifyTimeout, unit); - } - - /** - * @deprecated use {@link #setCloseNotifyFlushTimeoutMillis(long)} - */ - @Deprecated - public void setCloseNotifyTimeoutMillis(long closeNotifyFlushTimeoutMillis) { - setCloseNotifyFlushTimeoutMillis(closeNotifyFlushTimeoutMillis); - } - - /** - * Gets the timeout for flushing the close_notify that was triggered by closing the - * {@link Channel}. If the close_notify was not flushed in the given timeout the {@link Channel} will be closed - * forcibly. - */ - public final long getCloseNotifyFlushTimeoutMillis() { - return closeNotifyFlushTimeoutMillis; - } - - /** - * Sets the timeout for flushing the close_notify that was triggered by closing the - * {@link Channel}. If the close_notify was not flushed in the given timeout the {@link Channel} will be closed - * forcibly. - */ - public final void setCloseNotifyFlushTimeout(long closeNotifyFlushTimeout, TimeUnit unit) { - setCloseNotifyFlushTimeoutMillis(unit.toMillis(closeNotifyFlushTimeout)); - } - - /** - * See {@link #setCloseNotifyFlushTimeout(long, TimeUnit)}. - */ - public final void setCloseNotifyFlushTimeoutMillis(long closeNotifyFlushTimeoutMillis) { - this.closeNotifyFlushTimeoutMillis = checkPositiveOrZero(closeNotifyFlushTimeoutMillis, - "closeNotifyFlushTimeoutMillis"); - } - - /** - * Gets the timeout (in ms) for receiving the response for the close_notify that was triggered by closing the - * {@link Channel}. This timeout starts after the close_notify message was successfully written to the - * remote peer. Use {@code 0} to directly close the {@link Channel} and not wait for the response. - */ - public final long getCloseNotifyReadTimeoutMillis() { - return closeNotifyReadTimeoutMillis; - } - - /** - * Sets the timeout for receiving the response for the close_notify that was triggered by closing the - * {@link Channel}. This timeout starts after the close_notify message was successfully written to the - * remote peer. Use {@code 0} to directly close the {@link Channel} and not wait for the response. - */ - public final void setCloseNotifyReadTimeout(long closeNotifyReadTimeout, TimeUnit unit) { - setCloseNotifyReadTimeoutMillis(unit.toMillis(closeNotifyReadTimeout)); - } - - /** - * See {@link #setCloseNotifyReadTimeout(long, TimeUnit)}. - */ - public final void setCloseNotifyReadTimeoutMillis(long closeNotifyReadTimeoutMillis) { - this.closeNotifyReadTimeoutMillis = checkPositiveOrZero(closeNotifyReadTimeoutMillis, - "closeNotifyReadTimeoutMillis"); - } - - /** - * Returns the {@link SSLEngine} which is used by this handler. - */ - public SSLEngine engine() { - return engine; - } - - /** - * Returns the name of the current application-level protocol. - * - * @return the protocol name or {@code null} if application-level protocol has not been negotiated - */ - public String applicationProtocol() { - SSLEngine engine = engine(); - if (!(engine instanceof ApplicationProtocolAccessor)) { - return null; - } - - return ((ApplicationProtocolAccessor) engine).getNegotiatedApplicationProtocol(); - } - - /** - * Returns a {@link Future} that will get notified once the current TLS handshake completes. - * - * @return the {@link Future} for the initial TLS handshake if {@link #renegotiate()} was not invoked. - * The {@link Future} for the most recent {@linkplain #renegotiate() TLS renegotiation} otherwise. - */ - public Future handshakeFuture() { - return handshakePromise.asFuture(); - } - - /** - * 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 Future closeOutbound() { - final ChannelHandlerContext ctx = this.ctx; - Promise promise = ctx.newPromise(); - if (ctx.executor().inEventLoop()) { - closeOutbound0(promise); - } else { - ctx.executor().execute(() -> closeOutbound0(promise)); - } - return promise.asFuture(); - } - - private void closeOutbound0(Promise promise) { - setState(STATE_OUTBOUND_CLOSED); - 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. - * - * This method will return the same {@link Future} all the time. - * - * @see SSLEngine - */ - public Future sslCloseFuture() { - return sslClosePromise.asFuture(); - } - - @Override - public void handlerRemoved0(ChannelHandlerContext ctx) throws Exception { - try { - if (!pendingUnencryptedWrites.isEmpty()) { - // Check if queue is not empty first because create a new ChannelException is expensive - pendingUnencryptedWrites.releaseAndFailAll(ctx, - new ChannelException("Pending write on removal of SslHandler")); - } - pendingUnencryptedWrites = null; - - SSLHandshakeException cause = null; - - // If the handshake is not done yet we should fail the handshake promise and notify the rest of the - // pipeline. - if (!handshakePromise.isDone()) { - cause = new SSLHandshakeException("SslHandler removed before handshake completed"); - if (handshakePromise.tryFailure(cause)) { - ctx.fireUserEventTriggered(new SslHandshakeCompletionEvent(cause)); - } - } - if (!sslClosePromise.isDone()) { - if (cause == null) { - cause = new SSLHandshakeException("SslHandler removed before handshake completed"); - } - notifyClosePromise(cause); - } - } finally { - ReferenceCountUtil.release(engine); - } - } - - @Override - public Future disconnect(final ChannelHandlerContext ctx) { - return closeOutboundAndChannel(ctx, true); - } - - @Override - public Future close(final ChannelHandlerContext ctx) { - return closeOutboundAndChannel(ctx, false); - } - - @Override - public void read(ChannelHandlerContext ctx) { - if (!handshakePromise.isDone()) { - setState(STATE_READ_DURING_HANDSHAKE); - } - - ctx.read(); - } - - private static IllegalStateException newPendingWritesNullException() { - return new IllegalStateException("pendingUnencryptedWrites is null, handlerRemoved0 called?"); - } - - @Override - public Future write(final ChannelHandlerContext ctx, Object msg) { - if (!(msg instanceof ByteBufConvertible)) { - UnsupportedMessageTypeException exception = new UnsupportedMessageTypeException(msg, ByteBuf.class); - ReferenceCountUtil.safeRelease(msg); - return ctx.newFailedFuture(exception); - } else if (pendingUnencryptedWrites == null) { - ReferenceCountUtil.safeRelease(msg); - return ctx.newFailedFuture(newPendingWritesNullException()); - } else { - Promise promise = ctx.newPromise(); - pendingUnencryptedWrites.add(((ByteBufConvertible) msg).asByteBuf(), promise); - return promise.asFuture(); - } - } - - @Override - public void flush(ChannelHandlerContext ctx) { - // Do not encrypt the first write request if this handler is - // created with startTLS flag turned on. - if (startTls && !isStateSet(STATE_SENT_FIRST_MESSAGE)) { - setState(STATE_SENT_FIRST_MESSAGE); - 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(true); - return; - } - - if (isStateSet(STATE_PROCESS_TASK)) { - return; - } - - try { - wrapAndFlush(ctx); - } catch (Throwable cause) { - setHandshakeFailure(ctx, cause); - } - } - - private void wrapAndFlush(ChannelHandlerContext ctx) throws SSLException { - if (pendingUnencryptedWrites.isEmpty()) { - pendingUnencryptedWrites.add(Unpooled.EMPTY_BUFFER, ctx.newPromise()); - } - if (!handshakePromise.isDone()) { - setState(STATE_FLUSHED_BEFORE_HANDSHAKE); - } - try { - wrap(ctx, false); - } finally { - // We may have written some parts of data before an exception was thrown so ensure we always flush. - // See https://github.com/netty/netty/issues/3900#issuecomment-172481830 - forceFlush(ctx); - } - } - - // This method will not call setHandshakeFailure(...) ! - private void wrap(ChannelHandlerContext ctx, boolean inUnwrap) throws SSLException { - ByteBuf out = null; - ByteBufAllocator alloc = ctx.alloc(); - try { - 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 - outer: while (!ctx.isRemoved()) { - Promise promise = ctx.newPromise(); - ByteBuf buf = wrapDataSize > 0 ? - pendingUnencryptedWrites.remove(alloc, wrapDataSize, promise) : - pendingUnencryptedWrites.removeFirst(promise); - if (buf == null) { - break; - } - - if (out == null) { - out = allocateOutNetBuf(ctx, buf.readableBytes(), buf.nioBufferCount()); - } - - SSLEngineResult result = wrap(alloc, engine, buf, out); - if (buf.isReadable()) { - pendingUnencryptedWrites.addFirst(buf, promise); - // When we add the buffer/promise pair back we need to be sure we don't complete the promise - // later. We only complete the promise if the buffer is completely consumed. - promise = null; - } else { - buf.release(); - } - - // We need to write any data before we invoke any methods which may trigger re-entry, otherwise - // writes may occur out of order and TLS sequencing may be off (e.g. SSLV3_ALERT_BAD_RECORD_MAC). - if (out.isReadable()) { - final ByteBuf b = out; - out = null; - if (promise != null) { - ctx.write(b).cascadeTo(promise); - } else { - ctx.write(b); - } - } else if (promise != null) { - ctx.write(Unpooled.EMPTY_BUFFER).cascadeTo(promise); - } - // else out is not readable we can re-use it and so save an extra allocation - - if (result.getStatus() == Status.CLOSED) { - // Make a best effort to preserve any exception that way previously encountered from the handshake - // or the transport, else fallback to a general error. - Throwable exception = handshakePromise.isDone() ? handshakePromise.cause() : null; - if (exception == null) { - exception = sslClosePromise.isDone() ? sslClosePromise.cause() : null; - if (exception == null) { - exception = new SslClosedEngineException("SSLEngine closed already"); - } - } - pendingUnencryptedWrites.releaseAndFailAll(ctx, exception); - return; - } else { - switch (result.getHandshakeStatus()) { - case NEED_TASK: - 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: - case NOT_HANDSHAKING: // work around for android bug that skips the FINISHED state. - setHandshakeSuccess(); - break; - case NEED_WRAP: - // If we are expected to wrap again and we produced some data we need to ensure there - // is something in the queue to process as otherwise we will not try again before there - // was more added. Failing to do so may fail to produce an alert that can be - // consumed by the remote peer. - if (result.bytesProduced() > 0 && pendingUnencryptedWrites.isEmpty()) { - pendingUnencryptedWrites.add(Unpooled.EMPTY_BUFFER); - } - break; - case NEED_UNWRAP: - // The underlying engine is starving so we need to feed it with more data. - // See https://github.com/netty/netty/pull/5039 - readIfNeeded(ctx); - return; - default: - throw new IllegalStateException( - "Unknown handshake status: " + result.getHandshakeStatus()); - } - } - } - } finally { - if (out != null) { - out.release(); - } - if (inUnwrap) { - setState(STATE_NEEDS_FLUSH); - } - } - } - - /** - * This method will not call - * {@link #setHandshakeFailure(ChannelHandlerContext, Throwable, boolean, boolean, boolean)} or - * {@link #setHandshakeFailure(ChannelHandlerContext, Throwable)}. - * @return {@code true} if this method ends on {@link SSLEngineResult.HandshakeStatus#NOT_HANDSHAKING}. - */ - private boolean wrapNonAppData(final ChannelHandlerContext ctx, boolean inUnwrap) throws SSLException { - ByteBuf out = null; - ByteBufAllocator alloc = ctx.alloc(); - try { - // Only continue to loop if the handler was not removed in the meantime. - // See https://github.com/netty/netty/issues/5860 - 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. - // If this is not enough we will increase the buffer in wrap(...). - out = allocateOutNetBuf(ctx, 2048, 1); - } - SSLEngineResult result = wrap(alloc, engine, Unpooled.EMPTY_BUFFER, out); - if (result.bytesProduced() > 0) { - ctx.write(out).addListener(future -> { - Throwable cause = future.cause(); - if (cause != null) { - setHandshakeFailureTransportFailure(ctx, cause); - } - }); - if (inUnwrap) { - setState(STATE_NEEDS_FLUSH); - } - out = null; - } - - HandshakeStatus status = result.getHandshakeStatus(); - switch (status) { - case FINISHED: - // We may be here because we read data and discovered the remote peer initiated a renegotiation - // and this write is to complete the new handshake. The user may have previously done a - // writeAndFlush which wasn't able to wrap data due to needing the pending handshake, so we - // attempt to wrap application data here if any is pending. - if (setHandshakeSuccess() && inUnwrap && !pendingUnencryptedWrites.isEmpty()) { - wrap(ctx, true); - } - return false; - case NEED_TASK: - 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 || unwrapNonAppData(ctx) <= 0) { - // If we asked for a wrap, the engine requested an unwrap, and we are in unwrap there is - // no use in trying to call wrap again because we have already attempted (or will after we - // return) to feed more data to the engine. - return false; - } - break; - case NEED_WRAP: - break; - case NOT_HANDSHAKING: - if (setHandshakeSuccess() && inUnwrap && !pendingUnencryptedWrites.isEmpty()) { - wrap(ctx, true); - } - // Workaround for TLS False Start problem reported at: - // https://github.com/netty/netty/issues/1108#issuecomment-14266970 - if (!inUnwrap) { - unwrapNonAppData(ctx); - } - return true; - default: - throw new IllegalStateException("Unknown handshake status: " + result.getHandshakeStatus()); - } - - // Check if did not produce any bytes and if so break out of the loop, but only if we did not process - // a task as last action. It's fine to not produce any data as part of executing a task. - if (result.bytesProduced() == 0 && status != HandshakeStatus.NEED_TASK) { - break; - } - - // It should not consume empty buffers when it is not handshaking - // Fix for Android, where it was encrypting empty buffers even when not handshaking - if (result.bytesConsumed() == 0 && result.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING) { - break; - } - } - } finally { - if (out != null) { - out.release(); - } - } - return false; - } - - private SSLEngineResult wrap(ByteBufAllocator alloc, SSLEngine engine, ByteBuf in, ByteBuf out) - throws SSLException { - ByteBuf newDirectIn = null; - try { - int readerIndex = in.readerIndex(); - int readableBytes = in.readableBytes(); - - // We will call SslEngine.wrap(ByteBuffer[], ByteBuffer) to allow efficient handling of - // CompositeByteBuf without force an extra memory copy when CompositeByteBuffer.nioBuffer() is called. - final ByteBuffer[] in0; - if (in.isDirect() || !engineType.wantsDirectBuffer) { - // As CompositeByteBuf.nioBufferCount() can be expensive (as it needs to check all composed ByteBuf - // to calculate the count) we will just assume a CompositeByteBuf contains more then 1 ByteBuf. - // The worst that can happen is that we allocate an extra ByteBuffer[] in CompositeByteBuf.nioBuffers() - // which is better then walking the composed ByteBuf in most cases. - if (!(in instanceof CompositeByteBuf) && in.nioBufferCount() == 1) { - in0 = singleBuffer; - // We know its only backed by 1 ByteBuffer so use internalNioBuffer to keep object allocation - // to a minimum. - in0[0] = in.internalNioBuffer(readerIndex, readableBytes); - } else { - in0 = in.nioBuffers(); - } - } else { - // We could even go further here and check if its a CompositeByteBuf and if so try to decompose it and - // only replace the ByteBuffer that are not direct. At the moment we just will replace the whole - // CompositeByteBuf to keep the complexity to a minimum - newDirectIn = alloc.directBuffer(readableBytes); - newDirectIn.writeBytes(in, readerIndex, readableBytes); - in0 = singleBuffer; - in0[0] = newDirectIn.internalNioBuffer(newDirectIn.readerIndex(), readableBytes); - } - - for (;;) { - ByteBuffer out0 = out.nioBuffer(out.writerIndex(), out.writableBytes()); - SSLEngineResult result = engine.wrap(in0, out0); - in.skipBytes(result.bytesConsumed()); - out.writerIndex(out.writerIndex() + result.bytesProduced()); - - if (result.getStatus() == Status.BUFFER_OVERFLOW) { - out.ensureWritable(engine.getSession().getPacketBufferSize()); - } else { - return result; - } - } - } finally { - // Null out to allow GC of ByteBuffer - singleBuffer[0] = null; - - if (newDirectIn != null) { - newDirectIn.release(); - } - } - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - boolean handshakeFailed = handshakePromise.isFailed(); - - ClosedChannelException exception = new ClosedChannelException(); - // Make sure to release SSLEngine, - // and notify the handshake future if the connection has been closed during handshake. - setHandshakeFailure(ctx, exception, !isStateSet(STATE_OUTBOUND_CLOSED), isStateSet(STATE_HANDSHAKE_STARTED), - false); - - // Ensure we always notify the sslClosePromise as well - notifyClosePromise(exception); - - try { - super.channelInactive(ctx); - } catch (DecoderException e) { - if (!handshakeFailed || !(e.getCause() instanceof SSLException)) { - // We only rethrow the exception if the handshake did not fail before channelInactive(...) was called - // as otherwise this may produce duplicated failures as super.channelInactive(...) will also call - // channelRead(...). - // - // See https://github.com/netty/netty/issues/10119 - throw e; - } - } - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - if (ignoreException(cause)) { - // It is safe to ignore the 'connection reset by peer' or - // 'broken pipe' error after sending close_notify. - if (logger.isDebugEnabled()) { - logger.debug( - "{} Swallowing a harmless 'connection reset by peer / broken pipe' error that occurred " + - "while writing close_notify in response to the peer's close_notify", - ctx.channel(), cause); - } - - // Close the connection explicitly just in case the transport - // did not close the connection automatically. - if (ctx.channel().isActive()) { - ctx.close(); - } - } else { - ctx.fireExceptionCaught(cause); - - if (cause instanceof SSLException || - ((cause instanceof DecoderException) && cause.getCause() instanceof SSLException)) { - ctx.close(); - } - } - } - - /** - * Checks if the given {@link Throwable} can be ignore and just "swallowed" - * - * When an ssl connection is closed a close_notify message is sent. - * After that the peer also sends close_notify however, it's not mandatory to receive - * the close_notify. The party who sent the initial close_notify can close the connection immediately - * then the peer will get connection reset error. - * - */ - private boolean ignoreException(Throwable t) { - if (!(t instanceof SSLException) && t instanceof IOException && sslClosePromise.isDone()) { - String message = t.getMessage(); - - // first try to match connection reset / broke peer based on the regex. This is the fastest way - // but may fail on different jdk impls or OS's - if (message != null && IGNORABLE_ERROR_MESSAGE.matcher(message).matches()) { - return true; - } - - // Inspect the StackTraceElements to see if it was a connection reset / broken pipe or not - StackTraceElement[] elements = t.getStackTrace(); - for (StackTraceElement element: elements) { - String classname = element.getClassName(); - String methodname = element.getMethodName(); - - // skip all classes that belong to the io.netty package - if (classname.startsWith("io.netty.")) { - continue; - } - - // check if the method name is read if not skip it - if (!"read".equals(methodname)) { - continue; - } - - // This will also match against SocketInputStream which is used by openjdk 7 and maybe - // also others - if (IGNORABLE_CLASS_IN_STACK.matcher(classname).matches()) { - return true; - } - - try { - // No match by now.. Try to load the class via classloader and inspect it. - // This is mainly done as other JDK implementations may differ in name of - // the impl. - Class clazz = PlatformDependent.getClassLoader(getClass()).loadClass(classname); - - if (SocketChannel.class.isAssignableFrom(clazz) - || DatagramChannel.class.isAssignableFrom(clazz)) { - return true; - } - - // also match against SctpChannel via String matching as it may not present. - if ("com.sun.nio.sctp.SctpChannel".equals(clazz.getSuperclass().getName())) { - return true; - } - } catch (Throwable cause) { - if (logger.isDebugEnabled()) { - logger.debug("Unexpected exception while loading class {} classname {}", - getClass(), classname, cause); - } - } - } - } - - return false; - } - - /** - * Returns {@code true} if the given {@link ByteBuf} is encrypted. Be aware that this method - * will not increase the readerIndex of the given {@link ByteBuf}. - * - * @param buffer - * The {@link ByteBuf} to read from. Be aware that it must have at least 5 bytes to read, - * otherwise it will throw an {@link IllegalArgumentException}. - * @return encrypted - * {@code true} if the {@link ByteBuf} is encrypted, {@code false} otherwise. - * @throws IllegalArgumentException - * Is thrown if the given {@link ByteBuf} has not at least 5 bytes to read. - */ - public static boolean isEncrypted(ByteBuf buffer) { - if (buffer.readableBytes() < SslUtils.SSL_RECORD_HEADER_LENGTH) { - throw new IllegalArgumentException( - "buffer must have at least " + SslUtils.SSL_RECORD_HEADER_LENGTH + " readable bytes"); - } - return getEncryptedPacketLength(buffer, buffer.readerIndex()) != SslUtils.NOT_ENCRYPTED; - } - - private void decodeJdkCompatible(ChannelHandlerContext ctx, ByteBuf in) throws NotSslRecordException { - int packetLength = this.packetLength; - // If we calculated the length of the current SSL record before, use that information. - if (packetLength > 0) { - if (in.readableBytes() < packetLength) { - return; - } - } else { - // Get the packet length and wait until we get a packets worth of data to unwrap. - final int readableBytes = in.readableBytes(); - if (readableBytes < SslUtils.SSL_RECORD_HEADER_LENGTH) { - return; - } - packetLength = getEncryptedPacketLength(in, in.readerIndex()); - if (packetLength == SslUtils.NOT_ENCRYPTED) { - // Not an SSL/TLS packet - NotSslRecordException e = new NotSslRecordException( - "not an SSL/TLS record: " + ByteBufUtil.hexDump(in)); - in.skipBytes(in.readableBytes()); - - // First fail the handshake promise as we may need to have access to the SSLEngine which may - // be released because the user will remove the SslHandler in an exceptionCaught(...) implementation. - setHandshakeFailure(ctx, e); - - throw e; - } - assert packetLength > 0; - if (packetLength > readableBytes) { - // wait until the whole packet can be read - this.packetLength = packetLength; - return; - } - } - - // Reset the state of this class so we can get the length of the next packet. We assume the entire packet will - // be consumed by the SSLEngine. - this.packetLength = 0; - try { - final int bytesConsumed = unwrap(ctx, in, packetLength); - assert bytesConsumed == packetLength || engine.isInboundDone() : - "we feed the SSLEngine a packets worth of data: " + packetLength + " but it only consumed: " + - bytesConsumed; - } catch (Throwable cause) { - handleUnwrapThrowable(ctx, cause); - } - } - - private void decodeNonJdkCompatible(ChannelHandlerContext ctx, ByteBuf in) { - try { - unwrap(ctx, in, in.readableBytes()); - } catch (Throwable cause) { - handleUnwrapThrowable(ctx, cause); - } - } - - private void handleUnwrapThrowable(ChannelHandlerContext ctx, Throwable cause) { - try { - // We should attempt to notify the handshake failure before writing any pending data. If we are in unwrap - // and failed during the handshake process, and we attempt to wrap, then promises will fail, and if - // listeners immediately close the Channel then we may end up firing the handshake event after the Channel - // has been closed. - if (handshakePromise.tryFailure(cause)) { - ctx.fireUserEventTriggered(new SslHandshakeCompletionEvent(cause)); - } - - // We need to flush one time as there may be an alert that we should send to the remote peer because - // of the SSLException reported here. - wrapAndFlush(ctx); - } catch (SSLException ex) { - logger.debug("SSLException during trying to call SSLEngine.wrap(...)" + - " because of an previous SSLException, ignoring...", ex); - } finally { - // ensure we always flush and close the channel. - setHandshakeFailure(ctx, cause, true, false, true); - } - PlatformDependent.throwException(cause); - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) throws SSLException { - if (isStateSet(STATE_PROCESS_TASK)) { - return; - } - if (jdkCompatibilityMode) { - decodeJdkCompatible(ctx, in); - } else { - decodeNonJdkCompatible(ctx, in); - } - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { - channelReadComplete0(ctx); - } - - private void channelReadComplete0(ChannelHandlerContext ctx) { - // Discard bytes of the cumulation buffer if needed. - discardSomeReadBytes(); - - flushIfNeeded(ctx); - readIfNeeded(ctx); - - clearState(STATE_FIRE_CHANNEL_READ); - ctx.fireChannelReadComplete(); - } - - private void readIfNeeded(ChannelHandlerContext ctx) { - // If handshake is not finished yet, we need more data. - if (!ctx.channel().config().isAutoRead() && - (!isStateSet(STATE_FIRE_CHANNEL_READ) || !handshakePromise.isDone())) { - // No auto-read used and no message passed through the ChannelPipeline or the handshake was not complete - // yet, which means we need to trigger the read to ensure we not encounter any stalls. - ctx.read(); - } - } - - private void flushIfNeeded(ChannelHandlerContext ctx) { - if (isStateSet(STATE_NEEDS_FLUSH)) { - forceFlush(ctx); - } - } - - /** - * Calls {@link SSLEngine#unwrap(ByteBuffer, ByteBuffer)} with an empty buffer to handle handshakes, etc. - */ - private int unwrapNonAppData(ChannelHandlerContext ctx) throws SSLException { - return unwrap(ctx, Unpooled.EMPTY_BUFFER, 0); - } - - /** - * Unwraps inbound SSL records. - */ - private int unwrap(ChannelHandlerContext ctx, ByteBuf packet, int length) throws SSLException { - final int originalLength = length; - boolean wrapLater = false; - boolean notifyClosure = false; - boolean executedRead = false; - ByteBuf decodeOut = allocate(ctx, length); - try { - // Only continue to loop if the handler was not removed in the meantime. - // See https://github.com/netty/netty/issues/5860 - do { - final SSLEngineResult result = engineType.unwrap(this, packet, length, decodeOut); - final Status status = result.getStatus(); - final HandshakeStatus handshakeStatus = result.getHandshakeStatus(); - final int produced = result.bytesProduced(); - final int consumed = result.bytesConsumed(); - - // Skip bytes now in case unwrap is called in a re-entry scenario. For example LocalChannel.read() - // may entry this method in a re-entry fashion and if the peer is writing into a shared buffer we may - // unwrap the same data multiple times. - packet.skipBytes(consumed); - length -= consumed; - - // The expected sequence of events is: - // 1. Notify of handshake success - // 2. fireChannelRead for unwrapped data - if (handshakeStatus == HandshakeStatus.FINISHED || handshakeStatus == HandshakeStatus.NOT_HANDSHAKING) { - wrapLater |= (decodeOut.isReadable() ? - setHandshakeSuccessUnwrapMarkReentry() : setHandshakeSuccess()) || - handshakeStatus == HandshakeStatus.FINISHED; - } - - // Dispatch decoded data after we have notified of handshake success. If this method has been invoked - // in a re-entry fashion we execute a task on the executor queue to process after the stack unwinds - // to preserve order of events. - if (decodeOut.isReadable()) { - setState(STATE_FIRE_CHANNEL_READ); - if (isStateSet(STATE_UNWRAP_REENTRY)) { - executedRead = true; - executeChannelRead(ctx, decodeOut); - } else { - ctx.fireChannelRead(decodeOut); - } - decodeOut = null; - } - - if (status == Status.CLOSED) { - notifyClosure = true; // notify about the CLOSED state of the SSLEngine. See #137 - } else if (status == Status.BUFFER_OVERFLOW) { - if (decodeOut != null) { - decodeOut.release(); - } - final int applicationBufferSize = engine.getSession().getApplicationBufferSize(); - // Allocate a new buffer which can hold all the rest data and loop again. - // It may happen that applicationBufferSize < produced while there is still more to unwrap, in this - // case we will just allocate a new buffer with the capacity of applicationBufferSize and call - // unwrap again. - decodeOut = allocate(ctx, engineType.calculatePendingData(this, applicationBufferSize < produced ? - applicationBufferSize : applicationBufferSize - produced)); - continue; - } - - if (handshakeStatus == HandshakeStatus.NEED_TASK) { - boolean pending = runDelegatedTasks(true); - if (!pending) { - // 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; - } - } else if (handshakeStatus == HandshakeStatus.NEED_WRAP) { - // If the wrap operation transitions the status to 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. - if (wrapNonAppData(ctx, true) && length == 0) { - break; - } - } - - 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 || - (length == 0 && handshakeStatus == HandshakeStatus.NOT_HANDSHAKING))) { - 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 - readIfNeeded(ctx); - } - - break; - } else if (decodeOut == null) { - decodeOut = allocate(ctx, length); - } - } while (!ctx.isRemoved()); - - if (isStateSet(STATE_FLUSHED_BEFORE_HANDSHAKE) && 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 - clearState(STATE_FLUSHED_BEFORE_HANDSHAKE); - wrapLater = true; - } - - if (wrapLater) { - wrap(ctx, true); - } - } finally { - if (decodeOut != null) { - decodeOut.release(); - } - - if (notifyClosure) { - if (executedRead) { - executeNotifyClosePromise(ctx); - } else { - notifyClosePromise(null); - } - } - } - return originalLength - length; - } - - private boolean setHandshakeSuccessUnwrapMarkReentry() { - // setHandshakeSuccess calls out to external methods which may trigger re-entry. We need to preserve ordering of - // fireChannelRead for decodeOut relative to re-entry data. - final boolean setReentryState = !isStateSet(STATE_UNWRAP_REENTRY); - if (setReentryState) { - setState(STATE_UNWRAP_REENTRY); - } - try { - return setHandshakeSuccess(); - } finally { - // It is unlikely this specific method will be re-entry because handshake completion is infrequent, but just - // in case we only clear the state if we set it in the first place. - if (setReentryState) { - clearState(STATE_UNWRAP_REENTRY); - } - } - } - - private void executeNotifyClosePromise(final ChannelHandlerContext ctx) { - try { - ctx.executor().execute(() -> notifyClosePromise(null)); - } catch (RejectedExecutionException e) { - notifyClosePromise(e); - } - } - - private static void executeChannelRead(final ChannelHandlerContext ctx, final ByteBuf decodedOut) { - try { - ctx.executor().execute(() -> ctx.fireChannelRead(decodedOut)); - } catch (RejectedExecutionException e) { - decodedOut.release(); - throw e; - } - } - - private static ByteBuffer toByteBuffer(ByteBuf out, int index, int len) { - return out.nioBufferCount() == 1 ? out.internalNioBuffer(index, len) : - out.nioBuffer(index, len); - } - - private static boolean inEventLoop(Executor executor) { - return executor instanceof EventExecutor && ((EventExecutor) executor).inEventLoop(); - } - - /** - * 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 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. As we are on the - // EventLoop we can just run all tasks at once. - for (;;) { - Runnable task = engine.getDelegatedTask(); - if (task == null) { - return true; - } - setState(STATE_PROCESS_TASK); - if (task instanceof AsyncRunnable) { - // Let's set the task to processing task before we try to execute it. - boolean pending = false; - try { - AsyncRunnable asyncTask = (AsyncRunnable) task; - AsyncTaskCompletionHandler completionHandler = new AsyncTaskCompletionHandler(inUnwrap); - asyncTask.run(completionHandler); - pending = completionHandler.resumeLater(); - if (pending) { - return false; - } - } finally { - if (!pending) { - // The task has completed, lets clear the state. If it is not completed we will clear the - // state once it is. - clearState(STATE_PROCESS_TASK); - } - } - } else { - try { - task.run(); - } finally { - clearState(STATE_PROCESS_TASK); - } - } - } - } else { - executeDelegatedTask(inUnwrap); - return false; - } - } - - private SslTasksRunner getTaskRunner(boolean inUnwrap) { - return inUnwrap ? sslTaskRunnerForUnwrap : sslTaskRunner; - } - - private void executeDelegatedTask(boolean inUnwrap) { - executeDelegatedTask(getTaskRunner(inUnwrap)); - } - - private void executeDelegatedTask(SslTasksRunner task) { - setState(STATE_PROCESS_TASK); - try { - delegatedTaskExecutor.execute(task); - } catch (RejectedExecutionException e) { - clearState(STATE_PROCESS_TASK); - throw e; - } - } - - private final class AsyncTaskCompletionHandler implements Runnable { - private final boolean inUnwrap; - boolean didRun; - boolean resumeLater; - - AsyncTaskCompletionHandler(boolean inUnwrap) { - this.inUnwrap = inUnwrap; - } - - @Override - public void run() { - didRun = true; - if (resumeLater) { - getTaskRunner(inUnwrap).runComplete(); - } - } - - boolean resumeLater() { - if (!didRun) { - resumeLater = true; - return true; - } - return false; - } - } - - /** - * {@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; - private final Runnable runCompleteTask = new Runnable() { - @Override - public void run() { - runComplete(); - } - }; - - 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); - } - } - - // Try to call exceptionCaught(...) - private void safeExceptionCaught(Throwable cause) { - try { - exceptionCaught(ctx, wrapIfNeeded(cause)); - } catch (Throwable error) { - ctx.fireExceptionCaught(error); - } - } - - 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); - } - } - - /** - * 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(); - clearState(STATE_PROCESS_TASK); - try { - HandshakeStatus status = engine.getHandshakeStatus(); - switch (status) { - // There is another task that needs to be executed and offloaded to the delegatingTaskExecutor as - // a result of this. Let's reschedule.... - case NEED_TASK: - executeDelegatedTask(this); - - break; - - // The handshake finished, lets notify about the completion of it and resume processing. - case FINISHED: - // Not handshaking anymore, lets notify about the completion if not done yet and resume processing. - case NOT_HANDSHAKING: - setHandshakeSuccess(); // NOT_HANDSHAKING -> workaround for android skipping FINISHED state. - 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; - } - 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); - - 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: - try { - unwrapNonAppData(ctx); - } catch (SSLException e) { - handleUnwrapThrowable(ctx, e); - return; - } - 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 { - 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; - } - - // 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); - } - } - - void runComplete() { - EventExecutor executor = ctx.executor(); - if (executor.inEventLoop()) { - resumeOnEventExecutor(); - } else { - // Jump back on the EventExecutor. - executor.execute(new Runnable() { - @Override - public void run() { - resumeOnEventExecutor(); - } - }); - } - } - - @Override - public void run() { - try { - Runnable task = engine.getDelegatedTask(); - if (task == null) { - // The task was processed in the meantime. Let's just return. - return; - } - if (task instanceof AsyncRunnable) { - AsyncRunnable asyncTask = (AsyncRunnable) task; - asyncTask.run(runCompleteTask); - } else { - task.run(); - runComplete(); - } - } catch (Throwable cause) { - handleException(cause); - } - } - - private void handleException(final Throwable cause) { - EventExecutor executor = ctx.executor(); - if (executor.inEventLoop()) { - clearState(STATE_PROCESS_TASK); - safeExceptionCaught(cause); - } else { - try { - executor.execute(() -> { - clearState(STATE_PROCESS_TASK); - safeExceptionCaught(cause); - }); - } catch (RejectedExecutionException ignore) { - clearState(STATE_PROCESS_TASK); - // the context itself will handle the rejected exception when try to schedule the operation so - // ignore the RejectedExecutionException - ctx.fireExceptionCaught(cause); - } - } - } - } - - /** - * Notify all the handshake futures about the successfully handshake - * @return {@code true} if {@link #handshakePromise} was set successfully and a {@link SslHandshakeCompletionEvent} - * was fired. {@code false} otherwise. - */ - private boolean setHandshakeSuccess() { - // Our control flow may invoke this method multiple times for a single FINISHED event. For example - // wrapNonAppData may drain pendingUnencryptedWrites in wrap which transitions to handshake from FINISHED to - // NOT_HANDSHAKING which invokes setHandshakeSuccess, and then wrapNonAppData also directly invokes this method. - final boolean notified = !handshakePromise.isDone() && handshakePromise.trySuccess(ctx.channel()); - if (notified) { - if (logger.isDebugEnabled()) { - SSLSession session = engine.getSession(); - logger.debug( - "{} HANDSHAKEN: protocol:{} cipher suite:{}", - ctx.channel(), - session.getProtocol(), - session.getCipherSuite()); - } - ctx.fireUserEventTriggered(SslHandshakeCompletionEvent.SUCCESS); - } - if (isStateSet(STATE_READ_DURING_HANDSHAKE)) { - clearState(STATE_READ_DURING_HANDSHAKE); - if (!ctx.channel().config().isAutoRead()) { - ctx.read(); - } - } - return notified; - } - - /** - * Notify all the handshake futures about the failure during the handshake. - */ - private void setHandshakeFailure(ChannelHandlerContext ctx, Throwable cause) { - setHandshakeFailure(ctx, cause, true, true, false); - } - - /** - * Notify all the handshake futures about the failure during the handshake. - */ - private void setHandshakeFailure(ChannelHandlerContext ctx, Throwable cause, boolean closeInbound, - boolean notify, boolean alwaysFlushAndClose) { - try { - // Release all resources such as internal buffers that SSLEngine is managing. - setState(STATE_OUTBOUND_CLOSED); - engine.closeOutbound(); - - if (closeInbound) { - try { - engine.closeInbound(); - } catch (SSLException e) { - if (logger.isDebugEnabled()) { - // only log in debug mode as it most likely harmless and latest chrome still trigger - // this all the time. - // - // See https://github.com/netty/netty/issues/1340 - String msg = e.getMessage(); - 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); - } - } - } - } - if (handshakePromise.tryFailure(cause) || alwaysFlushAndClose) { - SslUtils.handleHandshakeFailure(ctx, cause, notify); - } - } finally { - // Ensure we remove and fail all pending writes in all cases and so release memory quickly. - releaseAndFailAll(ctx, cause); - } - } - - private void setHandshakeFailureTransportFailure(ChannelHandlerContext ctx, Throwable cause) { - // If TLS control frames fail to write we are in an unknown state and may become out of - // sync with our peer. We give up and close the channel. This will also take care of - // cleaning up any outstanding state (e.g. handshake promise, queued unencrypted data). - try { - SSLException transportFailure = new SSLException("failure when writing TLS control frames", cause); - releaseAndFailAll(ctx, transportFailure); - if (handshakePromise.tryFailure(transportFailure)) { - ctx.fireUserEventTriggered(new SslHandshakeCompletionEvent(transportFailure)); - } - } finally { - ctx.close(); - } - } - - private void releaseAndFailAll(ChannelHandlerContext ctx, Throwable cause) { - if (pendingUnencryptedWrites != null) { - pendingUnencryptedWrites.releaseAndFailAll(ctx, cause); - } - } - - private void notifyClosePromise(Throwable cause) { - if (cause == null) { - if (sslClosePromise.trySuccess(ctx.channel())) { - ctx.fireUserEventTriggered(SslCloseCompletionEvent.SUCCESS); - } - } else { - if (sslClosePromise.tryFailure(cause)) { - ctx.fireUserEventTriggered(new SslCloseCompletionEvent(cause)); - } - } - } - - private Future closeOutboundAndChannel( - final ChannelHandlerContext ctx, boolean disconnect) { - setState(STATE_OUTBOUND_CLOSED); - engine.closeOutbound(); - - if (!ctx.channel().isActive()) { - if (disconnect) { - return ctx.disconnect(); - } - return ctx.close(); - } - Promise promise = ctx.newPromise(); - Promise closeNotifyPromise = ctx.newPromise(); - try { - flush(ctx, closeNotifyPromise); - } finally { - if (!isStateSet(STATE_CLOSE_NOTIFY)) { - setState(STATE_CLOSE_NOTIFY); - // It's important that we do not pass the original Promise to safeClose(...) as when flush(....) - // throws an Exception it will be propagated to the AbstractChannelHandlerContext which will try - // to fail the promise because of this. This will then fail as it was already completed by - // safeClose(...). We create a new Promise and try to notify the original Promise - // once it is complete. If we fail to do so we just ignore it as in this case it was failed already - // because of a propagated Exception. - // - // See https://github.com/netty/netty/issues/5931 - Promise cascade = ctx.newPromise(); - cascade.asFuture().cascadeTo(promise); - safeClose(ctx, closeNotifyPromise.asFuture(), cascade); - } else { - /// We already handling the close_notify so just attach the promise to the sslClosePromise. - sslCloseFuture().addListener(future -> promise.setSuccess(null)); - } - } - return promise.asFuture(); - } - - private void flush(ChannelHandlerContext ctx, Promise promise) { - if (pendingUnencryptedWrites != null) { - pendingUnencryptedWrites.add(Unpooled.EMPTY_BUFFER, promise); - } else { - promise.setFailure(newPendingWritesNullException()); - } - flush(ctx); - } - - @Override - public void handlerAdded0(final ChannelHandlerContext ctx) throws Exception { - this.ctx = ctx; - - Channel channel = ctx.channel(); - pendingUnencryptedWrites = new SslHandlerCoalescingBufferQueue(channel, 16); - boolean fastOpen = Boolean.TRUE.equals(channel.config().getOption(ChannelOption.TCP_FASTOPEN_CONNECT)); - boolean active = channel.isActive(); - if (active || fastOpen) { - // Explicitly flush the handshake only if the channel is already active. - // With TCP Fast Open, we write to the outbound buffer before the TCP connect is established. - // The buffer will then be flushed as part of establishing the connection, saving us a round-trip. - startHandshakeProcessing(active); - // If we weren't able to include client_hello in the TCP SYN (e.g. no token, disabled at the OS) we have to - // flush pending data in the outbound buffer later in channelActive(). - final ChannelOutboundBuffer outboundBuffer; - if (fastOpen && ((outboundBuffer = channel.unsafe().outboundBuffer()) == null || - outboundBuffer.totalPendingWriteBytes() > 0)) { - setState(STATE_NEEDS_FLUSH); - } - } - } - - private void startHandshakeProcessing(boolean flushAtEnd) { - if (!isStateSet(STATE_HANDSHAKE_STARTED)) { - setState(STATE_HANDSHAKE_STARTED); - 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(flushAtEnd); - } - applyHandshakeTimeout(); - } else if (isStateSet(STATE_NEEDS_FLUSH)) { - forceFlush(ctx); - } - } - - /** - * Performs TLS renegotiation. - */ - public Future renegotiate() { - ChannelHandlerContext ctx = this.ctx; - if (ctx == null) { - throw new IllegalStateException(); - } - - return renegotiate(ctx.executor().newPromise()); - } - - /** - * Performs TLS renegotiation. - */ - public Future renegotiate(final Promise promise) { - requireNonNull(promise, "promise"); - - ChannelHandlerContext ctx = this.ctx; - if (ctx == null) { - throw new IllegalStateException(); - } - - EventExecutor executor = ctx.executor(); - if (!executor.inEventLoop()) { - executor.execute(() -> renegotiateOnEventLoop(promise)); - return promise.asFuture(); - } - - renegotiateOnEventLoop(promise); - return promise.asFuture(); - } - - private void renegotiateOnEventLoop(final Promise newHandshakePromise) { - Future oldHandshakePromise = handshakeFuture(); - if (!oldHandshakePromise.isDone()) { - // There's no need to handshake because handshake is in progress already. - // Merge the new promise into the old one. - oldHandshakePromise.cascadeTo(newHandshakePromise); - } else { - handshakePromise = newHandshakePromise; - handshake(true); - applyHandshakeTimeout(); - } - } - - /** - * Performs TLS (re)negotiation. - * @param flushAtEnd Set to {@code true} if the outbound buffer should be flushed (written to the network) at the - * end. Set to {@code false} if the handshake will be flushed later, e.g. as part of TCP Fast Open - * connect. - */ - private void handshake(boolean flushAtEnd) { - 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; - } - 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 FutureListener that was added to the Future 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; - } - - // Begin handshake. - final ChannelHandlerContext ctx = this.ctx; - try { - engine.beginHandshake(); - wrapNonAppData(ctx, false); - } catch (Throwable e) { - setHandshakeFailure(ctx, e); - } finally { - if (flushAtEnd) { - forceFlush(ctx); - } - } - } - - private void applyHandshakeTimeout() { - final Promise localHandshakePromise = handshakePromise; - - // Set timeout if necessary. - final long handshakeTimeoutMillis = this.handshakeTimeoutMillis; - if (handshakeTimeoutMillis <= 0 || localHandshakePromise.isDone()) { - return; - } - - Future timeoutFuture = ctx.executor().schedule(() -> { - if (localHandshakePromise.isDone()) { - return; - } - - SSLException exception = - new SslHandshakeTimeoutException("handshake timed out after " + handshakeTimeoutMillis + "ms"); - try { - if (localHandshakePromise.tryFailure(exception)) { - SslUtils.handleHandshakeFailure(ctx, exception, true); - } - } finally { - releaseAndFailAll(ctx, exception); - } - }, handshakeTimeoutMillis, TimeUnit.MILLISECONDS); - - // Cancel the handshake timeout when handshake is finished. - localHandshakePromise.asFuture().addListener(f -> timeoutFuture.cancel()); - } - - private void forceFlush(ChannelHandlerContext ctx) { - clearState(STATE_NEEDS_FLUSH); - ctx.flush(); - } - - /** - * Issues an initial TLS handshake once connected when used in client-mode - */ - @Override - public void channelActive(final ChannelHandlerContext ctx) throws Exception { - if (!startTls) { - startHandshakeProcessing(true); - } - ctx.fireChannelActive(); - } - - private void safeClose( - final ChannelHandlerContext ctx, final Future flushFuture, - final Promise promise) { - if (!ctx.channel().isActive()) { - ctx.close().cascadeTo(promise); - return; - } - - Future timeoutFuture; - if (!flushFuture.isDone()) { - long closeNotifyTimeout = closeNotifyFlushTimeoutMillis; - if (closeNotifyTimeout > 0) { - // Force-close the connection if close_notify is not fully sent in time. - timeoutFuture = ctx.executor().schedule(() -> { - // May be done in the meantime as cancel(...) is only best effort. - if (!flushFuture.isDone()) { - logger.warn("{} Last write attempt timed out; force-closing the connection.", - ctx.channel()); - addCloseListener(ctx.close(), promise); - } - }, closeNotifyTimeout, TimeUnit.MILLISECONDS); - } else { - timeoutFuture = null; - } - } else { - timeoutFuture = null; - } - - // Close the connection if close_notify is sent in time. - flushFuture.addListener(f -> { - if (timeoutFuture != null) { - timeoutFuture.cancel(); - } - final long closeNotifyReadTimeout = closeNotifyReadTimeoutMillis; - if (closeNotifyReadTimeout <= 0) { - if (ctx.channel().isActive()) { - // Trigger the close in all cases to make sure the promise is notified - // See https://github.com/netty/netty/issues/2358 - addCloseListener(ctx.close(), promise); - } else { - promise.trySuccess(null); - } - } else { - Future closeNotifyReadTimeoutFuture; - - Future closeFuture = sslCloseFuture(); - - if (!closeFuture.isDone()) { - closeNotifyReadTimeoutFuture = ctx.executor().schedule(() -> { - if (!closeFuture.isDone()) { - logger.debug( - "{} did not receive close_notify in {}ms; force-closing the connection.", - ctx.channel(), closeNotifyReadTimeout); - - // Do the close now... - addCloseListener(ctx.close(), promise); - } - }, closeNotifyReadTimeout, TimeUnit.MILLISECONDS); - } else { - closeNotifyReadTimeoutFuture = null; - } - - // Do the close once we received the close_notify. - closeFuture.addListener(future -> { - if (closeNotifyReadTimeoutFuture != null) { - closeNotifyReadTimeoutFuture.cancel(); - } - if (ctx.channel().isActive()) { - addCloseListener(ctx.close(), promise); - } else { - promise.trySuccess(null); - } - }); - } - }); - } - - private static void addCloseListener(Future future, Promise promise) { - // We notify the promise in the PromiseNotifier as there is a "race" where the close(...) call - // by the timeoutFuture and the close call in the flushFuture listener will be called. Because of - // this we need to use trySuccess() and tryFailure(...) as otherwise we can cause an - // IllegalStateException. - // Also we not want to log if the notification happens as this is expected in some cases. - // See https://github.com/netty/netty/issues/5598 - future.cascadeTo(promise); - } - - /** - * Always prefer a direct buffer when it's pooled, so that we reduce the number of memory copies - * in {@link OpenSslEngine}. - */ - private ByteBuf allocate(ChannelHandlerContext ctx, int capacity) { - ByteBufAllocator alloc = ctx.alloc(); - if (engineType.wantsDirectBuffer) { - return alloc.directBuffer(capacity); - } else { - return alloc.buffer(capacity); - } - } - - /** - * Allocates an outbound network buffer for {@link SSLEngine#wrap(ByteBuffer, ByteBuffer)} which can encrypt - * the specified amount of pending bytes. - */ - private ByteBuf allocateOutNetBuf(ChannelHandlerContext ctx, int pendingBytes, int numComponents) { - return engineType.allocateWrapBuffer(this, ctx.alloc(), pendingBytes, numComponents); - } - - private boolean isStateSet(int bit) { - return (state & bit) == bit; - } - - private void setState(int bit) { - state |= bit; - } - - private void clearState(int bit) { - state &= ~bit; - } - - /** - * Each call to SSL_write will introduce about ~100 bytes of overhead. This coalescing queue attempts to increase - * goodput by aggregating the plaintext in chunks of {@link #wrapDataSize}. If many small chunks are written - * this can increase goodput, decrease the amount of calls to SSL_write, and decrease overall encryption operations. - */ - private final class SslHandlerCoalescingBufferQueue extends AbstractCoalescingBufferQueue { - - SslHandlerCoalescingBufferQueue(Channel channel, int initSize) { - super(channel, initSize); - } - - @Override - protected ByteBuf compose(ByteBufAllocator alloc, ByteBuf cumulation, ByteBuf next) { - final int wrapDataSize = SslHandler.this.wrapDataSize; - if (cumulation instanceof CompositeByteBuf) { - CompositeByteBuf composite = (CompositeByteBuf) cumulation; - int numComponents = composite.numComponents(); - if (numComponents == 0 || - !attemptCopyToCumulation(composite.internalComponent(numComponents - 1), next, wrapDataSize)) { - composite.addComponent(true, next); - } - return composite; - } - return attemptCopyToCumulation(cumulation, next, wrapDataSize) ? cumulation : - copyAndCompose(alloc, cumulation, next); - } - - @Override - protected ByteBuf composeFirst(ByteBufAllocator allocator, ByteBuf first) { - if (first instanceof CompositeByteBuf) { - CompositeByteBuf composite = (CompositeByteBuf) first; - if (engineType.wantsDirectBuffer) { - first = allocator.directBuffer(composite.readableBytes()); - } else { - first = allocator.heapBuffer(composite.readableBytes()); - } - try { - first.writeBytes(composite); - } catch (Throwable cause) { - first.release(); - throw cause; - } - composite.release(); - } - return first; - } - - @Override - protected ByteBuf removeEmptyValue() { - return null; - } - } - - private static boolean attemptCopyToCumulation(ByteBuf cumulation, ByteBuf next, int wrapDataSize) { - final int inReadableBytes = next.readableBytes(); - final int cumulationCapacity = cumulation.capacity(); - if (wrapDataSize - cumulation.readableBytes() >= inReadableBytes && - // Avoid using the same buffer if next's data would make cumulation exceed the wrapDataSize. - // Only copy if there is enough space available and the capacity is large enough, and attempt to - // resize if the capacity is small. - (cumulation.isWritable(inReadableBytes) && cumulationCapacity >= wrapDataSize || - cumulationCapacity < wrapDataSize && - ensureWritableSuccess(cumulation.ensureWritable(inReadableBytes, false)))) { - cumulation.writeBytes(next); - next.release(); - return true; - } - return false; - } - - private final class LazyPromise extends DefaultPromise { - - LazyPromise() { - super(ImmediateEventExecutor.INSTANCE); - } - - @Override - protected void checkDeadLock() { - if (ctx == null) { - // If ctx is null the handlerAdded(...) callback was not called, in this case the checkDeadLock() - // method was called from another Thread then the one that is used by ctx.executor(). We need to - // guard against this as a user can see a race if handshakeFuture().sync() is called but the - // handlerAdded(..) method was not yet as it is called from the EventExecutor of the - // ChannelHandlerContext. - return; - } - checkDeadLock(ctx.executor()); - } - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/SslHandshakeCompletionEvent.java b/handler/src/main/java/io/netty/handler/ssl/SslHandshakeCompletionEvent.java deleted file mode 100644 index 004c7b0196..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/SslHandshakeCompletionEvent.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - - -/** - * Event that is fired once the SSL handshake is complete, which may be because it was successful or there - * was an error. - */ -public final class SslHandshakeCompletionEvent extends SslCompletionEvent { - - public static final SslHandshakeCompletionEvent SUCCESS = new SslHandshakeCompletionEvent(); - - /** - * Creates a new event that indicates a successful handshake. - */ - private SslHandshakeCompletionEvent() { } - - /** - * Creates a new event that indicates an unsuccessful handshake. - * Use {@link #SUCCESS} to indicate a successful handshake. - */ - public SslHandshakeCompletionEvent(Throwable cause) { - super(cause); - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/SslHandshakeTimeoutException.java b/handler/src/main/java/io/netty/handler/ssl/SslHandshakeTimeoutException.java deleted file mode 100644 index cbbbd753de..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/SslHandshakeTimeoutException.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import javax.net.ssl.SSLHandshakeException; - -/** - * {@link SSLHandshakeException} that is used when a handshake failed due a configured timeout. - */ -public final class SslHandshakeTimeoutException extends SSLHandshakeException { - - SslHandshakeTimeoutException(String reason) { - super(reason); - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/SslMasterKeyHandler.java b/handler/src/main/java/io/netty/handler/ssl/SslMasterKeyHandler.java deleted file mode 100644 index 01bdd78924..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/SslMasterKeyHandler.java +++ /dev/null @@ -1,197 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.buffer.ByteBufUtil; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.internal.ReflectionUtil; -import io.netty.util.internal.SystemPropertyUtil; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLSession; -import java.lang.reflect.Field; - -/** - * The {@link SslMasterKeyHandler} is a channel-handler you can include in your pipeline to consume the master key - * & session identifier for a TLS session. - * This can be very useful, for instance the {@link WiresharkSslMasterKeyHandler} implementation will - * log the secret & identifier in a format that is consumable by Wireshark -- allowing easy decryption of pcap/tcpdumps. - */ -public abstract class SslMasterKeyHandler implements ChannelHandler { - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(SslMasterKeyHandler.class); - - /** - * The JRE SSLSessionImpl cannot be imported - */ - private static final Class SSL_SESSIONIMPL_CLASS; - - /** - * The master key field in the SSLSessionImpl - */ - private static final Field SSL_SESSIONIMPL_MASTER_SECRET_FIELD; - - /** - * A system property that can be used to turn on/off the {@link SslMasterKeyHandler} dynamically without having - * to edit your pipeline. - * {@code -Dio.netty.ssl.masterKeyHandler=true} - */ - public static final String SYSTEM_PROP_KEY = "io.netty.ssl.masterKeyHandler"; - - /** - * The unavailability cause of whether the private Sun implementation of SSLSessionImpl is available. - */ - private static final Throwable UNAVAILABILITY_CAUSE; - - static { - Throwable cause; - Class clazz = null; - Field field = null; - try { - clazz = Class.forName("sun.security.ssl.SSLSessionImpl"); - field = clazz.getDeclaredField("masterSecret"); - cause = ReflectionUtil.trySetAccessible(field, true); - } catch (Throwable e) { - cause = e; - if (logger.isTraceEnabled()) { - logger.debug("sun.security.ssl.SSLSessionImpl is unavailable.", e); - } else { - logger.debug("sun.security.ssl.SSLSessionImpl is unavailable: {}", e.getMessage()); - } - } - UNAVAILABILITY_CAUSE = cause; - SSL_SESSIONIMPL_CLASS = clazz; - SSL_SESSIONIMPL_MASTER_SECRET_FIELD = field; - } - - /** - * Constructor. - */ - protected SslMasterKeyHandler() { - } - - /** - * Ensure that SSLSessionImpl is available. - * @throws UnsatisfiedLinkError if unavailable - */ - public static void ensureSunSslEngineAvailability() { - if (UNAVAILABILITY_CAUSE != null) { - throw new IllegalStateException( - "Failed to find SSLSessionImpl on classpath", UNAVAILABILITY_CAUSE); - } - } - - /** - * Returns the cause of unavailability. - * - * @return the cause if unavailable. {@code null} if available. - */ - public static Throwable sunSslEngineUnavailabilityCause() { - return UNAVAILABILITY_CAUSE; - } - - /* Returns {@code true} if and only if sun.security.ssl.SSLSessionImpl exists in the runtime. - */ - public static boolean isSunSslEngineAvailable() { - return UNAVAILABILITY_CAUSE == null; - } - - /** - * Consume the master key for the session and the sessionId - * @param masterKey A 48-byte secret shared between the client and server. - * @param session The current TLS session - */ - protected abstract void accept(SecretKey masterKey, SSLSession session); - - @Override - public final void userEventTriggered(ChannelHandlerContext ctx, Object evt) { - //only try to log the session info if the ssl handshake has successfully completed. - if (evt == SslHandshakeCompletionEvent.SUCCESS && masterKeyHandlerEnabled()) { - final SslHandler handler = ctx.pipeline().get(SslHandler.class); - final SSLEngine engine = handler.engine(); - final SSLSession sslSession = engine.getSession(); - - //the OpenJDK does not expose a way to get the master secret, so try to use reflection to get it. - if (isSunSslEngineAvailable() && sslSession.getClass().equals(SSL_SESSIONIMPL_CLASS)) { - final SecretKey secretKey; - try { - secretKey = (SecretKey) SSL_SESSIONIMPL_MASTER_SECRET_FIELD.get(sslSession); - } catch (IllegalAccessException e) { - throw new IllegalArgumentException("Failed to access the field 'masterSecret' " + - "via reflection.", e); - } - accept(secretKey, sslSession); - } else if (OpenSsl.isAvailable() && engine instanceof ReferenceCountedOpenSslEngine) { - SecretKeySpec secretKey = ((ReferenceCountedOpenSslEngine) engine).masterKey(); - accept(secretKey, sslSession); - } - } - - ctx.fireUserEventTriggered(evt); - } - - /** - * Checks if the handler is set up to actually handle/accept the event. - * By default the {@link #SYSTEM_PROP_KEY} property is checked, but any implementations of this class are - * free to override if they have different mechanisms of checking. - * - * @return true if it should handle, false otherwise. - */ - protected boolean masterKeyHandlerEnabled() { - return SystemPropertyUtil.getBoolean(SYSTEM_PROP_KEY, false); - } - - /** - * Create a {@link WiresharkSslMasterKeyHandler} instance. - * This TLS master key handler logs the master key and session-id in a format - * understood by Wireshark -- this can be especially useful if you need to ever - * decrypt a TLS session and are using perfect forward secrecy (i.e. Diffie-Hellman) - * The key and session identifier are forwarded to the log named 'io.netty.wireshark'. - */ - public static SslMasterKeyHandler newWireSharkSslMasterKeyHandler() { - return new WiresharkSslMasterKeyHandler(); - } - - /** - * Record the session identifier and master key to the {@link InternalLogger} named {@code io.netty.wireshark}. - * ex. {@code RSA Session-ID:XXX Master-Key:YYY} - * This format is understood by Wireshark 1.6.0. - * https://code.wireshark.org/review/gitweb?p=wireshark.git;a=commit;h=686d4cabb41185591c361f9ec6b709034317144b - * The key and session identifier are forwarded to the log named 'io.netty.wireshark'. - */ - private static final class WiresharkSslMasterKeyHandler extends SslMasterKeyHandler { - - private static final InternalLogger wireshark_logger = - InternalLoggerFactory.getInstance("io.netty.wireshark"); - - @Override - protected void accept(SecretKey masterKey, SSLSession session) { - if (masterKey.getEncoded().length != 48) { - throw new IllegalArgumentException("An invalid length master key was provided."); - } - final byte[] sessionId = session.getId(); - wireshark_logger.warn("RSA Session-ID:{} Master-Key:{}", - ByteBufUtil.hexDump(sessionId).toLowerCase(), - ByteBufUtil.hexDump(masterKey.getEncoded()).toLowerCase()); - } - } - -} diff --git a/handler/src/main/java/io/netty/handler/ssl/SslProtocols.java b/handler/src/main/java/io/netty/handler/ssl/SslProtocols.java deleted file mode 100644 index c38e1cfab7..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/SslProtocols.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -/** - * SSL/TLS protocols - */ -public final class SslProtocols { - - /** - * SSL v2 Hello - * - * @deprecated SSLv2Hello is no longer secure. Consider using {@link #TLS_v1_2} or {@link #TLS_v1_3} - */ - @Deprecated - public static final String SSL_v2_HELLO = "SSLv2Hello"; - - /** - * SSL v2 - * - * @deprecated SSLv2 is no longer secure. Consider using {@link #TLS_v1_2} or {@link #TLS_v1_3} - */ - @Deprecated - public static final String SSL_v2 = "SSLv2"; - - /** - * SSLv3 - * - * @deprecated SSLv3 is no longer secure. Consider using {@link #TLS_v1_2} or {@link #TLS_v1_3} - */ - @Deprecated - public static final String SSL_v3 = "SSLv3"; - - /** - * TLS v1 - * - * @deprecated TLSv1 is no longer secure. Consider using {@link #TLS_v1_2} or {@link #TLS_v1_3} - */ - @Deprecated - public static final String TLS_v1 = "TLSv1"; - - /** - * TLS v1.1 - * - * @deprecated TLSv1.1 is no longer secure. Consider using {@link #TLS_v1_2} or {@link #TLS_v1_3} - */ - @Deprecated - public static final String TLS_v1_1 = "TLSv1.1"; - - /** - * TLS v1.2 - */ - public static final String TLS_v1_2 = "TLSv1.2"; - - /** - * TLS v1.3 - */ - public static final String TLS_v1_3 = "TLSv1.3"; - - private SslProtocols() { - // Prevent outside initialization - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/SslProvider.java b/handler/src/main/java/io/netty/handler/ssl/SslProvider.java deleted file mode 100644 index 9f890e2e79..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/SslProvider.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.ssl; - -import io.netty.util.ReferenceCounted; -import io.netty.util.internal.UnstableApi; - -import java.security.Provider; - -/** - * An enumeration of SSL/TLS protocol providers. - */ -public enum SslProvider { - /** - * JDK's default implementation. - */ - JDK, - /** - * OpenSSL-based implementation. - */ - OPENSSL, - /** - * OpenSSL-based implementation which does not have finalizers and instead implements {@link ReferenceCounted}. - */ - @UnstableApi - OPENSSL_REFCNT; - - /** - * Returns {@code true} if the specified {@link SslProvider} supports - * TLS ALPN Extension, {@code false} otherwise. - */ - @SuppressWarnings("deprecation") - public static boolean isAlpnSupported(final SslProvider provider) { - switch (provider) { - case JDK: - return JdkAlpnApplicationProtocolNegotiator.isAlpnSupported(); - case OPENSSL: - case OPENSSL_REFCNT: - return OpenSsl.isAlpnSupported(); - default: - throw new Error("Unknown SslProvider: " + provider); - } - } - - /** - * Returns {@code true} if the specified {@link SslProvider} supports - * TLS 1.3, {@code false} otherwise. - */ - public static boolean isTlsv13Supported(final SslProvider sslProvider) { - return isTlsv13Supported(sslProvider, null); - } - - /** - * Returns {@code true} if the specified {@link SslProvider} supports - * TLS 1.3, {@code false} otherwise. - */ - public static boolean isTlsv13Supported(final SslProvider sslProvider, Provider provider) { - switch (sslProvider) { - case JDK: - return SslUtils.isTLSv13SupportedByJDK(provider); - case OPENSSL: - case OPENSSL_REFCNT: - return OpenSsl.isTlsv13Supported(); - default: - throw new Error("Unknown SslProvider: " + sslProvider); - } - } - - /** - * Returns {@code true} if the specified {@link SslProvider} enables - * TLS 1.3 by default, {@code false} otherwise. - */ - static boolean isTlsv13EnabledByDefault(final SslProvider sslProvider, Provider provider) { - switch (sslProvider) { - case JDK: - return SslUtils.isTLSv13EnabledByJDK(provider); - case OPENSSL: - case OPENSSL_REFCNT: - return OpenSsl.isTlsv13Supported(); - default: - throw new Error("Unknown SslProvider: " + sslProvider); - } - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/SslUtils.java b/handler/src/main/java/io/netty/handler/ssl/SslUtils.java deleted file mode 100644 index ea13dfdf4b..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/SslUtils.java +++ /dev/null @@ -1,495 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.ByteBufUtil; -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 io.netty.util.internal.EmptyArrays; -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.security.KeyManagementException; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.Provider; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; - -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLHandshakeException; -import javax.net.ssl.TrustManager; - -import static java.util.Arrays.asList; - -/** - * Constants for SSL packets. - */ -final class SslUtils { - private static final InternalLogger logger = InternalLoggerFactory.getInstance(SslUtils.class); - - // See https://tools.ietf.org/html/rfc8446#appendix-B.4 - 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"))); - - /** - * GMSSL Protocol Version - */ - static final int GMSSL_PROTOCOL_VERSION = 0x101; - - static final String INVALID_CIPHER = "SSL_NULL_WITH_NULL_NULL"; - - /** - * change cipher spec - */ - static final int SSL_CONTENT_TYPE_CHANGE_CIPHER_SPEC = 20; - - /** - * alert - */ - static final int SSL_CONTENT_TYPE_ALERT = 21; - - /** - * handshake - */ - static final int SSL_CONTENT_TYPE_HANDSHAKE = 22; - - /** - * application data - */ - static final int SSL_CONTENT_TYPE_APPLICATION_DATA = 23; - - /** - * HeartBeat Extension - */ - static final int SSL_CONTENT_TYPE_EXTENSION_HEARTBEAT = 24; - - /** - * the length of the ssl record header (in bytes) - */ - static final int SSL_RECORD_HEADER_LENGTH = 5; - - /** - * Not enough data in buffer to parse the record length - */ - static final int NOT_ENOUGH_DATA = -1; - - /** - * data is not encrypted - */ - static final int NOT_ENCRYPTED = -2; - - 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" }; - - private static final boolean TLSV1_3_JDK_SUPPORTED; - private static final boolean TLSV1_3_JDK_DEFAULT_ENABLED; - - static { - TLSV1_3_JDK_SUPPORTED = isTLSv13SupportedByJDK0(null); - TLSV1_3_JDK_DEFAULT_ENABLED = isTLSv13EnabledByJDK0(null); - if (TLSV1_3_JDK_SUPPORTED) { - DEFAULT_TLSV13_CIPHER_SUITES = TLSV13_CIPHER_SUITES; - } else { - DEFAULT_TLSV13_CIPHER_SUITES = EmptyArrays.EMPTY_STRINGS; - } - - Set defaultCiphers = new LinkedHashSet<>(); - // GCM (Galois/Counter Mode) requires JDK 8. - 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_256_GCM_SHA384"); - defaultCiphers.add("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"); - // AES256 requires JCE unlimited strength jurisdiction policy files. - defaultCiphers.add("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA"); - // GCM (Galois/Counter Mode) requires JDK 8. - 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. - defaultCiphers.add("TLS_RSA_WITH_AES_256_CBC_SHA"); - - Collections.addAll(defaultCiphers, DEFAULT_TLSV13_CIPHER_SUITES); - - DEFAULT_CIPHER_SUITES = defaultCiphers.toArray(EmptyArrays.EMPTY_STRINGS); - } - - /** - * Returns {@code true} if the JDK itself supports TLSv1.3, {@code false} otherwise. - */ - static boolean isTLSv13SupportedByJDK(Provider provider) { - if (provider == null) { - return TLSV1_3_JDK_SUPPORTED; - } - return isTLSv13SupportedByJDK0(provider); - } - - private static boolean isTLSv13SupportedByJDK0(Provider provider) { - try { - return arrayContains(newInitContext(provider) - .getSupportedSSLParameters().getProtocols(), SslProtocols.TLS_v1_3); - } catch (Throwable cause) { - logger.debug("Unable to detect if JDK SSLEngine with provider {} supports TLSv1.3, assuming no", - provider, cause); - return false; - } - } - - /** - * Returns {@code true} if the JDK itself supports TLSv1.3 and enabled it by default, {@code false} otherwise. - */ - static boolean isTLSv13EnabledByJDK(Provider provider) { - if (provider == null) { - return TLSV1_3_JDK_DEFAULT_ENABLED; - } - return isTLSv13EnabledByJDK0(provider); - } - - private static boolean isTLSv13EnabledByJDK0(Provider provider) { - try { - return arrayContains(newInitContext(provider) - .getDefaultSSLParameters().getProtocols(), SslProtocols.TLS_v1_3); - } catch (Throwable cause) { - logger.debug("Unable to detect if JDK SSLEngine with provider {} enables TLSv1.3 by default," + - " assuming no", provider, cause); - return false; - } - } - - private static SSLContext newInitContext(Provider provider) - throws NoSuchAlgorithmException, KeyManagementException { - final SSLContext context; - if (provider == null) { - context = SSLContext.getInstance("TLS"); - } else { - context = SSLContext.getInstance("TLS", provider); - } - context.init(null, new TrustManager[0], null); - return context; - } - - static SSLContext getSSLContext(String provider) - throws NoSuchAlgorithmException, KeyManagementException, NoSuchProviderException { - final SSLContext context; - if (StringUtil.isNullOrEmpty(provider)) { - context = SSLContext.getInstance(getTlsVersion()); - } else { - context = SSLContext.getInstance(getTlsVersion(), provider); - } - context.init(null, new TrustManager[0], null); - return context; - } - - private static String getTlsVersion() { - return TLSV1_3_JDK_SUPPORTED ? SslProtocols.TLS_v1_3 : SslProtocols.TLS_v1_2; - } - - static boolean arrayContains(String[] array, String value) { - for (String v: array) { - if (value.equals(v)) { - return true; - } - } - return false; - } - - /** - * Add elements from {@code names} into {@code enabled} if they are in {@code supported}. - */ - static void addIfSupported(Set supported, List enabled, String... names) { - for (String n: names) { - if (supported.contains(n)) { - enabled.add(n); - } - } - } - - static void useFallbackCiphersIfDefaultIsEmpty(List defaultCiphers, Iterable fallbackCiphers) { - if (defaultCiphers.isEmpty()) { - for (String cipher : fallbackCiphers) { - if (cipher.startsWith("SSL_") || cipher.contains("_RC4_")) { - continue; - } - defaultCiphers.add(cipher); - } - } - } - - static void useFallbackCiphersIfDefaultIsEmpty(List defaultCiphers, String... fallbackCiphers) { - useFallbackCiphersIfDefaultIsEmpty(defaultCiphers, asList(fallbackCiphers)); - } - - /** - * Converts the given exception to a {@link SSLHandshakeException}, if it isn't already. - */ - static SSLHandshakeException toSSLHandshakeException(Throwable e) { - if (e instanceof SSLHandshakeException) { - return (SSLHandshakeException) e; - } - - return (SSLHandshakeException) new SSLHandshakeException(e.getMessage()).initCause(e); - } - - /** - * Return how much bytes can be read out of the encrypted data. Be aware that this method will not increase - * the readerIndex of the given {@link ByteBuf}. - * - * @param buffer - * The {@link ByteBuf} to read from. Be aware that it must have at least - * {@link #SSL_RECORD_HEADER_LENGTH} bytes to read, - * otherwise it will throw an {@link IllegalArgumentException}. - * @return length - * The length of the encrypted packet that is included in the buffer or - * {@link #SslUtils#NOT_ENOUGH_DATA} if not enough data is present in the - * {@link ByteBuf}. This will return {@link SslUtils#NOT_ENCRYPTED} if - * the given {@link ByteBuf} is not encrypted at all. - * @throws IllegalArgumentException - * Is thrown if the given {@link ByteBuf} has not at least {@link #SSL_RECORD_HEADER_LENGTH} - * bytes to read. - */ - static int getEncryptedPacketLength(ByteBuf buffer, int offset) { - int packetLength = 0; - - // SSLv3 or TLS - Check ContentType - boolean tls; - switch (buffer.getUnsignedByte(offset)) { - case SSL_CONTENT_TYPE_CHANGE_CIPHER_SPEC: - case SSL_CONTENT_TYPE_ALERT: - case SSL_CONTENT_TYPE_HANDSHAKE: - case SSL_CONTENT_TYPE_APPLICATION_DATA: - case SSL_CONTENT_TYPE_EXTENSION_HEARTBEAT: - tls = true; - break; - default: - // SSLv2 or bad data - tls = false; - } - - if (tls) { - // SSLv3 or TLS or GMSSLv1.0 or GMSSLv1.1 - Check ProtocolVersion - int majorVersion = buffer.getUnsignedByte(offset + 1); - if (majorVersion == 3 || buffer.getShort(offset + 1) == GMSSL_PROTOCOL_VERSION) { - // SSLv3 or TLS or GMSSLv1.0 or GMSSLv1.1 - packetLength = unsignedShortBE(buffer, offset + 3) + SSL_RECORD_HEADER_LENGTH; - if (packetLength <= SSL_RECORD_HEADER_LENGTH) { - // Neither SSLv3 or TLSv1 (i.e. SSLv2 or bad data) - tls = false; - } - } else { - // Neither SSLv3 or TLSv1 (i.e. SSLv2 or bad data) - tls = false; - } - } - - if (!tls) { - // SSLv2 or bad data - Check the version - int headerLength = (buffer.getUnsignedByte(offset) & 0x80) != 0 ? 2 : 3; - int majorVersion = buffer.getUnsignedByte(offset + headerLength + 1); - if (majorVersion == 2 || majorVersion == 3) { - // SSLv2 - packetLength = headerLength == 2 ? - (shortBE(buffer, offset) & 0x7FFF) + 2 : (shortBE(buffer, offset) & 0x3FFF) + 3; - if (packetLength <= headerLength) { - return NOT_ENOUGH_DATA; - } - } else { - return NOT_ENCRYPTED; - } - } - return packetLength; - } - - // Reads a big-endian unsigned short integer from the buffer - @SuppressWarnings("deprecation") - private static int unsignedShortBE(ByteBuf buffer, int offset) { - return buffer.order() == ByteOrder.BIG_ENDIAN ? - buffer.getUnsignedShort(offset) : buffer.getUnsignedShortLE(offset); - } - - // Reads a big-endian short integer from the buffer - @SuppressWarnings("deprecation") - private static short shortBE(ByteBuf buffer, int offset) { - return buffer.order() == ByteOrder.BIG_ENDIAN ? - buffer.getShort(offset) : buffer.getShortLE(offset); - } - - private static short unsignedByte(byte b) { - return (short) (b & 0xFF); - } - - // Reads a big-endian unsigned short integer from the buffer - private static int unsignedShortBE(ByteBuffer buffer, int offset) { - return shortBE(buffer, offset) & 0xFFFF; - } - - // Reads a big-endian short integer from the buffer - private static short shortBE(ByteBuffer buffer, int offset) { - return buffer.order() == ByteOrder.BIG_ENDIAN ? - buffer.getShort(offset) : ByteBufUtil.swapShort(buffer.getShort(offset)); - } - - static int getEncryptedPacketLength(ByteBuffer[] buffers, int offset) { - ByteBuffer buffer = buffers[offset]; - - // Check if everything we need is in one ByteBuffer. If so we can make use of the fast-path. - if (buffer.remaining() >= SSL_RECORD_HEADER_LENGTH) { - return getEncryptedPacketLength(buffer); - } - - // We need to copy 5 bytes into a temporary buffer so we can parse out the packet length easily. - ByteBuffer tmp = ByteBuffer.allocate(5); - - do { - buffer = buffers[offset++].duplicate(); - if (buffer.remaining() > tmp.remaining()) { - buffer.limit(buffer.position() + tmp.remaining()); - } - tmp.put(buffer); - } while (tmp.hasRemaining()); - - // Done, flip the buffer so we can read from it. - tmp.flip(); - return getEncryptedPacketLength(tmp); - } - - private static int getEncryptedPacketLength(ByteBuffer buffer) { - int packetLength = 0; - int pos = buffer.position(); - // SSLv3 or TLS - Check ContentType - boolean tls; - switch (unsignedByte(buffer.get(pos))) { - case SSL_CONTENT_TYPE_CHANGE_CIPHER_SPEC: - case SSL_CONTENT_TYPE_ALERT: - case SSL_CONTENT_TYPE_HANDSHAKE: - case SSL_CONTENT_TYPE_APPLICATION_DATA: - case SSL_CONTENT_TYPE_EXTENSION_HEARTBEAT: - tls = true; - break; - default: - // SSLv2 or bad data - tls = false; - } - - if (tls) { - // SSLv3 or TLS or GMSSLv1.0 or GMSSLv1.1 - Check ProtocolVersion - int majorVersion = unsignedByte(buffer.get(pos + 1)); - if (majorVersion == 3 || buffer.getShort(pos + 1) == GMSSL_PROTOCOL_VERSION) { - // SSLv3 or TLS or GMSSLv1.0 or GMSSLv1.1 - packetLength = unsignedShortBE(buffer, pos + 3) + SSL_RECORD_HEADER_LENGTH; - if (packetLength <= SSL_RECORD_HEADER_LENGTH) { - // Neither SSLv3 or TLSv1 (i.e. SSLv2 or bad data) - tls = false; - } - } else { - // Neither SSLv3 or TLSv1 (i.e. SSLv2 or bad data) - tls = false; - } - } - - if (!tls) { - // SSLv2 or bad data - Check the version - int headerLength = (unsignedByte(buffer.get(pos)) & 0x80) != 0 ? 2 : 3; - int majorVersion = unsignedByte(buffer.get(pos + headerLength + 1)); - if (majorVersion == 2 || majorVersion == 3) { - // SSLv2 - packetLength = headerLength == 2 ? - (shortBE(buffer, pos) & 0x7FFF) + 2 : (shortBE(buffer, pos) & 0x3FFF) + 3; - if (packetLength <= headerLength) { - return NOT_ENOUGH_DATA; - } - } else { - return NOT_ENCRYPTED; - } - } - return packetLength; - } - - static void handleHandshakeFailure(ChannelHandlerContext ctx, Throwable cause, boolean notify) { - // We have may haven written some parts of data before an exception was thrown so ensure we always flush. - // See https://github.com/netty/netty/issues/3900#issuecomment-172481830 - ctx.flush(); - if (notify) { - ctx.fireUserEventTriggered(new SslHandshakeCompletionEvent(cause)); - } - } - - /** - * Fills the {@link ByteBuf} with zero bytes. - */ - static void zeroout(ByteBuf buffer) { - if (!buffer.isReadOnly()) { - buffer.setZero(0, buffer.capacity()); - } - } - - /** - * Fills the {@link ByteBuf} with zero bytes and releases it. - */ - static void zerooutAndRelease(ByteBuf buffer) { - zeroout(buffer); - buffer.release(); - } - - /** - * Same as {@link Base64#encode(ByteBuf, boolean)} but allows the use of a custom {@link ByteBufAllocator}. - * - * @see Base64#encode(ByteBuf, boolean) - */ - static ByteBuf toBase64(ByteBufAllocator allocator, ByteBuf src) { - ByteBuf dst = Base64.encode(src, src.readerIndex(), - src.readableBytes(), true, Base64Dialect.STANDARD, allocator); - src.readerIndex(src.writerIndex()); - 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); - } - - /** - * 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); - } - - static boolean isEmpty(Object[] arr) { - return arr == null || arr.length == 0; - } - - private SslUtils() { - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/SupportedCipherSuiteFilter.java b/handler/src/main/java/io/netty/handler/ssl/SupportedCipherSuiteFilter.java deleted file mode 100644 index 84a717db98..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/SupportedCipherSuiteFilter.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import static java.util.Objects.requireNonNull; - -import javax.net.ssl.SSLEngine; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - -/** - * This class will filter all requested ciphers out that are not supported by the current {@link SSLEngine}. - */ -public final class SupportedCipherSuiteFilter implements CipherSuiteFilter { - public static final SupportedCipherSuiteFilter INSTANCE = new SupportedCipherSuiteFilter(); - - private SupportedCipherSuiteFilter() { } - - @Override - public String[] filterCipherSuites(Iterable ciphers, List defaultCiphers, - Set supportedCiphers) { - requireNonNull(defaultCiphers, "defaultCiphers"); - requireNonNull(supportedCiphers, "supportedCiphers"); - - final List newCiphers; - if (ciphers == null) { - newCiphers = new ArrayList<>(defaultCiphers.size()); - ciphers = defaultCiphers; - } else { - newCiphers = new ArrayList<>(supportedCiphers.size()); - } - for (String c : ciphers) { - if (c == null) { - break; - } - if (supportedCiphers.contains(c)) { - newCiphers.add(c); - } - } - return newCiphers.toArray(new String[0]); - } - -} diff --git a/handler/src/main/java/io/netty/handler/ssl/ocsp/OcspClientHandler.java b/handler/src/main/java/io/netty/handler/ssl/ocsp/OcspClientHandler.java deleted file mode 100644 index 615f632995..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/ocsp/OcspClientHandler.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl.ocsp; - -import static java.util.Objects.requireNonNull; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.ssl.ReferenceCountedOpenSslContext; -import io.netty.handler.ssl.ReferenceCountedOpenSslEngine; -import io.netty.handler.ssl.SslHandshakeCompletionEvent; -import io.netty.util.internal.UnstableApi; - -import javax.net.ssl.SSLHandshakeException; - -/** - * A handler for SSL clients to handle and act upon stapled OCSP responses. - * - * @see ReferenceCountedOpenSslContext#enableOcsp() - * @see ReferenceCountedOpenSslEngine#getOcspResponse() - */ -@UnstableApi -public abstract class OcspClientHandler implements ChannelHandler { - - private final ReferenceCountedOpenSslEngine engine; - - protected OcspClientHandler(ReferenceCountedOpenSslEngine engine) { - this.engine = requireNonNull(engine, "engine"); - } - - /** - * @see ReferenceCountedOpenSslEngine#getOcspResponse() - */ - protected abstract boolean verify(ChannelHandlerContext ctx, ReferenceCountedOpenSslEngine engine) throws Exception; - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - ctx.fireUserEventTriggered(evt); - if (evt instanceof SslHandshakeCompletionEvent) { - SslHandshakeCompletionEvent event = (SslHandshakeCompletionEvent) evt; - if (event.isSuccess() && !verify(ctx, engine)) { - throw new SSLHandshakeException("Bad OCSP response"); - } - ctx.pipeline().remove(this); - } - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/ocsp/package-info.java b/handler/src/main/java/io/netty/handler/ssl/ocsp/package-info.java deleted file mode 100644 index 7e81ae6b23..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/ocsp/package-info.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * OCSP stapling, - * formally known as the TLS Certificate Status Request extension, is an - * alternative approach to the Online Certificate Status Protocol (OCSP) - * for checking the revocation status of X.509 digital certificates. - */ -package io.netty.handler.ssl.ocsp; diff --git a/handler/src/main/java/io/netty/handler/ssl/util/BouncyCastleSelfSignedCertGenerator.java b/handler/src/main/java/io/netty/handler/ssl/util/BouncyCastleSelfSignedCertGenerator.java deleted file mode 100644 index 890ade34d8..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/util/BouncyCastleSelfSignedCertGenerator.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.ssl.util; - -import org.bouncycastle.asn1.x500.X500Name; -import org.bouncycastle.cert.X509CertificateHolder; -import org.bouncycastle.cert.X509v3CertificateBuilder; -import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; -import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.operator.ContentSigner; -import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; - -import java.math.BigInteger; -import java.security.KeyPair; -import java.security.PrivateKey; -import java.security.Provider; -import java.security.SecureRandom; -import java.security.cert.X509Certificate; -import java.util.Date; - -import static io.netty.handler.ssl.util.SelfSignedCertificate.newSelfSignedCertificate; - -/** - * Generates a self-signed certificate using Bouncy Castle. - */ -final class BouncyCastleSelfSignedCertGenerator { - - private static final Provider PROVIDER = new BouncyCastleProvider(); - - static String[] generate(String fqdn, KeyPair keypair, SecureRandom random, Date notBefore, Date notAfter, - String algorithm) throws Exception { - PrivateKey key = keypair.getPrivate(); - - // Prepare the information required for generating an X.509 certificate. - X500Name owner = new X500Name("CN=" + fqdn); - X509v3CertificateBuilder builder = new JcaX509v3CertificateBuilder( - owner, new BigInteger(64, random), notBefore, notAfter, owner, keypair.getPublic()); - - ContentSigner signer = new JcaContentSignerBuilder( - algorithm.equalsIgnoreCase("EC") ? "SHA256withECDSA" : "SHA256WithRSAEncryption").build(key); - X509CertificateHolder certHolder = builder.build(signer); - X509Certificate cert = new JcaX509CertificateConverter().setProvider(PROVIDER).getCertificate(certHolder); - cert.verify(keypair.getPublic()); - - return newSelfSignedCertificate(fqdn, key, cert); - } - - private BouncyCastleSelfSignedCertGenerator() { } -} 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 deleted file mode 100644 index 18383d4d2c..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/util/FingerprintTrustManagerFactory.java +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.ssl.util; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBufUtil; -import io.netty.buffer.Unpooled; -import io.netty.util.internal.EmptyArrays; -import io.netty.util.concurrent.FastThreadLocal; -import io.netty.util.internal.StringUtil; - -import javax.net.ssl.ManagerFactoryParameters; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509TrustManager; -import java.security.KeyStore; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.cert.CertificateEncodingException; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.regex.Pattern; - -/** - * An {@link TrustManagerFactory} that trusts an X.509 certificate whose hash matches. - *

- * NOTE: It is recommended to verify certificates and their chain to prevent - * Man-in-the-middle attacks. - * This {@link TrustManagerFactory} will only verify that the fingerprint of certificates match one - * of the given fingerprints. This procedure is called - * certificate pinning and - * is an effective protection. For maximum security one should verify that the whole certificate chain is as expected. - * It is worth mentioning that certain firewalls, proxies or other appliances found in corporate environments, - * actually perform Man-in-the-middle attacks and thus present a different certificate fingerprint. - *

- *

- * The hash of an X.509 certificate is calculated from its DER encoded format. You can get the fingerprint of - * an X.509 certificate using the {@code openssl} command. For example: - * - *

- * $ openssl x509 -fingerprint -sha256 -in my_certificate.crt
- * SHA256 Fingerprint=1C:53:0E:6B:FF:93:F0:DE:C2:E6:E7:9D:10:53:58:FF:DD:8E:68:CD:82:D9:C9:36:9B:43:EE:B3:DC:13:68:FB
- * -----BEGIN CERTIFICATE-----
- * MIIC/jCCAeagAwIBAgIIIMONxElm0AIwDQYJKoZIhvcNAQELBQAwPjE8MDoGA1UE
- * AwwzZThhYzAyZmEwZDY1YTg0MjE5MDE2MDQ1ZGI4YjA1YzQ4NWI0ZWNkZi5uZXR0
- * eS50ZXN0MCAXDTEzMDgwMjA3NTEzNloYDzk5OTkxMjMxMjM1OTU5WjA+MTwwOgYD
- * VQQDDDNlOGFjMDJmYTBkNjVhODQyMTkwMTYwNDVkYjhiMDVjNDg1YjRlY2RmLm5l
- * dHR5LnRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDb+HBO3C0U
- * RBKvDUgJHbhIlBye8X/cbNH3lDq3XOOFBz7L4XZKLDIXS+FeQqSAUMo2otmU+Vkj
- * 0KorshMjbUXfE1KkTijTMJlaga2M2xVVt21fRIkJNWbIL0dWFLWyRq7OXdygyFkI
- * iW9b2/LYaePBgET22kbtHSCAEj+BlSf265+1rNxyAXBGGGccCKzEbcqASBKHOgVp
- * 6pLqlQAfuSy6g/OzGzces3zXRrGu1N3pBIzAIwCW429n52ZlYfYR0nr+REKDnRrP
- * IIDsWASmEHhBezTD+v0qCJRyLz2usFgWY+7agUJE2yHHI2mTu2RAFngBilJXlMCt
- * VwT0xGuQxkbHAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAEv8N7Xm8qaY2FgrOc6P
- * a1GTgA+AOb3aU33TGwAR86f+nLf6BSPaohcQfOeJid7FkFuYInuXl+oqs+RqM/j8
- * R0E5BuGYY2wOKpL/PbFi1yf/Kyvft7KVh8e1IUUec/i1DdYTDB0lNWvXXxjfMKGL
- * ct3GMbEHKvLfHx42Iwz/+fva6LUrO4u2TDfv0ycHuR7UZEuC1DJ4xtFhbpq/QRAj
- * CyfNx3cDc7L2EtJWnCmivTFA9l8MF1ZPMDSVd4ecQ7B0xZIFQ5cSSFt7WGaJCsGM
- * zYkU4Fp4IykQcWxdlNX7wJZRwQ2TZJFFglpTiFZdeq6I6Ad9An1Encpz5W8UJ4tv
- * hmw=
- * -----END CERTIFICATE-----
- * 
- *

- */ -public final class FingerprintTrustManagerFactory extends SimpleTrustManagerFactory { - - private static final Pattern FINGERPRINT_PATTERN = Pattern.compile("^[0-9a-fA-F:]+$"); - private static final Pattern FINGERPRINT_STRIP_PATTERN = Pattern.compile(":"); - - /** - * Creates a builder for {@link FingerprintTrustManagerFactory}. - * - * @param algorithm a hash algorithm - * @return a builder - */ - public static FingerprintTrustManagerFactoryBuilder builder(String algorithm) { - return new FingerprintTrustManagerFactoryBuilder(algorithm); - } - - private final FastThreadLocal tlmd; - - private final TrustManager tm = new X509TrustManager() { - - @Override - public void checkClientTrusted(X509Certificate[] chain, String s) throws CertificateException { - checkTrusted("client", chain); - } - - @Override - public void checkServerTrusted(X509Certificate[] chain, String s) throws CertificateException { - checkTrusted("server", chain); - } - - private void checkTrusted(String type, X509Certificate[] chain) throws CertificateException { - X509Certificate cert = chain[0]; - byte[] fingerprint = fingerprint(cert); - boolean found = false; - for (byte[] allowedFingerprint: fingerprints) { - if (Arrays.equals(fingerprint, allowedFingerprint)) { - found = true; - break; - } - } - - if (!found) { - throw new CertificateException( - type + " certificate with unknown fingerprint: " + cert.getSubjectDN()); - } - } - - private byte[] fingerprint(X509Certificate cert) throws CertificateEncodingException { - MessageDigest md = tlmd.get(); - md.reset(); - return md.digest(cert.getEncoded()); - } - - @Override - public X509Certificate[] getAcceptedIssuers() { - return EmptyArrays.EMPTY_X509_CERTIFICATES; - } - }; - - private final byte[][] fingerprints; - - /** - * Creates a new instance. - * - * @param algorithm a hash algorithm - * @param fingerprints a list of fingerprints - */ - FingerprintTrustManagerFactory(final String algorithm, byte[][] fingerprints) { - Objects.requireNonNull(algorithm, "algorithm"); - Objects.requireNonNull(fingerprints, "fingerprints"); - - if (fingerprints.length == 0) { - throw new IllegalArgumentException("No fingerprints provided"); - } - - // check early if the hash algorithm is available - final MessageDigest md; - try { - md = MessageDigest.getInstance(algorithm); - } catch (NoSuchAlgorithmException e) { - throw new IllegalArgumentException( - String.format("Unsupported hash algorithm: %s", algorithm), e); - } - - int hashLength = md.getDigestLength(); - List list = new ArrayList(fingerprints.length); - for (byte[] f: fingerprints) { - if (f == null) { - break; - } - if (f.length != hashLength) { - throw new IllegalArgumentException( - String.format("malformed fingerprint (length is %d but expected %d): %s", - f.length, hashLength, ByteBufUtil.hexDump(Unpooled.wrappedBuffer(f)))); - } - list.add(f.clone()); - } - - this.tlmd = new FastThreadLocal() { - - @Override - protected MessageDigest initialValue() { - try { - return MessageDigest.getInstance(algorithm); - } catch (NoSuchAlgorithmException e) { - throw new IllegalArgumentException( - String.format("Unsupported hash algorithm: %s", algorithm), e); - } - } - }; - - this.fingerprints = list.toArray(new byte[0][]); - } - - static byte[][] toFingerprintArray(Iterable fingerprints) { - requireNonNull(fingerprints, "fingerprints"); - - List list = new ArrayList<>(); - for (String f: fingerprints) { - if (f == null) { - break; - } - - if (!FINGERPRINT_PATTERN.matcher(f).matches()) { - throw new IllegalArgumentException("malformed fingerprint: " + f); - } - f = FINGERPRINT_STRIP_PATTERN.matcher(f).replaceAll(""); - - list.add(StringUtil.decodeHexDump(f)); - } - - return list.toArray(new byte[0][]); - } - - @Override - protected void engineInit(KeyStore keyStore) throws Exception { } - - @Override - protected void engineInit(ManagerFactoryParameters managerFactoryParameters) throws Exception { } - - @Override - protected TrustManager[] engineGetTrustManagers() { - return new TrustManager[] { tm }; - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/util/FingerprintTrustManagerFactoryBuilder.java b/handler/src/main/java/io/netty/handler/ssl/util/FingerprintTrustManagerFactoryBuilder.java deleted file mode 100644 index a7e939bc59..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/util/FingerprintTrustManagerFactoryBuilder.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.ssl.util; - -import static io.netty.util.internal.ObjectUtil.checkNotNull; -import static io.netty.util.internal.ObjectUtil.checkNotNullWithIAE; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * A builder for creating {@link FingerprintTrustManagerFactory}. - */ -public final class FingerprintTrustManagerFactoryBuilder { - - /** - * A hash algorithm for fingerprints. - */ - private final String algorithm; - - /** - * A list of fingerprints. - */ - private final List fingerprints = new ArrayList(); - - /** - * Creates a builder. - * - * @param algorithm a hash algorithm - */ - FingerprintTrustManagerFactoryBuilder(String algorithm) { - this.algorithm = checkNotNull(algorithm, "algorithm"); - } - - /** - * Adds fingerprints. - * - * @param fingerprints a number of fingerprints - * @return the same builder - */ - public FingerprintTrustManagerFactoryBuilder fingerprints(CharSequence... fingerprints) { - return fingerprints(Arrays.asList(checkNotNull(fingerprints, "fingerprints"))); - } - - /** - * Adds fingerprints. - * - * @param fingerprints a number of fingerprints - * @return the same builder - */ - public FingerprintTrustManagerFactoryBuilder fingerprints(Iterable fingerprints) { - checkNotNull(fingerprints, "fingerprints"); - for (CharSequence fingerprint : fingerprints) { - checkNotNullWithIAE(fingerprint, "fingerprint"); - this.fingerprints.add(fingerprint.toString()); - } - return this; - } - - /** - * Creates a {@link FingerprintTrustManagerFactory}. - * - * @return a new {@link FingerprintTrustManagerFactory} - */ - public FingerprintTrustManagerFactory build() { - if (fingerprints.isEmpty()) { - throw new IllegalStateException("No fingerprints provided"); - } - return new FingerprintTrustManagerFactory(this.algorithm, - FingerprintTrustManagerFactory.toFingerprintArray(this.fingerprints)); - } -} 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 deleted file mode 100644 index 6efe959cd7..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/util/InsecureTrustManagerFactory.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.ssl.util; - -import io.netty.util.internal.EmptyArrays; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import javax.net.ssl.ManagerFactoryParameters; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509TrustManager; -import java.security.KeyStore; -import java.security.cert.X509Certificate; - -/** - * An insecure {@link TrustManagerFactory} that trusts all X.509 certificates without any verification. - *

- * NOTE: - * Never use this {@link TrustManagerFactory} in production. - * It is purely for testing purposes, and thus it is very insecure. - *

- */ -public final class InsecureTrustManagerFactory extends SimpleTrustManagerFactory { - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(InsecureTrustManagerFactory.class); - - public static final TrustManagerFactory INSTANCE = new InsecureTrustManagerFactory(); - - private static final TrustManager tm = new X509TrustManager() { - @Override - public void checkClientTrusted(X509Certificate[] chain, String s) { - if (logger.isDebugEnabled()) { - logger.debug("Accepting a client certificate: " + chain[0].getSubjectDN()); - } - } - - @Override - public void checkServerTrusted(X509Certificate[] chain, String s) { - if (logger.isDebugEnabled()) { - logger.debug("Accepting a server certificate: " + chain[0].getSubjectDN()); - } - } - - @Override - public X509Certificate[] getAcceptedIssuers() { - return EmptyArrays.EMPTY_X509_CERTIFICATES; - } - }; - - private InsecureTrustManagerFactory() { } - - @Override - protected void engineInit(KeyStore keyStore) throws Exception { } - - @Override - protected void engineInit(ManagerFactoryParameters managerFactoryParameters) throws Exception { } - - @Override - protected TrustManager[] engineGetTrustManagers() { - return new TrustManager[] { tm }; - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/util/KeyManagerFactoryWrapper.java b/handler/src/main/java/io/netty/handler/ssl/util/KeyManagerFactoryWrapper.java deleted file mode 100644 index 6c27eb02b2..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/util/KeyManagerFactoryWrapper.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.ssl.util; - -import java.security.KeyStore; -import java.util.Objects; -import javax.net.ssl.KeyManager; -import javax.net.ssl.ManagerFactoryParameters; - -public final class KeyManagerFactoryWrapper extends SimpleKeyManagerFactory { - private final KeyManager km; - - public KeyManagerFactoryWrapper(KeyManager km) { - this.km = Objects.requireNonNull(km, "km"); - } - - @Override - protected void engineInit(KeyStore keyStore, char[] var2) throws Exception { } - - @Override - protected void engineInit(ManagerFactoryParameters managerFactoryParameters) - throws Exception { } - - @Override - protected KeyManager[] engineGetKeyManagers() { - return new KeyManager[] {km}; - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/util/LazyJavaxX509Certificate.java b/handler/src/main/java/io/netty/handler/ssl/util/LazyJavaxX509Certificate.java deleted file mode 100644 index 66a769d14a..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/util/LazyJavaxX509Certificate.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl.util; - -import io.netty.util.internal.ObjectUtil; - -import javax.security.cert.CertificateException; -import javax.security.cert.CertificateExpiredException; -import javax.security.cert.CertificateNotYetValidException; -import javax.security.cert.X509Certificate; -import java.math.BigInteger; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.Principal; -import java.security.PublicKey; -import java.security.SignatureException; -import java.util.Date; - -public final class LazyJavaxX509Certificate extends X509Certificate { - private final byte[] bytes; - private X509Certificate wrapped; - - /** - * Creates a new instance which will lazy parse the given bytes. Be aware that the bytes will not be cloned. - */ - public LazyJavaxX509Certificate(byte[] bytes) { - this.bytes = ObjectUtil.checkNotNull(bytes, "bytes"); - } - - @Override - public void checkValidity() throws CertificateExpiredException, CertificateNotYetValidException { - unwrap().checkValidity(); - } - - @Override - public void checkValidity(Date date) throws CertificateExpiredException, CertificateNotYetValidException { - unwrap().checkValidity(date); - } - - @Override - public int getVersion() { - return unwrap().getVersion(); - } - - @Override - public BigInteger getSerialNumber() { - return unwrap().getSerialNumber(); - } - - @Override - public Principal getIssuerDN() { - return unwrap().getIssuerDN(); - } - - @Override - public Principal getSubjectDN() { - return unwrap().getSubjectDN(); - } - - @Override - public Date getNotBefore() { - return unwrap().getNotBefore(); - } - - @Override - public Date getNotAfter() { - return unwrap().getNotAfter(); - } - - @Override - public String getSigAlgName() { - return unwrap().getSigAlgName(); - } - - @Override - public String getSigAlgOID() { - return unwrap().getSigAlgOID(); - } - - @Override - public byte[] getSigAlgParams() { - return unwrap().getSigAlgParams(); - } - - @Override - public byte[] getEncoded() { - return bytes.clone(); - } - - /** - * Return the underyling {@code byte[]} without cloning it first. This {@code byte[]} must never - * be mutated. - */ - byte[] getBytes() { - return bytes; - } - - @Override - public void verify(PublicKey key) - throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, - SignatureException { - unwrap().verify(key); - } - - @Override - public void verify(PublicKey key, String sigProvider) - throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, - SignatureException { - unwrap().verify(key, sigProvider); - } - - @Override - public String toString() { - return unwrap().toString(); - } - - @Override - public PublicKey getPublicKey() { - return unwrap().getPublicKey(); - } - - private X509Certificate unwrap() { - X509Certificate wrapped = this.wrapped; - if (wrapped == null) { - try { - wrapped = this.wrapped = X509Certificate.getInstance(bytes); - } catch (CertificateException e) { - throw new IllegalStateException(e); - } - } - return wrapped; - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/util/LazyX509Certificate.java b/handler/src/main/java/io/netty/handler/ssl/util/LazyX509Certificate.java deleted file mode 100644 index 8dd0f572a6..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/util/LazyX509Certificate.java +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl.util; - -import io.netty.util.internal.ObjectUtil; - -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.CertificateFactory; -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; - -public final class LazyX509Certificate extends X509Certificate { - - static final CertificateFactory X509_CERT_FACTORY; - static { - try { - X509_CERT_FACTORY = CertificateFactory.getInstance("X.509"); - } catch (CertificateException e) { - throw new ExceptionInInitializerError(e); - } - } - - private final byte[] bytes; - private X509Certificate wrapped; - - /** - * Creates a new instance which will lazy parse the given bytes. Be aware that the bytes will not be cloned. - */ - public LazyX509Certificate(byte[] bytes) { - this.bytes = ObjectUtil.checkNotNull(bytes, "bytes"); - } - - @Override - public void checkValidity() throws CertificateExpiredException, CertificateNotYetValidException { - unwrap().checkValidity(); - } - - @Override - public void checkValidity(Date date) throws CertificateExpiredException, CertificateNotYetValidException { - 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. - @Override - public void verify(PublicKey key, Provider sigProvider) - throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, SignatureException { - unwrap().verify(key, sigProvider); - } - - @Override - public int getVersion() { - return unwrap().getVersion(); - } - - @Override - public BigInteger getSerialNumber() { - return unwrap().getSerialNumber(); - } - - @Override - public Principal getIssuerDN() { - return unwrap().getIssuerDN(); - } - - @Override - public Principal getSubjectDN() { - return unwrap().getSubjectDN(); - } - - @Override - public Date getNotBefore() { - return unwrap().getNotBefore(); - } - - @Override - public Date getNotAfter() { - return unwrap().getNotAfter(); - } - - @Override - public byte[] getTBSCertificate() throws CertificateEncodingException { - return unwrap().getTBSCertificate(); - } - - @Override - public byte[] getSignature() { - return unwrap().getSignature(); - } - - @Override - public String getSigAlgName() { - return unwrap().getSigAlgName(); - } - - @Override - public String getSigAlgOID() { - return unwrap().getSigAlgOID(); - } - - @Override - public byte[] getSigAlgParams() { - return unwrap().getSigAlgParams(); - } - - @Override - public boolean[] getIssuerUniqueID() { - return unwrap().getIssuerUniqueID(); - } - - @Override - public boolean[] getSubjectUniqueID() { - return unwrap().getSubjectUniqueID(); - } - - @Override - public boolean[] getKeyUsage() { - return unwrap().getKeyUsage(); - } - - @Override - public int getBasicConstraints() { - return unwrap().getBasicConstraints(); - } - - @Override - public byte[] getEncoded() { - return bytes.clone(); - } - - @Override - public void verify(PublicKey key) - throws CertificateException, NoSuchAlgorithmException, - InvalidKeyException, NoSuchProviderException, SignatureException { - unwrap().verify(key); - } - - @Override - public void verify(PublicKey key, String sigProvider) - throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, - NoSuchProviderException, SignatureException { - unwrap().verify(key, sigProvider); - } - - @Override - public String toString() { - return unwrap().toString(); - } - - @Override - public PublicKey getPublicKey() { - return unwrap().getPublicKey(); - } - - @Override - public boolean hasUnsupportedCriticalExtension() { - return unwrap().hasUnsupportedCriticalExtension(); - } - - @Override - public Set getCriticalExtensionOIDs() { - return unwrap().getCriticalExtensionOIDs(); - } - - @Override - public Set getNonCriticalExtensionOIDs() { - return unwrap().getNonCriticalExtensionOIDs(); - } - - @Override - public byte[] getExtensionValue(String oid) { - return unwrap().getExtensionValue(oid); - } - - private X509Certificate unwrap() { - X509Certificate wrapped = this.wrapped; - if (wrapped == null) { - try { - wrapped = this.wrapped = (X509Certificate) X509_CERT_FACTORY.generateCertificate( - new ByteArrayInputStream(bytes)); - } catch (CertificateException e) { - throw new IllegalStateException(e); - } - } - return wrapped; - } -} 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 deleted file mode 100644 index c8404ca3bc..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/util/SelfSignedCertificate.java +++ /dev/null @@ -1,394 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.ssl.util; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.handler.codec.base64.Base64; -import io.netty.util.CharsetUtil; -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.SystemPropertyUtil; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.SecureRandom; -import java.security.cert.CertificateEncodingException; -import java.security.cert.CertificateException; -import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; -import java.util.Date; - -/** - * Generates a temporary self-signed certificate for testing purposes. - *

- * NOTE: - * Never use the certificate and private key generated by this class in production. - * It is purely for testing purposes, and thus it is very insecure. - * It even uses an insecure pseudo-random generator for faster generation internally. - *

- * An X.509 certificate file and a EC/RSA private key file are generated in a system's temporary directory using - * {@link java.io.File#createTempFile(String, String)}, and they are deleted when the JVM exits using - * {@link java.io.File#deleteOnExit()}. - *

- * The certificate is generated using Bouncy Castle, which is an - * optional dependency of Netty. - *

- */ -public final class SelfSignedCertificate { - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(SelfSignedCertificate.class); - - /** Current time minus 1 year, just in case software clock goes back due to time synchronization */ - private static final Date DEFAULT_NOT_BEFORE = new Date(SystemPropertyUtil.getLong( - "io.netty.selfSignedCertificate.defaultNotBefore", System.currentTimeMillis() - 86400000L * 365)); - /** The maximum possible value in X.509 specification: 9999-12-31 23:59:59 */ - private static final Date DEFAULT_NOT_AFTER = new Date(SystemPropertyUtil.getLong( - "io.netty.selfSignedCertificate.defaultNotAfter", 253402300799000L)); - - /** - * FIPS 140-2 encryption requires the RSA key length to be 2048 bits or greater. - * Let's use that as a sane default but allow the default to be set dynamically - * for those that need more stringent security requirements. - */ - private static final int DEFAULT_KEY_LENGTH_BITS = - SystemPropertyUtil.getInt("io.netty.handler.ssl.util.selfSignedKeyStrength", 2048); - - private final File certificate; - private final File privateKey; - private final X509Certificate cert; - private final PrivateKey key; - - /** - * Creates a new instance. - *

Algorithm: RSA

- */ - public SelfSignedCertificate() throws CertificateException { - this(DEFAULT_NOT_BEFORE, DEFAULT_NOT_AFTER, "RSA", DEFAULT_KEY_LENGTH_BITS); - } - - /** - * Creates a new instance. - *

Algorithm: RSA

- * - * @param notBefore Certificate is not valid before this time - * @param notAfter Certificate is not valid after this time - */ - public SelfSignedCertificate(Date notBefore, Date notAfter) - throws CertificateException { - this("localhost", notBefore, notAfter, "RSA", DEFAULT_KEY_LENGTH_BITS); - } - - /** - * Creates a new instance. - * - * @param notBefore Certificate is not valid before this time - * @param notAfter Certificate is not valid after this time - * @param algorithm Key pair algorithm - * @param bits the number of bits of the generated private key - */ - public SelfSignedCertificate(Date notBefore, Date notAfter, String algorithm, int bits) - throws CertificateException { - this("localhost", notBefore, notAfter, algorithm, bits); - } - - /** - * Creates a new instance. - *

Algorithm: RSA

- * - * @param fqdn a fully qualified domain name - */ - public SelfSignedCertificate(String fqdn) throws CertificateException { - this(fqdn, DEFAULT_NOT_BEFORE, DEFAULT_NOT_AFTER, "RSA", DEFAULT_KEY_LENGTH_BITS); - } - - /** - * Creates a new instance. - * - * @param fqdn a fully qualified domain name - * @param algorithm Key pair algorithm - * @param bits the number of bits of the generated private key - */ - public SelfSignedCertificate(String fqdn, String algorithm, int bits) throws CertificateException { - this(fqdn, DEFAULT_NOT_BEFORE, DEFAULT_NOT_AFTER, algorithm, bits); - } - - /** - * Creates a new instance. - *

Algorithm: RSA

- * - * @param fqdn a fully qualified domain name - * @param notBefore Certificate is not valid before this time - * @param notAfter Certificate is not valid after this time - */ - public SelfSignedCertificate(String fqdn, Date notBefore, Date notAfter) throws CertificateException { - // Bypass entropy collection by using insecure random generator. - // We just want to generate it without any delay because it's for testing purposes only. - this(fqdn, ThreadLocalInsecureRandom.current(), DEFAULT_KEY_LENGTH_BITS, notBefore, notAfter, "RSA"); - } - - /** - * Creates a new instance. - * - * @param fqdn a fully qualified domain name - * @param notBefore Certificate is not valid before this time - * @param notAfter Certificate is not valid after this time - * @param algorithm Key pair algorithm - * @param bits the number of bits of the generated private key - */ - public SelfSignedCertificate(String fqdn, Date notBefore, Date notAfter, String algorithm, int bits) - throws CertificateException { - // Bypass entropy collection by using insecure random generator. - // We just want to generate it without any delay because it's for testing purposes only. - this(fqdn, ThreadLocalInsecureRandom.current(), bits, notBefore, notAfter, algorithm); - } - - /** - * Creates a new instance. - *

Algorithm: RSA

- * - * @param fqdn a fully qualified domain name - * @param random the {@link SecureRandom} to use - * @param bits the number of bits of the generated private key - */ - public SelfSignedCertificate(String fqdn, SecureRandom random, int bits) - throws CertificateException { - this(fqdn, random, bits, DEFAULT_NOT_BEFORE, DEFAULT_NOT_AFTER, "RSA"); - } - - /** - * Creates a new instance. - * - * @param fqdn a fully qualified domain name - * @param random the {@link SecureRandom} to use - * @param algorithm Key pair algorithm - * @param bits the number of bits of the generated private key - */ - public SelfSignedCertificate(String fqdn, SecureRandom random, String algorithm, int bits) - throws CertificateException { - this(fqdn, random, bits, DEFAULT_NOT_BEFORE, DEFAULT_NOT_AFTER, algorithm); - } - - /** - * Creates a new instance. - *

Algorithm: RSA

- * - * @param fqdn a fully qualified domain name - * @param random the {@link SecureRandom} to use - * @param bits the number of bits of the generated private key - * @param notBefore Certificate is not valid before this time - * @param notAfter Certificate is not valid after this time - */ - public SelfSignedCertificate(String fqdn, SecureRandom random, int bits, Date notBefore, Date notAfter) - throws CertificateException { - this(fqdn, random, bits, notBefore, notAfter, "RSA"); - } - - /** - * Creates a new instance. - * - * @param fqdn a fully qualified domain name - * @param random the {@link SecureRandom} to use - * @param bits the number of bits of the generated private key - * @param notBefore Certificate is not valid before this time - * @param notAfter Certificate is not valid after this time - * @param algorithm Key pair algorithm - */ - public SelfSignedCertificate(String fqdn, SecureRandom random, int bits, Date notBefore, Date notAfter, - String algorithm) throws CertificateException { - - if (!algorithm.equalsIgnoreCase("EC") && !algorithm.equalsIgnoreCase("RSA")) { - throw new IllegalArgumentException("Algorithm not valid: " + algorithm); - } - - final KeyPair keypair; - try { - KeyPairGenerator keyGen = KeyPairGenerator.getInstance(algorithm); - keyGen.initialize(bits, random); - keypair = keyGen.generateKeyPair(); - } catch (NoSuchAlgorithmException e) { - // Should not reach here because every Java implementation must have RSA and EC key pair generator. - throw new Error(e); - } - - String[] paths; - try { - paths = BouncyCastleSelfSignedCertGenerator.generate( - fqdn, keypair, random, notBefore, notAfter, algorithm); - } catch (Throwable throwable) { - logger.debug("Failed to generate a self-signed X.509 certificate using Bouncy Castle:", throwable); - throw new CertificateException( - "No provider succeeded to generate a self-signed certificate. " + - "See debug log for the root cause.", throwable); - } - - certificate = new File(paths[0]); - privateKey = new File(paths[1]); - key = keypair.getPrivate(); - FileInputStream certificateInput = null; - try { - certificateInput = new FileInputStream(certificate); - cert = (X509Certificate) CertificateFactory.getInstance("X509").generateCertificate(certificateInput); - } catch (Exception e) { - throw new CertificateEncodingException(e); - } finally { - if (certificateInput != null) { - try { - certificateInput.close(); - } catch (IOException e) { - if (logger.isWarnEnabled()) { - logger.warn("Failed to close a file: " + certificate, e); - } - } - } - } - } - - /** - * Returns the generated X.509 certificate file in PEM format. - */ - public File certificate() { - return certificate; - } - - /** - * Returns the generated EC/RSA private key file in PEM format. - */ - public File privateKey() { - return privateKey; - } - - /** - * Returns the generated X.509 certificate. - */ - public X509Certificate cert() { - return cert; - } - - /** - * Returns the generated EC/RSA private key. - */ - public PrivateKey key() { - return key; - } - - /** - * Deletes the generated X.509 certificate file and EC/RSA private key file. - */ - public void delete() { - safeDelete(certificate); - safeDelete(privateKey); - } - - static String[] newSelfSignedCertificate( - String fqdn, PrivateKey key, X509Certificate cert) throws IOException, CertificateEncodingException { - // Encode the private key into a file. - ByteBuf wrappedBuf = Unpooled.wrappedBuffer(key.getEncoded()); - ByteBuf encodedBuf; - final String keyText; - try { - encodedBuf = Base64.encode(wrappedBuf, true); - try { - keyText = "-----BEGIN PRIVATE KEY-----\n" + - encodedBuf.toString(CharsetUtil.US_ASCII) + - "\n-----END PRIVATE KEY-----\n"; - } finally { - encodedBuf.release(); - } - } finally { - wrappedBuf.release(); - } - - // Change all asterisk to 'x' for file name safety. - fqdn = fqdn.replaceAll("[^\\w.-]", "x"); - - File keyFile = PlatformDependent.createTempFile("keyutil_" + fqdn + '_', ".key", null); - keyFile.deleteOnExit(); - - OutputStream keyOut = new FileOutputStream(keyFile); - try { - keyOut.write(keyText.getBytes(CharsetUtil.US_ASCII)); - keyOut.close(); - keyOut = null; - } finally { - if (keyOut != null) { - safeClose(keyFile, keyOut); - safeDelete(keyFile); - } - } - - wrappedBuf = Unpooled.wrappedBuffer(cert.getEncoded()); - final String certText; - try { - encodedBuf = Base64.encode(wrappedBuf, true); - try { - // Encode the certificate into a CRT file. - certText = "-----BEGIN CERTIFICATE-----\n" + - encodedBuf.toString(CharsetUtil.US_ASCII) + - "\n-----END CERTIFICATE-----\n"; - } finally { - encodedBuf.release(); - } - } finally { - wrappedBuf.release(); - } - - File certFile = PlatformDependent.createTempFile("keyutil_" + fqdn + '_', ".crt", null); - certFile.deleteOnExit(); - - OutputStream certOut = new FileOutputStream(certFile); - try { - certOut.write(certText.getBytes(CharsetUtil.US_ASCII)); - certOut.close(); - certOut = null; - } finally { - if (certOut != null) { - safeClose(certFile, certOut); - safeDelete(certFile); - safeDelete(keyFile); - } - } - - return new String[] { certFile.getPath(), keyFile.getPath() }; - } - - private static void safeDelete(File certFile) { - if (!certFile.delete()) { - if (logger.isWarnEnabled()) { - logger.warn("Failed to delete a file: " + certFile); - } - } - } - - private static void safeClose(File keyFile, OutputStream keyOut) { - try { - keyOut.close(); - } catch (IOException e) { - if (logger.isWarnEnabled()) { - logger.warn("Failed to close a file: " + keyFile, e); - } - } - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/util/SimpleKeyManagerFactory.java b/handler/src/main/java/io/netty/handler/ssl/util/SimpleKeyManagerFactory.java deleted file mode 100644 index 19f3e81360..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/util/SimpleKeyManagerFactory.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.ssl.util; - -import io.netty.util.concurrent.FastThreadLocal; -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.SuppressJava6Requirement; -import java.security.InvalidAlgorithmParameterException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.Provider; -import java.util.Objects; -import javax.net.ssl.ManagerFactoryParameters; -import javax.net.ssl.KeyManager; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.KeyManagerFactorySpi; -import javax.net.ssl.X509ExtendedKeyManager; -import javax.net.ssl.X509KeyManager; - -/** - * Helps to implement a custom {@link KeyManagerFactory}. - */ -public abstract class SimpleKeyManagerFactory extends KeyManagerFactory { - - private static final Provider PROVIDER = new Provider("", 0.0, "") { - private static final long serialVersionUID = -2680540247105807895L; - }; - - /** - * {@link SimpleKeyManagerFactorySpi} must have a reference to {@link SimpleKeyManagerFactory} - * to delegate its callbacks back to {@link SimpleKeyManagerFactory}. However, it is impossible to do so, - * because {@link KeyManagerFactory} requires {@link KeyManagerFactorySpi} at construction time and - * does not provide a way to access it later. - * - * To work around this issue, we use an ugly hack which uses a {@link FastThreadLocal }. - */ - private static final FastThreadLocal CURRENT_SPI = - new FastThreadLocal() { - @Override - protected SimpleKeyManagerFactorySpi initialValue() { - return new SimpleKeyManagerFactorySpi(); - } - }; - - /** - * Creates a new instance. - */ - protected SimpleKeyManagerFactory() { - this(StringUtil.EMPTY_STRING); - } - - /** - * Creates a new instance. - * - * @param name the name of this {@link KeyManagerFactory} - */ - protected SimpleKeyManagerFactory(String name) { - super(CURRENT_SPI.get(), PROVIDER, Objects.requireNonNull(name, "name")); - CURRENT_SPI.get().init(this); - CURRENT_SPI.remove(); - } - - /** - * Initializes this factory with a source of certificate authorities and related key material. - * - * @see KeyManagerFactorySpi#engineInit(KeyStore, char[]) - */ - protected abstract void engineInit(KeyStore keyStore, char[] var2) throws Exception; - - /** - * Initializes this factory with a source of provider-specific key material. - * - * @see KeyManagerFactorySpi#engineInit(ManagerFactoryParameters) - */ - protected abstract void engineInit(ManagerFactoryParameters managerFactoryParameters) throws Exception; - - /** - * Returns one key manager for each type of key material. - * - * @see KeyManagerFactorySpi#engineGetKeyManagers() - */ - protected abstract KeyManager[] engineGetKeyManagers(); - - private static final class SimpleKeyManagerFactorySpi extends KeyManagerFactorySpi { - - private SimpleKeyManagerFactory parent; - private volatile KeyManager[] keyManagers; - - void init(SimpleKeyManagerFactory parent) { - this.parent = parent; - } - - @Override - protected void engineInit(KeyStore keyStore, char[] pwd) throws KeyStoreException { - try { - parent.engineInit(keyStore, pwd); - } catch (KeyStoreException e) { - throw e; - } catch (Exception e) { - throw new KeyStoreException(e); - } - } - - @Override - protected void engineInit( - ManagerFactoryParameters managerFactoryParameters) throws InvalidAlgorithmParameterException { - try { - parent.engineInit(managerFactoryParameters); - } catch (InvalidAlgorithmParameterException e) { - throw e; - } catch (Exception e) { - throw new InvalidAlgorithmParameterException(e); - } - } - - @Override - protected KeyManager[] engineGetKeyManagers() { - KeyManager[] keyManagers = this.keyManagers; - if (keyManagers == null) { - keyManagers = parent.engineGetKeyManagers(); - if (PlatformDependent.javaVersion() >= 7) { - wrapIfNeeded(keyManagers); - } - this.keyManagers = keyManagers; - } - return keyManagers.clone(); - } - - @SuppressJava6Requirement(reason = "Usage guarded by java version check") - private static void wrapIfNeeded(KeyManager[] keyManagers) { - for (int i = 0; i < keyManagers.length; i++) { - final KeyManager tm = keyManagers[i]; - if (tm instanceof X509KeyManager && !(tm instanceof X509ExtendedKeyManager)) { - keyManagers[i] = new X509KeyManagerWrapper((X509KeyManager) tm); - } - } - } - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/util/SimpleTrustManagerFactory.java b/handler/src/main/java/io/netty/handler/ssl/util/SimpleTrustManagerFactory.java deleted file mode 100644 index b2886b73a6..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/util/SimpleTrustManagerFactory.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.ssl.util; - -import static java.util.Objects.requireNonNull; - -import io.netty.util.concurrent.FastThreadLocal; - -import javax.net.ssl.ManagerFactoryParameters; -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 java.security.InvalidAlgorithmParameterException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.Provider; - -/** - * Helps to implement a custom {@link TrustManagerFactory}. - */ -public abstract class SimpleTrustManagerFactory extends TrustManagerFactory { - - private static final Provider PROVIDER = new Provider("", 0.0, "") { - private static final long serialVersionUID = -2680540247105807895L; - }; - - /** - * {@link SimpleTrustManagerFactorySpi} must have a reference to {@link SimpleTrustManagerFactory} - * to delegate its callbacks back to {@link SimpleTrustManagerFactory}. However, it is impossible to do so, - * because {@link TrustManagerFactory} requires {@link TrustManagerFactorySpi} at construction time and - * does not provide a way to access it later. - * - * To work around this issue, we use an ugly hack which uses a {@link ThreadLocal}. - */ - private static final FastThreadLocal CURRENT_SPI = - new FastThreadLocal() { - @Override - protected SimpleTrustManagerFactorySpi initialValue() { - return new SimpleTrustManagerFactorySpi(); - } - }; - - /** - * Creates a new instance. - */ - protected SimpleTrustManagerFactory() { - this(""); - } - - /** - * Creates a new instance. - * - * @param name the name of this {@link TrustManagerFactory} - */ - protected SimpleTrustManagerFactory(String name) { - super(CURRENT_SPI.get(), PROVIDER, name); - CURRENT_SPI.get().init(this); - CURRENT_SPI.remove(); - - requireNonNull(name, "name"); - } - - /** - * Initializes this factory with a source of certificate authorities and related trust material. - * - * @see TrustManagerFactorySpi#engineInit(KeyStore) - */ - protected abstract void engineInit(KeyStore keyStore) throws Exception; - - /** - * Initializes this factory with a source of provider-specific key material. - * - * @see TrustManagerFactorySpi#engineInit(ManagerFactoryParameters) - */ - protected abstract void engineInit(ManagerFactoryParameters managerFactoryParameters) throws Exception; - - /** - * Returns one trust manager for each type of trust material. - * - * @see TrustManagerFactorySpi#engineGetTrustManagers() - */ - protected abstract TrustManager[] engineGetTrustManagers(); - - static final class SimpleTrustManagerFactorySpi extends TrustManagerFactorySpi { - - private SimpleTrustManagerFactory parent; - private volatile TrustManager[] trustManagers; - - void init(SimpleTrustManagerFactory parent) { - this.parent = parent; - } - - @Override - protected void engineInit(KeyStore keyStore) throws KeyStoreException { - try { - parent.engineInit(keyStore); - } catch (KeyStoreException e) { - throw e; - } catch (Exception e) { - throw new KeyStoreException(e); - } - } - - @Override - protected void engineInit( - ManagerFactoryParameters managerFactoryParameters) throws InvalidAlgorithmParameterException { - try { - parent.engineInit(managerFactoryParameters); - } catch (InvalidAlgorithmParameterException e) { - throw e; - } catch (Exception e) { - throw new InvalidAlgorithmParameterException(e); - } - } - - @Override - protected TrustManager[] engineGetTrustManagers() { - TrustManager[] trustManagers = this.trustManagers; - if (trustManagers == null) { - trustManagers = parent.engineGetTrustManagers(); - for (int i = 0; i < trustManagers.length; i++) { - final TrustManager tm = trustManagers[i]; - if (tm instanceof X509TrustManager && !(tm instanceof X509ExtendedTrustManager)) { - trustManagers[i] = new X509TrustManagerWrapper((X509TrustManager) tm); - } - } - this.trustManagers = trustManagers; - } - return trustManagers.clone(); - } - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/util/ThreadLocalInsecureRandom.java b/handler/src/main/java/io/netty/handler/ssl/util/ThreadLocalInsecureRandom.java deleted file mode 100644 index fa2267f01e..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/util/ThreadLocalInsecureRandom.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.ssl.util; - -import java.security.SecureRandom; -import java.util.Random; -import java.util.concurrent.ThreadLocalRandom; - -/** - * Insecure {@link SecureRandom} which relies on {@link ThreadLocalRandom#current()} for random number - * generation. - */ -final class ThreadLocalInsecureRandom extends SecureRandom { - - private static final long serialVersionUID = -8209473337192526191L; - - private static final SecureRandom INSTANCE = new ThreadLocalInsecureRandom(); - - static SecureRandom current() { - return INSTANCE; - } - - private ThreadLocalInsecureRandom() { } - - @Override - public String getAlgorithm() { - return "insecure"; - } - - @Override - public void setSeed(byte[] seed) { } - - @Override - public void setSeed(long seed) { } - - @Override - public void nextBytes(byte[] bytes) { - random().nextBytes(bytes); - } - - @Override - public byte[] generateSeed(int numBytes) { - byte[] seed = new byte[numBytes]; - random().nextBytes(seed); - return seed; - } - - @Override - public int nextInt() { - return random().nextInt(); - } - - @Override - public int nextInt(int n) { - return random().nextInt(n); - } - - @Override - public boolean nextBoolean() { - return random().nextBoolean(); - } - - @Override - public long nextLong() { - return random().nextLong(); - } - - @Override - public float nextFloat() { - return random().nextFloat(); - } - - @Override - public double nextDouble() { - return random().nextDouble(); - } - - @Override - public double nextGaussian() { - return random().nextGaussian(); - } - - private static Random random() { - return ThreadLocalRandom.current(); - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/util/TrustManagerFactoryWrapper.java b/handler/src/main/java/io/netty/handler/ssl/util/TrustManagerFactoryWrapper.java deleted file mode 100644 index 273fdc50af..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/util/TrustManagerFactoryWrapper.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.ssl.util; - -import java.security.KeyStore; -import java.util.Objects; -import javax.net.ssl.ManagerFactoryParameters; -import javax.net.ssl.TrustManager; - -public final class TrustManagerFactoryWrapper extends SimpleTrustManagerFactory { - private final TrustManager tm; - - public TrustManagerFactoryWrapper(TrustManager tm) { - this.tm = Objects.requireNonNull(tm, "tm"); - } - - @Override - protected void engineInit(KeyStore keyStore) throws Exception { } - - @Override - protected void engineInit(ManagerFactoryParameters managerFactoryParameters) - throws Exception { } - - @Override - protected TrustManager[] engineGetTrustManagers() { - return new TrustManager[] {tm}; - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/util/X509KeyManagerWrapper.java b/handler/src/main/java/io/netty/handler/ssl/util/X509KeyManagerWrapper.java deleted file mode 100644 index 16736a8c0a..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/util/X509KeyManagerWrapper.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.ssl.util; - - -import java.net.Socket; -import java.security.Principal; -import java.security.PrivateKey; -import java.security.cert.X509Certificate; -import java.util.Objects; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.X509ExtendedKeyManager; -import javax.net.ssl.X509KeyManager; - -final class X509KeyManagerWrapper extends X509ExtendedKeyManager { - - private final X509KeyManager delegate; - - X509KeyManagerWrapper(X509KeyManager delegate) { - this.delegate = Objects.requireNonNull(delegate, "delegate"); - } - - @Override - public String[] getClientAliases(String var1, Principal[] var2) { - return delegate.getClientAliases(var1, var2); - } - - @Override - public String chooseClientAlias(String[] var1, Principal[] var2, Socket var3) { - return delegate.chooseClientAlias(var1, var2, var3); - } - - @Override - public String[] getServerAliases(String var1, Principal[] var2) { - return delegate.getServerAliases(var1, var2); - } - - @Override - public String chooseServerAlias(String var1, Principal[] var2, Socket var3) { - return delegate.chooseServerAlias(var1, var2, var3); - } - - @Override - public X509Certificate[] getCertificateChain(String var1) { - return delegate.getCertificateChain(var1); - } - - @Override - public PrivateKey getPrivateKey(String var1) { - return delegate.getPrivateKey(var1); - } - - @Override - public String chooseEngineClientAlias(String[] keyType, Principal[] issuers, SSLEngine engine) { - return delegate.chooseClientAlias(keyType, issuers, null); - } - - @Override - public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine) { - return delegate.chooseServerAlias(keyType, issuers, null); - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/util/X509TrustManagerWrapper.java b/handler/src/main/java/io/netty/handler/ssl/util/X509TrustManagerWrapper.java deleted file mode 100644 index 4e42d9cdc4..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/util/X509TrustManagerWrapper.java +++ /dev/null @@ -1,73 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl.util; - -import javax.net.ssl.SSLEngine; -import javax.net.ssl.X509ExtendedTrustManager; -import javax.net.ssl.X509TrustManager; -import java.net.Socket; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; - -import static java.util.Objects.requireNonNull; - -final class X509TrustManagerWrapper extends X509ExtendedTrustManager { - - private final X509TrustManager delegate; - - X509TrustManagerWrapper(X509TrustManager delegate) { - this.delegate = requireNonNull(delegate, "delegate"); - } - - @Override - public void checkClientTrusted(X509Certificate[] chain, String s) throws CertificateException { - delegate.checkClientTrusted(chain, s); - } - - @Override - public void checkClientTrusted(X509Certificate[] chain, String s, Socket socket) - throws CertificateException { - delegate.checkClientTrusted(chain, s); - } - - @Override - public void checkClientTrusted(X509Certificate[] chain, String s, SSLEngine sslEngine) - throws CertificateException { - delegate.checkClientTrusted(chain, s); - } - - @Override - public void checkServerTrusted(X509Certificate[] chain, String s) throws CertificateException { - delegate.checkServerTrusted(chain, s); - } - - @Override - public void checkServerTrusted(X509Certificate[] chain, String s, Socket socket) - throws CertificateException { - delegate.checkServerTrusted(chain, s); - } - - @Override - public void checkServerTrusted(X509Certificate[] chain, String s, SSLEngine sslEngine) - throws CertificateException { - delegate.checkServerTrusted(chain, s); - } - - @Override - public X509Certificate[] getAcceptedIssuers() { - return delegate.getAcceptedIssuers(); - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/util/package-info.java b/handler/src/main/java/io/netty/handler/ssl/util/package-info.java deleted file mode 100644 index e2abab73a5..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/util/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * Utility classes that helps easier development of TLS/SSL applications. - */ -package io.netty.handler.ssl.util; diff --git a/handler/src/main/java/io/netty/handler/stream/ChunkedFile.java b/handler/src/main/java/io/netty/handler/stream/ChunkedFile.java deleted file mode 100644 index f236c446a4..0000000000 --- a/handler/src/main/java/io/netty/handler/stream/ChunkedFile.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.stream; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.FileRegion; - -import java.io.File; -import java.io.IOException; -import java.io.RandomAccessFile; - -/** - * A {@link ChunkedInput} that fetches data from a file chunk by chunk. - *

- * If your operating system supports - * zero-copy file transfer - * such as {@code sendfile()}, you might want to use {@link FileRegion} instead. - */ -public class ChunkedFile implements ChunkedInput { - - private final RandomAccessFile file; - private final long startOffset; - private final long endOffset; - private final int chunkSize; - private long offset; - - /** - * Creates a new instance that fetches data from the specified file. - */ - public ChunkedFile(File file) throws IOException { - this(file, ChunkedStream.DEFAULT_CHUNK_SIZE); - } - - /** - * Creates a new instance that fetches data from the specified file. - * - * @param chunkSize the number of bytes to fetch on each - * {@link #readChunk(ChannelHandlerContext)} call - */ - public ChunkedFile(File file, int chunkSize) throws IOException { - this(new RandomAccessFile(file, "r"), chunkSize); - } - - /** - * Creates a new instance that fetches data from the specified file. - */ - public ChunkedFile(RandomAccessFile file) throws IOException { - this(file, ChunkedStream.DEFAULT_CHUNK_SIZE); - } - - /** - * Creates a new instance that fetches data from the specified file. - * - * @param chunkSize the number of bytes to fetch on each - * {@link #readChunk(ChannelHandlerContext)} call - */ - public ChunkedFile(RandomAccessFile file, int chunkSize) throws IOException { - this(file, 0, file.length(), chunkSize); - } - - /** - * Creates a new instance that fetches data from the specified file. - * - * @param offset the offset of the file where the transfer begins - * @param length the number of bytes to transfer - * @param chunkSize the number of bytes to fetch on each - * {@link #readChunk(ChannelHandlerContext)} call - */ - public ChunkedFile(RandomAccessFile file, long offset, long length, int chunkSize) throws IOException { - requireNonNull(file, "file"); - if (offset < 0) { - throw new IllegalArgumentException( - "offset: " + offset + " (expected: 0 or greater)"); - } - if (length < 0) { - throw new IllegalArgumentException( - "length: " + length + " (expected: 0 or greater)"); - } - if (chunkSize <= 0) { - throw new IllegalArgumentException( - "chunkSize: " + chunkSize + - " (expected: a positive integer)"); - } - - this.file = file; - this.offset = startOffset = offset; - endOffset = offset + length; - this.chunkSize = chunkSize; - - file.seek(offset); - } - - /** - * Returns the offset in the file where the transfer began. - */ - public long startOffset() { - return startOffset; - } - - /** - * Returns the offset in the file where the transfer will end. - */ - public long endOffset() { - return endOffset; - } - - /** - * Returns the offset in the file where the transfer is happening currently. - */ - public long currentOffset() { - return offset; - } - - @Override - public boolean isEndOfInput() throws Exception { - return !(offset < endOffset && file.getChannel().isOpen()); - } - - @Override - public void close() throws Exception { - file.close(); - } - - @Deprecated - @Override - public ByteBuf readChunk(ChannelHandlerContext ctx) throws Exception { - return readChunk(ctx.alloc()); - } - - @Override - public ByteBuf readChunk(ByteBufAllocator allocator) throws Exception { - long offset = this.offset; - if (offset >= endOffset) { - return null; - } - - int chunkSize = (int) Math.min(this.chunkSize, endOffset - offset); - // Check if the buffer is backed by an byte array. If so we can optimize it a bit an safe a copy - - ByteBuf buf = allocator.heapBuffer(chunkSize); - boolean release = true; - try { - file.readFully(buf.array(), buf.arrayOffset(), chunkSize); - buf.writerIndex(chunkSize); - this.offset = offset + chunkSize; - release = false; - return buf; - } finally { - if (release) { - buf.release(); - } - } - } - - @Override - public long length() { - return endOffset - startOffset; - } - - @Override - public long progress() { - return offset - startOffset; - } -} diff --git a/handler/src/main/java/io/netty/handler/stream/ChunkedInput.java b/handler/src/main/java/io/netty/handler/stream/ChunkedInput.java deleted file mode 100644 index 6d932d6354..0000000000 --- a/handler/src/main/java/io/netty/handler/stream/ChunkedInput.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.stream; - - -import io.netty.buffer.ByteBufAllocator; -import io.netty.channel.ChannelHandlerContext; - -/** - * A data stream of indefinite length which is consumed by {@link ChunkedWriteHandler}. - */ -public interface ChunkedInput { - - /** - * Return {@code true} if and only if there is no data left in the stream - * and the stream has reached at its end. - */ - boolean isEndOfInput() throws Exception; - - /** - * Releases the resources associated with the input. - */ - void close() throws Exception; - - /** - * @deprecated Use {@link #readChunk(ByteBufAllocator)}. - * - *

Fetches a chunked data from the stream. Once this method returns the last chunk - * and thus the stream has reached at its end, any subsequent {@link #isEndOfInput()} - * call must return {@code true}. - * - * @param ctx The context which provides a {@link ByteBufAllocator} if buffer allocation is necessary. - * @return the fetched chunk. - * {@code null} if there is no data left in the stream. - * Please note that {@code null} does not necessarily mean that the - * stream has reached at its end. In a slow stream, the next chunk - * might be unavailable just momentarily. - */ - @Deprecated - B readChunk(ChannelHandlerContext ctx) throws Exception; - - /** - * Fetches a chunked data from the stream. Once this method returns the last chunk - * and thus the stream has reached at its end, any subsequent {@link #isEndOfInput()} - * call must return {@code true}. - * - * @param allocator {@link ByteBufAllocator} if buffer allocation is necessary. - * @return the fetched chunk. - * {@code null} if there is no data left in the stream. - * Please note that {@code null} does not necessarily mean that the - * stream has reached at its end. In a slow stream, the next chunk - * might be unavailable just momentarily. - */ - B readChunk(ByteBufAllocator allocator) throws Exception; - - /** - * Returns the length of the input. - * @return the length of the input if the length of the input is known. - * a negative value if the length of the input is unknown. - */ - long length(); - - /** - * Returns current transfer progress. - */ - long progress(); - -} diff --git a/handler/src/main/java/io/netty/handler/stream/ChunkedNioFile.java b/handler/src/main/java/io/netty/handler/stream/ChunkedNioFile.java deleted file mode 100644 index 50eb8a67c1..0000000000 --- a/handler/src/main/java/io/netty/handler/stream/ChunkedNioFile.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.stream; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.FileRegion; - -import java.io.File; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.nio.channels.ClosedChannelException; -import java.nio.channels.FileChannel; - -/** - * A {@link ChunkedInput} that fetches data from a file chunk by chunk using - * NIO {@link FileChannel}. - *

- * If your operating system supports - * zero-copy file transfer - * such as {@code sendfile()}, you might want to use {@link FileRegion} instead. - */ -public class ChunkedNioFile implements ChunkedInput { - - private final FileChannel in; - private final long startOffset; - private final long endOffset; - private final int chunkSize; - private long offset; - - /** - * Creates a new instance that fetches data from the specified file. - */ - public ChunkedNioFile(File in) throws IOException { - this(new RandomAccessFile(in, "r").getChannel()); - } - - /** - * Creates a new instance that fetches data from the specified file. - * - * @param chunkSize the number of bytes to fetch on each - * {@link #readChunk(ChannelHandlerContext)} call - */ - public ChunkedNioFile(File in, int chunkSize) throws IOException { - this(new RandomAccessFile(in, "r").getChannel(), chunkSize); - } - - /** - * Creates a new instance that fetches data from the specified file. - */ - public ChunkedNioFile(FileChannel in) throws IOException { - this(in, ChunkedStream.DEFAULT_CHUNK_SIZE); - } - - /** - * Creates a new instance that fetches data from the specified file. - * - * @param chunkSize the number of bytes to fetch on each - * {@link #readChunk(ChannelHandlerContext)} call - */ - public ChunkedNioFile(FileChannel in, int chunkSize) throws IOException { - this(in, 0, in.size(), chunkSize); - } - - /** - * Creates a new instance that fetches data from the specified file. - * - * @param offset the offset of the file where the transfer begins - * @param length the number of bytes to transfer - * @param chunkSize the number of bytes to fetch on each - * {@link #readChunk(ChannelHandlerContext)} call - */ - public ChunkedNioFile(FileChannel in, long offset, long length, int chunkSize) throws IOException { - requireNonNull(in, "in"); - if (offset < 0) { - throw new IllegalArgumentException( - "offset: " + offset + " (expected: 0 or greater)"); - } - if (length < 0) { - throw new IllegalArgumentException( - "length: " + length + " (expected: 0 or greater)"); - } - if (chunkSize <= 0) { - throw new IllegalArgumentException( - "chunkSize: " + chunkSize + - " (expected: a positive integer)"); - } - if (!in.isOpen()) { - throw new ClosedChannelException(); - } - this.in = in; - this.chunkSize = chunkSize; - this.offset = startOffset = offset; - endOffset = offset + length; - } - - /** - * Returns the offset in the file where the transfer began. - */ - public long startOffset() { - return startOffset; - } - - /** - * Returns the offset in the file where the transfer will end. - */ - public long endOffset() { - return endOffset; - } - - /** - * Returns the offset in the file where the transfer is happening currently. - */ - public long currentOffset() { - return offset; - } - - @Override - public boolean isEndOfInput() throws Exception { - return !(offset < endOffset && in.isOpen()); - } - - @Override - public void close() throws Exception { - in.close(); - } - - @Deprecated - @Override - public ByteBuf readChunk(ChannelHandlerContext ctx) throws Exception { - return readChunk(ctx.alloc()); - } - - @Override - public ByteBuf readChunk(ByteBufAllocator allocator) throws Exception { - long offset = this.offset; - if (offset >= endOffset) { - return null; - } - - int chunkSize = (int) Math.min(this.chunkSize, endOffset - offset); - ByteBuf buffer = allocator.buffer(chunkSize); - boolean release = true; - try { - int readBytes = 0; - for (;;) { - int localReadBytes = buffer.writeBytes(in, offset + readBytes, chunkSize - readBytes); - if (localReadBytes < 0) { - break; - } - readBytes += localReadBytes; - if (readBytes == chunkSize) { - break; - } - } - this.offset += readBytes; - release = false; - return buffer; - } finally { - if (release) { - buffer.release(); - } - } - } - - @Override - public long length() { - return endOffset - startOffset; - } - - @Override - public long progress() { - return offset - startOffset; - } -} diff --git a/handler/src/main/java/io/netty/handler/stream/ChunkedNioStream.java b/handler/src/main/java/io/netty/handler/stream/ChunkedNioStream.java deleted file mode 100644 index 74ee0a0ed3..0000000000 --- a/handler/src/main/java/io/netty/handler/stream/ChunkedNioStream.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.stream; - -import static io.netty.util.internal.ObjectUtil.checkPositive; -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.channel.ChannelHandlerContext; - -import java.nio.ByteBuffer; -import java.nio.channels.ReadableByteChannel; - -/** - * A {@link ChunkedInput} that fetches data from a {@link ReadableByteChannel} - * chunk by chunk. Please note that the {@link ReadableByteChannel} must - * operate in blocking mode. Non-blocking mode channels are not supported. - */ -public class ChunkedNioStream implements ChunkedInput { - - private final ReadableByteChannel in; - - private final int chunkSize; - private long offset; - - /** - * Associated ByteBuffer - */ - private final ByteBuffer byteBuffer; - - /** - * Creates a new instance that fetches data from the specified channel. - */ - public ChunkedNioStream(ReadableByteChannel in) { - this(in, ChunkedStream.DEFAULT_CHUNK_SIZE); - } - - /** - * Creates a new instance that fetches data from the specified channel. - * - * @param chunkSize the number of bytes to fetch on each - * {@link #readChunk(ChannelHandlerContext)} call - */ - public ChunkedNioStream(ReadableByteChannel in, int chunkSize) { - this.in = requireNonNull(in, "in"); - this.chunkSize = checkPositive(chunkSize, "chunkSize"); - byteBuffer = ByteBuffer.allocate(chunkSize); - } - - /** - * Returns the number of transferred bytes. - */ - public long transferredBytes() { - return offset; - } - - @Override - public boolean isEndOfInput() throws Exception { - if (byteBuffer.position() > 0) { - // A previous read was not over, so there is a next chunk in the buffer at least - return false; - } - if (in.isOpen()) { - // Try to read a new part, and keep this part (no rewind) - int b = in.read(byteBuffer); - if (b < 0) { - return true; - } else { - offset += b; - return false; - } - } - return true; - } - - @Override - public void close() throws Exception { - in.close(); - } - - @Deprecated - @Override - public ByteBuf readChunk(ChannelHandlerContext ctx) throws Exception { - return readChunk(ctx.alloc()); - } - - @Override - public ByteBuf readChunk(ByteBufAllocator allocator) throws Exception { - if (isEndOfInput()) { - return null; - } - // buffer cannot be not be empty from there - int readBytes = byteBuffer.position(); - for (;;) { - int localReadBytes = in.read(byteBuffer); - if (localReadBytes < 0) { - break; - } - readBytes += localReadBytes; - offset += localReadBytes; - if (readBytes == chunkSize) { - break; - } - } - byteBuffer.flip(); - boolean release = true; - ByteBuf buffer = allocator.buffer(byteBuffer.remaining()); - try { - buffer.writeBytes(byteBuffer); - byteBuffer.clear(); - release = false; - return buffer; - } finally { - if (release) { - buffer.release(); - } - } - } - - @Override - public long length() { - return -1; - } - - @Override - public long progress() { - return offset; - } -} diff --git a/handler/src/main/java/io/netty/handler/stream/ChunkedStream.java b/handler/src/main/java/io/netty/handler/stream/ChunkedStream.java deleted file mode 100644 index 2dc176c60e..0000000000 --- a/handler/src/main/java/io/netty/handler/stream/ChunkedStream.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.stream; - -import static java.util.Objects.requireNonNull; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.channel.ChannelHandlerContext; - -import java.io.InputStream; -import java.io.PushbackInputStream; - -/** - * A {@link ChunkedInput} that fetches data from an {@link InputStream} chunk by - * chunk. - *

- * Please note that the {@link InputStream} instance that feeds data into - * {@link ChunkedStream} must implement {@link InputStream#available()} as - * accurately as possible, rather than using the default implementation. - * Otherwise, {@link ChunkedStream} will generate many too small chunks or - * block unnecessarily often. - */ -public class ChunkedStream implements ChunkedInput { - - static final int DEFAULT_CHUNK_SIZE = 8192; - - private final PushbackInputStream in; - private final int chunkSize; - private long offset; - private boolean closed; - - /** - * Creates a new instance that fetches data from the specified stream. - */ - public ChunkedStream(InputStream in) { - this(in, DEFAULT_CHUNK_SIZE); - } - - /** - * Creates a new instance that fetches data from the specified stream. - * - * @param chunkSize the number of bytes to fetch on each - * {@link #readChunk(ChannelHandlerContext)} call - */ - public ChunkedStream(InputStream in, int chunkSize) { - requireNonNull(in, "in"); - if (chunkSize <= 0) { - throw new IllegalArgumentException( - "chunkSize: " + chunkSize + - " (expected: a positive integer)"); - } - - if (in instanceof PushbackInputStream) { - this.in = (PushbackInputStream) in; - } else { - this.in = new PushbackInputStream(in); - } - this.chunkSize = chunkSize; - } - - /** - * Returns the number of transferred bytes. - */ - public long transferredBytes() { - return offset; - } - - @Override - public boolean isEndOfInput() throws Exception { - if (closed) { - return true; - } - if (in.available() > 0) { - return false; - } - - int b = in.read(); - if (b < 0) { - return true; - } else { - in.unread(b); - return false; - } - } - - @Override - public void close() throws Exception { - closed = true; - in.close(); - } - - @Deprecated - @Override - public ByteBuf readChunk(ChannelHandlerContext ctx) throws Exception { - return readChunk(ctx.alloc()); - } - - @Override - public ByteBuf readChunk(ByteBufAllocator allocator) throws Exception { - if (isEndOfInput()) { - return null; - } - - final int availableBytes = in.available(); - final int chunkSize; - if (availableBytes <= 0) { - chunkSize = this.chunkSize; - } else { - chunkSize = Math.min(this.chunkSize, in.available()); - } - - boolean release = true; - ByteBuf buffer = allocator.buffer(chunkSize); - try { - // transfer to buffer - offset += buffer.writeBytes(in, chunkSize); - release = false; - return buffer; - } finally { - if (release) { - buffer.release(); - } - } - } - - @Override - public long length() { - return -1; - } - - @Override - public long progress() { - return offset; - } -} diff --git a/handler/src/main/java/io/netty/handler/stream/ChunkedWriteHandler.java b/handler/src/main/java/io/netty/handler/stream/ChunkedWriteHandler.java deleted file mode 100644 index 8bf9ac01ae..0000000000 --- a/handler/src/main/java/io/netty/handler/stream/ChunkedWriteHandler.java +++ /dev/null @@ -1,352 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.stream; - -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.nio.channels.ClosedChannelException; -import java.util.ArrayDeque; -import java.util.Queue; - -import static io.netty.util.internal.ObjectUtil.checkPositive; - -/** - * A {@link ChannelHandler} that adds support for writing a large data stream - * asynchronously neither spending a lot of memory nor getting - * {@link OutOfMemoryError}. Large data streaming such as file - * transfer requires complicated state management in a {@link ChannelHandler} - * implementation. {@link ChunkedWriteHandler} manages such complicated states - * so that you can send a large data stream without difficulties. - *

- * To use {@link ChunkedWriteHandler} in your application, you have to insert - * a new {@link ChunkedWriteHandler} instance: - *

- * {@link ChannelPipeline} p = ...;
- * p.addLast("streamer", new {@link ChunkedWriteHandler}());
- * p.addLast("handler", new MyHandler());
- * 
- * Once inserted, you can write a {@link ChunkedInput} so that the - * {@link ChunkedWriteHandler} can pick it up and fetch the content of the - * stream chunk by chunk and write the fetched chunk downstream: - *
- * {@link Channel} ch = ...;
- * ch.write(new {@link ChunkedFile}(new File("video.mkv"));
- * 
- * - *

Sending a stream which generates a chunk intermittently

- * - * Some {@link ChunkedInput} generates a chunk on a certain event or timing. - * Such {@link ChunkedInput} implementation often returns {@code null} on - * {@link ChunkedInput#readChunk(ChannelHandlerContext)}, resulting in the indefinitely suspended - * transfer. To resume the transfer when a new chunk is available, you have to - * call {@link #resumeTransfer()}. - */ -public class ChunkedWriteHandler implements ChannelHandler { - - private static final InternalLogger logger = - InternalLoggerFactory.getInstance(ChunkedWriteHandler.class); - - private final Queue queue = new ArrayDeque<>(); - private volatile ChannelHandlerContext ctx; - - public ChunkedWriteHandler() { - } - - /** - * @deprecated use {@link #ChunkedWriteHandler()} - */ - @Deprecated - public ChunkedWriteHandler(int maxPendingWrites) { - checkPositive(maxPendingWrites, "maxPendingWrites"); - } - - @Override - public void handlerAdded(ChannelHandlerContext ctx) throws Exception { - this.ctx = ctx; - } - - /** - * Continues to fetch the chunks from the input. - */ - public void resumeTransfer() { - final ChannelHandlerContext ctx = this.ctx; - if (ctx == null) { - return; - } - if (ctx.executor().inEventLoop()) { - resumeTransfer0(ctx); - } else { - // let the transfer resume on the next event loop round - ctx.executor().execute(() -> resumeTransfer0(ctx)); - } - } - - private void resumeTransfer0(ChannelHandlerContext ctx) { - try { - doFlush(ctx); - } catch (Exception e) { - logger.warn("Unexpected exception while sending chunks.", e); - } - } - - @Override - public Future write(ChannelHandlerContext ctx, Object msg) { - Promise promise = ctx.newPromise(); - queue.add(new PendingWrite(msg, promise)); - return promise.asFuture(); - } - - @Override - public void flush(ChannelHandlerContext ctx) { - doFlush(ctx); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - doFlush(ctx); - ctx.fireChannelInactive(); - } - - @Override - public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception { - if (ctx.channel().isWritable()) { - // channel is writable again try to continue flushing - doFlush(ctx); - } - ctx.fireChannelWritabilityChanged(); - } - - private void discard(Throwable cause) { - for (;;) { - PendingWrite currentWrite = queue.poll(); - - if (currentWrite == null) { - break; - } - Object message = currentWrite.msg; - if (message instanceof ChunkedInput) { - ChunkedInput in = (ChunkedInput) message; - boolean endOfInput; - try { - endOfInput = in.isEndOfInput(); - closeInput(in); - } catch (Exception e) { - closeInput(in); - currentWrite.fail(e); - if (logger.isWarnEnabled()) { - logger.warn(ChunkedInput.class.getSimpleName() + " failed", e); - } - continue; - } - - if (!endOfInput) { - if (cause == null) { - cause = new ClosedChannelException(); - } - currentWrite.fail(cause); - } else { - currentWrite.success(); - } - } else { - if (cause == null) { - cause = new ClosedChannelException(); - } - currentWrite.fail(cause); - } - } - } - - private void doFlush(final ChannelHandlerContext ctx) { - final Channel channel = ctx.channel(); - if (!channel.isActive()) { - discard(null); - return; - } - - boolean requiresFlush = true; - ByteBufAllocator allocator = ctx.alloc(); - while (channel.isWritable()) { - final PendingWrite currentWrite = queue.peek(); - - 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. - queue.remove(); - continue; - } - - final Object pendingMessage = currentWrite.msg; - - if (pendingMessage instanceof ChunkedInput) { - final ChunkedInput chunks = (ChunkedInput) pendingMessage; - boolean endOfInput; - boolean suspend; - Object message = null; - try { - message = chunks.readChunk(allocator); - endOfInput = chunks.isEndOfInput(); - - if (message == null) { - // No need to suspend when reached at the end. - suspend = !endOfInput; - } else { - suspend = false; - } - } catch (final Throwable t) { - queue.remove(); - - if (message != null) { - ReferenceCountUtil.release(message); - } - - closeInput(chunks); - currentWrite.fail(t); - break; - } - - if (suspend) { - // ChunkedInput.nextChunk() returned null and it has - // not reached at the end of input. Let's wait until - // more chunks arrive. Nothing to write or notify. - break; - } - - if (message == null) { - // If message is null write an empty ByteBuf. - // See https://github.com/netty/netty/issues/1671 - message = Unpooled.EMPTY_BUFFER; - } - - if (endOfInput) { - // We need to remove the element from the queue before we call writeAndFlush() as this operation - // may cause an action that also touches the queue. - queue.remove(); - } - // Flush each chunk to conserve memory - Future f = ctx.writeAndFlush(message); - if (endOfInput) { - if (f.isDone()) { - handleEndOfInputFuture(f, currentWrite); - } else { - // Register a listener which will close the input once the write is complete. - // This is needed because the Chunk may have some resource bound that can not - // be closed before its not written. - // - // See https://github.com/netty/netty/issues/303 - f.addListener(future -> handleEndOfInputFuture(future, currentWrite)); - } - } else { - final boolean resume = !channel.isWritable(); - if (f.isDone()) { - handleFuture(channel, f, currentWrite, resume); - } else { - f.addListener(future -> handleFuture(channel, future, currentWrite, resume)); - } - } - requiresFlush = false; - } else { - queue.remove(); - ctx.write(pendingMessage).cascadeTo(currentWrite.promise); - requiresFlush = true; - } - - if (!channel.isActive()) { - discard(new ClosedChannelException()); - break; - } - } - - if (requiresFlush) { - ctx.flush(); - } - } - - private static void handleEndOfInputFuture(Future future, PendingWrite currentWrite) { - ChunkedInput input = (ChunkedInput) currentWrite.msg; - closeInput(input); - if (future.isFailed()) { - currentWrite.fail(future.cause()); - } else { - currentWrite.success(); - } - } - - private void handleFuture(Channel channel, Future future, PendingWrite currentWrite, boolean resume) { - ChunkedInput input = (ChunkedInput) currentWrite.msg; - if (future.isFailed()) { - closeInput(input); - currentWrite.fail(future.cause()); - } else { - if (resume && channel.isWritable()) { - resumeTransfer(); - } - } - } - - private static void closeInput(ChunkedInput chunks) { - try { - chunks.close(); - } catch (Throwable t) { - if (logger.isWarnEnabled()) { - logger.warn("Failed to close a chunked input.", t); - } - } - } - - private static final class PendingWrite { - final Object msg; - final Promise promise; - - PendingWrite(Object msg, Promise promise) { - this.msg = msg; - this.promise = promise; - } - - void fail(Throwable cause) { - ReferenceCountUtil.release(msg); - promise.tryFailure(cause); - } - - void success() { - if (promise.isDone()) { - // No need to notify the progress or fulfill the promise because it's done already. - return; - } - promise.trySuccess(null); - } - } -} diff --git a/handler/src/main/java/io/netty/handler/stream/package-info.java b/handler/src/main/java/io/netty/handler/stream/package-info.java deleted file mode 100644 index fb4e05cc91..0000000000 --- a/handler/src/main/java/io/netty/handler/stream/package-info.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * Writes very large data stream asynchronously neither spending a lot of - * memory nor getting {@link java.lang.OutOfMemoryError}. For a detailed - * example, please refer to {@code io.netty.example.http.file}. - */ -package io.netty.handler.stream; diff --git a/handler/src/main/java/io/netty/handler/timeout/IdleState.java b/handler/src/main/java/io/netty/handler/timeout/IdleState.java deleted file mode 100644 index 7f185b6471..0000000000 --- a/handler/src/main/java/io/netty/handler/timeout/IdleState.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.timeout; - -import io.netty.channel.Channel; - - -/** - * An {@link Enum} that represents the idle state of a {@link Channel}. - */ -public enum IdleState { - /** - * No data was received for a while. - */ - READER_IDLE, - /** - * No data was sent for a while. - */ - WRITER_IDLE, - /** - * No data was either received or sent for a while. - */ - ALL_IDLE -} diff --git a/handler/src/main/java/io/netty/handler/timeout/IdleStateEvent.java b/handler/src/main/java/io/netty/handler/timeout/IdleStateEvent.java deleted file mode 100644 index d3d21fdcb3..0000000000 --- a/handler/src/main/java/io/netty/handler/timeout/IdleStateEvent.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.timeout; - -import static java.util.Objects.requireNonNull; - -import io.netty.channel.Channel; - -import io.netty.util.internal.StringUtil; - -/** - * A user event triggered by {@link IdleStateHandler} when a {@link Channel} is idle. - */ -public class IdleStateEvent { - public static final IdleStateEvent FIRST_READER_IDLE_STATE_EVENT = - new DefaultIdleStateEvent(IdleState.READER_IDLE, true); - public static final IdleStateEvent READER_IDLE_STATE_EVENT = - new DefaultIdleStateEvent(IdleState.READER_IDLE, false); - public static final IdleStateEvent FIRST_WRITER_IDLE_STATE_EVENT = - new DefaultIdleStateEvent(IdleState.WRITER_IDLE, true); - public static final IdleStateEvent WRITER_IDLE_STATE_EVENT = - new DefaultIdleStateEvent(IdleState.WRITER_IDLE, false); - public static final IdleStateEvent FIRST_ALL_IDLE_STATE_EVENT = - new DefaultIdleStateEvent(IdleState.ALL_IDLE, true); - public static final IdleStateEvent ALL_IDLE_STATE_EVENT = - new DefaultIdleStateEvent(IdleState.ALL_IDLE, false); - - private final IdleState state; - private final boolean first; - - /** - * Constructor for sub-classes. - * - * @param state the {@link IdleStateEvent} which triggered the event. - * @param first {@code true} if its the first idle event for the {@link IdleStateEvent}. - */ - protected IdleStateEvent(IdleState state, boolean first) { - this.state = requireNonNull(state, "state"); - this.first = first; - } - - /** - * Returns the idle state. - */ - public IdleState state() { - return state; - } - - /** - * Returns {@code true} if this was the first event for the {@link IdleState} - */ - public boolean isFirst() { - return first; - } - - @Override - public String toString() { - return StringUtil.simpleClassName(this) + '(' + state + (first ? ", first" : "") + ')'; - } - - private static final class DefaultIdleStateEvent extends IdleStateEvent { - private final String representation; - - DefaultIdleStateEvent(IdleState state, boolean first) { - super(state, first); - this.representation = "IdleStateEvent(" + state + (first ? ", first" : "") + ')'; - } - - @Override - public String toString() { - return representation; - } - } -} diff --git a/handler/src/main/java/io/netty/handler/timeout/IdleStateHandler.java b/handler/src/main/java/io/netty/handler/timeout/IdleStateHandler.java deleted file mode 100644 index 7ce25394ca..0000000000 --- a/handler/src/main/java/io/netty/handler/timeout/IdleStateHandler.java +++ /dev/null @@ -1,586 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.timeout; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.Channel; -import io.netty.channel.Channel.Unsafe; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelOutboundBuffer; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.FutureListener; - -import java.util.concurrent.TimeUnit; - -import static java.util.Objects.requireNonNull; - -/** - * Triggers an {@link IdleStateEvent} when a {@link Channel} has not performed - * read, write, or both operation for a while. - * - *

Supported idle states

- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
PropertyMeaning
{@code readerIdleTime}an {@link IdleStateEvent} whose state is {@link IdleState#READER_IDLE} - * will be triggered when no read was performed for the specified period of - * time. Specify {@code 0} to disable.
{@code writerIdleTime}an {@link IdleStateEvent} whose state is {@link IdleState#WRITER_IDLE} - * will be triggered when no write was performed for the specified period of - * time. Specify {@code 0} to disable.
{@code allIdleTime}an {@link IdleStateEvent} whose state is {@link IdleState#ALL_IDLE} - * will be triggered when neither read nor write was performed for the - * specified period of time. Specify {@code 0} to disable.
- * - *
- * // An example that sends a ping message when there is no outbound traffic
- * // for 30 seconds.  The connection is closed when there is no inbound traffic
- * // for 60 seconds.
- *
- * public class MyChannelInitializer extends {@link ChannelInitializer}<{@link Channel}> {
- *     {@code @Override}
- *     public void initChannel({@link Channel} channel) {
- *         channel.pipeline().addLast("idleStateHandler", new {@link IdleStateHandler}(60, 30, 0));
- *         channel.pipeline().addLast("myHandler", new MyHandler());
- *     }
- * }
- *
- * // Handler should handle the {@link IdleStateEvent} triggered by {@link IdleStateHandler}.
- * public class MyHandler implements {@link ChannelHandler} {
- *     {@code @Override}
- *     public void userEventTriggered({@link ChannelHandlerContext} ctx, {@link Object} evt) throws {@link Exception} {
- *         if (evt instanceof {@link IdleStateEvent}) {
- *             {@link IdleStateEvent} e = ({@link IdleStateEvent}) evt;
- *             if (e.state() == {@link IdleState}.READER_IDLE) {
- *                 ctx.close();
- *             } else if (e.state() == {@link IdleState}.WRITER_IDLE) {
- *                 ctx.writeAndFlush(new PingMessage());
- *             }
- *         }
- *     }
- * }
- *
- * {@link ServerBootstrap} bootstrap = ...;
- * ...
- * bootstrap.childHandler(new MyChannelInitializer());
- * ...
- * 
- * - * @see ReadTimeoutHandler - * @see WriteTimeoutHandler - */ -public class IdleStateHandler implements ChannelHandler { - private static final long MIN_TIMEOUT_NANOS = TimeUnit.MILLISECONDS.toNanos(1); - - // Not create a new ChannelFutureListeners per write operation to reduce GC pressure. - private final FutureListener writeListener = future -> { - lastWriteTime = ticksInNanos(); - firstWriterIdleEvent = firstAllIdleEvent = true; - }; - - private final boolean observeOutput; - private final long readerIdleTimeNanos; - private final long writerIdleTimeNanos; - private final long allIdleTimeNanos; - - private Future readerIdleTimeout; - private long lastReadTime; - private boolean firstReaderIdleEvent = true; - - private Future writerIdleTimeout; - private long lastWriteTime; - private boolean firstWriterIdleEvent = true; - - private Future allIdleTimeout; - private boolean firstAllIdleEvent = true; - - private byte state; // 0 - none, 1 - initialized, 2 - destroyed - private boolean reading; - - private long lastChangeCheckTimeStamp; - private int lastMessageHashCode; - private long lastPendingWriteBytes; - private long lastFlushProgress; - - /** - * Creates a new instance firing {@link IdleStateEvent}s. - * - * @param readerIdleTimeSeconds - * an {@link IdleStateEvent} whose state is {@link IdleState#READER_IDLE} - * will be triggered when no read was performed for the specified - * period of time. Specify {@code 0} to disable. - * @param writerIdleTimeSeconds - * an {@link IdleStateEvent} whose state is {@link IdleState#WRITER_IDLE} - * will be triggered when no write was performed for the specified - * period of time. Specify {@code 0} to disable. - * @param allIdleTimeSeconds - * an {@link IdleStateEvent} whose state is {@link IdleState#ALL_IDLE} - * will be triggered when neither read nor write was performed for - * the specified period of time. Specify {@code 0} to disable. - */ - public IdleStateHandler( - int readerIdleTimeSeconds, - int writerIdleTimeSeconds, - int allIdleTimeSeconds) { - - this(readerIdleTimeSeconds, writerIdleTimeSeconds, allIdleTimeSeconds, - TimeUnit.SECONDS); - } - - /** - * @see #IdleStateHandler(boolean, long, long, long, TimeUnit) - */ - public IdleStateHandler( - long readerIdleTime, long writerIdleTime, long allIdleTime, - TimeUnit unit) { - this(false, readerIdleTime, writerIdleTime, allIdleTime, unit); - } - - /** - * Creates a new instance firing {@link IdleStateEvent}s. - * - * @param observeOutput - * whether or not the consumption of {@code bytes} should be taken into - * consideration when assessing write idleness. The default is {@code false}. - * @param readerIdleTime - * an {@link IdleStateEvent} whose state is {@link IdleState#READER_IDLE} - * will be triggered when no read was performed for the specified - * period of time. Specify {@code 0} to disable. - * @param writerIdleTime - * an {@link IdleStateEvent} whose state is {@link IdleState#WRITER_IDLE} - * will be triggered when no write was performed for the specified - * period of time. Specify {@code 0} to disable. - * @param allIdleTime - * an {@link IdleStateEvent} whose state is {@link IdleState#ALL_IDLE} - * will be triggered when neither read nor write was performed for - * the specified period of time. Specify {@code 0} to disable. - * @param unit - * the {@link TimeUnit} of {@code readerIdleTime}, - * {@code writeIdleTime}, and {@code allIdleTime} - */ - public IdleStateHandler(boolean observeOutput, - long readerIdleTime, long writerIdleTime, long allIdleTime, - TimeUnit unit) { - requireNonNull(unit, "unit"); - - this.observeOutput = observeOutput; - - if (readerIdleTime <= 0) { - readerIdleTimeNanos = 0; - } else { - readerIdleTimeNanos = Math.max(unit.toNanos(readerIdleTime), MIN_TIMEOUT_NANOS); - } - if (writerIdleTime <= 0) { - writerIdleTimeNanos = 0; - } else { - writerIdleTimeNanos = Math.max(unit.toNanos(writerIdleTime), MIN_TIMEOUT_NANOS); - } - if (allIdleTime <= 0) { - allIdleTimeNanos = 0; - } else { - allIdleTimeNanos = Math.max(unit.toNanos(allIdleTime), MIN_TIMEOUT_NANOS); - } - } - - /** - * Return the readerIdleTime that was given when instance this class in milliseconds. - * - */ - public long getReaderIdleTimeInMillis() { - return TimeUnit.NANOSECONDS.toMillis(readerIdleTimeNanos); - } - - /** - * Return the writerIdleTime that was given when instance this class in milliseconds. - * - */ - public long getWriterIdleTimeInMillis() { - return TimeUnit.NANOSECONDS.toMillis(writerIdleTimeNanos); - } - - /** - * Return the allIdleTime that was given when instance this class in milliseconds. - * - */ - public long getAllIdleTimeInMillis() { - return TimeUnit.NANOSECONDS.toMillis(allIdleTimeNanos); - } - - @Override - public void handlerAdded(ChannelHandlerContext ctx) throws Exception { - if (ctx.channel().isActive() && ctx.channel().isRegistered()) { - // channelActive() event has been fired already, which means this.channelActive() will - // not be invoked. We have to initialize here instead. - initialize(ctx); - } else { - // channelActive() event has not been fired yet. this.channelActive() will be invoked - // and initialization will occur there. - } - } - - @Override - public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { - destroy(); - } - - @Override - public void channelRegistered(ChannelHandlerContext ctx) throws Exception { - // Initialize early if channel is active already. - if (ctx.channel().isActive()) { - initialize(ctx); - } - ctx.fireChannelRegistered(); - } - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - // This method will be invoked only if this handler was added - // before channelActive() event is fired. If a user adds this handler - // after the channelActive() event, initialize() will be called by beforeAdd(). - initialize(ctx); - ctx.fireChannelActive(); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - destroy(); - ctx.fireChannelInactive(); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - if (readerIdleTimeNanos > 0 || allIdleTimeNanos > 0) { - reading = true; - firstReaderIdleEvent = firstAllIdleEvent = true; - } - ctx.fireChannelRead(msg); - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { - if ((readerIdleTimeNanos > 0 || allIdleTimeNanos > 0) && reading) { - lastReadTime = ticksInNanos(); - reading = false; - } - ctx.fireChannelReadComplete(); - } - - @Override - public Future write(ChannelHandlerContext ctx, Object msg) { - Future future = ctx.write(msg); - // Allow writing with void promise if handler is only configured for read timeout events. - if (writerIdleTimeNanos > 0 || allIdleTimeNanos > 0) { - future.addListener(writeListener); - } - return future; - } - - private void initialize(ChannelHandlerContext ctx) { - // Avoid the case where destroy() is called before scheduling timeouts. - // See: https://github.com/netty/netty/issues/143 - switch (state) { - case 1: - case 2: - return; - default: - break; - } - - state = 1; - initOutputChanged(ctx); - - lastReadTime = lastWriteTime = ticksInNanos(); - if (readerIdleTimeNanos > 0) { - readerIdleTimeout = schedule(ctx, new ReaderIdleTimeoutTask(ctx), - readerIdleTimeNanos, TimeUnit.NANOSECONDS); - } - if (writerIdleTimeNanos > 0) { - writerIdleTimeout = schedule(ctx, new WriterIdleTimeoutTask(ctx), - writerIdleTimeNanos, TimeUnit.NANOSECONDS); - } - if (allIdleTimeNanos > 0) { - allIdleTimeout = schedule(ctx, new AllIdleTimeoutTask(ctx), - allIdleTimeNanos, TimeUnit.NANOSECONDS); - } - } - - /** - * This method is visible for testing! - */ - long ticksInNanos() { - return System.nanoTime(); - } - - /** - * This method is visible for testing! - */ - Future schedule(ChannelHandlerContext ctx, Runnable task, long delay, TimeUnit unit) { - return ctx.executor().schedule(task, delay, unit); - } - - private void destroy() { - state = 2; - - if (readerIdleTimeout != null) { - readerIdleTimeout.cancel(); - readerIdleTimeout = null; - } - if (writerIdleTimeout != null) { - writerIdleTimeout.cancel(); - writerIdleTimeout = null; - } - if (allIdleTimeout != null) { - allIdleTimeout.cancel(); - allIdleTimeout = null; - } - } - - /** - * Is called when an {@link IdleStateEvent} should be fired. This implementation calls - * {@link ChannelHandlerContext#fireUserEventTriggered(Object)}. - */ - protected void channelIdle(ChannelHandlerContext ctx, IdleStateEvent evt) throws Exception { - ctx.fireUserEventTriggered(evt); - } - - /** - * Returns a {@link IdleStateEvent}. - */ - protected IdleStateEvent newIdleStateEvent(IdleState state, boolean first) { - switch (state) { - case ALL_IDLE: - return first ? IdleStateEvent.FIRST_ALL_IDLE_STATE_EVENT : IdleStateEvent.ALL_IDLE_STATE_EVENT; - case READER_IDLE: - return first ? IdleStateEvent.FIRST_READER_IDLE_STATE_EVENT : IdleStateEvent.READER_IDLE_STATE_EVENT; - case WRITER_IDLE: - return first ? IdleStateEvent.FIRST_WRITER_IDLE_STATE_EVENT : IdleStateEvent.WRITER_IDLE_STATE_EVENT; - default: - throw new IllegalArgumentException("Unhandled: state=" + state + ", first=" + first); - } - } - - /** - * @see #hasOutputChanged(ChannelHandlerContext, boolean) - */ - private void initOutputChanged(ChannelHandlerContext ctx) { - if (observeOutput) { - Channel channel = ctx.channel(); - Unsafe unsafe = channel.unsafe(); - ChannelOutboundBuffer buf = unsafe.outboundBuffer(); - - if (buf != null) { - lastMessageHashCode = System.identityHashCode(buf.current()); - lastPendingWriteBytes = buf.totalPendingWriteBytes(); - lastFlushProgress = buf.currentProgress(); - } - } - } - - /** - * Returns {@code true} if and only if the {@link IdleStateHandler} was constructed - * with {@link #observeOutput} enabled and there has been an observed change in the - * {@link ChannelOutboundBuffer} between two consecutive calls of this method. - * - * https://github.com/netty/netty/issues/6150 - */ - private boolean hasOutputChanged(ChannelHandlerContext ctx, boolean first) { - if (observeOutput) { - - // We can take this shortcut if the ChannelPromises that got passed into write() - // appear to complete. It indicates "change" on message level and we simply assume - // that there's change happening on byte level. If the user doesn't observe channel - // writability events then they'll eventually OOME and there's clearly a different - // problem and idleness is least of their concerns. - if (lastChangeCheckTimeStamp != lastWriteTime) { - lastChangeCheckTimeStamp = lastWriteTime; - - // But this applies only if it's the non-first call. - if (!first) { - return true; - } - } - - Channel channel = ctx.channel(); - Unsafe unsafe = channel.unsafe(); - ChannelOutboundBuffer buf = unsafe.outboundBuffer(); - - if (buf != null) { - int messageHashCode = System.identityHashCode(buf.current()); - long pendingWriteBytes = buf.totalPendingWriteBytes(); - - if (messageHashCode != lastMessageHashCode || pendingWriteBytes != lastPendingWriteBytes) { - lastMessageHashCode = messageHashCode; - lastPendingWriteBytes = pendingWriteBytes; - - if (!first) { - return true; - } - } - - long flushProgress = buf.currentProgress(); - if (flushProgress != lastFlushProgress) { - lastFlushProgress = flushProgress; - - if (!first) { - return true; - } - } - } - } - - return false; - } - - private abstract static class AbstractIdleTask implements Runnable { - - private final ChannelHandlerContext ctx; - - AbstractIdleTask(ChannelHandlerContext ctx) { - this.ctx = ctx; - } - - @Override - public void run() { - if (!ctx.channel().isOpen()) { - return; - } - - run(ctx); - } - - protected abstract void run(ChannelHandlerContext ctx); - } - - private final class ReaderIdleTimeoutTask extends AbstractIdleTask { - - ReaderIdleTimeoutTask(ChannelHandlerContext ctx) { - super(ctx); - } - - @Override - protected void run(ChannelHandlerContext ctx) { - long nextDelay = readerIdleTimeNanos; - if (!reading) { - nextDelay -= ticksInNanos() - lastReadTime; - } - - if (nextDelay <= 0) { - // Reader is idle - set a new timeout and notify the callback. - readerIdleTimeout = schedule(ctx, this, readerIdleTimeNanos, TimeUnit.NANOSECONDS); - - boolean first = firstReaderIdleEvent; - firstReaderIdleEvent = false; - - try { - IdleStateEvent event = newIdleStateEvent(IdleState.READER_IDLE, first); - channelIdle(ctx, event); - } catch (Throwable t) { - ctx.fireExceptionCaught(t); - } - } else { - // Read occurred before the timeout - set a new timeout with shorter delay. - readerIdleTimeout = schedule(ctx, this, nextDelay, TimeUnit.NANOSECONDS); - } - } - } - - private final class WriterIdleTimeoutTask extends AbstractIdleTask { - - WriterIdleTimeoutTask(ChannelHandlerContext ctx) { - super(ctx); - } - - @Override - protected void run(ChannelHandlerContext ctx) { - - long lastWriteTime = IdleStateHandler.this.lastWriteTime; - long nextDelay = writerIdleTimeNanos - (ticksInNanos() - lastWriteTime); - if (nextDelay <= 0) { - // Writer is idle - set a new timeout and notify the callback. - writerIdleTimeout = schedule(ctx, this, writerIdleTimeNanos, TimeUnit.NANOSECONDS); - - boolean first = firstWriterIdleEvent; - firstWriterIdleEvent = false; - - try { - if (hasOutputChanged(ctx, first)) { - return; - } - - IdleStateEvent event = newIdleStateEvent(IdleState.WRITER_IDLE, first); - channelIdle(ctx, event); - } catch (Throwable t) { - ctx.fireExceptionCaught(t); - } - } else { - // Write occurred before the timeout - set a new timeout with shorter delay. - writerIdleTimeout = schedule(ctx, this, nextDelay, TimeUnit.NANOSECONDS); - } - } - } - - private final class AllIdleTimeoutTask extends AbstractIdleTask { - - AllIdleTimeoutTask(ChannelHandlerContext ctx) { - super(ctx); - } - - @Override - protected void run(ChannelHandlerContext ctx) { - - long nextDelay = allIdleTimeNanos; - if (!reading) { - nextDelay -= ticksInNanos() - Math.max(lastReadTime, lastWriteTime); - } - if (nextDelay <= 0) { - // Both reader and writer are idle - set a new timeout and - // notify the callback. - allIdleTimeout = schedule(ctx, this, allIdleTimeNanos, TimeUnit.NANOSECONDS); - - boolean first = firstAllIdleEvent; - firstAllIdleEvent = false; - - try { - if (hasOutputChanged(ctx, first)) { - return; - } - - IdleStateEvent event = newIdleStateEvent(IdleState.ALL_IDLE, first); - channelIdle(ctx, event); - } catch (Throwable t) { - ctx.fireExceptionCaught(t); - } - } else { - // Either read or write occurred before the timeout - set a new - // timeout with shorter delay. - allIdleTimeout = schedule(ctx, this, nextDelay, TimeUnit.NANOSECONDS); - } - } - } -} diff --git a/handler/src/main/java/io/netty/handler/timeout/ReadTimeoutException.java b/handler/src/main/java/io/netty/handler/timeout/ReadTimeoutException.java deleted file mode 100644 index 3a3ecbe698..0000000000 --- a/handler/src/main/java/io/netty/handler/timeout/ReadTimeoutException.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.timeout; - -/** - * A {@link TimeoutException} raised by {@link ReadTimeoutHandler} when no data - * was read within a certain period of time. - */ -public final class ReadTimeoutException extends TimeoutException { - - private static final long serialVersionUID = 169287984113283421L; - - public static final ReadTimeoutException INSTANCE = new ReadTimeoutException(); - - private ReadTimeoutException() { - super(true); - } -} diff --git a/handler/src/main/java/io/netty/handler/timeout/ReadTimeoutHandler.java b/handler/src/main/java/io/netty/handler/timeout/ReadTimeoutHandler.java deleted file mode 100644 index 3c349b8bb7..0000000000 --- a/handler/src/main/java/io/netty/handler/timeout/ReadTimeoutHandler.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.timeout; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; - -import java.util.concurrent.TimeUnit; - -/** - * Raises a {@link ReadTimeoutException} when no data was read within a certain - * period of time. - * - *
- * // The connection is closed when there is no inbound traffic
- * // for 30 seconds.
- *
- * 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("myHandler", new MyHandler());
- *     }
- * }
- *
- * // Handler should handle the {@link ReadTimeoutException}.
- * public class MyHandler implements {@link ChannelHandler} {
- *     {@code @Override}
- *     public void exceptionCaught({@link ChannelHandlerContext} ctx, {@link Throwable} cause)
- *             throws {@link Exception} {
- *         if (cause instanceof {@link ReadTimeoutException}) {
- *             // do something
- *         } else {
- *             super.exceptionCaught(ctx, cause);
- *         }
- *     }
- * }
- *
- * {@link ServerBootstrap} bootstrap = ...;
- * ...
- * bootstrap.childHandler(new MyChannelInitializer());
- * ...
- * 
- * @see WriteTimeoutHandler - * @see IdleStateHandler - */ -public class ReadTimeoutHandler extends IdleStateHandler { - private boolean closed; - - /** - * Creates a new instance. - * - * @param timeoutSeconds - * read timeout in seconds - */ - public ReadTimeoutHandler(int timeoutSeconds) { - this(timeoutSeconds, TimeUnit.SECONDS); - } - - /** - * Creates a new instance. - * - * @param timeout - * read timeout - * @param unit - * the {@link TimeUnit} of {@code timeout} - */ - public ReadTimeoutHandler(long timeout, TimeUnit unit) { - super(timeout, 0, 0, unit); - } - - @Override - protected final void channelIdle(ChannelHandlerContext ctx, IdleStateEvent evt) throws Exception { - assert evt.state() == IdleState.READER_IDLE; - readTimedOut(ctx); - } - - /** - * Is called when a read timeout was detected. - */ - protected void readTimedOut(ChannelHandlerContext ctx) throws Exception { - if (!closed) { - ctx.fireExceptionCaught(ReadTimeoutException.INSTANCE); - ctx.close(); - closed = true; - } - } -} diff --git a/handler/src/main/java/io/netty/handler/timeout/TimeoutException.java b/handler/src/main/java/io/netty/handler/timeout/TimeoutException.java deleted file mode 100644 index 9cb9f74697..0000000000 --- a/handler/src/main/java/io/netty/handler/timeout/TimeoutException.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.timeout; - -import io.netty.channel.ChannelException; - -/** - * A {@link TimeoutException} when no data was either read or written within a - * certain period of time. - */ -public class TimeoutException extends ChannelException { - - private static final long serialVersionUID = 4673641882869672533L; - - TimeoutException() { - } - - TimeoutException(boolean shared) { - super(null, null, shared); - } -} diff --git a/handler/src/main/java/io/netty/handler/timeout/WriteTimeoutException.java b/handler/src/main/java/io/netty/handler/timeout/WriteTimeoutException.java deleted file mode 100644 index d2afc46eb9..0000000000 --- a/handler/src/main/java/io/netty/handler/timeout/WriteTimeoutException.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.timeout; - -/** - * A {@link TimeoutException} raised by {@link WriteTimeoutHandler} when a write operation - * cannot finish in a certain period of time. - */ -public final class WriteTimeoutException extends TimeoutException { - - private static final long serialVersionUID = -144786655770296065L; - - public static final WriteTimeoutException INSTANCE = new WriteTimeoutException(); - - private WriteTimeoutException() { - super(true); - } - -} diff --git a/handler/src/main/java/io/netty/handler/timeout/WriteTimeoutHandler.java b/handler/src/main/java/io/netty/handler/timeout/WriteTimeoutHandler.java deleted file mode 100644 index d8ceaaa599..0000000000 --- a/handler/src/main/java/io/netty/handler/timeout/WriteTimeoutHandler.java +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.timeout; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.FutureListener; - -import java.util.concurrent.TimeUnit; - -import static java.util.Objects.requireNonNull; - -/** - * Raises a {@link WriteTimeoutException} when a write operation cannot finish in a certain period of time. - * - *
- * // The connection is closed when a write operation cannot finish in 30 seconds.
- *
- * public class MyChannelInitializer extends {@link ChannelInitializer}<{@link Channel}> {
- *     public void initChannel({@link Channel} channel) {
- *         channel.pipeline().addLast("writeTimeoutHandler", new {@link WriteTimeoutHandler}(30);
- *         channel.pipeline().addLast("myHandler", new MyHandler());
- *     }
- * }
- *
- * // Handler should handle the {@link WriteTimeoutException}.
- * public class MyHandler implements {@link ChannelHandler} {
- *     {@code @Override}
- *     public void exceptionCaught({@link ChannelHandlerContext} ctx, {@link Throwable} cause)
- *             throws {@link Exception} {
- *         if (cause instanceof {@link WriteTimeoutException}) {
- *             // do something
- *         } else {
- *             super.exceptionCaught(ctx, cause);
- *         }
- *     }
- * }
- *
- * {@link ServerBootstrap} bootstrap = ...;
- * ...
- * bootstrap.childHandler(new MyChannelInitializer());
- * ...
- * 
- * @see ReadTimeoutHandler - * @see IdleStateHandler - */ -public class WriteTimeoutHandler implements ChannelHandler { - private static final long MIN_TIMEOUT_NANOS = TimeUnit.MILLISECONDS.toNanos(1); - - private final long timeoutNanos; - - /** - * A doubly-linked list to track all WriteTimeoutTasks - */ - private WriteTimeoutTask lastTask; - - private boolean closed; - - /** - * Creates a new instance. - * - * @param timeoutSeconds - * write timeout in seconds - */ - public WriteTimeoutHandler(int timeoutSeconds) { - this(timeoutSeconds, TimeUnit.SECONDS); - } - - /** - * Creates a new instance. - * - * @param timeout - * write timeout - * @param unit - * the {@link TimeUnit} of {@code timeout} - */ - public WriteTimeoutHandler(long timeout, TimeUnit unit) { - requireNonNull(unit, "unit"); - - if (timeout <= 0) { - timeoutNanos = 0; - } else { - timeoutNanos = Math.max(unit.toNanos(timeout), MIN_TIMEOUT_NANOS); - } - } - - @Override - public Future write(ChannelHandlerContext ctx, Object msg) { - Future f = ctx.write(msg); - if (timeoutNanos > 0) { - scheduleTimeout(ctx, f); - } - return f; - } - - @Override - public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { - assert ctx.executor().inEventLoop(); - WriteTimeoutTask task = lastTask; - lastTask = null; - while (task != null) { - assert task.ctx.executor().inEventLoop(); - task.scheduledFuture.cancel(); - WriteTimeoutTask prev = task.prev; - task.prev = null; - task.next = null; - task = prev; - } - } - - private void scheduleTimeout(final ChannelHandlerContext ctx, final Future future) { - // Schedule a timeout. - final WriteTimeoutTask task = new WriteTimeoutTask(ctx, future); - task.scheduledFuture = ctx.executor().schedule(task, timeoutNanos, TimeUnit.NANOSECONDS); - - if (!task.scheduledFuture.isDone()) { - addWriteTimeoutTask(task); - - // Cancel the scheduled timeout if the flush promise is complete. - future.addListener(task); - } - } - - private void addWriteTimeoutTask(WriteTimeoutTask task) { - assert task.ctx.executor().inEventLoop(); - if (lastTask != null) { - lastTask.next = task; - task.prev = lastTask; - } - lastTask = task; - } - - private void removeWriteTimeoutTask(WriteTimeoutTask task) { - assert task.ctx.executor().inEventLoop(); - if (task == lastTask) { - // task is the tail of list - assert task.next == null; - lastTask = lastTask.prev; - if (lastTask != null) { - lastTask.next = null; - } - } else if (task.prev == null && task.next == null) { - // Since task is not lastTask, then it has been removed or not been added. - return; - } else if (task.prev == null) { - // task is the head of list and the list has at least 2 nodes - task.next.prev = null; - } else { - task.prev.next = task.next; - task.next.prev = task.prev; - } - task.prev = null; - task.next = null; - } - - /** - * Is called when a write timeout was detected - */ - protected void writeTimedOut(ChannelHandlerContext ctx) throws Exception { - if (!closed) { - ctx.fireExceptionCaught(WriteTimeoutException.INSTANCE); - ctx.close(); - closed = true; - } - } - - private final class WriteTimeoutTask implements Runnable, FutureListener { - - private final ChannelHandlerContext ctx; - private final Future future; - - // WriteTimeoutTask is also a node of a doubly-linked list - WriteTimeoutTask prev; - WriteTimeoutTask next; - - Future scheduledFuture; - WriteTimeoutTask(ChannelHandlerContext ctx, Future future) { - this.ctx = ctx; - this.future = future; - } - - @Override - public void run() { - // Was not written yet so issue a write timeout - // The promise itself will be failed with a ClosedChannelException once the close() was issued - // See https://github.com/netty/netty/issues/2159 - if (!future.isDone()) { - try { - writeTimedOut(ctx); - } catch (Throwable t) { - ctx.fireExceptionCaught(t); - } - } - removeWriteTimeoutTask(this); - } - - @Override - public void operationComplete(Future future) throws Exception { - // scheduledFuture has already be set when reaching here - scheduledFuture.cancel(); - - // Check if its safe to modify the "doubly-linked-list" that we maintain. If its not we will schedule the - // modification so its picked up by the executor.. - if (ctx.executor().inEventLoop()) { - removeWriteTimeoutTask(this); - } else { - // So let's just pass outself to the executor which will then take care of remove this task - // from the doubly-linked list. Schedule ourself is fine as the promise itself is done. - // - // This fixes https://github.com/netty/netty/issues/11053 - assert future.isDone(); - ctx.executor().execute(this); - } - } - } -} diff --git a/handler/src/main/java/io/netty/handler/timeout/package-info.java b/handler/src/main/java/io/netty/handler/timeout/package-info.java deleted file mode 100644 index c403681516..0000000000 --- a/handler/src/main/java/io/netty/handler/timeout/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * Adds support for read and write timeout and idle connection notification - * using a {@link io.netty.util.Timer}. - */ -package io.netty.handler.timeout; diff --git a/handler/src/main/java/io/netty/handler/traffic/AbstractTrafficShapingHandler.java b/handler/src/main/java/io/netty/handler/traffic/AbstractTrafficShapingHandler.java deleted file mode 100644 index bfdcb90530..0000000000 --- a/handler/src/main/java/io/netty/handler/traffic/AbstractTrafficShapingHandler.java +++ /dev/null @@ -1,660 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.traffic; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufConvertible; -import io.netty.buffer.ByteBufHolder; -import io.netty.channel.Channel; -import io.netty.channel.ChannelConfig; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelOutboundBuffer; -import io.netty.channel.FileRegion; -import io.netty.util.Attribute; -import io.netty.util.AttributeKey; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.util.concurrent.TimeUnit; - -import static io.netty.util.internal.ObjectUtil.checkPositive; - -/** - *

AbstractTrafficShapingHandler allows to limit the global bandwidth - * (see {@link GlobalTrafficShapingHandler}) or per session - * bandwidth (see {@link ChannelTrafficShapingHandler}), as traffic shaping. - * It allows you to implement an almost real time monitoring of the bandwidth using - * the monitors from {@link TrafficCounter} that will call back every checkInterval - * the method doAccounting of this handler.

- * - *

If you want for any particular reasons to stop the monitoring (accounting) or to change - * the read/write limit or the check interval, several methods allow that for you:

- *
    - *
  • configure allows you to change read or write limits, or the checkInterval
  • - *
  • getTrafficCounter allows you to have access to the TrafficCounter and so to stop - * or start the monitoring, to change the checkInterval directly, or to have access to its values.
  • - *
- */ -public abstract class AbstractTrafficShapingHandler implements ChannelHandler { - private static final InternalLogger logger = - InternalLoggerFactory.getInstance(AbstractTrafficShapingHandler.class); - /** - * Default delay between two checks: 1s - */ - public static final long DEFAULT_CHECK_INTERVAL = 1000; - - /** - * Default max delay in case of traffic shaping - * (during which no communication will occur). - * Shall be less than TIMEOUT. Here half of "standard" 30s - */ - public static final long DEFAULT_MAX_TIME = 15000; - - /** - * Default max size to not exceed in buffer (write only). - */ - static final long DEFAULT_MAX_SIZE = 4 * 1024 * 1024L; - - /** - * Default minimal time to wait: 10ms - */ - static final long MINIMAL_WAIT = 10; - - /** - * Traffic Counter - */ - protected TrafficCounter trafficCounter; - - /** - * Limit in B/s to apply to write - */ - private volatile long writeLimit; - - /** - * Limit in B/s to apply to read - */ - private volatile long readLimit; - - /** - * Max delay in wait - */ - protected volatile long maxTime = DEFAULT_MAX_TIME; // default 15 s - - /** - * Delay between two performance snapshots - */ - protected volatile long checkInterval = DEFAULT_CHECK_INTERVAL; // default 1 s - - static final AttributeKey READ_SUSPENDED = AttributeKey - .valueOf(AbstractTrafficShapingHandler.class.getName() + ".READ_SUSPENDED"); - static final AttributeKey REOPEN_TASK = AttributeKey.valueOf(AbstractTrafficShapingHandler.class - .getName() + ".REOPEN_TASK"); - - /** - * Max time to delay before proposing to stop writing new objects from next handlers - */ - volatile long maxWriteDelay = 4 * DEFAULT_CHECK_INTERVAL; // default 4 s - /** - * Max size in the list before proposing to stop writing new objects from next handlers - */ - volatile long maxWriteSize = DEFAULT_MAX_SIZE; // default 4MB - - /** - * Rank in UserDefinedWritability (1 for Channel, 2 for Global TrafficShapingHandler). - * Set in final constructor. Must be between 1 and 31 - */ - final int userDefinedWritabilityIndex; - - /** - * Default value for Channel UserDefinedWritability index - */ - static final int CHANNEL_DEFAULT_USER_DEFINED_WRITABILITY_INDEX = 1; - - /** - * Default value for Global UserDefinedWritability index - */ - static final int GLOBAL_DEFAULT_USER_DEFINED_WRITABILITY_INDEX = 2; - - /** - * Default value for GlobalChannel UserDefinedWritability index - */ - static final int GLOBALCHANNEL_DEFAULT_USER_DEFINED_WRITABILITY_INDEX = 3; - - /** - * @param newTrafficCounter - * the TrafficCounter to set - */ - void setTrafficCounter(TrafficCounter newTrafficCounter) { - trafficCounter = newTrafficCounter; - } - - /** - * @return the index to be used by the TrafficShapingHandler to manage the user defined writability. - * For Channel TSH it is defined as {@value #CHANNEL_DEFAULT_USER_DEFINED_WRITABILITY_INDEX}, - * for Global TSH it is defined as {@value #GLOBAL_DEFAULT_USER_DEFINED_WRITABILITY_INDEX}, - * for GlobalChannel TSH it is defined as - * {@value #GLOBALCHANNEL_DEFAULT_USER_DEFINED_WRITABILITY_INDEX}. - */ - protected int userDefinedWritabilityIndex() { - return CHANNEL_DEFAULT_USER_DEFINED_WRITABILITY_INDEX; - } - - /** - * @param writeLimit - * 0 or a limit in bytes/s - * @param readLimit - * 0 or a limit in bytes/s - * @param checkInterval - * The delay between two computations of performances for - * channels or 0 if no stats are to be computed. - * @param maxTime - * The maximum delay to wait in case of traffic excess. - * Must be positive. - */ - protected AbstractTrafficShapingHandler(long writeLimit, long readLimit, long checkInterval, long maxTime) { - this.maxTime = checkPositive(maxTime, "maxTime"); - - userDefinedWritabilityIndex = userDefinedWritabilityIndex(); - this.writeLimit = writeLimit; - this.readLimit = readLimit; - this.checkInterval = checkInterval; - } - - /** - * Constructor using default max time as delay allowed value of {@value #DEFAULT_MAX_TIME} ms. - * @param writeLimit - * 0 or a limit in bytes/s - * @param readLimit - * 0 or a limit in bytes/s - * @param checkInterval - * The delay between two computations of performances for - * channels or 0 if no stats are to be computed. - */ - protected AbstractTrafficShapingHandler(long writeLimit, long readLimit, long checkInterval) { - this(writeLimit, readLimit, checkInterval, DEFAULT_MAX_TIME); - } - - /** - * Constructor using default Check Interval value of {@value #DEFAULT_CHECK_INTERVAL} ms and - * default max time as delay allowed value of {@value #DEFAULT_MAX_TIME} ms. - * - * @param writeLimit - * 0 or a limit in bytes/s - * @param readLimit - * 0 or a limit in bytes/s - */ - protected AbstractTrafficShapingHandler(long writeLimit, long readLimit) { - this(writeLimit, readLimit, DEFAULT_CHECK_INTERVAL, DEFAULT_MAX_TIME); - } - - /** - * Constructor using NO LIMIT, default Check Interval value of {@value #DEFAULT_CHECK_INTERVAL} ms and - * default max time as delay allowed value of {@value #DEFAULT_MAX_TIME} ms. - */ - protected AbstractTrafficShapingHandler() { - this(0, 0, DEFAULT_CHECK_INTERVAL, DEFAULT_MAX_TIME); - } - - /** - * Constructor using NO LIMIT and - * default max time as delay allowed value of {@value #DEFAULT_MAX_TIME} ms. - * - * @param checkInterval - * The delay between two computations of performances for - * channels or 0 if no stats are to be computed. - */ - protected AbstractTrafficShapingHandler(long checkInterval) { - this(0, 0, checkInterval, DEFAULT_MAX_TIME); - } - - /** - * Change the underlying limitations and check interval. - *

Note the change will be taken as best effort, meaning - * that all already scheduled traffics will not be - * changed, but only applied to new traffics.

- *

So the expected usage of this method is to be used not too often, - * accordingly to the traffic shaping configuration.

- * - * @param newWriteLimit The new write limit (in bytes) - * @param newReadLimit The new read limit (in bytes) - * @param newCheckInterval The new check interval (in milliseconds) - */ - public void configure(long newWriteLimit, long newReadLimit, - long newCheckInterval) { - configure(newWriteLimit, newReadLimit); - configure(newCheckInterval); - } - - /** - * Change the underlying limitations. - *

Note the change will be taken as best effort, meaning - * that all already scheduled traffics will not be - * changed, but only applied to new traffics.

- *

So the expected usage of this method is to be used not too often, - * accordingly to the traffic shaping configuration.

- * - * @param newWriteLimit The new write limit (in bytes) - * @param newReadLimit The new read limit (in bytes) - */ - public void configure(long newWriteLimit, long newReadLimit) { - writeLimit = newWriteLimit; - readLimit = newReadLimit; - if (trafficCounter != null) { - trafficCounter.resetAccounting(TrafficCounter.milliSecondFromNano()); - } - } - - /** - * Change the check interval. - * - * @param newCheckInterval The new check interval (in milliseconds) - */ - public void configure(long newCheckInterval) { - checkInterval = newCheckInterval; - if (trafficCounter != null) { - trafficCounter.configure(checkInterval); - } - } - - /** - * @return the writeLimit - */ - public long getWriteLimit() { - return writeLimit; - } - - /** - *

Note the change will be taken as best effort, meaning - * that all already scheduled traffics will not be - * changed, but only applied to new traffics.

- *

So the expected usage of this method is to be used not too often, - * accordingly to the traffic shaping configuration.

- * - * @param writeLimit the writeLimit to set - */ - public void setWriteLimit(long writeLimit) { - this.writeLimit = writeLimit; - if (trafficCounter != null) { - trafficCounter.resetAccounting(TrafficCounter.milliSecondFromNano()); - } - } - - /** - * @return the readLimit - */ - public long getReadLimit() { - return readLimit; - } - - /** - *

Note the change will be taken as best effort, meaning - * that all already scheduled traffics will not be - * changed, but only applied to new traffics.

- *

So the expected usage of this method is to be used not too often, - * accordingly to the traffic shaping configuration.

- * - * @param readLimit the readLimit to set - */ - public void setReadLimit(long readLimit) { - this.readLimit = readLimit; - if (trafficCounter != null) { - trafficCounter.resetAccounting(TrafficCounter.milliSecondFromNano()); - } - } - - /** - * @return the checkInterval - */ - public long getCheckInterval() { - return checkInterval; - } - - /** - * @param checkInterval the interval in ms between each step check to set, default value being 1000 ms. - */ - public void setCheckInterval(long checkInterval) { - this.checkInterval = checkInterval; - if (trafficCounter != null) { - trafficCounter.configure(checkInterval); - } - } - - /** - *

Note the change will be taken as best effort, meaning - * that all already scheduled traffics will not be - * changed, but only applied to new traffics.

- *

So the expected usage of this method is to be used not too often, - * accordingly to the traffic shaping configuration.

- * - * @param maxTime - * Max delay in wait, shall be less than TIME OUT in related protocol. - * Must be positive. - */ - public void setMaxTimeWait(long maxTime) { - this.maxTime = checkPositive(maxTime, "maxTime"); - } - - /** - * @return the max delay in wait to prevent TIME OUT - */ - public long getMaxTimeWait() { - return maxTime; - } - - /** - * @return the maxWriteDelay - */ - public long getMaxWriteDelay() { - return maxWriteDelay; - } - - /** - *

Note the change will be taken as best effort, meaning - * that all already scheduled traffics will not be - * changed, but only applied to new traffics.

- *

So the expected usage of this method is to be used not too often, - * accordingly to the traffic shaping configuration.

- * - * @param maxWriteDelay the maximum Write Delay in ms in the buffer allowed before write suspension is set. - * Must be positive. - */ - public void setMaxWriteDelay(long maxWriteDelay) { - this.maxWriteDelay = checkPositive(maxWriteDelay, "maxWriteDelay"); - } - - /** - * @return the maxWriteSize default being {@value #DEFAULT_MAX_SIZE} bytes. - */ - public long getMaxWriteSize() { - return maxWriteSize; - } - - /** - *

Note that this limit is a best effort on memory limitation to prevent Out Of - * Memory Exception. To ensure it works, the handler generating the write should - * use one of the way provided by Netty to handle the capacity:

- *

- the {@code Channel.isWritable()} property and the corresponding - * {@code channelWritabilityChanged()}

- *

- the {@code Future.addListener(future -> ...)}

- * - * @param maxWriteSize the maximum Write Size allowed in the buffer - * per channel before write suspended is set, - * default being {@value #DEFAULT_MAX_SIZE} bytes. - */ - public void setMaxWriteSize(long maxWriteSize) { - this.maxWriteSize = maxWriteSize; - } - - /** - * Called each time the accounting is computed from the TrafficCounters. - * This method could be used for instance to implement almost real time accounting. - * - * @param counter - * the TrafficCounter that computes its performance - */ - protected void doAccounting(TrafficCounter counter) { - // NOOP by default - } - - /** - * Class to implement setReadable at fix time - */ - static final class ReopenReadTimerTask implements Runnable { - final ChannelHandlerContext ctx; - ReopenReadTimerTask(ChannelHandlerContext ctx) { - this.ctx = ctx; - } - - @Override - public void run() { - Channel channel = ctx.channel(); - ChannelConfig config = channel.config(); - if (!config.isAutoRead() && isHandlerActive(ctx)) { - // If AutoRead is False and Active is True, user make a direct setAutoRead(false) - // Then Just reset the status - if (logger.isDebugEnabled()) { - logger.debug("Not unsuspend: " + config.isAutoRead() + ':' + - isHandlerActive(ctx)); - } - channel.attr(READ_SUSPENDED).set(false); - } else { - // Anything else allows the handler to reset the AutoRead - if (logger.isDebugEnabled()) { - if (config.isAutoRead() && !isHandlerActive(ctx)) { - if (logger.isDebugEnabled()) { - logger.debug("Unsuspend: " + config.isAutoRead() + ':' + - isHandlerActive(ctx)); - } - } else { - if (logger.isDebugEnabled()) { - logger.debug("Normal unsuspend: " + config.isAutoRead() + ':' - + isHandlerActive(ctx)); - } - } - } - channel.attr(READ_SUSPENDED).set(false); - config.setAutoRead(true); - channel.read(); - } - if (logger.isDebugEnabled()) { - logger.debug("Unsuspend final status => " + config.isAutoRead() + ':' - + isHandlerActive(ctx)); - } - } - } - - /** - * Release the Read suspension - */ - void releaseReadSuspended(ChannelHandlerContext ctx) { - Channel channel = ctx.channel(); - channel.attr(READ_SUSPENDED).set(false); - channel.config().setAutoRead(true); - } - - @Override - public void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception { - long size = calculateSize(msg); - long now = TrafficCounter.milliSecondFromNano(); - if (size > 0) { - // compute the number of ms to wait before reopening the channel - long wait = trafficCounter.readTimeToWait(size, readLimit, maxTime, now); - wait = checkWaitReadTime(ctx, wait, now); - if (wait >= MINIMAL_WAIT) { // At least 10ms seems a minimal - // time in order to try to limit the traffic - // Only AutoRead AND HandlerActive True means Context Active - Channel channel = ctx.channel(); - ChannelConfig config = channel.config(); - if (logger.isDebugEnabled()) { - logger.debug("Read suspend: " + wait + ':' + config.isAutoRead() + ':' - + isHandlerActive(ctx)); - } - if (config.isAutoRead() && isHandlerActive(ctx)) { - config.setAutoRead(false); - channel.attr(READ_SUSPENDED).set(true); - // Create a Runnable to reactive the read if needed. If one was create before it will just be - // reused to limit object creation - Attribute attr = channel.attr(REOPEN_TASK); - Runnable reopenTask = attr.get(); - if (reopenTask == null) { - reopenTask = new ReopenReadTimerTask(ctx); - attr.set(reopenTask); - } - ctx.executor().schedule(reopenTask, wait, TimeUnit.MILLISECONDS); - if (logger.isDebugEnabled()) { - logger.debug("Suspend final status => " + config.isAutoRead() + ':' - + isHandlerActive(ctx) + " will reopened at: " + wait); - } - } - } - } - informReadOperation(ctx, now); - ctx.fireChannelRead(msg); - } - - @Override - public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { - Channel channel = ctx.channel(); - if (channel.hasAttr(REOPEN_TASK)) { - //release the reopen task - channel.attr(REOPEN_TASK).set(null); - } - } - - /** - * Method overridden in GTSH to take into account specific timer for the channel. - * @param wait the wait delay computed in ms - * @param now the relative now time in ms - * @return the wait to use according to the context - */ - long checkWaitReadTime(final ChannelHandlerContext ctx, long wait, final long now) { - // no change by default - return wait; - } - - /** - * Method overridden in GTSH to take into account specific timer for the channel. - * @param now the relative now time in ms - */ - void informReadOperation(final ChannelHandlerContext ctx, final long now) { - // default noop - } - - protected static boolean isHandlerActive(ChannelHandlerContext ctx) { - Boolean suspended = ctx.channel().attr(READ_SUSPENDED).get(); - return suspended == null || Boolean.FALSE.equals(suspended); - } - - @Override - public void read(ChannelHandlerContext ctx) { - if (isHandlerActive(ctx)) { - // For Global Traffic (and Read when using EventLoop in pipeline) : check if READ_SUSPENDED is False - ctx.read(); - } - } - - @Override - public Future write(final ChannelHandlerContext ctx, final Object msg) { - long size = calculateSize(msg); - long now = TrafficCounter.milliSecondFromNano(); - Promise promise = ctx.newPromise(); - if (size > 0) { - // compute the number of ms to wait before continue with the channel - long wait = trafficCounter.writeTimeToWait(size, writeLimit, maxTime, now); - if (wait >= MINIMAL_WAIT) { - if (logger.isDebugEnabled()) { - logger.debug("Write suspend: " + wait + ':' + ctx.channel().config().isAutoRead() + ':' - + isHandlerActive(ctx)); - } - submitWrite(ctx, msg, size, wait, now, promise); - return promise.asFuture(); - } - } - // to maintain order of write - submitWrite(ctx, msg, size, 0, now, promise); - return promise.asFuture(); - } - - @Deprecated - protected void submitWrite(final ChannelHandlerContext ctx, final Object msg, - final long delay, final Promise promise) { - submitWrite(ctx, msg, calculateSize(msg), - delay, TrafficCounter.milliSecondFromNano(), promise); - } - - abstract void submitWrite( - ChannelHandlerContext ctx, Object msg, long size, long delay, long now, Promise promise); - - @Override - public void channelRegistered(ChannelHandlerContext ctx) throws Exception { - setUserDefinedWritability(ctx, true); - ctx.fireChannelRegistered(); - } - - void setUserDefinedWritability(ChannelHandlerContext ctx, boolean writable) { - ChannelOutboundBuffer cob = ctx.channel().unsafe().outboundBuffer(); - if (cob != null) { - cob.setUserDefinedWritability(userDefinedWritabilityIndex, writable); - } - } - - /** - * Check the writability according to delay and size for the channel. - * Set if necessary setUserDefinedWritability status. - * @param delay the computed delay - * @param queueSize the current queueSize - */ - void checkWriteSuspend(ChannelHandlerContext ctx, long delay, long queueSize) { - if (queueSize > maxWriteSize || delay > maxWriteDelay) { - setUserDefinedWritability(ctx, false); - } - } - /** - * Explicitly release the Write suspended status. - */ - void releaseWriteSuspended(ChannelHandlerContext ctx) { - setUserDefinedWritability(ctx, true); - } - - /** - * @return the current TrafficCounter (if - * channel is still connected) - */ - public TrafficCounter trafficCounter() { - return trafficCounter; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(290) - .append("TrafficShaping with Write Limit: ").append(writeLimit) - .append(" Read Limit: ").append(readLimit) - .append(" CheckInterval: ").append(checkInterval) - .append(" maxDelay: ").append(maxWriteDelay) - .append(" maxSize: ").append(maxWriteSize) - .append(" and Counter: "); - if (trafficCounter != null) { - builder.append(trafficCounter); - } else { - builder.append("none"); - } - return builder.toString(); - } - - /** - * Calculate the size of the given {@link Object}. - * - * This implementation supports {@link ByteBuf}, {@link ByteBufHolder} and {@link FileRegion}. - * Sub-classes may override this. - * @param msg the msg for which the size should be calculated. - * @return size the size of the msg or {@code -1} if unknown. - */ - protected long calculateSize(Object msg) { - if (msg instanceof ByteBufConvertible) { - return ((ByteBufConvertible) msg).asByteBuf().readableBytes(); - } - if (msg instanceof ByteBufHolder) { - return ((ByteBufHolder) msg).content().readableBytes(); - } - if (msg instanceof FileRegion) { - return ((FileRegion) msg).count(); - } - return -1; - } -} diff --git a/handler/src/main/java/io/netty/handler/traffic/ChannelTrafficShapingHandler.java b/handler/src/main/java/io/netty/handler/traffic/ChannelTrafficShapingHandler.java deleted file mode 100644 index 5ebd59ad86..0000000000 --- a/handler/src/main/java/io/netty/handler/traffic/ChannelTrafficShapingHandler.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.traffic; - -import io.netty.buffer.ByteBufConvertible; -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.concurrent.Promise; - -import java.util.ArrayDeque; -import java.util.concurrent.TimeUnit; - -/** - *

This implementation of the {@link AbstractTrafficShapingHandler} is for channel - * traffic shaping, that is to say a per channel limitation of the bandwidth.

- *

Note the index used in {@code OutboundBuffer.setUserDefinedWritability(index, boolean)} is 1.

- * - *

The general use should be as follow:

- *
    - *
  • Add in your pipeline a new ChannelTrafficShapingHandler.

    - *

    ChannelTrafficShapingHandler myHandler = new ChannelTrafficShapingHandler();

    - *

    pipeline.addLast(myHandler);

    - * - *

    Note that this handler has a Pipeline Coverage of "one" which means a new handler must be created - * for each new channel as the counter cannot be shared among all channels..

    - * - *

    Other arguments can be passed like write or read limitation (in bytes/s where 0 means no limitation) - * or the check interval (in millisecond) that represents the delay between two computations of the - * bandwidth and so the call back of the doAccounting method (0 means no accounting at all).

    - * - *

    A value of 0 means no accounting for checkInterval. If you need traffic shaping but no such accounting, - * it is recommended to set a positive value, even if it is high since the precision of the - * Traffic Shaping depends on the period where the traffic is computed. The highest the interval, - * the less precise the traffic shaping will be. It is suggested as higher value something close - * to 5 or 10 minutes.

    - * - *

    maxTimeToWait, by default set to 15s, allows to specify an upper bound of time shaping.

    - *
  • - *
  • In your handler, you should consider to use the {@code channel.isWritable()} and - * {@code channelWritabilityChanged(ctx)} to handle writability, or through - * {@code future.addListener(future -> ...)} on the future returned by - * {@code ctx.write()}.
  • - *
  • You shall also consider to have object size in read or write operations relatively adapted to - * the bandwidth you required: for instance having 10 MB objects for 10KB/s will lead to burst effect, - * while having 100 KB objects for 1 MB/s should be smoothly handle by this TrafficShaping handler.

  • - *
  • Some configuration methods will be taken as best effort, meaning - * that all already scheduled traffics will not be - * changed, but only applied to new traffics.

    - *

    So the expected usage of those methods are to be used not too often, - * accordingly to the traffic shaping configuration.

  • - *
- */ -public class ChannelTrafficShapingHandler extends AbstractTrafficShapingHandler { - private final ArrayDeque messagesQueue = new ArrayDeque<>(); - private long queueSize; - - /** - * Create a new instance. - * - * @param writeLimit - * 0 or a limit in bytes/s - * @param readLimit - * 0 or a limit in bytes/s - * @param checkInterval - * The delay between two computations of performances for - * channels or 0 if no stats are to be computed. - * @param maxTime - * The maximum delay to wait in case of traffic excess. - */ - public ChannelTrafficShapingHandler(long writeLimit, long readLimit, - long checkInterval, long maxTime) { - super(writeLimit, readLimit, checkInterval, maxTime); - } - - /** - * Create a new instance using default - * max time as delay allowed value of 15000 ms. - * - * @param writeLimit - * 0 or a limit in bytes/s - * @param readLimit - * 0 or a limit in bytes/s - * @param checkInterval - * The delay between two computations of performances for - * channels or 0 if no stats are to be computed. - */ - public ChannelTrafficShapingHandler(long writeLimit, - long readLimit, long checkInterval) { - super(writeLimit, readLimit, checkInterval); - } - - /** - * Create a new instance using default Check Interval value of 1000 ms and - * max time as delay allowed value of 15000 ms. - * - * @param writeLimit - * 0 or a limit in bytes/s - * @param readLimit - * 0 or a limit in bytes/s - */ - public ChannelTrafficShapingHandler(long writeLimit, - long readLimit) { - super(writeLimit, readLimit); - } - - /** - * Create a new instance using - * default max time as delay allowed value of 15000 ms and no limit. - * - * @param checkInterval - * The delay between two computations of performances for - * channels or 0 if no stats are to be computed. - */ - public ChannelTrafficShapingHandler(long checkInterval) { - super(checkInterval); - } - - @Override - public void handlerAdded(ChannelHandlerContext ctx) throws Exception { - TrafficCounter trafficCounter = new TrafficCounter(this, ctx.executor(), "ChannelTC" + - ctx.channel().hashCode(), checkInterval); - setTrafficCounter(trafficCounter); - trafficCounter.start(); - super.handlerAdded(ctx); - } - - @Override - public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { - trafficCounter.stop(); - // write order control - synchronized (this) { - if (ctx.channel().isActive()) { - for (ToSend toSend : messagesQueue) { - long size = calculateSize(toSend.toSend); - trafficCounter.bytesRealWriteFlowControl(size); - queueSize -= size; - ctx.write(toSend.toSend).cascadeTo(toSend.promise); - } - } else { - for (ToSend toSend : messagesQueue) { - if (toSend.toSend instanceof ByteBufConvertible) { - ((ByteBufConvertible) toSend.toSend).asByteBuf().release(); - } - } - } - messagesQueue.clear(); - } - releaseWriteSuspended(ctx); - releaseReadSuspended(ctx); - super.handlerRemoved(ctx); - } - - private static final class ToSend { - final long relativeTimeAction; - final Object toSend; - final Promise promise; - - private ToSend(final long delay, final Object toSend, final Promise promise) { - relativeTimeAction = delay; - this.toSend = toSend; - this.promise = promise; - } - } - - @Override - void submitWrite(final ChannelHandlerContext ctx, final Object msg, - final long size, final long delay, final long now, - final Promise promise) { - final ToSend newToSend; - // write order control - synchronized (this) { - if (delay == 0 && messagesQueue.isEmpty()) { - trafficCounter.bytesRealWriteFlowControl(size); - ctx.write(msg).cascadeTo(promise); - return; - } - newToSend = new ToSend(delay + now, msg, promise); - messagesQueue.addLast(newToSend); - queueSize += size; - checkWriteSuspend(ctx, delay, queueSize); - } - final long futureNow = newToSend.relativeTimeAction; - ctx.executor().schedule(() -> sendAllValid(ctx, futureNow), delay, TimeUnit.MILLISECONDS); - } - - private void sendAllValid(final ChannelHandlerContext ctx, final long now) { - // write order control - synchronized (this) { - ToSend newToSend = messagesQueue.pollFirst(); - for (; newToSend != null; newToSend = messagesQueue.pollFirst()) { - if (newToSend.relativeTimeAction <= now) { - long size = calculateSize(newToSend.toSend); - trafficCounter.bytesRealWriteFlowControl(size); - queueSize -= size; - ctx.write(newToSend.toSend).cascadeTo(newToSend.promise); - } else { - messagesQueue.addFirst(newToSend); - break; - } - } - if (messagesQueue.isEmpty()) { - releaseWriteSuspended(ctx); - } - } - ctx.flush(); - } - - /** - * @return current size in bytes of the write buffer. - */ - public long queueSize() { - return queueSize; - } -} diff --git a/handler/src/main/java/io/netty/handler/traffic/GlobalChannelTrafficCounter.java b/handler/src/main/java/io/netty/handler/traffic/GlobalChannelTrafficCounter.java deleted file mode 100644 index 0915b6f373..0000000000 --- a/handler/src/main/java/io/netty/handler/traffic/GlobalChannelTrafficCounter.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.traffic; - -import io.netty.handler.traffic.GlobalChannelTrafficShapingHandler.PerChannel; -import io.netty.util.concurrent.EventExecutorGroup; - -import java.util.concurrent.TimeUnit; - -import static io.netty.util.internal.ObjectUtil.checkNotNullWithIAE; - -/** - * Version for {@link GlobalChannelTrafficShapingHandler}. - * This TrafficCounter is the Global one, and its special property is to directly handle - * other channel's TrafficCounters. In particular, there are no scheduler for those - * channel's TrafficCounters because it is managed by this one. - */ -public class GlobalChannelTrafficCounter extends TrafficCounter { - /** - * @param trafficShapingHandler the associated {@link GlobalChannelTrafficShapingHandler}. - * @param executor the underlying executor service for scheduling checks (both Global and per Channel). - * @param name the name given to this monitor. - * @param checkInterval the checkInterval in millisecond between two computations. - */ - public GlobalChannelTrafficCounter(GlobalChannelTrafficShapingHandler trafficShapingHandler, - EventExecutorGroup executor, String name, long checkInterval) { - super(trafficShapingHandler, executor, name, checkInterval); - checkNotNullWithIAE(executor, "executor"); - } - - /** - * Class to implement monitoring at fix delay. - * This version is Mixed in the way it mixes Global and Channel counters. - */ - private static class MixedTrafficMonitoringTask implements Runnable { - /** - * The associated TrafficShapingHandler - */ - private final GlobalChannelTrafficShapingHandler trafficShapingHandler1; - - /** - * The associated TrafficCounter - */ - private final TrafficCounter counter; - - /** - * @param trafficShapingHandler The parent handler to which this task needs to callback to for accounting. - * @param counter The parent TrafficCounter that we need to reset the statistics for. - */ - MixedTrafficMonitoringTask( - GlobalChannelTrafficShapingHandler trafficShapingHandler, - TrafficCounter counter) { - trafficShapingHandler1 = trafficShapingHandler; - this.counter = counter; - } - - @Override - public void run() { - if (!counter.monitorActive) { - return; - } - long newLastTime = milliSecondFromNano(); - counter.resetAccounting(newLastTime); - for (PerChannel perChannel : trafficShapingHandler1.channelQueues.values()) { - perChannel.channelTrafficCounter.resetAccounting(newLastTime); - } - trafficShapingHandler1.doAccounting(counter); - } - } - - /** - * Start the monitoring process. - */ - @Override - public synchronized void start() { - if (monitorActive) { - return; - } - lastTime.set(milliSecondFromNano()); - long localCheckInterval = checkInterval.get(); - if (localCheckInterval > 0) { - monitorActive = true; - monitor = new MixedTrafficMonitoringTask((GlobalChannelTrafficShapingHandler) trafficShapingHandler, this); - scheduledFuture = - executor.scheduleAtFixedRate(monitor, 0, localCheckInterval, TimeUnit.MILLISECONDS); - } - } - - /** - * Stop the monitoring process. - */ - @Override - public synchronized void stop() { - if (!monitorActive) { - return; - } - monitorActive = false; - resetAccounting(milliSecondFromNano()); - trafficShapingHandler.doAccounting(this); - if (scheduledFuture != null) { - scheduledFuture.cancel(); - } - } - - @Override - public void resetCumulativeTime() { - for (PerChannel perChannel : - ((GlobalChannelTrafficShapingHandler) trafficShapingHandler).channelQueues.values()) { - perChannel.channelTrafficCounter.resetCumulativeTime(); - } - super.resetCumulativeTime(); - } - -} diff --git a/handler/src/main/java/io/netty/handler/traffic/GlobalChannelTrafficShapingHandler.java b/handler/src/main/java/io/netty/handler/traffic/GlobalChannelTrafficShapingHandler.java deleted file mode 100644 index faa780845c..0000000000 --- a/handler/src/main/java/io/netty/handler/traffic/GlobalChannelTrafficShapingHandler.java +++ /dev/null @@ -1,771 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.traffic; - -import io.netty.buffer.ByteBufConvertible; -import io.netty.channel.Channel; -import io.netty.channel.ChannelConfig; -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.Attribute; -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.EventExecutorGroup; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.util.AbstractCollection; -import java.util.ArrayDeque; -import java.util.Collection; -import java.util.Iterator; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; - -import static io.netty.util.internal.ObjectUtil.checkNotNullWithIAE; -import static io.netty.util.internal.ObjectUtil.checkPositive; -import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; - -/** - * This implementation of the {@link AbstractTrafficShapingHandler} is for global - * and per channel traffic shaping, that is to say a global limitation of the bandwidth, whatever - * the number of opened channels and a per channel limitation of the bandwidth.

- * This version shall not be in the same pipeline than other TrafficShapingHandler.

- * - * The general use should be as follow:
- *
    - *
  • Create your unique GlobalChannelTrafficShapingHandler like:

    - * GlobalChannelTrafficShapingHandler myHandler = new GlobalChannelTrafficShapingHandler(executor);

    - * The executor could be the underlying IO worker pool
    - * pipeline.addLast(myHandler);

    - * - * Note that this handler has a Pipeline Coverage of "all" which means only one such handler must be created - * and shared among all channels as the counter must be shared among all channels.

    - * - * Other arguments can be passed like write or read limitation (in bytes/s where 0 means no limitation) - * or the check interval (in millisecond) that represents the delay between two computations of the - * bandwidth and so the call back of the doAccounting method (0 means no accounting at all).
    - * Note that as this is a fusion of both Global and Channel Traffic Shaping, limits are in 2 sets, - * respectively Global and Channel.

    - * - * A value of 0 means no accounting for checkInterval. If you need traffic shaping but no such accounting, - * it is recommended to set a positive value, even if it is high since the precision of the - * Traffic Shaping depends on the period where the traffic is computed. The highest the interval, - * the less precise the traffic shaping will be. It is suggested as higher value something close - * to 5 or 10 minutes.

    - * - * maxTimeToWait, by default set to 15s, allows to specify an upper bound of time shaping.

    - *
  • - *
  • In your handler, you should consider to use the {@code channel.isWritable()} and - * {@code channelWritabilityChanged(ctx)} to handle writability, or through - * {@code future.addListener(future -> ...)} on the future returned by - * {@code ctx.write()}.
  • - *
  • You shall also consider to have object size in read or write operations relatively adapted to - * the bandwidth you required: for instance having 10 MB objects for 10KB/s will lead to burst effect, - * while having 100 KB objects for 1 MB/s should be smoothly handle by this TrafficShaping handler.

  • - *
  • Some configuration methods will be taken as best effort, meaning - * that all already scheduled traffics will not be - * changed, but only applied to new traffics.
    - * So the expected usage of those methods are to be used not too often, - * accordingly to the traffic shaping configuration.
  • - *

- * - * Be sure to call {@link #release()} once this handler is not needed anymore to release all internal resources. - * This will not shutdown the {@link EventExecutor} as it may be shared, so you need to do this by your own. - */ -@Sharable -public class GlobalChannelTrafficShapingHandler extends AbstractTrafficShapingHandler { - private static final InternalLogger logger = - InternalLoggerFactory.getInstance(GlobalChannelTrafficShapingHandler.class); - /** - * All queues per channel - */ - final ConcurrentMap channelQueues = new ConcurrentHashMap<>(); - - /** - * Global queues size - */ - private final AtomicLong queuesSize = new AtomicLong(); - - /** - * Maximum cumulative writing bytes for one channel among all (as long as channels stay the same) - */ - private final AtomicLong cumulativeWrittenBytes = new AtomicLong(); - - /** - * Maximum cumulative read bytes for one channel among all (as long as channels stay the same) - */ - private final AtomicLong cumulativeReadBytes = new AtomicLong(); - - /** - * Max size in the list before proposing to stop writing new objects from next handlers - * for all channel (global) - */ - volatile long maxGlobalWriteSize = DEFAULT_MAX_SIZE * 100; // default 400MB - - /** - * Limit in B/s to apply to write - */ - private volatile long writeChannelLimit; - - /** - * Limit in B/s to apply to read - */ - private volatile long readChannelLimit; - - private static final float DEFAULT_DEVIATION = 0.1F; - private static final float MAX_DEVIATION = 0.4F; - private static final float DEFAULT_SLOWDOWN = 0.4F; - private static final float DEFAULT_ACCELERATION = -0.1F; - private volatile float maxDeviation; - private volatile float accelerationFactor; - private volatile float slowDownFactor; - private volatile boolean readDeviationActive; - private volatile boolean writeDeviationActive; - - static final class PerChannel { - ArrayDeque messagesQueue; - TrafficCounter channelTrafficCounter; - long queueSize; - long lastWriteTimestamp; - long lastReadTimestamp; - } - - /** - * Create the global TrafficCounter - */ - void createGlobalTrafficCounter(EventExecutorGroup executor) { - // Default - setMaxDeviation(DEFAULT_DEVIATION, DEFAULT_SLOWDOWN, DEFAULT_ACCELERATION); - checkNotNullWithIAE(executor, "executor"); - TrafficCounter tc = new GlobalChannelTrafficCounter(this, executor, "GlobalChannelTC", checkInterval); - setTrafficCounter(tc); - tc.start(); - } - - @Override - protected int userDefinedWritabilityIndex() { - return AbstractTrafficShapingHandler.GLOBALCHANNEL_DEFAULT_USER_DEFINED_WRITABILITY_INDEX; - } - - /** - * Create a new instance. - * - * @param executor - * the {@link EventExecutorGroup} to use for the {@link TrafficCounter}. - * @param writeGlobalLimit - * 0 or a limit in bytes/s - * @param readGlobalLimit - * 0 or a limit in bytes/s - * @param writeChannelLimit - * 0 or a limit in bytes/s - * @param readChannelLimit - * 0 or a limit in bytes/s - * @param checkInterval - * The delay between two computations of performances for - * channels or 0 if no stats are to be computed. - * @param maxTime - * The maximum delay to wait in case of traffic excess. - */ - public GlobalChannelTrafficShapingHandler(EventExecutorGroup executor, - long writeGlobalLimit, long readGlobalLimit, - long writeChannelLimit, long readChannelLimit, - long checkInterval, long maxTime) { - super(writeGlobalLimit, readGlobalLimit, checkInterval, maxTime); - createGlobalTrafficCounter(executor); - this.writeChannelLimit = writeChannelLimit; - this.readChannelLimit = readChannelLimit; - } - - /** - * Create a new instance. - * - * @param executor - * the {@link EventExecutorGroup} to use for the {@link TrafficCounter}. - * @param writeGlobalLimit - * 0 or a limit in bytes/s - * @param readGlobalLimit - * 0 or a limit in bytes/s - * @param writeChannelLimit - * 0 or a limit in bytes/s - * @param readChannelLimit - * 0 or a limit in bytes/s - * @param checkInterval - * The delay between two computations of performances for - * channels or 0 if no stats are to be computed. - */ - public GlobalChannelTrafficShapingHandler(EventExecutorGroup executor, - long writeGlobalLimit, long readGlobalLimit, - long writeChannelLimit, long readChannelLimit, - long checkInterval) { - super(writeGlobalLimit, readGlobalLimit, checkInterval); - this.writeChannelLimit = writeChannelLimit; - this.readChannelLimit = readChannelLimit; - createGlobalTrafficCounter(executor); - } - - /** - * Create a new instance. - * - * @param executor - * the {@link EventExecutorGroup} to use for the {@link TrafficCounter}. - * @param writeGlobalLimit - * 0 or a limit in bytes/s - * @param readGlobalLimit - * 0 or a limit in bytes/s - * @param writeChannelLimit - * 0 or a limit in bytes/s - * @param readChannelLimit - * 0 or a limit in bytes/s - */ - public GlobalChannelTrafficShapingHandler(EventExecutorGroup executor, - long writeGlobalLimit, long readGlobalLimit, - long writeChannelLimit, long readChannelLimit) { - super(writeGlobalLimit, readGlobalLimit); - this.writeChannelLimit = writeChannelLimit; - this.readChannelLimit = readChannelLimit; - createGlobalTrafficCounter(executor); - } - - /** - * Create a new instance. - * - * @param executor - * the {@link EventExecutorGroup} to use for the {@link TrafficCounter}. - * @param checkInterval - * The delay between two computations of performances for - * channels or 0 if no stats are to be computed. - */ - public GlobalChannelTrafficShapingHandler(EventExecutorGroup executor, long checkInterval) { - super(checkInterval); - createGlobalTrafficCounter(executor); - } - - /** - * Create a new instance. - * - * @param executor - * the {@link EventExecutorGroup} to use for the {@link TrafficCounter}. - */ - public GlobalChannelTrafficShapingHandler(EventExecutorGroup executor) { - createGlobalTrafficCounter(executor); - } - - /** - * @return the current max deviation - */ - public float maxDeviation() { - return maxDeviation; - } - - /** - * @return the current acceleration factor - */ - public float accelerationFactor() { - return accelerationFactor; - } - - /** - * @return the current slow down factor - */ - public float slowDownFactor() { - return slowDownFactor; - } - - /** - * @param maxDeviation - * the maximum deviation to allow during computation of average, default deviation - * being 0.1, so +/-10% of the desired bandwidth. Maximum being 0.4. - * @param slowDownFactor - * the factor set as +x% to the too fast client (minimal value being 0, meaning no - * slow down factor), default being 40% (0.4). - * @param accelerationFactor - * the factor set as -x% to the too slow client (maximal value being 0, meaning no - * acceleration factor), default being -10% (-0.1). - */ - public void setMaxDeviation(float maxDeviation, float slowDownFactor, float accelerationFactor) { - if (maxDeviation > MAX_DEVIATION) { - throw new IllegalArgumentException("maxDeviation must be <= " + MAX_DEVIATION); - } - checkPositiveOrZero(slowDownFactor, "slowDownFactor"); - if (accelerationFactor > 0) { - throw new IllegalArgumentException("accelerationFactor must be <= 0"); - } - this.maxDeviation = maxDeviation; - this.accelerationFactor = 1 + accelerationFactor; - this.slowDownFactor = 1 + slowDownFactor; - } - - private void computeDeviationCumulativeBytes() { - // compute the maximum cumulativeXxxxBytes among still connected Channels - long maxWrittenBytes = 0; - long maxReadBytes = 0; - long minWrittenBytes = Long.MAX_VALUE; - long minReadBytes = Long.MAX_VALUE; - for (PerChannel perChannel : channelQueues.values()) { - long value = perChannel.channelTrafficCounter.cumulativeWrittenBytes(); - if (maxWrittenBytes < value) { - maxWrittenBytes = value; - } - if (minWrittenBytes > value) { - minWrittenBytes = value; - } - value = perChannel.channelTrafficCounter.cumulativeReadBytes(); - if (maxReadBytes < value) { - maxReadBytes = value; - } - if (minReadBytes > value) { - minReadBytes = value; - } - } - boolean multiple = channelQueues.size() > 1; - readDeviationActive = multiple && minReadBytes < maxReadBytes / 2; - writeDeviationActive = multiple && minWrittenBytes < maxWrittenBytes / 2; - cumulativeWrittenBytes.set(maxWrittenBytes); - cumulativeReadBytes.set(maxReadBytes); - } - - @Override - protected void doAccounting(TrafficCounter counter) { - computeDeviationCumulativeBytes(); - super.doAccounting(counter); - } - - private long computeBalancedWait(float maxLocal, float maxGlobal, long wait) { - if (maxGlobal == 0) { - // no change - return wait; - } - float ratio = maxLocal / maxGlobal; - // if in the boundaries, same value - if (ratio > maxDeviation) { - if (ratio < 1 - maxDeviation) { - return wait; - } else { - ratio = slowDownFactor; - if (wait < MINIMAL_WAIT) { - wait = MINIMAL_WAIT; - } - } - } else { - ratio = accelerationFactor; - } - return (long) (wait * ratio); - } - - /** - * @return the maxGlobalWriteSize - */ - public long getMaxGlobalWriteSize() { - return maxGlobalWriteSize; - } - - /** - * Note the change will be taken as best effort, meaning - * that all already scheduled traffics will not be - * changed, but only applied to new traffics.
- * So the expected usage of this method is to be used not too often, - * accordingly to the traffic shaping configuration. - * - * @param maxGlobalWriteSize the maximum Global Write Size allowed in the buffer - * globally for all channels before write suspended is set. - */ - public void setMaxGlobalWriteSize(long maxGlobalWriteSize) { - this.maxGlobalWriteSize = checkPositive(maxGlobalWriteSize, "maxGlobalWriteSize"); - } - - /** - * @return the global size of the buffers for all queues. - */ - public long queuesSize() { - return queuesSize.get(); - } - - /** - * @param newWriteLimit Channel write limit - * @param newReadLimit Channel read limit - */ - public void configureChannel(long newWriteLimit, long newReadLimit) { - writeChannelLimit = newWriteLimit; - readChannelLimit = newReadLimit; - long now = TrafficCounter.milliSecondFromNano(); - for (PerChannel perChannel : channelQueues.values()) { - perChannel.channelTrafficCounter.resetAccounting(now); - } - } - - /** - * @return Channel write limit - */ - public long getWriteChannelLimit() { - return writeChannelLimit; - } - - /** - * @param writeLimit Channel write limit - */ - public void setWriteChannelLimit(long writeLimit) { - writeChannelLimit = writeLimit; - long now = TrafficCounter.milliSecondFromNano(); - for (PerChannel perChannel : channelQueues.values()) { - perChannel.channelTrafficCounter.resetAccounting(now); - } - } - - /** - * @return Channel read limit - */ - public long getReadChannelLimit() { - return readChannelLimit; - } - - /** - * @param readLimit Channel read limit - */ - public void setReadChannelLimit(long readLimit) { - readChannelLimit = readLimit; - long now = TrafficCounter.milliSecondFromNano(); - for (PerChannel perChannel : channelQueues.values()) { - perChannel.channelTrafficCounter.resetAccounting(now); - } - } - - /** - * Release all internal resources of this instance. - */ - public final void release() { - trafficCounter.stop(); - } - - private PerChannel getOrSetPerChannel(ChannelHandlerContext ctx) { - // ensure creation is limited to one thread per channel - Channel channel = ctx.channel(); - Integer key = channel.hashCode(); - PerChannel perChannel = channelQueues.get(key); - if (perChannel == null) { - perChannel = new PerChannel(); - perChannel.messagesQueue = new ArrayDeque<>(); - // Don't start it since managed through the Global one - perChannel.channelTrafficCounter = new TrafficCounter(this, null, "ChannelTC" + - ctx.channel().hashCode(), checkInterval); - perChannel.queueSize = 0L; - perChannel.lastReadTimestamp = TrafficCounter.milliSecondFromNano(); - perChannel.lastWriteTimestamp = perChannel.lastReadTimestamp; - channelQueues.put(key, perChannel); - } - return perChannel; - } - - @Override - public void handlerAdded(ChannelHandlerContext ctx) throws Exception { - getOrSetPerChannel(ctx); - trafficCounter.resetCumulativeTime(); - super.handlerAdded(ctx); - } - - @Override - public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { - trafficCounter.resetCumulativeTime(); - Channel channel = ctx.channel(); - Integer key = channel.hashCode(); - PerChannel perChannel = channelQueues.remove(key); - if (perChannel != null) { - // write operations need synchronization - synchronized (perChannel) { - if (channel.isActive()) { - for (ToSend toSend : perChannel.messagesQueue) { - long size = calculateSize(toSend.toSend); - trafficCounter.bytesRealWriteFlowControl(size); - perChannel.channelTrafficCounter.bytesRealWriteFlowControl(size); - perChannel.queueSize -= size; - queuesSize.addAndGet(-size); - ctx.write(toSend.toSend).cascadeTo(toSend.promise); - } - } else { - queuesSize.addAndGet(-perChannel.queueSize); - for (ToSend toSend : perChannel.messagesQueue) { - if (toSend.toSend instanceof ByteBufConvertible) { - ((ByteBufConvertible) toSend.toSend).asByteBuf().release(); - } - } - } - perChannel.messagesQueue.clear(); - } - } - releaseWriteSuspended(ctx); - releaseReadSuspended(ctx); - super.handlerRemoved(ctx); - } - - @Override - public void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception { - long size = calculateSize(msg); - long now = TrafficCounter.milliSecondFromNano(); - if (size > 0) { - // compute the number of ms to wait before reopening the channel - long waitGlobal = trafficCounter.readTimeToWait(size, getReadLimit(), maxTime, now); - Integer key = ctx.channel().hashCode(); - PerChannel perChannel = channelQueues.get(key); - long wait = 0; - if (perChannel != null) { - wait = perChannel.channelTrafficCounter.readTimeToWait(size, readChannelLimit, maxTime, now); - if (readDeviationActive) { - // now try to balance between the channels - long maxLocalRead; - maxLocalRead = perChannel.channelTrafficCounter.cumulativeReadBytes(); - long maxGlobalRead = cumulativeReadBytes.get(); - if (maxLocalRead <= 0) { - maxLocalRead = 0; - } - if (maxGlobalRead < maxLocalRead) { - maxGlobalRead = maxLocalRead; - } - wait = computeBalancedWait(maxLocalRead, maxGlobalRead, wait); - } - } - if (wait < waitGlobal) { - wait = waitGlobal; - } - wait = checkWaitReadTime(ctx, wait, now); - if (wait >= MINIMAL_WAIT) { // At least 10ms seems a minimal - // time in order to try to limit the traffic - // Only AutoRead AND HandlerActive True means Context Active - Channel channel = ctx.channel(); - ChannelConfig config = channel.config(); - if (logger.isDebugEnabled()) { - logger.debug("Read Suspend: " + wait + ':' + config.isAutoRead() + ':' - + isHandlerActive(ctx)); - } - if (config.isAutoRead() && isHandlerActive(ctx)) { - config.setAutoRead(false); - channel.attr(READ_SUSPENDED).set(true); - // Create a Runnable to reactive the read if needed. If one was create before it will just be - // reused to limit object creation - Attribute attr = channel.attr(REOPEN_TASK); - Runnable reopenTask = attr.get(); - if (reopenTask == null) { - reopenTask = new ReopenReadTimerTask(ctx); - attr.set(reopenTask); - } - ctx.executor().schedule(reopenTask, wait, TimeUnit.MILLISECONDS); - if (logger.isDebugEnabled()) { - logger.debug("Suspend final status => " + config.isAutoRead() + ':' - + isHandlerActive(ctx) + " will reopened at: " + wait); - } - } - } - } - informReadOperation(ctx, now); - ctx.fireChannelRead(msg); - } - - @Override - protected long checkWaitReadTime(final ChannelHandlerContext ctx, long wait, final long now) { - Integer key = ctx.channel().hashCode(); - PerChannel perChannel = channelQueues.get(key); - if (perChannel != null) { - if (wait > maxTime && now + wait - perChannel.lastReadTimestamp > maxTime) { - wait = maxTime; - } - } - return wait; - } - - @Override - protected void informReadOperation(final ChannelHandlerContext ctx, final long now) { - Integer key = ctx.channel().hashCode(); - PerChannel perChannel = channelQueues.get(key); - if (perChannel != null) { - perChannel.lastReadTimestamp = now; - } - } - - private static final class ToSend { - final long relativeTimeAction; - final Object toSend; - final Promise promise; - final long size; - - private ToSend(final long delay, final Object toSend, final long size, final Promise promise) { - relativeTimeAction = delay; - this.toSend = toSend; - this.size = size; - this.promise = promise; - } - } - - protected long maximumCumulativeWrittenBytes() { - return cumulativeWrittenBytes.get(); - } - - protected long maximumCumulativeReadBytes() { - return cumulativeReadBytes.get(); - } - - /** - * To allow for instance doAccounting to use the TrafficCounter per channel. - * @return the list of TrafficCounters that exists at the time of the call. - */ - public Collection channelTrafficCounters() { - return new AbstractCollection() { - @Override - public Iterator iterator() { - return new Iterator() { - final Iterator iter = channelQueues.values().iterator(); - @Override - public boolean hasNext() { - return iter.hasNext(); - } - @Override - public TrafficCounter next() { - return iter.next().channelTrafficCounter; - } - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - }; - } - @Override - public int size() { - return channelQueues.size(); - } - }; - } - - @Override - public Future write(final ChannelHandlerContext ctx, final Object msg) { - long size = calculateSize(msg); - long now = TrafficCounter.milliSecondFromNano(); - if (size > 0) { - // compute the number of ms to wait before continue with the channel - long waitGlobal = trafficCounter.writeTimeToWait(size, getWriteLimit(), maxTime, now); - Integer key = ctx.channel().hashCode(); - PerChannel perChannel = channelQueues.get(key); - long wait = 0; - if (perChannel != null) { - wait = perChannel.channelTrafficCounter.writeTimeToWait(size, writeChannelLimit, maxTime, now); - if (writeDeviationActive) { - // now try to balance between the channels - long maxLocalWrite; - maxLocalWrite = perChannel.channelTrafficCounter.cumulativeWrittenBytes(); - long maxGlobalWrite = cumulativeWrittenBytes.get(); - if (maxLocalWrite <= 0) { - maxLocalWrite = 0; - } - if (maxGlobalWrite < maxLocalWrite) { - maxGlobalWrite = maxLocalWrite; - } - wait = computeBalancedWait(maxLocalWrite, maxGlobalWrite, wait); - } - } - if (wait < waitGlobal) { - wait = waitGlobal; - } - if (wait >= MINIMAL_WAIT) { - if (logger.isDebugEnabled()) { - logger.debug("Write suspend: " + wait + ':' + ctx.channel().config().isAutoRead() + ':' - + isHandlerActive(ctx)); - } - Promise promise = ctx.newPromise(); - submitWrite(ctx, msg, size, wait, now, promise); - return promise.asFuture(); - } - } - Promise promise = ctx.newPromise(); - // to maintain order of write - submitWrite(ctx, msg, size, 0, now, promise); - return promise.asFuture(); - } - - @Override - protected void submitWrite(final ChannelHandlerContext ctx, final Object msg, - final long size, final long writedelay, final long now, - final Promise promise) { - Channel channel = ctx.channel(); - Integer key = channel.hashCode(); - PerChannel perChannel = channelQueues.get(key); - if (perChannel == null) { - // in case write occurs before handlerAdded is raised for this handler - // imply a synchronized only if needed - perChannel = getOrSetPerChannel(ctx); - } - final ToSend newToSend; - long delay = writedelay; - boolean globalSizeExceeded = false; - // write operations need synchronization - synchronized (perChannel) { - if (writedelay == 0 && perChannel.messagesQueue.isEmpty()) { - trafficCounter.bytesRealWriteFlowControl(size); - perChannel.channelTrafficCounter.bytesRealWriteFlowControl(size); - ctx.write(msg).cascadeTo(promise); - perChannel.lastWriteTimestamp = now; - return; - } - if (delay > maxTime && now + delay - perChannel.lastWriteTimestamp > maxTime) { - delay = maxTime; - } - newToSend = new ToSend(delay + now, msg, size, promise); - perChannel.messagesQueue.addLast(newToSend); - perChannel.queueSize += size; - queuesSize.addAndGet(size); - checkWriteSuspend(ctx, delay, perChannel.queueSize); - if (queuesSize.get() > maxGlobalWriteSize) { - globalSizeExceeded = true; - } - } - if (globalSizeExceeded) { - setUserDefinedWritability(ctx, false); - } - final long futureNow = newToSend.relativeTimeAction; - final PerChannel forSchedule = perChannel; - ctx.executor().schedule(() -> sendAllValid(ctx, forSchedule, futureNow), delay, TimeUnit.MILLISECONDS); - } - - private void sendAllValid(final ChannelHandlerContext ctx, final PerChannel perChannel, final long now) { - // write operations need synchronization - synchronized (perChannel) { - ToSend newToSend = perChannel.messagesQueue.pollFirst(); - for (; newToSend != null; newToSend = perChannel.messagesQueue.pollFirst()) { - if (newToSend.relativeTimeAction <= now) { - long size = newToSend.size; - trafficCounter.bytesRealWriteFlowControl(size); - perChannel.channelTrafficCounter.bytesRealWriteFlowControl(size); - perChannel.queueSize -= size; - queuesSize.addAndGet(-size); - ctx.write(newToSend.toSend).cascadeTo(newToSend.promise); - perChannel.lastWriteTimestamp = now; - } else { - perChannel.messagesQueue.addFirst(newToSend); - break; - } - } - if (perChannel.messagesQueue.isEmpty()) { - releaseWriteSuspended(ctx); - } - } - ctx.flush(); - } - - @Override - public String toString() { - return new StringBuilder(340).append(super.toString()) - .append(" Write Channel Limit: ").append(writeChannelLimit) - .append(" Read Channel Limit: ").append(readChannelLimit).toString(); - } -} diff --git a/handler/src/main/java/io/netty/handler/traffic/GlobalTrafficShapingHandler.java b/handler/src/main/java/io/netty/handler/traffic/GlobalTrafficShapingHandler.java deleted file mode 100644 index fbd16c8c48..0000000000 --- a/handler/src/main/java/io/netty/handler/traffic/GlobalTrafficShapingHandler.java +++ /dev/null @@ -1,395 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.traffic; - -import io.netty.buffer.ByteBufConvertible; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.EventExecutorGroup; -import io.netty.util.concurrent.Promise; - -import java.util.ArrayDeque; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; - -import static java.util.Objects.requireNonNull; - -/** - *

This implementation of the {@link AbstractTrafficShapingHandler} is for global - * traffic shaping, that is to say a global limitation of the bandwidth, whatever - * the number of opened channels.

- *

Note the index used in {@code OutboundBuffer.setUserDefinedWritability(index, boolean)} is 2.

- * - *

The general use should be as follow:

- *
    - *
  • Create your unique GlobalTrafficShapingHandler like:

    - *

    GlobalTrafficShapingHandler myHandler = new GlobalTrafficShapingHandler(executor);

    - *

    The executor could be the underlying IO worker pool

    - *

    pipeline.addLast(myHandler);

    - * - *

    Note that this handler has a Pipeline Coverage of "all" which means only one such handler must be created - * and shared among all channels as the counter must be shared among all channels.

    - * - *

    Other arguments can be passed like write or read limitation (in bytes/s where 0 means no limitation) - * or the check interval (in millisecond) that represents the delay between two computations of the - * bandwidth and so the call back of the doAccounting method (0 means no accounting at all).

    - * - *

    A value of 0 means no accounting for checkInterval. If you need traffic shaping but no such accounting, - * it is recommended to set a positive value, even if it is high since the precision of the - * Traffic Shaping depends on the period where the traffic is computed. The highest the interval, - * the less precise the traffic shaping will be. It is suggested as higher value something close - * to 5 or 10 minutes.

    - * - *

    maxTimeToWait, by default set to 15s, allows to specify an upper bound of time shaping.

    - *
  • - *
  • In your handler, you should consider to use the {@code channel.isWritable()} and - * {@code channelWritabilityChanged(ctx)} to handle writability, or through - * {@code future.addListener(future -> ...)} on the future returned by - * {@code ctx.write()}.
  • - *
  • You shall also consider to have object size in read or write operations relatively adapted to - * the bandwidth you required: for instance having 10 MB objects for 10KB/s will lead to burst effect, - * while having 100 KB objects for 1 MB/s should be smoothly handle by this TrafficShaping handler.

  • - *
  • Some configuration methods will be taken as best effort, meaning - * that all already scheduled traffics will not be - * changed, but only applied to new traffics.

    - * So the expected usage of those methods are to be used not too often, - * accordingly to the traffic shaping configuration.
  • - *
- * - * Be sure to call {@link #release()} once this handler is not needed anymore to release all internal resources. - * This will not shutdown the {@link EventExecutor} as it may be shared, so you need to do this by your own. - */ -@Sharable -public class GlobalTrafficShapingHandler extends AbstractTrafficShapingHandler { - /** - * All queues per channel - */ - private final ConcurrentMap channelQueues = new ConcurrentHashMap<>(); - - /** - * Global queues size - */ - private final AtomicLong queuesSize = new AtomicLong(); - - /** - * Max size in the list before proposing to stop writing new objects from next handlers - * for all channel (global) - */ - long maxGlobalWriteSize = DEFAULT_MAX_SIZE * 100; // default 400MB - - private static final class PerChannel { - ArrayDeque messagesQueue; - long queueSize; - long lastWriteTimestamp; - long lastReadTimestamp; - } - - /** - * Create the global TrafficCounter. - */ - void createGlobalTrafficCounter(EventExecutorGroup executor) { - requireNonNull(executor, "executor"); - TrafficCounter tc = new TrafficCounter(this, executor, "GlobalTC", checkInterval); - setTrafficCounter(tc); - tc.start(); - } - - @Override - protected int userDefinedWritabilityIndex() { - return AbstractTrafficShapingHandler.GLOBAL_DEFAULT_USER_DEFINED_WRITABILITY_INDEX; - } - - /** - * Create a new instance. - * - * @param executor - * the {@link EventExecutorGroup} to use for the {@link TrafficCounter}. - * @param writeLimit - * 0 or a limit in bytes/s - * @param readLimit - * 0 or a limit in bytes/s - * @param checkInterval - * The delay between two computations of performances for - * channels or 0 if no stats are to be computed. - * @param maxTime - * The maximum delay to wait in case of traffic excess. - */ - public GlobalTrafficShapingHandler(EventExecutorGroup executor, long writeLimit, long readLimit, - long checkInterval, long maxTime) { - super(writeLimit, readLimit, checkInterval, maxTime); - createGlobalTrafficCounter(executor); - } - - /** - * Create a new instance using - * default max time as delay allowed value of 15000 ms. - * - * @param executor - * the {@link EventExecutorGroup} to use for the {@link TrafficCounter}. - * @param writeLimit - * 0 or a limit in bytes/s - * @param readLimit - * 0 or a limit in bytes/s - * @param checkInterval - * The delay between two computations of performances for - * channels or 0 if no stats are to be computed. - */ - public GlobalTrafficShapingHandler(EventExecutorGroup executor, long writeLimit, - long readLimit, long checkInterval) { - super(writeLimit, readLimit, checkInterval); - createGlobalTrafficCounter(executor); - } - - /** - * Create a new instance using default Check Interval value of 1000 ms and - * default max time as delay allowed value of 15000 ms. - * - * @param executor - * the {@link EventExecutorGroup} to use for the {@link TrafficCounter}. - * @param writeLimit - * 0 or a limit in bytes/s - * @param readLimit - * 0 or a limit in bytes/s - */ - public GlobalTrafficShapingHandler(EventExecutorGroup executor, long writeLimit, - long readLimit) { - super(writeLimit, readLimit); - createGlobalTrafficCounter(executor); - } - - /** - * Create a new instance using - * default max time as delay allowed value of 15000 ms and no limit. - * - * @param executor - * the {@link EventExecutorGroup} to use for the {@link TrafficCounter}. - * @param checkInterval - * The delay between two computations of performances for - * channels or 0 if no stats are to be computed. - */ - public GlobalTrafficShapingHandler(EventExecutorGroup executor, long checkInterval) { - super(checkInterval); - createGlobalTrafficCounter(executor); - } - - /** - * Create a new instance using default Check Interval value of 1000 ms and - * default max time as delay allowed value of 15000 ms and no limit. - * - * @param executor - * the {@link ScheduledExecutorService} to use for the {@link TrafficCounter}. - */ - public GlobalTrafficShapingHandler(EventExecutor executor) { - createGlobalTrafficCounter(executor); - } - - /** - * @return the maxGlobalWriteSize default value being 400 MB. - */ - public long getMaxGlobalWriteSize() { - return maxGlobalWriteSize; - } - - /** - * Note the change will be taken as best effort, meaning - * that all already scheduled traffics will not be - * changed, but only applied to new traffics.
- * So the expected usage of this method is to be used not too often, - * accordingly to the traffic shaping configuration. - * - * @param maxGlobalWriteSize the maximum Global Write Size allowed in the buffer - * globally for all channels before write suspended is set, - * default value being 400 MB. - */ - public void setMaxGlobalWriteSize(long maxGlobalWriteSize) { - this.maxGlobalWriteSize = maxGlobalWriteSize; - } - - /** - * @return the global size of the buffers for all queues. - */ - public long queuesSize() { - return queuesSize.get(); - } - - /** - * Release all internal resources of this instance. - */ - public final void release() { - trafficCounter.stop(); - } - - private PerChannel getOrSetPerChannel(ChannelHandlerContext ctx) { - // ensure creation is limited to one thread per channel - Channel channel = ctx.channel(); - Integer key = channel.hashCode(); - PerChannel perChannel = channelQueues.get(key); - if (perChannel == null) { - perChannel = new PerChannel(); - perChannel.messagesQueue = new ArrayDeque<>(); - perChannel.queueSize = 0L; - perChannel.lastReadTimestamp = TrafficCounter.milliSecondFromNano(); - perChannel.lastWriteTimestamp = perChannel.lastReadTimestamp; - channelQueues.put(key, perChannel); - } - return perChannel; - } - - @Override - public void handlerAdded(ChannelHandlerContext ctx) throws Exception { - getOrSetPerChannel(ctx); - super.handlerAdded(ctx); - } - - @Override - public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { - Channel channel = ctx.channel(); - Integer key = channel.hashCode(); - PerChannel perChannel = channelQueues.remove(key); - if (perChannel != null) { - // write operations need synchronization - synchronized (perChannel) { - if (channel.isActive()) { - for (ToSend toSend : perChannel.messagesQueue) { - long size = calculateSize(toSend.toSend); - trafficCounter.bytesRealWriteFlowControl(size); - perChannel.queueSize -= size; - queuesSize.addAndGet(-size); - ctx.write(toSend.toSend).cascadeTo(toSend.promise); - } - } else { - queuesSize.addAndGet(-perChannel.queueSize); - for (ToSend toSend : perChannel.messagesQueue) { - if (toSend.toSend instanceof ByteBufConvertible) { - ((ByteBufConvertible) toSend.toSend).asByteBuf().release(); - } - } - } - perChannel.messagesQueue.clear(); - } - } - releaseWriteSuspended(ctx); - releaseReadSuspended(ctx); - super.handlerRemoved(ctx); - } - - @Override - long checkWaitReadTime(final ChannelHandlerContext ctx, long wait, final long now) { - Integer key = ctx.channel().hashCode(); - PerChannel perChannel = channelQueues.get(key); - if (perChannel != null) { - if (wait > maxTime && now + wait - perChannel.lastReadTimestamp > maxTime) { - wait = maxTime; - } - } - return wait; - } - - @Override - void informReadOperation(final ChannelHandlerContext ctx, final long now) { - Integer key = ctx.channel().hashCode(); - PerChannel perChannel = channelQueues.get(key); - if (perChannel != null) { - perChannel.lastReadTimestamp = now; - } - } - - private static final class ToSend { - final long relativeTimeAction; - final Object toSend; - final long size; - final Promise promise; - - private ToSend(final long delay, final Object toSend, final long size, final Promise promise) { - relativeTimeAction = delay; - this.toSend = toSend; - this.size = size; - this.promise = promise; - } - } - - @Override - void submitWrite(final ChannelHandlerContext ctx, final Object msg, - final long size, final long writedelay, final long now, - final Promise promise) { - Channel channel = ctx.channel(); - Integer key = channel.hashCode(); - PerChannel perChannel = channelQueues.get(key); - if (perChannel == null) { - // in case write occurs before handlerAdded is raised for this handler - // imply a synchronized only if needed - perChannel = getOrSetPerChannel(ctx); - } - final ToSend newToSend; - long delay = writedelay; - boolean globalSizeExceeded = false; - // write operations need synchronization - synchronized (perChannel) { - if (writedelay == 0 && perChannel.messagesQueue.isEmpty()) { - trafficCounter.bytesRealWriteFlowControl(size); - ctx.write(msg).cascadeTo(promise); - perChannel.lastWriteTimestamp = now; - return; - } - if (delay > maxTime && now + delay - perChannel.lastWriteTimestamp > maxTime) { - delay = maxTime; - } - newToSend = new ToSend(delay + now, msg, size, promise); - perChannel.messagesQueue.addLast(newToSend); - perChannel.queueSize += size; - queuesSize.addAndGet(size); - checkWriteSuspend(ctx, delay, perChannel.queueSize); - if (queuesSize.get() > maxGlobalWriteSize) { - globalSizeExceeded = true; - } - } - if (globalSizeExceeded) { - setUserDefinedWritability(ctx, false); - } - final long futureNow = newToSend.relativeTimeAction; - final PerChannel forSchedule = perChannel; - ctx.executor().schedule(() -> sendAllValid(ctx, forSchedule, futureNow), delay, TimeUnit.MILLISECONDS); - } - - private void sendAllValid(final ChannelHandlerContext ctx, final PerChannel perChannel, final long now) { - // write operations need synchronization - synchronized (perChannel) { - ToSend newToSend = perChannel.messagesQueue.pollFirst(); - for (; newToSend != null; newToSend = perChannel.messagesQueue.pollFirst()) { - if (newToSend.relativeTimeAction <= now) { - long size = newToSend.size; - trafficCounter.bytesRealWriteFlowControl(size); - perChannel.queueSize -= size; - queuesSize.addAndGet(-size); - ctx.write(newToSend.toSend).cascadeTo(newToSend.promise); - perChannel.lastWriteTimestamp = now; - } else { - perChannel.messagesQueue.addFirst(newToSend); - break; - } - } - if (perChannel.messagesQueue.isEmpty()) { - releaseWriteSuspended(ctx); - } - } - ctx.flush(); - } -} diff --git a/handler/src/main/java/io/netty/handler/traffic/TrafficCounter.java b/handler/src/main/java/io/netty/handler/traffic/TrafficCounter.java deleted file mode 100644 index fd384e2de2..0000000000 --- a/handler/src/main/java/io/netty/handler/traffic/TrafficCounter.java +++ /dev/null @@ -1,622 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.traffic; - -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.EventExecutorGroup; -import io.netty.util.concurrent.Future; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; - -import static io.netty.util.internal.ObjectUtil.checkNotNullWithIAE; -import static java.util.Objects.requireNonNull; - - -/** - * Counts the number of read and written bytes for rate-limiting traffic. - *

- * It computes the statistics for both inbound and outbound traffic periodically at the given - * {@code checkInterval}, and calls the {@link AbstractTrafficShapingHandler#doAccounting(TrafficCounter)} method back. - * If the {@code checkInterval} is {@code 0}, no accounting will be done and statistics will only be computed at each - * receive or write operation. - *

- */ -public class TrafficCounter { - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(TrafficCounter.class); - - /** - * @return the time in ms using nanoTime, so not real EPOCH time but elapsed time in ms. - */ - public static long milliSecondFromNano() { - return System.nanoTime() / 1000000; - } - - /** - * Current written bytes - */ - private final AtomicLong currentWrittenBytes = new AtomicLong(); - - /** - * Current read bytes - */ - private final AtomicLong currentReadBytes = new AtomicLong(); - - /** - * Last writing time during current check interval - */ - private long writingTime; - - /** - * Last reading delay during current check interval - */ - private long readingTime; - - /** - * Long life written bytes - */ - private final AtomicLong cumulativeWrittenBytes = new AtomicLong(); - - /** - * Long life read bytes - */ - private final AtomicLong cumulativeReadBytes = new AtomicLong(); - - /** - * Last Time where cumulative bytes where reset to zero: this time is a real EPOC time (informative only) - */ - private long lastCumulativeTime; - - /** - * Last writing bandwidth - */ - private long lastWriteThroughput; - - /** - * Last reading bandwidth - */ - private long lastReadThroughput; - - /** - * Last Time Check taken - */ - final AtomicLong lastTime = new AtomicLong(); - - /** - * Last written bytes number during last check interval - */ - private volatile long lastWrittenBytes; - - /** - * Last read bytes number during last check interval - */ - private volatile long lastReadBytes; - - /** - * Last future writing time during last check interval - */ - private volatile long lastWritingTime; - - /** - * Last reading time during last check interval - */ - private volatile long lastReadingTime; - - /** - * Real written bytes - */ - private final AtomicLong realWrittenBytes = new AtomicLong(); - - /** - * Real writing bandwidth - */ - private long realWriteThroughput; - - /** - * Delay between two captures - */ - final AtomicLong checkInterval = new AtomicLong( - AbstractTrafficShapingHandler.DEFAULT_CHECK_INTERVAL); - - // default 1 s - - /** - * Name of this Monitor - */ - final String name; - - /** - * The associated TrafficShapingHandler - */ - final AbstractTrafficShapingHandler trafficShapingHandler; - - /** - * Executor that will run the monitor - */ - final EventExecutorGroup executor; - /** - * Monitor created once in start() - */ - Runnable monitor; - /** - * used in stop() to cancel the timer - */ - volatile Future scheduledFuture; - - /** - * Is Monitor active - */ - volatile boolean monitorActive; - - /** - * Class to implement monitoring at fix delay - * - */ - private final class TrafficMonitoringTask implements Runnable { - @Override - public void run() { - if (!monitorActive) { - return; - } - resetAccounting(milliSecondFromNano()); - if (trafficShapingHandler != null) { - trafficShapingHandler.doAccounting(TrafficCounter.this); - } - } - } - - /** - * Start the monitoring process. - */ - public synchronized void start() { - if (monitorActive) { - return; - } - lastTime.set(milliSecondFromNano()); - long localCheckInterval = checkInterval.get(); - // if executor is null, it means it is piloted by a GlobalChannelTrafficCounter, so no executor - if (localCheckInterval > 0 && executor != null) { - monitorActive = true; - monitor = new TrafficMonitoringTask(); - scheduledFuture = - executor.scheduleAtFixedRate(monitor, 0, localCheckInterval, TimeUnit.MILLISECONDS); - } - } - - /** - * Stop the monitoring process. - */ - public synchronized void stop() { - if (!monitorActive) { - return; - } - monitorActive = false; - resetAccounting(milliSecondFromNano()); - if (trafficShapingHandler != null) { - trafficShapingHandler.doAccounting(this); - } - if (scheduledFuture != null) { - scheduledFuture.cancel(); - } - } - - /** - * Reset the accounting on Read and Write. - * - * @param newLastTime the milliseconds unix timestamp that we should be considered up-to-date for. - */ - synchronized void resetAccounting(long newLastTime) { - long interval = newLastTime - lastTime.getAndSet(newLastTime); - if (interval == 0) { - // nothing to do - return; - } - if (logger.isDebugEnabled() && interval > checkInterval() << 1) { - logger.debug("Acct schedule not ok: " + interval + " > 2*" + checkInterval() + " from " + name); - } - lastReadBytes = currentReadBytes.getAndSet(0); - lastWrittenBytes = currentWrittenBytes.getAndSet(0); - lastReadThroughput = lastReadBytes * 1000 / interval; - // nb byte / checkInterval in ms * 1000 (1s) - lastWriteThroughput = lastWrittenBytes * 1000 / interval; - // nb byte / checkInterval in ms * 1000 (1s) - realWriteThroughput = realWrittenBytes.getAndSet(0) * 1000 / interval; - lastWritingTime = Math.max(lastWritingTime, writingTime); - lastReadingTime = Math.max(lastReadingTime, readingTime); - } - - /** - * Constructor with the {@link AbstractTrafficShapingHandler} that hosts it, the {@link ScheduledExecutorService} - * to use, its name, the checkInterval between two computations in milliseconds. - * - * @param executor - * the underlying executor service for scheduling checks, might be null when used - * from {@link GlobalChannelTrafficCounter}. - * @param name - * the name given to this monitor. - * @param checkInterval - * the checkInterval in millisecond between two computations. - */ - public TrafficCounter(EventExecutor executor, String name, long checkInterval) { - requireNonNull(name, "name"); - - trafficShapingHandler = null; - this.executor = executor; - this.name = name; - - init(checkInterval); - } - - /** - * Constructor with the {@link AbstractTrafficShapingHandler} that hosts it, the Timer to use, its - * name, the checkInterval between two computations in millisecond. - * - * @param trafficShapingHandler - * the associated AbstractTrafficShapingHandler. - * @param executor - * the underlying executor service for scheduling checks, might be null when used - * from {@link GlobalChannelTrafficCounter}. - * @param name - * the name given to this monitor. - * @param checkInterval - * the checkInterval in millisecond between two computations. - */ - public TrafficCounter( - AbstractTrafficShapingHandler trafficShapingHandler, EventExecutorGroup executor, - String name, long checkInterval) { - this.name = requireNonNull(name, "name"); - this.trafficShapingHandler = checkNotNullWithIAE(trafficShapingHandler, "trafficShapingHandler"); - this.executor = executor; - - init(checkInterval); - } - - private void init(long checkInterval) { - // absolute time: informative only - lastCumulativeTime = System.currentTimeMillis(); - writingTime = milliSecondFromNano(); - readingTime = writingTime; - lastWritingTime = writingTime; - lastReadingTime = writingTime; - configure(checkInterval); - } - - /** - * Change checkInterval between two computations in millisecond. - * - * @param newCheckInterval The new check interval (in milliseconds) - */ - public void configure(long newCheckInterval) { - long newInterval = newCheckInterval / 10 * 10; - if (checkInterval.getAndSet(newInterval) != newInterval) { - stop(); - if (newInterval <= 0) { - // No more active monitoring - lastTime.set(milliSecondFromNano()); - } else { - // Restart - start(); - } - } - } - - /** - * Computes counters for Read. - * - * @param recv - * the size in bytes to read - */ - void bytesRecvFlowControl(long recv) { - currentReadBytes.addAndGet(recv); - cumulativeReadBytes.addAndGet(recv); - } - - /** - * Computes counters for Write. - * - * @param write - * the size in bytes to write - */ - void bytesWriteFlowControl(long write) { - currentWrittenBytes.addAndGet(write); - cumulativeWrittenBytes.addAndGet(write); - } - - /** - * Computes counters for Real Write. - * - * @param write - * the size in bytes to write - */ - void bytesRealWriteFlowControl(long write) { - realWrittenBytes.addAndGet(write); - } - - /** - * @return the current checkInterval between two computations of traffic counter - * in millisecond. - */ - public long checkInterval() { - return checkInterval.get(); - } - - /** - * @return the Read Throughput in bytes/s computes in the last check interval. - */ - public long lastReadThroughput() { - return lastReadThroughput; - } - - /** - * @return the Write Throughput in bytes/s computes in the last check interval. - */ - public long lastWriteThroughput() { - return lastWriteThroughput; - } - - /** - * @return the number of bytes read during the last check Interval. - */ - public long lastReadBytes() { - return lastReadBytes; - } - - /** - * @return the number of bytes written during the last check Interval. - */ - public long lastWrittenBytes() { - return lastWrittenBytes; - } - - /** - * @return the current number of bytes read since the last checkInterval. - */ - public long currentReadBytes() { - return currentReadBytes.get(); - } - - /** - * @return the current number of bytes written since the last check Interval. - */ - public long currentWrittenBytes() { - return currentWrittenBytes.get(); - } - - /** - * @return the Time in millisecond of the last check as of System.currentTimeMillis(). - */ - public long lastTime() { - return lastTime.get(); - } - - /** - * @return the cumulativeWrittenBytes - */ - public long cumulativeWrittenBytes() { - return cumulativeWrittenBytes.get(); - } - - /** - * @return the cumulativeReadBytes - */ - public long cumulativeReadBytes() { - return cumulativeReadBytes.get(); - } - - /** - * @return the lastCumulativeTime in millisecond as of System.currentTimeMillis() - * when the cumulative counters were reset to 0. - */ - public long lastCumulativeTime() { - return lastCumulativeTime; - } - - /** - * @return the realWrittenBytes - */ - public AtomicLong getRealWrittenBytes() { - return realWrittenBytes; - } - - /** - * @return the realWriteThroughput - */ - public long getRealWriteThroughput() { - return realWriteThroughput; - } - - /** - * Reset both read and written cumulative bytes counters and the associated absolute time - * from System.currentTimeMillis(). - */ - public void resetCumulativeTime() { - lastCumulativeTime = System.currentTimeMillis(); - cumulativeReadBytes.set(0); - cumulativeWrittenBytes.set(0); - } - - /** - * @return the name of this TrafficCounter. - */ - public String name() { - return name; - } - - /** - * Returns the time to wait (if any) for the given length message, using the given limitTraffic and the max wait - * time. - * - * @param size - * the recv size - * @param limitTraffic - * the traffic limit in bytes per second. - * @param maxTime - * the max time in ms to wait in case of excess of traffic. - * @return the current time to wait (in ms) if needed for Read operation. - */ - @Deprecated - public long readTimeToWait(final long size, final long limitTraffic, final long maxTime) { - return readTimeToWait(size, limitTraffic, maxTime, milliSecondFromNano()); - } - - /** - * Returns the time to wait (if any) for the given length message, using the given limitTraffic and the max wait - * time. - * - * @param size - * the recv size - * @param limitTraffic - * the traffic limit in bytes per second - * @param maxTime - * the max time in ms to wait in case of excess of traffic. - * @param now the current time - * @return the current time to wait (in ms) if needed for Read operation. - */ - public long readTimeToWait(final long size, final long limitTraffic, final long maxTime, final long now) { - bytesRecvFlowControl(size); - if (size == 0 || limitTraffic == 0) { - return 0; - } - final long lastTimeCheck = lastTime.get(); - long sum = currentReadBytes.get(); - long localReadingTime = readingTime; - long lastRB = lastReadBytes; - final long interval = now - lastTimeCheck; - long pastDelay = Math.max(lastReadingTime - lastTimeCheck, 0); - if (interval > AbstractTrafficShapingHandler.MINIMAL_WAIT) { - // Enough interval time to compute shaping - long time = sum * 1000 / limitTraffic - interval + pastDelay; - if (time > AbstractTrafficShapingHandler.MINIMAL_WAIT) { - if (logger.isDebugEnabled()) { - logger.debug("Time: " + time + ':' + sum + ':' + interval + ':' + pastDelay); - } - if (time > maxTime && now + time - localReadingTime > maxTime) { - time = maxTime; - } - readingTime = Math.max(localReadingTime, now + time); - return time; - } - readingTime = Math.max(localReadingTime, now); - return 0; - } - // take the last read interval check to get enough interval time - long lastsum = sum + lastRB; - long lastinterval = interval + checkInterval.get(); - long time = lastsum * 1000 / limitTraffic - lastinterval + pastDelay; - if (time > AbstractTrafficShapingHandler.MINIMAL_WAIT) { - if (logger.isDebugEnabled()) { - logger.debug("Time: " + time + ':' + lastsum + ':' + lastinterval + ':' + pastDelay); - } - if (time > maxTime && now + time - localReadingTime > maxTime) { - time = maxTime; - } - readingTime = Math.max(localReadingTime, now + time); - return time; - } - readingTime = Math.max(localReadingTime, now); - return 0; - } - - /** - * Returns the time to wait (if any) for the given length message, using the given limitTraffic and - * the max wait time. - * - * @param size - * the write size - * @param limitTraffic - * the traffic limit in bytes per second. - * @param maxTime - * the max time in ms to wait in case of excess of traffic. - * @return the current time to wait (in ms) if needed for Write operation. - */ - @Deprecated - public long writeTimeToWait(final long size, final long limitTraffic, final long maxTime) { - return writeTimeToWait(size, limitTraffic, maxTime, milliSecondFromNano()); - } - - /** - * Returns the time to wait (if any) for the given length message, using the given limitTraffic and - * the max wait time. - * - * @param size - * the write size - * @param limitTraffic - * the traffic limit in bytes per second. - * @param maxTime - * the max time in ms to wait in case of excess of traffic. - * @param now the current time - * @return the current time to wait (in ms) if needed for Write operation. - */ - public long writeTimeToWait(final long size, final long limitTraffic, final long maxTime, final long now) { - bytesWriteFlowControl(size); - if (size == 0 || limitTraffic == 0) { - return 0; - } - final long lastTimeCheck = lastTime.get(); - long sum = currentWrittenBytes.get(); - long lastWB = lastWrittenBytes; - long localWritingTime = writingTime; - long pastDelay = Math.max(lastWritingTime - lastTimeCheck, 0); - final long interval = now - lastTimeCheck; - if (interval > AbstractTrafficShapingHandler.MINIMAL_WAIT) { - // Enough interval time to compute shaping - long time = sum * 1000 / limitTraffic - interval + pastDelay; - if (time > AbstractTrafficShapingHandler.MINIMAL_WAIT) { - if (logger.isDebugEnabled()) { - logger.debug("Time: " + time + ':' + sum + ':' + interval + ':' + pastDelay); - } - if (time > maxTime && now + time - localWritingTime > maxTime) { - time = maxTime; - } - writingTime = Math.max(localWritingTime, now + time); - return time; - } - writingTime = Math.max(localWritingTime, now); - return 0; - } - // take the last write interval check to get enough interval time - long lastsum = sum + lastWB; - long lastinterval = interval + checkInterval.get(); - long time = lastsum * 1000 / limitTraffic - lastinterval + pastDelay; - if (time > AbstractTrafficShapingHandler.MINIMAL_WAIT) { - if (logger.isDebugEnabled()) { - logger.debug("Time: " + time + ':' + lastsum + ':' + lastinterval + ':' + pastDelay); - } - if (time > maxTime && now + time - localWritingTime > maxTime) { - time = maxTime; - } - writingTime = Math.max(localWritingTime, now + time); - return time; - } - writingTime = Math.max(localWritingTime, now); - return 0; - } - - @Override - public String toString() { - return new StringBuilder(165).append("Monitor ").append(name) - .append(" Current Speed Read: ").append(lastReadThroughput >> 10).append(" KB/s, ") - .append("Asked Write: ").append(lastWriteThroughput >> 10).append(" KB/s, ") - .append("Real Write: ").append(realWriteThroughput >> 10).append(" KB/s, ") - .append("Current Read: ").append(currentReadBytes.get() >> 10).append(" KB, ") - .append("Current asked Write: ").append(currentWrittenBytes.get() >> 10).append(" KB, ") - .append("Current real Write: ").append(realWrittenBytes.get() >> 10).append(" KB").toString(); - } -} diff --git a/handler/src/main/java/io/netty/handler/traffic/package-info.java b/handler/src/main/java/io/netty/handler/traffic/package-info.java deleted file mode 100644 index 285318ba4d..0000000000 --- a/handler/src/main/java/io/netty/handler/traffic/package-info.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * Implementation of a Traffic Shaping Handler and Dynamic Statistics. - * - *

The main goal of this package is to allow you to shape the traffic (bandwidth limitation), - * but also to get statistics on how many bytes are read or written. Both functions can - * be active or inactive (traffic or statistics).

- * - *

Two classes implement this behavior: - *

    - *
  • {@link io.netty.handler.traffic.TrafficCounter}: this class implements the counters needed by the - * handlers. It can be accessed to get some extra information like the read or write bytes since last check, - * the read and write bandwidth from last check...
  • - * - *
  • {@link io.netty.handler.traffic.AbstractTrafficShapingHandler}: this abstract class implements - * the kernel of traffic shaping. It could be extended to fit your needs. Two classes are proposed as default - * implementations: see {@link io.netty.handler.traffic.ChannelTrafficShapingHandler} and - * {@link io.netty.handler.traffic.GlobalTrafficShapingHandler} respectively for Channel traffic shaping and - * global traffic shaping.
  • - *

- * - *

Both inbound and outbound traffic can be shaped independently. This is done by either passing in - * the desired limiting values to the constructors of both the Channel and Global traffic shaping handlers, - * or by calling the configure method on the {@link io.netty.handler.traffic.AbstractTrafficShapingHandler}. - * A value of 0 for either parameter indicates that there should be no limitation. This allows you to monitor the - * incoming and outgoing traffic without shaping.

- * - *

To activate or deactivate the statistics, you can adjust the delay to a low (suggested not less than 200ms - * for efficiency reasons) or a high value (let say 24H in millisecond is huge enough to not get the problem) - * or even using 0 which means no computation will be done.

- * - *

If you want to do anything with these statistics, just override the doAccounting method.
- * This interval can be changed either from the method configure - * in {@link io.netty.handler.traffic.AbstractTrafficShapingHandler} or directly using the method configure - * of {@link io.netty.handler.traffic.TrafficCounter}.

- * - *

Note that a new {@link io.netty.handler.traffic.ChannelTrafficShapingHandler} must be created - * for each new channel, but only one {@link io.netty.handler.traffic.GlobalTrafficShapingHandler} must be created - * for all channels.

- * - *

Note also that you can create different GlobalTrafficShapingHandler if you want to separate classes of - * channels (for instance either from business point of view or from bind address point of view).

- */ -package io.netty.handler.traffic; - diff --git a/handler/src/main/resources/META-INF/native-image/io.netty/handler/native-image.properties b/handler/src/main/resources/META-INF/native-image/io.netty/handler/native-image.properties deleted file mode 100644 index 8a57474bb9..0000000000 --- a/handler/src/main/resources/META-INF/native-image/io.netty/handler/native-image.properties +++ /dev/null @@ -1,15 +0,0 @@ -# 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: -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -Args = --initialize-at-run-time=io.netty.handler.ssl.util.ThreadLocalInsecureRandom diff --git a/handler/src/test/java/io/netty/handler/address/DynamicAddressConnectHandlerTest.java b/handler/src/test/java/io/netty/handler/address/DynamicAddressConnectHandlerTest.java deleted file mode 100644 index 390b6056c9..0000000000 --- a/handler/src/test/java/io/netty/handler/address/DynamicAddressConnectHandlerTest.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.address; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.concurrent.Future; -import org.junit.jupiter.api.Test; - -import java.net.SocketAddress; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; - -public class DynamicAddressConnectHandlerTest { - private static final SocketAddress LOCAL = new SocketAddress() { }; - private static final SocketAddress LOCAL_NEW = new SocketAddress() { }; - private static final SocketAddress REMOTE = new SocketAddress() { }; - private static final SocketAddress REMOTE_NEW = new SocketAddress() { }; - @Test - public void testReplaceAddresses() { - - EmbeddedChannel channel = new EmbeddedChannel(new ChannelHandler() { - @Override - public Future connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, - SocketAddress localAddress) { - try { - assertSame(REMOTE_NEW, remoteAddress); - assertSame(LOCAL_NEW, localAddress); - return ctx.newSucceededFuture(); - } catch (Throwable cause) { - return ctx.newFailedFuture(cause); - } - } - }, new DynamicAddressConnectHandler() { - @Override - protected SocketAddress localAddress(SocketAddress remoteAddress, SocketAddress localAddress) { - assertSame(REMOTE, remoteAddress); - assertSame(LOCAL, localAddress); - return LOCAL_NEW; - } - - @Override - protected SocketAddress remoteAddress(SocketAddress remoteAddress, SocketAddress localAddress) { - assertSame(REMOTE, remoteAddress); - assertSame(LOCAL, localAddress); - return REMOTE_NEW; - } - }); - channel.connect(REMOTE, LOCAL).syncUninterruptibly(); - assertNull(channel.pipeline().get(DynamicAddressConnectHandler.class)); - assertFalse(channel.finish()); - } - - @Test - public void testLocalAddressThrows() { - testThrows0(true); - } - - @Test - public void testRemoteAddressThrows() { - testThrows0(false); - } - - private static void testThrows0(final boolean localThrows) { - final IllegalStateException exception = new IllegalStateException(); - - EmbeddedChannel channel = new EmbeddedChannel(new DynamicAddressConnectHandler() { - @Override - protected SocketAddress localAddress( - SocketAddress remoteAddress, SocketAddress localAddress) throws Exception { - if (localThrows) { - throw exception; - } - return super.localAddress(remoteAddress, localAddress); - } - - @Override - protected SocketAddress remoteAddress(SocketAddress remoteAddress, SocketAddress localAddress) - throws Exception { - if (!localThrows) { - throw exception; - } - return super.remoteAddress(remoteAddress, localAddress); - } - }); - assertSame(exception, channel.connect(REMOTE, LOCAL).cause()); - assertNotNull(channel.pipeline().get(DynamicAddressConnectHandler.class)); - assertFalse(channel.finish()); - } -} diff --git a/handler/src/test/java/io/netty/handler/address/ResolveAddressHandlerTest.java b/handler/src/test/java/io/netty/handler/address/ResolveAddressHandlerTest.java deleted file mode 100644 index d552b9c214..0000000000 --- a/handler/src/test/java/io/netty/handler/address/ResolveAddressHandlerTest.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.address; - -import io.netty.bootstrap.Bootstrap; -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.local.LocalAddress; -import io.netty.channel.local.LocalChannel; -import io.netty.channel.local.LocalHandler; -import io.netty.channel.local.LocalServerChannel; -import io.netty.resolver.AbstractAddressResolver; -import io.netty.resolver.AddressResolver; -import io.netty.resolver.AddressResolverGroup; -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.net.SocketAddress; -import java.net.UnknownHostException; -import java.util.List; -import java.util.UUID; - -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - -public class ResolveAddressHandlerTest { - - private static final LocalAddress UNRESOLVED = new LocalAddress("unresolved-" + UUID.randomUUID()); - private static final LocalAddress RESOLVED = new LocalAddress("resolved-" + UUID.randomUUID()); - private static final Exception ERROR = new UnknownHostException(); - - private static EventLoopGroup group; - - @BeforeAll - public static void createEventLoop() { - group = new MultithreadEventLoopGroup(LocalHandler.newFactory()); - } - - @AfterAll - public static void destroyEventLoop() { - if (group != null) { - group.shutdownGracefully(); - } - } - - @Test - public void testResolveSuccessful() throws Exception { - testResolve(false); - } - - @Test - public void testResolveFails() throws Exception { - testResolve(true); - } - - private static void testResolve(boolean fail) throws Exception { - AddressResolverGroup resolverGroup = new TestResolverGroup(fail); - Bootstrap cb = new Bootstrap(); - cb.group(group).channel(LocalChannel.class).handler(new ResolveAddressHandler(resolverGroup)); - - ServerBootstrap sb = new ServerBootstrap(); - sb.group(group) - .channel(LocalServerChannel.class) - .childHandler(new ChannelHandler() { - @Override - public void channelActive(ChannelHandlerContext ctx) { - ctx.close(); - } - }); - - // Start server - Channel sc = sb.bind(RESOLVED).get(); - Future future = cb.connect(UNRESOLVED).awaitUninterruptibly(); - try { - if (fail) { - assertSame(ERROR, future.cause()); - } else { - assertTrue(future.isSuccess()); - future.get().close().syncUninterruptibly(); - } - } finally { - sc.close().syncUninterruptibly(); - resolverGroup.close(); - } - } - - private static final class TestResolverGroup extends AddressResolverGroup { - private final boolean fail; - - TestResolverGroup(boolean fail) { - this.fail = fail; - } - - @Override - protected AddressResolver newResolver(EventExecutor executor) { - return new AbstractAddressResolver(executor) { - @Override - protected boolean doIsResolved(SocketAddress address) { - return address == RESOLVED; - } - - @Override - protected void doResolve(SocketAddress unresolvedAddress, Promise promise) { - assertSame(UNRESOLVED, unresolvedAddress); - if (fail) { - promise.setFailure(ERROR); - } else { - promise.setSuccess(RESOLVED); - } - } - - @Override - protected void doResolveAll(SocketAddress unresolvedAddress, Promise> promise) { - fail(); - } - }; - } - } -} diff --git a/handler/src/test/java/io/netty/handler/flow/FlowControlHandlerTest.java b/handler/src/test/java/io/netty/handler/flow/FlowControlHandlerTest.java deleted file mode 100644 index da374847e5..0000000000 --- a/handler/src/test/java/io/netty/handler/flow/FlowControlHandlerTest.java +++ /dev/null @@ -1,555 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.flow; - -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.ChannelConfig; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelOption; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.codec.ByteToMessageDecoder; -import io.netty.handler.timeout.IdleStateEvent; -import io.netty.handler.timeout.IdleStateHandler; -import io.netty.util.ReferenceCountUtil; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.net.SocketAddress; -import java.util.Queue; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.Exchanger; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.atomic.AtomicReference; - -import static java.util.concurrent.TimeUnit.MILLISECONDS; -import static java.util.concurrent.TimeUnit.SECONDS; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - -public class FlowControlHandlerTest { - private static EventLoopGroup GROUP; - - @BeforeAll - public static void init() { - GROUP = new MultithreadEventLoopGroup(NioHandler.newFactory()); - } - - @AfterAll - public static void destroy() { - GROUP.shutdownGracefully(); - } - - /** - * The {@link OneByteToThreeStringsDecoder} decodes this {@code byte[]} into three messages. - */ - private static ByteBuf newOneMessage() { - return Unpooled.wrappedBuffer(new byte[]{ 1 }); - } - - private static Channel newServer(final boolean autoRead, final ChannelHandler... handlers) throws Exception { - assertTrue(handlers.length >= 1); - - ServerBootstrap serverBootstrap = new ServerBootstrap(); - serverBootstrap.group(GROUP) - .channel(NioServerSocketChannel.class) - .childOption(ChannelOption.AUTO_READ, autoRead) - .childHandler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) { - ChannelPipeline pipeline = ch.pipeline(); - pipeline.addLast(new OneByteToThreeStringsDecoder()); - pipeline.addLast(handlers); - } - }); - - return serverBootstrap.bind(0).get(); - } - - private static Channel newClient(SocketAddress server) throws Exception { - Bootstrap bootstrap = new Bootstrap(); - - bootstrap.group(GROUP) - .channel(NioSocketChannel.class) - .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000) - .handler(new ChannelHandler() { - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - fail("In this test the client is never receiving a message from the server."); - } - }); - - return bootstrap.connect(server).get(); - } - - /** - * This test demonstrates the default behavior if auto reading - * is turned on from the get-go and you're trying to turn it off - * once you've received your first message. - * - * NOTE: This test waits for the client to disconnect which is - * interpreted as the signal that all {@code byte}s have been - * transferred to the server. - */ - @Test - public void testAutoReadingOn() throws Exception { - final CountDownLatch latch = new CountDownLatch(3); - - ChannelHandler handler = new ChannelHandler() { - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - ReferenceCountUtil.release(msg); - // We're turning off auto reading in the hope that no - // new messages are being sent but that is not true. - ctx.channel().config().setAutoRead(false); - - latch.countDown(); - } - }; - - Channel server = newServer(true, handler); - Channel client = newClient(server.localAddress()); - - try { - client.writeAndFlush(newOneMessage()) - .syncUninterruptibly(); - - // We received three messages even through auto reading - // was turned off after we received the first message. - assertTrue(latch.await(1L, SECONDS)); - } finally { - client.close(); - server.close(); - } - } - - /** - * This test demonstrates the default behavior if auto reading - * is turned off from the get-go and you're calling read() in - * the hope that only one message will be returned. - * - * NOTE: This test waits for the client to disconnect which is - * interpreted as the signal that all {@code byte}s have been - * transferred to the server. - */ - @Test - public void testAutoReadingOff() throws Exception { - final Exchanger peerRef = new Exchanger<>(); - final CountDownLatch latch = new CountDownLatch(3); - - ChannelHandler handler = new ChannelHandler() { - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - peerRef.exchange(ctx.channel(), 1L, SECONDS); - ctx.fireChannelActive(); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - ReferenceCountUtil.release(msg); - latch.countDown(); - } - }; - - Channel server = newServer(false, handler); - Channel client = newClient(server.localAddress()); - - try { - // The client connection on the server side - Channel peer = peerRef.exchange(null, 1L, SECONDS); - - // Write the message - client.writeAndFlush(newOneMessage()) - .syncUninterruptibly(); - - // Read the message - peer.read(); - - // We received all three messages but hoped that only one - // message was read because auto reading was off and we - // invoked the read() method only once. - assertTrue(latch.await(1L, SECONDS)); - } finally { - client.close(); - server.close(); - } - } - - /** - * The {@link FlowControlHandler} will simply pass-through all messages - * if auto reading is on and remains on. - */ - @Test - public void testFlowAutoReadOn() throws Exception { - final CountDownLatch latch = new CountDownLatch(3); - - ChannelHandler handler = new ChannelHandler() { - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - latch.countDown(); - } - }; - - FlowControlHandler flow = new FlowControlHandler(); - Channel server = newServer(true, flow, handler); - Channel client = newClient(server.localAddress()); - try { - // Write the message - client.writeAndFlush(newOneMessage()) - .syncUninterruptibly(); - - // We should receive 3 messages - assertTrue(latch.await(1L, SECONDS)); - assertTrue(flow.isQueueEmpty()); - } finally { - client.close(); - server.close(); - } - } - - /** - * The {@link FlowControlHandler} will pass down messages one by one - * if {@link ChannelConfig#setAutoRead(boolean)} is being toggled. - */ - @Test - public void testFlowToggleAutoRead() throws Exception { - final Exchanger peerRef = new Exchanger<>(); - final CountDownLatch msgRcvLatch1 = new CountDownLatch(1); - final CountDownLatch msgRcvLatch2 = new CountDownLatch(1); - final CountDownLatch msgRcvLatch3 = new CountDownLatch(1); - final CountDownLatch setAutoReadLatch1 = new CountDownLatch(1); - final CountDownLatch setAutoReadLatch2 = new CountDownLatch(1); - - ChannelHandler handler = new ChannelHandler() { - private int msgRcvCount; - private int expectedMsgCount; - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - peerRef.exchange(ctx.channel(), 1L, SECONDS); - ctx.fireChannelActive(); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws InterruptedException { - ReferenceCountUtil.release(msg); - - // Disable auto reading after each message - ctx.channel().config().setAutoRead(false); - - if (msgRcvCount++ != expectedMsgCount) { - return; - } - switch (msgRcvCount) { - case 1: - msgRcvLatch1.countDown(); - if (setAutoReadLatch1.await(1L, SECONDS)) { - ++expectedMsgCount; - } - break; - case 2: - msgRcvLatch2.countDown(); - if (setAutoReadLatch2.await(1L, SECONDS)) { - ++expectedMsgCount; - } - break; - default: - msgRcvLatch3.countDown(); - break; - } - } - }; - - FlowControlHandler flow = new FlowControlHandler(); - Channel server = newServer(true, flow, handler); - Channel client = newClient(server.localAddress()); - try { - // The client connection on the server side - Channel peer = peerRef.exchange(null, 1L, SECONDS); - - client.writeAndFlush(newOneMessage()) - .syncUninterruptibly(); - - // channelRead(1) - assertTrue(msgRcvLatch1.await(1L, SECONDS)); - - // channelRead(2) - peer.config().setAutoRead(true); - setAutoReadLatch1.countDown(); - assertTrue(msgRcvLatch1.await(1L, SECONDS)); - - // channelRead(3) - peer.config().setAutoRead(true); - setAutoReadLatch2.countDown(); - assertTrue(msgRcvLatch3.await(1L, SECONDS)); - assertTrue(flow.isQueueEmpty()); - } finally { - client.close(); - server.close(); - } - } - - /** - * The {@link FlowControlHandler} will pass down messages one by one - * if auto reading is off and the user is calling {@code read()} on - * their own. - */ - @Test - public void testFlowAutoReadOff() throws Exception { - final Exchanger peerRef = new Exchanger<>(); - final CountDownLatch msgRcvLatch1 = new CountDownLatch(1); - final CountDownLatch msgRcvLatch2 = new CountDownLatch(2); - final CountDownLatch msgRcvLatch3 = new CountDownLatch(3); - - ChannelHandler handler = new ChannelHandler() { - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - ctx.fireChannelActive(); - peerRef.exchange(ctx.channel(), 1L, SECONDS); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - msgRcvLatch1.countDown(); - msgRcvLatch2.countDown(); - msgRcvLatch3.countDown(); - } - }; - - FlowControlHandler flow = new FlowControlHandler(); - Channel server = newServer(false, flow, handler); - Channel client = newClient(server.localAddress()); - try { - // The client connection on the server side - Channel peer = peerRef.exchange(null, 1L, SECONDS); - - // Write the message - client.writeAndFlush(newOneMessage()) - .syncUninterruptibly(); - - // channelRead(1) - peer.read(); - assertTrue(msgRcvLatch1.await(1L, SECONDS)); - - // channelRead(2) - peer.read(); - assertTrue(msgRcvLatch2.await(1L, SECONDS)); - - // channelRead(3) - peer.read(); - assertTrue(msgRcvLatch3.await(1L, SECONDS)); - assertTrue(flow.isQueueEmpty()); - } finally { - client.close(); - server.close(); - } - } - - @Test - public void testReentranceNotCausesNPE() throws Throwable { - final Exchanger peerRef = new Exchanger(); - final CountDownLatch latch = new CountDownLatch(3); - final AtomicReference causeRef = new AtomicReference(); - ChannelHandler handler = new ChannelHandler() { - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - ctx.fireChannelActive(); - peerRef.exchange(ctx.channel(), 1L, SECONDS); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - latch.countDown(); - ctx.read(); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - causeRef.set(cause); - } - }; - - FlowControlHandler flow = new FlowControlHandler(); - Channel server = newServer(false, flow, handler); - Channel client = newClient(server.localAddress()); - try { - // The client connection on the server side - Channel peer = peerRef.exchange(null, 1L, SECONDS); - - // Write the message - client.writeAndFlush(newOneMessage()) - .syncUninterruptibly(); - - // channelRead(1) - peer.read(); - assertTrue(latch.await(1L, SECONDS)); - assertTrue(flow.isQueueEmpty()); - - Throwable cause = causeRef.get(); - if (cause != null) { - throw cause; - } - } finally { - client.close(); - server.close(); - } - } - - @Test - public void testSwallowedReadComplete() throws Exception { - final long delayMillis = 100; - final Queue userEvents = new LinkedBlockingQueue(); - final EmbeddedChannel channel = new EmbeddedChannel(false, false, - new FlowControlHandler(), - new IdleStateHandler(delayMillis, 0, 0, MILLISECONDS), - new ChannelHandler() { - @Override - public void channelActive(ChannelHandlerContext ctx) { - ctx.fireChannelActive(); - ctx.read(); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - ctx.fireChannelRead(msg); - ctx.read(); - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) { - ctx.fireChannelReadComplete(); - ctx.read(); - } - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { - if (evt instanceof IdleStateEvent) { - userEvents.add((IdleStateEvent) evt); - } - ctx.fireUserEventTriggered(evt); - } - } - ); - - channel.config().setAutoRead(false); - assertFalse(channel.config().isAutoRead()); - - channel.register(); - - // Reset read timeout by some message - assertTrue(channel.writeInbound(Unpooled.EMPTY_BUFFER)); - channel.flushInbound(); - assertEquals(Unpooled.EMPTY_BUFFER, channel.readInbound()); - - // Emulate 'no more messages in NIO channel' on the next read attempt. - channel.flushInbound(); - assertNull(channel.readInbound()); - - Thread.sleep(delayMillis + 20L); - channel.runPendingTasks(); - assertEquals(IdleStateEvent.FIRST_READER_IDLE_STATE_EVENT, userEvents.poll()); - assertFalse(channel.finish()); - } - - @Test - public void testRemoveFlowControl() throws Exception { - final CountDownLatch latch = new CountDownLatch(3); - - ChannelHandler handler = new ChannelHandler() { - @Override - public void channelActive(ChannelHandlerContext ctx) { - //do the first read - ctx.read(); - ctx.fireChannelActive(); - } - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - latch.countDown(); - ctx.fireChannelRead(msg); - } - }; - - FlowControlHandler flow = new FlowControlHandler() { - private int num; - @Override - public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception { - super.channelRead(ctx, msg); - ++num; - if (num >= 3) { - //We have received 3 messages. Remove myself later - final ChannelHandler handler = this; - ctx.channel().executor().execute(new Runnable() { - @Override - public void run() { - ctx.pipeline().remove(handler); - } - }); - } - } - }; - ChannelHandler tail = new ChannelHandler() { - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - //consume this msg - ReferenceCountUtil.release(msg); - } - }; - - Channel server = newServer(false /* no auto read */, flow, handler, tail); - Channel client = newClient(server.localAddress()); - try { - // Write one message - client.writeAndFlush(newOneMessage()).sync(); - - // We should receive 3 messages - assertTrue(latch.await(1L, SECONDS)); - assertTrue(flow.isQueueEmpty()); - } finally { - client.close(); - server.close(); - } - } - - /** - * This is a fictional message decoder. It decodes each {@code byte} - * into three strings. - */ - private static final class OneByteToThreeStringsDecoder extends ByteToMessageDecoder { - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) { - for (int i = 0; i < in.readableBytes(); i++) { - ctx.fireChannelRead("1"); - ctx.fireChannelRead("2"); - ctx.fireChannelRead("3"); - } - in.readerIndex(in.readableBytes()); - } - } -} diff --git a/handler/src/test/java/io/netty/handler/flush/FlushConsolidationHandlerTest.java b/handler/src/test/java/io/netty/handler/flush/FlushConsolidationHandlerTest.java deleted file mode 100644 index 788f0aaa23..0000000000 --- a/handler/src/test/java/io/netty/handler/flush/FlushConsolidationHandlerTest.java +++ /dev/null @@ -1,198 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.flush; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.embedded.EmbeddedChannel; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; - -import java.util.concurrent.atomic.AtomicInteger; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; - -public class FlushConsolidationHandlerTest { - - private static final int EXPLICIT_FLUSH_AFTER_FLUSHES = 3; - - @Test - public void testFlushViaScheduledTask() { - final AtomicInteger flushCount = new AtomicInteger(); - EmbeddedChannel channel = newChannel(flushCount, true); - channel.executor().execute(() -> { - // Flushes should not go through immediately, as they're scheduled as an async task - channel.flush(); - assertEquals(0, flushCount.get()); - channel.flush(); - assertEquals(0, flushCount.get()); - }); - assertEquals(1, flushCount.get()); - assertFalse(channel.finish()); - } - - @Test - public void testFlushViaThresholdOutsideOfReadLoop() { - final AtomicInteger flushCount = new AtomicInteger(); - EmbeddedChannel channel = newChannel(flushCount, true); - channel.executor().execute(() -> { - // After a given threshold, the async task should be bypassed and a flush should be triggered immediately - for (int i = 0; i < EXPLICIT_FLUSH_AFTER_FLUSHES; i++) { - channel.flush(); - } - assertEquals(1, flushCount.get()); - }); - - assertFalse(channel.finish()); - } - - @Test - public void testImmediateFlushOutsideOfReadLoop() { - final AtomicInteger flushCount = new AtomicInteger(); - EmbeddedChannel channel = newChannel(flushCount, false); - channel.flush(); - assertEquals(1, flushCount.get()); - assertFalse(channel.finish()); - } - - @Test - public void testFlushViaReadComplete() { - final AtomicInteger flushCount = new AtomicInteger(); - EmbeddedChannel channel = newChannel(flushCount, false); - // Flush should go through as there is no read loop in progress. - channel.flush(); - channel.runPendingTasks(); - assertEquals(1, flushCount.get()); - - // Simulate read loop; - channel.pipeline().fireChannelRead(1L); - assertEquals(1, flushCount.get()); - channel.pipeline().fireChannelRead(2L); - assertEquals(1, flushCount.get()); - assertNull(channel.readOutbound()); - channel.pipeline().fireChannelReadComplete(); - assertEquals(2, flushCount.get()); - // Now flush again as the read loop is complete. - channel.flush(); - channel.runPendingTasks(); - assertEquals(3, flushCount.get()); - assertEquals(1L, (Long) channel.readOutbound()); - assertEquals(2L, (Long) channel.readOutbound()); - assertNull(channel.readOutbound()); - assertFalse(channel.finish()); - } - - @Test - public void testFlushViaClose() { - final AtomicInteger flushCount = new AtomicInteger(); - EmbeddedChannel channel = newChannel(flushCount, false); - // Simulate read loop; - channel.pipeline().fireChannelRead(1L); - assertEquals(0, flushCount.get()); - assertNull(channel.readOutbound()); - channel.close(); - assertEquals(1, flushCount.get()); - assertEquals(1L, (Long) channel.readOutbound()); - assertNull(channel.readOutbound()); - assertFalse(channel.finish()); - } - - @Test - public void testFlushViaDisconnect() { - final AtomicInteger flushCount = new AtomicInteger(); - EmbeddedChannel channel = newChannel(flushCount, false); - // Simulate read loop; - channel.pipeline().fireChannelRead(1L); - assertEquals(0, flushCount.get()); - assertNull(channel.readOutbound()); - channel.disconnect(); - assertEquals(1, flushCount.get()); - assertEquals(1L, (Long) channel.readOutbound()); - assertNull(channel.readOutbound()); - assertFalse(channel.finish()); - } - - @Test - public void testFlushViaException() { - final AtomicInteger flushCount = new AtomicInteger(); - final EmbeddedChannel channel = newChannel(flushCount, false); - // Simulate read loop; - channel.pipeline().fireChannelRead(1L); - assertEquals(0, flushCount.get()); - assertNull(channel.readOutbound()); - channel.pipeline().fireExceptionCaught(new IllegalStateException()); - assertEquals(1, flushCount.get()); - assertEquals(1L, (Long) channel.readOutbound()); - assertNull(channel.readOutbound()); - assertThrows(IllegalStateException.class, new Executable() { - @Override - public void execute() throws Throwable { - channel.finish(); - } - }); - } - - @Test - public void testFlushViaRemoval() { - final AtomicInteger flushCount = new AtomicInteger(); - EmbeddedChannel channel = newChannel(flushCount, false); - // Simulate read loop; - channel.pipeline().fireChannelRead(1L); - assertEquals(0, flushCount.get()); - assertNull(channel.readOutbound()); - channel.pipeline().remove(FlushConsolidationHandler.class); - assertEquals(1, flushCount.get()); - assertEquals(1L, (Long) channel.readOutbound()); - assertNull(channel.readOutbound()); - assertFalse(channel.finish()); - } - - /** - * See https://github.com/netty/netty/issues/9923 - */ - @Test - public void testResend() throws Exception { - final AtomicInteger flushCount = new AtomicInteger(); - final EmbeddedChannel channel = newChannel(flushCount, true); - channel.writeAndFlush(1L).addListener(future -> channel.writeAndFlush(1L)); - channel.flushOutbound(); - assertEquals(1L, (Long) channel.readOutbound()); - assertEquals(1L, (Long) channel.readOutbound()); - assertNull(channel.readOutbound()); - assertFalse(channel.finish()); - } - - private static EmbeddedChannel newChannel(final AtomicInteger flushCount, boolean consolidateWhenNoReadInProgress) { - return new EmbeddedChannel( - new ChannelHandler() { - @Override - public void flush(ChannelHandlerContext ctx) { - flushCount.incrementAndGet(); - ctx.flush(); - } - }, - new FlushConsolidationHandler(EXPLICIT_FLUSH_AFTER_FLUSHES, consolidateWhenNoReadInProgress), - new ChannelHandler() { - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - ctx.writeAndFlush(msg); - } - }); - } -} diff --git a/handler/src/test/java/io/netty/handler/ipfilter/IpSubnetFilterTest.java b/handler/src/test/java/io/netty/handler/ipfilter/IpSubnetFilterTest.java deleted file mode 100644 index d72eea7bcc..0000000000 --- a/handler/src/test/java/io/netty/handler/ipfilter/IpSubnetFilterTest.java +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ipfilter; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.concurrent.Future; -import io.netty.util.internal.SocketUtils; -import org.junit.jupiter.api.Test; - -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.util.ArrayList; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - -public class IpSubnetFilterTest { - - @Test - public void testIpv4DefaultRoute() { - IpSubnetFilterRule rule = new IpSubnetFilterRule("0.0.0.0", 0, IpFilterRuleType.ACCEPT); - assertTrue(rule.matches(newSockAddress("91.114.240.43"))); - assertTrue(rule.matches(newSockAddress("10.0.0.3"))); - assertTrue(rule.matches(newSockAddress("192.168.93.2"))); - } - - @Test - public void testIpv4SubnetMaskCorrectlyHandlesIpv6() { - IpSubnetFilterRule rule = new IpSubnetFilterRule("0.0.0.0", 0, IpFilterRuleType.ACCEPT); - assertFalse(rule.matches(newSockAddress("2001:db8:abcd:0000::1"))); - } - - @Test - public void testIpv6SubnetMaskCorrectlyHandlesIpv4() { - IpSubnetFilterRule rule = new IpSubnetFilterRule("::", 0, IpFilterRuleType.ACCEPT); - assertFalse(rule.matches(newSockAddress("91.114.240.43"))); - } - - @Test - public void testIp4SubnetFilterRule() throws Exception { - IpSubnetFilterRule rule = new IpSubnetFilterRule("192.168.56.1", 24, IpFilterRuleType.ACCEPT); - for (int i = 0; i <= 255; i++) { - assertTrue(rule.matches(newSockAddress(String.format("192.168.56.%d", i)))); - } - assertFalse(rule.matches(newSockAddress("192.168.57.1"))); - - rule = new IpSubnetFilterRule("91.114.240.1", 23, IpFilterRuleType.ACCEPT); - assertTrue(rule.matches(newSockAddress("91.114.240.43"))); - assertTrue(rule.matches(newSockAddress("91.114.240.255"))); - assertTrue(rule.matches(newSockAddress("91.114.241.193"))); - assertTrue(rule.matches(newSockAddress("91.114.241.254"))); - assertFalse(rule.matches(newSockAddress("91.115.241.2"))); - } - - @Test - public void testIp6SubnetFilterRule() { - IpSubnetFilterRule rule; - - rule = new IpSubnetFilterRule("2001:db8:abcd:0000::", 52, IpFilterRuleType.ACCEPT); - assertTrue(rule.matches(newSockAddress("2001:db8:abcd:0000::1"))); - assertTrue(rule.matches(newSockAddress("2001:db8:abcd:0fff:ffff:ffff:ffff:ffff"))); - assertFalse(rule.matches(newSockAddress("2001:db8:abcd:1000::"))); - } - - @Test - public void testIp6SubnetFilterDefaultRule() { - IpFilterRule rule = new IpSubnetFilterRule("::", 0, IpFilterRuleType.ACCEPT); - assertTrue(rule.matches(newSockAddress("7FFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF"))); - assertTrue(rule.matches(newSockAddress("8000::"))); - } - - @Test - public void testIpFilterRuleHandler() throws Exception { - IpFilterRule filter0 = new IpFilterRule() { - @Override - public boolean matches(InetSocketAddress remoteAddress) { - return "192.168.57.1".equals(remoteAddress.getHostName()); - } - - @Override - public IpFilterRuleType ruleType() { - return IpFilterRuleType.REJECT; - } - }; - - RuleBasedIpFilter denyHandler = new RuleBasedIpFilter(filter0) { - private final byte[] message = {1, 2, 3, 4, 5, 6, 7}; - - @Override - protected Future channelRejected(ChannelHandlerContext ctx, InetSocketAddress remoteAddress) { - assertTrue(ctx.channel().isActive()); - assertTrue(ctx.channel().isWritable()); - assertEquals("192.168.57.1", remoteAddress.getHostName()); - - return ctx.writeAndFlush(Unpooled.wrappedBuffer(message)); - } - }; - EmbeddedChannel chDeny = newEmbeddedInetChannel("192.168.57.1", denyHandler); - ByteBuf out = chDeny.readOutbound(); - assertEquals(7, out.readableBytes()); - for (byte i = 1; i <= 7; i++) { - assertEquals(i, out.readByte()); - } - assertFalse(chDeny.isActive()); - assertFalse(chDeny.isOpen()); - - RuleBasedIpFilter allowHandler = new RuleBasedIpFilter(filter0) { - @Override - protected Future channelRejected(ChannelHandlerContext ctx, InetSocketAddress remoteAddress) { - fail(); - return null; - } - }; - EmbeddedChannel chAllow = newEmbeddedInetChannel("192.168.57.2", allowHandler); - assertTrue(chAllow.isActive()); - assertTrue(chAllow.isOpen()); - } - - @Test - public void testUniqueIpFilterHandler() { - UniqueIpFilter handler = new UniqueIpFilter(); - - EmbeddedChannel ch1 = newEmbeddedInetChannel("91.92.93.1", handler); - assertTrue(ch1.isActive()); - EmbeddedChannel ch2 = newEmbeddedInetChannel("91.92.93.2", handler); - assertTrue(ch2.isActive()); - EmbeddedChannel ch3 = newEmbeddedInetChannel("91.92.93.1", handler); - assertFalse(ch3.isActive()); - - // false means that no data is left to read/write - assertFalse(ch1.finish()); - - EmbeddedChannel ch4 = newEmbeddedInetChannel("91.92.93.1", handler); - assertTrue(ch4.isActive()); - } - - @Test - public void testBinarySearch() { - List ipSubnetFilterRuleList = new ArrayList(); - ipSubnetFilterRuleList.add(buildRejectIP("1.2.3.4", 32)); - ipSubnetFilterRuleList.add(buildRejectIP("1.1.1.1", 8)); - ipSubnetFilterRuleList.add(buildRejectIP("200.200.200.200", 32)); - ipSubnetFilterRuleList.add(buildRejectIP("108.0.0.0", 4)); - ipSubnetFilterRuleList.add(buildRejectIP("10.10.10.10", 8)); - ipSubnetFilterRuleList.add(buildRejectIP("2001:db8:abcd:0000::", 52)); - - // 1.0.0.0/8 - EmbeddedChannel ch1 = newEmbeddedInetChannel("1.1.1.1", new IpSubnetFilter(ipSubnetFilterRuleList)); - assertFalse(ch1.isActive()); - assertTrue(ch1.close().isSuccess()); - - // Nothing applies here - EmbeddedChannel ch2 = newEmbeddedInetChannel("2.2.2.2", new IpSubnetFilter(ipSubnetFilterRuleList)); - assertTrue(ch2.isActive()); - assertTrue(ch2.close().isSuccess()); - - // 108.0.0.0/4 - EmbeddedChannel ch3 = newEmbeddedInetChannel("97.100.100.100", new IpSubnetFilter(ipSubnetFilterRuleList)); - assertFalse(ch3.isActive()); - assertTrue(ch3.close().isSuccess()); - - // 200.200.200.200/32 - EmbeddedChannel ch4 = newEmbeddedInetChannel("200.200.200.200", new IpSubnetFilter(ipSubnetFilterRuleList)); - assertFalse(ch4.isActive()); - assertTrue(ch4.close().isSuccess()); - - // Nothing applies here - EmbeddedChannel ch5 = newEmbeddedInetChannel("127.0.0.1", new IpSubnetFilter(ipSubnetFilterRuleList)); - assertTrue(ch5.isActive()); - assertTrue(ch5.close().isSuccess()); - - // 10.0.0.0/8 - EmbeddedChannel ch6 = newEmbeddedInetChannel("10.1.1.2", new IpSubnetFilter(ipSubnetFilterRuleList)); - assertFalse(ch6.isActive()); - assertTrue(ch6.close().isSuccess()); - - //2001:db8:abcd:0000::/52 - EmbeddedChannel ch7 = newEmbeddedInetChannel("2001:db8:abcd:1000::", - new IpSubnetFilter(ipSubnetFilterRuleList)); - assertFalse(ch7.isActive()); - assertTrue(ch7.close().isSuccess()); - } - - private static IpSubnetFilterRule buildRejectIP(String ipAddress, int mask) { - return new IpSubnetFilterRule(ipAddress, mask, IpFilterRuleType.REJECT); - } - - private static EmbeddedChannel newEmbeddedInetChannel(final String ipAddress, ChannelHandler... handlers) { - return new EmbeddedChannel(handlers) { - @Override - protected SocketAddress remoteAddress0() { - return isActive()? SocketUtils.socketAddress(ipAddress, 5421) : null; - } - }; - } - - private static InetSocketAddress newSockAddress(String ipAddress) { - return SocketUtils.socketAddress(ipAddress, 1234); - } -} diff --git a/handler/src/test/java/io/netty/handler/ipfilter/UniqueIpFilterTest.java b/handler/src/test/java/io/netty/handler/ipfilter/UniqueIpFilterTest.java deleted file mode 100644 index f52712d4b7..0000000000 --- a/handler/src/test/java/io/netty/handler/ipfilter/UniqueIpFilterTest.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ipfilter; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.internal.SocketUtils; -import org.junit.jupiter.api.Test; - -import java.net.SocketAddress; -import java.util.concurrent.CyclicBarrier; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class UniqueIpFilterTest { - - @Test - public void testUniqueIpFilterHandler() throws Exception { - final CyclicBarrier barrier = new CyclicBarrier(2); - ExecutorService executorService = Executors.newFixedThreadPool(2); - try { - for (int round = 0; round < 10000; round++) { - final UniqueIpFilter ipFilter = new UniqueIpFilter(); - Future future1 = newChannelAsync(barrier, executorService, ipFilter); - Future future2 = newChannelAsync(barrier, executorService, ipFilter); - EmbeddedChannel ch1 = future1.get(); - EmbeddedChannel ch2 = future2.get(); - assertTrue(ch1.isActive() || ch2.isActive()); - 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(() -> { - barrier.await(); - return new EmbeddedChannel(handler) { - @Override - protected SocketAddress remoteAddress0() { - return isActive() ? SocketUtils.socketAddress("91.92.93.1", 5421) : null; - } - }; - }); - } - -} diff --git a/handler/src/test/java/io/netty/handler/logging/LoggingHandlerTest.java b/handler/src/test/java/io/netty/handler/logging/LoggingHandlerTest.java deleted file mode 100644 index bf1e2c84c1..0000000000 --- a/handler/src/test/java/io/netty/handler/logging/LoggingHandlerTest.java +++ /dev/null @@ -1,338 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.logging; - -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.core.Appender; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufHolder; -import io.netty.buffer.DefaultByteBufHolder; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelMetadata; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.CharsetUtil; - -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; -import org.mockito.ArgumentMatcher; -import org.slf4j.LoggerFactory; - -import java.net.InetSocketAddress; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import static io.netty.util.internal.StringUtil.NEWLINE; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.nullValue; -import static org.hamcrest.CoreMatchers.sameInstance; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.slf4j.Logger.ROOT_LOGGER_NAME; - -/** - * Verifies the correct functionality of the {@link LoggingHandler}. - */ -public class LoggingHandlerTest { - - private static final String LOGGER_NAME = LoggingHandler.class.getName(); - - private static final Logger rootLogger = (Logger) LoggerFactory.getLogger(ROOT_LOGGER_NAME); - private static final Logger logger = (Logger) LoggerFactory.getLogger(LOGGER_NAME); - - private static final List> oldAppenders = new ArrayList<>(); - /** - * Custom logback appender which gets used to match on log messages. - */ - private Appender appender; - - @BeforeAll - public static void beforeClass() { - for (Iterator> i = rootLogger.iteratorForAppenders(); i.hasNext();) { - Appender a = i.next(); - oldAppenders.add(a); - rootLogger.detachAppender(a); - } - - Unpooled.buffer(); - } - - @AfterAll - public static void afterClass() { - for (Appender a: oldAppenders) { - rootLogger.addAppender(a); - } - } - - @BeforeEach - @SuppressWarnings("unchecked") - public void setup() { - appender = mock(Appender.class); - logger.addAppender(appender); - } - - @AfterEach - public void teardown() { - logger.detachAppender(appender); - } - - @Test - public void shouldNotAcceptNullLogLevel() { - assertThrows(NullPointerException.class, new Executable() { - @Override - public void execute() throws Throwable { - LogLevel level = null; - new LoggingHandler(level); - } - }); - } - - @Test - public void shouldApplyCustomLogLevel() { - LoggingHandler handler = new LoggingHandler(LogLevel.INFO); - assertEquals(LogLevel.INFO, handler.level()); - } - - @Test - public void shouldLogChannelActive() { - new EmbeddedChannel(new LoggingHandler()); - verify(appender).doAppend(argThat(new RegexLogMatcher(".+ACTIVE$"))); - } - - @Test - public void shouldLogChannelWritabilityChanged() throws Exception { - EmbeddedChannel channel = new EmbeddedChannel(new LoggingHandler()); - // this is used to switch the channel to become unwritable - channel.config().setWriteBufferLowWaterMark(5); - channel.config().setWriteBufferHighWaterMark(10); - channel.write("hello"); - - // This is expected to be called 3 times: - // - Mark the channel unwritable when schedule the write on the EventLoop. - // - Mark writable when dequeue task - // - Mark unwritable when the write is actual be fired through the pipeline and hit the ChannelOutboundBuffer. - verify(appender, times(3)).doAppend(argThat(new RegexLogMatcher(".+WRITABILITY CHANGED$"))); - } - - @Test - public void shouldLogChannelRegistered() { - new EmbeddedChannel(new LoggingHandler()); - verify(appender).doAppend(argThat(new RegexLogMatcher(".+REGISTERED$"))); - } - - @Test - public void shouldLogChannelClose() throws Exception { - EmbeddedChannel channel = new EmbeddedChannel(new LoggingHandler()); - channel.close().await(); - verify(appender).doAppend(argThat(new RegexLogMatcher(".+CLOSE$"))); - } - - @Test - public void shouldLogChannelConnect() throws Exception { - EmbeddedChannel channel = new EmbeddedChannel(new LoggingHandler()); - channel.connect(new InetSocketAddress(80)).await(); - verify(appender).doAppend(argThat(new RegexLogMatcher(".+CONNECT: 0.0.0.0/0.0.0.0:80$"))); - } - - @Test - public void shouldLogChannelConnectWithLocalAddress() throws Exception { - EmbeddedChannel channel = new EmbeddedChannel(new LoggingHandler()); - channel.connect(new InetSocketAddress(80), new InetSocketAddress(81)).await(); - verify(appender).doAppend(argThat(new RegexLogMatcher( - "^\\[id: 0xembedded, L:embedded - R:embedded\\] CONNECT: 0.0.0.0/0.0.0.0:80, 0.0.0.0/0.0.0.0:81$"))); - } - - @Test - public void shouldLogChannelDisconnect() throws Exception { - EmbeddedChannel channel = new DisconnectingEmbeddedChannel(new LoggingHandler()); - channel.connect(new InetSocketAddress(80)).await(); - channel.disconnect().await(); - verify(appender).doAppend(argThat(new RegexLogMatcher(".+DISCONNECT$"))); - } - - @Test - public void shouldLogChannelInactive() throws Exception { - EmbeddedChannel channel = new EmbeddedChannel(new LoggingHandler()); - channel.pipeline().fireChannelInactive(); - verify(appender).doAppend(argThat(new RegexLogMatcher(".+INACTIVE$"))); - } - - @Test - public void shouldLogChannelBind() throws Exception { - EmbeddedChannel channel = new EmbeddedChannel(new LoggingHandler()); - channel.bind(new InetSocketAddress(80)); - verify(appender).doAppend(argThat(new RegexLogMatcher(".+BIND: 0.0.0.0/0.0.0.0:80$"))); - } - - @Test - @SuppressWarnings("RedundantStringConstructorCall") - public void shouldLogChannelUserEvent() throws Exception { - String userTriggered = "iAmCustom!"; - EmbeddedChannel channel = new EmbeddedChannel(new LoggingHandler()); - channel.pipeline().fireUserEventTriggered(new String(userTriggered)); - verify(appender).doAppend(argThat(new RegexLogMatcher(".+USER_EVENT: " + userTriggered + '$'))); - } - - @Test - public void shouldLogChannelException() throws Exception { - String msg = "illegalState"; - Throwable cause = new IllegalStateException(msg); - EmbeddedChannel channel = new EmbeddedChannel(new LoggingHandler()); - channel.pipeline().fireExceptionCaught(cause); - verify(appender).doAppend(argThat(new RegexLogMatcher( - ".+EXCEPTION: " + cause.getClass().getCanonicalName() + ": " + msg + '$'))); - } - - @Test - public void shouldLogDataWritten() throws Exception { - String msg = "hello"; - EmbeddedChannel channel = new EmbeddedChannel(new LoggingHandler()); - channel.writeOutbound(msg); - verify(appender).doAppend(argThat(new RegexLogMatcher(".+WRITE: " + msg + '$'))); - verify(appender).doAppend(argThat(new RegexLogMatcher(".+FLUSH$"))); - } - - @Test - public void shouldLogNonByteBufDataRead() throws Exception { - String msg = "hello"; - EmbeddedChannel channel = new EmbeddedChannel(new LoggingHandler()); - channel.writeInbound(msg); - verify(appender).doAppend(argThat(new RegexLogMatcher(".+READ: " + msg + '$'))); - - String handledMsg = channel.readInbound(); - assertThat(msg, is(sameInstance(handledMsg))); - assertThat(channel.readInbound(), is(nullValue())); - } - - @Test - public void shouldLogByteBufDataRead() throws Exception { - ByteBuf msg = Unpooled.copiedBuffer("hello", CharsetUtil.UTF_8); - EmbeddedChannel channel = new EmbeddedChannel(new LoggingHandler()); - channel.writeInbound(msg); - verify(appender).doAppend(argThat(new RegexLogMatcher(".+READ: " + msg.readableBytes() + "B$", true))); - - ByteBuf handledMsg = channel.readInbound(); - assertThat(msg, is(sameInstance(handledMsg))); - handledMsg.release(); - assertThat(channel.readInbound(), is(nullValue())); - } - - @Test - public void shouldLogByteBufDataReadWithSimpleFormat() throws Exception { - ByteBuf msg = Unpooled.copiedBuffer("hello", CharsetUtil.UTF_8); - EmbeddedChannel channel = new EmbeddedChannel(new LoggingHandler(LogLevel.DEBUG, ByteBufFormat.SIMPLE)); - channel.writeInbound(msg); - verify(appender).doAppend(argThat(new RegexLogMatcher(".+READ: " + msg.readableBytes() + "B$", false))); - - ByteBuf handledMsg = channel.readInbound(); - assertThat(msg, is(sameInstance(handledMsg))); - handledMsg.release(); - assertThat(channel.readInbound(), is(nullValue())); - } - - @Test - public void shouldLogEmptyByteBufDataRead() throws Exception { - ByteBuf msg = Unpooled.EMPTY_BUFFER; - EmbeddedChannel channel = new EmbeddedChannel(new LoggingHandler()); - channel.writeInbound(msg); - verify(appender).doAppend(argThat(new RegexLogMatcher(".+READ: 0B$", false))); - - ByteBuf handledMsg = channel.readInbound(); - assertThat(msg, is(sameInstance(handledMsg))); - assertThat(channel.readInbound(), is(nullValue())); - } - - @Test - public void shouldLogByteBufHolderDataRead() throws Exception { - ByteBufHolder msg = new DefaultByteBufHolder(Unpooled.copiedBuffer("hello", CharsetUtil.UTF_8)) { - @Override - public String toString() { - return "foobar"; - } - }; - - EmbeddedChannel channel = new EmbeddedChannel(new LoggingHandler()); - channel.writeInbound(msg); - verify(appender).doAppend(argThat(new RegexLogMatcher(".+READ: foobar, 5B$", true))); - - ByteBufHolder handledMsg = channel.readInbound(); - assertThat(msg, is(sameInstance(handledMsg))); - handledMsg.release(); - assertThat(channel.readInbound(), is(nullValue())); - } - - @Test - public void shouldLogChannelReadComplete() throws Exception { - ByteBuf msg = Unpooled.EMPTY_BUFFER; - EmbeddedChannel channel = new EmbeddedChannel(new LoggingHandler()); - channel.writeInbound(msg); - verify(appender).doAppend(argThat(new RegexLogMatcher(".+READ COMPLETE$"))); - } - - /** - * A custom EasyMock matcher that matches on Logback messages. - */ - private static final class RegexLogMatcher implements ArgumentMatcher { - - private final String expected; - private final boolean shouldContainNewline; - private String actualMsg; - - RegexLogMatcher(String expected) { - this(expected, false); - } - - RegexLogMatcher(String expected, boolean shouldContainNewline) { - this.expected = expected; - this.shouldContainNewline = shouldContainNewline; - } - - @Override - @SuppressWarnings("DynamicRegexReplaceableByCompiledPattern") - public boolean matches(ILoggingEvent actual) { - // Match only the first line to skip the validation of hex-dump format. - actualMsg = actual.getMessage().split("(?s)[\\r\\n]+")[0]; - if (actualMsg.matches(expected)) { - // The presence of a newline implies a hex-dump was logged - return actual.getMessage().contains(NEWLINE) == shouldContainNewline; - } - return false; - } - } - - private static final class DisconnectingEmbeddedChannel extends EmbeddedChannel { - - private DisconnectingEmbeddedChannel(ChannelHandler... handlers) { - super(handlers); - } - - @Override - public ChannelMetadata metadata() { - return new ChannelMetadata(true); - } - } -} diff --git a/handler/src/test/java/io/netty/handler/pcap/PcapWriteHandlerTest.java b/handler/src/test/java/io/netty/handler/pcap/PcapWriteHandlerTest.java deleted file mode 100644 index dd0fc68dd9..0000000000 --- a/handler/src/test/java/io/netty/handler/pcap/PcapWriteHandlerTest.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.pcap; - -import io.netty.bootstrap.Bootstrap; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufOutputStream; -import io.netty.buffer.ByteBufUtil; -import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.DatagramPacket; -import io.netty.channel.socket.nio.NioDatagramChannel; -import io.netty.util.CharsetUtil; -import io.netty.util.NetUtil; -import org.junit.jupiter.api.Test; - -import java.net.Inet4Address; -import java.net.InetSocketAddress; - -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class PcapWriteHandlerTest { - - @Test - public void udpV4() throws Exception { - - ByteBuf byteBuf = Unpooled.buffer(); - - InetSocketAddress srvReqAddr = new InetSocketAddress("127.0.0.1", 0); - InetSocketAddress cltReqAddr = new InetSocketAddress("127.0.0.1", 0); - - EventLoopGroup eventLoopGroup = new MultithreadEventLoopGroup(2, NioHandler.newFactory()); - - // We'll bootstrap a UDP Server to avoid "Network Unreachable errors" when sending UDP Packet. - Bootstrap server = new Bootstrap() - .group(eventLoopGroup) - .channel(NioDatagramChannel.class) - .handler(new SimpleChannelInboundHandler() { - @Override - protected void messageReceived(ChannelHandlerContext ctx, DatagramPacket msg) { - // Discard - } - }); - - Channel channelServer = server.bind(srvReqAddr).get(); - - // We'll bootstrap a UDP Client for sending UDP Packets to UDP Server. - Bootstrap client = new Bootstrap() - .group(eventLoopGroup) - .channel(NioDatagramChannel.class) - .handler(new PcapWriteHandler(new ByteBufOutputStream(byteBuf))); - - Channel channelClient = - client.connect(channelServer.localAddress(), cltReqAddr).get(); - assertTrue(channelClient.writeAndFlush(Unpooled.wrappedBuffer("Meow".getBytes())).sync().isSuccess()); - assertTrue(eventLoopGroup.shutdownGracefully().sync().isSuccess()); - - // Verify Pcap Global Headers - assertEquals(0xa1b2c3d4, byteBuf.readInt()); // magic_number - assertEquals(2, byteBuf.readShort()); // version_major - assertEquals(4, byteBuf.readShort()); // version_minor - assertEquals(0, byteBuf.readInt()); // thiszone - assertEquals(0, byteBuf.readInt()); // sigfigs - assertEquals(0xffff, byteBuf.readInt()); // snaplen - assertEquals(1, byteBuf.readInt()); // network - - // Verify Pcap Packet Header - byteBuf.readInt(); // Just read, we don't care about timestamps for now - byteBuf.readInt(); // Just read, we don't care about timestamps for now - assertEquals(46, byteBuf.readInt()); // Length of Packet Saved In Pcap - assertEquals(46, byteBuf.readInt()); // Actual Length of Packet - - // -------------------------------------------- Verify Packet -------------------------------------------- - // Verify Ethernet Packet - ByteBuf ethernetPacket = byteBuf.readBytes(46); - ByteBuf dstMac = ethernetPacket.readBytes(6); - ByteBuf srcMac = ethernetPacket.readBytes(6); - assertArrayEquals(new byte[]{0, 0, 94, 0, 83, -1}, ByteBufUtil.getBytes(dstMac)); - assertArrayEquals(new byte[]{0, 0, 94, 0, 83, 0}, ByteBufUtil.getBytes(srcMac)); - assertEquals(0x0800, ethernetPacket.readShort()); - - // Verify IPv4 Packet - ByteBuf ipv4Packet = ethernetPacket.readBytes(32); - assertEquals(0x45, ipv4Packet.readByte()); // Version + IHL - assertEquals(0x00, ipv4Packet.readByte()); // DSCP - assertEquals(32, ipv4Packet.readShort()); // Length - assertEquals(0x0000, ipv4Packet.readShort()); // Identification - assertEquals(0x0000, ipv4Packet.readShort()); // Fragment - assertEquals((byte) 0xff, ipv4Packet.readByte()); // TTL - assertEquals((byte) 17, ipv4Packet.readByte()); // Protocol - assertEquals(0, ipv4Packet.readShort()); // Checksum - InetSocketAddress localAddr = (InetSocketAddress) channelClient.remoteAddress(); - // Source IPv4 Address - assertEquals(NetUtil.ipv4AddressToInt((Inet4Address) localAddr.getAddress()), ipv4Packet.readInt()); - InetSocketAddress remoteAddr = (InetSocketAddress) channelClient.localAddress(); - // Destination IPv4 Address - assertEquals(NetUtil.ipv4AddressToInt((Inet4Address) remoteAddr.getAddress()), ipv4Packet.readInt()); - - // Verify UDP Packet - ByteBuf udpPacket = ipv4Packet.readBytes(12); - assertEquals(remoteAddr.getPort() & 0xffff, udpPacket.readUnsignedShort()); // Source Port - assertEquals(localAddr.getPort() & 0xffff, udpPacket.readUnsignedShort()); // Destination Port - assertEquals(12, udpPacket.readShort()); // Length - assertEquals(0x0001, udpPacket.readShort()); // Checksum - - // Verify Payload - ByteBuf payload = udpPacket.readBytes(4); - assertArrayEquals("Meow".getBytes(CharsetUtil.UTF_8), ByteBufUtil.getBytes(payload)); // Payload - - // Release all ByteBuf - assertTrue(dstMac.release()); - assertTrue(srcMac.release()); - assertTrue(payload.release()); - assertTrue(byteBuf.release()); - assertTrue(ethernetPacket.release()); - assertTrue(ipv4Packet.release()); - assertTrue(udpPacket.release()); - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/AmazonCorrettoSslEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/AmazonCorrettoSslEngineTest.java deleted file mode 100644 index 498e734f0d..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/AmazonCorrettoSslEngineTest.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import com.amazon.corretto.crypto.provider.AmazonCorrettoCryptoProvider; -import com.amazon.corretto.crypto.provider.SelfTestStatus; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.condition.DisabledIf; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -import javax.crypto.Cipher; -import java.security.Security; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; - - -@DisabledIf("checkIfAccpIsDisabled") -public class AmazonCorrettoSslEngineTest extends SSLEngineTest { - - static boolean checkIfAccpIsDisabled() { - return AmazonCorrettoCryptoProvider.INSTANCE.getLoadingError() != null || - !AmazonCorrettoCryptoProvider.INSTANCE.runSelfTests().equals(SelfTestStatus.PASSED); - } - - public AmazonCorrettoSslEngineTest() { - super(SslProvider.isTlsv13Supported(SslProvider.JDK)); - } - - @Override - protected SslProvider sslClientProvider() { - return SslProvider.JDK; - } - - @Override - protected SslProvider sslServerProvider() { - return SslProvider.JDK; - } - - @BeforeEach - @Override - public void setup() { - // See https://github.com/corretto/amazon-corretto-crypto-provider/blob/develop/README.md#code - Security.insertProviderAt(AmazonCorrettoCryptoProvider.INSTANCE, 1); - - // See https://github.com/corretto/amazon-corretto-crypto-provider/blob/develop/README.md#verification-optional - try { - AmazonCorrettoCryptoProvider.INSTANCE.assertHealthy(); - String providerName = Cipher.getInstance("AES/GCM/NoPadding").getProvider().getName(); - assertEquals(AmazonCorrettoCryptoProvider.PROVIDER_NAME, providerName); - } catch (Throwable e) { - Security.removeProvider(AmazonCorrettoCryptoProvider.PROVIDER_NAME); - throw new AssertionError(e); - } - super.setup(); - } - - @AfterEach - @Override - public void tearDown() throws InterruptedException { - super.tearDown(); - - // Remove the provider again and verify that it was removed - Security.removeProvider(AmazonCorrettoCryptoProvider.PROVIDER_NAME); - assertNull(Security.getProvider(AmazonCorrettoCryptoProvider.PROVIDER_NAME)); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Disabled /* Does the JDK support a "max certificate chain length"? */ - @Override - public void testMutualAuthValidClientCertChainTooLongFailOptionalClientAuth(SSLEngineTestParam param) { - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Disabled /* Does the JDK support a "max certificate chain length"? */ - @Override - public void testMutualAuthValidClientCertChainTooLongFailRequireClientAuth(SSLEngineTestParam param) { - } - - @Override - protected boolean mySetupMutualAuthServerIsValidException(Throwable cause) { - // TODO(scott): work around for a JDK issue. The exception should be SSLHandshakeException. - return super.mySetupMutualAuthServerIsValidException(cause) || causedBySSLException(cause); - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/ApplicationProtocolNegotiationHandlerTest.java b/handler/src/test/java/io/netty/handler/ssl/ApplicationProtocolNegotiationHandlerTest.java deleted file mode 100644 index e1af2c054e..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/ApplicationProtocolNegotiationHandlerTest.java +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.channel.socket.ChannelInputShutdownEvent; -import io.netty.handler.codec.DecoderException; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; - -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLHandshakeException; -import java.security.NoSuchAlgorithmException; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; - -import static io.netty.handler.ssl.CloseNotifyTest.assertCloseNotify; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - -public class ApplicationProtocolNegotiationHandlerTest { - - @Test - public void testRemoveItselfIfNoSslHandlerPresent() throws NoSuchAlgorithmException { - ChannelHandler alpnHandler = new ApplicationProtocolNegotiationHandler(ApplicationProtocolNames.HTTP_1_1) { - @Override - protected void configurePipeline(ChannelHandlerContext ctx, String protocol) { - fail(); - } - }; - - SSLEngine engine = SSLContext.getDefault().createSSLEngine(); - // This test is mocked/simulated and doesn't go through full TLS handshake. Currently only JDK SSLEngineImpl - // client mode will generate a close_notify. - engine.setUseClientMode(true); - - EmbeddedChannel channel = new EmbeddedChannel(alpnHandler); - String msg = "msg"; - String msg2 = "msg2"; - - assertTrue(channel.writeInbound(msg)); - assertTrue(channel.writeInbound(msg2)); - assertNull(channel.pipeline().context(alpnHandler)); - assertEquals(msg, channel.readInbound()); - assertEquals(msg2, channel.readInbound()); - - assertFalse(channel.finishAndReleaseAll()); - } - - @Test - public void testHandshakeFailure() { - ChannelHandler alpnHandler = new ApplicationProtocolNegotiationHandler(ApplicationProtocolNames.HTTP_1_1) { - @Override - protected void configurePipeline(ChannelHandlerContext ctx, String protocol) { - fail(); - } - }; - - EmbeddedChannel channel = new EmbeddedChannel(alpnHandler); - SSLHandshakeException exception = new SSLHandshakeException("error"); - SslHandshakeCompletionEvent completionEvent = new SslHandshakeCompletionEvent(exception); - channel.pipeline().fireUserEventTriggered(completionEvent); - channel.pipeline().fireExceptionCaught(new DecoderException(exception)); - assertNull(channel.pipeline().context(alpnHandler)); - assertFalse(channel.finishAndReleaseAll()); - } - - @Test - public void testHandshakeSuccess() throws NoSuchAlgorithmException { - testHandshakeSuccess0(false); - } - - @Test - public void testHandshakeSuccessWithSslHandlerAddedLater() throws NoSuchAlgorithmException { - testHandshakeSuccess0(true); - } - - private static void testHandshakeSuccess0(boolean addLater) throws NoSuchAlgorithmException { - final AtomicBoolean configureCalled = new AtomicBoolean(false); - ChannelHandler alpnHandler = new ApplicationProtocolNegotiationHandler(ApplicationProtocolNames.HTTP_1_1) { - @Override - protected void configurePipeline(ChannelHandlerContext ctx, String protocol) { - configureCalled.set(true); - assertEquals(ApplicationProtocolNames.HTTP_1_1, protocol); - } - }; - - SSLEngine engine = SSLContext.getDefault().createSSLEngine(); - // This test is mocked/simulated and doesn't go through full TLS handshake. Currently only JDK SSLEngineImpl - // client mode will generate a close_notify. - engine.setUseClientMode(true); - - EmbeddedChannel channel = new EmbeddedChannel(); - if (addLater) { - channel.pipeline().addLast(alpnHandler); - channel.pipeline().addFirst(new SslHandler(engine)); - } else { - channel.pipeline().addLast(new SslHandler(engine)); - channel.pipeline().addLast(alpnHandler); - } - channel.pipeline().fireUserEventTriggered(SslHandshakeCompletionEvent.SUCCESS); - assertNull(channel.pipeline().context(alpnHandler)); - // Should produce the close_notify messages - channel.releaseOutbound(); - channel.close(); - assertCloseNotify((ByteBuf) channel.readOutbound()); - channel.finishAndReleaseAll(); - assertTrue(configureCalled.get()); - } - - @Test - public void testHandshakeSuccessButNoSslHandler() { - ChannelHandler alpnHandler = new ApplicationProtocolNegotiationHandler(ApplicationProtocolNames.HTTP_1_1) { - @Override - protected void configurePipeline(ChannelHandlerContext ctx, String protocol) { - fail(); - } - }; - final EmbeddedChannel channel = new EmbeddedChannel(alpnHandler); - channel.pipeline().fireUserEventTriggered(SslHandshakeCompletionEvent.SUCCESS); - assertNull(channel.pipeline().context(alpnHandler)); - assertThrows(IllegalStateException.class, new Executable() { - @Override - public void execute() throws Throwable { - channel.finishAndReleaseAll(); - } - }); - } - - @Test - public void testBufferMessagesUntilHandshakeComplete() throws Exception { - testBufferMessagesUntilHandshakeComplete(null); - } - - @Test - public void testBufferMessagesUntilHandshakeCompleteWithClose() throws Exception { - testBufferMessagesUntilHandshakeComplete( - new ApplicationProtocolNegotiationHandlerTest.Consumer() { - @Override - public void consume(ChannelHandlerContext ctx) { - ctx.channel().close(); - } - }); - } - - @Test - public void testBufferMessagesUntilHandshakeCompleteWithInputShutdown() throws Exception { - testBufferMessagesUntilHandshakeComplete( - new ApplicationProtocolNegotiationHandlerTest.Consumer() { - @Override - public void consume(ChannelHandlerContext ctx) { - ctx.fireUserEventTriggered(ChannelInputShutdownEvent.INSTANCE); - } - }); - } - - private void testBufferMessagesUntilHandshakeComplete(final Consumer pipelineConfigurator) - throws Exception { - final AtomicReference channelReadData = new AtomicReference(); - final AtomicBoolean channelReadCompleteCalled = new AtomicBoolean(false); - ChannelHandler alpnHandler = new ApplicationProtocolNegotiationHandler(ApplicationProtocolNames.HTTP_1_1) { - @Override - protected void configurePipeline(ChannelHandlerContext ctx, String protocol) { - assertEquals(ApplicationProtocolNames.HTTP_1_1, protocol); - ctx.pipeline().addLast(new ChannelHandler() { - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - channelReadData.set((byte[]) msg); - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) { - channelReadCompleteCalled.set(true); - } - }); - if (pipelineConfigurator != null) { - pipelineConfigurator.consume(ctx); - } - } - }; - - SSLEngine engine = SSLContext.getDefault().createSSLEngine(); - // This test is mocked/simulated and doesn't go through full TLS handshake. Currently only JDK SSLEngineImpl - // client mode will generate a close_notify. - engine.setUseClientMode(true); - - final byte[] someBytes = new byte[1024]; - - EmbeddedChannel channel = new EmbeddedChannel(new SslHandler(engine), new ChannelHandler() { - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { - if (evt == SslHandshakeCompletionEvent.SUCCESS) { - ctx.fireChannelRead(someBytes); - } - ctx.fireUserEventTriggered(evt); - } - }, alpnHandler); - channel.pipeline().fireUserEventTriggered(SslHandshakeCompletionEvent.SUCCESS); - assertNull(channel.pipeline().context(alpnHandler)); - assertArrayEquals(someBytes, channelReadData.get()); - assertTrue(channelReadCompleteCalled.get()); - assertNull(channel.readInbound()); - assertTrue(channel.finishAndReleaseAll()); - } - - private interface Consumer { - void consume(T t); - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/CipherSuiteCanaryTest.java b/handler/src/test/java/io/netty/handler/ssl/CipherSuiteCanaryTest.java deleted file mode 100644 index 96e8e165a1..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/CipherSuiteCanaryTest.java +++ /dev/null @@ -1,285 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.handler.ssl; - -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; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.channel.local.LocalAddress; -import io.netty.channel.local.LocalChannel; -import io.netty.channel.local.LocalHandler; -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.Future; -import io.netty.util.concurrent.Promise; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLEngine; -import java.net.SocketAddress; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -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 static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assumptions.assumeTrue; - -/** - * The purpose of this unit test is to act as a canary and catch changes in supported cipher suites. - */ -public class CipherSuiteCanaryTest { - - private static EventLoopGroup GROUP; - - private static SelfSignedCertificate CERT; - - 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; - } - - @BeforeAll - public static void init() throws Exception { - GROUP = new MultithreadEventLoopGroup(LocalHandler.newFactory()); - CERT = new SelfSignedCertificate(); - } - - @AfterAll - public static void destroy() { - GROUP.shutdownGracefully(); - CERT.delete(); - } - - 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); - } - assumeTrue(cipherSupported, "Unsupported cipher: " + cipher); - } - - private static SslHandler newSslHandler(SslContext sslCtx, ByteBufAllocator allocator, Executor executor) { - if (executor == null) { - return sslCtx.newHandler(allocator); - } else { - return sslCtx.newHandler(allocator, executor); - } - } - - @ParameterizedTest( - name = "{index}: serverSslProvider = {0}, clientSslProvider = {1}, rfcCipherName = {2}, delegate = {3}") - @MethodSource("parameters") - public void testHandshake(SslProvider serverSslProvider, SslProvider clientSslProvider, - String rfcCipherName, boolean delegate) throws Exception { - // 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 = Collections.singletonList(rfcCipherName); - - 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(SslProtocols.TLS_v1_2) - .build(); - - final ExecutorService executorService = delegate ? Executors.newCachedThreadPool() : null; - - 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(SslProtocols.TLS_v1_2) - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .build(); - - try { - 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(newSslHandler(sslServerContext, ch.alloc(), executorService)); - - pipeline.addLast(new SimpleChannelInboundHandler() { - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - serverPromise.cancel(); - ctx.fireChannelInactive(); - } - - @Override - public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { - if (serverPromise.trySuccess(null)) { - ctx.writeAndFlush(Unpooled.wrappedBuffer(new byte[] {'P', 'O', 'N', 'G'})); - } - 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(newSslHandler(sslClientContext, ch.alloc(), executorService)); - - pipeline.addLast(new SimpleChannelInboundHandler() { - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - clientPromise.cancel(); - ctx.fireChannelInactive(); - } - - @Override - public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { - clientPromise.trySuccess(null); - 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(Unpooled.wrappedBuffer(new byte[] {'P', 'I', 'N', 'G'})) - .syncUninterruptibly(); - - Future clientFuture = clientPromise.asFuture(); - Future serverFuture = serverPromise.asFuture(); - - assertTrue(clientFuture.await(5L, TimeUnit.SECONDS), "client timeout"); - assertTrue(serverFuture.await(5L, TimeUnit.SECONDS), "server timeout"); - - clientFuture.sync(); - serverFuture.sync(); - } finally { - client.close().sync(); - } - } finally { - server.close().sync(); - } - } finally { - ReferenceCountUtil.release(sslClientContext); - } - } finally { - ReferenceCountUtil.release(sslServerContext); - - if (executorService != null) { - executorService.shutdown(); - } - } - } - - 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).get(); - } - - 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).get(); - } - - private static List expand(String rfcCipherName) { - List dst = new ArrayList<>(); - SslProvider[] sslProviders = SslProvider.values(); - - for (SslProvider serverSslProvider : sslProviders) { - for (SslProvider clientSslProvider : sslProviders) { - if ((serverSslProvider != SslProvider.JDK || clientSslProvider != SslProvider.JDK) - && !OpenSsl.isAvailable()) { - continue; - } - - dst.add(new Object[] { serverSslProvider, clientSslProvider, rfcCipherName, true }); - dst.add(new Object[] { serverSslProvider, clientSslProvider, rfcCipherName, false }); - } - } - - if (dst.isEmpty()) { - throw new IllegalStateException(); - } - - return dst; - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/CipherSuiteConverterTest.java b/handler/src/test/java/io/netty/handler/ssl/CipherSuiteConverterTest.java deleted file mode 100644 index 77206a1631..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/CipherSuiteConverterTest.java +++ /dev/null @@ -1,415 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.ssl; - -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; -import org.junit.jupiter.api.Test; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.sameInstance; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; - -public class CipherSuiteConverterTest { - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(CipherSuiteConverterTest.class); - - @Test - public void testJ2OMappings() throws Exception { - testJ2OMapping("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", "ECDHE-ECDSA-AES128-SHA256"); - testJ2OMapping("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "ECDHE-RSA-AES128-SHA256"); - testJ2OMapping("TLS_RSA_WITH_AES_128_CBC_SHA256", "AES128-SHA256"); - testJ2OMapping("TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256", "ECDH-ECDSA-AES128-SHA256"); - testJ2OMapping("TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256", "ECDH-RSA-AES128-SHA256"); - testJ2OMapping("TLS_DHE_RSA_WITH_AES_128_CBC_SHA256", "DHE-RSA-AES128-SHA256"); - testJ2OMapping("TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", "DHE-DSS-AES128-SHA256"); - testJ2OMapping("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", "ECDHE-ECDSA-AES128-SHA"); - testJ2OMapping("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", "ECDHE-RSA-AES128-SHA"); - testJ2OMapping("TLS_RSA_WITH_AES_128_CBC_SHA", "AES128-SHA"); - testJ2OMapping("TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA", "ECDH-ECDSA-AES128-SHA"); - testJ2OMapping("TLS_ECDH_RSA_WITH_AES_128_CBC_SHA", "ECDH-RSA-AES128-SHA"); - testJ2OMapping("TLS_DHE_RSA_WITH_AES_128_CBC_SHA", "DHE-RSA-AES128-SHA"); - testJ2OMapping("TLS_DHE_DSS_WITH_AES_128_CBC_SHA", "DHE-DSS-AES128-SHA"); - testJ2OMapping("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "ECDHE-ECDSA-AES128-GCM-SHA256"); - testJ2OMapping("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "ECDHE-RSA-AES128-GCM-SHA256"); - testJ2OMapping("TLS_RSA_WITH_AES_128_GCM_SHA256", "AES128-GCM-SHA256"); - testJ2OMapping("TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256", "ECDH-ECDSA-AES128-GCM-SHA256"); - testJ2OMapping("TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256", "ECDH-RSA-AES128-GCM-SHA256"); - testJ2OMapping("TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", "DHE-RSA-AES128-GCM-SHA256"); - testJ2OMapping("TLS_DHE_DSS_WITH_AES_128_GCM_SHA256", "DHE-DSS-AES128-GCM-SHA256"); - testJ2OMapping("TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA", "ECDHE-ECDSA-DES-CBC3-SHA"); - testJ2OMapping("TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", "ECDHE-RSA-DES-CBC3-SHA"); - testJ2OMapping("SSL_RSA_WITH_3DES_EDE_CBC_SHA", "DES-CBC3-SHA"); - testJ2OMapping("TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA", "ECDH-ECDSA-DES-CBC3-SHA"); - testJ2OMapping("TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA", "ECDH-RSA-DES-CBC3-SHA"); - testJ2OMapping("SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA", "DHE-RSA-DES-CBC3-SHA"); - testJ2OMapping("SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA", "DHE-DSS-DES-CBC3-SHA"); - testJ2OMapping("TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", "ECDHE-ECDSA-RC4-SHA"); - testJ2OMapping("TLS_ECDHE_RSA_WITH_RC4_128_SHA", "ECDHE-RSA-RC4-SHA"); - testJ2OMapping("SSL_RSA_WITH_RC4_128_SHA", "RC4-SHA"); - testJ2OMapping("TLS_ECDH_ECDSA_WITH_RC4_128_SHA", "ECDH-ECDSA-RC4-SHA"); - testJ2OMapping("TLS_ECDH_RSA_WITH_RC4_128_SHA", "ECDH-RSA-RC4-SHA"); - testJ2OMapping("SSL_RSA_WITH_RC4_128_MD5", "RC4-MD5"); - testJ2OMapping("TLS_DH_anon_WITH_AES_128_GCM_SHA256", "ADH-AES128-GCM-SHA256"); - testJ2OMapping("TLS_DH_anon_WITH_AES_128_CBC_SHA256", "ADH-AES128-SHA256"); - testJ2OMapping("TLS_ECDH_anon_WITH_AES_128_CBC_SHA", "AECDH-AES128-SHA"); - testJ2OMapping("TLS_DH_anon_WITH_AES_128_CBC_SHA", "ADH-AES128-SHA"); - testJ2OMapping("TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA", "AECDH-DES-CBC3-SHA"); - testJ2OMapping("SSL_DH_anon_WITH_3DES_EDE_CBC_SHA", "ADH-DES-CBC3-SHA"); - testJ2OMapping("TLS_ECDH_anon_WITH_RC4_128_SHA", "AECDH-RC4-SHA"); - testJ2OMapping("SSL_DH_anon_WITH_RC4_128_MD5", "ADH-RC4-MD5"); - testJ2OMapping("SSL_RSA_WITH_DES_CBC_SHA", "DES-CBC-SHA"); - testJ2OMapping("SSL_DHE_RSA_WITH_DES_CBC_SHA", "DHE-RSA-DES-CBC-SHA"); - testJ2OMapping("SSL_DHE_DSS_WITH_DES_CBC_SHA", "DHE-DSS-DES-CBC-SHA"); - testJ2OMapping("SSL_DH_anon_WITH_DES_CBC_SHA", "ADH-DES-CBC-SHA"); - testJ2OMapping("SSL_RSA_EXPORT_WITH_DES40_CBC_SHA", "EXP-DES-CBC-SHA"); - testJ2OMapping("SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", "EXP-DHE-RSA-DES-CBC-SHA"); - testJ2OMapping("SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA", "EXP-DHE-DSS-DES-CBC-SHA"); - testJ2OMapping("SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA", "EXP-ADH-DES-CBC-SHA"); - testJ2OMapping("SSL_RSA_EXPORT_WITH_RC4_40_MD5", "EXP-RC4-MD5"); - testJ2OMapping("SSL_DH_anon_EXPORT_WITH_RC4_40_MD5", "EXP-ADH-RC4-MD5"); - testJ2OMapping("TLS_RSA_WITH_NULL_SHA256", "NULL-SHA256"); - testJ2OMapping("TLS_ECDHE_ECDSA_WITH_NULL_SHA", "ECDHE-ECDSA-NULL-SHA"); - testJ2OMapping("TLS_ECDHE_RSA_WITH_NULL_SHA", "ECDHE-RSA-NULL-SHA"); - testJ2OMapping("SSL_RSA_WITH_NULL_SHA", "NULL-SHA"); - testJ2OMapping("TLS_ECDH_ECDSA_WITH_NULL_SHA", "ECDH-ECDSA-NULL-SHA"); - testJ2OMapping("TLS_ECDH_RSA_WITH_NULL_SHA", "ECDH-RSA-NULL-SHA"); - testJ2OMapping("TLS_ECDH_anon_WITH_NULL_SHA", "AECDH-NULL-SHA"); - testJ2OMapping("SSL_RSA_WITH_NULL_MD5", "NULL-MD5"); - testJ2OMapping("TLS_KRB5_WITH_3DES_EDE_CBC_SHA", "KRB5-DES-CBC3-SHA"); - testJ2OMapping("TLS_KRB5_WITH_3DES_EDE_CBC_MD5", "KRB5-DES-CBC3-MD5"); - testJ2OMapping("TLS_KRB5_WITH_RC4_128_SHA", "KRB5-RC4-SHA"); - testJ2OMapping("TLS_KRB5_WITH_RC4_128_MD5", "KRB5-RC4-MD5"); - testJ2OMapping("TLS_KRB5_WITH_DES_CBC_SHA", "KRB5-DES-CBC-SHA"); - testJ2OMapping("TLS_KRB5_WITH_DES_CBC_MD5", "KRB5-DES-CBC-MD5"); - testJ2OMapping("TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA", "EXP-KRB5-DES-CBC-SHA"); - testJ2OMapping("TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5", "EXP-KRB5-DES-CBC-MD5"); - testJ2OMapping("TLS_KRB5_EXPORT_WITH_RC4_40_SHA", "EXP-KRB5-RC4-SHA"); - testJ2OMapping("TLS_KRB5_EXPORT_WITH_RC4_40_MD5", "EXP-KRB5-RC4-MD5"); - testJ2OMapping("SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5", "EXP-RC2-CBC-MD5"); - testJ2OMapping("TLS_DHE_DSS_WITH_AES_256_CBC_SHA", "DHE-DSS-AES256-SHA"); - testJ2OMapping("TLS_DHE_RSA_WITH_AES_256_CBC_SHA", "DHE-RSA-AES256-SHA"); - testJ2OMapping("TLS_DH_anon_WITH_AES_256_CBC_SHA", "ADH-AES256-SHA"); - testJ2OMapping("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", "ECDHE-ECDSA-AES256-SHA"); - testJ2OMapping("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", "ECDHE-RSA-AES256-SHA"); - testJ2OMapping("TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA", "ECDH-ECDSA-AES256-SHA"); - testJ2OMapping("TLS_ECDH_RSA_WITH_AES_256_CBC_SHA", "ECDH-RSA-AES256-SHA"); - testJ2OMapping("TLS_ECDH_anon_WITH_AES_256_CBC_SHA", "AECDH-AES256-SHA"); - testJ2OMapping("TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5", "EXP-KRB5-RC2-CBC-MD5"); - testJ2OMapping("TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA", "EXP-KRB5-RC2-CBC-SHA"); - testJ2OMapping("TLS_RSA_WITH_AES_256_CBC_SHA", "AES256-SHA"); - - // For historical reasons the CHACHA20 ciphers do not follow OpenSSL's custom naming - // convention and omits the HMAC algorithm portion of the name. - testJ2OMapping("TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", "ECDHE-RSA-CHACHA20-POLY1305"); - testJ2OMapping("TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", "ECDHE-ECDSA-CHACHA20-POLY1305"); - testJ2OMapping("TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256", "DHE-RSA-CHACHA20-POLY1305"); - testJ2OMapping("TLS_PSK_WITH_CHACHA20_POLY1305_SHA256", "PSK-CHACHA20-POLY1305"); - 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, false); - logger.info("{} => {}", javaCipherSuite, actual); - assertThat(actual, is(openSslCipherSuite)); - } - - @Test - public void testO2JMappings() throws Exception { - testO2JMapping("ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", "ECDHE-ECDSA-AES128-SHA256"); - testO2JMapping("ECDHE_RSA_WITH_AES_128_CBC_SHA256", "ECDHE-RSA-AES128-SHA256"); - testO2JMapping("RSA_WITH_AES_128_CBC_SHA256", "AES128-SHA256"); - testO2JMapping("ECDH_ECDSA_WITH_AES_128_CBC_SHA256", "ECDH-ECDSA-AES128-SHA256"); - testO2JMapping("ECDH_RSA_WITH_AES_128_CBC_SHA256", "ECDH-RSA-AES128-SHA256"); - testO2JMapping("DHE_RSA_WITH_AES_128_CBC_SHA256", "DHE-RSA-AES128-SHA256"); - testO2JMapping("DHE_DSS_WITH_AES_128_CBC_SHA256", "DHE-DSS-AES128-SHA256"); - testO2JMapping("ECDHE_ECDSA_WITH_AES_128_CBC_SHA", "ECDHE-ECDSA-AES128-SHA"); - testO2JMapping("ECDHE_RSA_WITH_AES_128_CBC_SHA", "ECDHE-RSA-AES128-SHA"); - testO2JMapping("RSA_WITH_AES_128_CBC_SHA", "AES128-SHA"); - testO2JMapping("ECDH_ECDSA_WITH_AES_128_CBC_SHA", "ECDH-ECDSA-AES128-SHA"); - testO2JMapping("ECDH_RSA_WITH_AES_128_CBC_SHA", "ECDH-RSA-AES128-SHA"); - testO2JMapping("DHE_RSA_WITH_AES_128_CBC_SHA", "DHE-RSA-AES128-SHA"); - testO2JMapping("DHE_DSS_WITH_AES_128_CBC_SHA", "DHE-DSS-AES128-SHA"); - testO2JMapping("ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "ECDHE-ECDSA-AES128-GCM-SHA256"); - testO2JMapping("ECDHE_RSA_WITH_AES_128_GCM_SHA256", "ECDHE-RSA-AES128-GCM-SHA256"); - testO2JMapping("RSA_WITH_AES_128_GCM_SHA256", "AES128-GCM-SHA256"); - testO2JMapping("ECDH_ECDSA_WITH_AES_128_GCM_SHA256", "ECDH-ECDSA-AES128-GCM-SHA256"); - testO2JMapping("ECDH_RSA_WITH_AES_128_GCM_SHA256", "ECDH-RSA-AES128-GCM-SHA256"); - testO2JMapping("DHE_RSA_WITH_AES_128_GCM_SHA256", "DHE-RSA-AES128-GCM-SHA256"); - testO2JMapping("DHE_DSS_WITH_AES_128_GCM_SHA256", "DHE-DSS-AES128-GCM-SHA256"); - testO2JMapping("ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA", "ECDHE-ECDSA-DES-CBC3-SHA"); - testO2JMapping("ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", "ECDHE-RSA-DES-CBC3-SHA"); - testO2JMapping("RSA_WITH_3DES_EDE_CBC_SHA", "DES-CBC3-SHA"); - testO2JMapping("ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA", "ECDH-ECDSA-DES-CBC3-SHA"); - testO2JMapping("ECDH_RSA_WITH_3DES_EDE_CBC_SHA", "ECDH-RSA-DES-CBC3-SHA"); - testO2JMapping("DHE_RSA_WITH_3DES_EDE_CBC_SHA", "DHE-RSA-DES-CBC3-SHA"); - testO2JMapping("DHE_DSS_WITH_3DES_EDE_CBC_SHA", "DHE-DSS-DES-CBC3-SHA"); - testO2JMapping("ECDHE_ECDSA_WITH_RC4_128_SHA", "ECDHE-ECDSA-RC4-SHA"); - testO2JMapping("ECDHE_RSA_WITH_RC4_128_SHA", "ECDHE-RSA-RC4-SHA"); - testO2JMapping("RSA_WITH_RC4_128_SHA", "RC4-SHA"); - testO2JMapping("ECDH_ECDSA_WITH_RC4_128_SHA", "ECDH-ECDSA-RC4-SHA"); - testO2JMapping("ECDH_RSA_WITH_RC4_128_SHA", "ECDH-RSA-RC4-SHA"); - testO2JMapping("RSA_WITH_RC4_128_MD5", "RC4-MD5"); - testO2JMapping("DH_anon_WITH_AES_128_GCM_SHA256", "ADH-AES128-GCM-SHA256"); - testO2JMapping("DH_anon_WITH_AES_128_CBC_SHA256", "ADH-AES128-SHA256"); - testO2JMapping("ECDH_anon_WITH_AES_128_CBC_SHA", "AECDH-AES128-SHA"); - testO2JMapping("DH_anon_WITH_AES_128_CBC_SHA", "ADH-AES128-SHA"); - testO2JMapping("ECDH_anon_WITH_3DES_EDE_CBC_SHA", "AECDH-DES-CBC3-SHA"); - testO2JMapping("DH_anon_WITH_3DES_EDE_CBC_SHA", "ADH-DES-CBC3-SHA"); - testO2JMapping("ECDH_anon_WITH_RC4_128_SHA", "AECDH-RC4-SHA"); - testO2JMapping("DH_anon_WITH_RC4_128_MD5", "ADH-RC4-MD5"); - testO2JMapping("RSA_WITH_DES_CBC_SHA", "DES-CBC-SHA"); - testO2JMapping("DHE_RSA_WITH_DES_CBC_SHA", "DHE-RSA-DES-CBC-SHA"); - testO2JMapping("DHE_DSS_WITH_DES_CBC_SHA", "DHE-DSS-DES-CBC-SHA"); - testO2JMapping("DH_anon_WITH_DES_CBC_SHA", "ADH-DES-CBC-SHA"); - testO2JMapping("RSA_EXPORT_WITH_DES_CBC_40_SHA", "EXP-DES-CBC-SHA"); - testO2JMapping("DHE_RSA_EXPORT_WITH_DES_CBC_40_SHA", "EXP-DHE-RSA-DES-CBC-SHA"); - testO2JMapping("DHE_DSS_EXPORT_WITH_DES_CBC_40_SHA", "EXP-DHE-DSS-DES-CBC-SHA"); - testO2JMapping("DH_anon_EXPORT_WITH_DES_CBC_40_SHA", "EXP-ADH-DES-CBC-SHA"); - testO2JMapping("RSA_EXPORT_WITH_RC4_40_MD5", "EXP-RC4-MD5"); - testO2JMapping("DH_anon_EXPORT_WITH_RC4_40_MD5", "EXP-ADH-RC4-MD5"); - testO2JMapping("RSA_WITH_NULL_SHA256", "NULL-SHA256"); - testO2JMapping("ECDHE_ECDSA_WITH_NULL_SHA", "ECDHE-ECDSA-NULL-SHA"); - testO2JMapping("ECDHE_RSA_WITH_NULL_SHA", "ECDHE-RSA-NULL-SHA"); - testO2JMapping("RSA_WITH_NULL_SHA", "NULL-SHA"); - testO2JMapping("ECDH_ECDSA_WITH_NULL_SHA", "ECDH-ECDSA-NULL-SHA"); - testO2JMapping("ECDH_RSA_WITH_NULL_SHA", "ECDH-RSA-NULL-SHA"); - testO2JMapping("ECDH_anon_WITH_NULL_SHA", "AECDH-NULL-SHA"); - testO2JMapping("RSA_WITH_NULL_MD5", "NULL-MD5"); - testO2JMapping("KRB5_WITH_3DES_EDE_CBC_SHA", "KRB5-DES-CBC3-SHA"); - testO2JMapping("KRB5_WITH_3DES_EDE_CBC_MD5", "KRB5-DES-CBC3-MD5"); - testO2JMapping("KRB5_WITH_RC4_128_SHA", "KRB5-RC4-SHA"); - testO2JMapping("KRB5_WITH_RC4_128_MD5", "KRB5-RC4-MD5"); - testO2JMapping("KRB5_WITH_DES_CBC_SHA", "KRB5-DES-CBC-SHA"); - testO2JMapping("KRB5_WITH_DES_CBC_MD5", "KRB5-DES-CBC-MD5"); - testO2JMapping("KRB5_EXPORT_WITH_DES_CBC_40_SHA", "EXP-KRB5-DES-CBC-SHA"); - testO2JMapping("KRB5_EXPORT_WITH_DES_CBC_40_MD5", "EXP-KRB5-DES-CBC-MD5"); - testO2JMapping("KRB5_EXPORT_WITH_RC4_40_SHA", "EXP-KRB5-RC4-SHA"); - testO2JMapping("KRB5_EXPORT_WITH_RC4_40_MD5", "EXP-KRB5-RC4-MD5"); - testO2JMapping("RSA_EXPORT_WITH_RC2_CBC_40_MD5", "EXP-RC2-CBC-MD5"); - testO2JMapping("DHE_DSS_WITH_AES_256_CBC_SHA", "DHE-DSS-AES256-SHA"); - testO2JMapping("DHE_RSA_WITH_AES_256_CBC_SHA", "DHE-RSA-AES256-SHA"); - testO2JMapping("DH_anon_WITH_AES_256_CBC_SHA", "ADH-AES256-SHA"); - testO2JMapping("ECDHE_ECDSA_WITH_AES_256_CBC_SHA", "ECDHE-ECDSA-AES256-SHA"); - testO2JMapping("ECDHE_RSA_WITH_AES_256_CBC_SHA", "ECDHE-RSA-AES256-SHA"); - testO2JMapping("ECDH_ECDSA_WITH_AES_256_CBC_SHA", "ECDH-ECDSA-AES256-SHA"); - testO2JMapping("ECDH_RSA_WITH_AES_256_CBC_SHA", "ECDH-RSA-AES256-SHA"); - testO2JMapping("ECDH_anon_WITH_AES_256_CBC_SHA", "AECDH-AES256-SHA"); - testO2JMapping("KRB5_EXPORT_WITH_RC2_CBC_40_MD5", "EXP-KRB5-RC2-CBC-MD5"); - testO2JMapping("KRB5_EXPORT_WITH_RC2_CBC_40_SHA", "EXP-KRB5-RC2-CBC-SHA"); - testO2JMapping("RSA_WITH_AES_256_CBC_SHA", "AES256-SHA"); - - // Test the known mappings that actually do not exist in Java - testO2JMapping("EDH_DSS_WITH_3DES_EDE_CBC_SHA", "EDH-DSS-DES-CBC3-SHA"); - testO2JMapping("RSA_WITH_SEED_SHA", "SEED-SHA"); - testO2JMapping("RSA_WITH_CAMELLIA128_SHA", "CAMELLIA128-SHA"); - testO2JMapping("RSA_WITH_IDEA_CBC_SHA", "IDEA-CBC-SHA"); - testO2JMapping("PSK_WITH_AES_128_CBC_SHA", "PSK-AES128-CBC-SHA"); - testO2JMapping("PSK_WITH_3DES_EDE_CBC_SHA", "PSK-3DES-EDE-CBC-SHA"); - testO2JMapping("KRB5_WITH_IDEA_CBC_SHA", "KRB5-IDEA-CBC-SHA"); - testO2JMapping("KRB5_WITH_IDEA_CBC_MD5", "KRB5-IDEA-CBC-MD5"); - testO2JMapping("PSK_WITH_RC4_128_SHA", "PSK-RC4-SHA"); - testO2JMapping("ECDHE_RSA_WITH_AES_256_GCM_SHA384", "ECDHE-RSA-AES256-GCM-SHA384"); - testO2JMapping("ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "ECDHE-ECDSA-AES256-GCM-SHA384"); - testO2JMapping("ECDHE_RSA_WITH_AES_256_CBC_SHA384", "ECDHE-RSA-AES256-SHA384"); - testO2JMapping("ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", "ECDHE-ECDSA-AES256-SHA384"); - testO2JMapping("DHE_DSS_WITH_AES_256_GCM_SHA384", "DHE-DSS-AES256-GCM-SHA384"); - testO2JMapping("DHE_RSA_WITH_AES_256_GCM_SHA384", "DHE-RSA-AES256-GCM-SHA384"); - testO2JMapping("DHE_RSA_WITH_AES_256_CBC_SHA256", "DHE-RSA-AES256-SHA256"); - testO2JMapping("DHE_DSS_WITH_AES_256_CBC_SHA256", "DHE-DSS-AES256-SHA256"); - testO2JMapping("DHE_RSA_WITH_CAMELLIA256_SHA", "DHE-RSA-CAMELLIA256-SHA"); - testO2JMapping("DHE_DSS_WITH_CAMELLIA256_SHA", "DHE-DSS-CAMELLIA256-SHA"); - testO2JMapping("ECDH_RSA_WITH_AES_256_GCM_SHA384", "ECDH-RSA-AES256-GCM-SHA384"); - testO2JMapping("ECDH_ECDSA_WITH_AES_256_GCM_SHA384", "ECDH-ECDSA-AES256-GCM-SHA384"); - testO2JMapping("ECDH_RSA_WITH_AES_256_CBC_SHA384", "ECDH-RSA-AES256-SHA384"); - testO2JMapping("ECDH_ECDSA_WITH_AES_256_CBC_SHA384", "ECDH-ECDSA-AES256-SHA384"); - testO2JMapping("RSA_WITH_AES_256_GCM_SHA384", "AES256-GCM-SHA384"); - testO2JMapping("RSA_WITH_AES_256_CBC_SHA256", "AES256-SHA256"); - testO2JMapping("RSA_WITH_CAMELLIA256_SHA", "CAMELLIA256-SHA"); - testO2JMapping("PSK_WITH_AES_256_CBC_SHA", "PSK-AES256-CBC-SHA"); - testO2JMapping("DHE_RSA_WITH_SEED_SHA", "DHE-RSA-SEED-SHA"); - testO2JMapping("DHE_DSS_WITH_SEED_SHA", "DHE-DSS-SEED-SHA"); - testO2JMapping("DHE_RSA_WITH_CAMELLIA128_SHA", "DHE-RSA-CAMELLIA128-SHA"); - testO2JMapping("DHE_DSS_WITH_CAMELLIA128_SHA", "DHE-DSS-CAMELLIA128-SHA"); - testO2JMapping("EDH_RSA_WITH_3DES_EDE_CBC_SHA", "EDH-RSA-DES-CBC3-SHA"); - testO2JMapping("SRP_DSS_WITH_AES_256_CBC_SHA", "SRP-DSS-AES-256-CBC-SHA"); - testO2JMapping("SRP_RSA_WITH_AES_256_CBC_SHA", "SRP-RSA-AES-256-CBC-SHA"); - testO2JMapping("SRP_WITH_AES_256_CBC_SHA", "SRP-AES-256-CBC-SHA"); - testO2JMapping("DH_anon_WITH_AES_256_GCM_SHA384", "ADH-AES256-GCM-SHA384"); - testO2JMapping("DH_anon_WITH_AES_256_CBC_SHA256", "ADH-AES256-SHA256"); - testO2JMapping("DH_anon_WITH_CAMELLIA256_SHA", "ADH-CAMELLIA256-SHA"); - testO2JMapping("SRP_DSS_WITH_AES_128_CBC_SHA", "SRP-DSS-AES-128-CBC-SHA"); - testO2JMapping("SRP_RSA_WITH_AES_128_CBC_SHA", "SRP-RSA-AES-128-CBC-SHA"); - testO2JMapping("SRP_WITH_AES_128_CBC_SHA", "SRP-AES-128-CBC-SHA"); - testO2JMapping("DH_anon_WITH_SEED_SHA", "ADH-SEED-SHA"); - testO2JMapping("DH_anon_WITH_CAMELLIA128_SHA", "ADH-CAMELLIA128-SHA"); - testO2JMapping("RSA_WITH_RC2_CBC_MD5", "RC2-CBC-MD5"); - testO2JMapping("SRP_DSS_WITH_3DES_EDE_CBC_SHA", "SRP-DSS-3DES-EDE-CBC-SHA"); - testO2JMapping("SRP_RSA_WITH_3DES_EDE_CBC_SHA", "SRP-RSA-3DES-EDE-CBC-SHA"); - testO2JMapping("SRP_WITH_3DES_EDE_CBC_SHA", "SRP-3DES-EDE-CBC-SHA"); - testO2JMapping("RSA_WITH_3DES_EDE_CBC_MD5", "DES-CBC3-MD5"); - testO2JMapping("EDH_RSA_WITH_DES_CBC_SHA", "EDH-RSA-DES-CBC-SHA"); - testO2JMapping("EDH_DSS_WITH_DES_CBC_SHA", "EDH-DSS-DES-CBC-SHA"); - testO2JMapping("RSA_WITH_DES_CBC_MD5", "DES-CBC-MD5"); - testO2JMapping("EDH_RSA_EXPORT_WITH_DES_CBC_40_SHA", "EXP-EDH-RSA-DES-CBC-SHA"); - testO2JMapping("EDH_DSS_EXPORT_WITH_DES_CBC_40_SHA", "EXP-EDH-DSS-DES-CBC-SHA"); - - // For historical reasons the CHACHA20 ciphers do not follow OpenSSL's custom naming - // convention and omits the HMAC algorithm portion of the name. - testO2JMapping("ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", "ECDHE-RSA-CHACHA20-POLY1305"); - testO2JMapping("ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", "ECDHE-ECDSA-CHACHA20-POLY1305"); - testO2JMapping("DHE_RSA_WITH_CHACHA20_POLY1305_SHA256", "DHE-RSA-CHACHA20-POLY1305"); - testO2JMapping("PSK_WITH_CHACHA20_POLY1305_SHA256", "PSK-CHACHA20-POLY1305"); - testO2JMapping("ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256", "ECDHE-PSK-CHACHA20-POLY1305"); - testO2JMapping("DHE_PSK_WITH_CHACHA20_POLY1305_SHA256", "DHE-PSK-CHACHA20-POLY1305"); - testO2JMapping("RSA_PSK_WITH_CHACHA20_POLY1305_SHA256", "RSA-PSK-CHACHA20-POLY1305"); - } - - private static void testO2JMapping(String javaCipherSuite, String openSslCipherSuite) { - final String actual = CipherSuiteConverter.toJavaUncached(openSslCipherSuite); - logger.info("{} => {}", openSslCipherSuite, actual); - assertThat(actual, is(javaCipherSuite)); - } - - @Test - public void testCachedJ2OMappings() { - testCachedJ2OMapping("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", "ECDHE-ECDSA-AES128-SHA256"); - } - - @Test - public void testUnknownOpenSSLCiphersToJava() { - testUnknownOpenSSLCiphersToJava("(NONE)"); - testUnknownOpenSSLCiphersToJava("unknown"); - testUnknownOpenSSLCiphersToJava(""); - } - - @Test - public void testUnknownJavaCiphersToOpenSSL() { - testUnknownJavaCiphersToOpenSSL("(NONE)"); - testUnknownJavaCiphersToOpenSSL("unknown"); - testUnknownJavaCiphersToOpenSSL(""); - } - - private static void testUnknownOpenSSLCiphersToJava(String openSslCipherSuite) { - CipherSuiteConverter.clearCache(); - - assertNull(CipherSuiteConverter.toJava(openSslCipherSuite, "TLS")); - assertNull(CipherSuiteConverter.toJava(openSslCipherSuite, "SSL")); - } - - private static void testUnknownJavaCiphersToOpenSSL(String javaCipherSuite) { - CipherSuiteConverter.clearCache(); - - assertNull(CipherSuiteConverter.toOpenSsl(javaCipherSuite, false)); - assertNull(CipherSuiteConverter.toOpenSsl(javaCipherSuite, true)); - } - - private static void testCachedJ2OMapping(String javaCipherSuite, String openSslCipherSuite) { - CipherSuiteConverter.clearCache(); - - // 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)); - assertThat(CipherSuiteConverter.isO2JCached(actual1, "", javaCipherSuite.substring(4)), is(true)); - assertThat(CipherSuiteConverter.isO2JCached(actual1, "SSL", "SSL_" + javaCipherSuite.substring(4)), is(true)); - assertThat(CipherSuiteConverter.isO2JCached(actual1, "TLS", "TLS_" + javaCipherSuite.substring(4)), is(true)); - - 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(actual3))); - } - - @Test - public void testCachedO2JMappings() { - testCachedO2JMapping("ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", "ECDHE-ECDSA-AES128-SHA256"); - } - - private static void testCachedO2JMapping(String javaCipherSuite, String openSslCipherSuite) { - CipherSuiteConverter.clearCache(); - - final String tlsExpected = "TLS_" + javaCipherSuite; - final String sslExpected = "SSL_" + javaCipherSuite; - - final String tlsActual1 = CipherSuiteConverter.toJava(openSslCipherSuite, "TLS"); - final String sslActual1 = CipherSuiteConverter.toJava(openSslCipherSuite, "SSL"); - assertThat(tlsActual1, is(tlsExpected)); - assertThat(sslActual1, is(sslExpected)); - - // Ensure that the cache entries have been created. - assertThat(CipherSuiteConverter.isO2JCached(openSslCipherSuite, "", javaCipherSuite), is(true)); - assertThat(CipherSuiteConverter.isO2JCached(openSslCipherSuite, "SSL", sslExpected), is(true)); - assertThat(CipherSuiteConverter.isO2JCached(openSslCipherSuite, "TLS", tlsExpected), is(true)); - assertThat(CipherSuiteConverter.isJ2OCached(tlsExpected, openSslCipherSuite), is(true)); - assertThat(CipherSuiteConverter.isJ2OCached(sslExpected, openSslCipherSuite), is(true)); - - final String tlsActual2 = CipherSuiteConverter.toJava(openSslCipherSuite, "TLS"); - final String sslActual2 = CipherSuiteConverter.toJava(openSslCipherSuite, "SSL"); - assertThat(tlsActual2, is(tlsExpected)); - assertThat(sslActual2, is(sslExpected)); - - // Test if the returned cipher strings are identical, - // so that the TLS sessions with the same cipher suite do not create many strings. - 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/CloseNotifyTest.java b/handler/src/test/java/io/netty/handler/ssl/CloseNotifyTest.java deleted file mode 100644 index d88245c476..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/CloseNotifyTest.java +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.ssl.util.InsecureTrustManagerFactory; -import io.netty.handler.ssl.util.SelfSignedCertificate; -import org.junit.jupiter.api.Timeout; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -import java.util.Collection; -import java.util.Queue; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; -import javax.annotation.Nullable; -import javax.net.ssl.SSLSession; - -import static io.netty.buffer.ByteBufUtil.writeAscii; -import static io.netty.buffer.Unpooled.EMPTY_BUFFER; -import static io.netty.handler.codec.ByteToMessageDecoder.MERGE_CUMULATOR; -import static java.nio.charset.StandardCharsets.US_ASCII; -import static java.util.Arrays.asList; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.empty; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.greaterThanOrEqualTo; -import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.notNullValue; -import static org.junit.jupiter.api.Assumptions.assumeTrue; - -public class CloseNotifyTest { - - private static final UnpooledByteBufAllocator ALLOC = UnpooledByteBufAllocator.DEFAULT; - private static final Object INACTIVE = new Object() { - @Override - public String toString() { - return "INACTIVE"; - } - }; - - static Collection data() { - return asList(new Object[][] { - { SslProvider.JDK, SslProtocols.TLS_v1_2 }, - { SslProvider.JDK, SslProtocols.TLS_v1_3 }, - { SslProvider.OPENSSL, SslProtocols.TLS_v1_2 }, - { SslProvider.OPENSSL, SslProtocols.TLS_v1_3 }, - }); - } - - @ParameterizedTest(name = "{index}: provider={0}, protocol={1}") - @Timeout(value = 5000, unit = TimeUnit.MILLISECONDS) - @MethodSource("data") - public void eventsOrder(SslProvider provider, String protocol) throws Exception { - assumeTrue(provider != SslProvider.OPENSSL || OpenSsl.isAvailable(), "OpenSSL is not available"); - - if (SslProtocols.TLS_v1_3.equals(protocol)) { - // Ensure we support TLSv1.3 - assumeTrue(SslProvider.isTlsv13Supported(provider)); - } - BlockingQueue clientEventQueue = new LinkedBlockingQueue(); - BlockingQueue serverEventQueue = new LinkedBlockingQueue(); - - EmbeddedChannel clientChannel = initChannel(provider, protocol, true, clientEventQueue); - EmbeddedChannel serverChannel = initChannel(provider, protocol, false, serverEventQueue); - - try { - // handshake: - forwardData(clientChannel, serverChannel); - forwardData(serverChannel, clientChannel); - forwardData(clientChannel, serverChannel); - forwardData(serverChannel, clientChannel); - assertThat(clientEventQueue.poll(), instanceOf(SslHandshakeCompletionEvent.class)); - assertThat(serverEventQueue.poll(), instanceOf(SslHandshakeCompletionEvent.class)); - assertThat(handshakenProtocol(clientChannel), equalTo(protocol)); - - // send data: - clientChannel.writeOutbound(writeAscii(ALLOC, "request_msg")); - forwardData(clientChannel, serverChannel); - assertThat(serverEventQueue.poll(), equalTo((Object) "request_msg")); - - // respond with data and close_notify: - serverChannel.writeOutbound(writeAscii(ALLOC, "response_msg")); - assertThat(serverChannel.finish(), is(true)); - assertThat(serverEventQueue.poll(), instanceOf(SslCloseCompletionEvent.class)); - assertThat(clientEventQueue, empty()); - - // consume server response with close_notify: - forwardAllWithCloseNotify(serverChannel, clientChannel); - assertThat(clientEventQueue.poll(), equalTo((Object) "response_msg")); - assertThat(clientEventQueue.poll(), instanceOf(SslCloseCompletionEvent.class)); - - // make sure client automatically responds with close_notify: - if (!jdkTls13(provider, protocol)) { - // JDK impl of TLSv1.3 does not automatically generate "close_notify" in response to the received - // "close_notify" alert. This is a legit behavior according to the spec: - // https://tools.ietf.org/html/rfc8446#section-6.1. Handle it differently: - assertCloseNotify((ByteBuf) clientChannel.readOutbound()); - } - } finally { - try { - clientChannel.finish(); - } finally { - serverChannel.finish(); - } - } - - if (jdkTls13(provider, protocol)) { - assertCloseNotify((ByteBuf) clientChannel.readOutbound()); - } else { - discardEmptyOutboundBuffers(clientChannel); - } - - assertThat(clientEventQueue.poll(), is(INACTIVE)); - assertThat(clientEventQueue, empty()); - assertThat(serverEventQueue.poll(), is(INACTIVE)); - assertThat(serverEventQueue, empty()); - - assertThat(clientChannel.releaseInbound(), is(false)); - assertThat(clientChannel.releaseOutbound(), is(false)); - assertThat(serverChannel.releaseInbound(), is(false)); - assertThat(serverChannel.releaseOutbound(), is(false)); - } - - private static boolean jdkTls13(SslProvider provider, String protocol) { - return provider == SslProvider.JDK && SslProtocols.TLS_v1_3.equals(protocol); - } - - private static EmbeddedChannel initChannel(SslProvider provider, String protocol, final boolean useClientMode, - final BlockingQueue eventQueue) throws Exception { - - SelfSignedCertificate ssc = new SelfSignedCertificate(); - final SslContext sslContext = (useClientMode - ? SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE) - : SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())) - .sslProvider(provider) - .protocols(protocol) - .build(); - return new EmbeddedChannel( - // use sslContext.newHandler(ALLOC) instead of new SslHandler(sslContext.newEngine(ALLOC)) to create - // non-JDK compatible OpenSSL engine that can process partial packets: - sslContext.newHandler(ALLOC), - new SimpleChannelInboundHandler() { - - @Override - protected void messageReceived(ChannelHandlerContext ctx, ByteBuf msg) { - eventQueue.add(msg.toString(US_ASCII)); - } - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { - eventQueue.add(evt); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - eventQueue.add(INACTIVE); - super.channelInactive(ctx); - } - } - ); - } - - private static void forwardData(EmbeddedChannel from, EmbeddedChannel to) { - ByteBuf in; - while ((in = from.readOutbound()) != null) { - to.writeInbound(in); - } - } - - private static void forwardAllWithCloseNotify(EmbeddedChannel from, EmbeddedChannel to) { - ByteBuf cumulation = EMPTY_BUFFER; - ByteBuf in, closeNotify = null; - while ((in = from.readOutbound()) != null) { - if (closeNotify != null) { - closeNotify.release(); - } - closeNotify = in.duplicate(); - cumulation = MERGE_CUMULATOR.cumulate(ALLOC, cumulation, in.retain()); - } - assertCloseNotify(closeNotify); - to.writeInbound(cumulation); - } - - private static String handshakenProtocol(EmbeddedChannel channel) { - SslHandler sslHandler = channel.pipeline().get(SslHandler.class); - SSLSession session = sslHandler.engine().getSession(); - return session.getProtocol(); - } - - private static void discardEmptyOutboundBuffers(EmbeddedChannel channel) { - Queue outbound = channel.outboundMessages(); - while (outbound.peek() instanceof ByteBuf) { - ByteBuf buf = (ByteBuf) outbound.peek(); - if (!buf.isReadable()) { - buf.release(); - outbound.poll(); - } else { - break; - } - } - } - - static void assertCloseNotify(@Nullable ByteBuf closeNotify) { - assertThat(closeNotify, notNullValue()); - try { - assertThat("Doesn't match expected length of close_notify alert", - closeNotify.readableBytes(), greaterThanOrEqualTo(7)); - } finally { - closeNotify.release(); - } - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/ConscryptJdkSslEngineInteropTest.java b/handler/src/test/java/io/netty/handler/ssl/ConscryptJdkSslEngineInteropTest.java deleted file mode 100644 index 9b6a39a7dd..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/ConscryptJdkSslEngineInteropTest.java +++ /dev/null @@ -1,82 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import java.security.Provider; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.condition.DisabledIf; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -import javax.net.ssl.SSLSessionContext; - - -@DisabledIf("checkConscryptDisabled") -public class ConscryptJdkSslEngineInteropTest extends SSLEngineTest { - - public ConscryptJdkSslEngineInteropTest() { - super(false); - } - - static boolean checkConscryptDisabled() { - return !Conscrypt.isAvailable(); - } - - @Override - protected SslProvider sslClientProvider() { - return SslProvider.JDK; - } - - @Override - protected SslProvider sslServerProvider() { - return SslProvider.JDK; - } - - @Override - protected Provider clientSslContextProvider() { - return Java8SslTestUtils.conscryptProvider(); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Disabled /* Does the JDK support a "max certificate chain length"? */ - @Override - public void testMutualAuthValidClientCertChainTooLongFailOptionalClientAuth(SSLEngineTestParam param) - throws Exception { - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Disabled /* Does the JDK support a "max certificate chain length"? */ - @Override - public void testMutualAuthValidClientCertChainTooLongFailRequireClientAuth(SSLEngineTestParam param) - throws Exception { - } - - @MethodSource("newTestParams") - @ParameterizedTest - @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 void invalidateSessionsAndAssert(SSLSessionContext context) { - // Not supported by conscrypt - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/ConscryptOpenSslEngineInteropTest.java b/handler/src/test/java/io/netty/handler/ssl/ConscryptOpenSslEngineInteropTest.java deleted file mode 100644 index 54b13d8a90..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/ConscryptOpenSslEngineInteropTest.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.condition.DisabledIf; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLSessionContext; - -import java.security.Provider; -import java.util.ArrayList; -import java.util.List; - -import static io.netty.handler.ssl.OpenSslTestUtils.checkShouldUseKeyManagerFactory; -import static org.junit.jupiter.api.Assumptions.assumeTrue; - -@DisabledIf("checkConscryptDisabled") -public class ConscryptOpenSslEngineInteropTest extends ConscryptSslEngineTest { - - @Override - protected List newTestParams() { - List params = super.newTestParams(); - List testParams = new ArrayList(); - for (SSLEngineTestParam param: params) { - testParams.add(new OpenSslEngineTestParam(true, param)); - testParams.add(new OpenSslEngineTestParam(false, param)); - } - return testParams; - } - - @BeforeAll - public static void checkOpenssl() { - OpenSsl.ensureAvailability(); - } - - @Override - protected SslProvider sslClientProvider() { - return SslProvider.JDK; - } - - @Override - protected SslProvider sslServerProvider() { - return SslProvider.OPENSSL; - } - - @Override - protected Provider serverSslContextProvider() { - return null; - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Disabled("TODO: Make this work with Conscrypt") - @Override - public void testMutualAuthValidClientCertChainTooLongFailOptionalClientAuth(SSLEngineTestParam param) { - super.testMutualAuthValidClientCertChainTooLongFailOptionalClientAuth(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Disabled("TODO: Make this work with Conscrypt") - @Override - public void testMutualAuthValidClientCertChainTooLongFailRequireClientAuth(SSLEngineTestParam param) { - super.testMutualAuthValidClientCertChainTooLongFailRequireClientAuth(param); - } - - @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); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testMutualAuthInvalidIntermediateCASucceedWithOptionalClientAuth(SSLEngineTestParam param) - throws Exception { - checkShouldUseKeyManagerFactory(); - super.testMutualAuthInvalidIntermediateCASucceedWithOptionalClientAuth(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testMutualAuthInvalidIntermediateCAFailWithOptionalClientAuth(SSLEngineTestParam param) - throws Exception { - checkShouldUseKeyManagerFactory(); - super.testMutualAuthInvalidIntermediateCAFailWithOptionalClientAuth(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testMutualAuthInvalidIntermediateCAFailWithRequiredClientAuth(SSLEngineTestParam param) - throws Exception { - checkShouldUseKeyManagerFactory(); - super.testMutualAuthInvalidIntermediateCAFailWithRequiredClientAuth(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testSessionAfterHandshakeKeyManagerFactory(SSLEngineTestParam param) throws Exception { - checkShouldUseKeyManagerFactory(); - super.testSessionAfterHandshakeKeyManagerFactory(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testSessionAfterHandshakeKeyManagerFactoryMutualAuth(SSLEngineTestParam param) throws Exception { - checkShouldUseKeyManagerFactory(); - super.testSessionAfterHandshakeKeyManagerFactoryMutualAuth(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testSupportedSignatureAlgorithms(SSLEngineTestParam param) throws Exception { - checkShouldUseKeyManagerFactory(); - super.testSupportedSignatureAlgorithms(param); - } - - @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); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testSessionLocalWhenNonMutualWithKeyManager(SSLEngineTestParam param) throws Exception { - checkShouldUseKeyManagerFactory(); - super.testSessionLocalWhenNonMutualWithKeyManager(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testSessionLocalWhenNonMutualWithoutKeyManager(SSLEngineTestParam param) throws Exception { - // This only really works when the KeyManagerFactory is supported as otherwise we not really know when - // we need to provide a cert. - assumeTrue(OpenSsl.supportsKeyManagerFactory()); - super.testSessionLocalWhenNonMutualWithoutKeyManager(param); - } - - @Override - protected void invalidateSessionsAndAssert(SSLSessionContext context) { - // Not supported by conscrypt - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testSessionCache(SSLEngineTestParam param) throws Exception { - assumeTrue(OpenSsl.isSessionCacheSupported()); - super.testSessionCache(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testSessionCacheTimeout(SSLEngineTestParam param) throws Exception { - assumeTrue(OpenSsl.isSessionCacheSupported()); - super.testSessionCacheTimeout(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testSessionCacheSize(SSLEngineTestParam param) throws Exception { - assumeTrue(OpenSsl.isSessionCacheSupported()); - super.testSessionCacheSize(param); - } - - @Override - protected SSLEngine wrapEngine(SSLEngine engine) { - return Java8SslTestUtils.wrapSSLEngineForTesting(engine); - } - - @SuppressWarnings("deprecation") - @Override - protected SslContext wrapContext(SSLEngineTestParam param, SslContext context) { - if (context instanceof OpenSslContext) { - if (param instanceof OpenSslEngineTestParam) { - ((OpenSslContext) context).setUseTasks(((OpenSslEngineTestParam) param).useTasks); - } - // Explicit enable the session cache as its disabled by default on the client side. - ((OpenSslContext) context).sessionContext().setSessionCacheEnabled(true); - } - return context; - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/ConscryptSslEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/ConscryptSslEngineTest.java deleted file mode 100644 index 209aca9062..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/ConscryptSslEngineTest.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.condition.DisabledIf; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -import javax.net.ssl.SSLSessionContext; -import java.security.Provider; - -@DisabledIf("checkConscryptDisabled") -public class ConscryptSslEngineTest extends SSLEngineTest { - - static boolean checkConscryptDisabled() { - return !Conscrypt.isAvailable(); - } - - public ConscryptSslEngineTest() { - super(false); - } - - @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(); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Disabled /* Does the JDK support a "max certificate chain length"? */ - @Override - public void testMutualAuthValidClientCertChainTooLongFailOptionalClientAuth(SSLEngineTestParam param) { - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Disabled /* Does the JDK support a "max certificate chain length"? */ - @Override - public void testMutualAuthValidClientCertChainTooLongFailRequireClientAuth(SSLEngineTestParam param) { - } - - @Override - protected void invalidateSessionsAndAssert(SSLSessionContext context) { - // Not supported by conscrypt - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Disabled("Possible Conscrypt bug") - @Override - public void testSessionCacheTimeout(SSLEngineTestParam param) throws Exception { - // Skip - // https://github.com/google/conscrypt/issues/851 - } - - @Disabled("Not supported") - @Override - public void testRSASSAPSS(SSLEngineTestParam param) { - // skip - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/DelegatingSslContextTest.java b/handler/src/test/java/io/netty/handler/ssl/DelegatingSslContextTest.java deleted file mode 100644 index 56b36dee37..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/DelegatingSslContextTest.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.ssl; - -import io.netty.buffer.UnpooledByteBufAllocator; -import org.junit.jupiter.api.Test; - -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLEngine; - -import static org.junit.jupiter.api.Assertions.assertArrayEquals; - -public class DelegatingSslContextTest { - private static final String[] EXPECTED_PROTOCOLS = { SslProtocols.TLS_v1_1 }; - - @Test - public void testInitEngineOnNewEngine() throws Exception { - SslContext delegating = newDelegatingSslContext(); - - SSLEngine engine = delegating.newEngine(UnpooledByteBufAllocator.DEFAULT); - assertArrayEquals(EXPECTED_PROTOCOLS, engine.getEnabledProtocols()); - - engine = delegating.newEngine(UnpooledByteBufAllocator.DEFAULT, "localhost", 9090); - assertArrayEquals(EXPECTED_PROTOCOLS, engine.getEnabledProtocols()); - } - - @Test - public void testInitEngineOnNewSslHandler() throws Exception { - SslContext delegating = newDelegatingSslContext(); - - SslHandler handler = delegating.newHandler(UnpooledByteBufAllocator.DEFAULT); - assertArrayEquals(EXPECTED_PROTOCOLS, handler.engine().getEnabledProtocols()); - - handler = delegating.newHandler(UnpooledByteBufAllocator.DEFAULT, "localhost", 9090); - assertArrayEquals(EXPECTED_PROTOCOLS, handler.engine().getEnabledProtocols()); - } - - private static SslContext newDelegatingSslContext() throws Exception { - return new DelegatingSslContext(new JdkSslContext(SSLContext.getDefault(), false, ClientAuth.NONE)) { - @Override - protected void initEngine(SSLEngine engine) { - engine.setEnabledProtocols(EXPECTED_PROTOCOLS); - } - }; - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/IdentityCipherSuiteFilterTest.java b/handler/src/test/java/io/netty/handler/ssl/IdentityCipherSuiteFilterTest.java deleted file mode 100644 index 0c0cd57eca..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/IdentityCipherSuiteFilterTest.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import org.junit.jupiter.api.Test; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import static org.junit.jupiter.api.Assertions.assertArrayEquals; - -public class IdentityCipherSuiteFilterTest { - - @Test - public void regularInstanceDefaultsToDefaultCiphers() { - List defaultCiphers = Arrays.asList("FOO", "BAR"); - Set supportedCiphers = new HashSet<>(Arrays.asList("BAZ", "QIX")); - String[] filtered = IdentityCipherSuiteFilter.INSTANCE - .filterCipherSuites(null, defaultCiphers, supportedCiphers); - assertArrayEquals(defaultCiphers.toArray(), filtered); - } - - @Test - public void alternativeInstanceDefaultsToSupportedCiphers() { - List defaultCiphers = Arrays.asList("FOO", "BAR"); - Set supportedCiphers = new HashSet<>(Arrays.asList("BAZ", "QIX")); - String[] filtered = IdentityCipherSuiteFilter.INSTANCE_DEFAULTING_TO_SUPPORTED_CIPHERS - .filterCipherSuites(null, defaultCiphers, supportedCiphers); - assertArrayEquals(supportedCiphers.toArray(), filtered); - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/Java8SslTestUtils.java b/handler/src/test/java/io/netty/handler/ssl/Java8SslTestUtils.java deleted file mode 100644 index 1021619695..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/Java8SslTestUtils.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.ssl; - -import org.conscrypt.OpenSSLProvider; - -import javax.net.ssl.SNIMatcher; -import javax.net.ssl.SNIServerName; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLParameters; -import java.io.InputStream; -import java.security.Provider; -import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; -import java.util.Arrays; -import java.util.Collections; - -import static org.junit.jupiter.api.Assertions.assertNotNull; - -public final class Java8SslTestUtils { - - private Java8SslTestUtils() { } - - static void setSNIMatcher(SSLParameters parameters, final byte[] match) { - SNIMatcher matcher = new SNIMatcher(0) { - @Override - public boolean matches(SNIServerName sniServerName) { - return Arrays.equals(match, sniServerName.getEncoded()); - } - }; - parameters.setSNIMatchers(Collections.singleton(matcher)); - } - - 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; - } - - public static X509Certificate[] loadCertCollection(String... resourceNames) - throws Exception { - CertificateFactory certFactory = CertificateFactory - .getInstance("X.509"); - - X509Certificate[] certCollection = new X509Certificate[resourceNames.length]; - for (int i = 0; i < resourceNames.length; i++) { - String resourceName = resourceNames[i]; - InputStream is = null; - try { - is = SslContextTest.class.getResourceAsStream(resourceName); - assertNotNull(is, "Cannot find " + resourceName); - certCollection[i] = (X509Certificate) certFactory - .generateCertificate(is); - } finally { - if (is != null) { - is.close(); - } - } - } - return certCollection; - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/JdkConscryptSslEngineInteropTest.java b/handler/src/test/java/io/netty/handler/ssl/JdkConscryptSslEngineInteropTest.java deleted file mode 100644 index be453dbbfa..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/JdkConscryptSslEngineInteropTest.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import java.security.Provider; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.condition.DisabledIf; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -import javax.net.ssl.SSLSessionContext; - -@DisabledIf("checkConscryptDisabled") -public class JdkConscryptSslEngineInteropTest extends SSLEngineTest { - - static boolean checkConscryptDisabled() { - return !Conscrypt.isAvailable(); - } - - public JdkConscryptSslEngineInteropTest() { - super(false); - } - - @Override - protected SslProvider sslClientProvider() { - return SslProvider.JDK; - } - - @Override - protected SslProvider sslServerProvider() { - return SslProvider.JDK; - } - - @Override - protected Provider serverSslContextProvider() { - return Java8SslTestUtils.conscryptProvider(); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Disabled("TODO: Make this work with Conscrypt") - @Override - public void testMutualAuthValidClientCertChainTooLongFailOptionalClientAuth(SSLEngineTestParam param) - throws Exception { - super.testMutualAuthValidClientCertChainTooLongFailOptionalClientAuth(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Disabled("TODO: Make this work with Conscrypt") - @Override - public void testMutualAuthValidClientCertChainTooLongFailRequireClientAuth(SSLEngineTestParam param) - throws Exception { - super.testMutualAuthValidClientCertChainTooLongFailRequireClientAuth(param); - } - - @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); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Disabled("Ignore due bug in Conscrypt") - @Override - public void testHandshakeSession(SSLEngineTestParam param) 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 - } - - @Override - protected void invalidateSessionsAndAssert(SSLSessionContext context) { - // Not supported by conscrypt - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Disabled("Possible Conscrypt bug") - @Override - public void testSessionCacheTimeout(SSLEngineTestParam param) { - // Skip - // https://github.com/google/conscrypt/issues/851 - } - - @Disabled("Not supported") - @Override - public void testRSASSAPSS(SSLEngineTestParam param) { - // skip - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/JdkOpenSslEngineInteroptTest.java b/handler/src/test/java/io/netty/handler/ssl/JdkOpenSslEngineInteroptTest.java deleted file mode 100644 index 0d166a8430..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/JdkOpenSslEngineInteroptTest.java +++ /dev/null @@ -1,256 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.condition.DisabledOnOs; -import org.junit.jupiter.api.condition.OS; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -import javax.net.ssl.SSLEngine; -import java.util.ArrayList; -import java.util.List; - -import static io.netty.handler.ssl.OpenSslTestUtils.checkShouldUseKeyManagerFactory; -import static io.netty.internal.tcnative.SSL.SSL_CVERIFY_IGNORED; -import static org.junit.jupiter.api.Assumptions.assumeTrue; - -public class JdkOpenSslEngineInteroptTest extends SSLEngineTest { - - public JdkOpenSslEngineInteroptTest() { - super(SslProvider.isTlsv13Supported(SslProvider.JDK) && - SslProvider.isTlsv13Supported(SslProvider.OPENSSL)); - } - - @Override - protected List newTestParams() { - List params = super.newTestParams(); - List testParams = new ArrayList(); - for (SSLEngineTestParam param: params) { - testParams.add(new OpenSslEngineTestParam(true, param)); - testParams.add(new OpenSslEngineTestParam(false, param)); - } - return testParams; - } - - @BeforeAll - public static void checkOpenSsl() { - OpenSsl.ensureAvailability(); - } - - @Override - protected SslProvider sslClientProvider() { - return SslProvider.JDK; - } - - @Override - protected SslProvider sslServerProvider() { - return SslProvider.OPENSSL; - } - - @MethodSource("newTestParams") - @ParameterizedTest - @DisabledOnOs(value = OS.WINDOWS, disabledReason = "Disable until figured out why this sometimes fail on the CI") - @Override - public void testMutualAuthSameCerts(SSLEngineTestParam param) throws Throwable { - super.testMutualAuthSameCerts(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @DisabledOnOs(value = OS.WINDOWS, disabledReason = "Disable until figured out why this sometimes fail on the CI") - @Override - public void testMutualAuthDiffCerts(SSLEngineTestParam param) throws Exception { - super.testMutualAuthDiffCerts(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @DisabledOnOs(value = OS.WINDOWS, disabledReason = "Disable until figured out why this sometimes fail on the CI") - @Override - public void testMutualAuthDiffCertsServerFailure(SSLEngineTestParam param) throws Exception { - super.testMutualAuthDiffCertsServerFailure(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @DisabledOnOs(value = OS.WINDOWS, disabledReason = "Disable until figured out why this sometimes fail on the CI") - @Override - public void testMutualAuthDiffCertsClientFailure(SSLEngineTestParam param) throws Exception { - super.testMutualAuthDiffCertsClientFailure(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @DisabledOnOs(value = OS.WINDOWS, disabledReason = "Disable until figured out why this sometimes fail on the CI") - @Override - public void testMutualAuthInvalidIntermediateCASucceedWithOptionalClientAuth(SSLEngineTestParam param) - throws Exception { - checkShouldUseKeyManagerFactory(); - super.testMutualAuthInvalidIntermediateCASucceedWithOptionalClientAuth(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @DisabledOnOs(value = OS.WINDOWS, disabledReason = "Disable until figured out why this sometimes fail on the CI") - @Override - public void testMutualAuthInvalidIntermediateCAFailWithOptionalClientAuth(SSLEngineTestParam param) - throws Exception { - checkShouldUseKeyManagerFactory(); - super.testMutualAuthInvalidIntermediateCAFailWithOptionalClientAuth(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @DisabledOnOs(value = OS.WINDOWS, disabledReason = "Disable until figured out why this sometimes fail on the CI") - @Override - public void testMutualAuthInvalidIntermediateCAFailWithRequiredClientAuth(SSLEngineTestParam param) - throws Exception { - checkShouldUseKeyManagerFactory(); - super.testMutualAuthInvalidIntermediateCAFailWithRequiredClientAuth(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @DisabledOnOs(value = OS.WINDOWS, disabledReason = "Disable until figured out why this sometimes fail on the CI") - @Override - public void testMutualAuthValidClientCertChainTooLongFailOptionalClientAuth(SSLEngineTestParam param) - throws Exception { - checkShouldUseKeyManagerFactory(); - super.testMutualAuthValidClientCertChainTooLongFailOptionalClientAuth(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @DisabledOnOs(value = OS.WINDOWS, disabledReason = "Disable until figured out why this sometimes fail on the CI") - @Override - public void testMutualAuthValidClientCertChainTooLongFailRequireClientAuth(SSLEngineTestParam param) - throws Exception { - checkShouldUseKeyManagerFactory(); - super.testMutualAuthValidClientCertChainTooLongFailRequireClientAuth(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testSessionAfterHandshakeKeyManagerFactoryMutualAuth(SSLEngineTestParam param) throws Exception { - checkShouldUseKeyManagerFactory(); - super.testSessionAfterHandshakeKeyManagerFactoryMutualAuth(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testSessionAfterHandshakeKeyManagerFactory(SSLEngineTestParam param) throws Exception { - checkShouldUseKeyManagerFactory(); - super.testSessionAfterHandshakeKeyManagerFactory(param); - } - - @Override - protected void mySetupMutualAuthServerInitSslHandler(SslHandler handler) { - ReferenceCountedOpenSslEngine engine = (ReferenceCountedOpenSslEngine) handler.engine(); - engine.setVerify(SSL_CVERIFY_IGNORED, 1); - } - - @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); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testHandshakeSession(SSLEngineTestParam param) throws Exception { - checkShouldUseKeyManagerFactory(); - super.testHandshakeSession(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testSupportedSignatureAlgorithms(SSLEngineTestParam param) throws Exception { - checkShouldUseKeyManagerFactory(); - super.testSupportedSignatureAlgorithms(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testSessionLocalWhenNonMutualWithKeyManager(SSLEngineTestParam param) throws Exception { - checkShouldUseKeyManagerFactory(); - super.testSessionLocalWhenNonMutualWithKeyManager(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testSessionLocalWhenNonMutualWithoutKeyManager(SSLEngineTestParam param) throws Exception { - // This only really works when the KeyManagerFactory is supported as otherwise we not really know when - // we need to provide a cert. - assumeTrue(OpenSsl.supportsKeyManagerFactory()); - super.testSessionLocalWhenNonMutualWithoutKeyManager(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testSessionCache(SSLEngineTestParam param) throws Exception { - assumeTrue(OpenSsl.isSessionCacheSupported()); - super.testSessionCache(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testSessionCacheTimeout(SSLEngineTestParam param) throws Exception { - assumeTrue(OpenSsl.isSessionCacheSupported()); - super.testSessionCacheTimeout(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testSessionCacheSize(SSLEngineTestParam param) throws Exception { - assumeTrue(OpenSsl.isSessionCacheSupported()); - super.testSessionCacheSize(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testRSASSAPSS(SSLEngineTestParam param) throws Exception { - checkShouldUseKeyManagerFactory(); - super.testRSASSAPSS(param); - } - - @Override - protected SSLEngine wrapEngine(SSLEngine engine) { - return Java8SslTestUtils.wrapSSLEngineForTesting(engine); - } - - @SuppressWarnings("deprecation") - @Override - protected SslContext wrapContext(SSLEngineTestParam param, SslContext context) { - if (context instanceof OpenSslContext && param instanceof OpenSslEngineTestParam) { - ((OpenSslContext) context).setUseTasks(((OpenSslEngineTestParam) param).useTasks); - // Explicit enable the session cache as its disabled by default on the client side. - ((OpenSslContext) context).sessionContext().setSessionCacheEnabled(true); - } - return context; - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/JdkSslClientContextTest.java b/handler/src/test/java/io/netty/handler/ssl/JdkSslClientContextTest.java deleted file mode 100644 index cea95f9446..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/JdkSslClientContextTest.java +++ /dev/null @@ -1,29 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import javax.net.ssl.SSLException; -import java.io.File; - -public class JdkSslClientContextTest extends SslContextTest { - @Override - protected SslContext newSslContext(File crtFile, File keyFile, String pass) throws SSLException { - return SslContextBuilder.forClient() - .sslProvider(SslProvider.JDK) - .keyManager(crtFile, keyFile, pass) - .build(); - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/JdkSslEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/JdkSslEngineTest.java deleted file mode 100644 index 95c7741cbd..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/JdkSslEngineTest.java +++ /dev/null @@ -1,399 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol; -import io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior; -import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior; -import io.netty.handler.ssl.JdkApplicationProtocolNegotiator.ProtocolSelector; -import io.netty.handler.ssl.util.InsecureTrustManagerFactory; -import io.netty.handler.ssl.util.SelfSignedCertificate; -import java.security.Provider; - -import io.netty.util.internal.EmptyArrays; -import org.junit.AssumptionViolatedException; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLHandshakeException; - -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class JdkSslEngineTest extends SSLEngineTest { - public enum ProviderType { - NPN_JETTY { - @Override - boolean isAvailable() { - return JettyNpnSslEngine.isAvailable(); - } - - @Override - Protocol protocol() { - return Protocol.NPN; - } - - @Override - Provider provider() { - return null; - } - }, - ALPN_JETTY { - @Override - boolean isAvailable() { - return JettyAlpnSslEngine.isAvailable(); - } - - @Override - Protocol protocol() { - return Protocol.ALPN; - } - - @Override - Provider provider() { - // Use the default provider. - return null; - } - }, - ALPN_JAVA { - @Override - boolean isAvailable() { - return JdkAlpnSslUtils.supportsAlpn(); - } - - @Override - Protocol protocol() { - return Protocol.ALPN; - } - - @Override - Provider provider() { - // Use the default provider. - return null; - } - }, - ALPN_CONSCRYPT { - private Provider provider; - - @Override - boolean isAvailable() { - return Conscrypt.isAvailable(); - } - - @Override - Protocol protocol() { - return Protocol.ALPN; - } - - @Override - Provider provider() { - try { - if (provider == null) { - provider = (Provider) Class.forName("org.conscrypt.OpenSSLProvider") - .getConstructor().newInstance(); - } - return provider; - } catch (Exception e) { - throw new IllegalStateException(e); - } - } - }; - - abstract boolean isAvailable(); - abstract Protocol protocol(); - abstract Provider provider(); - - final void activate(JdkSslEngineTest instance) { - // Typical code will not have to check this, but will get a initialization error on class load. - // Check in this test just in case we have multiple tests that just the class and we already ignored the - // initialization error. - if (!isAvailable()) { - throw tlsExtensionNotFound(protocol()); - } - instance.provider = provider(); - } - } - - private static final String PREFERRED_APPLICATION_LEVEL_PROTOCOL = "my-protocol-http2"; - private static final String FALLBACK_APPLICATION_LEVEL_PROTOCOL = "my-protocol-http1_1"; - private static final String APPLICATION_LEVEL_PROTOCOL_NOT_COMPATIBLE = "my-protocol-FOO"; - - private Provider provider; - - public JdkSslEngineTest() { - super(SslProvider.isTlsv13Supported(SslProvider.JDK)); - } - - List newJdkParams() { - List params = newTestParams(); - - List jdkParams = new ArrayList(); - for (ProviderType providerType: ProviderType.values()) { - for (SSLEngineTestParam param: params) { - jdkParams.add(new JdkSSLEngineTestParam(providerType, param)); - } - } - return jdkParams; - } - - private static final class JdkSSLEngineTestParam extends SSLEngineTestParam { - final ProviderType providerType; - JdkSSLEngineTestParam(ProviderType providerType, SSLEngineTestParam param) { - super(param.type(), param.combo(), param.delegate()); - this.providerType = providerType; - } - - @Override - public String toString() { - return "JdkSSLEngineTestParam{" + - "type=" + type() + - ", protocolCipherCombo=" + combo() + - ", delegate=" + delegate() + - ", providerType=" + providerType + - '}'; - } - } - - @MethodSource("newJdkParams") - @ParameterizedTest - public void testTlsExtension(JdkSSLEngineTestParam param) throws Exception { - try { - param.providerType.activate(this); - ApplicationProtocolConfig apn = failingNegotiator(param.providerType.protocol(), - PREFERRED_APPLICATION_LEVEL_PROTOCOL); - setupHandlers(param, apn); - runTest(); - } catch (SkipTestException e) { - // ALPN availability is dependent on the java version. If ALPN is not available because of - // java version incompatibility don't fail the test, but instead just skip the test - throw new AssumptionViolatedException("Not expected", e); - } - } - - @MethodSource("newJdkParams") - @ParameterizedTest - public void testTlsExtensionNoCompatibleProtocolsNoHandshakeFailure(JdkSSLEngineTestParam param) throws Exception { - try { - param.providerType.activate(this); - ApplicationProtocolConfig clientApn = acceptingNegotiator(param.providerType.protocol(), - PREFERRED_APPLICATION_LEVEL_PROTOCOL); - ApplicationProtocolConfig serverApn = acceptingNegotiator(param.providerType.protocol(), - APPLICATION_LEVEL_PROTOCOL_NOT_COMPATIBLE); - setupHandlers(param, serverApn, clientApn); - runTest(null); - } catch (SkipTestException e) { - // ALPN availability is dependent on the java version. If ALPN is not available because of - // java version incompatibility don't fail the test, but instead just skip the test - throw new AssumptionViolatedException("Not expected", e); - } - } - - @MethodSource("newJdkParams") - @ParameterizedTest - public void testTlsExtensionNoCompatibleProtocolsClientHandshakeFailure(JdkSSLEngineTestParam param) - throws Exception { - try { - param.providerType.activate(this); - if (param.providerType == ProviderType.NPN_JETTY) { - ApplicationProtocolConfig clientApn = failingNegotiator(param.providerType.protocol(), - PREFERRED_APPLICATION_LEVEL_PROTOCOL); - ApplicationProtocolConfig serverApn = acceptingNegotiator(param.providerType.protocol(), - APPLICATION_LEVEL_PROTOCOL_NOT_COMPATIBLE); - setupHandlers(param, serverApn, clientApn); - assertTrue(clientLatch.await(2, TimeUnit.SECONDS)); - assertTrue(clientException instanceof SSLHandshakeException); - } else { - // ALPN - SelfSignedCertificate ssc = new SelfSignedCertificate(); - JdkApplicationProtocolNegotiator clientApn = new JdkAlpnApplicationProtocolNegotiator(true, true, - PREFERRED_APPLICATION_LEVEL_PROTOCOL); - JdkApplicationProtocolNegotiator serverApn = new JdkAlpnApplicationProtocolNegotiator( - (engine, supportedProtocols) -> new ProtocolSelector() { - @Override - public void unsupported() { - } - - @Override - public String select(List protocols) { - return APPLICATION_LEVEL_PROTOCOL_NOT_COMPATIBLE; - } - }, JdkBaseApplicationProtocolNegotiator.FAIL_SELECTION_LISTENER_FACTORY, - APPLICATION_LEVEL_PROTOCOL_NOT_COMPATIBLE); - - SslContext serverSslCtx = new JdkSslServerContext(param.providerType.provider(), - ssc.certificate(), ssc.privateKey(), null, null, - IdentityCipherSuiteFilter.INSTANCE, serverApn, 0, 0); - SslContext clientSslCtx = new JdkSslClientContext(param.providerType.provider(), null, - InsecureTrustManagerFactory.INSTANCE, null, - IdentityCipherSuiteFilter.INSTANCE, clientApn, 0, 0); - - setupHandlers(param.type(), param.delegate(), new TestDelegatingSslContext(param, serverSslCtx), - new TestDelegatingSslContext(param, clientSslCtx)); - assertTrue(clientLatch.await(2, TimeUnit.SECONDS)); - // 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 - // java version incompatibility don't fail the test, but instead just skip the test - throw new AssumptionViolatedException("Not expected", e); - } - } - - @MethodSource("newJdkParams") - @ParameterizedTest - public void testTlsExtensionNoCompatibleProtocolsServerHandshakeFailure(JdkSSLEngineTestParam param) - throws Exception { - try { - param.providerType.activate(this); - ApplicationProtocolConfig clientApn = acceptingNegotiator(param.providerType.protocol(), - PREFERRED_APPLICATION_LEVEL_PROTOCOL); - ApplicationProtocolConfig serverApn = failingNegotiator(param.providerType.protocol(), - APPLICATION_LEVEL_PROTOCOL_NOT_COMPATIBLE); - setupHandlers(param, serverApn, clientApn); - assertTrue(serverLatch.await(2, TimeUnit.SECONDS)); - assertTrue(serverException instanceof SSLHandshakeException); - } catch (SkipTestException e) { - // ALPN availability is dependent on the java version. If ALPN is not available because of - // java version incompatibility don't fail the test, but instead just skip the test - throw new AssumptionViolatedException("Not expected", e); - } - } - - @MethodSource("newJdkParams") - @ParameterizedTest - public void testAlpnCompatibleProtocolsDifferentClientOrder(JdkSSLEngineTestParam param) throws Exception { - try { - param.providerType.activate(this); - if (param.providerType == ProviderType.NPN_JETTY) { - // This test only applies to ALPN. - throw tlsExtensionNotFound(param.providerType.protocol()); - } - // Even the preferred application protocol appears second in the client's list, it will be picked - // because it's the first one on server's list. - ApplicationProtocolConfig clientApn = acceptingNegotiator(Protocol.ALPN, - FALLBACK_APPLICATION_LEVEL_PROTOCOL, PREFERRED_APPLICATION_LEVEL_PROTOCOL); - ApplicationProtocolConfig serverApn = failingNegotiator(Protocol.ALPN, - PREFERRED_APPLICATION_LEVEL_PROTOCOL, FALLBACK_APPLICATION_LEVEL_PROTOCOL); - setupHandlers(param, serverApn, clientApn); - assertNull(serverException); - runTest(PREFERRED_APPLICATION_LEVEL_PROTOCOL); - } catch (SkipTestException e) { - // ALPN availability is dependent on the java version. If ALPN is not available because of - // java version incompatibility don't fail the test, but instead just skip the test - throw new AssumptionViolatedException("Not expected", e); - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testEnablingAnAlreadyDisabledSslProtocol(SSLEngineTestParam param) throws Exception { - testEnablingAnAlreadyDisabledSslProtocol(param, new String[]{}, new String[]{ SslProtocols.TLS_v1_2 }); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Disabled /* Does the JDK support a "max certificate chain length"? */ - @Override - public void testMutualAuthValidClientCertChainTooLongFailOptionalClientAuth(SSLEngineTestParam param) - throws Exception { - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Disabled /* Does the JDK support a "max certificate chain length"? */ - @Override - public void testMutualAuthValidClientCertChainTooLongFailRequireClientAuth(SSLEngineTestParam param) - throws Exception { - } - - @Override - protected boolean mySetupMutualAuthServerIsValidException(Throwable cause) { - // TODO(scott): work around for a JDK issue. The exception should be SSLHandshakeException. - return super.mySetupMutualAuthServerIsValidException(cause) || causedBySSLException(cause); - } - - private void runTest() throws Exception { - runTest(PREFERRED_APPLICATION_LEVEL_PROTOCOL); - } - - @Override - protected SslProvider sslClientProvider() { - return SslProvider.JDK; - } - - @Override - protected SslProvider sslServerProvider() { - return SslProvider.JDK; - } - - @Override - protected Provider clientSslContextProvider() { - return provider; - } - - @Override - protected Provider serverSslContextProvider() { - return provider; - } - - private static ApplicationProtocolConfig failingNegotiator(Protocol protocol, String... supportedProtocols) { - return new ApplicationProtocolConfig(protocol, - SelectorFailureBehavior.FATAL_ALERT, - SelectedListenerFailureBehavior.FATAL_ALERT, - supportedProtocols); - } - - private static ApplicationProtocolConfig acceptingNegotiator(Protocol protocol, String... supportedProtocols) { - return new ApplicationProtocolConfig(protocol, - SelectorFailureBehavior.NO_ADVERTISE, - SelectedListenerFailureBehavior.ACCEPT, - supportedProtocols); - } - - private static SkipTestException tlsExtensionNotFound(Protocol protocol) { - throw new SkipTestException(protocol + " not on classpath"); - } - - private static final class SkipTestException extends RuntimeException { - private static final long serialVersionUID = 9214869217774035223L; - - SkipTestException(String message) { - super(message); - } - } - - private static final class TestDelegatingSslContext extends DelegatingSslContext { - private final SSLEngineTestParam param; - - TestDelegatingSslContext(SSLEngineTestParam param, SslContext ctx) { - super(ctx); - this.param = param; - } - - @Override - protected void initEngine(SSLEngine engine) { - engine.setEnabledProtocols(param.protocols().toArray(EmptyArrays.EMPTY_STRINGS)); - engine.setEnabledCipherSuites(param.ciphers().toArray(EmptyArrays.EMPTY_STRINGS)); - } - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/JdkSslRenegotiateTest.java b/handler/src/test/java/io/netty/handler/ssl/JdkSslRenegotiateTest.java deleted file mode 100644 index 984f9b59cf..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/JdkSslRenegotiateTest.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -public class JdkSslRenegotiateTest extends RenegotiateTest { - - @Override - protected SslProvider serverSslProvider() { - return SslProvider.JDK; - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/JdkSslServerContextTest.java b/handler/src/test/java/io/netty/handler/ssl/JdkSslServerContextTest.java deleted file mode 100644 index 47ab3558f1..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/JdkSslServerContextTest.java +++ /dev/null @@ -1,27 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import javax.net.ssl.SSLException; -import java.io.File; - -public class JdkSslServerContextTest extends SslContextTest { - - @Override - protected SslContext newSslContext(File crtFile, File keyFile, String pass) throws SSLException { - return SslContextBuilder.forServer(crtFile, keyFile, pass).sslProvider(SslProvider.JDK).build(); - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslCachingKeyMaterialProviderTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslCachingKeyMaterialProviderTest.java deleted file mode 100644 index 969470e0fc..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslCachingKeyMaterialProviderTest.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.buffer.UnpooledByteBufAllocator; -import org.hamcrest.CoreMatchers; -import org.junit.jupiter.api.Test; - -import javax.net.ssl.KeyManagerFactory; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -public class OpenSslCachingKeyMaterialProviderTest extends OpenSslKeyMaterialProviderTest { - - @Override - protected KeyManagerFactory newKeyManagerFactory() throws Exception { - return new OpenSslCachingX509KeyManagerFactory(super.newKeyManagerFactory()); - } - - @Override - protected OpenSslKeyMaterialProvider newMaterialProvider(KeyManagerFactory factory, String password) { - return new OpenSslCachingKeyMaterialProvider(ReferenceCountedOpenSslContext.chooseX509KeyManager( - factory.getKeyManagers()), password, Integer.MAX_VALUE); - } - - @Override - protected void assertRelease(OpenSslKeyMaterial material) { - assertFalse(material.release()); - } - - @Test - public void testMaterialCached() throws Exception { - OpenSslKeyMaterialProvider provider = newMaterialProvider(newKeyManagerFactory(), 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()); - } - - @Test - public void testCacheForSunX509() throws Exception { - OpenSslCachingX509KeyManagerFactory factory = new OpenSslCachingX509KeyManagerFactory( - super.newKeyManagerFactory("SunX509")); - OpenSslKeyMaterialProvider provider = factory.newProvider(PASSWORD); - assertThat(provider, - CoreMatchers.instanceOf(OpenSslCachingKeyMaterialProvider.class)); - } - - @Test - public void testNotCacheForX509() throws Exception { - OpenSslCachingX509KeyManagerFactory factory = new OpenSslCachingX509KeyManagerFactory( - super.newKeyManagerFactory("PKIX")); - OpenSslKeyMaterialProvider provider = factory.newProvider(PASSWORD); - assertThat(provider, CoreMatchers.not( - CoreMatchers.instanceOf(OpenSslCachingKeyMaterialProvider.class))); - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslCertificateExceptionTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslCertificateExceptionTest.java deleted file mode 100644 index 6380855e55..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslCertificateExceptionTest.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.internal.tcnative.CertificateVerifier; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; - -import java.lang.reflect.Field; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - -public class OpenSslCertificateExceptionTest { - - @BeforeAll - public static void ensureOpenSsl() { - OpenSsl.ensureAvailability(); - } - - @Test - public void testValidErrorCode() throws Exception { - Field[] fields = CertificateVerifier.class.getFields(); - for (Field field : fields) { - if (field.isAccessible()) { - int errorCode = field.getInt(null); - OpenSslCertificateException exception = new OpenSslCertificateException(errorCode); - assertEquals(errorCode, exception.errorCode()); - } - } - } - - @Test - public void testNonValidErrorCode() { - assertThrows(IllegalArgumentException.class, new Executable() { - @Override - public void execute() throws Throwable { - new OpenSslCertificateException(Integer.MIN_VALUE); - } - }); - } - - @Test - public void testCanBeInstancedWhenOpenSslIsNotAvailable() { - new OpenSslCertificateException(0); - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslClientContextTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslClientContextTest.java deleted file mode 100644 index d9e8b1e2b4..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslClientContextTest.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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.handler.ssl.util.InsecureTrustManagerFactory; -import org.junit.jupiter.api.BeforeAll; - -import javax.net.ssl.SSLException; -import java.io.File; - -public class OpenSslClientContextTest extends SslContextTest { - - @BeforeAll - public static void checkOpenSsl() { - OpenSsl.ensureAvailability(); - } - - @Override - protected SslContext newSslContext(File crtFile, File keyFile, String pass) throws SSLException { - return SslContextBuilder.forClient() - .sslProvider(SslProvider.OPENSSL) - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .keyManager(crtFile, keyFile, pass) - .applicationProtocolConfig(ApplicationProtocolConfig.DISABLED) - .build(); - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslConscryptSslEngineInteropTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslConscryptSslEngineInteropTest.java deleted file mode 100644 index b5d4903938..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslConscryptSslEngineInteropTest.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.condition.DisabledIf; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLSessionContext; -import java.security.Provider; -import java.util.ArrayList; -import java.util.List; - -import static io.netty.handler.ssl.OpenSslTestUtils.checkShouldUseKeyManagerFactory; -import static org.junit.jupiter.api.Assumptions.assumeTrue; - -@DisabledIf("checkConscryptDisabled") -public class OpenSslConscryptSslEngineInteropTest extends ConscryptSslEngineTest { - @Override - protected List newTestParams() { - List params = super.newTestParams(); - List testParams = new ArrayList(); - for (SSLEngineTestParam param: params) { - testParams.add(new OpenSslEngineTestParam(true, param)); - testParams.add(new OpenSslEngineTestParam(false, param)); - } - return testParams; - } - - @BeforeAll - public static void checkOpenssl() { - OpenSsl.ensureAvailability(); - } - - @Override - protected SslProvider sslClientProvider() { - return SslProvider.OPENSSL; - } - - @Override - protected SslProvider sslServerProvider() { - return SslProvider.JDK; - } - - @Override - protected Provider clientSslContextProvider() { - return null; - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Disabled("TODO: Make this work with Conscrypt") - @Override - public void testMutualAuthValidClientCertChainTooLongFailOptionalClientAuth(SSLEngineTestParam param) { - super.testMutualAuthValidClientCertChainTooLongFailOptionalClientAuth(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Disabled("TODO: Make this work with Conscrypt") - @Override - public void testMutualAuthValidClientCertChainTooLongFailRequireClientAuth(SSLEngineTestParam param) { - super.testMutualAuthValidClientCertChainTooLongFailRequireClientAuth(param); - } - - @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); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testMutualAuthInvalidIntermediateCASucceedWithOptionalClientAuth(SSLEngineTestParam param) - throws Exception { - checkShouldUseKeyManagerFactory(); - super.testMutualAuthInvalidIntermediateCASucceedWithOptionalClientAuth(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testMutualAuthInvalidIntermediateCAFailWithOptionalClientAuth(SSLEngineTestParam param) - throws Exception { - checkShouldUseKeyManagerFactory(); - super.testMutualAuthInvalidIntermediateCAFailWithOptionalClientAuth(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testMutualAuthInvalidIntermediateCAFailWithRequiredClientAuth(SSLEngineTestParam param) - throws Exception { - checkShouldUseKeyManagerFactory(); - super.testMutualAuthInvalidIntermediateCAFailWithRequiredClientAuth(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testSessionAfterHandshakeKeyManagerFactoryMutualAuth(SSLEngineTestParam param) throws Exception { - checkShouldUseKeyManagerFactory(); - super.testSessionAfterHandshakeKeyManagerFactoryMutualAuth(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testSupportedSignatureAlgorithms(SSLEngineTestParam param) throws Exception { - checkShouldUseKeyManagerFactory(); - super.testSupportedSignatureAlgorithms(param); - } - - @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); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testSessionLocalWhenNonMutualWithKeyManager(SSLEngineTestParam param) throws Exception { - checkShouldUseKeyManagerFactory(); - super.testSessionLocalWhenNonMutualWithKeyManager(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testSessionCache(SSLEngineTestParam param) throws Exception { - assumeTrue(OpenSsl.isSessionCacheSupported()); - super.testSessionCache(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testSessionCacheTimeout(SSLEngineTestParam param) throws Exception { - assumeTrue(OpenSsl.isSessionCacheSupported()); - super.testSessionCacheTimeout(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testSessionCacheSize(SSLEngineTestParam param) throws Exception { - assumeTrue(OpenSsl.isSessionCacheSupported()); - super.testSessionCacheSize(param); - } - - @Override - protected void invalidateSessionsAndAssert(SSLSessionContext context) { - // Not supported by conscrypt - } - - @Override - protected SSLEngine wrapEngine(SSLEngine engine) { - return Java8SslTestUtils.wrapSSLEngineForTesting(engine); - } - - @SuppressWarnings("deprecation") - @Override - protected SslContext wrapContext(SSLEngineTestParam param, SslContext context) { - if (context instanceof OpenSslContext) { - if (param instanceof OpenSslEngineTestParam) { - ((OpenSslContext) context).setUseTasks(((OpenSslEngineTestParam) param).useTasks); - } - // Explicit enable the session cache as its disabled by default on the client side. - ((OpenSslContext) context).sessionContext().setSessionCacheEnabled(true); - } - return context; - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java deleted file mode 100644 index dad26c1561..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTest.java +++ /dev/null @@ -1,1578 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol; -import io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior; -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.CharsetUtil; -import io.netty.util.internal.EmptyArrays; -import io.netty.util.internal.PlatformDependent; -import org.junit.AssumptionViolatedException; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.function.Executable; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -import javax.crypto.Cipher; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; -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.SSLHandshakeException; -import javax.net.ssl.SSLParameters; -import javax.net.ssl.X509ExtendedKeyManager; -import java.net.Socket; -import java.nio.ByteBuffer; -import java.security.AlgorithmConstraints; -import java.security.AlgorithmParameters; -import java.security.CryptoPrimitive; -import java.security.Key; -import java.security.Principal; -import java.security.PrivateKey; -import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Set; -import java.util.concurrent.ThreadLocalRandom; - -import static io.netty.handler.ssl.OpenSslTestUtils.checkShouldUseKeyManagerFactory; -import static io.netty.handler.ssl.ReferenceCountedOpenSslEngine.MAX_PLAINTEXT_LENGTH; -import static io.netty.internal.tcnative.SSL.SSL_CVERIFY_IGNORED; -import static java.lang.Integer.MAX_VALUE; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assumptions.assumeTrue; - -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"; - - public OpenSslEngineTest() { - super(SslProvider.isTlsv13Supported(SslProvider.OPENSSL)); - } - - @Override - protected List newTestParams() { - List params = super.newTestParams(); - List testParams = new ArrayList(); - for (SSLEngineTestParam param: params) { - testParams.add(new OpenSslEngineTestParam(true, param)); - testParams.add(new OpenSslEngineTestParam(false, param)); - } - return testParams; - } - - @BeforeAll - public static void checkOpenSsl() { - OpenSsl.ensureAvailability(); - } - - @AfterEach - @Override - public void tearDown() throws InterruptedException { - super.tearDown(); - assertEquals(0, SSL.getLastErrorNumber(), "SSL error stack not correctly consumed"); - } - - @Override - public void testSessionAfterHandshakeKeyManagerFactory(SSLEngineTestParam param) throws Exception { - checkShouldUseKeyManagerFactory(); - super.testSessionAfterHandshakeKeyManagerFactory(param); - } - - @Override - public void testSessionAfterHandshakeKeyManagerFactoryMutualAuth(SSLEngineTestParam param) throws Exception { - checkShouldUseKeyManagerFactory(); - super.testSessionAfterHandshakeKeyManagerFactoryMutualAuth(param); - } - - @Override - public void testMutualAuthInvalidIntermediateCASucceedWithOptionalClientAuth(SSLEngineTestParam param) - throws Exception { - checkShouldUseKeyManagerFactory(); - super.testMutualAuthInvalidIntermediateCASucceedWithOptionalClientAuth(param); - } - - @Override - public void testMutualAuthInvalidIntermediateCAFailWithOptionalClientAuth(SSLEngineTestParam param) - throws Exception { - checkShouldUseKeyManagerFactory(); - super.testMutualAuthInvalidIntermediateCAFailWithOptionalClientAuth(param); - } - - @Override - public void testMutualAuthInvalidIntermediateCAFailWithRequiredClientAuth(SSLEngineTestParam param) - throws Exception { - checkShouldUseKeyManagerFactory(); - super.testMutualAuthInvalidIntermediateCAFailWithRequiredClientAuth(param); - } - - @Override - public void testMutualAuthValidClientCertChainTooLongFailOptionalClientAuth(SSLEngineTestParam param) - throws Exception { - checkShouldUseKeyManagerFactory(); - super.testMutualAuthValidClientCertChainTooLongFailOptionalClientAuth(param); - } - - @Override - public void testMutualAuthValidClientCertChainTooLongFailRequireClientAuth(SSLEngineTestParam param) - throws Exception { - checkShouldUseKeyManagerFactory(); - super.testMutualAuthValidClientCertChainTooLongFailRequireClientAuth(param); - } - - @Override - public void testHandshakeSession(SSLEngineTestParam param) throws Exception { - checkShouldUseKeyManagerFactory(); - super.testHandshakeSession(param); - } - - @Override - public void testSupportedSignatureAlgorithms(SSLEngineTestParam param) throws Exception { - checkShouldUseKeyManagerFactory(); - super.testSupportedSignatureAlgorithms(param); - } - - private static boolean isNpnSupported(String versionString) { - String[] versionStringParts = versionString.split(" ", -1); - if (versionStringParts.length == 2 && "LibreSSL".equals(versionStringParts[0])) { - String[] versionParts = versionStringParts[1].split("\\.", -1); - if (versionParts.length == 3) { - int major = Integer.parseInt(versionParts[0]); - if (major < 2) { - return true; - } - if (major > 2) { - return false; - } - int minor = Integer.parseInt(versionParts[1]); - if (minor < 6) { - return true; - } - if (minor > 6) { - return false; - } - int bugfix = Integer.parseInt(versionParts[2]); - if (bugfix > 0) { - return false; - } - } - } - return true; - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testNpn(SSLEngineTestParam param) throws Exception { - String versionString = OpenSsl.versionString(); - assumeTrue(isNpnSupported(versionString), "LibreSSL 2.6.1 removed NPN support, detected " + versionString); - ApplicationProtocolConfig apn = acceptingNegotiator(Protocol.NPN, - PREFERRED_APPLICATION_LEVEL_PROTOCOL); - setupHandlers(param, apn); - runTest(PREFERRED_APPLICATION_LEVEL_PROTOCOL); - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testAlpn(SSLEngineTestParam param) throws Exception { - assumeTrue(OpenSsl.isAlpnSupported()); - ApplicationProtocolConfig apn = acceptingNegotiator(Protocol.ALPN, - PREFERRED_APPLICATION_LEVEL_PROTOCOL); - setupHandlers(param, apn); - runTest(PREFERRED_APPLICATION_LEVEL_PROTOCOL); - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testAlpnCompatibleProtocolsDifferentClientOrder(SSLEngineTestParam param) throws Exception { - assumeTrue(OpenSsl.isAlpnSupported()); - ApplicationProtocolConfig clientApn = acceptingNegotiator(Protocol.ALPN, - FALLBACK_APPLICATION_LEVEL_PROTOCOL, PREFERRED_APPLICATION_LEVEL_PROTOCOL); - ApplicationProtocolConfig serverApn = acceptingNegotiator(Protocol.ALPN, - PREFERRED_APPLICATION_LEVEL_PROTOCOL, FALLBACK_APPLICATION_LEVEL_PROTOCOL); - setupHandlers(param, serverApn, clientApn); - assertNull(serverException); - runTest(PREFERRED_APPLICATION_LEVEL_PROTOCOL); - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testEnablingAnAlreadyDisabledSslProtocol(SSLEngineTestParam param) throws Exception { - testEnablingAnAlreadyDisabledSslProtocol(param, new String[]{SslProtocols.SSL_v2_HELLO}, - new String[]{SslProtocols.SSL_v2_HELLO, SslProtocols.TLS_v1_2}); - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testWrapBuffersNoWritePendingError(SSLEngineTestParam param) throws Exception { - clientSslCtx = wrapContext(param, SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslClientProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SelfSignedCertificate ssc = new SelfSignedCertificate(); - serverSslCtx = wrapContext(param, SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(sslServerProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SSLEngine clientEngine = null; - SSLEngine serverEngine = null; - try { - clientEngine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - serverEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - handshake(param.type(), param.delegate(), clientEngine, serverEngine); - - ByteBuffer src = allocateBuffer(param.type(), 1024 * 10); - byte[] data = new byte[src.capacity()]; - ThreadLocalRandom.current().nextBytes(data); - src.put(data).flip(); - ByteBuffer dst = allocateBuffer(param.type(), 1); - // Try to wrap multiple times so we are more likely to hit the issue. - for (int i = 0; i < 100; i++) { - src.position(0); - dst.position(0); - assertSame(SSLEngineResult.Status.BUFFER_OVERFLOW, clientEngine.wrap(src, dst).getStatus()); - } - } finally { - cleanupClientSslEngine(clientEngine); - cleanupServerSslEngine(serverEngine); - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testOnlySmallBufferNeededForWrap(SSLEngineTestParam param) throws Exception { - clientSslCtx = wrapContext(param, SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslClientProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SelfSignedCertificate ssc = new SelfSignedCertificate(); - serverSslCtx = wrapContext(param, SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(sslServerProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SSLEngine clientEngine = null; - SSLEngine serverEngine = null; - try { - clientEngine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - serverEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - handshake(param.type(), param.delegate(), clientEngine, serverEngine); - - // Allocate a buffer which is small enough and set the limit to the capacity to mark its whole content - // as readable. - int srcLen = 1024; - ByteBuffer src = allocateBuffer(param.type(), srcLen); - - ByteBuffer dstTooSmall = allocateBuffer( - param.type(), src.capacity() + unwrapEngine(clientEngine).maxWrapOverhead() - 1); - ByteBuffer dst = allocateBuffer( - param.type(), 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 - SSLEngineResult result = clientEngine.wrap(src, dstTooSmall); - assertEquals(SSLEngineResult.Status.BUFFER_OVERFLOW, result.getStatus()); - assertEquals(0, result.bytesConsumed()); - assertEquals(0, result.bytesProduced()); - assertEquals(src.remaining(), src.capacity()); - assertEquals(dst.remaining(), dst.capacity()); - - // Check that we can wrap with a dst buffer that has the capacity of - // src.capacity() + ReferenceCountedOpenSslEngine.MAX_TLS_RECORD_OVERHEAD_LENGTH - result = clientEngine.wrap(src, dst); - assertEquals(SSLEngineResult.Status.OK, result.getStatus()); - assertEquals(srcLen, result.bytesConsumed()); - assertEquals(0, src.remaining()); - assertTrue(result.bytesProduced() > srcLen); - assertEquals(src.capacity() - result.bytesConsumed(), src.remaining()); - assertEquals(dst.capacity() - result.bytesProduced(), dst.remaining()); - } finally { - cleanupClientSslEngine(clientEngine); - cleanupServerSslEngine(serverEngine); - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testNeededDstCapacityIsCorrectlyCalculated(SSLEngineTestParam param) throws Exception { - clientSslCtx = wrapContext(param, SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslClientProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SelfSignedCertificate ssc = new SelfSignedCertificate(); - serverSslCtx = wrapContext(param, SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(sslServerProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SSLEngine clientEngine = null; - SSLEngine serverEngine = null; - try { - clientEngine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - serverEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - handshake(param.type(), param.delegate(), clientEngine, serverEngine); - - ByteBuffer src = allocateBuffer(param.type(), 1024); - ByteBuffer src2 = src.duplicate(); - - ByteBuffer dst = allocateBuffer(param.type(), src.capacity() - + unwrapEngine(clientEngine).maxWrapOverhead()); - - SSLEngineResult result = clientEngine.wrap(new ByteBuffer[] { src, src2 }, dst); - assertEquals(SSLEngineResult.Status.BUFFER_OVERFLOW, result.getStatus()); - assertEquals(0, src.position()); - assertEquals(0, src2.position()); - assertEquals(0, dst.position()); - assertEquals(0, result.bytesConsumed()); - assertEquals(0, result.bytesProduced()); - } finally { - cleanupClientSslEngine(clientEngine); - cleanupServerSslEngine(serverEngine); - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testSrcsLenOverFlowCorrectlyHandled(SSLEngineTestParam param) throws Exception { - clientSslCtx = wrapContext(param, SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslClientProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SelfSignedCertificate ssc = new SelfSignedCertificate(); - serverSslCtx = wrapContext(param, SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(sslServerProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SSLEngine clientEngine = null; - SSLEngine serverEngine = null; - try { - clientEngine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - serverEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - handshake(param.type(), param.delegate(), clientEngine, serverEngine); - - ByteBuffer src = allocateBuffer(param.type(), 1024); - List srcList = new ArrayList(); - long srcsLen = 0; - long maxLen = ((long) MAX_VALUE) * 2; - - while (srcsLen < maxLen) { - ByteBuffer dup = src.duplicate(); - srcList.add(dup); - srcsLen += dup.capacity(); - } - - ByteBuffer[] srcs = srcList.toArray(new ByteBuffer[0]); - ByteBuffer dst = allocateBuffer( - param.type(), unwrapEngine(clientEngine).maxEncryptedPacketLength() - 1); - - SSLEngineResult result = clientEngine.wrap(srcs, dst); - assertEquals(SSLEngineResult.Status.BUFFER_OVERFLOW, result.getStatus()); - - for (ByteBuffer buffer : srcs) { - assertEquals(0, buffer.position()); - } - assertEquals(0, dst.position()); - assertEquals(0, result.bytesConsumed()); - assertEquals(0, result.bytesProduced()); - } finally { - cleanupClientSslEngine(clientEngine); - cleanupServerSslEngine(serverEngine); - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testCalculateOutNetBufSizeOverflow(SSLEngineTestParam param) throws SSLException { - clientSslCtx = wrapContext(param, SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslClientProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SSLEngine clientEngine = null; - try { - clientEngine = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); - int value = ((ReferenceCountedOpenSslEngine) clientEngine).calculateMaxLengthForWrap(MAX_VALUE, 1); - assertTrue(value > 0); - } finally { - cleanupClientSslEngine(clientEngine); - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testCalculateOutNetBufSize0(SSLEngineTestParam param) throws SSLException { - clientSslCtx = wrapContext(param, SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslClientProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SSLEngine clientEngine = null; - try { - clientEngine = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); - assertTrue(((ReferenceCountedOpenSslEngine) clientEngine).calculateMaxLengthForWrap(0, 1) > 0); - } finally { - cleanupClientSslEngine(clientEngine); - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testCorrectlyCalculateSpaceForAlert(SSLEngineTestParam param) throws Exception { - testCorrectlyCalculateSpaceForAlert(param, true); - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testCorrectlyCalculateSpaceForAlertJDKCompatabilityModeOff(SSLEngineTestParam param) throws Exception { - testCorrectlyCalculateSpaceForAlert(param, false); - } - - private void testCorrectlyCalculateSpaceForAlert(SSLEngineTestParam param, boolean jdkCompatabilityMode) - throws Exception { - SelfSignedCertificate ssc = new SelfSignedCertificate(); - serverSslCtx = wrapContext(param, SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(sslServerProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - - clientSslCtx = wrapContext(param, SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslClientProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .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(param.type(), param.delegate(), clientEngine, serverEngine); - - // This should produce an alert - clientEngine.closeOutbound(); - - ByteBuffer empty = allocateBuffer(param.type(), 0); - ByteBuffer dst = allocateBuffer(param.type(), clientEngine.getSession().getPacketBufferSize()); - // Limit to something that is guaranteed to be too small to hold an 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(); - engine.setVerify(SSL_CVERIFY_IGNORED, 1); - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testWrapWithDifferentSizesTLSv1(SSLEngineTestParam param) throws Exception { - clientSslCtx = wrapContext(param, SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslClientProvider()) - .build()); - SelfSignedCertificate ssc = new SelfSignedCertificate(); - serverSslCtx = wrapContext(param, SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(sslServerProvider()) - .build()); - - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1, "AES128-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1, "ECDHE-RSA-AES128-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1, "DES-CBC3-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1, "AECDH-DES-CBC3-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1, "CAMELLIA128-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1, "SEED-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1, "RC4-MD5"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1, "AES256-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1, "ADH-DES-CBC3-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1, "EDH-RSA-DES-CBC3-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1, "ADH-RC4-MD5"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1, "IDEA-CBC-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1, "RC4-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1, "CAMELLIA256-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1, "AECDH-RC4-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1, "ECDHE-RSA-DES-CBC3-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1, "ECDHE-RSA-AES256-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1, "ECDHE-RSA-RC4-SHA"); - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testWrapWithDifferentSizesTLSv1_1(SSLEngineTestParam param) throws Exception { - clientSslCtx = wrapContext(param, SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslClientProvider()) - .build()); - SelfSignedCertificate ssc = new SelfSignedCertificate(); - serverSslCtx = wrapContext(param, SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(sslServerProvider()) - .build()); - - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_1, "ECDHE-RSA-AES256-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_1, "AES256-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_1, "CAMELLIA256-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_1, "ECDHE-RSA-AES256-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_1, "SEED-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_1, "CAMELLIA128-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_1, "IDEA-CBC-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_1, "AECDH-RC4-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_1, "ADH-RC4-MD5"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_1, "RC4-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_1, "ECDHE-RSA-DES-CBC3-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_1, "EDH-RSA-DES-CBC3-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_1, "AECDH-DES-CBC3-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_1, "ADH-DES-CBC3-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_1, "DES-CBC3-SHA"); - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testWrapWithDifferentSizesTLSv1_2(SSLEngineTestParam param) throws Exception { - clientSslCtx = wrapContext(param, SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslClientProvider()) - .build()); - SelfSignedCertificate ssc = new SelfSignedCertificate(); - serverSslCtx = wrapContext(param, SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(sslServerProvider()) - .build()); - - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_2, "AES128-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_2, "ECDHE-RSA-AES128-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_2, "DES-CBC3-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_2, "AES128-GCM-SHA256"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_2, "ECDHE-RSA-AES256-SHA384"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_2, "AECDH-DES-CBC3-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_2, "AES256-GCM-SHA384"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_2, "AES256-SHA256"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_2, "ECDHE-RSA-AES128-GCM-SHA256"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_2, "ECDHE-RSA-AES128-SHA256"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_2, "CAMELLIA128-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_2, "SEED-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_2, "RC4-MD5"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_2, "AES256-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_2, "ADH-DES-CBC3-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_2, "EDH-RSA-DES-CBC3-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_2, "ADH-RC4-MD5"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_2, "RC4-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_2, "CAMELLIA256-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_2, "AES128-SHA256"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_2, "AECDH-RC4-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_2, "ECDHE-RSA-DES-CBC3-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_2, "ECDHE-RSA-AES256-GCM-SHA384"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_2, "ECDHE-RSA-AES256-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.TLS_v1_2, "ECDHE-RSA-RC4-SHA"); - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testWrapWithDifferentSizesSSLv3(SSLEngineTestParam param) throws Exception { - clientSslCtx = wrapContext(param, SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslClientProvider()) - .build()); - SelfSignedCertificate ssc = new SelfSignedCertificate(); - serverSslCtx = wrapContext(param, SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(sslServerProvider()) - .build()); - - testWrapWithDifferentSizes(param, SslProtocols.SSL_v3, "ADH-AES128-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.SSL_v3, "ADH-CAMELLIA128-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.SSL_v3, "AECDH-AES128-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.SSL_v3, "AECDH-DES-CBC3-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.SSL_v3, "CAMELLIA128-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.SSL_v3, "DHE-RSA-AES256-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.SSL_v3, "SEED-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.SSL_v3, "RC4-MD5"); - testWrapWithDifferentSizes(param, SslProtocols.SSL_v3, "ADH-AES256-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.SSL_v3, "ADH-SEED-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.SSL_v3, "ADH-DES-CBC3-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.SSL_v3, "EDH-RSA-DES-CBC3-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.SSL_v3, "ADH-RC4-MD5"); - testWrapWithDifferentSizes(param, SslProtocols.SSL_v3, "IDEA-CBC-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.SSL_v3, "DHE-RSA-AES128-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.SSL_v3, "RC4-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.SSL_v3, "CAMELLIA256-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.SSL_v3, "AECDH-RC4-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.SSL_v3, "DHE-RSA-SEED-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.SSL_v3, "AECDH-AES256-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.SSL_v3, "ECDHE-RSA-DES-CBC3-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.SSL_v3, "ADH-CAMELLIA256-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.SSL_v3, "DHE-RSA-CAMELLIA256-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.SSL_v3, "DHE-RSA-CAMELLIA128-SHA"); - testWrapWithDifferentSizes(param, SslProtocols.SSL_v3, "ECDHE-RSA-RC4-SHA"); - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testMultipleRecordsInOneBufferWithNonZeroPositionJDKCompatabilityModeOff(SSLEngineTestParam param) - throws Exception { - SelfSignedCertificate cert = new SelfSignedCertificate(); - - clientSslCtx = wrapContext(param, SslContextBuilder - .forClient() - .trustManager(cert.cert()) - .sslProvider(sslClientProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SSLEngine client = wrapEngine(clientSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine()); - - serverSslCtx = wrapContext(param, SslContextBuilder - .forServer(cert.certificate(), cert.privateKey()) - .sslProvider(sslServerProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - 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 - // unwrap call without exceed MAX_ENCRYPTED_PACKET_LENGTH. - final int plainClientOutLen = 1024; - ByteBuffer plainClientOut = allocateBuffer(param.type(), plainClientOutLen); - ByteBuffer plainServerOut = allocateBuffer(param.type(), server.getSession().getApplicationBufferSize()); - - ByteBuffer encClientToServer = allocateBuffer(param.type(), client.getSession().getPacketBufferSize()); - - int positionOffset = 1; - // We need to be able to hold 2 records + positionOffset - ByteBuffer combinedEncClientToServer = allocateBuffer( - param.type(), encClientToServer.capacity() * 2 + positionOffset); - combinedEncClientToServer.position(positionOffset); - - handshake(param.type(), param.delegate(), client, server); - - plainClientOut.limit(plainClientOut.capacity()); - SSLEngineResult result = client.wrap(plainClientOut, encClientToServer); - assertEquals(plainClientOut.capacity(), result.bytesConsumed()); - assertTrue(result.bytesProduced() > 0); - - encClientToServer.flip(); - - // Copy the first record into the combined buffer - combinedEncClientToServer.put(encClientToServer); - - plainClientOut.clear(); - encClientToServer.clear(); - - result = client.wrap(plainClientOut, encClientToServer); - assertEquals(plainClientOut.capacity(), result.bytesConsumed()); - assertTrue(result.bytesProduced() > 0); - - encClientToServer.flip(); - - // Copy the first record into the combined buffer - combinedEncClientToServer.put(encClientToServer); - - encClientToServer.clear(); - - combinedEncClientToServer.flip(); - combinedEncClientToServer.position(positionOffset); - - // Make sure the limit takes positionOffset into account to the content we are looking at is correct. - combinedEncClientToServer.limit( - combinedEncClientToServer.limit() - positionOffset); - final int combinedEncClientToServerLen = combinedEncClientToServer.remaining(); - - result = server.unwrap(combinedEncClientToServer, plainServerOut); - assertEquals(0, combinedEncClientToServer.remaining()); - assertEquals(combinedEncClientToServerLen, result.bytesConsumed()); - assertEquals(plainClientOutLen, result.bytesProduced()); - } finally { - cert.delete(); - cleanupClientSslEngine(client); - cleanupServerSslEngine(server); - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testInputTooBigAndFillsUpBuffersJDKCompatabilityModeOff(SSLEngineTestParam param) throws Exception { - SelfSignedCertificate cert = new SelfSignedCertificate(); - - clientSslCtx = wrapContext(param, SslContextBuilder - .forClient() - .trustManager(cert.cert()) - .sslProvider(sslClientProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SSLEngine client = wrapEngine(clientSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine()); - - serverSslCtx = wrapContext(param, SslContextBuilder - .forServer(cert.certificate(), cert.privateKey()) - .sslProvider(sslServerProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SSLEngine server = wrapEngine(serverSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine()); - - try { - ByteBuffer plainClient = allocateBuffer(param.type(), MAX_PLAINTEXT_LENGTH + 100); - ByteBuffer plainClient2 = allocateBuffer(param.type(), 512); - ByteBuffer plainClientTotal = - allocateBuffer(param.type(), plainClient.capacity() + plainClient2.capacity()); - plainClientTotal.put(plainClient); - plainClientTotal.put(plainClient2); - plainClient.clear(); - plainClient2.clear(); - plainClientTotal.flip(); - - // The capacity is designed to trigger an overflow condition. - ByteBuffer encClientToServerTooSmall = allocateBuffer(param.type(), MAX_PLAINTEXT_LENGTH + 28); - ByteBuffer encClientToServer = allocateBuffer(param.type(), client.getSession().getApplicationBufferSize()); - ByteBuffer encClientToServerTotal = - allocateBuffer(param.type(), client.getSession().getApplicationBufferSize() << 1); - ByteBuffer plainServer = allocateBuffer(param.type(), server.getSession().getApplicationBufferSize() << 1); - - handshake(param.type(), param.delegate(), client, server); - - int plainClientRemaining = plainClient.remaining(); - int encClientToServerTooSmallRemaining = encClientToServerTooSmall.remaining(); - SSLEngineResult result = client.wrap(plainClient, encClientToServerTooSmall); - assertEquals(SSLEngineResult.Status.OK, result.getStatus()); - assertEquals(plainClientRemaining - plainClient.remaining(), result.bytesConsumed()); - assertEquals(encClientToServerTooSmallRemaining - encClientToServerTooSmall.remaining(), - result.bytesProduced()); - - result = client.wrap(plainClient, encClientToServerTooSmall); - assertEquals(SSLEngineResult.Status.BUFFER_OVERFLOW, result.getStatus()); - assertEquals(0, result.bytesConsumed()); - assertEquals(0, result.bytesProduced()); - - plainClientRemaining = plainClient.remaining(); - int encClientToServerRemaining = encClientToServer.remaining(); - result = client.wrap(plainClient, encClientToServer); - assertEquals(SSLEngineResult.Status.OK, result.getStatus()); - assertEquals(plainClientRemaining, result.bytesConsumed()); - assertEquals(encClientToServerRemaining - encClientToServer.remaining(), result.bytesProduced()); - assertEquals(0, plainClient.remaining()); - - final int plainClient2Remaining = plainClient2.remaining(); - encClientToServerRemaining = encClientToServer.remaining(); - result = client.wrap(plainClient2, encClientToServer); - assertEquals(SSLEngineResult.Status.OK, result.getStatus()); - assertEquals(plainClient2Remaining, result.bytesConsumed()); - assertEquals(encClientToServerRemaining - encClientToServer.remaining(), result.bytesProduced()); - - // Concatenate the too small buffer - encClientToServerTooSmall.flip(); - encClientToServer.flip(); - encClientToServerTotal.put(encClientToServerTooSmall); - encClientToServerTotal.put(encClientToServer); - encClientToServerTotal.flip(); - - // Unwrap in a single call. - final int encClientToServerTotalRemaining = encClientToServerTotal.remaining(); - result = server.unwrap(encClientToServerTotal, plainServer); - assertEquals(SSLEngineResult.Status.OK, result.getStatus()); - assertEquals(encClientToServerTotalRemaining, result.bytesConsumed()); - plainServer.flip(); - assertEquals(plainClientTotal, plainServer); - } finally { - cert.delete(); - cleanupClientSslEngine(client); - cleanupServerSslEngine(server); - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testPartialPacketUnwrapJDKCompatabilityModeOff(SSLEngineTestParam param) throws Exception { - SelfSignedCertificate cert = new SelfSignedCertificate(); - - clientSslCtx = wrapContext(param, SslContextBuilder - .forClient() - .trustManager(cert.cert()) - .sslProvider(sslClientProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SSLEngine client = wrapEngine(clientSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine()); - - serverSslCtx = wrapContext(param, SslContextBuilder - .forServer(cert.certificate(), cert.privateKey()) - .sslProvider(sslServerProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SSLEngine server = wrapEngine(serverSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine()); - - try { - ByteBuffer plainClient = allocateBuffer(param.type(), 1024); - ByteBuffer plainClient2 = allocateBuffer(param.type(), 512); - ByteBuffer plainClientTotal = - allocateBuffer(param.type(), plainClient.capacity() + plainClient2.capacity()); - plainClientTotal.put(plainClient); - plainClientTotal.put(plainClient2); - plainClient.clear(); - plainClient2.clear(); - plainClientTotal.flip(); - - ByteBuffer encClientToServer = allocateBuffer(param.type(), client.getSession().getPacketBufferSize()); - ByteBuffer plainServer = allocateBuffer(param.type(), server.getSession().getApplicationBufferSize()); - - handshake(param.type(), param.delegate(), client, server); - - SSLEngineResult result = client.wrap(plainClient, encClientToServer); - assertEquals(SSLEngineResult.Status.OK, result.getStatus()); - assertEquals(result.bytesConsumed(), plainClient.capacity()); - final int encClientLen = result.bytesProduced(); - - result = client.wrap(plainClient2, encClientToServer); - assertEquals(SSLEngineResult.Status.OK, result.getStatus()); - assertEquals(result.bytesConsumed(), plainClient2.capacity()); - final int encClientLen2 = result.bytesProduced(); - - // Flip so we can read it. - encClientToServer.flip(); - - // Consume a partial TLS packet. - ByteBuffer encClientFirstHalf = encClientToServer.duplicate(); - encClientFirstHalf.limit(encClientLen / 2); - result = server.unwrap(encClientFirstHalf, plainServer); - assertEquals(SSLEngineResult.Status.OK, result.getStatus()); - assertEquals(result.bytesConsumed(), encClientLen / 2); - encClientToServer.position(result.bytesConsumed()); - - // We now have half of the first packet and the whole second packet, so lets decode all but the last byte. - ByteBuffer encClientAllButLastByte = encClientToServer.duplicate(); - final int encClientAllButLastByteLen = encClientAllButLastByte.remaining() - 1; - encClientAllButLastByte.limit(encClientAllButLastByte.limit() - 1); - result = server.unwrap(encClientAllButLastByte, plainServer); - assertEquals(SSLEngineResult.Status.OK, result.getStatus()); - assertEquals(result.bytesConsumed(), encClientAllButLastByteLen); - encClientToServer.position(encClientToServer.position() + result.bytesConsumed()); - - // Read the last byte and verify the original content has been decrypted. - result = server.unwrap(encClientToServer, plainServer); - assertEquals(SSLEngineResult.Status.OK, result.getStatus()); - assertEquals(result.bytesConsumed(), 1); - plainServer.flip(); - assertEquals(plainClientTotal, plainServer); - } finally { - cert.delete(); - cleanupClientSslEngine(client); - cleanupServerSslEngine(server); - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testBufferUnderFlowAvoidedIfJDKCompatabilityModeOff(SSLEngineTestParam param) throws Exception { - SelfSignedCertificate cert = new SelfSignedCertificate(); - - clientSslCtx = wrapContext(param, SslContextBuilder - .forClient() - .trustManager(cert.cert()) - .sslProvider(sslClientProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SSLEngine client = wrapEngine(clientSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine()); - - serverSslCtx = wrapContext(param, SslContextBuilder - .forServer(cert.certificate(), cert.privateKey()) - .sslProvider(sslServerProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SSLEngine server = wrapEngine(serverSslCtx.newHandler(UnpooledByteBufAllocator.DEFAULT).engine()); - - try { - ByteBuffer plainClient = allocateBuffer(param.type(), 1024); - plainClient.limit(plainClient.capacity()); - - ByteBuffer encClientToServer = allocateBuffer(param.type(), client.getSession().getPacketBufferSize()); - ByteBuffer plainServer = allocateBuffer(param.type(), server.getSession().getApplicationBufferSize()); - - handshake(param.type(), param.delegate(), client, server); - - SSLEngineResult result = client.wrap(plainClient, encClientToServer); - assertEquals(SSLEngineResult.Status.OK, result.getStatus()); - assertEquals(result.bytesConsumed(), plainClient.capacity()); - - // Flip so we can read it. - encClientToServer.flip(); - int remaining = encClientToServer.remaining(); - - // We limit the buffer so we have less then the header to read, this should result in an BUFFER_UNDERFLOW. - encClientToServer.limit(SslUtils.SSL_RECORD_HEADER_LENGTH - 1); - result = server.unwrap(encClientToServer, plainServer); - assertEquals(SSLEngineResult.Status.OK, result.getStatus()); - assertEquals(SslUtils.SSL_RECORD_HEADER_LENGTH - 1, result.bytesConsumed()); - assertEquals(0, result.bytesProduced()); - remaining -= result.bytesConsumed(); - - // We limit the buffer so we can read the header but not the rest, this should result in an - // BUFFER_UNDERFLOW. - encClientToServer.limit(SslUtils.SSL_RECORD_HEADER_LENGTH); - result = server.unwrap(encClientToServer, plainServer); - assertEquals(SSLEngineResult.Status.OK, result.getStatus()); - assertEquals(1, result.bytesConsumed()); - assertEquals(0, result.bytesProduced()); - remaining -= result.bytesConsumed(); - - // We limit the buffer so we can read the header and partly the rest, this should result in an - // BUFFER_UNDERFLOW. - encClientToServer.limit( - SslUtils.SSL_RECORD_HEADER_LENGTH + remaining - 1 - SslUtils.SSL_RECORD_HEADER_LENGTH); - result = server.unwrap(encClientToServer, plainServer); - assertEquals(SSLEngineResult.Status.OK, result.getStatus()); - assertEquals(encClientToServer.limit() - SslUtils.SSL_RECORD_HEADER_LENGTH, result.bytesConsumed()); - assertEquals(0, result.bytesProduced()); - remaining -= result.bytesConsumed(); - - // Reset limit so we can read the full record. - encClientToServer.limit(remaining); - assertEquals(0, encClientToServer.remaining()); - result = server.unwrap(encClientToServer, plainServer); - assertEquals(SSLEngineResult.Status.BUFFER_UNDERFLOW, result.getStatus()); - assertEquals(0, result.bytesConsumed()); - assertEquals(0, result.bytesProduced()); - - encClientToServer.position(0); - result = server.unwrap(encClientToServer, plainServer); - assertEquals(SSLEngineResult.Status.OK, result.getStatus()); - assertEquals(remaining, result.bytesConsumed()); - assertEquals(0, result.bytesProduced()); - } finally { - cert.delete(); - cleanupClientSslEngine(client); - cleanupServerSslEngine(server); - } - } - - private void testWrapWithDifferentSizes(SSLEngineTestParam param, String protocol, String cipher) throws Exception { - assumeTrue(OpenSsl.SUPPORTED_PROTOCOLS_SET.contains(protocol)); - if (!OpenSsl.isCipherSuiteAvailable(cipher)) { - return; - } - - SSLEngine clientEngine = null; - SSLEngine serverEngine = null; - try { - 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 }); - serverEngine.setEnabledProtocols(new String[] { protocol }); - - try { - handshake(param.type(), param.delegate(), clientEngine, serverEngine); - } catch (SSLException e) { - if (e.getMessage().contains("unsupported protocol") || - e.getMessage().contains("no protocols available")) { - throw new AssumptionViolatedException(protocol + " not supported with cipher " + cipher, e); - } - throw e; - } - - int srcLen = 64; - do { - testWrapDstBigEnough(param.type(), clientEngine, srcLen); - srcLen += 64; - } while (srcLen < MAX_PLAINTEXT_LENGTH); - - testWrapDstBigEnough(param.type(), clientEngine, MAX_PLAINTEXT_LENGTH); - } finally { - cleanupClientSslEngine(clientEngine); - cleanupServerSslEngine(serverEngine); - } - } - - private void testWrapDstBigEnough(BufferType type, SSLEngine engine, int srcLen) throws SSLException { - ByteBuffer src = allocateBuffer(type, srcLen); - ByteBuffer dst = allocateBuffer(type, srcLen + unwrapEngine(engine).maxWrapOverhead()); - - SSLEngineResult result = engine.wrap(src, dst); - assertEquals(SSLEngineResult.Status.OK, result.getStatus()); - int consumed = result.bytesConsumed(); - int produced = result.bytesProduced(); - assertEquals(srcLen, consumed); - assertTrue(produced > consumed); - - dst.flip(); - assertEquals(produced, dst.remaining()); - assertFalse(src.hasRemaining()); - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testSNIMatchersDoesNotThrow(SSLEngineTestParam param) throws Exception { - assumeTrue(PlatformDependent.javaVersion() >= 8); - SelfSignedCertificate ssc = new SelfSignedCertificate(); - serverSslCtx = wrapContext(param, SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(sslServerProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - - SSLEngine engine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - try { - SSLParameters parameters = new SSLParameters(); - Java8SslTestUtils.setSNIMatcher(parameters, EmptyArrays.EMPTY_BYTES); - engine.setSSLParameters(parameters); - } finally { - cleanupServerSslEngine(engine); - ssc.delete(); - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testSNIMatchersWithSNINameWithUnderscore(SSLEngineTestParam param) throws Exception { - assumeTrue(PlatformDependent.javaVersion() >= 8); - byte[] name = "rb8hx3pww30y3tvw0mwy.v1_1".getBytes(CharsetUtil.UTF_8); - SelfSignedCertificate ssc = new SelfSignedCertificate(); - serverSslCtx = wrapContext(param, SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(sslServerProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - - SSLEngine engine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - try { - SSLParameters parameters = new SSLParameters(); - Java8SslTestUtils.setSNIMatcher(parameters, name); - engine.setSSLParameters(parameters); - assertFalse(unwrapEngine(engine).checkSniHostnameMatch("other".getBytes(CharsetUtil.UTF_8))); - } finally { - cleanupServerSslEngine(engine); - ssc.delete(); - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testAlgorithmConstraintsThrows(SSLEngineTestParam param) throws Exception { - SelfSignedCertificate ssc = new SelfSignedCertificate(); - serverSslCtx = wrapContext(param, SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(sslServerProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - - final SSLEngine engine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - final SSLParameters parameters = new SSLParameters(); - parameters.setAlgorithmConstraints(new AlgorithmConstraints() { - @Override - public boolean permits( - Set primitives, String algorithm, AlgorithmParameters parameters) { - return false; - } - - @Override - public boolean permits(Set primitives, Key key) { - return false; - } - - @Override - public boolean permits( - Set primitives, String algorithm, Key key, AlgorithmParameters parameters) { - return false; - } - }); - try { - assertThrows(IllegalArgumentException.class, new Executable() { - @Override - public void execute() throws Throwable { - engine.setSSLParameters(parameters); - } - }); - } finally { - cleanupServerSslEngine(engine); - ssc.delete(); - } - } - - private static void runTasksIfNeeded(SSLEngine engine) { - if (engine.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { - for (;;) { - Runnable task = engine.getDelegatedTask(); - if (task == null) { - assertNotEquals(HandshakeStatus.NEED_TASK, engine.getHandshakeStatus()); - break; - } - task.run(); - } - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testExtractMasterkeyWorksCorrectly(SSLEngineTestParam param) throws Exception { - if (param.combo() != ProtocolCipherCombo.tlsv12()) { - return; - } - SelfSignedCertificate cert = new SelfSignedCertificate(); - serverSslCtx = wrapContext(param, SslContextBuilder.forServer(cert.key(), cert.cert()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .sslProvider(SslProvider.OPENSSL).build()); - final SSLEngine serverEngine = - wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - clientSslCtx = wrapContext(param, SslContextBuilder.forClient() - .trustManager(cert.certificate()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .sslProvider(SslProvider.OPENSSL).build()); - final SSLEngine clientEngine = - wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - - final String enabledCipher = "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"; - try { - //lets set the cipher suite to a specific one with DHE - assumeTrue(Arrays.asList(clientEngine.getSupportedCipherSuites()).contains(enabledCipher), - "The diffie hellman cipher is not supported on your runtime."); - - //https://www.ietf.org/rfc/rfc5289.txt - //For cipher suites ending with _SHA256, the PRF is the TLS PRF - //[RFC5246] with SHA-256 as the hash function. The MAC is HMAC - //[RFC2104] with SHA-256 as the hash function. - clientEngine.setEnabledCipherSuites(new String[] { enabledCipher }); - serverEngine.setEnabledCipherSuites(new String[] { enabledCipher }); - - int appBufferMax = clientEngine.getSession().getApplicationBufferSize(); - int netBufferMax = clientEngine.getSession().getPacketBufferSize(); - - /* - * We'll make the input buffers a bit bigger than the max needed - * size, so that unwrap()s following a successful data transfer - * won't generate BUFFER_OVERFLOWS. - */ - ByteBuffer clientIn = ByteBuffer.allocate(appBufferMax + 50); - ByteBuffer serverIn = ByteBuffer.allocate(appBufferMax + 50); - - ByteBuffer cTOs = ByteBuffer.allocate(netBufferMax); - ByteBuffer sTOc = ByteBuffer.allocate(netBufferMax); - - ByteBuffer clientOut = ByteBuffer.wrap("Hi Server, I'm Client".getBytes(CharsetUtil.US_ASCII)); - ByteBuffer serverOut = ByteBuffer.wrap("Hello Client, I'm Server".getBytes(CharsetUtil.US_ASCII)); - - // This implementation is largely imitated from - // https://docs.oracle.com/javase/8/docs/technotes/ - // guides/security/jsse/samples/sslengine/SSLEngineSimpleDemo.java - // It has been simplified however without the need for running delegation tasks - - // Do handshake for SSL - // A typical handshake will usually contain the following steps: - // 1. wrap: ClientHello - // 2. unwrap: ServerHello/Cert/ServerHelloDone - // 3. wrap: ClientKeyExchange - // 4. wrap: ChangeCipherSpec - // 5. wrap: Finished - // 6. unwrap: ChangeCipherSpec - // 7. unwrap: Finished - - //set a for loop; instead of a while loop to guarantee we quit out eventually - boolean asserted = false; - for (int i = 0; i < 1000; i++) { - - clientEngine.wrap(clientOut, cTOs); - serverEngine.wrap(serverOut, sTOc); - - cTOs.flip(); - sTOc.flip(); - - runTasksIfNeeded(clientEngine); - runTasksIfNeeded(serverEngine); - - clientEngine.unwrap(sTOc, clientIn); - serverEngine.unwrap(cTOs, serverIn); - - runTasksIfNeeded(clientEngine); - runTasksIfNeeded(serverEngine); - - // check when the application data has fully been consumed and sent - // for both the client and server - if ((clientOut.limit() == serverIn.position()) && - (serverOut.limit() == clientIn.position())) { - byte[] serverRandom = SSL.getServerRandom(unwrapEngine(serverEngine).sslPointer()); - byte[] clientRandom = SSL.getClientRandom(unwrapEngine(clientEngine).sslPointer()); - byte[] serverMasterKey = SSL.getMasterKey(unwrapEngine(serverEngine).sslPointer()); - byte[] clientMasterKey = SSL.getMasterKey(unwrapEngine(clientEngine).sslPointer()); - - asserted = true; - assertArrayEquals(serverMasterKey, clientMasterKey); - - // let us re-read the encrypted data and decrypt it ourselves! - cTOs.flip(); - sTOc.flip(); - - // See https://tools.ietf.org/html/rfc5246#section-6.3: - // key_block = PRF(SecurityParameters.master_secret, "key expansion", - // SecurityParameters.server_random + SecurityParameters.client_random); - // - // partitioned: - // client_write_MAC_secret[SecurityParameters.hash_size] - // server_write_MAC_secret[SecurityParameters.hash_size] - // client_write_key[SecurityParameters.key_material_length] - // server_write_key[SecurityParameters.key_material_length] - - int keySize = 16; // AES is 16 bytes or 128 bits - int macSize = 32; // SHA256 is 32 bytes or 256 bits - int keyBlockSize = (2 * keySize) + (2 * macSize); - - byte[] seed = new byte[serverRandom.length + clientRandom.length]; - System.arraycopy(serverRandom, 0, seed, 0, serverRandom.length); - System.arraycopy(clientRandom, 0, seed, serverRandom.length, clientRandom.length); - byte[] keyBlock = PseudoRandomFunction.hash(serverMasterKey, - "key expansion".getBytes(CharsetUtil.US_ASCII), seed, keyBlockSize, "HmacSha256"); - - int offset = 0; - byte[] clientWriteMac = Arrays.copyOfRange(keyBlock, offset, offset + macSize); - offset += macSize; - - byte[] serverWriteMac = Arrays.copyOfRange(keyBlock, offset, offset + macSize); - offset += macSize; - - byte[] clientWriteKey = Arrays.copyOfRange(keyBlock, offset, offset + keySize); - offset += keySize; - - byte[] serverWriteKey = Arrays.copyOfRange(keyBlock, offset, offset + keySize); - offset += keySize; - - //advance the cipher text by 5 - //to take into account the TLS Record Header - cTOs.position(cTOs.position() + 5); - - byte[] ciphertext = new byte[cTOs.remaining()]; - cTOs.get(ciphertext); - - //the initialization vector is the first 16 bytes (128 bits) of the payload - byte[] clientWriteIV = Arrays.copyOfRange(ciphertext, 0, 16); - ciphertext = Arrays.copyOfRange(ciphertext, 16, ciphertext.length); - - SecretKeySpec secretKey = new SecretKeySpec(clientWriteKey, "AES"); - final IvParameterSpec ivForCBC = new IvParameterSpec(clientWriteIV); - Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); - cipher.init(Cipher.DECRYPT_MODE, secretKey, ivForCBC); - byte[] plaintext = cipher.doFinal(ciphertext); - assertTrue(new String(plaintext).startsWith("Hi Server, I'm Client")); - break; - } else { - cTOs.compact(); - sTOc.compact(); - } - } - - assertTrue(asserted, "The assertions were never executed."); - } finally { - cleanupClientSslEngine(clientEngine); - cleanupServerSslEngine(serverEngine); - cert.delete(); - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testNoKeyFound(final SSLEngineTestParam param) throws Exception { - checkShouldUseKeyManagerFactory(); - clientSslCtx = wrapContext(param, SslContextBuilder - .forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslClientProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - final SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - - serverSslCtx = wrapContext(param, SslContextBuilder - .forServer(new X509ExtendedKeyManager() { - @Override - public String[] getClientAliases(String keyType, Principal[] issuers) { - return new String[0]; - } - - @Override - public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) { - return null; - } - - @Override - public String[] getServerAliases(String keyType, Principal[] issuers) { - return new String[0]; - } - - @Override - public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) { - return null; - } - - @Override - public X509Certificate[] getCertificateChain(String alias) { - return new X509Certificate[0]; - } - - @Override - public PrivateKey getPrivateKey(String alias) { - return null; - } - }) - .sslProvider(sslServerProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - final SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - - try { - assertThrows(SSLException.class, new Executable() { - @Override - public void execute() throws Throwable { - handshake(param.type(), param.delegate(), client, server); - } - }); - } finally { - cleanupClientSslEngine(client); - cleanupServerSslEngine(server); - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testSessionLocalWhenNonMutualWithKeyManager(SSLEngineTestParam param) throws Exception { - checkShouldUseKeyManagerFactory(); - super.testSessionLocalWhenNonMutualWithKeyManager(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testSessionLocalWhenNonMutualWithoutKeyManager(SSLEngineTestParam param) throws Exception { - // This only really works when the KeyManagerFactory is supported as otherwise we not really know when - // we need to provide a cert. - assumeTrue(OpenSsl.supportsKeyManagerFactory()); - super.testSessionLocalWhenNonMutualWithoutKeyManager(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testDefaultTLS1NotAcceptedByDefaultServer(SSLEngineTestParam param) throws Exception { - testDefaultTLS1NotAcceptedByDefault(param, null, SslProtocols.TLS_v1); - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testDefaultTLS11NotAcceptedByDefaultServer(SSLEngineTestParam param) throws Exception { - testDefaultTLS1NotAcceptedByDefault(param, null, SslProtocols.TLS_v1_1); - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testDefaultTLS1NotAcceptedByDefaultClient(SSLEngineTestParam param) throws Exception { - testDefaultTLS1NotAcceptedByDefault(param, SslProtocols.TLS_v1, null); - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testDefaultTLS11NotAcceptedByDefaultClient(SSLEngineTestParam param) throws Exception { - testDefaultTLS1NotAcceptedByDefault(param, SslProtocols.TLS_v1_1, null); - } - - private void testDefaultTLS1NotAcceptedByDefault(final SSLEngineTestParam param, - String clientProtocol, String serverProtocol) throws Exception { - SslContextBuilder clientCtxBuilder = SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslClientProvider()) - .sslContextProvider(clientSslContextProvider()); - if (clientProtocol != null) { - clientCtxBuilder.protocols(clientProtocol); - } - clientSslCtx = wrapContext(param, clientCtxBuilder.build()); - SelfSignedCertificate ssc = new SelfSignedCertificate(); - - SslContextBuilder serverCtxBuilder = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(sslServerProvider()) - .sslContextProvider(serverSslContextProvider()); - if (serverProtocol != null) { - serverCtxBuilder.protocols(serverProtocol); - } - serverSslCtx = wrapContext(param, serverCtxBuilder.build()); - final SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - final SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - - try { - assertThrows(SSLHandshakeException.class, new Executable() { - @Override - public void execute() throws Throwable { - handshake(param.type(), param.delegate(), client, server); - } - }); - } finally { - cleanupClientSslEngine(client); - cleanupServerSslEngine(server); - ssc.delete(); - } - } - - @Override - protected SslProvider sslClientProvider() { - return SslProvider.OPENSSL; - } - - @Override - protected SslProvider sslServerProvider() { - return SslProvider.OPENSSL; - } - - private static ApplicationProtocolConfig acceptingNegotiator(Protocol protocol, - String... supportedProtocols) { - return new ApplicationProtocolConfig(protocol, - SelectorFailureBehavior.NO_ADVERTISE, - SelectedListenerFailureBehavior.ACCEPT, - supportedProtocols); - } - - @Override - protected SSLEngine wrapEngine(SSLEngine engine) { - return Java8SslTestUtils.wrapSSLEngineForTesting(engine); - } - - ReferenceCountedOpenSslEngine unwrapEngine(SSLEngine engine) { - if (engine instanceof JdkSslEngine) { - return (ReferenceCountedOpenSslEngine) ((JdkSslEngine) engine).getWrappedEngine(); - } - return (ReferenceCountedOpenSslEngine) engine; - } - - @SuppressWarnings("deprecation") - @Override - protected SslContext wrapContext(SSLEngineTestParam param, SslContext context) { - if (context instanceof OpenSslContext) { - if (param instanceof OpenSslEngineTestParam) { - ((OpenSslContext) context).setUseTasks(((OpenSslEngineTestParam) param).useTasks); - } - // Explicit enable the session cache as its disabled by default on the client side. - ((OpenSslContext) context).sessionContext().setSessionCacheEnabled(true); - } - return context; - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testSessionCache(SSLEngineTestParam param) throws Exception { - assumeTrue(OpenSsl.isSessionCacheSupported()); - super.testSessionCache(param); - assertSessionContext(clientSslCtx); - assertSessionContext(serverSslCtx); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testSessionCacheTimeout(SSLEngineTestParam param) throws Exception { - assumeTrue(OpenSsl.isSessionCacheSupported()); - super.testSessionCacheTimeout(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testSessionCacheSize(SSLEngineTestParam param) throws Exception { - assumeTrue(OpenSsl.isSessionCacheSupported()); - super.testSessionCacheSize(param); - } - - private static void assertSessionContext(SslContext context) { - if (context == null) { - return; - } - OpenSslSessionContext serverSessionCtx = (OpenSslSessionContext) context.sessionContext(); - assertTrue(serverSessionCtx.isSessionCacheEnabled()); - if (serverSessionCtx.getIds().hasMoreElements()) { - serverSessionCtx.setSessionCacheEnabled(false); - assertFalse(serverSessionCtx.getIds().hasMoreElements()); - assertFalse(serverSessionCtx.isSessionCacheEnabled()); - } - } - - @Override - protected void assertSessionReusedForEngine(SSLEngine clientEngine, SSLEngine serverEngine, boolean reuse) { - assertEquals(reuse, unwrapEngine(clientEngine).isSessionReused()); - assertEquals(reuse, unwrapEngine(serverEngine).isSessionReused()); - } - - @Override - protected boolean isSessionMaybeReused(SSLEngine engine) { - return unwrapEngine(engine).isSessionReused(); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testRSASSAPSS(SSLEngineTestParam param) throws Exception { - checkShouldUseKeyManagerFactory(); - super.testRSASSAPSS(param); - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTestParam.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTestParam.java deleted file mode 100644 index a8f725fce5..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslEngineTestParam.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -final class OpenSslEngineTestParam extends SSLEngineTest.SSLEngineTestParam { - final boolean useTasks; - OpenSslEngineTestParam(boolean useTasks, SSLEngineTest.SSLEngineTestParam param) { - super(param.type(), param.combo(), param.delegate()); - this.useTasks = useTasks; - } - - @Override - public String toString() { - return "OpenSslEngineTestParam{" + - "type=" + type() + - ", protocolCipherCombo=" + combo() + - ", delegate=" + delegate() + - ", useTasks=" + useTasks + - '}'; - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslErrorStackAssertSSLEngine.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslErrorStackAssertSSLEngine.java deleted file mode 100644 index 916395086b..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslErrorStackAssertSSLEngine.java +++ /dev/null @@ -1,442 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.internal.tcnative.SSL; -import io.netty.util.ReferenceCounted; -import io.netty.util.internal.PlatformDependent; - -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; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -/** - * 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 JdkAlpnSslUtils.getApplicationProtocol(getWrappedEngine()); - } finally { - assertErrorStackEmpty(); - } - } - throw new UnsupportedOperationException(); - } - - public String getHandshakeApplicationProtocol() { - if (PlatformDependent.javaVersion() >= 9) { - try { - return JdkAlpnSslUtils.getHandshakeApplicationProtocol(getWrappedEngine()); - } finally { - assertErrorStackEmpty(); - } - } - throw new UnsupportedOperationException(); - } - - public void setHandshakeApplicationProtocolSelector(BiFunction, String> selector) { - if (PlatformDependent.javaVersion() >= 9) { - try { - JdkAlpnSslUtils.setHandshakeApplicationProtocolSelector(getWrappedEngine(), selector); - } finally { - assertErrorStackEmpty(); - } - } - throw new UnsupportedOperationException(); - } - - public BiFunction, String> getHandshakeApplicationProtocolSelector() { - if (PlatformDependent.javaVersion() >= 9) { - try { - return JdkAlpnSslUtils.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(decrement); - } - - @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() { - long error = SSL.getLastErrorNumber(); - assertEquals(0, error, "SSL error stack non-empty: " + SSL.getErrorString(error)); - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslJdkSslEngineInteroptTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslJdkSslEngineInteroptTest.java deleted file mode 100644 index 0d47fd09b3..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslJdkSslEngineInteroptTest.java +++ /dev/null @@ -1,202 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -import javax.net.ssl.SSLEngine; - -import java.util.ArrayList; -import java.util.List; - -import static io.netty.handler.ssl.OpenSslTestUtils.checkShouldUseKeyManagerFactory; -import static org.junit.jupiter.api.Assumptions.assumeTrue; - -public class OpenSslJdkSslEngineInteroptTest extends SSLEngineTest { - - public OpenSslJdkSslEngineInteroptTest() { - super(SslProvider.isTlsv13Supported(SslProvider.JDK) && - SslProvider.isTlsv13Supported(SslProvider.OPENSSL)); - } - - @Override - protected List newTestParams() { - List params = super.newTestParams(); - List testParams = new ArrayList(); - for (SSLEngineTestParam param: params) { - testParams.add(new OpenSslEngineTestParam(true, param)); - testParams.add(new OpenSslEngineTestParam(false, param)); - } - return testParams; - } - - @BeforeAll - public static void checkOpenSsl() { - OpenSsl.ensureAvailability(); - } - - @Override - protected SslProvider sslClientProvider() { - return SslProvider.OPENSSL; - } - - @Override - protected SslProvider sslServerProvider() { - return SslProvider.JDK; - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Disabled /* Does the JDK support a "max certificate chain length"? */ - @Override - public void testMutualAuthValidClientCertChainTooLongFailOptionalClientAuth(SSLEngineTestParam param) - throws Exception { - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Disabled /* Does the JDK support a "max certificate chain length"? */ - @Override - public void testMutualAuthValidClientCertChainTooLongFailRequireClientAuth(SSLEngineTestParam param) - throws Exception { - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testMutualAuthInvalidIntermediateCASucceedWithOptionalClientAuth(SSLEngineTestParam param) - throws Exception { - checkShouldUseKeyManagerFactory(); - super.testMutualAuthInvalidIntermediateCASucceedWithOptionalClientAuth(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testMutualAuthInvalidIntermediateCAFailWithOptionalClientAuth(SSLEngineTestParam param) - throws Exception { - checkShouldUseKeyManagerFactory(); - super.testMutualAuthInvalidIntermediateCAFailWithOptionalClientAuth(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testMutualAuthInvalidIntermediateCAFailWithRequiredClientAuth(SSLEngineTestParam param) - throws Exception { - checkShouldUseKeyManagerFactory(); - super.testMutualAuthInvalidIntermediateCAFailWithRequiredClientAuth(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testSessionAfterHandshakeKeyManagerFactoryMutualAuth(SSLEngineTestParam param) throws Exception { - checkShouldUseKeyManagerFactory(); - super.testSessionAfterHandshakeKeyManagerFactoryMutualAuth(param); - } - - @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); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testHandshakeSession(SSLEngineTestParam param) throws Exception { - checkShouldUseKeyManagerFactory(); - super.testHandshakeSession(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testSupportedSignatureAlgorithms(SSLEngineTestParam param) throws Exception { - checkShouldUseKeyManagerFactory(); - super.testSupportedSignatureAlgorithms(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testSessionLocalWhenNonMutualWithKeyManager(SSLEngineTestParam param) throws Exception { - checkShouldUseKeyManagerFactory(); - super.testSessionLocalWhenNonMutualWithKeyManager(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testSessionLocalWhenNonMutualWithoutKeyManager(SSLEngineTestParam param) throws Exception { - // This only really works when the KeyManagerFactory is supported as otherwise we not really know when - // we need to provide a cert. - assumeTrue(OpenSsl.supportsKeyManagerFactory()); - super.testSessionLocalWhenNonMutualWithoutKeyManager(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testSessionCache(SSLEngineTestParam param) throws Exception { - assumeTrue(OpenSsl.isSessionCacheSupported()); - super.testSessionCache(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testSessionCacheTimeout(SSLEngineTestParam param) throws Exception { - assumeTrue(OpenSsl.isSessionCacheSupported()); - super.testSessionCacheTimeout(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testSessionCacheSize(SSLEngineTestParam param) throws Exception { - assumeTrue(OpenSsl.isSessionCacheSupported()); - super.testSessionCacheSize(param); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Override - public void testRSASSAPSS(SSLEngineTestParam param) throws Exception { - checkShouldUseKeyManagerFactory(); - super.testRSASSAPSS(param); - } - - @Override - protected SSLEngine wrapEngine(SSLEngine engine) { - return Java8SslTestUtils.wrapSSLEngineForTesting(engine); - } - - @SuppressWarnings("deprecation") - @Override - protected SslContext wrapContext(SSLEngineTestParam param, SslContext context) { - if (context instanceof OpenSslContext && param instanceof OpenSslEngineTestParam) { - ((OpenSslContext) context).setUseTasks(((OpenSslEngineTestParam) param).useTasks); - // Explicit enable the session cache as its disabled by default on the client side. - ((OpenSslContext) context).sessionContext().setSessionCacheEnabled(true); - } - return context; - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslKeyMaterialManagerTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslKeyMaterialManagerTest.java deleted file mode 100644 index c828dc985f..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslKeyMaterialManagerTest.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty.util.internal.EmptyArrays; -import org.junit.jupiter.api.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; - -import static org.junit.jupiter.api.Assertions.fail; - -public class OpenSslKeyMaterialManagerTest { - - @Test - public void testChooseClientAliasReturnsNull() throws SSLException { - OpenSsl.ensureAvailability(); - - 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 { - 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); - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslKeyMaterialProviderTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslKeyMaterialProviderTest.java deleted file mode 100644 index 09976ee4aa..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslKeyMaterialProviderTest.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty.internal.tcnative.SSL; -import io.netty.util.ReferenceCountUtil; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.X509KeyManager; - -import java.net.Socket; -import java.security.KeyStore; -import java.security.Principal; -import java.security.PrivateKey; -import java.security.cert.X509Certificate; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class OpenSslKeyMaterialProviderTest { - - static final String PASSWORD = "example"; - static final String EXISTING_ALIAS = "1"; - private static final String NON_EXISTING_ALIAS = "nonexisting"; - - @BeforeAll - static void checkOpenSsl() { - OpenSsl.ensureAvailability(); - } - - protected KeyManagerFactory newKeyManagerFactory() throws Exception { - return newKeyManagerFactory(KeyManagerFactory.getDefaultAlgorithm()); - } - - protected KeyManagerFactory newKeyManagerFactory(String algorithm) 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(algorithm); - kmf.init(keystore, password); - return kmf; - } - - protected OpenSslKeyMaterialProvider newMaterialProvider(KeyManagerFactory factory, String password) { - return new OpenSslKeyMaterialProvider(ReferenceCountedOpenSslContext.chooseX509KeyManager( - factory.getKeyManagers()), password); - } - - protected void assertRelease(OpenSslKeyMaterial material) { - assertTrue(material.release()); - } - - @Test - public void testChooseKeyMaterial() throws Exception { - OpenSslKeyMaterialProvider provider = newMaterialProvider(newKeyManagerFactory(), 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 class used by testChooseOpenSslPrivateKeyMaterial(). - */ - private static final class SingleKeyManager implements X509KeyManager { - private final String keyAlias; - private final PrivateKey pk; - private final X509Certificate[] certChain; - - SingleKeyManager(String keyAlias, PrivateKey pk, X509Certificate[] certChain) { - this.keyAlias = keyAlias; - this.pk = pk; - this.certChain = certChain; - } - - @Override - public String[] getClientAliases(String keyType, Principal[] issuers) { - return new String[]{keyAlias}; - } - - @Override - public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) { - return keyAlias; - } - - @Override - public String[] getServerAliases(String keyType, Principal[] issuers) { - return new String[]{keyAlias}; - } - - @Override - public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) { - return keyAlias; - } - - @Override - public X509Certificate[] getCertificateChain(String alias) { - return certChain; - } - - @Override - public PrivateKey getPrivateKey(String alias) { - return pk; - } - } - - @Test - public void testChooseOpenSslPrivateKeyMaterial() throws Exception { - PrivateKey privateKey = SslContext.toPrivateKey( - getClass().getResourceAsStream("localhost_server.key"), - null); - assertNotNull(privateKey); - assertEquals("PKCS#8", privateKey.getFormat()); - final X509Certificate[] certChain = SslContext.toX509Certificates( - getClass().getResourceAsStream("localhost_server.pem")); - assertNotNull(certChain); - PemEncoded pemKey = null; - long pkeyBio = 0L; - OpenSslPrivateKey sslPrivateKey; - try { - pemKey = PemPrivateKey.toPEM(ByteBufAllocator.DEFAULT, true, privateKey); - pkeyBio = ReferenceCountedOpenSslContext.toBIO(ByteBufAllocator.DEFAULT, pemKey.retain()); - sslPrivateKey = new OpenSslPrivateKey(SSL.parsePrivateKey(pkeyBio, null)); - } finally { - ReferenceCountUtil.safeRelease(pemKey); - if (pkeyBio != 0L) { - SSL.freeBIO(pkeyBio); - } - } - final String keyAlias = "key"; - - OpenSslKeyMaterialProvider provider = new OpenSslKeyMaterialProvider( - new SingleKeyManager(keyAlias, sslPrivateKey, certChain), - null); - OpenSslKeyMaterial material = provider.chooseKeyMaterial(ByteBufAllocator.DEFAULT, keyAlias); - assertNotNull(material); - assertEquals(2, sslPrivateKey.refCnt()); - assertEquals(1, material.refCnt()); - assertTrue(material.release()); - assertEquals(1, sslPrivateKey.refCnt()); - // Can get material multiple times from the same key - material = provider.chooseKeyMaterial(ByteBufAllocator.DEFAULT, keyAlias); - assertNotNull(material); - assertEquals(2, sslPrivateKey.refCnt()); - assertTrue(material.release()); - assertTrue(sslPrivateKey.release()); - assertEquals(0, sslPrivateKey.refCnt()); - assertEquals(0, material.refCnt()); - assertEquals(0, ((OpenSslPrivateKey.OpenSslPrivateKeyMaterial) material).certificateChain); - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslPrivateKeyMethodTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslPrivateKeyMethodTest.java deleted file mode 100644 index 3d8229d405..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslPrivateKeyMethodTest.java +++ /dev/null @@ -1,478 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.ssl; - -import io.netty.bootstrap.Bootstrap; -import io.netty.bootstrap.ServerBootstrap; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.Unpooled; -import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.channel.local.LocalAddress; -import io.netty.channel.local.LocalChannel; -import io.netty.channel.local.LocalHandler; -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.Future; -import io.netty.util.concurrent.ImmediateEventExecutor; -import io.netty.util.concurrent.Promise; -import org.hamcrest.Matchers; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLHandshakeException; -import java.net.SocketAddress; -import java.security.NoSuchAlgorithmException; -import java.security.Signature; -import java.security.SignatureException; -import java.security.spec.MGF1ParameterSpec; -import java.security.spec.PSSParameterSpec; -import java.util.ArrayList; -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.ThreadLocalRandom; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - -import static io.netty.handler.ssl.OpenSslTestUtils.checkShouldUseKeyManagerFactory; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assumptions.assumeTrue; - -public class OpenSslPrivateKeyMethodTest { - private static final String RFC_CIPHER_NAME = "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"; - private static EventLoopGroup GROUP; - private static SelfSignedCertificate CERT; - private static ExecutorService EXECUTOR; - - static Collection parameters() { - List dst = new ArrayList(); - for (int a = 0; a < 2; a++) { - for (int b = 0; b < 2; b++) { - for (int c = 0; c < 2; c++) { - dst.add(new Object[] { a == 0, b == 0, c == 0 }); - } - } - } - return dst; - } - - @BeforeAll - public static void init() throws Exception { - checkShouldUseKeyManagerFactory(); - - assumeTrue(OpenSsl.isBoringSSL()); - // Check if the cipher is supported at all which may not be the case for various JDK versions and OpenSSL API - // implementations. - assumeCipherAvailable(SslProvider.OPENSSL); - assumeCipherAvailable(SslProvider.JDK); - - GROUP = new MultithreadEventLoopGroup(LocalHandler.newFactory()); - CERT = new SelfSignedCertificate(); - EXECUTOR = Executors.newCachedThreadPool(DelegateThread::new); - } - - @AfterAll - public static void destroy() { - if (OpenSsl.isBoringSSL()) { - GROUP.shutdownGracefully(); - CERT.delete(); - EXECUTOR.shutdown(); - } - } - - private static void assumeCipherAvailable(SslProvider provider) throws NoSuchAlgorithmException { - boolean cipherSupported = false; - if (provider == SslProvider.JDK) { - SSLEngine engine = SSLContext.getDefault().createSSLEngine(); - for (String c: engine.getSupportedCipherSuites()) { - if (RFC_CIPHER_NAME.equals(c)) { - cipherSupported = true; - break; - } - } - } else { - cipherSupported = OpenSsl.isCipherSuiteAvailable(RFC_CIPHER_NAME); - } - assumeTrue(cipherSupported, "Unsupported cipher: " + RFC_CIPHER_NAME); - } - - private static SslHandler newSslHandler(SslContext sslCtx, ByteBufAllocator allocator, Executor executor) { - if (executor == null) { - return sslCtx.newHandler(allocator); - } else { - return sslCtx.newHandler(allocator, executor); - } - } - - private SslContext buildServerContext(OpenSslPrivateKeyMethod method) throws Exception { - List ciphers = Collections.singletonList(RFC_CIPHER_NAME); - - final KeyManagerFactory kmf = OpenSslX509KeyManagerFactory.newKeyless(CERT.cert()); - - return SslContextBuilder.forServer(kmf) - .sslProvider(SslProvider.OPENSSL) - .ciphers(ciphers) - // As this is not a TLSv1.3 cipher we should ensure we talk something else. - .protocols(SslProtocols.TLS_v1_2) - .option(OpenSslContextOption.PRIVATE_KEY_METHOD, method) - .build(); - } - - private SslContext buildClientContext() throws Exception { - return SslContextBuilder.forClient() - .sslProvider(SslProvider.JDK) - .ciphers(Collections.singletonList(RFC_CIPHER_NAME)) - // As this is not a TLSv1.3 cipher we should ensure we talk something else. - .protocols(SslProtocols.TLS_v1_2) - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .build(); - } - - private static Executor delegateExecutor(boolean delegate) { - return delegate ? EXECUTOR : null; - } - private SslContext buildServerContext(OpenSslAsyncPrivateKeyMethod method) throws Exception { - List ciphers = Collections.singletonList(RFC_CIPHER_NAME); - - final KeyManagerFactory kmf = OpenSslX509KeyManagerFactory.newKeyless(CERT.cert()); - - return SslContextBuilder.forServer(kmf) - .sslProvider(SslProvider.OPENSSL) - .ciphers(ciphers) - // As this is not a TLSv1.3 cipher we should ensure we talk something else. - .protocols(SslProtocols.TLS_v1_2) - .option(OpenSslContextOption.ASYNC_PRIVATE_KEY_METHOD, method) - .build(); - } - - private static void assertThread(boolean delegate) { - if (delegate && OpenSslContext.USE_TASKS) { - assertEquals(DelegateThread.class, Thread.currentThread().getClass()); - } - } - - @ParameterizedTest(name = "{index}: delegate = {0}, async = {1}, newThread={2}") - @MethodSource("parameters") - public void testPrivateKeyMethod(final boolean delegate, boolean async, boolean newThread) throws Exception { - final AtomicBoolean signCalled = new AtomicBoolean(); - OpenSslPrivateKeyMethod keyMethod = new OpenSslPrivateKeyMethod() { - @Override - public byte[] sign(SSLEngine engine, int signatureAlgorithm, byte[] input) throws Exception { - signCalled.set(true); - assertThread(delegate); - - assertEquals(CERT.cert().getPublicKey(), - engine.getSession().getLocalCertificates()[0].getPublicKey()); - - // Delegate signing to Java implementation. - final Signature signature; - // Depending on the Java version it will pick one or the other. - if (signatureAlgorithm == OpenSslPrivateKeyMethod.SSL_SIGN_RSA_PKCS1_SHA256) { - signature = Signature.getInstance("SHA256withRSA"); - } else if (signatureAlgorithm == OpenSslPrivateKeyMethod.SSL_SIGN_RSA_PSS_RSAE_SHA256) { - signature = Signature.getInstance("RSASSA-PSS"); - signature.setParameter(new PSSParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, - 32, 1)); - } else { - throw new AssertionError("Unexpected signature algorithm " + signatureAlgorithm); - } - signature.initSign(CERT.key()); - signature.update(input); - return signature.sign(); - } - - @Override - public byte[] decrypt(SSLEngine engine, byte[] input) { - throw new UnsupportedOperationException(); - } - }; - - final SslContext sslServerContext = async ? buildServerContext( - new OpenSslPrivateKeyMethodAdapter(keyMethod, newThread)) : buildServerContext(keyMethod); - - final SslContext sslClientContext = buildClientContext(); - try { - try { - final Promise serverPromise = GROUP.next().newPromise(); - final Promise clientPromise = GROUP.next().newPromise(); - - ChannelHandler serverHandler = new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) { - ChannelPipeline pipeline = ch.pipeline(); - pipeline.addLast(newSslHandler(sslServerContext, ch.alloc(), delegateExecutor(delegate))); - - pipeline.addLast(new SimpleChannelInboundHandler() { - @Override - public void channelInactive(ChannelHandlerContext ctx) { - serverPromise.cancel(); - ctx.fireChannelInactive(); - } - - @Override - public void messageReceived(ChannelHandlerContext ctx, Object msg) { - if (serverPromise.trySuccess(null)) { - ctx.writeAndFlush(Unpooled.wrappedBuffer(new byte[] {'P', 'O', 'N', 'G'})); - } - ctx.close(); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - if (!serverPromise.tryFailure(cause)) { - ctx.fireExceptionCaught(cause); - } - } - }); - } - }; - - LocalAddress address = new LocalAddress("test-" + SslProvider.OPENSSL - + '-' + SslProvider.JDK + '-' + RFC_CIPHER_NAME + '-' + delegate); - - Channel server = server(address, serverHandler); - try { - ChannelHandler clientHandler = new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) { - ChannelPipeline pipeline = ch.pipeline(); - pipeline.addLast(newSslHandler(sslClientContext, ch.alloc(), delegateExecutor(delegate))); - - pipeline.addLast(new SimpleChannelInboundHandler() { - @Override - public void channelInactive(ChannelHandlerContext ctx) { - clientPromise.cancel(); - ctx.fireChannelInactive(); - } - - @Override - public void messageReceived(ChannelHandlerContext ctx, Object msg) { - clientPromise.trySuccess(null); - ctx.close(); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - if (!clientPromise.tryFailure(cause)) { - ctx.fireExceptionCaught(cause); - } - } - }); - } - }; - - Channel client = client(server, clientHandler); - try { - client.writeAndFlush(Unpooled.wrappedBuffer(new byte[] {'P', 'I', 'N', 'G'})) - .syncUninterruptibly(); - - Future clientFuture = clientPromise.asFuture(); - Future serverFuture = serverPromise.asFuture(); - assertTrue(clientFuture.await(5L, TimeUnit.SECONDS), "client timeout"); - assertTrue(serverFuture.await(5L, TimeUnit.SECONDS), "server timeout"); - - clientFuture.sync(); - serverFuture.sync(); - assertTrue(signCalled.get()); - } finally { - client.close().sync(); - } - } finally { - server.close().sync(); - } - } finally { - ReferenceCountUtil.release(sslClientContext); - } - } finally { - ReferenceCountUtil.release(sslServerContext); - } - } - - @ParameterizedTest(name = "{index}: delegate = {0}") - @MethodSource("parameters") - public void testPrivateKeyMethodFailsBecauseOfException(final boolean delegate) throws Exception { - testPrivateKeyMethodFails(delegate, false); - } - - @ParameterizedTest(name = "{index}: delegate = {0}") - @MethodSource("parameters") - public void testPrivateKeyMethodFailsBecauseOfNull(final boolean delegate) throws Exception { - testPrivateKeyMethodFails(delegate, true); - } - - private void testPrivateKeyMethodFails(final boolean delegate, final boolean returnNull) throws Exception { - final SslContext sslServerContext = buildServerContext(new OpenSslPrivateKeyMethod() { - @Override - public byte[] sign(SSLEngine engine, int signatureAlgorithm, byte[] input) throws Exception { - assertThread(delegate); - if (returnNull) { - return null; - } - throw new SignatureException(); - } - - @Override - public byte[] decrypt(SSLEngine engine, byte[] input) { - throw new UnsupportedOperationException(); - } - }); - final SslContext sslClientContext = buildClientContext(); - - SslHandler serverSslHandler = newSslHandler( - sslServerContext, UnpooledByteBufAllocator.DEFAULT, delegateExecutor(delegate)); - SslHandler clientSslHandler = newSslHandler( - sslClientContext, UnpooledByteBufAllocator.DEFAULT, delegateExecutor(delegate)); - - try { - try { - LocalAddress address = new LocalAddress("test-" + SslProvider.OPENSSL - + '-' + SslProvider.JDK + '-' + RFC_CIPHER_NAME + '-' + delegate); - - Channel server = server(address, serverSslHandler); - try { - Channel client = client(server, clientSslHandler); - try { - Throwable clientCause = clientSslHandler.handshakeFuture().await().cause(); - Throwable serverCause = serverSslHandler.handshakeFuture().await().cause(); - assertNotNull(clientCause); - assertThat(serverCause, Matchers.instanceOf(SSLHandshakeException.class)); - } 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).get(); - } - - 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).get(); - } - - private static final class DelegateThread extends Thread { - DelegateThread(Runnable target) { - super(target); - } - } - - private static final class OpenSslPrivateKeyMethodAdapter implements OpenSslAsyncPrivateKeyMethod { - private final OpenSslPrivateKeyMethod keyMethod; - private final boolean newThread; - - OpenSslPrivateKeyMethodAdapter(OpenSslPrivateKeyMethod keyMethod, boolean newThread) { - this.keyMethod = keyMethod; - this.newThread = newThread; - } - - @Override - public Future sign(final SSLEngine engine, final int signatureAlgorithm, final byte[] input) { - final Promise promise = ImmediateEventExecutor.INSTANCE.newPromise(); - try { - if (newThread) { - // Let's run these in an extra thread to ensure that this would also work if the promise is - // notified later. - new DelegateThread(new Runnable() { - @Override - public void run() { - try { - // Let's sleep for some time to ensure we would notify in an async fashion - Thread.sleep(ThreadLocalRandom.current().nextLong(100, 500)); - promise.setSuccess(keyMethod.sign(engine, signatureAlgorithm, input)); - } catch (Throwable cause) { - promise.setFailure(cause); - } - } - }).start(); - } else { - promise.setSuccess(keyMethod.sign(engine, signatureAlgorithm, input)); - } - } catch (Throwable cause) { - promise.setFailure(cause); - } - return promise.asFuture(); - } - - @Override - public Future decrypt(final SSLEngine engine, final byte[] input) { - final Promise promise = ImmediateEventExecutor.INSTANCE.newPromise(); - try { - if (newThread) { - // Let's run these in an extra thread to ensure that this would also work if the promise is - // notified later. - new DelegateThread(new Runnable() { - @Override - public void run() { - try { - // Let's sleep for some time to ensure we would notify in an async fashion - Thread.sleep(ThreadLocalRandom.current().nextLong(100, 500)); - promise.setSuccess(keyMethod.decrypt(engine, input)); - } catch (Throwable cause) { - promise.setFailure(cause); - } - } - }).start(); - } else { - promise.setSuccess(keyMethod.decrypt(engine, input)); - } - } catch (Throwable cause) { - promise.setFailure(cause); - } - return promise.asFuture(); - } - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslRenegotiateTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslRenegotiateTest.java deleted file mode 100644 index f77cc6fd28..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslRenegotiateTest.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import org.junit.jupiter.api.BeforeAll; - -import java.util.concurrent.atomic.AtomicReference; - -import javax.net.ssl.SSLException; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.is; - -public class OpenSslRenegotiateTest extends RenegotiateTest { - - @BeforeAll - public static void checkOpenSsl() { - OpenSsl.ensureAvailability(); - } - - @Override - protected SslProvider serverSslProvider() { - return SslProvider.OPENSSL; - } - - protected void verifyResult(AtomicReference error) throws Throwable { - Throwable cause = error.get(); - // Renegotation is not supported by the OpenSslEngine. - assertThat(cause, is(instanceOf(SSLException.class))); - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslServerContextTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslServerContextTest.java deleted file mode 100644 index eb9c71402c..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslServerContextTest.java +++ /dev/null @@ -1,34 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import org.junit.jupiter.api.BeforeAll; - -import javax.net.ssl.SSLException; -import java.io.File; - -public class OpenSslServerContextTest extends SslContextTest { - - @BeforeAll - public static void checkOpenSsl() { - OpenSsl.ensureAvailability(); - } - - @Override - protected SslContext newSslContext(File crtFile, File keyFile, String pass) throws SSLException { - return SslContextBuilder.forServer(crtFile, keyFile, pass).sslProvider(SslProvider.OPENSSL).build(); - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslTest.java deleted file mode 100644 index 18e8464ec5..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslTest.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class OpenSslTest { - - @Test - public void testDefaultCiphers() { - if (!OpenSsl.isTlsv13Supported()) { - assertTrue( - OpenSsl.DEFAULT_CIPHERS.size() <= SslUtils.DEFAULT_CIPHER_SUITES.length); - } - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslTestUtils.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslTestUtils.java deleted file mode 100644 index 0d3387baf2..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslTestUtils.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import static org.junit.jupiter.api.Assumptions.assumeTrue; - -final class OpenSslTestUtils { - private OpenSslTestUtils() { - } - - static void checkShouldUseKeyManagerFactory() { - assumeTrue(OpenSsl.supportsKeyManagerFactory()); - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslX509KeyManagerFactoryProviderTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslX509KeyManagerFactoryProviderTest.java deleted file mode 100644 index c111ab8f79..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/OpenSslX509KeyManagerFactoryProviderTest.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.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(); - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/OptionalSslHandlerTest.java b/handler/src/test/java/io/netty/handler/ssl/OptionalSslHandlerTest.java deleted file mode 100644 index 1085702c29..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/OptionalSslHandlerTest.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; - -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; -import static org.mockito.Mockito.when; - -public class OptionalSslHandlerTest { - - private static final String SSL_HANDLER_NAME = "sslhandler"; - private static final String HANDLER_NAME = "handler"; - - @Mock - private ChannelHandlerContext context; - - @Mock - private SslContext sslContext; - - @Mock - private ChannelPipeline pipeline; - - @BeforeEach - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - when(context.pipeline()).thenReturn(pipeline); - } - - @Test - public void handlerRemoved() throws Exception { - OptionalSslHandler handler = new OptionalSslHandler(sslContext); - final ByteBuf payload = Unpooled.copiedBuffer("plaintext".getBytes()); - try { - handler.decode(context, payload); - verify(pipeline).remove(handler); - } finally { - payload.release(); - } - } - - @Test - public void handlerReplaced() throws Exception { - final ChannelHandler nonSslHandler = Mockito.mock(ChannelHandler.class); - OptionalSslHandler handler = new OptionalSslHandler(sslContext) { - @Override - protected ChannelHandler newNonSslHandler(ChannelHandlerContext context) { - return nonSslHandler; - } - - @Override - protected String newNonSslHandlerName() { - return HANDLER_NAME; - } - }; - final ByteBuf payload = Unpooled.copiedBuffer("plaintext".getBytes()); - try { - handler.decode(context, payload); - verify(pipeline).replace(handler, HANDLER_NAME, nonSslHandler); - } finally { - payload.release(); - } - } - - @Test - public void sslHandlerReplaced() throws Exception { - final SslHandler sslHandler = Mockito.mock(SslHandler.class); - OptionalSslHandler handler = new OptionalSslHandler(sslContext) { - @Override - protected SslHandler newSslHandler(ChannelHandlerContext context, SslContext sslContext) { - return sslHandler; - } - - @Override - protected String newSslHandlerName() { - return SSL_HANDLER_NAME; - } - }; - final ByteBuf payload = Unpooled.wrappedBuffer(new byte[] { 22, 3, 1, 0, 5 }); - try { - handler.decode(context, payload); - verify(pipeline).replace(handler, SSL_HANDLER_NAME, sslHandler); - } finally { - payload.release(); - } - } - - @Test - public void decodeBuffered() throws Exception { - OptionalSslHandler handler = new OptionalSslHandler(sslContext); - final ByteBuf payload = Unpooled.wrappedBuffer(new byte[] { 22, 3 }); - try { - handler.decode(context, payload); - verifyZeroInteractions(pipeline); - } finally { - payload.release(); - } - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/ParameterizedSslHandlerTest.java b/handler/src/test/java/io/netty/handler/ssl/ParameterizedSslHandlerTest.java deleted file mode 100644 index 15af2e1b78..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/ParameterizedSslHandlerTest.java +++ /dev/null @@ -1,692 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.bootstrap.Bootstrap; -import io.netty.bootstrap.ServerBootstrap; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.CompositeByteBuf; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelOption; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.ServerChannel; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.channel.local.LocalAddress; -import io.netty.channel.local.LocalChannel; -import io.netty.channel.local.LocalHandler; -import io.netty.channel.local.LocalServerChannel; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioServerSocketChannel; -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.ReferenceCountUtil; -import io.netty.util.concurrent.Promise; -import io.netty.util.internal.EmptyArrays; -import io.netty.util.internal.ResourcesUtil; -import org.junit.jupiter.api.Timeout; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -import javax.net.ssl.ManagerFactoryParameters; -import javax.net.ssl.SSLException; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.PrintStream; -import java.io.UnsupportedEncodingException; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.security.KeyStore; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - -import static io.netty.buffer.ByteBufUtil.writeAscii; -import static java.util.concurrent.ThreadLocalRandom.current; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class ParameterizedSslHandlerTest { - - private static final String PARAMETERIZED_NAME = "{index}: clientProvider={0}, {index}: serverProvider={1}"; - - static Collection data() { - List providers = new ArrayList<>(3); - if (OpenSsl.isAvailable()) { - providers.add(SslProvider.OPENSSL); - providers.add(SslProvider.OPENSSL_REFCNT); - } - providers.add(SslProvider.JDK); - - List params = new ArrayList<>(); - - for (SslProvider cp: providers) { - for (SslProvider sp: providers) { - params.add(new Object[] { cp, sp }); - } - } - return params; - } - - @ParameterizedTest(name = PARAMETERIZED_NAME) - @MethodSource("data") - @Timeout(value = 48000, unit = TimeUnit.MILLISECONDS) - public void testCompositeBufSizeEstimationGuaranteesSynchronousWrite( - SslProvider clientProvider, SslProvider serverProvider) - throws Exception { - compositeBufSizeEstimationGuaranteesSynchronousWrite(serverProvider, clientProvider, - true, true, true); - compositeBufSizeEstimationGuaranteesSynchronousWrite(serverProvider, clientProvider, - true, true, false); - compositeBufSizeEstimationGuaranteesSynchronousWrite(serverProvider, clientProvider, - true, false, true); - compositeBufSizeEstimationGuaranteesSynchronousWrite(serverProvider, clientProvider, - true, false, false); - compositeBufSizeEstimationGuaranteesSynchronousWrite(serverProvider, clientProvider, - false, true, true); - compositeBufSizeEstimationGuaranteesSynchronousWrite(serverProvider, clientProvider, - false, true, false); - compositeBufSizeEstimationGuaranteesSynchronousWrite(serverProvider, clientProvider, - false, false, true); - compositeBufSizeEstimationGuaranteesSynchronousWrite(serverProvider, clientProvider, - false, false, false); - } - - private static void compositeBufSizeEstimationGuaranteesSynchronousWrite( - SslProvider serverProvider, SslProvider clientProvider, - final boolean serverDisableWrapSize, - final boolean letHandlerCreateServerEngine, final boolean letHandlerCreateClientEngine) - throws CertificateException, SSLException, ExecutionException, InterruptedException { - SelfSignedCertificate ssc = new SelfSignedCertificate(); - - final SslContext sslServerCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(serverProvider) - .build(); - - final SslContext sslClientCtx = SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(clientProvider).build(); - - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - Channel sc = null; - Channel cc = null; - try { - final Promise donePromise = group.next().newPromise(); - // The goal is to provide the SSLEngine with many ByteBuf components to ensure that the overhead for wrap - // is correctly accounted for on each component. - final int numComponents = 150; - // This is the TLS packet size. The goal is to divide the maximum amount of application data that can fit - // into a single TLS packet into many components to ensure the overhead is correctly taken into account. - final int desiredBytes = 16384; - final int singleComponentSize = desiredBytes / numComponents; - final int expectedBytes = numComponents * singleComponentSize; - - sc = new ServerBootstrap() - .group(group) - .channel(NioServerSocketChannel.class) - .childHandler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - final SslHandler handler = letHandlerCreateServerEngine - ? sslServerCtx.newHandler(ch.alloc()) - : new SslHandler(sslServerCtx.newEngine(ch.alloc())); - if (serverDisableWrapSize) { - handler.setWrapDataSize(-1); - } - ch.pipeline().addLast(handler); - ch.pipeline().addLast(new ChannelHandler() { - private boolean sentData; - private Throwable writeCause; - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { - if (evt instanceof SslHandshakeCompletionEvent) { - SslHandshakeCompletionEvent sslEvt = (SslHandshakeCompletionEvent) evt; - if (sslEvt.isSuccess()) { - CompositeByteBuf content = ctx.alloc().compositeDirectBuffer(numComponents); - for (int i = 0; i < numComponents; ++i) { - ByteBuf buf = ctx.alloc().directBuffer(singleComponentSize); - buf.writerIndex(buf.writerIndex() + singleComponentSize); - content.addComponent(true, buf); - } - ctx.writeAndFlush(content).addListener(future -> { - writeCause = future.cause(); - if (writeCause == null) { - sentData = true; - } - }); - } else { - donePromise.tryFailure(sslEvt.cause()); - } - } - ctx.fireUserEventTriggered(evt); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - donePromise.tryFailure(new IllegalStateException("server exception sentData: " + - sentData + " writeCause: " + writeCause, cause)); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) { - donePromise.tryFailure(new IllegalStateException("server closed sentData: " + - sentData + " writeCause: " + writeCause)); - } - }); - } - }).bind(new InetSocketAddress(0)).get(); - - cc = new Bootstrap() - .group(group) - .channel(NioSocketChannel.class) - .handler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - if (letHandlerCreateClientEngine) { - ch.pipeline().addLast(sslClientCtx.newHandler(ch.alloc())); - } else { - ch.pipeline().addLast(new SslHandler(sslClientCtx.newEngine(ch.alloc()))); - } - ch.pipeline().addLast(new ChannelHandler() { - private int bytesSeen; - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - if (msg instanceof ByteBuf) { - bytesSeen += ((ByteBuf) msg).readableBytes(); - if (bytesSeen == expectedBytes) { - donePromise.trySuccess(null); - } - } - ReferenceCountUtil.release(msg); - } - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { - if (evt instanceof SslHandshakeCompletionEvent) { - SslHandshakeCompletionEvent sslEvt = (SslHandshakeCompletionEvent) evt; - if (!sslEvt.isSuccess()) { - donePromise.tryFailure(sslEvt.cause()); - } - } - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - donePromise.tryFailure(new IllegalStateException("client exception. bytesSeen: " + - bytesSeen, cause)); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) { - donePromise.tryFailure(new IllegalStateException("client closed. bytesSeen: " + - bytesSeen)); - } - }); - } - }).connect(sc.localAddress()).get(); - - donePromise.asFuture().sync(); - } finally { - if (cc != null) { - cc.close().syncUninterruptibly(); - } - if (sc != null) { - sc.close().syncUninterruptibly(); - } - group.shutdownGracefully(); - - ReferenceCountUtil.release(sslServerCtx); - ReferenceCountUtil.release(sslClientCtx); - ssc.delete(); - } - } - - @ParameterizedTest(name = PARAMETERIZED_NAME) - @MethodSource("data") - @Timeout(value = 30000, unit = TimeUnit.MILLISECONDS) - public void testAlertProducedAndSend(SslProvider clientProvider, SslProvider serverProvider) throws Exception { - SelfSignedCertificate ssc = new SelfSignedCertificate(); - - final SslContext sslServerCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(serverProvider) - .trustManager(new SimpleTrustManagerFactory() { - @Override - protected void engineInit(KeyStore keyStore) { } - @Override - protected void engineInit(ManagerFactoryParameters managerFactoryParameters) { } - - @Override - protected TrustManager[] engineGetTrustManagers() { - return new TrustManager[] { new X509TrustManager() { - - @Override - public void checkClientTrusted(X509Certificate[] x509Certificates, String s) - throws CertificateException { - // Fail verification which should produce an alert that is send back to the client. - throw new CertificateException(); - } - - @Override - public void checkServerTrusted(X509Certificate[] x509Certificates, String s) { - // NOOP - } - - @Override - public X509Certificate[] getAcceptedIssuers() { - return EmptyArrays.EMPTY_X509_CERTIFICATES; - } - } }; - } - }).clientAuth(ClientAuth.REQUIRE).build(); - - final SslContext sslClientCtx = SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .keyManager(ResourcesUtil.getFile(getClass(), "test.crt"), - ResourcesUtil.getFile(getClass(), "test_unencrypted.pem")) - .sslProvider(clientProvider).build(); - - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - Channel sc = null; - Channel cc = null; - try { - final Promise promise = group.next().newPromise(); - sc = new ServerBootstrap() - .group(group) - .channel(NioServerSocketChannel.class) - .childHandler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - ch.pipeline().addLast(sslServerCtx.newHandler(ch.alloc())); - ch.pipeline().addLast(new ChannelHandler() { - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - // Just trigger a close - ctx.close(); - } - }); - } - }).bind(new InetSocketAddress(0)).get(); - - cc = new Bootstrap() - .group(group) - .channel(NioSocketChannel.class) - .handler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - ch.pipeline().addLast(sslClientCtx.newHandler(ch.alloc())); - ch.pipeline().addLast(new ChannelHandler() { - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - if (cause.getCause() instanceof SSLException) { - // We received the alert and so produce an SSLException. - promise.trySuccess(null); - } - } - }); - } - }).connect(sc.localAddress()).get(); - - promise.asFuture().syncUninterruptibly(); - } finally { - if (cc != null) { - cc.close().syncUninterruptibly(); - } - if (sc != null) { - sc.close().syncUninterruptibly(); - } - group.shutdownGracefully(); - - ReferenceCountUtil.release(sslServerCtx); - ReferenceCountUtil.release(sslClientCtx); - } - } - - @ParameterizedTest(name = PARAMETERIZED_NAME) - @MethodSource("data") - @Timeout(value = 30000, unit = TimeUnit.MILLISECONDS) - public void testCloseNotify(SslProvider clientProvider, SslProvider serverProvider) throws Exception { - testCloseNotify(clientProvider, serverProvider, 5000, false); - } - - @ParameterizedTest(name = PARAMETERIZED_NAME) - @MethodSource("data") - @Timeout(value = 30000, unit = TimeUnit.MILLISECONDS) - public void testCloseNotifyReceivedTimeout(SslProvider clientProvider, SslProvider serverProvider) - throws Exception { - testCloseNotify(clientProvider, serverProvider, 100, true); - } - - @ParameterizedTest(name = PARAMETERIZED_NAME) - @MethodSource("data") - @Timeout(value = 30000, unit = TimeUnit.MILLISECONDS) - public void testCloseNotifyNotWaitForResponse(SslProvider clientProvider, SslProvider serverProvider) - throws Exception { - testCloseNotify(clientProvider, serverProvider, 0, false); - } - - private static void testCloseNotify(SslProvider clientProvider, SslProvider serverProvider, - final long closeNotifyReadTimeout, final boolean timeout) throws Exception { - SelfSignedCertificate ssc = new SelfSignedCertificate(); - - final SslContext sslServerCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .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(SslProtocols.TLS_v1_2) - .build(); - - final SslContext sslClientCtx = SslContextBuilder.forClient() - .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(SslProtocols.TLS_v1_2) - .build(); - - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - Channel sc = null; - Channel cc = null; - try { - final Promise clientPromise = group.next().newPromise(); - final Promise serverPromise = group.next().newPromise(); - - sc = new ServerBootstrap() - .group(group) - .channel(NioServerSocketChannel.class) - .childHandler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - SslHandler handler = sslServerCtx.newHandler(ch.alloc()); - handler.setCloseNotifyReadTimeoutMillis(closeNotifyReadTimeout); - handler.sslCloseFuture().cascadeTo(serverPromise); - - handler.handshakeFuture().addListener(future -> { - if (future.isFailed()) { - // Something bad happened during handshake fail the promise! - serverPromise.tryFailure(future.cause()); - } - }); - ch.pipeline().addLast(handler); - } - }).bind(new InetSocketAddress(0)).get(); - - cc = new Bootstrap() - .group(group) - .channel(NioSocketChannel.class) - .handler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - final AtomicBoolean closeSent = new AtomicBoolean(); - if (timeout) { - ch.pipeline().addFirst(new ChannelHandler() { - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - if (closeSent.get()) { - // Drop data on the floor so we will get a timeout while waiting for the - // close_notify. - ReferenceCountUtil.release(msg); - } else { - ctx.fireChannelRead(msg); - } - } - }); - } - - SslHandler handler = sslClientCtx.newHandler(ch.alloc()); - handler.setCloseNotifyReadTimeoutMillis(closeNotifyReadTimeout); - handler.sslCloseFuture().cascadeTo(clientPromise); - handler.handshakeFuture().addListener(future -> { - if (future.isSuccess()) { - closeSent.compareAndSet(false, true); - future.getNow().close(); - } else { - // Something bad happened during handshake fail the promise! - clientPromise.tryFailure(future.cause()); - } - }); - ch.pipeline().addLast(handler); - } - }).connect(sc.localAddress()).get(); - - serverPromise.asFuture().awaitUninterruptibly(); - clientPromise.asFuture().awaitUninterruptibly(); - - // Server always received the close_notify as the client triggers the close sequence. - assertTrue(serverPromise.isSuccess()); - - // Depending on if we wait for the response or not the promise will be failed or not. - if (closeNotifyReadTimeout > 0 && !timeout) { - assertTrue(clientPromise.isSuccess()); - } else { - assertFalse(clientPromise.isSuccess()); - } - } finally { - if (cc != null) { - cc.close().syncUninterruptibly(); - } - if (sc != null) { - sc.close().syncUninterruptibly(); - } - group.shutdownGracefully(); - - ReferenceCountUtil.release(sslServerCtx); - ReferenceCountUtil.release(sslClientCtx); - } - } - - @ParameterizedTest(name = PARAMETERIZED_NAME) - @MethodSource("data") - @Timeout(value = 30000, unit = TimeUnit.MILLISECONDS) - public void reentryOnHandshakeCompleteNioChannel(SslProvider clientProvider, SslProvider serverProvider) - throws Exception { - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - Class serverClass = NioServerSocketChannel.class; - Class clientClass = NioSocketChannel.class; - SocketAddress bindAddress = new InetSocketAddress(0); - reentryOnHandshakeComplete(clientProvider, serverProvider, group, bindAddress, - serverClass, clientClass, false, false); - reentryOnHandshakeComplete(clientProvider, serverProvider, group, bindAddress, - serverClass, clientClass, false, true); - reentryOnHandshakeComplete(clientProvider, serverProvider, group, bindAddress, - serverClass, clientClass, true, false); - reentryOnHandshakeComplete(clientProvider, serverProvider, group, bindAddress, - serverClass, clientClass, true, true); - } finally { - group.shutdownGracefully(); - } - } - - @ParameterizedTest(name = PARAMETERIZED_NAME) - @MethodSource("data") - @Timeout(value = 30000, unit = TimeUnit.MILLISECONDS) - public void reentryOnHandshakeCompleteLocalChannel(SslProvider clientProvider, SslProvider serverProvider) - throws Exception { - EventLoopGroup group = new MultithreadEventLoopGroup(LocalHandler.newFactory()); - try { - Class serverClass = LocalServerChannel.class; - Class clientClass = LocalChannel.class; - SocketAddress bindAddress = new LocalAddress(String.valueOf(current().nextLong())); - reentryOnHandshakeComplete(clientProvider, serverProvider, group, bindAddress, - serverClass, clientClass, false, false); - reentryOnHandshakeComplete(clientProvider, serverProvider, group, bindAddress, - serverClass, clientClass, false, true); - reentryOnHandshakeComplete(clientProvider, serverProvider, group, bindAddress, - serverClass, clientClass, true, false); - reentryOnHandshakeComplete(clientProvider, serverProvider, group, bindAddress, - serverClass, clientClass, true, true); - } finally { - group.shutdownGracefully(); - } - } - - private static void reentryOnHandshakeComplete(SslProvider clientProvider, SslProvider serverProvider, - EventLoopGroup group, SocketAddress bindAddress, - Class serverClass, - Class clientClass, boolean serverAutoRead, - boolean clientAutoRead) throws Exception { - SelfSignedCertificate ssc = new SelfSignedCertificate(); - final SslContext sslServerCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(serverProvider) - .build(); - - final SslContext sslClientCtx = SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(clientProvider) - .build(); - - Channel sc = null; - Channel cc = null; - try { - final String expectedContent = "HelloWorld"; - final CountDownLatch serverLatch = new CountDownLatch(1); - final CountDownLatch clientLatch = new CountDownLatch(1); - final StringBuilder serverQueue = new StringBuilder(expectedContent.length()); - final StringBuilder clientQueue = new StringBuilder(expectedContent.length()); - - sc = new ServerBootstrap() - .group(group) - .channel(serverClass) - .childOption(ChannelOption.AUTO_READ, serverAutoRead) - .childHandler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) { - ch.pipeline().addLast(disableHandshakeTimeout(sslServerCtx.newHandler(ch.alloc()))); - ch.pipeline().addLast(new ReentryWriteSslHandshakeHandler(expectedContent, serverQueue, - serverLatch)); - } - }).bind(bindAddress).get(); - - cc = new Bootstrap() - .group(group) - .channel(clientClass) - .option(ChannelOption.AUTO_READ, clientAutoRead) - .handler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) { - ch.pipeline().addLast(disableHandshakeTimeout(sslClientCtx.newHandler(ch.alloc()))); - ch.pipeline().addLast(new ReentryWriteSslHandshakeHandler(expectedContent, clientQueue, - clientLatch)); - } - }).connect(sc.localAddress()).get(); - - serverLatch.await(); - assertEquals(expectedContent, serverQueue.toString()); - clientLatch.await(); - assertEquals(expectedContent, clientQueue.toString()); - } finally { - if (cc != null) { - cc.close().syncUninterruptibly(); - } - if (sc != null) { - sc.close().syncUninterruptibly(); - } - - ReferenceCountUtil.release(sslServerCtx); - ReferenceCountUtil.release(sslClientCtx); - } - } - - private static SslHandler disableHandshakeTimeout(SslHandler handler) { - handler.setHandshakeTimeoutMillis(0); - return handler; - } - - private static final class ReentryWriteSslHandshakeHandler extends SimpleChannelInboundHandler { - private final String toWrite; - private final StringBuilder readQueue; - private final CountDownLatch doneLatch; - - ReentryWriteSslHandshakeHandler(String toWrite, StringBuilder readQueue, CountDownLatch doneLatch) { - this.toWrite = toWrite; - this.readQueue = readQueue; - this.doneLatch = doneLatch; - } - - @Override - public void channelActive(ChannelHandlerContext ctx) { - // Write toWrite in two chunks, first here then we get SslHandshakeCompletionEvent (which is re-entry). - ctx.writeAndFlush(writeAscii(ctx.alloc(), toWrite.substring(0, toWrite.length() / 2))); - } - - @Override - protected void messageReceived(ChannelHandlerContext ctx, ByteBuf msg) { - readQueue.append(msg.toString(CharsetUtil.US_ASCII)); - if (readQueue.length() >= toWrite.length()) { - doneLatch.countDown(); - } - } - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { - if (evt instanceof SslHandshakeCompletionEvent) { - SslHandshakeCompletionEvent sslEvt = (SslHandshakeCompletionEvent) evt; - if (sslEvt.isSuccess()) { - // this is the re-entry write, it should be ordered after the subsequent write. - ctx.writeAndFlush(writeAscii(ctx.alloc(), toWrite.substring(toWrite.length() / 2))); - } else { - appendError(sslEvt.cause()); - } - } - ctx.fireUserEventTriggered(evt); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - appendError(cause); - ctx.fireExceptionCaught(cause); - } - - private void appendError(Throwable cause) { - readQueue.append("failed to write '").append(toWrite).append("': "); - - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try { - cause.printStackTrace(new PrintStream(out)); - readQueue.append(out.toString(CharsetUtil.US_ASCII.name())); - } catch (UnsupportedEncodingException ignore) { - // Let's just fallback to using toString(). - readQueue.append(cause); - } finally { - doneLatch.countDown(); - try { - out.close(); - } catch (IOException ignore) { - // ignore - } - } - } - } -} - diff --git a/handler/src/test/java/io/netty/handler/ssl/PemEncodedTest.java b/handler/src/test/java/io/netty/handler/ssl/PemEncodedTest.java deleted file mode 100644 index 4706d5fef3..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/PemEncodedTest.java +++ /dev/null @@ -1,123 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.ssl; - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.security.PrivateKey; - -import io.netty.buffer.UnpooledByteBufAllocator; - -import io.netty.handler.ssl.util.SelfSignedCertificate; -import io.netty.util.ReferenceCountUtil; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assumptions.assumeFalse; - -public class PemEncodedTest { - - @Test - public void testPemEncodedOpenSsl() throws Exception { - testPemEncoded(SslProvider.OPENSSL); - } - - @Test - public void testPemEncodedOpenSslRef() throws Exception { - testPemEncoded(SslProvider.OPENSSL_REFCNT); - } - - private static void testPemEncoded(SslProvider provider) throws Exception { - OpenSsl.ensureAvailability(); - assumeFalse(OpenSsl.supportsKeyManagerFactory()); - PemPrivateKey pemKey; - PemX509Certificate pemCert; - SelfSignedCertificate ssc = new SelfSignedCertificate(); - try { - pemKey = PemPrivateKey.valueOf(toByteArray(ssc.privateKey())); - pemCert = PemX509Certificate.valueOf(toByteArray(ssc.certificate())); - } finally { - ssc.delete(); - } - - SslContext context = SslContextBuilder.forServer(pemKey, pemCert) - .sslProvider(provider) - .build(); - assertEquals(1, pemKey.refCnt()); - assertEquals(1, pemCert.refCnt()); - try { - assertTrue(context instanceof ReferenceCountedOpenSslContext); - } finally { - ReferenceCountUtil.release(context); - assertRelease(pemKey); - assertRelease(pemCert); - } - } - - @Test - public void testEncodedReturnsNull() throws Exception { - assertThrows(IllegalArgumentException.class, new Executable() { - @Override - public void execute() throws Throwable { - 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()); - } - - private static byte[] toByteArray(File file) throws Exception { - FileInputStream in = new FileInputStream(file); - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try { - byte[] buf = new byte[1024]; - int len; - while ((len = in.read(buf)) != -1) { - baos.write(buf, 0, len); - } - } finally { - baos.close(); - } - - return baos.toByteArray(); - } finally { - in.close(); - } - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/PseudoRandomFunctionTest.java b/handler/src/test/java/io/netty/handler/ssl/PseudoRandomFunctionTest.java deleted file mode 100644 index a480fa030c..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/PseudoRandomFunctionTest.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.ssl; - -import io.netty.util.CharsetUtil; -import org.bouncycastle.util.encoders.Hex; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -/** - * The test vectors here were provided via: - * https://www.ietf.org/mail-archive/web/tls/current/msg03416.html - */ -public class PseudoRandomFunctionTest { - - @Test - public void testPrfSha256() { - byte[] secret = Hex.decode("9b be 43 6b a9 40 f0 17 b1 76 52 84 9a 71 db 35"); - byte[] seed = Hex.decode("a0 ba 9f 93 6c da 31 18 27 a6 f7 96 ff d5 19 8c"); - byte[] label = "test label".getBytes(CharsetUtil.US_ASCII); - byte[] expected = Hex.decode( - "e3 f2 29 ba 72 7b e1 7b" + - "8d 12 26 20 55 7c d4 53" + - "c2 aa b2 1d 07 c3 d4 95" + - "32 9b 52 d4 e6 1e db 5a" + - "6b 30 17 91 e9 0d 35 c9" + - "c9 a4 6b 4e 14 ba f9 af" + - "0f a0 22 f7 07 7d ef 17" + - "ab fd 37 97 c0 56 4b ab" + - "4f bc 91 66 6e 9d ef 9b" + - "97 fc e3 4f 79 67 89 ba" + - "a4 80 82 d1 22 ee 42 c5" + - "a7 2e 5a 51 10 ff f7 01" + - "87 34 7b 66"); - byte[] actual = PseudoRandomFunction.hash(secret, label, seed, expected.length, "HmacSha256"); - assertArrayEquals(expected, actual); - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngineTest.java deleted file mode 100644 index aa32b7de2f..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngineTest.java +++ /dev/null @@ -1,112 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty.handler.ssl.util.InsecureTrustManagerFactory; -import io.netty.util.ReferenceCountUtil; -import org.junit.jupiter.api.function.Executable; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -import javax.net.ssl.SSLEngine; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - -public class ReferenceCountedOpenSslEngineTest extends OpenSslEngineTest { - - @Override - protected SslProvider sslClientProvider() { - return SslProvider.OPENSSL_REFCNT; - } - - @Override - protected SslProvider sslServerProvider() { - return SslProvider.OPENSSL_REFCNT; - } - - @Override - protected void cleanupClientSslContext(SslContext ctx) { - ReferenceCountUtil.release(ctx); - } - - @Override - protected void cleanupClientSslEngine(SSLEngine engine) { - ReferenceCountUtil.release(unwrapEngine(engine)); - } - - @Override - protected void cleanupServerSslContext(SslContext ctx) { - ReferenceCountUtil.release(ctx); - } - - @Override - protected void cleanupServerSslEngine(SSLEngine engine) { - ReferenceCountUtil.release(unwrapEngine(engine)); - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testNotLeakOnException(SSLEngineTestParam param) throws Exception { - clientSslCtx = wrapContext(param, SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslClientProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - - assertThrows(NullPointerException.class, new Executable() { - @Override - public void execute() throws Throwable { - clientSslCtx.newEngine(null); - } - }); - } - - @SuppressWarnings("deprecation") - @Override - protected SslContext wrapContext(SSLEngineTestParam param, SslContext context) { - if (context instanceof ReferenceCountedOpenSslContext) { - if (param instanceof OpenSslEngineTestParam) { - ((ReferenceCountedOpenSslContext) context).setUseTasks(((OpenSslEngineTestParam) param).useTasks); - } - // Explicit enable the session cache as its disabled by default on the client side. - ((ReferenceCountedOpenSslContext) context).sessionContext().setSessionCacheEnabled(true); - } - return context; - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void parentContextIsRetainedByChildEngines(SSLEngineTestParam param) throws Exception { - SslContext clientSslCtx = wrapContext(param, SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslClientProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - - SSLEngine engine = clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT); - assertEquals(ReferenceCountUtil.refCnt(clientSslCtx), 2); - - cleanupClientSslContext(clientSslCtx); - assertEquals(ReferenceCountUtil.refCnt(clientSslCtx), 1); - - cleanupClientSslEngine(engine); - assertEquals(ReferenceCountUtil.refCnt(clientSslCtx), 0); - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/RenegotiateTest.java b/handler/src/test/java/io/netty/handler/ssl/RenegotiateTest.java deleted file mode 100644 index 811966dc52..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/RenegotiateTest.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.bootstrap.Bootstrap; -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.local.LocalAddress; -import io.netty.channel.local.LocalChannel; -import io.netty.channel.local.LocalHandler; -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.FutureListener; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; - -public abstract class RenegotiateTest { - - @Test - @Timeout(value = 30000, unit = TimeUnit.MILLISECONDS) - public void testRenegotiateServer() throws Throwable { - final AtomicReference error = new AtomicReference<>(); - final CountDownLatch latch = new CountDownLatch(2); - SelfSignedCertificate cert = new SelfSignedCertificate(); - EventLoopGroup group = new MultithreadEventLoopGroup(LocalHandler.newFactory()); - try { - final SslContext context = SslContextBuilder.forServer(cert.key(), cert.cert()) - .sslProvider(serverSslProvider()) - .protocols(SslProtocols.TLS_v1_2) - .build(); - - ServerBootstrap sb = new ServerBootstrap(); - sb.group(group).channel(LocalServerChannel.class) - .childHandler(new ChannelInitializer<>() { - @Override - protected void initChannel(Channel ch) { - SslHandler handler = context.newHandler(ch.alloc()); - handler.setHandshakeTimeoutMillis(0); - ch.pipeline().addLast(handler); - ch.pipeline().addLast(new ChannelHandler() { - - private boolean renegotiate; - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - ReferenceCountUtil.release(msg); - } - - @Override - public void userEventTriggered( - final ChannelHandlerContext ctx, Object evt) { - if (!renegotiate && evt instanceof SslHandshakeCompletionEvent) { - SslHandshakeCompletionEvent event = (SslHandshakeCompletionEvent) evt; - - if (event.isSuccess()) { - final SslHandler handler = ctx.pipeline().get(SslHandler.class); - - renegotiate = true; - handler.renegotiate().addListener((FutureListener) future -> { - if (future.isFailed()) { - error.compareAndSet(null, future.cause()); - ctx.close(); - } - latch.countDown(); - }); - } else { - error.compareAndSet(null, event.cause()); - latch.countDown(); - - ctx.close(); - } - } - } - }); - } - }); - Channel channel = sb.bind(new LocalAddress("test")).get(); - - final SslContext clientContext = SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(SslProvider.JDK) - .protocols(SslProtocols.TLS_v1_2) - .build(); - - Bootstrap bootstrap = new Bootstrap(); - bootstrap.group(group).channel(LocalChannel.class) - .handler(new ChannelInitializer<>() { - @Override - protected void initChannel(Channel ch) { - SslHandler handler = clientContext.newHandler(ch.alloc()); - handler.setHandshakeTimeoutMillis(0); - ch.pipeline().addLast(handler); - ch.pipeline().addLast(new ChannelHandler() { - @Override - public void userEventTriggered( - ChannelHandlerContext ctx, Object evt) { - if (evt instanceof SslHandshakeCompletionEvent) { - SslHandshakeCompletionEvent event = (SslHandshakeCompletionEvent) evt; - if (!event.isSuccess()) { - error.compareAndSet(null, event.cause()); - ctx.close(); - } - latch.countDown(); - } - } - }); - } - }); - - Channel clientChannel = bootstrap.connect(channel.localAddress()).get(); - latch.await(); - clientChannel.close().syncUninterruptibly(); - channel.close().syncUninterruptibly(); - verifyResult(error); - } finally { - group.shutdownGracefully(); - } - } - - protected abstract SslProvider serverSslProvider(); - - protected void verifyResult(AtomicReference error) throws Throwable { - Throwable cause = error.get(); - if (cause != null) { - throw cause; - } - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java deleted file mode 100644 index fbdc2f4314..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java +++ /dev/null @@ -1,4203 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.bootstrap.Bootstrap; -import io.netty.bootstrap.ServerBootstrap; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.CompositeByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.channel.nio.NioHandler; -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; -import io.netty.util.CharsetUtil; -import io.netty.util.NetUtil; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.ImmediateEventExecutor; -import io.netty.util.concurrent.Promise; -import io.netty.util.internal.EmptyArrays; -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.ResourcesUtil; -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.SystemPropertyUtil; -import org.conscrypt.OpenSSLProvider; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; -import org.junit.jupiter.api.Timeout; -import org.junit.jupiter.api.function.Executable; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.opentest4j.AssertionFailedError; - -import javax.crypto.SecretKey; -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.SSLContext; -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; -import javax.net.ssl.SSLPeerUnverifiedException; -import javax.net.ssl.SSLSession; -import javax.net.ssl.SSLSessionBindingEvent; -import javax.net.ssl.SSLSessionBindingListener; -import javax.net.ssl.SSLSessionContext; -import javax.net.ssl.SSLSocketFactory; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.TrustManagerFactorySpi; -import javax.net.ssl.X509ExtendedKeyManager; -import javax.net.ssl.X509ExtendedTrustManager; -import javax.net.ssl.X509TrustManager; -import javax.security.cert.X509Certificate; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.Closeable; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.nio.ByteBuffer; -import java.nio.channels.ClosedChannelException; -import java.nio.file.Files; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.Principal; -import java.security.PrivateKey; -import java.security.Provider; -import java.security.UnrecoverableKeyException; -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.Enumeration; -import java.util.HashSet; -import java.util.List; -import java.util.Random; -import java.util.Set; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.ThreadLocalRandom; -import java.util.concurrent.TimeUnit; - -import static io.netty.handler.ssl.SslUtils.SSL_RECORD_HEADER_LENGTH; -import static io.netty.handler.ssl.SslUtils.isValidHostNameForSNI; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; -import static org.junit.jupiter.api.Assumptions.assumeFalse; -import static org.junit.jupiter.api.Assumptions.assumeTrue; -import static org.mockito.Mockito.verify; - -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -public abstract class SSLEngineTest { - - private static final String PRINCIPAL_NAME = "CN=e8ac02fa0d65a84219016045db8b05c485b4ecdf.netty.test"; - private final boolean tlsv13Supported; - - @Mock - protected MessageReceiver serverReceiver; - @Mock - protected MessageReceiver clientReceiver; - - protected Throwable serverException; - protected Throwable clientException; - protected SslContext serverSslCtx; - protected SslContext clientSslCtx; - protected ServerBootstrap sb; - protected Bootstrap cb; - protected Channel serverChannel; - protected Channel serverConnectedChannel; - protected Channel clientChannel; - protected CountDownLatch serverLatch; - protected CountDownLatch clientLatch; - - interface MessageReceiver { - void messageReceived(ByteBuf msg); - } - - protected static final class MessageDelegatorChannelHandler extends SimpleChannelInboundHandler { - private final MessageReceiver receiver; - private final CountDownLatch latch; - - public MessageDelegatorChannelHandler(MessageReceiver receiver, CountDownLatch latch) { - super(false); - this.receiver = receiver; - this.latch = latch; - } - - @Override - protected void messageReceived(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { - receiver.messageReceived(msg); - latch.countDown(); - } - } - - enum BufferType { - Direct, - Heap, - Mixed - } - - static final class ProtocolCipherCombo { - private static final ProtocolCipherCombo TLSV12 = new ProtocolCipherCombo( - SslProtocols.TLS_v1_2, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"); - private static final ProtocolCipherCombo TLSV13 = new ProtocolCipherCombo( - SslProtocols.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 + '\'' + - '}'; - } - } - - protected SSLEngineTest(boolean tlsv13Supported) { - this.tlsv13Supported = tlsv13Supported; - } - - protected static class SSLEngineTestParam { - private final BufferType type; - private final ProtocolCipherCombo protocolCipherCombo; - private final boolean delegate; - - SSLEngineTestParam(BufferType type, ProtocolCipherCombo protocolCipherCombo, boolean delegate) { - this.type = type; - this.protocolCipherCombo = protocolCipherCombo; - this.delegate = delegate; - } - - final BufferType type() { - return type; - } - - final ProtocolCipherCombo combo() { - return protocolCipherCombo; - } - - final boolean delegate() { - return delegate; - } - - final List protocols() { - return Collections.singletonList(protocolCipherCombo.protocol); - } - - final List ciphers() { - return Collections.singletonList(protocolCipherCombo.cipher); - } - } - - protected List newTestParams() { - List params = new ArrayList(); - for (BufferType type: BufferType.values()) { - params.add(new SSLEngineTestParam(type, ProtocolCipherCombo.tlsv12(), false)); - params.add(new SSLEngineTestParam(type, ProtocolCipherCombo.tlsv12(), true)); - - if (tlsv13Supported) { - params.add(new SSLEngineTestParam(type, ProtocolCipherCombo.tlsv13(), false)); - params.add(new SSLEngineTestParam(type, ProtocolCipherCombo.tlsv13(), true)); - } - } - return params; - } - - private ExecutorService delegatingExecutor; - - protected ByteBuffer allocateBuffer(BufferType type, int len) { - switch (type) { - case Direct: - return ByteBuffer.allocateDirect(len); - case Heap: - return ByteBuffer.allocate(len); - case Mixed: - return ThreadLocalRandom.current().nextBoolean() ? - ByteBuffer.allocateDirect(len) : ByteBuffer.allocate(len); - default: - throw new Error(); - } - } - - private static final class TestByteBufAllocator implements ByteBufAllocator { - - private final ByteBufAllocator allocator; - private final BufferType type; - - TestByteBufAllocator(ByteBufAllocator allocator, BufferType type) { - this.allocator = allocator; - this.type = type; - } - - @Override - public ByteBuf buffer() { - switch (type) { - case Direct: - return allocator.directBuffer(); - case Heap: - return allocator.heapBuffer(); - case Mixed: - return ThreadLocalRandom.current().nextBoolean() ? - allocator.directBuffer() : allocator.heapBuffer(); - default: - throw new Error(); - } - } - - @Override - public ByteBuf buffer(int initialCapacity) { - switch (type) { - case Direct: - return allocator.directBuffer(initialCapacity); - case Heap: - return allocator.heapBuffer(initialCapacity); - case Mixed: - return ThreadLocalRandom.current().nextBoolean() ? - allocator.directBuffer(initialCapacity) : allocator.heapBuffer(initialCapacity); - default: - throw new Error(); - } - } - - @Override - public ByteBuf buffer(int initialCapacity, int maxCapacity) { - switch (type) { - case Direct: - return allocator.directBuffer(initialCapacity, maxCapacity); - case Heap: - return allocator.heapBuffer(initialCapacity, maxCapacity); - case Mixed: - return ThreadLocalRandom.current().nextBoolean() ? - allocator.directBuffer(initialCapacity, maxCapacity) : - allocator.heapBuffer(initialCapacity, maxCapacity); - default: - throw new Error(); - } - } - - @Override - public ByteBuf ioBuffer() { - return allocator.ioBuffer(); - } - - @Override - public ByteBuf ioBuffer(int initialCapacity) { - return allocator.ioBuffer(initialCapacity); - } - - @Override - public ByteBuf ioBuffer(int initialCapacity, int maxCapacity) { - return allocator.ioBuffer(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() { - switch (type) { - case Direct: - return allocator.compositeDirectBuffer(); - case Heap: - return allocator.compositeHeapBuffer(); - case Mixed: - return ((Random) ThreadLocalRandom.current()).nextBoolean() ? - allocator.compositeDirectBuffer() : - allocator.compositeHeapBuffer(); - default: - throw new Error(); - } - } - - @Override - public CompositeByteBuf compositeBuffer(int maxNumComponents) { - switch (type) { - case Direct: - return allocator.compositeDirectBuffer(maxNumComponents); - case Heap: - return allocator.compositeHeapBuffer(maxNumComponents); - case Mixed: - return ((Random) ThreadLocalRandom.current()).nextBoolean() ? - allocator.compositeDirectBuffer(maxNumComponents) : - allocator.compositeHeapBuffer(maxNumComponents); - default: - throw new Error(); - } - } - - @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); - } - } - - @BeforeEach - public void setup() { - MockitoAnnotations.initMocks(this); - serverLatch = new CountDownLatch(1); - clientLatch = new CountDownLatch(1); - delegatingExecutor = Executors.newCachedThreadPool(); - } - - @AfterEach - public void tearDown() throws InterruptedException { - Future clientCloseFuture = null; - Future serverConnectedCloseFuture = null; - Future serverCloseFuture = null; - if (clientChannel != null) { - clientCloseFuture = clientChannel.close(); - clientChannel = null; - } - if (serverConnectedChannel != null) { - serverConnectedCloseFuture = serverConnectedChannel.close(); - serverConnectedChannel = null; - } - if (serverChannel != null) { - serverCloseFuture = serverChannel.close(); - serverChannel = null; - } - // We must wait for the Channel cleanup to finish. In the case if the ReferenceCountedOpenSslEngineTest - // the ReferenceCountedOpenSslEngine depends upon the SslContext and so we must wait the cleanup the - // SslContext to avoid JVM core dumps! - // - // See https://github.com/netty/netty/issues/5692 - if (clientCloseFuture != null) { - clientCloseFuture.sync(); - } - if (serverConnectedCloseFuture != null) { - serverConnectedCloseFuture.sync(); - } - if (serverCloseFuture != null) { - serverCloseFuture.sync(); - } - if (serverSslCtx != null) { - cleanupServerSslContext(serverSslCtx); - serverSslCtx = null; - } - if (clientSslCtx != null) { - cleanupClientSslContext(clientSslCtx); - clientSslCtx = null; - } - Future serverGroupShutdownFuture = null; - Future serverChildGroupShutdownFuture = null; - Future clientGroupShutdownFuture = null; - if (sb != null) { - serverGroupShutdownFuture = sb.config().group().shutdownGracefully(0, 0, TimeUnit.MILLISECONDS); - serverChildGroupShutdownFuture = sb.config().childGroup().shutdownGracefully(0, 0, TimeUnit.MILLISECONDS); - } - if (cb != null) { - clientGroupShutdownFuture = cb.config().group().shutdownGracefully(0, 0, TimeUnit.MILLISECONDS); - } - if (serverGroupShutdownFuture != null) { - serverGroupShutdownFuture.sync(); - serverChildGroupShutdownFuture.sync(); - } - if (clientGroupShutdownFuture != null) { - clientGroupShutdownFuture.sync(); - } - delegatingExecutor.shutdown(); - serverException = null; - clientException = null; - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testMutualAuthSameCerts(SSLEngineTestParam param) throws Throwable { - mySetupMutualAuth(param, ResourcesUtil.getFile(getClass(), "test_unencrypted.pem"), - ResourcesUtil.getFile(getClass(), "test.crt"), - null); - runTest(null); - assertTrue(serverLatch.await(2, TimeUnit.SECONDS)); - Throwable cause = serverException; - if (cause != null) { - throw cause; - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testSetSupportedCiphers(SSLEngineTestParam param) throws Exception { - if (param.protocolCipherCombo != ProtocolCipherCombo.tlsv12()) { - return; - } - SelfSignedCertificate cert = new SelfSignedCertificate(); - serverSslCtx = wrapContext(param, SslContextBuilder.forServer(cert.key(), cert.cert()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .sslProvider(sslServerProvider()).build()); - final SSLEngine serverEngine = - wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - clientSslCtx = wrapContext(param, SslContextBuilder.forClient() - .trustManager(cert.certificate()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .sslProvider(sslClientProvider()).build()); - final SSLEngine clientEngine = - wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - - final String[] enabledCiphers = { param.ciphers().get(0) }; - - try { - clientEngine.setEnabledCipherSuites(enabledCiphers); - serverEngine.setEnabledCipherSuites(enabledCiphers); - - assertArrayEquals(enabledCiphers, clientEngine.getEnabledCipherSuites()); - assertArrayEquals(enabledCiphers, serverEngine.getEnabledCipherSuites()); - } finally { - cleanupClientSslEngine(clientEngine); - cleanupServerSslEngine(serverEngine); - cert.delete(); - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testIncompatibleCiphers(final SSLEngineTestParam param) throws Exception { - assumeTrue(SslProvider.isTlsv13Supported(sslClientProvider())); - assumeTrue(SslProvider.isTlsv13Supported(sslServerProvider())); - - SelfSignedCertificate ssc = new SelfSignedCertificate(); - // Select a mandatory cipher from the TLSv1.2 RFC https://www.ietf.org/rfc/rfc5246.txt so handshakes won't fail - // due to no shared/supported cipher. - clientSslCtx = wrapContext(param, SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .protocols(SslProtocols.TLS_v1_3, SslProtocols.TLS_v1_2, SslProtocols.TLS_v1) - .sslContextProvider(clientSslContextProvider()) - .sslProvider(sslClientProvider()) - .build()); - - serverSslCtx = wrapContext(param, SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .protocols(SslProtocols.TLS_v1_3, SslProtocols.TLS_v1_2, SslProtocols.TLS_v1) - .sslContextProvider(serverSslContextProvider()) - .sslProvider(sslServerProvider()) - .build()); - SSLEngine clientEngine = null; - SSLEngine serverEngine = null; - try { - clientEngine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - serverEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - - // Set the server to only support a single TLSv1.2 cipher - final String serverCipher = "TLS_RSA_WITH_AES_128_CBC_SHA"; - serverEngine.setEnabledCipherSuites(new String[] { serverCipher }); - - // Set the client to only support a single TLSv1.3 cipher - final String clientCipher = "TLS_AES_256_GCM_SHA384"; - clientEngine.setEnabledCipherSuites(new String[] { clientCipher }); - - final SSLEngine client = clientEngine; - final SSLEngine server = serverEngine; - assertThrows(SSLHandshakeException.class, new Executable() { - @Override - public void execute() throws Throwable { - handshake(param.type(), param.delegate(), client, server); - } - }); - } finally { - cleanupClientSslEngine(clientEngine); - cleanupServerSslEngine(serverEngine); - ssc.delete(); - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testMutualAuthDiffCerts(SSLEngineTestParam param) throws Exception { - File serverKeyFile = ResourcesUtil.getFile(getClass(), "test_encrypted.pem"); - File serverCrtFile = ResourcesUtil.getFile(getClass(), "test.crt"); - String serverKeyPassword = "12345"; - File clientKeyFile = ResourcesUtil.getFile(getClass(), "test2_encrypted.pem"); - File clientCrtFile = ResourcesUtil.getFile(getClass(), "test2.crt"); - String clientKeyPassword = "12345"; - mySetupMutualAuth(param, clientCrtFile, serverKeyFile, serverCrtFile, serverKeyPassword, - serverCrtFile, clientKeyFile, clientCrtFile, clientKeyPassword); - runTest(null); - assertTrue(serverLatch.await(2, TimeUnit.SECONDS)); - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testMutualAuthDiffCertsServerFailure(SSLEngineTestParam param) throws Exception { - File serverKeyFile = ResourcesUtil.getFile(getClass(), "test_encrypted.pem"); - File serverCrtFile = ResourcesUtil.getFile(getClass(), "test.crt"); - String serverKeyPassword = "12345"; - 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(param, serverCrtFile, serverKeyFile, serverCrtFile, serverKeyPassword, - serverCrtFile, clientKeyFile, clientCrtFile, clientKeyPassword); - assertTrue(serverLatch.await(10, TimeUnit.SECONDS)); - assertTrue(serverException instanceof SSLHandshakeException); - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testMutualAuthDiffCertsClientFailure(SSLEngineTestParam param) throws Exception { - File serverKeyFile = ResourcesUtil.getFile(getClass(), "test_unencrypted.pem"); - File serverCrtFile = ResourcesUtil.getFile(getClass(), "test.crt"); - String serverKeyPassword = null; - 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(param, clientCrtFile, serverKeyFile, serverCrtFile, serverKeyPassword, - clientCrtFile, clientKeyFile, clientCrtFile, clientKeyPassword); - assertTrue(clientLatch.await(10, TimeUnit.SECONDS)); - assertTrue(clientException instanceof SSLHandshakeException); - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testMutualAuthInvalidIntermediateCASucceedWithOptionalClientAuth(SSLEngineTestParam param) - throws Exception { - testMutualAuthInvalidClientCertSucceed(param, ClientAuth.NONE); - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testMutualAuthInvalidIntermediateCAFailWithOptionalClientAuth(SSLEngineTestParam param) - throws Exception { - testMutualAuthClientCertFail(param, ClientAuth.OPTIONAL); - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testMutualAuthInvalidIntermediateCAFailWithRequiredClientAuth(SSLEngineTestParam param) - throws Exception { - testMutualAuthClientCertFail(param, ClientAuth.REQUIRE); - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testMutualAuthValidClientCertChainTooLongFailOptionalClientAuth(SSLEngineTestParam param) - throws Exception { - testMutualAuthClientCertFail(param, ClientAuth.OPTIONAL, "mutual_auth_client.p12", true); - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testMutualAuthValidClientCertChainTooLongFailRequireClientAuth(SSLEngineTestParam param) - throws Exception { - testMutualAuthClientCertFail(param, ClientAuth.REQUIRE, "mutual_auth_client.p12", true); - } - - private void testMutualAuthInvalidClientCertSucceed(SSLEngineTestParam param, ClientAuth auth) throws Exception { - char[] password = "example".toCharArray(); - final KeyStore serverKeyStore = KeyStore.getInstance("PKCS12"); - serverKeyStore.load(getClass().getResourceAsStream("mutual_auth_server.p12"), password); - final KeyStore clientKeyStore = KeyStore.getInstance("PKCS12"); - clientKeyStore.load(getClass().getResourceAsStream("mutual_auth_invalid_client.p12"), password); - final KeyManagerFactory serverKeyManagerFactory = - KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); - serverKeyManagerFactory.init(serverKeyStore, password); - final KeyManagerFactory clientKeyManagerFactory = - KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); - clientKeyManagerFactory.init(clientKeyStore, password); - File commonCertChain = ResourcesUtil.getFile(getClass(), "mutual_auth_ca.pem"); - - mySetupMutualAuth(param, serverKeyManagerFactory, commonCertChain, clientKeyManagerFactory, commonCertChain, - auth, false, false); - assertTrue(clientLatch.await(10, TimeUnit.SECONDS)); - rethrowIfNotNull(clientException); - assertTrue(serverLatch.await(5, TimeUnit.SECONDS)); - rethrowIfNotNull(serverException); - } - - private void testMutualAuthClientCertFail(SSLEngineTestParam param, ClientAuth auth) throws Exception { - testMutualAuthClientCertFail(param, auth, "mutual_auth_invalid_client.p12", false); - } - - private void testMutualAuthClientCertFail(SSLEngineTestParam param, ClientAuth auth, String clientCert, - boolean serverInitEngine) - throws Exception { - char[] password = "example".toCharArray(); - final KeyStore serverKeyStore = KeyStore.getInstance("PKCS12"); - serverKeyStore.load(getClass().getResourceAsStream("mutual_auth_server.p12"), password); - final KeyStore clientKeyStore = KeyStore.getInstance("PKCS12"); - clientKeyStore.load(getClass().getResourceAsStream(clientCert), password); - final KeyManagerFactory serverKeyManagerFactory = - KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); - serverKeyManagerFactory.init(serverKeyStore, password); - final KeyManagerFactory clientKeyManagerFactory = - KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); - clientKeyManagerFactory.init(clientKeyStore, password); - File commonCertChain = ResourcesUtil.getFile(getClass(), "mutual_auth_ca.pem"); - - mySetupMutualAuth(param, serverKeyManagerFactory, commonCertChain, clientKeyManagerFactory, commonCertChain, - auth, true, serverInitEngine); - assertTrue(clientLatch.await(10, TimeUnit.SECONDS)); - assertTrue(mySetupMutualAuthServerIsValidClientException(clientException), - "unexpected exception: " + clientException); - assertTrue(serverLatch.await(5, TimeUnit.SECONDS)); - assertTrue(mySetupMutualAuthServerIsValidServerException(serverException), - "unexpected exception: " + serverException); - } - - protected static boolean causedBySSLException(Throwable cause) { - Throwable next = cause; - do { - if (next instanceof SSLException) { - return true; - } - next = next.getCause(); - } while (next != null); - return false; - } - - protected boolean mySetupMutualAuthServerIsValidServerException(Throwable cause) { - return mySetupMutualAuthServerIsValidException(cause); - } - - protected boolean mySetupMutualAuthServerIsValidClientException(Throwable cause) { - return mySetupMutualAuthServerIsValidException(cause); - } - - protected boolean mySetupMutualAuthServerIsValidException(Throwable cause) { - // 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) { - } - - protected void mySetupMutualAuth(final SSLEngineTestParam param, KeyManagerFactory serverKMF, - final File serverTrustManager, - KeyManagerFactory clientKMF, File clientTrustManager, - ClientAuth clientAuth, final boolean failureExpected, - final boolean serverInitEngine) - throws Exception { - serverSslCtx = - wrapContext(param, SslContextBuilder.forServer(serverKMF) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .sslProvider(sslServerProvider()) - .sslContextProvider(serverSslContextProvider()) - .trustManager(serverTrustManager) - .clientAuth(clientAuth) - .ciphers(null, IdentityCipherSuiteFilter.INSTANCE) - .sessionCacheSize(0) - .sessionTimeout(0).build()); - - clientSslCtx = - wrapContext(param, SslContextBuilder.forClient() - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .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(); - - sb.group(new MultithreadEventLoopGroup(NioHandler.newFactory()), - new MultithreadEventLoopGroup(NioHandler.newFactory())); - sb.channel(NioServerSocketChannel.class); - sb.childHandler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - ch.config().setAllocator(new TestByteBufAllocator(ch.config().getAllocator(), param.type())); - - ChannelPipeline p = ch.pipeline(); - SslHandler handler = !param.delegate ? serverSslCtx.newHandler(ch.alloc()) : - serverSslCtx.newHandler(ch.alloc(), delegatingExecutor); - if (serverInitEngine) { - mySetupMutualAuthServerInitSslHandler(handler); - } - p.addLast(handler); - p.addLast(new MessageDelegatorChannelHandler(serverReceiver, serverLatch)); - p.addLast(new ChannelHandler() { - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (evt == SslHandshakeCompletionEvent.SUCCESS) { - if (failureExpected) { - serverException = new IllegalStateException("handshake complete. expected failure"); - } - serverLatch.countDown(); - } else if (evt instanceof SslHandshakeCompletionEvent) { - serverException = ((SslHandshakeCompletionEvent) evt).cause(); - serverLatch.countDown(); - } - ctx.fireUserEventTriggered(evt); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - if (cause.getCause() instanceof SSLHandshakeException) { - serverException = cause.getCause(); - serverLatch.countDown(); - } else { - serverException = cause; - ctx.fireExceptionCaught(cause); - } - } - }); - serverConnectedChannel = ch; - } - }); - - cb.group(new MultithreadEventLoopGroup(NioHandler.newFactory())); - cb.channel(NioSocketChannel.class); - cb.handler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - ch.config().setAllocator(new TestByteBufAllocator(ch.config().getAllocator(), param.type)); - ChannelPipeline p = ch.pipeline(); - - SslHandler handler = !param.delegate ? clientSslCtx.newHandler(ch.alloc()) : - clientSslCtx.newHandler(ch.alloc(), delegatingExecutor); - p.addLast(handler); - p.addLast(new MessageDelegatorChannelHandler(clientReceiver, clientLatch)); - p.addLast(new ChannelHandler() { - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (evt == SslHandshakeCompletionEvent.SUCCESS) { - // 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(); - } - } else if (evt instanceof SslHandshakeCompletionEvent) { - clientException = ((SslHandshakeCompletionEvent) evt).cause(); - clientLatch.countDown(); - } - ctx.fireUserEventTriggered(evt); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - if (cause.getCause() instanceof SSLException) { - clientException = cause.getCause(); - clientLatch.countDown(); - } else { - ctx.fireExceptionCaught(cause); - } - } - }); - } - }); - - serverChannel = sb.bind(new InetSocketAddress(0)).get(); - int port = ((InetSocketAddress) serverChannel.localAddress()).getPort(); - - Future ccf = cb.connect(new InetSocketAddress(NetUtil.LOCALHOST, port)); - assertTrue(ccf.awaitUninterruptibly().isSuccess()); - clientChannel = ccf.get(); - } - - protected static void rethrowIfNotNull(Throwable error) { - if (error != null) { - throw new AssertionFailedError("Expected no error", error); - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testClientHostnameValidationSuccess(SSLEngineTestParam param) throws Exception { - mySetupClientHostnameValidation(param, ResourcesUtil.getFile(getClass(), "localhost_server.pem"), - ResourcesUtil.getFile(getClass(), "localhost_server.key"), - ResourcesUtil.getFile(getClass(), "mutual_auth_ca.pem"), - false); - assertTrue(clientLatch.await(10, TimeUnit.SECONDS)); - - rethrowIfNotNull(clientException); - assertTrue(serverLatch.await(5, TimeUnit.SECONDS)); - rethrowIfNotNull(serverException); - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testClientHostnameValidationFail(SSLEngineTestParam param) throws Exception { - Future clientWriteFuture = - mySetupClientHostnameValidation(param, ResourcesUtil.getFile(getClass(), "notlocalhost_server.pem"), - ResourcesUtil.getFile(getClass(), "notlocalhost_server.key"), - ResourcesUtil.getFile(getClass(), "mutual_auth_ca.pem"), - true); - assertTrue(clientLatch.await(10, TimeUnit.SECONDS)); - assertTrue(mySetupMutualAuthServerIsValidClientException(clientException), - "unexpected exception: " + clientException); - assertTrue(serverLatch.await(5, TimeUnit.SECONDS)); - assertTrue(mySetupMutualAuthServerIsValidServerException(serverException), - "unexpected exception: " + serverException); - - // Verify that any pending writes are failed with the cached handshake exception and not a general SSLException. - clientWriteFuture.awaitUninterruptibly(); - Throwable actualCause = clientWriteFuture.cause(); - assertSame(clientException, actualCause); - } - - private Future mySetupClientHostnameValidation(final SSLEngineTestParam param, File serverCrtFile, - File serverKeyFile, - File clientTrustCrtFile, - final boolean failureExpected) - throws Exception { - final String expectedHost = "localhost"; - serverSslCtx = wrapContext(param, SslContextBuilder.forServer(serverCrtFile, serverKeyFile, null) - .sslProvider(sslServerProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .sslContextProvider(serverSslContextProvider()) - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .ciphers(null, IdentityCipherSuiteFilter.INSTANCE) - .sessionCacheSize(0) - .sessionTimeout(0) - .build()); - - clientSslCtx = wrapContext(param, SslContextBuilder.forClient() - .sslProvider(sslClientProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .sslContextProvider(clientSslContextProvider()) - .trustManager(clientTrustCrtFile) - .ciphers(null, IdentityCipherSuiteFilter.INSTANCE) - .sessionCacheSize(0) - .sessionTimeout(0) - .build()); - - serverConnectedChannel = null; - sb = new ServerBootstrap(); - cb = new Bootstrap(); - - sb.group(new MultithreadEventLoopGroup(NioHandler.newFactory()), - new MultithreadEventLoopGroup(NioHandler.newFactory())); - sb.channel(NioServerSocketChannel.class); - sb.childHandler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - ch.config().setAllocator(new TestByteBufAllocator(ch.config().getAllocator(), param.type)); - ChannelPipeline p = ch.pipeline(); - - SslHandler handler = !param.delegate ? serverSslCtx.newHandler(ch.alloc()) : - serverSslCtx.newHandler(ch.alloc(), delegatingExecutor); - p.addLast(handler); - p.addLast(new MessageDelegatorChannelHandler(serverReceiver, serverLatch)); - p.addLast(new ChannelHandler() { - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (evt == SslHandshakeCompletionEvent.SUCCESS) { - if (failureExpected) { - serverException = new IllegalStateException("handshake complete. expected failure"); - } - serverLatch.countDown(); - } else if (evt instanceof SslHandshakeCompletionEvent) { - serverException = ((SslHandshakeCompletionEvent) evt).cause(); - serverLatch.countDown(); - } - ctx.fireUserEventTriggered(evt); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - if (cause.getCause() instanceof SSLHandshakeException) { - serverException = cause.getCause(); - serverLatch.countDown(); - } else { - serverException = cause; - ctx.fireExceptionCaught(cause); - } - } - }); - serverConnectedChannel = ch; - } - }); - - final Promise clientWritePromise = ImmediateEventExecutor.INSTANCE.newPromise(); - cb.group(new MultithreadEventLoopGroup(NioHandler.newFactory())); - cb.channel(NioSocketChannel.class); - cb.handler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - ch.config().setAllocator(new TestByteBufAllocator(ch.config().getAllocator(), param.type)); - ChannelPipeline p = ch.pipeline(); - - SslHandler sslHandler = !param.delegate ? - clientSslCtx.newHandler(ch.alloc(), expectedHost, 0) : - clientSslCtx.newHandler(ch.alloc(), expectedHost, 0, delegatingExecutor); - - SSLParameters parameters = sslHandler.engine().getSSLParameters(); - if (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); - p.addLast(new MessageDelegatorChannelHandler(clientReceiver, clientLatch)); - p.addLast(new ChannelHandler() { - @Override - public void handlerAdded(ChannelHandlerContext ctx) { - // Only write if there is a failure expected. We don't actually care about the write going - // through we just want to verify the local failure condition. This way we don't have to worry - // about verifying the payload and releasing the content on the server side. - if (failureExpected) { - ctx.write(ctx.alloc().buffer(1).writeByte(1)).cascadeTo(clientWritePromise); - } - } - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (evt == SslHandshakeCompletionEvent.SUCCESS) { - if (failureExpected) { - clientException = new IllegalStateException("handshake complete. expected failure"); - } - clientLatch.countDown(); - } else if (evt instanceof SslHandshakeCompletionEvent) { - clientException = ((SslHandshakeCompletionEvent) evt).cause(); - clientLatch.countDown(); - } - ctx.fireUserEventTriggered(evt); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - if (cause.getCause() instanceof SSLHandshakeException) { - clientException = cause.getCause(); - clientLatch.countDown(); - } else { - ctx.fireExceptionCaught(cause); - } - } - }); - } - }); - - serverChannel = sb.bind(new InetSocketAddress(expectedHost, 0)).get(); - final int port = ((InetSocketAddress) serverChannel.localAddress()).getPort(); - - Future ccf = cb.connect(new InetSocketAddress(expectedHost, port)); - assertTrue(ccf.awaitUninterruptibly().isSuccess()); - clientChannel = ccf.get(); - return clientWritePromise.asFuture(); - } - - private void mySetupMutualAuth(SSLEngineTestParam param, File keyFile, File crtFile, String keyPassword) - throws Exception { - mySetupMutualAuth(param, crtFile, keyFile, crtFile, keyPassword, crtFile, keyFile, crtFile, keyPassword); - } - - private static void verifySSLSessionForMutualAuth( - SSLEngineTestParam param, 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(param.combo().cipher, session.getCipherSuite()); - assertEquals(param.combo().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()); - - try { - assertEquals(1, session.getPeerCertificateChain().length); - assertArrayEquals(certBytes, session.getPeerCertificateChain()[0].getEncoded()); - } catch (UnsupportedOperationException e) { - // See https://bugs.openjdk.java.net/browse/JDK-8241039 - assertTrue(PlatformDependent.javaVersion() >= 15); - } - - assertEquals(1, session.getLocalCertificates().length); - assertArrayEquals(certBytes, session.getLocalCertificates()[0].getEncoded()); - } finally { - if (in != null) { - in.close(); - } - } - } - - private void mySetupMutualAuth(final SSLEngineTestParam param, - File servertTrustCrtFile, File serverKeyFile, final File serverCrtFile, String serverKeyPassword, - File clientTrustCrtFile, File clientKeyFile, final File clientCrtFile, String clientKeyPassword) - throws Exception { - serverSslCtx = - wrapContext(param, SslContextBuilder.forServer(serverCrtFile, serverKeyFile, serverKeyPassword) - .sslProvider(sslServerProvider()) - .sslContextProvider(serverSslContextProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .trustManager(servertTrustCrtFile) - .ciphers(null, IdentityCipherSuiteFilter.INSTANCE) - .sessionCacheSize(0) - .sessionTimeout(0).build()); - clientSslCtx = - wrapContext(param, SslContextBuilder.forClient() - .sslProvider(sslClientProvider()) - .sslContextProvider(clientSslContextProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .trustManager(clientTrustCrtFile) - .keyManager(clientCrtFile, clientKeyFile, clientKeyPassword) - .ciphers(null, IdentityCipherSuiteFilter.INSTANCE) - .sessionCacheSize(0) - .sessionTimeout(0).build()); - - serverConnectedChannel = null; - sb = new ServerBootstrap(); - cb = new Bootstrap(); - - sb.group(new MultithreadEventLoopGroup(NioHandler.newFactory()), - new MultithreadEventLoopGroup(NioHandler.newFactory())); - sb.channel(NioServerSocketChannel.class); - sb.childHandler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) { - ch.config().setAllocator(new TestByteBufAllocator(ch.config().getAllocator(), param.type)); - - ChannelPipeline p = ch.pipeline(); - final SSLEngine engine = wrapEngine(serverSslCtx.newEngine(ch.alloc())); - engine.setUseClientMode(false); - engine.setNeedClientAuth(true); - - p.addLast(new SslHandler(engine)); - p.addLast(new MessageDelegatorChannelHandler(serverReceiver, serverLatch)); - p.addLast(new ChannelHandler() { - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - if (cause.getCause() instanceof SSLHandshakeException) { - serverException = cause.getCause(); - serverLatch.countDown(); - } else { - serverException = cause; - ctx.fireExceptionCaught(cause); - } - } - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (evt == SslHandshakeCompletionEvent.SUCCESS) { - try { - verifySSLSessionForMutualAuth( - param, engine.getSession(), serverCrtFile, PRINCIPAL_NAME); - } catch (Throwable cause) { - serverException = cause; - } - } - } - }); - serverConnectedChannel = ch; - } - }); - - cb.group(new MultithreadEventLoopGroup(NioHandler.newFactory())); - cb.channel(NioSocketChannel.class); - cb.handler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - ch.config().setAllocator(new TestByteBufAllocator(ch.config().getAllocator(), param.type)); - - final SslHandler handler = !param.delegate ? - clientSslCtx.newHandler(ch.alloc()) : - clientSslCtx.newHandler(ch.alloc(), delegatingExecutor); - - handler.engine().setNeedClientAuth(true); - ChannelPipeline p = ch.pipeline(); - p.addLast(handler); - p.addLast(new MessageDelegatorChannelHandler(clientReceiver, clientLatch)); - p.addLast(new ChannelHandler() { - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (evt == SslHandshakeCompletionEvent.SUCCESS) { - try { - verifySSLSessionForMutualAuth( - param, 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) { - clientException = cause.getCause(); - clientLatch.countDown(); - } else { - ctx.fireExceptionCaught(cause); - } - } - }); - } - }); - - serverChannel = sb.bind(new InetSocketAddress(0)).get(); - int port = ((InetSocketAddress) serverChannel.localAddress()).getPort(); - - Future ccf = cb.connect(new InetSocketAddress(NetUtil.LOCALHOST, port)); - assertTrue(ccf.awaitUninterruptibly().isSuccess()); - clientChannel = ccf.get(); - } - - protected void runTest(String expectedApplicationProtocol) throws Exception { - final ByteBuf clientMessage = Unpooled.copiedBuffer("I am a client".getBytes()); - final ByteBuf serverMessage = Unpooled.copiedBuffer("I am a server".getBytes()); - try { - writeAndVerifyReceived(clientMessage.retain(), clientChannel, serverLatch, serverReceiver); - writeAndVerifyReceived(serverMessage.retain(), serverConnectedChannel, clientLatch, clientReceiver); - verifyApplicationLevelProtocol(clientChannel, expectedApplicationProtocol); - verifyApplicationLevelProtocol(serverConnectedChannel, expectedApplicationProtocol); - } finally { - clientMessage.release(); - serverMessage.release(); - } - } - - private static void verifyApplicationLevelProtocol(Channel channel, String expectedApplicationProtocol) { - SslHandler handler = channel.pipeline().get(SslHandler.class); - assertNotNull(handler); - String appProto = handler.applicationProtocol(); - assertEquals(expectedApplicationProtocol, appProto); - - SSLEngine engine = handler.engine(); - if (engine instanceof JdkAlpnSslEngine) { - // Also verify the Java9 exposed method. - JdkAlpnSslEngine java9SslEngine = (JdkAlpnSslEngine) engine; - assertEquals(expectedApplicationProtocol == null ? StringUtil.EMPTY_STRING : expectedApplicationProtocol, - java9SslEngine.getApplicationProtocol()); - } - } - - private static void writeAndVerifyReceived(ByteBuf message, Channel sendChannel, CountDownLatch receiverLatch, - MessageReceiver receiver) throws Exception { - List dataCapture = null; - try { - assertTrue(sendChannel.writeAndFlush(message).await(10, TimeUnit.SECONDS)); - receiverLatch.await(5, TimeUnit.SECONDS); - message.readerIndex(0); - ArgumentCaptor captor = ArgumentCaptor.forClass(ByteBuf.class); - verify(receiver).messageReceived(captor.capture()); - dataCapture = captor.getAllValues(); - assertEquals(message, dataCapture.get(0)); - } finally { - if (dataCapture != null) { - for (ByteBuf data : dataCapture) { - data.release(); - } - } - } - } - - @Test - public void testGetCreationTime() throws Exception { - clientSslCtx = wrapContext(null, SslContextBuilder.forClient() - .sslProvider(sslClientProvider()) - .sslContextProvider(clientSslContextProvider()).build()); - SSLEngine engine = null; - try { - engine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - assertTrue(engine.getSession().getCreationTime() <= System.currentTimeMillis()); - } finally { - cleanupClientSslEngine(engine); - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testSessionInvalidate(SSLEngineTestParam param) throws Exception { - clientSslCtx = wrapContext(param, SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslClientProvider()) - .sslContextProvider(clientSslContextProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SelfSignedCertificate ssc = new SelfSignedCertificate(); - serverSslCtx = wrapContext(param, SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(sslServerProvider()) - .sslContextProvider(serverSslContextProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SSLEngine clientEngine = null; - SSLEngine serverEngine = null; - try { - clientEngine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - serverEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - handshake(param.type(), param.delegate(), clientEngine, serverEngine); - - SSLSession session = serverEngine.getSession(); - assertTrue(session.isValid()); - session.invalidate(); - assertFalse(session.isValid()); - } finally { - cleanupClientSslEngine(clientEngine); - cleanupServerSslEngine(serverEngine); - ssc.delete(); - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testSSLSessionId(SSLEngineTestParam param) throws Exception { - clientSslCtx = wrapContext(param, SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslClientProvider()) - // This test only works for non TLSv1.3 for now - .protocols(param.protocols()) - .sslContextProvider(clientSslContextProvider()) - .build()); - SelfSignedCertificate ssc = new SelfSignedCertificate(); - serverSslCtx = wrapContext(param, SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(sslServerProvider()) - // This test only works for non TLSv1.3 for now - .protocols(param.protocols()) - .sslContextProvider(serverSslContextProvider()) - .build()); - SSLEngine clientEngine = null; - SSLEngine serverEngine = null; - try { - 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); - assertEquals(0, serverEngine.getSession().getId().length); - - handshake(param.type(), param.delegate(), clientEngine, serverEngine); - - if (param.protocolCipherCombo == ProtocolCipherCombo.TLSV13) { - // Allocate something which is big enough for sure - ByteBuffer packetBuffer = allocateBuffer(param.type(), 32 * 1024); - ByteBuffer appBuffer = allocateBuffer(param.type(), 32 * 1024); - - appBuffer.clear().position(4).flip(); - packetBuffer.clear(); - - do { - SSLEngineResult result; - - do { - result = serverEngine.wrap(appBuffer, packetBuffer); - } while (appBuffer.hasRemaining() || result.bytesProduced() > 0); - - appBuffer.clear(); - packetBuffer.flip(); - do { - result = clientEngine.unwrap(packetBuffer, appBuffer); - } while (packetBuffer.hasRemaining() || result.bytesProduced() > 0); - - packetBuffer.clear(); - appBuffer.clear().position(4).flip(); - - do { - result = clientEngine.wrap(appBuffer, packetBuffer); - } while (appBuffer.hasRemaining() || result.bytesProduced() > 0); - - appBuffer.clear(); - packetBuffer.flip(); - - do { - result = serverEngine.unwrap(packetBuffer, appBuffer); - } while (packetBuffer.hasRemaining() || result.bytesProduced() > 0); - - packetBuffer.clear(); - appBuffer.clear().position(4).flip(); - } while (clientEngine.getSession().getId().length == 0); - - // With TLS1.3 we should see pseudo IDs and so these should never match. - assertFalse(Arrays.equals(clientEngine.getSession().getId(), serverEngine.getSession().getId())); - } else { - // After the handshake the id should have length > 0 - assertNotEquals(0, clientEngine.getSession().getId().length); - assertNotEquals(0, serverEngine.getSession().getId().length); - - assertArrayEquals(clientEngine.getSession().getId(), serverEngine.getSession().getId()); - } - } finally { - cleanupClientSslEngine(clientEngine); - cleanupServerSslEngine(serverEngine); - ssc.delete(); - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Timeout(30) - public void clientInitiatedRenegotiationWithFatalAlertDoesNotInfiniteLoopServer(final SSLEngineTestParam param) - throws Exception { - assumeTrue(PlatformDependent.javaVersion() >= 11); - final SelfSignedCertificate ssc = new SelfSignedCertificate(); - serverSslCtx = wrapContext(param, SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(sslServerProvider()) - .sslContextProvider(serverSslContextProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - sb = new ServerBootstrap() - .group(new MultithreadEventLoopGroup(1, NioHandler.newFactory())) - .channel(NioServerSocketChannel.class) - .childHandler(new ChannelInitializer() { - @Override - public void initChannel(SocketChannel ch) { - ch.config().setAllocator(new TestByteBufAllocator(ch.config().getAllocator(), param.type)); - - ChannelPipeline p = ch.pipeline(); - - SslHandler handler = !param.delegate ? - serverSslCtx.newHandler(ch.alloc()) : - serverSslCtx.newHandler(ch.alloc(), delegatingExecutor); - - p.addLast(handler); - p.addLast(new ChannelHandler() { - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { - if (evt instanceof SslHandshakeCompletionEvent && - ((SslHandshakeCompletionEvent) evt).isSuccess()) { - // This data will be sent to the client before any of the re-negotiation data can be - // sent. The client will read this, detect that it is not the response to - // renegotiation which was expected, and respond with a fatal alert. - ctx.writeAndFlush(ctx.alloc().buffer(1).writeByte(100)); - } - ctx.fireUserEventTriggered(evt); - } - - @Override - public void channelRead(final ChannelHandlerContext ctx, Object msg) { - ReferenceCountUtil.release(msg); - // The server then attempts to trigger a flush operation once the application data is - // received from the client. The flush will encrypt all data and should not result in - // deadlock. - ctx.channel().executor().schedule(() -> { - ctx.writeAndFlush(ctx.alloc().buffer(1).writeByte(101)); - }, 500, TimeUnit.MILLISECONDS); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) { - serverLatch.countDown(); - } - }); - serverConnectedChannel = ch; - } - }); - - serverChannel = sb.bind(new InetSocketAddress(0)).get(); - - clientSslCtx = wrapContext(param, SslContextBuilder.forClient() - // OpenSslEngine doesn't support renegotiation on client side - .sslProvider(SslProvider.JDK) - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - - cb = new Bootstrap(); - cb.group(new MultithreadEventLoopGroup(1, NioHandler.newFactory())) - .channel(NioSocketChannel.class) - .handler(new ChannelInitializer() { - @Override - public void initChannel(SocketChannel ch) { - ch.config().setAllocator(new TestByteBufAllocator(ch.config().getAllocator(), param.type())); - - ChannelPipeline p = ch.pipeline(); - - SslHandler sslHandler = !param.delegate ? - 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); - p.addLast(sslHandler); - p.addLast(new ChannelHandler() { - private int handshakeCount; - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { - // OpenSSL SSLEngine sends a fatal alert for the renegotiation handshake because the - // user data read as part of the handshake. The client receives this fatal alert and is - // expected to shutdown the connection. The "invalid data" during the renegotiation - // handshake is also delivered to channelRead(..) on the server. - // JDK SSLEngine completes the renegotiation handshake and delivers the "invalid data" - // is also delivered to channelRead(..) on the server. JDK SSLEngine does not send a - // fatal error and so for testing purposes we close the connection after we have - // completed the first renegotiation handshake (which is the second handshake). - if (evt instanceof SslHandshakeCompletionEvent && ++handshakeCount == 2) { - ctx.close(); - return; - } - ctx.fireUserEventTriggered(evt); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - ReferenceCountUtil.release(msg); - // Simulate a request that the server's application logic will think is invalid. - ctx.writeAndFlush(ctx.alloc().buffer(1).writeByte(102)); - ctx.pipeline().get(SslHandler.class).renegotiate(); - } - }); - } - }); - - Future ccf = cb.connect(serverChannel.localAddress()); - assertTrue(ccf.syncUninterruptibly().isSuccess()); - clientChannel = ccf.get(); - - serverLatch.await(); - ssc.delete(); - } - - protected void testEnablingAnAlreadyDisabledSslProtocol(SSLEngineTestParam param, - String[] protocols1, String[] protocols2) throws Exception { - SSLEngine sslEngine = null; - try { - File serverKeyFile = ResourcesUtil.getFile(getClass(), "test_unencrypted.pem"); - File serverCrtFile = ResourcesUtil.getFile(getClass(), "test.crt"); - serverSslCtx = wrapContext(param, SslContextBuilder.forServer(serverCrtFile, serverKeyFile) - .sslProvider(sslServerProvider()) - .sslContextProvider(serverSslContextProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - - sslEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - - // Disable all protocols - sslEngine.setEnabledProtocols(EmptyArrays.EMPTY_STRINGS); - - // The only protocol that should be enabled is SSLv2Hello - String[] enabledProtocols = sslEngine.getEnabledProtocols(); - assertArrayEquals(protocols1, enabledProtocols); - - // Enable a protocol that is currently disabled - sslEngine.setEnabledProtocols(new String[]{ SslProtocols.TLS_v1_2 }); - - // The protocol that was just enabled should be returned - enabledProtocols = sslEngine.getEnabledProtocols(); - assertEquals(protocols2.length, enabledProtocols.length); - assertArrayEquals(protocols2, enabledProtocols); - } finally { - if (sslEngine != null) { - sslEngine.closeInbound(); - sslEngine.closeOutbound(); - cleanupServerSslEngine(sslEngine); - } - } - } - - protected void handshake(BufferType type, boolean delegate, SSLEngine clientEngine, SSLEngine serverEngine) - throws Exception { - ByteBuffer cTOs = allocateBuffer(type, clientEngine.getSession().getPacketBufferSize()); - ByteBuffer sTOc = allocateBuffer(type, serverEngine.getSession().getPacketBufferSize()); - - ByteBuffer serverAppReadBuffer = allocateBuffer(type, serverEngine.getSession().getApplicationBufferSize()); - ByteBuffer clientAppReadBuffer = allocateBuffer(type, clientEngine.getSession().getApplicationBufferSize()); - - clientEngine.beginHandshake(); - serverEngine.beginHandshake(); - - ByteBuffer empty = allocateBuffer(type, 0); - - SSLEngineResult clientResult; - SSLEngineResult serverResult; - - boolean clientHandshakeFinished = false; - boolean serverHandshakeFinished = false; - boolean cTOsHasRemaining; - boolean sTOcHasRemaining; - - do { - int cTOsPos = cTOs.position(); - int sTOcPos = sTOc.position(); - - if (!clientHandshakeFinished) { - clientResult = clientEngine.wrap(empty, cTOs); - runDelegatedTasks(delegate, clientResult, clientEngine); - assertEquals(empty.remaining(), clientResult.bytesConsumed()); - assertEquals(cTOs.position() - cTOsPos, clientResult.bytesProduced()); - - if (isHandshakeFinished(clientResult)) { - clientHandshakeFinished = true; - } - } - - if (!serverHandshakeFinished) { - serverResult = serverEngine.wrap(empty, sTOc); - runDelegatedTasks(delegate, serverResult, serverEngine); - assertEquals(empty.remaining(), serverResult.bytesConsumed()); - assertEquals(sTOc.position() - sTOcPos, serverResult.bytesProduced()); - - if (isHandshakeFinished(serverResult)) { - serverHandshakeFinished = true; - } - } - - cTOs.flip(); - sTOc.flip(); - - cTOsPos = cTOs.position(); - sTOcPos = sTOc.position(); - - 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. - SslProtocols.TLS_v1_3.equals(clientEngine.getSession().getProtocol())) { - int clientAppReadBufferPos = clientAppReadBuffer.position(); - clientResult = clientEngine.unwrap(sTOc, clientAppReadBuffer); - - runDelegatedTasks(delegate, clientResult, clientEngine); - assertEquals(sTOc.position() - sTOcPos, clientResult.bytesConsumed()); - assertEquals(clientAppReadBuffer.position() - clientAppReadBufferPos, clientResult.bytesProduced()); - - if (isHandshakeFinished(clientResult)) { - clientHandshakeFinished = true; - } - } else { - assertEquals(0, sTOc.remaining()); - } - - if (!serverHandshakeFinished) { - int serverAppReadBufferPos = serverAppReadBuffer.position(); - serverResult = serverEngine.unwrap(cTOs, serverAppReadBuffer); - runDelegatedTasks(delegate, serverResult, serverEngine); - assertEquals(cTOs.position() - cTOsPos, serverResult.bytesConsumed()); - assertEquals(serverAppReadBuffer.position() - serverAppReadBufferPos, serverResult.bytesProduced()); - - if (isHandshakeFinished(serverResult)) { - serverHandshakeFinished = true; - } - } else { - assertFalse(cTOs.hasRemaining()); - } - - cTOsHasRemaining = cTOs.hasRemaining(); - sTOcHasRemaining = sTOc.hasRemaining(); - - sTOc.compact(); - cTOs.compact(); - } while (!clientHandshakeFinished || !serverHandshakeFinished || - // We need to ensure we feed all the data to the engine to not end up with a corrupted state. - // This is especially important with TLS1.3 which may produce sessions after the "main handshake" is - // done - cTOsHasRemaining || sTOcHasRemaining); - } - - private static boolean isHandshakeFinished(SSLEngineResult result) { - return result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.FINISHED; - } - - private void runDelegatedTasks(boolean delegate, SSLEngineResult result, SSLEngine engine) throws Exception { - if (result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK) { - for (;;) { - Runnable task = engine.getDelegatedTask(); - if (task == null) { - break; - } - if (!delegate) { - task.run(); - } else { - delegatingExecutor.submit(task).get(); - } - } - } - } - - protected abstract SslProvider sslClientProvider(); - - protected abstract SslProvider sslServerProvider(); - - protected Provider clientSslContextProvider() { - return null; - } - protected Provider serverSslContextProvider() { - return null; - } - - /** - * Called from the test cleanup code and can be used to release the {@code ctx} if it must be done manually. - */ - protected void cleanupClientSslContext(SslContext ctx) { - } - - /** - * Called from the test cleanup code and can be used to release the {@code ctx} if it must be done manually. - */ - protected void cleanupServerSslContext(SslContext ctx) { - } - - /** - * Called when ever an SSLEngine is not wrapped by a {@link SslHandler} and inserted into a pipeline. - */ - protected void cleanupClientSslEngine(SSLEngine engine) { - } - - /** - * Called when ever an SSLEngine is not wrapped by a {@link SslHandler} and inserted into a pipeline. - */ - protected void cleanupServerSslEngine(SSLEngine engine) { - } - - protected void setupHandlers(SSLEngineTestParam param, ApplicationProtocolConfig apn) - throws Exception { - setupHandlers(param, apn, apn); - } - - protected void setupHandlers(SSLEngineTestParam param, - ApplicationProtocolConfig serverApn, ApplicationProtocolConfig clientApn) - throws Exception { - SelfSignedCertificate ssc = new SelfSignedCertificate(); - - try { - 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(SslProtocols.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(SslProtocols.TLS_v1_2); - } - - setupHandlers(param.type(), param.delegate(), - wrapContext(param, serverCtxBuilder.build()), wrapContext(param, clientCtxBuilder.build())); - } finally { - ssc.delete(); - } - } - - protected void setupHandlers(final BufferType type, final boolean delegate, - SslContext serverCtx, SslContext clientCtx) - throws Exception { - serverSslCtx = serverCtx; - clientSslCtx = clientCtx; - - serverConnectedChannel = null; - sb = new ServerBootstrap(); - cb = new Bootstrap(); - - sb.group(new MultithreadEventLoopGroup(NioHandler.newFactory()), - new MultithreadEventLoopGroup(NioHandler.newFactory())); - sb.channel(NioServerSocketChannel.class); - sb.childHandler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - ch.config().setAllocator(new TestByteBufAllocator(ch.config().getAllocator(), type)); - - ChannelPipeline p = ch.pipeline(); - - SslHandler sslHandler = !delegate ? - serverSslCtx.newHandler(ch.alloc()) : - serverSslCtx.newHandler(ch.alloc(), delegatingExecutor); - - p.addLast(sslHandler); - p.addLast(new MessageDelegatorChannelHandler(serverReceiver, serverLatch)); - p.addLast(new ChannelHandler() { - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - if (cause.getCause() instanceof SSLHandshakeException) { - serverException = cause.getCause(); - serverLatch.countDown(); - } else { - ctx.fireExceptionCaught(cause); - } - } - }); - serverConnectedChannel = ch; - } - }); - - cb.group(new MultithreadEventLoopGroup(NioHandler.newFactory())); - cb.channel(NioSocketChannel.class); - cb.handler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - ch.config().setAllocator(new TestByteBufAllocator(ch.config().getAllocator(), type)); - - ChannelPipeline p = ch.pipeline(); - - SslHandler sslHandler = !delegate ? - clientSslCtx.newHandler(ch.alloc()) : - clientSslCtx.newHandler(ch.alloc(), delegatingExecutor); - - p.addLast(sslHandler); - p.addLast(new MessageDelegatorChannelHandler(clientReceiver, clientLatch)); - p.addLast(new ChannelHandler() { - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - if (cause.getCause() instanceof SSLHandshakeException) { - clientException = cause.getCause(); - clientLatch.countDown(); - } else { - ctx.fireExceptionCaught(cause); - } - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - clientLatch.countDown(); - } - }); - } - }); - - serverChannel = sb.bind(new InetSocketAddress(0)).get(); - - Future ccf = cb.connect(serverChannel.localAddress()); - assertTrue(ccf.syncUninterruptibly().isSuccess()); - clientChannel = ccf.get(); - } - - @MethodSource("newTestParams") - @ParameterizedTest - @Timeout(30) - public void testMutualAuthSameCertChain(final SSLEngineTestParam param) throws Exception { - SelfSignedCertificate serverCert = new SelfSignedCertificate(); - SelfSignedCertificate clientCert = new SelfSignedCertificate(); - serverSslCtx = - wrapContext(param, SslContextBuilder.forServer(serverCert.certificate(), serverCert.privateKey()) - .trustManager(clientCert.cert()) - .clientAuth(ClientAuth.REQUIRE).sslProvider(sslServerProvider()) - .sslContextProvider(serverSslContextProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()).build()); - - sb = new ServerBootstrap(); - sb.group(new MultithreadEventLoopGroup(NioHandler.newFactory()), - new MultithreadEventLoopGroup(NioHandler.newFactory())); - sb.channel(NioServerSocketChannel.class); - - final Promise promise = sb.config().group().next().newPromise(); - serverChannel = sb.childHandler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - ch.config().setAllocator(new TestByteBufAllocator(ch.config().getAllocator(), param.type())); - - SslHandler sslHandler = !param.delegate ? - serverSslCtx.newHandler(ch.alloc()) : - serverSslCtx.newHandler(ch.alloc(), delegatingExecutor); - - ch.pipeline().addFirst(sslHandler); - ch.pipeline().addLast(new ChannelHandler() { - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (evt instanceof SslHandshakeCompletionEvent) { - Throwable cause = ((SslHandshakeCompletionEvent) evt).cause(); - if (cause == null) { - SSLSession session = ((SslHandler) ctx.pipeline().first()).engine().getSession(); - Certificate[] peerCertificates = session.getPeerCertificates(); - if (peerCertificates == null) { - promise.setFailure(new NullPointerException("peerCertificates")); - return; - } - try { - X509Certificate[] peerCertificateChain = session.getPeerCertificateChain(); - if (peerCertificateChain == null) { - promise.setFailure(new NullPointerException("peerCertificateChain")); - } else if (peerCertificateChain.length + peerCertificates.length != 4) { - String excTxtFmt = "peerCertificateChain.length:%s, peerCertificates.length:%s"; - promise.setFailure(new IllegalStateException(String.format(excTxtFmt, - peerCertificateChain.length, - peerCertificates.length))); - } else { - for (int i = 0; i < peerCertificateChain.length; i++) { - if (peerCertificateChain[i] == null || peerCertificates[i] == null) { - promise.setFailure( - new IllegalStateException("Certificate in chain is null")); - return; - } - } - promise.setSuccess(null); - } - } catch (UnsupportedOperationException e) { - // See https://bugs.openjdk.java.net/browse/JDK-8241039 - assertTrue(PlatformDependent.javaVersion() >= 15); - assertEquals(2, peerCertificates.length); - for (Certificate peerCertificate : peerCertificates) { - if (peerCertificate == null) { - promise.setFailure( - new IllegalStateException("Certificate in chain is null")); - return; - } - } - promise.setSuccess(null); - } - } else { - promise.setFailure(cause); - } - } - } - }); - serverConnectedChannel = ch; - } - }).bind(new InetSocketAddress(0)).get(); - - // We create a new chain for certificates which contains 2 certificates - ByteArrayOutputStream chainStream = new ByteArrayOutputStream(); - chainStream.write(Files.readAllBytes(clientCert.certificate().toPath())); - chainStream.write(Files.readAllBytes(serverCert.certificate().toPath())); - - clientSslCtx = - wrapContext(param, SslContextBuilder.forClient().keyManager( - new ByteArrayInputStream(chainStream.toByteArray()), - new FileInputStream(clientCert.privateKey())) - .trustManager(new FileInputStream(serverCert.certificate())) - .sslProvider(sslClientProvider()) - .sslContextProvider(clientSslContextProvider()) - .protocols(param.protocols()).ciphers(param.ciphers()).build()); - cb = new Bootstrap(); - cb.group(new MultithreadEventLoopGroup(NioHandler.newFactory())); - cb.channel(NioSocketChannel.class); - clientChannel = cb.handler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - ch.config().setAllocator(new TestByteBufAllocator(ch.config().getAllocator(), param.type())); - ch.pipeline().addLast(new SslHandler(wrapEngine(clientSslCtx.newEngine(ch.alloc())))); - } - - }).connect(serverChannel.localAddress()).get(); - - promise.asFuture().syncUninterruptibly(); - - serverCert.delete(); - clientCert.delete(); - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testUnwrapBehavior(SSLEngineTestParam param) throws Exception { - SelfSignedCertificate cert = new SelfSignedCertificate(); - - clientSslCtx = wrapContext(param, SslContextBuilder - .forClient() - .trustManager(cert.cert()) - .sslProvider(sslClientProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - - serverSslCtx = wrapContext(param, SslContextBuilder - .forServer(cert.certificate(), cert.privateKey()) - .sslProvider(sslServerProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - - byte[] bytes = "Hello World".getBytes(CharsetUtil.US_ASCII); - - try { - ByteBuffer plainClientOut = allocateBuffer(param.type, client.getSession().getApplicationBufferSize()); - ByteBuffer encryptedClientToServer = allocateBuffer( - param.type, server.getSession().getPacketBufferSize() * 2); - ByteBuffer plainServerIn = allocateBuffer(param.type, server.getSession().getApplicationBufferSize()); - - handshake(param.type(), param.delegate(), client, server); - - // create two TLS frames - - // first frame - plainClientOut.put(bytes, 0, 5); - plainClientOut.flip(); - - SSLEngineResult result = client.wrap(plainClientOut, encryptedClientToServer); - assertEquals(SSLEngineResult.Status.OK, result.getStatus()); - assertEquals(5, result.bytesConsumed()); - assertTrue(result.bytesProduced() > 0); - - assertFalse(plainClientOut.hasRemaining()); - - // second frame - plainClientOut.clear(); - plainClientOut.put(bytes, 5, 6); - plainClientOut.flip(); - - result = client.wrap(plainClientOut, encryptedClientToServer); - assertEquals(SSLEngineResult.Status.OK, result.getStatus()); - assertEquals(6, result.bytesConsumed()); - assertTrue(result.bytesProduced() > 0); - - // send over to server - encryptedClientToServer.flip(); - - // try with too small output buffer first (to check BUFFER_OVERFLOW case) - int remaining = encryptedClientToServer.remaining(); - ByteBuffer small = allocateBuffer(param.type, 3); - result = server.unwrap(encryptedClientToServer, small); - assertEquals(SSLEngineResult.Status.BUFFER_OVERFLOW, result.getStatus()); - assertEquals(remaining, encryptedClientToServer.remaining()); - - // now with big enough buffer - result = server.unwrap(encryptedClientToServer, plainServerIn); - assertEquals(SSLEngineResult.Status.OK, result.getStatus()); - - assertEquals(5, result.bytesProduced()); - assertTrue(encryptedClientToServer.hasRemaining()); - - result = server.unwrap(encryptedClientToServer, plainServerIn); - assertEquals(SSLEngineResult.Status.OK, result.getStatus()); - assertEquals(6, result.bytesProduced()); - assertFalse(encryptedClientToServer.hasRemaining()); - - plainServerIn.flip(); - - assertEquals(ByteBuffer.wrap(bytes), plainServerIn); - } finally { - cleanupClientSslEngine(client); - cleanupServerSslEngine(server); - cert.delete(); - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testProtocolMatch(SSLEngineTestParam param) throws Exception { - testProtocol(param, false, new String[] {"TLSv1.2"}, new String[] {"TLSv1", "TLSv1.1", "TLSv1.2"}); - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testProtocolNoMatch(SSLEngineTestParam param) throws Exception { - testProtocol(param, true, new String[] {"TLSv1.2"}, new String[] {"TLSv1", "TLSv1.1"}); - } - - private void testProtocol(final SSLEngineTestParam param, boolean handshakeFails, - String[] clientProtocols, String[] serverProtocols) - throws Exception { - SelfSignedCertificate cert = new SelfSignedCertificate(); - - clientSslCtx = wrapContext(param, SslContextBuilder - .forClient() - .trustManager(cert.cert()) - .sslProvider(sslClientProvider()) - .protocols(clientProtocols) - .build()); - SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - - serverSslCtx = wrapContext(param, SslContextBuilder - .forServer(cert.certificate(), cert.privateKey()) - .sslProvider(sslServerProvider()) - .protocols(serverProtocols) - .build()); - SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - - try { - if (handshakeFails) { - final SSLEngine clientEngine = client; - final SSLEngine serverEngine = server; - assertThrows(SSLHandshakeException.class, new Executable() { - @Override - public void execute() throws Throwable { - handshake(param.type(), param.delegate(), clientEngine, serverEngine); - } - }); - } else { - handshake(param.type(), param.delegate(), client, server); - } - } finally { - cleanupClientSslEngine(client); - cleanupServerSslEngine(server); - cert.delete(); - } - } - - private static String[] nonContiguousProtocols(SslProvider provider) { - if (provider != null) { - // conscrypt not correctly filters out TLSv1 and TLSv1.1 which is required now by the JDK. - // https://github.com/google/conscrypt/issues/1013 - return new String[] { SslProtocols.TLS_v1_2 }; - } - return new String[] {SslProtocols.TLS_v1_2, SslProtocols.TLS_v1}; - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testHandshakeCompletesWithNonContiguousProtocolsTLSv1_2CipherOnly(SSLEngineTestParam param) - throws Exception { - SelfSignedCertificate ssc = new SelfSignedCertificate(); - // Select a mandatory cipher from the TLSv1.2 RFC https://www.ietf.org/rfc/rfc5246.txt so handshakes won't fail - // due to no shared/supported cipher. - final String sharedCipher = "TLS_RSA_WITH_AES_128_CBC_SHA"; - clientSslCtx = wrapContext(param, SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .ciphers(Collections.singletonList(sharedCipher)) - .protocols(nonContiguousProtocols(sslClientProvider())) - .sslContextProvider(clientSslContextProvider()) - .sslProvider(sslClientProvider()) - .build()); - - serverSslCtx = wrapContext(param, SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .ciphers(Collections.singletonList(sharedCipher)) - .protocols(nonContiguousProtocols(sslServerProvider())) - .sslContextProvider(serverSslContextProvider()) - .sslProvider(sslServerProvider()) - .build()); - SSLEngine clientEngine = null; - SSLEngine serverEngine = null; - try { - clientEngine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - serverEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - handshake(param.type(), param.delegate(), clientEngine, serverEngine); - } finally { - cleanupClientSslEngine(clientEngine); - cleanupServerSslEngine(serverEngine); - ssc.delete(); - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testHandshakeCompletesWithoutFilteringSupportedCipher(SSLEngineTestParam param) throws Exception { - SelfSignedCertificate ssc = new SelfSignedCertificate(); - // Select a mandatory cipher from the TLSv1.2 RFC https://www.ietf.org/rfc/rfc5246.txt so handshakes won't fail - // due to no shared/supported cipher. - final String sharedCipher = "TLS_RSA_WITH_AES_128_CBC_SHA"; - clientSslCtx = wrapContext(param, SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .ciphers(Collections.singletonList(sharedCipher), SupportedCipherSuiteFilter.INSTANCE) - .protocols(nonContiguousProtocols(sslClientProvider())) - .sslContextProvider(clientSslContextProvider()) - .sslProvider(sslClientProvider()) - .build()); - - serverSslCtx = wrapContext(param, SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .ciphers(Collections.singletonList(sharedCipher), SupportedCipherSuiteFilter.INSTANCE) - .protocols(nonContiguousProtocols(sslServerProvider())) - .sslContextProvider(serverSslContextProvider()) - .sslProvider(sslServerProvider()) - .build()); - SSLEngine clientEngine = null; - SSLEngine serverEngine = null; - try { - clientEngine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - serverEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - handshake(param.type(), param.delegate(), clientEngine, serverEngine); - } finally { - cleanupClientSslEngine(clientEngine); - cleanupServerSslEngine(serverEngine); - ssc.delete(); - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testPacketBufferSizeLimit(SSLEngineTestParam param) throws Exception { - SelfSignedCertificate cert = new SelfSignedCertificate(); - - clientSslCtx = wrapContext(param, SslContextBuilder - .forClient() - .trustManager(cert.cert()) - .sslContextProvider(clientSslContextProvider()) - .sslProvider(sslClientProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - - serverSslCtx = wrapContext(param, SslContextBuilder - .forServer(cert.certificate(), cert.privateKey()) - .sslContextProvider(serverSslContextProvider()) - .sslProvider(sslServerProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - - try { - // Allocate an buffer that is bigger then the max plain record size. - ByteBuffer plainServerOut = allocateBuffer( - param.type(), server.getSession().getApplicationBufferSize() * 2); - - handshake(param.type(), param.delegate(), client, server); - - // Fill the whole buffer and flip it. - plainServerOut.position(plainServerOut.capacity()); - plainServerOut.flip(); - - ByteBuffer encryptedServerToClient = allocateBuffer( - param.type(), server.getSession().getPacketBufferSize()); - - int encryptedServerToClientPos = encryptedServerToClient.position(); - int plainServerOutPos = plainServerOut.position(); - SSLEngineResult result = server.wrap(plainServerOut, encryptedServerToClient); - assertEquals(SSLEngineResult.Status.OK, result.getStatus()); - assertEquals(plainServerOut.position() - plainServerOutPos, result.bytesConsumed()); - assertEquals(encryptedServerToClient.position() - encryptedServerToClientPos, result.bytesProduced()); - } finally { - cleanupClientSslEngine(client); - cleanupServerSslEngine(server); - cert.delete(); - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testSSLEngineUnwrapNoSslRecord(SSLEngineTestParam param) throws Exception { - clientSslCtx = wrapContext(param, SslContextBuilder - .forClient() - .sslContextProvider(clientSslContextProvider()) - .sslProvider(sslClientProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - final SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - - try { - final ByteBuffer src = allocateBuffer(param.type(), client.getSession().getApplicationBufferSize()); - final ByteBuffer dst = allocateBuffer(param.type(), client.getSession().getPacketBufferSize()); - ByteBuffer empty = allocateBuffer(param.type(), 0); - - SSLEngineResult clientResult = client.wrap(empty, dst); - assertEquals(SSLEngineResult.Status.OK, clientResult.getStatus()); - assertEquals(SSLEngineResult.HandshakeStatus.NEED_UNWRAP, clientResult.getHandshakeStatus()); - - assertThrows(SSLException.class, new Executable() { - @Override - public void execute() throws Throwable { - client.unwrap(src, dst); - } - }); - } finally { - cleanupClientSslEngine(client); - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testBeginHandshakeAfterEngineClosed(SSLEngineTestParam param) throws SSLException { - clientSslCtx = wrapContext(param, SslContextBuilder - .forClient() - .sslContextProvider(clientSslContextProvider()) - .sslProvider(sslClientProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - - try { - client.closeInbound(); - client.closeOutbound(); - try { - client.beginHandshake(); - fail(); - } catch (SSLException expected) { - // expected - } catch (IllegalStateException e) { - if (!Conscrypt.isEngineSupported(client)) { - throw e; - } - // Workaround for conscrypt bug - // See https://github.com/google/conscrypt/issues/840 - } - } finally { - cleanupClientSslEngine(client); - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testBeginHandshakeCloseOutbound(SSLEngineTestParam param) throws Exception { - SelfSignedCertificate cert = new SelfSignedCertificate(); - - clientSslCtx = wrapContext(param, SslContextBuilder - .forClient() - .sslContextProvider(clientSslContextProvider()) - .sslProvider(sslClientProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - - serverSslCtx = wrapContext(param, SslContextBuilder - .forServer(cert.certificate(), cert.privateKey()) - .sslContextProvider(serverSslContextProvider()) - .sslProvider(sslServerProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - - try { - testBeginHandshakeCloseOutbound(param, client); - testBeginHandshakeCloseOutbound(param, server); - } finally { - cleanupClientSslEngine(client); - cleanupServerSslEngine(server); - cert.delete(); - } - } - - private void testBeginHandshakeCloseOutbound(SSLEngineTestParam param, SSLEngine engine) throws SSLException { - ByteBuffer dst = allocateBuffer(param.type(), engine.getSession().getPacketBufferSize()); - ByteBuffer empty = allocateBuffer(param.type(), 0); - engine.beginHandshake(); - engine.closeOutbound(); - - SSLEngineResult result; - for (;;) { - result = engine.wrap(empty, dst); - dst.flip(); - - assertEquals(0, result.bytesConsumed()); - assertEquals(dst.remaining(), result.bytesProduced()); - if (result.getHandshakeStatus() != SSLEngineResult.HandshakeStatus.NEED_WRAP) { - break; - } - dst.clear(); - } - assertEquals(SSLEngineResult.Status.CLOSED, result.getStatus()); - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testCloseInboundAfterBeginHandshake(SSLEngineTestParam param) throws Exception { - SelfSignedCertificate cert = new SelfSignedCertificate(); - - clientSslCtx = wrapContext(param, SslContextBuilder - .forClient() - .sslContextProvider(clientSslContextProvider()) - .sslProvider(sslClientProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - - serverSslCtx = wrapContext(param, SslContextBuilder - .forServer(cert.certificate(), cert.privateKey()) - .sslContextProvider(serverSslContextProvider()) - .sslProvider(sslServerProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - - try { - testCloseInboundAfterBeginHandshake(client); - testCloseInboundAfterBeginHandshake(server); - } finally { - cleanupClientSslEngine(client); - cleanupServerSslEngine(server); - cert.delete(); - } - } - - private static void testCloseInboundAfterBeginHandshake(SSLEngine engine) throws SSLException { - engine.beginHandshake(); - try { - engine.closeInbound(); - // Workaround for conscrypt bug - // See https://github.com/google/conscrypt/issues/839 - if (!Conscrypt.isEngineSupported(engine)) { - fail(); - } - } catch (SSLException expected) { - // expected - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testCloseNotifySequence(SSLEngineTestParam param) throws Exception { - SelfSignedCertificate cert = new SelfSignedCertificate(); - - clientSslCtx = wrapContext(param, SslContextBuilder - .forClient() - .trustManager(cert.cert()) - .sslContextProvider(clientSslContextProvider()) - .sslProvider(sslClientProvider()) - // This test only works for non TLSv1.3 for now - .protocols(SslProtocols.TLS_v1_2) - .build()); - SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - - serverSslCtx = wrapContext(param, SslContextBuilder - .forServer(cert.certificate(), cert.privateKey()) - .sslContextProvider(serverSslContextProvider()) - .sslProvider(sslServerProvider()) - // This test only works for non TLSv1.3 for now - .protocols(SslProtocols.TLS_v1_2) - .build()); - SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - - try { - ByteBuffer plainClientOut = allocateBuffer(param.type(), client.getSession().getApplicationBufferSize()); - ByteBuffer plainServerOut = allocateBuffer(param.type(), server.getSession().getApplicationBufferSize()); - - ByteBuffer encryptedClientToServer = - allocateBuffer(param.type(), client.getSession().getPacketBufferSize()); - ByteBuffer encryptedServerToClient = - allocateBuffer(param.type(), server.getSession().getPacketBufferSize()); - ByteBuffer empty = allocateBuffer(param.type(), 0); - - handshake(param.type(), param.delegate(), client, server); - - // This will produce a close_notify - client.closeOutbound(); - - // Something still pending in the outbound buffer. - assertFalse(client.isOutboundDone()); - assertFalse(client.isInboundDone()); - - // Now wrap and so drain the outbound buffer. - SSLEngineResult result = client.wrap(empty, encryptedClientToServer); - encryptedClientToServer.flip(); - - assertEquals(SSLEngineResult.Status.CLOSED, result.getStatus()); - SSLEngineResult.HandshakeStatus hs = result.getHandshakeStatus(); - // Need an UNWRAP to read the response of the close_notify - if (sslClientProvider() == SslProvider.JDK || Conscrypt.isEngineSupported(client)) { - assertTrue(hs == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING - || hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP); - } else { - assertEquals(SSLEngineResult.HandshakeStatus.NEED_UNWRAP, hs); - } - - int produced = result.bytesProduced(); - int consumed = result.bytesConsumed(); - int closeNotifyLen = produced; - - assertTrue(produced > 0); - assertEquals(0, consumed); - assertEquals(produced, encryptedClientToServer.remaining()); - // Outbound buffer should be drained now. - assertTrue(client.isOutboundDone()); - assertFalse(client.isInboundDone()); - - assertFalse(server.isOutboundDone()); - assertFalse(server.isInboundDone()); - result = server.unwrap(encryptedClientToServer, plainServerOut); - plainServerOut.flip(); - - assertEquals(SSLEngineResult.Status.CLOSED, result.getStatus()); - // Need a WRAP to respond to the close_notify - assertEquals(SSLEngineResult.HandshakeStatus.NEED_WRAP, result.getHandshakeStatus()); - - produced = result.bytesProduced(); - consumed = result.bytesConsumed(); - assertEquals(closeNotifyLen, consumed); - assertEquals(0, produced); - // Should have consumed the complete close_notify - assertEquals(0, encryptedClientToServer.remaining()); - assertEquals(0, plainServerOut.remaining()); - - assertFalse(server.isOutboundDone()); - assertTrue(server.isInboundDone()); - - 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()); - - produced = result.bytesProduced(); - consumed = result.bytesConsumed(); - assertEquals(closeNotifyLen, produced); - assertEquals(0, consumed); - - assertEquals(produced, encryptedServerToClient.remaining()); - assertTrue(server.isOutboundDone()); - assertTrue(server.isInboundDone()); - - result = client.unwrap(encryptedServerToClient, plainClientOut); - - plainClientOut.flip(); - assertEquals(SSLEngineResult.Status.CLOSED, result.getStatus()); - // UNWRAP/WRAP are not expected after this point - assertEquals(SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING, result.getHandshakeStatus()); - - produced = result.bytesProduced(); - consumed = result.bytesConsumed(); - assertEquals(closeNotifyLen, consumed); - assertEquals(0, produced); - assertEquals(0, encryptedServerToClient.remaining()); - - assertTrue(client.isOutboundDone()); - assertTrue(client.isInboundDone()); - - // Ensure that calling wrap or unwrap again will not produce an SSLException - encryptedServerToClient.clear(); - plainServerOut.clear(); - - result = server.wrap(plainServerOut, encryptedServerToClient); - assertEngineRemainsClosed(result); - - encryptedClientToServer.clear(); - plainServerOut.clear(); - - result = server.unwrap(encryptedClientToServer, plainServerOut); - assertEngineRemainsClosed(result); - - encryptedClientToServer.clear(); - plainClientOut.clear(); - - result = client.wrap(plainClientOut, encryptedClientToServer); - assertEngineRemainsClosed(result); - - encryptedServerToClient.clear(); - plainClientOut.clear(); - - result = client.unwrap(encryptedServerToClient, plainClientOut); - assertEngineRemainsClosed(result); - } finally { - cert.delete(); - cleanupClientSslEngine(client); - cleanupServerSslEngine(server); - } - } - - private static void assertEngineRemainsClosed(SSLEngineResult result) { - assertEquals(SSLEngineResult.Status.CLOSED, result.getStatus()); - assertEquals(SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING, result.getHandshakeStatus()); - assertEquals(0, result.bytesConsumed()); - assertEquals(0, result.bytesProduced()); - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testWrapAfterCloseOutbound(SSLEngineTestParam param) throws Exception { - SelfSignedCertificate cert = new SelfSignedCertificate(); - - clientSslCtx = wrapContext(param, SslContextBuilder - .forClient() - .trustManager(cert.cert()) - .sslProvider(sslClientProvider()) - .sslContextProvider(clientSslContextProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - - serverSslCtx = wrapContext(param, SslContextBuilder - .forServer(cert.certificate(), cert.privateKey()) - .sslProvider(sslServerProvider()) - .sslContextProvider(serverSslContextProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - - try { - ByteBuffer dst = allocateBuffer(param.type(), client.getSession().getPacketBufferSize()); - ByteBuffer src = allocateBuffer(param.type(), 1024); - - handshake(param.type(), param.delegate(), client, server); - - // This will produce a close_notify - client.closeOutbound(); - SSLEngineResult result = client.wrap(src, dst); - assertEquals(SSLEngineResult.Status.CLOSED, result.getStatus()); - assertEquals(0, result.bytesConsumed()); - assertTrue(result.bytesProduced() > 0); - - assertTrue(client.isOutboundDone()); - assertFalse(client.isInboundDone()); - } finally { - cert.delete(); - cleanupClientSslEngine(client); - cleanupServerSslEngine(server); - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testMultipleRecordsInOneBufferWithNonZeroPosition(SSLEngineTestParam param) throws Exception { - SelfSignedCertificate cert = new SelfSignedCertificate(); - - clientSslCtx = wrapContext(param, SslContextBuilder - .forClient() - .trustManager(cert.cert()) - .sslContextProvider(clientSslContextProvider()) - .sslProvider(sslClientProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - - serverSslCtx = wrapContext(param, SslContextBuilder - .forServer(cert.certificate(), cert.privateKey()) - .sslContextProvider(serverSslContextProvider()) - .sslProvider(sslServerProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - 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 - // unwrap call without exceed MAX_ENCRYPTED_PACKET_LENGTH. - ByteBuffer plainClientOut = allocateBuffer(param.type(), 1024); - ByteBuffer plainServerOut = allocateBuffer(param.type(), server.getSession().getApplicationBufferSize()); - - ByteBuffer encClientToServer = allocateBuffer(param.type(), client.getSession().getPacketBufferSize()); - - int positionOffset = 1; - // We need to be able to hold 2 records + positionOffset - ByteBuffer combinedEncClientToServer = allocateBuffer( - param.type(), encClientToServer.capacity() * 2 + positionOffset); - combinedEncClientToServer.position(positionOffset); - - handshake(param.type(), param.delegate(), client, server); - - plainClientOut.limit(plainClientOut.capacity()); - SSLEngineResult result = client.wrap(plainClientOut, encClientToServer); - assertEquals(plainClientOut.capacity(), result.bytesConsumed()); - assertTrue(result.bytesProduced() > 0); - - encClientToServer.flip(); - - // Copy the first record into the combined buffer - combinedEncClientToServer.put(encClientToServer); - - plainClientOut.clear(); - encClientToServer.clear(); - - result = client.wrap(plainClientOut, encClientToServer); - assertEquals(plainClientOut.capacity(), result.bytesConsumed()); - assertTrue(result.bytesProduced() > 0); - - encClientToServer.flip(); - - int encClientToServerLen = encClientToServer.remaining(); - - // Copy the first record into the combined buffer - combinedEncClientToServer.put(encClientToServer); - - encClientToServer.clear(); - - combinedEncClientToServer.flip(); - combinedEncClientToServer.position(positionOffset); - - // Ensure we have the first record and a tiny amount of the second record in the buffer - combinedEncClientToServer.limit( - combinedEncClientToServer.limit() - (encClientToServerLen - positionOffset)); - result = server.unwrap(combinedEncClientToServer, plainServerOut); - assertEquals(encClientToServerLen, result.bytesConsumed()); - assertTrue(result.bytesProduced() > 0); - } finally { - cert.delete(); - cleanupClientSslEngine(client); - cleanupServerSslEngine(server); - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testMultipleRecordsInOneBufferBiggerThenPacketBufferSize(SSLEngineTestParam param) throws Exception { - SelfSignedCertificate cert = new SelfSignedCertificate(); - - clientSslCtx = wrapContext(param, SslContextBuilder - .forClient() - .trustManager(cert.cert()) - .sslContextProvider(clientSslContextProvider()) - .sslProvider(sslClientProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - - serverSslCtx = wrapContext(param, SslContextBuilder - .forServer(cert.certificate(), cert.privateKey()) - .sslContextProvider(serverSslContextProvider()) - .sslProvider(sslServerProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - - try { - ByteBuffer plainClientOut = allocateBuffer(param.type(), 4096); - ByteBuffer plainServerOut = allocateBuffer(param.type(), server.getSession().getApplicationBufferSize()); - - ByteBuffer encClientToServer = allocateBuffer(param.type(), server.getSession().getPacketBufferSize() * 2); - - handshake(param.type(), param.delegate(), client, server); - - int srcLen = plainClientOut.remaining(); - SSLEngineResult result; - - 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); - cleanupServerSslEngine(server); - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testBufferUnderFlow(SSLEngineTestParam param) throws Exception { - SelfSignedCertificate cert = new SelfSignedCertificate(); - - clientSslCtx = wrapContext(param, SslContextBuilder - .forClient() - .trustManager(cert.cert()) - .sslContextProvider(clientSslContextProvider()) - .sslProvider(sslClientProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - - serverSslCtx = wrapContext(param, SslContextBuilder - .forServer(cert.certificate(), cert.privateKey()) - .sslContextProvider(serverSslContextProvider()) - .sslProvider(sslServerProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - - try { - ByteBuffer plainClient = allocateBuffer(param.type(), 1024); - plainClient.limit(plainClient.capacity()); - - ByteBuffer encClientToServer = allocateBuffer(param.type(), client.getSession().getPacketBufferSize()); - ByteBuffer plainServer = allocateBuffer(param.type(), server.getSession().getApplicationBufferSize()); - - handshake(param.type(), param.delegate(), client, server); - - SSLEngineResult result = client.wrap(plainClient, encClientToServer); - assertEquals(SSLEngineResult.Status.OK, result.getStatus()); - assertEquals(result.bytesConsumed(), plainClient.capacity()); - - // Flip so we can read it. - encClientToServer.flip(); - int remaining = encClientToServer.remaining(); - - // We limit the buffer so we have less then the header to read, this should result in an BUFFER_UNDERFLOW. - encClientToServer.limit(SSL_RECORD_HEADER_LENGTH - 1); - result = server.unwrap(encClientToServer, plainServer); - assertResultIsBufferUnderflow(result); - - // We limit the buffer so we can read the header but not the rest, this should result in an - // BUFFER_UNDERFLOW. - encClientToServer.limit(SSL_RECORD_HEADER_LENGTH); - result = server.unwrap(encClientToServer, plainServer); - assertResultIsBufferUnderflow(result); - - // We limit the buffer so we can read the header and partly the rest, this should result in an - // BUFFER_UNDERFLOW. - encClientToServer.limit(SSL_RECORD_HEADER_LENGTH + remaining - 1 - SSL_RECORD_HEADER_LENGTH); - result = server.unwrap(encClientToServer, plainServer); - assertResultIsBufferUnderflow(result); - - // Reset limit so we can read the full record. - encClientToServer.limit(remaining); - - result = server.unwrap(encClientToServer, plainServer); - assertEquals(SSLEngineResult.Status.OK, result.getStatus()); - assertEquals(result.bytesConsumed(), remaining); - assertTrue(result.bytesProduced() > 0); - } finally { - cert.delete(); - cleanupClientSslEngine(client); - cleanupServerSslEngine(server); - } - } - - private static void assertResultIsBufferUnderflow(SSLEngineResult result) { - assertEquals(SSLEngineResult.Status.BUFFER_UNDERFLOW, result.getStatus()); - assertEquals(0, result.bytesConsumed()); - assertEquals(0, result.bytesProduced()); - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testWrapDoesNotZeroOutSrc(SSLEngineTestParam param) throws Exception { - SelfSignedCertificate cert = new SelfSignedCertificate(); - - clientSslCtx = wrapContext(param, SslContextBuilder - .forClient() - .trustManager(cert.cert()) - .sslContextProvider(clientSslContextProvider()) - .sslProvider(sslClientProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - - serverSslCtx = wrapContext(param, SslContextBuilder - .forServer(cert.certificate(), cert.privateKey()) - .sslContextProvider(serverSslContextProvider()) - .sslProvider(sslServerProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - - try { - ByteBuffer plainServerOut = - allocateBuffer(param.type(), server.getSession().getApplicationBufferSize() / 2); - - handshake(param.type(), param.delegate(), client, server); - - // Fill the whole buffer and flip it. - for (int i = 0; i < plainServerOut.capacity(); i++) { - plainServerOut.put(i, (byte) i); - } - plainServerOut.position(plainServerOut.capacity()); - plainServerOut.flip(); - - ByteBuffer encryptedServerToClient = - allocateBuffer(param.type(), server.getSession().getPacketBufferSize()); - SSLEngineResult result = server.wrap(plainServerOut, encryptedServerToClient); - assertEquals(SSLEngineResult.Status.OK, result.getStatus()); - assertTrue(result.bytesConsumed() > 0); - - for (int i = 0; i < plainServerOut.capacity(); i++) { - assertEquals((byte) i, plainServerOut.get(i)); - } - } finally { - cleanupClientSslEngine(client); - cleanupServerSslEngine(server); - cert.delete(); - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testDisableProtocols(SSLEngineTestParam param) throws Exception { - testDisableProtocols(param, SslProtocols.SSL_v2, SslProtocols.SSL_v2); - testDisableProtocols(param, SslProtocols.SSL_v3, SslProtocols.SSL_v2, SslProtocols.SSL_v3); - testDisableProtocols(param, SslProtocols.TLS_v1, SslProtocols.SSL_v2, SslProtocols.SSL_v3, SslProtocols.TLS_v1); - testDisableProtocols(param, - SslProtocols.TLS_v1_1, SslProtocols.SSL_v2, SslProtocols.SSL_v3, - SslProtocols.TLS_v1, SslProtocols.TLS_v1_1); - testDisableProtocols(param, SslProtocols.TLS_v1_2, SslProtocols.SSL_v2, - SslProtocols.SSL_v3, SslProtocols.TLS_v1, SslProtocols.TLS_v1_1, SslProtocols.TLS_v1_2); - } - - private void testDisableProtocols(SSLEngineTestParam param, - String protocol, String... disabledProtocols) throws Exception { - SelfSignedCertificate cert = new SelfSignedCertificate(); - - SslContext ctx = wrapContext(param, SslContextBuilder - .forServer(cert.certificate(), cert.privateKey()) - .sslContextProvider(serverSslContextProvider()) - .sslProvider(sslServerProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SSLEngine server = wrapEngine(ctx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - - try { - Set supported = new HashSet<>(Arrays.asList(server.getSupportedProtocols())); - if (supported.contains(protocol)) { - server.setEnabledProtocols(server.getSupportedProtocols()); - assertEquals(supported, new HashSet<>(Arrays.asList(server.getSupportedProtocols()))); - - for (String disabled : disabledProtocols) { - supported.remove(disabled); - } - if (supported.contains(SslProtocols.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(EmptyArrays.EMPTY_STRINGS)); - assertEquals(supported, new HashSet<>(Arrays.asList(server.getEnabledProtocols()))); - server.setEnabledProtocols(server.getSupportedProtocols()); - } - } finally { - cleanupServerSslEngine(server); - cleanupClientSslContext(ctx); - cert.delete(); - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testUsingX509TrustManagerVerifiesHostname(SSLEngineTestParam param) throws Exception { - if (clientSslContextProvider() != null) { - // Not supported when using conscrypt - return; - } - SelfSignedCertificate cert = new SelfSignedCertificate(); - clientSslCtx = wrapContext(param, 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()) { - }) - .sslContextProvider(clientSslContextProvider()) - .sslProvider(sslClientProvider()) - .build()); - - SSLEngine client = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT, "netty.io", 1234)); - SSLParameters sslParameters = client.getSSLParameters(); - sslParameters.setEndpointIdentificationAlgorithm("HTTPS"); - client.setSSLParameters(sslParameters); - - serverSslCtx = wrapContext(param, SslContextBuilder - .forServer(cert.certificate(), cert.privateKey()) - .sslContextProvider(serverSslContextProvider()) - .sslProvider(sslServerProvider()) - .build()); - - SSLEngine server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - try { - handshake(param.type(), param.delegate(), client, server); - fail(); - } catch (SSLException expected) { - // expected as the hostname not matches. - } finally { - cleanupClientSslEngine(client); - cleanupServerSslEngine(server); - cert.delete(); - } - } - - @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 = wrapContext(null, SslContextBuilder.forServer(cert.key(), cert.cert()) - .sslContextProvider(serverSslContextProvider()) - .sslProvider(sslServerProvider()) - .ciphers(cipherList).build()); - server = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - fail(); - } catch (IllegalArgumentException | SSLException expected) { - // Expected when invalid cipher is used. - } finally { - cert.delete(); - cleanupServerSslEngine(server); - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testGetCiphersuite(SSLEngineTestParam param) throws Exception { - clientSslCtx = wrapContext(param, SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslClientProvider()) - .sslContextProvider(clientSslContextProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SelfSignedCertificate ssc = new SelfSignedCertificate(); - serverSslCtx = wrapContext(param, SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(sslServerProvider()) - .sslContextProvider(serverSslContextProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SSLEngine clientEngine = null; - SSLEngine serverEngine = null; - try { - clientEngine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - serverEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - handshake(param.type(), param.delegate(), clientEngine, serverEngine); - - String clientCipher = clientEngine.getSession().getCipherSuite(); - String serverCipher = serverEngine.getSession().getCipherSuite(); - assertEquals(clientCipher, serverCipher); - - assertEquals(param.protocolCipherCombo.cipher, clientCipher); - } finally { - cleanupClientSslEngine(clientEngine); - cleanupServerSslEngine(serverEngine); - ssc.delete(); - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testSessionCache(SSLEngineTestParam param) throws Exception { - clientSslCtx = wrapContext(param, SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslClientProvider()) - .sslContextProvider(clientSslContextProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SelfSignedCertificate ssc = new SelfSignedCertificate(); - serverSslCtx = wrapContext(param, SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(sslServerProvider()) - .sslContextProvider(serverSslContextProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - - try { - doHandshakeVerifyReusedAndClose(param, "a.netty.io", 9999, false); - doHandshakeVerifyReusedAndClose(param, "a.netty.io", 9999, true); - doHandshakeVerifyReusedAndClose(param, "b.netty.io", 9999, false); - invalidateSessionsAndAssert(serverSslCtx.sessionContext()); - invalidateSessionsAndAssert(clientSslCtx.sessionContext()); - } finally { - ssc.delete(); - } - } - - protected void invalidateSessionsAndAssert(SSLSessionContext context) { - Enumeration ids = context.getIds(); - while (ids.hasMoreElements()) { - byte[] id = ids.nextElement(); - SSLSession session = context.getSession(id); - if (session != null) { - session.invalidate(); - assertFalse(session.isValid()); - assertNull(context.getSession(id)); - } - } - } - - private static void assertSessionCache(SSLSessionContext sessionContext, int numSessions) { - Enumeration ids = sessionContext.getIds(); - int numIds = 0; - while (ids.hasMoreElements()) { - numIds++; - byte[] id = ids.nextElement(); - assertNotEquals(0, id.length); - SSLSession session = sessionContext.getSession(id); - assertArrayEquals(id, session.getId()); - } - assertEquals(numSessions, numIds); - } - - private void doHandshakeVerifyReusedAndClose(SSLEngineTestParam param, String host, int port, boolean reuse) - throws Exception { - SSLEngine clientEngine = null; - SSLEngine serverEngine = null; - try { - clientEngine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT, host, port)); - serverEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - handshake(param.type(), param.delegate(), clientEngine, serverEngine); - int clientSessions = currentSessionCacheSize(clientSslCtx.sessionContext()); - int serverSessions = currentSessionCacheSize(serverSslCtx.sessionContext()); - int nCSessions = clientSessions; - int nSSessions = serverSessions; - boolean clientSessionReused = false; - boolean serverSessionReused = false; - if (param.protocolCipherCombo == ProtocolCipherCombo.TLSV13) { - // Allocate something which is big enough for sure - ByteBuffer packetBuffer = allocateBuffer(param.type(), 32 * 1024); - ByteBuffer appBuffer = allocateBuffer(param.type(), 32 * 1024); - - appBuffer.clear().position(4).flip(); - packetBuffer.clear(); - - do { - SSLEngineResult result; - - do { - result = serverEngine.wrap(appBuffer, packetBuffer); - } while (appBuffer.hasRemaining() || result.bytesProduced() > 0); - - appBuffer.clear(); - packetBuffer.flip(); - do { - result = clientEngine.unwrap(packetBuffer, appBuffer); - } while (packetBuffer.hasRemaining() || result.bytesProduced() > 0); - - packetBuffer.clear(); - appBuffer.clear().position(4).flip(); - - do { - result = clientEngine.wrap(appBuffer, packetBuffer); - } while (appBuffer.hasRemaining() || result.bytesProduced() > 0); - - appBuffer.clear(); - packetBuffer.flip(); - - do { - result = serverEngine.unwrap(packetBuffer, appBuffer); - } while (packetBuffer.hasRemaining() || result.bytesProduced() > 0); - - packetBuffer.clear(); - appBuffer.clear().position(4).flip(); - nCSessions = currentSessionCacheSize(clientSslCtx.sessionContext()); - nSSessions = currentSessionCacheSize(serverSslCtx.sessionContext()); - clientSessionReused = isSessionMaybeReused(clientEngine); - serverSessionReused = isSessionMaybeReused(serverEngine); - } while ((reuse && (!clientSessionReused || !serverSessionReused)) - || (!reuse && (nCSessions < clientSessions || - // server may use multiple sessions - nSSessions < serverSessions))); - } - - assertSessionReusedForEngine(clientEngine, serverEngine, reuse); - - closeOutboundAndInbound(param.type(), clientEngine, serverEngine); - } finally { - cleanupClientSslEngine(clientEngine); - cleanupServerSslEngine(serverEngine); - } - } - - protected boolean isSessionMaybeReused(SSLEngine engine) { - return true; - } - - private static int currentSessionCacheSize(SSLSessionContext ctx) { - Enumeration ids = ctx.getIds(); - int i = 0; - while (ids.hasMoreElements()) { - i++; - ids.nextElement(); - } - return i; - } - - private void closeOutboundAndInbound( - BufferType type, SSLEngine clientEngine, SSLEngine serverEngine) throws SSLException { - assertFalse(clientEngine.isInboundDone()); - assertFalse(clientEngine.isOutboundDone()); - assertFalse(serverEngine.isInboundDone()); - assertFalse(serverEngine.isOutboundDone()); - - ByteBuffer empty = allocateBuffer(type, 0); - - // Ensure we allocate a bit more so we can fit in multiple packets. This is needed as we may call multiple - // time wrap / unwrap in a for loop before we drain the buffer we are writing in. - ByteBuffer cTOs = allocateBuffer(type, clientEngine.getSession().getPacketBufferSize() * 4); - ByteBuffer sTOs = allocateBuffer(type, serverEngine.getSession().getPacketBufferSize() * 4); - ByteBuffer cApps = allocateBuffer(type, clientEngine.getSession().getApplicationBufferSize() * 4); - ByteBuffer sApps = allocateBuffer(type, serverEngine.getSession().getApplicationBufferSize() * 4); - - clientEngine.closeOutbound(); - for (;;) { - // call wrap till we produced all data - SSLEngineResult result = clientEngine.wrap(empty, cTOs); - if (result.getStatus() == Status.CLOSED && result.bytesProduced() == 0) { - break; - } - assertTrue(cTOs.hasRemaining()); - } - cTOs.flip(); - - for (;;) { - // call unwrap till we consumed all data - SSLEngineResult result = serverEngine.unwrap(cTOs, sApps); - if (result.getStatus() == Status.CLOSED && result.bytesProduced() == 0) { - break; - } - assertTrue(sApps.hasRemaining()); - } - - serverEngine.closeOutbound(); - for (;;) { - // call wrap till we produced all data - SSLEngineResult result = serverEngine.wrap(empty, sTOs); - if (result.getStatus() == Status.CLOSED && result.bytesProduced() == 0) { - break; - } - assertTrue(sTOs.hasRemaining()); - } - sTOs.flip(); - - for (;;) { - // call unwrap till we consumed all data - SSLEngineResult result = clientEngine.unwrap(sTOs, cApps); - if (result.getStatus() == Status.CLOSED && result.bytesProduced() == 0) { - break; - } - assertTrue(cApps.hasRemaining()); - } - - // Now close the inbound as well - clientEngine.closeInbound(); - serverEngine.closeInbound(); - } - - protected void assertSessionReusedForEngine(SSLEngine clientEngine, SSLEngine serverEngine, boolean reuse) { - // NOOP - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testSessionCacheTimeout(SSLEngineTestParam param) throws Exception { - clientSslCtx = wrapContext(param, SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslClientProvider()) - .sslContextProvider(clientSslContextProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .sessionTimeout(1) - .build()); - SelfSignedCertificate ssc = new SelfSignedCertificate(); - serverSslCtx = wrapContext(param, SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(sslServerProvider()) - .sslContextProvider(serverSslContextProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .sessionTimeout(1) - .build()); - - try { - doHandshakeVerifyReusedAndClose(param, "a.netty.io", 9999, false); - - // Let's sleep for a bit more then 1 second so the cache should timeout the sessions. - Thread.sleep(1500); - - assertSessionCache(serverSslCtx.sessionContext(), 0); - assertSessionCache(clientSslCtx.sessionContext(), 0); - } finally { - ssc.delete(); - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testSessionCacheSize(SSLEngineTestParam param) throws Exception { - clientSslCtx = wrapContext(param, SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslClientProvider()) - .sslContextProvider(clientSslContextProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .sessionCacheSize(1) - .build()); - SelfSignedCertificate ssc = new SelfSignedCertificate(); - serverSslCtx = wrapContext(param, SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(sslServerProvider()) - .sslContextProvider(serverSslContextProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - - try { - doHandshakeVerifyReusedAndClose(param, "a.netty.io", 9999, false); - // As we have a cache size of 1 we should never have more then one session in the cache - doHandshakeVerifyReusedAndClose(param, "b.netty.io", 9999, false); - - // We should at least reuse b.netty.io - doHandshakeVerifyReusedAndClose(param, "b.netty.io", 9999, true); - } finally { - ssc.delete(); - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testSessionBindingEvent(SSLEngineTestParam param) throws Exception { - clientSslCtx = wrapContext(param, SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslClientProvider()) - .sslContextProvider(clientSslContextProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SelfSignedCertificate ssc = new SelfSignedCertificate(); - serverSslCtx = wrapContext(param, SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(sslServerProvider()) - .sslContextProvider(serverSslContextProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SSLEngine clientEngine = null; - SSLEngine serverEngine = null; - try { - clientEngine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - serverEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - handshake(param.type(), param.delegate(), 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()); - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testSessionAfterHandshake(SSLEngineTestParam param) throws Exception { - testSessionAfterHandshake0(param, false, false); - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testSessionAfterHandshakeMutualAuth(SSLEngineTestParam param) throws Exception { - testSessionAfterHandshake0(param, false, true); - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testSessionAfterHandshakeKeyManagerFactory(SSLEngineTestParam param) throws Exception { - testSessionAfterHandshake0(param, true, false); - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testSessionAfterHandshakeKeyManagerFactoryMutualAuth(SSLEngineTestParam param) throws Exception { - testSessionAfterHandshake0(param, true, true); - } - - private void testSessionAfterHandshake0( - SSLEngineTestParam param, boolean useKeyManagerFactory, boolean mutualAuth) throws Exception { - SelfSignedCertificate ssc = new SelfSignedCertificate(); - KeyManagerFactory kmf = useKeyManagerFactory ? - SslContext.buildKeyManagerFactory( - new java.security.cert.X509Certificate[] { ssc.cert()}, null, - ssc.key(), null, null, null) : null; - - SslContextBuilder clientContextBuilder = SslContextBuilder.forClient(); - if (mutualAuth) { - if (kmf != null) { - clientContextBuilder.keyManager(kmf); - } else { - clientContextBuilder.keyManager(ssc.key(), ssc.cert()); - } - } - clientSslCtx = wrapContext(param, clientContextBuilder - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslClientProvider()) - .sslContextProvider(clientSslContextProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - - SslContextBuilder serverContextBuilder = kmf != null ? - SslContextBuilder.forServer(kmf) : - SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()); - if (mutualAuth) { - serverContextBuilder.clientAuth(ClientAuth.REQUIRE); - } - serverSslCtx = wrapContext(param, serverContextBuilder.trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslServerProvider()) - .sslContextProvider(serverSslContextProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SSLEngine clientEngine = null; - SSLEngine serverEngine = null; - try { - clientEngine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - serverEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - - handshake(param.type(), param.delegate(), 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(param.combo().protocol, clientSession.getProtocol()); - assertEquals(param.combo().protocol, serverSession.getProtocol()); - - assertEquals(param.combo().cipher, clientSession.getCipherSuite()); - assertEquals(param.combo().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()); - - // Workaround for JDK 14 regression. - // See https://bugs.openjdk.java.net/browse/JDK-8242008 - if (PlatformDependent.javaVersion() != 14) { - 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()); - - try { - X509Certificate[] serverPeerX509Certificates = serverSession.getPeerCertificateChain(); - assertEquals(1, serverPeerX509Certificates.length); - assertArrayEquals(clientLocalCertificates[0].getEncoded(), - serverPeerX509Certificates[0].getEncoded()); - } catch (UnsupportedOperationException e) { - // See https://bugs.openjdk.java.net/browse/JDK-8241039 - assertTrue(PlatformDependent.javaVersion() >= 15); - } - - 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 - } catch (UnsupportedOperationException e) { - // See https://bugs.openjdk.java.net/browse/JDK-8241039 - assertTrue(PlatformDependent.javaVersion() >= 15); - } - - 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()); - - try { - X509Certificate[] clientPeerX509Certificates = clientSession.getPeerCertificateChain(); - assertEquals(1, clientPeerX509Certificates.length); - assertArrayEquals(serverLocalCertificates[0].getEncoded(), clientPeerX509Certificates[0].getEncoded()); - } catch (UnsupportedOperationException e) { - // See https://bugs.openjdk.java.net/browse/JDK-8241039 - assertTrue(PlatformDependent.javaVersion() >= 15); - } - Principal clientPeerPrincipal = clientSession.getPeerPrincipal(); - assertEquals(serverLocalPrincipal, clientPeerPrincipal); - } finally { - cleanupClientSslEngine(clientEngine); - cleanupServerSslEngine(serverEngine); - ssc.delete(); - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testSupportedSignatureAlgorithms(SSLEngineTestParam param) throws Exception { - final SelfSignedCertificate ssc = new SelfSignedCertificate(); - - final class TestKeyManagerFactory extends KeyManagerFactory { - TestKeyManagerFactory(final KeyManagerFactory factory) { - super(new KeyManagerFactorySpi() { - - private final KeyManager[] managers = factory.getKeyManagers(); - - @Override - protected void engineInit(KeyStore keyStore, char[] chars) { - throw new UnsupportedOperationException(); - } - - @Override - protected void engineInit(ManagerFactoryParameters managerFactoryParameters) { - throw new UnsupportedOperationException(); - } - - @Override - protected KeyManager[] engineGetKeyManagers() { - KeyManager[] array = new KeyManager[managers.length]; - - for (int i = 0 ; i < array.length; i++) { - final X509ExtendedKeyManager x509ExtendedKeyManager = (X509ExtendedKeyManager) managers[i]; - - array[i] = new X509ExtendedKeyManager() { - @Override - public String[] getClientAliases(String s, Principal[] principals) { - fail(); - return null; - } - - @Override - public String chooseClientAlias( - String[] strings, Principal[] principals, Socket socket) { - fail(); - return null; - } - - @Override - public String[] getServerAliases(String s, Principal[] principals) { - fail(); - return null; - } - - @Override - public String chooseServerAlias(String s, Principal[] principals, Socket socket) { - fail(); - return null; - } - - @Override - public String chooseEngineClientAlias( - String[] strings, Principal[] principals, SSLEngine sslEngine) { - assertNotEquals(0, ((ExtendedSSLSession) sslEngine.getHandshakeSession()) - .getPeerSupportedSignatureAlgorithms().length); - assertNotEquals(0, ((ExtendedSSLSession) sslEngine.getHandshakeSession()) - .getLocalSupportedSignatureAlgorithms().length); - return x509ExtendedKeyManager.chooseEngineClientAlias( - strings, principals, sslEngine); - } - - @Override - public String chooseEngineServerAlias( - String s, Principal[] principals, SSLEngine sslEngine) { - assertNotEquals(0, ((ExtendedSSLSession) sslEngine.getHandshakeSession()) - .getPeerSupportedSignatureAlgorithms().length); - assertNotEquals(0, ((ExtendedSSLSession) sslEngine.getHandshakeSession()) - .getLocalSupportedSignatureAlgorithms().length); - return x509ExtendedKeyManager.chooseEngineServerAlias(s, principals, sslEngine); - } - - @Override - public java.security.cert.X509Certificate[] getCertificateChain(String s) { - return x509ExtendedKeyManager.getCertificateChain(s); - } - - @Override - public PrivateKey getPrivateKey(String s) { - return x509ExtendedKeyManager.getPrivateKey(s); - } - }; - } - return array; - } - }, factory.getProvider(), factory.getAlgorithm()); - } - } - - clientSslCtx = wrapContext(param, SslContextBuilder.forClient() - .keyManager(new TestKeyManagerFactory(newKeyManagerFactory(ssc))) - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslClientProvider()) - .sslContextProvider(clientSslContextProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - - serverSslCtx = wrapContext(param, SslContextBuilder.forServer( - new TestKeyManagerFactory(newKeyManagerFactory(ssc))) - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslContextProvider(serverSslContextProvider()) - .sslProvider(sslServerProvider()) - .protocols(param.protocols()) - .ciphers(param.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(param.type(), param.delegate(), clientEngine, serverEngine); - } finally { - cleanupClientSslEngine(clientEngine); - cleanupServerSslEngine(serverEngine); - ssc.delete(); - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testHandshakeSession(SSLEngineTestParam param) throws Exception { - final SelfSignedCertificate ssc = new SelfSignedCertificate(); - - final TestTrustManagerFactory clientTmf = new TestTrustManagerFactory(ssc.cert()); - final TestTrustManagerFactory serverTmf = new TestTrustManagerFactory(ssc.cert()); - - clientSslCtx = wrapContext(param, 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(param.protocols()) - .ciphers(param.ciphers()) - .build()); - serverSslCtx = wrapContext(param, 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(param.protocols()) - .ciphers(param.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(param.type(), param.delegate(), clientEngine, serverEngine); - - assertTrue(clientTmf.isVerified()); - assertTrue(serverTmf.isVerified()); - } finally { - cleanupClientSslEngine(clientEngine); - cleanupServerSslEngine(serverEngine); - ssc.delete(); - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testSessionLocalWhenNonMutualWithKeyManager(SSLEngineTestParam param) throws Exception { - testSessionLocalWhenNonMutual(param, true); - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testSessionLocalWhenNonMutualWithoutKeyManager(SSLEngineTestParam param) throws Exception { - testSessionLocalWhenNonMutual(param, false); - } - - private void testSessionLocalWhenNonMutual(SSLEngineTestParam param, boolean useKeyManager) throws Exception { - final SelfSignedCertificate ssc = new SelfSignedCertificate(); - - SslContextBuilder clientSslCtxBuilder = SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslClientProvider()) - .sslContextProvider(clientSslContextProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()); - - if (useKeyManager) { - clientSslCtxBuilder.keyManager(newKeyManagerFactory(ssc)); - } else { - clientSslCtxBuilder.keyManager(ssc.certificate(), ssc.privateKey()); - } - clientSslCtx = wrapContext(param, clientSslCtxBuilder.build()); - - final SslContextBuilder serverSslCtxBuilder; - if (useKeyManager) { - serverSslCtxBuilder = SslContextBuilder.forServer(newKeyManagerFactory(ssc)); - } else { - serverSslCtxBuilder = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()); - } - serverSslCtxBuilder.trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslServerProvider()) - .sslContextProvider(serverSslContextProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .clientAuth(ClientAuth.NONE); - - serverSslCtx = wrapContext(param, serverSslCtxBuilder.build()); - SSLEngine clientEngine = null; - SSLEngine serverEngine = null; - try { - clientEngine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - serverEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - handshake(param.type(), param.delegate(), clientEngine, serverEngine); - - SSLSession clientSession = clientEngine.getSession(); - assertNull(clientSession.getLocalCertificates()); - assertNull(clientSession.getLocalPrincipal()); - - SSLSession serverSession = serverEngine.getSession(); - assertNotNull(serverSession.getLocalCertificates()); - assertNotNull(serverSession.getLocalPrincipal()); - } finally { - cleanupClientSslEngine(clientEngine); - cleanupServerSslEngine(serverEngine); - ssc.delete(); - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testEnabledProtocolsAndCiphers(SSLEngineTestParam param) throws Exception { - clientSslCtx = wrapContext(param, SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslClientProvider()) - .sslContextProvider(clientSslContextProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SelfSignedCertificate ssc = new SelfSignedCertificate(); - serverSslCtx = wrapContext(param, SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(sslServerProvider()) - .sslContextProvider(serverSslContextProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - SSLEngine clientEngine = null; - SSLEngine serverEngine = null; - try { - clientEngine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - serverEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - handshake(param.type(), param.delegate(), clientEngine, serverEngine); - assertEnabledProtocolsAndCipherSuites(clientEngine); - assertEnabledProtocolsAndCipherSuites(serverEngine); - } finally { - cleanupClientSslEngine(clientEngine); - cleanupServerSslEngine(serverEngine); - ssc.delete(); - } - } - - private static void assertEnabledProtocolsAndCipherSuites(SSLEngine engine) { - String protocol = engine.getSession().getProtocol(); - String cipherSuite = engine.getSession().getCipherSuite(); - - assertArrayContains(protocol, engine.getEnabledProtocols()); - assertArrayContains(cipherSuite, engine.getEnabledCipherSuites()); - } - - private static void assertArrayContains(String expected, String[] array) { - for (String value: array) { - if (expected.equals(value)) { - return; - } - } - fail("Array did not contain '" + expected + "':" + Arrays.toString(array)); - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testMasterKeyLogging(final SSLEngineTestParam param) throws Exception { - if (param.combo() != ProtocolCipherCombo.tlsv12()) { - return; - } - /* - * At the moment master key logging is not supported for conscrypt - */ - assumeFalse(serverSslContextProvider() instanceof OpenSSLProvider); - - /* - * The JDK SSL engine master key retrieval relies on being able to set field access to true. - * That is not available in JDK9+ - */ - assumeFalse(sslServerProvider() == SslProvider.JDK && PlatformDependent.javaVersion() > 8); - - String originalSystemPropertyValue = SystemPropertyUtil.get(SslMasterKeyHandler.SYSTEM_PROP_KEY); - System.setProperty(SslMasterKeyHandler.SYSTEM_PROP_KEY, Boolean.TRUE.toString()); - - SelfSignedCertificate ssc = new SelfSignedCertificate(); - serverSslCtx = wrapContext(param, SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(sslServerProvider()) - .sslContextProvider(serverSslContextProvider()) - .protocols(param.protocols()) - .ciphers(param.ciphers()) - .build()); - Socket socket = null; - - try { - sb = new ServerBootstrap(); - sb.group(new MultithreadEventLoopGroup(NioHandler.newFactory()), - new MultithreadEventLoopGroup(NioHandler.newFactory())); - sb.channel(NioServerSocketChannel.class); - - final Promise promise = sb.config().group().next().newPromise(); - serverChannel = sb.childHandler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) { - ch.config().setAllocator(new TestByteBufAllocator(ch.config().getAllocator(), param.type())); - - SslHandler sslHandler = !param.delegate() ? - serverSslCtx.newHandler(ch.alloc()) : - serverSslCtx.newHandler(ch.alloc(), delegatingExecutor); - - ch.pipeline().addLast(sslHandler); - ch.pipeline().addLast(new SslMasterKeyHandler() { - @Override - protected void accept(SecretKey masterKey, SSLSession session) { - promise.setSuccess(masterKey); - } - }); - serverConnectedChannel = ch; - } - }).bind(new InetSocketAddress(0)).get(); - - int port = ((InetSocketAddress) serverChannel.localAddress()).getPort(); - - SSLContext sslContext = SSLContext.getInstance("TLS"); - sslContext.init(null, InsecureTrustManagerFactory.INSTANCE.getTrustManagers(), null); - socket = sslContext.getSocketFactory().createSocket(NetUtil.LOCALHOST, port); - OutputStream out = socket.getOutputStream(); - out.write(1); - out.flush(); - - Future future = promise.asFuture(); - assertTrue(future.await(10, TimeUnit.SECONDS)); - SecretKey key = future.get(); - assertEquals(48, key.getEncoded().length, "AES secret key must be 48 bytes"); - } finally { - closeQuietly(socket); - if (originalSystemPropertyValue != null) { - System.setProperty(SslMasterKeyHandler.SYSTEM_PROP_KEY, originalSystemPropertyValue); - } else { - System.clearProperty(SslMasterKeyHandler.SYSTEM_PROP_KEY); - } - ssc.delete(); - } - } - - private static void closeQuietly(Closeable c) { - if (c != null) { - try { - c.close(); - } catch (IOException ignore) { - // ignore - } - } - } - - private static KeyManagerFactory newKeyManagerFactory(SelfSignedCertificate ssc) - throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException, - CertificateException, IOException { - return SslContext.buildKeyManagerFactory( - new java.security.cert.X509Certificate[] { ssc.cert() }, null, ssc.key(), null, null, null); - } - - private static 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; - } - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testDefaultProtocolsIncludeTLSv13(SSLEngineTestParam param) throws Exception { - // Don't specify the protocols as we want to test the default selection - clientSslCtx = wrapContext(param, SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslClientProvider()) - .sslContextProvider(clientSslContextProvider()) - .ciphers(param.ciphers()) - .build()); - SelfSignedCertificate ssc = new SelfSignedCertificate(); - serverSslCtx = wrapContext(param, SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(sslServerProvider()) - .sslContextProvider(serverSslContextProvider()) - .ciphers(param.ciphers()) - .build()); - SSLEngine clientEngine = null; - SSLEngine serverEngine = null; - String[] clientProtocols; - String[] serverProtocols; - try { - clientEngine = wrapEngine(clientSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - serverEngine = wrapEngine(serverSslCtx.newEngine(UnpooledByteBufAllocator.DEFAULT)); - clientProtocols = clientEngine.getEnabledProtocols(); - serverProtocols = serverEngine.getEnabledProtocols(); - } finally { - cleanupClientSslEngine(clientEngine); - cleanupServerSslEngine(serverEngine); - ssc.delete(); - } - - assertEquals(SslProvider.isTlsv13EnabledByDefault(sslClientProvider(), clientSslContextProvider()), - SslUtils.arrayContains(clientProtocols, SslProtocols.TLS_v1_3)); - assertEquals(SslProvider.isTlsv13EnabledByDefault(sslServerProvider(), serverSslContextProvider()), - SslUtils.arrayContains(serverProtocols, SslProtocols.TLS_v1_3)); - } - - @MethodSource("newTestParams") - @ParameterizedTest - public void testRSASSAPSS(SSLEngineTestParam param) throws Exception { - char[] password = "password".toCharArray(); - - final KeyStore serverKeyStore = KeyStore.getInstance("PKCS12"); - serverKeyStore.load(getClass().getResourceAsStream("rsaValidations-server-keystore.p12"), password); - - final KeyStore clientKeyStore = KeyStore.getInstance("PKCS12"); - clientKeyStore.load(getClass().getResourceAsStream("rsaValidation-user-certs.p12"), password); - - final KeyManagerFactory serverKeyManagerFactory = - KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); - serverKeyManagerFactory.init(serverKeyStore, password); - final KeyManagerFactory clientKeyManagerFactory = - KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); - clientKeyManagerFactory.init(clientKeyStore, password); - - File commonChain = ResourcesUtil.getFile(getClass(), "rsapss-ca-cert.cert"); - ClientAuth auth = ClientAuth.REQUIRE; - - mySetupMutualAuth(param, serverKeyManagerFactory, commonChain, clientKeyManagerFactory, commonChain, - auth, false, true); - - assertTrue(clientLatch.await(10, TimeUnit.SECONDS)); - rethrowIfNotNull(clientException); - assertTrue(serverLatch.await(5, TimeUnit.SECONDS)); - rethrowIfNotNull(serverException); - } - - protected SSLEngine wrapEngine(SSLEngine engine) { - return engine; - } - - protected SslContext wrapContext(SSLEngineTestParam param, SslContext context) { - return context; - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/SignatureAlgorithmConverterTest.java b/handler/src/test/java/io/netty/handler/ssl/SignatureAlgorithmConverterTest.java deleted file mode 100644 index e0974c1e89..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/SignatureAlgorithmConverterTest.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.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 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")); - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/SniClientJava8TestUtil.java b/handler/src/test/java/io/netty/handler/ssl/SniClientJava8TestUtil.java deleted file mode 100644 index 056a906356..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/SniClientJava8TestUtil.java +++ /dev/null @@ -1,352 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.bootstrap.Bootstrap; -import io.netty.bootstrap.ServerBootstrap; -import io.netty.buffer.ByteBufAllocator; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.local.LocalAddress; -import io.netty.channel.local.LocalChannel; -import io.netty.channel.local.LocalHandler; -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.ReferenceCountUtil; -import io.netty.util.concurrent.Promise; -import io.netty.util.internal.EmptyArrays; -import io.netty.util.internal.ThrowableUtil; - -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; -import java.util.concurrent.CompletionException; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - -/** - * In extra class to be able to run tests with java7 without trying to load classes that not exists in java7. - */ -final class SniClientJava8TestUtil { - - private SniClientJava8TestUtil() { } - - static void testSniClient(SslProvider sslClientProvider, SslProvider sslServerProvider, final boolean match) - throws Throwable { - final String sniHost = "sni.netty.io"; - SelfSignedCertificate cert = new SelfSignedCertificate(); - LocalAddress address = new LocalAddress("test"); - EventLoopGroup group = new MultithreadEventLoopGroup(1, LocalHandler.newFactory()); - SslContext sslServerContext = null; - SslContext sslClientContext = null; - - Channel sc = null; - Channel cc = null; - try { - 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 = finalContext.newHandler(ch.alloc()); - SSLParameters parameters = handler.engine().getSSLParameters(); - SNIMatcher matcher = new SNIMatcher(0) { - @Override - public boolean matches(SNIServerName sniServerName) { - return match; - } - }; - parameters.setSNIMatchers(Collections.singleton(matcher)); - handler.engine().setSSLParameters(parameters); - - ch.pipeline().addFirst(handler); - ch.pipeline().addLast(new ChannelHandler() { - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (evt instanceof SslHandshakeCompletionEvent) { - SslHandshakeCompletionEvent event = (SslHandshakeCompletionEvent) evt; - if (match) { - if (event.isSuccess()) { - promise.setSuccess(null); - } else { - promise.setFailure(event.cause()); - } - } else { - if (event.isSuccess()) { - promise.setFailure(new AssertionError("expected SSLException")); - } else { - Throwable cause = event.cause(); - if (cause instanceof SSLException) { - promise.setSuccess(null); - } else { - promise.setFailure( - new AssertionError("cause not of type SSLException: " - + ThrowableUtil.stackTraceToString(cause))); - } - } - } - } - } - }); - } - }).bind(address).get(); - - sslClientContext = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(sslClientProvider).build(); - - SslHandler sslHandler = new SslHandler( - sslClientContext.newEngine(ByteBufAllocator.DEFAULT, sniHost, -1)); - Bootstrap cb = new Bootstrap(); - cc = cb.group(group).channel(LocalChannel.class).handler(sslHandler).connect(address).get(); - - promise.asFuture().syncUninterruptibly(); - sslHandler.handshakeFuture().syncUninterruptibly(); - } catch (CompletionException e) { - throw e.getCause(); - } finally { - if (cc != null) { - cc.close().syncUninterruptibly(); - } - if (sc != null) { - sc.close().syncUninterruptibly(); - } - - ReferenceCountUtil.release(sslServerContext); - ReferenceCountUtil.release(sslClientContext); - - cert.delete(); - - group.shutdownGracefully(); - } - } - - static void assertSSLSession(boolean clientSide, SSLSession session, String name) { - assertSSLSession(clientSide, session, new SNIHostName(name)); - } - - private static void assertSSLSession(boolean clientSide, SSLSession session, SNIServerName name) { - assertNotNull(session); - if (session instanceof ExtendedSSLSession) { - ExtendedSSLSession extendedSSLSession = (ExtendedSSLSession) session; - List names = extendedSSLSession.getRequestedServerNames(); - assertEquals(1, names.size()); - assertEquals(name, names.get(0)); - assertTrue(extendedSSLSession.getLocalSupportedSignatureAlgorithms().length > 0); - if (clientSide) { - assertEquals(0, extendedSSLSession.getPeerSupportedSignatureAlgorithms().length); - } else { - assertTrue(extendedSSLSession.getPeerSupportedSignatureAlgorithms().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 { - fail(); - } - - @Override - public void checkServerTrusted(X509Certificate[] x509Certificates, String s, Socket socket) - throws CertificateException { - fail(); - } - - @Override - public void checkClientTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) - throws CertificateException { - fail(); - } - - @Override - public void checkServerTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) - throws CertificateException { - assertSSLSession(sslEngine.getUseClientMode(), sslEngine.getHandshakeSession(), name); - } - - @Override - public void checkClientTrusted(X509Certificate[] x509Certificates, String s) - throws CertificateException { - fail(); - } - - @Override - public void checkServerTrusted(X509Certificate[] x509Certificates, String s) - throws CertificateException { - 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() }, null, cert.key(), null, 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(sslEngine.getUseClientMode(), 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 deleted file mode 100644 index b5ac17d50c..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/SniClientTest.java +++ /dev/null @@ -1,165 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.bootstrap.Bootstrap; -import io.netty.bootstrap.ServerBootstrap; -import io.netty.buffer.ByteBufAllocator; -import io.netty.channel.Channel; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.local.LocalAddress; -import io.netty.channel.local.LocalChannel; -import io.netty.channel.local.LocalHandler; -import io.netty.channel.local.LocalServerChannel; -import io.netty.handler.ssl.util.SelfSignedCertificate; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.concurrent.Promise; -import io.netty.util.internal.PlatformDependent; -import org.junit.jupiter.api.Timeout; -import org.junit.jupiter.api.function.Executable; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLException; -import javax.net.ssl.TrustManagerFactory; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assumptions.assumeTrue; - - -public class SniClientTest { - private static final String PARAMETERIZED_NAME = "{index}: serverSslProvider = {0}, clientSslProvider = {1}"; - static Collection parameters() { - List providers = new ArrayList<>(Arrays.asList(SslProvider.values())); - if (!OpenSsl.isAvailable()) { - providers.remove(SslProvider.OPENSSL); - providers.remove(SslProvider.OPENSSL_REFCNT); - } - - List params = new ArrayList<>(); - for (SslProvider sp: providers) { - for (SslProvider cp: providers) { - params.add(new Object[] { sp, cp }); - } - } - return params; - } - - @ParameterizedTest(name = PARAMETERIZED_NAME) - @Timeout(value = 30000, unit = TimeUnit.MILLISECONDS) - @MethodSource("parameters") - public void testSniSNIMatcherMatchesClient(SslProvider serverProvider, SslProvider clientProvider) - throws Throwable { - assumeTrue(PlatformDependent.javaVersion() >= 8); - SniClientJava8TestUtil.testSniClient(serverProvider, clientProvider, true); - } - - @ParameterizedTest(name = PARAMETERIZED_NAME) - @Timeout(value = 30000, unit = TimeUnit.MILLISECONDS) - @MethodSource("parameters") - public void testSniSNIMatcherDoesNotMatchClient( - final SslProvider serverProvider, final SslProvider clientProvider) { - assumeTrue(PlatformDependent.javaVersion() >= 8); - assertThrows(SSLException.class, new Executable() { - @Override - public void execute() throws Throwable { - SniClientJava8TestUtil.testSniClient(serverProvider, clientProvider, false); - } - }); - } - - @ParameterizedTest(name = PARAMETERIZED_NAME) - @Timeout(value = 30000, unit = TimeUnit.MILLISECONDS) - @MethodSource("parameters") - public void testSniClient(SslProvider sslServerProvider, SslProvider sslClientProvider) throws Exception { - String sniHostName = "sni.netty.io"; - LocalAddress address = new LocalAddress("test"); - EventLoopGroup group = new MultithreadEventLoopGroup(1, LocalHandler.newFactory()); - SelfSignedCertificate cert = new SelfSignedCertificate(); - SslContext sslServerContext = null; - SslContext sslClientContext = null; - - Channel sc = null; - Channel cc = null; - try { - if ((sslServerProvider == SslProvider.OPENSSL || sslServerProvider == SslProvider.OPENSSL_REFCNT) - && !OpenSsl.supportsKeyManagerFactory()) { - sslServerContext = SslContextBuilder.forServer(cert.certificate(), cert.privateKey()) - .sslProvider(sslServerProvider) - .build(); - } else { - // The used OpenSSL version does support a KeyManagerFactory, so use it. - KeyManagerFactory kmf = SniClientJava8TestUtil.newSniX509KeyManagerFactory(cert, sniHostName); - sslServerContext = SslContextBuilder.forServer(kmf) - .sslProvider(sslServerProvider) - .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() { - @Override - protected void initChannel(Channel ch) throws Exception { - ch.pipeline().addFirst(new SniHandler(input -> { - promise.setSuccess(input); - return finalContext; - })); - } - }).bind(address).get(); - - TrustManagerFactory tmf = SniClientJava8TestUtil.newSniX509TrustmanagerFactory(sniHostName); - sslClientContext = SslContextBuilder.forClient().trustManager(tmf) - .sslProvider(sslClientProvider).build(); - Bootstrap cb = new Bootstrap(); - - SslHandler handler = new SslHandler( - sslClientContext.newEngine(ByteBufAllocator.DEFAULT, sniHostName, -1)); - cc = cb.group(group).channel(LocalChannel.class).handler(handler).connect(address).get(); - assertEquals(sniHostName, promise.asFuture().syncUninterruptibly().getNow()); - - // After we are done with handshaking getHandshakeSession() should return null. - handler.handshakeFuture().syncUninterruptibly(); - assertNull(handler.engine().getHandshakeSession()); - - SniClientJava8TestUtil.assertSSLSession( - handler.engine().getUseClientMode(), handler.engine().getSession(), sniHostName); - } finally { - if (cc != null) { - cc.close().syncUninterruptibly(); - } - if (sc != null) { - sc.close().syncUninterruptibly(); - } - ReferenceCountUtil.release(sslServerContext); - ReferenceCountUtil.release(sslClientContext); - - cert.delete(); - - group.shutdownGracefully(); - } - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/SniHandlerTest.java b/handler/src/test/java/io/netty/handler/ssl/SniHandlerTest.java deleted file mode 100644 index a3d69c9c57..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/SniHandlerTest.java +++ /dev/null @@ -1,703 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.ssl; - -import io.netty.bootstrap.Bootstrap; -import io.netty.bootstrap.ServerBootstrap; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.channel.local.LocalAddress; -import io.netty.channel.local.LocalChannel; -import io.netty.channel.local.LocalHandler; -import io.netty.channel.local.LocalServerChannel; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.codec.DecoderException; -import io.netty.handler.ssl.util.InsecureTrustManagerFactory; -import io.netty.handler.ssl.util.SelfSignedCertificate; -import io.netty.util.DomainNameMapping; -import io.netty.util.DomainNameMappingBuilder; -import io.netty.util.Mapping; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.ReferenceCounted; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; -import io.netty.util.internal.ResourcesUtil; -import io.netty.util.internal.StringUtil; -import org.hamcrest.CoreMatchers; -import org.junit.jupiter.api.Timeout; -import org.junit.jupiter.api.function.Executable; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLException; -import java.io.File; -import java.net.InetSocketAddress; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; - -import static java.util.Objects.requireNonNull; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.nullValue; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assumptions.assumeTrue; -import static org.mockito.Mockito.mock; - -public class SniHandlerTest { - - private static ApplicationProtocolConfig newApnConfig() { - return new ApplicationProtocolConfig( - ApplicationProtocolConfig.Protocol.ALPN, - // NO_ADVERTISE is currently the only mode supported by both OpenSsl and JDK providers. - ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE, - // ACCEPT is currently the only mode supported by both OpenSsl and JDK providers. - ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT, - "myprotocol"); - } - - private static void assumeApnSupported(SslProvider provider) { - switch (provider) { - case OPENSSL: - case OPENSSL_REFCNT: - assumeTrue(OpenSsl.isAlpnSupported()); - break; - case JDK: - assumeTrue(JettyAlpnSslEngine.isAvailable()); - break; - default: - throw new Error(); - } - } - - private static SslContext makeSslContext(SslProvider provider, boolean apn) throws Exception { - if (apn) { - assumeApnSupported(provider); - } - - 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); - if (apn) { - sslCtxBuilder.applicationProtocolConfig(newApnConfig()); - } - return sslCtxBuilder.build(); - } - - private static SslContext makeSslClientContext(SslProvider provider, boolean apn) throws Exception { - if (apn) { - assumeApnSupported(provider); - } - - File crtFile = ResourcesUtil.getFile(SniHandlerTest.class, "test.crt"); - - SslContextBuilder sslCtxBuilder = SslContextBuilder.forClient().trustManager(crtFile).sslProvider(provider); - if (apn) { - sslCtxBuilder.applicationProtocolConfig(newApnConfig()); - } - return sslCtxBuilder.build(); - } - - static Iterable data() { - List params = new ArrayList<>(3); - if (OpenSsl.isAvailable()) { - params.add(SslProvider.OPENSSL); - params.add(SslProvider.OPENSSL_REFCNT); - } - params.add(SslProvider.JDK); - return params; - } - - @ParameterizedTest(name = "{index}: sslProvider={0}") - @MethodSource("data") - public void testNonSslRecord(SslProvider provider) throws Exception { - SslContext nettyContext = makeSslContext(provider, false); - try { - final AtomicReference evtRef = - new AtomicReference(); - SniHandler handler = new SniHandler(new DomainNameMappingBuilder<>(nettyContext).build()); - final EmbeddedChannel ch = new EmbeddedChannel(handler, new ChannelHandler() { - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (evt instanceof SslHandshakeCompletionEvent) { - assertTrue(evtRef.compareAndSet(null, (SslHandshakeCompletionEvent) evt)); - } - } - }); - - try { - final byte[] bytes = new byte[1024]; - bytes[0] = SslUtils.SSL_CONTENT_TYPE_ALERT; - - DecoderException e = assertThrows(DecoderException.class, new Executable() { - @Override - public void execute() throws Throwable { - ch.writeInbound(Unpooled.wrappedBuffer(bytes)); - } - }); - assertThat(e.getCause(), CoreMatchers.instanceOf(NotSslRecordException.class)); - assertFalse(ch.finish()); - } finally { - ch.finishAndReleaseAll(); - } - assertThat(evtRef.get().cause(), CoreMatchers.instanceOf(NotSslRecordException.class)); - } finally { - releaseAll(nettyContext); - } - } - - @ParameterizedTest(name = "{index}: sslProvider={0}") - @MethodSource("data") - public void testServerNameParsing(SslProvider provider) throws Exception { - SslContext nettyContext = makeSslContext(provider, false); - SslContext leanContext = makeSslContext(provider, false); - SslContext leanContext2 = makeSslContext(provider, false); - - try { - DomainNameMapping mapping = new DomainNameMappingBuilder<>(nettyContext) - .add("*.netty.io", nettyContext) - // input with custom cases - .add("*.LEANCLOUD.CN", leanContext) - // a hostname conflict with previous one, since we are using order-sensitive config, - // the engine won't be used with the handler. - .add("chat4.leancloud.cn", leanContext2) - .build(); - - final AtomicReference evtRef = new AtomicReference<>(); - SniHandler handler = new SniHandler(mapping); - EmbeddedChannel ch = new EmbeddedChannel(handler, new ChannelHandler() { - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (evt instanceof SniCompletionEvent) { - assertTrue(evtRef.compareAndSet(null, (SniCompletionEvent) evt)); - } else { - ctx.fireUserEventTriggered(evt); - } - } - }); - - try { - // hex dump of a client hello packet, which contains hostname "CHAT4.LEANCLOUD.CN" - String tlsHandshakeMessageHex1 = "16030100"; - // part 2 - String tlsHandshakeMessageHex = "c6010000c20303bb0855d66532c05a0ef784f7c384feeafa68b3" + - "b655ac7288650d5eed4aa3fb52000038c02cc030009fcca9cca8ccaac02b" + - "c02f009ec024c028006bc023c0270067c00ac0140039c009c0130033009d" + - "009c003d003c0035002f00ff010000610000001700150000124348415434" + - "2e4c45414e434c4f55442e434e000b000403000102000a000a0008001d00" + - "170019001800230000000d0020001e060106020603050105020503040104" + - "0204030301030203030201020202030016000000170000"; - - ch.writeInbound(Unpooled.wrappedBuffer(StringUtil.decodeHexDump(tlsHandshakeMessageHex1))); - ch.writeInbound(Unpooled.wrappedBuffer(StringUtil.decodeHexDump(tlsHandshakeMessageHex))); - - // This should produce an alert - assertTrue(ch.finish()); - - assertThat(handler.hostname(), is("chat4.leancloud.cn")); - assertThat(handler.sslContext(), is(leanContext)); - - SniCompletionEvent evt = evtRef.get(); - assertNotNull(evt); - assertEquals("chat4.leancloud.cn", evt.hostname()); - assertTrue(evt.isSuccess()); - assertNull(evt.cause()); - } finally { - ch.finishAndReleaseAll(); - } - } finally { - releaseAll(leanContext, leanContext2, nettyContext); - } - } - - @ParameterizedTest(name = "{index}: sslProvider={0}") - @MethodSource("data") - public void testNonAsciiServerNameParsing(SslProvider provider) throws Exception { - SslContext nettyContext = makeSslContext(provider, false); - SslContext leanContext = makeSslContext(provider, false); - SslContext leanContext2 = makeSslContext(provider, false); - - try { - DomainNameMapping mapping = new DomainNameMappingBuilder<>(nettyContext) - .add("*.netty.io", nettyContext) - // input with custom cases - .add("*.LEANCLOUD.CN", leanContext) - // a hostname conflict with previous one, since we are using order-sensitive config, - // the engine won't be used with the handler. - .add("chat4.leancloud.cn", leanContext2) - .build(); - - SniHandler handler = new SniHandler(mapping); - final EmbeddedChannel ch = new EmbeddedChannel(handler); - - try { - // hex dump of a client hello packet, which contains an invalid hostname "CHAT4。LEANCLOUD。CN" - String tlsHandshakeMessageHex1 = "16030100"; - // part 2 - final String tlsHandshakeMessageHex = "bd010000b90303a74225676d1814ba57faff3b366" + - "3656ed05ee9dbb2a4dbb1bb1c32d2ea5fc39e0000000100008c0000001700150000164348" + - "415434E380824C45414E434C4F5544E38082434E000b000403000102000a00340032000e0" + - "00d0019000b000c00180009000a0016001700080006000700140015000400050012001300" + - "0100020003000f0010001100230000000d0020001e0601060206030501050205030401040" + - "20403030103020303020102020203000f00010133740000"; - - // Push the handshake message. - // Decode should fail because of the badly encoded "HostName" string in the SNI extension - // that isn't ASCII as per RFC 6066 - https://tools.ietf.org/html/rfc6066#page-6 - ch.writeInbound(Unpooled.wrappedBuffer(StringUtil.decodeHexDump(tlsHandshakeMessageHex1))); - - assertThrows(DecoderException.class, new Executable() { - @Override - public void execute() throws Throwable { - ch.writeInbound(Unpooled.wrappedBuffer(StringUtil.decodeHexDump(tlsHandshakeMessageHex))); - } - }); - } finally { - ch.finishAndReleaseAll(); - } - } finally { - releaseAll(leanContext, leanContext2, nettyContext); - } - } - - @ParameterizedTest(name = "{index}: sslProvider={0}") - @MethodSource("data") - public void testFallbackToDefaultContext(SslProvider provider) throws Exception { - SslContext nettyContext = makeSslContext(provider, false); - SslContext leanContext = makeSslContext(provider, false); - SslContext leanContext2 = makeSslContext(provider, false); - - try { - DomainNameMapping mapping = new DomainNameMappingBuilder<>(nettyContext) - .add("*.netty.io", nettyContext) - // input with custom cases - .add("*.LEANCLOUD.CN", leanContext) - // a hostname conflict with previous one, since we are using order-sensitive config, - // the engine won't be used with the handler. - .add("chat4.leancloud.cn", leanContext2) - .build(); - - SniHandler handler = new SniHandler(mapping); - EmbeddedChannel ch = new EmbeddedChannel(handler); - - // invalid - byte[] message = {22, 3, 1, 0, 0}; - try { - // Push the handshake message. - ch.writeInbound(Unpooled.wrappedBuffer(message)); - // TODO(scott): This should fail because the engine should reject zero length records during handshake. - // See https://github.com/netty/netty/issues/6348. - // fail(); - } catch (Exception e) { - // expected - } - - ch.close(); - - // When the channel is closed the SslHandler will write an empty buffer to the channel. - ByteBuf buf = ch.readOutbound(); - // TODO(scott): if the engine is shutdown correctly then this buffer shouldn't be null! - // See https://github.com/netty/netty/issues/6348. - if (buf != null) { - assertFalse(buf.isReadable()); - buf.release(); - } - - assertThat(ch.finish(), is(false)); - assertThat(handler.hostname(), nullValue()); - assertThat(handler.sslContext(), is(nettyContext)); - } finally { - releaseAll(leanContext, leanContext2, nettyContext); - } - } - - @ParameterizedTest(name = "{index}: sslProvider={0}") - @MethodSource("data") - @Timeout(value = 10000, unit = TimeUnit.MILLISECONDS) - public void testMajorVersionNot3(SslProvider provider) throws Exception { - SslContext nettyContext = makeSslContext(provider, false); - - try { - DomainNameMapping mapping = new DomainNameMappingBuilder(nettyContext).build(); - - SniHandler handler = new SniHandler(mapping); - EmbeddedChannel ch = new EmbeddedChannel(handler); - - // invalid - byte[] message = {22, 2, 0, 0, 0}; - try { - // Push the handshake message. - ch.writeInbound(Unpooled.wrappedBuffer(message)); - // TODO(scott): This should fail because the engine should reject zero length records during handshake. - // See https://github.com/netty/netty/issues/6348. - // fail(); - } catch (Exception e) { - // expected - } - - ch.close(); - - // Consume all the outbound data that may be produced by the SSLEngine. - for (;;) { - ByteBuf buf = ch.readOutbound(); - if (buf == null) { - break; - } - buf.release(); - } - - assertThat(ch.finish(), is(false)); - assertThat(handler.hostname(), nullValue()); - assertThat(handler.sslContext(), is(nettyContext)); - } finally { - releaseAll(nettyContext); - } - } - - @ParameterizedTest(name = "{index}: sslProvider={0}") - @MethodSource("data") - public void testSniWithApnHandler(SslProvider provider) throws Exception { - SslContext nettyContext = makeSslContext(provider, true); - SslContext sniContext = makeSslContext(provider, true); - final SslContext clientContext = makeSslClientContext(provider, true); - try { - final AtomicBoolean serverApnCtx = new AtomicBoolean(false); - final AtomicBoolean clientApnCtx = new AtomicBoolean(false); - final CountDownLatch serverApnDoneLatch = new CountDownLatch(1); - final CountDownLatch clientApnDoneLatch = new CountDownLatch(1); - - final DomainNameMapping mapping = new DomainNameMappingBuilder<>(nettyContext) - .add("*.netty.io", nettyContext) - .add("sni.fake.site", sniContext).build(); - final SniHandler handler = new SniHandler(mapping); - EventLoopGroup group = new MultithreadEventLoopGroup(2, NioHandler.newFactory()); - Channel serverChannel = null; - Channel clientChannel = null; - try { - ServerBootstrap sb = new ServerBootstrap(); - sb.group(group); - sb.channel(NioServerSocketChannel.class); - sb.childHandler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - ChannelPipeline p = ch.pipeline(); - // Server side SNI. - p.addLast(handler); - // Catch the notification event that APN has completed successfully. - p.addLast(new ApplicationProtocolNegotiationHandler("foo") { - @Override - protected void configurePipeline(ChannelHandlerContext ctx, String protocol) { - // addresses issue #9131 - serverApnCtx.set(ctx.pipeline().context(this) != null); - serverApnDoneLatch.countDown(); - } - }); - } - }); - - Bootstrap cb = new Bootstrap(); - cb.group(group); - cb.channel(NioSocketChannel.class); - cb.handler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - ch.pipeline().addLast(new SslHandler(clientContext.newEngine( - ch.alloc(), "sni.fake.site", -1))); - // Catch the notification event that APN has completed successfully. - ch.pipeline().addLast(new ApplicationProtocolNegotiationHandler("foo") { - @Override - protected void configurePipeline(ChannelHandlerContext ctx, String protocol) { - // addresses issue #9131 - clientApnCtx.set(ctx.pipeline().context(this) != null); - clientApnDoneLatch.countDown(); - } - }); - } - }); - - serverChannel = sb.bind(new InetSocketAddress(0)).get(); - clientChannel = cb.connect(serverChannel.localAddress()).get(); - - assertTrue(serverApnDoneLatch.await(5, TimeUnit.SECONDS)); - assertTrue(clientApnDoneLatch.await(5, TimeUnit.SECONDS)); - assertTrue(serverApnCtx.get()); - assertTrue(clientApnCtx.get()); - assertThat(handler.hostname(), is("sni.fake.site")); - assertThat(handler.sslContext(), is(sniContext)); - } finally { - if (serverChannel != null) { - serverChannel.close().sync(); - } - if (clientChannel != null) { - clientChannel.close().sync(); - } - group.shutdownGracefully(0, 0, TimeUnit.MICROSECONDS); - } - } finally { - releaseAll(clientContext, nettyContext, sniContext); - } - } - - @ParameterizedTest(name = "{index}: sslProvider={0}") - @MethodSource("data") - @Timeout(value = 30000, unit = TimeUnit.MILLISECONDS) - public void testReplaceHandler(SslProvider provider) throws Exception { - switch (provider) { - case OPENSSL: - case OPENSSL_REFCNT: - final String sniHost = "sni.netty.io"; - LocalAddress address = new LocalAddress("testReplaceHandler-" + Math.random()); - EventLoopGroup group = new MultithreadEventLoopGroup(1, LocalHandler.newFactory()); - Channel sc = null; - Channel cc = null; - SslContext sslContext = null; - - SelfSignedCertificate cert = new SelfSignedCertificate(); - - try { - final SslContext sslServerContext = SslContextBuilder - .forServer(cert.key(), cert.cert()) - .sslProvider(provider) - .build(); - - final Mapping mapping = input -> sslServerContext; - - final Promise releasePromise = group.next().newPromise(); - - final SniHandler handler = new SniHandler(mapping) { - @Override - protected void replaceHandler(ChannelHandlerContext ctx, - String hostname, final SslContext sslContext) - throws Exception { - - boolean success = false; - try { - assertEquals(1, ((ReferenceCountedOpenSslContext) sslContext).refCnt()); - // The SniHandler's replaceHandler() method allows us to implement custom behavior. - // As an example, we want to release() the SslContext upon channelInactive() or rather - // when the SslHandler closes it's SslEngine. If you take a close look at SslHandler - // you'll see that it's doing it in the #handlerRemoved0() method. - - SSLEngine sslEngine = sslContext.newEngine(ctx.alloc()); - try { - assertEquals(2, ((ReferenceCountedOpenSslContext) sslContext).refCnt()); - SslHandler customSslHandler = new CustomSslHandler(sslContext, sslEngine) { - @Override - public void handlerRemoved0(ChannelHandlerContext ctx) throws Exception { - try { - super.handlerRemoved0(ctx); - } finally { - releasePromise.trySuccess(null); - } - } - }; - ctx.pipeline().replace(this, CustomSslHandler.class.getName(), customSslHandler); - success = true; - } finally { - if (!success) { - ReferenceCountUtil.safeRelease(sslEngine); - } - } - } finally { - if (!success) { - ReferenceCountUtil.safeRelease(sslContext); - releasePromise.cancel(); - } - } - } - }; - - ServerBootstrap sb = new ServerBootstrap(); - sc = sb.group(group).channel(LocalServerChannel.class) - .childHandler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - ch.pipeline().addFirst(handler); - } - }).bind(address).get(); - - sslContext = SslContextBuilder.forClient().sslProvider(provider) - .trustManager(InsecureTrustManagerFactory.INSTANCE).build(); - - Bootstrap cb = new Bootstrap(); - cc = cb.group(group).channel(LocalChannel.class).handler(new SslHandler( - sslContext.newEngine(ByteBufAllocator.DEFAULT, sniHost, -1))) - .connect(address).get(); - - cc.writeAndFlush(Unpooled.wrappedBuffer("Hello, World!".getBytes())) - .syncUninterruptibly(); - - // Notice how the server's SslContext refCnt is 2 as it is incremented when the SSLEngine is created - // and only decremented once it is destroyed. - assertEquals(2, ((ReferenceCounted) sslServerContext).refCnt()); - - // The client disconnects - cc.close().syncUninterruptibly(); - if (!releasePromise.asFuture().awaitUninterruptibly(10L, TimeUnit.SECONDS)) { - throw new IllegalStateException("It doesn't seem #replaceHandler() got called."); - } - - // We should have successfully release() the SslContext - assertEquals(0, ((ReferenceCounted) sslServerContext).refCnt()); - } finally { - if (cc != null) { - cc.close().syncUninterruptibly(); - } - if (sc != null) { - sc.close().syncUninterruptibly(); - } - if (sslContext != null) { - ReferenceCountUtil.release(sslContext); - } - group.shutdownGracefully(); - - cert.delete(); - } - case JDK: - return; - default: - throw new Error(); - } - } - - /** - * This is a {@link SslHandler} that will call {@code release()} on the {@link SslContext} when - * the client disconnects. - * - * @see SniHandlerTest#testReplaceHandler(SslProvider) - */ - private static class CustomSslHandler extends SslHandler { - private final SslContext sslContext; - - CustomSslHandler(SslContext sslContext, SSLEngine sslEngine) { - super(sslEngine); - this.sslContext = requireNonNull(sslContext, "sslContext"); - } - - @Override - public void handlerRemoved0(ChannelHandlerContext ctx) throws Exception { - super.handlerRemoved0(ctx); - ReferenceCountUtil.release(sslContext); - } - } - - private static void releaseAll(SslContext... contexts) { - for (SslContext ctx: contexts) { - ReferenceCountUtil.release(ctx); - } - } - - @ParameterizedTest(name = "{index}: sslProvider={0}") - @MethodSource("data") - public void testNonFragmented(SslProvider provider) throws Exception { - testWithFragmentSize(provider, Integer.MAX_VALUE); - } - - @ParameterizedTest(name = "{index}: sslProvider={0}") - @MethodSource("data") - public void testFragmented(SslProvider provider) throws Exception { - testWithFragmentSize(provider, 50); - } - - private static void testWithFragmentSize(SslProvider provider, final int maxFragmentSize) throws Exception { - final String sni = "netty.io"; - SelfSignedCertificate cert = new SelfSignedCertificate(); - final SslContext context = SslContextBuilder.forServer(cert.key(), cert.cert()) - .sslProvider(provider) - .build(); - try { - @SuppressWarnings("unchecked") final EmbeddedChannel server = new EmbeddedChannel( - new SniHandler(mock(DomainNameMapping.class)) { - @Override - protected Future lookup(final ChannelHandlerContext ctx, final String hostname) { - assertEquals(sni, hostname); - return ctx.executor().newSucceededFuture(context); - } - }); - - final List buffers = clientHelloInMultipleFragments(provider, sni, maxFragmentSize); - for (ByteBuf buffer : buffers) { - server.writeInbound(buffer); - } - assertTrue(server.finishAndReleaseAll()); - } finally { - releaseAll(context); - cert.delete(); - } - } - - private static List clientHelloInMultipleFragments( - SslProvider provider, String hostname, int maxTlsPlaintextSize) throws SSLException { - final EmbeddedChannel client = new EmbeddedChannel(); - final SslContext ctx = SslContextBuilder.forClient() - .sslProvider(provider) - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .build(); - try { - final SslHandler sslHandler = ctx.newHandler(client.alloc(), hostname, -1); - client.pipeline().addLast(sslHandler); - final ByteBuf clientHello = client.readOutbound(); - List buffers = split(clientHello, maxTlsPlaintextSize); - assertTrue(client.finishAndReleaseAll()); - return buffers; - } finally { - releaseAll(ctx); - } - } - - private static List split(ByteBuf clientHello, int maxSize) { - final int type = clientHello.readUnsignedByte(); - final int version = clientHello.readUnsignedShort(); - final int length = clientHello.readUnsignedShort(); - assertEquals(length, clientHello.readableBytes()); - - final List result = new ArrayList(); - while (clientHello.readableBytes() > 0) { - final int toRead = Math.min(maxSize, clientHello.readableBytes()); - final ByteBuf bb = clientHello.alloc().buffer(SslUtils.SSL_RECORD_HEADER_LENGTH + toRead); - bb.writeByte(type); - bb.writeShort(version); - bb.writeShort(toRead); - bb.writeBytes(clientHello, toRead); - result.add(bb); - } - clientHello.release(); - return result; - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/SslContextBuilderTest.java b/handler/src/test/java/io/netty/handler/ssl/SslContextBuilderTest.java deleted file mode 100644 index fb81904c58..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/SslContextBuilderTest.java +++ /dev/null @@ -1,377 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty.handler.ssl.util.SelfSignedCertificate; -import io.netty.util.CharsetUtil; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; - -import javax.net.ssl.KeyManager; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLException; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509ExtendedKeyManager; -import javax.net.ssl.X509ExtendedTrustManager; -import java.io.ByteArrayInputStream; -import java.net.Socket; -import java.security.Principal; -import java.security.PrivateKey; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; -import java.util.Collections; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; -import static org.junit.jupiter.api.Assumptions.assumeTrue; - -public class SslContextBuilderTest { - - @Test - public void testClientContextFromFileJdk() throws Exception { - testClientContextFromFile(SslProvider.JDK); - } - - @Test - public void testClientContextFromFileOpenssl() throws Exception { - OpenSsl.ensureAvailability(); - testClientContextFromFile(SslProvider.OPENSSL); - } - - @Test - public void testClientContextJdk() throws Exception { - testClientContext(SslProvider.JDK); - } - - @Test - public void testClientContextOpenssl() throws Exception { - OpenSsl.ensureAvailability(); - testClientContext(SslProvider.OPENSSL); - } - - @Test - public void testKeyStoreTypeJdk() throws Exception { - testKeyStoreType(SslProvider.JDK); - } - - @Test - public void testKeyStoreTypeOpenssl() throws Exception { - OpenSsl.ensureAvailability(); - testKeyStoreType(SslProvider.OPENSSL); - } - - @Test - public void testServerContextFromFileJdk() throws Exception { - testServerContextFromFile(SslProvider.JDK); - } - - @Test - public void testServerContextFromFileOpenssl() throws Exception { - OpenSsl.ensureAvailability(); - testServerContextFromFile(SslProvider.OPENSSL); - } - - @Test - public void testServerContextJdk() throws Exception { - testServerContext(SslProvider.JDK); - } - - @Test - public void testServerContextOpenssl() throws Exception { - OpenSsl.ensureAvailability(); - testServerContext(SslProvider.OPENSSL); - } - - @Test - public void testContextFromManagersJdk() throws Exception { - testContextFromManagers(SslProvider.JDK); - } - - @Test - public void testContextFromManagersOpenssl() throws Exception { - OpenSsl.ensureAvailability(); - assumeTrue(OpenSsl.supportsKeyManagerFactory()); - testContextFromManagers(SslProvider.OPENSSL); - } - - @Test - public void testUnsupportedPrivateKeyFailsFastForServer() throws Exception { - assumeTrue(OpenSsl.isBoringSSL()); - assertThrows(SSLException.class, new Executable() { - @Override - public void execute() throws Throwable { - testUnsupportedPrivateKeyFailsFast(true); - } - }); - } - - @Test - public void testUnsupportedPrivateKeyFailsFastForClient() throws Exception { - assumeTrue(OpenSsl.isBoringSSL()); - assertThrows(SSLException.class, new Executable() { - @Override - public void execute() throws Throwable { - testUnsupportedPrivateKeyFailsFast(false); - } - }); - } - private static void testUnsupportedPrivateKeyFailsFast(boolean server) throws Exception { - assumeTrue(OpenSsl.isBoringSSL()); - String cert = "-----BEGIN CERTIFICATE-----\n" + - "MIICODCCAY2gAwIBAgIEXKTrajAKBggqhkjOPQQDBDBUMQswCQYDVQQGEwJVUzEM\n" + - "MAoGA1UECAwDTi9hMQwwCgYDVQQHDANOL2ExDDAKBgNVBAoMA04vYTEMMAoGA1UE\n" + - "CwwDTi9hMQ0wCwYDVQQDDARUZXN0MB4XDTE5MDQwMzE3MjA0MloXDTIwMDQwMjE3\n" + - "MjA0MlowVDELMAkGA1UEBhMCVVMxDDAKBgNVBAgMA04vYTEMMAoGA1UEBwwDTi9h\n" + - "MQwwCgYDVQQKDANOL2ExDDAKBgNVBAsMA04vYTENMAsGA1UEAwwEVGVzdDCBpzAQ\n" + - "BgcqhkjOPQIBBgUrgQQAJwOBkgAEBPYWoTjlS2pCMGEM2P8qZnmURWA5e7XxPfIh\n" + - "HA876sjmgjJluPgT0OkweuxI4Y/XjzcPnnEBONgzAV1X93UmXdtRiIau/zvsAeFb\n" + - "j/q+6sfj1jdnUk6QsMx22kAwplXHmdz1z5ShXQ7mDZPxDbhCPEAUXzIzOqvWIZyA\n" + - "HgFxZXmQKEhExA8nxgSIvzQ3ucMwMAoGCCqGSM49BAMEA4GYADCBlAJIAdPD6jaN\n" + - "vGxkxcsIbcHn2gSfP1F1G8iNJYrXIN91KbQm8OEp4wxqnBwX8gb/3rmSoEhIU/te\n" + - "CcHuFs0guBjfgRWtJ/eDnKB/AkgDbkqrB5wqJFBmVd/rJ5QdwUVNuGP/vDjFVlb6\n" + - "Esny6//gTL7jYubLUKHOPIMftCZ2Jn4b+5l0kAs62HD5XkZLPDTwRbf7VCE=\n" + - "-----END CERTIFICATE-----"; - String key = "-----BEGIN PRIVATE KEY-----\n" + - "MIIBCQIBADAQBgcqhkjOPQIBBgUrgQQAJwSB8TCB7gIBAQRIALNClTXqQWWlYDHw\n" + - "LjNxXpLk17iPepkmablhbxmYX/8CNzoz1o2gcUidoIO2DM9hm7adI/W31EOmSiUJ\n" + - "+UsC/ZH3i2qr0wn+oAcGBSuBBAAnoYGVA4GSAAQE9hahOOVLakIwYQzY/ypmeZRF\n" + - "YDl7tfE98iEcDzvqyOaCMmW4+BPQ6TB67Ejhj9ePNw+ecQE42DMBXVf3dSZd21GI\n" + - "hq7/O+wB4VuP+r7qx+PWN2dSTpCwzHbaQDCmVceZ3PXPlKFdDuYNk/ENuEI8QBRf\n" + - "MjM6q9YhnIAeAXFleZAoSETEDyfGBIi/NDe5wzA=\n" + - "-----END PRIVATE KEY-----"; - if (server) { - SslContextBuilder.forServer(new ByteArrayInputStream(cert.getBytes(CharsetUtil.US_ASCII)), - new ByteArrayInputStream(key.getBytes(CharsetUtil.US_ASCII)), null) - .sslProvider(SslProvider.OPENSSL).build(); - } else { - SslContextBuilder.forClient().keyManager(new ByteArrayInputStream(cert.getBytes(CharsetUtil.US_ASCII)), - new ByteArrayInputStream(key.getBytes(CharsetUtil.US_ASCII)), null) - .sslProvider(SslProvider.OPENSSL).build(); - } - } - - @Test - public void testInvalidCipherJdk() throws Exception { - OpenSsl.ensureAvailability(); - assertThrows(IllegalArgumentException.class, new Executable() { - @Override - public void execute() throws Throwable { - testInvalidCipher(SslProvider.JDK); - } - }); - } - - @Test - public void testInvalidCipherOpenSSL() throws Exception { - OpenSsl.ensureAvailability(); - 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 testKeyStoreType(SslProvider provider) throws Exception { - SelfSignedCertificate cert = new SelfSignedCertificate(); - SslContextBuilder builder = SslContextBuilder.forServer(cert.certificate(), cert.privateKey()) - .sslProvider(provider) - .keyStoreType("PKCS12"); - SslContext context = builder.build(); - SSLEngine engine = context.newEngine(UnpooledByteBufAllocator.DEFAULT); - engine.closeInbound(); - engine.closeOutbound(); - } - - 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() - .sslProvider(provider) - .keyManager(cert.certificate(), - cert.privateKey()) - .trustManager(cert.certificate()) - .clientAuth(ClientAuth.OPTIONAL); - SslContext context = builder.build(); - SSLEngine engine = context.newEngine(UnpooledByteBufAllocator.DEFAULT); - assertFalse(engine.getWantClientAuth()); - assertFalse(engine.getNeedClientAuth()); - engine.closeInbound(); - engine.closeOutbound(); - } - - private static void testClientContext(SslProvider provider) throws Exception { - SelfSignedCertificate cert = new SelfSignedCertificate(); - SslContextBuilder builder = SslContextBuilder.forClient() - .sslProvider(provider) - .keyManager(cert.key(), cert.cert()) - .trustManager(cert.cert()) - .clientAuth(ClientAuth.OPTIONAL); - SslContext context = builder.build(); - SSLEngine engine = context.newEngine(UnpooledByteBufAllocator.DEFAULT); - assertFalse(engine.getWantClientAuth()); - assertFalse(engine.getNeedClientAuth()); - engine.closeInbound(); - engine.closeOutbound(); - } - - private static void testServerContextFromFile(SslProvider provider) throws Exception { - SelfSignedCertificate cert = new SelfSignedCertificate(); - SslContextBuilder builder = SslContextBuilder.forServer(cert.certificate(), cert.privateKey()) - .sslProvider(provider) - .trustManager(cert.certificate()) - .clientAuth(ClientAuth.OPTIONAL); - SslContext context = builder.build(); - SSLEngine engine = context.newEngine(UnpooledByteBufAllocator.DEFAULT); - assertTrue(engine.getWantClientAuth()); - assertFalse(engine.getNeedClientAuth()); - engine.closeInbound(); - engine.closeOutbound(); - } - - private static void testServerContext(SslProvider provider) throws Exception { - SelfSignedCertificate cert = new SelfSignedCertificate(); - SslContextBuilder builder = SslContextBuilder.forServer(cert.key(), cert.cert()) - .sslProvider(provider) - .trustManager(cert.cert()) - .clientAuth(ClientAuth.REQUIRE); - SslContext context = builder.build(); - SSLEngine engine = context.newEngine(UnpooledByteBufAllocator.DEFAULT); - assertFalse(engine.getWantClientAuth()); - assertTrue(engine.getNeedClientAuth()); - engine.closeInbound(); - engine.closeOutbound(); - } - - private static void testContextFromManagers(SslProvider provider) throws Exception { - final SelfSignedCertificate cert = new SelfSignedCertificate(); - KeyManager customKeyManager = new X509ExtendedKeyManager() { - @Override - public String[] getClientAliases(String s, - Principal[] principals) { - return new String[0]; - } - - @Override - public String chooseClientAlias(String[] strings, - Principal[] principals, - Socket socket) { - return "cert_sent_to_server"; - } - - @Override - public String[] getServerAliases(String s, - Principal[] principals) { - return new String[0]; - } - - @Override - public String chooseServerAlias(String s, - Principal[] principals, - Socket socket) { - return null; - } - - @Override - public X509Certificate[] getCertificateChain(String s) { - X509Certificate[] certificates = new X509Certificate[1]; - certificates[0] = cert.cert(); - return new X509Certificate[0]; - } - - @Override - public PrivateKey getPrivateKey(String s) { - return cert.key(); - } - }; - TrustManager customTrustManager = new X509ExtendedTrustManager() { - @Override - public void checkClientTrusted( - X509Certificate[] x509Certificates, String s, - Socket socket) throws CertificateException { } - - @Override - public void checkServerTrusted( - X509Certificate[] x509Certificates, String s, - Socket socket) throws CertificateException { } - - @Override - public void checkClientTrusted( - X509Certificate[] x509Certificates, String s, - SSLEngine sslEngine) throws CertificateException { } - - @Override - public void checkServerTrusted( - X509Certificate[] x509Certificates, String s, - SSLEngine sslEngine) throws CertificateException { } - - @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 new X509Certificate[0]; - } - }; - SslContextBuilder client_builder = SslContextBuilder.forClient() - .sslProvider(provider) - .keyManager(customKeyManager) - .trustManager(customTrustManager) - .clientAuth(ClientAuth.OPTIONAL); - SslContext client_context = client_builder.build(); - SSLEngine client_engine = client_context.newEngine(UnpooledByteBufAllocator.DEFAULT); - assertFalse(client_engine.getWantClientAuth()); - assertFalse(client_engine.getNeedClientAuth()); - client_engine.closeInbound(); - client_engine.closeOutbound(); - SslContextBuilder server_builder = SslContextBuilder.forServer(customKeyManager) - .sslProvider(provider) - .trustManager(customTrustManager) - .clientAuth(ClientAuth.REQUIRE); - SslContext server_context = server_builder.build(); - SSLEngine server_engine = server_context.newEngine(UnpooledByteBufAllocator.DEFAULT); - assertFalse(server_engine.getWantClientAuth()); - assertTrue(server_engine.getNeedClientAuth()); - server_engine.closeInbound(); - server_engine.closeOutbound(); - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/SslContextTest.java b/handler/src/test/java/io/netty/handler/ssl/SslContextTest.java deleted file mode 100644 index 0948458804..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/SslContextTest.java +++ /dev/null @@ -1,147 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.util.internal.ResourcesUtil; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; - -import java.io.File; -import java.io.IOException; -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; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLException; - -import static org.junit.jupiter.api.Assumptions.assumeTrue; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; - -public abstract class SslContextTest { - - @Test - public void testUnencryptedEmptyPassword() throws Exception { - assertThrows(IOException.class, new Executable() { - @Override - public void execute() throws Throwable { - SslContext.toPrivateKey( - ResourcesUtil.getFile(getClass(), "test2_unencrypted.pem"), ""); - } - }); - } - - @Test - public void testUnEncryptedNullPassword() throws Exception { - PrivateKey key = SslContext.toPrivateKey( - ResourcesUtil.getFile(getClass(), "test2_unencrypted.pem"), null); - assertNotNull(key); - } - - @Test - public void testEncryptedEmptyPassword() throws Exception { - PrivateKey key = SslContext.toPrivateKey( - ResourcesUtil.getFile(getClass(), "test_encrypted_empty_pass.pem"), ""); - assertNotNull(key); - } - - @Test - public void testEncryptedNullPassword() throws Exception { - assertThrows(InvalidKeySpecException.class, new Executable() { - @Override - public void execute() throws Throwable { - SslContext.toPrivateKey( - ResourcesUtil.getFile(getClass(), "test_encrypted_empty_pass.pem"), null); - } - }); - } - - @Test - public void testSslContextWithEncryptedPrivateKey() throws SSLException { - File keyFile = ResourcesUtil.getFile(getClass(), "test_encrypted.pem"); - File crtFile = ResourcesUtil.getFile(getClass(), "test.crt"); - - newSslContext(crtFile, keyFile, "12345"); - } - - @Test - public void testSslContextWithEncryptedPrivateKey2() throws SSLException { - File keyFile = ResourcesUtil.getFile(getClass(), "test2_encrypted.pem"); - File crtFile = ResourcesUtil.getFile(getClass(), "test2.crt"); - - newSslContext(crtFile, keyFile, "12345"); - } - - @Test - public void testSslContextWithUnencryptedPrivateKey() throws SSLException { - File keyFile = ResourcesUtil.getFile(getClass(), "test_unencrypted.pem"); - File crtFile = ResourcesUtil.getFile(getClass(), "test.crt"); - - newSslContext(crtFile, keyFile, null); - } - - @Test - public void testSslContextWithUnencryptedPrivateKeyEmptyPass() throws SSLException { - final File keyFile = ResourcesUtil.getFile(getClass(), "test_unencrypted.pem"); - final File crtFile = ResourcesUtil.getFile(getClass(), "test.crt"); - - assertThrows(IllegalArgumentException.class, new Executable() { - @Override - public void execute() throws Throwable { - newSslContext(crtFile, keyFile, ""); - } - }); - } - - @Test - public void testSupportedCiphers() throws KeyManagementException, NoSuchAlgorithmException, SSLException { - SSLContext jdkSslContext = SSLContext.getInstance("TLS"); - jdkSslContext.init(null, null, null); - SSLEngine sslEngine = jdkSslContext.createSSLEngine(); - - String unsupportedCipher = "TLS_DH_anon_WITH_DES_CBC_SHA"; - IllegalArgumentException exception = null; - try { - sslEngine.setEnabledCipherSuites(new String[] {unsupportedCipher}); - } catch (IllegalArgumentException e) { - exception = e; - } - assumeTrue(exception != null); - File keyFile = ResourcesUtil.getFile(getClass(), "test_unencrypted.pem"); - File crtFile = ResourcesUtil.getFile(getClass(), "test.crt"); - - SslContext sslContext = newSslContext(crtFile, keyFile, null); - assertFalse(sslContext.cipherSuites().contains(unsupportedCipher)); - } - - @Test - public void testUnsupportedParams() throws CertificateException { - assertThrows(CertificateException.class, new Executable() { - @Override - public void execute() throws Throwable { - SslContext.toX509Certificates( - new File(getClass().getResource("ec_params_unsupported.pem").getFile())); - } - }); - } - - protected abstract SslContext newSslContext(File crtFile, File keyFile, String pass) throws SSLException; -} diff --git a/handler/src/test/java/io/netty/handler/ssl/SslContextTrustManagerTest.java b/handler/src/test/java/io/netty/handler/ssl/SslContextTrustManagerTest.java deleted file mode 100644 index 509ac192b6..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/SslContextTrustManagerTest.java +++ /dev/null @@ -1,122 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import org.junit.jupiter.api.Test; - -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509TrustManager; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; -import java.util.Arrays; - -import static io.netty.handler.ssl.Java8SslTestUtils.loadCertCollection; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.fail; - -public class SslContextTrustManagerTest { - @Test - public void testUsingAllCAs() throws Exception { - runTests(new String[] { "tm_test_ca_1a.pem", "tm_test_ca_1b.pem", - "tm_test_ca_2.pem" }, new String[] { "tm_test_eec_1.pem", - "tm_test_eec_2.pem", "tm_test_eec_3.pem" }, new boolean[] { - true, true, true }); - } - - @Test - public void testUsingAllCAsWithDuplicates() throws Exception { - runTests(new String[] { "tm_test_ca_1a.pem", "tm_test_ca_1b.pem", - "tm_test_ca_2.pem", "tm_test_ca_2.pem" }, - new String[] { "tm_test_eec_1.pem", "tm_test_eec_2.pem", - "tm_test_eec_3.pem" }, - new boolean[] { true, true, true }); - } - - @Test - public void testUsingCAsOneAandB() throws Exception { - runTests(new String[] { "tm_test_ca_1a.pem", "tm_test_ca_1b.pem", }, - new String[] { "tm_test_eec_1.pem", "tm_test_eec_2.pem", - "tm_test_eec_3.pem" }, new boolean[] { true, true, - false }); - } - - @Test - public void testUsingCAsOneAandTwo() throws Exception { - runTests(new String[] { "tm_test_ca_1a.pem", "tm_test_ca_2.pem" }, - new String[] { "tm_test_eec_1.pem", "tm_test_eec_2.pem", - "tm_test_eec_3.pem" }, new boolean[] { true, false, - true }); - } - - /** - * - * @param caResources - * an array of paths to CA Certificates in PEM format to load - * from the classpath (relative to this class). - * @param eecResources - * an array of paths to Server Certificates in PEM format in to - * load from the classpath (relative to this class). - * @param expectations - * an array of expecting results for each EEC Server Certificate - * (the array is expected to have the same length the previous - * argument, and be arrange in matching order: true means - * expected to be valid, false otherwise. - */ - private static void runTests(String[] caResources, String[] eecResources, - boolean[] expectations) throws Exception { - X509TrustManager tm = getTrustManager(caResources); - - X509Certificate[] eecCerts = loadCertCollection(eecResources); - - for (int i = 0; i < eecResources.length; i++) { - X509Certificate eecCert = eecCerts[i]; - assertNotNull(eecCert, "Cannot use cert " + eecResources[i]); - try { - tm.checkServerTrusted(new X509Certificate[] { eecCert }, "RSA"); - if (!expectations[i]) { - fail(String.format( - "Certificate %s was expected not to be valid when using CAs %s, but its " - + "verification passed.", eecResources[i], - Arrays.asList(caResources))); - } - } catch (CertificateException e) { - if (expectations[i]) { - fail(String.format( - "Certificate %s was expected to be valid when using CAs %s, but its " - + "verification failed.", eecResources[i], - Arrays.asList(caResources))); - } - } - } - } - - private static X509TrustManager getTrustManager(String[] resourceNames) - throws Exception { - X509Certificate[] certCollection = loadCertCollection(resourceNames); - TrustManagerFactory tmf = SslContext.buildTrustManagerFactory( - certCollection, null, null); - - for (TrustManager tm : tmf.getTrustManagers()) { - if (tm instanceof X509TrustManager) { - return (X509TrustManager) tm; - } - } - - throw new Exception( - "Unable to find any X509TrustManager from this factory."); - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/SslErrorTest.java b/handler/src/test/java/io/netty/handler/ssl/SslErrorTest.java deleted file mode 100644 index e8cee1bfcf..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/SslErrorTest.java +++ /dev/null @@ -1,312 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.bootstrap.Bootstrap; -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; -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 org.junit.jupiter.api.Timeout; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -import javax.net.ssl.ManagerFactoryParameters; -import javax.net.ssl.SSLException; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; -import javax.security.auth.x500.X500Principal; -import java.io.File; -import java.security.KeyStore; -import java.security.cert.CRLReason; -import java.security.cert.CertPathValidatorException; -import java.security.cert.CertificateException; -import java.security.cert.CertificateExpiredException; -import java.security.cert.CertificateNotYetValidException; -import java.security.cert.CertificateRevokedException; -import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.Locale; -import java.util.concurrent.TimeUnit; - -public class SslErrorTest { - - static Collection data() { - List serverProviders = new ArrayList<>(2); - List clientProviders = new ArrayList<>(3); - - if (OpenSsl.isAvailable()) { - serverProviders.add(SslProvider.OPENSSL); - serverProviders.add(SslProvider.OPENSSL_REFCNT); - clientProviders.add(SslProvider.OPENSSL); - clientProviders.add(SslProvider.OPENSSL_REFCNT); - } - // We not test with SslProvider.JDK on the server side as the JDK implementation currently just send the same - // alert all the time, sigh..... - clientProviders.add(SslProvider.JDK); - - List exceptions = new ArrayList<>(6); - exceptions.add(new CertificateExpiredException()); - exceptions.add(new CertificateNotYetValidException()); - exceptions.add(new CertificateRevokedException( - new Date(), CRLReason.AA_COMPROMISE, new X500Principal(""), - Collections.emptyMap())); - - // Also use wrapped exceptions as this is what the JDK implementation of X509TrustManagerFactory is doing. - exceptions.add(newCertificateException(CertPathValidatorException.BasicReason.EXPIRED)); - exceptions.add(newCertificateException(CertPathValidatorException.BasicReason.NOT_YET_VALID)); - exceptions.add(newCertificateException(CertPathValidatorException.BasicReason.REVOKED)); - - List params = new ArrayList<>(); - for (SslProvider serverProvider: serverProviders) { - for (SslProvider clientProvider: clientProviders) { - for (CertificateException exception: exceptions) { - params.add(new Object[] { serverProvider, clientProvider, exception, true }); - params.add(new Object[] { serverProvider, clientProvider, exception, false }); - } - } - } - return params; - } - - private static CertificateException newCertificateException(CertPathValidatorException.Reason reason) { - return new TestCertificateException( - new CertPathValidatorException("x", null, null, -1, reason)); - } - - @ParameterizedTest( - name = "{index}: serverProvider = {0}, clientProvider = {1}, exception = {2}, serverProduceError = {3}") - @MethodSource("data") - @Timeout(value = 30000, unit = TimeUnit.MILLISECONDS) - public void testCorrectAlert(SslProvider serverProvider, final SslProvider clientProvider, - final CertificateException exception, final boolean serverProduceError) - throws Exception { - // As this only works correctly at the moment when OpenSslEngine is used on the server-side there is - // no need to run it if there is no openssl is available at all. - OpenSsl.ensureAvailability(); - - SelfSignedCertificate ssc = new SelfSignedCertificate(); - - SslContextBuilder sslServerCtxBuilder = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(serverProvider) - .clientAuth(ClientAuth.REQUIRE); - SslContextBuilder sslClientCtxBuilder = SslContextBuilder.forClient() - .keyManager(new File(getClass().getResource("test.crt").getFile()), - new File(getClass().getResource("test_unencrypted.pem").getFile())) - .sslProvider(clientProvider); - - if (serverProduceError) { - sslServerCtxBuilder.trustManager(new ExceptionTrustManagerFactory(exception)); - sslClientCtxBuilder.trustManager(InsecureTrustManagerFactory.INSTANCE); - } else { - sslServerCtxBuilder.trustManager(InsecureTrustManagerFactory.INSTANCE); - sslClientCtxBuilder.trustManager(new ExceptionTrustManagerFactory(exception)); - } - - final SslContext sslServerCtx = sslServerCtxBuilder.build(); - final SslContext sslClientCtx = sslClientCtxBuilder.build(); - - Channel serverChannel = null; - Channel clientChannel = null; - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - final Promise promise = group.next().newPromise(); - try { - serverChannel = new ServerBootstrap().group(group) - .channel(NioServerSocketChannel.class) - .handler(new LoggingHandler(LogLevel.INFO)) - .childHandler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) { - ch.pipeline().addLast(sslServerCtx.newHandler(ch.alloc())); - if (!serverProduceError) { - ch.pipeline().addLast(new AlertValidationHandler(clientProvider, serverProduceError, - exception, promise)); - } - ch.pipeline().addLast(new ChannelHandler() { - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - ctx.close(); - } - }); - } - }).bind(0).get(); - - clientChannel = new Bootstrap().group(group) - .channel(NioSocketChannel.class) - .handler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) { - ch.pipeline().addLast(sslClientCtx.newHandler(ch.alloc())); - - if (serverProduceError) { - ch.pipeline().addLast(new AlertValidationHandler(clientProvider, serverProduceError, - exception, promise)); - } - ch.pipeline().addLast(new ChannelHandler() { - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - ctx.close(); - } - }); - } - }).connect(serverChannel.localAddress()).get(); - // Block until we received the correct exception - promise.asFuture().syncUninterruptibly(); - } finally { - if (clientChannel != null) { - clientChannel.close().syncUninterruptibly(); - } - if (serverChannel != null) { - serverChannel.close().syncUninterruptibly(); - } - group.shutdownGracefully(); - - ReferenceCountUtil.release(sslServerCtx); - ReferenceCountUtil.release(sslClientCtx); - } - } - - private static final class ExceptionTrustManagerFactory extends SimpleTrustManagerFactory { - private final CertificateException exception; - - ExceptionTrustManagerFactory(CertificateException exception) { - this.exception = exception; - } - - @Override - protected void engineInit(KeyStore keyStore) { } - @Override - protected void engineInit(ManagerFactoryParameters managerFactoryParameters) { } - - @Override - protected TrustManager[] engineGetTrustManagers() { - return new TrustManager[] { new X509TrustManager() { - - @Override - public void checkClientTrusted(X509Certificate[] x509Certificates, String s) - throws CertificateException { - throw exception; - } - - @Override - public void checkServerTrusted(X509Certificate[] x509Certificates, String s) - throws CertificateException { - throw exception; - } - - @Override - public X509Certificate[] getAcceptedIssuers() { - return EmptyArrays.EMPTY_X509_CERTIFICATES; - } - } }; - } - } - - private static final class AlertValidationHandler implements ChannelHandler { - private final SslProvider clientProvider; - private final boolean serverProduceError; - private final CertificateException exception; - private final Promise promise; - - AlertValidationHandler(SslProvider clientProvider, boolean serverProduceError, - CertificateException exception, Promise promise) { - this.clientProvider = clientProvider; - this.serverProduceError = serverProduceError; - this.exception = exception; - this.promise = promise; - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - // Unwrap as its wrapped by a DecoderException - Throwable unwrappedCause = cause.getCause(); - if (unwrappedCause instanceof SSLException) { - if (exception instanceof TestCertificateException) { - CertPathValidatorException.Reason reason = - ((CertPathValidatorException) exception.getCause()).getReason(); - if (reason == CertPathValidatorException.BasicReason.EXPIRED) { - verifyException(clientProvider, serverProduceError, unwrappedCause, promise, "expired"); - } else if (reason == CertPathValidatorException.BasicReason.NOT_YET_VALID) { - // BoringSSL may use "expired" in this case while others use "bad" - verifyException(clientProvider, serverProduceError, unwrappedCause, promise, "expired", "bad"); - } else if (reason == CertPathValidatorException.BasicReason.REVOKED) { - verifyException(clientProvider, serverProduceError, unwrappedCause, promise, "revoked"); - } - } else if (exception instanceof CertificateExpiredException) { - verifyException(clientProvider, serverProduceError, unwrappedCause, promise, "expired"); - } else if (exception instanceof CertificateNotYetValidException) { - // BoringSSL may use "expired" in this case while others use "bad" - verifyException(clientProvider, serverProduceError, unwrappedCause, promise, "expired", "bad"); - } else if (exception instanceof CertificateRevokedException) { - verifyException(clientProvider, serverProduceError, unwrappedCause, promise, "revoked"); - } - } - } - } - - // Its a bit hacky to verify against the message that is part of the exception but there is no other way - // at the moment as there are no different exceptions for the different alerts. - private static void verifyException(SslProvider clientProvider, boolean serverProduceError, - Throwable cause, Promise promise, String... messageParts) { - String message = cause.getMessage(); - // When the error is produced on the client side and the client side uses JDK as provider it will always - // use "certificate unknown". - if (!serverProduceError && clientProvider == SslProvider.JDK && - message.toLowerCase(Locale.UK).contains("unknown")) { - promise.setSuccess(null); - return; - } - - for (String m: messageParts) { - if (message.toLowerCase(Locale.UK).contains(m.toLowerCase(Locale.UK))) { - promise.setSuccess(null); - return; - } - } - Throwable error = new AssertionError("message not contains any of '" - + Arrays.toString(messageParts) + "': " + message, cause); - promise.setFailure(error); - } - - private static final class TestCertificateException extends CertificateException { - private static final long serialVersionUID = -5816338303868751410L; - - TestCertificateException(Throwable cause) { - super(cause); - } - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/SslHandlerTest.java b/handler/src/test/java/io/netty/handler/ssl/SslHandlerTest.java deleted file mode 100644 index b86da5f617..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/SslHandlerTest.java +++ /dev/null @@ -1,1648 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.ssl; - -import io.netty.bootstrap.Bootstrap; -import io.netty.bootstrap.ServerBootstrap; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.Unpooled; -import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.DefaultChannelId; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.channel.local.LocalAddress; -import io.netty.channel.local.LocalChannel; -import io.netty.channel.local.LocalHandler; -import io.netty.channel.local.LocalServerChannel; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.codec.ByteToMessageDecoder; -import io.netty.handler.codec.CodecException; -import io.netty.handler.codec.DecoderException; -import io.netty.handler.codec.UnsupportedMessageTypeException; -import io.netty.handler.ssl.util.InsecureTrustManagerFactory; -import io.netty.handler.ssl.util.SelfSignedCertificate; -import io.netty.util.AbstractReferenceCounted; -import io.netty.util.IllegalReferenceCountException; -import io.netty.util.ReferenceCountUtil; -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 io.netty.util.internal.EmptyArrays; -import org.hamcrest.CoreMatchers; -import org.hamcrest.Matchers; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; -import org.junit.jupiter.api.function.Executable; - -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLException; -import javax.net.ssl.SSLProtocolException; -import javax.net.ssl.X509ExtendedTrustManager; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.nio.channels.ClosedChannelException; -import java.security.NoSuchAlgorithmException; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; -import java.util.Collections; -import java.util.Queue; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.CompletionException; -import java.util.concurrent.ConcurrentLinkedQueue; -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.ThreadLocalRandom; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; - -import static io.netty.buffer.Unpooled.wrappedBuffer; -import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.not; -import static org.hamcrest.CoreMatchers.nullValue; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assumptions.assumeFalse; -import static org.junit.jupiter.api.Assumptions.assumeTrue; - -public class SslHandlerTest { - - @Test - @Timeout(value = 5000, unit = TimeUnit.MILLISECONDS) - public void testNonApplicationDataFailureFailsQueuedWrites() throws Exception { - final CountDownLatch writeLatch = new CountDownLatch(1); - final Queue> writesToFail = new ConcurrentLinkedQueue<>(); - SSLEngine engine = newClientModeSSLEngine(); - SslHandler handler = new SslHandler(engine) { - @Override - public Future write(final ChannelHandlerContext ctx, Object msg) { - Future future = super.write(ctx, msg); - writeLatch.countDown(); - return future; - } - }; - EmbeddedChannel ch = new EmbeddedChannel(new ChannelHandler() { - @Override - public Future write(ChannelHandlerContext ctx, Object msg) { - try { - if (msg instanceof ByteBuf) { - if (((ByteBuf) msg).isReadable()) { - Promise promise = ctx.newPromise(); - writesToFail.add(promise); - return promise.asFuture(); - } - } - return ctx.newSucceededFuture(); - } finally { - ReferenceCountUtil.release(msg); - } - } - }, handler); - - try { - final CountDownLatch writeCauseLatch = new CountDownLatch(1); - final AtomicReference failureRef = new AtomicReference(); - ch.write(wrappedBuffer(new byte[]{1})).addListener(future -> { - failureRef.compareAndSet(null, future.cause()); - writeCauseLatch.countDown(); - }); - writeLatch.await(); - - // Simulate failing the SslHandler non-application writes after there are applications writes queued. - Promise promiseToFail; - while ((promiseToFail = writesToFail.poll()) != null) { - promiseToFail.setFailure(new RuntimeException("fake exception")); - } - - writeCauseLatch.await(); - Throwable writeCause = failureRef.get(); - assertNotNull(writeCause); - assertThat(writeCause, is(CoreMatchers.instanceOf(SSLException.class))); - Throwable cause = handler.handshakeFuture().cause(); - assertNotNull(cause); - assertThat(cause, is(CoreMatchers.instanceOf(SSLException.class))); - } finally { - assertFalse(ch.finishAndReleaseAll()); - } - } - - @Test - public void testNoSslHandshakeEventWhenNoHandshake() throws Exception { - final AtomicBoolean inActive = new AtomicBoolean(false); - - SSLEngine engine = SSLContext.getDefault().createSSLEngine(); - EmbeddedChannel ch = new EmbeddedChannel( - DefaultChannelId.newInstance(), false, false, new ChannelHandler() { - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - // Not forward the event to the SslHandler but just close the Channel. - ctx.close(); - } - }, new SslHandler(engine) { - @Override - public void handlerAdded0(ChannelHandlerContext ctx) throws Exception { - // We want to override what Channel.isActive() will return as otherwise it will - // return true and so trigger an handshake. - inActive.set(true); - super.handlerAdded0(ctx); - inActive.set(false); - } - }, new ChannelHandler() { - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (evt instanceof SslHandshakeCompletionEvent) { - throw (Exception) ((SslHandshakeCompletionEvent) evt).cause(); - } - } - }) { - @Override - public boolean isActive() { - return !inActive.get() && super.isActive(); - } - }; - - ch.register(); - assertFalse(ch.finishAndReleaseAll()); - } - - @Test - @Timeout(value = 3000, unit = TimeUnit.MILLISECONDS) - public void testClientHandshakeTimeout() throws Exception { - assertThrows(SslHandshakeTimeoutException.class, new Executable() { - @Override - public void execute() throws Throwable { - testHandshakeTimeout(true); - } - }); - } - - @Test - @Timeout(value = 3000, unit = TimeUnit.MILLISECONDS) - public void testServerHandshakeTimeout() throws Exception { - assertThrows(SslHandshakeTimeoutException.class, new Executable() { - @Override - public void execute() throws Throwable { - 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() - // - https://mail.openjdk.java.net/pipermail/security-dev/2018-July/017715.html - engine.setUseClientMode(false); - return engine; - } - - private static SSLEngine newClientModeSSLEngine() 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() - // - https://mail.openjdk.java.net/pipermail/security-dev/2018-July/017715.html - engine.setUseClientMode(true); - return engine; - } - - private static void testHandshakeTimeout(boolean client) throws Throwable { - SSLEngine engine = SSLContext.getDefault().createSSLEngine(); - engine.setUseClientMode(client); - SslHandler handler = new SslHandler(engine); - handler.setHandshakeTimeoutMillis(1); - - EmbeddedChannel ch = new EmbeddedChannel(handler); - try { - while (!handler.handshakeFuture().isDone()) { - Thread.sleep(10); - // We need to run all pending tasks as the handshake timeout is scheduled on the EventLoop. - ch.runPendingTasks(); - } - - handler.handshakeFuture().syncUninterruptibly(); - } catch (CompletionException e) { - throw e.getCause(); - } finally { - ch.finishAndReleaseAll(); - } - } - - @Test - @Timeout(value = 5000, unit = TimeUnit.MILLISECONDS) - public void testHandshakeAndClosePromiseFailedOnRemoval() throws Exception { - SSLEngine engine = SSLContext.getDefault().createSSLEngine(); - engine.setUseClientMode(true); - SslHandler handler = new SslHandler(engine); - final AtomicReference handshakeRef = new AtomicReference<>(); - final AtomicReference closeRef = new AtomicReference<>(); - EmbeddedChannel ch = new EmbeddedChannel(handler, new ChannelHandler() { - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { - if (evt instanceof SslHandshakeCompletionEvent) { - handshakeRef.set(((SslHandshakeCompletionEvent) evt).cause()); - } else if (evt instanceof SslCloseCompletionEvent) { - closeRef.set(((SslCloseCompletionEvent) evt).cause()); - } - } - }); - assertFalse(handler.handshakeFuture().isDone()); - assertFalse(handler.sslCloseFuture().isDone()); - - ch.pipeline().remove(handler); - - try { - while (!handler.handshakeFuture().isDone() || handshakeRef.get() == null - || !handler.sslCloseFuture().isDone() || closeRef.get() == null) { - Thread.sleep(10); - // Continue running all pending tasks until we notified for everything. - ch.runPendingTasks(); - } - - assertSame(handler.handshakeFuture().cause(), handshakeRef.get()); - assertSame(handler.sslCloseFuture().cause(), closeRef.get()); - } finally { - ch.finishAndReleaseAll(); - } - } - - @Test - public void testTruncatedPacket() throws Exception { - SSLEngine engine = newServerModeSSLEngine(); - final EmbeddedChannel ch = new EmbeddedChannel(new SslHandler(engine)); - - // Push the first part of a 5-byte handshake message. - ch.writeInbound(wrappedBuffer(new byte[]{22, 3, 1, 0, 5})); - - // Should decode nothing yet. - assertThat(ch.readInbound(), is(nullValue())); - - DecoderException e = assertThrows(DecoderException.class, new Executable() { - @Override - public void execute() throws Throwable { - // Push the second part of the 5-byte handshake message. - ch.writeInbound(wrappedBuffer(new byte[]{2, 0, 0, 1, 0})); - } - }); - // Be sure we cleanup the channel and release any pending messages that may have been generated because - // of an alert. - // See https://github.com/netty/netty/issues/6057. - ch.finishAndReleaseAll(); - - // The pushed message is invalid, so it should raise an exception if it decoded the message correctly. - assertThat(e.getCause(), is(instanceOf(SSLProtocolException.class))); - } - - @Test - public void testNonByteBufWriteIsReleased() throws Exception { - SSLEngine engine = newServerModeSSLEngine(); - final EmbeddedChannel ch = new EmbeddedChannel(new SslHandler(engine)); - - final AbstractReferenceCounted referenceCounted = new AbstractReferenceCounted() { - @Override - public ReferenceCounted touch(Object hint) { - return this; - } - - @Override - protected void deallocate() { - } - }; - - ExecutionException e = assertThrows(ExecutionException.class, new Executable() { - @Override - public void execute() throws Throwable { - ch.write(referenceCounted).get(); - } - }); - assertThat(e.getCause(), is(instanceOf(UnsupportedMessageTypeException.class))); - assertEquals(0, referenceCounted.refCnt()); - assertTrue(ch.finishAndReleaseAll()); - } - - @Test - public void testNonByteBufNotPassThrough() throws Exception { - SSLEngine engine = newServerModeSSLEngine(); - final EmbeddedChannel ch = new EmbeddedChannel(new SslHandler(engine)); - - assertThrows(UnsupportedMessageTypeException.class, new Executable() { - @Override - public void execute() throws Throwable { - ch.writeOutbound(new Object()); - } - }); - ch.finishAndReleaseAll(); - } - - @Test - public void testIncompleteWriteDoesNotCompletePromisePrematurely() throws NoSuchAlgorithmException { - SSLEngine engine = newServerModeSSLEngine(); - EmbeddedChannel ch = new EmbeddedChannel(new SslHandler(engine)); - - ByteBuf buf = Unpooled.buffer(10).writeZero(10); - Future future = ch.writeAndFlush(buf); - assertFalse(future.isDone()); - assertTrue(ch.finishAndReleaseAll()); - assertTrue(future.isDone()); - assertThat(future.cause(), is(instanceOf(SSLException.class))); - } - - @Test - public void testReleaseSslEngine() throws Exception { - OpenSsl.ensureAvailability(); - - SelfSignedCertificate cert = new SelfSignedCertificate(); - try { - SslContext sslContext = SslContextBuilder.forServer(cert.certificate(), cert.privateKey()) - .sslProvider(SslProvider.OPENSSL) - .build(); - try { - assertEquals(1, ((ReferenceCounted) sslContext).refCnt()); - SSLEngine sslEngine = sslContext.newEngine(ByteBufAllocator.DEFAULT); - EmbeddedChannel ch = new EmbeddedChannel(new SslHandler(sslEngine)); - - assertEquals(2, ((ReferenceCounted) sslContext).refCnt()); - assertEquals(1, ((ReferenceCounted) sslEngine).refCnt()); - - assertTrue(ch.finishAndReleaseAll()); - ch.close().syncUninterruptibly(); - - assertEquals(1, ((ReferenceCounted) sslContext).refCnt()); - assertEquals(0, ((ReferenceCounted) sslEngine).refCnt()); - } finally { - ReferenceCountUtil.release(sslContext); - } - } finally { - cert.delete(); - } - } - - private static final class TlsReadTest implements ChannelHandler { - private volatile boolean readIssued; - - @Override - public void read(ChannelHandlerContext ctx) { - readIssued = true; - ctx.read(); - } - - public void test(final boolean dropChannelActive) throws Exception { - SSLEngine engine = SSLContext.getDefault().createSSLEngine(); - engine.setUseClientMode(true); - - EmbeddedChannel ch = new EmbeddedChannel(false, false, - this, - new SslHandler(engine), - new ChannelHandler() { - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - if (!dropChannelActive) { - ctx.fireChannelActive(); - } - } - } - ); - ch.config().setAutoRead(false); - assertFalse(ch.config().isAutoRead()); - - ch.register(); - - assertTrue(readIssued); - readIssued = false; - - assertTrue(ch.writeOutbound(Unpooled.EMPTY_BUFFER)); - assertTrue(readIssued); - assertTrue(ch.finishAndReleaseAll()); - } - } - - @Test - public void testIssueReadAfterActiveWriteFlush() throws Exception { - // the handshake is initiated by channelActive - new TlsReadTest().test(false); - } - - @Test - public void testIssueReadAfterWriteFlushActive() throws Exception { - // the handshake is initiated by flush - new TlsReadTest().test(true); - } - - @Test - @Timeout(value = 30000, unit = TimeUnit.MILLISECONDS) - public void testRemoval() throws Exception { - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - Channel sc = null; - Channel cc = null; - try { - final Promise clientPromise = group.next().newPromise(); - Bootstrap bootstrap = new Bootstrap() - .group(group) - .channel(NioSocketChannel.class) - .handler(newHandler(SslContextBuilder.forClient().trustManager( - InsecureTrustManagerFactory.INSTANCE).build(), clientPromise)); - - SelfSignedCertificate ssc = new SelfSignedCertificate(); - final Promise serverPromise = group.next().newPromise(); - ServerBootstrap serverBootstrap = new ServerBootstrap() - .group(group, group) - .channel(NioServerSocketChannel.class) - .childHandler(newHandler(SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build(), - serverPromise)); - sc = serverBootstrap.bind(new InetSocketAddress(0)).get(); - cc = bootstrap.connect(sc.localAddress()).get(); - - serverPromise.asFuture().syncUninterruptibly(); - clientPromise.asFuture().syncUninterruptibly(); - } finally { - if (cc != null) { - cc.close().syncUninterruptibly(); - } - if (sc != null) { - sc.close().syncUninterruptibly(); - } - group.shutdownGracefully(); - } - } - - private static ChannelHandler newHandler(final SslContext sslCtx, final Promise promise) { - return new ChannelInitializer() { - @Override - protected void initChannel(final Channel ch) { - final SslHandler sslHandler = sslCtx.newHandler(ch.alloc()); - sslHandler.setHandshakeTimeoutMillis(1000); - ch.pipeline().addFirst(sslHandler); - sslHandler.handshakeFuture().addListener(future -> { - ch.executor().execute(() -> { - ch.pipeline().remove(sslHandler); - - // Schedule the close so removal has time to propagate exception if any. - ch.executor().execute(ch::close); - }); - }); - - ch.pipeline().addLast(new ChannelHandler() { - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - if (cause instanceof CodecException) { - cause = cause.getCause(); - } - if (cause instanceof IllegalReferenceCountException) { - promise.setFailure(cause); - } - cause.printStackTrace(); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) { - promise.trySuccess(null); - } - }); - } - }; - } - - @Test - public void testCloseFutureNotified() throws Exception { - SSLEngine engine = newServerModeSSLEngine(); - SslHandler handler = new SslHandler(engine); - EmbeddedChannel ch = new EmbeddedChannel(handler); - - ch.close(); - - // When the channel is closed the SslHandler will write an empty buffer to the channel. - ByteBuf buf = ch.readOutbound(); - assertFalse(buf.isReadable()); - buf.release(); - - assertFalse(ch.finishAndReleaseAll()); - - assertTrue(handler.handshakeFuture().cause() instanceof ClosedChannelException); - assertTrue(handler.sslCloseFuture().cause() instanceof ClosedChannelException); - } - - @Test - @Timeout(value = 5000, unit = TimeUnit.MILLISECONDS) - public void testEventsFired() throws Exception { - SSLEngine engine = newServerModeSSLEngine(); - final BlockingQueue events = new LinkedBlockingQueue<>(); - EmbeddedChannel channel = new EmbeddedChannel(new SslHandler(engine), new ChannelHandler() { - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (evt instanceof SslCompletionEvent) { - events.add((SslCompletionEvent) evt); - } - } - }); - assertTrue(events.isEmpty()); - assertTrue(channel.finishAndReleaseAll()); - - SslCompletionEvent evt = events.take(); - assertTrue(evt instanceof SslHandshakeCompletionEvent); - assertTrue(evt.cause() instanceof ClosedChannelException); - - evt = events.take(); - assertTrue(evt instanceof SslCloseCompletionEvent); - assertTrue(evt.cause() instanceof ClosedChannelException); - assertTrue(events.isEmpty()); - } - - @Test - @Timeout(value = 5000, unit = TimeUnit.MILLISECONDS) - public void testHandshakeFailBeforeWritePromise() throws Exception { - SelfSignedCertificate ssc = new SelfSignedCertificate(); - final SslContext sslServerCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build(); - final CountDownLatch latch = new CountDownLatch(2); - final CountDownLatch latch2 = new CountDownLatch(2); - final BlockingQueue events = new LinkedBlockingQueue<>(); - Channel serverChannel = null; - Channel clientChannel = null; - EventLoopGroup group = new MultithreadEventLoopGroup(LocalHandler.newFactory()); - try { - ServerBootstrap sb = new ServerBootstrap(); - sb.group(group) - .channel(LocalServerChannel.class) - .childHandler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) { - ch.pipeline().addLast(sslServerCtx.newHandler(ch.alloc())); - ch.pipeline().addLast(new ChannelHandler() { - @Override - public void channelActive(ChannelHandlerContext ctx) { - ByteBuf buf = ctx.alloc().buffer(10); - buf.writeZero(buf.capacity()); - ctx.writeAndFlush(buf).addListener(future -> { - events.add(future); - latch.countDown(); - }); - } - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { - if (evt instanceof SslCompletionEvent) { - events.add(evt); - latch.countDown(); - latch2.countDown(); - } - } - }); - } - }); - - Bootstrap cb = new Bootstrap(); - cb.group(group) - .channel(LocalChannel.class) - .handler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) { - ch.pipeline().addFirst(new ChannelHandler() { - @Override - public void channelActive(ChannelHandlerContext ctx) { - ByteBuf buf = ctx.alloc().buffer(1000); - buf.writeZero(buf.capacity()); - ctx.writeAndFlush(buf); - } - }); - } - }); - - serverChannel = sb.bind(new LocalAddress("SslHandlerTest")).get(); - clientChannel = cb.connect(serverChannel.localAddress()).get(); - latch.await(); - - SslCompletionEvent evt = (SslCompletionEvent) events.take(); - assertTrue(evt instanceof SslHandshakeCompletionEvent); - assertThat(evt.cause(), is(instanceOf(SSLException.class))); - - evt = (SslCompletionEvent) events.take(); - assertTrue(evt instanceof SslCloseCompletionEvent); - assertThat(evt.cause(), is(instanceOf(ClosedChannelException.class))); - - Future future = (Future) events.take(); - assertThat(future.cause(), is(instanceOf(SSLException.class))); - - serverChannel.close().sync(); - serverChannel = null; - clientChannel.close().sync(); - clientChannel = null; - - latch2.await(); - assertTrue(events.isEmpty()); - } finally { - if (serverChannel != null) { - serverChannel.close(); - } - if (clientChannel != null) { - clientChannel.close(); - } - group.shutdownGracefully(); - } - } - - @Test - public void writingReadOnlyBufferDoesNotBreakAggregation() throws Exception { - SelfSignedCertificate ssc = new SelfSignedCertificate(); - - final SslContext sslServerCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build(); - - final SslContext sslClientCtx = SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE).build(); - - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - Channel sc = null; - Channel cc = null; - final CountDownLatch serverReceiveLatch = new CountDownLatch(1); - try { - final int expectedBytes = 11; - sc = new ServerBootstrap() - .group(group) - .channel(NioServerSocketChannel.class) - .childHandler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - ch.pipeline().addLast(sslServerCtx.newHandler(ch.alloc())); - ch.pipeline().addLast(new SimpleChannelInboundHandler() { - private int readBytes; - @Override - protected void messageReceived(ChannelHandlerContext ctx, - ByteBuf msg) throws Exception { - readBytes += msg.readableBytes(); - if (readBytes >= expectedBytes) { - serverReceiveLatch.countDown(); - } - } - }); - } - }).bind(new InetSocketAddress(0)).get(); - - cc = new Bootstrap() - .group(group) - .channel(NioSocketChannel.class) - .handler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - ch.pipeline().addLast(sslClientCtx.newHandler(ch.alloc())); - } - }).connect(sc.localAddress()).get(); - - // We first write a ReadOnlyBuffer because SslHandler will attempt to take the first buffer and append to it - // until there is no room, or the aggregation size threshold is exceeded. We want to verify that we don't - // throw when a ReadOnlyBuffer is used and just verify that we don't aggregate in this case. - ByteBuf firstBuffer = Unpooled.buffer(10); - firstBuffer.writeByte(0); - firstBuffer = firstBuffer.asReadOnly(); - ByteBuf secondBuffer = Unpooled.buffer(10); - secondBuffer.writeZero(secondBuffer.capacity()); - cc.write(firstBuffer); - cc.writeAndFlush(secondBuffer).syncUninterruptibly(); - serverReceiveLatch.countDown(); - } finally { - if (cc != null) { - cc.close().syncUninterruptibly(); - } - if (sc != null) { - sc.close().syncUninterruptibly(); - } - group.shutdownGracefully(); - - ReferenceCountUtil.release(sslServerCtx); - ReferenceCountUtil.release(sslClientCtx); - } - } - - @Test - @Timeout(value = 10000, unit = TimeUnit.MILLISECONDS) - public void testCloseOnHandshakeFailure() throws Exception { - final SelfSignedCertificate ssc = new SelfSignedCertificate(); - - final SslContext sslServerCtx = SslContextBuilder.forServer(ssc.key(), ssc.cert()).build(); - final SslContext sslClientCtx = SslContextBuilder.forClient() - .trustManager(new SelfSignedCertificate().cert()) - .build(); - - EventLoopGroup group = new MultithreadEventLoopGroup(1, LocalHandler.newFactory()); - Channel sc = null; - Channel cc = null; - try { - LocalAddress address = new LocalAddress(getClass().getSimpleName() + ".testCloseOnHandshakeFailure"); - ServerBootstrap sb = new ServerBootstrap() - .group(group) - .channel(LocalServerChannel.class) - .childHandler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) { - ch.pipeline().addLast(sslServerCtx.newHandler(ch.alloc())); - } - }); - sc = sb.bind(address).get(); - - final AtomicReference sslHandlerRef = new AtomicReference<>(); - Bootstrap b = new Bootstrap() - .group(group) - .channel(LocalChannel.class) - .handler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) { - 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()).get(); - SslHandler handler = sslHandlerRef.get(); - handler.handshakeFuture().awaitUninterruptibly(); - assertFalse(handler.handshakeFuture().isSuccess()); - - cc.closeFuture().syncUninterruptibly(); - } finally { - if (cc != null) { - cc.close().syncUninterruptibly(); - } - if (sc != null) { - sc.close().syncUninterruptibly(); - } - group.shutdownGracefully(); - - ReferenceCountUtil.release(sslServerCtx); - ReferenceCountUtil.release(sslClientCtx); - } - } - - @Test - public void testOutboundClosedAfterChannelInactive() throws Exception { - SslContext context = SslContextBuilder.forClient().build(); - SSLEngine engine = context.newEngine(UnpooledByteBufAllocator.DEFAULT); - - EmbeddedChannel channel = new EmbeddedChannel(); - assertFalse(channel.finish()); - channel.pipeline().addLast(new SslHandler(engine)); - assertFalse(engine.isOutboundDone()); - channel.close().syncUninterruptibly(); - - assertTrue(engine.isOutboundDone()); - } - - @Test - @Timeout(value = 10000, unit = TimeUnit.MILLISECONDS) - public void testHandshakeFailedByWriteBeforeChannelActive() throws Exception { - final SslContext sslClientCtx = SslContextBuilder.forClient() - .protocols(SslProtocols.SSL_v3) - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(SslProvider.JDK).build(); - - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - 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 ChannelHandler() { }) - .bind(new InetSocketAddress(0)).get(); - - 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 ChannelHandler() { - @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(future -> { - // Write something to trigger the handshake before fireChannelActive is called. - future.get().writeAndFlush(wrappedBuffer(new byte [] { 1, 2, 3, 4 })); - }).get(); - - // 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(), instanceOf(SSLException.class)); - } finally { - if (cc != null) { - cc.close().syncUninterruptibly(); - } - if (sc != null) { - sc.close().syncUninterruptibly(); - } - group.shutdownGracefully(); - - ReferenceCountUtil.release(sslClientCtx); - } - } - - @Test - @Timeout(value = 10000, unit = TimeUnit.MILLISECONDS) - public void testHandshakeTimeoutFlushStartsHandshake() throws Exception { - testHandshakeTimeout0(false); - } - - @Test - @Timeout(value = 10000, unit = TimeUnit.MILLISECONDS) - 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 MultithreadEventLoopGroup(NioHandler.newFactory()); - 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 ChannelHandler() { }) - .bind(new InetSocketAddress(0)).get(); - - Future 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 ChannelHandler() { - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - ctx.writeAndFlush(wrappedBuffer(new byte[] { 1, 2, 3, 4 })); - } - }); - } - } - }).connect(sc.localAddress()); - if (!startTls) { - future.addListener(future1 -> { - // Write something to trigger the handshake before fireChannelActive is called. - future1.getNow().writeAndFlush(wrappedBuffer(new byte [] { 1, 2, 3, 4 })); - }); - } - cc = future.get(); - - Throwable cause = sslHandler.handshakeFuture().await().cause(); - assertThat(cause, 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); - } - } - - @Test - public void testHandshakeWithExecutorThatExecuteDirecty() throws Exception { - testHandshakeWithExecutor(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 static 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 MultithreadEventLoopGroup(NioHandler.newFactory()); - 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)).get(); - - Future 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.get(); - - 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 static 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 MultithreadEventLoopGroup(NioHandler.newFactory()); - Channel sc = null; - Channel cc = null; - final SslHandler clientSslHandler = sslClientCtx.newHandler(UnpooledByteBufAllocator.DEFAULT, 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, 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)).get(); - - Future 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.get(); - - if (client) { - Throwable cause = clientSslHandler.handshakeFuture().await().cause(); - assertThat(cause, CoreMatchers.instanceOf(SslHandshakeTimeoutException.class)); - assertFalse(serverSslHandler.handshakeFuture().await().isSuccess()); - } else { - Throwable cause = serverSslHandler.handshakeFuture().await().cause(); - assertThat(cause, CoreMatchers.instanceOf(SslHandshakeTimeoutException.class)); - assertFalse(clientSslHandler.handshakeFuture().await().isSuccess()); - } - } finally { - if (cc != null) { - cc.close().syncUninterruptibly(); - } - if (sc != null) { - sc.close().syncUninterruptibly(); - } - group.shutdownGracefully(); - ReferenceCountUtil.release(sslClientCtx); - } - } - - @Test - @Timeout(value = 5000, unit = TimeUnit.MILLISECONDS) - public void testSessionTicketsWithTLSv12() throws Throwable { - testSessionTickets(SslProvider.OPENSSL, SslProtocols.TLS_v1_2, true); - } - - @Test - @Timeout(value = 5000, unit = TimeUnit.MILLISECONDS) - public void testSessionTicketsWithTLSv13() throws Throwable { - assumeTrue(SslProvider.isTlsv13Supported(SslProvider.OPENSSL)); - testSessionTickets(SslProvider.OPENSSL, SslProtocols.TLS_v1_3, true); - } - - @Test - @Timeout(value = 5000, unit = TimeUnit.MILLISECONDS) - public void testSessionTicketsWithTLSv12AndNoKey() throws Throwable { - testSessionTickets(SslProvider.OPENSSL, SslProtocols.TLS_v1_2, false); - } - - @Test - @Timeout(value = 5000, unit = TimeUnit.MILLISECONDS) - public void testSessionTicketsWithTLSv13AndNoKey() throws Throwable { - assumeTrue(SslProvider.isTlsv13Supported(SslProvider.OPENSSL)); - testSessionTickets(SslProvider.OPENSSL, SslProtocols.TLS_v1_3, false); - } - - private static void testSessionTickets(SslProvider provider, String protocol, boolean withKey) throws Throwable { - OpenSsl.ensureAvailability(); - final SslContext sslClientCtx = SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .sslProvider(provider) - .protocols(protocol) - .build(); - - // Explicit enable session cache as it's disabled by default atm. - ((OpenSslContext) sslClientCtx).sessionContext() - .setSessionCacheEnabled(true); - - final SelfSignedCertificate cert = new SelfSignedCertificate(); - final SslContext sslServerCtx = SslContextBuilder.forServer(cert.key(), cert.cert()) - .sslProvider(provider) - .protocols(protocol) - .build(); - - if (withKey) { - OpenSslSessionTicketKey key = new OpenSslSessionTicketKey(new byte[OpenSslSessionTicketKey.NAME_SIZE], - new byte[OpenSslSessionTicketKey.HMAC_KEY_SIZE], new byte[OpenSslSessionTicketKey.AES_KEY_SIZE]); - ((OpenSslSessionContext) sslClientCtx.sessionContext()).setTicketKeys(key); - ((OpenSslSessionContext) sslServerCtx.sessionContext()).setTicketKeys(key); - } else { - ((OpenSslSessionContext) sslClientCtx.sessionContext()).setTicketKeys(); - ((OpenSslSessionContext) sslServerCtx.sessionContext()).setTicketKeys(); - } - - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - Channel sc = null; - final byte[] bytes = new byte[96]; - ThreadLocalRandom.current().nextBytes(bytes); - try { - final AtomicReference assertErrorRef = new AtomicReference(); - sc = new ServerBootstrap() - .group(group) - .channel(NioServerSocketChannel.class) - .childHandler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) { - final SslHandler sslHandler = sslServerCtx.newHandler(ch.alloc()); - ch.pipeline().addLast(sslServerCtx.newHandler(UnpooledByteBufAllocator.DEFAULT)); - ch.pipeline().addLast(new ChannelHandler() { - - private int handshakeCount; - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { - if (evt instanceof SslHandshakeCompletionEvent) { - handshakeCount++; - ReferenceCountedOpenSslEngine engine = - (ReferenceCountedOpenSslEngine) sslHandler.engine(); - // This test only works for non TLSv1.3 as TLSv1.3 will establish sessions after - // the handshake is done. - // See https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_sess_set_get_cb.html - if (!SslProtocols.TLS_v1_3.equals(engine.getSession().getProtocol())) { - // First should not re-use the session - try { - assertEquals(handshakeCount > 1, engine.isSessionReused()); - } catch (AssertionError error) { - assertErrorRef.set(error); - return; - } - } - - ctx.writeAndFlush(wrappedBuffer(bytes)); - } - } - }); - } - }) - .bind(new InetSocketAddress(0)).get(); - - InetSocketAddress serverAddr = (InetSocketAddress) sc.localAddress(); - testSessionTickets(serverAddr, group, sslClientCtx, bytes, false); - testSessionTickets(serverAddr, group, sslClientCtx, bytes, true); - AssertionError error = assertErrorRef.get(); - if (error != null) { - throw error; - } - } finally { - if (sc != null) { - sc.close().syncUninterruptibly(); - } - group.shutdownGracefully(); - ReferenceCountUtil.release(sslClientCtx); - } - } - - private static void testSessionTickets(InetSocketAddress serverAddress, EventLoopGroup group, - SslContext sslClientCtx, final byte[] bytes, boolean isReused) - throws Throwable { - Channel cc = null; - final BlockingQueue queue = new LinkedBlockingQueue(); - try { - final SslHandler clientSslHandler = sslClientCtx.newHandler(UnpooledByteBufAllocator.DEFAULT, - serverAddress.getAddress().getHostAddress(), serverAddress.getPort()); - - Future future = new Bootstrap() - .group(group) - .channel(NioSocketChannel.class) - .handler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) { - ch.pipeline().addLast(clientSslHandler); - ch.pipeline().addLast(new ByteToMessageDecoder() { - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in) { - if (in.readableBytes() == bytes.length) { - queue.add(in.readBytes(bytes.length)); - } - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - queue.add(cause); - } - }); - } - }).connect(serverAddress); - cc = future.get(); - - assertTrue(clientSslHandler.handshakeFuture().sync().isSuccess()); - - ReferenceCountedOpenSslEngine engine = (ReferenceCountedOpenSslEngine) clientSslHandler.engine(); - // This test only works for non TLSv1.3 as TLSv1.3 will establish sessions after - // the handshake is done. - // See https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_sess_set_get_cb.html - if (!SslProtocols.TLS_v1_3.equals(engine.getSession().getProtocol())) { - assertEquals(isReused, engine.isSessionReused()); - } - Object obj = queue.take(); - if (obj instanceof ByteBuf) { - ByteBuf buffer = (ByteBuf) obj; - ByteBuf expected = wrappedBuffer(bytes); - try { - assertEquals(expected, buffer); - } finally { - expected.release(); - buffer.release(); - } - } else { - throw (Throwable) obj; - } - } finally { - if (cc != null) { - cc.close().syncUninterruptibly(); - } - } - } - - @Test - @Timeout(value = 10000, unit = TimeUnit.MILLISECONDS) - public void testHandshakeFailureOnlyFireExceptionOnce() throws Exception { - final SslContext sslClientCtx = SslContextBuilder.forClient() - .trustManager(new X509ExtendedTrustManager() { - @Override - public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) - throws CertificateException { - failVerification(); - } - - @Override - public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) - throws CertificateException { - failVerification(); - } - - @Override - public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) - throws CertificateException { - failVerification(); - } - - @Override - public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) - throws CertificateException { - failVerification(); - } - - @Override - public void checkClientTrusted(X509Certificate[] chain, String authType) - throws CertificateException { - failVerification(); - } - - @Override - public void checkServerTrusted(X509Certificate[] chain, String authType) - throws CertificateException { - failVerification(); - } - - @Override - public X509Certificate[] getAcceptedIssuers() { - return EmptyArrays.EMPTY_X509_CERTIFICATES; - } - - private void failVerification() throws CertificateException { - throw new CertificateException(); - } - }) - .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 MultithreadEventLoopGroup(NioHandler.newFactory()); - Channel sc = null; - final SslHandler clientSslHandler = sslClientCtx.newHandler(UnpooledByteBufAllocator.DEFAULT); - final SslHandler serverSslHandler = sslServerCtx.newHandler(UnpooledByteBufAllocator.DEFAULT); - - try { - final Object terminalEvent = new Object(); - final BlockingQueue errorQueue = new LinkedBlockingQueue(); - sc = new ServerBootstrap() - .group(group) - .channel(NioServerSocketChannel.class) - .childHandler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) { - ch.pipeline().addLast(serverSslHandler); - ch.pipeline().addLast(new ChannelHandler() { - @Override - public void exceptionCaught(final ChannelHandlerContext ctx, Throwable cause) { - errorQueue.add(cause); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) { - errorQueue.add(terminalEvent); - } - }); - } - }) - .bind(new InetSocketAddress(0)).get(); - Channel channel = new Bootstrap() - .group(group) - .channel(NioSocketChannel.class) - .handler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) { - ch.pipeline().addLast(clientSslHandler); - } - }).connect(sc.localAddress()).get(); - clientSslHandler.handshakeFuture().addListener((FutureListener) f -> { - channel.close(); - }); - assertFalse(clientSslHandler.handshakeFuture().await().isSuccess()); - assertFalse(serverSslHandler.handshakeFuture().await().isSuccess()); - - Object error = errorQueue.take(); - assertThat(error, Matchers.instanceOf(DecoderException.class)); - assertThat(((Throwable) error).getCause(), Matchers.instanceOf(SSLException.class)); - Object terminal = errorQueue.take(); - assertSame(terminalEvent, terminal); - - assertNull(errorQueue.poll(1, TimeUnit.MILLISECONDS)); - } finally { - if (sc != null) { - sc.close().syncUninterruptibly(); - } - group.shutdownGracefully(); - } - } - - @Test - public void testHandshakeFailureCipherMissmatchTLSv12Jdk() throws Exception { - testHandshakeFailureCipherMissmatch(SslProvider.JDK, false); - } - - @Test - public void testHandshakeFailureCipherMissmatchTLSv13Jdk() throws Exception { - assumeTrue(SslProvider.isTlsv13Supported(SslProvider.JDK)); - testHandshakeFailureCipherMissmatch(SslProvider.JDK, true); - } - - @Disabled("This fails atm... needs investigation") - @Test - public void testHandshakeFailureCipherMissmatchTLSv12OpenSsl() throws Exception { - OpenSsl.ensureAvailability(); - testHandshakeFailureCipherMissmatch(SslProvider.OPENSSL, false); - } - - @Test - public void testHandshakeFailureCipherMissmatchTLSv13OpenSsl() throws Exception { - OpenSsl.ensureAvailability(); - assumeTrue(SslProvider.isTlsv13Supported(SslProvider.OPENSSL)); - assumeFalse(OpenSsl.isBoringSSL(), "BoringSSL does not support setting ciphers for TLSv1.3 explicit"); - testHandshakeFailureCipherMissmatch(SslProvider.OPENSSL, true); - } - - private static void testHandshakeFailureCipherMissmatch(SslProvider provider, boolean tls13) throws Exception { - final String clientCipher; - final String serverCipher; - final String protocol; - - if (tls13) { - clientCipher = "TLS_AES_128_GCM_SHA256"; - serverCipher = "TLS_AES_256_GCM_SHA384"; - protocol = SslProtocols.TLS_v1_3; - } else { - clientCipher = "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"; - serverCipher = "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"; - protocol = SslProtocols.TLS_v1_2; - } - final SslContext sslClientCtx = SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .protocols(protocol) - .ciphers(Collections.singleton(clientCipher)) - .sslProvider(provider).build(); - - final SelfSignedCertificate cert = new SelfSignedCertificate(); - final SslContext sslServerCtx = SslContextBuilder.forServer(cert.key(), cert.cert()) - .protocols(protocol) - .ciphers(Collections.singleton(serverCipher)) - .sslProvider(provider).build(); - - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - Channel sc; - Channel cc; - final SslHandler clientSslHandler = sslClientCtx.newHandler(UnpooledByteBufAllocator.DEFAULT); - final SslHandler serverSslHandler = sslServerCtx.newHandler(UnpooledByteBufAllocator.DEFAULT); - - class SslEventHandler implements ChannelHandler { - private final AtomicReference ref; - - SslEventHandler(AtomicReference ref) { - this.ref = ref; - } - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (evt instanceof SslHandshakeCompletionEvent) { - ref.set((SslHandshakeCompletionEvent) evt); - } - ctx.fireUserEventTriggered(evt); - } - } - final AtomicReference clientEvent = - new AtomicReference(); - final AtomicReference serverEvent = - new AtomicReference(); - try { - sc = new ServerBootstrap() - .group(group) - .channel(NioServerSocketChannel.class) - .childHandler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - ch.pipeline().addLast(serverSslHandler); - ch.pipeline().addLast(new SslEventHandler(serverEvent)); - } - }) - .bind(new InetSocketAddress(0)).get(); - - cc = new Bootstrap() - .group(group) - .channel(NioSocketChannel.class) - .handler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) { - ch.pipeline().addLast(clientSslHandler); - ch.pipeline().addLast(new SslEventHandler(clientEvent)); - } - }).connect(sc.localAddress()).get(); - - Throwable clientCause = clientSslHandler.handshakeFuture().await().cause(); - assertThat(clientCause, CoreMatchers.instanceOf(SSLException.class)); - assertThat(clientCause.getCause(), not(CoreMatchers.instanceOf(ClosedChannelException.class))); - Throwable serverCause = serverSslHandler.handshakeFuture().await().cause(); - assertThat(serverCause, CoreMatchers.instanceOf(SSLException.class)); - assertThat(serverCause.getCause(), not(CoreMatchers.instanceOf(ClosedChannelException.class))); - cc.close().syncUninterruptibly(); - sc.close().syncUninterruptibly(); - - Throwable eventClientCause = clientEvent.get().cause(); - assertThat(eventClientCause, CoreMatchers.instanceOf(SSLException.class)); - assertThat(eventClientCause.getCause(), - not(CoreMatchers.instanceOf(ClosedChannelException.class))); - Throwable serverEventCause = serverEvent.get().cause(); - - assertThat(serverEventCause, CoreMatchers.instanceOf(SSLException.class)); - assertThat(serverEventCause.getCause(), - not(CoreMatchers.instanceOf(ClosedChannelException.class))); - } finally { - group.shutdownGracefully(); - ReferenceCountUtil.release(sslClientCtx); - } - } - - @Test - public void testHandshakeEventsTls12JDK() throws Exception { - testHandshakeEvents(SslProvider.JDK, SslProtocols.TLS_v1_2); - } - - @Test - public void testHandshakeEventsTls12Openssl() throws Exception { - OpenSsl.ensureAvailability(); - testHandshakeEvents(SslProvider.OPENSSL, SslProtocols.TLS_v1_2); - } - - @Test - public void testHandshakeEventsTls13JDK() throws Exception { - assumeTrue(SslProvider.isTlsv13Supported(SslProvider.JDK)); - testHandshakeEvents(SslProvider.JDK, SslProtocols.TLS_v1_3); - } - - @Test - public void testHandshakeEventsTls13Openssl() throws Exception { - OpenSsl.ensureAvailability(); - assumeTrue(SslProvider.isTlsv13Supported(SslProvider.OPENSSL)); - testHandshakeEvents(SslProvider.OPENSSL, SslProtocols.TLS_v1_3); - } - - private static void testHandshakeEvents(SslProvider provider, String protocol) throws Exception { - final SslContext sslClientCtx = SslContextBuilder.forClient() - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .protocols(protocol) - .sslProvider(provider).build(); - - final SelfSignedCertificate cert = new SelfSignedCertificate(); - final SslContext sslServerCtx = SslContextBuilder.forServer(cert.key(), cert.cert()) - .protocols(protocol) - .sslProvider(provider).build(); - - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - - final LinkedBlockingQueue serverCompletionEvents = - new LinkedBlockingQueue(); - - final LinkedBlockingQueue clientCompletionEvents = - new LinkedBlockingQueue(); - try { - Channel sc = new ServerBootstrap() - .group(group) - .channel(NioServerSocketChannel.class) - .childHandler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - ch.pipeline().addLast(sslServerCtx.newHandler(UnpooledByteBufAllocator.DEFAULT)); - ch.pipeline().addLast(new SslHandshakeCompletionEventHandler(serverCompletionEvents)); - } - }) - .bind(new InetSocketAddress(0)).get(); - - Bootstrap bs = new Bootstrap() - .group(group) - .channel(NioSocketChannel.class) - .handler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) { - ch.pipeline().addLast(sslClientCtx.newHandler( - UnpooledByteBufAllocator.DEFAULT, "netty.io", 9999)); - ch.pipeline().addLast(new SslHandshakeCompletionEventHandler(clientCompletionEvents)); - } - }) - .remoteAddress(sc.localAddress()); - - Channel cc1 = bs.connect().get(); - Channel cc2 = bs.connect().get(); - - // We expect 4 events as we have 2 connections and for each connection there should be one event - // on the server-side and one on the client-side. - for (int i = 0; i < 2; i++) { - SslHandshakeCompletionEvent event = clientCompletionEvents.take(); - assertTrue(event.isSuccess()); - } - for (int i = 0; i < 2; i++) { - SslHandshakeCompletionEvent event = serverCompletionEvents.take(); - assertTrue(event.isSuccess()); - } - - cc1.close().sync(); - cc2.close().sync(); - sc.close().sync(); - assertEquals(0, clientCompletionEvents.size()); - assertEquals(0, serverCompletionEvents.size()); - } finally { - group.shutdownGracefully(); - ReferenceCountUtil.release(sslClientCtx); - ReferenceCountUtil.release(sslServerCtx); - } - } - - private static class SslHandshakeCompletionEventHandler implements ChannelHandler { - private final Queue completionEvents; - - SslHandshakeCompletionEventHandler(Queue completionEvents) { - this.completionEvents = completionEvents; - } - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { - if (evt instanceof SslHandshakeCompletionEvent) { - completionEvents.add((SslHandshakeCompletionEvent) evt); - } - } - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/SslUtilsTest.java b/handler/src/test/java/io/netty/handler/ssl/SslUtilsTest.java deleted file mode 100644 index f006429f2c..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/SslUtilsTest.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import org.junit.jupiter.api.Test; - -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.security.NoSuchAlgorithmException; - -import static io.netty.handler.ssl.SslUtils.getEncryptedPacketLength; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class SslUtilsTest { - - @SuppressWarnings("deprecation") - @Test - public void testPacketLength() throws SSLException, NoSuchAlgorithmException { - SSLEngine engineLE = newEngine(); - SSLEngine engineBE = newEngine(); - - ByteBuffer empty = ByteBuffer.allocate(0); - ByteBuffer cTOsLE = ByteBuffer.allocate(17 * 1024).order(ByteOrder.LITTLE_ENDIAN); - ByteBuffer cTOsBE = ByteBuffer.allocate(17 * 1024); - - assertTrue(engineLE.wrap(empty, cTOsLE).bytesProduced() > 0); - cTOsLE.flip(); - - assertTrue(engineBE.wrap(empty, cTOsBE).bytesProduced() > 0); - cTOsBE.flip(); - - ByteBuf bufferLE = Unpooled.buffer().order(ByteOrder.LITTLE_ENDIAN).writeBytes(cTOsLE); - ByteBuf bufferBE = Unpooled.buffer().writeBytes(cTOsBE); - - // Test that the packet-length for BE and LE is the same - assertEquals(getEncryptedPacketLength(bufferBE, 0), getEncryptedPacketLength(bufferLE, 0)); - assertEquals(getEncryptedPacketLength(new ByteBuffer[] { bufferBE.nioBuffer() }, 0), - getEncryptedPacketLength(new ByteBuffer[] { bufferLE.nioBuffer().order(ByteOrder.LITTLE_ENDIAN) }, 0)); - } - - private static SSLEngine newEngine() throws SSLException, NoSuchAlgorithmException { - SSLEngine engine = SSLContext.getDefault().createSSLEngine(); - engine.setUseClientMode(true); - 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")); - } - - @Test - public void shouldGetPacketLengthOfGmsslProtocolFromByteBuf() { - int bodyLength = 65; - ByteBuf buf = Unpooled.buffer() - .writeByte(SslUtils.SSL_CONTENT_TYPE_HANDSHAKE) - .writeShort(SslUtils.GMSSL_PROTOCOL_VERSION) - .writeShort(bodyLength); - - int packetLength = getEncryptedPacketLength(buf, 0); - assertEquals(bodyLength + SslUtils.SSL_RECORD_HEADER_LENGTH, packetLength); - buf.release(); - } - - @Test - public void shouldGetPacketLengthOfGmsslProtocolFromByteBuffer() { - int bodyLength = 65; - ByteBuf buf = Unpooled.buffer() - .writeByte(SslUtils.SSL_CONTENT_TYPE_HANDSHAKE) - .writeShort(SslUtils.GMSSL_PROTOCOL_VERSION) - .writeShort(bodyLength); - - int packetLength = getEncryptedPacketLength(new ByteBuffer[] { buf.nioBuffer() }, 0); - assertEquals(bodyLength + SslUtils.SSL_RECORD_HEADER_LENGTH, packetLength); - buf.release(); - } -} 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 deleted file mode 100644 index 33fafecfa2..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/ocsp/OcspTest.java +++ /dev/null @@ -1,527 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.ssl.ocsp; - -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; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.local.LocalAddress; -import io.netty.channel.local.LocalChannel; -import io.netty.channel.local.LocalHandler; -import io.netty.channel.local.LocalServerChannel; -import io.netty.handler.ssl.OpenSsl; -import io.netty.handler.ssl.ReferenceCountedOpenSslEngine; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.SslHandler; -import io.netty.handler.ssl.SslProvider; -import io.netty.handler.ssl.util.InsecureTrustManagerFactory; -import io.netty.handler.ssl.util.SelfSignedCertificate; -import io.netty.util.CharsetUtil; -import io.netty.util.ReferenceCountUtil; -import org.hamcrest.CoreMatchers; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; -import org.junit.jupiter.api.function.Executable; - -import javax.net.ssl.SSLHandshakeException; -import java.net.SocketAddress; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicReference; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNotSame; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assumptions.assumeTrue; - -public class OcspTest { - - @BeforeAll - public static void checkOcspSupported() { - assumeTrue(OpenSsl.isOcspSupported()); - } - - @Test - public void testJdkClientEnableOcsp() throws Exception { - assertThrows(IllegalArgumentException.class, new Executable() { - @Override - public void execute() throws Throwable { - SslContextBuilder.forClient() - .sslProvider(SslProvider.JDK) - .enableOcsp(true) - .build(); - } - }); - } - - @Test - public void testJdkServerEnableOcsp() throws Exception { - final SelfSignedCertificate ssc = new SelfSignedCertificate(); - try { - assertThrows(IllegalArgumentException.class, new Executable() { - @Override - public void execute() throws Throwable { - SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(SslProvider.JDK) - .enableOcsp(true) - .build(); - } - }); - } finally { - ssc.delete(); - } - } - - @Test - public void testClientOcspNotEnabledOpenSsl() throws Exception { - testClientOcspNotEnabled(SslProvider.OPENSSL); - } - - @Test - public void testClientOcspNotEnabledOpenSslRefCnt() throws Exception { - testClientOcspNotEnabled(SslProvider.OPENSSL_REFCNT); - } - - private static void testClientOcspNotEnabled(SslProvider sslProvider) throws Exception { - SslContext context = SslContextBuilder.forClient() - .sslProvider(sslProvider) - .build(); - try { - SslHandler sslHandler = context.newHandler(ByteBufAllocator.DEFAULT); - final ReferenceCountedOpenSslEngine engine = (ReferenceCountedOpenSslEngine) sslHandler.engine(); - try { - assertThrows(IllegalStateException.class, new Executable() { - @Override - public void execute() { - engine.getOcspResponse(); - } - }); - } finally { - engine.release(); - } - } finally { - ReferenceCountUtil.release(context); - } - } - - @Test - public void testServerOcspNotEnabledOpenSsl() throws Exception { - testServerOcspNotEnabled(SslProvider.OPENSSL); - } - - @Test - public void testServerOcspNotEnabledOpenSslRefCnt() throws Exception { - testServerOcspNotEnabled(SslProvider.OPENSSL_REFCNT); - } - - private static void testServerOcspNotEnabled(SslProvider sslProvider) throws Exception { - SelfSignedCertificate ssc = new SelfSignedCertificate(); - try { - SslContext context = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(sslProvider) - .build(); - try { - SslHandler sslHandler = context.newHandler(ByteBufAllocator.DEFAULT); - final ReferenceCountedOpenSslEngine engine = (ReferenceCountedOpenSslEngine) sslHandler.engine(); - try { - assertThrows(IllegalStateException.class, new Executable() { - @Override - public void execute() { - engine.setOcspResponse(new byte[] { 1, 2, 3 }); - } - }); - } finally { - engine.release(); - } - } finally { - ReferenceCountUtil.release(context); - } - } finally { - ssc.delete(); - } - } - - @Test - @Timeout(value = 10000L, unit = TimeUnit.MILLISECONDS) - public void testClientAcceptingOcspStapleOpenSsl() throws Exception { - testClientAcceptingOcspStaple(SslProvider.OPENSSL); - } - - @Test - @Timeout(value = 10000L, unit = TimeUnit.MILLISECONDS) - public void testClientAcceptingOcspStapleOpenSslRefCnt() throws Exception { - testClientAcceptingOcspStaple(SslProvider.OPENSSL_REFCNT); - } - - /** - * The Server provides an OCSP staple and the Client accepts it. - */ - private static void testClientAcceptingOcspStaple(SslProvider sslProvider) throws Exception { - final CountDownLatch latch = new CountDownLatch(1); - ChannelHandler serverHandler = new ChannelHandler() { - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - ctx.writeAndFlush(Unpooled.wrappedBuffer("Hello, World!".getBytes())); - ctx.fireChannelActive(); - } - }; - - ChannelHandler clientHandler = new ChannelHandler() { - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - try { - ReferenceCountUtil.release(msg); - } finally { - latch.countDown(); - } - } - }; - - byte[] response = newOcspResponse(); - TestClientOcspContext callback = new TestClientOcspContext(true); - - handshake(sslProvider, latch, serverHandler, response, clientHandler, callback); - - byte[] actual = callback.response(); - - assertNotNull(actual); - assertNotSame(response, actual); - assertArrayEquals(response, actual); - } - - @Test - @Timeout(value = 10000L, unit = TimeUnit.MILLISECONDS) - public void testClientRejectingOcspStapleOpenSsl() throws Exception { - testClientRejectingOcspStaple(SslProvider.OPENSSL); - } - - @Test - @Timeout(value = 10000L, unit = TimeUnit.MILLISECONDS) - public void testClientRejectingOcspStapleOpenSslRefCnt() throws Exception { - testClientRejectingOcspStaple(SslProvider.OPENSSL_REFCNT); - } - - /** - * The Server provides an OCSP staple and the Client rejects it. - */ - private static void testClientRejectingOcspStaple(SslProvider sslProvider) throws Exception { - final AtomicReference causeRef = new AtomicReference<>(); - final CountDownLatch latch = new CountDownLatch(1); - - ChannelHandler clientHandler = new ChannelHandler() { - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - try { - causeRef.set(cause); - } finally { - latch.countDown(); - } - } - }; - - byte[] response = newOcspResponse(); - TestClientOcspContext callback = new TestClientOcspContext(false); - - handshake(sslProvider, latch, null, response, clientHandler, callback); - - byte[] actual = callback.response(); - - assertNotNull(actual); - assertNotSame(response, actual); - assertArrayEquals(response, actual); - - Throwable cause = causeRef.get(); - assertThat(cause, CoreMatchers.instanceOf(SSLHandshakeException.class)); - } - - @Test - @Timeout(value = 10000L, unit = TimeUnit.MILLISECONDS) - public void testServerHasNoStapleOpenSsl() throws Exception { - testServerHasNoStaple(SslProvider.OPENSSL); - } - - @Test - @Timeout(value = 10000L, unit = TimeUnit.MILLISECONDS) - public void testServerHasNoStapleOpenSslRefCnt() throws Exception { - testServerHasNoStaple(SslProvider.OPENSSL_REFCNT); - } - - /** - * The server has OCSP stapling enabled but doesn't provide a staple. - */ - private static void testServerHasNoStaple(SslProvider sslProvider) throws Exception { - final CountDownLatch latch = new CountDownLatch(1); - ChannelHandler serverHandler = new ChannelHandler() { - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - ctx.writeAndFlush(Unpooled.wrappedBuffer("Hello, World!".getBytes())); - ctx.fireChannelActive(); - } - }; - - ChannelHandler clientHandler = new ChannelHandler() { - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - try { - ReferenceCountUtil.release(msg); - } finally { - latch.countDown(); - } - } - }; - - byte[] response = null; - TestClientOcspContext callback = new TestClientOcspContext(true); - - handshake(sslProvider, latch, serverHandler, response, clientHandler, callback); - - byte[] actual = callback.response(); - - assertNull(response); - assertNull(actual); - } - - @Test - @Timeout(value = 10000L, unit = TimeUnit.MILLISECONDS) - public void testClientExceptionOpenSsl() throws Exception { - testClientException(SslProvider.OPENSSL); - } - - @Test - @Timeout(value = 10000L, unit = TimeUnit.MILLISECONDS) - public void testClientExceptionOpenSslRefCnt() throws Exception { - testClientException(SslProvider.OPENSSL_REFCNT); - } - - /** - * Testing what happens if the {@link OcspClientCallback} throws an {@link Exception}. - * - * The exception should bubble up on the client side and the connection should get closed. - */ - private static void testClientException(SslProvider sslProvider) throws Exception { - final AtomicReference causeRef = new AtomicReference<>(); - final CountDownLatch latch = new CountDownLatch(1); - - ChannelHandler clientHandler = new ChannelHandler() { - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - try { - causeRef.set(cause); - } finally { - latch.countDown(); - } - } - }; - - final OcspTestException clientException = new OcspTestException("testClientException"); - byte[] response = newOcspResponse(); - OcspClientCallback callback = response1 -> { - throw clientException; - }; - - handshake(sslProvider, latch, null, response, clientHandler, callback); - - assertSame(clientException, causeRef.get()); - } - - private static void handshake(SslProvider sslProvider, CountDownLatch latch, ChannelHandler serverHandler, - byte[] response, ChannelHandler clientHandler, OcspClientCallback callback) throws Exception { - - SelfSignedCertificate ssc = new SelfSignedCertificate(); - try { - SslContext serverSslContext = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) - .sslProvider(sslProvider) - .enableOcsp(true) - .build(); - - try { - SslContext clientSslContext = SslContextBuilder.forClient() - .sslProvider(sslProvider) - .enableOcsp(true) - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .build(); - - try { - EventLoopGroup group = new MultithreadEventLoopGroup(LocalHandler.newFactory()); - try { - LocalAddress address = new LocalAddress("handshake-" + Math.random()); - Channel server = newServer(group, address, serverSslContext, response, serverHandler); - Channel client = newClient(group, address, clientSslContext, callback, clientHandler); - try { - assertTrue(latch.await(10L, TimeUnit.SECONDS)); - } finally { - client.close().syncUninterruptibly(); - server.close().syncUninterruptibly(); - } - } finally { - group.shutdownGracefully(1L, 1L, TimeUnit.SECONDS); - } - } finally { - ReferenceCountUtil.release(clientSslContext); - } - } finally { - ReferenceCountUtil.release(serverSslContext); - } - } finally { - ssc.delete(); - } - } - - private static Channel newServer(EventLoopGroup group, SocketAddress address, - SslContext context, byte[] response, ChannelHandler handler) throws Exception { - - ServerBootstrap bootstrap = new ServerBootstrap() - .channel(LocalServerChannel.class) - .group(group) - .childHandler(newServerHandler(context, response, handler)); - - return bootstrap.bind(address).get(); - } - - private static Channel newClient(EventLoopGroup group, SocketAddress address, - SslContext context, OcspClientCallback callback, ChannelHandler handler) throws Exception { - - Bootstrap bootstrap = new Bootstrap() - .channel(LocalChannel.class) - .group(group) - .handler(newClientHandler(context, callback, handler)); - - return bootstrap.connect(address).get(); - } - - private static ChannelHandler newServerHandler(final SslContext context, - final byte[] response, final ChannelHandler handler) { - return new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - ChannelPipeline pipeline = ch.pipeline(); - SslHandler sslHandler = context.newHandler(ch.alloc()); - - if (response != null) { - ReferenceCountedOpenSslEngine engine = (ReferenceCountedOpenSslEngine) sslHandler.engine(); - engine.setOcspResponse(response); - } - - pipeline.addLast(sslHandler); - - if (handler != null) { - pipeline.addLast(handler); - } - } - }; - } - - private static ChannelHandler newClientHandler(final SslContext context, - final OcspClientCallback callback, final ChannelHandler handler) { - return new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - ChannelPipeline pipeline = ch.pipeline(); - - SslHandler sslHandler = context.newHandler(ch.alloc()); - ReferenceCountedOpenSslEngine engine = (ReferenceCountedOpenSslEngine) sslHandler.engine(); - - pipeline.addLast(sslHandler); - pipeline.addLast(new OcspClientCallbackHandler(engine, callback)); - - if (handler != null) { - pipeline.addLast(handler); - } - } - }; - } - - private static byte[] newOcspResponse() { - // Assume we got the OCSP staple from somewhere. Using a bogus byte[] - // in the test because getting a true staple from the CA is quite involved. - // It requires HttpCodec and Bouncycastle and the test may be very unreliable - // because the OCSP responder servers are basically being DDoS'd by the - // Internet. - - return "I am a bogus OCSP staple. OpenSSL does not care about the format of the byte[]!" - .getBytes(CharsetUtil.US_ASCII); - } - - private interface OcspClientCallback { - boolean verify(byte[] staple) throws Exception; - } - - private static final class TestClientOcspContext implements OcspClientCallback { - - private final CountDownLatch latch = new CountDownLatch(1); - private final boolean valid; - - private volatile byte[] response; - - TestClientOcspContext(boolean valid) { - this.valid = valid; - } - - public byte[] response() throws InterruptedException, TimeoutException { - assertTrue(latch.await(10L, TimeUnit.SECONDS)); - return response; - } - - @Override - public boolean verify(byte[] response) throws Exception { - this.response = response; - latch.countDown(); - - return valid; - } - } - - private static final class OcspClientCallbackHandler extends OcspClientHandler { - - private final OcspClientCallback callback; - - OcspClientCallbackHandler(ReferenceCountedOpenSslEngine engine, OcspClientCallback callback) { - super(engine); - this.callback = callback; - } - - @Override - protected boolean verify(ChannelHandlerContext ctx, ReferenceCountedOpenSslEngine engine) throws Exception { - byte[] response = engine.getOcspResponse(); - return callback.verify(response); - } - } - - private static final class OcspTestException extends IllegalStateException { - private static final long serialVersionUID = 4516426833250228159L; - - OcspTestException(String message) { - super(message); - } - } -} diff --git a/handler/src/test/java/io/netty/handler/ssl/util/FingerprintTrustManagerFactoryTest.java b/handler/src/test/java/io/netty/handler/ssl/util/FingerprintTrustManagerFactoryTest.java deleted file mode 100644 index fe69ed19d7..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/util/FingerprintTrustManagerFactoryTest.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.ssl.util; - - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; - -import javax.net.ssl.X509TrustManager; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; - -import static io.netty.handler.ssl.Java8SslTestUtils.loadCertCollection; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class FingerprintTrustManagerFactoryTest { - - private static final String FIRST_CERT_SHA1_FINGERPRINT - = "18:C7:C2:76:1F:DF:72:3B:2A:A7:BB:2C:B0:30:D4:C0:C0:72:AD:84"; - - private static final String FIRST_CERT_SHA256_FINGERPRINT - = "1C:53:0E:6B:FF:93:F0:DE:C2:E6:E7:9D:10:53:58:FF:" + - "DD:8E:68:CD:82:D9:C9:36:9B:43:EE:B3:DC:13:68:FB"; - - private static final X509Certificate[] FIRST_CHAIN; - - private static final X509Certificate[] SECOND_CHAIN; - - static { - try { - FIRST_CHAIN = loadCertCollection("test.crt"); - SECOND_CHAIN = loadCertCollection("test2.crt"); - } catch (Exception e) { - throw new Error(e); - } - } - - @Test - public void testFingerprintWithInvalidLength() { - assertThrows(IllegalArgumentException.class, new Executable() { - @Override - public void execute() { - FingerprintTrustManagerFactory.builder("SHA-256").fingerprints("00:00:00").build(); - } - }); - } - - @Test - public void testFingerprintWithUnexpectedCharacters() { - assertThrows(IllegalArgumentException.class, new Executable() { - @Override - public void execute() { - FingerprintTrustManagerFactory.builder("SHA-256").fingerprints("00:00:00\n").build(); - } - }); - } - - @Test - public void testWithNoFingerprints() { - assertThrows(IllegalStateException.class, new Executable() { - @Override - public void execute() { - FingerprintTrustManagerFactory.builder("SHA-256").build(); - } - }); - } - - @Test - public void testWithNullFingerprint() { - assertThrows(IllegalArgumentException.class, new Executable() { - @Override - public void execute() { - FingerprintTrustManagerFactory - .builder("SHA-256") - .fingerprints(FIRST_CERT_SHA256_FINGERPRINT, null) - .build(); - } - }); - } - - @Test - public void testValidSHA1Fingerprint() throws Exception { - FingerprintTrustManagerFactory factory = FingerprintTrustManagerFactory.builder("SHA-1") - .fingerprints(FIRST_CERT_SHA1_FINGERPRINT).build(); - - assertTrue(factory.engineGetTrustManagers().length > 0); - assertTrue(factory.engineGetTrustManagers()[0] instanceof X509TrustManager); - X509TrustManager tm = (X509TrustManager) factory.engineGetTrustManagers()[0]; - tm.checkClientTrusted(FIRST_CHAIN, "test"); - } - - @Test - public void testTrustedCertificateWithSHA256Fingerprint() throws Exception { - FingerprintTrustManagerFactory factory = FingerprintTrustManagerFactory - .builder("SHA-256") - .fingerprints(FIRST_CERT_SHA256_FINGERPRINT) - .build(); - - X509Certificate[] keyCertChain = loadCertCollection("test.crt"); - assertNotNull(keyCertChain); - assertTrue(factory.engineGetTrustManagers().length > 0); - assertTrue(factory.engineGetTrustManagers()[0] instanceof X509TrustManager); - X509TrustManager tm = (X509TrustManager) factory.engineGetTrustManagers()[0]; - tm.checkClientTrusted(keyCertChain, "test"); - } - - @Test - public void testUntrustedCertificateWithSHA256Fingerprint() throws Exception { - FingerprintTrustManagerFactory factory = FingerprintTrustManagerFactory - .builder("SHA-256") - .fingerprints(FIRST_CERT_SHA256_FINGERPRINT) - .build(); - - assertTrue(factory.engineGetTrustManagers().length > 0); - assertTrue(factory.engineGetTrustManagers()[0] instanceof X509TrustManager); - final X509TrustManager tm = (X509TrustManager) factory.engineGetTrustManagers()[0]; - - assertThrows(CertificateException.class, new Executable() { - @Override - public void execute() throws Throwable { - tm.checkClientTrusted(SECOND_CHAIN, "test"); - } - }); - } - -} diff --git a/handler/src/test/java/io/netty/handler/ssl/util/SelfSignedCertificateTest.java b/handler/src/test/java/io/netty/handler/ssl/util/SelfSignedCertificateTest.java deleted file mode 100644 index 59ca37635b..0000000000 --- a/handler/src/test/java/io/netty/handler/ssl/util/SelfSignedCertificateTest.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ssl.util; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; - -import java.security.cert.CertificateException; - -import static org.junit.jupiter.api.Assertions.*; - -class SelfSignedCertificateTest { - - @Test - void fqdnAsteriskDoesNotThrowTest() { - assertDoesNotThrow(new Executable() { - @Override - public void execute() throws Throwable { - new SelfSignedCertificate("*.netty.io", "EC", 256); - } - }); - - assertDoesNotThrow(new Executable() { - @Override - public void execute() throws Throwable { - new SelfSignedCertificate("*.netty.io", "RSA", 2048); - } - }); - } - - @Test - void fqdnAsteriskFileNameTest() throws CertificateException { - SelfSignedCertificate ssc = new SelfSignedCertificate("*.netty.io", "EC", 256); - assertFalse(ssc.certificate().getName().contains("*")); - assertFalse(ssc.privateKey().getName().contains("*")); - } -} diff --git a/handler/src/test/java/io/netty/handler/stream/ChunkedWriteHandlerTest.java b/handler/src/test/java/io/netty/handler/stream/ChunkedWriteHandlerTest.java deleted file mode 100644 index e92d327dcb..0000000000 --- a/handler/src/test/java/io/netty/handler/stream/ChunkedWriteHandlerTest.java +++ /dev/null @@ -1,834 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.stream; - -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.embedded.EmbeddedChannel; -import io.netty.util.CharsetUtil; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.FutureListener; -import io.netty.util.internal.PlatformDependent; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; - -import java.io.ByteArrayInputStream; -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.ClosedChannelException; -import java.nio.channels.FileChannel; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - -import static java.util.concurrent.TimeUnit.SECONDS; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class ChunkedWriteHandlerTest { - private static final byte[] BYTES = new byte[1024 * 64]; - private static final File TMP; - - static { - for (int i = 0; i < BYTES.length; i++) { - BYTES[i] = (byte) i; - } - - FileOutputStream out = null; - try { - TMP = PlatformDependent.createTempFile("netty-chunk-", ".tmp", null); - TMP.deleteOnExit(); - out = new FileOutputStream(TMP); - out.write(BYTES); - out.flush(); - } catch (IOException e) { - throw new RuntimeException(e); - } finally { - if (out != null) { - try { - out.close(); - } catch (IOException e) { - // ignore - } - } - } - } - - // See #310 - @Test - public void testChunkedStream() { - check(new ChunkedStream(new ByteArrayInputStream(BYTES))); - - check(new ChunkedStream(new ByteArrayInputStream(BYTES)), - new ChunkedStream(new ByteArrayInputStream(BYTES)), - new ChunkedStream(new ByteArrayInputStream(BYTES))); - } - - @Test - public void testChunkedNioStream() { - check(new ChunkedNioStream(Channels.newChannel(new ByteArrayInputStream(BYTES)))); - - check(new ChunkedNioStream(Channels.newChannel(new ByteArrayInputStream(BYTES))), - new ChunkedNioStream(Channels.newChannel(new ByteArrayInputStream(BYTES))), - new ChunkedNioStream(Channels.newChannel(new ByteArrayInputStream(BYTES)))); - } - - @Test - public void testChunkedFile() throws IOException { - check(new ChunkedFile(TMP)); - - check(new ChunkedFile(TMP), new ChunkedFile(TMP), new ChunkedFile(TMP)); - } - - @Test - public void testChunkedNioFile() throws IOException { - check(new ChunkedNioFile(TMP)); - - check(new ChunkedNioFile(TMP), new ChunkedNioFile(TMP), new ChunkedNioFile(TMP)); - } - - @Test - public void testChunkedNioFileLeftPositionUnchanged() throws IOException { - FileChannel in = null; - final long expectedPosition = 10; - try { - in = new RandomAccessFile(TMP, "r").getChannel(); - in.position(expectedPosition); - check(new ChunkedNioFile(in) { - @Override - public void close() throws Exception { - //no op - } - }); - assertTrue(in.isOpen()); - assertEquals(expectedPosition, in.position()); - } finally { - if (in != null) { - in.close(); - } - } - } - - @Test - public void testChunkedNioFileFailOnClosedFileChannel() throws IOException { - final FileChannel in = new RandomAccessFile(TMP, "r").getChannel(); - in.close(); - - assertThrows(ClosedChannelException.class, new Executable() { - @Override - public void execute() throws Throwable { - check(new ChunkedNioFile(in) { - @Override - public void close() throws Exception { - //no op - } - }); - } - }); - } - - @Test - public void testUnchunkedData() throws IOException { - check(Unpooled.wrappedBuffer(BYTES)); - - check(Unpooled.wrappedBuffer(BYTES), Unpooled.wrappedBuffer(BYTES), Unpooled.wrappedBuffer(BYTES)); - } - - // Test case which shows that there is not a bug like stated here: - // https://stackoverflow.com/a/10426305 - @Test - public void testListenerNotifiedWhenIsEnd() { - ByteBuf buffer = Unpooled.copiedBuffer("Test", CharsetUtil.ISO_8859_1); - - ChunkedInput input = 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(); - } - - @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; - } - }; - - final AtomicBoolean listenerNotified = new AtomicBoolean(false); - final FutureListener listener = future -> listenerNotified.set(true); - - EmbeddedChannel ch = new EmbeddedChannel(new ChunkedWriteHandler()); - ch.writeAndFlush(input).addListener(listener).syncUninterruptibly(); - assertTrue(ch.finish()); - - // the listener should have been notified - assertTrue(listenerNotified.get()); - - ByteBuf buffer2 = ch.readOutbound(); - assertEquals(buffer, buffer2); - assertNull(ch.readOutbound()); - - buffer.release(); - buffer2.release(); - } - - @Test - public void testChunkedMessageInput() { - - ChunkedInput input = new ChunkedInput() { - private boolean done; - - @Override - public boolean isEndOfInput() throws Exception { - return done; - } - - @Override - public void close() throws Exception { - // NOOP - } - - @Deprecated - @Override - public Object readChunk(ChannelHandlerContext ctx) throws Exception { - return readChunk(ctx.alloc()); - } - - @Override - public Object readChunk(ByteBufAllocator ctx) throws Exception { - if (done) { - return false; - } - done = true; - return 0; - } - - @Override - public long length() { - return -1; - } - - @Override - public long progress() { - return 1; - } - }; - - EmbeddedChannel ch = new EmbeddedChannel(new ChunkedWriteHandler()); - ch.writeAndFlush(input).syncUninterruptibly(); - assertTrue(ch.finish()); - - assertEquals(0, (Integer) 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 { - ChannelHandler failLast = new ChannelHandler() { - private int passedWrites; - - @Override - public Future write(ChannelHandlerContext ctx, Object msg) { - if (++this.passedWrites < 4) { - return ctx.write(msg); - } - ReferenceCountUtil.release(msg); - return ctx.newFailedFuture(new RuntimeException()); - } - }; - - EmbeddedChannel ch = new EmbeddedChannel(failLast, new ChunkedWriteHandler()); - Future 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 - Future r1 = ch.write(new ChunkedFile(TMP)); - Future 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; - } - }; - - ChannelHandler noOpWrites = new ChannelHandler() { - @Override - public Future write(ChannelHandlerContext ctx, Object msg) { - ReferenceCountUtil.release(msg); - return ctx.newFailedFuture(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()); - } - - @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"); - final ThrowingChunkedInput input = new ThrowingChunkedInput(error); - final EmbeddedChannel ch = new EmbeddedChannel(new ChunkedWriteHandler()); - - Exception e = assertThrows(Exception.class, new Executable() { - @Override - public void execute() throws Throwable { - ch.writeOutbound(input); - } - }); - 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); - - Future writeFuture = ch.write(input); - writeFuture.addListener(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); - - Future writeFuture = ch.write(input); - writeFuture.addListener(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); - - Future writeFuture = ch.write(input); - writeFuture.addListener(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 testEndOfInputWhenChannelIsClosedwhenWrite() { - ChunkedInput input = new ChunkedInput() { - - @Override - public boolean isEndOfInput() { - return true; - } - - @Override - public void close() { - } - - @Deprecated - @Override - public ByteBuf readChunk(ChannelHandlerContext ctx) { - return null; - } - - @Override - public ByteBuf readChunk(ByteBufAllocator allocator) { - return null; - } - - @Override - public long length() { - return -1; - } - - @Override - public long progress() { - return 1; - } - }; - - EmbeddedChannel ch = new EmbeddedChannel(new ChannelHandler() { - @Override - public Future write(ChannelHandlerContext ctx, Object msg) { - ReferenceCountUtil.release(msg); - // Calling close so we will drop all queued messages in the ChunkedWriteHandler. - ctx.close(); - return ctx.newSucceededFuture(); - } - }, new ChunkedWriteHandler()); - - ch.writeAndFlush(input).syncUninterruptibly(); - assertFalse(ch.finishAndReleaseAll()); - } - - @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); - - Future writeFuture = ch.write(input); - writeFuture.addListener(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()); - - for (Object input: inputs) { - ch.writeOutbound(input); - } - - assertTrue(ch.finish()); - - 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 * inputs.length, read); - } - - private static void checkFirstFailed(Object input) { - ChannelHandler noOpWrites = new ChannelHandler() { - @Override - public Future write(ChannelHandlerContext ctx, Object msg) { - ReferenceCountUtil.release(msg); - return ctx.newFailedFuture(new RuntimeException()); - } - }; - - EmbeddedChannel ch = new EmbeddedChannel(noOpWrites, new ChunkedWriteHandler()); - Future 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) { - ChannelHandler failFirst = new ChannelHandler() { - private boolean alreadyFailed; - - @Override - public Future write(ChannelHandlerContext ctx, Object msg) { - if (alreadyFailed) { - return ctx.write(msg); - } - this.alreadyFailed = true; - ReferenceCountUtil.release(msg); - return ctx.newFailedFuture(new RuntimeException()); - } - }; - - EmbeddedChannel ch = new EmbeddedChannel(failFirst, new ChunkedWriteHandler()); - Future r1 = ch.write(input1); - Future 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); - } - - 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; - } - } -} diff --git a/handler/src/test/java/io/netty/handler/timeout/IdleStateEventTest.java b/handler/src/test/java/io/netty/handler/timeout/IdleStateEventTest.java deleted file mode 100644 index 87511bc3ca..0000000000 --- a/handler/src/test/java/io/netty/handler/timeout/IdleStateEventTest.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.timeout; - - -import org.junit.jupiter.api.Test; - -import static io.netty.handler.timeout.IdleStateEvent.*; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.hasToString; - -public class IdleStateEventTest { - @Test - public void testHumanReadableToString() { - assertThat(FIRST_READER_IDLE_STATE_EVENT, hasToString("IdleStateEvent(READER_IDLE, first)")); - assertThat(READER_IDLE_STATE_EVENT, hasToString("IdleStateEvent(READER_IDLE)")); - assertThat(FIRST_WRITER_IDLE_STATE_EVENT, hasToString("IdleStateEvent(WRITER_IDLE, first)")); - assertThat(WRITER_IDLE_STATE_EVENT, hasToString("IdleStateEvent(WRITER_IDLE)")); - assertThat(FIRST_ALL_IDLE_STATE_EVENT, hasToString("IdleStateEvent(ALL_IDLE, first)")); - assertThat(ALL_IDLE_STATE_EVENT, hasToString("IdleStateEvent(ALL_IDLE)")); - } -} diff --git a/handler/src/test/java/io/netty/handler/timeout/IdleStateHandlerTest.java b/handler/src/test/java/io/netty/handler/timeout/IdleStateHandlerTest.java deleted file mode 100644 index 1e770abaea..0000000000 --- a/handler/src/test/java/io/netty/handler/timeout/IdleStateHandlerTest.java +++ /dev/null @@ -1,417 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.timeout; - -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelOutboundBuffer; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.concurrent.Future; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.greaterThanOrEqualTo; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; - -public class IdleStateHandlerTest { - - @Test - public void testReaderIdle() throws Exception { - TestableIdleStateHandler idleStateHandler = new TestableIdleStateHandler( - false, 1L, 0L, 0L, TimeUnit.SECONDS); - - // We start with one FIRST_READER_IDLE_STATE_EVENT, followed by an infinite number of READER_IDLE_STATE_EVENTs - anyIdle(idleStateHandler, IdleStateEvent.FIRST_READER_IDLE_STATE_EVENT, - IdleStateEvent.READER_IDLE_STATE_EVENT, IdleStateEvent.READER_IDLE_STATE_EVENT); - } - - @Test - public void testWriterIdle() throws Exception { - TestableIdleStateHandler idleStateHandler = new TestableIdleStateHandler( - false, 0L, 1L, 0L, TimeUnit.SECONDS); - - anyIdle(idleStateHandler, IdleStateEvent.FIRST_WRITER_IDLE_STATE_EVENT, - IdleStateEvent.WRITER_IDLE_STATE_EVENT, IdleStateEvent.WRITER_IDLE_STATE_EVENT); - } - - @Test - public void testAllIdle() throws Exception { - TestableIdleStateHandler idleStateHandler = new TestableIdleStateHandler( - false, 0L, 0L, 1L, TimeUnit.SECONDS); - - anyIdle(idleStateHandler, IdleStateEvent.FIRST_ALL_IDLE_STATE_EVENT, - IdleStateEvent.ALL_IDLE_STATE_EVENT, IdleStateEvent.ALL_IDLE_STATE_EVENT); - } - - private static void anyIdle(TestableIdleStateHandler idleStateHandler, Object... expected) throws Exception { - assertThat(expected.length, greaterThanOrEqualTo(1)); - - final List events = new ArrayList<>(); - ChannelHandler handler = new ChannelHandler() { - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - events.add(evt); - } - }; - - EmbeddedChannel channel = new EmbeddedChannel(idleStateHandler, handler); - try { - // For each expected event advance the ticker and run() the task. Each - // step should yield in an IdleStateEvent because we haven't written - // or read anything from the channel. - for (int i = 0; i < expected.length; i++) { - idleStateHandler.tickRun(); - } - - assertEquals(expected.length, events.size()); - - // Compare the expected with the actual IdleStateEvents - for (int i = 0; i < expected.length; i++) { - Object evt = events.get(i); - assertSame(expected[i], evt, "Element " + i + " is not matching"); - } - } finally { - channel.finishAndReleaseAll(); - } - } - - @Test - public void testReaderNotIdle() throws Exception { - TestableIdleStateHandler idleStateHandler = new TestableIdleStateHandler( - false, 1L, 0L, 0L, TimeUnit.SECONDS); - - Action action = channel -> channel.writeInbound("Hello, World!"); - - anyNotIdle(idleStateHandler, action, IdleStateEvent.FIRST_READER_IDLE_STATE_EVENT); - } - - @Test - public void testWriterNotIdle() throws Exception { - TestableIdleStateHandler idleStateHandler = new TestableIdleStateHandler( - false, 0L, 1L, 0L, TimeUnit.SECONDS); - - Action action = channel -> channel.writeAndFlush("Hello, World!"); - - anyNotIdle(idleStateHandler, action, IdleStateEvent.FIRST_WRITER_IDLE_STATE_EVENT); - } - - @Test - public void testAllNotIdle() throws Exception { - // Reader... - TestableIdleStateHandler idleStateHandler = new TestableIdleStateHandler( - false, 0L, 0L, 1L, TimeUnit.SECONDS); - - Action reader = channel -> channel.writeInbound("Hello, World!"); - - anyNotIdle(idleStateHandler, reader, IdleStateEvent.FIRST_ALL_IDLE_STATE_EVENT); - - // Writer... - idleStateHandler = new TestableIdleStateHandler( - false, 0L, 0L, 1L, TimeUnit.SECONDS); - - Action writer = channel -> channel.writeAndFlush("Hello, World!"); - - anyNotIdle(idleStateHandler, writer, IdleStateEvent.FIRST_ALL_IDLE_STATE_EVENT); - } - - private static void anyNotIdle(TestableIdleStateHandler idleStateHandler, - Action action, Object expected) throws Exception { - - final List events = new ArrayList<>(); - ChannelHandler handler = new ChannelHandler() { - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - events.add(evt); - } - }; - - EmbeddedChannel channel = new EmbeddedChannel(idleStateHandler, handler); - try { - idleStateHandler.tick(1L, TimeUnit.NANOSECONDS); - action.run(channel); - - // Advance the ticker by some fraction and run() the task. - // There shouldn't be an IdleStateEvent getting fired because - // we've just performed an action on the channel that is meant - // to reset the idle task. - long delayInNanos = idleStateHandler.delay(TimeUnit.NANOSECONDS); - assertNotEquals(0L, delayInNanos); - - idleStateHandler.tickRun(delayInNanos / 2L, TimeUnit.NANOSECONDS); - assertEquals(0, events.size()); - - // Advance the ticker by the full amount and it should yield - // in an IdleStateEvent. - idleStateHandler.tickRun(); - assertEquals(1, events.size()); - assertSame(expected, events.get(0)); - } finally { - channel.finishAndReleaseAll(); - } - } - - @Test - public void testObserveWriterIdle() throws Exception { - observeOutputIdle(true); - } - - @Test - public void testObserveAllIdle() throws Exception { - observeOutputIdle(false); - } - - private static void observeOutputIdle(boolean writer) throws Exception { - - long writerIdleTime = 0L; - long allIdleTime = 0L; - IdleStateEvent expected; - - if (writer) { - writerIdleTime = 5L; - expected = IdleStateEvent.FIRST_WRITER_IDLE_STATE_EVENT; - } else { - allIdleTime = 5L; - expected = IdleStateEvent.FIRST_ALL_IDLE_STATE_EVENT; - } - - TestableIdleStateHandler idleStateHandler = new TestableIdleStateHandler( - true, 0L, writerIdleTime, allIdleTime, TimeUnit.SECONDS); - - final List events = new ArrayList<>(); - ChannelHandler handler = new ChannelHandler() { - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - events.add(evt); - } - }; - - ObservableChannel channel = new ObservableChannel(idleStateHandler, handler); - try { - // We're writing 3 messages that will be consumed at different rates! - channel.writeAndFlush(Unpooled.wrappedBuffer(new byte[] { 1 })); - channel.writeAndFlush(Unpooled.wrappedBuffer(new byte[] { 2 })); - channel.writeAndFlush(Unpooled.wrappedBuffer(new byte[] { 3 })); - channel.writeAndFlush(Unpooled.wrappedBuffer(new byte[5 * 1024])); - - // Establish a baseline. We're not consuming anything and let it idle once. - idleStateHandler.tickRun(); - assertEquals(1, events.size()); - assertSame(expected, events.get(0)); - events.clear(); - - // Our ticker should be at second 5 - assertEquals(5L, idleStateHandler.tick(TimeUnit.SECONDS)); - - // Consume one message in 4 seconds, then be idle for 2 seconds, - // then run the task and we shouldn't get an IdleStateEvent because - // we haven't been idle for long enough! - idleStateHandler.tick(4L, TimeUnit.SECONDS); - assertNotNullAndRelease(channel.consume()); - - idleStateHandler.tickRun(2L, TimeUnit.SECONDS); - assertEquals(0, events.size()); - assertEquals(11L, idleStateHandler.tick(TimeUnit.SECONDS)); // 5s + 4s + 2s - - // Consume one message in 3 seconds, then be idle for 4 seconds, - // then run the task and we shouldn't get an IdleStateEvent because - // we haven't been idle for long enough! - idleStateHandler.tick(3L, TimeUnit.SECONDS); - assertNotNullAndRelease(channel.consume()); - - idleStateHandler.tickRun(4L, TimeUnit.SECONDS); - assertEquals(0, events.size()); - assertEquals(18L, idleStateHandler.tick(TimeUnit.SECONDS)); // 11s + 3s + 4s - - // Don't consume a message and be idle for 5 seconds. - // We should get an IdleStateEvent! - idleStateHandler.tickRun(5L, TimeUnit.SECONDS); - assertEquals(1, events.size()); - assertEquals(23L, idleStateHandler.tick(TimeUnit.SECONDS)); // 18s + 5s - events.clear(); - - // Consume one message in 2 seconds, then be idle for 1 seconds, - // then run the task and we shouldn't get an IdleStateEvent because - // we haven't been idle for long enough! - idleStateHandler.tick(2L, TimeUnit.SECONDS); - assertNotNullAndRelease(channel.consume()); - - idleStateHandler.tickRun(1L, TimeUnit.SECONDS); - assertEquals(0, events.size()); - assertEquals(26L, idleStateHandler.tick(TimeUnit.SECONDS)); // 23s + 2s + 1s - - // Consume part of the message every 2 seconds, then be idle for 1 seconds, - // then run the task and we should get an IdleStateEvent because the first trigger - idleStateHandler.tick(2L, TimeUnit.SECONDS); - assertNotNullAndRelease(channel.consumePart(1024)); - idleStateHandler.tick(2L, TimeUnit.SECONDS); - assertNotNullAndRelease(channel.consumePart(1024)); - idleStateHandler.tickRun(1L, TimeUnit.SECONDS); - assertEquals(1, events.size()); - assertEquals(31L, idleStateHandler.tick(TimeUnit.SECONDS)); // 26s + 2s + 2s + 1s - events.clear(); - - // Consume part of the message every 2 seconds, then be idle for 1 seconds, - // then consume all the rest of the message, then run the task and we shouldn't - // get an IdleStateEvent because the data is flowing and we haven't been idle for long enough! - idleStateHandler.tick(2L, TimeUnit.SECONDS); - assertNotNullAndRelease(channel.consumePart(1024)); - idleStateHandler.tick(2L, TimeUnit.SECONDS); - assertNotNullAndRelease(channel.consumePart(1024)); - idleStateHandler.tickRun(1L, TimeUnit.SECONDS); - assertEquals(0, events.size()); - assertEquals(36L, idleStateHandler.tick(TimeUnit.SECONDS)); // 31s + 2s + 2s + 1s - idleStateHandler.tick(2L, TimeUnit.SECONDS); - assertNotNullAndRelease(channel.consumePart(1024)); - - // There are no messages left! Advance the ticker by 3 seconds, - // attempt a consume() but it will be null, then advance the - // ticker by an another 2 seconds and we should get an IdleStateEvent - // because we've been idle for 5 seconds. - idleStateHandler.tick(3L, TimeUnit.SECONDS); - assertNull(channel.consume()); - - idleStateHandler.tickRun(2L, TimeUnit.SECONDS); - assertEquals(1, events.size()); - assertEquals(43L, idleStateHandler.tick(TimeUnit.SECONDS)); // 36s + 2s + 3s + 2s - - // q.e.d. - } finally { - channel.finishAndReleaseAll(); - } - } - - private static void assertNotNullAndRelease(Object msg) { - assertNotNull(msg); - ReferenceCountUtil.release(msg); - } - - private interface Action { - void run(EmbeddedChannel channel) throws Exception; - } - - private static class TestableIdleStateHandler extends IdleStateHandler { - - private Runnable task; - - private long delayInNanos; - - private long ticksInNanos; - - TestableIdleStateHandler(boolean observeOutput, - long readerIdleTime, long writerIdleTime, long allIdleTime, - TimeUnit unit) { - super(observeOutput, readerIdleTime, writerIdleTime, allIdleTime, unit); - } - - public long delay(TimeUnit unit) { - return unit.convert(delayInNanos, TimeUnit.NANOSECONDS); - } - - public void run() { - task.run(); - } - - public void tickRun() { - tickRun(delayInNanos, TimeUnit.NANOSECONDS); - } - - public void tickRun(long delay, TimeUnit unit) { - tick(delay, unit); - run(); - } - - /** - * Advances the current ticker by the given amount. - */ - public void tick(long delay, TimeUnit unit) { - ticksInNanos += unit.toNanos(delay); - } - - /** - * Returns {@link #ticksInNanos()} in the given {@link TimeUnit}. - */ - public long tick(TimeUnit unit) { - return unit.convert(ticksInNanos(), TimeUnit.NANOSECONDS); - } - - @Override - long ticksInNanos() { - return ticksInNanos; - } - - @Override - Future schedule(ChannelHandlerContext ctx, Runnable task, long delay, TimeUnit unit) { - this.task = task; - this.delayInNanos = unit.toNanos(delay); - return null; - } - } - - private static class ObservableChannel extends EmbeddedChannel { - - ObservableChannel(ChannelHandler... handlers) { - super(handlers); - } - - @Override - protected void doWrite(ChannelOutboundBuffer in) throws Exception { - // Overridden to change EmbeddedChannel's default behavior. We went to keep - // the messages in the ChannelOutboundBuffer. - } - - private Object consume() { - ChannelOutboundBuffer buf = unsafe().outboundBuffer(); - if (buf != null) { - Object msg = buf.current(); - if (msg != null) { - ReferenceCountUtil.retain(msg); - buf.remove(); - return msg; - } - } - return null; - } - - /** - * Consume the part of a message. - * - * @param byteCount count of byte to be consumed - * @return the message currently being consumed - */ - private Object consumePart(int byteCount) { - ChannelOutboundBuffer buf = unsafe().outboundBuffer(); - if (buf != null) { - Object msg = buf.current(); - if (msg != null) { - ReferenceCountUtil.retain(msg); - buf.removeBytes(byteCount); - return msg; - } - } - return null; - } - } -} diff --git a/handler/src/test/java/io/netty/handler/traffic/FileRegionThrottleTest.java b/handler/src/test/java/io/netty/handler/traffic/FileRegionThrottleTest.java deleted file mode 100644 index a9d8a190e2..0000000000 --- a/handler/src/test/java/io/netty/handler/traffic/FileRegionThrottleTest.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version - * 2.0 (the "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.traffic; - -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.ChannelInitializer; -import io.netty.channel.DefaultFileRegion; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.codec.LineBasedFrameDecoder; -import io.netty.util.CharsetUtil; -import io.netty.util.internal.PlatformDependent; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.net.SocketAddress; -import java.nio.charset.Charset; -import java.util.Random; -import java.util.concurrent.CountDownLatch; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class FileRegionThrottleTest { - private static final byte[] BYTES = new byte[64 * 1024 * 4]; - private static final long WRITE_LIMIT = 64 * 1024; - private static File tmp; - private EventLoopGroup group; - - @BeforeAll - public static void beforeClass() throws IOException { - final Random r = new Random(); - for (int i = 0; i < BYTES.length; i++) { - BYTES[i] = (byte) r.nextInt(255); - } - - tmp = PlatformDependent.createTempFile("netty-traffic", ".tmp", null); - tmp.deleteOnExit(); - try (FileOutputStream out = new FileOutputStream(tmp)) { - out.write(BYTES); - out.flush(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - @BeforeEach - public void setUp() { - group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - } - - @AfterEach - public void tearDown() { - group.shutdownGracefully(); - } - - @Disabled("This test is flaky, need more investigation") - @Test - public void testGlobalWriteThrottle() throws Exception { - final CountDownLatch latch = new CountDownLatch(1); - final GlobalTrafficShapingHandler gtsh = new GlobalTrafficShapingHandler(group, WRITE_LIMIT, 0); - ServerBootstrap bs = new ServerBootstrap(); - bs.group(group).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer() { - @Override - protected void initChannel(SocketChannel ch) { - ch.pipeline().addLast(new LineBasedFrameDecoder(Integer.MAX_VALUE)); - ch.pipeline().addLast(new MessageDecoder()); - ch.pipeline().addLast(gtsh); - } - }); - Channel sc = bs.bind(0).get(); - Channel cc = clientConnect(sc.localAddress(), new ReadHandler(latch)); - - long start = TrafficCounter.milliSecondFromNano(); - cc.writeAndFlush(Unpooled.copiedBuffer("send-file\n", CharsetUtil.US_ASCII)).sync(); - latch.await(); - long timeTaken = TrafficCounter.milliSecondFromNano() - start; - assertTrue(timeTaken > 3000, "Data streamed faster than expected"); - sc.close().sync(); - cc.close().sync(); - } - - private Channel clientConnect(final SocketAddress server, final ReadHandler readHandler) throws Exception { - Bootstrap bc = new Bootstrap(); - bc.group(group).channel(NioSocketChannel.class).handler(new ChannelInitializer() { - @Override - protected void initChannel(SocketChannel ch) { - ch.pipeline().addLast(readHandler); - } - }); - return bc.connect(server).get(); - } - - private static final class MessageDecoder implements ChannelHandler { - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - if (msg instanceof ByteBuf) { - ByteBuf buf = (ByteBuf) msg; - String message = buf.toString(Charset.defaultCharset()); - buf.release(); - if ("send-file".equals(message)) { - RandomAccessFile raf = new RandomAccessFile(tmp, "r"); - ctx.channel().writeAndFlush(new DefaultFileRegion(raf.getChannel(), 0, tmp.length())); - } - } - } - } - - private static final class ReadHandler implements ChannelHandler { - private long bytesTransferred; - private final CountDownLatch latch; - - ReadHandler(CountDownLatch latch) { - this.latch = latch; - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - if (msg instanceof ByteBuf) { - ByteBuf buf = (ByteBuf) msg; - bytesTransferred += buf.readableBytes(); - buf.release(); - if (bytesTransferred == tmp.length()) { - latch.countDown(); - } - } - } - } -} diff --git a/handler/src/test/java/io/netty/handler/traffic/TrafficShapingHandlerTest.java b/handler/src/test/java/io/netty/handler/traffic/TrafficShapingHandlerTest.java deleted file mode 100644 index 80dff79e5c..0000000000 --- a/handler/src/test/java/io/netty/handler/traffic/TrafficShapingHandlerTest.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.traffic; - -import io.netty.bootstrap.Bootstrap; -import io.netty.bootstrap.ServerBootstrap; -import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.local.LocalAddress; -import io.netty.channel.local.LocalChannel; -import io.netty.channel.local.LocalHandler; -import io.netty.channel.local.LocalServerChannel; -import io.netty.util.Attribute; -import io.netty.util.CharsetUtil; -import io.netty.util.concurrent.EventExecutorGroup; -import io.netty.util.concurrent.SingleThreadEventExecutor; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.Test; - -import java.util.concurrent.TimeUnit; - -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; - -public class TrafficShapingHandlerTest { - - private static final long READ_LIMIT_BYTES_PER_SECOND = 1; - private static final EventExecutorGroup SES = new SingleThreadEventExecutor(); - private static final MultithreadEventLoopGroup GROUP = - new MultithreadEventLoopGroup(1, LocalHandler.newFactory()); - - @AfterAll - public static void destroy() { - GROUP.shutdownGracefully(); - SES.shutdownGracefully(0, 0, TimeUnit.MILLISECONDS); - } - - @Test - public void testHandlerRemove() throws Exception { - testHandlerRemove0(new ChannelTrafficShapingHandler(0, READ_LIMIT_BYTES_PER_SECOND)); - GlobalTrafficShapingHandler trafficHandler1 = - new GlobalTrafficShapingHandler(SES, 0, READ_LIMIT_BYTES_PER_SECOND); - try { - testHandlerRemove0(trafficHandler1); - } finally { - trafficHandler1.release(); - } - GlobalChannelTrafficShapingHandler trafficHandler2 = - new GlobalChannelTrafficShapingHandler(SES, 0, - READ_LIMIT_BYTES_PER_SECOND, 0, READ_LIMIT_BYTES_PER_SECOND); - try { - testHandlerRemove0(trafficHandler2); - } finally { - trafficHandler2.release(); - } - } - - private static void testHandlerRemove0(final AbstractTrafficShapingHandler trafficHandler) - throws Exception { - Channel svrChannel = null; - Channel ch = null; - try { - ServerBootstrap serverBootstrap = new ServerBootstrap(); - serverBootstrap.channel(LocalServerChannel.class).group(GROUP, GROUP) - .childHandler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) { - ch.pipeline().addLast(new ChannelHandler() { - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - ctx.writeAndFlush(msg); - } - }); - } - }); - final LocalAddress svrAddr = new LocalAddress("foo"); - svrChannel = serverBootstrap.bind(svrAddr).get(); - Bootstrap bootstrap = new Bootstrap(); - bootstrap.channel(LocalChannel.class).group(GROUP).handler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) { - ch.pipeline().addLast("traffic-shaping", trafficHandler); - } - }); - ch = bootstrap.connect(svrAddr).get(); - Attribute attr = ch.attr(AbstractTrafficShapingHandler.REOPEN_TASK); - assertNull(attr.get()); - ch.writeAndFlush(Unpooled.wrappedBuffer("foo".getBytes(CharsetUtil.UTF_8))); - ch.writeAndFlush(Unpooled.wrappedBuffer("bar".getBytes(CharsetUtil.UTF_8))).await(); - assertNotNull(attr.get()); - final Channel clientChannel = ch; - ch.executor().submit(() -> { - clientChannel.pipeline().remove("traffic-shaping"); - }).await(); - //the attribute--reopen task must be released. - assertNull(attr.get()); - } finally { - if (ch != null) { - ch.close().sync(); - } - if (svrChannel != null) { - svrChannel.close().sync(); - } - } - } - -} diff --git a/handler/src/test/resources/io/netty/handler/ssl/mutual_auth_client.p12 b/handler/src/test/resources/io/net5/handler/ssl/mutual_auth_client.p12 similarity index 100% rename from handler/src/test/resources/io/netty/handler/ssl/mutual_auth_client.p12 rename to handler/src/test/resources/io/net5/handler/ssl/mutual_auth_client.p12 diff --git a/handler/src/test/resources/io/netty/handler/ssl/mutual_auth_invalid_client.p12 b/handler/src/test/resources/io/net5/handler/ssl/mutual_auth_invalid_client.p12 similarity index 100% rename from handler/src/test/resources/io/netty/handler/ssl/mutual_auth_invalid_client.p12 rename to handler/src/test/resources/io/net5/handler/ssl/mutual_auth_invalid_client.p12 diff --git a/handler/src/test/resources/io/netty/handler/ssl/mutual_auth_server.p12 b/handler/src/test/resources/io/net5/handler/ssl/mutual_auth_server.p12 similarity index 100% rename from handler/src/test/resources/io/netty/handler/ssl/mutual_auth_server.p12 rename to handler/src/test/resources/io/net5/handler/ssl/mutual_auth_server.p12 diff --git a/handler/src/test/resources/io/netty/handler/ssl/rsaValidation-user-certs.p12 b/handler/src/test/resources/io/net5/handler/ssl/rsaValidation-user-certs.p12 similarity index 100% rename from handler/src/test/resources/io/netty/handler/ssl/rsaValidation-user-certs.p12 rename to handler/src/test/resources/io/net5/handler/ssl/rsaValidation-user-certs.p12 diff --git a/handler/src/test/resources/io/netty/handler/ssl/rsaValidations-server-keystore.p12 b/handler/src/test/resources/io/net5/handler/ssl/rsaValidations-server-keystore.p12 similarity index 100% rename from handler/src/test/resources/io/netty/handler/ssl/rsaValidations-server-keystore.p12 rename to handler/src/test/resources/io/net5/handler/ssl/rsaValidations-server-keystore.p12 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 deleted file mode 100644 index cafaea4224..0000000000 --- a/handler/src/test/resources/io/netty/handler/ssl/ec_params_unsupported.pem +++ /dev/null @@ -1,18 +0,0 @@ ------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----- diff --git a/handler/src/test/resources/io/netty/handler/ssl/generate-certificate.sh b/handler/src/test/resources/io/netty/handler/ssl/generate-certificate.sh deleted file mode 100755 index b9c5bd119b..0000000000 --- a/handler/src/test/resources/io/netty/handler/ssl/generate-certificate.sh +++ /dev/null @@ -1,18 +0,0 @@ -# Generate CA key and certificate. -openssl req -x509 -newkey rsa:2048 -days 3650 -keyout rsapss-ca-key.pem -out rsapss-ca-cert.cert -subj "/C=GB/O=Netty/OU=netty-parent/CN=west.int" -sigopt rsa_padding_mode:pss -sha256 -sigopt rsa_pss_saltlen:20 - -# Generate user key nand. -openssl req -newkey rsa:2048 -keyout rsapss-user-key.pem -out rsaValidation-req.pem -subj "/C=GB/O=Netty/OU=netty-parent/CN=c1" -sigopt rsa_padding_mode:pss -sha256 -sigopt rsa_pss_saltlen:20 - -# Sign user cert request using CA certificate. -openssl x509 -req -in rsaValidation-req.pem -days 365 -extensions ext -extfile rsapss-signing-ext.txt -CA rsapss-ca-cert.cert -CAkey rsapss-ca-key.pem -CAcreateserial -out rsapss-user-singed.cert -sigopt rsa_padding_mode:pss -sha256 -sigopt rsa_pss_saltlen:20 - -# Create user certificate keystore. -openssl pkcs12 -export -out rsaValidation-user-certs.p12 -inkey rsapss-user-key.pem -in rsapss-user-singed.cert - -# create keystore for the -openssl pkcs12 -in rsapss-ca-cert.cert -inkey rsapss-ca-key.pem -passin pass:password -certfile rsapss-ca-cert.cert -export -out rsaValidations-server-keystore.p12 -passout pass:password -name localhost - - -# Create Trustore to verify the EndEntity certificate we have created. -keytool -importcert -storetype PKCS12 -keystore rsaValidations-truststore.p12 -storepass password -alias ca -file rsapss-ca-cert.cert -noprompt diff --git a/handler/src/test/resources/io/netty/handler/ssl/generate-certs.sh b/handler/src/test/resources/io/netty/handler/ssl/generate-certs.sh deleted file mode 100755 index bdcb080de2..0000000000 --- a/handler/src/test/resources/io/netty/handler/ssl/generate-certs.sh +++ /dev/null @@ -1,64 +0,0 @@ -#!/bin/sh -# ---------------------------------------------------------------------------- -# Copyright 2021 The Netty Project -# -# The Netty Project licenses this file to you under the Apache License, -# version 2.0 (the "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at: -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- - -# Generate a new, self-signed root CA -openssl req -extensions v3_ca -new -x509 -days 30 -nodes -subj "/CN=NettyTestRoot" -newkey rsa:2048 -sha512 -out mutual_auth_ca.pem -keyout mutual_auth_ca.key - -# Generate a certificate/key for the server -openssl req -new -keyout mutual_auth_server.key -nodes -newkey rsa:2048 -subj "/CN=NettyTestServer" | \ - openssl x509 -req -CAkey mutual_auth_ca.key -CA mutual_auth_ca.pem -days 36500 -set_serial $RANDOM -sha512 -out mutual_auth_server.pem - -# Generate a certificate/key for the server to use for Hostname Verification via localhost -openssl req -new -keyout localhost_server_rsa.key -nodes -newkey rsa:2048 -subj "/CN=localhost" | \ - openssl x509 -req -CAkey mutual_auth_ca.key -CA mutual_auth_ca.pem -days 36500 -set_serial $RANDOM -sha512 -out localhost_server.pem -openssl pkcs8 -topk8 -inform PEM -outform PEM -in localhost_server_rsa.key -out localhost_server.key -nocrypt -rm localhost_server_rsa.key - -# Generate a certificate/key for the server to fail for Hostname Verification via localhost -openssl req -new -keyout notlocalhost_server_rsa.key -nodes -newkey rsa:2048 -subj "/CN=NOTlocalhost" | \ - openssl x509 -req -CAkey mutual_auth_ca.key -CA mutual_auth_ca.pem -days 36500 -set_serial $RANDOM -sha512 -out notlocalhost_server.pem -openssl pkcs8 -topk8 -inform PEM -outform PEM -in notlocalhost_server_rsa.key -out notlocalhost_server.key -nocrypt -rm notlocalhost_server_rsa.key - -# Generate an invalid intermediate CA which will be used to sign the client certificate -openssl req -new -keyout mutual_auth_invalid_intermediate_ca.key -nodes -newkey rsa:2048 -subj "/CN=NettyTestInvalidIntermediate" | \ - openssl x509 -req -CAkey mutual_auth_ca.key -CA mutual_auth_ca.pem -days 36500 -set_serial $RANDOM -sha512 -out mutual_auth_invalid_intermediate_ca.pem - -# Generate a client certificate signed by the invalid intermediate CA -openssl req -new -keyout mutual_auth_invalid_client.key -nodes -newkey rsa:2048 -subj "/CN=NettyTestInvalidClient/UID=ClientWithInvalidCa" | \ - openssl x509 -req -CAkey mutual_auth_invalid_intermediate_ca.key -CA mutual_auth_invalid_intermediate_ca.pem -days 36500 -set_serial $RANDOM -sha512 -out mutual_auth_invalid_client.pem - -# Generate a valid intermediate CA which will be used to sign the client certificate -openssl req -new -keyout mutual_auth_intermediate_ca.key -nodes -newkey rsa:2048 -out mutual_auth_intermediate_ca.key -openssl req -new -sha512 -key mutual_auth_intermediate_ca.key -subj "/CN=NettyTestIntermediate" -out intermediate.csr -openssl x509 -req -days 1825 -in intermediate.csr -extfile openssl.cnf -extensions v3_ca -CA mutual_auth_ca.pem -CAkey mutual_auth_ca.key -set_serial $RANDOM -out mutual_auth_intermediate_ca.pem - -# Generate a client certificate signed by the intermediate CA -openssl req -new -keyout mutual_auth_client.key -nodes -newkey rsa:2048 -subj "/CN=NettyTestClient/UID=Client" | \ - openssl x509 -req -CAkey mutual_auth_intermediate_ca.key -CA mutual_auth_intermediate_ca.pem -days 36500 -set_serial $RANDOM -sha512 -out mutual_auth_client.pem - -# For simplicity, squish everything down into PKCS#12 keystores -cat mutual_auth_invalid_intermediate_ca.pem mutual_auth_ca.pem > mutual_auth_invalid_client_cert_chain.pem -cat mutual_auth_intermediate_ca.pem mutual_auth_ca.pem > mutual_auth_client_cert_chain.pem -openssl pkcs12 -export -in mutual_auth_server.pem -inkey mutual_auth_server.key -certfile mutual_auth_ca.pem -out mutual_auth_server.p12 -password pass:example -openssl pkcs12 -export -in mutual_auth_invalid_client.pem -inkey mutual_auth_invalid_client.key -certfile mutual_auth_invalid_client_cert_chain.pem -out mutual_auth_invalid_client.p12 -password pass:example -openssl pkcs12 -export -in mutual_auth_client.pem -inkey mutual_auth_client.key -certfile mutual_auth_client_cert_chain.pem -out mutual_auth_client.p12 -password pass:example - -# Clean up intermediate files -rm intermediate.csr -rm mutual_auth_ca.key mutual_auth_invalid_client.key mutual_auth_client.key mutual_auth_server.key mutual_auth_invalid_intermediate_ca.key mutual_auth_intermediate_ca.key -rm mutual_auth_invalid_client.pem mutual_auth_client.pem mutual_auth_server.pem mutual_auth_client_cert_chain.pem mutual_auth_invalid_intermediate_ca.pem mutual_auth_intermediate_ca.pem mutual_auth_invalid_client_cert_chain.pem diff --git a/handler/src/test/resources/io/netty/handler/ssl/localhost_server.key b/handler/src/test/resources/io/netty/handler/ssl/localhost_server.key deleted file mode 100644 index 9aa6611400..0000000000 --- a/handler/src/test/resources/io/netty/handler/ssl/localhost_server.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDYrLtMlZzoe2BP -iCURF3So5XNLfsOLcAVERXXjnxqX6Mex55WdJiy6uWTFKbRHWJdbWELdZxVl5+GX -pMv3OdkKZt+19ZdSfByv6bB5RNdZOEGnKOHSY2XdnzYnF5JBaWEx0fvtvIPZOUlW -DWgsQzJk1UQhu+XnBc7P1hHYNvwsVNOR+HD9LGebDy+UcfiL34XwAyBdHUsbcIr8 -hltABcj6vNbqOLndpU86DxU9z9b1PDmkFVfisElhpDEhpxmTCwI22Us1GC8D81LM -ZzMlbWSzTfNPEuqNzJYGiFt/XPwPkPPyVvti0XWPBQpwzJFFUX5xKsOGERolELRT -0yNQYznFAgMBAAECggEAOFR/xSNITbB1k3ejm1PrwlUUqlXkZIXU+LDOO0UL1t5v -vDKm1Not2sWECzYSZlID132UtJauG3YzUgdH95gUcv3XvyiAFLOriZhJht181vcn -KlwYiWfJ/dn8bCFWpqbM2/TpeB8AcCLSjAqkQI2ftlMziUmeNXdvEt1mej2hRay1 -ULfoxlC0mftNRQptD5gBFzrc47O4mVpVEQt4yS3Qyzp2/9ds9UkhaCIFpXPVCalZ -ds7R+bDDP+wiYTkUcd8fvelaMkD3Wcy8DedGRShhILZvBYTDdWcpJ7+e5EkNlEq4 -+Ys4Y/u6aFDJD53g3zCaJhatmdAZcct2MMmWH1vewQKBgQD3Y2S245cad1D9AqYD -ChZGp95EfRo3EzXk4VkE50bjZXjHq9fD8T0CWEZGWQZrXJCR+vBpEURy0mrPD8se -QQ0Q5+I27RadtfPnMd6ry9nDGMPxyd/10vzU6LazzLNE+uf9ljF1RHZu1iDAvInR -r1cQGbn/wKBF6BurPPIXABZEuQKBgQDgN6JHbIfDzHKhwEoUTvRrYJsTXqplD+h0 -Whg+kSQyhtKdlpINFOoEj8FUNJvTjG8les1aoajyWIqikVdvHto/mrxrSIeRkEmt -X+KG+5ld2n466tzv1DmVcIGXSrBrH3lA0i6R8Ly26FLSqw0Z12fx5GUUa1qaVRqo -rwcrIZovbQKBgHa2mojs9AC+Sv3uvG1u9LuZKJ7jDaZqMI2R2d7xgOH0Op5Ohy6+ -39D1PVvasqroc3Op4J36rEcRVDHi2Uy+WJ/JNpO2+AhcXRuPodP88ZWel8C6aB+V -zL/6oFntnAU5BgR5g2hLny2W0YbLsrMNmhDe15O0AvUo6cYla+K/pu/5AoGACr/g -EdiMMcDthf+4DX0zjqpVBPq25J18oYdoPierOpjoJBIB8oqcJZfWxvi2t8+1zHA0 -xDGX7fZ8vwqEzJkIEaCTg/k4NqxaO+uq6pnJYoyFHMIB0aW1FQsNy3kTOC+MGqV5 -Ahoukf5VajA1MpX3L8upZO84qsmFu6yYhWLZB4kCgYBlgSD5G4q6rX4ELa3XG61h -fDtu75IYEsjWm4vgJzHjeYT2xPIm9OFFYXjPghto0f1oH37ODD3DoXmsnmddgpmn -tH7aRWWHsSpB5zVgftV4urNCIsm87LWw8mvUGgCwYV1CtCX8warKokfeoA2ltz4u -oeuUzo98hN+aKRU5RO6Bmg== ------END PRIVATE KEY----- diff --git a/handler/src/test/resources/io/netty/handler/ssl/localhost_server.pem b/handler/src/test/resources/io/netty/handler/ssl/localhost_server.pem deleted file mode 100644 index 70759b29e5..0000000000 --- a/handler/src/test/resources/io/netty/handler/ssl/localhost_server.pem +++ /dev/null @@ -1,17 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICozCCAYsCAnS/MA0GCSqGSIb3DQEBDQUAMBgxFjAUBgNVBAMTDU5ldHR5VGVz -dFJvb3QwIBcNMTcwMjE3MDMzMzQ0WhgPMjExNzAxMjQwMzMzNDRaMBQxEjAQBgNV -BAMTCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANis -u0yVnOh7YE+IJREXdKjlc0t+w4twBURFdeOfGpfox7HnlZ0mLLq5ZMUptEdYl1tY -Qt1nFWXn4Zeky/c52Qpm37X1l1J8HK/psHlE11k4Qaco4dJjZd2fNicXkkFpYTHR -++28g9k5SVYNaCxDMmTVRCG75ecFzs/WEdg2/CxU05H4cP0sZ5sPL5Rx+IvfhfAD -IF0dSxtwivyGW0AFyPq81uo4ud2lTzoPFT3P1vU8OaQVV+KwSWGkMSGnGZMLAjbZ -SzUYLwPzUsxnMyVtZLNN808S6o3MlgaIW39c/A+Q8/JW+2LRdY8FCnDMkUVRfnEq -w4YRGiUQtFPTI1BjOcUCAwEAATANBgkqhkiG9w0BAQ0FAAOCAQEAQNXnwE2MJFy5 -ti07xyi8h/mY0Kl1dwZUqx4F9D9eoxLCq2/p3h/Z18AlOmjdW06pvC2sGtQtyEqL -YjuQFbMjXRo9c+6+d+xwdDKTu7+XOTHvznJ8xJpKnFOlohGq/n3efBIJSsaeasTU -slFzmdKYABDZzbsQ4X6YCIOF4XVdEQqmXpS+uEbn5C2sVtG+LXI8srmkVGpCcRew -SuTGanwxLparhBBeN1ARjKzNxXUWuK2UKZ9p8c7n7TXGhd12ZNTcLhk4rCnOFq1J -ySFvP5YL2q29fpEt+Tq0zm3V7An2qtaNDp26cEdevtKPjRyOLkCJx8OlZxc9DZvJ -HjalFDoRUw== ------END CERTIFICATE----- diff --git a/handler/src/test/resources/io/netty/handler/ssl/mutual_auth_ca.pem b/handler/src/test/resources/io/netty/handler/ssl/mutual_auth_ca.pem deleted file mode 100644 index 9c9241bc65..0000000000 --- a/handler/src/test/resources/io/netty/handler/ssl/mutual_auth_ca.pem +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDLDCCAhSgAwIBAgIJAO1m5pioZhLLMA0GCSqGSIb3DQEBDQUAMBgxFjAUBgNV -BAMTDU5ldHR5VGVzdFJvb3QwHhcNMTcwMjE3MDMzMzQ0WhcNMTcwMzE5MDMzMzQ0 -WjAYMRYwFAYDVQQDEw1OZXR0eVRlc3RSb290MIIBIjANBgkqhkiG9w0BAQEFAAOC -AQ8AMIIBCgKCAQEAnC7Y/p/TSWI1KxBKETfFKaRWCPEkoYn5G973WbCF0VDT90PX -xK6yHvhqNdDQZPmddgfDAQfjekHeeIFkjCKlvQu0js0G4Bubz4NffNumd/Mgsix8 -SWJ13lPk+Ly4PDv0bK1zB6BxP1qQm1qxVwsPy9zNP8ylJrM0Div4TXHmnWOfc0JD -4/XPpfeUHH1tt/GMtsS2Gx6EpTVPD2w7LDKUza1/rQ7d9sqmFpgsNcI9Db/sAtFP -lK2iJku5WIXQkmHimn4bqZ9wkiXJ85pm5ggGQqGMPSbe+2Lh24AvZMIBiwPbkjEU -EDFXEJfKOC3Dl71JgWOthtHZ9vcCRDQ3Sky6AQIDAQABo3kwdzAdBgNVHQ4EFgQU -qT+cH8qrebiVPpKCBQDB6At2iOAwSAYDVR0jBEEwP4AUqT+cH8qrebiVPpKCBQDB -6At2iOChHKQaMBgxFjAUBgNVBAMTDU5ldHR5VGVzdFJvb3SCCQDtZuaYqGYSyzAM -BgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4IBAQCEemXTIew4pR2cHEFpVsW2 -bLHXLAnC23wBMT46D3tqyxscukMYjFuWosCdEsgRW8d50BXy9o4dHWeg94+aDo3A -DX4OTRN/veQGIG7dgM6poDzFuVJlSN0ubKKg6gpDD60IhopZpMviFAOsmzr7OXwS -9hjbTqUWujMIEHQ95sPlQFdSaavYSFfqhSltWmVCPSbArxrw0lZ2QcnUqGN47EFp -whc5wFB+rSw/ojU1jBLMvgvgzf/8V8zr1IBTDSiHNlknGqGpOOaookzUh95YRiAT -hH82y9bBeflqroOeztqMpONpWoZjlz0sWbJNvXztXINL7LaNmVYOcoUrCcxPS54T ------END CERTIFICATE----- diff --git a/handler/src/test/resources/io/netty/handler/ssl/notlocalhost_server.key b/handler/src/test/resources/io/netty/handler/ssl/notlocalhost_server.key deleted file mode 100644 index 406ad48a78..0000000000 --- a/handler/src/test/resources/io/netty/handler/ssl/notlocalhost_server.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDEkT/UNROZLBfR -LvmGv6TmP7IftPH3P2KQ16sCBEzsISy51kA7IdIEBsAEho3gTgqRsJwNpGQeRJTD -PFDEDDB5MJnxKsUkN2PAEC1VF5CD0E7grqrUXOn/sqNbE9jDe7sziVaZmV35EnNj -f+RW55mHtoFNW8pDb35v14oKiB4mp31fkFn84xeAwG+stAhY50qcmcSQ9woAx7vK -vv8CTZhEYeHrnd8FkLYF1RZkn2tvcLvS7RIUQOBIY9IZa+hf3AMJE0khJsQgO1Z3 -eOLIlq2tOW4VVzb84M/9te9L/KXQcjWkEhX0qbITSRLd7Oqgs8/bUMY9oq7gMWSj -IXkzMGiLAgMBAAECggEAPlEyJCujh/bzogy7bN3LAHgHF4g9Ab6ll/fXri8A2Ad4 -b34eJOEG/OHK9GRYDFmRfji4cJbCatohM3gmvtCCeZlP5KZDk8S/qf827/glh7R9 -nTuiyuqeAMN4iIJVQTwz0BXlT5LQuQsBa/T6xhZslRrmCUllaVWqhviVOduHe4cl -ftK5D1IlsBElxeWSXy5Cbu7FhWz4dbJjduuXKfbEPpoHxk/Nsg7bhDh86kOablUG -FraqvMg9GfeOcFlP+PJV4Xlu02GmY7mYBgRnlccErDSFd6QvZIFcUW5hca+B937i -FiToQ+uvYaNeD3IZ/t8PDKIb2APxzRrwj3J5j+fcYQKBgQDtk95rH2xdncYlqYsg -3Lbvo/HZUv38uqjgUofMV87ijcRpKc8l7pbeg/xG5IIYwICaIVuc5idHJGpc63GC -ZZSxwPPfREU+9cZsf3Fn2pTP1Knf9gfjLW7UZi5SdJ7F6S6zMQawlTjKsvvvXTvE -vIeO5BU07jhhEE5W4W+aJ5AycQKBgQDTz0pJn5VkzigdMvyki+QKuF+DZUWUuesL -TzjPm6xblBARUL1vpOLUsm4IR7anOH/6UWQY7+ojTyo1S3QqfLVP4SMkqAEaP8j8 -Qixy9KD0Ma2vUF2FaE55lHcnJbGjo8gd4elry1oLRsl26HKi8/fXAZfMkwASYr92 -KaY6PjyQuwKBgCXs+w6iv4vaSe1tOVaPpWxwAYWt5/nWWmslAaSvR8mG1BntoLHv -eoofC8DNba9B28W/GX7chtymhYw70az9RlVx9fxoS52XYoujdJkdna/nxQ6Jmgy8 -Dfs+LJrhHw99AAV/CBBcYeqJ1AoMGiR37DP1WSVP+hQUpEzeRBCZzD0hAoGAJHS5 -RJLDE+an4N61vVyWk4HIbsUvhLm7SucH0DPrJpgFa/WJpX2rzRApACPXvFBtNphr -sh8ZHZqymRmzlNyCGI2XjuebyPjRjxmUrPSN/3nNey9J6XxahXKYT5SdTp0SBJKt -qnAFv4sfgefgU0vKjKcpiXyDtqZobd4dTkkIA6ECgYB2QkGweLbB+Auus5KcuWt1 -iG1qJIv5cna0VGaUkhwYDsCBgTM5i8wpdl1UvCVWuETU8+FO19jBx5X/Gv6TwKE8 -vWY83HzGvoIcHpXOHlfMFlZnY1Fl6RO5V1XlbzJzjButqdiGEG1oqygVbpn8ui9i -c0AdsCH2P2Ef28bVgiwYBA== ------END PRIVATE KEY----- diff --git a/handler/src/test/resources/io/netty/handler/ssl/notlocalhost_server.pem b/handler/src/test/resources/io/netty/handler/ssl/notlocalhost_server.pem deleted file mode 100644 index b8ada86a75..0000000000 --- a/handler/src/test/resources/io/netty/handler/ssl/notlocalhost_server.pem +++ /dev/null @@ -1,17 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICpjCCAY4CAnvYMA0GCSqGSIb3DQEBDQUAMBgxFjAUBgNVBAMTDU5ldHR5VGVz -dFJvb3QwIBcNMTcwMjE3MDMzMzQ1WhgPMjExNzAxMjQwMzMzNDVaMBcxFTATBgNV -BAMTDE5PVGxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB -AMSRP9Q1E5ksF9Eu+Ya/pOY/sh+08fc/YpDXqwIETOwhLLnWQDsh0gQGwASGjeBO -CpGwnA2kZB5ElMM8UMQMMHkwmfEqxSQ3Y8AQLVUXkIPQTuCuqtRc6f+yo1sT2MN7 -uzOJVpmZXfkSc2N/5FbnmYe2gU1bykNvfm/XigqIHianfV+QWfzjF4DAb6y0CFjn -SpyZxJD3CgDHu8q+/wJNmERh4eud3wWQtgXVFmSfa29wu9LtEhRA4Ehj0hlr6F/c -AwkTSSEmxCA7Vnd44siWra05bhVXNvzgz/2170v8pdByNaQSFfSpshNJEt3s6qCz -z9tQxj2iruAxZKMheTMwaIsCAwEAATANBgkqhkiG9w0BAQ0FAAOCAQEAbYBv+biZ -9wr8LIBKkUp+gMRlPj3YHEbMqSjDpDmtgE9/rYWwrrWbC3Nux6Otwm8lCuofxdZ0 -HcpMzj6npOzyW+r21Xhdnwd8b4GclPXJHUP09TxzV6yS+8t6pHVvzVGFGKMPUSt2 -L7sKz6/BZFZqynZxoh5LPmdxoFVfFY/05tLCazzz8SWBjzO6kfVLL2RJKIXKeBYm -ipRvCi2YWoHivfg1D+kjVCk42F148R53HQURHiBUMkTLuUzgTnUpg6TKRIJXGsz3 -onXWJ1k9a27vGvLMxS5EGtcZRKFOAq0pZ5JJg5E/DkGiFAZb2BonvVHtITU9RqaS -Uvf16yaUsKDHFA== ------END CERTIFICATE----- diff --git a/handler/src/test/resources/io/netty/handler/ssl/openssl.cnf b/handler/src/test/resources/io/netty/handler/ssl/openssl.cnf deleted file mode 100644 index 74fee1590d..0000000000 --- a/handler/src/test/resources/io/netty/handler/ssl/openssl.cnf +++ /dev/null @@ -1,123 +0,0 @@ -# -# This OpenSSL configuration file is necessary to generate an Intermediate CA -# which is capable of signing other certificates. The most important part is -# the [ v3_ca ] profile and the "basicConstraints = CA:true" value -# - -# This definition stops the following lines choking if HOME isn't -# defined. -# For the CA policy -[ policy_match ] -countryName = match -stateOrProvinceName = match -organizationName = match -organizationalUnitName = optional -commonName = supplied -emailAddress = optional - -# For the 'anything' policy -# At this point in time, you must list all acceptable 'object' -# types. -[ policy_anything ] -countryName = optional -stateOrProvinceName = optional -localityName = optional -organizationName = optional -organizationalUnitName = optional -commonName = supplied -emailAddress = optional - -#################################################################### -[ req ] -default_bits = 2048 -default_keyfile = privkey.pem -distinguished_name = req_distinguished_name -attributes = req_attributes -x509_extensions = v3_ca # The extentions to add to the self signed cert - -# Passwords for private keys if not present they will be prompted for -# input_password = secret -# output_password = secret - -# This sets a mask for permitted string types. There are several options. -# default: PrintableString, T61String, BMPString. -# pkix : PrintableString, BMPString (PKIX recommendation before 2004) -# utf8only: only UTF8Strings (PKIX recommendation after 2004). -# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). -# MASK:XXXX a literal mask value. -# WARNING: ancient versions of Netscape crash on BMPStrings or UTF8Strings. -string_mask = utf8only - -# req_extensions = v3_req # The extensions to add to a certificate request - -[ req_distinguished_name ] -countryName = Country Name (2 letter code) -countryName_default = AU -countryName_min = 2 -countryName_max = 2 - -stateOrProvinceName = State or Province Name (full name) -stateOrProvinceName_default = Some-State - -localityName = Locality Name (eg, city) - -0.organizationName = Organization Name (eg, company) -0.organizationName_default = Internet Widgits Pty Ltd - -# we can do this but it is not needed normally :-) -#1.organizationName = Second Organization Name (eg, company) -#1.organizationName_default = World Wide Web Pty Ltd - -organizationalUnitName = Organizational Unit Name (eg, section) -#organizationalUnitName_default = - -commonName = Common Name (e.g. server FQDN or YOUR name) -commonName_max = 64 - -emailAddress = Email Address -emailAddress_max = 64 - -# SET-ex3 = SET extension number 3 - -[ req_attributes ] -challengePassword = A challenge password -challengePassword_min = 4 -challengePassword_max = 20 - -unstructuredName = An optional company name - -[ usr_cert ] - -# These extensions are added when 'ca' signs a request. - -# This goes against PKIX guidelines but some CAs do it and some software -# requires this to avoid interpreting an end user certificate as a CA. - -basicConstraints=CA:FALSE - -# This will be displayed in Netscape's comment listbox. -nsComment = "OpenSSL Generated Certificate" - -# PKIX recommendations harmless if included in all certificates. -subjectKeyIdentifier=hash -authorityKeyIdentifier=keyid,issuer - -# This is required for TSA certificates. -# extendedKeyUsage = critical,timeStamping - -[ v3_req ] - -# Extensions to add to a certificate request - -basicConstraints = CA:FALSE -keyUsage = nonRepudiation, digitalSignature, keyEncipherment - -[ v3_ca ] - -subjectKeyIdentifier=hash -authorityKeyIdentifier=keyid:always,issuer -# This is what PKIX recommends but some broken software chokes on critical -# extensions. -#basicConstraints = critical,CA:true -# So we do this instead. -basicConstraints = CA:true diff --git a/handler/src/test/resources/io/netty/handler/ssl/rsapss-ca-cert.cert b/handler/src/test/resources/io/netty/handler/ssl/rsapss-ca-cert.cert deleted file mode 100644 index f767e738d3..0000000000 --- a/handler/src/test/resources/io/netty/handler/ssl/rsapss-ca-cert.cert +++ /dev/null @@ -1,23 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDxTCCAoKgAwIBAgIUJ2aZ084kIATHBPDJFXVu7SJ4uVcwOAYJKoZIhvcNAQEK -MCugDTALBglghkgBZQMEAgGhGjAYBgkqhkiG9w0BAQgwCwYJYIZIAWUDBAIBMEcx -CzAJBgNVBAYTAkdCMQ4wDAYDVQQKDAVOZXR0eTEVMBMGA1UECwwMbmV0dHktcGFy -ZW50MREwDwYDVQQDDAh3ZXN0LmludDAeFw0yMTA4MjkwNjAxMTNaFw0zMTA4Mjcw -NjAxMTNaMEcxCzAJBgNVBAYTAkdCMQ4wDAYDVQQKDAVOZXR0eTEVMBMGA1UECwwM -bmV0dHktcGFyZW50MREwDwYDVQQDDAh3ZXN0LmludDCCASIwDQYJKoZIhvcNAQEB -BQADggEPADCCAQoCggEBAL+xcxKjWgbYHIRqnP3Sw91SNTwY85ocb+3D4xh7/F6w -cYgXwxgaHRKlk97HUzYZGFEb34BG89EOdDa1DvwxAMaN8sirefjrMLpvmfUD3Yti -kGKj+CM3gh5wFSb9mPPoY/S61+KoRSAeMKeYyFQh5IIJyVqN5mrziu0+t04X4YEw -9nATkmoS1V27Ucmo3OTkNNamqlXqVeiLKhvHtMViRGua8HwfEmjvFOTfyFHudcAz -NFFH9JR9C2g9wuokcWFD3sdFfOZ4DJVN35NrXCO4FhxxcjHOXKRdbtsucFHqCPaE -fVL0qrlkAm3pd9jKnBujC5sQbritg0uvmVuoxzy1jIUCAwEAAaNTMFEwHQYDVR0O -BBYEFAzguQlpxd/3TPhYZqEryBQ6lUdJMB8GA1UdIwQYMBaAFAzguQlpxd/3TPhY -ZqEryBQ6lUdJMA8GA1UdEwEB/wQFMAMBAf8wOAYJKoZIhvcNAQEKMCugDTALBglg -hkgBZQMEAgGhGjAYBgkqhkiG9w0BAQgwCwYJYIZIAWUDBAIBA4IBAQB3jsUwdyFO -9u/abLBGuETWbyuLX7NA9yvQL7cei40fJdsZZpZkHDJvNnrblpdaeFjuAI4vmAqz -odiHzZodSaFCwODFX8oYyBcMTHW99UYiGywskF1NnJKq13r4kP7+7w7ZaE/5YukW -VSeCXTHp1c0umuieluG87MZH4dCZgrvzpZwBeGoLLNyMyo4qHwYfkZiG2rTRpVX3 -+VsWnMOaRVMYrzTB2tPZyAZyRMEfTd0fNi7ufSu6ywrOdziTu6Y1qVh18qDKpPsG -eaSCNQoO5D9vUbiFjxKPJe8hZ0bDWTbVKRpeIrQMeHXnXGPEV5rPOcJUzwnDsGqI -gqr6XlcEs+lp ------END CERTIFICATE----- diff --git a/handler/src/test/resources/io/netty/handler/ssl/rsapss-signing-ext.txt b/handler/src/test/resources/io/netty/handler/ssl/rsapss-signing-ext.txt deleted file mode 100644 index 9716541c0d..0000000000 --- a/handler/src/test/resources/io/netty/handler/ssl/rsapss-signing-ext.txt +++ /dev/null @@ -1,21 +0,0 @@ -[ ext ] -extendedKeyUsage = clientAuth -keyUsage = nonRepudiation, digitalSignature, keyEncipherment -#subjectKeyIdentifier = hash -authorityKeyIdentifier = keyid,issuer - - -[ exts ] -extendedKeyUsage = serverAuth -keyUsage = nonRepudiation, digitalSignature, keyEncipherment -#subjectKeyIdentifier = hash -authorityKeyIdentifier = keyid,issuer -#subjectAltName = @alt_names - -[ extca ] -authorityKeyIdentifier = keyid,issuer -basicConstraints=CA:TRUE -subjectKeyIdentifier = hash - -[alt_names] -DNS.1 = aws-dev-node.skylo.local diff --git a/handler/src/test/resources/io/netty/handler/ssl/test.crt b/handler/src/test/resources/io/netty/handler/ssl/test.crt deleted file mode 100644 index 7d3437e13e..0000000000 --- a/handler/src/test/resources/io/netty/handler/ssl/test.crt +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIC/jCCAeagAwIBAgIIIMONxElm0AIwDQYJKoZIhvcNAQELBQAwPjE8MDoGA1UE -AwwzZThhYzAyZmEwZDY1YTg0MjE5MDE2MDQ1ZGI4YjA1YzQ4NWI0ZWNkZi5uZXR0 -eS50ZXN0MCAXDTEzMDgwMjA3NTEzNloYDzk5OTkxMjMxMjM1OTU5WjA+MTwwOgYD -VQQDDDNlOGFjMDJmYTBkNjVhODQyMTkwMTYwNDVkYjhiMDVjNDg1YjRlY2RmLm5l -dHR5LnRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDb+HBO3C0U -RBKvDUgJHbhIlBye8X/cbNH3lDq3XOOFBz7L4XZKLDIXS+FeQqSAUMo2otmU+Vkj -0KorshMjbUXfE1KkTijTMJlaga2M2xVVt21fRIkJNWbIL0dWFLWyRq7OXdygyFkI -iW9b2/LYaePBgET22kbtHSCAEj+BlSf265+1rNxyAXBGGGccCKzEbcqASBKHOgVp -6pLqlQAfuSy6g/OzGzces3zXRrGu1N3pBIzAIwCW429n52ZlYfYR0nr+REKDnRrP -IIDsWASmEHhBezTD+v0qCJRyLz2usFgWY+7agUJE2yHHI2mTu2RAFngBilJXlMCt -VwT0xGuQxkbHAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAEv8N7Xm8qaY2FgrOc6P -a1GTgA+AOb3aU33TGwAR86f+nLf6BSPaohcQfOeJid7FkFuYInuXl+oqs+RqM/j8 -R0E5BuGYY2wOKpL/PbFi1yf/Kyvft7KVh8e1IUUec/i1DdYTDB0lNWvXXxjfMKGL -ct3GMbEHKvLfHx42Iwz/+fva6LUrO4u2TDfv0ycHuR7UZEuC1DJ4xtFhbpq/QRAj -CyfNx3cDc7L2EtJWnCmivTFA9l8MF1ZPMDSVd4ecQ7B0xZIFQ5cSSFt7WGaJCsGM -zYkU4Fp4IykQcWxdlNX7wJZRwQ2TZJFFglpTiFZdeq6I6Ad9An1Encpz5W8UJ4tv -hmw= ------END CERTIFICATE----- diff --git a/handler/src/test/resources/io/netty/handler/ssl/test2.crt b/handler/src/test/resources/io/netty/handler/ssl/test2.crt deleted file mode 100644 index 7f4b30d446..0000000000 --- a/handler/src/test/resources/io/netty/handler/ssl/test2.crt +++ /dev/null @@ -1,18 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIC6DCCAdACCQCp0Mn/2UCl2TANBgkqhkiG9w0BAQsFADA2MTQwMgYDVQQDDCtj -ZmYyNGEwY2I4NGFmNjExZDdhODFjMGI4MDY4OTA2OC5uZXR0eS50ZXN0MB4XDTE0 -MTAxNzE4NDczM1oXDTE0MTExNjE4NDczM1owNjE0MDIGA1UEAwwrY2ZmMjRhMGNi -ODRhZjYxMWQ3YTgxYzBiODA2ODkwNjgubmV0dHkudGVzdDCCASIwDQYJKoZIhvcN -AQEBBQADggEPADCCAQoCggEBALgddI5XJcUK45ONr4QTfZZxbJJeOYKPEWVIWK/P -Wz6EJXt3hDdpmnaRUKAv4mMIFlxWVkxTqa/dB3hjcm5hPvNgPAUaEWzMtGd32p95 -sJzbxiWvxhf5rqF0n1Zk5KX+EcasiCupNg3TL7gfTSSZfaGSWf460oaCS6WCU4X9 -XTUhys7N5BFM+uQLE048CnkBCO1An980Fau/0+BLXgW+iJC6XWTJbpZ+r7rDpBKl -+HmQQ5tgGlCZcnhmS9bzYT3hoag6JkDoIwbFsVOkwemxZGb8GsGE74/rrzUJ9MdR -/ETCA2km1na6ESst0/wm0qD3clJahP8xEoaJ+W1TFGizRWkCAwEAATANBgkqhkiG -9w0BAQsFAAOCAQEAmeGPRWXzu7+f20ZJA/u6WjcmsUhSTtt0YcBNtii4Pm0snIE9 -UyRBGlvS2uFHTilD7MOYOHX6ATlHZAsfegpiPE5jCvE4CzFPpQaVAT/sKNtsWH43 -ZQHn4NK1DAFIVDysO3AGGhL0mub8iWEYHs81+6tSSFlbDFqwYtw7ueerhVLUIaIa -S0SvtXUVitX2LzMlYCEto2s50fcqOnj8uve/dG8BmiwR1DqqVKkAWAXf8uGhwwD+ -659E3g9vNz6QUchd8K/TIv52i8EDuWu3FElohmfFUXu43A+Z+lbuDrEW3suqTC3y -0JIa2DfHWA7WTyF4UD32aAC+U6BLIOA6WoPi1Q== ------END CERTIFICATE----- diff --git a/handler/src/test/resources/io/netty/handler/ssl/test2_encrypted.pem b/handler/src/test/resources/io/netty/handler/ssl/test2_encrypted.pem deleted file mode 100644 index a17f9dc27e..0000000000 --- a/handler/src/test/resources/io/netty/handler/ssl/test2_encrypted.pem +++ /dev/null @@ -1,29 +0,0 @@ ------BEGIN ENCRYPTED PRIVATE KEY----- -MIIE6jAcBgoqhkiG9w0BDAEDMA4ECCqT2dycwPtCAgIIAASCBMg/Z60Q85kVL5kv -q8WIIY9tbXo/2Q+6rspxdit9SRd86MV9QRdfZ5Vjwt0JTa+Rd1gMaNK4PySW23bq -F2+dD0sjVBcE24Qg0h4BcmL+YBdTftBfk7NDH/rHhsew7DZru9fdDvkO9bV3jXIz -fARW9U7JIfgAi6CfJ8Q1PS7sg6dVtrcjMRIie32x0TSbZrn+h9AaXpLHsC8oXiyY -BhWe4i9B7PobyJ0r/CTBFhbfUCGwRyHac0+bZXvlcwX9wy3W7jagc6RDlznOpowU -FP35CQGeKsJ9WD+yy5MU8X8M8v+eeaJk4oX+PSWJX669CxbYocVP/+LUtOXpe+4h -7yMmVNLUtsgBlY6tNsU0XBQkrqqb+voSxVBEVZ1WTKgLWsE/EiQ2P2GU8Gnr+J6c -/yHxw0D4q9J3jV40SiuXQlgFwlf8u9FuVjOcGxTidfKXyvNqPKqgkf9QD+7E09q3 -JQoNbI/A8BXrpdx9h87Gt0TblPwVJP2nf5whig9W62R4y9SWybUUNr2MFNkvEfKe -1QK8isf+HlvIO+VBYi4jof9HkWLwnAszlkpC+k1cOiSjNRn8QyLzsqX7A/VuS6W8 -6kKeND4yRNA4b7rfQqhyGg7gBwiwN+22UF6SKiikX4TB1ZyLdzlbPe0L+X/Gq0Jz -Kf+8/slgzB5K9WpDtKsARH/lRPAx1rcascvFxMuCJL5O9MO9l4xWDJor71WgPC2N -KwXxvEW3Kyvs3pSgWc8MC0BKcD9WIAahAlAVmSQBxDNWvJlGTgUVhzPqan7h03Fd -nWAxSn315ObfK9rjbqUBO9x/nkSZFS9nApmeiWkOIwVzgNfAfb9md07TYyC/rpK3 -nGIsThekqqQULMQaAPmEFqUj6A/0KlpBj1gZwddYvVvEL/MuQO0QBdz4n/OncxYP -TVoQEqXsndmNQnkuk2Kr4FACV2M9rbr84HJUIZVGGVSM5h80GrRqK03qpTzM8Nkc -e04R4KDpLDKHm+G4xYZbbraIGXNTkhxTqdNA2FyjJWFurmpQyFay55vC6WBFBVNA -BGVIqD1/9K3dJJGlpiHyymRCK9YGvflZlSr7dm7PW7PPEthwTijbAHkABOKsFSiu -xaUj027WIVuDb5FFIAaF3Wmn4GFXvsSH+8L95CQuXGB8J/5Buo+/Hg6S7PeDwrf+ -qNRAfg9vxo+AZOWpWfGEYGHQeX6BxVjdffar9RwL99cele4h2FgBLtIuAXvgLPyx -b+MIjDliCe1Nqx0PCCuaB1xRnaKiwbl7itDidzI8BUAaFcKxbBH2lpr44+vYPVHb -70Xrw55RLvrVYKAcaZgryTNOvbRatifJIMg3kf8V++2rwUMoZ+DQfXin/C4S/2/b -c6I1OvYaGxmI1YiI6qSpOryDSzTNlDEWcdh5feuixiP5RbyaQFswq2fH0hsWWHS4 -OsCeqT0nm5vd1CdUFQJ4Nuh/TTdgCAVKk5yJZJvH2BX77I2d4T0ZRGHLDKUm8P0E -n6ntrMqLFR+QooONAZg0DTaxvbsCvaupRJCn9NgiwtXyYJKbvf5F8NEOe57NoGwd -LqQ332mVTuJ1DiqnChLoe7Mz7OY21RsTa/AK5Q/onClvBATrLD0ynK4WiLn4+hGs -HK5t3audgdnrLxs4UoA= ------END ENCRYPTED PRIVATE KEY----- diff --git a/handler/src/test/resources/io/netty/handler/ssl/test2_unencrypted.pem b/handler/src/test/resources/io/netty/handler/ssl/test2_unencrypted.pem deleted file mode 100644 index 209a9c05be..0000000000 --- a/handler/src/test/resources/io/netty/handler/ssl/test2_unencrypted.pem +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC4HXSOVyXFCuOT -ja+EE32WcWySXjmCjxFlSFivz1s+hCV7d4Q3aZp2kVCgL+JjCBZcVlZMU6mv3Qd4 -Y3JuYT7zYDwFGhFszLRnd9qfebCc28Ylr8YX+a6hdJ9WZOSl/hHGrIgrqTYN0y+4 -H00kmX2hkln+OtKGgkulglOF/V01IcrOzeQRTPrkCxNOPAp5AQjtQJ/fNBWrv9Pg -S14FvoiQul1kyW6Wfq+6w6QSpfh5kEObYBpQmXJ4ZkvW82E94aGoOiZA6CMGxbFT -pMHpsWRm/BrBhO+P6681CfTHUfxEwgNpJtZ2uhErLdP8JtKg93JSWoT/MRKGiflt -UxRos0VpAgMBAAECggEAYiTZd/L+oD3AuGwjrp0RKjwGKzPtJiqLlFjvZbB8LCQX -Muyv3zX8781glDNSU4YBHXGsiP1kC+ofzE3+ttZBz0xyUinmNgAc/rbGJJKi0crZ -okdDqo4fR9O6CDy6Ib4Azc40vEl0FgSIgHa3EZZ8gL9aF4pVpPwZxP1m9prrr6EP -SOlJP7rJNA/sTpuy0gz+UAu2Xf53pdkREUW7E2uzIGwrHxQVserN7Xxtft/zT79/ -oIHF09pHfiqE8a2TuVvVavjwV6787PSewFs7j8iKId9bpo1O7iqvj0UKOE+/63Lf -1pWRn7lRGS9ACw8EoyTY/M0njUbDEfaObJUzt08pjQKBgQDevZLRQjbGDtKOfQe6 -PKb/6PeFEE466NPFKH1bEz26VmC5vzF8U7lk71S11Dma51+vbOENzS5VlqOWqO+N -CyXTzb8a0rHXXUEP4+V6CazesTOEoBKViDswt2ffJfQYoCOFfKrcKq0j1Ps8Svhq -yzcMjAfX8eKIDWxK3qk+09SBtwKBgQDTm2Te4ENYwV5be+Z5L5See8gHNU5w3RtU -koO54TYBeJOTsTTtGDqEg60MoWIcx69OAJlHwTp5nPV5fhrjB8I9WUmI+2sPK7sU -OmhV/QzPjr6HW7fpbvbZ6fT+/Ay3aREa+qsJMypXsoqML1/fAeBno3hvHQt5Neog -leu3m0/x3wKBgQCCc8b8FeqcfuvkleejtIgeU2Q8I3ud1uTIkNkyMQezDYni385s -wWBQdDdJsvz181LAHGWGvsfHSs2OnGyIT6Ic9WBaplGQD8beNpwcqHP9jQzePR4F -Q99evdvw/nqCva9wK76p6bizxrZJ7qKlcVVRXOXvHHSPOEVXaCb5a/kG6wKBgGN6 -2G8XC1I8hfmIRA+Q2NOw6ZbJ7riMmf6mapsGT3ddkjOKyZD1JP2LUd1wOUnCbp3D -FkxvgOgPbC/Toxw8V4qz4Sgu2mPlcSvPUaGrN0yUlOnZqpppek9z96OwJuJK2KnQ -Unweu7dCznOdCfszTKYsacAC7ZPsTsdG8+v7bhgNAoGBAL8wlTp3tfQ2iuGDnQaf -268BBUtqp2qPlGPXCdkc5XXbnHXLFY/UYGw27Vh+UNW8UORTFYEb8XPvUxB4q2Mx -8ZZdcjFB1J4dM2+KGr51CEuzzpFuhFU8Nn4D/hcfYNKg733gTeSoI0Gs2Y9R+bDo -+cA9UxmyFSgS+Dq/7BOmPCDI ------END PRIVATE KEY----- diff --git a/handler/src/test/resources/io/netty/handler/ssl/test_encrypted.pem b/handler/src/test/resources/io/netty/handler/ssl/test_encrypted.pem deleted file mode 100644 index 58d181e116..0000000000 --- a/handler/src/test/resources/io/netty/handler/ssl/test_encrypted.pem +++ /dev/null @@ -1,29 +0,0 @@ ------BEGIN ENCRYPTED PRIVATE KEY----- -MIIE9jAoBgoqhkiG9w0BDAEDMBoEFDBlaUwB8TQ9ImbApCmAyVRTTX+kAgIIAASC -BMhC8QFNyn0VbVp7I+R9Yvmr+Ksl0xZshGg3zaUN8/HRblNSS3gPiP673rmnhcU3 -PfSNFR9hOrTqdtd5i6Qq4HznECs81KBlqRNB9ihgy++ByFkf6GTzdfBA6zJInhNx -qSWjUwpFtV4or1w/N23bTcpdGmjfdCSFBMQdbkIDgT7GaWxd3mCLxSbfVzF64tev -x+V22nA/TR0VWnG+aj7aVbReK6VpepiCX7ZmQ5KehXAeB0SDrgT89kcz2VIfDxvE -hkCymNTcJY/ETdPfTSiR+DSZvVJMgVmfk7j1toZZSnoMwl4IhlXmIPmDOUE465l3 -sNWLygkNKymTmMI5FTT1hChAIdsmeVTfDmVzNPK4HQi5gfEnTCy0uxj9U3HCZWr1 -Zlzmw7/430TRqNYSEJ/XkhFaV5V+6LfeZOyuwf2VJAs+CwNo+UYzEQqkW11JMqhA -i9fz8bCNoy4/dyWbE/wEK8UPGif1rzCpoodBYeWTt0QtHcIokE3ylXWyTTarz7jV -u9Rnbq4HAXYYEwPjLmWFQ6NeD/rx/t44oEAyekxS+ZPIHNTVXRLBH5Tl/LDkpK15 -x0FoIZ0vrDiFbmtHCq/TeDyFtudSbmihnn0Of6PtXKZJpXgEADQBnak/P4IE39/d -1hWd3H635goC6OkqHv9IAAyLlCNZCOVqC5Wa8TvyZdaKi5A2mZfGrpxPrUQDlnqN -8d3xlysNCaRH1hSMw4hGHu0xxGJaK4DQtklxfZB7IMMw5MkQh6Rim5TOXfopmzmK -PISJge1atiHbVIBP6sr3Egik3h6v0j7xXVmwj3UUQRaSBznZ43ShlYieLnin9sh8 -x/gLyvQrtJRvScN6skgrXFKVH3Jojxut9if64jjLo4C61UgNrvuka05treRTI+jT -hHB3GLy7hwSHnbsOvwvYbG3WgyePPq6jIM+LV4Vm3fPX6NPNI/jZMebROGwjTL0C -2403yvgeIpEOQyZpKsDBqAwgKB91Na53K05qGSbr8AgcZvgFflJdLzai+5Cg7hNg -YTEff0NKPeYnk4u3xQ8EqxI2jwdqfgzd0RcPcx60CHRBTULaKOU2sAYTSpwQmApj -+TnJNcQnWRAEcZ35b/b+oGlVH/BUmvjSdu2qvvU3g4GoHL7MuVGvzk0Cgo1Esktt -S6gO/pTQPaKGJ1ztxoHu2zzi7/URaus3sqI5qV9krWMSa35BMG21Eik/y9rou6LC -yT0EtMLOCxSrfM1I26XTU/7qPIEJlVZg0CJ39niZ7EEm1Hef0cmT8Aq9t5cRTyvR -BqbqBCJpcsgeIZUMH6RJ1zv616eJvY7wjd13Sl0Tbj9+nNS482D9PIlaXSD8UySh -mZ0bMPhCeyOsmRmz2qT1X+Zct8XtdXc/NPKBA6rnOtH8vJAHn7S120le5XIn5t9l -rDiO1Hozhb+0xcTk+SNc/vIORA6KrBoZrNpJpmyL3BzRp+/VLbR+/S3ikTDkYj7J -sktK2ap6vK7u50Jnrt9C/wynVACzGx1tlDVxiVerDmwjfQWL08qCXHlouEdjh9dD -L5XyVlT2FxEXXLRgKGHxFaSQw3Fzzug/o4SgizbNjKffJU5xQlC0aq3WX5+/l3Ic -LWTalgdli3edsR/9RGuu8EsZ11dmNh3csGs= ------END ENCRYPTED PRIVATE KEY----- diff --git a/handler/src/test/resources/io/netty/handler/ssl/test_encrypted_empty_pass.pem b/handler/src/test/resources/io/netty/handler/ssl/test_encrypted_empty_pass.pem deleted file mode 100644 index d28b0478a0..0000000000 --- a/handler/src/test/resources/io/netty/handler/ssl/test_encrypted_empty_pass.pem +++ /dev/null @@ -1,29 +0,0 @@ ------BEGIN ENCRYPTED PRIVATE KEY----- -MIIE6TAbBgkqhkiG9w0BBQMwDgQIr2JTEf0lU/cCAggABIIEyKHtsAnPt1FmHeAF -AWZFF4KddWUW13iaaxXs+cHENCN1dpVxDPiCWNIkstQxC16bwKlfb0qFz42jQg4O -DM4kmtUyJoAt76wewteZvFPjzSuIepvJc8+SVaQuZPafSbGUcSPyATZbA2LmRWcx -HIvuya7gJX40aZm1VT1NM439UybJgmlrByoRGBjsrvYXGMZnMWLEYBgccN1wZ6WI -Hpuzv8LlIqFXL1DZZFFeFI60INlvfdwZWsz5SGf1zP68ZoGlS3R7LuJWMTEXfDUR -CLSturHFKI0jRAsloSYuHV/dJ6Io8hYWhAu84wc++dMTT+iSnaMWv7McF2nEFWx9 -UVKZiar509z/Rm/C+yN7TkT8TMaPRoYC6jxDW3IsE2wpBdLSCeF8Nh0vCNeay13I -2OcOFz1UrZcopEqXwl5SBUfhU3VjNm5N+h2p9WW9HTa3TXZDbZ04kyJEPYj3e8OE -viVDMRunJGWRkJ2//oCp2E9+NBYMMj9gN11mCUTyFmfpY/Kec/0nhQMCcZRlMzUD -i9AAzCVsm1HYnSzRq4JwnTbt6KBnPyS/rW+IfKQ+2aI/7Or6JfzrpX/v7/ECe98S -zaVhO9rJjsfML0ceR8UBK+dkl1R6hYV2+xXO8UZEo+X9IR1c03gDXMW0nZ4/Jgq4 -d3PmzuAuObCGnyr2k3PJqOoNZk3f44qfkNPQgbIROgX5zA8GNpL4XxDFFOW4YN2i -opod3doJ5r00WVmJowrKjWYMs0Ljik9FFla3986oZ+s/+WEWoFOssXvJMYiYQUIT -mrvXsyqNI+Nhs1rl8zzuZQ0BKGfwHyrUEcAgngH7itKR+IoqNXx1+pcIqJe/oIQr -oIEr5bYxAtDSi6DUGrvsuVoe5l5ByE+YvW68S7gIore4UXl77EVWQ5yfuyFteQE6 -Bm8vcnjGeoo0YDPwwziN1MUsYJPX6cnFIV20phXm4w5YAYfrI2yKt3AIKFkDU9n0 -5yLogR4NlkKgHd2DjELJXqvFBSivW6wZXL0wusDI5imopET/SD3vUexYML480Ulk -CxhbYc9FAgcKgYr4K6vyPAYj4K/w0qKC7ceAdJarPxS2qeXBHeHAepr1wHBu7G9S -7J8yHht0xB06Ar3nJ6J6KcCikv6ZMpQImYeSbE/5jrsh8hnqdDmKz5dAvwq4fwy9 -r0zUev0HB4+ON4XH8XAcwt5p/3zJISRpQZb4G7vm7VaA5cIbPOneuMxO2pw3CTI3 -qXnvWWkGdoc4BYacHJxA/awCUk6SrglCM5X479BG+ZnMzkCuXbJHn+zh2BgSQ7B3 -Z3JsNtnCaPGp8QEqFYk9tLdFcZ6/PlUsTawcIjG/6VMvpLcNBahHqwqDg1rax4Mx -xJDnGfVXFsbBQIYku8yvrXi7QnANgD/N+AAAjo6CDNJZmhsc2nhE7fsVWxz1hbl1 -vK1N13kT8CbfOFCUs8Od/O369vbv5C7yvK3JeG11sNfOZ/MqYBAVPDFLPdtJwFu6 -0M/c8+TDgLOp+qlRkIgDwXkjed4ncjDHSl9g41Zp4n1uR94VsKtneCNLuj4+qr4h -uhBnUap1s7uBZ0PW9TA5DXXNvhCrHu2vz3YIMBmlYv/i5qu+bPtPki7eTfm8UWAB -p9pJJHa/9J0gfM4a4w== ------END ENCRYPTED PRIVATE KEY----- diff --git a/handler/src/test/resources/io/netty/handler/ssl/test_unencrypted.pem b/handler/src/test/resources/io/netty/handler/ssl/test_unencrypted.pem deleted file mode 100644 index 608e7f4da0..0000000000 --- a/handler/src/test/resources/io/netty/handler/ssl/test_unencrypted.pem +++ /dev/null @@ -1,24 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDb+HBO3C0URBKvDUgJHbhIlBye -8X/cbNH3lDq3XOOFBz7L4XZKLDIXS+FeQqSAUMo2otmU+Vkj0KorshMjbUXfE1KkTijTMJlaga2M -2xVVt21fRIkJNWbIL0dWFLWyRq7OXdygyFkIiW9b2/LYaePBgET22kbtHSCAEj+BlSf265+1rNxy -AXBGGGccCKzEbcqASBKHOgVp6pLqlQAfuSy6g/OzGzces3zXRrGu1N3pBIzAIwCW429n52ZlYfYR -0nr+REKDnRrPIIDsWASmEHhBezTD+v0qCJRyLz2usFgWY+7agUJE2yHHI2mTu2RAFngBilJXlMCt -VwT0xGuQxkbHAgMBAAECggEBAJJdKaVfXWNptCDkLnVaYB9y5eRgfppVkhQxfiw5023Vl1QjrgjG -hYH4zHli0IBMwXA/RZWZoFVzZ3dxoshk0iQPgGKxWvrDEJcnSCo8MGL7jPvh52jILp6uzsGZQBji -bTgFPmOBS7ShdgZiQKD9PD2psrmqHZ1yTwjIm5cGfzQM8Y6tjm0xLBn676ecJNdS1TL10y9vmSUM -Ofdkmeg9Z9TEK95lP2fF/NIcxCo0LF9JcHUvTuYBDnBH0XMZi0w0ZcRReMSdAZ2lLiXgBeCO53el -2NIrtkRx+qOvLua9UfwO2h/0rs66ZeV0YuFCjv067nytyZf2zhU/QbCHRypzfrkCgYEA/facuAJs -6MQKsNvhozoBeDRMkrZPMh8Sb0w50EqzIGz3pdms6UvCiggoMbhxKOwuYWZ689fBPGwm7x0RdwDO -jyUuEbFnQFe+CpdHy6VK7vIQed1SwAcdTMDwCYbkJNglqHEB7qUYYTFLr8okGyWVdthUoh4IAubU -TR3TFbGraDUCgYEA3bwJ/UNA5pHtb/nh4/dNL7/bRMwXyPZPpC5z+gjjgUMgsSRBz8+iPNTB4iSQ -1j9zm+pnXGi35zWZcI4jvIcFusb08eS7xcZDb+7X2r2wenLNmyuTOa1812y233FicU+ah91fa9aD -yUfTjj3GFawbgNNhMyWa3aEMV+c73t6sKosCgYEA35oQZhsMlOx2lT0jrzlVLeauPMZzeCfPbVrp -1DDRAg2vBcFf8pCXmjyQVyaTy3oXY/585tDh/DclGIa5Z9O4CmSr6TwPMqGOW3jS58SC81sBkqqB -Pz2EWJ3POjQgDyiYD3RgRSPrETf78azCmXw/2sGh0pMqbpOZ/MPzpDgoOLkCgYEAsdv4g09kCs75 -Dz34hRzErE2P+8JePdPdlEuyudhRbUlEOvNjWucpMvRSRSyhhUnGWUWP/V7+TRcAanmJjtsbrHOU -3Udlm0HqrCmAubQ4kC/wXsx4Pua7Yi2RDvBrT4rT4LGgreaXNWhI+Srx7kZslUx5Bkbez3I0bXpM -2vvwS/sCgYAducNt1KC4W7jzMWUivvuy5hQQmX/G0JHtu1pfv9cmA8agnc1I/r7xoirftuSG25Pm -r+eP5SKbKb8ZQlp10JeBkNnk8eAG8OkQyBaECYDBadEr1/LK2LmIEjYKzKAjYQ4cX2KMtY271jjX -WrzzXNqBdThFfMHiJE8k9xYmaLDKhQ== ------END PRIVATE KEY----- diff --git a/handler/src/test/resources/io/netty/handler/ssl/tm_test_ca_1a.pem b/handler/src/test/resources/io/netty/handler/ssl/tm_test_ca_1a.pem deleted file mode 100644 index 120859eda5..0000000000 --- a/handler/src/test/resources/io/netty/handler/ssl/tm_test_ca_1a.pem +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIC/TCCAeWgAwIBAgIBATANBgkqhkiG9w0BAQsFADAXMRUwEwYDVQQDEwxuZXR0 -eS50ZXN0LjEwIBcNMTYwMzIyMTIwMDAwWhgPMjExNjAzMjIxMjAwMDBaMBcxFTAT -BgNVBAMTDG5ldHR5LnRlc3QuMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC -ggEBALWMMCP4QWYWJNt+fNqpwLNM9/LkJlS3NtzJl1chvnyHpxt8OFSD8/cYSl6z -MrbgRYyGNuaL3lsKIL5p2ZnUYzcR61niAhjuMXQgM6ZkptlIsgK6426OTALOSN6l -HukItWDDL/om0Mnc8zMuLL/kIpfnzYOKMseUf/1R1MftzlNSSAMPQ7Rn8So/3nUG -j42NywEInoONv89UZ4L+xPpyJwrp0k/u19ckwhFWdudw7l2lVo6s5aBJW9CK8v/f -uUxC75eUYiQ57suKhXCy1Vf8T4vVDiEjKxa3whD1QxlxRxZNYdHJA6tEQIhiWjCC -RiDZZcaAcCCD0evE/0l5V9nnRc8CAwEAAaNSMFAwDwYDVR0TAQH/BAUwAwEB/zAd -BgNVHQ4EFgQUvdqINGhE1D1xZi9Q8NyR+G+5bLwwCwYDVR0PBAQDAgEGMBEGCWCG -SAGG+EIBAQQEAwIABzANBgkqhkiG9w0BAQsFAAOCAQEAR8gHn7MJp6cNwMR6qF3e -jU3tAzCshZVM03NyoHvMpcHsILlR0g/q2KTjcgHzpMMo5PrUGf3oR6ad4JFr5els -kstgbCe4Vv/XzEC6faTEuhLolHGMyzr3Pd6k/wJSsMktF7Ob+YjsyZbgQbyhXqJV -UDQDDncIwxl5rdsRwfiltLUOle4702b4hSCb/1NsDsvsuZQVfeAHHzT1aS8XDSwK -bHOgrDgQGhVR6rBTH9WhcRgFY9rKQ4vVjhoNbwWweQvHmQSO8xYNUhtQnxVOeB7B -NzBM+kx5nw7oIqPCYT0hBINNqeoac9Bidfl4UoTB5YjsQFse4BNuBDPFowAXq5ZB -fg== ------END CERTIFICATE----- diff --git a/handler/src/test/resources/io/netty/handler/ssl/tm_test_ca_1b.pem b/handler/src/test/resources/io/netty/handler/ssl/tm_test_ca_1b.pem deleted file mode 100644 index df75823fc9..0000000000 --- a/handler/src/test/resources/io/netty/handler/ssl/tm_test_ca_1b.pem +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIC/TCCAeWgAwIBAgIBATANBgkqhkiG9w0BAQsFADAXMRUwEwYDVQQDEwxuZXR0 -eS50ZXN0LjEwIBcNMTYwMzIyMTMwMDAwWhgPMjExNjAzMjIxMzAwMDBaMBcxFTAT -BgNVBAMTDG5ldHR5LnRlc3QuMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC -ggEBANEUVJUwvWr+qyS28W2EMiDq5frQwforMED7Q8wMiMod+LFxy7y04p12zYWW -35iqC3RaQUQ31DOknAxc7H8vfr0vdl87BIsxc27Ud9h+Do0ggktCaz9Te8/q2Yxo -4TQ8QEFJ8x37zPB05LVqF4djim4GE/yaj0WFMuaRaZLUFvGbHTL7ilC2l6p9SuYx -y40cCucP5nNAXGNhnVYsJCPa/LkyIDLGbkvMMARorkbr7zfaYI2D1YfedwmCOEo1 -CkfBm2qL9/+ig/8VFrTRPlYzUWHsyPvCzfL9F/69NxRCdVk7XCyMEgmf/ztyy/7k -3iZeBhQ0z+RXiNLqBkK3RbaMD7ECAwEAAaNSMFAwDwYDVR0TAQH/BAUwAwEB/zAd -BgNVHQ4EFgQU2vr0yImPHyJv84PXeoSJYbI12uEwCwYDVR0PBAQDAgEGMBEGCWCG -SAGG+EIBAQQEAwIABzANBgkqhkiG9w0BAQsFAAOCAQEAT8mmZi/4dzozqa76tgxM -8ZQgw2C+WgetC81CKqIN3F3tFtu2KEsaAsXEpOVvDR278bLk+r3H3d47Or0xn053 -grk6kdI4C9IPHP7IDaNmAskZ5u9Hrl25P1fxMKG6hXwrk2Je7gD8aNP5IkOSKulo -e9b3XSW53WdtHZ+b98LKVMO0lRLQsiG1EmNrL0kJwMXuPxq5s0Ljqz/L19iWGupk -kybRWPcmjHnWIOnnYTwFswI/h79/afvwW5xUP4HgcU/nKrNDWveE7lSYq66zcvpt -rBCESrr3gvETNTJKCPN4u41EOJKGGgoN4U9fBopU4DfzIrcwZ5a4eFLsAFEqAB23 -3A== ------END CERTIFICATE----- diff --git a/handler/src/test/resources/io/netty/handler/ssl/tm_test_ca_2.pem b/handler/src/test/resources/io/netty/handler/ssl/tm_test_ca_2.pem deleted file mode 100644 index 1d96bfc7fe..0000000000 --- a/handler/src/test/resources/io/netty/handler/ssl/tm_test_ca_2.pem +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIC/TCCAeWgAwIBAgIBATANBgkqhkiG9w0BAQUFADAXMRUwEwYDVQQDEwxuZXR0 -eS50ZXN0LjIwIBcNMTYwMzIyMTIwMDAwWhgPMjExNjAzMjIxMjAwMDBaMBcxFTAT -BgNVBAMTDG5ldHR5LnRlc3QuMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC -ggEBAMFTCqEUaMvZPwV8PFHgjpcqlrVfIadY+B2DAsYBQp6HirRv2STYdBue9bHS -Ya8n6J99Qcp0jct692MeOh9BhWoX7wOvi8Tckiu+LAMo7vBHAyUSamJ5qKyNvZNW -3Uwrng1LFwOo6uhY6N6vqyv5CIoDGv+afOlnZOS7484ZYvmYEPejbIPLZpj7IP8Z -c2xmi7TOj781uO2rwUzZgqGSEKsYZNMhVp7yZrsJ3el9T2+2Dma1aYt2w/grOW1t -pzxWvXqjkrjbNjJamAxyiy3qyQ2iDhpFYz8ONqSxqN/QRE72q2N8e+QtajceCFz7 -lpMWRqr7Z6Fdh5zUS7yJrCksJh8CAwEAAaNSMFAwDwYDVR0TAQH/BAUwAwEB/zAd -BgNVHQ4EFgQUnk0/s/aeR4sjFcubiyB/QMWryj8wCwYDVR0PBAQDAgEGMBEGCWCG -SAGG+EIBAQQEAwIABzANBgkqhkiG9w0BAQUFAAOCAQEAWu+RyHkL3lNl8dLlVuDa -A/Vakxf/xbd8+qFEIox2nLvSYZ3OkLzE+vUip/KP0JyQmmmzaz3sx5eZXx33gw9Z -rRYW3I0/c2QPjT5xNYnITUoX5z17FKd71lMr/bz8uhaF9Do+ZV84HgORwtmOCwNg -bOIIVtHO6Ht3V2RmLcQgUV4dK3neNJHa75/Wi3OkJNEZqbzcJX2r69BqupoLte8j -FxqkLBmwUruuCVl5gUFoXsxT3+qgWMxNweLSxEmbqkQ54g8W+06PTHMM/BpbsApv -Ce5mKeC8lHvbV3CxaOYp8w5xJPJbEt/vK6w8jrN47Tz6LaQcimDMdJVVM2H5zubG -RA== ------END CERTIFICATE----- diff --git a/handler/src/test/resources/io/netty/handler/ssl/tm_test_eec_1.pem b/handler/src/test/resources/io/netty/handler/ssl/tm_test_eec_1.pem deleted file mode 100644 index e90e20549f..0000000000 --- a/handler/src/test/resources/io/netty/handler/ssl/tm_test_eec_1.pem +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDADCCAeigAwIBAgIBAjANBgkqhkiG9w0BAQUFADAXMRUwEwYDVQQDEwxuZXR0 -eS50ZXN0LjEwIBcNMTYwMzIyMTQwMDAwWhgPMjExNjAzMjIxMjAwMDBaMB0xGzAZ -BgNVBAMTEm5ldHR5LnRlc3Quc2VydmVyMTCCASIwDQYJKoZIhvcNAQEBBQADggEP -ADCCAQoCggEBALEytHncQ4iJ0d3nbLcRhe3wWmHzeqr9JKvAMSnIGw2Oc+nUK3FP -xMjdYnY3CSw6BYjK5zJsSF2UWU/jycqc5caQTEXVUQJDjQENScHHsWB+jEevd1i7 -HDmd4Ykw/bqRRRP/I6npukpwJkPuqJ7LAsDfA7FuHJY15ZlhDod5+1zleWv2tdDK -fRCvODi8ehujyNqBqNeRL2YF4wt4yqviFbDNy5+JreyfzPvv1iujSprN5JJCRJ1C -7AABrHYYMQXuEYb10t9dS908Zg2B3sDUa969F1RnU86bCppwK1otQr/RO2hUqqdq -IItb9FHRkeko81OvUiM6nvLzzJLBOInyAzMCAwEAAaNPME0wDAYDVR0TAQH/BAIw -ADAdBgNVHQ4EFgQUt5uJ55JYS1Qw2YE5OWOhgv6RxUAwCwYDVR0PBAQDAgXgMBEG -CWCGSAGG+EIBAQQEAwIGQDANBgkqhkiG9w0BAQUFAAOCAQEAUS9HzI9VXyZiaGM6 -RwpUEDgUhbAeI7i7xsdJgqlbvKrTQQy+MKIbxDgsyoz2buqwdX7ekvykTmo0pltS -ASr36gTTW4dwRtiecn/HutrnyuJIckbvMZzld5xIdNERqLHnoiRAopVhe1Fc5UFd -YGEOd+685X2fuc9PMy3G8JjQAOftYOx21JaaNumyVVLcyvciGK0Ptwh/q+6hf4+h -XUHHtIzjnPAM9vkcCmHttVbl3uvare7TfeAoU82NODz0sUaOrIwG8dQbmEdrafHa -JHXti1wv+9ZEEiYKcecvnB3q4e0MT3atf3qedw4B9ZkzoniHEOhFpZgQg6UVA7/f -ga0mCg== ------END CERTIFICATE----- diff --git a/handler/src/test/resources/io/netty/handler/ssl/tm_test_eec_2.pem b/handler/src/test/resources/io/netty/handler/ssl/tm_test_eec_2.pem deleted file mode 100644 index 898a6e092d..0000000000 --- a/handler/src/test/resources/io/netty/handler/ssl/tm_test_eec_2.pem +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDADCCAeigAwIBAgIBAjANBgkqhkiG9w0BAQUFADAXMRUwEwYDVQQDEwxuZXR0 -eS50ZXN0LjEwIBcNMTYwMzIyMTQwMDAwWhgPMjExNjAzMjIxMzAwMDBaMB0xGzAZ -BgNVBAMTEm5ldHR5LnRlc3Quc2VydmVyMjCCASIwDQYJKoZIhvcNAQEBBQADggEP -ADCCAQoCggEBAMFf61lsJ5Tpcg6ux5lbZhPEzvLi117aZ42figORTeZg0fMX8a3W -XfRUHldTYD6CugTIpgheZDzsYfwrSLyK5jMyQl332rCoozj2dXdi+HY/JQ5gw8DY -odeQCjCWS3V6HpHTu8tqgNquqjygSLQHSsh+oCVUa/5IWA1N+zZY5/XARnyajwS0 -8Nrgok9kd//jR7hIwHm40YThrTawRetooDK/MuFxJb6FX6UPwZ5/9g2UZMUaKzIa -hrGfjAQmRxzkRyACbVqYv+wjBMSCq2SqYZ2Fq3nKeW+dvpPeFfHGOyF/F8kIqtpa -BRawXPHKaUoQYn0PDU+RRfZjfkWTQuz/OMcCAwEAAaNPME0wDAYDVR0TAQH/BAIw -ADAdBgNVHQ4EFgQUIUVnoWei3itUqTyFxPczCYsvW4EwCwYDVR0PBAQDAgXgMBEG -CWCGSAGG+EIBAQQEAwIGQDANBgkqhkiG9w0BAQUFAAOCAQEAfK+YlGqBVExATkGF -1ZIcJZtvaiX8rGH8mwqj1wPvKjPRCHvNpPTDLNGhHrFu/0sJlZQDz6hDn0NpJpD8 -TffF+jqmBfvGQW1MEd+jyfp5IXHwR0ZejJepQIeGYuMwyrlZXUKnXvQR2QDkLyx+ -rxmO58XWLNoFUkM4guts3Jb7oAgfCbzYnmBELMVhI8v+SQhuZamvL6S5Wdb18O9i -/N/zH/KDwJmIVtWo7D8UOAMeq69s9zYZLKkqwt8o+DSXth0YPZcNcU8IouDzEJ14 -C35My7Ll7vFehgetXq9D7cMYltx2VPKKYOeT5ZzI580ZvtryT8yCTBj4GoSvAzb6 -RHwFRw== ------END CERTIFICATE----- diff --git a/handler/src/test/resources/io/netty/handler/ssl/tm_test_eec_3.pem b/handler/src/test/resources/io/netty/handler/ssl/tm_test_eec_3.pem deleted file mode 100644 index 82fa9c9f53..0000000000 --- a/handler/src/test/resources/io/netty/handler/ssl/tm_test_eec_3.pem +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDADCCAeigAwIBAgIBAjANBgkqhkiG9w0BAQUFADAXMRUwEwYDVQQDEwxuZXR0 -eS50ZXN0LjIwIBcNMTYwMzIyMTUwMDAwWhgPMjExNjAzMjIxMjAwMDBaMB0xGzAZ -BgNVBAMTEm5ldHR5LnRlc3Quc2VydmVyMzCCASIwDQYJKoZIhvcNAQEBBQADggEP -ADCCAQoCggEBALGdif7vCqwoWg001wmw2X3HnF/9cjZGTY6BWiex3cIarO+aby6L -QObv7lXzzh3r7bJc1JquaawKUek6CIt2mNM+KLAIscScNUOCYg4T1XOD0rh3Qrin -V8Q+hY8InSrUqN4cuX95YOdJoOddw3mDXs376fcNBU10raP4L7k7EsyvnQqIKyNP -ysU9PpDoDLPCMBEGB8cDASv1bkopvvH9u5sic1OHtTLVllsbGNnG6Y9B+1ysG1UO -iJFAX+teigPXKVokJ1+a8dt1CkzDd+iTK1j6PrY+TXc4XOhP/cSnbxwq6JDzkkSb -3pMTJK8ypi7DQNkDbPx73A8qbjRT32gJz4ECAwEAAaNPME0wDAYDVR0TAQH/BAIw -ADAdBgNVHQ4EFgQUCIQ1MChHCo6/1mI6B7S6QPPvEZcwCwYDVR0PBAQDAgXgMBEG -CWCGSAGG+EIBAQQEAwIGQDANBgkqhkiG9w0BAQUFAAOCAQEAZas05SOIsNFmKUYY -kyR1ctlgaA7OZwSzeRPh6vZJ4YaT2lVhNPUeO84tf3LqKE8B827FzWH9mcO/2zeJ -6PTR+QYls/wg8VR881V0Xb5KVNGfwTYpmfhH9+JSzKvKiEtlOoHyvYBMdUon7LJL -ojvlragwXm4QA246345+md5C8PEyQYQf/AoZVZWeLL/BRXZ2ZjsuIT+LzpMIXuTW -AKoH7IlFbKQ5tccQDGCzZb6V1txRDFlKZ/5bvFQZqo12n0MeJy2WPjrmeRm2NC+9 -imP9oR9GIGNyGKTT1h1qjnaZZwK24cx/82eb63qQKUx80pD4DYW9EDU6/tULz5gs -Kw0iig== ------END CERTIFICATE----- diff --git a/microbench/README.md b/microbench/README.md deleted file mode 100644 index 4505f1d9c9..0000000000 --- a/microbench/README.md +++ /dev/null @@ -1,4 +0,0 @@ -## Microbenchmark tests - -See [our wiki page](https://netty.io/wiki/microbenchmarks.html). - diff --git a/microbench/pom.xml b/microbench/pom.xml deleted file mode 100644 index 5eb3582d7e..0000000000 --- a/microbench/pom.xml +++ /dev/null @@ -1,276 +0,0 @@ - - - - - 4.0.0 - - io.netty - netty-parent - 5.0.0.Final-SNAPSHOT - - - netty-microbench - jar - - Netty/Microbench - - - - true - 1.22 - - - - - true - - - - linux - - - linux - - - - ${jni.classifier} - - - - - maven-compiler-plugin - - - **/*.java - - - - - - - - mac - - - mac - - - - ${jni.classifier} - - - - - maven-compiler-plugin - - - **/*.java - - - - - - - - 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 - - - - - - - - - - - - - - - ${project.groupId} - netty-handler - ${project.version} - - - ${project.groupId} - netty-codec-http - ${project.version} - - - ${project.groupId} - netty-codec-http2 - ${project.version} - - - ${project.groupId} - netty-codec-redis - ${project.version} - - - io.netty - netty-codec-mqtt - ${project.version} - - - io.netty - netty-codec-stomp - ${project.version} - - - ${project.groupId} - netty-transport-native-epoll - ${project.version} - ${epoll.classifier} - - - ${project.groupId} - netty-transport-native-kqueue - ${project.version} - ${kqueue.classifier} - - - org.junit.jupiter - junit-jupiter-api - compile - - - org.junit.jupiter - junit-jupiter-engine - compile - - - org.junit.vintage - junit-vintage-engine - compile - - - junit - junit - compile - - - org.openjdk.jmh - jmh-core - ${jmh.version} - - - org.openjdk.jmh - jmh-generator-annprocess - ${jmh.version} - provided - - - org.agrona - Agrona - 0.5.1 - - - ${project.groupId} - ${tcnative.artifactId} - ${tcnative.classifier} - false - - - - - - - maven-compiler-plugin - - - **/Http2FrameWriterBenchmark.java - - - - - maven-surefire-plugin - - ${project.build.sourceDirectory} - ${project.build.outputDirectory} - - **/AbstractMicrobenchmark.java - **/*$*.class - **/generated/*.class - - - ${project.build.directory}/reports/performance/ - - - - - org.apache.felix - maven-bundle-plugin - - - generate-manifest - process-classes - - manifest - - - - jar - bundle - - - ${project.groupId}.* - !*.generated.* - - sun.nio.ch;resolution:=optional,org.eclipse.jetty.npn;version="[1,2)";resolution:=optional,org.eclipse.jetty.alpn;version="[1,2)";resolution:=optional,* - - !* - - - - - - - - - diff --git a/microbench/src/main/java/io/netty/buffer/AbstractByteBufGetCharSequenceBenchmark.java b/microbench/src/main/java/io/netty/buffer/AbstractByteBufGetCharSequenceBenchmark.java deleted file mode 100644 index 4a2bbb87e0..0000000000 --- a/microbench/src/main/java/io/netty/buffer/AbstractByteBufGetCharSequenceBenchmark.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.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; - } -} diff --git a/microbench/src/main/java/io/netty/buffer/AbstractByteBufNoCleanerBenchmark.java b/microbench/src/main/java/io/netty/buffer/AbstractByteBufNoCleanerBenchmark.java deleted file mode 100644 index d4269d6959..0000000000 --- a/microbench/src/main/java/io/netty/buffer/AbstractByteBufNoCleanerBenchmark.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.buffer; - -import io.netty.microbench.util.AbstractMicrobenchmark; -import org.openjdk.jmh.annotations.Param; - -public abstract class AbstractByteBufNoCleanerBenchmark extends AbstractMicrobenchmark { - - public enum ByteBufType { - UNPOOLED_NO_CLEANER { - @Override - ByteBuf newBuffer(int initialCapacity) { - return new UnpooledUnsafeNoCleanerDirectByteBuf( - UnpooledByteBufAllocator.DEFAULT, initialCapacity, Integer.MAX_VALUE); - } - }, - UNPOOLED { - @Override - ByteBuf newBuffer(int initialCapacity) { - return new UnpooledUnsafeDirectByteBuf( - UnpooledByteBufAllocator.DEFAULT, initialCapacity, Integer.MAX_VALUE); - } - }; - abstract ByteBuf newBuffer(int initialCapacity); - } - - @Param - public ByteBufType bufferType; -} diff --git a/microbench/src/main/java/io/netty/buffer/AbstractReferenceCountedByteBufBenchmark.java b/microbench/src/main/java/io/netty/buffer/AbstractReferenceCountedByteBufBenchmark.java deleted file mode 100644 index 3e3a3292ee..0000000000 --- a/microbench/src/main/java/io/netty/buffer/AbstractReferenceCountedByteBufBenchmark.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.buffer; - -import io.netty.microbench.util.AbstractMicrobenchmark; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.GroupThreads; -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.infra.Blackhole; - -import java.util.concurrent.TimeUnit; - -@State(Scope.Benchmark) -public class AbstractReferenceCountedByteBufBenchmark extends AbstractMicrobenchmark { - - @Param({ "1", "10", "100", "1000", "10000" }) - public int delay; - - AbstractReferenceCountedByteBuf buf; - - @Setup - public void setUp() { - buf = (AbstractReferenceCountedByteBuf) Unpooled.buffer(1); - } - - @TearDown - public void tearDown() { - buf.release(); - } - - @Benchmark - @BenchmarkMode(Mode.AverageTime) - @OutputTimeUnit(TimeUnit.NANOSECONDS) - public boolean retainReleaseUncontended() { - buf.retain(); - Blackhole.consumeCPU(delay); - return buf.release(); - } - - @Benchmark - @BenchmarkMode(Mode.AverageTime) - @OutputTimeUnit(TimeUnit.NANOSECONDS) - @GroupThreads(4) - public boolean retainReleaseContended() { - buf.retain(); - Blackhole.consumeCPU(delay); - return buf.release(); - } -} diff --git a/microbench/src/main/java/io/netty/buffer/ByteBufAccessBenchmark.java b/microbench/src/main/java/io/netty/buffer/ByteBufAccessBenchmark.java deleted file mode 100644 index d8e7a8088a..0000000000 --- a/microbench/src/main/java/io/netty/buffer/ByteBufAccessBenchmark.java +++ /dev/null @@ -1,165 +0,0 @@ -/* -* 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: -* -* https://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -* License for the specific language governing permissions and limitations -* under the License. -*/ -package io.netty.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; - // WARNING! - // Please do not replace this sum loop with a BlackHole::consume loop: - // BlackHole::consume could prevent the JVM to perform certain optimizations - // forcing ByteBuf::readByte to be executed in order. - // The purpose of the benchmark is to mimic accesses on ByteBuf - // as in a real (single-threaded) case ie without (compiler) memory barriers that would - // disable certain optimizations or would make bounds checks (if enabled) - // to happen on each access. - for (int i = 0, size = batchSize; i < size; i++) { - result += buffer.readByte(); - } - return result; - } -} diff --git a/microbench/src/main/java/io/netty/buffer/ByteBufNoCleanerAllocReleaseBenchmark.java b/microbench/src/main/java/io/netty/buffer/ByteBufNoCleanerAllocReleaseBenchmark.java deleted file mode 100644 index 77b9aa6784..0000000000 --- a/microbench/src/main/java/io/netty/buffer/ByteBufNoCleanerAllocReleaseBenchmark.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.buffer; - -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.Param; -import org.openjdk.jmh.annotations.Threads; -import org.openjdk.jmh.annotations.Warmup; - -@BenchmarkMode(Mode.Throughput) -@Threads(16) -@Warmup(iterations = 5) -@Measurement(iterations = 10) -public class ByteBufNoCleanerAllocReleaseBenchmark extends AbstractByteBufNoCleanerBenchmark { - - @Param({ "64", "1024", "8192" }) - public int initialCapacity; - - @Benchmark - public boolean allocateRelease() { - ByteBuf buffer = bufferType.newBuffer(initialCapacity); - return buffer.release(); - } -} diff --git a/microbench/src/main/java/io/netty/buffer/ByteBufNoCleanerChangeCapacityBenchmark.java b/microbench/src/main/java/io/netty/buffer/ByteBufNoCleanerChangeCapacityBenchmark.java deleted file mode 100644 index 160f760585..0000000000 --- a/microbench/src/main/java/io/netty/buffer/ByteBufNoCleanerChangeCapacityBenchmark.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.buffer; - -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.Param; -import org.openjdk.jmh.annotations.Threads; -import org.openjdk.jmh.annotations.Warmup; - -@BenchmarkMode(Mode.Throughput) -@Threads(16) -@Warmup(iterations = 5) -@Measurement(iterations = 10) -public class ByteBufNoCleanerChangeCapacityBenchmark extends AbstractByteBufNoCleanerBenchmark { - private static final int MAX_DIRECT_MEMORY_PER_THREAD = 1024 * 1024; // 1 mb per thread. - - @Param("1024") - public int initialCapacity; - - @Benchmark - public boolean capacityChange() { - ByteBuf buffer = bufferType.newBuffer(initialCapacity); - // Change capacity until we would exceed the 1mb per thread limit - for (int newCapacity = initialCapacity << 1; newCapacity <= MAX_DIRECT_MEMORY_PER_THREAD; - newCapacity += initialCapacity) { - buffer.capacity(newCapacity); - } - return buffer.release(); - } -} diff --git a/microbench/src/main/java/io/netty/buffer/ByteBufUtilDecodeStringBenchmark.java b/microbench/src/main/java/io/netty/buffer/ByteBufUtilDecodeStringBenchmark.java deleted file mode 100644 index 7b4f45fde5..0000000000 --- a/microbench/src/main/java/io/netty/buffer/ByteBufUtilDecodeStringBenchmark.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.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); - } -} diff --git a/microbench/src/main/java/io/netty/buffer/CompositeByteBufRandomAccessBenchmark.java b/microbench/src/main/java/io/netty/buffer/CompositeByteBufRandomAccessBenchmark.java deleted file mode 100644 index d2db000476..0000000000 --- a/microbench/src/main/java/io/netty/buffer/CompositeByteBufRandomAccessBenchmark.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.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 deleted file mode 100644 index 093b4e1448..0000000000 --- a/microbench/src/main/java/io/netty/buffer/CompositeByteBufSequentialBenchmark.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.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 = value -> { - 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 deleted file mode 100644 index 3064d13dc6..0000000000 --- a/microbench/src/main/java/io/netty/buffer/CompositeByteBufWriteOutBenchmark.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.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]); - } -} diff --git a/microbench/src/main/java/io/netty/buffer/package-info.java b/microbench/src/main/java/io/netty/buffer/package-info.java deleted file mode 100644 index c8dc884cf1..0000000000 --- a/microbench/src/main/java/io/netty/buffer/package-info.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -/** - * Benchmarks for {@link io.netty.buffer}. - */ -package io.netty.buffer; diff --git a/microbench/src/main/java/io/netty/handler/codec/AdvancedCodecOutputListBenchmark.java b/microbench/src/main/java/io/netty/handler/codec/AdvancedCodecOutputListBenchmark.java deleted file mode 100644 index c181b4a5e1..0000000000 --- a/microbench/src/main/java/io/netty/handler/codec/AdvancedCodecOutputListBenchmark.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import io.netty.microbench.util.AbstractMicrobenchmark; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.Param; -import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.Threads; - -@State(Scope.Benchmark) -@Threads(16) -public class AdvancedCodecOutputListBenchmark extends AbstractMicrobenchmark { - - private static final Object ELEMENT = new Object(); - - @Param({ "1", "4" }) - public int elements; - - @Benchmark - public boolean codecOutListAllocRecycle() { - return benchmark(elements, CodecOutputList.newInstance(), CodecOutputList.newInstance(), - CodecOutputList.newInstance(), CodecOutputList.newInstance()); - } - - private static boolean benchmark(int elements, CodecOutputList list1, CodecOutputList list2, - CodecOutputList list3, CodecOutputList list4) { - return (benchmark(elements, list1) == benchmark(elements, list2)) == - (benchmark(elements, list3) == benchmark(elements, list4)); - } - - private static boolean benchmark(int elements, CodecOutputList list) { - for (int i = 0; i < elements; ++i) { - list.add(ELEMENT); - } - list.recycle(); - return list.insertSinceRecycled(); - } -} diff --git a/microbench/src/main/java/io/netty/handler/codec/CodecOutputListBenchmark.java b/microbench/src/main/java/io/netty/handler/codec/CodecOutputListBenchmark.java deleted file mode 100644 index 323c2d23ec..0000000000 --- a/microbench/src/main/java/io/netty/handler/codec/CodecOutputListBenchmark.java +++ /dev/null @@ -1,70 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import io.netty.microbench.util.AbstractMicrobenchmark; -import io.netty.util.internal.RecyclableArrayList; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.Param; -import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.TearDown; - -import java.util.ArrayList; -import java.util.List; - -@State(Scope.Benchmark) -public class CodecOutputListBenchmark extends AbstractMicrobenchmark { - - private static final Object ELEMENT = new Object(); - private CodecOutputList codecOutputList; - private RecyclableArrayList recycleableArrayList; - private List arrayList; - - @Param({ "1", "4" }) - public int elements; - - @TearDown - public void destroy() { - codecOutputList.recycle(); - recycleableArrayList.recycle(); - } - - @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); - } - - private static void benchmarkAddAndClear(List list, int elements) { - for (int i = 0; i < elements; i++) { - list.add(ELEMENT); - } - list.clear(); - } -} diff --git a/microbench/src/main/java/io/netty/handler/codec/DateFormatter2Benchmark.java b/microbench/src/main/java/io/netty/handler/codec/DateFormatter2Benchmark.java deleted file mode 100644 index 0beb3a66d2..0000000000 --- a/microbench/src/main/java/io/netty/handler/codec/DateFormatter2Benchmark.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.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; - } - */ - -} diff --git a/microbench/src/main/java/io/netty/handler/codec/http/DecodeHexBenchmark.java b/microbench/src/main/java/io/netty/handler/codec/http/DecodeHexBenchmark.java deleted file mode 100644 index fb297e4c19..0000000000 --- a/microbench/src/main/java/io/netty/handler/codec/http/DecodeHexBenchmark.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.microbench.util.AbstractMicrobenchmark; -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.StringUtil; -import org.jctools.util.Pow2; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.CompilerControl; -import org.openjdk.jmh.annotations.CompilerControl.Mode; -import org.openjdk.jmh.annotations.Measurement; -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.Warmup; - -import java.util.Arrays; -import java.util.Random; -import java.util.concurrent.TimeUnit; - -@State(Scope.Benchmark) -@Warmup(iterations = 5, time = 1) -@Measurement(iterations = 5, time = 1) -@OutputTimeUnit(TimeUnit.MICROSECONDS) -public class DecodeHexBenchmark extends AbstractMicrobenchmark { - - @Param({ - //with HEX chars - "135aBa9BBCEA030b947d79fCcaf48Bde", - //with HEX chars + 'g' - "4DDeA5gDD1C6fE567E1b6gf0C40FEcDg", - }) - private String hex; - // Needs to specify a high number of inputs to allow the current strategy - // on nextHexDigits to produce enough branch-misses - @Param({ "2048" }) - private int inputs; - private char[][] hexDigits; - private static final long SEED = 1578675524L; - private long next; - - @Setup - public void init() { - final char[] hexCh = hex.toCharArray(); - next = 0; - inputs = Pow2.roundToPowerOfTwo(inputs); - hexDigits = new char[inputs][]; - hexDigits[0] = hexCh; - if (inputs > 1) { - final Random rnd = new Random(SEED); - for (int i = 1; i < inputs; i++) { - hexDigits[i] = shuffle(Arrays.copyOf(hexCh, hexCh.length), rnd); - } - } - } - - // https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle - private static char[] shuffle(char[] chars, Random rnd) { - int index; - char tmp; - for (int i = chars.length - 1; i > 0; i--) { - index = rnd.nextInt(i + 1); - tmp = chars[index]; - chars[index] = chars[i]; - chars[i] = tmp; - } - return chars; - } - - private int nextHexDigits() { - final int idx = (int) (next & (inputs - 1)); - next++; - return idx; - } - - @Benchmark - @CompilerControl(Mode.DONT_INLINE) - public long hexDigits() { - long v = 0; - final char[] hexDigits = this.hexDigits[nextHexDigits()]; - for (int i = 0, size = hexDigits.length; i < size; i++) { - v += StringUtil.decodeHexNibble(hexDigits[i]); - } - return v; - } - - @Benchmark - @CompilerControl(Mode.DONT_INLINE) - public long hexDigitsWithChecks() { - long v = 0; - final char[] hexDigits = this.hexDigits[nextHexDigits()]; - for (int i = 0, size = hexDigits.length; i < size; i++) { - v += decodeHexNibbleWithCheck(hexDigits[i]); - } - return v; - } - - @Benchmark - @CompilerControl(Mode.DONT_INLINE) - public long hexDigitsOriginal() { - long v = 0; - final char[] hexDigits = this.hexDigits[nextHexDigits()]; - for (int i = 0, size = hexDigits.length; i < size; i++) { - v += decodeHexNibble(hexDigits[i]); - } - return v; - } - - private static int decodeHexNibble(final char c) { - if (c >= '0' && c <= '9') { - return c - '0'; - } - if (c >= 'A' && c <= 'F') { - return c - ('A' - 0xA); - } - if (c >= 'a' && c <= 'f') { - return c - ('a' - 0xA); - } - return -1; - } - - private static final byte[] HEX2B; - - static { - HEX2B = new byte['f' + 1]; - Arrays.fill(HEX2B, (byte) -1); - HEX2B['0'] = (byte) 0; - HEX2B['1'] = (byte) 1; - HEX2B['2'] = (byte) 2; - HEX2B['3'] = (byte) 3; - HEX2B['4'] = (byte) 4; - HEX2B['5'] = (byte) 5; - HEX2B['6'] = (byte) 6; - HEX2B['7'] = (byte) 7; - HEX2B['8'] = (byte) 8; - HEX2B['9'] = (byte) 9; - HEX2B['A'] = (byte) 10; - HEX2B['B'] = (byte) 11; - HEX2B['C'] = (byte) 12; - HEX2B['D'] = (byte) 13; - HEX2B['E'] = (byte) 14; - HEX2B['F'] = (byte) 15; - HEX2B['a'] = (byte) 10; - HEX2B['b'] = (byte) 11; - HEX2B['c'] = (byte) 12; - HEX2B['d'] = (byte) 13; - HEX2B['e'] = (byte) 14; - HEX2B['f'] = (byte) 15; - } - - private static int decodeHexNibbleWithCheck(final char c) { - if ((int) c >= HEX2B.length) { - return -1; - } - if (PlatformDependent.hasUnsafe()) { - return PlatformDependent.getByte(HEX2B, c); - } - return HEX2B[c]; - } - -} diff --git a/microbench/src/main/java/io/netty/handler/codec/http/HttpMethodMapBenchmark.java b/microbench/src/main/java/io/netty/handler/codec/http/HttpMethodMapBenchmark.java deleted file mode 100644 index a06f9a3429..0000000000 --- a/microbench/src/main/java/io/netty/handler/codec/http/HttpMethodMapBenchmark.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.microbench.util.AbstractMicrobenchmark; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.Warmup; -import org.openjdk.jmh.infra.Blackhole; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -import static io.netty.handler.codec.http.HttpMethod.CONNECT; -import static io.netty.handler.codec.http.HttpMethod.DELETE; -import static io.netty.handler.codec.http.HttpMethod.GET; -import static io.netty.handler.codec.http.HttpMethod.HEAD; -import static io.netty.handler.codec.http.HttpMethod.OPTIONS; -import static io.netty.handler.codec.http.HttpMethod.PATCH; -import static io.netty.handler.codec.http.HttpMethod.POST; -import static io.netty.handler.codec.http.HttpMethod.PUT; -import static io.netty.handler.codec.http.HttpMethod.TRACE; -import static io.netty.util.internal.MathUtil.findNextPositivePowerOfTwo; - -@State(Scope.Benchmark) -@Warmup(iterations = 5) -@Measurement(iterations = 8) -@OutputTimeUnit(TimeUnit.MICROSECONDS) -public class HttpMethodMapBenchmark extends AbstractMicrobenchmark { - private static final Map OLD_MAP = new HashMap<>(); - private static final SimpleStringMap NEW_MAP; - private static final String[] KNOWN_METHODS; - private static final String[] MIXED_METHODS; - private static final String[] UNKNOWN_METHODS; - - static { - // We intentionally don't use HttpMethod.toString() here to avoid the equals(..) comparison method from being - // able to short circuit due to reference equality checks and being biased toward the new approach. This - // simulates the behavior of HttpObjectDecoder which will build new String objects during the decode operation. - KNOWN_METHODS = new String[] { - "OPTIONS", - "GET", - "HEAD", - "POST", - "PUT", - "PATCH", - "DELETE", - "TRACE", - "CONNECT" - }; - MIXED_METHODS = new String[] { - "OPTIONS", - "FAKEMETHOD", - "GET", - "HEAD", - "POST", - "UBERGET", - "PUT", - "PATCH", - "MYMETHOD", - "DELETE", - "TRACE", - "CONNECT", - "WHATMETHOD" - }; - UNKNOWN_METHODS = new String[] { - "FAKEMETHOD", - "UBERGET", - "MYMETHOD", - "TESTING", - "WHATMETHOD", - "UNKNOWN", - "FOOBAR" - }; - OLD_MAP.put(OPTIONS.toString(), OPTIONS); - OLD_MAP.put(GET.toString(), GET); - OLD_MAP.put(HEAD.toString(), HEAD); - OLD_MAP.put(POST.toString(), POST); - OLD_MAP.put(PUT.toString(), PUT); - OLD_MAP.put(PATCH.toString(), PATCH); - OLD_MAP.put(DELETE.toString(), DELETE); - OLD_MAP.put(TRACE.toString(), TRACE); - OLD_MAP.put(CONNECT.toString(), CONNECT); - - NEW_MAP = new SimpleStringMap<>( - new SimpleStringMap.Node<>(OPTIONS.toString(), OPTIONS), - new SimpleStringMap.Node<>(GET.toString(), GET), - new SimpleStringMap.Node<>(HEAD.toString(), HEAD), - new SimpleStringMap.Node<>(POST.toString(), POST), - new SimpleStringMap.Node<>(PUT.toString(), PUT), - new SimpleStringMap.Node<>(PATCH.toString(), PATCH), - new SimpleStringMap.Node<>(DELETE.toString(), DELETE), - new SimpleStringMap.Node<>(TRACE.toString(), TRACE), - new SimpleStringMap.Node<>(CONNECT.toString(), CONNECT)); - } - - private static final class SimpleStringMap { - private final SimpleStringMap.Node[] values; - private final int valuesMask; - - SimpleStringMap(SimpleStringMap.Node... nodes) { - values = (SimpleStringMap.Node[]) new SimpleStringMap.Node[findNextPositivePowerOfTwo(nodes.length)]; - valuesMask = values.length - 1; - for (SimpleStringMap.Node node : nodes) { - int i = hashCode(node.key) & valuesMask; - if (values[i] != null) { - throw new IllegalArgumentException("index " + i + " collision between values: [" + - values[i].key + ", " + node.key + "]"); - } - values[i] = node; - } - } - - T get(String name) { - SimpleStringMap.Node node = values[hashCode(name) & valuesMask]; - return node == null || !node.key.equals(name) ? null : node.value; - } - - private static int hashCode(String name) { - // This hash code needs to produce a unique index for each HttpMethod. If new methods are added this - // algorithm will need to be adjusted. The goal is to have each enum name's hash value correlate to a unique - // index in the values array. - return name.hashCode() >>> 6; - } - - private static final class Node { - final String key; - final T value; - - Node(String key, T value) { - this.key = key; - this.value = value; - } - } - } - - @Benchmark - public void oldMapKnownMethods(Blackhole bh) throws Exception { - for (int i = 0; i < KNOWN_METHODS.length; ++i) { - bh.consume(OLD_MAP.get(KNOWN_METHODS[i])); - } - } - - @Benchmark - public void newMapKnownMethods(Blackhole bh) throws Exception { - for (int i = 0; i < KNOWN_METHODS.length; ++i) { - bh.consume(NEW_MAP.get(KNOWN_METHODS[i])); - } - } - - @Benchmark - public void oldMapMixMethods(Blackhole bh) throws Exception { - for (int i = 0; i < MIXED_METHODS.length; ++i) { - HttpMethod method = OLD_MAP.get(MIXED_METHODS[i]); - if (method != null) { - bh.consume(method); - } - } - } - - @Benchmark - public void newMapMixMethods(Blackhole bh) throws Exception { - for (int i = 0; i < MIXED_METHODS.length; ++i) { - HttpMethod method = NEW_MAP.get(MIXED_METHODS[i]); - if (method != null) { - bh.consume(method); - } - } - } - - @Benchmark - public void oldMapUnknownMethods(Blackhole bh) throws Exception { - for (int i = 0; i < UNKNOWN_METHODS.length; ++i) { - HttpMethod method = OLD_MAP.get(UNKNOWN_METHODS[i]); - if (method != null) { - bh.consume(method); - } - } - } - - @Benchmark - public void newMapUnknownMethods(Blackhole bh) throws Exception { - for (int i = 0; i < UNKNOWN_METHODS.length; ++i) { - HttpMethod method = NEW_MAP.get(UNKNOWN_METHODS[i]); - if (method != null) { - bh.consume(method); - } - } - } -} diff --git a/microbench/src/main/java/io/netty/handler/codec/http/HttpRequestEncoderInsertBenchmark.java b/microbench/src/main/java/io/netty/handler/codec/http/HttpRequestEncoderInsertBenchmark.java deleted file mode 100644 index 7d7df184c6..0000000000 --- a/microbench/src/main/java/io/netty/handler/codec/http/HttpRequestEncoderInsertBenchmark.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.buffer.Unpooled; -import io.netty.microbench.util.AbstractMicrobenchmark; -import io.netty.util.AsciiString; -import io.netty.util.CharsetUtil; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.Warmup; - -import static io.netty.handler.codec.http.HttpConstants.*; - -@State(Scope.Benchmark) -@Warmup(iterations = 10) -@Measurement(iterations = 20) -public class HttpRequestEncoderInsertBenchmark extends AbstractMicrobenchmark { - - private final String uri = "http://localhost?eventType=CRITICAL&from=0&to=1497437160327&limit=10&offset=0"; - private final OldHttpRequestEncoder encoderOld = new OldHttpRequestEncoder(); - private final HttpRequestEncoder encoderNew = new HttpRequestEncoder(); - - @Benchmark - public ByteBuf oldEncoder() throws Exception { - ByteBuf buffer = Unpooled.buffer(100); - try { - encoderOld.encodeInitialLine(buffer, new DefaultHttpRequest(HttpVersion.HTTP_1_1, - HttpMethod.GET, uri)); - return buffer; - } finally { - buffer.release(); - } - } - - @Benchmark - public ByteBuf newEncoder() throws Exception { - ByteBuf buffer = Unpooled.buffer(100); - try { - encoderNew.encodeInitialLine(buffer, new DefaultHttpRequest(HttpVersion.HTTP_1_1, - HttpMethod.GET, uri)); - return buffer; - } finally { - buffer.release(); - } - } - - private static class OldHttpRequestEncoder extends HttpObjectEncoder { - private static final byte[] CRLF = {CR, LF}; - private static final char SLASH = '/'; - private static final char QUESTION_MARK = '?'; - - @Override - public boolean acceptOutboundMessage(Object msg) throws Exception { - return super.acceptOutboundMessage(msg) && !(msg instanceof HttpResponse); - } - - @Override - protected void encodeInitialLine(ByteBuf buf, HttpRequest request) throws Exception { - AsciiString method = request.method().asciiName(); - ByteBufUtil.copy(method, method.arrayOffset(), buf, method.length()); - buf.writeByte(SP); - - // Add / as absolute path if no is present. - // See https://tools.ietf.org/html/rfc2616#section-5.1.2 - String uri = request.uri(); - - if (uri.isEmpty()) { - uri += SLASH; - } else { - int start = uri.indexOf("://"); - if (start != -1 && uri.charAt(0) != SLASH) { - int startIndex = start + 3; - // Correctly handle query params. - // See https://github.com/netty/netty/issues/2732 - int index = uri.indexOf(QUESTION_MARK, startIndex); - if (index == -1) { - if (uri.lastIndexOf(SLASH) <= startIndex) { - uri += SLASH; - } - } else { - if (uri.lastIndexOf(SLASH, index) <= startIndex) { - int len = uri.length(); - StringBuilder sb = new StringBuilder(len + 1); - sb.append(uri, 0, index) - .append(SLASH) - .append(uri, index, len); - uri = sb.toString(); - } - } - } - } - - buf.writeBytes(uri.getBytes(CharsetUtil.UTF_8)); - - buf.writeByte(SP); - request.protocolVersion().encode(buf); - buf.writeBytes(CRLF); - } - } -} diff --git a/microbench/src/main/java/io/netty/handler/codec/http/QueryStringDecoderBenchmark.java b/microbench/src/main/java/io/netty/handler/codec/http/QueryStringDecoderBenchmark.java deleted file mode 100644 index 9283bc6aa5..0000000000 --- a/microbench/src/main/java/io/netty/handler/codec/http/QueryStringDecoderBenchmark.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.microbench.util.AbstractMicrobenchmark; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Threads; -import org.openjdk.jmh.annotations.Warmup; - -import java.nio.charset.Charset; -import java.util.List; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -@Threads(1) -@Warmup(iterations = 3) -@Measurement(iterations = 3) -@OutputTimeUnit(TimeUnit.MICROSECONDS) -public class QueryStringDecoderBenchmark extends AbstractMicrobenchmark { - - private static final Charset SHIFT_JIS = Charset.forName("Shift-JIS"); - - @Benchmark - public Map> noDecoding() { - return new QueryStringDecoder("foo=bar&cat=dog", false).parameters(); - } - - @Benchmark - public Map> onlyDecoding() { - // ãģげ=ãŧけ&ねこ=いãŦ - return new QueryStringDecoder("%E3%81%BB%E3%81%92=%E3%81%BC%E3%81%91&%E3%81%AD%E3%81%93=%E3%81%84%E3%81%AC", - false) - .parameters(); - } - - @Benchmark - public Map> mixedDecoding() { - // foo=bar&ãģげ=ãŧけ&cat=dog&ねこ=いãŦ - return new QueryStringDecoder("foo=bar%E3%81%BB%E3%81%92=%E3%81%BC%E3%81%91&cat=dog&" + - "&%E3%81%AD%E3%81%93=%E3%81%84%E3%81%AC", false) - .parameters(); - } - - @Benchmark - public Map> nonStandardDecoding() { - // ãģげ=ãŧけ&ねこ=いãŦ in Shift-JIS - return new QueryStringDecoder("%82%D9%82%B0=%82%DA%82%AF&%82%CB%82%B1=%82%A2%82%CA", - SHIFT_JIS, false) - .parameters(); - } -} diff --git a/microbench/src/main/java/io/netty/handler/codec/http/QueryStringEncoderBenchmark.java b/microbench/src/main/java/io/netty/handler/codec/http/QueryStringEncoderBenchmark.java deleted file mode 100644 index b26faa5f77..0000000000 --- a/microbench/src/main/java/io/netty/handler/codec/http/QueryStringEncoderBenchmark.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.microbench.util.AbstractMicrobenchmark; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.Threads; -import org.openjdk.jmh.annotations.Warmup; - -import java.util.concurrent.TimeUnit; - -@Threads(1) -@Warmup(iterations = 3) -@Measurement(iterations = 3) -@OutputTimeUnit(TimeUnit.MICROSECONDS) -public class QueryStringEncoderBenchmark extends AbstractMicrobenchmark { - private String shortAscii; - private String shortUtf8; - private String shortAsciiFirst; - - private String longAscii; - private String longUtf8; - private String longAsciiFirst; - - @Setup - public void setUp() { - // Avoid constant pool for strings since it's common for at least values to not be constant. - shortAscii = new String("foo".toCharArray()); - shortUtf8 = new String("ãģげãģげ".toCharArray()); - shortAsciiFirst = shortAscii + shortUtf8; - longAscii = repeat(shortAscii, 100); - longUtf8 = repeat(shortUtf8, 100); - longAsciiFirst = longAscii + longUtf8; - } - - @Benchmark - public String shortAscii() { - return encode(shortAscii); - } - - @Benchmark - public String shortUtf8() { - return encode(shortUtf8); - } - - @Benchmark - public String shortAsciiFirst() { - return encode(shortAsciiFirst); - } - - @Benchmark - public String longAscii() { - return encode(longAscii); - } - - @Benchmark - public String longUtf8() { - return encode(longUtf8); - } - - @Benchmark - public String longAsciiFirst() { - return encode(longAsciiFirst); - } - - private static String encode(String s) { - QueryStringEncoder encoder = new QueryStringEncoder(""); - encoder.addParam(s, s); - return encoder.toString(); - } - - private static String repeat(String s, int num) { - StringBuilder sb = new StringBuilder(num * s.length()); - for (int i = 0; i < num; i++) { - sb.append(s); - } - return sb.toString(); - } -} diff --git a/microbench/src/main/java/io/netty/handler/codec/http/WriteBytesVsShortOrMediumBenchmark.java b/microbench/src/main/java/io/netty/handler/codec/http/WriteBytesVsShortOrMediumBenchmark.java deleted file mode 100644 index 0dcbcc771f..0000000000 --- a/microbench/src/main/java/io/netty/handler/codec/http/WriteBytesVsShortOrMediumBenchmark.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.buffer.Unpooled; -import io.netty.microbench.util.AbstractMicrobenchmark; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Threads; -import org.openjdk.jmh.annotations.Warmup; - -import java.util.concurrent.TimeUnit; - -import static io.netty.handler.codec.http.HttpConstants.*; - -@Threads(1) -@Warmup(iterations = 3) -@Measurement(iterations = 3) -@OutputTimeUnit(TimeUnit.MICROSECONDS) -public class WriteBytesVsShortOrMediumBenchmark extends AbstractMicrobenchmark { - private static final int CRLF_SHORT = (CR << 8) + LF; - private static final byte[] CRLF = { CR, LF }; - private static final int ZERO_CRLF_MEDIUM = ('0' << 16) + (CR << 8) + LF; - private static final byte[] ZERO_CRLF = { '0', CR, LF }; - - private final ByteBuf buf = Unpooled.directBuffer(16); - - @Benchmark - public ByteBuf shortInt() { - return ByteBufUtil.writeShortBE(buf, CRLF_SHORT).writerIndex(0); - } - - @Benchmark - public ByteBuf mediumInt() { - return ByteBufUtil.writeMediumBE(buf, ZERO_CRLF_MEDIUM).writerIndex(0); - } - - @Benchmark - public ByteBuf byteArray2() { - return buf.writeBytes(CRLF).writerIndex(0); - } - - @Benchmark - public ByteBuf byteArray3() { - return buf.writeBytes(ZERO_CRLF).writerIndex(0); - } - - @Benchmark - public ByteBuf chainedBytes2() { - return buf.writeByte(CR).writeByte(LF).writerIndex(0); - } - - @Benchmark - public ByteBuf chainedBytes3() { - return buf.writeByte('0').writeByte(CR).writeByte(LF).writerIndex(0); - } -} diff --git a/microbench/src/main/java/io/netty/handler/codec/http/multipart/HttpPostMultipartRequestDecoderBenchmark.java b/microbench/src/main/java/io/netty/handler/codec/http/multipart/HttpPostMultipartRequestDecoderBenchmark.java deleted file mode 100644 index 29d1013923..0000000000 --- a/microbench/src/main/java/io/netty/handler/codec/http/multipart/HttpPostMultipartRequestDecoderBenchmark.java +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.handler.codec.http.DefaultHttpContent; -import io.netty.handler.codec.http.DefaultHttpRequest; -import io.netty.handler.codec.http.DefaultLastHttpContent; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.microbench.util.AbstractMicrobenchmark; -import io.netty.util.CharsetUtil; -import io.netty.util.ResourceLeakDetector; -import io.netty.util.ResourceLeakDetector.Level; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Threads; -import org.openjdk.jmh.annotations.Warmup; - -import java.util.concurrent.TimeUnit; - - -@Threads(1) -@Warmup(iterations = 2) -@Measurement(iterations = 3) -@OutputTimeUnit(TimeUnit.MILLISECONDS) -public class HttpPostMultipartRequestDecoderBenchmark - extends AbstractMicrobenchmark { - - public double testHighNumberChunks(boolean big, boolean noDisk) { - String BOUNDARY = "01f136d9282f"; - int size = 8 * 1024; - int chunkNumber = 64; - StringBuilder stringBuilder = new StringBuilder(size); - stringBuilder.setLength(size); - String data = stringBuilder.toString(); - - byte[] bodyStartBytes = ("--" + BOUNDARY + "\n" + - "Content-Disposition: form-data; name=\"msg_id\"\n\n15200\n--" + - BOUNDARY + - "\nContent-Disposition: form-data; name=\"msg1\"; filename=\"file1.txt\"\n\n" + - data).getBytes(CharsetUtil.UTF_8); - byte[] bodyPartBigBytes = data.getBytes(CharsetUtil.UTF_8); - byte[] intermediaryBytes = ("\n--" + BOUNDARY + - "\nContent-Disposition: form-data; name=\"msg2\"; filename=\"file2.txt\"\n\n" + - data).getBytes(CharsetUtil.UTF_8); - byte[] finalBigBytes = ("\n" + "--" + BOUNDARY + "--\n").getBytes(CharsetUtil.UTF_8); - ByteBuf firstBuf = Unpooled.wrappedBuffer(bodyStartBytes); - ByteBuf finalBuf = Unpooled.wrappedBuffer(finalBigBytes); - ByteBuf nextBuf; - if (big) { - nextBuf = Unpooled.wrappedBuffer(bodyPartBigBytes); - } else { - nextBuf = Unpooled.wrappedBuffer(intermediaryBytes); - } - DefaultHttpRequest req = - new DefaultHttpRequest(HttpVersion.HTTP_1_0, HttpMethod.POST, "/up"); - req.headers().add(HttpHeaderNames.CONTENT_TYPE, - "multipart/form-data; boundary=" + BOUNDARY); - - long start = System.nanoTime(); - - DefaultHttpDataFactory defaultHttpDataFactory = - new DefaultHttpDataFactory(noDisk? 1024 * 1024 : 16 * 1024); - HttpPostRequestDecoder decoder = - new HttpPostRequestDecoder(defaultHttpDataFactory, req); - firstBuf.retain(); - decoder.offer(new DefaultHttpContent(firstBuf)); - firstBuf.release(); - for (int i = 1; i < chunkNumber; i++) { - nextBuf.retain(); - decoder.offer(new DefaultHttpContent(nextBuf)); - nextBuf.release(); - nextBuf.readerIndex(0); - } - finalBuf.retain(); - decoder.offer(new DefaultLastHttpContent(finalBuf)); - finalBuf.release(); - while (decoder.hasNext()) { - InterfaceHttpData httpData = decoder.next(); - } - while (finalBuf.refCnt() > 0) { - finalBuf.release(); - } - while (nextBuf.refCnt() > 0) { - nextBuf.release(); - } - while (finalBuf.refCnt() > 0) { - finalBuf.release(); - } - long stop = System.nanoTime(); - double time = (stop - start) / 1000000.0; - defaultHttpDataFactory.cleanAllHttpData(); - defaultHttpDataFactory.cleanRequestHttpData(req); - decoder.destroy(); - return time; - } - - @Benchmark - public double multipartRequestDecoderHighDisabledLevel() { - final Level level = ResourceLeakDetector.getLevel(); - try { - ResourceLeakDetector.setLevel(Level.DISABLED); - return testHighNumberChunks(false, true); - } finally { - ResourceLeakDetector.setLevel(level); - } - } - - @Benchmark - public double multipartRequestDecoderBigDisabledLevel() { - final Level level = ResourceLeakDetector.getLevel(); - try { - ResourceLeakDetector.setLevel(Level.DISABLED); - return testHighNumberChunks(true, true); - } finally { - ResourceLeakDetector.setLevel(level); - } - } - - @Benchmark - public double multipartRequestDecoderHighSimpleLevel() { - final Level level = ResourceLeakDetector.getLevel(); - try { - ResourceLeakDetector.setLevel(Level.SIMPLE); - return testHighNumberChunks(false, true); - } finally { - ResourceLeakDetector.setLevel(level); - } - } - - @Benchmark - public double multipartRequestDecoderBigSimpleLevel() { - final Level level = ResourceLeakDetector.getLevel(); - try { - ResourceLeakDetector.setLevel(Level.SIMPLE); - return testHighNumberChunks(true, true); - } finally { - ResourceLeakDetector.setLevel(level); - } - } - - @Benchmark - public double multipartRequestDecoderHighAdvancedLevel() { - final Level level = ResourceLeakDetector.getLevel(); - try { - ResourceLeakDetector.setLevel(Level.ADVANCED); - return testHighNumberChunks(false, true); - } finally { - ResourceLeakDetector.setLevel(level); - } - } - - @Benchmark - public double multipartRequestDecoderBigAdvancedLevel() { - final Level level = ResourceLeakDetector.getLevel(); - try { - ResourceLeakDetector.setLevel(Level.ADVANCED); - return testHighNumberChunks(true, true); - } finally { - ResourceLeakDetector.setLevel(level); - } - } - - @Benchmark - public double multipartRequestDecoderHighParanoidLevel() { - final Level level = ResourceLeakDetector.getLevel(); - try { - ResourceLeakDetector.setLevel(Level.PARANOID); - return testHighNumberChunks(false, true); - } finally { - ResourceLeakDetector.setLevel(level); - } - } - - @Benchmark - public double multipartRequestDecoderBigParanoidLevel() { - final Level level = ResourceLeakDetector.getLevel(); - try { - ResourceLeakDetector.setLevel(Level.PARANOID); - return testHighNumberChunks(true, true); - } finally { - ResourceLeakDetector.setLevel(level); - } - } - -} diff --git a/microbench/src/main/java/io/netty/handler/codec/http/multipart/package-info.java b/microbench/src/main/java/io/netty/handler/codec/http/multipart/package-info.java deleted file mode 100644 index 1f4cc119dc..0000000000 --- a/microbench/src/main/java/io/netty/handler/codec/http/multipart/package-info.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -/** - * Benchmarks for {@link io.netty.handler.codec.http.multipart}. - */ -package io.netty.handler.codec.http.multipart; diff --git a/microbench/src/main/java/io/netty/handler/codec/http/package-info.java b/microbench/src/main/java/io/netty/handler/codec/http/package-info.java deleted file mode 100644 index b1686c7fc0..0000000000 --- a/microbench/src/main/java/io/netty/handler/codec/http/package-info.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -/** - * Benchmarks for {@link io.netty.handler.codec.http}. - */ -package io.netty.handler.codec.http; 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 deleted file mode 100644 index 8eeb043fd7..0000000000 --- a/microbench/src/main/java/io/netty/handler/codec/http2/HpackBenchmarkUtil.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/* - * Copyright 2015 Twitter, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.netty.handler.codec.http2; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Utility methods for hpack tests. - */ -public final class HpackBenchmarkUtil { - private HpackBenchmarkUtil() { - } - - /** - * Internal key used to index a particular set of headers in the map. - */ - private static class HeadersKey { - final HpackHeadersSize size; - final boolean limitToAscii; - - HeadersKey(HpackHeadersSize size, boolean limitToAscii) { - this.size = size; - this.limitToAscii = limitToAscii; - } - - List newHeaders() { - return size.newHeaders(limitToAscii); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - HeadersKey that = (HeadersKey) o; - - if (limitToAscii != that.limitToAscii) { - return false; - } - return size == that.size; - } - - @Override - public int hashCode() { - int result = size.hashCode(); - result = 31 * result + (limitToAscii ? 1 : 0); - return result; - } - } - - private static final Map> headersMap; - - static { - HpackHeadersSize[] sizes = HpackHeadersSize.values(); - headersMap = new HashMap<>(sizes.length * 2); - for (HpackHeadersSize size : sizes) { - HeadersKey key = new HeadersKey(size, true); - headersMap.put(key, key.newHeaders()); - - key = new HeadersKey(size, false); - headersMap.put(key, key.newHeaders()); - } - } - - /** - * Gets headers for the given size and whether the key/values should be limited to ASCII. - */ - static List headers(HpackHeadersSize size, boolean limitToAscii) { - return headersMap.get(new HeadersKey(size, limitToAscii)); - } - - static Http2Headers http2Headers(HpackHeadersSize size, boolean limitToAscii) { - List hpackHeaders = headersMap.get(new HeadersKey(size, limitToAscii)); - Http2Headers http2Headers = new DefaultHttp2Headers(false); - for (int i = 0; i < hpackHeaders.size(); ++i) { - HpackHeader hpackHeader = hpackHeaders.get(i); - http2Headers.add(hpackHeader.name, hpackHeader.value); - } - return http2Headers; - } -} diff --git a/microbench/src/main/java/io/netty/handler/codec/http2/HpackDecoderBenchmark.java b/microbench/src/main/java/io/netty/handler/codec/http2/HpackDecoderBenchmark.java deleted file mode 100644 index 445c324a5e..0000000000 --- a/microbench/src/main/java/io/netty/handler/codec/http2/HpackDecoderBenchmark.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/* - * Copyright 2015 Twitter, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.microbench.util.AbstractMicrobenchmark; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.Level; -import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.Param; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.TearDown; -import org.openjdk.jmh.infra.Blackhole; - -import static io.netty.buffer.Unpooled.wrappedBuffer; -import static io.netty.handler.codec.http2.HpackBenchmarkUtil.http2Headers; - -public class HpackDecoderBenchmark extends AbstractMicrobenchmark { - - @Param - public HpackHeadersSize size; - - @Param({ "true", "false" }) - public boolean sensitive; - - @Param({ "true", "false" }) - public boolean limitToAscii; - - private ByteBuf input; - - @Setup(Level.Trial) - public void setup() throws Http2Exception { - input = wrappedBuffer(getSerializedHeaders(http2Headers(size, limitToAscii), sensitive)); - } - - @TearDown(Level.Trial) - public void teardown() { - input.release(); - } - - @Benchmark - @BenchmarkMode(Mode.Throughput) - public void decode(final Blackhole bh) throws Http2Exception { - HpackDecoder hpackDecoder = new HpackDecoder(Integer.MAX_VALUE); - @SuppressWarnings("unchecked") - Http2Headers headers = - new DefaultHttp2Headers() { - @Override - public Http2Headers add(CharSequence name, CharSequence value) { - bh.consume(sensitive); - return this; - } - }; - hpackDecoder.decode(0, input.duplicate(), headers, true); - } - - private byte[] getSerializedHeaders(Http2Headers headers, boolean sensitive) throws Http2Exception { - HpackEncoder hpackEncoder = HpackUtilBenchmark.newTestEncoder(); - ByteBuf out = size.newOutBuffer(); - try { - hpackEncoder.encodeHeaders(3 /* randomly chosen */, out, headers, - sensitive ? Http2HeadersEncoder.ALWAYS_SENSITIVE - : Http2HeadersEncoder.NEVER_SENSITIVE); - byte[] bytes = new byte[out.readableBytes()]; - out.readBytes(bytes); - return bytes; - } finally { - out.release(); - } - } -} diff --git a/microbench/src/main/java/io/netty/handler/codec/http2/HpackDecoderULE128Benchmark.java b/microbench/src/main/java/io/netty/handler/codec/http2/HpackDecoderULE128Benchmark.java deleted file mode 100644 index fc098628de..0000000000 --- a/microbench/src/main/java/io/netty/handler/codec/http2/HpackDecoderULE128Benchmark.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.microbench.util.AbstractMicrobenchmark; -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.Scope; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.Threads; -import org.openjdk.jmh.annotations.Warmup; - -import java.util.concurrent.TimeUnit; - -@Threads(1) -@State(Scope.Benchmark) -@Fork(1) -@Warmup(iterations = 5) -@Measurement(iterations = 10) -@OutputTimeUnit(TimeUnit.NANOSECONDS) -public class HpackDecoderULE128Benchmark extends AbstractMicrobenchmark { - private static final Http2Exception DECODE_ULE_128_TO_LONG_DECOMPRESSION_EXCEPTION = - new Http2Exception(Http2Error.COMPRESSION_ERROR); - private static final Http2Exception DECODE_ULE_128_TO_INT_DECOMPRESSION_EXCEPTION = - new Http2Exception(Http2Error.COMPRESSION_ERROR); - private static final Http2Exception DECODE_ULE_128_DECOMPRESSION_EXCEPTION = - new Http2Exception(Http2Error.COMPRESSION_ERROR); - - private ByteBuf longMaxBuf; - private ByteBuf intMaxBuf; - - @Setup - public void setup() { - byte[] longMax = {(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, - (byte) 0xFF, (byte) 0x7F}; - longMaxBuf = Unpooled.wrappedBuffer(longMax); - byte[] intMax = {(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x07}; - intMaxBuf = Unpooled.wrappedBuffer(intMax); - } - - @Benchmark - @BenchmarkMode(Mode.AverageTime) - public long decodeMaxLong() throws Http2Exception { - long v = decodeULE128(longMaxBuf, 0L); - longMaxBuf.readerIndex(0); - return v; - } - - @Benchmark - @BenchmarkMode(Mode.AverageTime) - public long decodeMaxIntWithLong() throws Http2Exception { - long v = decodeULE128(intMaxBuf, 0L); - intMaxBuf.readerIndex(0); - return v; - } - - @Benchmark - @BenchmarkMode(Mode.AverageTime) - public int decodeMaxInt() throws Http2Exception { - int v = decodeULE128(intMaxBuf, 0); - intMaxBuf.readerIndex(0); - return v; - } - - @Benchmark - @BenchmarkMode(Mode.AverageTime) - public int decodeMaxIntUsingLong() throws Http2Exception { - int v = decodeULE128UsingLong(intMaxBuf, 0); - intMaxBuf.readerIndex(0); - return v; - } - - static int decodeULE128UsingLong(ByteBuf in, int result) throws Http2Exception { - final int readerIndex = in.readerIndex(); - final long v = decodeULE128(in, (long) result); - if (v > Integer.MAX_VALUE) { - in.readerIndex(readerIndex); - throw DECODE_ULE_128_TO_INT_DECOMPRESSION_EXCEPTION; - } - return (int) v; - } - - static long decodeULE128(ByteBuf in, long result) throws Http2Exception { - assert result <= 0x7f && result >= 0; - final boolean resultStartedAtZero = result == 0; - final int writerIndex = in.writerIndex(); - for (int readerIndex = in.readerIndex(), shift = 0; readerIndex < writerIndex; ++readerIndex, shift += 7) { - byte b = in.getByte(readerIndex); - if (shift == 56 && ((b & 0x80) != 0 || b == 0x7F && !resultStartedAtZero)) { - // the maximum value that can be represented by a signed 64 bit number is: - // [0x01L, 0x7fL] + 0x7fL + (0x7fL << 7) + (0x7fL << 14) + (0x7fL << 21) + (0x7fL << 28) + (0x7fL << 35) - // + (0x7fL << 42) + (0x7fL << 49) + (0x7eL << 56) - // OR - // 0x0L + 0x7fL + (0x7fL << 7) + (0x7fL << 14) + (0x7fL << 21) + (0x7fL << 28) + (0x7fL << 35) + - // (0x7fL << 42) + (0x7fL << 49) + (0x7fL << 56) - // this means any more shifts will result longMaxBuf overflow so we should break out and throw an error. - throw DECODE_ULE_128_TO_LONG_DECOMPRESSION_EXCEPTION; - } - - if ((b & 0x80) == 0) { - in.readerIndex(readerIndex + 1); - return result + ((b & 0x7FL) << shift); - } - result += (b & 0x7FL) << shift; - } - - throw DECODE_ULE_128_DECOMPRESSION_EXCEPTION; - } - - static int decodeULE128(ByteBuf in, int result) throws Http2Exception { - assert result <= 0x7f && result >= 0; - final boolean resultStartedAtZero = result == 0; - final int writerIndex = in.writerIndex(); - for (int readerIndex = in.readerIndex(), shift = 0; readerIndex < writerIndex; ++readerIndex, shift += 7) { - byte b = in.getByte(readerIndex); - if (shift == 28 && ((b & 0x80) != 0 || !resultStartedAtZero && b > 6 || resultStartedAtZero && b > 7)) { - // the maximum value that can be represented by a signed 32 bit number is: - // [0x1,0x7f] + 0x7f + (0x7f << 7) + (0x7f << 14) + (0x7f << 21) + (0x6 << 28) - // OR - // 0x0 + 0x7f + (0x7f << 7) + (0x7f << 14) + (0x7f << 21) + (0x7 << 28) - // this means any more shifts will result longMaxBuf overflow so we should break out and throw an error. - throw DECODE_ULE_128_TO_INT_DECOMPRESSION_EXCEPTION; - } - - if ((b & 0x80) == 0) { - in.readerIndex(readerIndex + 1); - return result + ((b & 0x7F) << shift); - } - result += (b & 0x7F) << shift; - } - - throw DECODE_ULE_128_DECOMPRESSION_EXCEPTION; - } -} diff --git a/microbench/src/main/java/io/netty/handler/codec/http2/HpackEncoderBenchmark.java b/microbench/src/main/java/io/netty/handler/codec/http2/HpackEncoderBenchmark.java deleted file mode 100644 index 7d6d83785d..0000000000 --- a/microbench/src/main/java/io/netty/handler/codec/http2/HpackEncoderBenchmark.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/* - * Copyright 2015 Twitter, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.microbench.util.AbstractMicrobenchmark; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.Fork; -import org.openjdk.jmh.annotations.Level; -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.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.annotations.Warmup; -import org.openjdk.jmh.infra.Blackhole; - -import java.util.Iterator; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -@Fork(1) -@Threads(1) -@State(Scope.Benchmark) -@Warmup(iterations = 5) -@Measurement(iterations = 5) -@OutputTimeUnit(TimeUnit.NANOSECONDS) -public class HpackEncoderBenchmark extends AbstractMicrobenchmark { - - @Param - public HpackHeadersSize size; - - @Param({ "true", "false" }) - public boolean sensitive; - - @Param({ "true", "false" }) - public boolean duplicates; - - @Param({ "true", "false" }) - public boolean limitToAscii; - - private Http2Headers http2Headers; - private ByteBuf output; - private Http2HeadersEncoder.SensitivityDetector sensitivityDetector; - - @Setup(Level.Trial) - public void setup() { - http2Headers = HpackBenchmarkUtil.http2Headers(size, limitToAscii); - if (duplicates) { - int size = http2Headers.size(); - if (size > 0) { - Iterator> itr = http2Headers.iterator(); - Map.Entry entry = itr.next(); - http2Headers.clear(); - for (int i = 0; i < size; ++i) { - http2Headers.add(entry.getKey(), entry.getValue()); - } - } - } - output = size.newOutBuffer(); - sensitivityDetector = sensitive ? Http2HeadersEncoder.ALWAYS_SENSITIVE : Http2HeadersEncoder.NEVER_SENSITIVE; - } - - @TearDown(Level.Trial) - public void tearDown() { - output.release(); - } - - @Benchmark - @BenchmarkMode(Mode.AverageTime) - public void encode(Blackhole bh) throws Exception { - HpackEncoder hpackEncoder = HpackUtilBenchmark.newTestEncoder(); - output.clear(); - hpackEncoder.encodeHeaders(3 /*randomly chosen*/, output, http2Headers, sensitivityDetector); - bh.consume(output); - } -} diff --git a/microbench/src/main/java/io/netty/handler/codec/http2/HpackHeader.java b/microbench/src/main/java/io/netty/handler/codec/http2/HpackHeader.java deleted file mode 100644 index de100bc349..0000000000 --- a/microbench/src/main/java/io/netty/handler/codec/http2/HpackHeader.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/* - * Copyright 2015 Twitter, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.util.AsciiString; - -import java.util.ArrayList; -import java.util.List; -import java.util.Random; - -/** - * Helper class representing a single header entry. Used by the benchmarks. - */ -final class HpackHeader { - private static final String ALPHABET = - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"; - - final CharSequence name; - final CharSequence value; - - private HpackHeader(byte[] name, byte[] value) { - this.name = new AsciiString(name, false); - this.value = new AsciiString(value, false); - } - - /** - * Creates a number of random headers with the given name/value lengths. - */ - static List createHeaders(int numHeaders, int nameLength, int valueLength, - boolean limitToAscii) { - List hpackHeaders = new ArrayList<>(numHeaders); - for (int i = 0; i < numHeaders; ++i) { - // Force always ascii for header names - byte[] name = randomBytes(new byte[nameLength], true); - byte[] value = randomBytes(new byte[valueLength], limitToAscii); - hpackHeaders.add(new HpackHeader(name, value)); - } - return hpackHeaders; - } - - private static byte[] randomBytes(byte[] bytes, boolean limitToAscii) { - Random r = new Random(); - if (limitToAscii) { - for (int index = 0; index < bytes.length; ++index) { - int charIndex = r.nextInt(ALPHABET.length()); - bytes[index] = (byte) ALPHABET.charAt(charIndex); - } - } else { - r.nextBytes(bytes); - } - return bytes; - } -} diff --git a/microbench/src/main/java/io/netty/handler/codec/http2/HpackHeadersSize.java b/microbench/src/main/java/io/netty/handler/codec/http2/HpackHeadersSize.java deleted file mode 100644 index d145244ad7..0000000000 --- a/microbench/src/main/java/io/netty/handler/codec/http2/HpackHeadersSize.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/* - * Copyright 2015 Twitter, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; - -import java.util.List; - -/** - * Enum that indicates the size of the headers to be used for the benchmark. - */ -public enum HpackHeadersSize { - SMALL(5, 20, 40), - MEDIUM(20, 40, 80), - LARGE(100, 100, 300); - - private final int numHeaders; - private final int nameLength; - private final int valueLength; - - HpackHeadersSize(int numHeaders, int nameLength, int valueLength) { - this.numHeaders = numHeaders; - this.nameLength = nameLength; - this.valueLength = valueLength; - } - - public List newHeaders(boolean limitAscii) { - return HpackHeader.createHeaders(numHeaders, nameLength, valueLength, limitAscii); - } - - public ByteBuf newOutBuffer() { - return Unpooled.buffer(numHeaders * (nameLength + valueLength)); - } -} diff --git a/microbench/src/main/java/io/netty/handler/codec/http2/HpackStaticTableBenchmark.java b/microbench/src/main/java/io/netty/handler/codec/http2/HpackStaticTableBenchmark.java deleted file mode 100644 index e771cfcea8..0000000000 --- a/microbench/src/main/java/io/netty/handler/codec/http2/HpackStaticTableBenchmark.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.microbench.util.AbstractMicrobenchmark; -import io.netty.util.CharsetUtil; -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.Scope; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.Threads; -import org.openjdk.jmh.annotations.Warmup; - -import java.util.concurrent.TimeUnit; - -import io.netty.util.AsciiString; - -@Fork(1) -@Threads(1) -@State(Scope.Benchmark) -@Warmup(iterations = 5) -@Measurement(iterations = 5) -@OutputTimeUnit(TimeUnit.NANOSECONDS) -public class HpackStaticTableBenchmark extends AbstractMicrobenchmark { - - private static final CharSequence X_CONTENT_ENCODING = - new AsciiString("x-content-encoding".getBytes(CharsetUtil.US_ASCII), false); - private static final CharSequence X_GZIP = new AsciiString("x-gzip".getBytes(CharsetUtil.US_ASCII), false); - private static final CharSequence STATUS = new AsciiString(":status".getBytes(CharsetUtil.US_ASCII), false); - private static final CharSequence STATUS_200 = new AsciiString("200".getBytes(CharsetUtil.US_ASCII), false); - private static final CharSequence STATUS_500 = new AsciiString("500".getBytes(CharsetUtil.US_ASCII), false); - private static final CharSequence AUTHORITY = - new AsciiString(":authority".getBytes(CharsetUtil.US_ASCII), false); - private static final CharSequence AUTHORITY_NETTY = - new AsciiString("netty.io".getBytes(CharsetUtil.US_ASCII), false); - private static final CharSequence USER_AGENT = - new AsciiString("user-agent".getBytes(CharsetUtil.US_ASCII), false); - private static final CharSequence USER_AGENT_CURL = - new AsciiString("curl/7.64.1".getBytes(CharsetUtil.US_ASCII), false); - - @Benchmark - @BenchmarkMode(Mode.AverageTime) - public int lookupNoNameMatch() { - return HpackStaticTable.getIndexInsensitive(X_CONTENT_ENCODING, X_GZIP); - } - - @Benchmark - @BenchmarkMode(Mode.AverageTime) - public int lookupNameAndValueMatchFirst() { - return HpackStaticTable.getIndexInsensitive(STATUS, STATUS_200); - } - - @Benchmark - @BenchmarkMode(Mode.AverageTime) - public int lookupNameAndValueMatchLast() { - return HpackStaticTable.getIndexInsensitive(STATUS, STATUS_500); - } - - @Benchmark - @BenchmarkMode(Mode.AverageTime) - public int lookupNameOnlyMatchBeginTable() { - return HpackStaticTable.getIndexInsensitive(AUTHORITY, AUTHORITY_NETTY); - } - - @Benchmark - @BenchmarkMode(Mode.AverageTime) - public int lookupNameOnlyMatchEndTable() { - return HpackStaticTable.getIndexInsensitive(USER_AGENT, USER_AGENT_CURL); - } -} diff --git a/microbench/src/main/java/io/netty/handler/codec/http2/HpackUtilBenchmark.java b/microbench/src/main/java/io/netty/handler/codec/http2/HpackUtilBenchmark.java deleted file mode 100644 index 45da548c3a..0000000000 --- a/microbench/src/main/java/io/netty/handler/codec/http2/HpackUtilBenchmark.java +++ /dev/null @@ -1,113 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.microbench.util.AbstractMicrobenchmark; -import io.netty.util.AsciiString; -import io.netty.util.internal.ConstantTimeUtils; -import io.netty.util.internal.PlatformDependent; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.Level; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Param; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.Threads; -import org.openjdk.jmh.annotations.Warmup; - -import java.util.List; - -import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_HEADER_LIST_SIZE; -import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_HEADER_TABLE_SIZE; - -@Threads(1) -@Warmup(iterations = 5) -@Measurement(iterations = 5) -public class HpackUtilBenchmark extends AbstractMicrobenchmark { - @Param - public HpackHeadersSize size; - - private List hpackHeaders; - - @Setup(Level.Trial) - public void setup() { - hpackHeaders = HpackBenchmarkUtil.headers(size, false); - } - - @Benchmark - public int oldEquals() { - int count = 0; - for (int i = 0; i < hpackHeaders.size(); ++i) { - HpackHeader hpackHeader = hpackHeaders.get(i); - if (oldEquals(hpackHeader.name, hpackHeader.name)) { - ++count; - } - } - return count; - } - - @Benchmark - public int newEquals() { - int count = 0; - for (int i = 0; i < hpackHeaders.size(); ++i) { - HpackHeader hpackHeader = hpackHeaders.get(i); - if (newEquals(hpackHeader.name, hpackHeader.name)) { - ++count; - } - } - return count; - } - - private static boolean oldEquals(CharSequence s1, CharSequence s2) { - if (s1.length() != s2.length()) { - return false; - } - char c = 0; - for (int i = 0; i < s1.length(); i++) { - c |= s1.charAt(i) ^ s2.charAt(i); - } - return c == 0; - } - - private static boolean newEquals(CharSequence s1, CharSequence s2) { - if (s1 instanceof AsciiString && s2 instanceof AsciiString) { - if (s1.length() != s2.length()) { - return false; - } - AsciiString s1Ascii = (AsciiString) s1; - AsciiString s2Ascii = (AsciiString) s2; - return PlatformDependent.equalsConstantTime(s1Ascii.array(), s1Ascii.arrayOffset(), - s2Ascii.array(), s2Ascii.arrayOffset(), s1.length()) != 0; - } - - return ConstantTimeUtils.equalsConstantTime(s1, s2) != 0; - } - - static HpackEncoder newTestEncoder() { - HpackEncoder hpackEncoder = new HpackEncoder(); - ByteBuf buf = Unpooled.buffer(); - try { - hpackEncoder.setMaxHeaderTableSize(buf, MAX_HEADER_TABLE_SIZE); - hpackEncoder.setMaxHeaderListSize(MAX_HEADER_LIST_SIZE); - } catch (Http2Exception e) { - throw new Error("max size not allowed?", e); - } finally { - buf.release(); - } - return hpackEncoder; - } -} diff --git a/microbench/src/main/java/io/netty/handler/codec/http2/Http2FrameWriterDataBenchmark.java b/microbench/src/main/java/io/netty/handler/codec/http2/Http2FrameWriterDataBenchmark.java deleted file mode 100644 index 3509cc54c7..0000000000 --- a/microbench/src/main/java/io/netty/handler/codec/http2/Http2FrameWriterDataBenchmark.java +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.PooledByteBufAllocator; -import io.netty.buffer.Unpooled; -import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.microbench.channel.EmbeddedChannelWriteReleaseHandlerContext; -import io.netty.microbench.util.AbstractMicrobenchmark; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.Fork; -import org.openjdk.jmh.annotations.Level; -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.Scope; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.TearDown; -import org.openjdk.jmh.annotations.Warmup; - -import java.util.concurrent.TimeUnit; - -import static io.netty.buffer.Unpooled.directBuffer; -import static io.netty.buffer.Unpooled.unreleasableBuffer; -import static io.netty.handler.codec.http2.Http2CodecUtil.DATA_FRAME_HEADER_LENGTH; -import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_MAX_FRAME_SIZE; -import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_UNSIGNED_BYTE; -import static io.netty.handler.codec.http2.Http2CodecUtil.verifyPadding; -import static io.netty.handler.codec.http2.Http2CodecUtil.writeFrameHeaderInternal; -import static io.netty.handler.codec.http2.Http2FrameTypes.DATA; -import static io.netty.util.internal.ObjectUtil.checkPositive; -import static java.lang.Math.max; -import static java.lang.Math.min; - -@Fork(1) -@Warmup(iterations = 5) -@Measurement(iterations = 5) -@State(Scope.Benchmark) -@OutputTimeUnit(TimeUnit.NANOSECONDS) -public class Http2FrameWriterDataBenchmark extends AbstractMicrobenchmark { - @Param({ "64", "1024", "4096", "16384", "1048576", "4194304" }) - public int payloadSize; - - @Param({ "0", "100", "255" }) - public int padding; - - @Param({ "true", "false" }) - public boolean pooled; - - private ByteBuf payload; - private ChannelHandlerContext ctx; - private Http2DataWriter writer; - private Http2DataWriter oldWriter; - - @Setup(Level.Trial) - public void setup() { - writer = new DefaultHttp2FrameWriter(); - oldWriter = new OldDefaultHttp2FrameWriter(); - payload = pooled ? PooledByteBufAllocator.DEFAULT.buffer(payloadSize) : Unpooled.buffer(payloadSize); - payload.writeZero(payloadSize); - ctx = new EmbeddedChannelWriteReleaseHandlerContext( - pooled ? PooledByteBufAllocator.DEFAULT : UnpooledByteBufAllocator.DEFAULT, - new ChannelHandler() { }) { - @Override - protected void handleException(Throwable t) { - handleUnexpectedException(t); - } - }; - } - - @TearDown(Level.Trial) - public void teardown() throws Exception { - if (payload != null) { - payload.release(); - } - if (ctx != null) { - ctx.close(); - } - } - - @Benchmark - @BenchmarkMode(Mode.AverageTime) - public void newWriter() { - writer.writeData(ctx, 3, payload.retain(), padding, true); - ctx.flush(); - } - - @Benchmark - @BenchmarkMode(Mode.AverageTime) - public void oldWriter() { - oldWriter.writeData(ctx, 3, payload.retain(), padding, true); - ctx.flush(); - } - - private static final class OldDefaultHttp2FrameWriter implements Http2DataWriter { - private static final ByteBuf ZERO_BUFFER = - unreleasableBuffer(directBuffer(MAX_UNSIGNED_BYTE).writeZero(MAX_UNSIGNED_BYTE)).asReadOnly(); - private final int maxFrameSize = DEFAULT_MAX_FRAME_SIZE; - @Override - public Future writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data, - int padding, boolean endStream) { - final Http2CodecUtil.SimpleChannelPromiseAggregator promiseAggregator = - new Http2CodecUtil.SimpleChannelPromiseAggregator(ctx.newPromise(), ctx.executor()); - final DataFrameHeader header = new DataFrameHeader(ctx, streamId); - boolean needToReleaseHeaders = true; - boolean needToReleaseData = true; - try { - checkPositive(streamId, "streamId"); - verifyPadding(padding); - - boolean lastFrame; - int remainingData = data.readableBytes(); - do { - // Determine how much data and padding to write in this frame. Put all padding at the end. - int frameDataBytes = min(remainingData, maxFrameSize); - int framePaddingBytes = min(padding, max(0, maxFrameSize - 1 - frameDataBytes)); - - // Decrement the remaining counters. - padding -= framePaddingBytes; - remainingData -= frameDataBytes; - - // Determine whether or not this is the last frame to be sent. - lastFrame = remainingData == 0 && padding == 0; - - // Only the last frame is not retained. Until then, the outer finally must release. - ByteBuf frameHeader = header.slice(frameDataBytes, framePaddingBytes, lastFrame && endStream); - needToReleaseHeaders = !lastFrame; - ctx.write(lastFrame ? frameHeader : frameHeader.retain()).cascadeTo(promiseAggregator.newPromise()); - - // Write the frame data. - ByteBuf frameData = data.readSlice(frameDataBytes); - // Only the last frame is not retained. Until then, the outer finally must release. - needToReleaseData = !lastFrame; - ctx.write(lastFrame ? frameData : frameData.retain()) - .cascadeTo(promiseAggregator.newPromise()); - - // Write the frame padding. - if (paddingBytes(framePaddingBytes) > 0) { - ctx.write(ZERO_BUFFER.slice(0, paddingBytes(framePaddingBytes))) - .cascadeTo(promiseAggregator.newPromise()); - } - } while (!lastFrame); - } catch (Throwable t) { - try { - if (needToReleaseHeaders) { - header.release(); - } - if (needToReleaseData) { - data.release(); - } - } finally { - promiseAggregator.setFailure(t); - promiseAggregator.doneAllocatingPromises(); - } - return promiseAggregator; - } - return promiseAggregator.doneAllocatingPromises(); - } - - private static int paddingBytes(int padding) { - // The padding parameter contains the 1 byte pad length field as well as the trailing padding bytes. - // Subtract 1, so to only get the number of padding bytes that need to be appended to the end of a frame. - return padding - 1; - } - - private static void writePaddingLength(ByteBuf buf, int padding) { - if (padding > 0) { - // It is assumed that the padding length has been bounds checked before this - // Minus 1, as the pad length field is included in the padding parameter and is 1 byte wide. - buf.writeByte(padding - 1); - } - } - - /** - * Utility class that manages the creation of frame header buffers for {@code DATA} frames. Attempts - * to reuse the same buffer repeatedly when splitting data into multiple frames. - */ - private static final class DataFrameHeader { - private final int streamId; - private final ByteBuf buffer; - private final Http2Flags flags = new Http2Flags(); - private int prevData; - private int prevPadding; - private ByteBuf frameHeader; - - DataFrameHeader(ChannelHandlerContext ctx, int streamId) { - // All padding will be put at the end, so in the worst case we need 3 headers: - // a repeated no-padding frame of maxFrameSize, a frame that has part data and part - // padding, and a frame that has the remainder of the padding. - buffer = ctx.alloc().buffer(3 * DATA_FRAME_HEADER_LENGTH); - this.streamId = streamId; - } - - /** - * Gets the frame header buffer configured for the current frame. - */ - ByteBuf slice(int data, int padding, boolean endOfStream) { - // Since we're reusing the current frame header whenever possible, check if anything changed - // that requires a new header. - if (data != prevData || padding != prevPadding - || endOfStream != flags.endOfStream() || frameHeader == null) { - // Update the header state. - prevData = data; - prevPadding = padding; - flags.paddingPresent(padding > 0); - flags.endOfStream(endOfStream); - frameHeader = buffer.slice(buffer.readerIndex(), DATA_FRAME_HEADER_LENGTH).writerIndex(0); - buffer.setIndex(buffer.readerIndex() + DATA_FRAME_HEADER_LENGTH, - buffer.writerIndex() + DATA_FRAME_HEADER_LENGTH); - - int payloadLength = data + padding; - writeFrameHeaderInternal(frameHeader, payloadLength, DATA, flags, streamId); - writePaddingLength(frameHeader, padding); - } - return frameHeader.slice(); - } - - void release() { - buffer.release(); - } - } - } -} diff --git a/microbench/src/main/java/io/netty/handler/codec/http2/package-info.java b/microbench/src/main/java/io/netty/handler/codec/http2/package-info.java deleted file mode 100644 index e618206711..0000000000 --- a/microbench/src/main/java/io/netty/handler/codec/http2/package-info.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -/** - * Benchmarks for {@link io.netty.handler.codec.http2}. - */ -package io.netty.handler.codec.http2; diff --git a/microbench/src/main/java/io/netty/handler/codec/mqtt/MqttConnectReturnCodeBench.java b/microbench/src/main/java/io/netty/handler/codec/mqtt/MqttConnectReturnCodeBench.java deleted file mode 100644 index 7759cf6864..0000000000 --- a/microbench/src/main/java/io/netty/handler/codec/mqtt/MqttConnectReturnCodeBench.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.mqtt; - -import io.netty.microbench.util.AbstractMicrobenchmark; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.Warmup; - -import java.util.concurrent.TimeUnit; - -@BenchmarkMode(Mode.AverageTime) -@Warmup(iterations = 5, time = 5) -@Measurement(iterations = 5, time = 5) -@OutputTimeUnit(TimeUnit.NANOSECONDS) -public class MqttConnectReturnCodeBench extends AbstractMicrobenchmark { - - private static final byte[] DATASET = new byte[] { - -103, -105, -100, -116, 0, 2, -100, 5, -125, 4, -118, -103, -123, 3, 0, -128, -119, 3, -99, 5, 0, -118, - -100, 5, -102, -127, -116, - -103, 4, -128, 0, -122, -101, -116, -128, -112, -124, -127, -118, -116, -107, -126, -119, -100, -121, -103, - -103, -103, 3, - -105, -126, 3, -105, -120, -127, -116, -100, -99, -122, -123, -101, -112, -101, 1, -100, 2, -116, 2, -101, - -97, -100, -124, -123, - -97, -112, -105, -118, -122, -120, 3, -102, -120, -103, 1, -119, -126, 3, -102, -125, -128, -125, -100, - -123, 0, -99, -118, -101, - -118, -112, -126, -128, 4, -121, -101, -124, 1, -118, -127, 1, -119, -120, -127, -100, -112, -124, 2, 5, - -103, -126, -101, -119, - -128, -122, -101, -119, -121, 4, -105, -103, 3, -122, 4, -119, -107, 4, -118, -121, -112, -118, 0, -100, - -116, -124, -107, 3, - -101, -125, -112, -102, -127, -105, -101, 5, -116, 4, -102, 0, -116, -101, -103, -99, -122, 2, -112, -118, - -103, -118, 3, -105, - -120, -99, -103, -116, -124, -124, -123, -128, -116, -101, -124, -122, -101, -122, -116, -118, 5, -97, 1, - -122, -127, -124, 1, 5, - 4, -116, -112, -123, -123, -99, 4, -124, -126, -124, -103, 2, -119, -97, -116, -99, -101, -124, -97, -99, 0, - -126, -105, -120, - -123, -121, -100, -102, 3, -103, 0, -124, -123, -120, 0, -112, -118, -103, 1, -101, 0, -102, -99, 5, -125, - -100, -116, -105, - -122, -122, -99, 0, -121, -102, -102, 3, -121, -124, -116, 1, -122, -121, 2, 0, -101, -101, 5, -112, -101, - -128, 4, -99, - -105, -121, -118, 4, -122, -99, -103, 1, -102, -120, -99, -101, -103, 5, -120, -112, -118, -123, -128, -103, - -120, 4, -100, -101, - -107, -120, -99, -125, -119, -101, 5, 5, -107, -101, 3, -112, -122, -119, -122, -119, 0, -107, -127, 0, - -122, 4, 0, -119, - -97, 5, -119, -124, -102, -126, -107, -120, -100, -125, 4, -121, 1, -107, -100, -103, -105, -121, -123, - -120, -100, 0, 5, -127, - -128, -103, 3, -99, -121, -105, -101, -116, -127, -116, 1, 2, -105, -116, -126, 4, -123, -112, -105, -127, - -128, -123, -119, -121, - 0, -119, 4, -119, -99, -97, 4, -112, -127, -103, -107, -124, -119, -102, -122, -122, -116, 1, -102, -107, - -119, -102, -128, 3, - -107, -99, -101, -126, -128, 4, -128, -103, 2, -127, -101, 4, -102, 1, -128, -97, -99, -112, -128, -107, 4, - -118, -100, -125, - -120, -116, 4, 3, 4, 2, -99, 1, -123, -120, -122, 5, -116, 2, -128, -101, -125, 3, 3, 3, -127, 5, 2, 4, - -122, -118, 0, -128, -121, -125, -97, -127, -124, 2, -127, -97, -97, -112, 0, -128, -105, -116, -118, -126, - -116, -119, -126, -127, - -116, -97, -105, -102, -107, -101, -126, -123, -107, -125, -107, -125, -120, -116, -123, -128, -119, -103, - -121, -126, -127, - -112, -121, 5, -127, 4, -124, -99, 4, -127, -118, -107, -99, 4, -119, -116, -127, 4, 4, -112, -124, -102, - -112, -99, -112, -121, - -128, 5, -101, -121, -116, -125, -112, -121, -107, -116, -123, 1, -105, -97, -101, -107, -100, -107, -125, - -121, -118, -123, - -99, -105, 5, 0, -123, -116, -127, -97, -124, -105, -124, -124, -118, -112, -103, -116, -125, -127, 1, -124, - 5, -103, -128, 3, 2, - -103, -127, -103, -112, 0, 5, -120, -101, 4, 3, 1, 4, 4, -99, 0, -121, -124, -103, 3, -118, -127, 2, 2, 2, - -128, -119, 0, - -122, -112, -124, -101, 3, -102, 3, 3, -118, -99, -120, -125, 2, -100, -120, 0, 3, -120, -127, -122, -122, - -126, -127, -97, - -120, -99, -122, -102, -123, -116, -118, -119, -112, 2, 5, -100, -101, 4, -120, -107, 5, 1, -119, 0, -125, - -105, -101, -100, - -125, 0, -119, -123, 3, -118, 2, -127, -122, -100, 4, -126, 4, -125, -121, -100, 0, -112, -123, -125, 0, - -122, -101, -112, - -119, -118, -127, -124, -128, -127, -102, -125, -103, -103, -126, -116, -107, -125, 0, 3, -123, -100, 3, - -97, 2, -123, -121, -101, - -100, -119, -101, -123, -118, 4, 5, -125, -107, -107, 1, -105, 3, -99, -126, -99, 0, -118, 5, -122, -116, - -105, 2, -124, - -116, -126, 2, -121, -118, -100, 3, 0, -124, 2, -128, -126, -100, -99, -101, 1, -120, 2, -112, -102, -101, - 5, -107, 1, - 4, 2, -124, -100, -123, 4, -122, -118, -107, -103, -121, -101, -128, -112, -127, -105, -118, 2, -125, -121, - -101, 4, -126, -123, - 0, -102, -128, -119, -99, 3, 4, -97, -128, -119, -99, -107, -116, -99, -127, -100, -119, -127, -122, -102, - -119, -118, -119, -103, - -123, -100, 5, -127, -112, 1, -125, -103, 4, 4, -99, 2, -116, -118, -105, 5, -123, -101, -123, -97, 4, -116, - 2, -124, - -116, -125, 1, -118, -118, -124, -120, -118, -120, 4, 5, -118, -97, 0, -127, -100, -121, -97, -97, -125, - -120, -122, -126, -125, - -100, -97, 1, -97, -116, -126, -97, 2, 4, 2, -101, -103, -124, 2, 4, -123, -124, -107, -120, -122, 1, -123, - -97, -112, - -126, 0, -97, 0, -102, -99, -125, -101, -103, 1, -118, 5, -120, -102, -101, -116, -125, -125, -116, -102, - -120, -121, 3, -118, - -118, -123, -128, -126, 4, -101, -100, -103, -100, -105, -126, -121, -118, 4, -126, -123, -120, -99, -105, - -116, -127, -128, - -119, -124, -100, -120, -101, -100, 5, -116, -119, -105, -99, -119, -103, -103, -101, -107, -102, 5, -107, - -99, -102, -122, 2, - -125, -126, 0, -97, -123, 1, 3, 1, 5, -127, 2, -112, -103, -125, -112, -124, -118, -99, 0, 4, 2, -118, 5, - -128, -122, -120, 5, - -121, -112, 2, 5, -102, -125, -116, -127, -128, -102, 0, 2, 5, -122, -126, -120, -127, -101, -102, 5, -100, - -120, -107, -107, - -126, -101, 5, 4, -125, 4, -124, -125, -119, -123, -103, 2, -123, -105, 0, 1, 3, -121, -101, 3, -107, -105, - 1, -105, - -122, -124, 0, -103, -116, 0, -101, -127, -122, -118, -103, 1, -107, -123, 1, -121, -107, 4, -102, -101, 4, - -127, -101, 3, - -121, -103, -125, -124, -127, 5, -128, 1, 3, -119, -126, -119, -125, -112, -124 - }; - - byte[] types; - long next; - long mask; - - @Setup - public void initDataSet() { - types = DATASET; - next = 0; - mask = types.length - 1; - if (Integer.bitCount(types.length) != 1) { - throw new AssertionError("The data set should contains power of 2 items"); - } - } - - @Benchmark - public MqttConnectReturnCode getViaArray() { - long next = this.next; - int nextIndex = (int) (next & mask); - MqttConnectReturnCode code = MqttConnectReturnCode.valueOf(types[nextIndex]); - this.next = next + 1; - return code; - } - - @Benchmark - public MqttConnectReturnCode getViaSwitch() { - long next = this.next; - int nextIndex = (int) (next & mask); - MqttConnectReturnCode code = switchValueOf(types[nextIndex]); - this.next = next + 1; - return code; - } - - public static MqttConnectReturnCode switchValueOf(byte b) { - switch (b) { - case 0: - return MqttConnectReturnCode.CONNECTION_ACCEPTED; - case 1: - return MqttConnectReturnCode.CONNECTION_REFUSED_UNACCEPTABLE_PROTOCOL_VERSION; - case 2: - return MqttConnectReturnCode.CONNECTION_REFUSED_IDENTIFIER_REJECTED; - case 3: - return MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_UNAVAILABLE; - case 4: - return MqttConnectReturnCode.CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD; - case 5: - return MqttConnectReturnCode.CONNECTION_REFUSED_NOT_AUTHORIZED; - case -128: - return MqttConnectReturnCode.CONNECTION_REFUSED_UNSPECIFIED_ERROR; - case -127: - return MqttConnectReturnCode.CONNECTION_REFUSED_MALFORMED_PACKET; - case -126: - return MqttConnectReturnCode.CONNECTION_REFUSED_PROTOCOL_ERROR; - case -125: - return MqttConnectReturnCode.CONNECTION_REFUSED_IMPLEMENTATION_SPECIFIC; - case -124: - return MqttConnectReturnCode.CONNECTION_REFUSED_UNSUPPORTED_PROTOCOL_VERSION; - case -123: - return MqttConnectReturnCode.CONNECTION_REFUSED_CLIENT_IDENTIFIER_NOT_VALID; - case -122: - return MqttConnectReturnCode.CONNECTION_REFUSED_BAD_USERNAME_OR_PASSWORD; - case -121: - return MqttConnectReturnCode.CONNECTION_REFUSED_NOT_AUTHORIZED_5; - case -120: - return MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_UNAVAILABLE_5; - case -119: - return MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_BUSY; - case -118: - return MqttConnectReturnCode.CONNECTION_REFUSED_BANNED; - case -116: - return MqttConnectReturnCode.CONNECTION_REFUSED_BAD_AUTHENTICATION_METHOD; - case -112: - return MqttConnectReturnCode.CONNECTION_REFUSED_TOPIC_NAME_INVALID; - case -107: - return MqttConnectReturnCode.CONNECTION_REFUSED_PACKET_TOO_LARGE; - case -105: - return MqttConnectReturnCode.CONNECTION_REFUSED_QUOTA_EXCEEDED; - case -103: - return MqttConnectReturnCode.CONNECTION_REFUSED_PAYLOAD_FORMAT_INVALID; - case -102: - return MqttConnectReturnCode.CONNECTION_REFUSED_RETAIN_NOT_SUPPORTED; - case -101: - return MqttConnectReturnCode.CONNECTION_REFUSED_QOS_NOT_SUPPORTED; - case -100: - return MqttConnectReturnCode.CONNECTION_REFUSED_USE_ANOTHER_SERVER; - case -99: - return MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_MOVED; - case -97: - return MqttConnectReturnCode.CONNECTION_REFUSED_CONNECTION_RATE_EXCEEDED; - default: - throw new IllegalArgumentException("unknown connect return code: " + (b & 0xFF)); - } - } -} diff --git a/microbench/src/main/java/io/netty/handler/codec/mqtt/MqttMessageTypeValueOfBench.java b/microbench/src/main/java/io/netty/handler/codec/mqtt/MqttMessageTypeValueOfBench.java deleted file mode 100644 index 3486382f83..0000000000 --- a/microbench/src/main/java/io/netty/handler/codec/mqtt/MqttMessageTypeValueOfBench.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.mqtt; - -import io.netty.microbench.util.AbstractMicrobenchmark; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.Warmup; - -import java.util.concurrent.TimeUnit; - -@BenchmarkMode(Mode.AverageTime) -@Warmup(iterations = 5, time = 5) -@Measurement(iterations = 5, time = 5) -@OutputTimeUnit(TimeUnit.NANOSECONDS) -public class MqttMessageTypeValueOfBench extends AbstractMicrobenchmark { - - private static final int[] DATASET = new int[] { - 9, 5, 2, 9, 8, 4, 3, 7, 7, 9, 6, 11, 10, 4, 10, 6, 12, 6, 14, 8, 2, 2, 5, 7, 13, 3, 10, 12, 6, 2, 8, 1, 6, - 4, 10, 8, 13, 9, 9, 2, 7, 12, 2, 3, 12, 9, 3, 12, - 11, 4, 10, 11, 9, 10, 5, 9, 4, 14, 6, 10, 13, 9, 12, 7, 5, 3, 1, 2, 7, 11, 1, 8, 4, 9, 5, 11, 14, 6, 3, 4, - 3, 1, 12, 9, 6, 1, 10, 2, 9, 5, 10, 4, 5, 1, 8, - 2, 11, 9, 7, 10, 14, 9, 12, 14, 6, 13, 6, 14, 6, 1, 3, 1, 10, 13, 13, 2, 2, 8, 7, 10, 9, 9, 4, 7, 13, 4, 10, - 3, 14, 14, 4, 3, 6, 7, 13, 13, 2, 3, 13, 5, 2, - 14, 11, 1, 5, 6, 14, 13, 12, 3, 9, 10, 1, 4, 1, 1, 13, 5, 8, 1, 8, 2, 7, 9, 14, 13, 2, 11, 10, 11, 5, 9, 13, - 13, 12, 11, 6, 1, 7, 11, 1, 11, 7, 8, 1, 13, - 12, 1, 5, 10, 2, 13, 4, 8, 2, 14, 8, 8, 9, 14, 12, 11, 1, 10, 6, 7, 2, 1, 12, 11, 8, 9, 10, 13, 2, 12, 3, 8, - 1, 13, 11, 8, 6, 4, 5, 8, 5, 12, 10, 9, 4, 7, - 2, 1, 11, 6, 7, 11, 5, 1, 5, 2, 7, 7, 14, 14, 3, 2, 1, 8, 5, 7, 4, 13, 13, 7, 8, 2, 14, 1, 12, 7, 8, 8, 3, - 9, 8, 1, 11, 10, 13, 10, 2, 1, 12, 5, 3, 3, 12, 5, - 7, 12, 13, 10, 14, 9, 2, 4, 12, 4, 10, 10, 2, 9, 2, 7, 5, 6, 2, 14, 10, 3, 4, 5, 8, 1, 14, 13, 1, 2, 5, 11, - 8, 6, 8, 3, 8, 13, 12, 8, 2, 12, 6, 2, 5, 4, 13, - 5, 11, 11, 5, 12, 9, 9, 9, 6, 4, 4, 11, 14, 12, 9, 3, 4, 12, 10, 10, 6, 3, 2, 12, 3, 2, 10, 8, 7, 10, 12, - 13, 1, 2, 7, 13, 2, 13, 4, 13, 14, 10, 14, 7, 5, - 11, 10, 9, 9, 1, 9, 10, 3, 9, 1, 13, 7, 9, 7, 1, 8, 14, 2, 6, 11, 2, 2, 11, 4, 10, 10, 9, 4, 4, 13, 7, 2, 1, - 4, 14, 6, 11, 5, 2, 5, 9, 5, 8, 4, 5, 6, 2, 12, 2, - 5, 2, 14, 3, 11, 5, 4, 14, 14, 2, 7, 7, 2, 3, 11, 2, 10, 9, 13, 3, 4, 2, 10, 1, 2, 10, 7, 7, 6, 8, 8, 12, - 14, 8, 13, 1, 9, 5, 9, 1, 14, 2, 5, 5, 5, 3, 13, 11, - 9, 6, 11, 1, 10, 13, 4, 7, 9, 6, 3, 4, 11, 8, 13, 3, 13, 12, 7, 7, 5, 9, 11, 3, 9, 6, 5, 6, 6, 11, 9, 2, 7, - 1, 12, 7, 5, 8, 11, 4, 9, 10, 11, 12, 7, 8, 1, 2, - 3, 14, 3, 9, 11, 9, 7, 4, 4, 4, 8, 4, 4, 2, 5, 8, 2, 11, 7, 13, 2, 14, 3, 6, 7, 14, 12, 6, 9, 11, 10, 9, 6, - 10, 6, 14, 4, 1, 7, 12, 4, 13, 10, 2, 2, 3, 3, 14, - 14, 2, 9, 12, 3, 9, 7, 6, 12, 8, 9, 5, 11, 13, 14, 14, 4, 1, 11, 14, 5, 9, 7, 14, 7, 13, 7, 14, 3, 14, 2, 8, - 2, 5, 10, 12, 14, 9, 11, 3, 14, 8, 12, 12, 5, 2, - 6, 2, 1, 14, 12, 8, 14, 1, 11, 14, 8, 9, 9, 1, 12, 13, 7, 8, 10, 5, 8, 5, 14, 13, 14, 3, 14, 2, 9, 12, 3, - 10, 3, 2, 4, 3, 5, 5, 10, 10, 13, 10, 7, 6, 4, 2, 10, - 8, 14, 2, 7, 1, 2, 7, 13, 2, 3, 6, 14, 3, 8, 12, 3, 4, 12, 6, 3, 10, 6, 14, 9, 1, 6, 3, 14, 7, 1, 7, 2, 12, - 9, 5, 9, 6, 13, 5, 11, 13, 11, 10, 1, 14, 9, 13, 8, - 12, 14, 14, 8, 13, 2, 6, 14, 2, 2, 9, 12, 9, 7, 2, 11, 4, 6, 8, 10, 12, 10, 11, 2, 9, 9, 5, 4, 3, 4, 4, 10, - 3, 1, 12, 13, 9, 8, 1, 9, 9, 4, 2, 7, 3, 4, 11, 11, - 8, 10, 14, 5, 14, 1, 10, 10, 13, 5, 6, 13, 14, 5, 7, 11, 4, 13, 3, 14, 7, 2, 10, 13, 2, 4, 14, 5, 1, 12, 3, - 13, 11, 2, 11, 14, 2, 5, 8, 13, 4, 13, 13, 3, 3, - 3, 13, 6, 11, 5, 3, 2, 13, 9, 2, 10, 8, 3, 11, 4, 6, 12, 14, 6, 2, 14, 1, 2, 6, 8, 4, 12, 8, 11, 9, 1, 7, 1, - 10, 4, 10, 9, 9, 3, 11, 5, 10, 8, 9, 4, 13, 4, - 5, 7, 12, 14, 12, 6, 1, 2, 10, 9, 10, 12, 1, 2, 6, 9, 5, 13, 4, 6, 11, 7, 1, 3, 10, 2, 1, 13, 14, 3, 5, 5, - 5, 7, 14, 9, 9, 3, 12, 1, 1, 1, 3, 12, 6, 9, 7, 8, 1, - 8, 2, 8, 13, 1, 11, 11, 1, 4, 10, 4, 3, 10, 3, 2, 2, 8, 2, 4, 13, 14, 4, 12, 14, 7, 6, 7, 13, 7, 11, 13, 12, - 14, 1, 14, 3, 4, 13, 12, 10, 5, 12, 12, 4, 5, 6, - 9, 12, 13, 3, 4, 13, 8, 14, 3, 2, 8, 5, 6, 13, 8, 7, 4, 5, 8, 14, 8, 14, 7, 5, 4, 9, 12, 12, 10, 3, 1, 12, - 5, 1, 11, 6, 10, 5, 14, 4, 5, 13, 8, 11, 13, 4, 9, - 9, 7, 6, 2, 2, 5, 12, 13, 13, 6, 11, 13, 12, 10, 6, 7, 1, 2, 6, 1, 9, 10, 14, 7, 9, 2, 2, 2, 8, 8, 11, 14, - 12, 9, 13, 1 - }; - - int[] types; - long next; - long mask; - - @Setup - public void initDataSet() { - types = DATASET; - next = 0; - mask = types.length - 1; - if (Integer.bitCount(types.length) != 1) { - throw new AssertionError("The data set should contains power of 2 items"); - } - } - - @Benchmark - public MqttMessageType getViaArray() { - long next = this.next; - int nextIndex = (int) (next & mask); - MqttMessageType type = MqttMessageType.valueOf(types[nextIndex]); - this.next = next + 1; - return type; - } - - @Benchmark - public MqttMessageType getViaSwitch() { - long next = this.next; - int nextIndex = (int) (next & mask); - MqttMessageType type = switchValueOf(types[nextIndex]); - this.next = next + 1; - return type; - } - - private static MqttMessageType switchValueOf(int type) { - switch (type) { - case 1: - return MqttMessageType.CONNECT; - case 2: - return MqttMessageType.CONNACK; - case 3: - return MqttMessageType.PUBLISH; - case 4: - return MqttMessageType.PUBACK; - case 5: - return MqttMessageType.PUBREC; - case 6: - return MqttMessageType.PUBREL; - case 7: - return MqttMessageType.PUBCOMP; - case 8: - return MqttMessageType.SUBSCRIBE; - case 9: - return MqttMessageType.SUBACK; - case 10: - return MqttMessageType.UNSUBSCRIBE; - case 11: - return MqttMessageType.UNSUBACK; - case 12: - return MqttMessageType.PINGREQ; - case 13: - return MqttMessageType.PINGRESP; - case 14: - return MqttMessageType.DISCONNECT; - default: - throw new IllegalArgumentException("unknown message type: " + type); - } - } -} diff --git a/microbench/src/main/java/io/netty/handler/codec/mqtt/MqttPropertyTypeValueOfBench.java b/microbench/src/main/java/io/netty/handler/codec/mqtt/MqttPropertyTypeValueOfBench.java deleted file mode 100644 index 1dc301e8fa..0000000000 --- a/microbench/src/main/java/io/netty/handler/codec/mqtt/MqttPropertyTypeValueOfBench.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.mqtt; - -import io.netty.handler.codec.mqtt.MqttProperties.MqttPropertyType; -import io.netty.microbench.util.AbstractMicrobenchmark; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.Warmup; - -import java.util.concurrent.TimeUnit; - -@BenchmarkMode(Mode.AverageTime) -@Warmup(iterations = 5, time = 5) -@Measurement(iterations = 5, time = 5) -@OutputTimeUnit(TimeUnit.NANOSECONDS) -public class MqttPropertyTypeValueOfBench extends AbstractMicrobenchmark { - - private static final int[] DATASET = new int[] { - 28, 28, 40, 28, 36, 22, 23, 22, 11, 40, 18, 33, 8, 37, 23, 23, 33, 2, 2, 33, 18, - 31, 39, 41, 39, 25, 2, 28, 26, 38, 42, 9, 40, 3, 2, 19, 1, 8, 18, 38, 28, 9, 18, - 22, 42, 22, 37, 42, 23, 38, 22, 40, 2, 8, 40, 26, 1, 8, 17, 2, 37, 21, 41, 8, 37, 26, 1, 17, - 39, 40, 41, 41, 9, 28, 38, 38, 36, 23, 19, 9, 9, 11, 25, 11, 9, 35, 31, 25, 3, 39, - 17, 2, 35, 33, 21, 26, 34, 1, 34, 22, 33, 19, 17, 24, 9, 17, 40, 40, 11, 3, 19, 34, - 42, 42, 11, 22, 39, 1, 25, 37, 41, 21, 42, 39, 1, 22, 21, 8, 28, 21, 17, 3, 39, 24, - 33, 25, 9, 19, 42, 11, 42, 19, 2, 19, 35, 1, 11, 35, 42, 39, 9, 36, 36, 1, 23, 26, - 41, 2, 41, 38, 9, 37, 9, 33, 2, 26, 18, 39, 21, 21, 8, 24, 17, 40, 1, 35, 36, 37, - 21, 25, 1, 2, 24, 37, 3, 9, 42, 24, 28, 37, 24, 3, 35, 31, 11, 17, 42, 21, 23, 11, - 41, 22, 38, 23, 11, 42, 38, 39, 34, 28, 21, 24, 9, 2, 35, 36, 21, 9, 21, 1, 8, 18, - 3, 17, 24, 28, 40, 36, 21, 21, 3, 41, 41, 41, 22, 24, 19, 38, 39, 26, 3, 2, 41, 11, - 25, 34, 42, 38, 31, 21, 23, 34, 8, 36, 19, 19, 42, 39, 3, 42, 35, 11, 33, 9, 17, 21, - 18, 9, 42, 38, 24, 17, 34, 24, 3, 21, 23, 40, 25, 33, 11, 19, 31, 40, 24, 25, 23, 36, - 23, 17, 39, 17, 22, 34, 28, 18, 25, 42, 31, 24, 19, 40, 21, 38, 22, 42, 35, 37, 41, 1, - 17, 33, 3, 21, 42, 33, 2, 36, 36, 35, 42, 24, 37, 41, 8, 22, 36, 26, 42, 11, 41, 26, - 24, 34, 11, 9, 34, 19, 23, 41, 22, 3, 35, 24, 1, 36, 24, 3, 18, 33, 2, 42, 42, 18, - 19, 41, 38, 21, 34, 19, 40, 38, 19, 39, 21, 39, 42, 3, 36, 18, 22, 3, 25, 22, 28, 31, - 31, 23, 24, 19, 34, 26, 33, 34, 42, 18, 3, 42, 19, 24, 21, 31, 8, 42, 25, 24, 39, 35, - 3, 42, 31, 3, 18, 19, 24, 28, 3, 25, 39, 40, 40, 34, 33, 1, 41, 21, 17, 34, 31, 34, - 34, 8, 17, 17, 19, 21, 21, 9, 21, 39, 24, 1, 23, 8, 37, 37, 23, 21, 34, 42, 23, 18, - 42, 9, 34, 23, 24, 22, 11, 18, 18, 35, 24, 42, 23, 1, 31, 2, 9, 11, 24, 22, 34, 28, - 11, 23, 26, 25, 31, 19, 39, 11, 40, 24, 41, 2, 11, 23, 33, 42, 34, 9, 17, 28, 33, 28, - 2, 2, 21, 41, 42, 33, 33, 2, 8, 28, 19, 24, 36, 21, 36, 1, 19, 8, 1, 23, 21, 3, - 40, 28, 38, 22, 21, 19, 37, 2, 23, 8, 33, 8, 31, 25, 17, 40, 36, 22, 3, 41, 21, 22, - 41, 23, 3, 33, 26, 11, 33, 1, 9, 33, 40, 24, 11, 34, 8, 34, 19, 21, 34, 41, 19, 34, - 42, 26, 41, 37, 28, 24, 42, 11, 38, 35, 33, 2, 26, 21, 9, 25, 9, 18, 33, 24, 19, 2, - 11, 40, 37, 36, 19, 28, 40, 26, 41, 35, 21, 23, 28, 22, 19, 34, 3, 31, 36, 38, 25, 34, - 31, 40, 38, 3, 22, 9, 8, 40, 26, 9, 17, 11, 11, 31, 19, 3, 24, 23, 3, 2, 19, 9, - 28, 19, 28, 37, 18, 42, 38, 26, 37, 26, 39, 3, 33, 28, 17, 11, 25, 38, 34, 22, 34, 17, - 3, 1, 34, 38, 8, 2, 37, 25, 9, 11, 36, 23, 19, 8, 35, 24, 11, 11, 11, 25, 11, 11, - 11, 28, 17, 42, 19, 41, 40, 34, 38, 24, 28, 23, 39, 28, 41, 40, 3, 39, 34, 11, 25, 33, - 2, 1, 3, 26, 28, 2, 17, 18, 2, 41, 42, 37, 36, 33, 38, 33, 18, 3, 34, 37, 21, 37, - 23, 35, 21, 3, 9, 21, 34, 38, 22, 37, 28, 38, 8, 2, 31, 1, 38, 25, 40, 35, 37, 41, - 36, 31, 23, 21, 37, 3, 24, 17, 17, 8, 22, 8, 2, 23, 1, 17, 31, 38, 9, 23, 42, 41, - 2, 33, 11, 23, 33, 38, 17, 25, 1, 33, 37, 19, 8, 23, 41, 26, 39, 18, 18, 31, 17, 18, - 34, 3, 41, 34, 40, 9, 23, 33, 11, 40, 39, 34, 19, 40, 3, 2, 19, 17, 23, 33, 2, 19, - 26, 25, 36, 37, 34, 17, 39, 42, 22, 22, 19, 35, 22, 18, 18, 41, 40, 40, 26, 3, 19, 40, - 9, 1, 19, 41, 24, 9, 18, 1, 28, 31, 18, 3, 21, 11, 24, 3, 22, 11, 11, 37, 22, 8, - 2, 38, 3, 2, 37, 28, 11, 35, 18, 36, 9, 35, 21, 19, 42, 35, 24, 2, 2, 17, 18, 33, - 33, 34, 8, 37, 24, 42, 17, 37, 21, 1, 36, 38, 25, 40, 1, 22, 26, 28, 22, 33, 28, 1, - 33, 33, 33, 19, 40, 2, 36, 38, 33, 41, 2, 3, 31, 22, 1, 24, 18, 36, 28, 39, 28, 3, - 8, 35, 17, 18, 18, 8, 18, 22, 2, 25, 18, 41, 37, 21, 17, 28, 34, 1, 35, 25, 22, 38, - 17, 28, 19, 25, 35, 36, 39, 9, 21, 36, 39, 41, 22, 38, 39, 19, 34, 22, 40, 8, 1, 11, - 1, 31, 1, 17, 24, 23, 28, 21, 8, 37, 42, 33, 17, 24, 19, 18, 2, 42, 39, 36, 19, 2, - 34, 35, 36, 11, 9, 35, 2, 21, 3, 42, 28, 37, 24, 1, 38, 2, 11, 41, 33, 39, 25, 17, - 26, 39, 36, 37, 11, 25, 42, 17, 8, 31, 41, 21, 22, 2, 2, 24, 19, 21, 31, 34, 2, 39, - 39, 18, 1, 33, 28, 11, 34, 40, 17, 42, - }; - - int[] types; - long next; - long mask; - - @Setup - public void initDataSet() { - types = DATASET; - next = 0; - mask = types.length - 1; - if (Integer.bitCount(types.length) != 1) { - throw new AssertionError("The data set should contains power of 2 items"); - } - } - - @Benchmark - public MqttPropertyType getViaArray() { - long next = this.next; - int nextIndex = (int) (next & mask); - MqttPropertyType type = MqttPropertyType.valueOf(types[nextIndex]); - this.next = next + 1; - return type; - } - - @Benchmark - public MqttPropertyType getViaSwitch() { - long next = this.next; - int nextIndex = (int) (next & mask); - MqttPropertyType type = switchValueOf(types[nextIndex]); - this.next = next + 1; - return type; - } - - private static MqttPropertyType switchValueOf(int type) { - switch (type) { - case 1: - return MqttPropertyType.PAYLOAD_FORMAT_INDICATOR; - case 2: - return MqttPropertyType.PUBLICATION_EXPIRY_INTERVAL; - case 3: - return MqttPropertyType.CONTENT_TYPE; - case 8: - return MqttPropertyType.RESPONSE_TOPIC; - case 9: - return MqttPropertyType.CORRELATION_DATA; - case 11: - return MqttPropertyType.SUBSCRIPTION_IDENTIFIER; - case 17: - return MqttPropertyType.SESSION_EXPIRY_INTERVAL; - case 18: - return MqttPropertyType.ASSIGNED_CLIENT_IDENTIFIER; - case 19: - return MqttPropertyType.SERVER_KEEP_ALIVE; - case 21: - return MqttPropertyType.AUTHENTICATION_METHOD; - case 22: - return MqttPropertyType.AUTHENTICATION_DATA; - case 23: - return MqttPropertyType.REQUEST_PROBLEM_INFORMATION; - case 24: - return MqttPropertyType.WILL_DELAY_INTERVAL; - case 25: - return MqttPropertyType.REQUEST_RESPONSE_INFORMATION; - case 26: - return MqttPropertyType.RESPONSE_INFORMATION; - case 28: - return MqttPropertyType.SERVER_REFERENCE; - case 31: - return MqttPropertyType.REASON_STRING; - case 33: - return MqttPropertyType.RECEIVE_MAXIMUM; - case 34: - return MqttPropertyType.TOPIC_ALIAS_MAXIMUM; - case 35: - return MqttPropertyType.TOPIC_ALIAS; - case 36: - return MqttPropertyType.MAXIMUM_QOS; - case 37: - return MqttPropertyType.RETAIN_AVAILABLE; - case 38: - return MqttPropertyType.USER_PROPERTY; - case 39: - return MqttPropertyType.MAXIMUM_PACKET_SIZE; - case 40: - return MqttPropertyType.WILDCARD_SUBSCRIPTION_AVAILABLE; - case 41: - return MqttPropertyType.SUBSCRIPTION_IDENTIFIER_AVAILABLE; - case 42: - return MqttPropertyType.SHARED_SUBSCRIPTION_AVAILABLE; - default: - throw new IllegalArgumentException("unknown message type: " + type); - } - } -} diff --git a/microbench/src/main/java/io/netty/handler/codec/mqtt/MqttQoSValueOfBench.java b/microbench/src/main/java/io/netty/handler/codec/mqtt/MqttQoSValueOfBench.java deleted file mode 100644 index 8392030efa..0000000000 --- a/microbench/src/main/java/io/netty/handler/codec/mqtt/MqttQoSValueOfBench.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.mqtt; - -import io.netty.microbench.util.AbstractMicrobenchmark; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.Warmup; - -import java.util.concurrent.TimeUnit; - -@BenchmarkMode(Mode.AverageTime) -@Warmup(iterations = 5, time = 5) -@Measurement(iterations = 5, time = 5) -@OutputTimeUnit(TimeUnit.NANOSECONDS) -public class MqttQoSValueOfBench extends AbstractMicrobenchmark { - - private static final int[] DATASET = new int[] { - 2, 0, 2, 2, 2, 0, 1, 2, 0, 1, 0, 1, 128, 1, 1, 2, 0, 2, 2, 1, 2, 1, 2, 0, 1, 2, 1, 0, 2, 1, 1, 2, 1, 128, - 2, 2, 1, 2, 2, 1, 1, 128, 0, 128, 2, 2, 1, 1, 2, 128, 128, 128, 128, 128, 0, 2, 128, 128, 2, 1, 128, 1, 0, - 2, 1, 0, 2, 0, 2, 0, 2, 128, 2, 2, 128, 0, 128, 2, 128, 0, 0, 1, 2, 1, 128, 0, 2, 0, 2, 0, 0, 128, 2, 0, - 0, 2, 2, 0, 2, 128, 0, 2, 1, 1, 0, 2, 2, 128, 1, 128, 0, 0, 1, 128, 0, 2, 128, 0, 2, 2, 128, 2, 128, 128, - 0, 2, 128, 0, 1, 2, 2, 1, 128, 0, 1, 128, 1, 2, 0, 0, 2, 0, 128, 0, 1, 0, 0, 1, 0, 1, 2, 128, 128, 1, 2, - 128, 0, 1, 1, 1, 1, 0, 2, 1, 1, 128, 1, 2, 128, 128, 1, 0, 128, 1, 2, 2, 128, 2, 0, 0, 2, 2, 128, 1, 1, - 0, 0, 2, 0, 2, 2, 2, 1, 2, 0, 128, 0, 1, 128, 0, 2, 128, 1, 2, 1, 1, 1, 1, 1, 1, 1, 128, 1, 128, 1, 2, - 0, 0, 128, 128, 1, 2, 2, 0, 0, 0, 2, 128, 128, 0, 1, 128, 1, 128, 1, 2, 128, 2, 0, 0, 2, 128, 128, 0, 2, - 1, 1, 0, 0, 1, 128, 1, 128, 2, 128, 1, 1, 128, 1, 1, 1, 2, 2, 2, 128, 128, 0, 1, 1, 2, 128, 1, 0, 2, 2, - 2, 2, 1, 1, 0, 128, 1, 128, 2, 1, 1, 0, 128, 0, 2, 128, 128, 2, 0, 128, 128, 1, 128, 0, 1, 128, 0, 128, - 1, 1, 2, 128, 0, 2, 128, 2, 0, 128, 0, 1, 0, 0, 0, 2, 0, 2, 1, 2, 1, 2, 0, 2, 2, 128, 128, 2, 0, 2, 1, - 128, 1, 128, 1, 0, 128, 0, 0, 128, 1, 0, 128, 1, 0, 128, 0, 128, 1, 2, 1, 128, 2, 0, 1, 0, 1, 2, 1, 0, - 1, 2, 2, 2, 1, 0, 0, 128, 1, 2, 128, 0, 2, 128, 0, 1, 2, 128, 2, 1, 1, 2, 2, 0, 0, 2, 1, 1, 128, 0, 2, - 1, 128, 128, 128, 1, 1, 0, 128, 0, 0, 1, 128, 0, 2, 1, 2, 1, 0, 1, 0, 0, 1, 128, 1, 2, 128, 128, 2, 1, 1, - 0, 1, 0, 1, 2, 0, 128, 2, 0, 2, 1, 2, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 128, 0, 1, 2, 0, 0, 2, 2, 2, 0, 0, - 128, 1, 0, 0, 0, 1, 2, 128, 2, 0, 128, 128, 128, 128, 2, 2, 0, 1, 1, 0, 1, 128, 128, 1, 0, 0, 128, 128, - 1, 2, 128, 2, 128, 128, 128, 2, 0, 1, 128, 1, 0, 1, 2, 128, 128, 2, 2, 0, 0, 1, 1, 0, 1, 1, 0, 128, 0, - 0, 0, 128, 0, 0, 0, 128, 2, 1, 0, 1, 1, 0, 2, 1, 1, 0, 2, 1, 2, 1, 2, 0, 0, 2, 128, 128, 0, 0, 2, 128, - 0, 128, 2, 2, 128, 0, 128, 1, 1, 0, 0, 128, 2, 1, 2, 128, 0, 2, 128, 1, 0, 1, 2, 0, 0, 128, 128, 0, 1, - 2, 128, 1, 2, 2, 0, 128, 2, 1, 0, 1, 128, 128, 2, 0, 1, 0, 0, 2, 0, 128, 2, 2, 1, 128, 128, 128, 1, 0, - 128, 2, 128, 1, 128, 1, 1, 2, 0, 128, 0, 1, 0, 128, 0, 2, 2, 0, 0, 2, 128, 128, 2, 0, 128, 1, 128, 128, - 2, 1, 1, 2, 0, 2, 128, 0, 0, 0, 2, 2, 2, 0, 128, 0, 1, 2, 1, 128, 1, 2, 2, 0, 128, 1, 1, 1, 128, 2, 2, - 128, 0, 0, 1, 2, 1, 1, 2, 0, 1, 2, 128, 2, 2, 128, 128, 0, 128, 1, 1, 128, 128, 128, 2, 1, 1, 1, 2, 1, - 1, 0, 1, 128, 0, 2, 2, 0, 1, 2, 128, 128, 128, 2, 128, 128, 128, 2, 2, 2, 0, 128, 2, 128, 1, 0, 128, - 128, 2, 128, 0, 2, 1, 128, 128, 0, 1, 1, 0, 128, 0, 0, 2, 1, 0, 2, 1, 2, 128, 0, 128, 1, 128, 1, 0, 2, - 1, 1, 1, 2, 1, 0, 1, 128, 2, 2, 0, 1, 128, 1, 0, 1, 0, 0, 128, 0, 128, 2, 2, 128, 128, 1, 0, 128, 1, 0, - 2, 1, 128, 128, 0, 0, 0, 0, 128, 2, 2, 1, 128, 1, 0, 1, 128, 0, 128, 128, 1, 128, 0, 2, 2, 2, 0, 2, 0, - 1, 1, 2, 1, 1, 1, 128, 0, 2, 2, 2, 0, 2, 0, 1, 1, 1, 128, 128, 128, 128, 2, 128, 1, 0, 1, 1, 1, 1, 2, 2, - 1, 0, 128, 2, 128, 128, 0, 1, 128, 128, 128, 128, 128, 1, 0, 2, 0, 128, 0, 0, 2, 2, 0, 1, 2, 1, 0, 2, 128, - 0, 2, 2, 2, 0, 0, 1, 128, 2, 1, 128, 128, 1, 128, 0, 1, 128, 1, 1, 1, 2, 0, 128, 1, 128, 2, 2, 0, 2, 0, - 0, 0, 0, 0, 2, 128, 0, 1, 2, 0, 0, 2, 2, 2, 0, 0, 0, 1, 0, 128, 1, 0, 1, 1, 128, 128, 128, 1, 128, 0, 128, - 2, 1, 2, 1, 0, 1, 2, 128, 2, 1, 1, 2, 1, 128, 1, 2, 0, 2, 128, 2, 128, 2, 2, 1, 1, 128, 2, 2, 0, 128, 0, 2, - 1, 128, 128, 128, 0, 1, 2, 2, 2, 0, 0, 128, 1, 2, 2, 128, 128, 128, 1, 128, 0, 2, 2, 1, 2, 2, 2, 0, 0, 2, - 2, 0, 2, 2, 128, 0, 2, 128, 1, 0, 1, 128, 1, 0, 128, 1, 128, 1, 0, 1, 2, 1, 128, 1, 128, 2, 128, 128, 1, - 2, 128, 1, 2, 0, 2 - }; - - int[] types; - long next; - long mask; - - @Setup - public void initDataSet() { - types = DATASET; - next = 0; - mask = types.length - 1; - if (Integer.bitCount(types.length) != 1) { - throw new AssertionError("The data set should contains power of 2 items"); - } - } - - @Benchmark - public MqttQoS getViaArray() { - long next = this.next; - int nextIndex = (int) (next & mask); - MqttQoS mqttQoS = arrayValueOf(types[nextIndex]); - this.next = next + 1; - return mqttQoS; - } - - @Benchmark - public MqttQoS getViaSwitch() { - long next = this.next; - int nextIndex = (int) (next & mask); - MqttQoS mqttQoS = MqttQoS.valueOf(types[nextIndex]); - this.next = next + 1; - return mqttQoS; - } - - private static final MqttQoS[] VALUES; - - static { - VALUES = new MqttQoS[129]; - for (MqttQoS value : MqttQoS.values()) { - VALUES[value.value()] = value; - } - } - - public static MqttQoS arrayValueOf(int value) { - MqttQoS mqttQoS = null; - try { - mqttQoS = VALUES[value]; - return mqttQoS; - } catch (ArrayIndexOutOfBoundsException ignored) { - // nop - } - if (mqttQoS == null) { - throw new IllegalArgumentException("invalid QoS: " + value); - } - return mqttQoS; - } -} diff --git a/microbench/src/main/java/io/netty/handler/codec/mqtt/package-info.java b/microbench/src/main/java/io/netty/handler/codec/mqtt/package-info.java deleted file mode 100644 index 5b2ae79625..0000000000 --- a/microbench/src/main/java/io/netty/handler/codec/mqtt/package-info.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -/** - * Benchmarks for {@link io.netty.handler.codec.mqtt}. - */ -package io.netty.handler.codec.mqtt; diff --git a/microbench/src/main/java/io/netty/handler/codec/package-info.java b/microbench/src/main/java/io/netty/handler/codec/package-info.java deleted file mode 100644 index 70b424f292..0000000000 --- a/microbench/src/main/java/io/netty/handler/codec/package-info.java +++ /dev/null @@ -1,19 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -/** - * Benchmarks for {@link io.netty.handler.codec}. - */ -package io.netty.handler.codec; diff --git a/microbench/src/main/java/io/netty/microbench/buffer/ByteBufAllocatorBenchmark.java b/microbench/src/main/java/io/netty/microbench/buffer/ByteBufAllocatorBenchmark.java deleted file mode 100644 index 364cf610f6..0000000000 --- a/microbench/src/main/java/io/netty/microbench/buffer/ByteBufAllocatorBenchmark.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.buffer; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.PooledByteBufAllocator; -import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty.microbench.util.AbstractMicrobenchmark; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.Param; -import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.State; - -import java.util.Random; - -/** - * This class benchmarks different allocators with different allocation sizes. - */ -@State(Scope.Benchmark) -public class ByteBufAllocatorBenchmark extends AbstractMicrobenchmark { - - private static final ByteBufAllocator unpooledAllocator = new UnpooledByteBufAllocator(true); - private static final ByteBufAllocator pooledAllocator = - new PooledByteBufAllocator(true, 4, 4, 8192, 11, 0, 0, 0, true, 0); // Disable thread-local cache - - private static final int MAX_LIVE_BUFFERS = 8192; - private static final Random rand = new Random(); - private static final ByteBuf[] unpooledHeapBuffers = new ByteBuf[MAX_LIVE_BUFFERS]; - private static final ByteBuf[] unpooledDirectBuffers = new ByteBuf[MAX_LIVE_BUFFERS]; - private static final ByteBuf[] pooledHeapBuffers = new ByteBuf[MAX_LIVE_BUFFERS]; - private static final ByteBuf[] pooledDirectBuffers = new ByteBuf[MAX_LIVE_BUFFERS]; - private static final ByteBuf[] defaultPooledHeapBuffers = new ByteBuf[MAX_LIVE_BUFFERS]; - private static final ByteBuf[] defaultPooledDirectBuffers = new ByteBuf[MAX_LIVE_BUFFERS]; - - @Param({ "00000", "00256", "01024", "04096", "16384", "65536" }) - public int size; - - @Benchmark - public void unpooledHeapAllocAndFree() { - int idx = rand.nextInt(unpooledHeapBuffers.length); - ByteBuf oldBuf = unpooledHeapBuffers[idx]; - if (oldBuf != null) { - oldBuf.release(); - } - unpooledHeapBuffers[idx] = unpooledAllocator.heapBuffer(size); - } - - @Benchmark - public void unpooledDirectAllocAndFree() { - int idx = rand.nextInt(unpooledDirectBuffers.length); - ByteBuf oldBuf = unpooledDirectBuffers[idx]; - if (oldBuf != null) { - oldBuf.release(); - } - unpooledDirectBuffers[idx] = unpooledAllocator.directBuffer(size); - } - - @Benchmark - public void pooledHeapAllocAndFree() { - int idx = rand.nextInt(pooledHeapBuffers.length); - ByteBuf oldBuf = pooledHeapBuffers[idx]; - if (oldBuf != null) { - oldBuf.release(); - } - pooledHeapBuffers[idx] = pooledAllocator.heapBuffer(size); - } - - @Benchmark - public void pooledDirectAllocAndFree() { - int idx = rand.nextInt(pooledDirectBuffers.length); - ByteBuf oldBuf = pooledDirectBuffers[idx]; - if (oldBuf != null) { - oldBuf.release(); - } - pooledDirectBuffers[idx] = pooledAllocator.directBuffer(size); - } - - @Benchmark - public void defaultPooledHeapAllocAndFree() { - int idx = rand.nextInt(defaultPooledHeapBuffers.length); - ByteBuf oldBuf = defaultPooledHeapBuffers[idx]; - if (oldBuf != null) { - oldBuf.release(); - } - defaultPooledHeapBuffers[idx] = PooledByteBufAllocator.DEFAULT.heapBuffer(size); - } - - @Benchmark - public void defaultPooledDirectAllocAndFree() { - int idx = rand.nextInt(defaultPooledDirectBuffers.length); - ByteBuf oldBuf = defaultPooledDirectBuffers[idx]; - if (oldBuf != null) { - oldBuf.release(); - } - defaultPooledDirectBuffers[idx] = PooledByteBufAllocator.DEFAULT.directBuffer(size); - } -} diff --git a/microbench/src/main/java/io/netty/microbench/buffer/ByteBufAllocatorConcurrentBenchmark.java b/microbench/src/main/java/io/netty/microbench/buffer/ByteBufAllocatorConcurrentBenchmark.java deleted file mode 100644 index 09beff3d6a..0000000000 --- a/microbench/src/main/java/io/netty/microbench/buffer/ByteBufAllocatorConcurrentBenchmark.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.buffer; - -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.UnpooledByteBufAllocator; -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.Scope; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.Threads; -import org.openjdk.jmh.annotations.Warmup; - -@State(Scope.Benchmark) -@Warmup(iterations = 5) -@Measurement(iterations = 10) -@Threads(8) -public class ByteBufAllocatorConcurrentBenchmark extends AbstractMicrobenchmark { - - private static final ByteBufAllocator unpooledAllocator = new UnpooledByteBufAllocator(true, true); - - @Param({ "00064", "00256", "01024", "04096" }) - public int size; - - @Benchmark - public boolean allocateRelease() { - return unpooledAllocator.directBuffer(size).release(); - } -} diff --git a/microbench/src/main/java/io/netty/microbench/buffer/ByteBufBenchmark.java b/microbench/src/main/java/io/netty/microbench/buffer/ByteBufBenchmark.java deleted file mode 100644 index 86b7d8c79e..0000000000 --- a/microbench/src/main/java/io/netty/microbench/buffer/ByteBufBenchmark.java +++ /dev/null @@ -1,85 +0,0 @@ -/* -* Copyright 2015 The Netty Project -* -* The Netty Project licenses this file to you under the Apache License, -* version 2.0 (the "License"); you may not use this file except in compliance -* with the License. You may obtain a copy of the License at: -* -* https://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -* License for the specific language governing permissions and limitations -* under the License. -*/ -package io.netty.microbench.buffer; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.PooledByteBufAllocator; -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; - -import java.nio.ByteBuffer; - -public class ByteBufBenchmark extends AbstractMicrobenchmark { - static { - 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; - private ByteBuf directBuffer; - private ByteBuf directBufferPooled; - - @Setup - public void setup() { - System.setProperty("io.netty.buffer.checkBounds", checkBounds); - byteBuffer = ByteBuffer.allocate(8); - directByteBuffer = ByteBuffer.allocateDirect(8); - buffer = Unpooled.buffer(8); - directBuffer = Unpooled.directBuffer(8); - directBufferPooled = PooledByteBufAllocator.DEFAULT.directBuffer(8); - } - - @TearDown - public void tearDown() { - buffer.release(); - directBuffer.release(); - directBufferPooled.release(); - } - - @Benchmark - public ByteBuffer setByteBufferHeap() { - return byteBuffer.put(0, BYTE); - } - - @Benchmark - public ByteBuffer setByteBufferDirect() { - return directByteBuffer.put(0, BYTE); - } - - @Benchmark - public ByteBuf setByteBufHeap() { - return buffer.setByte(0, BYTE); - } - - @Benchmark - public ByteBuf setByteBufDirect() { - return directBuffer.setByte(0, BYTE); - } - - @Benchmark - public ByteBuf setByteBufDirectPooled() { - return directBufferPooled.setByte(0, BYTE); - } -} diff --git a/microbench/src/main/java/io/netty/microbench/buffer/ByteBufCopyBenchmark.java b/microbench/src/main/java/io/netty/microbench/buffer/ByteBufCopyBenchmark.java deleted file mode 100644 index 81a7426c81..0000000000 --- a/microbench/src/main/java/io/netty/microbench/buffer/ByteBufCopyBenchmark.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.buffer; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.PooledByteBufAllocator; -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; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -public class ByteBufCopyBenchmark extends AbstractMicrobenchmark { - static { - System.setProperty("io.netty.buffer.checkAccessible", "false"); - } - - @Param({"7", "36", "128", "512" }) - private int size; - @Param({"true", "false" }) - private boolean directByteBuff; - @Param({"true", "false" }) - private boolean directByteBuffer; - @Param({"false", "true" }) - private boolean readonlyByteBuffer; - @Param({"true", "false" }) - private boolean pooledByteBuf; - @Param({"true", "false" }) - private boolean alignedCopyByteBuffer; - @Param({"true", "false" }) - private boolean alignedCopyByteBuf; - @Param({"true", "false" }) - private boolean nativeOrderByteBuffer; - - private ByteBuffer byteBuffer; - private ByteBuf buffer; - private int index; - - @Setup - public void setup() { - final int requiredByteBufSize = alignedCopyByteBuf ? size : size + 1; - final int requiredByteBufferSize = alignedCopyByteBuffer ? size : size + 1; - byteBuffer = directByteBuffer ? - ByteBuffer.allocateDirect(requiredByteBufferSize) : - ByteBuffer.allocate(requiredByteBufferSize); - if (pooledByteBuf) { - buffer = directByteBuff ? - PooledByteBufAllocator.DEFAULT.directBuffer(requiredByteBufSize, requiredByteBufSize) : - PooledByteBufAllocator.DEFAULT.heapBuffer(requiredByteBufSize, requiredByteBufSize); - } else { - buffer = directByteBuff ? - Unpooled.directBuffer(requiredByteBufSize, requiredByteBufSize) : - Unpooled.buffer(requiredByteBufSize, requiredByteBufSize); - } - if (!alignedCopyByteBuffer) { - byteBuffer.position(1); - byteBuffer = byteBuffer.slice(); - } - if (readonlyByteBuffer) { - byteBuffer = byteBuffer.asReadOnlyBuffer(); - } - final ByteOrder byteBufferOrder; - if (!nativeOrderByteBuffer) { - byteBufferOrder = ByteOrder.LITTLE_ENDIAN == ByteOrder.nativeOrder() ? - ByteOrder.BIG_ENDIAN : - ByteOrder.LITTLE_ENDIAN; - } else { - byteBufferOrder = ByteOrder.nativeOrder(); - } - byteBuffer.order(byteBufferOrder); - index = alignedCopyByteBuf ? 0 : 1; - } - - @Benchmark - public ByteBuf setBytes() { - byteBuffer.clear(); - return buffer.setBytes(index, byteBuffer); - } - - @TearDown - public void tearDown() { - buffer.release(); - } - -} diff --git a/microbench/src/main/java/io/netty/microbench/buffer/ByteBufIndexOfBenchmark.java b/microbench/src/main/java/io/netty/microbench/buffer/ByteBufIndexOfBenchmark.java deleted file mode 100644 index dcb6362f69..0000000000 --- a/microbench/src/main/java/io/netty/microbench/buffer/ByteBufIndexOfBenchmark.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.buffer; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.PooledByteBufAllocator; -import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty.microbench.util.AbstractMicrobenchmark; -import io.netty.util.internal.SuppressJava6Requirement; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.Fork; -import org.openjdk.jmh.annotations.Level; -import org.openjdk.jmh.annotations.Measurement; -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.Warmup; - -import java.util.SplittableRandom; -import java.util.concurrent.TimeUnit; - -@State(Scope.Benchmark) -@OutputTimeUnit(TimeUnit.MICROSECONDS) -@Fork(2) -@Warmup(iterations = 5, time = 1) -@Measurement(iterations = 8, time = 1) -public class ByteBufIndexOfBenchmark extends AbstractMicrobenchmark { - - @Param({ "7", "16", "23", "32" }) - int size; - - @Param({ "4", "11" }) - int logPermutations; - - @Param({ "1" }) - int seed; - - int permutations; - - ByteBuf[] data; - private int i; - - @Param({ "0" }) - private byte needleByte; - - @Param({ "true", "false" }) - private boolean direct; - @Param({ "false", "true" }) - private boolean noUnsafe; - - @Param({ "false", "true" }) - private boolean pooled; - - @Setup(Level.Trial) - @SuppressJava6Requirement(reason = "using SplittableRandom to reliably produce data") - public void init() { - System.setProperty("io.netty.noUnsafe", Boolean.valueOf(noUnsafe).toString()); - SplittableRandom random = new SplittableRandom(seed); - permutations = 1 << logPermutations; - this.data = new ByteBuf[permutations]; - final ByteBufAllocator allocator = pooled? PooledByteBufAllocator.DEFAULT : UnpooledByteBufAllocator.DEFAULT; - for (int i = 0; i < permutations; ++i) { - data[i] = direct? allocator.directBuffer(size, size) : allocator.heapBuffer(size, size); - for (int j = 0; j < size; j++) { - int value = random.nextInt(Byte.MIN_VALUE, Byte.MAX_VALUE + 1); - // turn any found value into something different - if (value == needleByte) { - if (needleByte != 1) { - value = 1; - } else { - value = 0; - } - } - data[i].setByte(j, value); - } - final int foundIndex = random.nextInt(Math.max(0, size - 8), size); - data[i].setByte(foundIndex, needleByte); - } - } - - private ByteBuf getData() { - return data[i++ & (permutations - 1)]; - } - - @Benchmark - public int indexOf() { - return getData().indexOf(0, size, needleByte); - } - - @TearDown - public void releaseBuffers() { - for (ByteBuf buffer : data) { - buffer.release(); - } - } - -} diff --git a/microbench/src/main/java/io/netty/microbench/buffer/ByteBufUtilBenchmark.java b/microbench/src/main/java/io/netty/microbench/buffer/ByteBufUtilBenchmark.java deleted file mode 100644 index 1d4a8c0a58..0000000000 --- a/microbench/src/main/java/io/netty/microbench/buffer/ByteBufUtilBenchmark.java +++ /dev/null @@ -1,192 +0,0 @@ -/* -* Copyright 2014 The Netty Project -* -* The Netty Project licenses this file to you under the Apache License, -* version 2.0 (the "License"); you may not use this file except in compliance -* with the License. You may obtain a copy of the License at: -* -* https://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -* License for the specific language governing permissions and limitations -* under the License. -*/ -package io.netty.microbench.buffer; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.buffer.Unpooled; -import io.netty.microbench.util.AbstractMicrobenchmark; -import io.netty.util.CharsetUtil; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.Measurement; -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.Warmup; - - -@State(Scope.Benchmark) -@Warmup(iterations = 5) -@Measurement(iterations = 10) -public class - ByteBufUtilBenchmark extends AbstractMicrobenchmark { - - @Param({ "true", "false" }) - private boolean direct; - @Param({ "8", "16", "64", "128" }) - private int length; - private ByteBuf buffer; - private ByteBuf wrapped; - private ByteBuf asciiBuffer; - private ByteBuf utf8Buffer; - - private StringBuilder asciiSequence; - private String ascii; - - private StringBuilder utf8Sequence; - private String utf8; - - @Setup - public void setup() { - // Use buffer sizes that will also allow to write UTF-8 without grow the buffer - final int maxBytes = ByteBufUtil.utf8MaxBytes(length); - buffer = direct? Unpooled.directBuffer(maxBytes) : Unpooled.buffer(maxBytes); - wrapped = Unpooled.unreleasableBuffer(direct? Unpooled.directBuffer(maxBytes) : Unpooled.buffer(maxBytes)); - asciiSequence = new StringBuilder(length); - for (int i = 0; i < length; i++) { - asciiSequence.append('a'); - } - ascii = asciiSequence.toString(); - - // Generate some mixed UTF-8 String for benchmark - utf8Sequence = new StringBuilder(length); - char[] chars = "Some UTF-8 like äÄ∏ŒŒ".toCharArray(); - for (int i = 0; i < length; i++) { - utf8Sequence.append(chars[i % chars.length]); - } - utf8 = utf8Sequence.toString(); - asciiSequence = utf8Sequence; - - asciiBuffer = Unpooled.copiedBuffer(ascii, CharsetUtil.US_ASCII); - utf8Buffer = Unpooled.copiedBuffer(utf8, CharsetUtil.UTF_8); - } - - @TearDown - public void tearDown() { - buffer.release(); - wrapped.release(); - asciiBuffer.release(); - utf8Buffer.release(); - } - - @Benchmark - public void writeAsciiStringViaArray() { - buffer.writerIndex(0); - buffer.writeBytes(ascii.getBytes(CharsetUtil.US_ASCII)); - } - - @Benchmark - public void writeAsciiStringViaArrayWrapped() { - wrapped.writerIndex(0); - wrapped.writeBytes(ascii.getBytes(CharsetUtil.US_ASCII)); - } - - @Benchmark - public void writeAsciiString() { - buffer.writerIndex(0); - ByteBufUtil.writeAscii(buffer, ascii); - } - - @Benchmark - public void writeAsciiStringWrapped() { - wrapped.writerIndex(0); - ByteBufUtil.writeAscii(wrapped, ascii); - } - - @Benchmark - public void writeAsciiViaArray() { - buffer.writerIndex(0); - buffer.writeBytes(asciiSequence.toString().getBytes(CharsetUtil.US_ASCII)); - } - - @Benchmark - public void writeAsciiViaArrayWrapped() { - wrapped.writerIndex(0); - wrapped.writeBytes(asciiSequence.toString().getBytes(CharsetUtil.US_ASCII)); - } - - @Benchmark - public void writeAscii() { - buffer.writerIndex(0); - ByteBufUtil.writeAscii(buffer, asciiSequence); - } - - @Benchmark - public void writeAsciiWrapped() { - wrapped.writerIndex(0); - ByteBufUtil.writeAscii(wrapped, asciiSequence); - } - - @Benchmark - public void writeUtf8StringViaArray() { - buffer.writerIndex(0); - buffer.writeBytes(utf8.getBytes(CharsetUtil.UTF_8)); - } - - @Benchmark - public void writeUtf8StringViaArrayWrapped() { - wrapped.writerIndex(0); - wrapped.writeBytes(utf8.getBytes(CharsetUtil.UTF_8)); - } - - @Benchmark - public void writeUtf8String() { - buffer.writerIndex(0); - ByteBufUtil.writeUtf8(buffer, utf8); - } - - @Benchmark - public void writeUtf8StringWrapped() { - wrapped.writerIndex(0); - ByteBufUtil.writeUtf8(wrapped, utf8); - } - - @Benchmark - public void writeUtf8ViaArray() { - buffer.writerIndex(0); - buffer.writeBytes(utf8Sequence.toString().getBytes(CharsetUtil.UTF_8)); - } - - @Benchmark - public void writeUtf8ViaArrayWrapped() { - wrapped.writerIndex(0); - wrapped.writeBytes(utf8Sequence.toString().getBytes(CharsetUtil.UTF_8)); - } - - @Benchmark - public void writeUtf8() { - buffer.writerIndex(0); - ByteBufUtil.writeUtf8(buffer, utf8Sequence); - } - - @Benchmark - public void writeUtf8Wrapped() { - wrapped.writerIndex(0); - ByteBufUtil.writeUtf8(wrapped, utf8Sequence); - } - - @Benchmark - public String decodeStringAscii() { - return asciiBuffer.toString(CharsetUtil.US_ASCII); - } - - @Benchmark - public String decodeStringUtf8() { - return utf8Buffer.toString(CharsetUtil.UTF_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 deleted file mode 100644 index 5b620e26db..0000000000 --- a/microbench/src/main/java/io/netty/microbench/buffer/HeapByteBufBenchmark.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.buffer; - -import io.netty.buffer.ByteBuf; -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; - -import java.lang.reflect.Constructor; - -public class HeapByteBufBenchmark extends AbstractMicrobenchmark { - - @Param({ "true", "false" }) - public String checkBounds; - - private ByteBuf unsafeBuffer; - private ByteBuf buffer; - - private static ByteBuf newBuffer(String classname) throws Exception { - // Using reflection to workaround package-private implementations. - Class clazz = Class.forName(classname); - Constructor constructor = clazz.getDeclaredConstructor(ByteBufAllocator.class, int.class, int.class); - constructor.setAccessible(true); - return (ByteBuf) constructor.newInstance(ByteBufAllocator.DEFAULT, 8, Integer.MAX_VALUE); - } - - @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); - buffer.writeLong(1L); - } - - @TearDown - public void destroy() { - unsafeBuffer.release(); - buffer.release(); - } - - @Benchmark - public byte getByteUnsafe() { - return unsafeBuffer.getByte(0); - } - - @Benchmark - public short getByte() { - return buffer.getByte(0); - } - - @Benchmark - public short getShortUnsafe() { - return unsafeBuffer.getShort(0); - } - - @Benchmark - public short getShort() { - return buffer.getShort(0); - } - - @Benchmark - public int getMediumUnsafe() { - return unsafeBuffer.getMedium(0); - } - - @Benchmark - public int getMedium() { - return buffer.getMedium(0); - } - - @Benchmark - public int getIntUnsafe() { - return unsafeBuffer.getInt(0); - } - - @Benchmark - public int getInt() { - return buffer.getInt(0); - } - - @Benchmark - public long getLongUnsafe() { - return unsafeBuffer.getLong(0); - } - - @Benchmark - public long getLong() { - return buffer.getLong(0); - } -} diff --git a/microbench/src/main/java/io/netty/microbench/buffer/PooledByteBufAllocatorAlignBenchmark.java b/microbench/src/main/java/io/netty/microbench/buffer/PooledByteBufAllocatorAlignBenchmark.java deleted file mode 100644 index 7cc6b081ce..0000000000 --- a/microbench/src/main/java/io/netty/microbench/buffer/PooledByteBufAllocatorAlignBenchmark.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.buffer; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.PooledByteBufAllocator; -import io.netty.microbench.util.AbstractMicrobenchmark; - -import java.util.Random; -import java.util.concurrent.TimeUnit; - -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.Fork; -import org.openjdk.jmh.annotations.Measurement; -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.Warmup; - -@State(Scope.Thread) -@Warmup(iterations = 5, time = 100, timeUnit = TimeUnit.MILLISECONDS) -@Measurement(iterations = 5, time = 100, timeUnit = TimeUnit.MILLISECONDS) -@Fork(5) -@OutputTimeUnit(TimeUnit.MILLISECONDS) -public class PooledByteBufAllocatorAlignBenchmark extends - AbstractMicrobenchmark { - - private static final Random rand = new Random(); - - /** - * Cache line power of 2. - */ - private static final int CACHE_LINE_MAX = 256; - - /** - * PRNG to walk the chunk randomly to avoid streaming reads. - */ - private static final int OFFSET_ADD = CACHE_LINE_MAX * 1337; - - /** - * Block of bytes to write/read. (Corresponds to int type) - */ - private static final int BLOCK = 4; - - @Param({ "0", "64" }) - private int cacheAlign; - - @Param({ "01024", "04096", "16384", "65536", "1048576" }) - private int size; - - private ByteBuf pooledDirectBuffer; - - private byte[] bytes; - - private int sizeMask; - - private int alignOffset; - - @Setup - public void doSetup() { - PooledByteBufAllocator pooledAllocator = new PooledByteBufAllocator(true, 4, 4, 8192, 11, 0, - 0, 0, true, cacheAlign); - pooledDirectBuffer = pooledAllocator.directBuffer(size + 64); - sizeMask = size - 1; - if (cacheAlign == 0) { - long addr = pooledDirectBuffer.memoryAddress(); - // make sure address is miss-aligned - if (addr % 64 == 0) { - alignOffset = 63; - } - int off = 0; - for (int c = 0; c < size; c++) { - off = (off + OFFSET_ADD) & sizeMask; - if ((addr + off + alignOffset) % BLOCK == 0) { - throw new IllegalStateException( - "Misaligned address is not really aligned"); - } - } - } else { - alignOffset = 0; - int off = 0; - long addr = pooledDirectBuffer.memoryAddress(); - for (int c = 0; c < size; c++) { - off = (off + OFFSET_ADD) & sizeMask; - if ((addr + off) % BLOCK != 0) { - throw new IllegalStateException( - "Aligned address is not really aligned"); - } - } - } - bytes = new byte[BLOCK]; - rand.nextBytes(bytes); - } - - @TearDown - public void doTearDown() { - pooledDirectBuffer.release(); - } - - @Benchmark - public void writeRead() { - int off = 0; - int lSize = size; - int lSizeMask = sizeMask; - int lAlignOffset = alignOffset; - for (int i = 0; i < lSize; i++) { - off = (off + OFFSET_ADD) & lSizeMask; - pooledDirectBuffer.setBytes(off + lAlignOffset, bytes); - pooledDirectBuffer.getBytes(off + lAlignOffset, bytes); - } - } - - @Benchmark - public void write() { - int off = 0; - int lSize = size; - int lSizeMask = sizeMask; - int lAlignOffset = alignOffset; - for (int i = 0; i < lSize; i++) { - off = (off + OFFSET_ADD) & lSizeMask; - pooledDirectBuffer.setBytes(off + lAlignOffset, bytes); - } - } - - @Benchmark - public void read() { - int off = 0; - int lSize = size; - int lSizeMask = sizeMask; - int lAlignOffset = alignOffset; - for (int i = 0; i < lSize; i++) { - off = (off + OFFSET_ADD) & lSizeMask; - pooledDirectBuffer.getBytes(off + lAlignOffset, bytes); - } - } -} diff --git a/microbench/src/main/java/io/netty/microbench/buffer/PooledByteBufAllocatorBenchmark.java b/microbench/src/main/java/io/netty/microbench/buffer/PooledByteBufAllocatorBenchmark.java deleted file mode 100644 index 6e51ba6d08..0000000000 --- a/microbench/src/main/java/io/netty/microbench/buffer/PooledByteBufAllocatorBenchmark.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.buffer; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.PooledByteBufAllocator; -import io.netty.microbench.util.AbstractMicrobenchmark; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.infra.Blackhole; - -public class PooledByteBufAllocatorBenchmark extends - AbstractMicrobenchmark { - private ByteBufAllocator allocator; - - @Setup - public void setup() { - allocator = new PooledByteBufAllocator(true); - } - - @Benchmark - @BenchmarkMode(Mode.Throughput) - public void allocateAndFree(Blackhole blackhole) { - ByteBuf buf = allocator.directBuffer(32768); - buf.release(); - blackhole.consume(buf); - } -} diff --git a/microbench/src/main/java/io/netty/microbench/buffer/SimpleByteBufPooledAllocatorBenchmark.java b/microbench/src/main/java/io/netty/microbench/buffer/SimpleByteBufPooledAllocatorBenchmark.java deleted file mode 100644 index 3892902c26..0000000000 --- a/microbench/src/main/java/io/netty/microbench/buffer/SimpleByteBufPooledAllocatorBenchmark.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.buffer; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.PooledByteBufAllocator; -import io.netty.microbench.util.AbstractMicrobenchmark; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.Level; -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.infra.Blackhole; - -import java.util.concurrent.TimeUnit; - -@State(Scope.Benchmark) -@BenchmarkMode(Mode.AverageTime) -@OutputTimeUnit(TimeUnit.NANOSECONDS) -public class SimpleByteBufPooledAllocatorBenchmark extends AbstractMicrobenchmark { - - public SimpleByteBufPooledAllocatorBenchmark() { - super(true, false); - } - - @Param({"123", "1234", "12345", "123456", "1234567"}) - public int size; - - @Param({"0", "5", "10", "100"}) - public long tokens; - - @Param({"false", "true"}) - public boolean useThreadCache; - - public ByteBufAllocator allocator; - - @Setup(Level.Trial) - public void doSetup() { - allocator = new PooledByteBufAllocator( - PooledByteBufAllocator.defaultPreferDirect(), - PooledByteBufAllocator.defaultNumHeapArena(), - PooledByteBufAllocator.defaultNumDirectArena(), - PooledByteBufAllocator.defaultPageSize(), - PooledByteBufAllocator.defaultMaxOrder(), - PooledByteBufAllocator.defaultTinyCacheSize(), - PooledByteBufAllocator.defaultSmallCacheSize(), - PooledByteBufAllocator.defaultNormalCacheSize(), - useThreadCache); - } - - @Benchmark - public boolean getAndRelease() { - ByteBuf buf = allocator.directBuffer(size); - if (tokens > 0) { - Blackhole.consumeCPU(tokens); - } - return buf.release(); - } -} diff --git a/microbench/src/main/java/io/netty/microbench/buffer/SlicedByteBufBenchmark.java b/microbench/src/main/java/io/netty/microbench/buffer/SlicedByteBufBenchmark.java deleted file mode 100644 index a9e2164610..0000000000 --- a/microbench/src/main/java/io/netty/microbench/buffer/SlicedByteBufBenchmark.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.buffer; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.buffer.Unpooled; -import io.netty.microbench.util.AbstractMicrobenchmark; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.Measurement; -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.Warmup; - - -@State(Scope.Benchmark) -@Warmup(iterations = 10) -@Measurement(iterations = 25) -public class SlicedByteBufBenchmark extends AbstractMicrobenchmark { - - private ByteBuf slicedByteBuf; - private ByteBuf slicedAbstractByteBuf; - private String ascii; - - @Setup - public void setup() { - // Use buffer sizes that will also allow to write UTF-8 without grow the buffer - ByteBuf buffer = Unpooled.buffer(512).retain(); - slicedByteBuf = buffer.slice(0, 256); - slicedAbstractByteBuf = buffer.slice(0, 256); - - if (slicedByteBuf.getClass() == slicedAbstractByteBuf.getClass()) { - throw new IllegalStateException(); - } - - StringBuilder asciiSequence = new StringBuilder(128); - for (int i = 0; i < 128; i++) { - asciiSequence.append('a'); - } - ascii = asciiSequence.toString(); - } - - @TearDown - public void tearDown() { - slicedByteBuf.release(); - slicedAbstractByteBuf.release(); - } - - @Benchmark - public void writeAsciiStringSlice() { - slicedByteBuf.writerIndex(0); - ByteBufUtil.writeAscii(slicedByteBuf, ascii); - } - - @Benchmark - public void writeAsciiStringSliceAbstract() { - slicedAbstractByteBuf.writerIndex(0); - ByteBufUtil.writeAscii(slicedAbstractByteBuf, ascii); - } -} diff --git a/microbench/src/main/java/io/netty/microbench/buffer/SwappedByteBufBenchmark.java b/microbench/src/main/java/io/netty/microbench/buffer/SwappedByteBufBenchmark.java deleted file mode 100644 index 5b96bbf385..0000000000 --- a/microbench/src/main/java/io/netty/microbench/buffer/SwappedByteBufBenchmark.java +++ /dev/null @@ -1,80 +0,0 @@ -/* -* Copyright 2014 The Netty Project -* -* The Netty Project licenses this file to you under the Apache License, -* version 2.0 (the "License"); you may not use this file except in compliance -* with the License. You may obtain a copy of the License at: -* -* https://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -* License for the specific language governing permissions and limitations -* under the License. -*/ -package io.netty.microbench.buffer; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.SwappedByteBuf; -import io.netty.buffer.Unpooled; -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.Scope; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.Warmup; - -import java.nio.ByteOrder; - -@State(Scope.Benchmark) -@Warmup(iterations = 10) -@Measurement(iterations = 25) -public class SwappedByteBufBenchmark extends AbstractMicrobenchmark { - private ByteBuf swappedByteBuf; - private ByteBuf unsafeSwappedByteBuf; - - @Setup - public void setup() { - swappedByteBuf = new SwappedByteBuf(Unpooled.directBuffer(8)); - unsafeSwappedByteBuf = Unpooled.directBuffer(8).order(ByteOrder.LITTLE_ENDIAN); - if (unsafeSwappedByteBuf.getClass().equals(SwappedByteBuf.class)) { - throw new IllegalStateException("Should not use " + SwappedByteBuf.class.getSimpleName()); - } - } - - @Param("16384") - public int size; - - @Benchmark - public void swappedByteBufSetInt() { - swappedByteBuf.setLong(0, size); - } - - @Benchmark - public void swappedByteBufSetShort() { - swappedByteBuf.setShort(0, size); - } - - @Benchmark - public void swappedByteBufSetLong() { - swappedByteBuf.setLong(0, size); - } - - @Benchmark - public void unsafeSwappedByteBufSetInt() { - unsafeSwappedByteBuf.setInt(0, size); - } - - @Benchmark - public void unsafeSwappedByteBufSetShort() { - unsafeSwappedByteBuf.setShort(0, size); - } - - @Benchmark - public void unsafeSwappedByteBufSetLong() { - unsafeSwappedByteBuf.setLong(0, size); - } -} diff --git a/microbench/src/main/java/io/netty/microbench/buffer/UnsafeByteBufBenchmark.java b/microbench/src/main/java/io/netty/microbench/buffer/UnsafeByteBufBenchmark.java deleted file mode 100644 index ae5a2611b9..0000000000 --- a/microbench/src/main/java/io/netty/microbench/buffer/UnsafeByteBufBenchmark.java +++ /dev/null @@ -1,64 +0,0 @@ -/* -* 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: -* -* https://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -* License for the specific language governing permissions and limitations -* under the License. -*/ -package io.netty.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); - } -} diff --git a/microbench/src/main/java/io/netty/microbench/buffer/Utf8EncodingBenchmark.java b/microbench/src/main/java/io/netty/microbench/buffer/Utf8EncodingBenchmark.java deleted file mode 100644 index 6a3fc12dd7..0000000000 --- a/microbench/src/main/java/io/netty/microbench/buffer/Utf8EncodingBenchmark.java +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.buffer; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.buffer.Unpooled; -import io.netty.microbench.util.AbstractMicrobenchmark; -import io.netty.util.AsciiString; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.CompilerControl; -import org.openjdk.jmh.annotations.CompilerControl.Mode; -import org.openjdk.jmh.annotations.Fork; -import org.openjdk.jmh.annotations.Param; -import org.openjdk.jmh.annotations.Setup; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.io.UnsupportedEncodingException; -import java.util.ArrayList; -import java.util.List; - -@Fork(value = 2, jvmArgsAppend = "-XX:MaxInlineLevel=9") -public class Utf8EncodingBenchmark extends AbstractMicrobenchmark { - private static class AnotherCharSequence implements CharSequence { - private final char[] chars; - - AnotherCharSequence(String chars) { - this.chars = new char[chars.length()]; - chars.getChars(0, chars.length(), this.chars, 0); - } - - @Override - public int length() { - return chars.length; - } - - @Override - public char charAt(int i) { - return chars[i]; - } - - @Override - public CharSequence subSequence(int start, int end) { - throw new UnsupportedOperationException(); - } - - @Override - public String toString() { - throw new UnsupportedOperationException(); - } - } - - // experiment test input - private String[] strings; - private StringBuilder[] stringBuilders; - private AnotherCharSequence[] anotherCharSequences; - private AsciiString[] asciiStrings; - @Param({ "false", "true" }) - private boolean direct; - private ByteBuf buffer; - @Param({ "false", "true" }) - private boolean noUnsafe; - private int dataSetLength; - - @Setup - public void init() { - System.setProperty("io.netty.noUnsafe", Boolean.valueOf(noUnsafe).toString()); - InputStream testTextStream = null; - InputStreamReader inStreamReader = null; - BufferedReader buffReader = null; - int maxExpectedSize = 0; - List strings = new ArrayList(); - List stringBuilders = new ArrayList(); - List anotherCharSequenceList = new ArrayList(); - List asciiStrings = new ArrayList(); - try { - testTextStream = getClass().getResourceAsStream("/Utf8Samples.txt"); - inStreamReader = new InputStreamReader(testTextStream, "UTF-8"); - buffReader = new BufferedReader(inStreamReader); - String line; - while ((line = buffReader.readLine()) != null) { - strings.add(line); - stringBuilders.add(new StringBuilder(line)); - anotherCharSequenceList.add(new AnotherCharSequence(line)); - asciiStrings.add(new AsciiString(line)); - maxExpectedSize = Math.max(maxExpectedSize, ByteBufUtil.utf8MaxBytes(line.length())); - } - } catch (Exception e) { - throw new RuntimeException(e); - } finally { - closeStream(testTextStream); - closeReader(inStreamReader); - closeReader(buffReader); - } - buffer = direct? Unpooled.directBuffer(maxExpectedSize, maxExpectedSize) : - Unpooled.buffer(maxExpectedSize, maxExpectedSize); - buffer.setByte(maxExpectedSize - 1, 0); - this.strings = strings.toArray(new String[strings.size()]); - this.stringBuilders = stringBuilders.toArray(new StringBuilder[stringBuilders.size()]); - this.anotherCharSequences = - anotherCharSequenceList.toArray(new AnotherCharSequence[anotherCharSequenceList.size()]); - this.asciiStrings = asciiStrings.toArray(new AsciiString[asciiStrings.size()]); - this.dataSetLength = this.strings.length; - } - - private static void closeStream(InputStream inStream) { - if (inStream != null) { - try { - inStream.close(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - } - - private static void closeReader(Reader buffReader) { - if (buffReader != null) { - try { - buffReader.close(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - } - - @Benchmark - @CompilerControl(Mode.DONT_INLINE) - public int nestedByteBufUtilWriteUtf8String() { - int countBytes = 0; - for (String string : strings) { - countBytes += nestedByteBufUtilWriteUtf8String1(string); - } - return countBytes; - } - - private int nestedByteBufUtilWriteUtf8String1(String string) { - return nestedByteBufUtilWriteUtf8String2(string); - } - - private int nestedByteBufUtilWriteUtf8String2(String string) { - return nestedByteBufUtilWriteUtf8String3(string); - } - - private int nestedByteBufUtilWriteUtf8String3(String string) { - return nestedByteBufUtilWriteUtf8String4(string); - } - - private int nestedByteBufUtilWriteUtf8String4(String string) { - return nestedByteBufUtilWriteUtf8String5(string); - } - - private int nestedByteBufUtilWriteUtf8String5(String string) { - return nestedByteBufUtilWriteUtf8String6(string); - } - - private int nestedByteBufUtilWriteUtf8String6(String string) { - // this calls should be inlined but...what happen to the subsequent calls > MaxInlineLevel? - buffer.writerIndex(0); - ByteBufUtil.writeUtf8(buffer, string, 0, string.length()); - return buffer.writerIndex(); - } - - @Benchmark - @CompilerControl(Mode.DONT_INLINE) - public int byteBufUtilWriteUtf8String() { - int countBytes = 0; - for (String string : strings) { - buffer.writerIndex(0); - ByteBufUtil.writeUtf8(buffer, string, 0, string.length()); - countBytes += buffer.writerIndex(); - } - return countBytes; - } - - @Benchmark - @CompilerControl(Mode.DONT_INLINE) - public int byteBufUtilWriteUtf8Bimorphic() { - int countBytes = 0; - for (int i = 0, size = dataSetLength; i < size; i++) { - final StringBuilder stringBuilder = stringBuilders[i]; - final String string = strings[i]; - buffer.writerIndex(0); - ByteBufUtil.writeUtf8(buffer, stringBuilder, 0, stringBuilder.length()); - countBytes += buffer.writerIndex(); - buffer.writerIndex(0); - ByteBufUtil.writeUtf8(buffer, string, 0, string.length()); - countBytes += buffer.writerIndex(); - } - return countBytes; - } - - @Benchmark - @CompilerControl(Mode.DONT_INLINE) - public int byteBufUtilWriteUtf8Megamorphic() { - int countBytes = 0; - for (int i = 0, size = dataSetLength; i < size; i++) { - final StringBuilder stringBuilder = stringBuilders[i]; - final String string = strings[i]; - final AnotherCharSequence anotherCharSequence = anotherCharSequences[i]; - buffer.writerIndex(0); - ByteBufUtil.writeUtf8(buffer, stringBuilder, 0, stringBuilder.length()); - countBytes += buffer.writerIndex(); - buffer.writerIndex(0); - ByteBufUtil.writeUtf8(buffer, string, 0, string.length()); - countBytes += buffer.writerIndex(); - buffer.writerIndex(0); - ByteBufUtil.writeUtf8(buffer, anotherCharSequence, 0, anotherCharSequence.length()); - countBytes += buffer.writerIndex(); - } - return countBytes; - } - - @Benchmark - @CompilerControl(Mode.DONT_INLINE) - public int byteBufUtilWriteUtf8CommonCharSequences() { - int countBytes = 0; - for (int i = 0, size = dataSetLength; i < size; i++) { - final StringBuilder stringBuilder = stringBuilders[i]; - final String string = strings[i]; - final AsciiString asciiString = asciiStrings[i]; - buffer.writerIndex(0); - ByteBufUtil.writeUtf8(buffer, stringBuilder, 0, stringBuilder.length()); - countBytes += buffer.writerIndex(); - buffer.writerIndex(0); - ByteBufUtil.writeUtf8(buffer, string, 0, string.length()); - countBytes += buffer.writerIndex(); - buffer.writerIndex(0); - ByteBufUtil.writeUtf8(buffer, asciiString, 0, asciiString.length()); - countBytes += buffer.writerIndex(); - } - return countBytes; - } - - @Benchmark - @CompilerControl(Mode.DONT_INLINE) - public int byteBufUtilWriteUtf8AsciiString() { - int countBytes = 0; - for (int i = 0, size = dataSetLength; i < size; i++) { - final AsciiString asciiString = asciiStrings[i]; - buffer.writerIndex(0); - ByteBufUtil.writeUtf8(buffer, asciiString, 0, asciiString.length()); - countBytes += buffer.writerIndex(); - } - return countBytes; - } - - @Benchmark - @CompilerControl(Mode.DONT_INLINE) - public int writeGetBytes() throws UnsupportedEncodingException { - int countBytes = 0; - for (String string : strings) { - buffer.writerIndex(0); - final byte[] bytes = string.getBytes("UTF-8"); - buffer.writeBytes(bytes); - countBytes += buffer.writerIndex(); - } - return countBytes; - } - -} diff --git a/microbench/src/main/java/io/netty/microbench/buffer/package-info.java b/microbench/src/main/java/io/netty/microbench/buffer/package-info.java deleted file mode 100644 index 033a79e7e0..0000000000 --- a/microbench/src/main/java/io/netty/microbench/buffer/package-info.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -/** - * Benchmarks for {@link io.netty.buffer}. - */ -package io.netty.microbench.buffer; diff --git a/microbench/src/main/java/io/netty/microbench/channel/DefaultChannelPipelineBenchmark.java b/microbench/src/main/java/io/netty/microbench/channel/DefaultChannelPipelineBenchmark.java deleted file mode 100644 index 5d728d5ad4..0000000000 --- a/microbench/src/main/java/io/netty/microbench/channel/DefaultChannelPipelineBenchmark.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.channel; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.microbench.util.AbstractMicrobenchmark; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.Level; -import org.openjdk.jmh.annotations.Measurement; -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.Warmup; -import org.openjdk.jmh.infra.Blackhole; - -@Warmup(iterations = 5) -@Measurement(iterations = 5) -@State(Scope.Benchmark) -public class DefaultChannelPipelineBenchmark extends AbstractMicrobenchmark { - - @ChannelHandler.Sharable - private static final ChannelHandler NOOP_HANDLER = new ChannelHandler() { }; - - @ChannelHandler.Sharable - private static final ChannelHandler CONSUMING_HANDLER = new ChannelHandler() { - @Override - public void channelReadComplete(ChannelHandlerContext ctx) { - // NOOP - } - }; - - @Param({ "4" }) - public int extraHandlers; - - private ChannelPipeline pipeline; - - @Setup(Level.Iteration) - public void setup() { - pipeline = new EmbeddedChannel().pipeline(); - for (int i = 0; i < extraHandlers; i++) { - pipeline.addLast(NOOP_HANDLER); - } - pipeline.addLast(CONSUMING_HANDLER); - } - - @TearDown - public void tearDown() { - pipeline.channel().close(); - } - - @Benchmark - public void propagateEvent(Blackhole hole) { - for (int i = 0; i < 100; i++) { - hole.consume(pipeline.fireChannelReadComplete()); - } - } -} diff --git a/microbench/src/main/java/io/netty/microbench/channel/EmbeddedChannelHandlerContext.java b/microbench/src/main/java/io/netty/microbench/channel/EmbeddedChannelHandlerContext.java deleted file mode 100644 index 4c8a99f3fc..0000000000 --- a/microbench/src/main/java/io/netty/microbench/channel/EmbeddedChannelHandlerContext.java +++ /dev/null @@ -1,278 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.microbench.channel; - -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.api.BufferAllocator; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoop; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.Attribute; -import io.netty.util.AttributeKey; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; - -import java.net.SocketAddress; - -import static java.util.Objects.requireNonNull; - -public abstract class EmbeddedChannelHandlerContext implements ChannelHandlerContext { - private static final String HANDLER_NAME = "microbench-delegator-ctx"; - private final EventLoop eventLoop; - private final Channel channel; - private final ByteBufAllocator alloc; - private final BufferAllocator bufferAllocator; - private final ChannelHandler handler; - private SocketAddress localAddress; - - protected EmbeddedChannelHandlerContext(ByteBufAllocator alloc, ChannelHandler handler, EmbeddedChannel channel) { - this.alloc = requireNonNull(alloc, "alloc"); - this.bufferAllocator = null; - this.channel = requireNonNull(channel, "channel"); - this.handler = requireNonNull(handler, "handler"); - eventLoop = requireNonNull(channel.executor(), "eventLoop"); - } - - protected EmbeddedChannelHandlerContext(BufferAllocator bufferAllocator, ChannelHandler handler, - EmbeddedChannel channel) { - this.bufferAllocator = requireNonNull(bufferAllocator, "bufferAllocator"); - this.alloc = null; - this.channel = requireNonNull(channel, "channel"); - this.handler = requireNonNull(handler, "handler"); - eventLoop = requireNonNull(channel.executor(), "eventLoop"); - } - - protected abstract void handleException(Throwable t); - - @Override - public final Attribute attr(AttributeKey key) { - return null; - } - - @Override - public final boolean hasAttr(AttributeKey key) { - return false; - } - - @Override - public final Channel channel() { - return channel; - } - - @Override - public final EventExecutor executor() { - return eventLoop; - } - - @Override - public final String name() { - return HANDLER_NAME; - } - - @Override - public final ChannelHandler handler() { - return handler; - } - - @Override - public final boolean isRemoved() { - return false; - } - - @Override - public final ChannelHandlerContext fireChannelRegistered() { - return this; - } - - @Override - public final ChannelHandlerContext fireChannelUnregistered() { - return this; - } - - @Override - public final ChannelHandlerContext fireChannelActive() { - return this; - } - - @Override - public final ChannelHandlerContext fireChannelInactive() { - return this; - } - - @Override - public final ChannelHandlerContext fireExceptionCaught(Throwable cause) { - try { - handler().exceptionCaught(this, cause); - } catch (Exception e) { - handleException(e); - } - return this; - } - - @Override - public final ChannelHandlerContext fireUserEventTriggered(Object event) { - ReferenceCountUtil.release(event); - return this; - } - - @Override - public final ChannelHandlerContext fireChannelRead(Object msg) { - ReferenceCountUtil.release(msg); - return this; - } - - @Override - public final ChannelHandlerContext fireChannelReadComplete() { - return this; - } - - @Override - public final ChannelHandlerContext fireChannelWritabilityChanged() { - return this; - } - - @Override - public final Future register() { - try { - return channel().register(); - } catch (Exception e) { - handleException(e); - return channel().newFailedFuture(e); - } - } - - @Override - public final Future deregister() { - try { - return channel().deregister(); - } catch (Exception e) { - handleException(e); - return channel().newFailedFuture(e); - } - } - - @Override - public final Future bind(SocketAddress localAddress) { - try { - this.localAddress = localAddress; - return channel().bind(localAddress); - } catch (Exception e) { - this.localAddress = null; - handleException(e); - return channel().newFailedFuture(e); - } - } - - @Override - public final Future connect(SocketAddress remoteAddress) { - try { - return channel().connect(remoteAddress, localAddress); - } catch (Exception e) { - handleException(e); - return channel().newFailedFuture(e); - } - } - - @Override - public final Future connect(SocketAddress remoteAddress, SocketAddress localAddress) { - try { - return channel().connect(remoteAddress, localAddress); - } catch (Exception e) { - handleException(e); - return channel().newFailedFuture(e); - } - } - - @Override - public final Future disconnect() { - try { - return channel().disconnect(); - } catch (Exception e) { - handleException(e); - return channel().newFailedFuture(e); - } - } - - @Override - public final Future close() { - try { - return channel().close(); - } catch (Exception e) { - handleException(e); - return channel().newFailedFuture(e); - } - } - - @Override - public final ChannelHandlerContext read() { - try { - channel().read(); - } catch (Exception e) { - handleException(e); - } - return this; - } - - @Override - public Future write(Object msg) { - return channel().write(msg); - } - - @Override - public ChannelHandlerContext flush() { - channel().flush(); - return this; - } - - @Override - public Future writeAndFlush(Object msg) { - return channel().writeAndFlush(msg); - } - - @Override - public final ChannelPipeline pipeline() { - return channel().pipeline(); - } - - @Override - public final ByteBufAllocator alloc() { - return alloc; - } - - @Override - public BufferAllocator bufferAllocator() { - return bufferAllocator; - } - - @Override - public final Promise newPromise() { - return channel().newPromise(); - } - - @Override - public final Future newSucceededFuture() { - return channel().newSucceededFuture(); - } - - @Override - public final Future newFailedFuture(Throwable cause) { - return channel().newFailedFuture(cause); - } -} diff --git a/microbench/src/main/java/io/netty/microbench/channel/EmbeddedChannelWriteAccumulatingHandlerContext.java b/microbench/src/main/java/io/netty/microbench/channel/EmbeddedChannelWriteAccumulatingHandlerContext.java deleted file mode 100644 index e59afd02db..0000000000 --- a/microbench/src/main/java/io/netty/microbench/channel/EmbeddedChannelWriteAccumulatingHandlerContext.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.microbench.channel; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.api.BufferAllocator; -import io.netty.channel.ChannelHandler; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.ByteToMessageDecoder; -import io.netty.util.concurrent.Future; - -import static java.util.Objects.requireNonNull; - -public abstract class EmbeddedChannelWriteAccumulatingHandlerContext extends EmbeddedChannelHandlerContext { - private ByteBuf cumulation; - private final ByteToMessageDecoder.Cumulator cumulator; - - protected EmbeddedChannelWriteAccumulatingHandlerContext(ByteBufAllocator alloc, ChannelHandler handler, - ByteToMessageDecoder.Cumulator writeCumulator) { - this(alloc, handler, writeCumulator, new EmbeddedChannel()); - } - - protected EmbeddedChannelWriteAccumulatingHandlerContext(ByteBufAllocator alloc, ChannelHandler handler, - ByteToMessageDecoder.Cumulator writeCumulator, - EmbeddedChannel channel) { - super(alloc, handler, channel); - cumulator = requireNonNull(writeCumulator, "writeCumulator"); - } - - protected EmbeddedChannelWriteAccumulatingHandlerContext(BufferAllocator alloc, ChannelHandler handler, - ByteToMessageDecoder.Cumulator writeCumulator) { - this(alloc, handler, writeCumulator, new EmbeddedChannel()); - } - - protected EmbeddedChannelWriteAccumulatingHandlerContext(BufferAllocator alloc, ChannelHandler handler, - ByteToMessageDecoder.Cumulator writeCumulator, - EmbeddedChannel channel) { - super(alloc, handler, channel); - cumulator = requireNonNull(writeCumulator, "writeCumulator"); - } - - public final ByteBuf cumulation() { - return cumulation; - } - - public final void releaseCumulation() { - if (cumulation != null) { - cumulation.release(); - cumulation = null; - } - } - - @Override - public final Future write(Object msg) { - try { - if (msg instanceof ByteBuf) { - if (cumulation == null) { - cumulation = (ByteBuf) msg; - } else { - cumulation = cumulator.cumulate(alloc(), cumulation, (ByteBuf) msg); - } - return channel().newSucceededFuture(); - } - return channel().write(msg); - } catch (Exception e) { - handleException(e); - return channel().newFailedFuture(e); - } - } - - @Override - public final Future writeAndFlush(Object msg) { - try { - if (msg instanceof ByteBuf) { - ByteBuf buf = (ByteBuf) msg; - if (cumulation == null) { - cumulation = buf; - } else { - cumulation = cumulator.cumulate(alloc(), cumulation, buf); - } - return channel().newSucceededFuture(); - } else { - return channel().writeAndFlush(msg); - } - } catch (Exception e) { - handleException(e); - return channel().newFailedFuture(e); - } - } -} diff --git a/microbench/src/main/java/io/netty/microbench/channel/EmbeddedChannelWriteReleaseHandlerContext.java b/microbench/src/main/java/io/netty/microbench/channel/EmbeddedChannelWriteReleaseHandlerContext.java deleted file mode 100644 index 2b79bee3f9..0000000000 --- a/microbench/src/main/java/io/netty/microbench/channel/EmbeddedChannelWriteReleaseHandlerContext.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.microbench.channel; - -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.api.BufferAllocator; -import io.netty.channel.ChannelHandler; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.ReferenceCounted; -import io.netty.util.concurrent.Future; - -public abstract class EmbeddedChannelWriteReleaseHandlerContext extends EmbeddedChannelHandlerContext { - protected EmbeddedChannelWriteReleaseHandlerContext(ByteBufAllocator alloc, ChannelHandler handler) { - this(alloc, handler, new EmbeddedChannel()); - } - - protected EmbeddedChannelWriteReleaseHandlerContext(ByteBufAllocator alloc, ChannelHandler handler, - EmbeddedChannel channel) { - super(alloc, handler, channel); - } - protected EmbeddedChannelWriteReleaseHandlerContext(BufferAllocator alloc, ChannelHandler handler) { - this(alloc, handler, new EmbeddedChannel()); - } - - protected EmbeddedChannelWriteReleaseHandlerContext(BufferAllocator alloc, ChannelHandler handler, - EmbeddedChannel channel) { - super(alloc, handler, channel); - } - - @Override - protected abstract void handleException(Throwable t); - - @Override - public final Future write(Object msg) { - try { - if (msg instanceof ReferenceCounted) { - ((ReferenceCounted) msg).release(); - return channel().newSucceededFuture(); - } - return channel().write(msg); - } catch (Exception e) { - handleException(e); - return channel().newFailedFuture(e); - } - } - - @Override - public final Future writeAndFlush(Object msg) { - try { - if (msg instanceof ReferenceCounted) { - ((ReferenceCounted) msg).release(); - return channel().newSucceededFuture(); - } - return channel().writeAndFlush(msg); - } catch (Exception e) { - handleException(e); - return channel().newFailedFuture(e); - } - } -} 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 deleted file mode 100644 index 6d5b9223d9..0000000000 --- a/microbench/src/main/java/io/netty/microbench/channel/epoll/EpollSocketChannelBenchmark.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.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.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.epoll.EpollHandler; -import io.netty.channel.epoll.EpollServerSocketChannel; -import io.netty.channel.epoll.EpollSocketChannel; -import io.netty.microbench.util.AbstractMicrobenchmark; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.GroupThreads; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.TearDown; - -import java.util.concurrent.TimeUnit; - -public class EpollSocketChannelBenchmark extends AbstractMicrobenchmark { - private static final Runnable runnable = () -> { }; - - private EventLoopGroup group; - private Channel serverChan; - private Channel chan; - private ByteBuf abyte; - private Future future; - - @Setup - public void setup() throws Exception { - group = new MultithreadEventLoopGroup(1, EpollHandler.newFactory()); - - // add an arbitrary timeout to make the timer reschedule - future = group.schedule((Runnable) () -> { - 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 ChannelHandler() { - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - if (msg instanceof ByteBuf) { - ctx.writeAndFlush(msg); - } else { - throw new AssertionError(); - } - } - }); - } - }) - .bind(0) - .get(); - chan = new Bootstrap() - .channel(EpollSocketChannel.class) - .handler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) { - ch.pipeline().addLast(new ChannelHandler() { - - private Promise lastWritePromise; - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - if (msg instanceof ByteBuf) { - - ByteBuf buf = (ByteBuf) msg; - try { - if (buf.readableBytes() == 1) { - lastWritePromise.trySuccess(null); - lastWritePromise = null; - } else { - throw new AssertionError(); - } - } finally { - buf.release(); - } - } else { - throw new AssertionError(); - } - } - - @Override - public Future write(ChannelHandlerContext ctx, Object msg) { - if (lastWritePromise != null) { - throw new IllegalStateException(); - } - lastWritePromise = ctx.newPromise(); - return ctx.write(msg); - } - }); - } - }) - .group(group) - .connect(serverChan.localAddress()) - .get(); - - abyte = chan.alloc().directBuffer(1); - abyte.writeByte('a'); - } - - @TearDown - public void tearDown() throws Exception { - chan.close().sync(); - serverChan.close().sync(); - future.cancel(); - group.shutdownGracefully(0, 0, TimeUnit.SECONDS).sync(); - abyte.release(); - } - - @Benchmark - public Object pingPong() throws Exception { - return chan.pipeline().writeAndFlush(abyte.retainedSlice()).sync(); - } - - @Benchmark - public Object executeSingle() throws Exception { - return chan.executor().submit(runnable).get(); - } - - @Benchmark - @GroupThreads(3) - public Object executeMulti() throws Exception { - return chan.executor().submit(runnable).get(); - } -} 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 deleted file mode 100644 index f44b4aceb9..0000000000 --- a/microbench/src/main/java/io/netty/microbench/channel/epoll/package-info.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -/** - * Benchmarks for {@link io.netty.microbench.channel.epoll}. - */ -package io.netty.microbench.channel.epoll; diff --git a/microbench/src/main/java/io/netty/microbench/channel/package-info.java b/microbench/src/main/java/io/netty/microbench/channel/package-info.java deleted file mode 100644 index 81bcf85784..0000000000 --- a/microbench/src/main/java/io/netty/microbench/channel/package-info.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -/** - * Benchmarks for {@link io.netty.channel}. - */ -package io.netty.microbench.channel; diff --git a/microbench/src/main/java/io/netty/microbench/concurrent/BurstCostExecutorsBenchmark.java b/microbench/src/main/java/io/netty/microbench/concurrent/BurstCostExecutorsBenchmark.java deleted file mode 100644 index 0bbfdf5b73..0000000000 --- a/microbench/src/main/java/io/netty/microbench/concurrent/BurstCostExecutorsBenchmark.java +++ /dev/null @@ -1,345 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.microbench.concurrent; - -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.epoll.Epoll; -import io.netty.channel.epoll.EpollHandler; -import io.netty.channel.kqueue.KQueue; -import io.netty.channel.kqueue.KQueueHandler; -import io.netty.channel.nio.NioHandler; -import io.netty.microbench.util.AbstractMicrobenchmark; -import io.netty.util.concurrent.DefaultThreadFactory; -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.EventExecutorGroup; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.ImmediateEventExecutor; -import io.netty.util.concurrent.Promise; -import io.netty.util.concurrent.RejectedExecutionHandlers; -import io.netty.util.concurrent.SingleThreadEventExecutor; -import io.netty.util.concurrent.UnorderedThreadPoolEventExecutor; -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.Collections; -import java.util.Iterator; -import java.util.Queue; -import java.util.concurrent.Callable; -import java.util.concurrent.RejectedExecutionException; -import java.util.concurrent.TimeUnit; -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 EventExecutorGroup { - private static final Runnable POISON_PILL = () -> { - }; - private final Queue tasks; - private final AtomicBoolean poisoned = new AtomicBoolean(); - private final Thread executorThread; - private final Promise terminationFuture = ImmediateEventExecutor.INSTANCE.newPromise(); - - SpinExecutorService(int maxTasks) { - tasks = PlatformDependent.newFixedMpscQueue(maxTasks); - executorThread = new Thread(() -> { - Runnable task; - while ((task = tasks.poll()) != POISON_PILL) { - if (task != null) { - task.run(); - } - } - }); - executorThread.start(); - } - - @Override - public boolean isShuttingDown() { - return poisoned.get(); - } - - @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 schedule(Runnable task, long delay, TimeUnit unit) { - throw new UnsupportedOperationException(); - } - - @Override - public Future schedule(Callable task, long delay, TimeUnit unit) { - throw new UnsupportedOperationException(); - } - - @Override - public Future scheduleAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit unit) { - throw new UnsupportedOperationException(); - } - - @Override - public Future scheduleWithFixedDelay(Runnable task, long initialDelay, long delay, TimeUnit unit) { - 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 void execute(Runnable task) { - if (!tasks.offer(task)) { - throw new RejectedExecutionException( - "If that happens, there is something wrong with the available capacity/burst size"); - } - } - - @Override - public Future shutdownGracefully() { - if (poisoned.compareAndSet(false, true)) { - while (!tasks.offer(POISON_PILL)) { - // Just try again - } - try { - executorThread.join(); - } catch (InterruptedException e) { - //We're quite trusty :) - } - } - terminationFuture.trySuccess(null); - return terminationFuture.asFuture(); - } - - @Override - public Future shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) { - return shutdownGracefully(); - } - - @Override - public Future terminationFuture() { - return terminationFuture.asFuture(); - } - - @Override - public EventExecutor next() { - throw new UnsupportedOperationException(); - } - - @Override - public Iterator iterator() { - return Collections.emptyIterator(); - } - } - - 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 EventExecutorGroup executor; - private EventExecutorGroup 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 SingleThreadEventExecutor(); - executorToShutdown = executor; - break; - case juc: - executor = new UnorderedThreadPoolEventExecutor(1); - executorToShutdown = executor; - break; - case nioEventLoop: - EventLoopGroup nioEventLoopGroup = new MultithreadEventLoopGroup(1, - new DefaultThreadFactory(MultithreadEventLoopGroup.class), NioHandler.newFactory(), - Integer.MAX_VALUE, RejectedExecutionHandlers.reject(), Integer.MAX_VALUE); - executor = nioEventLoopGroup.next(); - executorToShutdown = nioEventLoopGroup; - break; - case epollEventLoop: - Epoll.ensureAvailability(); - EventLoopGroup epollEventLoopGroup = new MultithreadEventLoopGroup(1, - new DefaultThreadFactory(MultithreadEventLoopGroup.class), EpollHandler.newFactory(), - Integer.MAX_VALUE, RejectedExecutionHandlers.reject(), Integer.MAX_VALUE); - executor = epollEventLoopGroup.next(); - executorToShutdown = epollEventLoopGroup; - break; - case kqueueEventLoop: - KQueue.ensureAvailability(); - EventLoopGroup kqueueEventLoopGroup = new MultithreadEventLoopGroup(1, - new DefaultThreadFactory(MultithreadEventLoopGroup.class), KQueueHandler.newFactory(), - Integer.MAX_VALUE, RejectedExecutionHandlers.reject(), Integer.MAX_VALUE); - executor = kqueueEventLoopGroup.next(); - executorToShutdown = kqueueEventLoopGroup; - break; - } - } - - @TearDown - public void tearDown() { - executorToShutdown.shutdownGracefully(0, 0, TimeUnit.MILLISECONDS); - } - - @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 = () -> { - 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(this, completed + 1); - }; - } else { - completeTask = () -> { - //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(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 = 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 EventExecutorGroup 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; - } -} diff --git a/microbench/src/main/java/io/netty/microbench/concurrent/FastThreadLocalFastPathBenchmark.java b/microbench/src/main/java/io/netty/microbench/concurrent/FastThreadLocalFastPathBenchmark.java deleted file mode 100644 index 36b291f413..0000000000 --- a/microbench/src/main/java/io/netty/microbench/concurrent/FastThreadLocalFastPathBenchmark.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.concurrent; - -import io.netty.microbench.util.AbstractMicrobenchmark; -import io.netty.util.concurrent.FastThreadLocal; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Threads; -import org.openjdk.jmh.infra.Blackhole; - -import java.util.Random; - -/** - * This class benchmarks the fast path of FastThreadLocal and the JDK ThreadLocal. - */ -@Threads(4) -@Measurement(iterations = 10, batchSize = 100) -public class FastThreadLocalFastPathBenchmark extends AbstractMicrobenchmark { - - private static final Random rand = new Random(); - - @SuppressWarnings("unchecked") - private static final ThreadLocal[] jdkThreadLocals = new ThreadLocal[128]; - @SuppressWarnings("unchecked") - private static final FastThreadLocal[] fastThreadLocals = new FastThreadLocal[jdkThreadLocals.length]; - - static { - for (int i = 0; i < jdkThreadLocals.length; i ++) { - final int num = rand.nextInt(); - jdkThreadLocals[i] = new ThreadLocal() { - @Override - protected Integer initialValue() { - return num; - } - }; - fastThreadLocals[i] = new FastThreadLocal() { - @Override - protected Integer initialValue() { - return num; - } - }; - } - } - - @Benchmark - public void jdkThreadLocalGet(Blackhole bh) { - for (ThreadLocal i: jdkThreadLocals) { - bh.consume(i.get()); - } - } - - @Benchmark - public void fastThreadLocal(Blackhole bh) { - for (FastThreadLocal i: fastThreadLocals) { - bh.consume(i.get()); - } - } -} diff --git a/microbench/src/main/java/io/netty/microbench/concurrent/FastThreadLocalSlowPathBenchmark.java b/microbench/src/main/java/io/netty/microbench/concurrent/FastThreadLocalSlowPathBenchmark.java deleted file mode 100644 index 6e1e7849cd..0000000000 --- a/microbench/src/main/java/io/netty/microbench/concurrent/FastThreadLocalSlowPathBenchmark.java +++ /dev/null @@ -1,76 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.concurrent; - -import io.netty.microbench.util.AbstractMicrobenchmark; -import io.netty.util.concurrent.FastThreadLocal; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Threads; -import org.openjdk.jmh.infra.Blackhole; - -import java.util.Random; - -/** - * This class benchmarks the slow path of FastThreadLocal and the JDK ThreadLocal. - */ -@Threads(4) -@Measurement(iterations = 10, batchSize = 100) -public class FastThreadLocalSlowPathBenchmark extends AbstractMicrobenchmark { - - private static final Random rand = new Random(); - - @SuppressWarnings("unchecked") - private static final ThreadLocal[] jdkThreadLocals = new ThreadLocal[128]; - @SuppressWarnings("unchecked") - private static final FastThreadLocal[] fastThreadLocals = new FastThreadLocal[jdkThreadLocals.length]; - - static { - for (int i = 0; i < jdkThreadLocals.length; i ++) { - final int num = rand.nextInt(); - jdkThreadLocals[i] = new ThreadLocal() { - @Override - protected Integer initialValue() { - return num; - } - }; - fastThreadLocals[i] = new FastThreadLocal() { - @Override - protected Integer initialValue() { - return num; - } - }; - } - } - - public FastThreadLocalSlowPathBenchmark() { - super(false, true); - } - - @Benchmark - public void jdkThreadLocalGet(Blackhole bh) { - for (ThreadLocal i: jdkThreadLocals) { - bh.consume(i.get()); - } - } - - @Benchmark - public void fastThreadLocal(Blackhole bh) { - for (FastThreadLocal i: fastThreadLocals) { - bh.consume(i.get()); - } - } -} diff --git a/microbench/src/main/java/io/netty/microbench/concurrent/RunnableScheduledFutureAdapterBenchmark.java b/microbench/src/main/java/io/netty/microbench/concurrent/RunnableScheduledFutureAdapterBenchmark.java deleted file mode 100644 index 7e0ff6cd32..0000000000 --- a/microbench/src/main/java/io/netty/microbench/concurrent/RunnableScheduledFutureAdapterBenchmark.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.concurrent; - -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.local.LocalHandler; -import io.netty.microbench.util.AbstractMicrobenchmark; -import io.netty.util.concurrent.Future; -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; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Callable; -import java.util.concurrent.TimeUnit; - -@State(Scope.Benchmark) -public class RunnableScheduledFutureAdapterBenchmark extends AbstractMicrobenchmark { - - static final EventLoopGroup executor = new MultithreadEventLoopGroup(1, LocalHandler.newFactory()); - - @State(Scope.Thread) - public static class FuturesHolder { - - private static final Callable NO_OP = () -> null; - - @Param({ "100", "1000", "10000", "100000" }) - int num; - - final List> futures = new ArrayList<>(); - - @Setup(Level.Invocation) - public void reset() { - futures.clear(); - executor.submit(() -> { - for (int i = 1; i <= num; i++) { - futures.add(executor.schedule(NO_OP, i, TimeUnit.HOURS)); - } - }).syncUninterruptibly(); - } - } - - @TearDown(Level.Trial) - public void stop() throws Exception { - executor.shutdownGracefully().syncUninterruptibly(); - } - - @Benchmark - public Future cancelInOrder(final FuturesHolder futuresHolder) { - return executor.submit(() -> { - for (int i = 0; i < futuresHolder.num; i++) { - futuresHolder.futures.get(i).cancel(); - } - }).syncUninterruptibly(); - } - - @Benchmark - public Future cancelInReverseOrder(final FuturesHolder futuresHolder) { - return executor.submit(() -> { - for (int i = futuresHolder.num - 1; i >= 0; i--) { - futuresHolder.futures.get(i).cancel(); - } - }).syncUninterruptibly(); - } -} diff --git a/microbench/src/main/java/io/netty/microbench/concurrent/package-info.java b/microbench/src/main/java/io/netty/microbench/concurrent/package-info.java deleted file mode 100644 index c417bd674c..0000000000 --- a/microbench/src/main/java/io/netty/microbench/concurrent/package-info.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -/** - * Benchmarks for {@link io.netty.util.concurrent}. - */ -package io.netty.microbench.concurrent; diff --git a/microbench/src/main/java/io/netty/microbench/handler/ssl/AbstractSslEngineBenchmark.java b/microbench/src/main/java/io/netty/microbench/handler/ssl/AbstractSslEngineBenchmark.java deleted file mode 100644 index 330f51a08f..0000000000 --- a/microbench/src/main/java/io/netty/microbench/handler/ssl/AbstractSslEngineBenchmark.java +++ /dev/null @@ -1,279 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.handler.ssl; - -import io.netty.buffer.ByteBufAllocator; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.SslProvider; -import io.netty.handler.ssl.util.InsecureTrustManagerFactory; -import io.netty.microbench.util.AbstractMicrobenchmark; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.internal.PlatformDependent; -import org.openjdk.jmh.annotations.Param; - -import java.io.File; -import java.nio.ByteBuffer; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLEngineResult; -import javax.net.ssl.SSLException; - - -public class AbstractSslEngineBenchmark extends AbstractMicrobenchmark { - - private static final String PROTOCOL_TLS_V1_2 = "TLSv1.2"; - - public enum SslEngineProvider { - JDK { - @Override - SslProvider sslProvider() { - return SslProvider.JDK; - } - }, - OPENSSL { - @Override - SslProvider sslProvider() { - return SslProvider.OPENSSL; - } - }, - OPENSSL_REFCNT { - @Override - SslProvider sslProvider() { - return SslProvider.OPENSSL_REFCNT; - } - }; - private final SslContext clientContext = newClientContext(); - private final SslContext serverContext = newServerContext(); - - private SslContext newClientContext() { - try { - return SslContextBuilder.forClient() - .sslProvider(sslProvider()) - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .build(); - } catch (SSLException e) { - throw new IllegalStateException(e); - } - } - - private SslContext newServerContext() { - try { - File keyFile = new File(getClass().getResource("test_unencrypted.pem").getFile()); - File crtFile = new File(getClass().getResource("test.crt").getFile()); - - return SslContextBuilder.forServer(crtFile, keyFile) - .sslProvider(sslProvider()) - .build(); - } catch (Exception e) { - throw new IllegalStateException(e); - } - } - - SSLEngine newClientEngine(ByteBufAllocator allocator, String cipher) { - return configureEngine(clientContext.newHandler(allocator).engine(), cipher); - } - - SSLEngine newServerEngine(ByteBufAllocator allocator, String cipher) { - return configureEngine(serverContext.newHandler(allocator).engine(), cipher); - } - - abstract SslProvider sslProvider(); - - static SSLEngine configureEngine(SSLEngine engine, String cipher) { - engine.setEnabledProtocols(new String[]{ PROTOCOL_TLS_V1_2 }); - engine.setEnabledCipherSuites(new String[]{ cipher }); - return engine; - } - } - - public enum BufferType { - HEAP { - @Override - ByteBuffer newBuffer(int size) { - return ByteBuffer.allocate(size); - } - }, - DIRECT { - @Override - ByteBuffer newBuffer(int size) { - return ByteBuffer.allocateDirect(size); - } - - @Override - void freeBuffer(ByteBuffer buffer) { - PlatformDependent.freeDirectBuffer(buffer); - } - }; - - abstract ByteBuffer newBuffer(int size); - - void freeBuffer(ByteBuffer buffer) { } - } - - @Param - public SslEngineProvider sslProvider; - - @Param - public BufferType bufferType; - - // Includes cipher required by HTTP/2 - @Param({ "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" }) - public String cipher; - - protected SSLEngine clientEngine; - protected SSLEngine serverEngine; - - private ByteBuffer cTOs; - private ByteBuffer sTOc; - private ByteBuffer serverAppReadBuffer; - private ByteBuffer clientAppReadBuffer; - private ByteBuffer empty; - - protected final void initEngines(ByteBufAllocator allocator) { - clientEngine = newClientEngine(allocator); - serverEngine = newServerEngine(allocator); - } - - protected final void destroyEngines() { - ReferenceCountUtil.release(clientEngine); - ReferenceCountUtil.release(serverEngine); - } - - protected final void initHandshakeBuffers() { - cTOs = allocateBuffer(clientEngine.getSession().getPacketBufferSize()); - sTOc = allocateBuffer(serverEngine.getSession().getPacketBufferSize()); - - serverAppReadBuffer = allocateBuffer( - serverEngine.getSession().getApplicationBufferSize()); - clientAppReadBuffer = allocateBuffer( - clientEngine.getSession().getApplicationBufferSize()); - empty = allocateBuffer(0); - } - - protected final void destroyHandshakeBuffers() { - freeBuffer(cTOs); - freeBuffer(sTOc); - freeBuffer(serverAppReadBuffer); - freeBuffer(clientAppReadBuffer); - freeBuffer(empty); - } - - protected final boolean doHandshake() throws SSLException { - clientEngine.beginHandshake(); - serverEngine.beginHandshake(); - - SSLEngineResult clientResult = null; - SSLEngineResult serverResult = null; - - boolean clientHandshakeFinished = false; - boolean serverHandshakeFinished = false; - - do { - int cTOsPos = cTOs.position(); - int sTOcPos = sTOc.position(); - - if (!clientHandshakeFinished) { - clientResult = clientEngine.wrap(empty, cTOs); - runDelegatedTasks(clientResult, clientEngine); - assert empty.remaining() == clientResult.bytesConsumed(); - assert cTOs.position() - cTOsPos == clientResult.bytesProduced(); - - clientHandshakeFinished = isHandshakeFinished(clientResult); - } - - if (!serverHandshakeFinished) { - serverResult = serverEngine.wrap(empty, sTOc); - runDelegatedTasks(serverResult, serverEngine); - assert empty.remaining() == serverResult.bytesConsumed(); - assert sTOc.position() - sTOcPos == serverResult.bytesProduced(); - - serverHandshakeFinished = isHandshakeFinished(serverResult); - } - - cTOs.flip(); - sTOc.flip(); - - cTOsPos = cTOs.position(); - sTOcPos = sTOc.position(); - - if (!clientHandshakeFinished) { - int clientAppReadBufferPos = clientAppReadBuffer.position(); - clientResult = clientEngine.unwrap(sTOc, clientAppReadBuffer); - - runDelegatedTasks(clientResult, clientEngine); - assert sTOc.position() - sTOcPos == clientResult.bytesConsumed(); - assert clientAppReadBuffer.position() - clientAppReadBufferPos == clientResult.bytesProduced(); - - clientHandshakeFinished = isHandshakeFinished(clientResult); - } else { - assert !sTOc.hasRemaining(); - } - - if (!serverHandshakeFinished) { - int serverAppReadBufferPos = serverAppReadBuffer.position(); - serverResult = serverEngine.unwrap(cTOs, serverAppReadBuffer); - runDelegatedTasks(serverResult, serverEngine); - assert cTOs.position() - cTOsPos == serverResult.bytesConsumed(); - assert serverAppReadBuffer.position() - serverAppReadBufferPos == serverResult.bytesProduced(); - - serverHandshakeFinished = isHandshakeFinished(serverResult); - } else { - assert !cTOs.hasRemaining(); - } - - sTOc.compact(); - cTOs.compact(); - } while (!clientHandshakeFinished || !serverHandshakeFinished); - return clientResult.getStatus() == SSLEngineResult.Status.OK && - serverResult.getStatus() == SSLEngineResult.Status.OK; - } - - protected final SSLEngine newClientEngine(ByteBufAllocator allocator) { - return sslProvider.newClientEngine(allocator, cipher); - } - - protected final SSLEngine newServerEngine(ByteBufAllocator allocator) { - return sslProvider.newServerEngine(allocator, cipher); - } - - static boolean checkSslEngineResult(SSLEngineResult result, ByteBuffer src, ByteBuffer dst) { - return result.getStatus() == SSLEngineResult.Status.OK && !src.hasRemaining() && dst.hasRemaining(); - } - - protected final ByteBuffer allocateBuffer(int size) { - return bufferType.newBuffer(size); - } - - protected final void freeBuffer(ByteBuffer buffer) { - bufferType.freeBuffer(buffer); - } - - private static boolean isHandshakeFinished(SSLEngineResult result) { - return result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.FINISHED; - } - - private static void runDelegatedTasks(SSLEngineResult result, SSLEngine engine) { - if (result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK) { - for (;;) { - Runnable task = engine.getDelegatedTask(); - if (task == null) { - break; - } - task.run(); - } - } - } -} diff --git a/microbench/src/main/java/io/netty/microbench/handler/ssl/AbstractSslEngineThroughputBenchmark.java b/microbench/src/main/java/io/netty/microbench/handler/ssl/AbstractSslEngineThroughputBenchmark.java deleted file mode 100644 index 1a9d6d9739..0000000000 --- a/microbench/src/main/java/io/netty/microbench/handler/ssl/AbstractSslEngineThroughputBenchmark.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.handler.ssl; - -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.PooledByteBufAllocator; -import org.openjdk.jmh.annotations.Level; -import org.openjdk.jmh.annotations.Param; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.TearDown; - -import java.nio.ByteBuffer; -import java.util.concurrent.ThreadLocalRandom; -import javax.net.ssl.SSLEngineResult; -import javax.net.ssl.SSLException; - -public abstract class AbstractSslEngineThroughputBenchmark extends AbstractSslEngineBenchmark { - - @Param({ "64", "128", "512", "1024", "4096" }) - public int messageSize; - - protected ByteBuffer wrapSrcBuffer; - private ByteBuffer wrapDstBuffer; - - @Setup(Level.Iteration) - public final void setup() throws Exception { - ByteBufAllocator allocator = new PooledByteBufAllocator(true); - initEngines(allocator); - initHandshakeBuffers(); - - wrapDstBuffer = allocateBuffer(clientEngine.getSession().getPacketBufferSize() << 2); - wrapSrcBuffer = allocateBuffer(messageSize); - - byte[] bytes = new byte[messageSize]; - ThreadLocalRandom.current().nextBytes(bytes); - wrapSrcBuffer.put(bytes); - wrapSrcBuffer.flip(); - - // Complete the initial TLS handshake. - if (!doHandshake()) { - throw new IllegalStateException(); - } - doSetup(); - } - - protected void doSetup() throws Exception { } - - @TearDown(Level.Iteration) - public final void tearDown() throws Exception { - destroyEngines(); - destroyHandshakeBuffers(); - freeBuffer(wrapSrcBuffer); - freeBuffer(wrapDstBuffer); - doTearDown(); - } - - protected void doTearDown() throws Exception { } - - protected final ByteBuffer doWrap(int numWraps) throws SSLException { - wrapDstBuffer.clear(); - - for (int i = 0; i < numWraps; ++i) { - wrapSrcBuffer.position(0).limit(messageSize); - - SSLEngineResult wrapResult = clientEngine.wrap(wrapSrcBuffer, wrapDstBuffer); - - assert checkSslEngineResult(wrapResult, wrapSrcBuffer, wrapDstBuffer); - } - return wrapDstBuffer; - } -} diff --git a/microbench/src/main/java/io/netty/microbench/handler/ssl/AbstractSslHandlerBenchmark.java b/microbench/src/main/java/io/netty/microbench/handler/ssl/AbstractSslHandlerBenchmark.java deleted file mode 100644 index bb11e2c7a3..0000000000 --- a/microbench/src/main/java/io/netty/microbench/handler/ssl/AbstractSslHandlerBenchmark.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.handler.ssl; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.api.BufferAllocator; -import io.netty.channel.ChannelHandler; -import io.netty.handler.codec.ByteToMessageDecoder; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.SslHandler; -import io.netty.handler.ssl.SslProvider; -import io.netty.handler.ssl.util.InsecureTrustManagerFactory; -import io.netty.microbench.channel.EmbeddedChannelWriteAccumulatingHandlerContext; -import io.netty.microbench.util.AbstractMicrobenchmark; -import io.netty.util.ReferenceCountUtil; -import org.openjdk.jmh.annotations.Param; - -import java.io.File; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLException; - -import static io.netty.handler.codec.ByteToMessageDecoder.COMPOSITE_CUMULATOR; - -public class AbstractSslHandlerBenchmark extends AbstractMicrobenchmark { - private static final String PROTOCOL_TLS_V1_2 = "TLSv1.2"; - - public enum SslEngineProvider { - JDK { - @Override - SslProvider sslProvider() { - return SslProvider.JDK; - } - }, - OPENSSL { - @Override - SslProvider sslProvider() { - return SslProvider.OPENSSL; - } - }, - OPENSSL_REFCNT { - @Override - SslProvider sslProvider() { - return SslProvider.OPENSSL_REFCNT; - } - }; - private final SslContext clientContext = newClientContext(); - private final SslContext serverContext = newServerContext(); - - private SslContext newClientContext() { - try { - return SslContextBuilder.forClient() - .sslProvider(sslProvider()) - .trustManager(InsecureTrustManagerFactory.INSTANCE) - .build(); - } catch (SSLException e) { - throw new IllegalStateException(e); - } - } - - private SslContext newServerContext() { - try { - File keyFile = new File(getClass().getResource("test_unencrypted.pem").getFile()); - File crtFile = new File(getClass().getResource("test.crt").getFile()); - - return SslContextBuilder.forServer(crtFile, keyFile) - .sslProvider(sslProvider()) - .build(); - } catch (Exception e) { - throw new IllegalStateException(e); - } - } - - SslHandler newClientHandler(ByteBufAllocator allocator, String cipher) { - SslHandler handler = clientContext.newHandler(allocator); - configureEngine(handler.engine(), cipher); - return handler; - } - - SslHandler newServerHandler(ByteBufAllocator allocator, String cipher) { - SslHandler handler = serverContext.newHandler(allocator); - configureEngine(handler.engine(), cipher); - return handler; - } - - abstract SslProvider sslProvider(); - - static SSLEngine configureEngine(SSLEngine engine, String cipher) { - engine.setEnabledProtocols(new String[]{ PROTOCOL_TLS_V1_2 }); - engine.setEnabledCipherSuites(new String[]{ cipher }); - return engine; - } - } - - @Param - public SslEngineProvider sslProvider; - - // Includes cipher required by HTTP/2 - @Param({ "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" }) - public String cipher; - - protected SslHandler clientSslHandler; - protected SslHandler serverSslHandler; - protected EmbeddedChannelWriteAccumulatingHandlerContext clientCtx; - protected EmbeddedChannelWriteAccumulatingHandlerContext serverCtx; - - protected final void initSslHandlers(ByteBufAllocator allocator) { - clientSslHandler = newClientHandler(allocator); - serverSslHandler = newServerHandler(allocator); - clientCtx = new SslThroughputBenchmarkHandlerContext(allocator, clientSslHandler, COMPOSITE_CUMULATOR); - serverCtx = new SslThroughputBenchmarkHandlerContext(allocator, serverSslHandler, COMPOSITE_CUMULATOR); - } - - protected final void destroySslHandlers() { - try { - if (clientSslHandler != null) { - ReferenceCountUtil.release(clientSslHandler.engine()); - } - } finally { - if (serverSslHandler != null) { - ReferenceCountUtil.release(serverSslHandler.engine()); - } - } - } - - protected final void doHandshake() throws Exception { - serverSslHandler.handlerAdded(serverCtx); - clientSslHandler.handlerAdded(clientCtx); - do { - ByteBuf clientCumulation = clientCtx.cumulation(); - if (clientCumulation != null) { - serverSslHandler.channelRead(serverCtx, clientCumulation.retain()); - clientCtx.releaseCumulation(); - } - ByteBuf serverCumulation = serverCtx.cumulation(); - if (serverCumulation != null) { - clientSslHandler.channelRead(clientCtx, serverCumulation.retain()); - serverCtx.releaseCumulation(); - } - } while (!clientSslHandler.handshakeFuture().isDone() || !serverSslHandler.handshakeFuture().isDone()); - } - - protected final SslHandler newClientHandler(ByteBufAllocator allocator) { - return sslProvider.newClientHandler(allocator, cipher); - } - - protected final SslHandler newServerHandler(ByteBufAllocator allocator) { - return sslProvider.newServerHandler(allocator, cipher); - } - - private static final class SslThroughputBenchmarkHandlerContext extends - EmbeddedChannelWriteAccumulatingHandlerContext { - SslThroughputBenchmarkHandlerContext(ByteBufAllocator alloc, ChannelHandler handler, - ByteToMessageDecoder.Cumulator writeCumulator) { - super(alloc, handler, writeCumulator); - } - - SslThroughputBenchmarkHandlerContext(BufferAllocator alloc, ChannelHandler handler, - ByteToMessageDecoder.Cumulator writeCumulator) { - super(alloc, handler, writeCumulator); - } - - @Override - protected void handleException(Throwable t) { - handleUnexpectedException(t); - } - } - - public static void handleUnexpectedException(Throwable t) { - if (t != null) { - throw new IllegalStateException(t); - } - } -} diff --git a/microbench/src/main/java/io/netty/microbench/handler/ssl/AbstractSslHandlerThroughputBenchmark.java b/microbench/src/main/java/io/netty/microbench/handler/ssl/AbstractSslHandlerThroughputBenchmark.java deleted file mode 100644 index d8c334d332..0000000000 --- a/microbench/src/main/java/io/netty/microbench/handler/ssl/AbstractSslHandlerThroughputBenchmark.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.microbench.handler.ssl; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.PooledByteBufAllocator; -import io.netty.channel.embedded.EmbeddedChannel; -import org.openjdk.jmh.annotations.Level; -import org.openjdk.jmh.annotations.Param; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.TearDown; - -import java.util.concurrent.ThreadLocalRandom; - -public abstract class AbstractSslHandlerThroughputBenchmark extends AbstractSslHandlerBenchmark { - @Param({ "64", "128", "512", "1024", "4096" }) - public int messageSize; - - @Param - public BufferType bufferType; - - public enum BufferType { - HEAP { - @Override - ByteBuf newBuffer(ByteBufAllocator allocator, int size) { - return allocator.heapBuffer(size); - } - }, - DIRECT { - @Override - ByteBuf newBuffer(ByteBufAllocator allocator, int size) { - return allocator.directBuffer(size); - } - }; - - abstract ByteBuf newBuffer(ByteBufAllocator allocator, int size); - } - - protected ByteBuf wrapSrcBuffer; - protected EmbeddedChannel channel; - private ByteBufAllocator allocator; - - @Setup(Level.Iteration) - public final void setup() throws Exception { - allocator = new PooledByteBufAllocator(true); - - initSslHandlers(allocator); - - wrapSrcBuffer = allocateBuffer(messageSize); - - byte[] bytes = new byte[messageSize]; - ThreadLocalRandom.current().nextBytes(bytes); - wrapSrcBuffer.writeBytes(bytes); - - // Complete the initial TLS handshake. - doHandshake(); - } - - @TearDown(Level.Iteration) - public final void tearDown() throws Exception { - destroySslHandlers(); - wrapSrcBuffer.release(); - clientCtx.releaseCumulation(); - serverCtx.releaseCumulation(); - } - - protected final ByteBuf allocateBuffer(int size) { - return bufferType.newBuffer(allocator, size); - } - - protected final ByteBuf doWrite(int numWrites) throws Exception { - clientCtx.releaseCumulation(); - - for (int i = 0; i < numWrites; ++i) { - ByteBuf wrapSrcBuffer = this.wrapSrcBuffer.retainedSlice(); - - clientSslHandler.write(clientCtx, wrapSrcBuffer); - } - clientSslHandler.flush(clientCtx); - return clientCtx.cumulation().retainedSlice(); - } -} diff --git a/microbench/src/main/java/io/netty/microbench/handler/ssl/SslEngineEchoBenchmark.java b/microbench/src/main/java/io/netty/microbench/handler/ssl/SslEngineEchoBenchmark.java deleted file mode 100644 index 5cc03f454f..0000000000 --- a/microbench/src/main/java/io/netty/microbench/handler/ssl/SslEngineEchoBenchmark.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.handler.ssl; - -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.Param; -import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.Threads; - -import java.nio.ByteBuffer; -import javax.net.ssl.SSLEngineResult; -import javax.net.ssl.SSLException; - -@State(Scope.Benchmark) -@Threads(1) -public class SslEngineEchoBenchmark extends AbstractSslEngineThroughputBenchmark { - - @Param({ "1", "2", "5", "10" }) - public int numWraps; - private ByteBuffer unwrapDstBuffer; - - @Override - protected void doSetup() { - unwrapDstBuffer = allocateBuffer(serverEngine.getSession().getApplicationBufferSize() << 2); - } - - @Override - protected void doTearDown() { - freeBuffer(unwrapDstBuffer); - } - - @Benchmark - public ByteBuffer wrapUnwrap() throws SSLException { - ByteBuffer src = doWrap(numWraps); - src.flip(); - - unwrapDstBuffer.clear(); - - SSLEngineResult unwrapResult; - do { - unwrapResult = serverEngine.unwrap(src, unwrapDstBuffer); - } while (unwrapResult.getStatus() == SSLEngineResult.Status.OK && src.hasRemaining()); - - assert checkSslEngineResult(unwrapResult, src, unwrapDstBuffer); - return unwrapDstBuffer; - } -} diff --git a/microbench/src/main/java/io/netty/microbench/handler/ssl/SslEngineHandshakeBenchmark.java b/microbench/src/main/java/io/netty/microbench/handler/ssl/SslEngineHandshakeBenchmark.java deleted file mode 100644 index 6620570c5e..0000000000 --- a/microbench/src/main/java/io/netty/microbench/handler/ssl/SslEngineHandshakeBenchmark.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.handler.ssl; - -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.PooledByteBufAllocator; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.Level; -import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.OutputTimeUnit; -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 java.util.concurrent.TimeUnit; - -@State(Scope.Benchmark) -@Threads(1) -public class SslEngineHandshakeBenchmark extends AbstractSslEngineBenchmark { - - private ByteBufAllocator allocator; - - @Setup(Level.Iteration) - public void setup() { - allocator = new PooledByteBufAllocator(true); - // Init an engine one time and create the buffers needed for an handshake so we can use them in the benchmark - initEngines(allocator); - initHandshakeBuffers(); - destroyEngines(); - } - - @TearDown(Level.Iteration) - public void teardown() { - destroyHandshakeBuffers(); - } - - @Benchmark - @BenchmarkMode(Mode.AverageTime) - @OutputTimeUnit(TimeUnit.MICROSECONDS) - public boolean handshake() throws Exception { - initEngines(allocator); - boolean ok = doHandshake(); - destroyEngines(); - assert ok; - return ok; - } -} diff --git a/microbench/src/main/java/io/netty/microbench/handler/ssl/SslEngineWrapBenchmark.java b/microbench/src/main/java/io/netty/microbench/handler/ssl/SslEngineWrapBenchmark.java deleted file mode 100644 index 1f9e559e26..0000000000 --- a/microbench/src/main/java/io/netty/microbench/handler/ssl/SslEngineWrapBenchmark.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.handler.ssl; - -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.Param; -import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.Threads; - -import java.nio.ByteBuffer; -import javax.net.ssl.SSLException; - -@State(Scope.Benchmark) -@Threads(1) -public class SslEngineWrapBenchmark extends AbstractSslEngineThroughputBenchmark { - - @Param({ "1", "2", "5", "10" }) - public int numWraps; - - @Benchmark - public ByteBuffer wrap() throws SSLException { - return doWrap(numWraps); - } -} diff --git a/microbench/src/main/java/io/netty/microbench/handler/ssl/SslHandlerEchoBenchmark.java b/microbench/src/main/java/io/netty/microbench/handler/ssl/SslHandlerEchoBenchmark.java deleted file mode 100644 index 68027af32a..0000000000 --- a/microbench/src/main/java/io/netty/microbench/handler/ssl/SslHandlerEchoBenchmark.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.handler.ssl; - -import io.netty.buffer.ByteBuf; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.Param; - -public class SslHandlerEchoBenchmark extends AbstractSslHandlerThroughputBenchmark { - @Param({ "1", "2", "5", "10" }) - public int numWrites; - - @Benchmark - public ByteBuf wrapUnwrap() throws Exception { - ByteBuf src = doWrite(numWrites); - - do { - serverSslHandler.channelRead(serverCtx, src); - } while (src.isReadable()); - - assert !src.isReadable() && src.refCnt() == 1 : "src: " + src + " src.refCnt(): " + src.refCnt(); - - return src; - } -} diff --git a/microbench/src/main/java/io/netty/microbench/handler/ssl/package-info.java b/microbench/src/main/java/io/netty/microbench/handler/ssl/package-info.java deleted file mode 100644 index 9b22bc5962..0000000000 --- a/microbench/src/main/java/io/netty/microbench/handler/ssl/package-info.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -/** - * Benchmarks for SSL. - */ -package io.netty.microbench.handler.ssl; diff --git a/microbench/src/main/java/io/netty/microbench/headers/ExampleHeaders.java b/microbench/src/main/java/io/netty/microbench/headers/ExampleHeaders.java deleted file mode 100644 index 19034f531a..0000000000 --- a/microbench/src/main/java/io/netty/microbench/headers/ExampleHeaders.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.headers; - -import java.util.EnumMap; -import java.util.HashMap; -import java.util.Map; - -public final class ExampleHeaders { - - public enum HeaderExample { - THREE, - FIVE, - SIX, - EIGHT, - ELEVEN, - TWENTYTWO, - THIRTY - } - - public static final Map> EXAMPLES = - new EnumMap<>(HeaderExample.class); - - static { - Map header = new HashMap<>(); - header.put(":method", "GET"); - header.put(":scheme", "https"); - header.put(":path", "/index.html"); - EXAMPLES.put(HeaderExample.THREE, header); - - // Headers used by Norman's HTTP benchmarks with wrk - header = new HashMap<>(); - header.put("Method", "GET"); - header.put("Path", "/plaintext"); - header.put("Host", "localhost"); - header.put("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); - header.put("Connection", "keep-alive"); - EXAMPLES.put(HeaderExample.FIVE, header); - - header = new HashMap<>(); - header.put(":authority", "127.0.0.1:33333"); - header.put(":method", "POST"); - header.put(":path", "/grpc.testing.TestService/UnaryCall"); - header.put(":scheme", "http"); - header.put("content-type", "application/grpc"); - header.put("te", "trailers"); - EXAMPLES.put(HeaderExample.SIX, header); - - header = new HashMap<>(); - header.put(":method", "POST"); - header.put(":scheme", "http"); - header.put(":path", "/google.pubsub.v2.PublisherService/CreateTopic"); - header.put(":authority", "pubsub.googleapis.com"); - header.put("grpc-timeout", "1S"); - header.put("content-type", "application/grpc+proto"); - header.put("grpc-encoding", "gzip"); - header.put("authorization", "Bearer y235.wef315yfh138vh31hv93hv8h3v"); - EXAMPLES.put(HeaderExample.EIGHT, header); - - header = new HashMap<>(); - header.put(":host", "twitter.com"); - header.put(":method", "GET"); - header.put(":path", "/"); - header.put(":scheme", "https"); - header.put(":version", "HTTP/1.1"); - header.put("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"); - header.put("accept-encoding", "gzip, deflate, sdch"); - header.put("accept-language", "en-US,en;q=0.8"); - header.put("cache-control", "max-age=0"); - header.put("cookie", "noneofyourbusiness"); - header.put("user-agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko)"); - EXAMPLES.put(HeaderExample.ELEVEN, header); - - header = new HashMap<>(); - header.put("cache-control", "no-cache, no-store, must-revalidate, pre-check=0, post-check=0"); - header.put("content-encoding", "gzip"); - header.put("content-security-policy", "default-src https:; connect-src https:;"); - header.put("content-type", "text/html;charset=utf-8"); - header.put("date", "Wed, 22 Apr 2015 00:40:28 GMT"); - header.put("expires", "Tue, 31 Mar 1981 05:00:00 GMT"); - header.put("last-modified", "Wed, 22 Apr 2015 00:40:28 GMT"); - header.put("ms", "ms"); - header.put("pragma", "no-cache"); - header.put("server", "tsa_b"); - header.put("set-cookie", "noneofyourbusiness"); - header.put("status", "200 OK"); - header.put("strict-transport-security", "max-age=631138519"); - header.put("version", "HTTP/1.1"); - header.put("x-connection-hash", "e176fe40accc1e2c613a34bc1941aa98"); - header.put("x-content-type-options", "nosniff"); - header.put("x-frame-options", "SAMEORIGIN"); - header.put("x-response-time", "22"); - header.put("x-transaction", "a54142ede693444d9"); - header.put("x-twitter-response-tags", "BouncerCompliant"); - header.put("x-ua-compatible", "IE=edge,chrome=1"); - header.put("x-xss-protection", "1; mode=block"); - EXAMPLES.put(HeaderExample.TWENTYTWO, header); - - header = new HashMap<>(); - header.put("Cache-Control", "no-cache"); - header.put("Content-Encoding", "gzip"); - header.put("Content-Security-Policy", "default-src *; script-src assets-cdn.github.com ..."); - header.put("Content-Type", "text/html; charset=utf-8"); - header.put("Date", "Fri, 10 Apr 2015 02:15:38 GMT"); - header.put("Server", "GitHub.com"); - header.put("Set-Cookie", "_gh_sess=eyJzZXNza...; path=/; secure; HttpOnly"); - header.put("Status", "200 OK"); - header.put("Strict-Transport-Security", "max-age=31536000; includeSubdomains; preload"); - header.put("Transfer-Encoding", "chunked"); - header.put("Vary", "X-PJAX"); - header.put("X-Content-Type-Options", "nosniff"); - header.put("X-Frame-Options", "deny"); - header.put("X-GitHub-Request-Id", "1"); - header.put("X-GitHub-Session-Id", "1"); - header.put("X-GitHub-User", "buchgr"); - header.put("X-Request-Id", "28f245e02fc872dcf7f31149e52931dd"); - header.put("X-Runtime", "0.082529"); - header.put("X-Served-By", "b9c2a233f7f3119b174dbd8be2"); - header.put("X-UA-Compatible", "IE=Edge,chrome=1"); - header.put("X-XSS-Protection", "1; mode=block"); - header.put("Via", "http/1.1 ir50.fp.bf1.yahoo.com (ApacheTrafficServer)"); - header.put("Content-Language", "en"); - header.put("Connection", "keep-alive"); - header.put("Pragma", "no-cache"); - header.put("Expires", "Sat, 01 Jan 2000 00:00:00 GMT"); - header.put("X-Moose", "majestic"); - header.put("x-ua-compatible", "IE=edge"); - header.put("CF-Cache-Status", "HIT"); - header.put("CF-RAY", "6a47f4f911e3-"); - EXAMPLES.put(HeaderExample.THIRTY, header); - } - - private ExampleHeaders() { - } -} diff --git a/microbench/src/main/java/io/netty/microbench/headers/HeadersBenchmark.java b/microbench/src/main/java/io/netty/microbench/headers/HeadersBenchmark.java deleted file mode 100644 index 8bd385c6c1..0000000000 --- a/microbench/src/main/java/io/netty/microbench/headers/HeadersBenchmark.java +++ /dev/null @@ -1,714 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.headers; - -import io.netty.handler.codec.Headers; -import io.netty.handler.codec.http.DefaultHttpHeaders; -import io.netty.handler.codec.http2.DefaultHttp2Headers; -import io.netty.microbench.util.AbstractMicrobenchmark; -import io.netty.util.AsciiString; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.Fork; -import org.openjdk.jmh.annotations.Level; -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.Scope; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.Threads; -import org.openjdk.jmh.annotations.Warmup; -import org.openjdk.jmh.infra.Blackhole; - -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.concurrent.TimeUnit; - -@Threads(1) -@State(Scope.Benchmark) -@Fork(1) -@Warmup(iterations = 10) -@Measurement(iterations = 10) -@OutputTimeUnit(TimeUnit.NANOSECONDS) -public class HeadersBenchmark extends AbstractMicrobenchmark { - - private static String toHttpName(String name) { - return (name.startsWith(":")) ? name.substring(1) : name; - } - - static String toHttp2Name(String name) { - name = name.toLowerCase(); - return (name.equals("host")) ? "xhost" : name; - } - - @Param - ExampleHeaders.HeaderExample exampleHeader; - - AsciiString[] httpNames; - AsciiString[] http2Names; - AsciiString[] httpValues; - - DefaultHttpHeaders httpHeaders; - DefaultHttp2Headers http2Headers; - DefaultHttpHeaders emptyHttpHeaders; - DefaultHttp2Headers emptyHttp2Headers; - DefaultHttpHeaders emptyHttpHeadersNoValidate; - DefaultHttp2Headers emptyHttp2HeadersNoValidate; - SlowHeaders slowHttp2Headers; - - @Setup(Level.Trial) - public void setup() { - Map headers = ExampleHeaders.EXAMPLES.get(exampleHeader); - httpNames = new AsciiString[headers.size()]; - http2Names = new AsciiString[headers.size()]; - httpValues = new AsciiString[headers.size()]; - httpHeaders = new DefaultHttpHeaders(false); - http2Headers = new DefaultHttp2Headers(false); - int idx = 0; - for (Map.Entry header : headers.entrySet()) { - String name = header.getKey(); - String httpName = toHttpName(name); - String http2Name = toHttp2Name(name); - String value = header.getValue(); - httpNames[idx] = new AsciiString(httpName); - http2Names[idx] = new AsciiString(http2Name); - httpValues[idx] = new AsciiString(value); - httpHeaders.add(httpNames[idx], httpValues[idx]); - http2Headers.add(http2Names[idx], httpValues[idx]); - idx++; - } - slowHttp2Headers = new SlowHeaders(http2Headers); - emptyHttpHeaders = new DefaultHttpHeaders(true); - emptyHttp2Headers = new DefaultHttp2Headers(true); - emptyHttpHeadersNoValidate = new DefaultHttpHeaders(false); - emptyHttp2HeadersNoValidate = new DefaultHttp2Headers(false); - } - - @Benchmark - @BenchmarkMode(Mode.AverageTime) - public void httpRemove(Blackhole bh) { - for (AsciiString name : httpNames) { - bh.consume(httpHeaders.remove(name)); - } - } - - @Benchmark - @BenchmarkMode(Mode.AverageTime) - public void httpGet(Blackhole bh) { - for (AsciiString name : httpNames) { - bh.consume(httpHeaders.get(name)); - } - } - - @Benchmark - @BenchmarkMode(Mode.AverageTime) - public DefaultHttpHeaders httpPut() { - DefaultHttpHeaders headers = new DefaultHttpHeaders(false); - for (int i = 0; i < httpNames.length; i++) { - headers.add(httpNames[i], httpValues[i]); - } - return headers; - } - - @Benchmark - @BenchmarkMode(Mode.AverageTime) - public void httpIterate(Blackhole bh) { - Iterator> itr = httpHeaders.iteratorCharSequence(); - while (itr.hasNext()) { - bh.consume(itr.next()); - } - } - - @Benchmark - @BenchmarkMode(Mode.AverageTime) - public void http2Remove(Blackhole bh) { - for (AsciiString name : http2Names) { - bh.consume(http2Headers.remove(name)); - } - } - - @Benchmark - @BenchmarkMode(Mode.AverageTime) - public void http2Get(Blackhole bh) { - for (AsciiString name : http2Names) { - bh.consume(http2Headers.get(name)); - } - } - - @Benchmark - @BenchmarkMode(Mode.AverageTime) - public DefaultHttp2Headers http2Put() { - DefaultHttp2Headers headers = new DefaultHttp2Headers(false); - for (int i = 0; i < http2Names.length; i++) { - headers.add(http2Names[i], httpValues[i]); - } - return headers; - } - - @Benchmark - @BenchmarkMode(Mode.AverageTime) - public void http2Iterate(Blackhole bh) { - for (Entry entry : http2Headers) { - bh.consume(entry); - } - } - - @Benchmark - @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 { - private final Headers> delegate; - private SlowHeaders(Headers> delegate) { - this.delegate = delegate; - } - - @Override - public CharSequence get(CharSequence name) { - return delegate.get(name); - } - - @Override - public CharSequence get(CharSequence name, CharSequence defaultValue) { - return delegate.get(name, defaultValue); - } - - @Override - public CharSequence getAndRemove(CharSequence name) { - return delegate.getAndRemove(name); - } - - @Override - public CharSequence getAndRemove(CharSequence name, CharSequence defaultValue) { - return delegate.getAndRemove(name, defaultValue); - } - - @Override - public List getAll(CharSequence name) { - return delegate.getAll(name); - } - - @Override - public List getAllAndRemove(CharSequence name) { - return delegate.getAllAndRemove(name); - } - - @Override - public Boolean getBoolean(CharSequence name) { - return delegate.getBoolean(name); - } - - @Override - public boolean getBoolean(CharSequence name, boolean defaultValue) { - return delegate.getBoolean(name, defaultValue); - } - - @Override - public Byte getByte(CharSequence name) { - return delegate.getByte(name); - } - - @Override - public byte getByte(CharSequence name, byte defaultValue) { - return delegate.getByte(name, defaultValue); - } - - @Override - public Character getChar(CharSequence name) { - return delegate.getChar(name); - } - - @Override - public char getChar(CharSequence name, char defaultValue) { - return delegate.getChar(name, defaultValue); - } - - @Override - public Short getShort(CharSequence name) { - return delegate.getShort(name); - } - - @Override - public short getShort(CharSequence name, short defaultValue) { - return delegate.getShort(name, defaultValue); - } - - @Override - public Integer getInt(CharSequence name) { - return delegate.getInt(name); - } - - @Override - public int getInt(CharSequence name, int defaultValue) { - return delegate.getInt(name, defaultValue); - } - - @Override - public Long getLong(CharSequence name) { - return delegate.getLong(name); - } - - @Override - public long getLong(CharSequence name, long defaultValue) { - return delegate.getLong(name, defaultValue); - } - - @Override - public Float getFloat(CharSequence name) { - return delegate.getFloat(name); - } - - @Override - public float getFloat(CharSequence name, float defaultValue) { - return delegate.getFloat(name, defaultValue); - } - - @Override - public Double getDouble(CharSequence name) { - return delegate.getDouble(name); - } - - @Override - public double getDouble(CharSequence name, double defaultValue) { - return delegate.getDouble(name, defaultValue); - } - - @Override - public Long getTimeMillis(CharSequence name) { - return delegate.getTimeMillis(name); - } - - @Override - public long getTimeMillis(CharSequence name, long defaultValue) { - return delegate.getTimeMillis(name, defaultValue); - } - - @Override - public Boolean getBooleanAndRemove(CharSequence name) { - return delegate.getBooleanAndRemove(name); - } - - @Override - public boolean getBooleanAndRemove(CharSequence name, boolean defaultValue) { - return delegate.getBooleanAndRemove(name, defaultValue); - } - - @Override - public Byte getByteAndRemove(CharSequence name) { - return delegate.getByteAndRemove(name); - } - - @Override - public byte getByteAndRemove(CharSequence name, byte defaultValue) { - return delegate.getByteAndRemove(name, defaultValue); - } - - @Override - public Character getCharAndRemove(CharSequence name) { - return delegate.getCharAndRemove(name); - } - - @Override - public char getCharAndRemove(CharSequence name, char defaultValue) { - return delegate.getCharAndRemove(name, defaultValue); - } - - @Override - public Short getShortAndRemove(CharSequence name) { - return delegate.getShortAndRemove(name); - } - - @Override - public short getShortAndRemove(CharSequence name, short defaultValue) { - return delegate.getShortAndRemove(name, defaultValue); - } - - @Override - public Integer getIntAndRemove(CharSequence name) { - return delegate.getIntAndRemove(name); - } - - @Override - public int getIntAndRemove(CharSequence name, int defaultValue) { - return delegate.getIntAndRemove(name, defaultValue); - } - - @Override - public Long getLongAndRemove(CharSequence name) { - return delegate.getLongAndRemove(name); - } - - @Override - public long getLongAndRemove(CharSequence name, long defaultValue) { - return delegate.getLongAndRemove(name, defaultValue); - } - - @Override - public Float getFloatAndRemove(CharSequence name) { - return delegate.getFloatAndRemove(name); - } - - @Override - public float getFloatAndRemove(CharSequence name, float defaultValue) { - return delegate.getFloatAndRemove(name, defaultValue); - } - - @Override - public Double getDoubleAndRemove(CharSequence name) { - return delegate.getDoubleAndRemove(name); - } - - @Override - public double getDoubleAndRemove(CharSequence name, double defaultValue) { - return delegate.getDoubleAndRemove(name, defaultValue); - } - - @Override - public Long getTimeMillisAndRemove(CharSequence name) { - return delegate.getTimeMillisAndRemove(name); - } - - @Override - public long getTimeMillisAndRemove(CharSequence name, long defaultValue) { - return delegate.getTimeMillisAndRemove(name, defaultValue); - } - - @Override - public boolean contains(CharSequence name) { - return delegate.contains(name); - } - - @Override - public boolean contains(CharSequence name, CharSequence value) { - return delegate.contains(name, value); - } - - @Override - public boolean containsObject(CharSequence name, Object value) { - return delegate.containsObject(name, value); - } - - @Override - public boolean containsBoolean(CharSequence name, boolean value) { - return delegate.containsBoolean(name, value); - } - - @Override - public boolean containsByte(CharSequence name, byte value) { - return delegate.containsByte(name, value); - } - - @Override - public boolean containsChar(CharSequence name, char value) { - return delegate.containsChar(name, value); - } - - @Override - public boolean containsShort(CharSequence name, short value) { - return delegate.containsShort(name, value); - } - - @Override - public boolean containsInt(CharSequence name, int value) { - return delegate.containsInt(name, value); - } - - @Override - public boolean containsLong(CharSequence name, long value) { - return delegate.containsLong(name, value); - } - - @Override - public boolean containsFloat(CharSequence name, float value) { - return delegate.containsFloat(name, value); - } - - @Override - public boolean containsDouble(CharSequence name, double value) { - return delegate.containsDouble(name, value); - } - - @Override - public boolean containsTimeMillis(CharSequence name, long value) { - return delegate.containsTimeMillis(name, value); - } - - @Override - public int size() { - return delegate.size(); - } - - @Override - public boolean isEmpty() { - return delegate.isEmpty(); - } - - @Override - public Set names() { - return delegate.names(); - } - - @Override - public SlowHeaders add(CharSequence name, CharSequence value) { - delegate.add(name, value); - return this; - } - - @Override - public SlowHeaders add(CharSequence name, Iterable values) { - delegate.add(name, values); - return this; - } - - @Override - public SlowHeaders add(CharSequence name, CharSequence... values) { - delegate.add(name, values); - return this; - } - - @Override - public SlowHeaders addObject(CharSequence name, Object value) { - delegate.addObject(name, value); - return this; - } - - @Override - public SlowHeaders addObject(CharSequence name, Iterable values) { - delegate.addObject(name, values); - return this; - } - - @Override - public SlowHeaders addObject(CharSequence name, Object... values) { - delegate.addObject(name, values); - return this; - } - - @Override - public SlowHeaders addBoolean(CharSequence name, boolean value) { - delegate.addBoolean(name, value); - return this; - } - - @Override - public SlowHeaders addByte(CharSequence name, byte value) { - delegate.addByte(name, value); - return this; - } - - @Override - public SlowHeaders addChar(CharSequence name, char value) { - delegate.addChar(name, value); - return this; - } - - @Override - public SlowHeaders addShort(CharSequence name, short value) { - delegate.addShort(name, value); - return this; - } - - @Override - public SlowHeaders addInt(CharSequence name, int value) { - delegate.addInt(name, value); - return this; - } - - @Override - public SlowHeaders addLong(CharSequence name, long value) { - delegate.addLong(name, value); - return this; - } - - @Override - public SlowHeaders addFloat(CharSequence name, float value) { - delegate.addFloat(name, value); - return this; - } - - @Override - public SlowHeaders addDouble(CharSequence name, double value) { - delegate.addDouble(name, value); - return this; - } - - @Override - public SlowHeaders addTimeMillis(CharSequence name, long value) { - delegate.addTimeMillis(name, value); - return this; - } - - @Override - public SlowHeaders add(Headers headers) { - delegate.add(headers); - return this; - } - - @Override - public SlowHeaders set(CharSequence name, CharSequence value) { - delegate.set(name, value); - return this; - } - - @Override - public SlowHeaders set(CharSequence name, Iterable values) { - delegate.set(name, values); - return this; - } - - @Override - public SlowHeaders set(CharSequence name, CharSequence... values) { - delegate.set(name, values); - return this; - } - - @Override - public SlowHeaders setObject(CharSequence name, Object value) { - delegate.setObject(name, value); - return this; - } - - @Override - public SlowHeaders setObject(CharSequence name, Iterable values) { - delegate.setObject(name, values); - return this; - } - - @Override - public SlowHeaders setObject(CharSequence name, Object... values) { - delegate.setObject(name, values); - return this; - } - - @Override - public SlowHeaders setBoolean(CharSequence name, boolean value) { - delegate.setBoolean(name, value); - return this; - } - - @Override - public SlowHeaders setByte(CharSequence name, byte value) { - delegate.setByte(name, value); - return this; - } - - @Override - public SlowHeaders setChar(CharSequence name, char value) { - delegate.setChar(name, value); - return this; - } - - @Override - public SlowHeaders setShort(CharSequence name, short value) { - delegate.setShort(name, value); - return this; - } - - @Override - public SlowHeaders setInt(CharSequence name, int value) { - delegate.setInt(name, value); - return this; - } - - @Override - public SlowHeaders setLong(CharSequence name, long value) { - delegate.setLong(name, value); - return this; - } - - @Override - public SlowHeaders setFloat(CharSequence name, float value) { - delegate.setFloat(name, value); - return this; - } - - @Override - public SlowHeaders setDouble(CharSequence name, double value) { - delegate.setDouble(name, value); - return this; - } - - @Override - public SlowHeaders setTimeMillis(CharSequence name, long value) { - delegate.setTimeMillis(name, value); - return this; - } - - @Override - public SlowHeaders set(Headers headers) { - delegate.set(headers); - return this; - } - - @Override - public SlowHeaders setAll(Headers headers) { - delegate.setAll(headers); - return this; - } - - @Override - public boolean remove(CharSequence name) { - return delegate.remove(name); - } - - @Override - public SlowHeaders clear() { - delegate.clear(); - return this; - } - - @Override - public Iterator> iterator() { - return delegate.iterator(); - } - } -} diff --git a/microbench/src/main/java/io/netty/microbench/headers/ReadOnlyHttp2HeadersBenchmark.java b/microbench/src/main/java/io/netty/microbench/headers/ReadOnlyHttp2HeadersBenchmark.java deleted file mode 100644 index d6331bfc7e..0000000000 --- a/microbench/src/main/java/io/netty/microbench/headers/ReadOnlyHttp2HeadersBenchmark.java +++ /dev/null @@ -1,138 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.headers; - -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpScheme; -import io.netty.handler.codec.http2.DefaultHttp2Headers; -import io.netty.handler.codec.http2.Http2Headers; -import io.netty.handler.codec.http2.ReadOnlyHttp2Headers; -import io.netty.microbench.util.AbstractMicrobenchmark; -import io.netty.util.AsciiString; -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.Scope; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.Threads; -import org.openjdk.jmh.annotations.Warmup; -import org.openjdk.jmh.infra.Blackhole; - -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.TimeUnit; - -@Threads(1) -@State(Scope.Benchmark) -@Fork(2) -@Warmup(iterations = 10) -@Measurement(iterations = 10) -@OutputTimeUnit(TimeUnit.NANOSECONDS) -public class ReadOnlyHttp2HeadersBenchmark extends AbstractMicrobenchmark { - private AsciiString[] headerNames; - private AsciiString[] headerValues; - - @Param({ "1", "5", "10", "20" }) - public int headerCount; - - private final AsciiString path = new AsciiString("/BigDynamicPayload"); - private final AsciiString authority = new AsciiString("io.netty"); - - @Setup - public void setUp() throws Exception { - headerNames = new AsciiString[headerCount]; - headerValues = new AsciiString[headerCount]; - for (int i = 0; i < headerCount; ++i) { - headerNames[i] = new AsciiString("key-" + i); - headerValues[i] = new AsciiString(UUID.randomUUID().toString()); - } - } - - @Benchmark - @BenchmarkMode(Mode.AverageTime) - public void defaultTrailers(Blackhole bh) { - Http2Headers headers = new DefaultHttp2Headers(false); - for (int i = 0; i < headerCount; ++i) { - headers.add(headerNames[i], headerValues[i]); - } - iterate(headers, bh); - } - - @Benchmark - @BenchmarkMode(Mode.AverageTime) - public void readOnlyTrailers(Blackhole bh) { - iterate(ReadOnlyHttp2Headers.trailers(false, buildPairs()), bh); - } - - @Benchmark - @BenchmarkMode(Mode.AverageTime) - public void defaultClientHeaders(Blackhole bh) { - Http2Headers headers = new DefaultHttp2Headers(false); - for (int i = 0; i < headerCount; ++i) { - headers.add(headerNames[i], headerValues[i]); - } - headers.method(HttpMethod.POST.asciiName()); - headers.scheme(HttpScheme.HTTPS.name()); - headers.path(path); - headers.authority(authority); - iterate(headers, bh); - } - - @Benchmark - @BenchmarkMode(Mode.AverageTime) - public void readOnlyClientHeaders(Blackhole bh) { - iterate(ReadOnlyHttp2Headers.clientHeaders(false, HttpMethod.POST.asciiName(), path, - HttpScheme.HTTPS.name(), authority, buildPairs()), bh); - } - - @Benchmark - @BenchmarkMode(Mode.AverageTime) - public void defaultServerHeaders(Blackhole bh) { - Http2Headers headers = new DefaultHttp2Headers(false); - for (int i = 0; i < headerCount; ++i) { - headers.add(headerNames[i], headerValues[i]); - } - headers.status(HttpResponseStatus.OK.codeAsText()); - iterate(headers, bh); - } - - @Benchmark - @BenchmarkMode(Mode.AverageTime) - public void readOnlyServerHeaders(Blackhole bh) { - iterate(ReadOnlyHttp2Headers.serverHeaders(false, HttpResponseStatus.OK.codeAsText(), buildPairs()), bh); - } - - private static void iterate(Http2Headers headers, Blackhole bh) { - for (Map.Entry entry : headers) { - bh.consume(entry); - } - } - - private AsciiString[] buildPairs() { - AsciiString[] headerPairs = new AsciiString[headerCount * 2]; - for (int i = 0, j = 0; i < headerCount; ++i, ++j) { - headerPairs[j] = headerNames[i]; - headerPairs[++j] = headerValues[i]; - } - return headerPairs; - } -} diff --git a/microbench/src/main/java/io/netty/microbench/headers/package-info.java b/microbench/src/main/java/io/netty/microbench/headers/package-info.java deleted file mode 100644 index e878dddb42..0000000000 --- a/microbench/src/main/java/io/netty/microbench/headers/package-info.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -/** - * Benchmarks for HTTP and HTTP/2 Headers. - */ -package io.netty.microbench.headers; diff --git a/microbench/src/main/java/io/netty/microbench/http/ClientCookieDecoderBenchmark.java b/microbench/src/main/java/io/netty/microbench/http/ClientCookieDecoderBenchmark.java deleted file mode 100644 index 1b9d2161d9..0000000000 --- a/microbench/src/main/java/io/netty/microbench/http/ClientCookieDecoderBenchmark.java +++ /dev/null @@ -1,36 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.http; - -import io.netty.handler.codec.http.cookie.ClientCookieDecoder; -import io.netty.handler.codec.http.cookie.Cookie; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.OutputTimeUnit; - -import java.util.concurrent.TimeUnit; - -@OutputTimeUnit(TimeUnit.SECONDS) -public class ClientCookieDecoderBenchmark { - - private static final String COOKIE_STRING = - "__Host-user_session_same_site=fgfMsM59vJTpZg88nxqKkIhgOt0ADF8LX8wjMMbtcb4IJMufWCnCcXORhbo9QMuyiybdtx; " + - "path=/; expires=Mon, 28 Nov 2016 13:56:01 GMT; secure; HttpOnly"; - - @Benchmark - public Cookie decodeCookieWithRfc1123ExpiresField() { - return ClientCookieDecoder.STRICT.decode(COOKIE_STRING); - } -} diff --git a/microbench/src/main/java/io/netty/microbench/http/HttpObjectEncoderBenchmark.java b/microbench/src/main/java/io/netty/microbench/http/HttpObjectEncoderBenchmark.java deleted file mode 100644 index 2d1e4b0d30..0000000000 --- a/microbench/src/main/java/io/netty/microbench/http/HttpObjectEncoderBenchmark.java +++ /dev/null @@ -1,118 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.http; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.PooledByteBufAllocator; -import io.netty.buffer.Unpooled; -import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.DefaultHttpHeaders; -import io.netty.handler.codec.http.DefaultHttpRequest; -import io.netty.handler.codec.http.DefaultLastHttpContent; -import io.netty.handler.codec.http.EmptyHttpHeaders; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpRequestEncoder; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http.LastHttpContent; -import io.netty.microbench.channel.EmbeddedChannelWriteReleaseHandlerContext; -import io.netty.microbench.util.AbstractMicrobenchmark; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.Fork; -import org.openjdk.jmh.annotations.Level; -import org.openjdk.jmh.annotations.Measurement; -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.annotations.Warmup; - -@State(Scope.Benchmark) -@Fork(1) -@Threads(1) -@Warmup(iterations = 5) -@Measurement(iterations = 10) -public class HttpObjectEncoderBenchmark extends AbstractMicrobenchmark { - private HttpRequestEncoder encoder; - private FullHttpRequest fullRequest; - private LastHttpContent lastContent; - private HttpRequest contentLengthRequest; - private HttpRequest chunkedRequest; - private ByteBuf content; - private ChannelHandlerContext context; - - @Param({ "true", "false" }) - public boolean pooledAllocator; - - @Setup(Level.Trial) - public void setup() { - byte[] bytes = new byte[256]; - content = Unpooled.buffer(bytes.length); - content.writeBytes(bytes); - ByteBuf testContent = Unpooled.unreleasableBuffer(content.asReadOnly()); - HttpHeaders headersWithChunked = new DefaultHttpHeaders(false); - headersWithChunked.add(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED); - HttpHeaders headersWithContentLength = new DefaultHttpHeaders(false); - headersWithContentLength.add(HttpHeaderNames.CONTENT_LENGTH, testContent.readableBytes()); - - fullRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/index", testContent, - headersWithContentLength, EmptyHttpHeaders.INSTANCE); - contentLengthRequest = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/index", - headersWithContentLength); - chunkedRequest = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/index", headersWithChunked); - lastContent = new DefaultLastHttpContent(testContent, false); - - encoder = new HttpRequestEncoder(); - context = new EmbeddedChannelWriteReleaseHandlerContext(pooledAllocator ? PooledByteBufAllocator.DEFAULT : - UnpooledByteBufAllocator.DEFAULT, encoder) { - @Override - protected void handleException(Throwable t) { - handleUnexpectedException(t); - } - }; - } - - @TearDown(Level.Trial) - public void teardown() { - content.release(); - content = null; - } - - @Benchmark - public void fullMessage() throws Exception { - encoder.write(context, fullRequest); - } - - @Benchmark - public void contentLength() throws Exception { - encoder.write(context, contentLengthRequest); - encoder.write(context, lastContent); - } - - @Benchmark - public void chunked() throws Exception { - encoder.write(context, chunkedRequest); - encoder.write(context, lastContent); - } -} diff --git a/microbench/src/main/java/io/netty/microbench/http/HttpRequestDecoderBenchmark.java b/microbench/src/main/java/io/netty/microbench/http/HttpRequestDecoderBenchmark.java deleted file mode 100644 index 1b1a0a54b2..0000000000 --- a/microbench/src/main/java/io/netty/microbench/http/HttpRequestDecoderBenchmark.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.http; - -import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.http.HttpRequestDecoder; -import io.netty.microbench.util.AbstractMicrobenchmark; -import io.netty.util.CharsetUtil; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Param; -import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.Warmup; - -/** - * This benchmark is based on HttpRequestDecoderTest class. - */ -@State(Scope.Benchmark) -@Warmup(iterations = 10) -@Measurement(iterations = 20) -public class HttpRequestDecoderBenchmark extends AbstractMicrobenchmark { - - private static final byte[] CONTENT_MIXED_DELIMITERS = createContent("\r\n", "\n"); - private static final int CONTENT_LENGTH = 120; - - @Param({ "2", "4", "8", "16", "32" }) - public int step; - - private static byte[] createContent(String... lineDelimiters) { - String lineDelimiter; - String lineDelimiter2; - if (lineDelimiters.length == 2) { - lineDelimiter = lineDelimiters[0]; - lineDelimiter2 = lineDelimiters[1]; - } else { - lineDelimiter = lineDelimiters[0]; - lineDelimiter2 = lineDelimiters[0]; - } - // This GET request is incorrect but it does not matter for HttpRequestDecoder. - // It used only to get a long request. - return ("GET /some/path?foo=bar&wibble=eek HTTP/1.1" + "\r\n" + - "Upgrade: WebSocket" + lineDelimiter2 + - "Connection: Upgrade" + lineDelimiter + - "Host: localhost" + lineDelimiter2 + - "Referer: http://www.site.ru/index.html" + lineDelimiter + - "User-Agent: Mozilla/5.0 (X11; U; Linux i686; ru; rv:1.9b5) Gecko/2008050509 Firefox/3.0b5" + - lineDelimiter2 + - "Accept: text/html" + lineDelimiter + - "Cookie: income=1" + lineDelimiter2 + - "Origin: http://localhost:8080" + lineDelimiter + - "Sec-WebSocket-Key1: 10 28 8V7 8 48 0" + lineDelimiter2 + - "Sec-WebSocket-Key2: 8 Xt754O3Q3QW 0 _60" + lineDelimiter + - "Content-Type: application/x-www-form-urlencoded" + lineDelimiter2 + - "Content-Length: " + CONTENT_LENGTH + lineDelimiter + - "\r\n" + - "1234567890\r\n" + - "1234567890\r\n" + - "1234567890\r\n" + - "1234567890\r\n" + - "1234567890\r\n" + - "1234567890\r\n" + - "1234567890\r\n" + - "1234567890\r\n" + - "1234567890\r\n" + - "1234567890\r\n" - ).getBytes(CharsetUtil.US_ASCII); - } - - @Benchmark - public void testDecodeWholeRequestInMultipleStepsMixedDelimiters() { - testDecodeWholeRequestInMultipleSteps(CONTENT_MIXED_DELIMITERS, step); - } - - private static void testDecodeWholeRequestInMultipleSteps(byte[] content, int fragmentSize) { - final EmbeddedChannel channel = new EmbeddedChannel(new HttpRequestDecoder()); - - final int headerLength = content.length - CONTENT_LENGTH; - - // split up the header - for (int a = 0; a < headerLength;) { - int amount = fragmentSize; - if (a + amount > headerLength) { - amount = headerLength - a; - } - - // if header is done it should produce an HttpRequest - channel.writeInbound(Unpooled.wrappedBuffer(content, a, amount).asReadOnly()); - a += amount; - } - - for (int i = CONTENT_LENGTH; i > 0; i --) { - // Should produce HttpContent - channel.writeInbound(Unpooled.wrappedBuffer(content, content.length - i, 1).asReadOnly()); - } - } -} diff --git a/microbench/src/main/java/io/netty/microbench/http/package-info.java b/microbench/src/main/java/io/netty/microbench/http/package-info.java deleted file mode 100644 index 9928be188b..0000000000 --- a/microbench/src/main/java/io/netty/microbench/http/package-info.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -/** - * Benchmarks for {@link io.netty.handler.codec.http}. - */ -package io.netty.microbench.http; diff --git a/microbench/src/main/java/io/netty/microbench/http2/NoPriorityByteDistributionBenchmark.java b/microbench/src/main/java/io/netty/microbench/http2/NoPriorityByteDistributionBenchmark.java deleted file mode 100644 index 458c8b6171..0000000000 --- a/microbench/src/main/java/io/netty/microbench/http2/NoPriorityByteDistributionBenchmark.java +++ /dev/null @@ -1,293 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.microbench.http2; - -import io.netty.buffer.PooledByteBufAllocator; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http2.DefaultHttp2Connection; -import io.netty.handler.codec.http2.DefaultHttp2RemoteFlowController; -import io.netty.handler.codec.http2.Http2Connection; -import io.netty.handler.codec.http2.Http2ConnectionHandler; -import io.netty.handler.codec.http2.Http2ConnectionHandlerBuilder; -import io.netty.handler.codec.http2.Http2Exception; -import io.netty.handler.codec.http2.Http2FrameAdapter; -import io.netty.handler.codec.http2.Http2RemoteFlowController; -import io.netty.handler.codec.http2.Http2Stream; -import io.netty.handler.codec.http2.Http2StreamVisitor; -import io.netty.handler.codec.http2.StreamByteDistributor; -import io.netty.handler.codec.http2.UniformStreamByteDistributor; -import io.netty.handler.codec.http2.WeightedFairQueueByteDistributor; -import io.netty.microbench.channel.EmbeddedChannelWriteReleaseHandlerContext; -import io.netty.microbench.util.AbstractMicrobenchmark; -import org.openjdk.jmh.annotations.AuxCounters; -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; -import org.openjdk.jmh.annotations.Threads; - -/** - * Benchmark to compare stream byte distribution algorithms when priorities are identical for - * all streams. - */ -@Threads(1) -@State(Scope.Benchmark) -public class NoPriorityByteDistributionBenchmark extends AbstractMicrobenchmark { - public enum Algorithm { - WFQ, - UNIFORM - } - - @Param({ "100", "10000" }) - private int numStreams; - - @Param({ "1024", "65536", "1048576" }) - private int windowSize; - - @Param - private Algorithm algorithm; - - private Http2Connection connection; - private Http2Connection.PropertyKey dataRefresherKey; - private Http2RemoteFlowController controller; - private StreamByteDistributor distributor; - private AdditionalCounters counters; - private ChannelHandlerContext ctx; - - public NoPriorityByteDistributionBenchmark() { - super(true); - } - - /** - * Additional counters for a single iteration. - */ - @AuxCounters - @State(Scope.Thread) - public static class AdditionalCounters { - int minWriteSize = Integer.MAX_VALUE; - int maxWriteSize = Integer.MIN_VALUE; - long totalBytes; - long numWrites; - int invocations; - - public int minWriteSize() { - return minWriteSize; - } - - public int avgWriteSize() { - return (int) (totalBytes / numWrites); - } - - public int maxWriteSize() { - return maxWriteSize; - } - } - - private final Http2StreamVisitor invocationVisitor = stream -> { - // Restore the connection window. - resetWindow(stream); - - // Restore the data to each stream. - dataRefresher(stream).refreshData(); - return true; - }; - - @TearDown(Level.Trial) - public void tearDownTrial() throws Exception { - ctx.close(); - } - - @Setup(Level.Trial) - public void setupTrial() throws Exception { - connection = new DefaultHttp2Connection(false); - dataRefresherKey = connection.newKey(); - - // Create the flow controller - switch (algorithm) { - case WFQ: - distributor = new WeightedFairQueueByteDistributor(connection, 0); - break; - case UNIFORM: - distributor = new UniformStreamByteDistributor(connection); - break; - } - controller = new DefaultHttp2RemoteFlowController(connection, new ByteCounter(distributor)); - connection.remote().flowController(controller); - Http2ConnectionHandler handler = new Http2ConnectionHandlerBuilder() - .encoderEnforceMaxConcurrentStreams(false).validateHeaders(false) - .frameListener(new Http2FrameAdapter()) - .connection(connection) - .build(); - ctx = new EmbeddedChannelWriteReleaseHandlerContext(PooledByteBufAllocator.DEFAULT, handler) { - @Override - protected void handleException(Throwable t) { - handleUnexpectedException(t); - } - }; - handler.handlerAdded(ctx); - handler.channelActive(ctx); - - // Create the streams, each initialized with MAX_INT bytes. - for (int i = 0; i < numStreams; ++i) { - Http2Stream stream = connection.local().createStream(toStreamId(i), false); - addData(stream, Integer.MAX_VALUE); - stream.setProperty(dataRefresherKey, new DataRefresher(stream)); - } - } - - @Setup(Level.Invocation) - public void setupInvocation() throws Http2Exception { - resetWindow(connection.connectionStream()); - connection.forEachActiveStream(invocationVisitor); - } - - @Benchmark - public void write(AdditionalCounters counters) throws Http2Exception { - // Set up for this invocation. Doing this in the benchmark method since this - // seems to throw off the counters when run as a setup step for the invocation. - this.counters = counters; - counters.invocations++; - - // Now run the benchmark method. - controller.writePendingBytes(); - } - - private void resetWindow(Http2Stream stream) throws Http2Exception { - controller.incrementWindowSize(stream, windowSize - controller.windowSize(stream)); - } - - private DataRefresher dataRefresher(Http2Stream stream) { - return (DataRefresher) stream.getProperty(dataRefresherKey); - } - - private void addData(Http2Stream stream, final int dataSize) { - controller.addFlowControlled(stream, new Http2RemoteFlowController.FlowControlled() { - private int size = dataSize; - - @Override - public int size() { - return size; - } - - @Override - public void error(ChannelHandlerContext ctx, Throwable cause) { - cause.printStackTrace(); - } - - @Override - public void writeComplete() { - // Don't care. - } - - @Override - public void write(ChannelHandlerContext ctx, int allowedBytes) { - size -= allowedBytes; - } - - @Override - public boolean merge(ChannelHandlerContext ctx, - Http2RemoteFlowController.FlowControlled next) { - int nextSize = next.size(); - if (Integer.MAX_VALUE - nextSize < size) { - // Disallow merge to avoid integer overflow. - return false; - } - - // Merge. - size += nextSize; - return true; - } - }); - } - - private static int toStreamId(int i) { - return 2 * i + 1; - } - - private final class DataRefresher { - private final Http2Stream stream; - private int data; - - private DataRefresher(Http2Stream stream) { - this.stream = stream; - } - - void add(int data) { - this.data += data; - } - - void refreshData() { - if (data > 0) { - addData(stream, data); - data = 0; - } - } - } - - private final class ByteCounter implements StreamByteDistributor { - private final StreamByteDistributor delegate; - - private ByteCounter(StreamByteDistributor delegate) { - this.delegate = delegate; - } - - @Override - public void updateStreamableBytes(StreamState state) { - delegate.updateStreamableBytes(state); - } - - @Override - public void updateDependencyTree(int childStreamId, int parentStreamId, short weight, boolean exclusive) { - delegate.updateDependencyTree(childStreamId, parentStreamId, weight, exclusive); - } - - @Override - public boolean distribute(int maxBytes, Writer writer) throws Http2Exception { - return delegate.distribute(maxBytes, new CountingWriter(writer)); - } - - private final class CountingWriter implements Writer { - private final Writer delegate; - - private CountingWriter(Writer delegate) { - this.delegate = delegate; - } - - @Override - public void write(Http2Stream stream, int numBytes) { - if (numBytes > 0) { - // Add the data to the refresher so that it can be given back to the - // stream at the end of the iteration. - DataRefresher refresher = dataRefresher(stream); - refresher.add(numBytes); - - ++counters.numWrites; - counters.totalBytes += numBytes; - if (numBytes < counters.minWriteSize) { - counters.minWriteSize = numBytes; - } - if (numBytes > counters.maxWriteSize) { - counters.maxWriteSize = numBytes; - } - } - - delegate.write(stream, numBytes); - } - } - } -} diff --git a/microbench/src/main/java/io/netty/microbench/http2/NoopHttp2LocalFlowController.java b/microbench/src/main/java/io/netty/microbench/http2/NoopHttp2LocalFlowController.java deleted file mode 100644 index e0040db5e4..0000000000 --- a/microbench/src/main/java/io/netty/microbench/http2/NoopHttp2LocalFlowController.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.microbench.http2; - -import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_INITIAL_WINDOW_SIZE; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http2.Http2Exception; -import io.netty.handler.codec.http2.Http2FrameWriter; -import io.netty.handler.codec.http2.Http2LocalFlowController; -import io.netty.handler.codec.http2.Http2Stream; - -public final class NoopHttp2LocalFlowController implements Http2LocalFlowController { - public static final NoopHttp2LocalFlowController INSTANCE = new NoopHttp2LocalFlowController(); - - private NoopHttp2LocalFlowController() { } - - @Override - public void initialWindowSize(int newWindowSize) throws Http2Exception { - } - - @Override - public int initialWindowSize() { - return MAX_INITIAL_WINDOW_SIZE; - } - - @Override - public int windowSize(Http2Stream stream) { - return MAX_INITIAL_WINDOW_SIZE; - } - - @Override - public int initialWindowSize(Http2Stream stream) { - return MAX_INITIAL_WINDOW_SIZE; - } - - @Override - public void incrementWindowSize(Http2Stream stream, int delta) throws Http2Exception { - } - - @Override - public void receiveFlowControlledFrame(Http2Stream stream, ByteBuf data, int padding, boolean endOfStream) - throws Http2Exception { - } - - @Override - public boolean consumeBytes(Http2Stream stream, int numBytes) throws Http2Exception { - return false; - } - - @Override - public int unconsumedBytes(Http2Stream stream) { - return 0; - } - - @Override - public void channelHandlerContext(ChannelHandlerContext ctx) throws Http2Exception { - } - - @Override - public Http2LocalFlowController frameWriter(Http2FrameWriter frameWriter) { - return this; - } -} diff --git a/microbench/src/main/java/io/netty/microbench/http2/NoopHttp2RemoteFlowController.java b/microbench/src/main/java/io/netty/microbench/http2/NoopHttp2RemoteFlowController.java deleted file mode 100644 index 03c8032a07..0000000000 --- a/microbench/src/main/java/io/netty/microbench/http2/NoopHttp2RemoteFlowController.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.microbench.http2; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http2.Http2Exception; -import io.netty.handler.codec.http2.Http2RemoteFlowController; -import io.netty.handler.codec.http2.Http2Stream; - -import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_INITIAL_WINDOW_SIZE; - -public final class NoopHttp2RemoteFlowController implements Http2RemoteFlowController { - public static final NoopHttp2RemoteFlowController INSTANCE = new NoopHttp2RemoteFlowController(); - private ChannelHandlerContext ctx; - - private NoopHttp2RemoteFlowController() { } - - @Override - public void initialWindowSize(int newWindowSize) throws Http2Exception { - } - - @Override - public int initialWindowSize() { - return MAX_INITIAL_WINDOW_SIZE; - } - - @Override - public int windowSize(Http2Stream stream) { - return MAX_INITIAL_WINDOW_SIZE; - } - - @Override - public boolean isWritable(Http2Stream stream) { - return true; - } - - @Override - public void incrementWindowSize(Http2Stream stream, int delta) throws Http2Exception { - } - - @Override - public void writePendingBytes() throws Http2Exception { - } - - @Override - public void listener(Listener listener) { - } - - @Override - public void addFlowControlled(Http2Stream stream, FlowControlled payload) { - // Don't check size beforehand because Headers payload returns 0 all the time. - do { - payload.write(ctx, MAX_INITIAL_WINDOW_SIZE); - } while (payload.size() > 0); - } - - @Override - public boolean hasFlowControlled(Http2Stream stream) { - return false; - } - - @Override - public void channelHandlerContext(ChannelHandlerContext ctx) throws Http2Exception { - this.ctx = ctx; - } - - @Override - public ChannelHandlerContext channelHandlerContext() { - return ctx; - } - - @Override - public void channelWritabilityChanged() throws Http2Exception { - } - - @Override - public void updateDependencyTree(int childStreamId, int parentStreamId, short weight, boolean exclusive) { - } -} diff --git a/microbench/src/main/java/io/netty/microbench/http2/package-info.java b/microbench/src/main/java/io/netty/microbench/http2/package-info.java deleted file mode 100644 index 3fd7a41810..0000000000 --- a/microbench/src/main/java/io/netty/microbench/http2/package-info.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -/** - * Benchmarks for {@link io.netty.handler.codec.http2}. - */ -package io.netty.microbench.http2; diff --git a/microbench/src/main/java/io/netty/microbench/internal/EscapeCsvBenchmark.java b/microbench/src/main/java/io/netty/microbench/internal/EscapeCsvBenchmark.java deleted file mode 100644 index a5e2dbce04..0000000000 --- a/microbench/src/main/java/io/netty/microbench/internal/EscapeCsvBenchmark.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.internal; - -import io.netty.microbench.util.AbstractMicrobenchmark; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Param; -import org.openjdk.jmh.annotations.Threads; -import org.openjdk.jmh.annotations.Warmup; -import org.openjdk.jmh.runner.options.ChainedOptionsBuilder; - -import java.util.concurrent.TimeUnit; - -import static io.netty.util.internal.StringUtil.*; -import static java.util.Objects.requireNonNull; - -@Threads(1) -@Warmup(iterations = 3) -@Measurement(iterations = 3) -@OutputTimeUnit(TimeUnit.MICROSECONDS) -public class EscapeCsvBenchmark extends AbstractMicrobenchmark { - - private static final String value1024; - private static final String value1024commaAtEnd; - static { - StringBuilder s1024 = new StringBuilder(1024); - while (s1024.length() < 1024) { - s1024.append('A' + s1024.length() % 10); - } - value1024 = s1024.toString(); - value1024commaAtEnd = value1024 + ','; - } - - @Param("netty") - private String value; - - @Override - protected ChainedOptionsBuilder newOptionsBuilder() throws Exception { - return super.newOptionsBuilder() - .param("value", "netty") - .param("value", "\"123\"", "need\"escape", "need,quotes", " trim-me ", "short-comma-ended,") - .param("value", value1024) - .param("value", value1024commaAtEnd); - } - - private static CharSequence escapeCsvOld(CharSequence value, boolean trimWhiteSpace) { - int length = requireNonNull(value, "value").length(); - if (length == 0) { - return value; - } - - int start = 0; - int last = length - 1; - boolean trimmed = false; - if (trimWhiteSpace) { - start = indexOfFirstNonOwsChar(value, length); - if (start == length) { - return EMPTY_STRING; - } - last = indexOfLastNonOwsChar(value, start, length); - trimmed = start > 0 || last < length - 1; - if (trimmed) { - length = last - start + 1; - } - } - - StringBuilder result = new StringBuilder(length + 7); - boolean quoted = isDoubleQuote(value.charAt(start)) && isDoubleQuote(value.charAt(last)) && length != 1; - boolean foundSpecialCharacter = false; - boolean escapedDoubleQuote = false; - for (int i = start; i <= last; i++) { - char current = value.charAt(i); - switch (current) { - case DOUBLE_QUOTE: - if (i == start || i == last) { - if (!quoted) { - result.append(DOUBLE_QUOTE); - } else { - continue; - } - } else { - boolean isNextCharDoubleQuote = isDoubleQuote(value.charAt(i + 1)); - if (!isDoubleQuote(value.charAt(i - 1)) && - (!isNextCharDoubleQuote || i + 1 == last)) { - result.append(DOUBLE_QUOTE); - escapedDoubleQuote = true; - } - break; - } - case LINE_FEED: - case CARRIAGE_RETURN: - case COMMA: - foundSpecialCharacter = true; - } - result.append(current); - } - - if (escapedDoubleQuote || foundSpecialCharacter && !quoted) { - return quote(result); - } - if (trimmed) { - return quoted? quote(result) : result; - } - return value; - } - - private static StringBuilder quote(StringBuilder builder) { - return builder.insert(0, DOUBLE_QUOTE).append(DOUBLE_QUOTE); - } - - private static boolean isDoubleQuote(char c) { - return c == DOUBLE_QUOTE; - } - - private static int indexOfFirstNonOwsChar(CharSequence value, int length) { - int i = 0; - while (i < length && isOws(value.charAt(i))) { - i++; - } - return i; - } - - private static int indexOfLastNonOwsChar(CharSequence value, int start, int length) { - int i = length - 1; - while (i > start && isOws(value.charAt(i))) { - i--; - } - return i; - } - - private static boolean isOws(char c) { - return c == SPACE || c == TAB; - } - - @Benchmark - public CharSequence escapeCsvOld() { - return escapeCsvOld(value, true); - } - - @Benchmark - public CharSequence escapeCsvNew() { - return escapeCsv(value, true); - } - -} diff --git a/microbench/src/main/java/io/netty/microbench/internal/PlatformDependentBenchmark.java b/microbench/src/main/java/io/netty/microbench/internal/PlatformDependentBenchmark.java deleted file mode 100644 index d3d59b64b7..0000000000 --- a/microbench/src/main/java/io/netty/microbench/internal/PlatformDependentBenchmark.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.internal; - -import io.netty.microbench.util.AbstractMicrobenchmark; -import io.netty.util.internal.PlatformDependent; - -import java.util.Arrays; -import java.util.concurrent.TimeUnit; - -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.Level; -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.Threads; - -@Threads(1) -@State(Scope.Benchmark) -public class PlatformDependentBenchmark extends AbstractMicrobenchmark { - - @Param({ "10", "50", "100", "1000", "10000", "100000" }) - private int size; - private byte[] bytes1; - private byte[] bytes2; - - @Setup(Level.Trial) - public void setup() { - bytes1 = new byte[size]; - bytes2 = new byte[size]; - for (int i = 0; i < size; i++) { - bytes1[i] = bytes2[i] = (byte) i; - } - } - - @Benchmark - @BenchmarkMode(Mode.AverageTime) - @OutputTimeUnit(TimeUnit.NANOSECONDS) - public boolean unsafeBytesEqual() { - return PlatformDependent.equals(bytes1, 0, bytes2, 0, bytes1.length); - } - - @Benchmark - @BenchmarkMode(Mode.AverageTime) - @OutputTimeUnit(TimeUnit.NANOSECONDS) - public boolean arraysBytesEqual() { - return Arrays.equals(bytes1, bytes2); - } -} diff --git a/microbench/src/main/java/io/netty/microbench/internal/PrivilegedSocketOperationsBenchmark.java b/microbench/src/main/java/io/netty/microbench/internal/PrivilegedSocketOperationsBenchmark.java deleted file mode 100644 index 5032c273e0..0000000000 --- a/microbench/src/main/java/io/netty/microbench/internal/PrivilegedSocketOperationsBenchmark.java +++ /dev/null @@ -1,181 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.internal; - -import io.netty.microbench.util.AbstractMicrobenchmark; -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.Scope; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.TearDown; - -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.channels.ServerSocketChannel; -import java.security.AccessController; -import java.security.NoSuchAlgorithmException; -import java.security.Policy; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; -import java.security.URIParameter; -import java.util.concurrent.TimeUnit; - -@BenchmarkMode(Mode.Throughput) -@OutputTimeUnit(TimeUnit.SECONDS) -public class PrivilegedSocketOperationsBenchmark extends AbstractMicrobenchmark { - - @State(Scope.Benchmark) - public static class SecurityManagerInstalled { - - @Setup - public void setup() throws IOException, NoSuchAlgorithmException, URISyntaxException { - final URI policyFile = PrivilegedSocketOperationsBenchmark.class.getResource("/jmh-security.policy") - .toURI(); - Policy.setPolicy(Policy.getInstance("JavaPolicy", new URIParameter(policyFile))); - System.setSecurityManager(new SecurityManager()); - } - - @TearDown - public void tearDown() throws IOException { - System.setSecurityManager(null); - } - } - - @State(Scope.Benchmark) - public static class SecurityManagerEmpty { - - @Setup - public void setup() throws IOException, NoSuchAlgorithmException, URISyntaxException { - System.setSecurityManager(null); - } - } - - @Benchmark - public ServerSocketChannel testWithSMNoPrivileged(final SecurityManagerInstalled sm) throws IOException { - final ServerSocketChannel ssc = ServerSocketChannel.open(); - ssc.socket().bind(null); - ssc.configureBlocking(false); - ssc.accept(); - ssc.close(); - return ssc; - } - - @Benchmark - public ServerSocketChannel testWithSM(final SecurityManagerInstalled sm) throws IOException { - try { - final ServerSocketChannel ssc = AccessController.doPrivileged( - (PrivilegedExceptionAction) () -> { - final ServerSocketChannel ssc1 = ServerSocketChannel.open(); - ssc1.socket().bind(null); - ssc1.configureBlocking(false); - ssc1.accept(); - return ssc1; - }); - ssc.close(); - return ssc; - } catch (final PrivilegedActionException e) { - throw (IOException) e.getCause(); - } - } - - @Benchmark - public ServerSocketChannel testWithSMWithNullCheck(final SecurityManagerInstalled sm) throws IOException { - if (System.getSecurityManager() != null) { - try { - final ServerSocketChannel ssc = AccessController.doPrivileged( - (PrivilegedExceptionAction) () -> { - final ServerSocketChannel ssc1 = ServerSocketChannel.open(); - ssc1.socket().bind(null); - ssc1.configureBlocking(false); - ssc1.accept(); - return ssc1; - }); - ssc.close(); - return ssc; - } catch (final PrivilegedActionException e) { - throw (IOException) e.getCause(); - } - } else { - // this should never happen during benchmarking, but we write the correct code here - final ServerSocketChannel ssc = ServerSocketChannel.open(); - ssc.socket().bind(null); - ssc.configureBlocking(false); - ssc.accept(); - ssc.close(); - return ssc; - } - } - - @Benchmark - public ServerSocketChannel testWithoutSMNoPrivileged(final SecurityManagerEmpty sm) throws IOException { - final ServerSocketChannel ssc = ServerSocketChannel.open(); - ssc.socket().bind(null); - ssc.configureBlocking(false); - ssc.accept(); - ssc.close(); - return ssc; - } - - @Benchmark - public ServerSocketChannel testWithoutSM(final SecurityManagerEmpty sm) throws IOException { - try { - final ServerSocketChannel ssc = AccessController.doPrivileged( - (PrivilegedExceptionAction) () -> { - final ServerSocketChannel ssc1 = ServerSocketChannel.open(); - ssc1.socket().bind(null); - ssc1.configureBlocking(false); - ssc1.accept(); - return ssc1; - }); - ssc.close(); - return ssc; - } catch (final PrivilegedActionException e) { - throw (IOException) e.getCause(); - } - } - - @Benchmark - public ServerSocketChannel testWithoutSMWithNullCheck(final SecurityManagerEmpty sm) throws IOException { - if (System.getSecurityManager() != null) { - // this should never happen during benchmarking, but we write the correct code here - try { - final ServerSocketChannel ssc = AccessController.doPrivileged( - (PrivilegedExceptionAction) () -> { - final ServerSocketChannel ssc1 = ServerSocketChannel.open(); - ssc1.socket().bind(null); - ssc1.configureBlocking(false); - ssc1.accept(); - return ssc1; - }); - ssc.close(); - return ssc; - } catch (final PrivilegedActionException e) { - throw (IOException) e.getCause(); - } - } else { - final ServerSocketChannel ssc = ServerSocketChannel.open(); - ssc.socket().bind(null); - ssc.configureBlocking(false); - ssc.accept(); - ssc.close(); - return ssc; - } - } -} diff --git a/microbench/src/main/java/io/netty/microbench/internal/RecyclableArrayListBenchmark.java b/microbench/src/main/java/io/netty/microbench/internal/RecyclableArrayListBenchmark.java deleted file mode 100644 index f287d39a07..0000000000 --- a/microbench/src/main/java/io/netty/microbench/internal/RecyclableArrayListBenchmark.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.internal; - -import io.netty.microbench.util.AbstractMicrobenchmark; -import io.netty.util.internal.RecyclableArrayList; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Param; -import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.Threads; - -/** - * This class benchmarks different allocators with different allocation sizes. - */ -@State(Scope.Benchmark) -@Threads(4) -@Measurement(iterations = 10, batchSize = 100) -public class RecyclableArrayListBenchmark extends AbstractMicrobenchmark { - - @Param({ "00000", "00256", "01024", "04096", "16384", "65536" }) - public int size; - - @Benchmark - public boolean recycleSameThread() { - RecyclableArrayList list = RecyclableArrayList.newInstance(size); - return list.recycle(); - } -} diff --git a/microbench/src/main/java/io/netty/microbench/internal/UnitializedArrayBenchmark.java b/microbench/src/main/java/io/netty/microbench/internal/UnitializedArrayBenchmark.java deleted file mode 100644 index 19bd2bc397..0000000000 --- a/microbench/src/main/java/io/netty/microbench/internal/UnitializedArrayBenchmark.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.internal; - -import io.netty.microbench.util.AbstractMicrobenchmark; -import io.netty.util.internal.PlatformDependent; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.Fork; -import org.openjdk.jmh.annotations.Level; -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.Scope; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.Warmup; - -import java.util.concurrent.TimeUnit; - -@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS) -@Measurement(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS) -@Fork(2) -@BenchmarkMode(Mode.AverageTime) -@OutputTimeUnit(TimeUnit.NANOSECONDS) -@State(Scope.Benchmark) -public class UnitializedArrayBenchmark extends AbstractMicrobenchmark { - - @Param({ "1", "10", "100", "1000", "10000", "100000" }) - private int size; - - @Setup(Level.Trial) - public void setupTrial() { - if (PlatformDependent.javaVersion() < 9) { - throw new IllegalStateException("Needs Java9"); - } - if (!PlatformDependent.hasUnsafe()) { - throw new IllegalStateException("Needs Unsafe"); - } - } - - @Override - protected String[] jvmArgs() { - // Ensure we minimize the GC overhead for this benchmark and also open up required package. - // See also https://shipilev.net/jvm-anatomy-park/7-initialization-costs/ - return new String[] { "-XX:+UseParallelOldGC", "-Xmx8g", "-Xms8g", - "-Xmn6g", "--add-opens", "java.base/jdk.internal.misc=ALL-UNNAMED" }; - } - - @Benchmark - public byte[] allocateInitializedByteArray() { - return new byte[size]; - } - - @Benchmark - public byte[] allocateUninitializedByteArray() { - return PlatformDependent.allocateUninitializedArray(size); - } -} diff --git a/microbench/src/main/java/io/netty/microbench/internal/package-info.java b/microbench/src/main/java/io/netty/microbench/internal/package-info.java deleted file mode 100644 index 4dd0cc297c..0000000000 --- a/microbench/src/main/java/io/netty/microbench/internal/package-info.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -/** - * Benchmarks for {@link io.netty.util.internal}. - */ -package io.netty.microbench.internal; diff --git a/microbench/src/main/java/io/netty/microbench/redis/RedisEncoderBenchmark.java b/microbench/src/main/java/io/netty/microbench/redis/RedisEncoderBenchmark.java deleted file mode 100644 index 14a8950e4b..0000000000 --- a/microbench/src/main/java/io/netty/microbench/redis/RedisEncoderBenchmark.java +++ /dev/null @@ -1,94 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.redis; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.PooledByteBufAllocator; -import io.netty.buffer.Unpooled; -import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.redis.ArrayRedisMessage; -import io.netty.handler.codec.redis.FullBulkStringRedisMessage; -import io.netty.handler.codec.redis.RedisEncoder; -import io.netty.handler.codec.redis.RedisMessage; -import io.netty.microbench.channel.EmbeddedChannelWriteReleaseHandlerContext; -import io.netty.microbench.util.AbstractMicrobenchmark; -import io.netty.util.concurrent.Future; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.Fork; -import org.openjdk.jmh.annotations.Level; -import org.openjdk.jmh.annotations.Measurement; -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.annotations.Warmup; - -import java.util.ArrayList; -import java.util.List; - -@State(Scope.Benchmark) -@Fork(1) -@Threads(1) -@Warmup(iterations = 5) -@Measurement(iterations = 5) -public class RedisEncoderBenchmark extends AbstractMicrobenchmark { - private RedisEncoder encoder; - private ByteBuf content; - private ChannelHandlerContext context; - private ArrayRedisMessage redisArray; - - @Param({ "true", "false" }) - public boolean pooledAllocator; - - @Param({ "50", "200", "1000" }) - public int arraySize; - - @Setup(Level.Trial) - public void setup() { - byte[] bytes = new byte[256]; - content = Unpooled.buffer(bytes.length); - content.writeBytes(bytes); - ByteBuf testContent = Unpooled.unreleasableBuffer(content.asReadOnly()); - - List rList = new ArrayList<>(arraySize); - for (int i = 0; i < arraySize; ++i) { - rList.add(new FullBulkStringRedisMessage(testContent)); - } - redisArray = new ArrayRedisMessage(rList); - encoder = new RedisEncoder(); - context = new EmbeddedChannelWriteReleaseHandlerContext(pooledAllocator ? PooledByteBufAllocator.DEFAULT : - UnpooledByteBufAllocator.DEFAULT, encoder) { - @Override - protected void handleException(Throwable t) { - handleUnexpectedException(t); - } - }; - } - - @TearDown(Level.Trial) - public void teardown() { - content.release(); - content = null; - } - - @Benchmark - public Future writeArray() { - return encoder.write(context, redisArray.retain()); - } -} diff --git a/microbench/src/main/java/io/netty/microbench/redis/package-info.java b/microbench/src/main/java/io/netty/microbench/redis/package-info.java deleted file mode 100644 index 637f5194d6..0000000000 --- a/microbench/src/main/java/io/netty/microbench/redis/package-info.java +++ /dev/null @@ -1,19 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -/** - * Benchmarks for {@link io.netty.handler.codec.redis}. - */ -package io.netty.microbench.redis; diff --git a/microbench/src/main/java/io/netty/microbench/search/ByteBufType.java b/microbench/src/main/java/io/netty/microbench/search/ByteBufType.java deleted file mode 100644 index 74dd43997b..0000000000 --- a/microbench/src/main/java/io/netty/microbench/search/ByteBufType.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.search; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.CompositeByteBuf; -import io.netty.buffer.Unpooled; - -public enum ByteBufType { - HEAP { - @Override - ByteBuf newBuffer(byte[] bytes) { - return Unpooled.wrappedBuffer(bytes, 0, bytes.length); - } - }, - COMPOSITE { - @Override - ByteBuf newBuffer(byte[] bytes) { - CompositeByteBuf buf = Unpooled.compositeBuffer(); - int length = bytes.length; - int offset = 0; - int capacity = length / 8; // 8 buffers per composite - - while (length > 0) { - buf.addComponent(true, Unpooled.wrappedBuffer(bytes, offset, Math.min(length, capacity))); - length -= capacity; - offset += capacity; - } - return buf; - } - }, - DIRECT { - @Override - ByteBuf newBuffer(byte[] bytes) { - return Unpooled.directBuffer(bytes.length).writeBytes(bytes); - } - }; - abstract ByteBuf newBuffer(byte[] bytes); -} diff --git a/microbench/src/main/java/io/netty/microbench/search/SearchBenchmark.java b/microbench/src/main/java/io/netty/microbench/search/SearchBenchmark.java deleted file mode 100644 index 79c227eb39..0000000000 --- a/microbench/src/main/java/io/netty/microbench/search/SearchBenchmark.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.search; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.buffer.Unpooled; -import io.netty.buffer.search.AbstractMultiSearchProcessorFactory; -import io.netty.buffer.search.AbstractSearchProcessorFactory; -import io.netty.buffer.search.SearchProcessorFactory; -import io.netty.microbench.util.AbstractMicrobenchmark; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.CompilerControl; -import org.openjdk.jmh.annotations.CompilerControl.Mode; -import org.openjdk.jmh.annotations.Fork; -import org.openjdk.jmh.annotations.Measurement; -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 java.util.Arrays; -import java.util.Random; -import java.util.concurrent.TimeUnit; - -@OutputTimeUnit(TimeUnit.MILLISECONDS) -@Warmup(iterations = 5) -@Measurement(iterations = 5) -@Fork(1) -public class SearchBenchmark extends AbstractMicrobenchmark { - - private static final long SEED = 123; - - public enum Input { - RANDOM_256B { - @Override - byte[] getNeedle(Random rnd) { - return new byte[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' }; - } - @Override - byte[] getHaystack(Random rnd) { - return randomBytes(rnd, 256, ' ', 127); - } - }, - RANDOM_2KB { - @Override - byte[] getNeedle(Random rnd) { - return new byte[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' }; - } - @Override - byte[] getHaystack(Random rnd) { - return randomBytes(rnd, 2048, ' ', 127); - } - }, - PREDICTABLE { - @Override - byte[] getNeedle(Random rnd) { - // all 0s - return new byte[64]; - } - @Override - byte[] getHaystack(Random rnd) { - // no 0s except in the very end - byte[] bytes = randomBytes(rnd, 2048, 1, 255); - Arrays.fill(bytes, bytes.length - 64, bytes.length, (byte) 0); - return bytes; - } - }, - UNPREDICTABLE { - @Override - byte[] getNeedle(Random rnd) { - return randomBytes(rnd, 64, 0, 1); - } - @Override - byte[] getHaystack(Random rnd) { - return randomBytes(rnd, 2048, 0, 1); - } - }, - WORST_CASE { // Bitap will fail on it because the needle is >64 bytes long - @Override - byte[] getNeedle(Random rnd) { - // aa(...)aab - byte[] needle = new byte[1024]; - Arrays.fill(needle, (byte) 'a'); - needle[needle.length - 1] = 'b'; - return needle; - } - @Override - byte[] getHaystack(Random rnd) { - // aa(...)aaa - byte[] haystack = new byte[2048]; - Arrays.fill(haystack, (byte) 'a'); - return haystack; - } - }; - - abstract byte[] getNeedle(Random rnd); - abstract byte[] getHaystack(Random rnd); - } - - @Param - public Input input; - - @Param - public ByteBufType bufferType; - - private Random rnd; - private ByteBuf needle, haystack; - private byte[] needleBytes, haystackBytes; - private SearchProcessorFactory kmpFactory, bitapFactory, ahoCorasicFactory; - - @Setup - public void setup() { - rnd = new Random(SEED); - - needleBytes = input.getNeedle(rnd); - haystackBytes = input.getHaystack(rnd); - - needle = Unpooled.wrappedBuffer(needleBytes); - haystack = bufferType.newBuffer(haystackBytes); - - kmpFactory = AbstractSearchProcessorFactory.newKmpSearchProcessorFactory(needleBytes); - ahoCorasicFactory = AbstractMultiSearchProcessorFactory.newAhoCorasicSearchProcessorFactory(needleBytes); - - if (needleBytes.length <= 64) { - bitapFactory = AbstractSearchProcessorFactory.newBitapSearchProcessorFactory(needleBytes); - } - } - - @TearDown - public void teardown() { - needle.release(); - haystack.release(); - } - - @Benchmark - @CompilerControl(Mode.DONT_INLINE) - public int indexOf() { - return ByteBufUtil.indexOf(needle, haystack); - } - - @Benchmark - @CompilerControl(Mode.DONT_INLINE) - public int kmp() { - return haystack.forEachByte(kmpFactory.newSearchProcessor()); - } - - @Benchmark - @CompilerControl(Mode.DONT_INLINE) - public int bitap() { - return haystack.forEachByte(bitapFactory.newSearchProcessor()); - } - - @Benchmark - @CompilerControl(Mode.DONT_INLINE) - public int ahoCorasic() { - return haystack.forEachByte(ahoCorasicFactory.newSearchProcessor()); - } - - private static byte[] randomBytes(Random rnd, int size, int from, int to) { - byte[] bytes = new byte[size]; - for (int i = 0; i < size; i++) { - bytes[i] = (byte) (from + rnd.nextInt(to - from + 1)); - } - return bytes; - } - -} diff --git a/microbench/src/main/java/io/netty/microbench/search/SearchRealDataBenchmark.java b/microbench/src/main/java/io/netty/microbench/search/SearchRealDataBenchmark.java deleted file mode 100644 index 41a92727a8..0000000000 --- a/microbench/src/main/java/io/netty/microbench/search/SearchRealDataBenchmark.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.search; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.search.AbstractMultiSearchProcessorFactory; -import io.netty.buffer.search.AbstractSearchProcessorFactory; -import io.netty.buffer.search.SearchProcessor; -import io.netty.buffer.search.SearchProcessorFactory; -import io.netty.microbench.util.AbstractMicrobenchmark; -import io.netty.util.internal.ResourcesUtil; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.CompilerControl; -import org.openjdk.jmh.annotations.CompilerControl.Mode; -import org.openjdk.jmh.annotations.Fork; -import org.openjdk.jmh.annotations.Level; -import org.openjdk.jmh.annotations.Measurement; -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 org.openjdk.jmh.infra.Blackhole; - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.concurrent.TimeUnit; - -@OutputTimeUnit(TimeUnit.MILLISECONDS) -@Warmup(iterations = 5) -@Measurement(iterations = 5) -@Fork(1) -public class SearchRealDataBenchmark extends AbstractMicrobenchmark { - - public enum Algorithm { - AHO_CORASIC { - @Override - SearchProcessorFactory newFactory(byte[] needle) { - return AbstractMultiSearchProcessorFactory.newAhoCorasicSearchProcessorFactory(needle); - } - }, - KMP { - @Override - SearchProcessorFactory newFactory(byte[] needle) { - return AbstractSearchProcessorFactory.newKmpSearchProcessorFactory(needle); - } - }, - BITAP { - @Override - SearchProcessorFactory newFactory(byte[] needle) { - return AbstractSearchProcessorFactory.newBitapSearchProcessorFactory(needle); - } - }; - abstract SearchProcessorFactory newFactory(byte[] needle); - } - - @Param - public Algorithm algorithm; - - @Param - public ByteBufType bufferType; - - private ByteBuf haystack; - private SearchProcessorFactory[] searchProcessorFactories; - private SearchProcessorFactory searchProcessorFactory; - - private static final byte[][] NEEDLES = { - "Thank You".getBytes(), - "* Does not exist *".getBytes(), - "
  • ".getBytes(), - "".getBytes(), - "
  • ".getBytes(), - "github.com".getBytes(), - " Does not exist 2 ".getBytes(), - "".getBytes(), - "\"https://".getBytes(), - "Netty 4.1.45.Final released".getBytes() - }; - - private int needleId, searchFrom, haystackLength; - - @Setup - public void setup() throws IOException { - File haystackFile = ResourcesUtil.getFile(SearchRealDataBenchmark.class, "netty-io-news.html"); - byte[] haystackBytes = readBytes(haystackFile); - haystack = bufferType.newBuffer(haystackBytes); - - needleId = 0; - searchFrom = 0; - haystackLength = haystack.readableBytes(); - - searchProcessorFactories = new SearchProcessorFactory[NEEDLES.length]; - for (int i = 0; i < NEEDLES.length; i++) { - searchProcessorFactories[i] = algorithm.newFactory(NEEDLES[i]); - } - } - - @Setup(Level.Invocation) - public void invocationSetup() { - needleId = (needleId + 1) % searchProcessorFactories.length; - searchProcessorFactory = searchProcessorFactories[needleId]; - } - - @TearDown - public void teardown() { - haystack.release(); - } - - @Benchmark - @CompilerControl(Mode.DONT_INLINE) - public int findFirst() { - return haystack.forEachByte(searchProcessorFactory.newSearchProcessor()); - } - - @Benchmark - @CompilerControl(Mode.DONT_INLINE) - public int findFirstFromIndex() { - searchFrom = (searchFrom + 100) % haystackLength; - return haystack.forEachByte( - searchFrom, haystackLength - searchFrom, searchProcessorFactory.newSearchProcessor()); - } - - @Benchmark - @CompilerControl(Mode.DONT_INLINE) - public void findAll(Blackhole blackHole) { - SearchProcessor searchProcessor = searchProcessorFactory.newSearchProcessor(); - int pos = 0; - do { - pos = haystack.forEachByte(pos, haystackLength - pos, searchProcessor) + 1; - blackHole.consume(pos); - } while (pos > 0); - } - - private static byte[] readBytes(File file) throws IOException { - InputStream in = new FileInputStream(file); - try { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try { - byte[] buf = new byte[8192]; - for (;;) { - int ret = in.read(buf); - if (ret < 0) { - break; - } - out.write(buf, 0, ret); - } - return out.toByteArray(); - } finally { - safeClose(out); - } - } finally { - safeClose(in); - } - } - - private static void safeClose(InputStream in) { - try { - in.close(); - } catch (IOException ignored) { } - } - - private static void safeClose(OutputStream out) { - try { - out.close(); - } catch (IOException ignored) { } - } - -} diff --git a/microbench/src/main/java/io/netty/microbench/search/package-info.java b/microbench/src/main/java/io/netty/microbench/search/package-info.java deleted file mode 100644 index cddd08283d..0000000000 --- a/microbench/src/main/java/io/netty/microbench/search/package-info.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -/** - * Benchmarks for search ({@link io.netty.buffer.search} and {@link io.netty.buffer.ByteBufUtil#indexOf}). - */ -package io.netty.microbench.search; diff --git a/microbench/src/main/java/io/netty/microbench/stomp/ExampleStompHeadersSubframe.java b/microbench/src/main/java/io/netty/microbench/stomp/ExampleStompHeadersSubframe.java deleted file mode 100644 index 116c26eef8..0000000000 --- a/microbench/src/main/java/io/netty/microbench/stomp/ExampleStompHeadersSubframe.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.stomp; - -import io.netty.handler.codec.stomp.DefaultStompHeadersSubframe; -import io.netty.handler.codec.stomp.StompCommand; -import io.netty.handler.codec.stomp.StompHeaders; -import io.netty.handler.codec.stomp.StompHeadersSubframe; - -import java.util.EnumMap; -import java.util.Map; -import java.util.UUID; - -public final class ExampleStompHeadersSubframe { - - public enum HeadersType { - ONE, - THREE, - SEVEN, - // Next encoded headers size will be more than 256 bytes - ELEVEN, - TWENTY - } - - public static final Map EXAMPLES = - new EnumMap( - HeadersType.class); - - static { - StompHeadersSubframe headersSubframe = new DefaultStompHeadersSubframe(StompCommand.RECEIPT); - headersSubframe.headers() - .set(StompHeaders.RECEIPT_ID, UUID.randomUUID().toString()); - EXAMPLES.put(HeadersType.ONE, headersSubframe); - - headersSubframe = new DefaultStompHeadersSubframe(StompCommand.ERROR); - headersSubframe.headers() - .set(StompHeaders.RECEIPT_ID, UUID.randomUUID().toString()) - .set(StompHeaders.CONTENT_TYPE, "text/plain") - .set(StompHeaders.MESSAGE, "malformed frame received"); - EXAMPLES.put(HeadersType.THREE, headersSubframe); - - headersSubframe = new DefaultStompHeadersSubframe(StompCommand.MESSAGE); - headersSubframe.headers() - .set(StompHeaders.SUBSCRIPTION, "7") - .set(StompHeaders.MESSAGE_ID, UUID.randomUUID().toString()) - .set(StompHeaders.DESTINATION, "/queue/chat") - .set(StompHeaders.CONTENT_TYPE, "application/octet-stream") - .set(StompHeaders.ACK, UUID.randomUUID().toString()) - .setLong("timestamp", System.currentTimeMillis()) - .set("Message-Type: 007"); - EXAMPLES.put(HeadersType.SEVEN, headersSubframe); - - headersSubframe = new DefaultStompHeadersSubframe(StompCommand.MESSAGE); - headersSubframe.headers() - .set(StompHeaders.SUBSCRIPTION, "11") - .set(StompHeaders.MESSAGE_ID, UUID.randomUUID().toString()) - .set(StompHeaders.DESTINATION, "/queue/chat") - .set(StompHeaders.CONTENT_TYPE, "application/octet-stream") - .set(StompHeaders.ACK, UUID.randomUUID().toString()) - .setLong("timestamp", System.currentTimeMillis()) - .set("Message-Type: 0011") - .set("Strict-Transport-Security", "max-age=31536000; includeSubdomains; preload") - .set("Server", "GitHub.com") - .set("Expires", "Sat, 01 Jan 2000 00:00:00 GMT") - .set("Content-Language", "en"); - EXAMPLES.put(HeadersType.ELEVEN, headersSubframe); - - headersSubframe = new DefaultStompHeadersSubframe(StompCommand.MESSAGE); - headersSubframe.headers() - .set(StompHeaders.SUBSCRIPTION, "20") - .set(StompHeaders.MESSAGE_ID, UUID.randomUUID().toString()) - .set(StompHeaders.DESTINATION, "/queue/chat") - .set(StompHeaders.CONTENT_TYPE, "application/octet-stream") - .set(StompHeaders.ACK, UUID.randomUUID().toString()) - .setLong("timestamp", System.currentTimeMillis()) - .set("Message-Type: 0020") - .set("date", "Wed, 22 Apr 2015 00:40:28 GMT") - .set("expires", "Tue, 31 Mar 1981 05:00:00 GMT") - .set("last-modified", "Wed, 22 Apr 2015 00:40:28 GMT") - .set("ms", "ms") - .set("pragma", "no-cache") - .set("server", "tsa_b") - .set("set-cookie", "noneofyourbusiness") - .set("strict-transport-security", "max-age=631138519") - .set("version", "STOMP_v1.2") - .set("x-connection-hash", "e176fe40accc1e2c613a34bc1941aa98") - .set("x-content-type-options", "nosniff") - .set("x-frame-options", "SAMEORIGIN") - .set("x-transaction", "a54142ede693444d9"); - EXAMPLES.put(HeadersType.TWENTY, headersSubframe); - } - - private ExampleStompHeadersSubframe() { - } -} diff --git a/microbench/src/main/java/io/netty/microbench/stomp/StompEncoderBenchmark.java b/microbench/src/main/java/io/netty/microbench/stomp/StompEncoderBenchmark.java deleted file mode 100644 index 4cc8511cf5..0000000000 --- a/microbench/src/main/java/io/netty/microbench/stomp/StompEncoderBenchmark.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.stomp; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.PooledByteBufAllocator; -import io.netty.buffer.Unpooled; -import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.stomp.DefaultStompFrame; -import io.netty.handler.codec.stomp.StompFrame; -import io.netty.handler.codec.stomp.StompHeadersSubframe; -import io.netty.handler.codec.stomp.StompSubframeEncoder; -import io.netty.microbench.channel.EmbeddedChannelWriteReleaseHandlerContext; -import io.netty.microbench.util.AbstractMicrobenchmark; -import io.netty.util.concurrent.Future; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.Fork; -import org.openjdk.jmh.annotations.Level; -import org.openjdk.jmh.annotations.Measurement; -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.annotations.Warmup; -import org.openjdk.jmh.profile.GCProfiler; -import org.openjdk.jmh.runner.options.ChainedOptionsBuilder; - -import java.util.concurrent.ThreadLocalRandom; - -@State(Scope.Benchmark) -@Fork(value = 2) -@Threads(1) -@Warmup(iterations = 5) -@Measurement(iterations = 10) -public class StompEncoderBenchmark extends AbstractMicrobenchmark { - - private StompSubframeEncoder stompEncoder; - private ByteBuf content; - private StompFrame stompFrame; - private ChannelHandlerContext context; - - @Param({ "true", "false" }) - public boolean pooledAllocator; - - @Param - public ExampleStompHeadersSubframe.HeadersType headersType; - - @Param({ "0", "100", "1000" }) - public int contentLength; - - @Setup(Level.Trial) - public void setup() { - byte[] bytes = new byte[contentLength]; - ThreadLocalRandom.current().nextBytes(bytes); - content = Unpooled.wrappedBuffer(bytes); - ByteBuf testContent = Unpooled.unreleasableBuffer(content.asReadOnly()); - - StompHeadersSubframe headersSubframe = ExampleStompHeadersSubframe.EXAMPLES.get(headersType); - stompFrame = new DefaultStompFrame(headersSubframe.command(), testContent); - stompFrame.headers().setAll(headersSubframe.headers()); - - stompEncoder = new StompSubframeEncoder(); - context = new EmbeddedChannelWriteReleaseHandlerContext( - pooledAllocator? PooledByteBufAllocator.DEFAULT : UnpooledByteBufAllocator.DEFAULT, stompEncoder) { - @Override - protected void handleException(Throwable t) { - handleUnexpectedException(t); - } - }; - } - - @TearDown(Level.Trial) - public void teardown() { - content.release(); - content = null; - } - - @Benchmark - public Future writeStompFrame() { - return stompEncoder.write(context, stompFrame.retain()); - } - - protected ChainedOptionsBuilder newOptionsBuilder() throws Exception { - return super.newOptionsBuilder().addProfiler(GCProfiler.class); - } -} diff --git a/microbench/src/main/java/io/netty/microbench/stomp/package-info.java b/microbench/src/main/java/io/netty/microbench/stomp/package-info.java deleted file mode 100644 index ace726202a..0000000000 --- a/microbench/src/main/java/io/netty/microbench/stomp/package-info.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -/** - * Benchmarks for {@link io.netty.handler.codec.stomp.StompSubframeEncoder}. - */ -package io.netty.microbench.stomp; diff --git a/microbench/src/main/java/io/netty/microbench/util/AbstractMicrobenchmark.java b/microbench/src/main/java/io/netty/microbench/util/AbstractMicrobenchmark.java deleted file mode 100644 index d7583e7183..0000000000 --- a/microbench/src/main/java/io/netty/microbench/util/AbstractMicrobenchmark.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.util; - -import io.netty.util.concurrent.DefaultThreadFactory; -import io.netty.util.internal.SystemPropertyUtil; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -import org.openjdk.jmh.annotations.Fork; -import org.openjdk.jmh.runner.options.ChainedOptionsBuilder; - -/** - * Default implementation of the JMH microbenchmark adapter. There may be context switches introduced by this harness. - */ -@Fork(AbstractMicrobenchmark.DEFAULT_FORKS) -public class AbstractMicrobenchmark extends AbstractMicrobenchmarkBase { - - protected static final int DEFAULT_FORKS = 2; - - public static final class HarnessExecutor extends ThreadPoolExecutor { - private final InternalLogger logger = InternalLoggerFactory.getInstance(AbstractMicrobenchmark.class); - - public HarnessExecutor(int maxThreads, String prefix) { - super(maxThreads, maxThreads, 0, TimeUnit.MILLISECONDS, - new LinkedBlockingQueue<>(), new DefaultThreadFactory(prefix)); - logger.debug("Using harness executor"); - } - } - - private final String[] jvmArgs; - - public AbstractMicrobenchmark() { - this(false, false); - } - - public AbstractMicrobenchmark(boolean disableAssertions) { - this(disableAssertions, false); - } - - public AbstractMicrobenchmark(boolean disableAssertions, boolean disableHarnessExecutor) { - final String[] customArgs; - if (disableHarnessExecutor) { - customArgs = new String[]{"-Xms768m", "-Xmx768m", "-XX:MaxDirectMemorySize=768m", - "-XX:BiasedLockingStartupDelay=0"}; - } else { - customArgs = new String[]{"-Xms768m", "-Xmx768m", "-XX:MaxDirectMemorySize=768m", - "-XX:BiasedLockingStartupDelay=0", - "-Djmh.executor=CUSTOM", - "-Djmh.executor.class=io.netty.microbench.util.AbstractMicrobenchmark$HarnessExecutor"}; - } - String[] jvmArgs = new String[BASE_JVM_ARGS.length + customArgs.length]; - System.arraycopy(BASE_JVM_ARGS, 0, jvmArgs, 0, BASE_JVM_ARGS.length); - System.arraycopy(customArgs, 0, jvmArgs, BASE_JVM_ARGS.length, customArgs.length); - if (disableAssertions) { - jvmArgs = removeAssertions(jvmArgs); - } - this.jvmArgs = jvmArgs; - } - - @Override - protected String[] jvmArgs() { - return jvmArgs; - } - - @Override - protected ChainedOptionsBuilder newOptionsBuilder() throws Exception { - ChainedOptionsBuilder runnerOptions = super.newOptionsBuilder(); - if (getForks() > 0) { - runnerOptions.forks(getForks()); - } - - return runnerOptions; - } - - protected int getForks() { - return SystemPropertyUtil.getInt("forks", -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 deleted file mode 100644 index fc1247088f..0000000000 --- a/microbench/src/main/java/io/netty/microbench/util/AbstractMicrobenchmarkBase.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.util; - -import io.netty.util.ResourceLeakDetector; -import io.netty.util.internal.SystemPropertyUtil; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -import org.junit.jupiter.api.Test; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.Warmup; -import org.openjdk.jmh.results.format.ResultFormatType; -import org.openjdk.jmh.runner.Runner; -import org.openjdk.jmh.runner.options.ChainedOptionsBuilder; -import org.openjdk.jmh.runner.options.OptionsBuilder; - -import static org.junit.jupiter.api.Assertions.assertNull; - -/** - * Base class for all JMH benchmarks. - */ -@Warmup(iterations = AbstractMicrobenchmarkBase.DEFAULT_WARMUP_ITERATIONS) -@Measurement(iterations = AbstractMicrobenchmarkBase.DEFAULT_MEASURE_ITERATIONS) -@State(Scope.Thread) -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:+HeapDumpOnOutOfMemoryError", "-Dio.netty.leakDetection.level=disabled"}; - - static { - ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.DISABLED); - } - - protected ChainedOptionsBuilder newOptionsBuilder() throws Exception { - String className = getClass().getSimpleName(); - - ChainedOptionsBuilder runnerOptions = new OptionsBuilder() - .include(".*" + className + ".*") - .jvmArgs(jvmArgs()); - - if (getWarmupIterations() > 0) { - runnerOptions.warmupIterations(getWarmupIterations()); - } - - if (getMeasureIterations() > 0) { - runnerOptions.measurementIterations(getMeasureIterations()); - } - - if (getReportDir() != null) { - String filePath = getReportDir() + className + ".json"; - File file = new File(filePath); - if (file.exists()) { - file.delete(); - } else { - file.getParentFile().mkdirs(); - file.createNewFile(); - } - - runnerOptions.resultFormat(ResultFormatType.JSON); - runnerOptions.result(filePath); - } - - return runnerOptions; - } - - protected abstract String[] jvmArgs(); - - protected static String[] removeAssertions(String[] jvmArgs) { - List customArgs = new ArrayList<>(jvmArgs.length); - for (String arg : jvmArgs) { - if (!arg.startsWith("-ea")) { - customArgs.add(arg); - } - } - if (jvmArgs.length != customArgs.size()) { - jvmArgs = customArgs.toArray(new String[0]); - } - return jvmArgs; - } - - @Test - public void run() throws Exception { - new Runner(newOptionsBuilder().build()).run(); - } - - protected int getWarmupIterations() { - return SystemPropertyUtil.getInt("warmupIterations", -1); - } - - protected int getMeasureIterations() { - return SystemPropertyUtil.getInt("measureIterations", -1); - } - - protected String getReportDir() { - return SystemPropertyUtil.get("perfReportDir"); - } - - public static void handleUnexpectedException(Throwable t) { - assertNull(t); - } -} diff --git a/microbench/src/main/java/io/netty/microbench/util/AbstractSharedExecutorMicrobenchmark.java b/microbench/src/main/java/io/netty/microbench/util/AbstractSharedExecutorMicrobenchmark.java deleted file mode 100644 index d88680d90a..0000000000 --- a/microbench/src/main/java/io/netty/microbench/util/AbstractSharedExecutorMicrobenchmark.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.util; - -import io.netty.channel.EventLoop; -import io.netty.util.concurrent.AbstractEventExecutor; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; -import org.openjdk.jmh.annotations.Fork; - -import java.util.concurrent.Callable; -import java.util.concurrent.TimeUnit; - -/** - * This harness facilitates the sharing of an executor between JMH and Netty and - * thus avoid measuring context switching in microbenchmarks. - */ -@Fork(AbstractSharedExecutorMicrobenchmark.DEFAULT_FORKS) -public class AbstractSharedExecutorMicrobenchmark extends AbstractMicrobenchmarkBase { - - protected static final int DEFAULT_FORKS = 1; - protected static final String[] JVM_ARGS; - - static { - final String[] customArgs = { - "-Xms2g", "-Xmx2g", "-XX:MaxDirectMemorySize=2g", "-Djmh.executor=CUSTOM", - "-Djmh.executor.class=io.netty.microbench.util.AbstractSharedExecutorMicrobenchmark$DelegateHarnessExecutor" }; - - JVM_ARGS = new String[BASE_JVM_ARGS.length + customArgs.length]; - System.arraycopy(BASE_JVM_ARGS, 0, JVM_ARGS, 0, BASE_JVM_ARGS.length); - System.arraycopy(customArgs, 0, JVM_ARGS, BASE_JVM_ARGS.length, customArgs.length); - } - - /** - * Set the executor (in the form of an {@link EventLoop}) which JMH will use. - *

    - * This must be called before JMH requires an executor to execute objects. - * @param eventLoop Used as an executor by JMH to run benchmarks. - */ - public static void executor(EventLoop eventLoop) { - DelegateHarnessExecutor.executor(eventLoop); - } - - /** - * This executor allows Netty and JMH to share a common executor. - * This is achieved by using {@link DelegateHarnessExecutor#executor(EventLoop)} - * with the {@link EventLoop} used by Netty. - */ - public static final class DelegateHarnessExecutor extends AbstractEventExecutor { - private static EventLoop executor; - private final InternalLogger logger = InternalLoggerFactory.getInstance(DelegateHarnessExecutor.class); - - public DelegateHarnessExecutor(int maxThreads, String prefix) { - logger.debug("Using DelegateHarnessExecutor executor {}", this); - } - - /** - * Set the executor (in the form of an {@link EventLoop}) which JMH will use. - *

    - * This must be called before JMH requires an executor to execute objects. - * @param service Used as an executor by JMH to run benchmarks. - */ - public static void executor(EventLoop service) { - executor = service; - } - - @Override - public boolean inEventLoop(Thread thread) { - return executor.inEventLoop(thread); - } - - @Override - public Future shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) { - return executor.shutdownGracefully(quietPeriod, timeout, unit); - } - - @Override - public Future terminationFuture() { - return executor.terminationFuture(); - } - - @Override - public boolean isShuttingDown() { - return executor.isShuttingDown(); - } - - @Override - public boolean isShutdown() { - return executor.isShutdown(); - } - - @Override - public boolean isTerminated() { - return executor.isTerminated(); - } - - @Override - public boolean awaitTermination(long timeout, TimeUnit unit) { - try { - return executor.awaitTermination(timeout, unit); - } catch (InterruptedException e) { - handleUnexpectedException(e); - } - return false; - } - - @Override - public void execute(Runnable task) { - executor.execute(task); - } - - @Override - public Promise newPromise() { - return executor.newPromise(); - } - - @Override - public Future schedule(Runnable task, long delay, TimeUnit unit) { - return executor.schedule(task, delay, unit); - } - - @Override - public Future schedule(Callable task, long delay, TimeUnit unit) { - return executor.schedule(task, delay, unit); - } - - @Override - public Future scheduleAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit unit) { - return executor.scheduleAtFixedRate(task, initialDelay, period, unit); - } - - @Override - public Future scheduleWithFixedDelay( - Runnable task, long initialDelay, long delay, TimeUnit unit) { - return executor.scheduleWithFixedDelay(task, initialDelay, delay, unit); - } - } - - @Override - protected String[] jvmArgs() { - return JVM_ARGS; - } - - public static void handleUnexpectedException(Throwable t) { - if (t != null) { - throw new IllegalStateException(t); - } - } -} diff --git a/microbench/src/main/java/io/netty/microbench/util/ResourceLeakDetectorBenchmark.java b/microbench/src/main/java/io/netty/microbench/util/ResourceLeakDetectorBenchmark.java deleted file mode 100644 index 5a7ff1a308..0000000000 --- a/microbench/src/main/java/io/netty/microbench/util/ResourceLeakDetectorBenchmark.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.util; - -import io.netty.util.ResourceLeakDetector; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.Setup; - -public class ResourceLeakDetectorBenchmark extends AbstractMicrobenchmark { - - private static final Object DUMMY = new Object(); - private ResourceLeakDetector detector; - - @Setup - public void setup() { - detector = new ResourceLeakDetector<>(getClass(), 128); - } - - @Benchmark - public Object open() { - return detector.track(DUMMY); - } -} diff --git a/microbench/src/main/java/io/netty/microbench/util/ResourceLeakDetectorRecordBenchmark.java b/microbench/src/main/java/io/netty/microbench/util/ResourceLeakDetectorRecordBenchmark.java deleted file mode 100644 index fc8a657034..0000000000 --- a/microbench/src/main/java/io/netty/microbench/util/ResourceLeakDetectorRecordBenchmark.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbench.util; - -import io.netty.util.ResourceLeakDetector; -import io.netty.util.ResourceLeakHint; -import io.netty.util.ResourceLeakTracker; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.Level; -import org.openjdk.jmh.annotations.Param; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.TearDown; - -public class ResourceLeakDetectorRecordBenchmark extends AbstractMicrobenchmark { - private static final Object TRACKED = new Object(); - private static final ResourceLeakHint HINT = () -> "BenchmarkHint"; - - @Param({ "8", "16" }) - private int recordTimes; - private ResourceLeakDetector.Level level; - - ResourceLeakDetector detector = new ResourceLeakDetector(Object.class, 1) { - @Override - protected void reportTracedLeak(String resourceType, String records) { - // noop - } - - @Override - protected void reportUntracedLeak(String resourceType) { - // noop - } - - @Override - protected void reportInstancesLeak(String resourceType) { - // noop - } - }; - - @Setup(Level.Trial) - public void setup() { - level = ResourceLeakDetector.getLevel(); - ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.PARANOID); - } - - @TearDown(Level.Trial) - public void teardown() { - ResourceLeakDetector.setLevel(level); - } - - @Benchmark - public boolean record() { - ResourceLeakTracker tracker = detector.track(TRACKED); - for (int i = 0 ; i < recordTimes; i++) { - tracker.record(); - } - return tracker.close(TRACKED); - } - - @Benchmark - public boolean recordWithHint() { - ResourceLeakTracker tracker = detector.track(TRACKED); - for (int i = 0 ; i < recordTimes; i++) { - tracker.record(HINT); - } - return tracker.close(TRACKED); - } -} diff --git a/microbench/src/main/java/io/netty/microbench/util/package-info.java b/microbench/src/main/java/io/netty/microbench/util/package-info.java deleted file mode 100644 index 2f3464908e..0000000000 --- a/microbench/src/main/java/io/netty/microbench/util/package-info.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -/** - * Benchmarks for {@link io.netty.util}. - */ -package io.netty.microbench.util; diff --git a/microbench/src/main/java/io/netty/microbenchmark/common/AppendableCharSequenceBenchmark.java b/microbench/src/main/java/io/netty/microbenchmark/common/AppendableCharSequenceBenchmark.java deleted file mode 100644 index 8c66a1db1b..0000000000 --- a/microbench/src/main/java/io/netty/microbenchmark/common/AppendableCharSequenceBenchmark.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.microbenchmark.common; - -import java.util.Random; - -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.Level; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Param; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.Threads; -import org.openjdk.jmh.annotations.Warmup; - -import io.netty.microbench.util.AbstractMicrobenchmark; - -@Threads(1) -@Warmup(iterations = 5) -@Measurement(iterations = 5) -public class AppendableCharSequenceBenchmark extends AbstractMicrobenchmark { - @Param({ "32", "64", "128", "256" }) - private int charsInitSize; - - @Param({ "10", "100", "10000", "1000000" }) - private int simulatedDataSize; - - private static final Random rand = new Random(); - private char[] chars; - private char simulatedData; - private int pos; - - @Setup(Level.Trial) - public void setup() { - chars = new char[charsInitSize]; - simulatedData = (char) rand.nextInt(); - } - - @Benchmark - public void appendCheckBeforeCopy() { - checkReset(); - if (pos == chars.length) { - expand(); - } - chars[pos++] = simulatedData; - } - - @Benchmark - public void appendCatchExceptionAfter() { - checkReset(); - try { - chars[pos++] = simulatedData; - } catch (IndexOutOfBoundsException e) { - expand(); - chars[pos - 1] = simulatedData; - } - } - - private void checkReset() { - if (pos == simulatedDataSize) { - pos = 0; - chars = new char[charsInitSize]; - } - } - - private void expand() { - char[] old = chars; - // double it - int len = old.length << 1; - if (len < 0) { - throw new IllegalStateException(); - } - chars = new char[len]; - System.arraycopy(old, 0, chars, 0, old.length); - } -} diff --git a/microbench/src/main/java/io/netty/microbenchmark/common/AsciiStringBenchmark.java b/microbench/src/main/java/io/netty/microbenchmark/common/AsciiStringBenchmark.java deleted file mode 100644 index db20e38059..0000000000 --- a/microbench/src/main/java/io/netty/microbenchmark/common/AsciiStringBenchmark.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbenchmark.common; - -import io.netty.microbench.util.AbstractMicrobenchmark; -import io.netty.util.AsciiString; -import io.netty.util.CharsetUtil; -import io.netty.util.internal.PlatformDependent; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.Level; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Param; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.Threads; -import org.openjdk.jmh.annotations.Warmup; - -import java.util.Random; - -@Threads(1) -@Measurement(iterations = 5) -@Warmup(iterations = 5) -public class AsciiStringBenchmark extends AbstractMicrobenchmark { - - @Param({ "3", "5", "7", "8", "10", "20", "50", "100", "1000" }) - public int size; - - private AsciiString asciiString; - private String string; - private static final Random random = new Random(); - - @Setup(Level.Trial) - public void setup() { - byte[] bytes = new byte[size]; - random.nextBytes(bytes); - asciiString = new AsciiString(bytes, false); - string = new String(bytes, CharsetUtil.US_ASCII); - } - - @Benchmark - public int hashCodeBenchBytesOld() { - int h = 0; - final int end = asciiString.arrayOffset() + asciiString.length(); - for (int i = asciiString.arrayOffset(); i < end; ++i) { - // masking with 0x1F reduces the number of overall bits that impact the hash code but makes the hash - // code the same regardless of character case (upper case or lower case hash is the same). - h = h * 31 + (asciiString.array()[i] & 0x1F); - } - return h; - } - - @Benchmark - public int hashCodeBenchBytesNew() { - return PlatformDependent.hashCodeAscii(asciiString.array(), asciiString.arrayOffset(), asciiString.length()); - } - - @Benchmark - public int hashCodeBenchCharSequenceOld() { - int h = 0; - for (int i = 0; i < string.length(); ++i) { - // masking with 0x1F reduces the number of overall bits that impact the hash code but makes the hash - // code the same regardless of character case (upper case or lower case hash is the same). - h = h * 31 + (string.charAt(i) & 0x1F); - } - return h; - } - - @Benchmark - public int hashCodeBenchCharSequenceNew() { - return PlatformDependent.hashCodeAscii(string); - } -} diff --git a/microbench/src/main/java/io/netty/microbenchmark/common/IntObjectHashMapBenchmark.java b/microbench/src/main/java/io/netty/microbenchmark/common/IntObjectHashMapBenchmark.java deleted file mode 100644 index 03c265f3b7..0000000000 --- a/microbench/src/main/java/io/netty/microbenchmark/common/IntObjectHashMapBenchmark.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.microbenchmark.common; - -import io.netty.microbench.util.AbstractMicrobenchmark; -import io.netty.util.collection.IntObjectHashMap; -import org.agrona.collections.Int2ObjectHashMap; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.Level; -import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.Param; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.infra.Blackhole; - -import java.util.HashSet; -import java.util.Random; -import java.util.Set; - -public class IntObjectHashMapBenchmark extends AbstractMicrobenchmark { - private static final Long VALUE = Long.MAX_VALUE; - - public enum MapType { - AGRONA, - NETTY - } - - public enum KeyDistribution { - HTTP2, - RANDOM - } - - @Param({ "10", "100", "1000", "10000", "100000" }) - public int size; - - @Param - public MapType mapType; - - @Param - public KeyDistribution keyDistribution; - - private Environment environment; - - @Setup(Level.Trial) - public void setup() { - switch(mapType) { - case AGRONA: { - environment = new AgronaEnvironment(); - break; - } - case NETTY: { - environment = new NettyEnvironment(); - break; - } - default: { - throw new IllegalStateException("Invalid mapType: " + mapType); - } - } - } - - @Benchmark - @BenchmarkMode(Mode.Throughput) - public void put(Blackhole bh) { - environment.put(bh); - } - - @Benchmark - @BenchmarkMode(Mode.Throughput) - public void lookup(Blackhole bh) { - environment.lookup(bh); - } - - @Benchmark - @BenchmarkMode(Mode.Throughput) - public void remove(Blackhole bh) { - environment.remove(bh); - } - - private abstract class Environment { - final int[] keys; - Environment() { - keys = new int[size]; - switch(keyDistribution) { - case HTTP2: - for (int index = 0, key = 3; index < size; ++index, key += 2) { - keys[index] = key; - } - break; - case RANDOM: { - // Create a 'size' # of random integers. - Random r = new Random(); - Set keySet = new HashSet<>(); - while (keySet.size() < size) { - keySet.add(r.nextInt()); - } - - int index = 0; - for (Integer key : keySet) { - keys[index++] = key; - } - break; - } - default: { - throw new IllegalStateException("Unknown keyDistribution: " + keyDistribution); - } - } - } - abstract void put(Blackhole bh); - abstract void lookup(Blackhole bh); - abstract void remove(Blackhole bh); - } - - private class AgronaEnvironment extends Environment { - private final Int2ObjectHashMap map = new Int2ObjectHashMap<>(); - - AgronaEnvironment() { - for (int key : keys) { - map.put(key, VALUE); - } - } - - @Override - void put(Blackhole bh) { - Int2ObjectHashMap map = new Int2ObjectHashMap<>(); - for (int key : keys) { - bh.consume(map.put(key, VALUE)); - } - } - - @Override - void lookup(Blackhole bh) { - for (int key : keys) { - bh.consume(map.get(key)); - } - } - - @Override - void remove(Blackhole bh) { - Int2ObjectHashMap copy = new Int2ObjectHashMap<>(); - copy.putAll(map); - for (int key : keys) { - bh.consume(copy.remove(key)); - } - } - } - - private class NettyEnvironment extends Environment { - private final IntObjectHashMap map = new IntObjectHashMap(); - - NettyEnvironment() { - for (int key : keys) { - map.put(key, VALUE); - } - } - - @Override - void put(Blackhole bh) { - IntObjectHashMap map = new IntObjectHashMap(); - for (int key : keys) { - bh.consume(map.put(key, VALUE)); - } - } - - @Override - void lookup(Blackhole bh) { - for (int key : keys) { - bh.consume(map.get(key)); - } - } - - @Override - void remove(Blackhole bh) { - IntObjectHashMap copy = new IntObjectHashMap(); - copy.putAll(map); - for (int key : keys) { - bh.consume(copy.remove(key)); - } - } - } -} diff --git a/microbench/src/main/java/io/netty/microbenchmark/common/IsValidIpV4Benchmark.java b/microbench/src/main/java/io/netty/microbenchmark/common/IsValidIpV4Benchmark.java deleted file mode 100644 index 02c88438d9..0000000000 --- a/microbench/src/main/java/io/netty/microbenchmark/common/IsValidIpV4Benchmark.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbenchmark.common; - -import io.netty.microbench.util.AbstractMicrobenchmark; -import io.netty.util.NetUtil; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Param; -import org.openjdk.jmh.annotations.Threads; -import org.openjdk.jmh.annotations.Warmup; - -import java.util.concurrent.TimeUnit; - -@Threads(1) -@Warmup(iterations = 3) -@Measurement(iterations = 3) -@OutputTimeUnit(TimeUnit.MICROSECONDS) -public class IsValidIpV4Benchmark extends AbstractMicrobenchmark { - - @Param({ "127.0.0.1", "255.255.255.255", "1.1.1.1", "127.0.0.256", "127.0.0.1.1", "127.0.0", "[2001::1]" }) - private String ip; - - public static boolean isValidIpV4AddressOld(String value) { - - int periods = 0; - int i; - int length = value.length(); - - if (length > 15) { - return false; - } - char c; - StringBuilder word = new StringBuilder(); - for (i = 0; i < length; i++) { - c = value.charAt(i); - if (c == '.') { - periods++; - if (periods > 3) { - return false; - } - if (word.length() == 0) { - return false; - } - if (Integer.parseInt(word.toString()) > 255) { - return false; - } - word.delete(0, word.length()); - } else if (!Character.isDigit(c)) { - return false; - } else { - if (word.length() > 2) { - return false; - } - word.append(c); - } - } - - if (word.length() == 0 || Integer.parseInt(word.toString()) > 255) { - return false; - } - - return periods == 3; - } - - // Tests - - @Benchmark - public boolean isValidIpV4AddressOld() { - return isValidIpV4AddressOld(ip); - } - - @Benchmark - public boolean isValidIpV4AddressNew() { - return NetUtil.isValidIpV4Address(ip); - } -} diff --git a/microbench/src/main/java/io/netty/microbenchmark/common/IsValidIpV6Benchmark.java b/microbench/src/main/java/io/netty/microbenchmark/common/IsValidIpV6Benchmark.java deleted file mode 100644 index 42b3608dd0..0000000000 --- a/microbench/src/main/java/io/netty/microbenchmark/common/IsValidIpV6Benchmark.java +++ /dev/null @@ -1,225 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbenchmark.common; - -import io.netty.microbench.util.AbstractMicrobenchmark; -import io.netty.util.NetUtil; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Param; -import org.openjdk.jmh.annotations.Threads; -import org.openjdk.jmh.annotations.Warmup; - -import java.util.concurrent.TimeUnit; - -@Threads(1) -@Warmup(iterations = 3) -@Measurement(iterations = 3) -@OutputTimeUnit(TimeUnit.MICROSECONDS) -public class IsValidIpV6Benchmark extends AbstractMicrobenchmark { - - @Param({ - "127.0.0.1", "fdf8:f53b:82e4::53", "2001::1", - "2001:0000:4136:e378:8000:63bf:3fff:fdd2", "0:0:0:0:0:0:10.0.0.1" - }) - private String ip; - - private static boolean isValidIp4Word(String word) { - char c; - if (word.length() < 1 || word.length() > 3) { - return false; - } - for (int i = 0; i < word.length(); i++) { - c = word.charAt(i); - if (!(c >= '0' && c <= '9')) { - return false; - } - } - return Integer.parseInt(word) <= 255; - } - - private static boolean isValidHexChar(char c) { - return c >= '0' && c <= '9' || c >= 'A' && c <= 'F' || c >= 'a' && c <= 'f'; - } - - private static boolean isValidIPv4MappedChar(char c) { - return c == 'f' || c == 'F'; - } - - public static boolean isValidIpV6AddressOld(String ipAddress) { - boolean doubleColon = false; - int numberOfColons = 0; - int numberOfPeriods = 0; - StringBuilder word = new StringBuilder(); - char c = 0; - char prevChar; - int startOffset = 0; // offset for [] ip addresses - int endOffset = ipAddress.length(); - - if (endOffset < 2) { - return false; - } - - // Strip [] - if (ipAddress.charAt(0) == '[') { - if (ipAddress.charAt(endOffset - 1) != ']') { - return false; // must have a close ] - } - - startOffset = 1; - endOffset--; - } - - // Strip the interface name/index after the percent sign. - int percentIdx = ipAddress.indexOf('%', startOffset); - if (percentIdx >= 0) { - endOffset = percentIdx; - } - - for (int i = startOffset; i < endOffset; i++) { - prevChar = c; - c = ipAddress.charAt(i); - switch (c) { - // case for the last 32-bits represented as IPv4 x:x:x:x:x:x:d.d.d.d - case '.': - numberOfPeriods++; - if (numberOfPeriods > 3) { - return false; - } - if (numberOfPeriods == 1) { - // Verify this address is of the correct structure to contain an IPv4 address. - // It must be IPv4-Mapped or IPv4-Compatible - // (see https://tools.ietf.org/html/rfc4291#section-2.5.5). - int j = i - word.length() - 2; // index of character before the previous ':'. - final int beginColonIndex = ipAddress.lastIndexOf(':', j); - if (beginColonIndex == -1) { - return false; - } - char tmpChar = ipAddress.charAt(j); - if (isValidIPv4MappedChar(tmpChar)) { - if (j - beginColonIndex != 4 || - !isValidIPv4MappedChar(ipAddress.charAt(j - 1)) || - !isValidIPv4MappedChar(ipAddress.charAt(j - 2)) || - !isValidIPv4MappedChar(ipAddress.charAt(j - 3))) { - return false; - } - j -= 5; - } else if (tmpChar == '0' || tmpChar == ':') { - --j; - } else { - return false; - } - - // a special case ::1:2:3:4:5:d.d.d.d allows 7 colons with an - // IPv4 ending, otherwise 7 :'s is bad - if ((numberOfColons != 6 && !doubleColon) || numberOfColons > 7 || - (numberOfColons == 7 && (ipAddress.charAt(startOffset) != ':' || - ipAddress.charAt(1 + startOffset) != ':'))) { - return false; - } - - for (; j >= startOffset; --j) { - tmpChar = ipAddress.charAt(j); - if (tmpChar != '0' && tmpChar != ':') { - return false; - } - } - } - - if (!isValidIp4Word(word.toString())) { - return false; - } - word.delete(0, word.length()); - break; - - case ':': - // FIX "IP6 mechanism syntax #ip6-bad1" - // An IPV6 address cannot start with a single ":". - // Either it can start with "::" or with a number. - if (i == startOffset && (endOffset <= i || ipAddress.charAt(i + 1) != ':')) { - return false; - } - // END FIX "IP6 mechanism syntax #ip6-bad1" - numberOfColons++; - if (numberOfColons > 8) { - return false; - } - if (numberOfPeriods > 0) { - return false; - } - if (prevChar == ':') { - if (doubleColon) { - return false; - } - doubleColon = true; - } - word.delete(0, word.length()); - break; - - default: - if (word != null && word.length() > 3) { - return false; - } - if (!isValidHexChar(c)) { - return false; - } - word.append(c); - } - } - - // Check if we have an IPv4 ending - if (numberOfPeriods > 0) { - // There is a test case with 7 colons and valid ipv4 this should resolve it - if (numberOfPeriods != 3 || - !(isValidIp4Word(word.toString()) && (numberOfColons < 7 || doubleColon))) { - return false; - } - } else { - // If we're at then end and we haven't had 7 colons then there is a - // problem unless we encountered a doubleColon - if (numberOfColons != 7 && !doubleColon) { - return false; - } - - if (word.length() == 0) { - // If we have an empty word at the end, it means we ended in either - // a : or a . - // If we did not end in :: then this is invalid - if (ipAddress.charAt(endOffset - 1) == ':' && - ipAddress.charAt(endOffset - 2) != ':') { - return false; - } - } else if (numberOfColons == 8 && ipAddress.charAt(startOffset) != ':') { - return false; - } - } - - return true; - } - - // Tests - - @Benchmark - public boolean isValidIpV6AddressOld() { - return isValidIpV6AddressOld(ip); - } - - @Benchmark - public boolean isValidIpV6AddressNew() { - return NetUtil.isValidIpV6Address(ip); - } -} diff --git a/microbench/src/main/java/io/netty/microbenchmark/common/MathOperationsBenchmark.java b/microbench/src/main/java/io/netty/microbenchmark/common/MathOperationsBenchmark.java deleted file mode 100644 index 3f99f484bd..0000000000 --- a/microbench/src/main/java/io/netty/microbenchmark/common/MathOperationsBenchmark.java +++ /dev/null @@ -1,34 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.microbenchmark.common; - -import io.netty.microbench.util.AbstractMicrobenchmark; -import org.openjdk.jmh.annotations.Benchmark; - -public class MathOperationsBenchmark extends AbstractMicrobenchmark { - private int index; - private final int length = 1 << 20; - private final int mask = length - 1; - - @Benchmark - public int nextIndexNoConditionals() { - return (index + 1) & mask; - } - - @Benchmark - public int nextIndexConditionals() { - return index == length - 1 ? 0 : index + 1; - } -} diff --git a/microbench/src/main/java/io/netty/microbenchmark/common/NetUtilBenchmark.java b/microbench/src/main/java/io/netty/microbenchmark/common/NetUtilBenchmark.java deleted file mode 100644 index c53f420a9b..0000000000 --- a/microbench/src/main/java/io/netty/microbenchmark/common/NetUtilBenchmark.java +++ /dev/null @@ -1,341 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.microbenchmark.common; - -import io.netty.microbench.util.AbstractMicrobenchmark; -import io.netty.util.NetUtil; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Threads; -import org.openjdk.jmh.annotations.Warmup; - -@Threads(4) -@Warmup(iterations = 10) -@Measurement(iterations = 10) -public class NetUtilBenchmark extends AbstractMicrobenchmark { - - @Benchmark - public int useGetByNameIpv4() { - int invalidCount = 0; - for (String testEntry : invalidIpV4Hosts) { - if (NetUtil.getByName(testEntry) == null) { - ++invalidCount; - } - } - return invalidCount; - } - - @Benchmark - public int useGetByNameIpv6() { - int invalidCount = 0; - for (String testEntry : invalidIpV6Hosts) { - if (NetUtil.getByName(testEntry) == null) { - ++invalidCount; - } - } - return invalidCount; - } - - @Benchmark - public int useIsValidIpv6() { - int invalidCount = 0; - for (String host : invalidIpV6Hosts) { - if (!NetUtil.isValidIpV6Address(host)) { - ++invalidCount; - } - } - return invalidCount; - } - - @Benchmark - public int useIsValidIpv4() { - int invalidCount = 0; - for (String host : invalidIpV4Hosts) { - if (!NetUtil.isValidIpV4Address(host)) { - ++invalidCount; - } - } - return invalidCount; - } - - private static final String[] invalidIpV4Hosts = { - "1.256.3.4", - "256.0.0.1", - "1.1.1.1.1", - "x.255.255.255", - "0.1:0.0", - "0.1.0.0:", - "127.0.0.", - "1.2..4", - "192.0.1", - "192.0.1.1.1", - "192.0.1.a", - "19a.0.1.1", - "a.0.1.1", - ".0.1.1", - "127.0.0", - "192.0.1.256", - "0.0.200.259", - "1.1.-1.1", - "1.1. 1.1", - "1.1.1.1 ", - "1.1.+1.1", - "0.0x1.0.255", - "0.01x.0.255", - "0.x01.0.255", - "0.-.0.0", - "0..0.0", - "0.A.0.0", - "0.1111.0.0", - "..." - }; - - private static final String[] invalidIpV6Hosts = { - // Test method with garbage. - "Obvious Garbage", - // Test method with preferred style, too many : - "0:1:2:3:4:5:6:7:8", - // Test method with preferred style, not enough : - "0:1:2:3:4:5:6", - // Test method with preferred style, bad digits. - "0:1:2:3:4:5:6:x", - // Test method with preferred style, adjacent : - "0:1:2:3:4:5:6::7", - // Too many : separators trailing - "0:1:2:3:4:5:6:7::", - // Too many : separators leading - "::0:1:2:3:4:5:6:7", - // Too many : separators trailing - "1:2:3:4:5:6:7:", - // Too many : separators leading - ":1:2:3:4:5:6:7", - // Compression with : separators trailing - "0:1:2:3:4:5::7:", - "0:1:2:3:4::7:", - "0:1:2:3::7:", - "0:1:2::7:", - "0:1::7:", - "0::7:", - // Compression at start with : separators trailing - "::0:1:2:3:4:5:7:", - "::0:1:2:3:4:7:", - "::0:1:2:3:7:", - "::0:1:2:7:", - "::0:1:7:", - "::7:", - // The : separators leading and trailing - ":1:2:3:4:5:6:7:", - ":1:2:3:4:5:6:", - ":1:2:3:4:5:", - ":1:2:3:4:", - ":1:2:3:", - ":1:2:", - ":1:", - // Compression with : separators leading - ":1::2:3:4:5:6:7", - ":1::3:4:5:6:7", - ":1::4:5:6:7", - ":1::5:6:7", - ":1::6:7", - ":1::7", - ":1:2:3:4:5:6::7", - ":1:3:4:5:6::7", - ":1:4:5:6::7", - ":1:5:6::7", - ":1:6::7", - ":1::", - // Compression trailing with : separators leading - ":1:2:3:4:5:6:7::", - ":1:3:4:5:6:7::", - ":1:4:5:6:7::", - ":1:5:6:7::", - ":1:6:7::", - ":1:7::", - // Double compression - "1::2:3:4:5:6::", - "::1:2:3:4:5::6", - "::1:2:3:4:5:6::", - "::1:2:3:4:5::", - "::1:2:3:4::", - "::1:2:3::", - "::1:2::", - "::0::", - "12::0::12", - // Too many : separators leading 0 - "0::1:2:3:4:5:6:7", - // Test method with preferred style, too many digits. - "0:1:2:3:4:5:6:789abcdef", - // Test method with compressed style, bad digits. - "0:1:2:3::x", - // Test method with compressed style, too many adjacent : - "0:1:2:::3", - // Test method with compressed style, too many digits. - "0:1:2:3::abcde", - // Test method with compressed style, not enough : - "0:1", - // Test method with ipv4 style, bad ipv6 digits. - "0:0:0:0:0:x:10.0.0.1", - // Test method with ipv4 style, bad ipv4 digits. - "0:0:0:0:0:0:10.0.0.x", - // Test method with ipv4 style, too many ipv6 digits. - "0:0:0:0:0:00000:10.0.0.1", - // Test method with ipv4 style, too many : - "0:0:0:0:0:0:0:10.0.0.1", - // Test method with ipv4 style, not enough : - "0:0:0:0:0:10.0.0.1", - // Test method with ipv4 style, too many . - "0:0:0:0:0:0:10.0.0.0.1", - // Test method with ipv4 style, not enough . - "0:0:0:0:0:0:10.0.1", - // Test method with ipv4 style, adjacent . - "0:0:0:0:0:0:10..0.0.1", - // Test method with ipv4 style, leading . - "0:0:0:0:0:0:.0.0.1", - // Test method with ipv4 style, leading . - "0:0:0:0:0:0:.10.0.0.1", - // Test method with ipv4 style, trailing . - "0:0:0:0:0:0:10.0.0.", - // Test method with ipv4 style, trailing . - "0:0:0:0:0:0:10.0.0.1.", - // Test method with compressed ipv4 style, bad ipv6 digits. - "::fffx:192.168.0.1", - // Test method with compressed ipv4 style, bad ipv4 digits. - "::ffff:192.168.0.x", - // Test method with compressed ipv4 style, too many adjacent : - ":::ffff:192.168.0.1", - // Test method with compressed ipv4 style, too many ipv6 digits. - "::fffff:192.168.0.1", - // Test method with compressed ipv4 style, too many ipv4 digits. - "::ffff:1923.168.0.1", - // Test method with compressed ipv4 style, not enough : - ":ffff:192.168.0.1", - // Test method with compressed ipv4 style, too many . - "::ffff:192.168.0.1.2", - // Test method with compressed ipv4 style, not enough . - "::ffff:192.168.0", - // Test method with compressed ipv4 style, adjacent . - "::ffff:192.168..0.1", - // Test method, bad ipv6 digits. - "x:0:0:0:0:0:10.0.0.1", - // Test method, bad ipv4 digits. - "0:0:0:0:0:0:x.0.0.1", - // Test method, too many ipv6 digits. - "00000:0:0:0:0:0:10.0.0.1", - // Test method, too many ipv4 digits. - "0:0:0:0:0:0:10.0.0.1000", - // Test method, too many : - "0:0:0:0:0:0:0:10.0.0.1", - // Test method, not enough : - "0:0:0:0:0:10.0.0.1", - // Test method, out of order trailing : - "0:0:0:0:0:10.0.0.1:", - // Test method, out of order leading : - ":0:0:0:0:0:10.0.0.1", - // Test method, out of order leading : - "0:0:0:0::10.0.0.1:", - // Test method, out of order trailing : - ":0:0:0:0::10.0.0.1", - // Test method, too many . - "0:0:0:0:0:0:10.0.0.0.1", - // Test method, not enough . - "0:0:0:0:0:0:10.0.1", - // Test method, adjacent . - "0:0:0:0:0:0:10.0.0..1", - // Empty contents - "", - // Invalid single compression - ":", - ":::", - // Trailing : (max number of : = 8) - "2001:0:4136:e378:8000:63bf:3fff:fdd2:", - // Leading : (max number of : = 8) - ":aaaa:bbbb:cccc:dddd:eeee:ffff:1111:2222", - // Invalid character - "1234:2345:3456:4567:5678:6789::X890", - // Trailing . in IPv4 - "::ffff:255.255.255.255.", - // To many characters in IPv4 - "::ffff:0.0.1111.0", - // Test method, adjacent . - "::ffff:0.0..0", - // Not enough IPv4 entries trailing . - "::ffff:127.0.0.", - // Invalid trailing IPv4 character - "::ffff:127.0.0.a", - // Invalid leading IPv4 character - "::ffff:a.0.0.1", - // Invalid middle IPv4 character - "::ffff:127.a.0.1", - // Invalid middle IPv4 character - "::ffff:127.0.a.1", - // Not enough IPv4 entries no trailing . - "::ffff:1.2.4", - // Extra IPv4 entry - "::ffff:192.168.0.1.255", - // Not enough IPv6 content - ":ffff:192.168.0.1.255", - // Intermixed IPv4 and IPv6 symbols - "::ffff:255.255:255.255.", - // Invalid IPv4 mapped address - invalid ipv4 separator - "0:0:0::0:0:00f.0.0.1", - // Invalid IPv4 mapped address - not enough f's - "0:0:0:0:0:fff:1.0.0.1", - // Invalid IPv4 mapped address - not IPv4 mapped, not IPv4 compatible - "0:0:0:0:0:ff00:1.0.0.1", - // Invalid IPv4 mapped address - not IPv4 mapped, not IPv4 compatible - "0:0:0:0:0:ff:1.0.0.1", - // Invalid IPv4 mapped address - too many f's - "0:0:0:0:0:fffff:1.0.0.1", - // Invalid IPv4 mapped address - too many bytes (too many 0's) - "0:0:0:0:0:0:ffff:1.0.0.1", - // Invalid IPv4 mapped address - too many bytes (too many 0's) - "::0:0:0:0:0:ffff:1.0.0.1", - // Invalid IPv4 mapped address - too many bytes (too many 0's) - "0:0:0:0:0:0::1.0.0.1", - // Invalid IPv4 mapped address - too many bytes (too many 0's) - "0:0:0:0:0:00000:1.0.0.1", - // Invalid IPv4 mapped address - too few bytes (not enough 0's) - "0:0:0:0:ffff:1.0.0.1", - // Invalid IPv4 mapped address - too few bytes (not enough 0's) - "ffff:192.168.0.1", - // Invalid IPv4 mapped address - 0's after the mapped ffff indicator - "0:0:0:0:0:ffff::10.0.0.1", - // Invalid IPv4 mapped address - 0's after the mapped ffff indicator - "0:0:0:0:ffff::10.0.0.1", - // Invalid IPv4 mapped address - 0's after the mapped ffff indicator - "0:0:0:ffff::10.0.0.1", - // Invalid IPv4 mapped address - 0's after the mapped ffff indicator - "0:0:ffff::10.0.0.1", - // Invalid IPv4 mapped address - 0's after the mapped ffff indicator - "0:ffff::10.0.0.1", - // Invalid IPv4 mapped address - 0's after the mapped ffff indicator - "ffff::10.0.0.1", - // Invalid IPv4 mapped address - not all 0's before the mapped separator - "1:0:0:0:0:ffff:10.0.0.1", - // Address that is similar to IPv4 mapped, but is invalid - "0:0:0:0:ffff:ffff:1.0.0.1", - // Valid number of separators, but invalid IPv4 format - "::1:2:3:4:5:6.7.8.9", - // Too many digits - "0:0:0:0:0:0:ffff:10.0.0.1", - // Invalid IPv4 format - ":1.2.3.4", - // Invalid IPv4 format - "::.2.3.4", - // Invalid IPv4 format - "::ffff:0.1.2." - }; -} diff --git a/microbench/src/main/java/io/netty/microbenchmark/common/package-info.java b/microbench/src/main/java/io/netty/microbenchmark/common/package-info.java deleted file mode 100644 index 1f6e7e7349..0000000000 --- a/microbench/src/main/java/io/netty/microbenchmark/common/package-info.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -/** - * Benchmarks for {@link io.netty.util}. - */ -package io.netty.microbenchmark.common; diff --git a/microbench/src/main/java/io/netty/util/DefaultAttributeMapBenchmark.java b/microbench/src/main/java/io/netty/util/DefaultAttributeMapBenchmark.java deleted file mode 100644 index cb230b9983..0000000000 --- a/microbench/src/main/java/io/netty/util/DefaultAttributeMapBenchmark.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.util; - -import io.netty.microbench.util.AbstractMicrobenchmark; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.Level; -import org.openjdk.jmh.annotations.Measurement; -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.Threads; -import org.openjdk.jmh.annotations.Warmup; -import org.openjdk.jmh.infra.Blackhole; - -import java.util.IdentityHashMap; - -@Warmup(iterations = 5, time = 1) -@Measurement(iterations = 5, time = 1) -@State(Scope.Benchmark) -public class DefaultAttributeMapBenchmark extends AbstractMicrobenchmark { - - @Param({ "8", "32", "128" }) - private int keyCount; - private AttributeKey[] keys; - private IdentityHashMap, Attribute> identityHashMap; - private DefaultAttributeMap attributes; - - @State(Scope.Thread) - public static class KeySequence { - - long nextKey; - - @Setup(Level.Iteration) - public void reset() { - nextKey = 0; - } - - public long next() { - return nextKey++; - } - } - - @Setup - public void init() { - if (Integer.bitCount(keyCount) != 1) { - throw new AssertionError("keyCount should cbe a power of 2"); - } - attributes = new DefaultAttributeMap(); - keys = new AttributeKey[keyCount]; - identityHashMap = new IdentityHashMap, Attribute>(keyCount); - for (int i = 0; i < keyCount; i++) { - final AttributeKey key = AttributeKey.valueOf(Integer.toString(i)); - keys[i] = key; - final Attribute attribute = attributes.attr(key); - identityHashMap.put(key, attribute); - } - } - - @Benchmark - @Threads(3) - public Attribute nextAttributeIdentityHashMap(KeySequence sequence) { - long next = sequence.next(); - AttributeKey[] keys = this.keys; - AttributeKey key = keys[(int) (next & keys.length - 1)]; - return identityHashMap.get(key); - } - - @Benchmark - @Threads(3) - public boolean hasAttributeIdentityHashMap(KeySequence sequence) { - long next = sequence.next(); - AttributeKey[] keys = this.keys; - AttributeKey key = keys[(int) (next & keys.length - 1)]; - return identityHashMap.containsKey(key); - } - - @Benchmark - @Threads(3) - public void mixedAttributeIdentityHashMap(KeySequence sequence, Blackhole hole) { - long next = sequence.next(); - AttributeKey[] keys = this.keys; - AttributeKey key = keys[(int) (next & keys.length - 1)]; - if (next % 2 == 0) { - hole.consume(identityHashMap.get(key)); - } else { - hole.consume(identityHashMap.containsKey(key)); - } - } - - @Benchmark - @Threads(3) - public Attribute nextAttributeAttributeMap(KeySequence sequence) { - long next = sequence.next(); - AttributeKey[] keys = this.keys; - AttributeKey key = keys[(int) (next & keys.length - 1)]; - return attributes.attr(key); - } - - @Benchmark - @Threads(3) - public boolean nextHasAttributeAttributeMap(KeySequence sequence) { - long next = sequence.next(); - AttributeKey[] keys = this.keys; - AttributeKey key = keys[(int) (next & keys.length - 1)]; - return attributes.hasAttr(key); - } - - @Benchmark - @Threads(3) - public void mixedAttributeAttributeMap(KeySequence sequence, Blackhole hole) { - long next = sequence.next(); - AttributeKey[] keys = this.keys; - AttributeKey key = keys[(int) (next & keys.length - 1)]; - if (next % 2 == 0) { - hole.consume(attributes.attr(key)); - } else { - hole.consume(attributes.hasAttr(key)); - } - } -} diff --git a/microbench/src/main/java/io/netty/util/package-info.java b/microbench/src/main/java/io/netty/util/package-info.java deleted file mode 100644 index 56408a1bb9..0000000000 --- a/microbench/src/main/java/io/netty/util/package-info.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -/** - * Benchmarks for {@link io.netty.util}. - */ -package io.netty.util; diff --git a/microbench/src/main/resources/Utf8Samples.txt b/microbench/src/main/resources/Utf8Samples.txt deleted file mode 100644 index b293d8d93a..0000000000 --- a/microbench/src/main/resources/Utf8Samples.txt +++ /dev/null @@ -1,1399 +0,0 @@ -Kon nie 'n tydelike lÃĒer skep vir storing van: %s -Wil jy hÃĒ ek moet probeer om die muis-konfigurasieprogram te laat loop? Neem kennis datjy die stamwagwoord hiervoor nodig het. -VerifiÃĢringsbewysruiling vir gebruiker %s het gefaal -Moenie woorde oor twee reÃĢls _opbreek nie -Seçili KÃļrpÃŧ -AtkHyperlink cisminin seçildiyini bildirir -LÃļvbər SayÄą -AtkHyperlink cismi ilə əlaqələndirilmiş lÃļvbər ədədini bildirir. -Son indeks -AtkHyperlink cisminin son indeksi -Başlanğıc indeksi -AtkHyperlink cisminin başlanğıc indeksi -hÃļkmsÃŧz -sÃŧr'ətləndirici etiketi -xəbərdarlÄąq -animasiya -ox -təqvim -kanvas -işarələmə qutusu -işarələmə menyusu ÃŧzvÃŧ -rəng seçicisi -sÃŧtun başlığı -kombo qutusu -tarix editoru -masa ÃŧstÃŧ timsalÄą -masa ÃŧstÃŧ çərçivəsi -zəng-et -dialoq -cərgə lÃļvhəsi -rəsm sahəsi -fayl seçicisi -doldurucu -yazÄą nÃļvÃŧ seçicisi -çərçivə -ayna lÃļvhəsi -html konteyneri -timsal -şəkil -daxili çərçivə -etiket -laylanmÄąÅŸ lÃļvhə -siyahÄą -siyahÄą ÃŧzvÃŧ -menyu -menyu çubuğu -menyu ÃŧzvÃŧ -seçim lÃļvhəsi -səhifə səkməsi -səhifə səkməsi siyahÄąsÄą -panel -şifrə mətni -popup menyu -ilərləmə çubuğu -basma dÃŧyməsi -qərar dÃŧyməsi -qərar menyusu ÃŧzvÃŧ -kÃļk lÃļvhə -sətir başlığı -sÃŧrÃŧşdÃŧrmə çubuğu -sÃŧrÃŧşdÃŧrmə lÃļvhəsi -ayÄąrÄącÄą -sÃŧrÃŧşdÃŧrÃŧcÃŧ -ayÄąrma lÃļvhəsi -dÃļnən dÃŧymə -vəziyyət-çubuğu -cədvəl -cədvəl hÃŧcrəsi -cədvəl sÃŧtun başlığı -cədvəl sətir başlığı -qopardÄąla bilən menyu ÃŧzvÃŧ -terminal -mətn -keçiş dÃŧyməsi -vasitə çubuğu -məsləhət -ağac -budaq cədvəli -namə'lum -nÃŧmayiş-lÃļvhəsi -pəncərə -başlÄąq -altlÄąq -paraqraf -tə'minat -avtomatik tamamlama -dÃŧzəlişlər çubuğu -daxili tərkib hissəsi -Yetişilə Bilən Ad -YardÄąmÃ§Äą texnologiyalara yetişmə ÃŧçÃŧn şəkilləndilirmiş cismin adÄą -Yetişilə Bilən Ä°zahat -YardÄąmÃ§Äą texnologiyalara yetişmə ÃŧçÃŧn şəkilləndilirmiş cismin izahatÄą -Yetişilə Bilən Valideyn -Valideynin dəyişildiyini bildirmək ÃŧçÃŧn işlədilir -Yetişilə Bilən Qiymət -Qiymətin dəyişildiyini bildirmək ÃŧçÃŧn işlədilir -Yetişilə Bilən Rol -Bu ÃŧzvÃŧn yetişilə bilən rolu -Yetişilə Bilən Lay -Bu ÃŧzvÃŧn yetişilə bilən layÄą -Yetişilə Bilən MDI Qiyməti -Bu ÃŧzvÃŧn yetişilə bilən MDI qiyməti -Yetişilə Bilən Cədvəl Etiketi -Cədvəl etiket -ВŅ‹ĐģŅƒŅ‡Đ°ĐŊĐ°Ņ ŅĐŋĐ°ŅŅ‹ĐģĐēĐ° -ЗадаĐĩ, Ņ†Ņ– СŅŒŅŅžĐģŅĐĩŅ†Ņ†Đ° Đ°Đą'ĐĩĐēŅ‚ AtkHyperlink вŅ‹ĐģŅƒŅ‡Đ°ĐŊŅ‹Đŧ -КоĐģŅŒĐēĐ°ŅŅŒŅ†ŅŒ ŅĐēĐ°Ņ€Đ°Ņž -КоĐģŅŒĐēĐ°ŅŅŒŅ†ŅŒ ŅĐēĐ°Ņ€Đ°Ņž, ŅĐēŅ–Ņ СŅŒĐ˛ŅĐˇĐ°ĐŊŅ‹Ņ С Đ°Đą'ĐĩĐēŅ‚Đ°Đŧ AtkHyperlink -КаĐŊŅ†Đ°Đ˛Ņ‹ Ņ–ĐŊĐ´ŅĐēŅ -КаĐŊŅ†Đ°Đ˛Ņ‹ Ņ–ĐŊĐ´ŅĐēŅ Đ°Đą'ĐĩĐēŅ‚Ņƒ AtkHyperlink -ПаŅ‡Đ°Ņ‚ĐēОвŅ‹ Ņ–ĐŊĐ´ŅĐēŅ -ПаŅ‡Đ°Ņ‚ĐēОвŅ‹ Ņ–ĐŊĐ´ŅĐēŅ Đ°Đą'ĐĩĐēŅ‚Ņƒ AtkHyperlink -ĐŊĐĩŅ€ŅŅ‡Đ°Ņ–ŅĐŊĐ°Đĩ -Đ°Đ´ĐŧĐĩŅ†Ņ–ĐŊĐ° ĐŋĐ°ŅĐēĐ°Ņ€Đ°ĐģŅŒĐŊŅ–ĐēŅƒ -ŅŅ‹ĐŗĐŊĐ°Đģ -Đ°ĐŊŅ–ĐŧĐ°Ņ†Ņ‹Ņ -ŅŅ‚Ņ€ŅĐģĐēĐ° -ĐēĐ°ĐģŅĐŊĐ´Đ°Ņ€ -ĐēĐ°ĐŊва -ĐēĐŊĐžĐŋĐēĐ°-ĐŋаСĐŊĐ°Ņ‡Đ°ĐģŅŒĐŊŅ–Đē -ĐŋŅƒĐŊĐēŅ‚-ĐŋаСĐŊĐ°Ņ‡Đ°ĐģŅŒĐŊŅ–Đē ĐŧŅĐŊŅŽ -Đ°ĐąŅ–Ņ€Đ°ĐģŅŒĐŊŅ–Đē ĐēĐžĐģĐĩŅ€Ņƒ -СаĐŗĐ°ĐģОваĐē ŅĐģŅƒĐŋĐēŅƒ -ĐēĐ°ĐŧĐąŅ–ĐŊаваĐŊŅ‹ ŅĐģĐĩĐŧŅĐŊŅ‚ -Ņ€ŅĐ´Đ°ĐēŅ‚Đ°Ņ€ Đ´Đ°Ņ‚Ņ‹ -СĐŊĐ°Ņ‡ĐēĐ° ŅŅ‚Đ°ĐģŅŒŅ†Ņƒ -ĐēĐ°Đ´Đ°Ņ€ ŅŅ‚Đ°ĐģŅŒŅ†Ņƒ -ĐģŅ–Ņ‡ĐąĐ°Đ˛Đ°Đĩ Ņ‚Đ°ĐąĐģĐž -Đ´Ņ‹ŅĐģŅ‘Đŗ -Đ°ĐēĐŊĐž Ņ‚ŅŅ‡ĐēŅ– -ĐŋŅ€Đ°ŅŅ‚ĐžŅ€Đ° ĐŧĐ°ĐģŅĐ˛Đ°ĐŊŅŒĐŊŅ -Đ°ĐąŅ–Ņ€Đ°ĐģŅŒĐŊŅ–Đē Ņ„Đ°ĐšĐģŅƒ -Ņ„Ņ–ĐģŅŒŅ‚Đ°Ņ€ -Đ°ĐąŅ–Ņ€Đ°ĐģŅŒĐŊŅ–Đē ŅˆŅ€Ņ‹Ņ„Ņ‚Ņƒ -ĐēĐ°Đ´Đ°Ņ€ -Đ°ĐēĐŊĐž ĐŋŅ€Đ°ĐˇŅ€Ņ‹ŅŅ‚Đ°ŅŅŒŅ†Ņ– -ĐēĐ°ĐŊŅ‚ŅĐšĐŊŅŅ€ html -СĐŊĐ°Ņ‡ĐēĐ° -вŅ–Đ´Đ°Ņ€Ņ‹Ņ -ŅƒĐŊŅƒŅ‚Ņ€Đ°ĐŊŅ‹ ĐēĐ°Đ´Đ°Ņ€ -Đ°Đ´ĐŧĐĩŅ†Ņ–ĐŊĐ° -Đ°ĐēĐŊĐž ŅƒĐˇŅ€ĐžŅžĐŊŅŅž -ŅŅŒĐŋŅ–Ņ -ĐŋŅƒĐŊĐēŅ‚ ŅŅŒĐŋŅ–ŅŅƒ -ĐŧŅĐŊŅŽ -Ņ€Đ°Đ´ĐžĐē ĐŧŅĐŊŅŽ -ĐŋŅƒĐŊĐēŅ‚ ĐŧŅĐŊŅŽ -Đ°ĐēĐŊĐž вŅ‹ĐąĐ°Ņ€Đ°Ņž -ŅžĐēĐģĐ°Đ´ĐēĐ° ŅŅ‚Đ°Ņ€ĐžĐŊĐēŅ– -ŅŅŒĐŋŅ–Ņ ŅžĐēĐģĐ°Đ´Đ°Đē ŅŅ‚Đ°Ņ€ĐžĐŊĐēŅ– -ĐŋĐ°ĐŊŅĐģŅŒ -Ņ‚ŅĐēŅŅ‚ ĐŋĐ°Ņ€ĐžĐģŅŽ -ŅƒŅĐŋĐģŅ‹ŅžĐŊĐžĐĩ ĐŧŅĐŊŅŽ -ĐŋĐ°ĐģĐžŅĐēĐ° ĐŋĐžŅŅŒĐŋĐĩŅ…Ņƒ -ĐēĐŊĐžĐŋĐēĐ° ĐŊĐ°Ņ†Ņ–ŅĐēŅƒ -ĐēĐŊĐžĐŋĐēĐ° ŅžĐˇĐ°ĐĩĐŧавŅ‹ĐēĐģŅŽŅ‡ĐŊĐ°ĐŗĐ° вŅ‹ĐąĐ°Ņ€Ņƒ -ĐŋŅƒĐŊĐēŅ‚ ĐŧŅĐŊŅŽ ŅžĐˇĐ°ĐĩĐŧавŅ‹ĐēĐģŅŽŅ‡ĐŊĐ°ĐŗĐ° вŅ‹ĐąĐ°Ņ€Ņƒ -ĐēĐ°Ņ€ŅĐŊĐŊĐ°Đĩ Đ°ĐēĐŊĐž -СаĐŗĐ°ĐģОваĐē Ņ€Đ°Đ´ĐēŅƒ -ĐŋĐ°ĐģĐ°ŅĐ° ĐŋŅ€Đ°ĐēŅ€ŅƒŅ‚ĐēŅ– -Đ°ĐēĐŊĐž ĐŋŅ€Đ°ĐēŅ€ŅƒŅ‚ĐēŅ– -дСĐĩĐģŅŒĐŊŅ–Đē -ĐŋĐ°ŅžĐˇŅƒĐŊĐžĐē -дСĐĩĐģŅŒĐŊŅ–Đē вОĐēĐŊĐ°Ņž -ĐŋŅ€Đ°ĐēŅ€ŅƒŅ‚ĐēĐ° -Ņ€Đ°Đ´ĐžĐē ŅŅ‚Đ°ĐŊŅƒ -Ņ‚Đ°ĐąĐģŅ–Ņ†Đ° -ŅŅ‡ŅŅ Ņ‚Đ°ĐąĐģŅ–Ņ†Ņ‹ -СаĐŗĐ°ĐģОваĐē ŅĐģŅƒĐŋĐēŅƒ Ņ‚Đ°ĐąĐģŅ–Ņ†Ņ‹ -СаĐŗĐ°ĐģОваĐē Ņ€Đ°Đ´ĐēŅƒ Ņ‚Đ°ĐąĐģŅ–Ņ†Ņ‹ -ĐŋŅƒĐŊĐēŅ‚ СаŅ‡Đ°ĐŋĐģĐĩĐŊŅŒĐŊŅ ĐŧŅĐŊŅŽ -Ņ‚ŅŅ€ĐŧŅ–ĐŊĐ°Đģ -Ņ‚ŅĐēŅŅ‚ -ĐŋĐĩŅ€Đ°ĐēĐģŅŽŅ‡Đ°ĐģŅŒĐŊŅ–Đē -ĐŋĐ°ĐŊŅĐģŅŒ ŅŅ€ĐžĐ´ĐēĐ°Ņž -ĐŋĐ°Đ´ĐēаСĐēĐ° -Đ´Ņ€ŅĐ˛Đ° -Ņ‚Đ°ĐąĐģŅ–Ņ†Đ° Đ´Ņ€ŅĐ˛Đ° -ĐŊĐĩвŅĐ´ĐžĐŧĐ° -Đ°ĐēĐŊĐž ĐŋŅ€Đ°ĐŗĐģŅĐ´Ņƒ -Đ°ĐēĐŊĐž -СаĐŗĐ°ĐģОваĐē -ĐŊŅ–ĐļĐŊŅŅ Ņ‡Đ°ŅŅ‚ĐēĐ° Đ°ĐēĐŊĐ° -айСаŅ† -Đ´Đ°ŅŅ‚Đ°ŅĐ°Đ˛Đ°ĐŊŅŒĐŊĐĩ -Đ°ŅžŅ‚аСавŅŅ€ŅˆŅĐŊŅŒĐŊĐĩ -Ņ€Đ°Đ´ĐžĐē Ņ€ŅĐ´Đ°ĐŗаваĐŊŅŒĐŊŅ -ŅƒĐąŅƒĐ´Đ°Đ˛Đ°ĐŊŅ‹ ĐēĐ°ĐŧĐŋĐ°ĐŊŅĐŊŅ‚ -ДаŅŅ‚ŅƒĐŋĐŊĐ°Ņ ĐŊаСва -Назва ŅĐēСŅĐŧĐŋĐģŅŅ€Ņƒ Đ°Đą'ĐĩĐēŅ‚Ņƒ, Ņ„Đ°Ņ€ĐŧĐ°Ņ‚аваĐŊĐ°Ņ Đ´ĐģŅ Ņ‚ŅŅ…ĐŊĐ°ĐģŅ‘ĐŗŅ–Ņ– ŅĐ°Đ´ĐˇĐĩŅĐŊŅŒĐŊŅ -АĐŋŅ–ŅĐ°ĐŊŅŒĐŊĐĩ Đ´Đ°ŅŅ‚ŅƒĐŋĐŊĐ°ŅŅŒŅ†Ņ– -АĐŋŅ–ŅĐ°ĐŊŅŒĐŊĐĩ Đ°Đą'ĐĩĐēŅ‚Đ°, Ņ„Đ°Ņ€ĐŧĐ°Ņ‚аваĐŊĐ°Đĩ Đ´ĐģŅ Ņ‚ŅŅ…ĐŊĐ°ĐģŅ‘ĐŗŅ–Ņ– ŅĐ°Đ´ĐˇĐĩŅĐŊŅŒĐŊŅ -ДаŅŅ‚ŅƒĐŋĐŊĐ°ŅŅŒŅ†ŅŒ йаŅ†ŅŒĐēĐžŅžŅĐēĐ°ĐŗĐ° Đ°Đą'ĐĩĐēŅ‚Ņƒ -ВŅ‹ĐēĐ°Ņ€Ņ‹ŅŅ‚ĐžŅžĐ˛Đ°ĐĩŅ†Ņ†Đ°, ĐēĐ°Đą ĐŋавĐĩĐ´Đ°ĐŧĐģŅŅ†ŅŒ ĐŋŅ€Đ° СŅŒĐŧĐĩĐŊŅ‹ йаŅ†ŅŒĐēĐžŅžŅĐēĐ°ĐŗĐ° Đ°Đą'ĐĩĐēŅ‚Ņƒ -ЗĐŊĐ°Ņ‡ŅĐŊŅŒĐŊĐĩ Đ´Đ°ŅŅ‚ŅƒĐŋĐŊĐ°ŅŅŒŅ†Ņ– -ВŅ‹ĐēĐ°Ņ€Ņ‹ŅŅ‚ĐžŅžĐ˛Đ°ĐĩŅ†Ņ†Đ°, ĐēĐ°Đą ĐŋавĐĩĐ´Đ°ĐŧĐģŅŅ†ŅŒ ĐŋŅ€Đ° СŅŒĐŧĐĩĐŊŅƒ СĐŊĐ°Ņ‡ŅĐŊŅŒĐŊŅ -Đ ĐžĐģŅ Đ´Đ°ŅŅ‚ŅƒĐŋĐŊĐ°ŅŅŒŅ†Ņ– -Đ ĐžĐģŅ Đ°Đą'ĐĩĐēŅ‚Ņƒ Ņž Đ´Đ°ŅŅ‚ŅƒĐŋĐŊĐ°ŅŅŒŅ†Ņ– -ĐŖСŅ€ĐžĐ˛ĐĩĐŊŅŒ Đ´Đ°ŅŅ‚ŅƒĐŋĐŊĐ°ŅŅŒŅ†Ņ– -ĐŖСŅ€ĐžĐ˛ĐĩĐŊŅŒ Đ´Đ°ŅŅ‚ŅƒĐŋĐŊĐ°ŅŅŒŅ†Ņ– ĐŗŅŅ‚Đ°ĐŗĐ° -ИСйŅ€Đ°ĐŊĐ° ĐŋŅ€ĐĩĐŋŅ€Đ°Ņ‚ĐēĐ° -ОĐŋŅ€ĐĩĐ´ĐĩĐģŅ Đ´Đ°Đģи ОйĐĩĐēŅ‚ŅŠŅ‚ AtkHyperlink Đĩ иСйŅ€Đ°ĐŊ -БŅ€ĐžĐš ĐēĐžŅ‚ви -БŅ€ĐžŅŅ‚ ĐŊĐ° ĐēĐžŅ‚виŅ‚Đĩ ŅĐ˛ŅŠŅ€ĐˇĐ°ĐŊи Ņ ОйĐĩĐēŅ‚ AtkHyperlink -КŅ€Đ°Đš ĐŊĐ° иĐŊĐ´ĐĩĐēŅ -КŅ€Đ°ĐšĐŊиŅŅ‚ иĐŊĐ´ĐĩĐēŅ ĐŊĐ° ОйĐĩĐēŅ‚ AtkHyperlink -НаŅ‡Đ°ĐģĐž ĐŊĐ° иĐŊĐ´ĐĩĐēŅ -НаŅ‡Đ°ĐģĐŊиŅŅ‚ иĐŊĐ´ĐĩĐēŅ ĐŊĐ° ОйĐĩĐēŅ‚ AtkHyperlink -ĐŊĐĩваĐģидĐŊĐž -ĐŊĐ°Đ´ĐŋиŅ ĐŊĐ° ŅƒŅĐēĐžŅ€Đ¸Ņ‚ĐĩĐģ -вĐŊиĐŧĐ°ĐŊиĐĩ -Đ°ĐŊиĐŧĐ°Ņ†Đ¸Ņ -ŅŅ‚Ņ€ĐĩĐģĐēĐ° -ĐēĐ°ĐģĐĩĐŊĐ´Đ°Ņ€ -ĐŋĐģĐ°Ņ‚ĐŊĐž -ĐēŅƒŅ‚иКĐēĐ° Са ĐžŅ‚ĐŧŅŅ‚Đ°ĐŊĐĩ -ĐĩĐģĐĩĐŧĐĩĐŊŅ‚ ĐŊĐ° ĐŧĐĩĐŊŅŽŅ‚Đž Са ĐžŅ‚ĐŧŅŅ‚Đ°ĐŊĐĩ -иСйОŅ€ ĐŊĐ° Ņ†Đ˛ŅŅ‚ -СаĐŗĐģавиĐĩ ĐŊĐ° ĐēĐžĐģĐžĐŊĐ° -ĐēŅƒŅ‚иКĐēĐ° Са иСйОŅ€ ĐžŅ‚ ĐžĐŋŅ€ĐĩĐ´ĐĩĐģĐĩĐŊи ŅŅ‚ОКĐŊĐžŅŅ‚и -Ņ€ĐĩĐ´Đ°ĐēŅ‚ĐžŅ€ ĐŊĐ° Đ´Đ°Ņ‚Đ° -иĐēĐžĐŊĐ° Са Ņ€Đ°ĐąĐžŅ‚ĐŊиŅ ĐŋĐģĐžŅ‚ -Ņ€Đ°ĐŧĐēĐ° ĐŊĐ° Ņ€Đ°ĐąĐžŅ‚ĐŊиŅ ĐŋĐģĐžŅ‚ -ŅĐēĐ°ĐģĐ° -диаĐģĐžĐŗОва ĐēŅƒŅ‚иŅ -иСĐŗĐģĐĩĐ´ ĐēŅŠĐŧ ĐŋĐ°ĐŋĐēи -ОйĐģĐ°ŅŅ‚ Са Ņ€Đ¸ŅŅƒĐ˛Đ°ĐŊĐĩ -иСйОŅ€ ĐŊĐ° Ņ„Đ°ĐšĐģ -ĐŋŅŠĐģĐŊиŅ‚ĐĩĐģ -иСйОŅ€ ĐŊĐ° ŅˆŅ€Đ¸Ņ„Ņ‚ -Ņ€Đ°ĐŧĐēĐ° -ĐŊĐ°Đš-ĐŗĐžŅ€ĐŊĐ° Ņ€Đ°ĐŧĐēĐ° -ĐēĐžĐŊŅ‚ĐĩĐšĐŊĐĩŅ€ Ņ ĐēОд ĐŊĐ° html -иĐēĐžĐŊĐ° -ĐēĐ°Ņ€Ņ‚иĐŊĐ° -вŅŠŅ‚Ņ€ĐĩŅˆĐŊĐ° Ņ€Đ°ĐŧĐēĐ° -ĐĩŅ‚иĐēĐĩŅ‚ -Ņ€Đ°ĐŧĐēĐ° ŅŅŠŅ ŅĐģĐžĐĩвĐĩ ОйĐĩĐēŅ‚и -ŅĐŋиŅŅŠĐē -ĐĩĐģĐĩĐŧĐĩĐŊŅ‚ ĐŊĐ° ŅĐŋиŅŅŠĐē -ĐŧĐĩĐŊŅŽ -ĐģĐĩĐŊŅ‚Đ° Ņ ĐŧĐĩĐŊŅŽŅ‚Đ° -ĐĩĐģĐĩĐŧĐĩĐŊŅ‚ ĐžŅ‚ ĐŧĐĩĐŊŅŽ -ĐŋĐ°ĐŊĐĩĐģ Ņ ĐžĐŋŅ†Đ¸Đ¸ -Ņ‚Đ°Đą Са ŅŅ‚Ņ€Đ°ĐŊиŅ†Đ° -ŅĐŋиŅŅŠĐē Ņ Ņ‚айОвĐĩ -ĐŋĐ°ĐŊĐĩĐģ -Ņ‚ĐĩĐēŅŅ‚ОвО ĐŋĐžĐģĐĩ Са ĐŋĐ°Ņ€ĐžĐģĐ° -иСŅĐēĐ°Ņ‡Đ°Ņ‰Đž ĐŧĐĩĐŊŅŽ -ĐģĐĩĐŊŅ‚Đ° Са ĐŋŅ€ĐžĐŗŅ€ĐĩŅ -ĐąŅƒŅ‚ĐžĐŊ -Ņ€Đ°Đ´Đ¸Đž-ĐąŅƒŅ‚ĐžĐŊ -Ņ€Đ°Đ´Đ¸Đž-ĐĩĐģĐĩĐŧĐĩĐŊŅ‚ ĐžŅ‚ ĐŧĐĩĐŊŅŽ -ĐžŅĐŊОвĐŊĐ° Ņ€Đ°ĐŧĐēĐ° -СаĐŗĐģавиĐĩ ĐŊĐ° Ņ€ĐĩĐ´ -ĐģĐĩĐŊŅ‚Đ° Са ĐŋŅ€ĐĩĐģиŅŅ‚ваĐŊĐĩ -Ņ€Đ°ĐŧĐēĐ°, ĐēĐžŅŅ‚Đž ŅĐĩ ĐŋŅ€ĐĩĐģиŅŅ‚ва -Ņ€Đ°ĐˇĐ´ĐĩĐģиŅ‚ĐĩĐģ -ĐŋĐģŅŠĐˇĐŗĐ°Ņ‡ -Ņ€Đ°ĐˇĐ´ĐĩĐģиŅ‚ĐĩĐģĐŊĐ° Ņ€Đ°ĐŧĐēĐ° -ĐąŅƒŅ‚ĐžĐŊ Са ĐŋĐžŅĐģĐĩдОваŅ‚ĐĩĐģĐĩĐŊ иСйОŅ€ -ĐģĐĩĐŊŅ‚Đ° Са ŅŅŠŅŅ‚ĐžŅĐŊиĐĩ -Ņ‚Đ°ĐąĐģиŅ†Đ° -ĐēĐģĐĩŅ‚ĐēĐ° ĐžŅ‚ Ņ‚Đ°ĐąĐģиŅ†Đ° -СаĐŗĐģавиĐĩ ĐŊĐ° ĐēĐžĐģĐžĐŊĐ° в Ņ‚Đ°ĐąĐģиŅ†Đ° -СаĐŗĐģавиĐĩ ĐŊĐ° Ņ€ĐĩĐ´ в Ņ‚Đ°ĐąĐģиŅ†Đ° -ĐĩĐģĐĩĐŧĐĩĐŊŅ‚ ĐŊĐ° ĐŧĐĩĐŊŅŽŅ‚Đž, ĐēОКŅ‚Đž ĐŧĐžĐļĐĩ Đ´Đ° ŅĐĩ ĐžŅ‚Đ´ĐĩĐģи ĐžŅ‚ ĐŊĐĩĐŗĐž -Ņ‚ĐĩŅ€ĐŧиĐŊĐ°Đģ -Ņ‚ĐĩĐēŅŅ‚ -ĐŋŅ€ĐĩвĐēĐģŅŽŅ‡Đ˛Đ°Ņ‰ ĐąŅƒŅ‚ĐžĐŊ -ĐģĐĩĐŊŅ‚Đ° Ņ иĐŊŅŅ‚Ņ€ŅƒĐŧĐĩĐŊŅ‚и -ĐŋОдŅĐēаСĐēĐ° -Đ´ŅŠŅ€Đ˛Đž -Đ´ŅŠŅ€Đ˛ĐžĐ˛Đ¸Đ´ĐŊĐ° Ņ‚Đ°ĐąĐģиŅ†Đ° -ĐŊĐĩĐŋОСĐŊĐ°Ņ‚Đž -иСĐŗĐģĐĩĐ´ -ĐŋŅ€ĐžĐˇĐžŅ€ĐĩŅ† -ĐŗĐžŅ€ĐĩĐŊ ĐēĐžĐģĐžĐŊŅ‚иŅ‚ŅƒĐģ -Đ´ĐžĐģĐĩĐŊ ĐēĐžĐģĐžĐŊŅ‚иŅ‚ŅƒĐģ -айСаŅ† -ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩ -авŅ‚ĐžĐŧĐ°Ņ‚иŅ‡ĐŊĐž дОвŅŠŅ€ŅˆĐ˛Đ°ĐŊĐĩ -ĐģĐĩĐŊŅ‚Đ° Са Ņ€ĐĩĐ´Đ°ĐēŅ†Đ¸Ņ -вĐŗŅ€Đ°Đ´ĐĩĐŊ ĐēĐžĐŧĐŋĐžĐŊĐĩĐŊŅ‚ -ДоŅŅ‚ŅŠĐŋĐŊĐž иĐŧĐĩ -ИĐŧĐĩ ĐŊĐ° ОйĐĩĐēŅ‚ ĐžŅ‚ ĐŊŅĐēĐ°ĐēŅŠĐ˛ ĐēĐģĐ°Ņ. ИĐŧĐĩŅ‚Đž Đĩ Ņ„ĐžŅ€ĐŧĐ°Ņ‚иŅ€Đ°ĐŊĐž Са Ņ‚ĐĩŅ…ĐŊĐžĐģĐžĐŗии Са Đ´ĐžŅŅ‚ŅŠĐŋĐŊĐžŅŅ‚ -ДоŅŅ‚ŅŠĐŋĐŊĐž ĐžĐŋиŅĐ°ĐŊиĐĩ -ОĐŋиŅĐ°ĐŊиĐĩ ĐŊĐ° ОйĐĩĐēŅ‚, Ņ„ĐžŅ€ĐŧĐ°Ņ‚иŅ€Đ°ĐŊ Са Ņ‚ĐĩŅ…ĐŊĐžĐģĐžĐŗии Са Đ´ĐžŅŅ‚ŅŠĐŋĐŊĐžŅŅ‚ -ДоŅŅ‚ŅŠĐŋĐĩĐŊ Ņ€ĐžĐ´Đ¸Ņ‚ĐĩĐģ -ИСĐŋĐžĐģСва ŅĐĩ, Са Đ´Đ° ŅƒĐ˛ĐĩĐ´ĐžĐŧи Са ĐŋŅ€ĐžĐŧŅĐŊĐ° в Ņ€ĐžĐ´Đ¸Ņ‚ĐĩĐģŅ -ДоŅŅ‚ŅŠĐŋĐŊĐ° ŅŅ‚ОКĐŊĐžŅŅ‚ -ИСĐŋĐžĐģСва ŅĐĩ, Са Đ´Đ° ŅĐĩ ŅƒĐ˛ĐĩĐ´ĐžĐŧи Са ĐŋŅ€ĐžĐŧŅĐŊĐ° ĐŊĐ° ŅŅ‚ОКĐŊĐžŅŅ‚Ņ‚Đ° - -Izabrani link -Označava da li je izabran objekt AtkHyperlink -Broj sidara -Broj sidara zdruÅženih s objektom AtkHyperlink -ZavrÅĄni indeks -ZavrÅĄni indeks objekta AtkHyperlink -Početni indeks -Početni indeks objekta AtkHyperlink -nevaÅžeće -oznaka akceleratora -uzbuna -animacija -strelica -kalendar -kanafas -okvir za izbor -stavka menija za izbor -birač boja -zaglavlje kolone -kombinovani okvir -editor datuma -desktop ikona -okvir za desktop -biranje broja -dijalog -okno za direktorije -područje za crtanje -birač datoteka -punjač -birač fontova -okvir -stakleno okno -html spremiÅĄte -ikona -slika -interni okvir -oznaka -okno sa slojevima -lista -stavka liste -meni -traka menija -stavka menija -okno za opcije -kartica -lista kartica -panel -tekst ÅĄifre -popup meni -traka napredovanja -dugme -radio dugme -stavka radio menija -glavno okno -zaglavlje reda -traka s klizačem -okno sa klizačem -linija razdvajanja -klizač -razdijeljeno okno -spin dugme -statusna traka -tablica -polje tablice -zaglavlje kolone tablice -zaglavlje reda tablice -odvojiva stavka menija -terminal -tekst -dugme prekidač -traka s alatima -opis alata -stablo -tablica stabla -nepoznato -port pregleda -prozor -zaglavlje -tekst na dnu -paragraf -aplikacija -samodopuna -traka za izmjene -ugrađena komponenta -Dostupno ime -Ime primjerka objekta je formatirano za dostup uz pomoćnu tehnologiju -Dostupan opis -Opis objekta, formatiran za dostup uz pomoćnu tehnologiju -Dostupno porijeklo -Koristi se za obavijest o promjeni porijekla -Dostupna vrijednost -Koristi se za obavijest o promjeni vrijednosti -Dostupna uloga -Dostupna uloga ovog objekta -Dostupan sloj -Dostupan sloj ovog objekta -Dostupna MDI vrijednost -Dostupna MDI vrijednost ovog objekta -Dostupan natpis tablice -Koristi se za obavijest o promjeni natpisa tablice. Ova osobina se ne bi trebala -Pot ser voleu actualitzar a una versiÃŗ nova del sistema %s -Seleccioneu el producte per a l'informe d'error. -Escolliu un component, versiÃŗ, i nivell de gravetat per al producte %s -Seleccioneu una aplicaciÃŗ per a l'informe d'error. -Escolliu un component, versiÃŗ, i nivell de gravetat per a l'aplicaciÃŗ %s -Aquesta aplicaciÃŗ tÊ informaciÃŗ d'error, perÃ˛ Bug Buddy no sap res d'ella. Seleccioneu una altra aplicaciÃŗ. -Heu d'indicar el component a què es refereix l'informe d'error. -Nom de l'informador de l'error -Adreça electrÃ˛nica -Adreça de correu amb la qual enviar errors al Bugzilla del GNOME. Aquesta adreça s'utilitzarà per a la correspondència relacionada amb l'error que esteu enviant. Si ja teniu un compte al Bugzilla del GNOME, utilitzeu-la com la vostra adreça de correu. -Fitxer on desar els informes d'error -Fitxer on voleu desar el vostre informe d'error per a enviar-lo mÊs tard. -Última vegada que Bug Buddy va comprovar si hi ha actualitzacions -Camí al vostre sistema de fitxers local on es troba sendmail o un d'equivalent. -Camí al programa de correu semblant a sendmail -Nom real de l'usuari que està enviant l'informe d'error. -Aquesta Ês l'Ãēltima vegada (marca de temps UNIX) que Bug Buddy va comprovar si hi havien actualitzacions de la informaciÃŗ de Bugzilla. -Utilitza sendmail per a enviar els informes d'error al Bugzilla del GNOME. És l'Ãēnic mètode suportat per ara. -Utilitza sendmail per a enviar informes d'error -VybranÃŊ odkaz -Určuje, jestli je objekt AtkHyperlink vybrÃĄn -Počet ukotvení -Počet ukotvení asociovanÃŊch s objektem AtkHyperlink -Index konce -Index konce objektu AtkHyperlink -Index zaÄÃĄtku -Indek zaÄÃĄtku objektu AtkHyperlink -neplatnÃŊ -popisek klÃĄvesovÊ zkratky -upozornění -animace -ÅĄipka -kalendÃĄÅ™ -plÃĄtno -zaÅĄkrtÃĄvací políčko -zaÅĄkrtÃĄvací poloÅžka menu -vÃŊběr barvy -zÃĄhlaví sloupce -kombo box -editor data -ikona pracovní plochy -rÃĄm pracovní plochy -vytÃĄÄení -dialog -panel adresÃĄÅ™Å¯ -kreslicí oblast -vÃŊběr souboru -plnič -vÃŊběr písma -rÃĄm -skleněnÃŊ panel -kontejner html -ikona -obrÃĄzek -interní rÃĄm -popisek -vrstvenÃŊ panel -seznam -poloÅžka seznamu -menu -liÅĄta menu -poloÅžka menu -panel voleb -zÃĄloÅžka strÃĄnky -seznam zÃĄloÅžek strÃĄnky -panel -text hesla -vyskakovací menu -liÅĄta průběhu -tlačítko -rÃĄdiovÊ tlačítko -rÃĄdiovÃĄ poloÅžka menu -kořenovÃŊ panel -zÃĄhlaví Å™ÃĄdku -posuvnÃĄ liÅĄta -posuvnÃŊ panel -oddělovač -posunovač -oddělenÃŊ panel -otÃĄÄecí tlačítko -stavovÃĄ-liÅĄta -tabulka -buňka tabulky -zÃĄhlaví sloupce tabulky -zÃĄhlaví Å™ÃĄdku tabulky -odtrhÃĄvací poloÅžka menu -terminÃĄl -text -přepínač -nÃĄstrojovÃĄ liÅĄta -tip -strom -stromovÃĄ tabulka -neznÃĄmÃŊ -pohled -okno -zÃĄhlaví -zÃĄpatí -odstavec -aplikace -autodoplnění -liÅĄta Ãēprav -vloÅženÃĄ komponenta -ZpřístupněnÃŊ nÃĄzev -NÃĄzev instance objektu formÃĄtovanÃŊ pro pouÅžití technologiemi zpřístupnění -ZpřístupněnÃŊ popis -Popis objektu formÃĄtovanÃŊ pro pouÅžití technologiemi zpřístupnění -ZpřístupněnÃŊ rodič -PouŞívÃĄ se pro upozornění, Åže rodič byl změněn -ZpřístupněnÃĄ hodnota -PouŞívÃĄ se pro upozornění, Åže hodnota byla změněna -ZpřístupněnÃĄ role -ZpřístupněnÃĄ role tohoto objektu -ZpřístupněnÃĄ vrstva -ZpřístupněnÃĄ vrstva tohoto objektu -ZpřístupněnÃĄ hodnota MDI -ZpřístupněnÃĄ hodnota MDI tohoto objektu -ZpřístupněnÃŊ nadpis tabulky -PouŞívÃĄ se pro upozornění, Åže nadpis tabulky byl změn -Valgt henvisning -Angiver om AtkHyperlink-objektet er valgt -Antal ankre -Antallet af ankre der er associeret med AtkHyperlink-objektet -Slutindeks -Slutindeks for AtkHyperlink-objektet -Startindeks -Startindeks for AtkHyperlink-objektet -ugyldig -genvejsetiket -advarsel -animation -pil -kalender -lÃĻrred -afkrydsningsboks -afkrydsningsmenupunkt -farvevÃĻlger -kolonnetitel -kombinationsboks -datoredigering -skrivebordsikon -skrivebordsramme -opkald -vindue -mappepanel -tegneomrÃĨde -filvÃĻlger -udfylder -skrifttypevÃĻlger -ramme -glaspanel -html-beholder -ikon -billede -intern ramme -etiket -lagdelt panel -liste -listepunkt -menu -menulinje -menupunkt -indstillingspanel -sidefane -sidefaneliste -panel -adgangskodetekst -pop-op-menu -fremgangslinje -trykknap -radioknap -radiomenupunkt -rodpanel -rÃĻkketitel -rulleskakt -rullepanel -adskiller -skyder -delt panel -talindtastning -statuslinje -tabel -tabelcelle -tabelkolonnetitel -tabelrÃĻkketitel -afrivningsmenupunkt -terminal -tekst -skifteknap -vÃĻrktøjslinje -vÃĻrktøjstip -trÃĻ -trÃĻtabel -ukendt -visningsomrÃĨde -vindue -sidehoved -sidefod -afsnit -program -autofuldførelse -redigÊr linje -indlejret komponent -TilgÃĻngelighedsnavn -Objektinstansens navn formateret til assisterende teknologier -TilgÃĻngelighedsbeskrivelse -Beskrivelse af eet objekt, formateret til assisterende teknologier -TilgÃĻngelighedsophav -Bruges til at bekendtgøre at ophavet er ÃĻndret -TilgÃĻngelighedsvÃĻrdi -Bruges til at bekendtgøre at vÃĻrdien er ÃĻndret -TilgÃĻngelighedsrolle -TilgÃĻngelighedsrolle for dette objekt -TilgÃĻngelighedslag -TilgÃĻngelighedslaget for dette objekt -TilgÃĻngeligheds-MDI-vÃĻrdi -TilgÃĻngeligheds-MDI-vÃĻrdien for dette objekt -TilgÃĻngelighedstabeltitel -Bruges til at bekendtgøre at tabeltitlen er ÃĻndret; denne egenskab bør ikke benyttes. accessible-table-caption-object bør bruges i -Ausgewählter Link -Gibt an, ob das AtkHyperlink-Objekt ausgewählt ist -Ankeranzahl -Die Anzahl der mit dem AtkHyperlink-Objekt assoziierten Anker -Endindex -Der Endindex des AtkHyperlink-Objekts -Anfangsindex -Der Anfangsindex des AtkHyperlink-Objekts -ungÃŧltig -KÃŧrzelbeschriftung -Alarm -Animation -Pfeil -Kalender -Leinwand -Kontrollkästchen -KontrollmenÃŧobjekt -Farbwähler -Spaltenkopf -Kombinationsfeld -Datumseditor -Desktop-Symbol -Desktop-Rahmen -wählen -Dialog -Verzeichnisleiste -Zeichenfeld -Dateiwähler -FÃŧller -Schriftwähler -Rahmen -Glasleiste -HTML-Container -Symbol -Bild -interner Rahmen -Beschriftung -Schichtleiste -Liste -Listenobjekt -MenÃŧ -MenÃŧleiste -MenÃŧobjekt -Optionsleiste -Seitenreiter -Seitenreiter-Liste -Panel -Passworttext -Popup-MenÃŧ -Fortschrittsleiste -Druckknopf -Radioknopf -RadiomenÃŧobjekt -Wurzelleiste -Zeilenkopf -Rollbalken -Rollleiste -Trennlinie -Schieber -geteilte Leiste -Spin-Knopf -Statusleiste -Tabelle -Tabellenzelle -Spaltenkopf einer Tabelle -Zeilenkopf einer Tabelle -Objekt in AbrissmenÃŧ -Terminal -Text -AuslÃļseknopf -Werkzeugleiste -Minihilfe -Baum -Baumtabelle -unbekannt -Sichtfeld -Fenster -Kopfzeilen -Fußzeilen -Absatz -Anwendung -Auto-Vervollständigung -Bearbeitungsleiste -eingebettete Komponente -Barrierefreier Name -Name der Objektinstanz, formatiert fÃŧr den Zugriff durch Hilfstechnologien -Barrierefreie Beschreibung -Beschreibung eines Objekts, formatiert fÃŧr den Zugriff durch Hilfstechnologien -Barrierefreies Eltern-Element -Wird zur Benachrichtigung bei Änderungen am Eltern-Element verwendet -Barrierefreier Wert -Wird zur Benachrichtigung bei Wertänderungen verwendet -Barrierefreie Rolle -Die barrierefreie Rolle dieses Objekts -Barrierefreie Ebene -Die barrierefreie Ebene dieses Objekts -Barrierefreier MDI-Wert -Der barrierefreie MDI-Wert dieses Objekts -Barr -ΕĪ€ÎšÎģÎĩÎŗÎŧέÎŊÎŋĪ‚ ÎŖĪÎŊδÎĩĪƒÎŧÎŋĪ‚ -ΚαθÎŋĪÎ¯ÎļÎĩΚ ÎąÎŊ Ī„Îŋ ÎąÎŊĪ„ΚÎēÎĩίÎŧÎĩÎŊÎŋ AtkHyperlink ÎĩίÎŊιΚ ÎĩĪ€ÎšÎģÎĩÎŗÎŧέÎŊÎŋ -ΑĪÎšÎ¸ÎŧĪŒĪ‚ ΑÎŗÎēĪĪĪ‰ÎŊ -Ο ÎąĪÎšÎ¸ÎŧĪŒĪ‚ ÎąĪ€ĪŒ ÎŦÎŗÎēĪ…ĪÎĩĪ‚ Ī€ÎŋĪ… ĪƒĪ‡ÎĩĪ„ίÎļÎŋÎŊĪ„ιΚ ÎŧÎĩ Ī„Îŋ ÎąÎŊĪ„ΚÎēÎĩίÎŧÎĩÎŊÎŋ AtkHyperlink -End index -ΤÎŋ end index Ī„ÎŋĪ… ÎąÎŊĪ„ΚÎēÎĩΚÎŧέÎŊÎŋĪ… AtkHyperlink -Start index -ΤÎŋ start index Ī„ÎŋĪ… ÎąÎŊĪ„ΚÎēÎĩΚÎŧέÎŊÎŋĪ… AtkHyperlink -ÎŧΡ έÎŗÎēĪ…ĪÎŋ -ÎĩĪ„ΚÎēέĪ„Îą ĪƒĪ…ÎŊĪ„ĪŒÎŧ -ÎĩΚδÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ -ÎēΚÎŊÎŋĪÎŧÎĩÎŊÎŋ ĪƒĪ‡Î­Î´ÎšÎŋ -βέÎģÎŋĪ‚ -ΡÎŧÎĩĪÎŋÎģĪŒÎŗΚÎŋ -canvas -ÎēÎŋĪ…Ī„ί ÎĩĪ€ÎšÎģÎŋÎŗÎŽĪ‚ -ÎąÎŊĪ„ΚÎēÎĩίÎŧÎĩÎŊÎŋ ÎŧÎĩÎŊÎŋĪ ÎĩĪ€ÎšÎģÎŋÎŗÎŽĪ‚ -ÎĩĪ€ÎšÎģÎŋÎŗέιĪ‚ Ī‡ĪĪŽÎŧÎąĪ„ÎŋĪ‚ -ÎēÎĩĪ†ÎąÎģÎ¯Î´Îą ĪƒĪ„ÎŽÎģΡĪ‚ -ÎēÎŋĪ…Ī„ί Ī€ÎŋÎģÎģÎąĪ€ÎģĪŽÎŊ -ÎĩĪ€ÎĩΞÎĩĪÎŗÎąĪƒĪ„ÎŽĪ‚-ΡÎŧÎĩĪÎŋÎŧΡÎŊÎ¯ÎąĪ‚ -ÎĩΚÎēÎŋÎŊίδΚÎŋ ÎĩĪ€ÎšĪ†ÎŦÎŊÎĩΚιĪ‚ -Ī€ÎģÎąÎ¯ĪƒÎšÎŋ ÎĩĪ€ÎšĪ†ÎŦÎŊÎĩΚιĪ‚ ÎĩĪÎŗÎąĪƒÎ¯ÎąĪ‚ -dial -δΚÎŦÎģÎŋÎŗÎŋĪ‚ -Ī„ÎąÎŧĪ€ÎģĪŽ ÎēÎąĪ„ÎąÎģĪŒÎŗÎŋĪ… -Ī€ÎĩĪÎšÎŋĪ‡ÎŽ ĪƒĪ‡ÎĩÎ´Î¯ÎąĪƒÎˇĪ‚ -ÎĩĪ€ÎšÎģÎŋÎŗέιĪ‚ ÎąĪĪ‡ÎĩίÎŋĪ… -filler -ÎĩĪ€ÎšÎģÎŋÎŗέιĪ‚ ÎŗĪÎąÎŧÎŧÎąĪ„ÎŋĪƒÎĩΚĪÎŦĪ‚ -Ī€ÎģÎąÎ¯ĪƒÎšÎŋ -glass pane -html container -ÎĩΚÎēÎŋÎŊίδΚÎŋ -ÎĩΚÎēĪŒÎŊÎą -ÎĩĪƒĪ‰Ī„ÎĩĪÎšÎēĪŒ Ī€ÎģÎąÎ¯ĪƒÎšÎŋ -ÎĩĪ„ΚÎēέĪ„Îą -layered pane -ÎģίĪƒĪ„Îą -ÎąÎŊĪ„ΚÎēÎĩίÎŧÎĩÎŊÎŋ ÎģίĪƒĪ„ÎąĪ‚ -ÎŧÎĩÎŊÎŋĪ -ÎĩĪÎŗÎąÎģÎĩΚÎŋθΎÎēΡ ÎŧÎĩÎŊÎŋĪ -ÎąÎŊĪ„ΚÎēÎĩίÎŧÎĩÎŊÎŋ ÎŧÎĩÎŊÎŋĪ -Ī„ÎąÎŧĪ€ÎģĪŽ ÎĩĪ€ÎšÎģÎŋÎŗĪŽÎŊ -ĪƒĪ„ÎŽÎģΡ ĪƒÎĩÎģÎ¯Î´ÎąĪ‚ -ÎģίĪƒĪ„Îą ĪƒĪ„ÎŽÎģΡĪ‚ ĪƒÎĩÎģÎ¯Î´ÎąĪ‚ -Ī„ÎąÎŧĪ€ÎģĪŽ -ÎēÎĩίÎŧÎĩÎŊÎŋ ÎēĪ‰Î´ÎšÎēÎŋĪ -ÎąÎŊιδĪ…ĪŒÎŧÎĩÎŊÎŋ ÎŧÎĩÎŊÎŋĪ -ÎŧĪ€ÎŦĪÎą Ī€ĪÎŋĪŒÎ´ÎŋĪ… -ÎēÎŋĪ…ÎŧĪ€Î¯ Ī€Î¯ÎĩĪƒÎˇĪ‚ -ÎēÎŋĪ…ÎŧĪ€Î¯ radio -ÎąÎŊĪ„ΚÎēÎĩίÎŧÎĩÎŊÎŋ ÎŧÎĩÎŊÎŋĪ radio -Ī„ÎąÎŧĪ€ÎģĪŽ root -ÎēÎĩĪ†ÎąÎģÎ¯Î´Îą ÎŗĪÎąÎŧÎŧÎŽĪ‚ -ÎŗĪÎąÎŧÎŧÎŽ ÎēĪÎģΚĪƒÎˇĪ‚ -Ī„ÎąÎŧĪ€ÎģĪŽ ÎēĪÎģΚĪƒÎˇĪ‚ -δΚιĪ‡Ī‰ĪÎšĪƒĪ„ΚÎēĪŒ -ÎŧĪ€ÎŦĪÎą ÎēĪÎģΚĪƒÎˇĪ‚ -δΚιĪ‡Ī‰Ī Ī„ÎąÎŧĪ€ÎģĪŽ -ÎēÎŋĪ…ÎŧĪ€Î¯ ĪƒĪ„ĪÎŋβΚÎģΚĪƒÎŧÎŋĪ (spin) -ÎŗĪÎąÎŧÎŧÎŽ ÎēÎąĪ„ÎŦĪƒĪ„ÎąĪƒÎˇĪ‚ -Ī€Î¯ÎŊÎąÎēÎąĪ‚ -ÎēÎĩÎģί Ī€Î¯ÎŊÎąÎēÎą -ÎēÎĩĪ†ÎąÎģÎ¯Î´Îą ĪƒĪ„ÎŽÎģΡĪ‚ Ī€Î¯ÎŊÎąÎēÎą -ÎēÎĩĪ†ÎąÎģÎ¯Î´Îą ÎŗĪÎąÎŧÎŧÎŽĪ‚ Ī€Î¯ÎŊÎąÎēÎą -ÎąÎŊĪ„ΚÎēÎĩίÎŧÎĩÎŊÎŋ ÎąĪ€ÎŋĪƒĪ€ĪŽÎŧÎĩÎŊÎŋĪ… ÎŧÎĩÎŊÎŋĪ -Ī„ÎĩĪÎŧÎąĪ„ΚÎēĪŒ -ÎēÎĩίÎŧÎĩÎŊÎŋ -ÎēÎŋĪ…ÎŧĪ€Î¯ ÎĩÎŊÎąÎģÎģÎąÎŗÎŽĪ‚ -ÎĩĪÎŗÎąÎģÎĩΚÎŋθΎÎēΡ -ĪƒĪ…ÎŧβÎŋĪ…ÎģÎŽ ÎĩĪÎŗÎąÎģÎĩίÎŋĪ… -δέÎŊδĪÎŋ -δέÎŊδĪÎŋ Ī€Î¯ÎŊÎąÎēÎą -ÎŦÎŗÎŊĪ‰ĪƒĪ„Îŋ -θĪĪÎą ĪŒĪˆÎˇĪ‚ -Ī€ÎąĪÎŦθĪ…ĪÎŋ -ÎēÎĩĪ†ÎąÎģÎ¯Î´Îą -Ī…Ī€ÎŋĪƒÎ­ÎģΚδÎŋ -Ī€ÎąĪÎŦÎŗĪÎąĪ†ÎŋĪ‚ -ÎĩĪ†ÎąĪÎŧÎŋÎŗÎŽ -ÎąĪ…Ī„ĪŒÎŧÎąĪ„Ρ ĪƒĪ…ÎŧĪ€ÎģÎŽĪĪ‰ĪƒÎˇ -ÎĩĪ€ÎĩΞÎĩĪÎŗÎąĪƒÎ¯Îą ÎĩĪÎŗÎąÎģÎĩΚÎŋθΎÎēΡĪ‚ -ÎĩÎŊĪƒĪ‰ÎŧÎąĪ„ÎŋÎŧέÎŊÎŋ ĪƒĪ…ĪƒĪ„ÎąĪ„ΚÎēĪŒ -ΠĪÎŋĪƒÎ˛ÎŦĪƒÎšÎŧÎŋ ΌÎŊÎŋÎŧÎą -ΤÎŋ ĪŒÎŊÎŋÎŧÎą Ī„ΡĪ‚ ÎĩÎŧĪ†ÎŦÎŊΚĪƒÎˇĪ‚ Ī„ÎŋĪ… ÎąÎŊĪ„ΚÎēÎĩΚÎŧέÎŊÎŋĪ… ÎŧÎŋĪĪ†ÎŋĪ€ÎŋΚΡÎŧέÎŊÎŋ ÎŗΚι Ī€ĪĪŒĪƒÎ˛ÎąĪƒÎˇ ÎąĪ€ĪŒ Ī„ΡÎŊ βÎŋΡθΡĪ„ΚÎēÎŽ Ī„ÎĩĪ‡ÎŊÎŋÎģÎŋÎŗÎ¯Îą -ΠĪÎŋĪƒÎšĪ„ÎŽ Ī€ÎĩĪÎšÎŗĪÎąĪ†ÎŽ -ΠÎĩĪÎšÎŗĪÎąĪ†ÎŽ Ī„ÎŋĪ… ÎąÎŊĪ„ΚÎēÎĩΚÎŧέÎŊÎŋĪ… ÎŧÎŋĪĪ†ÎŋĪ€ÎŋΚΡÎŧέÎŊΡ ÎŗΚι Ī€ĪĪŒĪƒÎ˛ÎąĪƒÎˇ ÎąĪ€ĪŒ Ī„ΡÎŊ βÎŋΡθΡĪ„ΚÎēÎŽ Ī„ÎĩĪ‡ÎŊÎŋÎģÎŋÎŗÎ¯Îą -ΠĪÎŋĪƒÎ˛ÎŦĪƒÎšÎŧÎŋ ΓÎŋÎŊΚÎēĪŒ -ΧĪÎˇĪƒÎšÎŧÎŋĪ€ÎŋΚÎĩίĪ„ιΚ ÎŗΚι ÎĩΚδÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ ĪŒĪ„Κ Ī„Îŋ ÎŧΡĪ„ĪÎšÎēĪŒ έĪ‡ÎĩΚ ÎąÎģÎģιΞÎĩΚ -ΠĪÎŋĪƒÎ˛ÎŦĪƒÎšÎŧΡ ΤΚÎŧÎŽ -La informaciÃŗn de errores almacenada en su sistema no estÃĄ actualizada. Elija ÂĢActualizarÂģ para hacerlo. Si elije ÂĢNo actualizarÂģ forzarÃĄ a informar el error usando los datos antiguos. -Avanzado -FenÃŗmeno Assbarn -Archivo binario: -Bug Buddy -Bug Buddy puede enviar informaciÃŗn de depuraciÃŗn junto a su informe de error. -Las opciones correctas se deben haber seleccionado automÃĄticamente. -Bug Buddy estÃĄ recogiendo informaciÃŗn sobre su cuelgue para enviarla al sistema de seguimiento de errores. Este proceso estÃĄ automatizado, y tardarÃĄ unos pocos minutos. Cuando haya terminado, puede pulsar ÂĢMostrar detalles de depuraciÃŗnÂģ para ver la informaciÃŗn o pulsar ÂĢContinuarÂģ para moverse al siguiente paso en el proceso. -Bug Buddy usa el correo electrÃŗnico para enviar un informe de errores. -Elija cÃŗmo le gustaría que Bug Buddy envíe el correo-e. -Cc: -Archivo core -D_epurar una aplicaciÃŗn colgada o una aplicaciÃŗn en funcionamiento (sÃŗlo expertos) -DepuraciÃŗn -DescripciÃŗn -Descargando archivos -Correo-e: -Incluir un archivo de texto -Guard_arlo en un archivo para que pueda enviar el informe manualmente -Nombre: -Ruta de sendmail: -Elija un componente, versiÃŗn y nivel de severidad. -Haga cualquier correcciÃŗn final al informe de errores. Dese cuenta de que serÃĄ mostrado en https://bugzilla.gnome.org IncluirÃĄ su nombre, direcciÃŗn de correo-e, y quizÃĄ alguna informaciÃŗn acerca de cÃŗmo fallÃŗ la aplicaciÃŗn. Si el documento en el que estaba trabajando contenía informaciÃŗn sensible, quizÃĄ no quiera enviar este informe -Ühtegi rakendust "%s"-i avamiseks ei ole. Sa saad selle asemel selle alla laadida. -Salvesta _kui... -"%s" protokoll ei ole toetatud. -Toetatud protokollid on "http", "https", "ftp", "file", "smb" ja "sftp". -Faili “%s” ei leitud. -Kontrolli faili asukohta ja proovi uuesti. -“%s” pole vÃĩimalik leida. -Kontrolli kas oled Ãŧhendatud internetti ja kas aadress on korrektne. -Juhul kui see leht oli varem olemas, siis arhiveeritud versiooni vÃĩid leida: -"%s" keeldus Ãŧhendusest. -“%s” katkestas Ãŧhenduse. -“%s” ei vasta. -Vigane aadress -Sinu sisestatud aadress on vigane. -“%s” suunati Ãŧmber liiga palju kordi. -Turvakaalutlustel Ãŧmbersuunamine lÃĩpetati. -“%s” nÃĩuab krÃŧptitud Ãŧhendust. -“%s” loobus Ãŧhendusest. -Ühendus serveriga katkes enne kui andmeid lugeda jÃĩuti. -Autonoomses reÅžiimis pole vÃĩimalik dokumenti laadida. -“%s” keelab ligipääsu pordile “%d”. -Proksiserveriga pole vÃĩimalik Ãŧhenduda. -Google'i vahemälust -Internetiarhiivist -Nimetu -KÃĩik failid -Veebilehed -Tekstifailid -Pildid -XML failid -XUL failid -_Vali sertifikaat -Enda tÃĩendamiseks kasutatava sertifikaadi valimine. -Sertifikaadi Ãŧ_ksikasjad -_Ava sertifikaat -_NÃĩustu -Kas usaldad ebakorrektset turvainfot? -Kas Ãŧhenduda ebausaldusväärsesse saiti? -_Usalda seda turvainfot edaspidigi -Üh_endu -Kas vÃĩtad vastu kehtivuse kaotanud turvainfo? -"%s" turvaandmed aegusid %s. -Kas vÃĩtad vastu veel mittekehtiva turvainfo? -%a, %d, %b %Y -Sa peaks veenduma, et su arvuti kell on Ãĩige. -Ühendust "%s"-ga pole vÃĩimalik luua. -"%s" sertifikaatide tÃŧhistusnimekiri (CRL) vajab uuendamist. -Palun pÃļÃļrdu nÃĩuannete saamiseks oma sÃŧsteemihalduri poole. -Kas usaldada sertifitseerimiskeskust? -_Usalda sertifitseerimiskeskust -Kas usaldada uut sertifitseerimiskeskust "%s" veebisaitide tuvastamiseks? -Lien sÊlectionnÊ -SpÊcifie si l'objet AtkHyperlink est sÊlectionnÊ -Nombre d'ancres -Le nombre d'ancres associÊs avec l'objet AtkHyperlink -Fin d'index -La fin de l'index de l'objet AtkHyperlink -DÊbut d'index -Le dÊbut de l'index de l'objet AtkHyperlink -non valide -Êtiquette du raccourci -alerte -animation -curseur -calendrier -canevas -case à cocher -ÊlÊment de menu avec case à cocher -sÊlecteur de couleurs -en-tÃĒte de colonne -liste combinÊe -Êditeur de dates -icône du bureau -cadre du bureau -bouton de rÊglage -boÃŽte de dialogue -panneau des rÊpertoires -zone de dessin -sÊlecteur de fichiers -caractère de remplissage -sÊlecteur de polices -cadre -panneau de verre -conteneur html -icône -image -cadre interne -Êtiquette -panneau superposÊ -liste -ÊlÊment de liste -menu -barre de menus -ÊlÊment de menu -panneau d'options -onglet de page -liste d'onglets de page -tableau de bord -champ de mot de passe -menu contextuel -barre de progression -bouton poussoir -bouton radio -ÊlÊment de menu avec bouton radio -panneau racine -en-tÃĒte de ligne -barre de dÊfilement -panneau de dÊfilement -sÊparateur -case de dÊfilement -panneau divisible -bouton de rÊglages -barre d'Êtat -tableau -cellule de tableau -en-tÃĒte de colonne de tableau -en-tÃĒte de ligne de tableau -ÊlÊment de menu dÊtachable -console -texte -bouton à Êtat -barre d'outils -bulle d'aide -arbre -Tableau arborescent -inconnu -fenÃĒtre de travail -fenÃĒtre -en-tÃĒte de page -bas de page -paragraphe -application -AutocomplÊtion -barre d'Êdition -composant incorporÊ -Nom accessible -Nom d'une instance d'objet formatÊ pour ÃĒtre accessible par les aides techniques -Description accessible -Description d'un objet, formatÊ pour ÃĒtre accessible par les aides techniques -Parent accessible -Est utilisÊ pour notifier que le parent a changÊ -Valeur accessible -áˆŦዲዮ-ቁልፍ -á‹ļሴ `%s'ን ማáˆĩፈጠር አልተá‰ģለምáĻ -አዲáˆĩ á‹ļሴ ፍጠር -በመáŒĢን ላይ... -Ne cÃēðe findan Þone pÃĻÞ -Onlícnesgesceap ungecnÃĄwen -Ų„ØĒŲ†Ų‚ŲŠØ­ ØŖŲŠŲ‘ØŠ ØšŲ…Ų„ŲŠŲ‘ØŠ ŲŠØˇŲ„ب اØŗŲ… اŲ„ØĒŲ‘ØˇØ¨ŲŠŲ‚ ØŖŲŠØļاŲ‹.ØąØŦØ§ØĄŲ‹ Ø˛ŲˆŲ‘د ØŖŲŠØļاŲ‹ بŲ…ؚاŲ…Ų„ ØŗØˇØą ØŖŲˆØ§Ų…Øą --appname. -اŲ„Ų…Ų„Ų اŲ„Ø°ŲŠ ŲŲŠŲ‡ ØŗŲŠØ­ŲØ¸ ØĒŲ‚ØąŲŠØą اŲ„ØŽŲ„اŲ„ -ØĒؚذŲ‘Øą ØĒحدŲŠØ¯ Ų†ØŗŲ‚ اŲ„Ų…Ų„Ų %s -^ØĨØąØŗاŲ„ ØĒŲ‚ØąŲŠØą ØŽŲ„Ų„ -Ø§ØąØŗاŲ„ ØĒŲ‚ØąŲŠØąØ§ Ų„Ųƒ ŲŲ‚Øˇ -Ø­ŲØ¸ اŲ„ØĒŲ‚ØąŲŠØą ŲŲŠ Ų…Ų„Ų -اŲ„ØĒØˇØ¨ŲŠŲ‚ اŲ„Ų…Ų†Ų‡Ø§Øą -اŲ„Ų…Ų„Ų اŲ„Ų†ŲˆØ§ØŠ -īģģ Ø´ŲŠØĄ -اØŗŲ… اŲ„Ų…ŲˆØĩŲˆŲ„ -اīģģØŗŲ… -ØšŲ†ŲˆØ§Ų† اŲ„Ø¨ØąŲŠØ¯ اŲ„اŲ„ŲƒØĒØąŲˆŲ†ŲŠ Ų„Ų„Ų…ŲˆØĩŲˆŲ„ -اŲ„Ø¨ØąŲŠØ¯ اīģģŲ„ŲƒØĒØąŲˆŲ†ŲŠ -اŲ„ØąØ˛Ų…ØŠ اŲ„Ų…Ø­ØĒŲˆŲŠØŠ ØšŲ„Ų‰ اŲ„Ø¨ØąŲ†Ø§Ų…ØŦ -اŲ„ØąØ˛Ų…ØŠ -اØĩØ¯Ø§ØąØŠ اŲ„ØąØ˛Ų…ØŠ -اīģģØĩØ¯Ø§ØąØŠ -اØŗŲ… Ų…Ų„Ų اŲ„Ø¨ØąŲ†Ø§Ų…ØŦ اŲ„Ų…Ų†Ų‡Ø§Øą -اŲ„Ų…Ų„Ų -PID اŲ„Ø¨ØąŲ†Ø§Ų…ØŦ اŲ„Ų…Ų†Ų‡Ø§Øą -PID -اŲ„Ų…Ų„Ų اŲ„Ų†ŲˆØ§ØŠ Ų…Ų† اŲ„Ø¨ØąŲ†Ø§Ų…ØŦ -Ų…Ų„Ų اŲ„Ų†Øĩ اŲ„Ø°ŲŠ ØŗŲŠŲØļŲ…Ų† ŲŲŠ اŲ„ØĒŲ‚ØąŲŠØą -Ų„Ų… ŲŠŲ…ŲƒŲ† اŲ„ØšØĢŲˆØą ØšŲ„Ų‰ Ų‚ØˇØšØŠ باØŗŲ… %s ØšŲ†Ø¯ %s -Ų„Ų… ŲŠŲ†ØĒŲ‡ gdb Ų…Ų† ØĒŲ„Ų‚ŲŠ Ų…ØšŲ„ŲˆŲ…اØĒ اŲ„ØĒŲ†Ų‚ŲŠØ­. -ا اŲ‚ØĒŲ„ ØšŲ…Ų„ŲŠØŠ gdb (ØŗØĒŲƒŲˆŲ† Ų…ØĩŲŲˆŲØŠ ØĒØĒبؚ اŲ„ØŗŲŠØą Ų…Ų†Ų‚ŲˆØĩØŠ)؟ -Ų„Ų‚د ØŽØąØŦ gdb Ų…ØŗبŲ‚ا -Ø­ŲØ¸ ØĒØĒبؚ اŲ„ØŗŲŠØą -اŲ„ØąØŦØ§ØĄ اŲ„اŲ†ØĒØ¸Ø§Øą حاŲ„Ų…ا ŲŠØ­ŲØ¸ باŲƒ بدŲŠ Ų…ØĩŲŲˆŲØŠ ØĒØĒŲŠØš اŲ„ØŗŲŠØą... -Ų„Ų… ØĒØ­ŲØ¸ Ų…ØĩŲŲˆŲØŠ ØĒØĒبؚ اŲ„ØŗŲŠØą ŲŲŠ %s: -āĻ¸āĻ‚āĻ˛āĻžāĻĒ āĻĒā§°āĻŋāĻšā§Ÿ āĻĒā§āĻ°āĻŽāĻžāĻŖ -āĻ¸āĻ‚āĻ˛āĻžāĻĒ āĻ¸āĻ‚āĻ•ā§āĻ°āĻžāĻ¨ā§āĻ¤ āĻ¤āĻĨā§āĻ¯ -āĻ¸āĻ‚āĻ˛āĻžāĻĒ āĻ¸āĻ‚āĻ•ā§āĻ°āĻžāĻ¨ā§āĻ¤ āĻ¸āĻ¤āĻ°ā§āĻ•āĻŦāĻžāĻŖā§€ -āĻ¸āĻ‚āĻ˛āĻžāĻĒ āĻ¸āĻ‚āĻ•āĻĒā§āĻ°āĻļā§āĻ¨ -āĻŸāĻ¨āĻž āĻ†ā§°ā§ āĻā§°āĻŋ āĻĻāĻŋā§ŸāĻž -āĻŦāĻšā§āĻŦāĻžā§° āĻŸāĻ¨āĻž āĻ†ā§°ā§ āĻā§°āĻŋ āĻĻāĻŋā§ŸāĻž -āĻ¯ā§‹āĻ— -āĻ•āĻžāĻ°ā§āĻ¯āĻ•ā§° -āĻĄāĻžāĻ  -āĻŦāĻžāĻ¤āĻŋāĻ˛ -āĻ¸āĻŋāĻĄā§°ā§‚āĻĒāĻžāĻ¨ā§āĻ¤ā§° āĻ•ā§°āĻž -āĻ•āĻĒāĻŋ -āĻ•āĻŸāĻž -āĻŽā§‹āĻ›āĻž -āĻšāĻ˛ā§‹ā§ąāĻž -āĻŦāĻŋāĻšā§°āĻž -āĻŦāĻŋāĻšā§°āĻž āĻ†ā§°ā§ āĻĒā§°āĻŋāĻŦā§°āĻ¤āĻ¨ āĻ•ā§°āĻž -āĻĢā§āĻ˛āĻĒāĻŋ -āĻ¤āĻ˛āĻŋāĻ˛ā§ˆ @āĻŽā§‚āĻ§āĻšāĻ˛ā§ˆ āĻ¯ā§‹ā§ąāĻž -āĻĒāĻŋāĻ›āĻ˛ā§ˆ āĻ¯ā§‹ā§ąāĻž -āĻ¤āĻ˛āĻ˛ā§ˆ āĻ¯ā§‹ā§ąāĻž -āĻ†āĻ—āĻ˛ā§ˆ āĻ¯ā§‹ā§ąāĻž -āĻ‰āĻĒā§°āĻ˛ā§ˆ āĻ¯ā§‹ā§ąāĻž -āĻ¸āĻšāĻžā§Ÿ -āĻ˜ā§° -āĻ¸ā§‚āĻšāĻŋāĻĒāĻ¤ā§āĻ° -āĻŸāĻŋāĻĢāĻžāĻ‡ āĻ•ā§°āĻž -āĻ¸ā§āĻˇāĻŽāĻ­āĻžāĻŦā§‡ āĻœāĻžāĻ¸ā§āĻŸāĻŋāĻĢāĻžāĻ‡ āĻ•ā§°āĻž -āĻŦāĻžāĻāĻ“ āĻĒāĻŋāĻ¨ā§° āĻĒā§°āĻž āĻœāĻžāĻ¸ā§āĻŸāĻŋāĻĢāĻžāĻ‡ āĻ•ā§°āĻž -āĻ¸ā§‹āĻ āĻĒāĻŋāĻ¨ā§° āĻĒā§°āĻž āĻœāĻžāĻ¸ā§Ÿ -āĻ āĻŋāĻ• āĻ†āĻ›ā§‡ -āĻ–ā§‹āĻ˛āĻž -āĻ†āĻ āĻž āĻĻāĻŋā§ŸāĻž -āĻĒāĻ›āĻ¨ā§āĻĻ -āĻ›āĻĒāĻž āĻ•ā§°āĻž -āĻ›āĻĒāĻžā§° āĻ†āĻ—āĻ¤ā§‡ āĻ›ā§‹ā§ąāĻž -āĻŦā§ˆāĻļāĻŋāĻˇā§āĻŸ -āĻĒā§āĻ°āĻ¸ā§āĻĨāĻžāĻ¨ -āĻ† -āĻ¸āĻ‚ā§°āĻ•ā§āĻˇāĻŋāĻ¤ āĻ•āĻĒāĻŋ āĻŦā§āĻ¯āĻŦāĻšāĻžā§° āĻ•ā§°āĻž -āĻ¸āĻ‚ā§°āĻ•ā§āĻˇāĻŖ -āĻ¨āĻ¤ā§āĻ¨ āĻ¨āĻžāĻŽā§‡ā§°ā§‡ āĻ¸āĻ‚ā§°āĻ•ā§āĻˇāĻŖ -ā§°āĻ‚ āĻŦāĻžāĻ›āĻž -āĻĢāĻ¨ā§āĻŸ āĻŦāĻžāĻ›āĻž -āĻ‰āĻ° -āĻŦāĻžāĻ¨āĻžāĻ¨-āĻĒā§°ā§€āĻ•ā§āĻˇāĻŖ -ā§°āĻ–āĻž -āĻŽāĻžāĻœā§‡ā§°ā§‡ ā§°ā§‡āĻ–āĻž āĻ…āĻ•āĻžāĻ -āĻŽā§‹āĻ›āĻž ā§°āĻĻā§āĻĻ āĻ•ā§°āĻž -āĻ¤āĻ˛ā§‡ā§°ā§‡ ā§°ā§‡āĻ–āĻž āĻ…āĻ•āĻžāĻ -āĻšā§Ÿ -ā§§ā§Ļā§Ļ āĻĄāĻžāĻ™ā§° āĻ•ā§°āĻž -āĻ¸ā§°ā§ āĻ•ā§°āĻž -āĻ¸āĻŽā§Ÿā§°āĻ•ā§āĻˇāĻ• -āĻ¸āĻŽā§Ÿā§°āĻ•ā§āĻˇāĻ• āĻ¸ā§āĻĨāĻ—āĻŋāĻ¤ āĻ•ā§°āĻž -āĻ†āĻŦāĻ°ā§āĻœāĻ¨āĻž -āĻ†āĻŦāĻ°ā§āĻœāĻ¨āĻž āĻĒā§‚āĻ°ā§āĻŖ -āĻ¨āĻŽā§āĻŦā§°^āĻŽāĻŋāĻĄāĻŋ -āĻŽāĻžāĻ‡āĻ•ā§āĻ°ā§‹āĻĢā§‹āĻ¨ -āĻ˛āĻžāĻ‡āĻ¨ āĻ‡āĻ¨ -āĻŽā§‡āĻ‡āĻ˛ -āĻŽā§‡āĻ‡āĻ˛ āĻ—ā§āĻ°āĻšāĻŖ -āĻŽā§‡āĻ‡āĻ˛ āĻĒā§āĻ°ā§‡ā§°āĻŖ -āĻŽā§‡āĻ‡āĻ˛ā§° āĻ‰āĻ¤ā§āĻ¤ā§° -āĻŽā§‡āĻ‡āĻ˛ āĻ†āĻ—āĻ˛ā§ˆāĻ•ā§āĻ¤ āĻ•ā§°āĻž -āĻ•āĻŋāĻ¤āĻžāĻĒ ā§°āĻ™āĻž -āĻ•āĻŋāĻ¤āĻžāĻĒ āĻ¸ā§‡āĻ‰āĻœā§€ā§ŸāĻž -āĻ•āĻŋāĻ¤āĻžāĻĒ āĻ¨ā§€āĻ˛āĻž -āĻ•āĻŋāĻ¤āĻžāĻĒ āĻšāĻžāĻ˛āĻ§ā§€ā§ŸāĻž -āĻ•āĻŋāĻ¤āĻžāĻĒ āĻ–ā§‹āĻ˛āĻž -āĻāĻ•āĻžāĻ§āĻŋāĻ• āĻĒā§‚āĻ°ā§āĻŖ āĻ•ā§°āĻž -āĻŸā§‡āĻ•ā§āĻ¸āĻŸ āĻ…āĻŦāĻšā§āĻ›ā§‡āĻĻ(Indent) -āĻŸā§‡āĻ•ā§āĻ¸āĻŸ āĻ…āĻŦāĻšā§āĻ›ā§‡āĻĻ(Indent) ā§°āĻĻā§āĻĻ āĻ•ā§°āĻž -āĻŸā§‡āĻ•ā§āĻ¸āĻŸ āĻŦā§āĻ˛ā§‡ā§°āĻ¯ā§āĻ•ā§āĻ¤ āĻ¤āĻžāĻ˛āĻŋāĻ•āĻž -āĻĒā§°āĻŋāĻšā§Ÿ āĻĒā§āĻ°āĻŽāĻžāĻŖ āĻĒā§āĻ°āĻ•ā§āĻ°āĻŋā§ŸāĻž -(āĻ…āĻ™ā§āĻ•ā§āĻ° āĻŦāĻžāĻ‚āĻ˛āĻž āĻĒā§āĻ°āĻ•āĻ˛ā§āĻĒā§‡āĻ° āĻ¤āĻ°āĻĢ āĻĨā§‡āĻ•ā§‡) -āĻĄā§āĻ¯āĻžāĻļāĻžāĻ° -āĻĄā§āĻ¯āĻžāĻļāĻžāĻ° āĻāĻ•āĻŸāĻŋ āĻĒā§āĻ°ā§‡āĻĄāĻŋāĻ•āĻŸāĻŋāĻ­ āĻŸā§‡āĻ•ā§āĻ¸āĻŸ āĻāĻ¨ā§āĻŸ %ld āĻ¸ā§‡āĻ•ā§‡āĻ¨ā§āĻĄā§‡ā§‡āĻļāĻ¨ -%d āĻ…āĻ•ā§āĻˇāĻ° āĻ†āĻ‰āĻŸāĻĒā§āĻŸ -ā§Ģ: -ā§Ŧ: -ā§­: -ā§Ž: -ā§¯: -āĻŦāĻ°ā§āĻŖ āĻ¨āĻŋāĻ°ā§āĻŦāĻžāĻšāĻ¨: -āĻŦāĻžāĻŸāĻ¨ āĻ¨āĻŋā§ŸāĻ¨ā§āĻ¤ā§āĻ°āĻŖ āĻ¸ā§‡āĻŸāĻžāĻĒ: -āĻ°āĻ‚ -āĻ¨āĻŋā§ŸāĻ¨ā§āĻ¤ā§āĻ°āĻŖā§‡āĻ° āĻ§āĻ°āĻŖ: -āĻ…āĻ¨ā§āĻ¯āĻžāĻ¨ā§āĻ¯ āĻĒāĻ›āĻ¨ā§āĻĻāĻ¸āĻŽā§‚āĻš: -āĻĒāĻžāĻ°āĻŋāĻĒāĻžāĻ°ā§āĻļā§āĻŦāĻŋāĻ• āĻ…āĻŦāĻ¸ā§āĻĨāĻžāĻ¨: -āĻšāĻžāĻ˛ā§ āĻ•āĻ°āĻž āĻšāĻšā§āĻ›ā§‡ āĻ“ āĻŦāĻ¨ā§āĻ§ āĻ•āĻ°āĻž āĻšāĻšā§āĻ›ā§‡: -āĻŸāĻ—āĻ˛ āĻŦāĻžāĻŸāĻ¨ āĻŽā§‹āĻĄ: -āĻĒā§āĻ°āĻĻāĻ°ā§āĻļāĻ¨ āĻ¸āĻŽā§/b> -X/Y āĻ…āĻ•ā§āĻˇ: -Y āĻ…āĻ•ā§āĻˇ āĻ¸āĻŽā§āĻŦāĻ¨ā§āĻ§ā§€ā§Ÿ āĻĒāĻ›āĻ¨ā§āĻĻāĻ¸āĻŽā§‚āĻš: -āĻ…āĻ—ā§āĻ°āĻ¸āĻ° -āĻŦāĻ°ā§āĻŖ -āĻŦāĻ°ā§āĻŖ āĻĄāĻŋāĻ‰āĻĒāĻ° -āĻŦāĻžāĻŸāĻ¨ āĻ¨āĻŋā§ŸāĻ¨ā§āĻ¤ā§āĻ°āĻŖ āĻ¸ā§‡āĻŸāĻžāĻĒ -āĻŦāĻžāĻŸāĻ¨ āĻŽā§‹āĻĄ -āĻŦāĻžāĻŸāĻ¨: -āĻ¸ā§āĻŦā§ŸāĻ‚āĻ•ā§āĻ°ā§€ā§ŸāĻ­āĻžāĻŦā§‡ āĻ°āĻ™ā§‡āĻ° āĻ¸ā§āĻ•ā§€āĻŽ āĻŦāĻĻāĻ˛āĻžāĻ“ -āĻ°āĻ‚^ āĻ•āĻŋāĻ›ā§ āĻ•āĻĒāĻŋ āĻ•āĻ° (_āĻ¸) -āĻĨāĻžāĻŽāĻžāĻ° āĻĒāĻ°ā§‡ āĻ¸āĻŽāĻ¸ā§āĻ¤ āĻ•āĻŋāĻ›ā§ āĻ•āĻĒāĻŋ āĻ•āĻ° -āĻ•āĻžāĻŸā§‹ -āĻ†āĻŦāĻ°ā§āĻ¤āĻŽāĻžāĻ¨ āĻŦāĻžāĻŸāĻ¨ āĻŽā§‹āĻĄ -āĻĄā§āĻ¯āĻžāĻļāĻžāĻ°āĻ¸ā§‡āĻ° āĻ…āĻŦāĻ¸ā§āĻĨāĻžāĻ¨ā§‡ āĻļā§āĻ°ā§āĻ° āĻœāĻ¨ā§āĻ¯ā§‡ āĻŽāĻ§ā§āĻ¯āĻŽ āĻ°ā§‡āĻ–āĻžāĻ° āĻĨā§‡āĻ•ā§‡ āĻĻā§āĻ°āĻ¤ā§āĻŦ: -āĻ¨ā§€āĻšā§‡/ā§Š: -āĻŦāĻžāĻ•ā§āĻ¸ā§‡āĻ° āĻ¸ā§€āĻŽāĻž āĻ†āĻāĻ•ā§‹ -āĻ•āĻŦāĻžāĻŸāĻ¨ āĻ¨āĻŋā§ŸāĻ¨ā§āĻ¤ā§āĻ°āĻŖ āĻ¸āĻŽā§āĻŦāĻ¨ā§āĻ§ā§€ā§Ÿ āĻĒāĻ›āĻ¨ā§āĻĻ āĻ¸āĻŽā§āĻĒāĻžāĻĻāĻ¨ āĻ•āĻ°ā§āĻ¨ -āĻ…āĻ¨ā§āĻ¯ āĻ‰āĻ‡āĻ¨ā§āĻĄā§‹-āĻ¤ā§‡ āĻ˛ā§‡āĻ–ā§‹ -āĻ†āĻ‡āĻŸā§āĻ°ā§āĻ¯āĻžāĻ•āĻžāĻ° āĻŽā§‹āĻĒāĻ° āĻāĻŦāĻ‚ āĻ¨āĻŋāĻš āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°ā§‡āĻŋāĻ¤ āĻĨāĻžāĻ•āĻ˛ā§‡, āĻ‰ -āĻ•ā§‹āĻ…āĻ°ā§āĻĄāĻŋāĻ¨ā§‡āĻŸ āĻ¸āĻžāĻŽāĻ¨ā§‡āĻ° āĻŦāĻž āĻĒā§‡āĻ›āĻ¨ā§‡āĻ° āĻĻāĻŋāĻ•ā§‡ āĻ†āĻŦāĻ°ā§āĻ¤ āĻšāĻŦā§‡āĨ¤ āĻ†āĻ° āĻĄāĻžāĻ¨ āĻ āĻ¨āĻŋāĻ°ā§āĻŦāĻžāĻšāĻŋāĻ¤ āĻŽāĻžāĻ¨āĻŸāĻŋ āĻ¨āĻŋāĻļā§āĻšāĻŋāĻ¤ āĻ•āĻ°āĻŦ -āĻ¨āĻ¤ā§āĻ¨ āĻĢāĻžāĻ‡āĻ˛ -āĻ¸āĻžāĻ§āĻžāĻ°āĻŖ -y āĻ…āĻ‚āĻļāĻŸāĻŋāĻ•ā§‡ āĻ¯āĻ¤āĻ—ā§āĻ˛āĻŋ āĻĒāĻŋāĻ•ā§āĻ¸ā§‡āĻ˛ āĻĻāĻŋā§Ÿā§‡ āĻĸāĻžāĻ•āĻž āĻšāĻŦā§‡ -āĻāĻ• āĻŽāĻžāĻ¤ā§āĻ°āĻŋāĻ• āĻŽā§‹āĻĄ -āĻĢāĻžāĻ‡ā§ŸāĻŋāĻ• āĻŦāĻŋāĻ°āĻ¤āĻŋ -āĻĄāĻžāĻ¨ āĻĨā§‡āĻ•ā§‡ āĻŦāĻžāĻŽāĻĻāĻŋāĻ•ā§‡ -āĻĄāĻžāĻ¨/ā§Ē -āĻĢāĻžāĻ‡āĻ˛ āĻ¸āĻ‚āĻ°āĻ•ā§āĻˇāĻŖ āĻ•āĻ°ā§‹ -āĻ¨āĻ¤ā§āĻ¨ āĻ¨āĻžāĻŽā§‡ āĻĢāĻžāĻ‡āĻ˛ āĻ¸āĻ‚āĻ°āĻ•ā§āĻˇāĻŖ -āĻĢāĻ¨āĻ¸ā§āĻĨāĻžāĻ¨ āĻĻā§‡āĻ–āĻžāĻ“ -āĻ—āĻ¤āĻŋ āĻ¨āĻŋāĻ°ā§āĻĻā§‡āĻļāĻ• āĻ¸ā§āĻ˛āĻžāĻ‡āĻĄāĻžāĻ° āĻĻā§‡āĻ–āĻžāĻ“ -āĻŸā§āĻ˛āĻŦāĻžāĻ° āĻĻā§‡āĻ–āĻžāĻ“ -āĻĨāĻžāĻŽāĻ˛ā§‡ āĻ•āĻĨāĻž āĻŦā§‹āĻ˛ā§‹ -āĻ—āĻ¤āĻŋ -āĻŦāĻžāĻŽāĻ°āĻž āĻšāĻ˛ā§‡ āĻļā§āĻ°ā§ āĻ•ā§‹āĻ°ā§‹ -āĻ¸ā§āĻĒā§‡āĻ¸ āĻŦāĻžāĻ° āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻž āĻšāĻ˛ā§‡ āĻļā§āĻ°ā§ āĻ•ā§‹āĻ°ā§‹ -āĻŽāĻžāĻ‰āĻ¸ā§‡āĻ° āĻ…āĻŦāĻ¸ā§āĻĨāĻžāĻ¨ āĻŦāĻĻāĻ˛ āĻ•āĻ°āĻž āĻšāĻ˛āĻŽā§āĻĒ āĻ•ā§‹āĻ°ā§‹ -āĻ‰āĻĒāĻ° āĻĨā§‡āĻ•ā§‡ āĻ¨ā§€āĻšā§‡ -āĻĒā§āĻ°āĻļāĻŋāĻ•ā§āĻˇāĻŖ āĻšāĻ˛āĻ›ā§‡ -āĻĄā§āĻ¯āĻžāĻļāĻžāĻ°āĻ•ā§‡ āĻĒā§āĻ°āĻļāĻŋāĻ•ā§āĻˇāĻŖ āĻĻā§‡āĻ“ā§ŸāĻž āĻšāĻšā§āĻ›ā§‡ - āĻ…āĻ¨ā§āĻ—ā§āĻāĻ‡ āĻ•āĻ¨ā§āĻŸā§āĻ°ā§‹āĻ˛āĻŸāĻŋ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°ā§āĻ¨ āĻ¯āĻĻāĻŋ āĻ†āĻĒāĻ¨āĻŋ āĻ†āĻĒāĻ¨āĻžāĻ° āĻ˛ā§‡āĻŸāĻžāĻ° āĻŦāĻ•ā§āĻ¸ā§‡āĻ° -āĻ†āĻ•āĻžāĻ° āĻ¸āĻŽāĻ¨ā§āĻŦā§Ÿ āĻ•āĻ°āĻ¤ā§‡ āĻšāĻžāĻ¨āĨ¤ āĻŽāĻ¨ā§‡ āĻ°āĻžāĻ–āĻŦā§‡āĻ¨ āĻ‰āĻšā§āĻš āĻŽāĻžāĻ¨ āĻ¨āĻŋāĻ°ā§āĻŦāĻžāĻšāĻ¨ -āĻ†āĻĒāĻ¨āĻžāĻ° āĻ˛ā§‡āĻ–āĻžāĻ° āĻ—āĻ¤āĻŋāĻ•ā§‡ āĻ•āĻŽāĻŋā§Ÿā§‡ āĻĻāĻŋāĻŦā§‡ -āĻ–ā§āĻŦ āĻŦā§œ -āĻĻāĻ°ā§āĻļāĻ¨ -āĻĒāĻ°āĻŋāĻšāĻŋāĻ¤āĻŋ (_āĻĒ) -āĻĢāĻžāĻ‡āĻ˛ā§‡āĻ° āĻļā§‡āĻˇā§‡ āĻ¯āĻŸ (_āĻĄ) -āĻĄā§āĻ¯āĻžāĻļāĻžāĻ° āĻĢāĻ¨ā§āĻŸā§‡āĻ° āĻ¸āĻžāĻ‡āĻœ (_āĻĄ) -āĻ¸āĻŽā§āĻĒāĻžāĻĻāĻ¨ (_āĻŽ) -āĻĢāĻ¨ā§āĻŸ āĻ¸āĻŽā§āĻĒāĻžāĻĻāĻ¨ (_āĻŸ) -āĻĢāĻžāĻ‡āĻ˛ (_āĻĢ) -āĻ¸āĻšāĻ•ā§āĻˇāĻŖ āĻŸā§‡āĻ•ā§āĻ¸āĻŸ āĻ‡āĻŽāĻĒā§‹āĻ°ā§āĻŸ āĻ•āĻ°ā§‹ (_āĻ‡) -āĻĒāĻ›āĻ¨ā§āĻĻāĻ¸āĻŽā§‚āĻš (_āĻĒ) -āĻĒā§āĻ°ā§‹āĻ¨ā§‹ āĻĢāĻ¨ā§āĻŸā§‡ āĻĢā§‡āĻ°āĻ¤ āĻ¯āĻžāĻ“ (_āĻĢ) -āĻ…āĻ†āĻ‡āĻˆāĻ‰āĻŠ -āĻāĻ‡ āĻŽā§‹āĻĄāĻŸāĻŋ āĻ¨āĻŋāĻ°ā§āĻŦāĻžāĻšāĻŋāĻ¤ āĻĨāĻžāĻ•āĻ˛ā§‡, āĻ‰āĻĒāĻ° āĻāĻŦāĻ‚ āĻ¨āĻŋāĻš āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°ā§‡ -āĻŋāĻ°ā§āĻŦāĻžāĻšāĻ¨ āĻ¸ā§€āĻŽāĻž -āĻ•āĻžāĻ°ā§āĻ¸āĻžāĻ° āĻĨā§‡āĻ•ā§‡ āĻ¨āĻŋāĻ°ā§āĻŦāĻžāĻšāĻŋ -čĢžã—ãĻãŋぞすかīŧŸ -かしãĒčĒč¨ŧãƒ‰ãƒĄã‚¤ãƒŗ: %s -į™ē行者: %s -IMAP ã‚ĩãƒŧバからäēˆæœŸã—ãĒいåŋœį­”がありぞした: %s -%s (ポãƒŧト %s) へæŽĨįļšã§ããžã›ã‚“でした: %s -ニãƒĨãƒŧã‚šãƒģ゚トã‚ĸãĢフりãƒĢダをį”Ÿæˆã§ããžã›ã‚“: äģŖわりãĢčŗŧčĒ­ã‚’į”ŗčĢ‹ã—ãĻ下さい。 -指厚されたフりãƒĢダ名は不遊切です: %s -ėž‘ė—…ė¤‘ė¸ ëŦ¸ė„œ 내ė— ęŗĩ개하기 ë¯ŧ감한 ė •ëŗ´ę°€ 들ė–´ ėžˆë‹¤ëŠ´, -ė´ 버그 ëŗ´ęŗ ė„œëĨŧ ëŗ´ë‚´ė§€ ė•ŠëŠ” 편ė´ ėĸ‹ė„ 것ėž…니다. -ęŧ­ ė˜ė–´ëĄœ ëŗ´ęŗ ė„œëĨŧ ėž‘ė„ąí•˜ė‹­ė‹œ>ė˜¤. -버그ëĨŧ ė•ŒëĻ´ ė œí’ˆė´ëĻ„ė´ë‚˜ 프로그램ė„ ė„ íƒí•˜ė‹­ė‹œė˜¤. -ëŗ´ęŗ í•˜ë ¤ëŠ” 버그가 가ėžĨ >ėžėŖŧ ëŗ´ęŗ ë˜ëŠ” 버그 ė¤‘ė— 들ė–´ ėžˆë‹¤ëŠ´ ėž ė‹œ 기다려 ėŖŧė‹­ė‹œė˜¤. -버그가 ė´ë¯¸ ëŗ´ęŗ ë˜ė–´ ėžˆë‹¤ëŠ´, 또 ëŗ´ęŗ í•˜ė§€ëŠ” 말ė•„ ėŖŧė‹­ė‹œė˜¤. -버그버디가 ė œí’ˆ ëĒŠëĄ>ė„ ę°ąė‹ í•˜ëŠ” 동ė•ˆ -기다려 ėŖŧė‹­ė‹œė˜¤. -프로ė„¸ėŠ¤ ID: -ė €ėžĨ할 파ėŧ: -ëŗ´ęŗ ëĨŧ ėƒˆ ė´ëĻ„ėœŧ로 ė €ėžĨ하기... -ė € -ėžĨ: -sendmail ė„¤ė • -ė‹Ŧ각ė„ą: -프로그램 ëŗ´ę¸°(_A) -디버깅 ė •ëŗ´ ëŗ´ė—ŦėŖŧ기(_D) -ė œí’ˆ ëŗ´ę¸°(_P) -다ėŒė˜ 가ėžĨ 많ė´ ëŗ´ęŗ ë˜ëŠ” 버그 ëŗ´ė—ŦėŖŧ기: -간단히 -ė‹œėž‘ -ė¤‘ė§€ -또다ëĨ¸ >버그ëĨŧ ëŗ´ë‚´ę¸° -ėš”ė•Ŋ -프로그램ė´ ė˜Ŧ바ëĨ´ę˛Œ 동ėž‘하ė§€ ė•ŠėŠĩ니다(_A) -ëŦ¸ė„œę°€ 틀렸ėŠĩ니>다(_D) -번ė—­ė´ 틀렸ėŠĩ니다(_T) -받는 ė‚Ŧ람: -sendmail ė§ė ‘ ė‚ŦėšŠ(_S) -버ė „: -ė—…데ė´ -트 하ė§€ ė•ŠėŒ(_D) -ëš ė§„ 기ëŠĨė„ ėš”ė˛­í•˜ę¸°(_R) -ė—…데ė´íŠ¸(_U) -ėģ´íŦ넌트 -ė„¤ëĒ… -ė „ėžëŠ” -ėŧ ė •ëŗ´ -ė™„ëŖŒë¨ -gdb -ė†Œę°œ -메ėŧ ė„¤ė • -가ėžĨ ėžĻė€ -ė œí’ˆ -%2$d개ė¤‘ %1$d개 ID -ė œ>품ëĒ… -ėģ´íŦ넌트 -버그버디ė—ė„œ '%s'ė„(ëĨŧ) ė—´ ėˆ˜ ė—†ėŠĩ니다. -버그버디가 ė œëŒ€ëĄœ ė„¤ėš˜ë˜ė—ˆëŠ”ė§€ 확ė¸í•˜ė‹­ė‹œė˜¤. -버그버디는 ė§€ę¸ˆ ėĸ…ëŖŒí•Šë‹ˆë‹¤. -버그버디ė—ė„œ 버그ëĨŧ ė–´ë””ė— ė œėļœí•  ė§€ė— 관한 ė •ëŗ´ëĨŧ ėŊė§€ ëĒģ했ėŠĩ니다. -버그버디가 ė œëŒ€ëĄœ ė„¤ėš˜ë˜ė—ˆëŠ” ė§€ 확ė¸í•˜ė‹­ė‹œė˜¤. -@圖像/標įą¤æĄ† -在č­Ļå‘Šå°čŠąčĻ–įĒ—中īŧŒåŒ…圍標įą¤åŠåœ–åƒæĄ†įˇšįš„é—ŠåēĻ -č­Ļå‘ŠéĄžåž‹ -č­Ļ告įš„éĄžåž‹ -č­Ļ告按鈕 -å‡ēįžåœ¨č­Ļå‘Šå°čŠąčĻ–įĒ—中įš„按鈕 -éĄ¯į¤ēæ›´å¤šč¨Šæ¯(_D) -文字 -標įą¤ä¸­įš„æ–‡>字。 -對éŊŠæ–šåŧ -文字在標įą¤ä¸­æŽ’列時įš„對éŊŠæ–šåŧã€‚厃不會åŊąéŸŋ標įą¤å…ƒäģļæœŦčēĢ在įŠēäŊå…§æŽ’列 -įš„äŊįŊŽã€‚čŠ˛æ–šéĸįš„čŗ‡æ–™čĢ‹åƒč€ƒ GtkMisc::xalign。 -æ›čĄŒ -åĻ‚選į”¨æœŦ選項īŧŒį•ļ字åĨå¤Ēé•ˇæ™‚æœƒ -č‡Ēå‹•æ›čĄŒã€‚ -游標äŊįŊŽ -游標į›Žå‰įš„äŊįŊŽīŧŒäģĨå­—å…ƒčĄ¨į¤ē。 -選擇į¯„圍 -垞游標äŊįŊŽč‡ŗ選擇區 -域æœĢįĢ¯äš‹é–“įš„字元數į›Žã€‚ -全部選取 -čŧ¸å…Ĩæŗ• -GConf 錯čĒ¤īŧš -GConf 錯čĒ¤īŧš %s -æ‰€æœ‰é€˛ä¸€æ­Ĩįš„錯čĒ¤č¨Šæ¯åĒ會在įĩ‚įĢ¯æŠŸä¸­éĄ¯į¤ē。 -GConf 錯čĒ¤ -01/01/00, 01:00 上午 -1/1/00, 1:00 上午 - 1/ 1/00, 1:00 上午 -æ˛’æœ‰é¸å–äģģäŊ•åœ–像。 -čĢ‹æŒ‰ä¸‹åœ–像䞆選取厃。 -æ˛’æœ‰é¸å–äģģäŊ•æąčĨŋ -預設 -圖į¤ē -名į¨ą -æ˛’æœ‰é¸å–äģģäŊ•į¨‹åŧ -%s 文äģļ -不明 -選取一į¨‹åŧäž†é–‹å•Ÿ %s åŠéĄžåž‹į‚ē“%s”įš„æĒ”æĄˆ -į„Ąæŗ•åŸˇčĄŒį¨‹åŧ ->į„Ąæŗ•æ‰žåˆ°â€˜%s’ -į„Ąæŗ•æ‰žåˆ°į¨‹åŧ -į„Ąæŗ•åŠ å…Ĩį¨‹åŧ -į„Ąæŗ•å°‡į¨‹åŧåŠ å…Ĩį¨‹åŧčŗ‡æ–™åēĢ中 -選取一個į¨‹ -åŧ -äģĨå…ļ厃應į”¨į¨‹åŧé–‹å•Ÿ -選取一į¨‹åŧäž†æĒĸčĻ–厃įš„描čŋ°ã€‚ -äŊŋį”¨č‡Ē訂įš„指äģ¤(_C) -į€čĻŊ(_B)... -開啟(_O) -äģĨå…ļ厃į¨‹åŧé–‹å•Ÿ %såŠéĄžåž‹į‚ē“%s”įš„æĒ”æĄˆīŧš -加å…Ĩ(_A) -加å…Ĩį¨‹åŧ -您可äģĨ按下取æļˆäž†åœæ­ĸ這æŦĄæ“äŊœã€‚ -čŗ‡č¨Š -č­Ļ告 -錯čĒ¤ -å•éĄŒ -īŧˆį„Ąæ•ˆįš„įĩąä¸€įĸŧīŧ‰ -čĻ†å¯ĢæĒ”æĄˆ %sīŧŸ -æĒ”æĄˆåˇ˛įļ“存在。您įĸē厚čĻčĻ†č“‹åŽƒīŧŸ -į•Ĩ過 -čĻ†å¯Ģ -å„˛å­˜ %s 時į™ŧį”ŸéŒ¯>čĒ¤ã€‚ -重čŠĻ -į„Ąæŗ•æąē厚į›Žįš„地 uri。 -į„Ąæŗ•æąē厚 %s įš„æĒ”æĄˆæ ŧåŧ -čĢ‹äŊŋį”¨ä¸€å€‹åˆéŠįš„æĒ”æĄˆå -į¨ąåžŒįŊŽå­—īŧŒæˆ–選擇一個æĒ”æĄˆæ ŧåŧã€‚ -å„˛å­˜åœ–į‰‡æ™‚į™ŧį”ŸéŒ¯čĒ¤ã€‚ -列印 -預čĻŊ列印 -您įĸē厚čĻå°‡ - %i 個æĒ”æĄˆä¸Ÿé€˛å›žæ”ļį­’īŧŸ -į„Ąæŗ•å­˜å–回æ”ļį­’。 -į•ļåˆĒ除圖į‰‡ %s 時į™ŧį”ŸéŒ¯čĒ¤ -%i x %i 像į´  - %s %i%% -原因īŧš%s -čŧ‰å…Ĩ %s įš„圖į‰‡å¤ąæ•— -æĒ”æĄˆ(_F) -įˇ¨čŧ¯(_E) -éĄ¯į¤ē(_V) -圖į‰‡(_I) -æą‚åŠŠ(_H) -新åĸžčĻ–įĒ—(_N) -開啟新čĻ–įĒ— -開啟(_O)... -開啟æĒ”æĄˆ -開啟čŗ‡æ–™å¤ž(_F)... -開啟čŗ‡æ–™å¤ž -關閉(_C) -關閉čĻ–įĒ— -偏åĨŊč¨­åŽš(_N) -Eye of GNOME 偏åĨŊč¨­åŽš -內厚(_C) -æœŦ應į”¨į¨‹åŧįš„čĒĒ明文äģļ -關æ–ŧ(_A) -關æ–ŧæœŦį¨‹åŧ -åˇĨå…ˇåˆ—(_T) -在į›Žå‰įš„čĻ–įĒ—中切換是åĻ>éĄ¯į¤ēåˇĨå…ˇåˆ— -į‹€æ…‹åˆ—(_S) -在į›Žå‰įš„čĻ–įĒ—中切換是åĻéĄ¯į¤ēį‹€æ…‹åˆ— -å„˛å­˜(_S) -åĻ存新æĒ”(_A) -垊原(_U) -åˇĻåŗį›¸å(_H) -ä¸Šä¸‹éĄ›å€’(_V) -順時針斚向旋čŊ‰(_R) -逆時針斚向旋čŊ‰(_L) -180 åēĻ旋čŊ‰(_E) -åˆĒ除 -全čžĸåš•(_F) -投åŊąį‰‡(_S) -拉čŋ‘(_Z) -拉遠(_O) -原䞆大小(_N) -最遊大小(_F) -圖į‰‡čŗ‡č¨Š(_I) -在į›Žå‰įš„čĻ–įĒ—中切換是åĻéĄ¯į¤ēčŗ‡č¨ŠįĒ—æ ŧ -新åĸž -開啟 -關閉 -å„˛å­˜ -垊原 -åŗ -åˇĻ -拉čŋ‘ -拉遠 -原䞆大小 -įŦĻ合 -扞不到äŊŋį”¨č€…äģ‹éĸįš„描čŋ°ã€‚ -į„Ąæŗ• -åģēįĢ‹ Eye of GNOME äŊŋį”¨č€…äģ‹éĸ -是åĻ同時開啟多個įš„個åˆĨčĻ–įĒ—īŧŸ -這æ¨Ŗ將會同時開啟 %i 個čĻ–įĒ—。您是åĻæƒŗäģĨ圖į‰‡é›†įš„æ–šåŧé–‹å•ŸīŧŸ -喎一čĻ–įĒ— -圖į‰‡é›† -扞不到æĒ”æĄˆ -į„Ąæŗ•é–‹å•Ÿâ€˜%s’ -حاشیŲ‡ÛŒ ØĒØĩŲˆÛŒØą/Ø¨ØąÚ†Øŗب -ØšØąØļ حاشیŲ‡ÛŒ دŲˆØą Ø¨ØąÚ†Øŗب Ųˆ ØĒØĩŲˆÛŒØą Ø¯Øą Ų…حاŲˆØąŲ‡ÛŒ ØĸÚ˜ÛŒØą -Ų†ŲˆØš ØĸÚ˜ÛŒØą -Ų†ŲˆØš ØĸÚ˜ÛŒØą -دڊŲ…Ų‡Ų‡Ø§ÛŒ ØĸÚ˜ÛŒØą -دڊŲ…Ų‡Ų‡Ø§ÛŒ Ų†Ø´Ø§Ų† دادŲ‡ شدŲ‡ Ø¯Øą Ų…حاŲˆØąŲ‡ÛŒ ØĸÚ˜ÛŒØą -Ų†Ø´Ø§Ų† دادŲ† _ØŦØ˛ØĻیاØĒ بیشØĒØą -Ų…ØĒŲ† -Ų…ØĒŲ† Ø¨ØąÚ†Øŗب -ØĒØąØ§Ø˛ ÚŠØąØ¯Ų† -ŲžÛŒÚ†Ø´ ØŗØˇØąŲ‡Ø§ -Ø§Ú¯Øą یڊ Ø´ŲˆØ¯ØŒ Ø§Ú¯Øą ØŗØˇØąŲ‡Ø§ ØŽÛŒŲ„ÛŒ ØšØąÛŒØļ Ø´ŲˆŲ†Ø¯ Ų…ÛŒŲžÛŒÚ†Ų†Ø¯. -Ų…ŲˆŲ‚ØšÛŒØĒ Ų…ڊاŲ†Ų†Ų…ا -Ų…ŲˆŲ‚ØšÛŒØĒ ŲØšŲ„ÛŒ Ų…ڊاŲ†Ų†Ų…ای Ø¯ØąØŦ Ø¯Øą Ų†ŲˆÛŒØŗŲ‡Ų‡Ø§. -Ų…ŲˆŲ‚ØšÛŒØĒ ØˇØąŲ Ø¯ÛŒÚ¯Øą اŲ†ØĒ؎اب Ų†ØŗبØĒ بŲ‡ Ų…ڊاŲ†Ų†Ų…ا بŲ‡ Ų†ŲˆÛŒØŗŲ‡. -اŲ†ØĒ؎اب Ų‡Ų…Ų‡ -ØąŲˆØ´Ų‡Ø§ÛŒ ŲˆØąŲˆØ¯ÛŒ -ØŽØˇØ§ÛŒ GConf: - %s -ØŽØˇØ§ÛŒ GConf: %s -Ų‡Ų…Ų‡ÛŒ ØŽØˇØ§Ų‡Ø§ÛŒ بؚدی ŲŲ‚Øˇ ØąŲˆÛŒ ŲžØ§ÛŒØ§Ų†Ų‡ Ų†Ų…ایش Ų…ییابŲ†Ø¯. -ØŽØˇØ§ÛŒ GConf -ØĒØĩŲˆÛŒØąÛŒ اŲ†ØĒ؎اب Ų†Ø´Ø¯. -Ø¨ØąØ§ÛŒ اŲ†ØĒ؎اب یڊ ØĒØĩŲˆÛŒØą باید ØąŲˆÛŒØ´ ÚŠŲ„یڊ ÚŠŲ†ÛŒØ¯. -اŲ†ØĒ؎ابی اŲ†ØŦاŲ… Ų†Ø´Ø¯ -ŲžÛŒØ´ŲØąØļŲ†Ø§Ų… -Ø¨ØąŲ†Ø§Ų…Ų‡Ø§ÛŒ اŲ†ØĒ؎اب Ų†Ø´Ø¯ -%s Ų†ŲˆØ´ØĒØ§Øą -Ų†Ø§Ų…ØšŲ„ŲˆŲ… -Ø¨ØąŲ†Ø§Ų…Ų‡Ø§ÛŒ Ø¨ØąØ§ÛŒ Ø¨Ø§Ø˛ ÚŠØąØ¯Ų† %s Ųˆ Ø¯ÛŒÚ¯Øą ŲžØąŲˆŲ†Ø¯Ų‡Ų‡Ø§ÛŒ Ø§Ø˛ Ų†ŲˆØš "%s" اŲ†ØĒ؎اب ÚŠŲ†ÛŒØ¯ -Ø¨ØąŲ†Ø§Ų…Ų‡ اØŦØąØ§ Ų†Ø´Ø¯ -'%s' ŲžÛŒØ¯Ø§ Ų†Ø´Ø¯ -Ø¨ØąŲ†Ø§Ų…Ų‡ ŲžÛŒØ¯Ø§ Ų†Ø´Ø¯ -اŲØ˛ŲˆØ¯Ų† Ø¨ØąŲ†Ø§Ų…Ų‡ Ų…Ų…ÚŠŲ† Ų†ÛŒØŗØĒ -اŲØ˛ŲˆØ¯Ų† Ø¨ØąŲ†Ø§Ų…Ų‡ بŲ‡ ŲžØ§ÛŒÚ¯Ø§Ų‡ دادŲ‡ÛŒ Ø¨ØąŲ†Ø§Ų…Ų‡ Ų…Ų…ÚŠŲ† Ų†ÛŒØŗØĒ -یڊ Ø¨ØąŲ†Ø§Ų…Ų‡ اŲ†ØĒ؎اب ÚŠŲ†ÛŒØ¯ -Ø¨Ø§Ø˛ ÚŠØąØ¯Ų† با -Ø¨ØąŲ†Ø§Ų…Ų‡Ø§ÛŒ Ø¨ØąØ§ÛŒ Ø¨Ø§Ø˛ ÚŠØąØ¯Ų† %s Ųˆ Ø¯ÛŒÚ¯Øą ŲžØąŲˆŲ†Ø¯Ų‡Ų‡Ø§ÛŒ Ø§Ø˛ Ų†ŲˆØš "%s" اŲ†ØĒ؎اب ÚŠŲ†ÛŒØ¯ _Ų…ØąŲˆØą... Ø¨Ø§Ø˛ ÚŠØąØ¯Ų† %s Ųˆ Ø¯ÛŒÚ¯Øą ŲžØąŲˆŲ†Ø¯Ų‡Ų‡Ø§ÛŒ Ų†ŲˆØš "%s" با: -_اŲØ˛ŲˆØ¯Ų† -اŲØ˛ŲˆØ¯Ų† Ø¨ØąŲ†Ø§Ų…Ų‡ -Ų…ÛŒØĒŲˆØ§Ų†ÛŒØ¯ با ÚŠŲ„یڊ ÚŠØąØ¯Ų† ØąŲˆÛŒ اŲ†ØĩØąØ§ŲØŒ ایŲ† ØšŲ…Ų„یاØĒ ØąØ§ Ų…ØĒŲˆŲ‚Ų ÚŠŲ†ÛŒØ¯. -Ø§ØˇŲ„اؚاØĒ -Ų‡Ø´Ø¯Ø§Øą -ØŽØˇØ§ -ØŗؤاŲ„ - (یŲˆŲ†ÛŒÚŠØ¯ Ų†Ø§Ų…ØšØĒØ¨Øą) \ No newline at end of file diff --git a/microbench/src/main/resources/io/netty/microbench/handler/ssl/test.crt b/microbench/src/main/resources/io/netty/microbench/handler/ssl/test.crt deleted file mode 100644 index 7d3437e13e..0000000000 --- a/microbench/src/main/resources/io/netty/microbench/handler/ssl/test.crt +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIC/jCCAeagAwIBAgIIIMONxElm0AIwDQYJKoZIhvcNAQELBQAwPjE8MDoGA1UE -AwwzZThhYzAyZmEwZDY1YTg0MjE5MDE2MDQ1ZGI4YjA1YzQ4NWI0ZWNkZi5uZXR0 -eS50ZXN0MCAXDTEzMDgwMjA3NTEzNloYDzk5OTkxMjMxMjM1OTU5WjA+MTwwOgYD -VQQDDDNlOGFjMDJmYTBkNjVhODQyMTkwMTYwNDVkYjhiMDVjNDg1YjRlY2RmLm5l -dHR5LnRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDb+HBO3C0U -RBKvDUgJHbhIlBye8X/cbNH3lDq3XOOFBz7L4XZKLDIXS+FeQqSAUMo2otmU+Vkj -0KorshMjbUXfE1KkTijTMJlaga2M2xVVt21fRIkJNWbIL0dWFLWyRq7OXdygyFkI -iW9b2/LYaePBgET22kbtHSCAEj+BlSf265+1rNxyAXBGGGccCKzEbcqASBKHOgVp -6pLqlQAfuSy6g/OzGzces3zXRrGu1N3pBIzAIwCW429n52ZlYfYR0nr+REKDnRrP -IIDsWASmEHhBezTD+v0qCJRyLz2usFgWY+7agUJE2yHHI2mTu2RAFngBilJXlMCt -VwT0xGuQxkbHAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAEv8N7Xm8qaY2FgrOc6P -a1GTgA+AOb3aU33TGwAR86f+nLf6BSPaohcQfOeJid7FkFuYInuXl+oqs+RqM/j8 -R0E5BuGYY2wOKpL/PbFi1yf/Kyvft7KVh8e1IUUec/i1DdYTDB0lNWvXXxjfMKGL -ct3GMbEHKvLfHx42Iwz/+fva6LUrO4u2TDfv0ycHuR7UZEuC1DJ4xtFhbpq/QRAj -CyfNx3cDc7L2EtJWnCmivTFA9l8MF1ZPMDSVd4ecQ7B0xZIFQ5cSSFt7WGaJCsGM -zYkU4Fp4IykQcWxdlNX7wJZRwQ2TZJFFglpTiFZdeq6I6Ad9An1Encpz5W8UJ4tv -hmw= ------END CERTIFICATE----- diff --git a/microbench/src/main/resources/io/netty/microbench/handler/ssl/test_unencrypted.pem b/microbench/src/main/resources/io/netty/microbench/handler/ssl/test_unencrypted.pem deleted file mode 100644 index 608e7f4da0..0000000000 --- a/microbench/src/main/resources/io/netty/microbench/handler/ssl/test_unencrypted.pem +++ /dev/null @@ -1,24 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDb+HBO3C0URBKvDUgJHbhIlBye -8X/cbNH3lDq3XOOFBz7L4XZKLDIXS+FeQqSAUMo2otmU+Vkj0KorshMjbUXfE1KkTijTMJlaga2M -2xVVt21fRIkJNWbIL0dWFLWyRq7OXdygyFkIiW9b2/LYaePBgET22kbtHSCAEj+BlSf265+1rNxy -AXBGGGccCKzEbcqASBKHOgVp6pLqlQAfuSy6g/OzGzces3zXRrGu1N3pBIzAIwCW429n52ZlYfYR -0nr+REKDnRrPIIDsWASmEHhBezTD+v0qCJRyLz2usFgWY+7agUJE2yHHI2mTu2RAFngBilJXlMCt -VwT0xGuQxkbHAgMBAAECggEBAJJdKaVfXWNptCDkLnVaYB9y5eRgfppVkhQxfiw5023Vl1QjrgjG -hYH4zHli0IBMwXA/RZWZoFVzZ3dxoshk0iQPgGKxWvrDEJcnSCo8MGL7jPvh52jILp6uzsGZQBji -bTgFPmOBS7ShdgZiQKD9PD2psrmqHZ1yTwjIm5cGfzQM8Y6tjm0xLBn676ecJNdS1TL10y9vmSUM -Ofdkmeg9Z9TEK95lP2fF/NIcxCo0LF9JcHUvTuYBDnBH0XMZi0w0ZcRReMSdAZ2lLiXgBeCO53el -2NIrtkRx+qOvLua9UfwO2h/0rs66ZeV0YuFCjv067nytyZf2zhU/QbCHRypzfrkCgYEA/facuAJs -6MQKsNvhozoBeDRMkrZPMh8Sb0w50EqzIGz3pdms6UvCiggoMbhxKOwuYWZ689fBPGwm7x0RdwDO -jyUuEbFnQFe+CpdHy6VK7vIQed1SwAcdTMDwCYbkJNglqHEB7qUYYTFLr8okGyWVdthUoh4IAubU -TR3TFbGraDUCgYEA3bwJ/UNA5pHtb/nh4/dNL7/bRMwXyPZPpC5z+gjjgUMgsSRBz8+iPNTB4iSQ -1j9zm+pnXGi35zWZcI4jvIcFusb08eS7xcZDb+7X2r2wenLNmyuTOa1812y233FicU+ah91fa9aD -yUfTjj3GFawbgNNhMyWa3aEMV+c73t6sKosCgYEA35oQZhsMlOx2lT0jrzlVLeauPMZzeCfPbVrp -1DDRAg2vBcFf8pCXmjyQVyaTy3oXY/585tDh/DclGIa5Z9O4CmSr6TwPMqGOW3jS58SC81sBkqqB -Pz2EWJ3POjQgDyiYD3RgRSPrETf78azCmXw/2sGh0pMqbpOZ/MPzpDgoOLkCgYEAsdv4g09kCs75 -Dz34hRzErE2P+8JePdPdlEuyudhRbUlEOvNjWucpMvRSRSyhhUnGWUWP/V7+TRcAanmJjtsbrHOU -3Udlm0HqrCmAubQ4kC/wXsx4Pua7Yi2RDvBrT4rT4LGgreaXNWhI+Srx7kZslUx5Bkbez3I0bXpM -2vvwS/sCgYAducNt1KC4W7jzMWUivvuy5hQQmX/G0JHtu1pfv9cmA8agnc1I/r7xoirftuSG25Pm -r+eP5SKbKb8ZQlp10JeBkNnk8eAG8OkQyBaECYDBadEr1/LK2LmIEjYKzKAjYQ4cX2KMtY271jjX -WrzzXNqBdThFfMHiJE8k9xYmaLDKhQ== ------END PRIVATE KEY----- diff --git a/microbench/src/main/resources/io/netty/microbench/search/netty-io-news.html b/microbench/src/main/resources/io/netty/microbench/search/netty-io-news.html deleted file mode 100644 index 7424942c6b..0000000000 --- a/microbench/src/main/resources/io/netty/microbench/search/netty-io-news.html +++ /dev/null @@ -1,349 +0,0 @@ - - - - - Netty.news: Netty 4.1.45.Final released - Netty: Netty 4.1.45.Final released - - - - - - - - - - - - - - - -Skip navigation - -
    -
    -
    -
    -
    -

    - Netty 4.1.45.Final released -

    - -
    -

    I am happy to announce the release of netty 4.1.45.Final, our first release of 2020. This is a bug-fix release which also fixes two regressions. Please upgrade as soon as possible.

    -

    For more details please read-on.

    -

    The most important changes in this release are:

    -
      -
    • Fix BufferOverflowException during non-Unsafe PooledDirectByteBuf resize (#9912)
    • -
    • FlushConsolidationHandler may suppress flushes by mistake (#9931)
    • -
    • Utf8FrameValidator must release buffer when validation fails (#9909)
    • -
    • Avoid possible comparison contract violation (#9883)
    • -
    • Ignore inline comments when parsing nameservers (#9894)
    • -
    -

    For the details and all changes, please browse our issue tracker for 4.1.45.Final.

    -

    Thank You

    -

    Every idea and bug-report counts and so we thought it is worth mentioning those who helped in this area. Please report an unintended omission.

    - -
    - -
    -
    -
    - - -
    -
    -
    - -
    -
    -
    -
    -
    -
    - -
    - - - - - - diff --git a/microbench/src/main/resources/logback.xml b/microbench/src/main/resources/logback.xml deleted file mode 100644 index bccf735a13..0000000000 --- a/microbench/src/main/resources/logback.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/pom.xml b/pom.xml index e0a32b407a..8a38edc81d 100644 --- a/pom.xml +++ b/pom.xml @@ -17,13 +17,15 @@ 4.0.0 - - org.sonatype.oss - oss-parent - 9 - + + + mchv-snapshot + MCHV Apache Snapshot Maven Packages Distribution + https://mvn.mchv.eu/repository/mchv-snapshot + + - io.netty + io.net5 netty-parent pom 5.0.0.Final-SNAPSHOT @@ -113,27 +115,27 @@ Low-level data representation - io.netty.buffer* + io.net5.buffer* Central interface for all I/O operations - io.netty.channel* + io.net5.channel* Client & Server bootstrapping utilities - io.netty.bootstrap* + io.net5.bootstrap* Reusable I/O event interceptors - io.netty.handler* + io.net5.handler* DNS / Host resolvers - io.netty.resolver* + io.net5.resolver* Utils - io.netty.util* + io.net5.util* en_US @@ -346,30 +348,24 @@ unsafeBuffer - -Dio.netty.tryReflectionSetAccessible=true --add-opens java.base/java.nio=ALL-UNNAMED --add-opens java.base/jdk.internal.misc=ALL-UNNAMED -Dio.netty.buffer.api.MemoryManager=Unsafe + -Dio.net5.tryReflectionSetAccessible=true --add-opens java.base/java.nio=ALL-UNNAMED --add-opens java.base/jdk.internal.misc=ALL-UNNAMED -Dio.net5.buffer.api.MemoryManager=Unsafe boringssl - windows - netty-tcnative-boringssl-static - leak - -Dio.netty.leakDetectionLevel=paranoid -Dio.netty.leakDetection.targetRecords=32 + -Dio.net5.leakDetectionLevel=paranoid -Dio.net5.leakDetection.targetRecords=32 @@ -381,7 +377,7 @@ noUnsafe - -Dio.netty.noUnsafe=true + -Dio.net5.noUnsafe=true @@ -445,13 +441,13 @@ ${project.build.directory}/dev-tools UTF-8 UTF-8 - 29 + 30-SNAPSHOT 1.4.11.Final 2.0.10 "${settings.localRepository}"/org/mortbay/jetty/alpn/jetty-alpn-agent/${jetty.alpnAgent.version}/jetty-alpn-agent-${jetty.alpnAgent.version}.jar -server - -dsa -da -ea:io.netty... + -dsa -da -ea:io.net5... -XX:+HeapDumpOnOutOfMemoryError @@ -463,14 +459,10 @@ -D_ - 1.7.0 fedora,suse,arch - netty-tcnative - 2.0.43.Final - ${os.detected.classifier} org.conscrypt conscrypt-openjdk-uber 2.5.2 @@ -498,45 +490,9 @@ - all dev-tools common buffer - codec - codec-dns - codec-haproxy - codec-http - codec-http2 - codec-memcache - codec-mqtt - codec-redis - codec-smtp - codec-socks - codec-stomp - codec-xml - resolver - resolver-dns - resolver-dns-native-macos - transport - transport-native-unix-common-tests - transport-native-unix-common - transport-native-epoll - transport-native-kqueue - transport-sctp - handler - handler-proxy - example - testsuite - testsuite-autobahn - testsuite-http2 - testsuite-osgi - testsuite-shading - testsuite-native - testsuite-native-image - testsuite-native-image-client - testsuite-native-image-client-runtime-init - transport-blockhound-tests - microbench bom @@ -599,14 +555,6 @@ - - ${project.groupId} - ${tcnative.artifactId} - ${tcnative.version} - ${tcnative.classifier} - compile - true - @@ -973,14 +921,13 @@ ^(?!io\.netty\.).* - ^io\.netty\.internal\.tcnative\..* - @io.netty.util.internal.UnstableApi - io.netty.util.internal.shaded + @io.net5.util.internal.UnstableApi + io.net5.util.internal.shaded - io.netty.handler.codec.dns.TcpDnsQueryDecoder - io.netty.handler.codec.dns.TcpDnsResponseEncoder + io.net5.handler.codec.dns.TcpDnsQueryDecoder + io.net5.handler.codec.dns.TcpDnsResponseEncoder @@ -1068,7 +1015,7 @@ true true true - io/netty/checkstyle.xml + io/net5/checkstyle.xml ${project.build.sourceDirectory} ${project.build.testSourceDirectory} @@ -1175,7 +1122,7 @@ listener - io.netty.build.junit.TimedOutTestsListener + io.net5.build.junit.TimedOutTestsListener ${testJvm} @@ -1630,7 +1577,7 @@ 1.5 - io.netty:netty-dev-tools:${project.version} + io.net5:netty-dev-tools:${project.version} ${netty.dev.tools.directory} diff --git a/resolver-dns-native-macos/pom.xml b/resolver-dns-native-macos/pom.xml deleted file mode 100644 index 380a07b1ea..0000000000 --- a/resolver-dns-native-macos/pom.xml +++ /dev/null @@ -1,290 +0,0 @@ - - - - 4.0.0 - - io.netty - netty-parent - 5.0.0.Final-SNAPSHOT - - netty-resolver-dns-native-macos - - Netty/Resolver/DNS/MacOS - jar - - - - mac - - - mac - - - - LDFLAGS=-Wl,-weak_library,${unix.common.lib.unpacked.dir}/lib${unix.common.lib.name}.a - false - - - - - maven-dependency-plugin - - - - unpack - generate-sources - - unpack-dependencies - - - ${project.groupId} - netty-transport-native-unix-common - ${jni.classifier} - ${unix.common.lib.dir} - META-INF/native/** - false - true - - - - - - - org.fusesource.hawtjni - maven-hawtjni-plugin - - - build-native-lib - - netty_resolver_dns_native_macos_${os.detected.arch} - ${project.basedir}/src/main/c - ${project.build.outputDirectory} - - . - - ${jni.compiler.args.ldflags} - ${jni.compiler.args.cflags} - - - - generate - build - - - - - - - maven-jar-plugin - - - - native-jar - - jar - - - - - true - - - META-INF/native/libnetty_resolver_dns_native_macos_${os.detected.arch}.jnilib; osname=MacOSX; processor=${os.detected.arch} - ${javaModuleName} - - true - ${project.build.outputDirectory}/META-INF/MANIFEST.MF - - ${jni.classifier} - - - - - - - - - io.netty - netty-transport-native-unix-common - ${project.version} - ${jni.classifier} - - true - - - - - mac-m1-cross-compile - - LDFLAGS=-arch arm64 -Wl,-weak_library,${unix.common.lib.unpacked.dir}/lib${unix.common.lib.name}.a - CFLAGS=-target arm64-apple-macos11 -O3 -Werror -fno-omit-frame-pointer -Wunused-variable -fvisibility=hidden -I${unix.common.include.unpacked.dir} - - - ${os.detected.name}-aarch_64 - true - - - - - maven-dependency-plugin - - - - unpack - generate-sources - - unpack-dependencies - - - ${project.groupId} - netty-transport-native-unix-common - ${jni.classifier} - ${unix.common.lib.dir} - META-INF/native/** - false - true - - - - - - - org.fusesource.hawtjni - maven-hawtjni-plugin - - - build-native-lib - - netty_resolver_dns_native_macos_aarch_64 - ${project.basedir}/src/main/c - ${project.build.outputDirectory} - - . - - ${jni.compiler.args.ldflags} - ${jni.compiler.args.cflags} - --host=aarch64-apple-darwin - - - - generate - build - - - - - - - maven-jar-plugin - - - - native-jar - - jar - - - - - true - - - META-INF/native/libnetty_resolver_dns_native_macos_aarch_64.jnilib; osname=MacOSX; processor=aarch_64 - ${javaModuleName} - - true - ${project.build.outputDirectory}/META-INF/MANIFEST.MF - - ${jni.classifier} - - - - - - - - - io.netty - netty-transport-native-unix-common - ${project.version} - ${jni.classifier} - - true - - - - - - - io.netty.resolver.dns.macos - - netty-unix-common - ${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 -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 - - - - - io.netty - netty-common - ${project.version} - - - io.netty - netty-resolver-dns - ${project.version} - - - io.netty - netty-transport-native-unix-common - ${project.version} - - - - - - - maven-jar-plugin - - - - default-jar - - - META-INF/native/** - - - - - - - - - diff --git a/resolver-dns-native-macos/src/main/c/dnsinfo.h b/resolver-dns-native-macos/src/main/c/dnsinfo.h deleted file mode 100644 index 73ebb2491b..0000000000 --- a/resolver-dns-native-macos/src/main/c/dnsinfo.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -/* - * Copyright (c) 2004-2006, 2008, 2009, 2011 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * https://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#ifndef __DNSINFO_H__ -#define __DNSINFO_H__ -/* - * These routines provide access to the systems DNS configuration - */ -#include -#include -#include -#include -#include -#define DNSINFO_VERSION 20111104 -#define DEFAULT_SEARCH_ORDER 200000 /* search order for the "default" resolver domain name */ -#define DNS_PTR(type, name) \ - union { \ - type name; \ - uint64_t _ ## name ## _p; \ - } -#define DNS_VAR(type, name) \ - type name -#pragma pack(4) -typedef struct { - struct in_addr address; - struct in_addr mask; -} dns_sortaddr_t; -#pragma pack() -#pragma pack(4) -typedef struct { - DNS_PTR(char *, domain); /* domain */ - DNS_VAR(int32_t, n_nameserver); /* # nameserver */ - DNS_PTR(struct sockaddr **, nameserver); - DNS_VAR(uint16_t, port); /* port (in host byte order) */ - DNS_VAR(int32_t, n_search); /* # search */ - DNS_PTR(char **, search); - DNS_VAR(int32_t, n_sortaddr); /* # sortaddr */ - DNS_PTR(dns_sortaddr_t **, sortaddr); - DNS_PTR(char *, options); /* options */ - DNS_VAR(uint32_t, timeout); /* timeout */ - DNS_VAR(uint32_t, search_order); /* search_order */ - DNS_VAR(uint32_t, if_index); - DNS_VAR(uint32_t, flags); - DNS_VAR(uint32_t, reach_flags); /* SCNetworkReachabilityFlags */ - DNS_VAR(uint32_t, reserved[5]); -} dns_resolver_t; -#pragma pack() -#define DNS_RESOLVER_FLAGS_SCOPED 1 /* configuration is for scoped questions */ -#pragma pack(4) -typedef struct { - DNS_VAR(int32_t, n_resolver); /* resolver configurations */ - DNS_PTR(dns_resolver_t **, resolver); - DNS_VAR(int32_t, n_scoped_resolver); /* "scoped" resolver configurations */ - DNS_PTR(dns_resolver_t **, scoped_resolver); - DNS_VAR(uint32_t, reserved[5]); -} dns_config_t; -#pragma pack() -__BEGIN_DECLS -/* - * DNS configuration access APIs - */ -const char * -dns_configuration_notify_key (); -dns_config_t * -dns_configuration_copy (); -void -dns_configuration_free (dns_config_t *config); -void -_dns_configuration_ack (dns_config_t *config, - const char *bundle_id); -__END_DECLS -#endif /* __DNSINFO_H__ */ \ No newline at end of file diff --git a/resolver-dns-native-macos/src/main/c/netty_resolver_dns_macos.c b/resolver-dns-native-macos/src/main/c/netty_resolver_dns_macos.c deleted file mode 100644 index e3891fc814..0000000000 --- a/resolver-dns-native-macos/src/main/c/netty_resolver_dns_macos.c +++ /dev/null @@ -1,231 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -#include -#include -#include -#include -#include -#include -#include "dnsinfo.h" -#include "netty_unix_jni.h" -#include "netty_unix_util.h" -#include "netty_unix_socket.h" -#include "netty_unix_errors.h" - -// Add define if NETTY_BUILD_STATIC is defined so it is picked up in netty_jni_util.c -#ifdef NETTY_BUILD_STATIC -#define NETTY_JNI_UTIL_BUILD_STATIC -#endif - -#define STREAM_CLASSNAME "io/netty/resolver/dns/macos/MacOSDnsServerAddressStreamProvider" - -static jclass dnsResolverClass = NULL; -static jclass byteArrayClass = NULL; -static jclass stringClass = NULL; -static jmethodID dnsResolverMethodId = NULL; -static char* staticPackagePrefix = NULL; - -// JNI Registered Methods Begin - -// We use the same API as mDNSResponder and Chromium to retrieve the current nameserver configuration for the system: -// See: -// https://src.chromium.org/viewvc/chrome?revision=218617&view=revision -// https://opensource.apple.com/tarballs/mDNSResponder/ -static jobjectArray netty_resolver_dns_macos_resolvers(JNIEnv* env, jclass clazz) { - dns_config_t* config = dns_configuration_copy(); - if (config == NULL) { - goto error; - } - jobjectArray array = (*env)->NewObjectArray(env, config->n_resolver, dnsResolverClass, NULL); - if (array == NULL) { - goto error; - } - - for (int i = 0; i < config->n_resolver; i++) { - dns_resolver_t* resolver = config->resolver[i]; - if (resolver == NULL) { - goto error; - } - jstring domain = NULL; - - if (resolver->domain != NULL) { - domain = (*env)->NewStringUTF(env, resolver->domain); - if (domain == NULL) { - goto error; - } - } - - jobjectArray addressArray = (*env)->NewObjectArray(env, resolver->n_nameserver, byteArrayClass, NULL); - if (addressArray == NULL) { - goto error; - } - - for (int a = 0; a < resolver->n_nameserver; a++) { - const struct sockaddr_storage* addr = (const struct sockaddr_storage *) resolver->nameserver[a]; - if (addr == NULL) { - goto error; - } - jbyteArray address = netty_unix_socket_createInetSocketAddressArray(env, addr); - if (address == NULL) { - netty_unix_errors_throwOutOfMemoryError(env); - goto error; - } - (*env)->SetObjectArrayElement(env, addressArray, a, address); - } - - jint port = resolver->port; - - jobjectArray searchArray = (*env)->NewObjectArray(env, resolver->n_search, stringClass, NULL); - if (searchArray == NULL) { - goto error; - } - - for (int a = 0; a < resolver->n_search; a++) { - char* s = resolver->search[a]; - if (s == NULL) { - goto error; - } - jstring search = (*env)->NewStringUTF(env, s); - if (search == NULL) { - goto error; - } - - (*env)->SetObjectArrayElement(env, searchArray, a, search); - } - - jstring options = NULL; - if (resolver->options != NULL) { - options = (*env)->NewStringUTF(env, resolver->options); - if (options == NULL) { - goto error; - } - } - - jint timeout = resolver->timeout; - jint searchOrder = resolver->search_order; - - jobject java_resolver = (*env)->NewObject(env, dnsResolverClass, dnsResolverMethodId, domain, - addressArray, port, searchArray, options, timeout, searchOrder); - if (java_resolver == NULL) { - goto error; - } - (*env)->SetObjectArrayElement(env, array, i, java_resolver); - } - - dns_configuration_free(config); - return array; -error: - if (config != NULL) { - dns_configuration_free(config); - } - return NULL; -} - - -// JNI Method Registration Table Begin - -static JNINativeMethod* createDynamicMethodsTable(const char* packagePrefix) { - JNINativeMethod* dynamicMethods = malloc(sizeof(JNINativeMethod) * 1); - - char* dynamicTypeName = netty_jni_util_prepend(packagePrefix, "io/netty/resolver/dns/macos/DnsResolver;"); - JNINativeMethod* dynamicMethod = &dynamicMethods[0]; - dynamicMethod->name = "resolvers"; - dynamicMethod->signature = netty_jni_util_prepend("()[L", dynamicTypeName); - dynamicMethod->fnPtr = (void *) netty_resolver_dns_macos_resolvers; - free(dynamicTypeName); - return dynamicMethods; -} - -// JNI Method Registration Table End - -static void netty_resolver_dns_native_macos_JNI_OnUnLoad(JNIEnv* env) { - NETTY_JNI_UTIL_UNLOAD_CLASS(env, byteArrayClass); - NETTY_JNI_UTIL_UNLOAD_CLASS(env, stringClass); - netty_jni_util_unregister_natives(env, staticPackagePrefix, STREAM_CLASSNAME); - - if (staticPackagePrefix != NULL) { - free((void *) staticPackagePrefix); - staticPackagePrefix = NULL; - } -} - -// IMPORTANT: If you add any NETTY_JNI_UTIL_LOAD_CLASS or NETTY_JNI_UTIL_FIND_CLASS calls you also need to update -// MacOSDnsServerAddressStreamProvider to reflect that. -static jint netty_resolver_dns_native_macos_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) { - int ret = JNI_ERR; - int providerRegistered = 0; - char* nettyClassName = NULL; - - // Register the methods which are not referenced by static member variables - JNINativeMethod* dynamicMethods = createDynamicMethodsTable(packagePrefix); - if (dynamicMethods == NULL) { - goto done; - } - if (netty_jni_util_register_natives(env, - packagePrefix, - STREAM_CLASSNAME, - dynamicMethods, 1) != 0) { - goto done; - } - providerRegistered = 1; - - nettyClassName = netty_jni_util_prepend(packagePrefix, "io/netty/resolver/dns/macos/DnsResolver"); - NETTY_JNI_UTIL_LOAD_CLASS(env, dnsResolverClass, nettyClassName, done); - netty_jni_util_free_dynamic_name(&nettyClassName); - - NETTY_JNI_UTIL_GET_METHOD(env, dnsResolverClass, dnsResolverMethodId, "", "(Ljava/lang/String;[[BI[Ljava/lang/String;Ljava/lang/String;II)V", done); - - NETTY_JNI_UTIL_LOAD_CLASS(env, byteArrayClass, "[B", done); - NETTY_JNI_UTIL_LOAD_CLASS(env, stringClass, "java/lang/String", done); - - if (packagePrefix != NULL) { - staticPackagePrefix = strdup(packagePrefix); - } - - ret = NETTY_JNI_UTIL_JNI_VERSION; -done: - if (ret == JNI_ERR) { - if (providerRegistered == 1) { - netty_jni_util_unregister_natives(env, packagePrefix, STREAM_CLASSNAME); - } - } - netty_jni_util_free_dynamic_methods_table(dynamicMethods, 0, 1); - free(nettyClassName); - return ret; -} - -// We build with -fvisibility=hidden so ensure we mark everything that needs to be visible with JNIEXPORT -// https://mail.openjdk.java.net/pipermail/core-libs-dev/2013-February/014549.html - -// Invoked by the JVM when statically linked -JNIEXPORT jint JNI_OnLoad_netty_resolver_dns_native_macos(JavaVM* vm, void* reserved) { - return netty_jni_util_JNI_OnLoad(vm, reserved, "netty_resolver_dns_native_macos", netty_resolver_dns_native_macos_JNI_OnLoad); -} - -// Invoked by the JVM when statically linked -JNIEXPORT void JNI_OnUnload_netty_resolver_dns_native_macos(JavaVM* vm, void* reserved) { - netty_jni_util_JNI_OnUnload(vm, reserved, netty_resolver_dns_native_macos_JNI_OnUnLoad); -} - -#ifndef NETTY_BUILD_STATIC -JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { - return netty_jni_util_JNI_OnLoad(vm, reserved, "netty_resolver_dns_native_macos", netty_resolver_dns_native_macos_JNI_OnLoad); -} - -JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved) { - netty_jni_util_JNI_OnUnload(vm, reserved, netty_resolver_dns_native_macos_JNI_OnUnLoad); -} -#endif /* NETTY_BUILD_STATIC */ diff --git a/resolver-dns-native-macos/src/main/java/io/netty/resolver/dns/macos/DnsResolver.java b/resolver-dns-native-macos/src/main/java/io/netty/resolver/dns/macos/DnsResolver.java deleted file mode 100644 index 210b554a06..0000000000 --- a/resolver-dns-native-macos/src/main/java/io/netty/resolver/dns/macos/DnsResolver.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns.macos; - -import io.netty.channel.unix.NativeInetAddress; - -import java.net.InetSocketAddress; - -/** - * Represent the {@code dns_resolver_t} struct. - */ -final class DnsResolver { - - private final String domain; - private final InetSocketAddress[] nameservers; - private final int port; - private final String[] searches; - private final String options; - private final int timeout; - private final int searchOrder; - - DnsResolver(String domain, byte[][] nameservers, int port, - String[] searches, String options, int timeout, int searchOrder) { - this.domain = domain; - if (nameservers == null) { - this.nameservers = new InetSocketAddress[0]; - } else { - this.nameservers = new InetSocketAddress[nameservers.length]; - for (int i = 0; i < nameservers.length; i++) { - byte[] addr = nameservers[i]; - this.nameservers[i] = NativeInetAddress.address(addr, 0, addr.length); - } - } - this.port = port; - this.searches = searches; - this.options = options; - this.timeout = timeout; - this.searchOrder = searchOrder; - } - - String domain() { - return domain; - } - - InetSocketAddress[] nameservers() { - return nameservers; - } - - int port() { - return port; - } - - String[] searches() { - return searches; - } - - String options() { - return options; - } - - int timeout() { - return timeout; - } - - int searchOrder() { - return searchOrder; - } -} diff --git a/resolver-dns-native-macos/src/main/java/io/netty/resolver/dns/macos/MacOSDnsServerAddressStreamProvider.java b/resolver-dns-native-macos/src/main/java/io/netty/resolver/dns/macos/MacOSDnsServerAddressStreamProvider.java deleted file mode 100644 index f2207addc8..0000000000 --- a/resolver-dns-native-macos/src/main/java/io/netty/resolver/dns/macos/MacOSDnsServerAddressStreamProvider.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns.macos; - -import io.netty.resolver.dns.DnsServerAddressStream; -import io.netty.resolver.dns.DnsServerAddressStreamProvider; -import io.netty.resolver.dns.DnsServerAddressStreamProviders; -import io.netty.resolver.dns.DnsServerAddresses; -import io.netty.util.internal.ClassInitializerUtil; -import io.netty.util.internal.NativeLibraryLoader; -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.ThrowableUtil; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.net.InetSocketAddress; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; - -/** - * {@link DnsServerAddressStreamProvider} implementation which makes use of the same mechanism as - * Apple's open source mDNSResponder to retrieve the - * current nameserver configuration of the system. - */ -public final class MacOSDnsServerAddressStreamProvider implements DnsServerAddressStreamProvider { - - private static final Comparator RESOLVER_COMPARATOR = - new Comparator() { - @Override - public int compare(DnsResolver r1, DnsResolver r2) { - // Note: order is descending (from higher to lower) so entries with lower search order override - // entries with higher search order. - return r1.searchOrder() < r2.searchOrder() ? 1 : (r1.searchOrder() == r2.searchOrder() ? 0 : -1); - } - }; - - private static final Throwable UNAVAILABILITY_CAUSE; - - private static final InternalLogger logger = - InternalLoggerFactory.getInstance(MacOSDnsServerAddressStreamProvider.class); - - // Let's refresh every 10 seconds. - private static final long REFRESH_INTERVAL = TimeUnit.SECONDS.toNanos(10); - - static { - // Preload all classes that will be used in the OnLoad(...) function of JNI to eliminate the possiblity of a - // class-loader deadlock. This is a workaround for https://github.com/netty/netty/issues/11209. - - // This needs to match all the classes that are loaded via NETTY_JNI_UTIL_LOAD_CLASS or looked up via - // NETTY_JNI_UTIL_FIND_CLASS. - ClassInitializerUtil.tryLoadClasses(MacOSDnsServerAddressStreamProvider.class, - // netty_resolver_dns_macos - byte[].class, String.class - ); - - Throwable cause = null; - try { - loadNativeLibrary(); - } catch (Throwable error) { - cause = error; - } - UNAVAILABILITY_CAUSE = cause; - } - - private static void loadNativeLibrary() { - if (!PlatformDependent.isOsx()) { - throw new IllegalStateException("Only supported on MacOS/OSX"); - } - String staticLibName = "netty_resolver_dns_native_macos"; - String sharedLibName = staticLibName + '_' + PlatformDependent.normalizedArch(); - ClassLoader cl = PlatformDependent.getClassLoader(MacOSDnsServerAddressStreamProvider.class); - try { - NativeLibraryLoader.load(sharedLibName, cl); - } catch (UnsatisfiedLinkError e1) { - try { - NativeLibraryLoader.load(staticLibName, cl); - logger.debug("Failed to load {}", sharedLibName, e1); - } catch (UnsatisfiedLinkError e2) { - ThrowableUtil.addSuppressed(e1, e2); - throw e1; - } - } - } - - public static boolean isAvailable() { - return UNAVAILABILITY_CAUSE == null; - } - - public static void ensureAvailability() { - if (UNAVAILABILITY_CAUSE != null) { - throw (Error) new UnsatisfiedLinkError( - "failed to load the required native library").initCause(UNAVAILABILITY_CAUSE); - } - } - - public static Throwable unavailabilityCause() { - return UNAVAILABILITY_CAUSE; - } - - public MacOSDnsServerAddressStreamProvider() { - ensureAvailability(); - } - - private volatile Map currentMappings = retrieveCurrentMappings(); - private final AtomicLong lastRefresh = new AtomicLong(System.nanoTime()); - - private static Map retrieveCurrentMappings() { - DnsResolver[] resolvers = resolvers(); - - if (resolvers == null || resolvers.length == 0) { - return Collections.emptyMap(); - } - Arrays.sort(resolvers, RESOLVER_COMPARATOR); - Map resolverMap = new HashMap<>(resolvers.length); - for (DnsResolver resolver: resolvers) { - // Skip mdns - if ("mdns".equalsIgnoreCase(resolver.options())) { - continue; - } - InetSocketAddress[] nameservers = resolver.nameservers(); - if (nameservers == null || nameservers.length == 0) { - continue; - } - String domain = resolver.domain(); - if (domain == null) { - // Default mapping. - domain = StringUtil.EMPTY_STRING; - } - InetSocketAddress[] servers = resolver.nameservers(); - for (int a = 0; a < servers.length; a++) { - InetSocketAddress address = servers[a]; - // Check if the default port should be used - if (address.getPort() == 0) { - int port = resolver.port(); - if (port == 0) { - port = 53; - } - servers[a] = new InetSocketAddress(address.getAddress(), port); - } - } - - resolverMap.put(domain, DnsServerAddresses.sequential(servers)); - } - return resolverMap; - } - - @Override - public DnsServerAddressStream nameServerAddressStream(String hostname) { - long last = lastRefresh.get(); - Map resolverMap = currentMappings; - 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())) { - resolverMap = currentMappings = retrieveCurrentMappings(); - } - } - - final String originalHostname = hostname; - for (;;) { - int i = hostname.indexOf('.', 1); - if (i < 0 || i == hostname.length() - 1) { - // Try access default mapping. - DnsServerAddresses addresses = resolverMap.get(StringUtil.EMPTY_STRING); - if (addresses != null) { - return addresses.stream(); - } - return DnsServerAddressStreamProviders.unixDefault().nameServerAddressStream(originalHostname); - } - - DnsServerAddresses addresses = resolverMap.get(hostname); - if (addresses != null) { - return addresses.stream(); - } - - hostname = hostname.substring(i + 1); - } - } - - private static native DnsResolver[] resolvers(); -} diff --git a/resolver-dns-native-macos/src/main/java/io/netty/resolver/dns/macos/package-info.java b/resolver-dns-native-macos/src/main/java/io/netty/resolver/dns/macos/package-info.java deleted file mode 100644 index 8bbd477d69..0000000000 --- a/resolver-dns-native-macos/src/main/java/io/netty/resolver/dns/macos/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * MacOS specific nameserver resolution. - */ -package io.netty.resolver.dns.macos; diff --git a/resolver-dns-native-macos/src/test/java/io/netty/resolver/dns/macos/MacOSDnsServerAddressStreamProviderTest.java b/resolver-dns-native-macos/src/test/java/io/netty/resolver/dns/macos/MacOSDnsServerAddressStreamProviderTest.java deleted file mode 100644 index 434eec039e..0000000000 --- a/resolver-dns-native-macos/src/test/java/io/netty/resolver/dns/macos/MacOSDnsServerAddressStreamProviderTest.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns.macos; - -import io.netty.resolver.dns.DnsServerAddressStream; -import io.netty.resolver.dns.DnsServerAddressStreamProvider; -import io.netty.resolver.dns.DnsServerAddressStreamProviders; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.EnabledOnOs; -import org.junit.jupiter.api.condition.OS; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.instanceOf; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNotEquals; - -@EnabledOnOs(OS.MAC) -class MacOSDnsServerAddressStreamProviderTest { - - @Test - void testStream() { - MacOSDnsServerAddressStreamProvider.ensureAvailability(); - DnsServerAddressStreamProvider provider = new MacOSDnsServerAddressStreamProvider(); - DnsServerAddressStream stream = provider.nameServerAddressStream("netty.io"); - assertNotNull(stream); - assertNotEquals(0, stream.size()); - - for (int i = 0; i < stream.size(); i++) { - assertNotEquals(0, stream.next().getPort()); - } - } - - @Test - @EnabledOnOs(OS.MAC) - void testDefaultUseCorrectInstance() { - MacOSDnsServerAddressStreamProvider.ensureAvailability(); - assertThat(DnsServerAddressStreamProviders.platformDefault(), - instanceOf(MacOSDnsServerAddressStreamProvider.class)); - } -} diff --git a/resolver-dns/pom.xml b/resolver-dns/pom.xml deleted file mode 100644 index 1baa4af303..0000000000 --- a/resolver-dns/pom.xml +++ /dev/null @@ -1,94 +0,0 @@ - - - - - 4.0.0 - - io.netty - netty-parent - 5.0.0.Final-SNAPSHOT - - - netty-resolver-dns - jar - - Netty/Resolver/DNS - - - io.netty.resolver.dns - - - - - ${project.groupId} - netty-common - ${project.version} - - - ${project.groupId} - netty-buffer - ${project.version} - - - ${project.groupId} - netty-resolver - ${project.version} - - - ${project.groupId} - netty-transport - ${project.version} - - - ${project.groupId} - netty-codec - ${project.version} - - - ${project.groupId} - netty-codec-dns - ${project.version} - - - ${project.groupId} - netty-handler - ${project.version} - - - org.apache.directory.server - apacheds-protocol-dns - test - - - - commons-lang - commons-lang - - - - - commons-lang - commons-lang - 2.6 - test - - - - diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/AuthoritativeDnsServerCache.java b/resolver-dns/src/main/java/io/netty/resolver/dns/AuthoritativeDnsServerCache.java deleted file mode 100644 index 8ae43f02a4..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/AuthoritativeDnsServerCache.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.channel.EventLoop; - -import java.net.InetSocketAddress; - -/** - * Cache which stores the nameservers that should be used to resolve a specific hostname. - */ -public interface AuthoritativeDnsServerCache { - - /** - * Returns the cached nameservers that should be used to resolve the given hostname. The returned - * {@link DnsServerAddressStream} may contain unresolved {@link InetSocketAddress}es that will be resolved - * when needed while resolving other domain names. - * - * @param hostname the hostname - * @return the cached entries or an {@code null} if none. - */ - DnsServerAddressStream get(String hostname); - - /** - * Caches a nameserver that should be used to resolve the given hostname. - * - * @param hostname the hostname - * @param address the nameserver address (which may be unresolved). - * @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, InetSocketAddress address, 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/AuthoritativeDnsServerCacheAdapter.java b/resolver-dns/src/main/java/io/netty/resolver/dns/AuthoritativeDnsServerCacheAdapter.java deleted file mode 100644 index 1bce1bb00e..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/AuthoritativeDnsServerCacheAdapter.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.channel.EventLoop; -import io.netty.handler.codec.dns.DnsRecord; - -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.util.ArrayList; -import java.util.List; - -import static java.util.Objects.requireNonNull; - -/** - * {@link AuthoritativeDnsServerCache} implementation which delegates all operations to a wrapped {@link DnsCache}. - * This implementation is only present to preserve a upgrade story. - */ -final class AuthoritativeDnsServerCacheAdapter implements AuthoritativeDnsServerCache { - - private static final DnsRecord[] EMPTY = new DnsRecord[0]; - private final DnsCache cache; - - AuthoritativeDnsServerCacheAdapter(DnsCache cache) { - this.cache = requireNonNull(cache, "cache"); - } - - @Override - public DnsServerAddressStream get(String hostname) { - List entries = cache.get(hostname, EMPTY); - if (entries == null || entries.isEmpty()) { - return null; - } - if (entries.get(0).cause() != null) { - return null; - } - - List addresses = new ArrayList<>(entries.size()); - - int i = 0; - do { - InetAddress addr = entries.get(i).address(); - addresses.add(new InetSocketAddress(addr, DefaultDnsServerAddressStreamProvider.DNS_PORT)); - } while (++i < entries.size()); - return new SequentialDnsServerAddressStream(addresses, 0); - } - - @Override - public void cache(String hostname, InetSocketAddress address, long originalTtl, EventLoop loop) { - // We only cache resolved addresses. - if (!address.isUnresolved()) { - cache.cache(hostname, EMPTY, address.getAddress(), originalTtl, loop); - } - } - - @Override - public void clear() { - cache.clear(); - } - - @Override - public boolean clear(String hostname) { - return cache.clear(hostname); - } -} diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/BiDnsQueryLifecycleObserver.java b/resolver-dns/src/main/java/io/netty/resolver/dns/BiDnsQueryLifecycleObserver.java deleted file mode 100644 index 1b0abbfcd5..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/BiDnsQueryLifecycleObserver.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.handler.codec.dns.DnsQuestion; -import io.netty.handler.codec.dns.DnsResponseCode; -import io.netty.util.concurrent.Future; - -import java.net.InetSocketAddress; -import java.util.List; - -import static java.util.Objects.requireNonNull; - -/** - * Combines two {@link DnsQueryLifecycleObserver} into a single {@link DnsQueryLifecycleObserver}. - */ -public final class BiDnsQueryLifecycleObserver implements DnsQueryLifecycleObserver { - private final DnsQueryLifecycleObserver a; - private final DnsQueryLifecycleObserver b; - - /** - * Create a new instance. - * @param a The {@link DnsQueryLifecycleObserver} that will receive events first. - * @param b The {@link DnsQueryLifecycleObserver} that will receive events second. - */ - public BiDnsQueryLifecycleObserver(DnsQueryLifecycleObserver a, DnsQueryLifecycleObserver b) { - this.a = requireNonNull(a, "a"); - this.b = requireNonNull(b, "b"); - } - - @Override - public void queryWritten(InetSocketAddress dnsServerAddress, Future future) { - try { - a.queryWritten(dnsServerAddress, future); - } finally { - b.queryWritten(dnsServerAddress, future); - } - } - - @Override - public void queryCancelled(int queriesRemaining) { - try { - a.queryCancelled(queriesRemaining); - } finally { - b.queryCancelled(queriesRemaining); - } - } - - @Override - public DnsQueryLifecycleObserver queryRedirected(List nameServers) { - try { - a.queryRedirected(nameServers); - } finally { - b.queryRedirected(nameServers); - } - return this; - } - - @Override - public DnsQueryLifecycleObserver queryCNAMEd(DnsQuestion cnameQuestion) { - try { - a.queryCNAMEd(cnameQuestion); - } finally { - b.queryCNAMEd(cnameQuestion); - } - return this; - } - - @Override - public DnsQueryLifecycleObserver queryNoAnswer(DnsResponseCode code) { - try { - a.queryNoAnswer(code); - } finally { - b.queryNoAnswer(code); - } - return this; - } - - @Override - public void queryFailed(Throwable cause) { - try { - a.queryFailed(cause); - } finally { - b.queryFailed(cause); - } - } - - @Override - public void querySucceed() { - try { - a.querySucceed(); - } finally { - b.querySucceed(); - } - } -} diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/BiDnsQueryLifecycleObserverFactory.java b/resolver-dns/src/main/java/io/netty/resolver/dns/BiDnsQueryLifecycleObserverFactory.java deleted file mode 100644 index 7abe7cbc33..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/BiDnsQueryLifecycleObserverFactory.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.handler.codec.dns.DnsQuestion; - -import static java.util.Objects.requireNonNull; - -/** - * Combines two {@link DnsQueryLifecycleObserverFactory} into a single {@link DnsQueryLifecycleObserverFactory}. - */ -public final class BiDnsQueryLifecycleObserverFactory implements DnsQueryLifecycleObserverFactory { - private final DnsQueryLifecycleObserverFactory a; - private final DnsQueryLifecycleObserverFactory b; - - /** - * Create a new instance. - * @param a The {@link DnsQueryLifecycleObserverFactory} that will receive events first. - * @param b The {@link DnsQueryLifecycleObserverFactory} that will receive events second. - */ - public BiDnsQueryLifecycleObserverFactory(DnsQueryLifecycleObserverFactory a, DnsQueryLifecycleObserverFactory b) { - this.a = requireNonNull(a, "a"); - this.b = requireNonNull(b, "b"); - } - - @Override - public DnsQueryLifecycleObserver newDnsQueryLifecycleObserver(DnsQuestion question) { - return new BiDnsQueryLifecycleObserver(a.newDnsQueryLifecycleObserver(question), - b.newDnsQueryLifecycleObserver(question)); - } -} diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/Cache.java b/resolver-dns/src/main/java/io/netty/resolver/dns/Cache.java deleted file mode 100644 index 25ab8dcf6b..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/Cache.java +++ /dev/null @@ -1,424 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.channel.EventLoop; -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.FutureCompletionStage; -import io.netty.util.concurrent.FutureContextListener; -import io.netty.util.concurrent.FutureListener; -import io.netty.util.concurrent.Promise; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.concurrent.CancellationException; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.Delayed; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; -import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; -import java.util.function.Function; - -import static java.util.Collections.singletonList; - -/** - * Abstract cache that automatically removes entries for a hostname once the TTL for an entry is reached. - * - * @param - */ -abstract class Cache { - private static final AtomicReferenceFieldUpdater FUTURE_UPDATER = - AtomicReferenceFieldUpdater.newUpdater(Cache.Entries.class, FutureAndDelay.class, "expirationFuture"); - - private static final Future CANCELLED_FUTURE = new Future() { - @Override - public boolean cancel() { - return false; - } - - @Override - public boolean isSuccess() { - return false; - } - - @Override - public boolean isFailed() { - return true; - } - - @Override - public boolean isCancellable() { - return false; - } - - @Override - public Throwable cause() { - return new CancellationException(); - } - - @Override - public Future addListener(FutureListener listener) { - throw new UnsupportedOperationException(); - } - - @Override - public Future addListener(C context, FutureContextListener listener) { - throw new UnsupportedOperationException(); - } - - @Override - public Future sync() throws InterruptedException { - return this; - } - - @Override - public Future syncUninterruptibly() { - return this; - } - - @Override - public Future await() throws InterruptedException { - return this; - } - - @Override - public Future awaitUninterruptibly() { - return this; - } - - @Override - public boolean await(long timeout, TimeUnit unit) throws InterruptedException { - return true; - } - - @Override - public boolean await(long timeoutMillis) throws InterruptedException { - return true; - } - - @Override - public boolean awaitUninterruptibly(long timeout, TimeUnit unit) { - return true; - } - - @Override - public boolean awaitUninterruptibly(long timeoutMillis) { - return true; - } - - @Override - public Object getNow() { - return null; - } - - @Override - public EventExecutor executor() { - 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(); - } - - @Override - public FutureCompletionStage asStage() { - throw new UnsupportedOperationException(); - } - - @Override - public Future map(Function mapper) { - throw new UnsupportedOperationException(); - } - - @Override - public Future flatMap(Function> mapper) { - throw new UnsupportedOperationException(); - } - - @Override - public Future cascadeTo(Promise promise) { - throw new UnsupportedOperationException(); - } - }; - private static final FutureAndDelay CANCELLED = new FutureAndDelay(CANCELLED_FUTURE, Integer.MIN_VALUE); - - // 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 - static final int MAX_SUPPORTED_TTL_SECS = (int) TimeUnit.DAYS.toSeconds(365 * 2); - - private final ConcurrentMap resolveCache = new ConcurrentHashMap<>(); - - /** - * Remove everything from the cache. - */ - final void clear() { - while (!resolveCache.isEmpty()) { - for (Iterator> i = resolveCache.entrySet().iterator(); i.hasNext();) { - Map.Entry e = i.next(); - i.remove(); - - e.getValue().clearAndCancel(); - } - } - } - - /** - * Clear all entries (if anything exists) for the given hostname and return {@code true} if anything was removed. - */ - final boolean clear(String hostname) { - Entries entries = resolveCache.remove(hostname); - return entries != null && entries.clearAndCancel(); - } - - /** - * Returns all caches entries for the given hostname. - */ - final List get(String hostname) { - Entries entries = resolveCache.get(hostname); - return entries == null ? null : entries.get(); - } - - /** - * Cache a value for the given hostname that will automatically expire once the TTL is reached. - */ - final void cache(String hostname, E value, int ttl, EventLoop loop) { - Entries entries = resolveCache.get(hostname); - if (entries == null) { - entries = new Entries(hostname); - Entries oldEntries = resolveCache.putIfAbsent(hostname, entries); - if (oldEntries != null) { - entries = oldEntries; - } - } - entries.add(value, ttl, loop); - } - - /** - * Return the number of hostames for which we have cached something. - */ - final int size() { - return resolveCache.size(); - } - - /** - * Returns {@code true} if this entry should replace all other entries that are already cached for the hostname. - */ - protected abstract boolean shouldReplaceAll(E entry); - - /** - * Sort the {@link List} for a {@code hostname} before caching these. - */ - protected void sortEntries( - @SuppressWarnings("unused") String hostname, @SuppressWarnings("unused") List entries) { - // NOOP. - } - - /** - * Returns {@code true} if both entries are equal. - */ - protected abstract boolean equals(E entry, E otherEntry); - - // Directly extend AtomicReference for intrinsics and also to keep memory overhead low. - private final class Entries extends AtomicReference> implements Runnable { - - private final String hostname; - // Needs to be package-private to be able to access it via the AtomicReferenceFieldUpdater - volatile FutureAndDelay expirationFuture; - - Entries(String hostname) { - super(Collections.emptyList()); - this.hostname = hostname; - } - - void add(E e, int ttl, EventLoop loop) { - if (!shouldReplaceAll(e)) { - for (;;) { - List entries = get(); - if (!entries.isEmpty()) { - final E firstEntry = entries.get(0); - if (shouldReplaceAll(firstEntry)) { - assert entries.size() == 1; - - if (compareAndSet(entries, singletonList(e))) { - scheduleCacheExpirationIfNeeded(ttl, loop); - return; - } else { - // Need to try again as CAS failed - continue; - } - } - - // Create a new List for COW semantics - List newEntries = new ArrayList<>(entries.size() + 1); - int i = 0; - E replacedEntry = null; - do { - E 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 (!Cache.this.equals(e, entry)) { - newEntries.add(entry); - } else { - replacedEntry = entry; - newEntries.add(e); - - ++i; - for (; i < entries.size(); ++i) { - newEntries.add(entries.get(i)); - } - break; - } - } while (++i < entries.size()); - if (replacedEntry == null) { - newEntries.add(e); - } - sortEntries(hostname, newEntries); - - if (compareAndSet(entries, Collections.unmodifiableList(newEntries))) { - scheduleCacheExpirationIfNeeded(ttl, loop); - return; - } - } else if (compareAndSet(entries, singletonList(e))) { - scheduleCacheExpirationIfNeeded(ttl, loop); - return; - } - } - } else { - set(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. - FutureAndDelay oldFuture = FUTURE_UPDATER.get(this); - if (oldFuture == null || oldFuture.getDelay(TimeUnit.SECONDS) > ttl) { - Future 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, new FutureAndDelay(newFuture, ttl))) { - if (oldFuture != null) { - oldFuture.cancel(); - } - break; - } else { - // There was something else scheduled in the meantime... Cancel and try again. - newFuture.cancel(); - } - } else { - break; - } - } - } - - boolean clearAndCancel() { - List entries = getAndSet(Collections.emptyList()); - if (entries.isEmpty()) { - return false; - } - - FutureAndDelay expirationFuture = FUTURE_UPDATER.getAndSet(this, CANCELLED); - if (expirationFuture != null) { - expirationFuture.cancel(); - } - - return true; - } - - @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(); - } - } - - private static final class FutureAndDelay implements Delayed { - final Future future; - final long deadlineNanos; - - private FutureAndDelay(Future future, int ttl) { - this.future = Objects.requireNonNull(future, "future"); - deadlineNanos = System.nanoTime() + TimeUnit.SECONDS.toNanos(ttl); - } - - void cancel() { - future.cancel(); - } - - @Override - public long getDelay(TimeUnit unit) { - return unit.convert(deadlineNanos - System.nanoTime(), TimeUnit.NANOSECONDS); - } - - @Override - public int compareTo(Delayed other) { - return Long.compare(deadlineNanos, other.getDelay(TimeUnit.NANOSECONDS)); - } - - @Override - public boolean equals(Object o) { - return o instanceof FutureAndDelay && compareTo((FutureAndDelay) o) == 0; - } - - @Override - public int hashCode() { - int result = future.hashCode(); - result = 31 * result + (int) (deadlineNanos ^ deadlineNanos >>> 32); - return result; - } - } -} diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DatagramDnsQueryContext.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DatagramDnsQueryContext.java deleted file mode 100644 index be826184b1..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DatagramDnsQueryContext.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.channel.AddressedEnvelope; -import io.netty.channel.Channel; -import io.netty.handler.codec.dns.DatagramDnsQuery; -import io.netty.handler.codec.dns.DnsQuery; -import io.netty.handler.codec.dns.DnsQuestion; -import io.netty.handler.codec.dns.DnsRecord; -import io.netty.handler.codec.dns.DnsResponse; -import io.netty.util.concurrent.Promise; - -import java.net.InetSocketAddress; - -final class DatagramDnsQueryContext extends DnsQueryContext { - - DatagramDnsQueryContext(DnsNameResolver parent, InetSocketAddress nameServerAddr, DnsQuestion question, - DnsRecord[] additionals, - Promise> promise) { - super(parent, nameServerAddr, question, additionals, promise); - } - - @Override - protected DnsQuery newQuery(int id) { - return new DatagramDnsQuery(null, nameServerAddr(), id); - } - - @Override - protected Channel channel() { - return parent().ch; - } - - @Override - protected String protocol() { - return "UDP"; - } -} diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DefaultAuthoritativeDnsServerCache.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DefaultAuthoritativeDnsServerCache.java deleted file mode 100644 index 791d8a60e7..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DefaultAuthoritativeDnsServerCache.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.channel.EventLoop; - -import java.net.InetSocketAddress; -import java.util.Comparator; -import java.util.List; -import java.util.concurrent.ConcurrentMap; - -import static io.netty.util.internal.ObjectUtil.checkPositive; -import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; -import static java.util.Objects.requireNonNull; - -/** - * Default implementation of {@link AuthoritativeDnsServerCache}, backed by a {@link ConcurrentMap}. - */ -public class DefaultAuthoritativeDnsServerCache implements AuthoritativeDnsServerCache { - - private final int minTtl; - private final int maxTtl; - private final Comparator comparator; - private final Cache resolveCache = new Cache() { - @Override - protected boolean shouldReplaceAll(InetSocketAddress entry) { - return false; - } - - @Override - protected boolean equals(InetSocketAddress entry, InetSocketAddress otherEntry) { - return entry.getHostString().equalsIgnoreCase(otherEntry.getHostString()); - } - - @Override - protected void sortEntries(String hostname, List entries) { - if (comparator != null) { - entries.sort(comparator); - } - } - }; - - /** - * Create a cache that respects the TTL returned by the DNS server. - */ - public DefaultAuthoritativeDnsServerCache() { - this(0, Cache.MAX_SUPPORTED_TTL_SECS, null); - } - - /** - * Create a cache. - * - * @param minTtl the minimum TTL - * @param maxTtl the maximum TTL - * @param comparator the {@link Comparator} to order the {@link InetSocketAddress} for a hostname or {@code null} - * if insertion order should be used. - */ - public DefaultAuthoritativeDnsServerCache(int minTtl, int maxTtl, Comparator comparator) { - 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)"); - } - this.comparator = comparator; - } - - @Override - public DnsServerAddressStream get(String hostname) { - requireNonNull(hostname, "hostname"); - - List addresses = resolveCache.get(hostname); - if (addresses == null || addresses.isEmpty()) { - return null; - } - return new SequentialDnsServerAddressStream(addresses, 0); - } - - @Override - public void cache(String hostname, InetSocketAddress address, long originalTtl, EventLoop loop) { - requireNonNull(hostname, "hostname"); - requireNonNull(address, "address"); - requireNonNull(loop, "loop"); - - if (address.getHostString() == null) { - // We only cache addresses that have also a host string as we will need it later when trying to replace - // unresolved entries in the cache. - return; - } - - resolveCache.cache(hostname, address, Math.max(minTtl, (int) Math.min(maxTtl, originalTtl)), loop); - } - - @Override - public void clear() { - resolveCache.clear(); - } - - @Override - public boolean clear(String hostname) { - requireNonNull(hostname, "hostname"); - - return resolveCache.clear(hostname); - } - - @Override - public String toString() { - return "DefaultAuthoritativeDnsServerCache(minTtl=" + minTtl + ", maxTtl=" + maxTtl + ", cached nameservers=" + - resolveCache.size() + ')'; - } -} 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 deleted file mode 100644 index 2f48ded776..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DefaultDnsCache.java +++ /dev/null @@ -1,218 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.channel.EventLoop; -import io.netty.handler.codec.dns.DnsRecord; -import io.netty.util.internal.StringUtil; - -import java.net.InetAddress; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.ConcurrentMap; - -import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; -import static java.util.Objects.requireNonNull; - -/** - * Default implementation of {@link DnsCache}, backed by a {@link ConcurrentMap}. - * If any additional {@link DnsRecord} is used, no caching takes place. - */ -public class DefaultDnsCache implements DnsCache { - - private final Cache resolveCache = new Cache() { - - @Override - protected boolean shouldReplaceAll(DefaultDnsCacheEntry entry) { - return entry.cause() != null; - } - - @Override - protected boolean equals(DefaultDnsCacheEntry entry, DefaultDnsCacheEntry otherEntry) { - if (entry.address() != null) { - return entry.address().equals(otherEntry.address()); - } - if (otherEntry.address() != null) { - return false; - } - return entry.cause().equals(otherEntry.cause()); - } - }; - - private final int minTtl; - private final int maxTtl; - private final int negativeTtl; - - /** - * Create a cache that respects the TTL returned by the DNS server - * and doesn't cache negative responses. - */ - public DefaultDnsCache() { - this(0, Cache.MAX_SUPPORTED_TTL_SECS, 0); - } - - /** - * Create a cache. - * @param minTtl the minimum TTL - * @param maxTtl the maximum TTL - * @param negativeTtl the TTL for failed queries - */ - public DefaultDnsCache(int minTtl, int maxTtl, int negativeTtl) { - this.minTtl = Math.min(Cache.MAX_SUPPORTED_TTL_SECS, checkPositiveOrZero(minTtl, "minTtl")); - this.maxTtl = Math.min(Cache.MAX_SUPPORTED_TTL_SECS, checkPositiveOrZero(maxTtl, "maxTtl")); - if (minTtl > maxTtl) { - throw new IllegalArgumentException( - "minTtl: " + minTtl + ", maxTtl: " + maxTtl + " (expected: 0 <= minTtl <= maxTtl)"); - } - this.negativeTtl = checkPositiveOrZero(negativeTtl, "negativeTtl"); - } - - /** - * Returns the minimum TTL of the cached DNS resource records (in seconds). - * - * @see #maxTtl() - */ - public int minTtl() { - return minTtl; - } - - /** - * Returns the maximum TTL of the cached DNS resource records (in seconds). - * - * @see #minTtl() - */ - public int maxTtl() { - return maxTtl; - } - - /** - * Returns the TTL of the cache for the failed DNS queries (in seconds). The default value is {@code 0}, which - * disables the cache for negative results. - */ - public int negativeTtl() { - return negativeTtl; - } - - @Override - public void clear() { - resolveCache.clear(); - } - - @Override - public boolean clear(String hostname) { - requireNonNull(hostname, "hostname"); - return resolveCache.clear(appendDot(hostname)); - } - - private static boolean emptyAdditionals(DnsRecord[] additionals) { - return additionals == null || additionals.length == 0; - } - - @Override - public List get(String hostname, DnsRecord[] additionals) { - requireNonNull(hostname, "hostname"); - if (!emptyAdditionals(additionals)) { - return Collections.emptyList(); - } - - return resolveCache.get(appendDot(hostname)); - } - - @Override - public DnsCacheEntry cache(String hostname, DnsRecord[] additionals, - InetAddress address, long originalTtl, EventLoop loop) { - requireNonNull(hostname, "hostname"); - requireNonNull(address, "address"); - requireNonNull(loop, "loop"); - DefaultDnsCacheEntry e = new DefaultDnsCacheEntry(hostname, address); - if (maxTtl == 0 || !emptyAdditionals(additionals)) { - return e; - } - resolveCache.cache(appendDot(hostname), e, Math.max(minTtl, (int) Math.min(maxTtl, originalTtl)), loop); - return e; - } - - @Override - public DnsCacheEntry cache(String hostname, DnsRecord[] additionals, Throwable cause, EventLoop loop) { - requireNonNull(hostname, "hostname"); - requireNonNull(cause, "cause"); - requireNonNull(loop, "loop"); - - DefaultDnsCacheEntry e = new DefaultDnsCacheEntry(hostname, cause); - if (negativeTtl == 0 || !emptyAdditionals(additionals)) { - return e; - } - - resolveCache.cache(appendDot(hostname), e, negativeTtl, loop); - return e; - } - - @Override - public String toString() { - return new StringBuilder() - .append("DefaultDnsCache(minTtl=") - .append(minTtl).append(", maxTtl=") - .append(maxTtl).append(", negativeTtl=") - .append(negativeTtl).append(", cached resolved hostname=") - .append(resolveCache.size()).append(')') - .toString(); - } - - private static final class DefaultDnsCacheEntry implements DnsCacheEntry { - private final String hostname; - private final InetAddress address; - private final Throwable cause; - - DefaultDnsCacheEntry(String hostname, InetAddress address) { - this.hostname = hostname; - this.address = address; - cause = null; - } - - DefaultDnsCacheEntry(String hostname, Throwable cause) { - this.hostname = hostname; - this.cause = cause; - address = null; - } - - @Override - public InetAddress address() { - return address; - } - - @Override - public Throwable cause() { - return cause; - } - - String hostname() { - return hostname; - } - - @Override - public String toString() { - if (cause != null) { - return hostname + '/' + cause; - } else { - return address.toString(); - } - } - } - - private static String appendDot(String hostname) { - return StringUtil.endsWith(hostname, '.') ? hostname : hostname + '.'; - } -} 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 deleted file mode 100644 index 989c95226f..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DefaultDnsCnameCache.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.channel.EventLoop; -import io.netty.util.AsciiString; - -import java.util.List; - -import static io.netty.util.internal.ObjectUtil.checkPositive; -import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; -import static java.util.Objects.requireNonNull; - -/** - * Default implementation of a {@link DnsCnameCache}. - */ -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) { - requireNonNull(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) { - requireNonNull(hostname, "hostname"); - requireNonNull(cname, "cname"); - requireNonNull(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) { - requireNonNull(hostname, "hostname"); - return cache.clear(hostname); - } -} 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 deleted file mode 100644 index fa367822d2..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DefaultDnsServerAddressStreamProvider.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.util.NetUtil; -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.SocketUtils; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.lang.reflect.Method; -import java.net.Inet6Address; -import java.net.InetSocketAddress; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import static io.netty.resolver.dns.DnsServerAddresses.sequential; - -/** - * A {@link DnsServerAddressStreamProvider} which will use predefined default DNS servers to use for DNS resolution. - * These defaults do not respect your host's machines defaults. - *

    - * This may use the JDK's blocking DNS resolution to bootstrap the default DNS server addresses. - */ -public final class DefaultDnsServerAddressStreamProvider implements DnsServerAddressStreamProvider { - private static final InternalLogger logger = - InternalLoggerFactory.getInstance(DefaultDnsServerAddressStreamProvider.class); - public static final DefaultDnsServerAddressStreamProvider INSTANCE = new DefaultDnsServerAddressStreamProvider(); - - private static final List DEFAULT_NAME_SERVER_LIST; - private static final DnsServerAddresses DEFAULT_NAME_SERVERS; - static final int DNS_PORT = 53; - - static { - final List defaultNameServers = new ArrayList<>(2); - 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); - } - - // Only try when using Java8 and lower as otherwise it will produce: - // WARNING: Illegal reflective access by io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider - if (PlatformDependent.javaVersion() < 9 && defaultNameServers.isEmpty()) { - try { - Class configClass = Class.forName("sun.net.dns.ResolverConfiguration"); - Method open = configClass.getMethod("open"); - Method nameservers = configClass.getMethod("nameservers"); - Object instance = open.invoke(null); - - @SuppressWarnings("unchecked") - final List list = (List) nameservers.invoke(instance); - for (String a: list) { - if (a != null) { - defaultNameServers.add(new InetSocketAddress(SocketUtils.addressByName(a), DNS_PORT)); - } - } - } catch (Exception ignore) { - // Failed to get the system name server list via reflection. - // Will add the default name servers afterwards. - } - } - - if (!defaultNameServers.isEmpty()) { - if (logger.isDebugEnabled()) { - logger.debug( - "Default DNS servers: {} (sun.net.dns.ResolverConfiguration)", defaultNameServers); - } - } else { - // Depending if IPv6 or IPv4 is used choose the correct DNS servers provided by google: - // https://developers.google.com/speed/public-dns/docs/using - // https://docs.oracle.com/javase/7/docs/api/java/net/doc-files/net-properties.html - if (NetUtil.isIpV6AddressesPreferred() || - (NetUtil.LOCALHOST instanceof Inet6Address && !NetUtil.isIpV4StackPreferred())) { - Collections.addAll( - defaultNameServers, - SocketUtils.socketAddress("2001:4860:4860::8888", DNS_PORT), - SocketUtils.socketAddress("2001:4860:4860::8844", DNS_PORT)); - } else { - Collections.addAll( - defaultNameServers, - SocketUtils.socketAddress("8.8.8.8", DNS_PORT), - SocketUtils.socketAddress("8.8.4.4", DNS_PORT)); - } - - if (logger.isWarnEnabled()) { - logger.warn( - "Default DNS servers: {} (Google Public DNS as a fallback)", defaultNameServers); - } - } - - DEFAULT_NAME_SERVER_LIST = Collections.unmodifiableList(defaultNameServers); - DEFAULT_NAME_SERVERS = sequential(DEFAULT_NAME_SERVER_LIST); - } - - private DefaultDnsServerAddressStreamProvider() { - } - - @Override - public DnsServerAddressStream nameServerAddressStream(String hostname) { - return DEFAULT_NAME_SERVERS.stream(); - } - - /** - * Returns the list of the system DNS server addresses. If it failed to retrieve the list of the system DNS server - * addresses from the environment, it will return {@code "8.8.8.8"} and {@code "8.8.4.4"}, the addresses of the - * Google public DNS servers. - */ - public static List defaultAddressList() { - return DEFAULT_NAME_SERVER_LIST; - } - - /** - * Returns the {@link DnsServerAddresses} that yields the system DNS server addresses sequentially. If it failed to - * retrieve the list of the system DNS server addresses from the environment, it will use {@code "8.8.8.8"} and - * {@code "8.8.4.4"}, the addresses of the Google public DNS servers. - *

    - * This method has the same effect with the following code: - *

    -     * DnsServerAddresses.sequential(DnsServerAddresses.defaultAddressList());
    -     * 
    - *

    - */ - public static DnsServerAddresses defaultAddresses() { - return DEFAULT_NAME_SERVERS; - } -} diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DefaultDnsServerAddresses.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DefaultDnsServerAddresses.java deleted file mode 100644 index 936b99c8ec..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DefaultDnsServerAddresses.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.resolver.dns; - -import java.net.InetSocketAddress; -import java.util.List; - -abstract class DefaultDnsServerAddresses extends DnsServerAddresses { - - protected final List addresses; - private final String strVal; - - DefaultDnsServerAddresses(String type, List addresses) { - this.addresses = addresses; - - final StringBuilder buf = new StringBuilder(type.length() + 2 + addresses.size() * 16); - buf.append(type).append('('); - - for (InetSocketAddress a: addresses) { - buf.append(a).append(", "); - } - - buf.setLength(buf.length() - 2); - buf.append(')'); - - strVal = buf.toString(); - } - - @Override - public String toString() { - return strVal; - } -} 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 deleted file mode 100644 index 3b39e4da4e..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DirContextUtils.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.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: - // - https://docs.oracle.com/javase/8/docs/technotes/guides/jndi/jndi-dns.html - // - https://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. - } - } -} diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsAddressDecoder.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsAddressDecoder.java deleted file mode 100644 index d3f754512d..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsAddressDecoder.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import java.net.IDN; -import java.net.InetAddress; -import java.net.UnknownHostException; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufHolder; -import io.netty.handler.codec.dns.DnsRawRecord; -import io.netty.handler.codec.dns.DnsRecord; - -/** - * Decodes an {@link InetAddress} from an A or AAAA {@link DnsRawRecord}. - */ -final class DnsAddressDecoder { - - private static final int INADDRSZ4 = 4; - private static final int INADDRSZ6 = 16; - - /** - * Decodes an {@link InetAddress} from an A or AAAA {@link DnsRawRecord}. - * - * @param record the {@link DnsRecord}, most likely a {@link DnsRawRecord} - * @param name the host name of the decoded address - * @param decodeIdn whether to convert {@code name} to a unicode host name - * - * @return the {@link InetAddress}, or {@code null} if {@code record} is not a {@link DnsRawRecord} or - * its content is malformed - */ - static InetAddress decodeAddress(DnsRecord record, String name, boolean decodeIdn) { - if (!(record instanceof DnsRawRecord)) { - return null; - } - final ByteBuf content = ((ByteBufHolder) record).content(); - final int contentLen = content.readableBytes(); - if (contentLen != INADDRSZ4 && contentLen != INADDRSZ6) { - return null; - } - - final byte[] addrBytes = new byte[contentLen]; - content.getBytes(content.readerIndex(), addrBytes); - - try { - return InetAddress.getByAddress(decodeIdn ? IDN.toUnicode(name) : name, addrBytes); - } catch (UnknownHostException e) { - // Should never reach here. - throw new Error(e); - } - } - - private DnsAddressDecoder() { } -} 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 deleted file mode 100644 index e498dec7db..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsAddressResolveContext.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import static io.netty.resolver.dns.DnsAddressDecoder.decodeAddress; - -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.List; - -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 { - - private final DnsCache resolveCache; - private final AuthoritativeDnsServerCache authoritativeDnsServerCache; - private final boolean completeEarlyIfPossible; - - DnsAddressResolveContext(DnsNameResolver parent, Promise originalPromise, - String hostname, DnsRecord[] additionals, - DnsServerAddressStream nameServerAddrs, int allowedQueries, DnsCache resolveCache, - AuthoritativeDnsServerCache authoritativeDnsServerCache, - boolean completeEarlyIfPossible) { - super(parent, originalPromise, hostname, DnsRecord.CLASS_IN, - parent.resolveRecordTypes(), additionals, nameServerAddrs, allowedQueries); - this.resolveCache = resolveCache; - this.authoritativeDnsServerCache = authoritativeDnsServerCache; - this.completeEarlyIfPossible = completeEarlyIfPossible; - } - - @Override - DnsResolveContext newResolverContext(DnsNameResolver parent, Promise originalPromise, - String hostname, - int dnsClass, DnsRecordType[] expectedTypes, - DnsRecord[] additionals, - DnsServerAddressStream nameServerAddrs, int allowedQueries) { - return new DnsAddressResolveContext(parent, originalPromise, hostname, additionals, nameServerAddrs, - allowedQueries, resolveCache, authoritativeDnsServerCache, completeEarlyIfPossible); - } - - @Override - InetAddress convertRecord(DnsRecord record, String hostname, DnsRecord[] additionals, EventLoop eventLoop) { - return decodeAddress(record, hostname, parent.isDecodeIdn()); - } - - @Override - List filterResults(List unfiltered) { - unfiltered.sort(PreferredAddressTypeComparator.comparator(parent.preferredAddressType())); - return unfiltered; - } - - @Override - boolean isCompleteEarly(InetAddress resolved) { - return completeEarlyIfPossible && parent.preferredAddressType().addressType() == resolved.getClass(); - } - - @Override - boolean isDuplicateAllowed() { - // We don't want include duplicates to mimic JDK behaviour. - return false; - } - - @Override - void cache(String hostname, DnsRecord[] additionals, - DnsRecord result, InetAddress convertedResult) { - resolveCache.cache(hostname, additionals, convertedResult, result.timeToLive(), parent.ch.executor()); - } - - @Override - void cache(String hostname, DnsRecord[] additionals, UnknownHostException cause) { - resolveCache.cache(hostname, additionals, cause, parent.ch.executor()); - } - - @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 (!DnsNameResolver.doResolveAllCached( - hostname, additionals, nextPromise, resolveCache, parent.resolvedInternetProtocolFamiliesUnsafe())) { - super.doSearchDomainQuery(hostname, nextPromise); - } - } - - @Override - DnsCache resolveCache() { - return resolveCache; - } - - @Override - AuthoritativeDnsServerCache authoritativeDnsServerCache() { - return authoritativeDnsServerCache; - } -} diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsAddressResolverGroup.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsAddressResolverGroup.java deleted file mode 100644 index 31cf8a06cd..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsAddressResolverGroup.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.resolver.dns; - -import io.netty.channel.ChannelFactory; -import io.netty.channel.EventLoop; -import io.netty.channel.socket.DatagramChannel; -import io.netty.resolver.AddressResolver; -import io.netty.resolver.AddressResolverGroup; -import io.netty.resolver.InetSocketAddressResolver; -import io.netty.resolver.NameResolver; -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.Promise; -import io.netty.util.internal.StringUtil; - -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.util.List; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -/** - * A {@link AddressResolverGroup} of {@link DnsNameResolver}s. - */ -public class DnsAddressResolverGroup extends AddressResolverGroup { - - private final DnsNameResolverBuilder dnsResolverBuilder; - - private final ConcurrentMap> resolvesInProgress = new ConcurrentHashMap<>(); - private final ConcurrentMap>> resolveAllsInProgress = new ConcurrentHashMap<>(); - - public DnsAddressResolverGroup(DnsNameResolverBuilder dnsResolverBuilder) { - this.dnsResolverBuilder = dnsResolverBuilder.copy(); - } - - public DnsAddressResolverGroup( - Class channelType, - DnsServerAddressStreamProvider nameServerProvider) { - this.dnsResolverBuilder = new DnsNameResolverBuilder(); - dnsResolverBuilder.channelType(channelType).nameServerProvider(nameServerProvider); - } - - public DnsAddressResolverGroup( - ChannelFactory channelFactory, - DnsServerAddressStreamProvider nameServerProvider) { - this.dnsResolverBuilder = new DnsNameResolverBuilder(); - dnsResolverBuilder.channelFactory(channelFactory).nameServerProvider(nameServerProvider); - } - - @SuppressWarnings("deprecation") - @Override - protected final AddressResolver newResolver(EventExecutor executor) throws Exception { - if (!(executor instanceof EventLoop)) { - throw new IllegalStateException( - "unsupported executor type: " + StringUtil.simpleClassName(executor) + - " (expected: " + StringUtil.simpleClassName(EventLoop.class)); - } - - // we don't really need to pass channelFactory and nameServerProvider separately, - // but still keep this to ensure backward compatibility with (potentially) override methods - EventLoop loop = dnsResolverBuilder.eventLoop; - return newResolver(loop == null ? (EventLoop) executor : loop, - dnsResolverBuilder.channelFactory(), - dnsResolverBuilder.nameServerProvider()); - } - - /** - * @deprecated Override {@link #newNameResolver(EventLoop, ChannelFactory, DnsServerAddressStreamProvider)}. - */ - @Deprecated - protected AddressResolver newResolver( - EventLoop eventLoop, ChannelFactory channelFactory, - DnsServerAddressStreamProvider nameServerProvider) throws Exception { - - final NameResolver resolver = new InflightNameResolver<>( - eventLoop, - newNameResolver(eventLoop, channelFactory, nameServerProvider), - resolvesInProgress, - resolveAllsInProgress); - - return newAddressResolver(eventLoop, resolver); - } - - /** - * Creates a new {@link NameResolver}. Override this method to create an alternative {@link NameResolver} - * implementation or override the default configuration. - */ - protected NameResolver newNameResolver(EventLoop eventLoop, - ChannelFactory channelFactory, - DnsServerAddressStreamProvider nameServerProvider) - throws Exception { - DnsNameResolverBuilder builder = dnsResolverBuilder.copy(); - - // once again, channelFactory and nameServerProvider are most probably set in builder already, - // but I do reassign them again to avoid corner cases with override methods - return builder.eventLoop(eventLoop) - .channelFactory(channelFactory) - .nameServerProvider(nameServerProvider) - .build(); - } - - /** - * Creates a new {@link AddressResolver}. Override this method to create an alternative {@link AddressResolver} - * implementation or override the default configuration. - */ - protected AddressResolver newAddressResolver(EventLoop eventLoop, - NameResolver resolver) - throws Exception { - return new InetSocketAddressResolver(eventLoop, resolver); - } -} diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsCache.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsCache.java deleted file mode 100644 index 9310ac4b86..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsCache.java +++ /dev/null @@ -1,76 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.channel.EventLoop; -import io.netty.handler.codec.dns.DnsRecord; - -import java.net.InetAddress; -import java.util.List; - -/** - * A cache for DNS resolution entries. - */ -public interface DnsCache { - - /** - * Clears all the resolved addresses cached by this resolver. - * - * @see #clear(String) - */ - void clear(); - - /** - * Clears the resolved addresses of the specified host name from the cache of this resolver. - * - * @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); - - /** - * Return the cached entries for the given hostname. - * @param hostname the hostname - * @param additionals the additional records - * @return the cached entries - */ - List get(String hostname, DnsRecord[] additionals); - - /** - * Create a new {@link DnsCacheEntry} and cache a resolved address for a given hostname. - * @param hostname the hostname - * @param additionals the additional records - * @param address the resolved address - * @param originalTtl the TLL as returned by the DNS server - * @param loop the {@link EventLoop} used to register the TTL timeout - * @return The {@link DnsCacheEntry} corresponding to this cache entry. - */ - DnsCacheEntry cache(String hostname, DnsRecord[] additionals, InetAddress address, long originalTtl, - EventLoop loop); - - /** - * Cache the resolution failure for a given hostname. - * Be aware this won't be called with timeout / cancel / transport exceptions. - * - * @param hostname the hostname - * @param additionals the additional records - * @param cause the resolution failure - * @param loop the {@link EventLoop} used to register the TTL timeout - * @return The {@link DnsCacheEntry} corresponding to this cache entry, or {@code null} if this cache doesn't - * support caching failed responses. - */ - DnsCacheEntry cache(String hostname, DnsRecord[] additionals, Throwable cause, EventLoop loop); -} diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsCacheEntry.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsCacheEntry.java deleted file mode 100644 index 272e5b838a..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsCacheEntry.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import java.net.InetAddress; - -/** - * Represents the results from a previous DNS query which can be cached. - */ -public interface DnsCacheEntry { - /** - * Get the resolved address. - *

    - * This may be null if the resolution failed, and in that case {@link #cause()} will describe the failure. - * @return the resolved address. - */ - InetAddress address(); - - /** - * If the DNS query failed this will provide the rational. - * @return the rational for why the DNS query failed, or {@code null} if the query hasn't failed. - */ - Throwable cause(); -} 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 deleted file mode 100644 index c21399faed..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsCnameCache.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.channel.EventLoop; - -/** - * A cache for {@code CNAME}s. - */ -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 deleted file mode 100644 index 2fa6994ef6..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolver.java +++ /dev/null @@ -1,1472 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.bootstrap.Bootstrap; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.AddressedEnvelope; -import io.netty.channel.Channel; -import io.netty.channel.ChannelFactory; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelOption; -import io.netty.channel.EventLoop; -import io.netty.channel.FixedRecvByteBufAllocator; -import io.netty.channel.socket.DatagramChannel; -import io.netty.channel.socket.DatagramPacket; -import io.netty.channel.socket.InternetProtocolFamily; -import io.netty.channel.socket.SocketChannel; -import io.netty.handler.codec.CorruptedFrameException; -import io.netty.handler.codec.dns.DatagramDnsQueryEncoder; -import io.netty.handler.codec.dns.DatagramDnsResponse; -import io.netty.handler.codec.dns.DatagramDnsResponseDecoder; -import io.netty.handler.codec.dns.DefaultDnsRawRecord; -import io.netty.handler.codec.dns.DnsQuestion; -import io.netty.handler.codec.dns.DnsRawRecord; -import io.netty.handler.codec.dns.DnsRecord; -import io.netty.handler.codec.dns.DnsRecordType; -import io.netty.handler.codec.dns.DnsResponse; -import io.netty.handler.codec.dns.TcpDnsQueryEncoder; -import io.netty.handler.codec.dns.TcpDnsResponseDecoder; -import io.netty.resolver.DefaultHostsFileEntriesResolver; -import io.netty.resolver.HostsFileEntries; -import io.netty.resolver.HostsFileEntriesResolver; -import io.netty.resolver.InetNameResolver; -import io.netty.resolver.ResolvedAddressTypes; -import io.netty.util.NetUtil; -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.FastThreadLocal; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; -import io.netty.util.internal.EmptyArrays; -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.lang.reflect.Method; -import java.net.IDN; -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.NetworkInterface; -import java.net.SocketAddress; -import java.net.SocketException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider.DNS_PORT; -import static io.netty.util.internal.ObjectUtil.checkPositive; -import static java.util.Objects.requireNonNull; - -/** - * A DNS-based {@link InetNameResolver}. - */ -public class DnsNameResolver extends InetNameResolver { - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(DnsNameResolver.class); - private static final String LOCALHOST = "localhost"; - private static final String WINDOWS_HOST_NAME; - private static final InetAddress LOCALHOST_ADDRESS; - private static final DnsRecord[] EMPTY_ADDITIONALS = new DnsRecord[0]; - private static final DnsRecordType[] IPV4_ONLY_RESOLVED_RECORD_TYPES = - {DnsRecordType.A}; - private static final InternetProtocolFamily[] IPV4_ONLY_RESOLVED_PROTOCOL_FAMILIES = - {InternetProtocolFamily.IPv4}; - private static final DnsRecordType[] IPV4_PREFERRED_RESOLVED_RECORD_TYPES = - {DnsRecordType.A, DnsRecordType.AAAA}; - private static final InternetProtocolFamily[] IPV4_PREFERRED_RESOLVED_PROTOCOL_FAMILIES = - {InternetProtocolFamily.IPv4, InternetProtocolFamily.IPv6}; - private static final DnsRecordType[] IPV6_ONLY_RESOLVED_RECORD_TYPES = - {DnsRecordType.AAAA}; - private static final InternetProtocolFamily[] IPV6_ONLY_RESOLVED_PROTOCOL_FAMILIES = - {InternetProtocolFamily.IPv6}; - private static final DnsRecordType[] IPV6_PREFERRED_RESOLVED_RECORD_TYPES = - {DnsRecordType.AAAA, DnsRecordType.A}; - private static final InternetProtocolFamily[] IPV6_PREFERRED_RESOLVED_PROTOCOL_FAMILIES = - {InternetProtocolFamily.IPv6, InternetProtocolFamily.IPv4}; - - static final ResolvedAddressTypes DEFAULT_RESOLVE_ADDRESS_TYPES; - static final String[] DEFAULT_SEARCH_DOMAINS; - private static final UnixResolverOptions DEFAULT_OPTIONS; - - static { - if (NetUtil.isIpV4StackPreferred() || !anyInterfaceSupportsIpV6()) { - DEFAULT_RESOLVE_ADDRESS_TYPES = ResolvedAddressTypes.IPV4_ONLY; - LOCALHOST_ADDRESS = NetUtil.LOCALHOST4; - } else { - if (NetUtil.isIpV6AddressesPreferred()) { - DEFAULT_RESOLVE_ADDRESS_TYPES = ResolvedAddressTypes.IPV6_PREFERRED; - LOCALHOST_ADDRESS = NetUtil.LOCALHOST6; - } else { - DEFAULT_RESOLVE_ADDRESS_TYPES = ResolvedAddressTypes.IPV4_PREFERRED; - LOCALHOST_ADDRESS = NetUtil.LOCALHOST4; - } - } - - String hostName; - try { - hostName = PlatformDependent.isWindows() ? InetAddress.getLocalHost().getHostName() : null; - } catch (Exception ignore) { - hostName = null; - } - WINDOWS_HOST_NAME = hostName; - } - - static { - String[] searchDomains; - try { - List list = PlatformDependent.isWindows() - ? getSearchDomainsHack() - : UnixResolverDnsServerAddressStreamProvider.parseEtcResolverSearchDomains(); - searchDomains = list.toArray(EmptyArrays.EMPTY_STRINGS); - } catch (Exception ignore) { - // Failed to get the system name search domain list. - searchDomains = EmptyArrays.EMPTY_STRINGS; - } - DEFAULT_SEARCH_DOMAINS = searchDomains; - - UnixResolverOptions options; - try { - options = UnixResolverDnsServerAddressStreamProvider.parseEtcResolverOptions(); - } catch (Exception ignore) { - options = UnixResolverOptions.newBuilder().build(); - } - DEFAULT_OPTIONS = options; - } - - /** - * Returns {@code true} if any {@link NetworkInterface} supports {@code IPv6}, {@code false} otherwise. - */ - private static boolean anyInterfaceSupportsIpV6() { - try { - Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); - while (interfaces.hasMoreElements()) { - NetworkInterface iface = interfaces.nextElement(); - Enumeration addresses = iface.getInetAddresses(); - while (addresses.hasMoreElements()) { - InetAddress inetAddress = addresses.nextElement(); - if (inetAddress instanceof Inet6Address && !inetAddress.isAnyLocalAddress() && - !inetAddress.isLoopbackAddress() && !inetAddress.isLinkLocalAddress()) { - return true; - } - } - } - } catch (SocketException e) { - logger.debug("Unable to detect if any interface supports IPv6, assuming IPv4-only", e); - // ignore - } - return false; - } - - @SuppressWarnings("unchecked") - private static List getSearchDomainsHack() throws Exception { - // Only try if not using Java9 and later - // See https://github.com/netty/netty/issues/9500 - if (PlatformDependent.javaVersion() < 9) { - // 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); - } - return Collections.emptyList(); - } - - private static final DatagramDnsResponseDecoder DATAGRAM_DECODER = new DatagramDnsResponseDecoder() { - @Override - protected DnsResponse decodeResponse(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception { - DnsResponse response = super.decodeResponse(ctx, packet); - if (packet.content().isReadable()) { - // If there is still something to read we did stop parsing because of a truncated message. - // This can happen if we enabled EDNS0 but our MTU is not big enough to handle all the - // data. - response.setTruncated(true); - - if (logger.isDebugEnabled()) { - logger.debug( - "{} RECEIVED: UDP truncated packet received, consider adjusting maxPayloadSize for the {}.", - ctx.channel(), StringUtil.simpleClassName(DnsNameResolver.class)); - } - } - return response; - } - }; - private static final DatagramDnsQueryEncoder DATAGRAM_ENCODER = new DatagramDnsQueryEncoder(); - private static final TcpDnsQueryEncoder TCP_ENCODER = new TcpDnsQueryEncoder(); - - final Future channelFuture; - final Channel ch; - - // Comparator that ensures we will try first to use the nameservers that use our preferred address type. - private final Comparator nameServerComparator; - /** - * Manages the {@link DnsQueryContext}s in progress and their query IDs. - */ - final DnsQueryContextManager queryContextManager = new DnsQueryContextManager(); - - /** - * Cache for {@link #doResolve(String, Promise)} and {@link #doResolveAll(String, Promise)}. - */ - private final DnsCache resolveCache; - private final AuthoritativeDnsServerCache authoritativeDnsServerCache; - private final DnsCnameCache cnameCache; - - private final FastThreadLocal nameServerAddrStream = - new FastThreadLocal() { - @Override - protected DnsServerAddressStream initialValue() { - return dnsServerAddressStreamProvider.nameServerAddressStream(""); - } - }; - - private final long queryTimeoutMillis; - private final int maxQueriesPerResolve; - private final ResolvedAddressTypes resolvedAddressTypes; - private final InternetProtocolFamily[] resolvedInternetProtocolFamilies; - private final boolean recursionDesired; - private final int maxPayloadSize; - private final boolean optResourceEnabled; - private final HostsFileEntriesResolver hostsFileEntriesResolver; - private final DnsServerAddressStreamProvider dnsServerAddressStreamProvider; - private final String[] searchDomains; - private final int ndots; - private final boolean supportsAAAARecords; - private final boolean supportsARecords; - private final InternetProtocolFamily preferredAddressType; - private final DnsRecordType[] resolveRecordTypes; - private final boolean decodeIdn; - private final DnsQueryLifecycleObserverFactory dnsQueryLifecycleObserverFactory; - private final boolean completeOncePreferredResolved; - private final ChannelFactory socketChannelFactory; - - /** - * Creates a new DNS-based name resolver that communicates with the specified list of DNS servers. - * - * @param eventLoop the {@link EventLoop} which will perform the communication with the DNS servers - * @param channelFactory the {@link ChannelFactory} that will create a {@link DatagramChannel} - * @param resolveCache the DNS resolved entries cache - * @param authoritativeDnsServerCache the cache used to find the authoritative DNS server for a domain - * @param dnsQueryLifecycleObserverFactory used to generate new instances of {@link DnsQueryLifecycleObserver} which - * can be used to track metrics for DNS servers. - * @param queryTimeoutMillis timeout of each DNS query in millis - * @param resolvedAddressTypes the preferred address types - * @param recursionDesired if recursion desired flag must be set - * @param maxQueriesPerResolve the maximum allowed number of DNS queries for a given name resolution - * @param maxPayloadSize the capacity of the datagram packet buffer - * @param optResourceEnabled if automatic inclusion of a optional records is enabled - * @param hostsFileEntriesResolver the {@link HostsFileEntriesResolver} used to check for local aliases - * @param dnsServerAddressStreamProvider The {@link DnsServerAddressStreamProvider} used to determine the name - * servers for each hostname lookup. - * @param searchDomains the list of search domain - * (can be null, if so, will try to default to the underlying platform ones) - * @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, - final DnsCache resolveCache, - final DnsCache authoritativeDnsServerCache, - DnsQueryLifecycleObserverFactory dnsQueryLifecycleObserverFactory, - long queryTimeoutMillis, - ResolvedAddressTypes resolvedAddressTypes, - boolean recursionDesired, - int maxQueriesPerResolve, - int maxPayloadSize, - boolean optResourceEnabled, - HostsFileEntriesResolver hostsFileEntriesResolver, - DnsServerAddressStreamProvider dnsServerAddressStreamProvider, - String[] searchDomains, - int ndots, - boolean decodeIdn) { - this(eventLoop, channelFactory, resolveCache, - new AuthoritativeDnsServerCacheAdapter(authoritativeDnsServerCache), dnsQueryLifecycleObserverFactory, - queryTimeoutMillis, resolvedAddressTypes, recursionDesired, maxQueriesPerResolve, - maxPayloadSize, optResourceEnabled, hostsFileEntriesResolver, dnsServerAddressStreamProvider, - searchDomains, ndots, decodeIdn); - } - - /** - * Creates a new DNS-based name resolver that communicates with the specified list of DNS servers. - * - * @param eventLoop the {@link EventLoop} which will perform the communication with the DNS servers - * @param channelFactory the {@link ChannelFactory} that will create a {@link DatagramChannel} - * @param resolveCache the DNS resolved entries cache - * @param authoritativeDnsServerCache the cache used to find the authoritative DNS server for a domain - * @param dnsQueryLifecycleObserverFactory used to generate new instances of {@link DnsQueryLifecycleObserver} which - * can be used to track metrics for DNS servers. - * @param queryTimeoutMillis timeout of each DNS query in millis - * @param resolvedAddressTypes the preferred address types - * @param recursionDesired if recursion desired flag must be set - * @param maxQueriesPerResolve the maximum allowed number of DNS queries for a given name resolution - * @param maxPayloadSize the capacity of the datagram packet buffer - * @param optResourceEnabled if automatic inclusion of a optional records is enabled - * @param hostsFileEntriesResolver the {@link HostsFileEntriesResolver} used to check for local aliases - * @param dnsServerAddressStreamProvider The {@link DnsServerAddressStreamProvider} used to determine the name - * servers for each hostname lookup. - * @param searchDomains the list of search domain - * (can be null, if so, will try to default to the underlying platform ones) - * @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, - final DnsCache resolveCache, - final AuthoritativeDnsServerCache authoritativeDnsServerCache, - DnsQueryLifecycleObserverFactory dnsQueryLifecycleObserverFactory, - long queryTimeoutMillis, - ResolvedAddressTypes resolvedAddressTypes, - boolean recursionDesired, - int maxQueriesPerResolve, - int maxPayloadSize, - boolean optResourceEnabled, - HostsFileEntriesResolver hostsFileEntriesResolver, - DnsServerAddressStreamProvider dnsServerAddressStreamProvider, - String[] searchDomains, - int ndots, - boolean decodeIdn) { - this(eventLoop, channelFactory, null, resolveCache, NoopDnsCnameCache.INSTANCE, authoritativeDnsServerCache, - dnsQueryLifecycleObserverFactory, queryTimeoutMillis, resolvedAddressTypes, recursionDesired, - maxQueriesPerResolve, maxPayloadSize, optResourceEnabled, hostsFileEntriesResolver, - dnsServerAddressStreamProvider, searchDomains, ndots, decodeIdn, false); - } - - DnsNameResolver( - EventLoop eventLoop, - ChannelFactory channelFactory, - ChannelFactory socketChannelFactory, - final DnsCache resolveCache, - final DnsCnameCache cnameCache, - final AuthoritativeDnsServerCache authoritativeDnsServerCache, - DnsQueryLifecycleObserverFactory dnsQueryLifecycleObserverFactory, - long queryTimeoutMillis, - ResolvedAddressTypes resolvedAddressTypes, - boolean recursionDesired, - int maxQueriesPerResolve, - int maxPayloadSize, - boolean optResourceEnabled, - HostsFileEntriesResolver hostsFileEntriesResolver, - DnsServerAddressStreamProvider dnsServerAddressStreamProvider, - String[] searchDomains, - int ndots, - boolean decodeIdn, - boolean completeOncePreferredResolved) { - this(eventLoop, channelFactory, socketChannelFactory, resolveCache, cnameCache, authoritativeDnsServerCache, - null, dnsQueryLifecycleObserverFactory, queryTimeoutMillis, resolvedAddressTypes, - recursionDesired, maxQueriesPerResolve, maxPayloadSize, optResourceEnabled, - hostsFileEntriesResolver, dnsServerAddressStreamProvider, searchDomains, ndots, decodeIdn, - completeOncePreferredResolved); - } - - DnsNameResolver( - EventLoop eventLoop, - ChannelFactory channelFactory, - ChannelFactory socketChannelFactory, - final DnsCache resolveCache, - final DnsCnameCache cnameCache, - final AuthoritativeDnsServerCache authoritativeDnsServerCache, - SocketAddress localAddress, - DnsQueryLifecycleObserverFactory dnsQueryLifecycleObserverFactory, - long queryTimeoutMillis, - ResolvedAddressTypes resolvedAddressTypes, - boolean recursionDesired, - int maxQueriesPerResolve, - int maxPayloadSize, - boolean optResourceEnabled, - HostsFileEntriesResolver hostsFileEntriesResolver, - DnsServerAddressStreamProvider dnsServerAddressStreamProvider, - String[] searchDomains, - int ndots, - boolean decodeIdn, - boolean completeOncePreferredResolved) { - super(eventLoop); - this.queryTimeoutMillis = queryTimeoutMillis > 0 - ? queryTimeoutMillis - : TimeUnit.SECONDS.toMillis(DEFAULT_OPTIONS.timeout()); - this.resolvedAddressTypes = resolvedAddressTypes != null ? resolvedAddressTypes : DEFAULT_RESOLVE_ADDRESS_TYPES; - this.recursionDesired = recursionDesired; - this.maxQueriesPerResolve = maxQueriesPerResolve > 0 ? maxQueriesPerResolve : DEFAULT_OPTIONS.attempts(); - this.maxPayloadSize = checkPositive(maxPayloadSize, "maxPayloadSize"); - this.optResourceEnabled = optResourceEnabled; - this.hostsFileEntriesResolver = requireNonNull(hostsFileEntriesResolver, "hostsFileEntriesResolver"); - this.dnsServerAddressStreamProvider = - requireNonNull(dnsServerAddressStreamProvider, "dnsServerAddressStreamProvider"); - this.resolveCache = requireNonNull(resolveCache, "resolveCache"); - this.cnameCache = requireNonNull(cnameCache, "cnameCache"); - this.dnsQueryLifecycleObserverFactory = - requireNonNull(dnsQueryLifecycleObserverFactory, "dnsQueryLifecycleObserverFactory"); - this.searchDomains = searchDomains != null ? searchDomains.clone() : DEFAULT_SEARCH_DOMAINS; - this.ndots = ndots >= 0 ? ndots : DEFAULT_OPTIONS.ndots(); - this.decodeIdn = decodeIdn; - this.completeOncePreferredResolved = completeOncePreferredResolved; - this.socketChannelFactory = socketChannelFactory; - switch (this.resolvedAddressTypes) { - case IPV4_ONLY: - supportsAAAARecords = false; - supportsARecords = true; - resolveRecordTypes = IPV4_ONLY_RESOLVED_RECORD_TYPES; - resolvedInternetProtocolFamilies = IPV4_ONLY_RESOLVED_PROTOCOL_FAMILIES; - break; - case IPV4_PREFERRED: - supportsAAAARecords = true; - supportsARecords = true; - resolveRecordTypes = IPV4_PREFERRED_RESOLVED_RECORD_TYPES; - resolvedInternetProtocolFamilies = IPV4_PREFERRED_RESOLVED_PROTOCOL_FAMILIES; - break; - case IPV6_ONLY: - supportsAAAARecords = true; - supportsARecords = false; - resolveRecordTypes = IPV6_ONLY_RESOLVED_RECORD_TYPES; - resolvedInternetProtocolFamilies = IPV6_ONLY_RESOLVED_PROTOCOL_FAMILIES; - break; - case IPV6_PREFERRED: - supportsAAAARecords = true; - supportsARecords = true; - resolveRecordTypes = IPV6_PREFERRED_RESOLVED_RECORD_TYPES; - resolvedInternetProtocolFamilies = IPV6_PREFERRED_RESOLVED_PROTOCOL_FAMILIES; - break; - default: - throw new IllegalArgumentException("Unknown ResolvedAddressTypes " + resolvedAddressTypes); - } - preferredAddressType = preferredAddressType(this.resolvedAddressTypes); - this.authoritativeDnsServerCache = requireNonNull(authoritativeDnsServerCache, "authoritativeDnsServerCache"); - nameServerComparator = new NameServerComparator(preferredAddressType.addressType()); - - Bootstrap b = new Bootstrap(); - b.group(executor()); - b.channelFactory(channelFactory); - b.option(ChannelOption.DATAGRAM_CHANNEL_ACTIVE_ON_REGISTRATION, true); - final DnsResponseHandler responseHandler = new DnsResponseHandler(executor().newPromise()); - b.handler(new ChannelInitializer() { - @Override - protected void initChannel(DatagramChannel ch) { - ch.pipeline().addLast(DATAGRAM_ENCODER, DATAGRAM_DECODER, responseHandler); - ch.closeFuture().addListener(closeFuture -> { - resolveCache.clear(); - cnameCache.clear(); - authoritativeDnsServerCache.clear(); - }); - } - }); - b.option(ChannelOption.RCVBUF_ALLOCATOR, new FixedRecvByteBufAllocator(maxPayloadSize)); - - channelFuture = responseHandler.channelActivePromise.asFuture(); - try { - ch = b.createUnregistered(); - Future future = localAddress == null ? ch.register() : ch.bind(localAddress); - if (future.isFailed()) { - throw future.cause(); - } - } catch (Error | RuntimeException e) { - throw e; - } catch (Throwable cause) { - throw new IllegalStateException("Unable to create / register Channel", cause); - } - } - - static InternetProtocolFamily preferredAddressType(ResolvedAddressTypes resolvedAddressTypes) { - switch (resolvedAddressTypes) { - case IPV4_ONLY: - case IPV4_PREFERRED: - return InternetProtocolFamily.IPv4; - case IPV6_ONLY: - case IPV6_PREFERRED: - return InternetProtocolFamily.IPv6; - default: - throw new IllegalArgumentException("Unknown ResolvedAddressTypes " + resolvedAddressTypes); - } - } - - // Only here to override in unit tests. - InetSocketAddress newRedirectServerAddress(InetAddress server) { - return new InetSocketAddress(server, DNS_PORT); - } - - final DnsQueryLifecycleObserverFactory dnsQueryLifecycleObserverFactory() { - return dnsQueryLifecycleObserverFactory; - } - - /** - * Creates a new {@link DnsServerAddressStream} to following a redirected DNS query. By overriding this - * it provides the opportunity to sort the name servers before following a redirected DNS query. - * - * @param hostname the hostname. - * @param nameservers The addresses of the DNS servers which are used in the event of a redirect. This may - * contain resolved and unresolved addresses so the used {@link DnsServerAddressStream} must - * allow unresolved addresses if you want to include these as well. - * @return A {@link DnsServerAddressStream} which will be used to follow the DNS redirect or {@code null} if - * none should be followed. - */ - protected DnsServerAddressStream newRedirectDnsServerStream( - @SuppressWarnings("unused") String hostname, List nameservers) { - 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. - nameservers.sort(nameServerComparator); - return new SequentialDnsServerAddressStream(nameservers, 0); - } - return cached; - } - - /** - * Returns the resolution cache. - */ - public DnsCache resolveCache() { - return resolveCache; - } - - /** - * Returns the {@link DnsCnameCache}. - */ - public DnsCnameCache cnameCache() { - return cnameCache; - } - - /** - * Returns the cache used for authoritative DNS servers for a domain. - */ - public AuthoritativeDnsServerCache authoritativeDnsServerCache() { - return authoritativeDnsServerCache; - } - - /** - * Returns the timeout of each DNS query performed by this resolver (in milliseconds). - * The default value is 5 seconds. - */ - public long queryTimeoutMillis() { - return queryTimeoutMillis; - } - - /** - * Returns the {@link ResolvedAddressTypes} resolved by {@link #resolve(String)}. - * The default value depends on the value of the system property {@code "java.net.preferIPv6Addresses"}. - */ - public ResolvedAddressTypes resolvedAddressTypes() { - return resolvedAddressTypes; - } - - InternetProtocolFamily[] resolvedInternetProtocolFamiliesUnsafe() { - return resolvedInternetProtocolFamilies; - } - - final String[] searchDomains() { - return searchDomains; - } - - final int ndots() { - return ndots; - } - - final boolean supportsAAAARecords() { - return supportsAAAARecords; - } - - final boolean supportsARecords() { - return supportsARecords; - } - - final InternetProtocolFamily preferredAddressType() { - return preferredAddressType; - } - - final DnsRecordType[] resolveRecordTypes() { - return resolveRecordTypes; - } - - final boolean isDecodeIdn() { - return decodeIdn; - } - - /** - * Returns {@code true} if and only if this resolver sends a DNS query with the RD (recursion desired) flag set. - * The default value is {@code true}. - */ - public boolean isRecursionDesired() { - return recursionDesired; - } - - /** - * Returns the maximum allowed number of DNS queries to send when resolving a host name. - * The default value is {@code 8}. - */ - public int maxQueriesPerResolve() { - return maxQueriesPerResolve; - } - - /** - * Returns the capacity of the datagram packet buffer (in bytes). The default value is {@code 4096} bytes. - */ - public int maxPayloadSize() { - return maxPayloadSize; - } - - /** - * Returns the automatic inclusion of a optional records that tries to give the remote DNS server a hint about how - * much data the resolver can read per response is enabled. - */ - public boolean isOptResourceEnabled() { - return optResourceEnabled; - } - - /** - * Returns the component that tries to resolve hostnames against the hosts file prior to asking to - * remotes DNS servers. - */ - public HostsFileEntriesResolver hostsFileEntriesResolver() { - return hostsFileEntriesResolver; - } - - /** - * Closes the internal datagram channel used for sending and receiving DNS messages, and clears all DNS resource - * records from the cache. Attempting to send a DNS query or to resolve a domain name will fail once this method - * has been called. - */ - @Override - public void close() { - if (ch.isOpen()) { - ch.close(); - } - } - - @Override - protected EventLoop executor() { - return (EventLoop) super.executor(); - } - - private InetAddress resolveHostsFileEntry(String hostname) { - if (hostsFileEntriesResolver == null) { - return null; - } - InetAddress address = hostsFileEntriesResolver.address(hostname, resolvedAddressTypes); - return address == null && isLocalWindowsHost(hostname) ? LOCALHOST_ADDRESS : address; - } - - private List resolveHostsFileEntries(String hostname) { - if (hostsFileEntriesResolver == null) { - return null; - } - List addresses; - if (hostsFileEntriesResolver instanceof DefaultHostsFileEntriesResolver) { - addresses = ((DefaultHostsFileEntriesResolver) hostsFileEntriesResolver) - .addresses(hostname, resolvedAddressTypes); - } else { - InetAddress address = hostsFileEntriesResolver.address(hostname, resolvedAddressTypes); - addresses = address != null ? Collections.singletonList(address) : null; - } - return addresses == null && isLocalWindowsHost(hostname) ? - Collections.singletonList(LOCALHOST_ADDRESS) : addresses; - } - - /** - * Checks whether the given hostname is the localhost/host (computer) name on Windows OS. - * Windows OS removed the localhost/host (computer) name information from the hosts file in the later versions - * and such hostname cannot be resolved from hosts file. - * See https://github.com/netty/netty/issues/5386 - * See https://github.com/netty/netty/issues/11142 - */ - private static boolean isLocalWindowsHost(String hostname) { - return PlatformDependent.isWindows() && - (LOCALHOST.equalsIgnoreCase(hostname) || - WINDOWS_HOST_NAME != null && WINDOWS_HOST_NAME.equalsIgnoreCase(hostname)); - } - - /** - * Resolves the specified name into an address. - * - * @param inetHost the name to resolve - * @param additionals additional records ({@code OPT}) - * - * @return the address as the result of the resolution - */ - public final Future resolve(String inetHost, Iterable additionals) { - return resolve(inetHost, additionals, executor().newPromise()); - } - - /** - * Resolves the specified name into an address. - * - * @param inetHost the name to resolve - * @param additionals additional records ({@code OPT}) - * @param promise the {@link Promise} which will be fulfilled when the name resolution is finished - * - * @return the address as the result of the resolution - */ - public final Future resolve(String inetHost, Iterable additionals, - Promise promise) { - requireNonNull(promise, "promise"); - DnsRecord[] additionalsArray = toArray(additionals, true); - try { - doResolve(inetHost, additionalsArray, promise, resolveCache); - } catch (Exception e) { - promise.setFailure(e); - } - return promise.asFuture(); - } - - /** - * Resolves the specified host name and port into a list of address. - * - * @param inetHost the name to resolve - * @param additionals additional records ({@code OPT}) - * - * @return the list of the address as the result of the resolution - */ - public final Future> resolveAll(String inetHost, Iterable additionals) { - return resolveAll(inetHost, additionals, executor().newPromise()); - } - - /** - * Resolves the specified host name and port into a list of address. - * - * @param inetHost the name to resolve - * @param additionals additional records ({@code OPT}) - * @param promise the {@link Promise} which will be fulfilled when the name resolution is finished - * - * @return the list of the address as the result of the resolution - */ - public final Future> resolveAll(String inetHost, Iterable additionals, - Promise> promise) { - requireNonNull(promise, "promise"); - DnsRecord[] additionalsArray = toArray(additionals, true); - try { - doResolveAll(inetHost, additionalsArray, promise, resolveCache); - } catch (Exception e) { - promise.setFailure(e); - } - return promise.asFuture(); - } - - @Override - protected void doResolve(String inetHost, Promise promise) throws Exception { - doResolve(inetHost, EMPTY_ADDITIONALS, promise, resolveCache); - } - - /** - * Resolves the {@link DnsRecord}s that are matched by the specified {@link DnsQuestion}. Unlike - * {@link #query(DnsQuestion)}, this method handles redirection, CNAMEs and multiple name servers. - * If the specified {@link DnsQuestion} is {@code A} or {@code AAAA}, this method looks up the configured - * {@link HostsFileEntries} before sending a query to the name servers. If a match is found in the - * {@link HostsFileEntries}, a synthetic {@code A} or {@code AAAA} record will be returned. - * - * @param question the question - * - * @return the list of the {@link DnsRecord}s as the result of the resolution - */ - public final Future> resolveAll(DnsQuestion question) { - return resolveAll(question, EMPTY_ADDITIONALS, executor().newPromise()); - } - - /** - * Resolves the {@link DnsRecord}s that are matched by the specified {@link DnsQuestion}. Unlike - * {@link #query(DnsQuestion)}, this method handles redirection, CNAMEs and multiple name servers. - * If the specified {@link DnsQuestion} is {@code A} or {@code AAAA}, this method looks up the configured - * {@link HostsFileEntries} before sending a query to the name servers. If a match is found in the - * {@link HostsFileEntries}, a synthetic {@code A} or {@code AAAA} record will be returned. - * - * @param question the question - * @param additionals additional records ({@code OPT}) - * - * @return the list of the {@link DnsRecord}s as the result of the resolution - */ - public final Future> resolveAll(DnsQuestion question, Iterable additionals) { - return resolveAll(question, additionals, executor().newPromise()); - } - - /** - * Resolves the {@link DnsRecord}s that are matched by the specified {@link DnsQuestion}. Unlike - * {@link #query(DnsQuestion)}, this method handles redirection, CNAMEs and multiple name servers. - * If the specified {@link DnsQuestion} is {@code A} or {@code AAAA}, this method looks up the configured - * {@link HostsFileEntries} before sending a query to the name servers. If a match is found in the - * {@link HostsFileEntries}, a synthetic {@code A} or {@code AAAA} record will be returned. - * - * @param question the question - * @param additionals additional records ({@code OPT}) - * @param promise the {@link Promise} which will be fulfilled when the resolution is finished - * - * @return the list of the {@link DnsRecord}s as the result of the resolution - */ - public final Future> resolveAll(DnsQuestion question, Iterable additionals, - Promise> promise) { - final DnsRecord[] additionalsArray = toArray(additionals, true); - return resolveAll(question, additionalsArray, promise); - } - - private Future> resolveAll(DnsQuestion question, DnsRecord[] additionals, - Promise> promise) { - requireNonNull(question, "question"); - requireNonNull(promise, "promise"); - - // Respect /etc/hosts as well if the record type is A or AAAA. - final DnsRecordType type = question.type(); - final String hostname = question.name(); - - if (type == DnsRecordType.A || type == DnsRecordType.AAAA) { - final List hostsFileEntries = resolveHostsFileEntries(hostname); - if (hostsFileEntries != null) { - List result = new ArrayList(); - for (InetAddress hostsFileEntry : hostsFileEntries) { - ByteBuf content = null; - if (hostsFileEntry instanceof Inet4Address) { - if (type == DnsRecordType.A) { - content = Unpooled.wrappedBuffer(hostsFileEntry.getAddress()); - } - } else if (hostsFileEntry instanceof Inet6Address) { - if (type == DnsRecordType.AAAA) { - content = Unpooled.wrappedBuffer(hostsFileEntry.getAddress()); - } - } - if (content != null) { - // Our current implementation does not support reloading the hosts file, - // so use a fairly large TTL (1 day, i.e. 86400 seconds). - result.add(new DefaultDnsRawRecord(hostname, type, 86400, content)); - } - } - - if (!result.isEmpty()) { - trySuccess(promise, result); - return promise.asFuture(); - } - } - } - - // It was not A/AAAA question or there was no entry in /etc/hosts. - final DnsServerAddressStream nameServerAddrs = - dnsServerAddressStreamProvider.nameServerAddressStream(hostname); - new DnsRecordResolveContext(this, promise, question, additionals, nameServerAddrs, maxQueriesPerResolve) - .resolve(promise); - return promise.asFuture(); - } - - private static DnsRecord[] toArray(Iterable additionals, boolean validateType) { - requireNonNull(additionals, "additionals"); - if (additionals instanceof Collection) { - Collection records = (Collection) additionals; - for (DnsRecord r: additionals) { - validateAdditional(r, validateType); - } - return records.toArray(new DnsRecord[0]); - } - - Iterator additionalsIt = additionals.iterator(); - if (!additionalsIt.hasNext()) { - return EMPTY_ADDITIONALS; - } - List records = new ArrayList<>(); - do { - DnsRecord r = additionalsIt.next(); - validateAdditional(r, validateType); - records.add(r); - } while (additionalsIt.hasNext()); - - return records.toArray(new DnsRecord[0]); - } - - private static void validateAdditional(DnsRecord record, boolean validateType) { - requireNonNull(record, "record"); - if (validateType && record instanceof DnsRawRecord) { - throw new IllegalArgumentException("DnsRawRecord implementations not allowed: " + record); - } - } - - private InetAddress loopbackAddress() { - return preferredAddressType().localhost(); - } - - /** - * Hook designed for extensibility so one can pass a different cache on each resolution attempt - * instead of using the global one. - */ - protected void doResolve(String inetHost, - DnsRecord[] additionals, - Promise promise, - DnsCache resolveCache) throws Exception { - if (inetHost == null || inetHost.isEmpty()) { - // If an empty hostname is used we should use "localhost", just like InetAddress.getByName(...) does. - promise.setSuccess(loopbackAddress()); - return; - } - final byte[] bytes = NetUtil.createByteArrayFromIpAddressString(inetHost); - if (bytes != null) { - // The inetHost is actually an ipaddress. - promise.setSuccess(InetAddress.getByAddress(bytes)); - return; - } - - final String hostname = hostname(inetHost); - - InetAddress hostsFileEntry = resolveHostsFileEntry(hostname); - if (hostsFileEntry != null) { - promise.setSuccess(hostsFileEntry); - return; - } - - if (!doResolveCached(hostname, additionals, promise, resolveCache)) { - doResolveUncached(hostname, additionals, promise, resolveCache, true); - } - } - - private boolean doResolveCached(String hostname, - DnsRecord[] additionals, - Promise promise, - DnsCache resolveCache) { - final List cachedEntries = resolveCache.get(hostname, additionals); - if (cachedEntries == null || cachedEntries.isEmpty()) { - return false; - } - - Throwable cause = cachedEntries.get(0).cause(); - if (cause == null) { - final int numEntries = cachedEntries.size(); - // Find the first entry with the preferred address type. - for (InternetProtocolFamily f : resolvedInternetProtocolFamilies) { - for (int i = 0; i < numEntries; i++) { - final DnsCacheEntry e = cachedEntries.get(i); - if (f.addressType().isInstance(e.address())) { - trySuccess(promise, e.address()); - return true; - } - } - } - return false; - } else { - tryFailure(promise, cause); - return true; - } - } - - static boolean trySuccess(Promise promise, T result) { - final boolean notifiedRecords = promise.trySuccess(result); - if (!notifiedRecords) { - // There is nothing really wrong with not be able to notify the promise as we may have raced here because - // of multiple queries that have been executed. Log it with trace level anyway just in case the user - // wants to better understand what happened. - logger.trace("Failed to notify success ({}) to a promise: {}", result, promise); - } - return notifiedRecords; - } - - private static void tryFailure(Promise promise, Throwable cause) { - if (!promise.tryFailure(cause)) { - // There is nothing really wrong with not be able to notify the promise as we may have raced here because - // of multiple queries that have been executed. Log it with trace level anyway just in case the user - // wants to better understand what happened. - logger.trace("Failed to notify failure to a promise: {}", promise, cause); - } - } - - private void doResolveUncached(String hostname, - DnsRecord[] additionals, - final Promise promise, - DnsCache resolveCache, boolean completeEarlyIfPossible) { - final Promise> allPromise = executor().newPromise(); - doResolveAllUncached(hostname, additionals, promise, allPromise, resolveCache, true); - allPromise.asFuture().addListener(future -> { - if (future.isSuccess()) { - trySuccess(promise, future.getNow().get(0)); - } else { - tryFailure(promise, future.cause()); - } - }); - } - - @Override - protected void doResolveAll(String inetHost, Promise> promise) throws Exception { - doResolveAll(inetHost, EMPTY_ADDITIONALS, promise, resolveCache); - } - - /** - * Hook designed for extensibility so one can pass a different cache on each resolution attempt - * instead of using the global one. - */ - protected void doResolveAll(String inetHost, - DnsRecord[] additionals, - Promise> promise, - DnsCache resolveCache) throws Exception { - if (inetHost == null || inetHost.isEmpty()) { - // If an empty hostname is used we should use "localhost", just like InetAddress.getAllByName(...) does. - promise.setSuccess(Collections.singletonList(loopbackAddress())); - return; - } - final byte[] bytes = NetUtil.createByteArrayFromIpAddressString(inetHost); - if (bytes != null) { - // The unresolvedAddress was created via a String that contains an ipaddress. - promise.setSuccess(Collections.singletonList(InetAddress.getByAddress(bytes))); - return; - } - - final String hostname = hostname(inetHost); - - List hostsFileEntries = resolveHostsFileEntries(hostname); - if (hostsFileEntries != null) { - promise.setSuccess(hostsFileEntries); - return; - } - - if (!doResolveAllCached(hostname, additionals, promise, resolveCache, resolvedInternetProtocolFamilies)) { - doResolveAllUncached(hostname, additionals, promise, promise, - resolveCache, completeOncePreferredResolved); - } - } - - static boolean doResolveAllCached(String hostname, - DnsRecord[] additionals, - Promise> promise, - DnsCache resolveCache, - InternetProtocolFamily[] resolvedInternetProtocolFamilies) { - final List cachedEntries = resolveCache.get(hostname, additionals); - if (cachedEntries == null || cachedEntries.isEmpty()) { - return false; - } - - Throwable cause = cachedEntries.get(0).cause(); - if (cause == null) { - List result = null; - final int numEntries = cachedEntries.size(); - for (InternetProtocolFamily f : resolvedInternetProtocolFamilies) { - for (int i = 0; i < numEntries; i++) { - final DnsCacheEntry e = cachedEntries.get(i); - if (f.addressType().isInstance(e.address())) { - if (result == null) { - result = new ArrayList<>(numEntries); - } - result.add(e.address()); - } - } - } - if (result != null) { - trySuccess(promise, result); - return true; - } - return false; - } else { - tryFailure(promise, cause); - return true; - } - } - - private void doResolveAllUncached(final String hostname, - final DnsRecord[] additionals, - final Promise originalPromise, - final Promise> promise, - final DnsCache resolveCache, - final boolean completeEarlyIfPossible) { - // 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, originalPromise, - promise, resolveCache, completeEarlyIfPossible); - } else { - executor.execute(() -> - doResolveAllUncached0(hostname, additionals, originalPromise, - promise, resolveCache, completeEarlyIfPossible)); - } - } - - private void doResolveAllUncached0(String hostname, - DnsRecord[] additionals, - Promise originalPromise, - Promise> promise, - DnsCache resolveCache, - boolean completeEarlyIfPossible) { - - assert executor().inEventLoop(); - - final DnsServerAddressStream nameServerAddrs = - dnsServerAddressStreamProvider.nameServerAddressStream(hostname); - new DnsAddressResolveContext(this, originalPromise, hostname, additionals, nameServerAddrs, - maxQueriesPerResolve, resolveCache, - authoritativeDnsServerCache, completeEarlyIfPossible) - .resolve(promise); - } - - private static String hostname(String inetHost) { - String hostname = IDN.toASCII(inetHost); - // Check for https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6894622 - if (StringUtil.endsWith(inetHost, '.') && !StringUtil.endsWith(hostname, '.')) { - hostname += "."; - } - return hostname; - } - - /** - * Sends a DNS query with the specified question. - */ - public Future> query(DnsQuestion question) { - return query(nextNameServerAddress(), question); - } - - /** - * Sends a DNS query with the specified question with additional records. - */ - public Future> query( - DnsQuestion question, Iterable additionals) { - return query(nextNameServerAddress(), question, additionals); - } - - /** - * Sends a DNS query with the specified question. - */ - public Future> query( - DnsQuestion question, Promise> promise) { - return query(nextNameServerAddress(), question, Collections.emptyList(), promise); - } - - private InetSocketAddress nextNameServerAddress() { - return nameServerAddrStream.get().next(); - } - - /** - * Sends a DNS query with the specified question using the specified name server list. - */ - public Future> query( - InetSocketAddress nameServerAddr, DnsQuestion question) { - - return query0(nameServerAddr, question, EMPTY_ADDITIONALS, true, ch.newPromise(), - ch.executor().newPromise()); - } - - /** - * Sends a DNS query with the specified question with additional records using the specified name server list. - */ - public Future> query( - InetSocketAddress nameServerAddr, DnsQuestion question, Iterable additionals) { - - return query0(nameServerAddr, question, toArray(additionals, false), true, ch.newPromise(), - ch.executor().newPromise()); - } - - /** - * Sends a DNS query with the specified question using the specified name server list. - */ - public Future> query( - InetSocketAddress nameServerAddr, DnsQuestion question, - Promise> promise) { - - return query0(nameServerAddr, question, EMPTY_ADDITIONALS, true, ch.newPromise(), promise); - } - - /** - * Sends a DNS query with the specified question with additional records using the specified name server list. - */ - public Future> query( - InetSocketAddress nameServerAddr, DnsQuestion question, - Iterable additionals, - Promise> promise) { - - return query0(nameServerAddr, question, toArray(additionals, false), true, ch.newPromise(), promise); - } - - /** - * Returns {@code true} if the {@link Throwable} was caused by an timeout or transport error. - * These methods can be used on the {@link Future#cause()} that is returned by the various methods exposed by this - * {@link DnsNameResolver}. - */ - public static boolean isTransportOrTimeoutError(Throwable cause) { - return cause != null && cause.getCause() instanceof DnsNameResolverException; - } - - /** - * Returns {@code true} if the {@link Throwable} was caused by an timeout. - * These methods can be used on the {@link Future#cause()} that is returned by the various methods exposed by this - * {@link DnsNameResolver}. - */ - public static boolean isTimeoutError(Throwable cause) { - return cause != null && cause.getCause() instanceof DnsNameResolverTimeoutException; - } - - final void flushQueries() { - ch.flush(); - } - - final Future> query0( - InetSocketAddress nameServerAddr, DnsQuestion question, - DnsRecord[] additionals, - boolean flush, - Promise writePromise, - Promise> promise) { - - final Promise> castPromise = cast( - requireNonNull(promise, "promise")); - try { - new DatagramDnsQueryContext(this, nameServerAddr, question, additionals, castPromise) - .query(flush, writePromise); - } catch (Exception e) { - castPromise.setFailure(e); - } - return castPromise.asFuture(); - } - - @SuppressWarnings("unchecked") - private static Promise> cast(Promise promise) { - return (Promise>) promise; - } - - final DnsServerAddressStream newNameServerAddressStream(String hostname) { - return dnsServerAddressStreamProvider.nameServerAddressStream(hostname); - } - - private final class DnsResponseHandler implements ChannelHandler { - - private final Promise channelActivePromise; - - DnsResponseHandler(Promise channelActivePromise) { - this.channelActivePromise = channelActivePromise; - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - final DatagramDnsResponse res = (DatagramDnsResponse) msg; - final int queryId = res.id(); - - if (logger.isDebugEnabled()) { - logger.debug("{} RECEIVED: UDP [{}: {}], {}", ch, queryId, res.sender(), res); - } - - final DnsQueryContext qCtx = queryContextManager.get(res.sender(), queryId); - if (qCtx == null) { - logger.debug("Received a DNS response with an unknown ID: UDP [{}: {}]", ch, queryId); - res.release(); - return; - } - - // Check if the response was truncated and if we can fallback to TCP to retry. - if (!res.isTruncated() || socketChannelFactory == null) { - qCtx.finish(res); - return; - } - - Bootstrap bs = new Bootstrap(); - bs.option(ChannelOption.SO_REUSEADDR, true) - .group(executor()) - .channelFactory(socketChannelFactory) - .handler(TCP_ENCODER); - bs.connect(res.sender()).addListener(future -> { - if (future.isFailed()) { - if (logger.isDebugEnabled()) { - logger.debug("{} Unable to fallback to TCP [{}]", queryId, future.cause()); - } - - // TCP fallback failed, just use the truncated response. - qCtx.finish(res); - return; - } - final Channel channel = future.getNow(); - - Promise> promise = - channel.executor().newPromise(); - final TcpDnsQueryContext tcpCtx = new TcpDnsQueryContext(DnsNameResolver.this, channel, - (InetSocketAddress) channel.remoteAddress(), qCtx.question(), - EMPTY_ADDITIONALS, promise); - - channel.pipeline().addLast(new TcpDnsResponseDecoder()); - channel.pipeline().addLast(new ChannelHandler() { - @Override - public void channelRead(ChannelHandlerContext ctx1, Object msg1) { - Channel channel = ctx1.channel(); - DnsResponse response = (DnsResponse) msg1; - int queryId1 = response.id(); - - if (logger.isDebugEnabled()) { - logger.debug("{} RECEIVED: TCP [{}: {}], {}", channel, queryId1, - channel.remoteAddress(), response); - } - - DnsQueryContext foundCtx = queryContextManager.get(res.sender(), queryId1); - if (foundCtx == tcpCtx) { - tcpCtx.finish(new AddressedEnvelopeAdapter( - (InetSocketAddress) ctx1.channel().remoteAddress(), - (InetSocketAddress) ctx1.channel().localAddress(), - response)); - } else { - response.release(); - tcpCtx.tryFailure("Received TCP DNS response with unexpected ID", null, false); - logger.debug("Received a DNS response with an unexpected ID: TCP [{}: {}]", - channel, queryId); - } - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx1, Throwable cause) { - if (tcpCtx.tryFailure("TCP fallback error", cause, false) && logger.isDebugEnabled()) { - logger.debug("{} Error during processing response: TCP [{}: {}]", - ctx1.channel(), queryId, - ctx1.channel().remoteAddress(), cause); - } - } - }); - - promise.asFuture().addListener(addressEnvelopeFuture -> { - channel.close(); - - if (addressEnvelopeFuture.isSuccess()) { - qCtx.finish(addressEnvelopeFuture.getNow()); - res.release(); - } else { - // TCP fallback failed, just use the truncated response. - qCtx.finish(res); - } - }); - tcpCtx.query(true, channel.newPromise()); - }); - } - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - ctx.fireChannelActive(); - channelActivePromise.setSuccess(ctx.channel()); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - if (cause instanceof CorruptedFrameException) { - logger.debug("Unable to decode DNS response: UDP [{}]", ctx.channel(), cause); - } else { - logger.warn("Unexpected exception: UDP [{}]", ctx.channel(), cause); - } - } - } - - private static final class AddressedEnvelopeAdapter implements AddressedEnvelope { - private final InetSocketAddress sender; - private final InetSocketAddress recipient; - private final DnsResponse response; - - AddressedEnvelopeAdapter(InetSocketAddress sender, InetSocketAddress recipient, DnsResponse response) { - this.sender = sender; - this.recipient = recipient; - this.response = response; - } - - @Override - public DnsResponse content() { - return response; - } - - @Override - public InetSocketAddress sender() { - return sender; - } - - @Override - public InetSocketAddress recipient() { - return recipient; - } - - @Override - public AddressedEnvelope retain() { - response.retain(); - return this; - } - - @Override - public AddressedEnvelope retain(int increment) { - response.retain(increment); - return this; - } - - @Override - public AddressedEnvelope touch() { - response.touch(); - return this; - } - - @Override - public AddressedEnvelope touch(Object hint) { - response.touch(hint); - return this; - } - - @Override - public int refCnt() { - return response.refCnt(); - } - - @Override - public boolean release() { - return response.release(); - } - - @Override - public boolean release(int decrement) { - return response.release(decrement); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - - if (!(obj instanceof AddressedEnvelope)) { - return false; - } - - @SuppressWarnings("unchecked") - final AddressedEnvelope that = (AddressedEnvelope) obj; - if (sender() == null) { - if (that.sender() != null) { - return false; - } - } else if (!sender().equals(that.sender())) { - return false; - } - - if (recipient() == null) { - if (that.recipient() != null) { - return false; - } - } else if (!recipient().equals(that.recipient())) { - return false; - } - - return response.equals(obj); - } - - @Override - public int hashCode() { - int hashCode = response.hashCode(); - if (sender() != null) { - hashCode = hashCode * 31 + sender().hashCode(); - } - if (recipient() != null) { - hashCode = hashCode * 31 + recipient().hashCode(); - } - return hashCode; - } - } -} 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 deleted file mode 100644 index 4bea7a6e52..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolverBuilder.java +++ /dev/null @@ -1,560 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.channel.ChannelFactory; -import io.netty.channel.EventLoop; -import io.netty.channel.ReflectiveChannelFactory; -import io.netty.channel.socket.DatagramChannel; -import io.netty.channel.socket.InternetProtocolFamily; -import io.netty.channel.socket.SocketChannel; -import io.netty.resolver.HostsFileEntriesResolver; -import io.netty.resolver.ResolvedAddressTypes; -import io.netty.util.concurrent.Future; - -import java.net.SocketAddress; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import static io.netty.util.internal.ObjectUtil.intValue; -import static java.util.Objects.requireNonNull; - -/** - * A {@link DnsNameResolver} builder. - */ -public final class DnsNameResolverBuilder { - volatile EventLoop eventLoop; - private ChannelFactory channelFactory; - private ChannelFactory socketChannelFactory; - private DnsCache resolveCache; - private DnsCnameCache cnameCache; - private AuthoritativeDnsServerCache authoritativeDnsServerCache; - private SocketAddress localAddress; - private Integer minTtl; - private Integer maxTtl; - private Integer negativeTtl; - private long queryTimeoutMillis = -1; - private ResolvedAddressTypes resolvedAddressTypes = DnsNameResolver.DEFAULT_RESOLVE_ADDRESS_TYPES; - private boolean completeOncePreferredResolved; - private boolean recursionDesired = true; - private int maxQueriesPerResolve = -1; - private int maxPayloadSize = 4096; - private boolean optResourceEnabled = true; - private HostsFileEntriesResolver hostsFileEntriesResolver = HostsFileEntriesResolver.DEFAULT; - private DnsServerAddressStreamProvider dnsServerAddressStreamProvider = - DnsServerAddressStreamProviders.platformDefault(); - private DnsQueryLifecycleObserverFactory dnsQueryLifecycleObserverFactory = - NoopDnsQueryLifecycleObserverFactory.INSTANCE; - private String[] searchDomains; - private int ndots = -1; - private boolean decodeIdn = true; - - /** - * Creates a new builder. - */ - public DnsNameResolverBuilder() { - } - - /** - * Creates a new builder. - * - * @param eventLoop the {@link EventLoop} which will perform the communication with the DNS - * servers. - */ - public DnsNameResolverBuilder(EventLoop eventLoop) { - eventLoop(eventLoop); - } - - /** - * Sets the {@link EventLoop} which will perform the communication with the DNS servers. - * - * @param eventLoop the {@link EventLoop} - * @return {@code this} - */ - public DnsNameResolverBuilder eventLoop(EventLoop eventLoop) { - this.eventLoop = eventLoop; - return this; - } - - protected ChannelFactory channelFactory() { - return this.channelFactory; - } - - /** - * Sets the {@link ChannelFactory} that will create a {@link DatagramChannel}. - * - * @param channelFactory the {@link ChannelFactory} - * @return {@code this} - */ - public DnsNameResolverBuilder channelFactory(ChannelFactory channelFactory) { - this.channelFactory = channelFactory; - return this; - } - - /** - * Sets the {@link ChannelFactory} as a {@link ReflectiveChannelFactory} of this type. - * Use as an alternative to {@link #channelFactory(ChannelFactory)}. - * - * @param channelType the type - * @return {@code this} - */ - public DnsNameResolverBuilder channelType(Class channelType) { - return channelFactory(new ReflectiveChannelFactory(channelType)); - } - - /** - * Sets the {@link ChannelFactory} that will create a {@link SocketChannel} for - * TCP fallback if needed. - * - * @param channelFactory the {@link ChannelFactory} or {@code null} - * if TCP fallback should not be supported. - * @return {@code this} - */ - public DnsNameResolverBuilder socketChannelFactory(ChannelFactory channelFactory) { - this.socketChannelFactory = channelFactory; - return this; - } - - /** - * Sets the {@link ChannelFactory} as a {@link ReflectiveChannelFactory} of this type for - * TCP fallback if needed. - * Use as an alternative to {@link #socketChannelFactory(ChannelFactory)}. - * - * @param channelType the type or {@code null} if TCP fallback - * should not be supported. - * @return {@code this} - */ - public DnsNameResolverBuilder socketChannelType(Class channelType) { - if (channelType == null) { - return socketChannelFactory(null); - } - return socketChannelFactory(new ReflectiveChannelFactory(channelType)); - } - - /** - * Sets the cache for resolution results. - * - * @param resolveCache the DNS resolution results cache - * @return {@code this} - */ - public DnsNameResolverBuilder resolveCache(DnsCache resolveCache) { - this.resolveCache = 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. - * @return {@code this} - */ - public DnsNameResolverBuilder dnsQueryLifecycleObserverFactory(DnsQueryLifecycleObserverFactory - lifecycleObserverFactory) { - this.dnsQueryLifecycleObserverFactory = requireNonNull(lifecycleObserverFactory, "lifecycleObserverFactory"); - return this; - } - - /** - * Sets the cache for authoritative NS servers - * - * @param authoritativeDnsServerCache the authoritative NS servers cache - * @return {@code this} - * @deprecated Use {@link #authoritativeDnsServerCache(AuthoritativeDnsServerCache)} - */ - @Deprecated - public DnsNameResolverBuilder authoritativeDnsServerCache(DnsCache authoritativeDnsServerCache) { - this.authoritativeDnsServerCache = new AuthoritativeDnsServerCacheAdapter(authoritativeDnsServerCache); - return this; - } - - /** - * Sets the cache for authoritative NS servers - * - * @param authoritativeDnsServerCache the authoritative NS servers cache - * @return {@code this} - */ - public DnsNameResolverBuilder authoritativeDnsServerCache(AuthoritativeDnsServerCache authoritativeDnsServerCache) { - this.authoritativeDnsServerCache = authoritativeDnsServerCache; - return this; - } - - /** - * Configure the address that will be used to bind too. If `null` the default will be used. - * @param localAddress the bind address - * @return {@code this} - */ - public DnsNameResolverBuilder localAddress(SocketAddress localAddress) { - this.localAddress = localAddress; - return this; - } - - /** - * Sets the minimum and maximum TTL of the cached DNS resource records (in seconds). If the TTL of the DNS - * resource record returned by the DNS server is less than the minimum TTL or greater than the maximum TTL, - * this resolver will ignore the TTL from the DNS server and use the minimum TTL or the maximum TTL instead - * respectively. - * The default value is {@code 0} and {@link Integer#MAX_VALUE}, which practically tells this resolver to - * respect the TTL from the DNS server. - * - * @param minTtl the minimum TTL - * @param maxTtl the maximum TTL - * @return {@code this} - */ - public DnsNameResolverBuilder ttl(int minTtl, int maxTtl) { - this.maxTtl = maxTtl; - this.minTtl = minTtl; - return this; - } - - /** - * Sets the TTL of the cache for the failed DNS queries (in seconds). - * - * @param negativeTtl the TTL for failed cached queries - * @return {@code this} - */ - public DnsNameResolverBuilder negativeTtl(int negativeTtl) { - this.negativeTtl = negativeTtl; - return this; - } - - /** - * Sets the timeout of each DNS query performed by this resolver (in milliseconds). - * - * @param queryTimeoutMillis the query timeout - * @return {@code this} - */ - public DnsNameResolverBuilder queryTimeoutMillis(long queryTimeoutMillis) { - this.queryTimeoutMillis = queryTimeoutMillis; - return this; - } - - /** - * Compute a {@link ResolvedAddressTypes} from some {@link InternetProtocolFamily}s. - * An empty input will return the default value, based on "java.net" System properties. - * Valid inputs are (), (IPv4), (IPv6), (Ipv4, IPv6) and (IPv6, IPv4). - * @param internetProtocolFamilies a valid sequence of {@link InternetProtocolFamily}s - * @return a {@link ResolvedAddressTypes} - */ - public static ResolvedAddressTypes computeResolvedAddressTypes(InternetProtocolFamily... internetProtocolFamilies) { - if (internetProtocolFamilies == null || internetProtocolFamilies.length == 0) { - return DnsNameResolver.DEFAULT_RESOLVE_ADDRESS_TYPES; - } - if (internetProtocolFamilies.length > 2) { - throw new IllegalArgumentException("No more than 2 InternetProtocolFamilies"); - } - - switch(internetProtocolFamilies[0]) { - case IPv4: - return (internetProtocolFamilies.length >= 2 - && internetProtocolFamilies[1] == InternetProtocolFamily.IPv6) ? - ResolvedAddressTypes.IPV4_PREFERRED: ResolvedAddressTypes.IPV4_ONLY; - case IPv6: - return (internetProtocolFamilies.length >= 2 - && internetProtocolFamilies[1] == InternetProtocolFamily.IPv4) ? - ResolvedAddressTypes.IPV6_PREFERRED: ResolvedAddressTypes.IPV6_ONLY; - default: - throw new IllegalArgumentException( - "Couldn't resolve ResolvedAddressTypes from InternetProtocolFamily array"); - } - } - - /** - * Sets the list of the protocol families of the address resolved. - * You can use {@link DnsNameResolverBuilder#computeResolvedAddressTypes(InternetProtocolFamily...)} - * to get a {@link ResolvedAddressTypes} out of some {@link InternetProtocolFamily}s. - * - * @param resolvedAddressTypes the address types - * @return {@code this} - */ - public DnsNameResolverBuilder resolvedAddressTypes(ResolvedAddressTypes resolvedAddressTypes) { - this.resolvedAddressTypes = resolvedAddressTypes; - return this; - } - - /** - * If {@code true} {@link DnsNameResolver#resolveAll(String)} will notify the returned {@link Future} as - * soon as all queries for the preferred address-type are complete. - * - * @param completeOncePreferredResolved {@code true} to enable, {@code false} to disable. - * @return {@code this} - */ - public DnsNameResolverBuilder completeOncePreferredResolved(boolean completeOncePreferredResolved) { - this.completeOncePreferredResolved = completeOncePreferredResolved; - return this; - } - - /** - * Sets if this resolver has to send a DNS query with the RD (recursion desired) flag set. - * - * @param recursionDesired true if recursion is desired - * @return {@code this} - */ - public DnsNameResolverBuilder recursionDesired(boolean recursionDesired) { - this.recursionDesired = recursionDesired; - return this; - } - - /** - * Sets the maximum allowed number of DNS queries to send when resolving a host name. - * - * @param maxQueriesPerResolve the max number of queries - * @return {@code this} - */ - public DnsNameResolverBuilder maxQueriesPerResolve(int maxQueriesPerResolve) { - this.maxQueriesPerResolve = maxQueriesPerResolve; - return this; - } - - /** - * Sets the capacity of the datagram packet buffer (in bytes). The default value is {@code 4096} bytes. - * - * @param maxPayloadSize the capacity of the datagram packet buffer - * @return {@code this} - */ - public DnsNameResolverBuilder maxPayloadSize(int maxPayloadSize) { - this.maxPayloadSize = maxPayloadSize; - return this; - } - - /** - * Enable the automatic inclusion of a optional records that tries to give the remote DNS server a hint about - * how much data the resolver can read per response. Some DNSServer may not support this and so fail to answer - * queries. If you find problems you may want to disable this. - * - * @param optResourceEnabled if optional records inclusion is enabled - * @return {@code this} - */ - public DnsNameResolverBuilder optResourceEnabled(boolean optResourceEnabled) { - this.optResourceEnabled = optResourceEnabled; - return this; - } - - /** - * @param hostsFileEntriesResolver the {@link HostsFileEntriesResolver} used to first check - * if the hostname is locally aliased. - * @return {@code this} - */ - public DnsNameResolverBuilder hostsFileEntriesResolver(HostsFileEntriesResolver hostsFileEntriesResolver) { - this.hostsFileEntriesResolver = hostsFileEntriesResolver; - return this; - } - - protected DnsServerAddressStreamProvider nameServerProvider() { - return this.dnsServerAddressStreamProvider; - } - - /** - * Set the {@link DnsServerAddressStreamProvider} which is used to determine which DNS server is used to resolve - * each hostname. - * @return {@code this}. - */ - public DnsNameResolverBuilder nameServerProvider(DnsServerAddressStreamProvider dnsServerAddressStreamProvider) { - this.dnsServerAddressStreamProvider = - requireNonNull(dnsServerAddressStreamProvider, "dnsServerAddressStreamProvider"); - return this; - } - - /** - * Set the list of search domains of the resolver. - * - * @param searchDomains the search domains - * @return {@code this} - */ - public DnsNameResolverBuilder searchDomains(Iterable searchDomains) { - requireNonNull(searchDomains, "searchDomains"); - - final List list = new ArrayList<>(4); - - for (String f : searchDomains) { - if (f == null) { - break; - } - - // Avoid duplicate entries. - if (list.contains(f)) { - continue; - } - - list.add(f); - } - - this.searchDomains = list.toArray(new String[0]); - return this; - } - - /** - * Set the number of dots which must appear in a name before an initial absolute query is made. - * The default value is {@code 1}. - * - * @param ndots the ndots value - * @return {@code this} - */ - public DnsNameResolverBuilder ndots(int ndots) { - this.ndots = ndots; - return this; - } - - private DnsCache newCache() { - return new DefaultDnsCache(intValue(minTtl, 0), intValue(maxTtl, Integer.MAX_VALUE), intValue(negativeTtl, 0)); - } - - private AuthoritativeDnsServerCache newAuthoritativeDnsServerCache() { - return new DefaultAuthoritativeDnsServerCache( - intValue(minTtl, 0), intValue(maxTtl, Integer.MAX_VALUE), - // Let us use the sane ordering as DnsNameResolver will be used when returning - // nameservers from the cache. - 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. - * - * @param decodeIdn if should get decoded - * @return {@code this} - */ - public DnsNameResolverBuilder decodeIdn(boolean decodeIdn) { - this.decodeIdn = decodeIdn; - return this; - } - - /** - * Returns a new {@link DnsNameResolver} instance. - * - * @return a {@link DnsNameResolver} - */ - public DnsNameResolver build() { - if (eventLoop == null) { - throw new IllegalStateException("eventLoop should be specified to build a DnsNameResolver."); - } - - if (resolveCache != null && (minTtl != null || maxTtl != null || negativeTtl != null)) { - throw new IllegalStateException("resolveCache and TTLs are mutually exclusive"); - } - - if (authoritativeDnsServerCache != null && (minTtl != null || maxTtl != null || negativeTtl != null)) { - throw new IllegalStateException("authoritativeDnsServerCache and TTLs are mutually exclusive"); - } - - 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, - socketChannelFactory, - resolveCache, - cnameCache, - authoritativeDnsServerCache, - localAddress, - dnsQueryLifecycleObserverFactory, - queryTimeoutMillis, - resolvedAddressTypes, - recursionDesired, - maxQueriesPerResolve, - maxPayloadSize, - optResourceEnabled, - hostsFileEntriesResolver, - dnsServerAddressStreamProvider, - searchDomains, - ndots, - decodeIdn, - completeOncePreferredResolved); - } - - /** - * Creates a copy of this {@link DnsNameResolverBuilder} - * - * @return {@link DnsNameResolverBuilder} - */ - public DnsNameResolverBuilder copy() { - DnsNameResolverBuilder copiedBuilder = new DnsNameResolverBuilder(); - - if (eventLoop != null) { - copiedBuilder.eventLoop(eventLoop); - } - - if (channelFactory != null) { - copiedBuilder.channelFactory(channelFactory); - } - - if (socketChannelFactory != null) { - copiedBuilder.socketChannelFactory(socketChannelFactory); - } - - if (resolveCache != null) { - copiedBuilder.resolveCache(resolveCache); - } - - if (cnameCache != null) { - copiedBuilder.cnameCache(cnameCache); - } - if (maxTtl != null && minTtl != null) { - copiedBuilder.ttl(minTtl, maxTtl); - } - - if (negativeTtl != null) { - copiedBuilder.negativeTtl(negativeTtl); - } - - if (authoritativeDnsServerCache != null) { - copiedBuilder.authoritativeDnsServerCache(authoritativeDnsServerCache); - } - - if (dnsQueryLifecycleObserverFactory != null) { - copiedBuilder.dnsQueryLifecycleObserverFactory(dnsQueryLifecycleObserverFactory); - } - - copiedBuilder.queryTimeoutMillis(queryTimeoutMillis); - copiedBuilder.resolvedAddressTypes(resolvedAddressTypes); - copiedBuilder.recursionDesired(recursionDesired); - copiedBuilder.maxQueriesPerResolve(maxQueriesPerResolve); - copiedBuilder.maxPayloadSize(maxPayloadSize); - copiedBuilder.optResourceEnabled(optResourceEnabled); - copiedBuilder.hostsFileEntriesResolver(hostsFileEntriesResolver); - - if (dnsServerAddressStreamProvider != null) { - copiedBuilder.nameServerProvider(dnsServerAddressStreamProvider); - } - - if (searchDomains != null) { - copiedBuilder.searchDomains(Arrays.asList(searchDomains)); - } - - copiedBuilder.ndots(ndots); - copiedBuilder.decodeIdn(decodeIdn); - copiedBuilder.completeOncePreferredResolved(completeOncePreferredResolved); - - return copiedBuilder; - } -} diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolverException.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolverException.java deleted file mode 100644 index 714a60fbdf..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolverException.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import static java.util.Objects.requireNonNull; - -import io.netty.handler.codec.dns.DnsQuestion; - -import java.net.InetSocketAddress; - -/** - * A {@link RuntimeException} raised when {@link DnsNameResolver} failed to perform a successful query. - */ -public class DnsNameResolverException extends RuntimeException { - - private static final long serialVersionUID = -8826717909627131850L; - - private final InetSocketAddress remoteAddress; - private final DnsQuestion question; - - public DnsNameResolverException(InetSocketAddress remoteAddress, DnsQuestion question, String message) { - super(message, null, true, false); - this.remoteAddress = validateRemoteAddress(remoteAddress); - this.question = validateQuestion(question); - } - - public DnsNameResolverException( - InetSocketAddress remoteAddress, DnsQuestion question, String message, Throwable cause) { - super(message, cause, true, false); - this.remoteAddress = validateRemoteAddress(remoteAddress); - this.question = validateQuestion(question); - } - - private static InetSocketAddress validateRemoteAddress(InetSocketAddress remoteAddress) { - return requireNonNull(remoteAddress, "remoteAddress"); - } - - private static DnsQuestion validateQuestion(DnsQuestion question) { - return requireNonNull(question, "question"); - } - - /** - * Returns the {@link InetSocketAddress} of the DNS query that has failed. - */ - public InetSocketAddress remoteAddress() { - return remoteAddress; - } - - /** - * Returns the {@link DnsQuestion} of the DNS query that has failed. - */ - public DnsQuestion question() { - return question; - } -} diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolverTimeoutException.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolverTimeoutException.java deleted file mode 100644 index 0fca7f0d9f..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolverTimeoutException.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.handler.codec.dns.DnsQuestion; - -import java.net.InetSocketAddress; - -/** - * A {@link DnsNameResolverException} raised when {@link DnsNameResolver} failed to perform a successful query because - * of an timeout. In this case you may want to retry the operation. - */ -public final class DnsNameResolverTimeoutException extends DnsNameResolverException { - private static final long serialVersionUID = -8826717969627131854L; - - public DnsNameResolverTimeoutException( - InetSocketAddress remoteAddress, DnsQuestion question, String message) { - super(remoteAddress, question, message); - } -} 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 deleted file mode 100644 index 1fc45d3059..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsQueryContext.java +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.channel.AddressedEnvelope; -import io.netty.channel.Channel; -import io.netty.handler.codec.dns.AbstractDnsOptPseudoRrRecord; -import io.netty.handler.codec.dns.DnsQuery; -import io.netty.handler.codec.dns.DnsQuestion; -import io.netty.handler.codec.dns.DnsRecord; -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.Promise; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.net.InetSocketAddress; -import java.util.concurrent.TimeUnit; - -import static java.util.Objects.requireNonNull; - -abstract class DnsQueryContext implements FutureListener> { - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(DnsQueryContext.class); - - private final DnsNameResolver parent; - private final Promise> promise; - private final int id; - private final DnsQuestion question; - private final DnsRecord[] additionals; - private final DnsRecord optResource; - private final InetSocketAddress nameServerAddr; - - private final boolean recursionDesired; - private volatile Future timeoutFuture; - - DnsQueryContext(DnsNameResolver parent, - InetSocketAddress nameServerAddr, - DnsQuestion question, - DnsRecord[] additionals, - Promise> promise) { - - this.parent = requireNonNull(parent, "parent"); - this.nameServerAddr = requireNonNull(nameServerAddr, "nameServerAddr"); - this.question = requireNonNull(question, "question"); - this.additionals = requireNonNull(additionals, "additionals"); - this.promise = requireNonNull(promise, "promise"); - recursionDesired = parent.isRecursionDesired(); - id = parent.queryContextManager.add(this); - - // Ensure we remove the id from the QueryContextManager once the query completes. - promise.asFuture().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. - }; - } else { - optResource = null; - } - } - - InetSocketAddress nameServerAddr() { - return nameServerAddr; - } - - DnsQuestion question() { - return question; - } - - DnsNameResolver parent() { - return parent; - } - - protected abstract DnsQuery newQuery(int id); - protected abstract Channel channel(); - protected abstract String protocol(); - - void query(boolean flush, Promise writePromise) { - final DnsQuestion question = question(); - final InetSocketAddress nameServerAddr = nameServerAddr(); - final DnsQuery query = newQuery(id); - - query.setRecursionDesired(recursionDesired); - - query.addRecord(DnsSection.QUESTION, question); - - for (DnsRecord record: additionals) { - query.addRecord(DnsSection.ADDITIONAL, record); - } - - if (optResource != null) { - query.addRecord(DnsSection.ADDITIONAL, optResource); - } - - if (logger.isDebugEnabled()) { - logger.debug("{} WRITE: {}, [{}: {}], {}", channel(), protocol(), id, nameServerAddr, question); - } - - sendQuery(query, flush, writePromise); - } - - private void sendQuery(final DnsQuery query, final boolean flush, final Promise writePromise) { - if (parent.channelFuture.isDone()) { - writeQuery(query, flush, writePromise); - } else { - parent.channelFuture.addListener(future -> { - if (future.isSuccess()) { - // 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); - writePromise.setFailure(cause); - } - }); - } - } - - private void writeQuery(final DnsQuery query, final boolean flush, final Promise writePromise) { - final Future writeFuture = flush ? channel().writeAndFlush(query) : - channel().write(query); - if (writeFuture.isDone()) { - onQueryWriteCompletion(writeFuture, writePromise); - } else { - writeFuture.addListener(future -> - onQueryWriteCompletion(future, writePromise)); - } - } - - private void onQueryWriteCompletion(Future writeFuture, Promise writePromise) { - if (writeFuture.isFailed()) { - writePromise.setFailure(writeFuture.cause()); - tryFailure("failed to send a query via " + protocol(), writeFuture.cause(), false); - return; - } - writePromise.setSuccess(null); - // Schedule a query timeout task if necessary. - final long queryTimeoutMillis = parent.queryTimeoutMillis(); - if (queryTimeoutMillis > 0) { - timeoutFuture = parent.ch.executor().schedule(() -> { - if (promise.isDone()) { - // Received a response before the query times out. - return; - } - - tryFailure("query via " + protocol() + " timed out after " + - queryTimeoutMillis + " milliseconds", null, true); - }, queryTimeoutMillis, TimeUnit.MILLISECONDS); - } - } - - /** - * Takes ownership of passed envelope - */ - void finish(AddressedEnvelope envelope) { - final DnsResponse res = envelope.content(); - if (res.count(DnsSection.QUESTION) != 1) { - logger.warn("Received a DNS response with invalid number of questions: {}", envelope); - } else if (!question().equals(res.recordAt(DnsSection.QUESTION))) { - logger.warn("Received a mismatching DNS response: {}", envelope); - } else if (trySuccess(envelope)) { - return; // Ownership transferred, don't release - } - envelope.release(); - } - - @SuppressWarnings("unchecked") - private boolean trySuccess(AddressedEnvelope envelope) { - return promise.trySuccess((AddressedEnvelope) envelope); - } - - boolean tryFailure(String message, Throwable cause, boolean timeout) { - if (promise.isDone()) { - return false; - } - final InetSocketAddress nameServerAddr = nameServerAddr(); - - final StringBuilder buf = new StringBuilder(message.length() + 64); - buf.append('[') - .append(nameServerAddr) - .append("] ") - .append(message) - .append(" (no stack trace available)"); - - final DnsNameResolverException e; - if (timeout) { - // This was caused by an timeout so use DnsNameResolverTimeoutException to allow the user to - // handle it special (like retry the query). - e = new DnsNameResolverTimeoutException(nameServerAddr, question(), buf.toString()); - } else { - e = new DnsNameResolverException(nameServerAddr, question(), buf.toString(), cause); - } - return promise.tryFailure(e); - } - - @Override - public void operationComplete(Future> future) { - // Cancel the timeout task. - final Future timeoutFuture = this.timeoutFuture; - if (timeoutFuture != null) { - this.timeoutFuture = null; - timeoutFuture.cancel(); - } - - // 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); - } -} diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsQueryContextManager.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsQueryContextManager.java deleted file mode 100644 index 277890b9f4..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsQueryContextManager.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.resolver.dns; - -import io.netty.util.NetUtil; -import io.netty.util.collection.IntObjectHashMap; -import io.netty.util.collection.IntObjectMap; - -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.UnknownHostException; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ThreadLocalRandom; - -final class DnsQueryContextManager { - - /** - * A map whose key is the DNS server address and value is the map of the DNS query ID and its corresponding - * {@link DnsQueryContext}. - */ - final Map> map = new HashMap<>(); - - int add(DnsQueryContext qCtx) { - final IntObjectMap contexts = getOrCreateContextMap(qCtx.nameServerAddr()); - - int id = ThreadLocalRandom.current().nextInt(65536 - 1) + 1; - final int maxTries = 65535 << 1; - int tries = 0; - - synchronized (contexts) { - for (;;) { - if (!contexts.containsKey(id)) { - contexts.put(id, qCtx); - return id; - } - - id = id + 1 & 0xFFFF; - - if (++tries >= maxTries) { - throw new IllegalStateException("query ID space exhausted: " + qCtx.question()); - } - } - } - } - - DnsQueryContext get(InetSocketAddress nameServerAddr, int id) { - final IntObjectMap contexts = getContextMap(nameServerAddr); - final DnsQueryContext qCtx; - if (contexts != null) { - synchronized (contexts) { - qCtx = contexts.get(id); - } - } else { - qCtx = null; - } - - return qCtx; - } - - DnsQueryContext remove(InetSocketAddress nameServerAddr, int id) { - final IntObjectMap contexts = getContextMap(nameServerAddr); - if (contexts == null) { - return null; - } - - synchronized (contexts) { - return contexts.remove(id); - } - } - - private IntObjectMap getContextMap(InetSocketAddress nameServerAddr) { - synchronized (map) { - return map.get(nameServerAddr); - } - } - - private IntObjectMap getOrCreateContextMap(InetSocketAddress nameServerAddr) { - synchronized (map) { - final IntObjectMap contexts = map.get(nameServerAddr); - if (contexts != null) { - return contexts; - } - - final IntObjectMap newContexts = new IntObjectHashMap(); - final InetAddress a = nameServerAddr.getAddress(); - final int port = nameServerAddr.getPort(); - map.put(nameServerAddr, newContexts); - - if (a instanceof Inet4Address) { - // Also add the mapping for the IPv4-compatible IPv6 address. - final Inet4Address a4 = (Inet4Address) a; - if (a4.isLoopbackAddress()) { - map.put(new InetSocketAddress(NetUtil.LOCALHOST6, port), newContexts); - } else { - map.put(new InetSocketAddress(toCompactAddress(a4), port), newContexts); - } - } else if (a instanceof Inet6Address) { - // Also add the mapping for the IPv4 address if this IPv6 address is compatible. - final Inet6Address a6 = (Inet6Address) a; - if (a6.isLoopbackAddress()) { - map.put(new InetSocketAddress(NetUtil.LOCALHOST4, port), newContexts); - } else if (a6.isIPv4CompatibleAddress()) { - map.put(new InetSocketAddress(toIPv4Address(a6), port), newContexts); - } - } - - return newContexts; - } - } - - private static Inet6Address toCompactAddress(Inet4Address a4) { - byte[] b4 = a4.getAddress(); - byte[] b6 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, b4[0], b4[1], b4[2], b4[3] }; - try { - return (Inet6Address) InetAddress.getByAddress(b6); - } catch (UnknownHostException e) { - throw new Error(e); - } - } - - private static Inet4Address toIPv4Address(Inet6Address a6) { - byte[] b6 = a6.getAddress(); - byte[] b4 = { b6[12], b6[13], b6[14], b6[15] }; - try { - return (Inet4Address) InetAddress.getByAddress(b4); - } catch (UnknownHostException e) { - throw new Error(e); - } - } -} diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsQueryLifecycleObserver.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsQueryLifecycleObserver.java deleted file mode 100644 index 7e7fed5e3c..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsQueryLifecycleObserver.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.handler.codec.dns.DnsQuestion; -import io.netty.handler.codec.dns.DnsRecordType; -import io.netty.handler.codec.dns.DnsResponseCode; -import io.netty.util.concurrent.Future; - -import java.net.InetSocketAddress; -import java.util.List; - -/** - * This interface provides visibility into individual DNS queries. The lifecycle of an objects is as follows: - *

      - *
    1. Object creation
    2. - *
    3. {@link #queryCancelled(int)}
    4. - *
    - * OR - *
      - *
    1. Object creation
    2. - *
    3. {@link #queryWritten(InetSocketAddress, Future)}
    4. - *
    5. {@link #queryRedirected(List)} or {@link #queryCNAMEd(DnsQuestion)} or - * {@link #queryNoAnswer(DnsResponseCode)} or {@link #queryCancelled(int)} or - * {@link #queryFailed(Throwable)} or {@link #querySucceed()}
    6. - *
    - *

    - * This interface can be used to track metrics for individual DNS servers. Methods which may lead to another DNS query - * return an object of type {@link DnsQueryLifecycleObserver}. Implementations may use this to build a query tree to - * understand the "sub queries" generated by a single query. - */ -public interface DnsQueryLifecycleObserver { - /** - * The query has been written. - * @param dnsServerAddress The DNS server address which the query was sent to. - * @param future The future which represents the status of the write operation for the DNS query. - */ - void queryWritten(InetSocketAddress dnsServerAddress, Future future); - - /** - * The query may have been written but it was cancelled at some point. - * @param queriesRemaining The number of queries remaining. - */ - void queryCancelled(int queriesRemaining); - - /** - * The query has been redirected to another list of DNS servers. - * @param nameServers The name servers the query has been redirected to. - * @return An observer for the new query which we may issue. - */ - DnsQueryLifecycleObserver queryRedirected(List nameServers); - - /** - * The query returned a CNAME which we may attempt to follow with a new query. - *

    - * Note that multiple queries may be encountering a CNAME. For example a if both {@link DnsRecordType#AAAA} and - * {@link DnsRecordType#A} are supported we may query for both. - * @param cnameQuestion the question we would use if we issue a new query. - * @return An observer for the new query which we may issue. - */ - DnsQueryLifecycleObserver queryCNAMEd(DnsQuestion cnameQuestion); - - /** - * The response to the query didn't provide the expected response code, but it didn't return - * {@link DnsResponseCode#NXDOMAIN} so we may try to query again. - * @param code the unexpected response code. - * @return An observer for the new query which we may issue. - */ - DnsQueryLifecycleObserver queryNoAnswer(DnsResponseCode code); - - /** - * The following criteria are possible: - *

      - *
    • IO Error
    • - *
    • Server responded with an invalid DNS response
    • - *
    • Server responded with a valid DNS response, but it didn't progress the resolution
    • - *
    - * @param cause The cause which for the failure. - */ - void queryFailed(Throwable cause); - - /** - * The query received the expected results. - */ - void querySucceed(); -} diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsQueryLifecycleObserverFactory.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsQueryLifecycleObserverFactory.java deleted file mode 100644 index e3d277660c..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsQueryLifecycleObserverFactory.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.handler.codec.dns.DnsQuestion; - -/** - * Used to generate new instances of {@link DnsQueryLifecycleObserver}. - */ -public interface DnsQueryLifecycleObserverFactory { - /** - * Create a new instance of a {@link DnsQueryLifecycleObserver}. This will be called at the start of a new query. - * @param question The question being asked. - * @return a new instance of a {@link DnsQueryLifecycleObserver}. - */ - DnsQueryLifecycleObserver newDnsQueryLifecycleObserver(DnsQuestion question); -} diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsRecordResolveContext.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsRecordResolveContext.java deleted file mode 100644 index d5815980a2..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsRecordResolveContext.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import java.net.UnknownHostException; -import java.util.List; - -import io.netty.channel.EventLoop; -import io.netty.handler.codec.dns.DnsQuestion; -import io.netty.handler.codec.dns.DnsRecord; -import io.netty.handler.codec.dns.DnsRecordType; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.concurrent.Promise; - -final class DnsRecordResolveContext extends DnsResolveContext { - - DnsRecordResolveContext(DnsNameResolver parent, Promise originalPromise, DnsQuestion question, - DnsRecord[] additionals, DnsServerAddressStream nameServerAddrs, int allowedQueries) { - this(parent, originalPromise, question.name(), question.dnsClass(), - new DnsRecordType[] { question.type() }, - additionals, nameServerAddrs, allowedQueries); - } - - private DnsRecordResolveContext(DnsNameResolver parent, Promise originalPromise, String hostname, - int dnsClass, DnsRecordType[] expectedTypes, - DnsRecord[] additionals, - DnsServerAddressStream nameServerAddrs, - int allowedQueries) { - super(parent, originalPromise, hostname, dnsClass, expectedTypes, additionals, nameServerAddrs, allowedQueries); - } - - @Override - DnsResolveContext newResolverContext(DnsNameResolver parent, Promise originalPromise, - String hostname, - int dnsClass, DnsRecordType[] expectedTypes, - DnsRecord[] additionals, - DnsServerAddressStream nameServerAddrs, - int allowedQueries) { - return new DnsRecordResolveContext(parent, originalPromise, hostname, dnsClass, - expectedTypes, additionals, nameServerAddrs, allowedQueries); - } - - @Override - DnsRecord convertRecord(DnsRecord record, String hostname, DnsRecord[] additionals, EventLoop eventLoop) { - return ReferenceCountUtil.retain(record); - } - - @Override - List filterResults(List unfiltered) { - return unfiltered; - } - - @Override - boolean isCompleteEarly(DnsRecord resolved) { - return false; - } - - @Override - boolean isDuplicateAllowed() { - return true; - } - - @Override - void cache(String hostname, DnsRecord[] additionals, DnsRecord result, DnsRecord convertedResult) { - // Do not cache. - // XXX: When we implement cache, we would need to retain the reference count of the result record. - } - - @Override - void cache(String hostname, DnsRecord[] additionals, UnknownHostException cause) { - // Do not cache. - // XXX: When we implement cache, we would need to retain the reference count of the result record. - } - - @Override - DnsCnameCache cnameCache() { - // We don't use a cache here at all as we also don't cache if we end up using the DnsRecordResolverContext. - return NoopDnsCnameCache.INSTANCE; - } -} 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 deleted file mode 100644 index 26166f784c..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsResolveContext.java +++ /dev/null @@ -1,1374 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.resolver.dns; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufHolder; -import io.netty.channel.AddressedEnvelope; -import io.netty.channel.EventLoop; -import io.netty.handler.codec.CorruptedFrameException; -import io.netty.handler.codec.dns.DefaultDnsQuestion; -import io.netty.handler.codec.dns.DefaultDnsRecordDecoder; -import io.netty.handler.codec.dns.DnsQuestion; -import io.netty.handler.codec.dns.DnsRawRecord; -import io.netty.handler.codec.dns.DnsRecord; -import io.netty.handler.codec.dns.DnsRecordType; -import io.netty.handler.codec.dns.DnsResponse; -import io.netty.handler.codec.dns.DnsResponseCode; -import io.netty.handler.codec.dns.DnsSection; -import io.netty.util.NetUtil; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.FutureListener; -import io.netty.util.concurrent.Promise; -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.ThrowableUtil; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.UnknownHostException; -import java.util.AbstractList; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.IdentityHashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Set; - -import static io.netty.resolver.dns.DnsAddressDecoder.decodeAddress; -import static java.lang.Math.min; -import static java.util.Objects.requireNonNull; - -abstract class DnsResolveContext { - private static final InternalLogger logger = InternalLoggerFactory.getInstance(DnsResolveContext.class); - - private static final RuntimeException NXDOMAIN_QUERY_FAILED_EXCEPTION = - DnsResolveContextException.newStatic("No answer found and NXDOMAIN response code returned", - DnsResolveContext.class, "onResponse(..)"); - private static final RuntimeException CNAME_NOT_FOUND_QUERY_FAILED_EXCEPTION = - DnsResolveContextException.newStatic("No matching CNAME record found", - DnsResolveContext.class, "onResponseCNAME(..)"); - private static final RuntimeException NO_MATCHING_RECORD_QUERY_FAILED_EXCEPTION = - DnsResolveContextException.newStatic("No matching record type found", - DnsResolveContext.class, "onResponseAorAAAA(..)"); - private static final RuntimeException UNRECOGNIZED_TYPE_QUERY_FAILED_EXCEPTION = - DnsResolveContextException.newStatic("Response type was unrecognized", - DnsResolveContext.class, "onResponse(..)"); - private static final RuntimeException NAME_SERVERS_EXHAUSTED_EXCEPTION = - DnsResolveContextException.newStatic("No name servers returned an answer", - DnsResolveContext.class, "tryToFinishResolve(..)"); - - final DnsNameResolver parent; - private final Promise originalPromise; - private final DnsServerAddressStream nameServerAddrs; - private final String hostname; - private final int dnsClass; - private final DnsRecordType[] expectedTypes; - final DnsRecord[] additionals; - - private final Set>> queriesInProgress = - Collections.newSetFromMap( - new IdentityHashMap<>()); - - private List finalResult; - private int allowedQueries; - private boolean triedCNAME; - private boolean completeEarly; - - DnsResolveContext(DnsNameResolver parent, Promise originalPromise, - String hostname, int dnsClass, DnsRecordType[] expectedTypes, - DnsRecord[] additionals, DnsServerAddressStream nameServerAddrs, int allowedQueries) { - assert expectedTypes.length > 0; - - this.parent = parent; - this.originalPromise = originalPromise; - this.hostname = hostname; - this.dnsClass = dnsClass; - this.expectedTypes = expectedTypes; - this.additionals = additionals; - - this.nameServerAddrs = requireNonNull(nameServerAddrs, "nameServerAddrs"); - this.allowedQueries = allowedQueries; - } - - static final class DnsResolveContextException extends RuntimeException { - - private static final long serialVersionUID = 1209303419266433003L; - - private DnsResolveContextException(String message) { - super(message, null, false, true); // Need to keep the stack trace mutable. - } - - // Override fillInStackTrace() so we not populate the backtrace via a native call and so leak the - // Classloader. - @Override - public Throwable fillInStackTrace() { - return this; - } - - static DnsResolveContextException newStatic(String message, Class clazz, String method) { - return ThrowableUtil.unknownStackTrace(new DnsResolveContextException(message), clazz, method); - } - } - - /** - * The {@link DnsCache} to use while resolving. - */ - 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. - */ - AuthoritativeDnsServerCache authoritativeDnsServerCache() { - return parent.authoritativeDnsServerCache(); - } - - /** - * Creates a new context with the given parameters. - */ - abstract DnsResolveContext newResolverContext(DnsNameResolver parent, Promise originalPromise, - String hostname, - int dnsClass, DnsRecordType[] expectedTypes, - DnsRecord[] additionals, - DnsServerAddressStream nameServerAddrs, int allowedQueries); - - /** - * Converts the given {@link DnsRecord} into {@code T}. - */ - abstract T convertRecord(DnsRecord record, String hostname, DnsRecord[] additionals, EventLoop eventLoop); - - /** - * Returns a filtered list of results which should be the final result of DNS resolution. This must take into - * account JDK semantics such as {@link NetUtil#isIpV6AddressesPreferred()}. - */ - abstract List filterResults(List unfiltered); - - abstract boolean isCompleteEarly(T resolved); - - /** - * Returns {@code true} if we should allow duplicates in the result or {@code false} if no duplicates should - * be included. - */ - abstract boolean isDuplicateAllowed(); - - /** - * Caches a successful resolution. - */ - abstract void cache(String hostname, DnsRecord[] additionals, - DnsRecord result, T convertedResult); - - /** - * Caches a failed resolution. - */ - abstract void cache(String hostname, DnsRecord[] additionals, - UnknownHostException cause); - - void resolve(final Promise> promise) { - final String[] searchDomains = parent.searchDomains(); - if (searchDomains.length == 0 || parent.ndots() == 0 || StringUtil.endsWith(hostname, '.')) { - internalResolve(hostname, promise); - } else { - final boolean startWithoutSearchDomain = hasNDots(); - final String initialHostname = startWithoutSearchDomain ? hostname : hostname + '.' + searchDomains[0]; - final int initialSearchDomainIdx = startWithoutSearchDomain ? 0 : 1; - - final Promise> searchDomainPromise = parent.executor().newPromise(); - searchDomainPromise.asFuture().addListener(new FutureListener>() { - private int searchDomainIdx = initialSearchDomainIdx; - @Override - public void operationComplete(Future> future) { - Throwable cause = future.cause(); - if (cause == null) { - final List result = future.getNow(); - if (!promise.trySuccess(result)) { - for (T item : result) { - ReferenceCountUtil.safeRelease(item); - } - } - } else { - if (DnsNameResolver.isTransportOrTimeoutError(cause)) { - promise.tryFailure(new SearchDomainUnknownHostException(cause, hostname)); - } else if (searchDomainIdx < searchDomains.length) { - Promise> newPromise = parent.executor().newPromise(); - newPromise.asFuture().addListener(this); - doSearchDomainQuery(hostname + '.' + searchDomains[searchDomainIdx++], newPromise); - } else if (!startWithoutSearchDomain) { - internalResolve(hostname, promise); - } else { - promise.tryFailure(new SearchDomainUnknownHostException(cause, hostname)); - } - } - } - }); - doSearchDomainQuery(initialHostname, searchDomainPromise); - } - } - - private boolean hasNDots() { - for (int idx = hostname.length() - 1, dots = 0; idx >= 0; idx--) { - if (hostname.charAt(idx) == '.' && ++dots >= parent.ndots()) { - return true; - } - } - return false; - } - - private static final class SearchDomainUnknownHostException extends UnknownHostException { - private static final long serialVersionUID = -8573510133644997085L; - - SearchDomainUnknownHostException(Throwable cause, String originalHostname) { - super("Search domain query failed. Original hostname: '" + originalHostname + "' " + cause.getMessage()); - setStackTrace(cause.getStackTrace()); - - // Preserve the cause - initCause(cause.getCause()); - } - - // Suppress a warning since this method doesn't need synchronization - @Override - public Throwable fillInStackTrace() { // lgtm[java/non-sync-override] - return this; - } - } - - void doSearchDomainQuery(String hostname, Promise> nextPromise) { - DnsResolveContext nextContext = newResolverContext(parent, originalPromise, hostname, dnsClass, - expectedTypes, additionals, nameServerAddrs, - parent.maxQueriesPerResolve()); - nextContext.internalResolve(hostname, nextPromise); - } - - private static String hostnameWithDot(String name) { - if (StringUtil.endsWith(name, '.')) { - return name; - } - return name + '.'; - } - - // Resolve the final name from the CNAME cache until there is nothing to follow anymore. This also - // guards against loops in the cache but early return once a loop is detected. - // - // Visible for testing only - static String cnameResolveFromCache(DnsCnameCache cnameCache, String name) throws UnknownHostException { - String first = cnameCache.get(hostnameWithDot(name)); - if (first == null) { - // Nothing in the cache at all - return name; - } - - String second = cnameCache.get(hostnameWithDot(first)); - if (second == null) { - // Nothing else to follow, return first match. - return first; - } - - checkCnameLoop(name, first, second); - return cnameResolveFromCacheLoop(cnameCache, name, first, second); - } - - private static String cnameResolveFromCacheLoop( - DnsCnameCache cnameCache, String hostname, String first, String mapping) throws UnknownHostException { - // Detect loops by advance only every other iteration. - // See https://en.wikipedia.org/wiki/Cycle_detection#Floyd's_Tortoise_and_Hare - boolean advance = false; - - String name = mapping; - // Resolve from cnameCache() until there is no more cname entry cached. - while ((mapping = cnameCache.get(hostnameWithDot(name))) != null) { - checkCnameLoop(hostname, first, mapping); - name = mapping; - if (advance) { - first = cnameCache.get(first); - } - advance = !advance; - } - return name; - } - - private static void checkCnameLoop(String hostname, String first, String second) throws UnknownHostException { - if (first.equals(second)) { - // Follow CNAME from cache would loop. Lets throw and so fail the resolution. - throw new UnknownHostException("CNAME loop detected for '" + hostname + '\''); - } - } - private void internalResolve(String name, Promise> promise) { - try { - // Resolve from cnameCache() until there is no more cname entry cached. - name = cnameResolveFromCache(cnameCache(), name); - } catch (Throwable cause) { - promise.tryFailure(cause); - return; - } - - 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(), false, promise)) { - return; - } - } - query(name, expectedTypes[end], nameServerAddressStream, false, promise); - } finally { - // Now flush everything we submitted before. - parent.flushQueries(); - } - } - - /** - * Returns the {@link DnsServerAddressStream} that was cached for the given hostname or {@code null} if non - * could be found. - */ - private DnsServerAddressStream getNameServersFromCache(String hostname) { - int len = hostname.length(); - - if (len == 0) { - // We never cache for root servers. - return null; - } - - // We always store in the cache with a trailing '.'. - if (hostname.charAt(len - 1) != '.') { - hostname += "."; - } - - int idx = hostname.indexOf('.'); - if (idx == hostname.length() - 1) { - // We are not interested in handling '.' as we should never serve the root servers from cache. - return null; - } - - // We start from the closed match and then move down. - for (;;) { - // Skip '.' as well. - hostname = hostname.substring(idx + 1); - - int idx2 = hostname.indexOf('.'); - if (idx2 <= 0 || idx2 == hostname.length() - 1) { - // We are not interested in handling '.TLD.' as we should never serve the root servers from cache. - return null; - } - idx = idx2; - - DnsServerAddressStream entries = authoritativeDnsServerCache().get(hostname); - if (entries != null) { - // The returned List may contain unresolved InetSocketAddress instances that will be - // resolved on the fly in query(....). - return entries; - } - } - } - - 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 (completeEarly || nameServerAddrStreamIndex >= nameServerAddrStream.size() || - allowedQueries == 0 || originalPromise.isCancelled() || promise.isCancelled()) { - tryToFinishResolve(nameServerAddrStream, nameServerAddrStreamIndex, question, queryLifecycleObserver, - promise, cause); - return; - } - - --allowedQueries; - - final InetSocketAddress nameServerAddr = nameServerAddrStream.next(); - if (nameServerAddr.isUnresolved()) { - queryUnresolvedNameServer(nameServerAddr, nameServerAddrStream, nameServerAddrStreamIndex, question, - queryLifecycleObserver, promise, cause); - return; - } - final Promise writePromise = parent.ch.newPromise(); - final Promise> queryPromise = - parent.ch.executor().newPromise(); - - final Future> f = - parent.query0(nameServerAddr, question, additionals, flush, writePromise, queryPromise); - - queriesInProgress.add(f); - - queryLifecycleObserver.queryWritten(nameServerAddr, writePromise.asFuture()); - - f.addListener(future -> { - queriesInProgress.remove(future); - - if (promise.isDone() || future.isCancelled()) { - queryLifecycleObserver.queryCancelled(allowedQueries); - - // Check if we need to release the envelope itself. If the query was cancelled the getNow() will - // return null as well as the Future will be failed with a CancellationException. - AddressedEnvelope result = future.getNow(); - if (result != null) { - result.release(); - } - return; - } - - final Throwable queryCause = future.cause(); - try { - if (queryCause == null) { - onResponse(nameServerAddrStream, nameServerAddrStreamIndex, question, future.getNow(), - queryLifecycleObserver, promise); - } else { - // Server did not respond or I/O error occurred; try again. - queryLifecycleObserver.queryFailed(queryCause); - query(nameServerAddrStream, nameServerAddrStreamIndex + 1, question, - newDnsQueryLifecycleObserver(question), true, promise, queryCause); - } - } finally { - tryToFinishResolve(nameServerAddrStream, nameServerAddrStreamIndex, question, - // queryLifecycleObserver has already been terminated at this point so we must - // not allow it to be terminated again by tryToFinishResolve. - NoopDnsQueryLifecycleObserver.INSTANCE, - promise, queryCause); - } - }); - } - - private void queryUnresolvedNameServer(final InetSocketAddress nameServerAddr, - final DnsServerAddressStream nameServerAddrStream, - final int nameServerAddrStreamIndex, - final DnsQuestion question, - final DnsQueryLifecycleObserver queryLifecycleObserver, - final Promise> promise, - final Throwable cause) { - final String nameServerName = nameServerAddr.getHostString(); - assert nameServerName != null; - - // Placeholder so we will not try to finish the original query yet. - final Future> resolveFuture = parent.executor() - .newSucceededFuture(null); - queriesInProgress.add(resolveFuture); - - Promise> resolverPromise = parent.executor().newPromise(); - resolverPromise.asFuture().addListener(future -> { - // Remove placeholder. - queriesInProgress.remove(resolveFuture); - - if (future.isSuccess()) { - List resolvedAddresses = future.getNow(); - DnsServerAddressStream addressStream = new CombinedDnsServerAddressStream( - nameServerAddr, resolvedAddresses, nameServerAddrStream); - query(addressStream, nameServerAddrStreamIndex, question, - queryLifecycleObserver, true, promise, cause); - } else { - // Ignore the server and try the next one... - query(nameServerAddrStream, nameServerAddrStreamIndex + 1, - question, queryLifecycleObserver, true, promise, cause); - } - }); - DnsCache resolveCache = resolveCache(); - if (!DnsNameResolver.doResolveAllCached(nameServerName, additionals, resolverPromise, resolveCache, - parent.resolvedInternetProtocolFamiliesUnsafe())) { - - new DnsAddressResolveContext(parent, originalPromise, nameServerName, additionals, - parent.newNameServerAddressStream(nameServerName), - // Resolving the unresolved nameserver must be limited by allowedQueries - // so we eventually fail - allowedQueries, - resolveCache, - redirectAuthoritativeDnsServerCache(authoritativeDnsServerCache()), false) - .resolve(resolverPromise); - } - } - - private static AuthoritativeDnsServerCache redirectAuthoritativeDnsServerCache( - AuthoritativeDnsServerCache authoritativeDnsServerCache) { - // Don't wrap again to prevent the possibility of an StackOverflowError when wrapping another - // RedirectAuthoritativeDnsServerCache. - if (authoritativeDnsServerCache instanceof RedirectAuthoritativeDnsServerCache) { - return authoritativeDnsServerCache; - } - return new RedirectAuthoritativeDnsServerCache(authoritativeDnsServerCache); - } - - private static final class RedirectAuthoritativeDnsServerCache implements AuthoritativeDnsServerCache { - private final AuthoritativeDnsServerCache wrapped; - - RedirectAuthoritativeDnsServerCache(AuthoritativeDnsServerCache authoritativeDnsServerCache) { - wrapped = authoritativeDnsServerCache; - } - - @Override - public DnsServerAddressStream get(String hostname) { - // To not risk falling into any loop, we will not use the cache while following redirects but only - // on the initial query. - return null; - } - - @Override - public void cache(String hostname, InetSocketAddress address, long originalTtl, EventLoop loop) { - wrapped.cache(hostname, address, originalTtl, loop); - } - - @Override - public void clear() { - wrapped.clear(); - } - - @Override - public boolean clear(String hostname) { - return wrapped.clear(hostname); - } - } - - private void onResponse(final DnsServerAddressStream nameServerAddrStream, final int nameServerAddrStreamIndex, - final DnsQuestion question, AddressedEnvelope envelope, - final DnsQueryLifecycleObserver queryLifecycleObserver, - Promise> promise) { - try { - final DnsResponse res = envelope.content(); - final DnsResponseCode code = res.code(); - if (code == DnsResponseCode.NOERROR) { - if (handleRedirect(question, envelope, queryLifecycleObserver, promise)) { - // Was a redirect so return here as everything else is handled in handleRedirect(...) - return; - } - final DnsRecordType type = question.type(); - - if (type == DnsRecordType.CNAME) { - onResponseCNAME(question, buildAliasMap(envelope.content(), cnameCache(), parent.executor()), - queryLifecycleObserver, promise); - return; - } - - for (DnsRecordType expectedType : expectedTypes) { - if (type == expectedType) { - onExpectedResponse(question, envelope, queryLifecycleObserver, promise); - return; - } - } - - queryLifecycleObserver.queryFailed(UNRECOGNIZED_TYPE_QUERY_FAILED_EXCEPTION); - return; - } - - // 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), 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); - } - } - - /** - * Handles a redirect answer if needed and returns {@code true} if a redirect query has been made. - */ - private boolean handleRedirect( - DnsQuestion question, AddressedEnvelope envelope, - final DnsQueryLifecycleObserver queryLifecycleObserver, Promise> promise) { - final DnsResponse res = envelope.content(); - - // Check if we have answers, if not this may be an non authority NS and so redirects must be handled. - if (res.count(DnsSection.ANSWER) == 0) { - AuthoritativeNameServerList serverNames = extractAuthoritativeNameServers(question.name(), res); - if (serverNames != null) { - int additionalCount = res.count(DnsSection.ADDITIONAL); - - AuthoritativeDnsServerCache authoritativeDnsServerCache = authoritativeDnsServerCache(); - for (int i = 0; i < additionalCount; i++) { - final DnsRecord r = res.recordAt(DnsSection.ADDITIONAL, i); - - if (r.type() == DnsRecordType.A && !parent.supportsARecords() || - r.type() == DnsRecordType.AAAA && !parent.supportsAAAARecords()) { - continue; - } - - // We may have multiple ADDITIONAL entries for the same nameserver name. For example one AAAA and - // one A record. - serverNames.handleWithAdditional(parent, r, authoritativeDnsServerCache); - } - - // Process all unresolved nameservers as well. - serverNames.handleWithoutAdditionals(parent, resolveCache(), authoritativeDnsServerCache); - - List addresses = serverNames.addressList(); - - // Give the user the chance to sort or filter the used servers for the query. - DnsServerAddressStream serverStream = parent.newRedirectDnsServerStream( - question.name(), addresses); - - if (serverStream != null) { - query(serverStream, 0, question, - queryLifecycleObserver.queryRedirected(new DnsAddressStreamList(serverStream)), - true, promise, null); - return true; - } - } - } - return false; - } - - private static final class DnsAddressStreamList extends AbstractList { - - private final DnsServerAddressStream duplicate; - private List addresses; - - DnsAddressStreamList(DnsServerAddressStream stream) { - duplicate = stream.duplicate(); - } - - @Override - public InetSocketAddress get(int index) { - if (addresses == null) { - DnsServerAddressStream stream = duplicate.duplicate(); - addresses = new ArrayList<>(size()); - for (int i = 0; i < stream.size(); i++) { - addresses.add(stream.next()); - } - } - return addresses.get(index); - } - - @Override - public int size() { - return duplicate.size(); - } - - @Override - public Iterator iterator() { - return new Iterator() { - private final DnsServerAddressStream stream = duplicate.duplicate(); - private int i; - - @Override - public boolean hasNext() { - return i < stream.size(); - } - - @Override - public InetSocketAddress next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - i++; - return stream.next(); - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - }; - } - } - - /** - * Returns the {@code {@link AuthoritativeNameServerList} which were included in {@link DnsSection#AUTHORITY} - * or {@code null} if non are found. - */ - private static AuthoritativeNameServerList extractAuthoritativeNameServers(String questionName, DnsResponse res) { - int authorityCount = res.count(DnsSection.AUTHORITY); - if (authorityCount == 0) { - return null; - } - - AuthoritativeNameServerList serverNames = new AuthoritativeNameServerList(questionName); - for (int i = 0; i < authorityCount; i++) { - serverNames.add(res.recordAt(DnsSection.AUTHORITY, i)); - } - return serverNames.isEmpty() ? null : serverNames; - } - - private void onExpectedResponse( - DnsQuestion question, AddressedEnvelope envelope, - final DnsQueryLifecycleObserver queryLifecycleObserver, Promise> promise) { - - // 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, cnameCache(), parent.executor()); - final int answerCount = response.count(DnsSection.ANSWER); - - boolean found = false; - boolean completeEarly = this.completeEarly; - for (int i = 0; i < answerCount; i ++) { - final DnsRecord r = response.recordAt(DnsSection.ANSWER, i); - final DnsRecordType type = r.type(); - boolean matches = false; - for (DnsRecordType expectedType : expectedTypes) { - if (type == expectedType) { - matches = true; - break; - } - } - - if (!matches) { - continue; - } - - final String questionName = question.name().toLowerCase(Locale.US); - final String recordName = r.name().toLowerCase(Locale.US); - - // 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 = cnamesCopy.remove(resolved); - if (recordName.equals(resolved)) { - break; - } - } while (resolved != null); - - if (resolved == null) { - assert questionName.isEmpty() || questionName.charAt(questionName.length() - 1) == '.'; - - for (String searchDomain : parent.searchDomains()) { - if (searchDomain.isEmpty()) { - continue; - } - - final String fqdn; - if (searchDomain.charAt(searchDomain.length() - 1) == '.') { - fqdn = questionName + searchDomain; - } else { - fqdn = questionName + searchDomain + '.'; - } - if (recordName.equals(fqdn)) { - resolved = recordName; - break; - } - } - if (resolved == null) { - if (logger.isDebugEnabled()) { - logger.debug("Ignoring record {} as it contains a different name than the " + - "question name [{}]. Cnames: {}, Search domains: {}", - r.toString(), questionName, cnames, parent.searchDomains()); - } - continue; - } - } - } - - final T converted = convertRecord(r, hostname, additionals, parent.executor()); - if (converted == null) { - if (logger.isDebugEnabled()) { - logger.debug("Ignoring record {} as the converted record is null. hostname [{}], Additionals: {}", - r.toString(), hostname, additionals); - } - continue; - } - - boolean shouldRelease = false; - // Check if we did determine we wanted to complete early before. If this is the case we want to not - // include the result - if (!completeEarly) { - completeEarly = isCompleteEarly(converted); - } - - // We want to ensure we do not have duplicates in finalResult as this may be unexpected. - // - // While using a LinkedHashSet or HashSet may sound like the perfect fit for this we will use an - // ArrayList here as duplicates should be found quite unfrequently in the wild and we dont want to pay - // for the extra memory copy and allocations in this cases later on. - if (finalResult == null) { - finalResult = new ArrayList<>(8); - finalResult.add(converted); - } else if (isDuplicateAllowed() || !finalResult.contains(converted)) { - finalResult.add(converted); - } else { - shouldRelease = true; - } - - cache(hostname, additionals, r, converted); - found = true; - - if (shouldRelease) { - ReferenceCountUtil.release(converted); - } - // Note that we do not break from the loop here, so we decode/cache all A/AAAA records. - } - - if (cnames.isEmpty()) { - if (found) { - if (completeEarly) { - this.completeEarly = true; - } - queryLifecycleObserver.querySucceed(); - return; - } - queryLifecycleObserver.queryFailed(NO_MATCHING_RECORD_QUERY_FAILED_EXCEPTION); - } else { - queryLifecycleObserver.querySucceed(); - // We also got a CNAME so we need to ensure we also query it. - onResponseCNAME(question, cnames, newDnsQueryLifecycleObserver(question), promise); - } - } - - private void onResponseCNAME( - DnsQuestion question, Map cnames, - final DnsQueryLifecycleObserver queryLifecycleObserver, - Promise> promise) { - - // Resolve the host name in the question into the real host name. - String resolved = question.name().toLowerCase(Locale.US); - boolean found = false; - while (!cnames.isEmpty()) { // Do not attempt to call Map.remove() when the Map is empty - // because it can be Collections.emptyMap() - // whose remove() throws a UnsupportedOperationException. - final String next = cnames.remove(resolved); - if (next != null) { - found = true; - resolved = next; - } else { - break; - } - } - - if (found) { - followCname(question, resolved, queryLifecycleObserver, promise); - } else { - queryLifecycleObserver.queryFailed(CNAME_NOT_FOUND_QUERY_FAILED_EXCEPTION); - } - } - - 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 ++) { - final DnsRecord r = response.recordAt(DnsSection.ANSWER, i); - final DnsRecordType type = r.type(); - if (type != DnsRecordType.CNAME) { - continue; - } - - if (!(r instanceof DnsRawRecord)) { - continue; - } - - final ByteBuf recordContent = ((ByteBufHolder) r).content(); - final String domainName = decodeDomainName(recordContent); - if (domainName == null) { - continue; - } - - if (cnames == null) { - cnames = new HashMap<>(min(8, answerCount)); - } - - String name = r.name().toLowerCase(Locale.US); - String mapping = domainName.toLowerCase(Locale.US); - - // Cache the CNAME as well. - 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(); - } - - private void tryToFinishResolve(final DnsServerAddressStream nameServerAddrStream, - final int nameServerAddrStreamIndex, - final DnsQuestion question, - final DnsQueryLifecycleObserver queryLifecycleObserver, - final Promise> promise, - final Throwable cause) { - - // There are no queries left to try. - if (!completeEarly && !queriesInProgress.isEmpty()) { - queryLifecycleObserver.queryCancelled(allowedQueries); - - // There are still some queries in process, we will try to notify once the next one finishes until - // all are finished. - return; - } - - // There are no queries left to try. - if (finalResult == null) { - if (nameServerAddrStreamIndex < nameServerAddrStream.size()) { - 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, - newDnsQueryLifecycleObserver(question), true, promise, cause); - } else { - query(nameServerAddrStream, nameServerAddrStreamIndex + 1, question, queryLifecycleObserver, - true, promise, cause); - } - return; - } - - queryLifecycleObserver.queryFailed(NAME_SERVERS_EXHAUSTED_EXCEPTION); - - // .. and we could not find any expected records. - - // If cause != null we know this was caused by a timeout / cancel / transport exception. In this case we - // won't try to resolve the CNAME as we only should do this if we could not get the expected records - // because they do not exist and the DNS server did probably signal it. - if (cause == null && !triedCNAME && - (question.type() == DnsRecordType.A || question.type() == DnsRecordType.AAAA)) { - // As the last resort, try to query CNAME, just in case the name server has it. - triedCNAME = true; - - query(hostname, DnsRecordType.CNAME, getNameServers(hostname), true, promise); - return; - } - } else { - queryLifecycleObserver.queryCancelled(allowedQueries); - } - - // We have at least one resolved record or tried CNAME as the last resort.. - finishResolve(promise, cause); - } - - private void finishResolve(Promise> promise, Throwable cause) { - // If completeEarly was true we still want to continue processing the queries to ensure we still put everything - // in the cache eventually. - if (!completeEarly && !queriesInProgress.isEmpty()) { - // If there are queries in progress, we should cancel it because we already finished the resolution. - for (Iterator>> i = queriesInProgress.iterator(); - i.hasNext();) { - Future> f = i.next(); - i.remove(); - - f.cancel(); - } - } - - if (finalResult != null) { - if (!promise.isDone()) { - // Found at least one resolved record. - final List result = filterResults(finalResult); - if (!DnsNameResolver.trySuccess(promise, result)) { - for (T item : result) { - ReferenceCountUtil.safeRelease(item); - } - } - } - return; - } - - // No resolved address found. - final int maxAllowedQueries = parent.maxQueriesPerResolve(); - final int tries = maxAllowedQueries - allowedQueries; - final StringBuilder buf = new StringBuilder(64); - - buf.append("failed to resolve '").append(hostname).append('\''); - if (tries > 1) { - if (tries < maxAllowedQueries) { - buf.append(" after ") - .append(tries) - .append(" queries "); - } else { - buf.append(". Exceeded max queries per resolve ") - .append(maxAllowedQueries) - .append(' '); - } - } - final UnknownHostException unknownHostException = new UnknownHostException(buf.toString()); - if (cause == null) { - // Only cache if the failure was not because of an IO error / timeout that was caused by the query - // itself. - cache(hostname, additionals, unknownHostException); - } else { - unknownHostException.initCause(cause); - } - promise.tryFailure(unknownHostException); - } - - static String decodeDomainName(ByteBuf in) { - int readerIndex = in.readerIndex(); - try { - return DefaultDnsRecordDecoder.decodeName(in); - } catch (CorruptedFrameException e) { - // In this case we just return null. - return null; - } finally { - in.readerIndex(readerIndex); - } - } - - private DnsServerAddressStream getNameServers(String name) { - DnsServerAddressStream stream = getNameServersFromCache(name); - if (stream == null) { - // We need to obtain a new stream from the parent DnsNameResolver if the hostname is not the same as - // for the original query (for example we may follow CNAMEs). Otherwise let's just duplicate the - // original nameservers so we correctly update the internal index - if (name.equals(hostname)) { - return nameServerAddrs.duplicate(); - } - return parent.newNameServerAddressStream(name); - } - return stream; - } - - private void followCname(DnsQuestion question, String cname, DnsQueryLifecycleObserver queryLifecycleObserver, - Promise> promise) { - final DnsQuestion cnameQuestion; - final DnsServerAddressStream stream; - try { - cname = cnameResolveFromCache(cnameCache(), cname); - stream = getNameServers(cname); - cnameQuestion = new DefaultDnsQuestion(cname, question.type(), dnsClass); - } catch (Throwable cause) { - queryLifecycleObserver.queryFailed(cause); - PlatformDependent.throwException(cause); - return; - } - query(stream, 0, cnameQuestion, queryLifecycleObserver.queryCNAMEd(cnameQuestion), - true, promise, null); - } - - private boolean query(String hostname, DnsRecordType type, DnsServerAddressStream dnsServerAddressStream, - boolean flush, Promise> promise) { - 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, 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; - private final List resolvedAddresses; - private Iterator resolved; - - CombinedDnsServerAddressStream(InetSocketAddress replaced, List resolvedAddresses, - DnsServerAddressStream originalStream) { - this.replaced = replaced; - this.resolvedAddresses = resolvedAddresses; - this.originalStream = originalStream; - resolved = resolvedAddresses.iterator(); - } - - @Override - public InetSocketAddress next() { - if (resolved.hasNext()) { - return nextResolved0(); - } - InetSocketAddress address = originalStream.next(); - if (address.equals(replaced)) { - resolved = resolvedAddresses.iterator(); - return nextResolved0(); - } - return address; - } - - private InetSocketAddress nextResolved0() { - return parent.newRedirectServerAddress(resolved.next()); - } - - @Override - public int size() { - return originalStream.size() + resolvedAddresses.size() - 1; - } - - @Override - public DnsServerAddressStream duplicate() { - return new CombinedDnsServerAddressStream(replaced, resolvedAddresses, originalStream.duplicate()); - } - } - - /** - * Holds the closed DNS Servers for a domain. - */ - private static final class AuthoritativeNameServerList { - - private final String questionName; - - // We not expect the linked-list to be very long so a double-linked-list is overkill. - private AuthoritativeNameServer head; - - private int nameServerCount; - - AuthoritativeNameServerList(String questionName) { - this.questionName = questionName.toLowerCase(Locale.US); - } - - void add(DnsRecord r) { - if (r.type() != DnsRecordType.NS || !(r instanceof DnsRawRecord)) { - return; - } - - // Only include servers that serve the correct domain. - if (questionName.length() < r.name().length()) { - return; - } - - String recordName = r.name().toLowerCase(Locale.US); - - int dots = 0; - for (int a = recordName.length() - 1, b = questionName.length() - 1; a >= 0; a--, b--) { - char c = recordName.charAt(a); - if (questionName.charAt(b) != c) { - return; - } - if (c == '.') { - dots++; - } - } - - if (head != null && head.dots > dots) { - // We already have a closer match so ignore this one, no need to parse the domainName etc. - return; - } - - final ByteBuf recordContent = ((ByteBufHolder) r).content(); - final String domainName = decodeDomainName(recordContent); - if (domainName == null) { - // Could not be parsed, ignore. - return; - } - - // We are only interested in preserving the nameservers which are the closest to our qName, so ensure - // we drop servers that have a smaller dots count. - if (head == null || head.dots < dots) { - nameServerCount = 1; - head = new AuthoritativeNameServer(dots, r.timeToLive(), recordName, domainName); - } else if (head.dots == dots) { - AuthoritativeNameServer serverName = head; - while (serverName.next != null) { - serverName = serverName.next; - } - serverName.next = new AuthoritativeNameServer(dots, r.timeToLive(), recordName, domainName); - nameServerCount++; - } - } - - void handleWithAdditional( - DnsNameResolver parent, DnsRecord r, AuthoritativeDnsServerCache authoritativeCache) { - // Just walk the linked-list and mark the entry as handled when matched. - AuthoritativeNameServer serverName = head; - - String nsName = r.name(); - InetAddress resolved = decodeAddress(r, nsName, parent.isDecodeIdn()); - if (resolved == null) { - // Could not parse the address, just ignore. - return; - } - - while (serverName != null) { - if (serverName.nsName.equalsIgnoreCase(nsName)) { - if (serverName.address != null) { - // We received multiple ADDITIONAL records for the same name. - // Search for the last we insert before and then append a new one. - while (serverName.next != null && serverName.next.isCopy) { - serverName = serverName.next; - } - AuthoritativeNameServer server = new AuthoritativeNameServer(serverName); - server.next = serverName.next; - serverName.next = server; - serverName = server; - - nameServerCount++; - } - // We should replace the TTL if needed with the one of the ADDITIONAL record so we use - // the smallest for caching. - serverName.update(parent.newRedirectServerAddress(resolved), r.timeToLive()); - - // Cache the server now. - cache(serverName, authoritativeCache, parent.executor()); - return; - } - serverName = serverName.next; - } - } - - // Now handle all AuthoritativeNameServer for which we had no ADDITIONAL record - void handleWithoutAdditionals( - DnsNameResolver parent, DnsCache cache, AuthoritativeDnsServerCache authoritativeCache) { - AuthoritativeNameServer serverName = head; - - while (serverName != null) { - if (serverName.address == null) { - // These will be resolved on the fly if needed. - cacheUnresolved(serverName, authoritativeCache, parent.executor()); - - // Try to resolve via cache as we had no ADDITIONAL entry for the server. - - List entries = cache.get(serverName.nsName, null); - if (entries != null && !entries.isEmpty()) { - InetAddress address = entries.get(0).address(); - - // If address is null we have a resolution failure cached so just use an unresolved address. - if (address != null) { - serverName.update(parent.newRedirectServerAddress(address)); - - for (int i = 1; i < entries.size(); i++) { - address = entries.get(i).address(); - - assert address != null : - "Cache returned a cached failure, should never return anything else"; - - AuthoritativeNameServer server = new AuthoritativeNameServer(serverName); - server.next = serverName.next; - serverName.next = server; - serverName = server; - serverName.update(parent.newRedirectServerAddress(address)); - - nameServerCount++; - } - } - } - } - serverName = serverName.next; - } - } - - private static void cacheUnresolved( - AuthoritativeNameServer server, AuthoritativeDnsServerCache authoritativeCache, EventLoop loop) { - // We still want to cached the unresolved address - server.address = InetSocketAddress.createUnresolved( - server.nsName, DefaultDnsServerAddressStreamProvider.DNS_PORT); - - // Cache the server now. - cache(server, authoritativeCache, loop); - } - - private static void cache(AuthoritativeNameServer server, AuthoritativeDnsServerCache cache, EventLoop loop) { - // Cache NS record if not for a root server as we should never cache for root servers. - if (!server.isRootServer()) { - cache.cache(server.domainName, server.address, server.ttl, loop); - } - } - - /** - * Returns {@code true} if empty, {@code false} otherwise. - */ - boolean isEmpty() { - return nameServerCount == 0; - } - - /** - * Creates a new {@link List} which holds the {@link InetSocketAddress}es. - */ - List addressList() { - List addressList = new ArrayList<>(nameServerCount); - - AuthoritativeNameServer server = head; - while (server != null) { - if (server.address != null) { - addressList.add(server.address); - } - server = server.next; - } - return addressList; - } - } - - private static final class AuthoritativeNameServer { - private final int dots; - private final String domainName; - final boolean isCopy; - final String nsName; - - private long ttl; - private InetSocketAddress address; - - AuthoritativeNameServer next; - - AuthoritativeNameServer(int dots, long ttl, String domainName, String nsName) { - this.dots = dots; - this.ttl = ttl; - this.nsName = nsName; - this.domainName = domainName; - isCopy = false; - } - - AuthoritativeNameServer(AuthoritativeNameServer server) { - dots = server.dots; - ttl = server.ttl; - nsName = server.nsName; - domainName = server.domainName; - isCopy = true; - } - - /** - * Returns {@code true} if its a root server. - */ - boolean isRootServer() { - return dots == 1; - } - - /** - * Update the server with the given address and TTL if needed. - */ - void update(InetSocketAddress address, long ttl) { - assert this.address == null || this.address.isUnresolved(); - this.address = address; - this.ttl = min(this.ttl, ttl); - } - - void update(InetSocketAddress address) { - update(address, Long.MAX_VALUE); - } - } -} diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsServerAddressStream.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsServerAddressStream.java deleted file mode 100644 index 7a480b6a6d..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsServerAddressStream.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.resolver.dns; - -import java.net.InetSocketAddress; - -/** - * An infinite stream of DNS server addresses. - */ -public interface DnsServerAddressStream { - /** - * Retrieves the next DNS server address from the stream. - */ - InetSocketAddress next(); - - /** - * Get the number of times {@link #next()} will return a distinct element before repeating or terminating. - * @return the number of times {@link #next()} will return a distinct element before repeating or terminating. - */ - int size(); - - /** - * Duplicate this object. The result of this should be able to be independently iterated over via {@link #next()}. - *

    - * Note that {@link #clone()} isn't used because it may make sense for some implementations to have the following - * relationship {@code x.duplicate() == x}. - * @return A duplicate of this object. - */ - DnsServerAddressStream duplicate(); -} diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsServerAddressStreamProvider.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsServerAddressStreamProvider.java deleted file mode 100644 index 27fee4e4da..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsServerAddressStreamProvider.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -/** - * Provides an opportunity to override which {@link DnsServerAddressStream} is used to resolve a specific hostname. - *

    - * For example this can be used to represent /etc/resolv.conf and - * - * /etc/resolver. - */ -public interface DnsServerAddressStreamProvider { - /** - * Ask this provider for the name servers to query for {@code hostname}. - * @param hostname The hostname for which to lookup the DNS server addressed to use. - * If this is the final {@link DnsServerAddressStreamProvider} to be queried then generally empty - * string or {@code '.'} correspond to the default {@link DnsServerAddressStream}. - * @return The {@link DnsServerAddressStream} which should be used to resolve {@code hostname}. - */ - DnsServerAddressStream nameServerAddressStream(String hostname); -} 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 deleted file mode 100644 index 8f87b14bb1..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsServerAddressStreamProviders.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; -import java.lang.reflect.InvocationTargetException; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; - -/** - * Utility methods related to {@link DnsServerAddressStreamProvider}. - */ -public final class DnsServerAddressStreamProviders { - - private static final InternalLogger LOGGER = - InternalLoggerFactory.getInstance(DnsServerAddressStreamProviders.class); - private static final MethodHandle STREAM_PROVIDER_CONSTRUCTOR_HANDLE; - private static final String MACOS_PROVIDER_CLASS_NAME = - "io.netty.resolver.dns.macos.MacOSDnsServerAddressStreamProvider"; - - static { - MethodHandle constructorHandle = null; - if (PlatformDependent.isOsx()) { - try { - // As MacOSDnsServerAddressStreamProvider is contained in another jar which depends on this jar - // we use reflection to use it if its on the classpath. - Object maybeProvider = AccessController.doPrivileged((PrivilegedAction) () -> { - try { - return Class.forName( - MACOS_PROVIDER_CLASS_NAME, - true, - DnsServerAddressStreamProviders.class.getClassLoader()); - } catch (Throwable cause) { - return cause; - } - }); - if (maybeProvider instanceof Class) { - @SuppressWarnings("unchecked") - Class providerClass = - (Class) maybeProvider; - MethodHandles.Lookup lookup = MethodHandles.lookup(); - constructorHandle = lookup.findConstructor(providerClass, MethodType.methodType(void.class)); - constructorHandle.invoke(); // ctor ensures availability - LOGGER.debug("{}: available", MACOS_PROVIDER_CLASS_NAME); - } else { - throw (Throwable) maybeProvider; - } - } catch (ClassNotFoundException cause) { - LOGGER.warn("Can not find {} in the classpath, fallback to system defaults. This may result in " - + "incorrect DNS resolutions on MacOS.", MACOS_PROVIDER_CLASS_NAME); - } catch (Throwable cause) { - LOGGER.error("Unable to load {}, fallback to system defaults. This may result in " - + "incorrect DNS resolutions on MacOS.", MACOS_PROVIDER_CLASS_NAME, cause); - constructorHandle = null; - } - } - STREAM_PROVIDER_CONSTRUCTOR_HANDLE = constructorHandle; - } - - private DnsServerAddressStreamProviders() { - } - - /** - * A {@link DnsServerAddressStreamProvider} which inherits the DNS servers from your local host's configuration. - *

    - * Note that only macOS and Linux are currently supported. - * @return A {@link DnsServerAddressStreamProvider} which inherits the DNS servers from your local host's - * configuration. - */ - public static DnsServerAddressStreamProvider platformDefault() { - if (STREAM_PROVIDER_CONSTRUCTOR_HANDLE != null) { - try { - return (DnsServerAddressStreamProvider) STREAM_PROVIDER_CONSTRUCTOR_HANDLE.invoke(); - } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) { - // ignore - } catch (Throwable cause) { - PlatformDependent.throwException(cause); - } - } - return unixDefault(); - } - - public static DnsServerAddressStreamProvider unixDefault() { - return DefaultProviderHolder.DEFAULT_DNS_SERVER_ADDRESS_STREAM_PROVIDER; - } - - // We use a Holder class to only initialize DEFAULT_DNS_SERVER_ADDRESS_STREAM_PROVIDER if we really - // need it. - private static final class DefaultProviderHolder { - // 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. - 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. - return PlatformDependent.isWindows() ? DefaultDnsServerAddressStreamProvider.INSTANCE : - UnixResolverDnsServerAddressStreamProvider.parseSilently(); - } - }; - } -} 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 deleted file mode 100644 index 317951e634..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsServerAddresses.java +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.resolver.dns; - -import static java.util.Objects.requireNonNull; -import static io.netty.util.internal.ObjectUtil.checkNonEmpty; - -import java.net.InetSocketAddress; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -/** - * Provides an infinite sequence of DNS server addresses to {@link DnsNameResolver}. - */ -@SuppressWarnings("IteratorNextCanNotThrowNoSuchElementException") -public abstract class DnsServerAddresses { - /** - * @deprecated Use {@link DefaultDnsServerAddressStreamProvider#defaultAddressList()}. - *

    - * Returns the list of the system DNS server addresses. If it failed to retrieve the list of the system DNS server - * addresses from the environment, it will return {@code "8.8.8.8"} and {@code "8.8.4.4"}, the addresses of the - * Google public DNS servers. - */ - @Deprecated - public static List defaultAddressList() { - return DefaultDnsServerAddressStreamProvider.defaultAddressList(); - } - - /** - * @deprecated Use {@link DefaultDnsServerAddressStreamProvider#defaultAddresses()}. - *

    - * Returns the {@link DnsServerAddresses} that yields the system DNS server addresses sequentially. If it failed to - * retrieve the list of the system DNS server addresses from the environment, it will use {@code "8.8.8.8"} and - * {@code "8.8.4.4"}, the addresses of the Google public DNS servers. - *

    - * This method has the same effect with the following code: - *

    -     * DnsServerAddresses.sequential(DnsServerAddresses.defaultAddressList());
    -     * 
    - *

    - */ - @Deprecated - public static DnsServerAddresses defaultAddresses() { - return DefaultDnsServerAddressStreamProvider.defaultAddresses(); - } - - /** - * Returns the {@link DnsServerAddresses} that yields the specified {@code addresses} sequentially. Once the - * last address is yielded, it will start again from the first address. - */ - public static DnsServerAddresses sequential(Iterable addresses) { - return sequential0(sanitize(addresses)); - } - - /** - * Returns the {@link DnsServerAddresses} that yields the specified {@code addresses} sequentially. Once the - * last address is yielded, it will start again from the first address. - */ - public static DnsServerAddresses sequential(InetSocketAddress... addresses) { - return sequential0(sanitize(addresses)); - } - - private static DnsServerAddresses sequential0(final List addresses) { - if (addresses.size() == 1) { - return singleton(addresses.get(0)); - } - - return new DefaultDnsServerAddresses("sequential", addresses) { - @Override - public DnsServerAddressStream stream() { - return new SequentialDnsServerAddressStream(addresses, 0); - } - }; - } - - /** - * Returns the {@link DnsServerAddresses} that yields the specified {@code address} in a shuffled order. Once all - * addresses are yielded, the addresses are shuffled again. - */ - public static DnsServerAddresses shuffled(Iterable addresses) { - return shuffled0(sanitize(addresses)); - } - - /** - * Returns the {@link DnsServerAddresses} that yields the specified {@code addresses} in a shuffled order. Once all - * addresses are yielded, the addresses are shuffled again. - */ - public static DnsServerAddresses shuffled(InetSocketAddress... addresses) { - return shuffled0(sanitize(addresses)); - } - - private static DnsServerAddresses shuffled0(List addresses) { - if (addresses.size() == 1) { - return singleton(addresses.get(0)); - } - - return new DefaultDnsServerAddresses("shuffled", addresses) { - @Override - public DnsServerAddressStream stream() { - return new ShuffledDnsServerAddressStream(addresses); - } - }; - } - - /** - * Returns the {@link DnsServerAddresses} that yields the specified {@code addresses} in a rotational sequential - * order. It is similar to {@link #sequential(Iterable)}, but each {@link DnsServerAddressStream} starts from - * a different starting point. For example, the first {@link #stream()} will start from the first address, the - * second one will start from the second address, and so on. - */ - public static DnsServerAddresses rotational(Iterable addresses) { - return rotational0(sanitize(addresses)); - } - - /** - * Returns the {@link DnsServerAddresses} that yields the specified {@code addresses} in a rotational sequential - * order. It is similar to {@link #sequential(Iterable)}, but each {@link DnsServerAddressStream} starts from - * a different starting point. For example, the first {@link #stream()} will start from the first address, the - * second one will start from the second address, and so on. - */ - public static DnsServerAddresses rotational(InetSocketAddress... addresses) { - return rotational0(sanitize(addresses)); - } - - private static DnsServerAddresses rotational0(List addresses) { - if (addresses.size() == 1) { - return singleton(addresses.get(0)); - } - - return new RotationalDnsServerAddresses(addresses); - } - - /** - * Returns the {@link DnsServerAddresses} that yields only a single {@code address}. - */ - public static DnsServerAddresses singleton(final InetSocketAddress address) { - requireNonNull(address, "address"); - if (address.isUnresolved()) { - throw new IllegalArgumentException("cannot use an unresolved DNS server address: " + address); - } - - return new SingletonDnsServerAddresses(address); - } - - private static List sanitize(Iterable addresses) { - requireNonNull(addresses, "addresses"); - - final List list; - if (addresses instanceof Collection) { - list = new ArrayList<>(((Collection) addresses).size()); - } else { - list = new ArrayList<>(4); - } - - for (InetSocketAddress a : addresses) { - if (a == null) { - break; - } - if (a.isUnresolved()) { - throw new IllegalArgumentException("cannot use an unresolved DNS server address: " + a); - } - list.add(a); - } - - return checkNonEmpty(list, "list"); - } - - private static List sanitize(InetSocketAddress[] addresses) { - requireNonNull(addresses, "addresses"); - - List list = new ArrayList<>(addresses.length); - for (InetSocketAddress a: addresses) { - if (a == null) { - break; - } - if (a.isUnresolved()) { - throw new IllegalArgumentException("cannot use an unresolved DNS server address: " + a); - } - list.add(a); - } - - if (list.isEmpty()) { - return DefaultDnsServerAddressStreamProvider.defaultAddressList(); - } - - return list; - } - - /** - * Starts a new infinite stream of DNS server addresses. This method is invoked by {@link DnsNameResolver} on every - * uncached {@link DnsNameResolver#resolve(String)}or {@link DnsNameResolver#resolveAll(String)}. - */ - public abstract DnsServerAddressStream stream(); -} diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/InflightNameResolver.java b/resolver-dns/src/main/java/io/netty/resolver/dns/InflightNameResolver.java deleted file mode 100644 index bdd671d8fe..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/InflightNameResolver.java +++ /dev/null @@ -1,121 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.resolver.dns; - -import io.netty.resolver.NameResolver; -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; -import io.netty.util.internal.StringUtil; - -import java.util.List; -import java.util.concurrent.ConcurrentMap; - -import static java.util.Objects.requireNonNull; - -// FIXME(trustin): Find a better name and move it to the 'resolver' module. -final class InflightNameResolver implements NameResolver { - - private final EventExecutor executor; - private final NameResolver delegate; - private final ConcurrentMap> resolvesInProgress; - private final ConcurrentMap>> resolveAllsInProgress; - - InflightNameResolver(EventExecutor executor, NameResolver delegate, - ConcurrentMap> resolvesInProgress, - ConcurrentMap>> resolveAllsInProgress) { - - this.executor = requireNonNull(executor, "executor"); - this.delegate = requireNonNull(delegate, "delegate"); - this.resolvesInProgress = requireNonNull(resolvesInProgress, "resolvesInProgress"); - this.resolveAllsInProgress = requireNonNull(resolveAllsInProgress, "resolveAllsInProgress"); - } - - @Override - public Future resolve(String inetHost) { - return resolve(inetHost, executor.newPromise()); - } - - @Override - public Future> resolveAll(String inetHost) { - return resolveAll(inetHost, executor.newPromise()); - } - - @Override - public void close() { - delegate.close(); - } - - @Override - public Future resolve(String inetHost, Promise promise) { - return resolve(resolvesInProgress, inetHost, promise, false); - } - - @Override - public Future> resolveAll(String inetHost, Promise> promise) { - return resolve(resolveAllsInProgress, inetHost, promise, true); - } - - private Future resolve( - final ConcurrentMap> resolveMap, - final String inetHost, final Promise promise, boolean resolveAll) { - - final Promise earlyPromise = resolveMap.putIfAbsent(inetHost, promise); - if (earlyPromise != null) { - // Name resolution for the specified inetHost is in progress already. - Future earlyFuture = promise.asFuture(); - if (earlyFuture.isDone()) { - transferResult(earlyFuture, promise); - } else { - earlyFuture.addListener(f -> transferResult(f, promise)); - } - } else { - try { - if (resolveAll) { - @SuppressWarnings("unchecked") - final Promise> castPromise = (Promise>) promise; // U is List - delegate.resolveAll(inetHost, castPromise); - } else { - @SuppressWarnings("unchecked") - final Promise castPromise = (Promise) promise; // U is T - delegate.resolve(inetHost, castPromise); - } - } finally { - if (promise.isDone()) { - resolveMap.remove(inetHost); - } else { - promise.asFuture().addListener(f -> resolveMap.remove(inetHost)); - } - } - } - - return promise.asFuture(); - } - - private static void transferResult(Future src, Promise dst) { - if (src.isSuccess()) { - dst.trySuccess(src.getNow()); - } else { - dst.tryFailure(src.cause()); - } - } - - @Override - public String toString() { - return StringUtil.simpleClassName(this) + '(' + delegate + ')'; - } -} diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/LoggingDnsQueryLifeCycleObserverFactory.java b/resolver-dns/src/main/java/io/netty/resolver/dns/LoggingDnsQueryLifeCycleObserverFactory.java deleted file mode 100644 index ba55c1070b..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/LoggingDnsQueryLifeCycleObserverFactory.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.handler.codec.dns.DnsQuestion; -import io.netty.handler.logging.LogLevel; -import io.netty.util.internal.logging.InternalLogLevel; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import static java.util.Objects.requireNonNull; - -/** - * A {@link DnsQueryLifecycleObserverFactory} that enables detailed logging in the {@link DnsNameResolver}. - *

    - * When {@linkplain DnsNameResolverBuilder#dnsQueryLifecycleObserverFactory(DnsQueryLifecycleObserverFactory) - * configured on the resolver}, detailed trace information will be generated so that it is easier to understand the - * cause of resolution failure. - */ -public final class LoggingDnsQueryLifeCycleObserverFactory implements DnsQueryLifecycleObserverFactory { - private static final InternalLogger DEFAULT_LOGGER = - InternalLoggerFactory.getInstance(LoggingDnsQueryLifeCycleObserverFactory.class); - private final InternalLogger logger; - private final InternalLogLevel level; - - /** - * Create {@link DnsQueryLifecycleObserver} instances that log events at the default {@link LogLevel#DEBUG} level. - */ - public LoggingDnsQueryLifeCycleObserverFactory() { - this(LogLevel.DEBUG); - } - - /** - * Create {@link DnsQueryLifecycleObserver} instances that log events at the given log level. - * @param level The log level to use for logging resolver events. - */ - public LoggingDnsQueryLifeCycleObserverFactory(LogLevel level) { - this.level = checkAndConvertLevel(level); - logger = DEFAULT_LOGGER; - } - - /** - * Create {@link DnsQueryLifecycleObserver} instances that log events to a logger with the given class context, - * at the given log level. - * @param classContext The class context for the logger to use. - * @param level The log level to use for logging resolver events. - */ - public LoggingDnsQueryLifeCycleObserverFactory(Class classContext, LogLevel level) { - this.level = checkAndConvertLevel(level); - logger = InternalLoggerFactory.getInstance(requireNonNull(classContext, "classContext")); - } - - /** - * Create {@link DnsQueryLifecycleObserver} instances that log events to a logger with the given name context, - * at the given log level. - * @param name The name for the logger to use. - * @param level The log level to use for logging resolver events. - */ - public LoggingDnsQueryLifeCycleObserverFactory(String name, LogLevel level) { - this.level = checkAndConvertLevel(level); - logger = InternalLoggerFactory.getInstance(requireNonNull(name, "name")); - } - - private static InternalLogLevel checkAndConvertLevel(LogLevel level) { - return requireNonNull(level, "level").toInternalLevel(); - } - - @Override - public DnsQueryLifecycleObserver newDnsQueryLifecycleObserver(DnsQuestion question) { - return new LoggingDnsQueryLifecycleObserver(question, logger, level); - } -} diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/LoggingDnsQueryLifecycleObserver.java b/resolver-dns/src/main/java/io/netty/resolver/dns/LoggingDnsQueryLifecycleObserver.java deleted file mode 100644 index 101be0c90a..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/LoggingDnsQueryLifecycleObserver.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.handler.codec.dns.DnsQuestion; -import io.netty.handler.codec.dns.DnsResponseCode; -import io.netty.util.concurrent.Future; -import io.netty.util.internal.logging.InternalLogLevel; -import io.netty.util.internal.logging.InternalLogger; - -import java.net.InetSocketAddress; -import java.util.List; - -import static java.util.Objects.requireNonNull; - -final class LoggingDnsQueryLifecycleObserver implements DnsQueryLifecycleObserver { - private final InternalLogger logger; - private final InternalLogLevel level; - private final DnsQuestion question; - private InetSocketAddress dnsServerAddress; - - LoggingDnsQueryLifecycleObserver(DnsQuestion question, InternalLogger logger, InternalLogLevel level) { - this.question = requireNonNull(question, "question"); - this.logger = requireNonNull(logger, "logger"); - this.level = requireNonNull(level, "level"); - } - - @Override - public void queryWritten(InetSocketAddress dnsServerAddress, Future future) { - this.dnsServerAddress = dnsServerAddress; - } - - @Override - public void queryCancelled(int queriesRemaining) { - if (dnsServerAddress != null) { - logger.log(level, "from {} : {} cancelled with {} queries remaining", dnsServerAddress, question, - queriesRemaining); - } else { - logger.log(level, "{} query never written and cancelled with {} queries remaining", question, - queriesRemaining); - } - } - - @Override - public DnsQueryLifecycleObserver queryRedirected(List nameServers) { - logger.log(level, "from {} : {} redirected", dnsServerAddress, question); - return this; - } - - @Override - public DnsQueryLifecycleObserver queryCNAMEd(DnsQuestion cnameQuestion) { - logger.log(level, "from {} : {} CNAME question {}", dnsServerAddress, question, cnameQuestion); - return this; - } - - @Override - public DnsQueryLifecycleObserver queryNoAnswer(DnsResponseCode code) { - logger.log(level, "from {} : {} no answer {}", dnsServerAddress, question, code); - return this; - } - - @Override - public void queryFailed(Throwable cause) { - if (dnsServerAddress != null) { - logger.log(level, "from {} : {} failure", dnsServerAddress, question, cause); - } else { - logger.log(level, "{} query never written and failed", question, cause); - } - } - - @Override - public void querySucceed() { - } -} diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/MultiDnsServerAddressStreamProvider.java b/resolver-dns/src/main/java/io/netty/resolver/dns/MultiDnsServerAddressStreamProvider.java deleted file mode 100644 index 5d16446641..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/MultiDnsServerAddressStreamProvider.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import java.util.List; - -/** - * A {@link DnsServerAddressStreamProvider} which iterates through a collection of - * {@link DnsServerAddressStreamProvider} until the first non-{@code null} result is found. - */ -public final class MultiDnsServerAddressStreamProvider implements DnsServerAddressStreamProvider { - private final DnsServerAddressStreamProvider[] providers; - - /** - * Create a new instance. - * @param providers The providers to use for DNS resolution. They will be queried in order. - */ - public MultiDnsServerAddressStreamProvider(List providers) { - this.providers = providers.toArray(new DnsServerAddressStreamProvider[0]); - } - - /** - * Create a new instance. - * @param providers The providers to use for DNS resolution. They will be queried in order. - */ - public MultiDnsServerAddressStreamProvider(DnsServerAddressStreamProvider... providers) { - this.providers = providers.clone(); - } - - @Override - public DnsServerAddressStream nameServerAddressStream(String hostname) { - for (DnsServerAddressStreamProvider provider : providers) { - DnsServerAddressStream stream = provider.nameServerAddressStream(hostname); - if (stream != null) { - return stream; - } - } - return null; - } -} diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/NameServerComparator.java b/resolver-dns/src/main/java/io/netty/resolver/dns/NameServerComparator.java deleted file mode 100644 index 0ff4d4daf7..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/NameServerComparator.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import static java.util.Objects.requireNonNull; - -import java.io.Serializable; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.util.Comparator; -import java.util.List; - -/** - * Special {@link Comparator} implementation to sort the nameservers to use when follow redirects. - * - * This implementation follows all the semantics listed in the - * Comparator apidocs - * with the limitation that {@link InetSocketAddress#equals(Object)} will not result in the same return value as - * {@link #compare(InetSocketAddress, InetSocketAddress)}. This is completely fine as this should only be used - * to sort {@link List}s. - */ -public final class NameServerComparator implements Comparator, Serializable { - - private static final long serialVersionUID = 8372151874317596185L; - - private final Class preferredAddressType; - - public NameServerComparator(Class preferredAddressType) { - this.preferredAddressType = requireNonNull(preferredAddressType, "preferredAddressType"); - } - - @Override - public int compare(InetSocketAddress addr1, InetSocketAddress addr2) { - if (addr1.equals(addr2)) { - return 0; - } - if (!addr1.isUnresolved() && !addr2.isUnresolved()) { - if (addr1.getAddress().getClass() == addr2.getAddress().getClass()) { - return 0; - } - return preferredAddressType.isAssignableFrom(addr1.getAddress().getClass()) ? -1 : 1; - } - if (addr1.isUnresolved() && addr2.isUnresolved()) { - return 0; - } - return addr1.isUnresolved() ? 1 : -1; - } -} diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/NoopAuthoritativeDnsServerCache.java b/resolver-dns/src/main/java/io/netty/resolver/dns/NoopAuthoritativeDnsServerCache.java deleted file mode 100644 index 9266d627ff..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/NoopAuthoritativeDnsServerCache.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.channel.EventLoop; - -import java.net.InetSocketAddress; - -/** - * A noop {@link AuthoritativeDnsServerCache} that actually never caches anything. - */ -public final class NoopAuthoritativeDnsServerCache implements AuthoritativeDnsServerCache { - public static final NoopAuthoritativeDnsServerCache INSTANCE = new NoopAuthoritativeDnsServerCache(); - - private NoopAuthoritativeDnsServerCache() { } - - @Override - public DnsServerAddressStream get(String hostname) { - return null; - } - - @Override - public void cache(String hostname, InetSocketAddress address, long originalTtl, EventLoop loop) { - // NOOP - } - - @Override - public void clear() { - // NOOP - } - - @Override - public boolean clear(String hostname) { - return false; - } -} diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/NoopDnsCache.java b/resolver-dns/src/main/java/io/netty/resolver/dns/NoopDnsCache.java deleted file mode 100644 index ec1d8ddea5..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/NoopDnsCache.java +++ /dev/null @@ -1,90 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.channel.EventLoop; -import io.netty.handler.codec.dns.DnsRecord; - -import java.net.InetAddress; -import java.util.Collections; -import java.util.List; - -/** - * A noop DNS cache that actually never caches anything. - */ -public final class NoopDnsCache implements DnsCache { - - public static final NoopDnsCache INSTANCE = new NoopDnsCache(); - - /** - * Private singleton constructor. - */ - private NoopDnsCache() { - } - - @Override - public void clear() { - } - - @Override - public boolean clear(String hostname) { - return false; - } - - @Override - public List get(String hostname, DnsRecord[] additionals) { - return Collections.emptyList(); - } - - @Override - public DnsCacheEntry cache(String hostname, DnsRecord[] additional, - InetAddress address, long originalTtl, EventLoop loop) { - return new NoopDnsCacheEntry(address); - } - - @Override - public DnsCacheEntry cache(String hostname, DnsRecord[] additional, Throwable cause, EventLoop loop) { - return null; - } - - @Override - public String toString() { - return NoopDnsCache.class.getSimpleName(); - } - - private static final class NoopDnsCacheEntry implements DnsCacheEntry { - private final InetAddress address; - - NoopDnsCacheEntry(InetAddress address) { - this.address = address; - } - - @Override - public InetAddress address() { - return address; - } - - @Override - public Throwable cause() { - return null; - } - - @Override - public String toString() { - return address.toString(); - } - } -} 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 deleted file mode 100644 index 3efd0840ee..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/NoopDnsCnameCache.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.channel.EventLoop; - -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/main/java/io/netty/resolver/dns/NoopDnsQueryLifecycleObserver.java b/resolver-dns/src/main/java/io/netty/resolver/dns/NoopDnsQueryLifecycleObserver.java deleted file mode 100644 index c4583240e5..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/NoopDnsQueryLifecycleObserver.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.handler.codec.dns.DnsQuestion; -import io.netty.handler.codec.dns.DnsResponseCode; -import io.netty.util.concurrent.Future; - -import java.net.InetSocketAddress; -import java.util.List; - -final class NoopDnsQueryLifecycleObserver implements DnsQueryLifecycleObserver { - static final NoopDnsQueryLifecycleObserver INSTANCE = new NoopDnsQueryLifecycleObserver(); - - private NoopDnsQueryLifecycleObserver() { - } - - @Override - public void queryWritten(InetSocketAddress dnsServerAddress, Future future) { - } - - @Override - public void queryCancelled(int queriesRemaining) { - } - - @Override - public DnsQueryLifecycleObserver queryRedirected(List nameServers) { - return this; - } - - @Override - public DnsQueryLifecycleObserver queryCNAMEd(DnsQuestion cnameQuestion) { - return this; - } - - @Override - public DnsQueryLifecycleObserver queryNoAnswer(DnsResponseCode code) { - return this; - } - - @Override - public void queryFailed(Throwable cause) { - } - - @Override - public void querySucceed() { - } -} diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/NoopDnsQueryLifecycleObserverFactory.java b/resolver-dns/src/main/java/io/netty/resolver/dns/NoopDnsQueryLifecycleObserverFactory.java deleted file mode 100644 index f09d6981d3..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/NoopDnsQueryLifecycleObserverFactory.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.handler.codec.dns.DnsQuestion; - -public final class NoopDnsQueryLifecycleObserverFactory implements DnsQueryLifecycleObserverFactory { - public static final NoopDnsQueryLifecycleObserverFactory INSTANCE = new NoopDnsQueryLifecycleObserverFactory(); - - private NoopDnsQueryLifecycleObserverFactory() { - } - - @Override - public DnsQueryLifecycleObserver newDnsQueryLifecycleObserver(DnsQuestion question) { - return NoopDnsQueryLifecycleObserver.INSTANCE; - } -} diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/PreferredAddressTypeComparator.java b/resolver-dns/src/main/java/io/netty/resolver/dns/PreferredAddressTypeComparator.java deleted file mode 100644 index 17dfa030a3..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/PreferredAddressTypeComparator.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.channel.socket.InternetProtocolFamily; - -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.util.Comparator; - -final class PreferredAddressTypeComparator implements Comparator { - - private static final PreferredAddressTypeComparator IPv4 = new PreferredAddressTypeComparator(Inet4Address.class); - private static final PreferredAddressTypeComparator IPv6 = new PreferredAddressTypeComparator(Inet6Address.class); - - static PreferredAddressTypeComparator comparator(InternetProtocolFamily family) { - switch (family) { - case IPv4: - return IPv4; - case IPv6: - return IPv6; - default: - throw new IllegalArgumentException(); - } - } - - private final Class preferredAddressType; - - private PreferredAddressTypeComparator(Class preferredAddressType) { - this.preferredAddressType = preferredAddressType; - } - - @Override - public int compare(InetAddress o1, InetAddress o2) { - if (o1.getClass() == o2.getClass()) { - return 0; - } - return preferredAddressType.isAssignableFrom(o1.getClass()) ? -1 : 1; - } -} diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/RotationalDnsServerAddresses.java b/resolver-dns/src/main/java/io/netty/resolver/dns/RotationalDnsServerAddresses.java deleted file mode 100644 index ff85bf3e87..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/RotationalDnsServerAddresses.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.resolver.dns; - -import java.net.InetSocketAddress; -import java.util.List; -import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; - -final class RotationalDnsServerAddresses extends DefaultDnsServerAddresses { - - private static final AtomicIntegerFieldUpdater startIdxUpdater = - AtomicIntegerFieldUpdater.newUpdater(RotationalDnsServerAddresses.class, "startIdx"); - - @SuppressWarnings("UnusedDeclaration") - private volatile int startIdx; - - RotationalDnsServerAddresses(List addresses) { - super("rotational", addresses); - } - - @Override - public DnsServerAddressStream stream() { - for (;;) { - int curStartIdx = startIdx; - int nextStartIdx = curStartIdx + 1; - if (nextStartIdx >= addresses.size()) { - nextStartIdx = 0; - } - if (startIdxUpdater.compareAndSet(this, curStartIdx, nextStartIdx)) { - return new SequentialDnsServerAddressStream(addresses, curStartIdx); - } - } - } -} diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/RoundRobinDnsAddressResolverGroup.java b/resolver-dns/src/main/java/io/netty/resolver/dns/RoundRobinDnsAddressResolverGroup.java deleted file mode 100644 index d389af7e96..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/RoundRobinDnsAddressResolverGroup.java +++ /dev/null @@ -1,66 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.resolver.dns; - -import io.netty.channel.ChannelFactory; -import io.netty.channel.EventLoop; -import io.netty.channel.socket.DatagramChannel; -import io.netty.resolver.AddressResolver; -import io.netty.resolver.AddressResolverGroup; -import io.netty.resolver.NameResolver; -import io.netty.resolver.RoundRobinInetAddressResolver; - -import java.net.InetAddress; -import java.net.InetSocketAddress; - -/** - * A {@link AddressResolverGroup} of {@link DnsNameResolver}s that supports random selection of destination addresses if - * multiple are provided by the nameserver. This is ideal for use in applications that use a pool of connections, for - * which connecting to a single resolved address would be inefficient. - */ -public class RoundRobinDnsAddressResolverGroup extends DnsAddressResolverGroup { - - public RoundRobinDnsAddressResolverGroup(DnsNameResolverBuilder dnsResolverBuilder) { - super(dnsResolverBuilder); - } - - public RoundRobinDnsAddressResolverGroup( - Class channelType, - DnsServerAddressStreamProvider nameServerProvider) { - super(channelType, nameServerProvider); - } - - public RoundRobinDnsAddressResolverGroup( - ChannelFactory channelFactory, - DnsServerAddressStreamProvider nameServerProvider) { - super(channelFactory, nameServerProvider); - } - - /** - * We need to override this method, not - * {@link #newNameResolver(EventLoop, ChannelFactory, DnsServerAddressStreamProvider)}, - * because we need to eliminate possible caching of {@link io.netty.resolver.NameResolver#resolve} - * by {@link InflightNameResolver} created in - * {@link #newResolver(EventLoop, ChannelFactory, DnsServerAddressStreamProvider)}. - */ - @Override - protected final AddressResolver newAddressResolver(EventLoop eventLoop, - NameResolver resolver) - throws Exception { - return new RoundRobinInetAddressResolver(eventLoop, resolver).asAddressResolver(); - } -} diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/SequentialDnsServerAddressStream.java b/resolver-dns/src/main/java/io/netty/resolver/dns/SequentialDnsServerAddressStream.java deleted file mode 100644 index 55830734d3..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/SequentialDnsServerAddressStream.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.resolver.dns; - -import java.net.InetSocketAddress; -import java.util.Collection; -import java.util.List; - -final class SequentialDnsServerAddressStream implements DnsServerAddressStream { - - private final List addresses; - private int i; - - SequentialDnsServerAddressStream(List addresses, int startIdx) { - this.addresses = addresses; - i = startIdx; - } - - @Override - public InetSocketAddress next() { - int i = this.i; - InetSocketAddress next = addresses.get(i); - if (++ i < addresses.size()) { - this.i = i; - } else { - this.i = 0; - } - return next; - } - - @Override - public int size() { - return addresses.size(); - } - - @Override - public SequentialDnsServerAddressStream duplicate() { - return new SequentialDnsServerAddressStream(addresses, i); - } - - @Override - public String toString() { - return toString("sequential", i, addresses); - } - - static String toString(String type, int index, Collection addresses) { - final StringBuilder buf = new StringBuilder(type.length() + 2 + addresses.size() * 16); - buf.append(type).append("(index: ").append(index); - buf.append(", addrs: ("); - for (InetSocketAddress a: addresses) { - buf.append(a).append(", "); - } - - buf.setLength(buf.length() - 2); - buf.append("))"); - - return buf.toString(); - } -} diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/SequentialDnsServerAddressStreamProvider.java b/resolver-dns/src/main/java/io/netty/resolver/dns/SequentialDnsServerAddressStreamProvider.java deleted file mode 100644 index ac769096f8..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/SequentialDnsServerAddressStreamProvider.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import java.net.InetSocketAddress; - -import static io.netty.resolver.dns.DnsServerAddresses.sequential; - -/** - * A {@link DnsServerAddressStreamProvider} which is backed by a sequential list of DNS servers. - */ -public final class SequentialDnsServerAddressStreamProvider extends UniSequentialDnsServerAddressStreamProvider { - /** - * Create a new instance. - * @param addresses The addresses which will be be returned in sequential order via - * {@link #nameServerAddressStream(String)} - */ - public SequentialDnsServerAddressStreamProvider(InetSocketAddress... addresses) { - super(sequential(addresses)); - } - - /** - * Create a new instance. - * @param addresses The addresses which will be be returned in sequential order via - * {@link #nameServerAddressStream(String)} - */ - public SequentialDnsServerAddressStreamProvider(Iterable addresses) { - super(sequential(addresses)); - } -} diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/ShuffledDnsServerAddressStream.java b/resolver-dns/src/main/java/io/netty/resolver/dns/ShuffledDnsServerAddressStream.java deleted file mode 100644 index 6187612c17..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/ShuffledDnsServerAddressStream.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.resolver.dns; - -import java.net.InetSocketAddress; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.ThreadLocalRandom; - -final class ShuffledDnsServerAddressStream implements DnsServerAddressStream { - - private final List addresses; - private int i; - - /** - * Create a new instance. - * @param addresses The addresses are not cloned. It is assumed the caller has cloned this array or otherwise will - * not modify the contents. - */ - ShuffledDnsServerAddressStream(List addresses) { - this.addresses = addresses; - - shuffle(); - } - - private ShuffledDnsServerAddressStream(List addresses, int startIdx) { - this.addresses = addresses; - i = startIdx; - } - - private void shuffle() { - Collections.shuffle(addresses, ThreadLocalRandom.current()); - } - - @Override - public InetSocketAddress next() { - int i = this.i; - InetSocketAddress next = addresses.get(i); - if (++ i < addresses.size()) { - this.i = i; - } else { - this.i = 0; - shuffle(); - } - return next; - } - - @Override - public int size() { - return addresses.size(); - } - - @Override - public ShuffledDnsServerAddressStream duplicate() { - return new ShuffledDnsServerAddressStream(addresses, i); - } - - @Override - public String toString() { - return SequentialDnsServerAddressStream.toString("shuffled", i, addresses); - } -} diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/SingletonDnsServerAddressStreamProvider.java b/resolver-dns/src/main/java/io/netty/resolver/dns/SingletonDnsServerAddressStreamProvider.java deleted file mode 100644 index 6bb7f3da77..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/SingletonDnsServerAddressStreamProvider.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import java.net.InetSocketAddress; - -/** - * A {@link DnsServerAddressStreamProvider} which always uses a single DNS server for resolution. - */ -public final class SingletonDnsServerAddressStreamProvider extends UniSequentialDnsServerAddressStreamProvider { - /** - * Create a new instance. - * @param address The singleton address to use for every DNS resolution. - */ - public SingletonDnsServerAddressStreamProvider(final InetSocketAddress address) { - super(DnsServerAddresses.singleton(address)); - } -} diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/SingletonDnsServerAddresses.java b/resolver-dns/src/main/java/io/netty/resolver/dns/SingletonDnsServerAddresses.java deleted file mode 100644 index 92358d4453..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/SingletonDnsServerAddresses.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.resolver.dns; - -import java.net.InetSocketAddress; - -final class SingletonDnsServerAddresses extends DnsServerAddresses { - - private final InetSocketAddress address; - - private final DnsServerAddressStream stream = new DnsServerAddressStream() { - @Override - public InetSocketAddress next() { - return address; - } - - @Override - public int size() { - return 1; - } - - @Override - public DnsServerAddressStream duplicate() { - return this; - } - - @Override - public String toString() { - return SingletonDnsServerAddresses.this.toString(); - } - }; - - SingletonDnsServerAddresses(InetSocketAddress address) { - this.address = address; - } - - @Override - public DnsServerAddressStream stream() { - return stream; - } - - @Override - public String toString() { - return "singleton(" + address + ")"; - } -} diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/TcpDnsQueryContext.java b/resolver-dns/src/main/java/io/netty/resolver/dns/TcpDnsQueryContext.java deleted file mode 100644 index c931a928f1..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/TcpDnsQueryContext.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.channel.AddressedEnvelope; -import io.netty.channel.Channel; -import io.netty.handler.codec.dns.DefaultDnsQuery; -import io.netty.handler.codec.dns.DnsQuery; -import io.netty.handler.codec.dns.DnsQuestion; -import io.netty.handler.codec.dns.DnsRecord; -import io.netty.handler.codec.dns.DnsResponse; -import io.netty.util.concurrent.Promise; - -import java.net.InetSocketAddress; - -final class TcpDnsQueryContext extends DnsQueryContext { - - private final Channel channel; - - TcpDnsQueryContext(DnsNameResolver parent, Channel channel, InetSocketAddress nameServerAddr, DnsQuestion question, - DnsRecord[] additionals, Promise> promise) { - super(parent, nameServerAddr, question, additionals, promise); - this.channel = channel; - } - - @Override - protected DnsQuery newQuery(int id) { - return new DefaultDnsQuery(id); - } - - @Override - protected Channel channel() { - return channel; - } - - @Override - protected String protocol() { - return "TCP"; - } -} diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/UniSequentialDnsServerAddressStreamProvider.java b/resolver-dns/src/main/java/io/netty/resolver/dns/UniSequentialDnsServerAddressStreamProvider.java deleted file mode 100644 index 994c4c512a..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/UniSequentialDnsServerAddressStreamProvider.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import static java.util.Objects.requireNonNull; - - -/** - * A {@link DnsServerAddressStreamProvider} which is backed by a single {@link DnsServerAddresses}. - */ -abstract class UniSequentialDnsServerAddressStreamProvider implements DnsServerAddressStreamProvider { - private final DnsServerAddresses addresses; - - UniSequentialDnsServerAddressStreamProvider(DnsServerAddresses addresses) { - this.addresses = requireNonNull(addresses, "addresses"); - } - - @Override - public final DnsServerAddressStream nameServerAddressStream(String hostname) { - return addresses.stream(); - } -} 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 deleted file mode 100644 index cc0ba6ad63..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/UnixResolverDnsServerAddressStreamProvider.java +++ /dev/null @@ -1,405 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.util.NetUtil; -import io.netty.util.internal.SocketUtils; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.net.InetSocketAddress; -import java.util.ArrayList; -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.StringUtil.indexOfNonWhiteSpace; -import static io.netty.util.internal.StringUtil.indexOfWhiteSpace; - -import static java.util.Objects.requireNonNull; - -/** - * Able to parse files such as /etc/resolv.conf and - * - * /etc/resolver to respect the system default domain servers. - */ -public final class UnixResolverDnsServerAddressStreamProvider implements DnsServerAddressStreamProvider { - private static final InternalLogger logger = - InternalLoggerFactory.getInstance(UnixResolverDnsServerAddressStreamProvider.class); - - private static final Pattern WHITESPACE_PATTERN = Pattern.compile("\\s+"); - private static final String RES_OPTIONS = System.getenv("RES_OPTIONS"); - - private static final String ETC_RESOLV_CONF_FILE = "/etc/resolv.conf"; - private static final String ETC_RESOLVER_DIR = "/etc/resolver"; - private static final String NAMESERVER_ROW_LABEL = "nameserver"; - private static final String SORTLIST_ROW_LABEL = "sortlist"; - private static final String OPTIONS_ROW_LABEL = "options "; - private static final String OPTIONS_ROTATE_FLAG = "rotate"; - 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 final DnsServerAddresses defaultNameServerAddresses; - private final Map domainToNameServerStreamMap; - - /** - * Attempt to parse {@code /etc/resolv.conf} and files in the {@code /etc/resolver} directory by default. - * A failure to parse will return {@link DefaultDnsServerAddressStreamProvider}. - */ - static DnsServerAddressStreamProvider parseSilently() { - try { - UnixResolverDnsServerAddressStreamProvider nameServerCache = - new UnixResolverDnsServerAddressStreamProvider(ETC_RESOLV_CONF_FILE, ETC_RESOLVER_DIR); - return nameServerCache.mayOverrideNameServers() ? nameServerCache - : DefaultDnsServerAddressStreamProvider.INSTANCE; - } catch (Exception e) { - if (logger.isDebugEnabled()) { - logger.debug("failed to parse {} and/or {}", ETC_RESOLV_CONF_FILE, ETC_RESOLVER_DIR, e); - } - return DefaultDnsServerAddressStreamProvider.INSTANCE; - } - } - - /** - * Parse a file of the format /etc/resolv.conf which may contain - * the default DNS server to use, and also overrides for individual domains. Also parse list of files of the format - * - * /etc/resolver which may contain multiple files to override the name servers used for multiple domains. - * @param etcResolvConf /etc/resolv.conf. - * @param etcResolverFiles List of files of the format defined in - * - * /etc/resolver. - * @throws IOException If an error occurs while parsing the input files. - */ - public UnixResolverDnsServerAddressStreamProvider(File etcResolvConf, File... etcResolverFiles) throws IOException { - Map etcResolvConfMap = parse(requireNonNull(etcResolvConf, "etcResolvConf")); - final boolean useEtcResolverFiles = etcResolverFiles != null && etcResolverFiles.length != 0; - domainToNameServerStreamMap = useEtcResolverFiles ? parse(etcResolverFiles) : etcResolvConfMap; - - DnsServerAddresses defaultNameServerAddresses - = etcResolvConfMap.get(etcResolvConf.getName()); // lgtm[java/dereferenced-value-may-be-null] - if (defaultNameServerAddresses == null) { - Collection values = etcResolvConfMap.values(); - if (values.isEmpty()) { - throw new IllegalArgumentException(etcResolvConf + " didn't provide any name servers"); - } - this.defaultNameServerAddresses = values.iterator().next(); - } else { - this.defaultNameServerAddresses = defaultNameServerAddresses; - } - - if (useEtcResolverFiles) { - domainToNameServerStreamMap.putAll(etcResolvConfMap); - } - } - - /** - * Parse a file of the format /etc/resolv.conf which may contain - * the default DNS server to use, and also overrides for individual domains. Also parse a directory of the format - * - * /etc/resolver which may contain multiple files to override the name servers used for multiple domains. - * @param etcResolvConf /etc/resolv.conf. - * @param etcResolverDir Directory containing files of the format defined in - * - * /etc/resolver. - * @throws IOException If an error occurs while parsing the input files. - */ - public UnixResolverDnsServerAddressStreamProvider(String etcResolvConf, String etcResolverDir) throws IOException { - this(etcResolvConf == null ? null : new File(etcResolvConf), - etcResolverDir == null ? null : new File(etcResolverDir).listFiles()); - } - - @Override - public DnsServerAddressStream nameServerAddressStream(String hostname) { - for (;;) { - int i = hostname.indexOf('.', 1); - if (i < 0 || i == hostname.length() - 1) { - return defaultNameServerAddresses.stream(); - } - - DnsServerAddresses addresses = domainToNameServerStreamMap.get(hostname); - if (addresses != null) { - return addresses.stream(); - } - - hostname = hostname.substring(i + 1); - } - } - - private boolean mayOverrideNameServers() { - return !domainToNameServerStreamMap.isEmpty() || defaultNameServerAddresses.stream().next() != null; - } - - private static Map parse(File... etcResolverFiles) throws IOException { - Map domainToNameServerStreamMap = - new HashMap<>(etcResolverFiles.length << 1); - boolean rotateGlobal = RES_OPTIONS != null && RES_OPTIONS.contains(OPTIONS_ROTATE_FLAG); - for (File etcResolverFile : etcResolverFiles) { - if (!etcResolverFile.isFile()) { - continue; - } - FileReader fr = new FileReader(etcResolverFile); - BufferedReader br = null; - try { - br = new BufferedReader(fr); - List addresses = new ArrayList<>(2); - String domainName = etcResolverFile.getName(); - boolean rotate = rotateGlobal; - int port = DNS_PORT; - String line; - while ((line = br.readLine()) != null) { - line = line.trim(); - try { - char c; - if (line.isEmpty() || (c = line.charAt(0)) == '#' || c == ';') { - continue; - } - if (!rotate && line.startsWith(OPTIONS_ROW_LABEL)) { - rotate = line.contains(OPTIONS_ROTATE_FLAG); - } else if (line.startsWith(NAMESERVER_ROW_LABEL)) { - int i = indexOfNonWhiteSpace(line, NAMESERVER_ROW_LABEL.length()); - if (i < 0) { - throw new IllegalArgumentException("error parsing label " + NAMESERVER_ROW_LABEL + - " in file " + etcResolverFile + ". value: " + line); - } - - String maybeIP; - int x = indexOfWhiteSpace(line, i); - if (x == -1) { - maybeIP = line.substring(i); - } else { - // ignore comments - int idx = indexOfNonWhiteSpace(line, x); - if (idx == -1 || line.charAt(idx) != '#') { - throw new IllegalArgumentException("error parsing label " + NAMESERVER_ROW_LABEL + - " in file " + etcResolverFile + ". value: " + line); - } - maybeIP = line.substring(i, x); - } - - // There may be a port appended onto the IP address so we attempt to extract it. - if (!NetUtil.isValidIpV4Address(maybeIP) && !NetUtil.isValidIpV6Address(maybeIP)) { - i = maybeIP.lastIndexOf('.'); - if (i + 1 >= maybeIP.length()) { - throw new IllegalArgumentException("error parsing label " + NAMESERVER_ROW_LABEL + - " in file " + etcResolverFile + ". invalid IP value: " + line); - } - port = Integer.parseInt(maybeIP.substring(i + 1)); - maybeIP = maybeIP.substring(0, i); - } - addresses.add(SocketUtils.socketAddress(maybeIP, port)); - } else if (line.startsWith(DOMAIN_ROW_LABEL)) { - int i = indexOfNonWhiteSpace(line, DOMAIN_ROW_LABEL.length()); - if (i < 0) { - throw new IllegalArgumentException("error parsing label " + DOMAIN_ROW_LABEL + - " in file " + etcResolverFile + " value: " + line); - } - domainName = line.substring(i); - if (!addresses.isEmpty()) { - putIfAbsent(domainToNameServerStreamMap, domainName, addresses, rotate); - } - addresses = new ArrayList<>(2); - } else if (line.startsWith(PORT_ROW_LABEL)) { - int i = indexOfNonWhiteSpace(line, PORT_ROW_LABEL.length()); - if (i < 0) { - throw new IllegalArgumentException("error parsing label " + PORT_ROW_LABEL + - " in file " + etcResolverFile + " value: " + line); - } - port = Integer.parseInt(line.substring(i)); - } else if (line.startsWith(SORTLIST_ROW_LABEL)) { - logger.info("row type {} not supported. Ignoring line: {}", SORTLIST_ROW_LABEL, line); - } - } catch (IllegalArgumentException e) { - logger.warn("Could not parse entry. Ignoring line: {}", line, e); - } - } - if (!addresses.isEmpty()) { - putIfAbsent(domainToNameServerStreamMap, domainName, addresses, rotate); - } - } finally { - if (br == null) { - fr.close(); - } else { - br.close(); - } - } - } - return domainToNameServerStreamMap; - } - - private static void putIfAbsent(Map domainToNameServerStreamMap, - String domainName, - List addresses, - boolean rotate) { - // TODO(scott): sortlist is being ignored. - DnsServerAddresses addrs = rotate - ? DnsServerAddresses.rotational(addresses) - : DnsServerAddresses.sequential(addresses); - putIfAbsent(domainToNameServerStreamMap, domainName, addrs); - } - - private static void putIfAbsent(Map domainToNameServerStreamMap, - String domainName, - DnsServerAddresses addresses) { - DnsServerAddresses existingAddresses = domainToNameServerStreamMap.put(domainName, addresses); - if (existingAddresses != null) { - domainToNameServerStreamMap.put(domainName, existingAddresses); - if (logger.isDebugEnabled()) { - logger.debug("Domain name {} already maps to addresses {} so new addresses {} will be discarded", - domainName, existingAddresses, addresses); - } - } - } - - /** - * Parse /etc/resolv.conf and return options of interest, namely: - * timeout, attempts and ndots. - * @return The options values provided by /etc/resolve.conf. - * @throws IOException If a failure occurs parsing the file. - */ - static UnixResolverOptions parseEtcResolverOptions() throws IOException { - return parseEtcResolverOptions(new File(ETC_RESOLV_CONF_FILE)); - } - - /** - * Parse a file of the format /etc/resolv.conf and return options - * of interest, namely: timeout, attempts and ndots. - * @param etcResolvConf a file of the format /etc/resolv.conf. - * @return The options values provided by /etc/resolve.conf. - * @throws IOException If a failure occurs parsing the file. - */ - static UnixResolverOptions parseEtcResolverOptions(File etcResolvConf) throws IOException { - UnixResolverOptions.Builder optionsBuilder = UnixResolverOptions.newBuilder(); - - FileReader fr = new FileReader(etcResolvConf); - BufferedReader br = null; - try { - br = new BufferedReader(fr); - String line; - while ((line = br.readLine()) != null) { - if (line.startsWith(OPTIONS_ROW_LABEL)) { - parseResOptions(line.substring(OPTIONS_ROW_LABEL.length()), optionsBuilder); - break; - } - } - } finally { - if (br == null) { - fr.close(); - } else { - br.close(); - } - } - - // amend options - if (RES_OPTIONS != null) { - parseResOptions(RES_OPTIONS, optionsBuilder); - } - - return optionsBuilder.build(); - } - - private static void parseResOptions(String line, UnixResolverOptions.Builder builder) { - String[] opts = WHITESPACE_PATTERN.split(line); - for (String opt : opts) { - try { - if (opt.startsWith("ndots:")) { - builder.setNdots(parseResIntOption(opt, "ndots:")); - } else if (opt.startsWith("attempts:")) { - builder.setAttempts(parseResIntOption(opt, "attempts:")); - } else if (opt.startsWith("timeout:")) { - builder.setTimeout(parseResIntOption(opt, "timeout:")); - } - } catch (NumberFormatException ignore) { - // skip bad int values from resolv.conf to keep value already set in UnixResolverOptions - } - } - } - - private static int parseResIntOption(String opt, String fullLabel) { - String optValue = opt.substring(fullLabel.length()); - return Integer.parseInt(optValue); - } - - /** - * 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) { - // May contain more then one entry, either separated by whitespace or tab. - // See https://linux.die.net/man/5/resolver - String[] domains = WHITESPACE_PATTERN.split(line.substring(i)); - Collections.addAll(searchDomains, domains); - } - } - } - } 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/main/java/io/netty/resolver/dns/UnixResolverOptions.java b/resolver-dns/src/main/java/io/netty/resolver/dns/UnixResolverOptions.java deleted file mode 100644 index 4c86671a9d..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/UnixResolverOptions.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -/** - * Represents options defined in a file of the format etc/resolv.conf. - */ -final class UnixResolverOptions { - - private final int ndots; - private final int timeout; - private final int attempts; - - UnixResolverOptions(int ndots, int timeout, int attempts) { - this.ndots = ndots; - this.timeout = timeout; - this.attempts = attempts; - } - - static UnixResolverOptions.Builder newBuilder() { - return new UnixResolverOptions.Builder(); - } - - /** - * The number of dots which must appear in a name before an initial absolute query is made. - * The default value is {@code 1}. - */ - int ndots() { - return ndots; - } - - /** - * The timeout of each DNS query performed by this resolver (in seconds). - * The default value is {@code 5}. - */ - int timeout() { - return timeout; - } - - /** - * The maximum allowed number of DNS queries to send when resolving a host name. - * The default value is {@code 16}. - */ - int attempts() { - return attempts; - } - - static final class Builder { - - private int ndots = 1; - private int timeout = 5; - private int attempts = 16; - - private Builder() { - } - - void setNdots(int ndots) { - this.ndots = ndots; - } - - void setTimeout(int timeout) { - this.timeout = timeout; - } - - void setAttempts(int attempts) { - this.attempts = attempts; - } - - UnixResolverOptions build() { - return new UnixResolverOptions(ndots, timeout, attempts); - } - } -} diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/package-info.java b/resolver-dns/src/main/java/io/netty/resolver/dns/package-info.java deleted file mode 100644 index c4c42bf1b7..0000000000 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * An alternative to Java's built-in domain name lookup mechanism that resolves a domain name asynchronously, - * which supports the queries of an arbitrary DNS record type as well. - */ -package io.netty.resolver.dns; diff --git a/resolver-dns/src/main/resources/META-INF/native-image/io.netty/resolver-dns/native-image.properties b/resolver-dns/src/main/resources/META-INF/native-image/io.netty/resolver-dns/native-image.properties deleted file mode 100644 index 43af984157..0000000000 --- a/resolver-dns/src/main/resources/META-INF/native-image/io.netty/resolver-dns/native-image.properties +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright 2020 The Netty Project -# -# The Netty Project licenses this file to you under the Apache License, -# version 2.0 (the "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at: -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -Args = --initialize-at-run-time=io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider \ - --initialize-at-run-time=io.netty.resolver.dns.DnsServerAddressStreamProviders$DefaultProviderHolder \ - --initialize-at-run-time=io.netty.resolver.dns.DnsNameResolver \ - --initialize-at-run-time=io.netty.resolver.HostsFileEntriesResolver diff --git a/resolver-dns/src/test/java/io/netty/resolver/dns/DefaultAuthoritativeDnsServerCacheTest.java b/resolver-dns/src/test/java/io/netty/resolver/dns/DefaultAuthoritativeDnsServerCacheTest.java deleted file mode 100644 index 75b64a0879..0000000000 --- a/resolver-dns/src/test/java/io/netty/resolver/dns/DefaultAuthoritativeDnsServerCacheTest.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.EventLoop; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.local.LocalHandler; -import io.netty.channel.nio.NioHandler; -import io.netty.util.NetUtil; -import org.junit.jupiter.api.Test; - -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.util.Comparator; -import java.util.concurrent.Callable; -import java.util.concurrent.TimeUnit; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; - -public class DefaultAuthoritativeDnsServerCacheTest { - - @Test - public void testExpire() throws Throwable { - InetSocketAddress resolved1 = new InetSocketAddress( - InetAddress.getByAddress("ns1", new byte[] { 10, 0, 0, 1 }), 53); - InetSocketAddress resolved2 = new InetSocketAddress( - InetAddress.getByAddress("ns2", new byte[] { 10, 0, 0, 2 }), 53); - EventLoopGroup group = new MultithreadEventLoopGroup(1, LocalHandler.newFactory()); - - try { - EventLoop loop = group.next(); - final DefaultAuthoritativeDnsServerCache cache = new DefaultAuthoritativeDnsServerCache(); - cache.cache("netty.io", resolved1, 1, loop); - cache.cache("netty.io", resolved2, 10000, loop); - - Throwable error = loop.schedule(() -> { - 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 MultithreadEventLoopGroup(1, NioHandler.newFactory()); - - try { - EventLoop loop = group.next(); - final DefaultAuthoritativeDnsServerCache cache = new DefaultAuthoritativeDnsServerCache(); - cache.cache("netty.io", new InetSocketAddress(NetUtil.LOCALHOST, 53), days, loop); - } finally { - group.shutdownGracefully(); - } - } - - @Test - public void testAddMultipleDnsServerForSameHostname() throws Exception { - InetSocketAddress resolved1 = new InetSocketAddress( - InetAddress.getByAddress("ns1", new byte[] { 10, 0, 0, 1 }), 53); - InetSocketAddress resolved2 = new InetSocketAddress( - InetAddress.getByAddress("ns2", new byte[] { 10, 0, 0, 2 }), 53); - EventLoopGroup group = new MultithreadEventLoopGroup(1, LocalHandler.newFactory()); - - try { - EventLoop loop = group.next(); - final DefaultAuthoritativeDnsServerCache cache = new DefaultAuthoritativeDnsServerCache(); - cache.cache("netty.io", resolved1, 100, loop); - cache.cache("netty.io", resolved2, 10000, loop); - - DnsServerAddressStream entries = cache.get("netty.io"); - assertEquals(2, entries.size()); - assertEquals(resolved1, entries.next()); - assertEquals(resolved2, entries.next()); - } finally { - group.shutdownGracefully(); - } - } - - @Test - public void testUnresolvedReplacedByResolved() throws Exception { - InetSocketAddress unresolved = InetSocketAddress.createUnresolved("ns1", 53); - InetSocketAddress resolved1 = new InetSocketAddress( - InetAddress.getByAddress("ns2", new byte[] { 10, 0, 0, 2 }), 53); - InetSocketAddress resolved2 = new InetSocketAddress( - InetAddress.getByAddress("ns1", new byte[] { 10, 0, 0, 1 }), 53); - EventLoopGroup group = new MultithreadEventLoopGroup(1, LocalHandler.newFactory()); - - try { - EventLoop loop = group.next(); - final DefaultAuthoritativeDnsServerCache cache = new DefaultAuthoritativeDnsServerCache(); - cache.cache("netty.io", unresolved, 100, loop); - cache.cache("netty.io", resolved1, 10000, loop); - - DnsServerAddressStream entries = cache.get("netty.io"); - assertEquals(2, entries.size()); - assertEquals(unresolved, entries.next()); - assertEquals(resolved1, entries.next()); - - cache.cache("netty.io", resolved2, 100, loop); - DnsServerAddressStream entries2 = cache.get("netty.io"); - - assertEquals(2, entries2.size()); - assertEquals(resolved2, entries2.next()); - assertEquals(resolved1, entries2.next()); - } finally { - group.shutdownGracefully(); - } - } - - @Test - public void testUseNoComparator() throws Exception { - testUseComparator0(true); - } - - @Test - public void testUseComparator() throws Exception { - testUseComparator0(false); - } - - private static void testUseComparator0(boolean noComparator) throws Exception { - InetSocketAddress unresolved = InetSocketAddress.createUnresolved("ns1", 53); - InetSocketAddress resolved = new InetSocketAddress( - InetAddress.getByAddress("ns2", new byte[] { 10, 0, 0, 2 }), 53); - EventLoopGroup group = new MultithreadEventLoopGroup(1, LocalHandler.newFactory()); - - try { - EventLoop loop = group.next(); - final DefaultAuthoritativeDnsServerCache cache; - - if (noComparator) { - cache = new DefaultAuthoritativeDnsServerCache(10000, 10000, null); - } else { - cache = new DefaultAuthoritativeDnsServerCache(10000, 10000, - (o1, o2) -> { - if (o1.equals(o2)) { - return 0; - } - if (o1.isUnresolved()) { - return 1; - } else { - return -1; - } - }); - } - cache.cache("netty.io", unresolved, 100, loop); - cache.cache("netty.io", resolved, 10000, loop); - - DnsServerAddressStream entries = cache.get("netty.io"); - assertEquals(2, entries.size()); - - if (noComparator) { - assertEquals(unresolved, entries.next()); - assertEquals(resolved, entries.next()); - } else { - assertEquals(resolved, entries.next()); - assertEquals(unresolved, entries.next()); - } - } finally { - group.shutdownGracefully(); - } - } - -} 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 deleted file mode 100644 index 69e1a6f62d..0000000000 --- a/resolver-dns/src/test/java/io/netty/resolver/dns/DefaultDnsCacheTest.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.EventLoop; -import io.netty.channel.EventLoopGroup; - -import io.netty.channel.local.LocalHandler; -import io.netty.channel.nio.NioHandler; -import io.netty.util.NetUtil; -import org.junit.jupiter.api.Test; - -import java.net.InetAddress; -import java.util.List; -import java.util.concurrent.Callable; -import java.util.concurrent.TimeUnit; - - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; - -public class DefaultDnsCacheTest { - - @Test - public void testExpire() throws Throwable { - InetAddress addr1 = InetAddress.getByAddress(new byte[] { 10, 0, 0, 1 }); - InetAddress addr2 = InetAddress.getByAddress(new byte[] { 10, 0, 0, 2 }); - EventLoopGroup group = new MultithreadEventLoopGroup(1, NioHandler.newFactory()); - - try { - EventLoop loop = group.next(); - final DefaultDnsCache cache = new DefaultDnsCache(); - cache.cache("netty.io", null, addr1, 1, loop); - cache.cache("netty.io", null, addr2, 10000, loop); - - Throwable error = loop.schedule(() -> { - try { - assertNull(cache.get("netty.io", null)); - 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 MultithreadEventLoopGroup(1, NioHandler.newFactory()); - - try { - EventLoop loop = group.next(); - final DefaultDnsCache cache = new DefaultDnsCache(); - assertNotNull(cache.cache("netty.io", null, NetUtil.LOCALHOST, days, loop)); - } finally { - group.shutdownGracefully(); - } - } - - @Test - public void testExpireWithToBigMinTTL() { - EventLoopGroup group = new MultithreadEventLoopGroup(1, NioHandler.newFactory()); - - try { - EventLoop loop = group.next(); - final DefaultDnsCache cache = new DefaultDnsCache(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE); - assertNotNull(cache.cache("netty.io", null, NetUtil.LOCALHOST, 100, loop)); - } finally { - group.shutdownGracefully(); - } - } - - @Test - public void testAddMultipleAddressesForSameHostname() 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 MultithreadEventLoopGroup(1, LocalHandler.newFactory()); - - try { - EventLoop loop = group.next(); - final DefaultDnsCache cache = new DefaultDnsCache(); - cache.cache("netty.io", null, addr1, 1, 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); - } finally { - group.shutdownGracefully(); - } - } - - @Test - public void testAddSameAddressForSameHostname() throws Exception { - InetAddress addr1 = InetAddress.getByAddress(new byte[] { 10, 0, 0, 1 }); - EventLoopGroup group = new MultithreadEventLoopGroup(1, LocalHandler.newFactory()); - - try { - EventLoop loop = group.next(); - final DefaultDnsCache cache = new DefaultDnsCache(); - cache.cache("netty.io", null, addr1, 1, loop); - cache.cache("netty.io", null, addr1, 10000, loop); - - List entries = cache.get("netty.io", null); - assertEquals(1, entries.size()); - assertEntry(entries.get(0), addr1); - } finally { - group.shutdownGracefully(); - } - } - - private static void assertEntry(DnsCacheEntry entry, InetAddress address) { - assertEquals(address, entry.address()); - assertNull(entry.cause()); - } - - @Test - public void testCacheFailed() 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 MultithreadEventLoopGroup(1, LocalHandler.newFactory()); - - 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); - - Exception exception = new Exception(); - cache.cache("netty.io", null, exception, loop); - entries = cache.get("netty.io", null); - DnsCacheEntry entry = entries.get(0); - assertEquals(1, entries.size()); - assertSame(exception, entry.cause()); - assertNull(entry.address()); - } finally { - 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 MultithreadEventLoopGroup(1, LocalHandler.newFactory()); - - 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/DefaultDnsCnameCacheTest.java b/resolver-dns/src/test/java/io/netty/resolver/dns/DefaultDnsCnameCacheTest.java deleted file mode 100644 index 606694d849..0000000000 --- a/resolver-dns/src/test/java/io/netty/resolver/dns/DefaultDnsCnameCacheTest.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.EventLoop; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.local.LocalHandler; -import io.netty.channel.nio.NioHandler; -import org.junit.jupiter.api.Test; - -import java.util.concurrent.Callable; -import java.util.concurrent.TimeUnit; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class DefaultDnsCnameCacheTest { - - @Test - public void testExpire() throws Throwable { - EventLoopGroup group = new MultithreadEventLoopGroup(1, LocalHandler.newFactory()); - - try { - EventLoop loop = group.next(); - final DefaultDnsCnameCache cache = new DefaultDnsCnameCache(); - cache.cache("netty.io", "mapping.netty.io", 1, loop); - - Throwable error = loop.schedule(() -> { - 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 MultithreadEventLoopGroup(1, LocalHandler.newFactory()); - - 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 MultithreadEventLoopGroup(1, NioHandler.newFactory()); - - 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 MultithreadEventLoopGroup(1, LocalHandler.newFactory()); - - 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 MultithreadEventLoopGroup(1, LocalHandler.newFactory()); - - 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/DnsAddressResolverGroupTest.java b/resolver-dns/src/test/java/io/netty/resolver/dns/DnsAddressResolverGroupTest.java deleted file mode 100644 index d9540a56ab..0000000000 --- a/resolver-dns/src/test/java/io/netty/resolver/dns/DnsAddressResolverGroupTest.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.channel.EventLoop; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.local.LocalHandler; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioDatagramChannel; -import io.netty.resolver.AddressResolver; -import io.netty.util.concurrent.Promise; -import org.junit.jupiter.api.Test; - -import java.net.SocketAddress; -import java.nio.channels.UnsupportedAddressTypeException; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.instanceOf; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class DnsAddressResolverGroupTest { - @Test - public void testUseConfiguredEventLoop() throws InterruptedException { - EventLoopGroup group = new MultithreadEventLoopGroup(1, NioHandler.newFactory()); - final EventLoop loop = group.next(); - MultithreadEventLoopGroup defaultEventLoopGroup = new MultithreadEventLoopGroup(1, LocalHandler.newFactory()); - DnsNameResolverBuilder builder = new DnsNameResolverBuilder() - .eventLoop(loop).channelType(NioDatagramChannel.class); - DnsAddressResolverGroup resolverGroup = new DnsAddressResolverGroup(builder); - try { - final Promise promise = loop.newPromise(); - AddressResolver resolver = resolverGroup.getResolver(defaultEventLoopGroup.next()); - resolver.resolve(new SocketAddress() { - private static final long serialVersionUID = 3169703458729818468L; - }).addListener(future -> { - try { - assertThat(future.cause(), - instanceOf(UnsupportedAddressTypeException.class)); - assertTrue(loop.inEventLoop()); - promise.setSuccess(null); - } catch (Throwable cause) { - promise.setFailure(cause); - } - }).await(); - promise.asFuture().sync(); - } finally { - resolverGroup.close(); - group.shutdownGracefully(); - defaultEventLoopGroup.shutdownGracefully(); - } - } -} diff --git a/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverClientSubnetTest.java b/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverClientSubnetTest.java deleted file mode 100644 index 9cd59aa6ff..0000000000 --- a/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverClientSubnetTest.java +++ /dev/null @@ -1,69 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioDatagramChannel; -import io.netty.handler.codec.dns.DefaultDnsOptEcsRecord; -import io.netty.util.internal.SocketUtils; -import io.netty.util.concurrent.Future; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -import java.net.InetAddress; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.TimeUnit; - -public class DnsNameResolverClientSubnetTest { - - // See https://www.gsic.uva.es/~jnisigl/dig-edns-client-subnet.html - // Ignore as this needs to query real DNS servers. - @Disabled - @Test - public void testSubnetQuery() throws Exception { - EventLoopGroup group = new MultithreadEventLoopGroup(1, NioHandler.newFactory()); - DnsNameResolver resolver = newResolver(group).build(); - try { - // Same as: - // # /.bind-9.9.3-edns/bin/dig @ns1.google.com www.google.es +client=157.88.0.0/24 - Future> future = resolver.resolveAll("www.google.es", - Collections.singleton( - // Suggest max payload size of 1024 - // 157.88.0.0 / 24 - new DefaultDnsOptEcsRecord(1024, 24, - SocketUtils.addressByName("157.88.0.0").getAddress()))); - for (InetAddress address: future.syncUninterruptibly().getNow()) { - System.out.println(address); - } - } finally { - resolver.close(); - group.shutdownGracefully(0, 0, TimeUnit.SECONDS); - } - } - - private static DnsNameResolverBuilder newResolver(EventLoopGroup group) { - return new DnsNameResolverBuilder(group.next()) - .channelType(NioDatagramChannel.class) - .nameServerProvider( - new SingletonDnsServerAddressStreamProvider(SocketUtils.socketAddress("8.8.8.8", 53))) - .maxQueriesPerResolve(1) - .optResourceEnabled(false) - .ndots(1); - } -} 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 deleted file mode 100644 index 0867acda6a..0000000000 --- a/resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverTest.java +++ /dev/null @@ -1,3471 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufHolder; -import io.netty.channel.AddressedEnvelope; -import io.netty.channel.ChannelFactory; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.EventLoop; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.ReflectiveChannelFactory; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.DatagramChannel; -import io.netty.channel.socket.DatagramPacket; -import io.netty.channel.socket.InternetProtocolFamily; -import io.netty.channel.socket.nio.NioDatagramChannel; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.codec.dns.DefaultDnsQuestion; -import io.netty.handler.codec.dns.DnsQuestion; -import io.netty.handler.codec.dns.DnsRawRecord; -import io.netty.handler.codec.dns.DnsRecord; -import io.netty.handler.codec.dns.DnsRecordType; -import io.netty.handler.codec.dns.DnsResponse; -import io.netty.handler.codec.dns.DnsResponseCode; -import io.netty.handler.codec.dns.DnsSection; -import io.netty.resolver.HostsFileEntriesProvider; -import io.netty.resolver.HostsFileEntriesResolver; -import io.netty.resolver.ResolvedAddressTypes; -import io.netty.resolver.dns.TestDnsServer.TestResourceRecord; -import io.netty.util.CharsetUtil; -import io.netty.util.NetUtil; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.SocketUtils; -import io.netty.util.internal.StringUtil; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; -import org.apache.directory.server.dns.DnsException; -import org.apache.directory.server.dns.io.encoder.DnsMessageEncoder; -import org.apache.directory.server.dns.messages.DnsMessage; -import org.apache.directory.server.dns.messages.DnsMessageModifier; -import org.apache.directory.server.dns.messages.QuestionRecord; -import org.apache.directory.server.dns.messages.RecordClass; -import org.apache.directory.server.dns.messages.RecordType; -import org.apache.directory.server.dns.messages.ResourceRecord; -import org.apache.directory.server.dns.messages.ResourceRecordModifier; -import org.apache.directory.server.dns.messages.ResponseCode; -import org.apache.directory.server.dns.store.DnsAttribute; -import org.apache.directory.server.dns.store.RecordStore; -import org.apache.mina.core.buffer.IoBuffer; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; -import org.junit.jupiter.api.function.Executable; - -import java.io.IOException; -import java.io.InputStream; -import java.net.DatagramSocket; -import java.net.Inet4Address; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.ServerSocket; -import java.net.Socket; -import java.net.UnknownHostException; -import java.nio.ByteBuffer; -import java.nio.charset.Charset; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Queue; -import java.util.Set; -import java.util.concurrent.CancellationException; -import java.util.concurrent.CompletionException; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ThreadLocalRandom; -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; -import static io.netty.handler.codec.dns.DnsRecordType.AAAA; -import static io.netty.handler.codec.dns.DnsRecordType.CNAME; -import static io.netty.handler.codec.dns.DnsRecordType.NAPTR; -import static io.netty.handler.codec.dns.DnsRecordType.SRV; -import static io.netty.resolver.dns.DnsNameResolver.DEFAULT_RESOLVE_ADDRESS_TYPES; -import static io.netty.resolver.dns.DnsServerAddresses.sequential; -import static io.netty.resolver.dns.TestDnsServer.newARecord; -import static java.util.Arrays.asList; -import static java.util.Collections.singletonList; -import static org.assertj.core.api.Assumptions.assumeThat; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.greaterThan; -import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.startsWith; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - -public class DnsNameResolverTest { - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(DnsNameResolver.class); - private static final long DEFAULT_TEST_TIMEOUT_MS = 30000; - - // Using the top-100 web sites ranked in Alexa.com (Oct 2014) - // Please use the following series of shell commands to get this up-to-date: - // $ curl -O https://s3.amazonaws.com/alexa-static/top-1m.csv.zip - // $ unzip -o top-1m.csv.zip top-1m.csv - // $ head -100 top-1m.csv | cut -d, -f2 | cut -d/ -f1 | while read L; do echo '"'"$L"'",'; done > topsites.txt - private static final Set DOMAINS = Collections.unmodifiableSet(new HashSet<>(asList( - "google.com", - "youtube.com", - "facebook.com", - "baidu.com", - "wikipedia.org", - "yahoo.com", - "reddit.com", - "google.co.in", - "qq.com", - "amazon.com", - "taobao.com", - "tmall.com", - "twitter.com", - "vk.com", - "live.com", - "sohu.com", - "instagram.com", - "google.co.jp", - "sina.com.cn", - "jd.com", - "weibo.com", - "360.cn", - "google.de", - "google.co.uk", - "google.com.br", - "list.tmall.com", - "google.ru", - "google.fr", - "yandex.ru", - "netflix.com", - "google.it", - "google.com.hk", - "linkedin.com", - "pornhub.com", - "t.co", - "google.es", - "twitch.tv", - "alipay.com", - "xvideos.com", - "ebay.com", - "yahoo.co.jp", - "google.ca", - "google.com.mx", - "bing.com", - "ok.ru", - "imgur.com", - "microsoft.com", - "mail.ru", - "imdb.com", - "aliexpress.com", - "hao123.com", - "msn.com", - "tumblr.com", - "csdn.net", - "wikia.com", - "wordpress.com", - "office.com", - "google.com.tr", - "livejasmin.com", - "amazon.co.jp", - "deloton.com", - "apple.com", - "google.com.au", - "paypal.com", - "google.com.tw", - "bongacams.com", - "popads.net", - "whatsapp.com", - "blogspot.com", - "detail.tmall.com", - "google.pl", - "microsoftonline.com", - "xhamster.com", - "google.co.id", - "github.com", - "stackoverflow.com", - "pinterest.com", - "amazon.de", - "diply.com", - "amazon.co.uk", - "so.com", - "google.com.ar", - "coccoc.com", - "soso.com", - "espn.com", - "adobe.com", - "google.com.ua", - "tianya.cn", - "xnxx.com", - "googleusercontent.com", - "savefrom.net", - "google.com.pk", - "amazon.in", - "nicovideo.jp", - "google.co.th", - "dropbox.com", - "thepiratebay.org", - "google.com.sa", - "google.com.eg", - "pixnet.net", - "localhost"))); - - private static final Map DOMAINS_PUNYCODE = new HashMap<>(); - - static { - DOMAINS_PUNYCODE.put("bÃŧchner.de", "xn--bchner-3ya.de"); - DOMAINS_PUNYCODE.put("mÃŧller.de", "xn--mller-kva.de"); - } - - private static final Set DOMAINS_ALL; - - static { - Set all = new HashSet<>(DOMAINS.size() + DOMAINS_PUNYCODE.size()); - all.addAll(DOMAINS); - all.addAll(DOMAINS_PUNYCODE.values()); - DOMAINS_ALL = Collections.unmodifiableSet(all); - } - - /** - * The list of the domain names to exclude from {@link #testResolveAorAAAA()}. - */ - private static final Set EXCLUSIONS_RESOLVE_A = new HashSet<>(); - - static { - Collections.addAll( - EXCLUSIONS_RESOLVE_A, - "akamaihd.net", - "googleusercontent.com", - StringUtil.EMPTY_STRING); - } - - /** - * The list of the domain names to exclude from {@link #testResolveAAAA()}. - * Unfortunately, there are only handful of domain names with IPv6 addresses. - */ - private static final Set EXCLUSIONS_RESOLVE_AAAA = new HashSet<>(); - - static { - EXCLUSIONS_RESOLVE_AAAA.addAll(EXCLUSIONS_RESOLVE_A); - EXCLUSIONS_RESOLVE_AAAA.addAll(DOMAINS); - EXCLUSIONS_RESOLVE_AAAA.removeAll(asList( - "google.com", - "facebook.com", - "youtube.com", - "wikipedia.org", - "google.co.in", - "blogspot.com", - "vk.com", - "google.de", - "google.co.jp", - "google.co.uk", - "google.fr", - "google.com.br", - "google.ru", - "google.it", - "google.es", - "google.com.mx", - "xhamster.com", - "google.ca", - "google.co.id", - "blogger.com", - "flipkart.com", - "google.com.tr", - "google.com.au", - "google.pl", - "google.com.hk", - "blogspot.in" - )); - } - - /** - * The list of the domain names to exclude from {@link #testQueryMx()}. - */ - private static final Set EXCLUSIONS_QUERY_MX = new HashSet<>(); - - static { - Collections.addAll( - EXCLUSIONS_QUERY_MX, - "hao123.com", - "blogspot.com", - "t.co", - "espn.go.com", - "people.com.cn", - "googleusercontent.com", - "blogspot.in", - "localhost", - StringUtil.EMPTY_STRING); - } - - private static final String WINDOWS_HOST_NAME; - private static final boolean WINDOWS_HOSTS_FILE_LOCALHOST_ENTRY_EXISTS; - private static final boolean WINDOWS_HOSTS_FILE_HOST_NAME_ENTRY_EXISTS; - - static { - String windowsHostName; - boolean windowsHostsFileLocalhostEntryExists; - boolean windowsHostsFileHostNameEntryExists; - try { - if (PlatformDependent.isWindows()) { - windowsHostName = InetAddress.getLocalHost().getHostName(); - - HostsFileEntriesProvider provider = - HostsFileEntriesProvider.parser() - .parseSilently(Charset.defaultCharset(), CharsetUtil.UTF_16, CharsetUtil.UTF_8); - windowsHostsFileLocalhostEntryExists = - provider.ipv4Entries().get("localhost") != null || - provider.ipv6Entries().get("localhost") != null; - windowsHostsFileHostNameEntryExists = - provider.ipv4Entries().get(windowsHostName) != null || - provider.ipv6Entries().get(windowsHostName) != null; - } else { - windowsHostName = null; - windowsHostsFileLocalhostEntryExists = false; - windowsHostsFileHostNameEntryExists = false; - } - } catch (Exception ignore) { - windowsHostName = null; - windowsHostsFileLocalhostEntryExists = false; - windowsHostsFileHostNameEntryExists = false; - } - WINDOWS_HOST_NAME = windowsHostName; - WINDOWS_HOSTS_FILE_LOCALHOST_ENTRY_EXISTS = windowsHostsFileLocalhostEntryExists; - WINDOWS_HOSTS_FILE_HOST_NAME_ENTRY_EXISTS = windowsHostsFileHostNameEntryExists; - } - - private static final TestDnsServer dnsServer = new TestDnsServer(DOMAINS_ALL); - private static final EventLoopGroup group = new MultithreadEventLoopGroup(1, NioHandler.newFactory()); - - private static DnsNameResolverBuilder newResolver(boolean decodeToUnicode) { - return newResolver(decodeToUnicode, null); - } - - private static DnsNameResolverBuilder newResolver(boolean decodeToUnicode, - DnsServerAddressStreamProvider dnsServerAddressStreamProvider) { - return newResolver(decodeToUnicode, dnsServerAddressStreamProvider, dnsServer); - } - - private static DnsNameResolverBuilder newResolver(boolean decodeToUnicode, - DnsServerAddressStreamProvider dnsServerAddressStreamProvider, - TestDnsServer dnsServer) { - DnsNameResolverBuilder builder = new DnsNameResolverBuilder(group.next()) - .dnsQueryLifecycleObserverFactory(new TestRecursiveCacheDnsQueryLifecycleObserverFactory()) - .channelType(NioDatagramChannel.class) - .maxQueriesPerResolve(1) - .decodeIdn(decodeToUnicode) - .optResourceEnabled(false) - .ndots(1); - - if (dnsServerAddressStreamProvider == null) { - builder.nameServerProvider(new SingletonDnsServerAddressStreamProvider(dnsServer.localAddress())); - } else { - builder.nameServerProvider(new MultiDnsServerAddressStreamProvider(dnsServerAddressStreamProvider, - new SingletonDnsServerAddressStreamProvider(dnsServer.localAddress()))); - } - - return builder; - } - - private static DnsNameResolverBuilder newResolver() { - return newResolver(true); - } - - private static DnsNameResolverBuilder newResolver(ResolvedAddressTypes resolvedAddressTypes) { - return newResolver() - .resolvedAddressTypes(resolvedAddressTypes); - } - - private static DnsNameResolverBuilder newNonCachedResolver(ResolvedAddressTypes resolvedAddressTypes) { - return newResolver() - .resolveCache(NoopDnsCache.INSTANCE) - .resolvedAddressTypes(resolvedAddressTypes); - } - - @BeforeAll - public static void init() throws Exception { - dnsServer.start(); - } - - @AfterAll - public static void destroy() { - dnsServer.stop(); - group.shutdownGracefully(); - } - - @Test - public void testResolveAorAAAA() throws Exception { - DnsNameResolver resolver = newResolver(ResolvedAddressTypes.IPV4_PREFERRED).build(); - try { - testResolve0(resolver, EXCLUSIONS_RESOLVE_A, AAAA); - } finally { - resolver.close(); - } - } - - @Test - public void testResolveAAAAorA() throws Exception { - DnsNameResolver resolver = newResolver(ResolvedAddressTypes.IPV6_PREFERRED).build(); - try { - testResolve0(resolver, EXCLUSIONS_RESOLVE_A, A); - } finally { - resolver.close(); - } - } - - /** - * This test will start an second DNS test server which returns fixed results that can be easily verified as - * originating from the second DNS test server. The resolver will put {@link DnsServerAddressStreamProvider} under - * test to ensure that some hostnames can be directed toward both the primary and secondary DNS test servers - * simultaneously. - */ - @Test - public void testNameServerCache() throws Exception { - final String overriddenIP = "12.34.12.34"; - final TestDnsServer dnsServer2 = new TestDnsServer(question -> { - if (question.getRecordType() == RecordType.A) { - Map attr = new HashMap<>(); - attr.put(DnsAttribute.IP_ADDRESS.toLowerCase(Locale.US), overriddenIP); - return Collections.singleton( - new TestResourceRecord( - question.getDomainName(), question.getRecordType(), attr)); - } - return null; - }); - dnsServer2.start(); - try { - final Set overriddenHostnames = new HashSet<>(); - for (String name : DOMAINS) { - if (EXCLUSIONS_RESOLVE_A.contains(name)) { - continue; - } - if (ThreadLocalRandom.current().nextBoolean()) { - overriddenHostnames.add(name); - } - } - DnsNameResolver resolver = newResolver(false, hostname -> - overriddenHostnames.contains(hostname) ? sequential(dnsServer2.localAddress()).stream() : null) - .build(); - try { - final Map resultA = testResolve0(resolver, EXCLUSIONS_RESOLVE_A, AAAA); - for (Entry resolvedEntry : resultA.entrySet()) { - if (resolvedEntry.getValue().isLoopbackAddress()) { - continue; - } - if (overriddenHostnames.contains(resolvedEntry.getKey())) { - assertEquals(overriddenIP, resolvedEntry.getValue().getHostAddress(), - "failed to resolve " + resolvedEntry.getKey()); - } else { - assertNotEquals(overriddenIP, resolvedEntry.getValue().getHostAddress(), - "failed to resolve " + resolvedEntry.getKey()); - } - } - } finally { - resolver.close(); - } - } finally { - dnsServer2.stop(); - } - } - - @Test - public void testResolveA() throws Exception { - DnsNameResolver resolver = newResolver(ResolvedAddressTypes.IPV4_ONLY) - // Cache for eternity - .ttl(Integer.MAX_VALUE, Integer.MAX_VALUE) - .build(); - try { - final Map resultA = testResolve0(resolver, EXCLUSIONS_RESOLVE_A, null); - - // Now, try to resolve again to see if it's cached. - // This test works because the DNS servers usually randomizes the order of the records in a response. - // If cached, the resolved addresses must be always same, because we reuse the same response. - - final Map resultB = testResolve0(resolver, EXCLUSIONS_RESOLVE_A, null); - - // Ensure the result from the cache is identical from the uncached one. - assertThat(resultB.size(), is(resultA.size())); - for (Entry e : resultA.entrySet()) { - InetAddress expected = e.getValue(); - InetAddress actual = resultB.get(e.getKey()); - assertThat("Cache for " + e.getKey() + ": " + resolver.resolveAll(e.getKey()).getNow(), - actual, is(expected)); - } - } finally { - resolver.close(); - } - } - - @Test - public void testResolveAAAA() throws Exception { - DnsNameResolver resolver = newResolver(ResolvedAddressTypes.IPV6_ONLY).build(); - try { - testResolve0(resolver, EXCLUSIONS_RESOLVE_AAAA, null); - } finally { - resolver.close(); - } - } - - @Test - public void testNonCachedResolve() throws Exception { - DnsNameResolver resolver = newNonCachedResolver(ResolvedAddressTypes.IPV4_ONLY).build(); - try { - testResolve0(resolver, EXCLUSIONS_RESOLVE_A, null); - } finally { - resolver.close(); - } - } - - @Test - @Timeout(value = DEFAULT_TEST_TIMEOUT_MS, unit = TimeUnit.MILLISECONDS) - public void testNonCachedResolveEmptyHostName() throws Exception { - testNonCachedResolveEmptyHostName(""); - } - - @Test - @Timeout(value = DEFAULT_TEST_TIMEOUT_MS, unit = TimeUnit.MILLISECONDS) - public void testNonCachedResolveNullHostName() throws Exception { - testNonCachedResolveEmptyHostName(null); - } - - private static void testNonCachedResolveEmptyHostName(String inetHost) throws Exception { - DnsNameResolver resolver = newNonCachedResolver(ResolvedAddressTypes.IPV4_ONLY).build(); - try { - InetAddress addr = resolver.resolve(inetHost).syncUninterruptibly().getNow(); - assertEquals(SocketUtils.addressByName(inetHost), addr); - } finally { - resolver.close(); - } - } - - @Test - @Timeout(value = DEFAULT_TEST_TIMEOUT_MS, unit = TimeUnit.MILLISECONDS) - public void testNonCachedResolveAllEmptyHostName() throws Exception { - testNonCachedResolveAllEmptyHostName(""); - } - - @Test - @Timeout(value = DEFAULT_TEST_TIMEOUT_MS, unit = TimeUnit.MILLISECONDS) - public void testNonCachedResolveAllNullHostName() throws Exception { - testNonCachedResolveAllEmptyHostName(null); - } - - private static void testNonCachedResolveAllEmptyHostName(String inetHost) throws UnknownHostException { - DnsNameResolver resolver = newNonCachedResolver(ResolvedAddressTypes.IPV4_ONLY).build(); - try { - List addrs = resolver.resolveAll(inetHost).syncUninterruptibly().getNow(); - assertEquals(asList( - SocketUtils.allAddressesByName(inetHost)), addrs); - } finally { - resolver.close(); - } - } - - private static Map testResolve0(DnsNameResolver resolver, Set excludedDomains, - DnsRecordType cancelledType) - throws InterruptedException { - - assertThat(resolver.isRecursionDesired(), is(true)); - - final Map results = new HashMap<>(); - final Map> futures = - new LinkedHashMap<>(); - - for (String name : DOMAINS) { - if (excludedDomains.contains(name)) { - continue; - } - - resolve(resolver, futures, name); - } - - for (Entry> e : futures.entrySet()) { - String unresolved = e.getKey(); - InetAddress resolved = e.getValue().sync().getNow(); - - logger.info("{}: {}", unresolved, resolved.getHostAddress()); - - assertThat(resolved.getHostName(), is(unresolved)); - - boolean typeMatches = false; - for (InternetProtocolFamily f : resolver.resolvedInternetProtocolFamiliesUnsafe()) { - Class resolvedType = resolved.getClass(); - if (f.addressType().isAssignableFrom(resolvedType)) { - typeMatches = true; - } - } - - assertThat(typeMatches, is(true)); - - results.put(resolved.getHostName(), resolved); - } - - assertQueryObserver(resolver, cancelledType); - - return results; - } - - @Test - public void testQueryMx() { - DnsNameResolver resolver = newResolver().build(); - try { - assertThat(resolver.isRecursionDesired(), is(true)); - - Map>> futures = - new LinkedHashMap<>(); - for (String name : DOMAINS) { - if (EXCLUSIONS_QUERY_MX.contains(name)) { - continue; - } - - queryMx(resolver, futures, name); - } - - for (Entry>> e : futures.entrySet()) { - String hostname = e.getKey(); - Future> f = e.getValue().awaitUninterruptibly(); - - DnsResponse response = f.getNow().content(); - assertThat(response.code(), is(DnsResponseCode.NOERROR)); - - final int answerCount = response.count(DnsSection.ANSWER); - final List mxList = new ArrayList<>(answerCount); - for (int i = 0; i < answerCount; i++) { - final DnsRecord r = response.recordAt(DnsSection.ANSWER, i); - if (r.type() == DnsRecordType.MX) { - mxList.add(r); - } - } - - assertThat(mxList.size(), is(greaterThan(0))); - StringBuilder buf = new StringBuilder(); - for (DnsRecord r : mxList) { - ByteBuf recordContent = ((ByteBufHolder) r).content(); - - buf.append(StringUtil.NEWLINE); - buf.append('\t'); - buf.append(r.name()); - buf.append(' '); - buf.append(r.type().name()); - buf.append(' '); - buf.append(recordContent.readUnsignedShort()); - buf.append(' '); - buf.append(DnsResolveContext.decodeDomainName(recordContent)); - } - - logger.info("{} has the following MX records:{}", hostname, buf); - response.release(); - - // We only track query lifecycle if it is managed by the DnsNameResolverContext, and not direct calls - // to query. - assertNoQueriesMade(resolver); - } - } finally { - resolver.close(); - } - } - - @Test - public void testNegativeTtl() throws Exception { - final DnsNameResolver resolver = newResolver().negativeTtl(10).build(); - try { - resolveNonExistentDomain(resolver); - - final int size = 10000; - final List exceptions = new ArrayList<>(); - - // If negative cache works, this thread should be done really quickly. - final Thread negativeLookupThread = new Thread() { - @Override - public void run() { - for (int i = 0; i < size; i++) { - exceptions.add(resolveNonExistentDomain(resolver)); - if (isInterrupted()) { - break; - } - } - } - }; - - negativeLookupThread.start(); - negativeLookupThread.join(DEFAULT_TEST_TIMEOUT_MS); - - if (negativeLookupThread.isAlive()) { - negativeLookupThread.interrupt(); - fail("Cached negative lookups did not finish quickly."); - } - - assertThat(exceptions, hasSize(size)); - } finally { - resolver.close(); - } - } - - private static UnknownHostException resolveNonExistentDomain(DnsNameResolver resolver) { - try { - resolver.resolve("non-existent.netty.io").syncUninterruptibly(); - fail(); - return null; - } catch (CompletionException cause) { - Throwable e = cause.getCause(); - assertThat(e, is(instanceOf(UnknownHostException.class))); - - TestRecursiveCacheDnsQueryLifecycleObserverFactory lifecycleObserverFactory = - (TestRecursiveCacheDnsQueryLifecycleObserverFactory) resolver.dnsQueryLifecycleObserverFactory(); - TestDnsQueryLifecycleObserver observer = lifecycleObserverFactory.observers.poll(); - if (observer != null) { - Object o = observer.events.poll(); - if (o instanceof QueryCancelledEvent) { - assertTrue(observer.question.type() == CNAME || observer.question.type() == AAAA, - "unexpected type: " + observer.question); - } else if (o instanceof QueryWrittenEvent) { - QueryFailedEvent failedEvent = (QueryFailedEvent) observer.events.poll(); - } else if (!(o instanceof QueryFailedEvent)) { - fail("unexpected event type: " + o); - } - assertTrue(observer.events.isEmpty()); - } - return (UnknownHostException) e; - } - } - - @Test - public void testResolveIp() { - DnsNameResolver resolver = newResolver().build(); - try { - InetAddress address = resolver.resolve("10.0.0.1").syncUninterruptibly().getNow(); - - assertEquals("10.0.0.1", address.getHostAddress()); - - // This address is already resolved, and so we shouldn't have to query for anything. - assertNoQueriesMade(resolver); - } finally { - resolver.close(); - } - } - - @Test - public void testResolveEmptyIpv4() { - testResolve0(ResolvedAddressTypes.IPV4_ONLY, NetUtil.LOCALHOST4, StringUtil.EMPTY_STRING); - } - - @Test - public void testResolveEmptyIpv6() { - testResolve0(ResolvedAddressTypes.IPV6_ONLY, NetUtil.LOCALHOST6, StringUtil.EMPTY_STRING); - } - - @Test - public void testResolveLocalhostIpv4() { - assumeThat(PlatformDependent.isWindows()).isTrue(); - assumeThat(WINDOWS_HOSTS_FILE_LOCALHOST_ENTRY_EXISTS).isFalse(); - assumeThat(DEFAULT_RESOLVE_ADDRESS_TYPES).isNotEqualTo(ResolvedAddressTypes.IPV6_PREFERRED); - testResolve0(ResolvedAddressTypes.IPV4_ONLY, NetUtil.LOCALHOST4, "localhost"); - } - - @Test - public void testResolveLocalhostIpv6() { - assumeThat(PlatformDependent.isWindows()).isTrue(); - assumeThat(WINDOWS_HOSTS_FILE_LOCALHOST_ENTRY_EXISTS).isFalse(); - assumeThat(DEFAULT_RESOLVE_ADDRESS_TYPES).isEqualTo(ResolvedAddressTypes.IPV6_PREFERRED); - testResolve0(ResolvedAddressTypes.IPV6_ONLY, NetUtil.LOCALHOST6, "localhost"); - } - - @Test - public void testResolveHostNameIpv4() { - assumeThat(PlatformDependent.isWindows()).isTrue(); - assumeThat(WINDOWS_HOSTS_FILE_HOST_NAME_ENTRY_EXISTS).isFalse(); - assumeThat(DEFAULT_RESOLVE_ADDRESS_TYPES).isNotEqualTo(ResolvedAddressTypes.IPV6_PREFERRED); - testResolve0(ResolvedAddressTypes.IPV4_ONLY, NetUtil.LOCALHOST4, WINDOWS_HOST_NAME); - } - - @Test - public void testResolveHostNameIpv6() { - assumeThat(PlatformDependent.isWindows()).isTrue(); - assumeThat(WINDOWS_HOSTS_FILE_HOST_NAME_ENTRY_EXISTS).isFalse(); - assumeThat(DEFAULT_RESOLVE_ADDRESS_TYPES).isEqualTo(ResolvedAddressTypes.IPV6_PREFERRED); - testResolve0(ResolvedAddressTypes.IPV6_ONLY, NetUtil.LOCALHOST6, WINDOWS_HOST_NAME); - } - - @Test - public void testResolveNullIpv4() { - testResolve0(ResolvedAddressTypes.IPV4_ONLY, NetUtil.LOCALHOST4, null); - } - - @Test - public void testResolveNullIpv6() { - testResolve0(ResolvedAddressTypes.IPV6_ONLY, NetUtil.LOCALHOST6, null); - } - - private static void testResolve0(ResolvedAddressTypes addressTypes, InetAddress expectedAddr, String name) { - DnsNameResolver resolver = newResolver(addressTypes).build(); - try { - InetAddress address = resolver.resolve(name).syncUninterruptibly().getNow(); - assertEquals(expectedAddr, address); - - // We are resolving the local address, so we shouldn't make any queries. - assertNoQueriesMade(resolver); - } finally { - resolver.close(); - } - } - - @Test - public void testResolveAllEmptyIpv4() { - testResolveAll0(ResolvedAddressTypes.IPV4_ONLY, NetUtil.LOCALHOST4, StringUtil.EMPTY_STRING); - } - - @Test - public void testResolveAllEmptyIpv6() { - testResolveAll0(ResolvedAddressTypes.IPV6_ONLY, NetUtil.LOCALHOST6, StringUtil.EMPTY_STRING); - } - - @Test - public void testResolveAllLocalhostIpv4() { - assumeThat(PlatformDependent.isWindows()).isTrue(); - assumeThat(WINDOWS_HOSTS_FILE_LOCALHOST_ENTRY_EXISTS).isFalse(); - assumeThat(DEFAULT_RESOLVE_ADDRESS_TYPES).isNotEqualTo(ResolvedAddressTypes.IPV6_PREFERRED); - testResolveAll0(ResolvedAddressTypes.IPV4_ONLY, NetUtil.LOCALHOST4, "localhost"); - } - - @Test - public void testResolveAllLocalhostIpv6() { - assumeThat(PlatformDependent.isWindows()).isTrue(); - assumeThat(WINDOWS_HOSTS_FILE_LOCALHOST_ENTRY_EXISTS).isFalse(); - assumeThat(DEFAULT_RESOLVE_ADDRESS_TYPES).isEqualTo(ResolvedAddressTypes.IPV6_PREFERRED); - testResolveAll0(ResolvedAddressTypes.IPV6_ONLY, NetUtil.LOCALHOST6, "localhost"); - } - - @Test - public void testResolveAllHostNameIpv4() { - assumeThat(PlatformDependent.isWindows()).isTrue(); - assumeThat(WINDOWS_HOSTS_FILE_HOST_NAME_ENTRY_EXISTS).isFalse(); - assumeThat(DEFAULT_RESOLVE_ADDRESS_TYPES).isNotEqualTo(ResolvedAddressTypes.IPV6_PREFERRED); - testResolveAll0(ResolvedAddressTypes.IPV4_ONLY, NetUtil.LOCALHOST4, WINDOWS_HOST_NAME); - } - - @Test - public void testResolveAllHostNameIpv6() { - assumeThat(PlatformDependent.isWindows()).isTrue(); - assumeThat(WINDOWS_HOSTS_FILE_HOST_NAME_ENTRY_EXISTS).isFalse(); - assumeThat(DEFAULT_RESOLVE_ADDRESS_TYPES).isEqualTo(ResolvedAddressTypes.IPV6_PREFERRED); - testResolveAll0(ResolvedAddressTypes.IPV6_ONLY, NetUtil.LOCALHOST6, WINDOWS_HOST_NAME); - } - - @Test - public void testCNAMEResolveAllIpv4() throws IOException { - testCNAMERecursiveResolve(true); - } - - @Test - public void testCNAMEResolveAllIpv6() throws IOException { - testCNAMERecursiveResolve(false); - } - - private static void testCNAMERecursiveResolve(boolean ipv4Preferred) throws IOException { - final String firstName = "firstname.com"; - final String secondName = "secondname.com"; - final String lastName = "lastname.com"; - final String ipv4Addr = "1.2.3.4"; - final String ipv6Addr = "::1"; - TestDnsServer dnsServer2 = new TestDnsServer(question -> { - ResourceRecordModifier rm = new ResourceRecordModifier(); - rm.setDnsClass(RecordClass.IN); - rm.setDnsName(question.getDomainName()); - rm.setDnsTtl(100); - rm.setDnsType(RecordType.CNAME); - - if (question.getDomainName().equals(firstName)) { - rm.put(DnsAttribute.DOMAIN_NAME, secondName); - } else if (question.getDomainName().equals(secondName)) { - rm.put(DnsAttribute.DOMAIN_NAME, lastName); - } else if (question.getDomainName().equals(lastName)) { - rm.setDnsType(question.getRecordType()); - switch (question.getRecordType()) { - case A: - rm.put(DnsAttribute.IP_ADDRESS, ipv4Addr); - break; - case AAAA: - rm.put(DnsAttribute.IP_ADDRESS, ipv6Addr); - break; - default: - return null; - } - } else { - return null; - } - return Collections.singleton(rm.getEntry()); - }); - dnsServer2.start(); - DnsNameResolver resolver = null; - try { - DnsNameResolverBuilder builder = newResolver() - .recursionDesired(true) - .maxQueriesPerResolve(16) - .nameServerProvider(new SingletonDnsServerAddressStreamProvider(dnsServer2.localAddress())); - if (ipv4Preferred) { - builder.resolvedAddressTypes(ResolvedAddressTypes.IPV4_PREFERRED); - } else { - builder.resolvedAddressTypes(ResolvedAddressTypes.IPV6_PREFERRED); - } - resolver = builder.build(); - InetAddress resolvedAddress = resolver.resolve(firstName).syncUninterruptibly().getNow(); - if (ipv4Preferred) { - assertEquals(ipv4Addr, resolvedAddress.getHostAddress()); - } else { - assertEquals(ipv6Addr, NetUtil.toAddressString(resolvedAddress)); - } - assertEquals(firstName, resolvedAddress.getHostName()); - } finally { - dnsServer2.stop(); - if (resolver != null) { - resolver.close(); - } - } - } - - @Test - public void testCNAMERecursiveResolveMultipleNameServersIPv4() throws IOException { - testCNAMERecursiveResolveMultipleNameServers(true); - } - - @Test - public void testCNAMERecursiveResolveMultipleNameServersIPv6() throws IOException { - testCNAMERecursiveResolveMultipleNameServers(false); - } - - private static void testCNAMERecursiveResolveMultipleNameServers(boolean ipv4Preferred) throws IOException { - final String firstName = "firstname.nettyfoo.com"; - final String lastName = "lastname.nettybar.com"; - final String ipv4Addr = "1.2.3.4"; - final String ipv6Addr = "::1"; - final AtomicBoolean hitServer2 = new AtomicBoolean(); - final TestDnsServer dnsServer2 = new TestDnsServer(question -> { - hitServer2.set(true); - if (question.getDomainName().equals(firstName)) { - ResourceRecordModifier rm = new ResourceRecordModifier(); - rm.setDnsClass(RecordClass.IN); - rm.setDnsName(question.getDomainName()); - rm.setDnsTtl(100); - rm.setDnsType(RecordType.CNAME); - rm.put(DnsAttribute.DOMAIN_NAME, lastName); - return Collections.singleton(rm.getEntry()); - } else { - throw new DnsException(ResponseCode.REFUSED); - } - }); - final TestDnsServer dnsServer3 = new TestDnsServer(question -> { - if (question.getDomainName().equals(lastName)) { - ResourceRecordModifier rm = new ResourceRecordModifier(); - rm.setDnsClass(RecordClass.IN); - rm.setDnsName(question.getDomainName()); - rm.setDnsTtl(100); - rm.setDnsType(question.getRecordType()); - switch (question.getRecordType()) { - case A: - rm.put(DnsAttribute.IP_ADDRESS, ipv4Addr); - break; - case AAAA: - rm.put(DnsAttribute.IP_ADDRESS, ipv6Addr); - break; - default: - return null; - } - - return Collections.singleton(rm.getEntry()); - } else { - throw new DnsException(ResponseCode.REFUSED); - } - }); - dnsServer2.start(); - dnsServer3.start(); - DnsNameResolver resolver = null; - try { - AuthoritativeDnsServerCache nsCache = new DefaultAuthoritativeDnsServerCache(); - // What we want to test is the following: - // 1. Do a DNS query. - // 2. CNAME is returned, we want to lookup that CNAME on multiple DNS servers - // 3. The first DNS server should fail - // 4. The second DNS server should succeed - // This verifies that we do in fact follow multiple DNS servers in the CNAME resolution. - // The DnsCache is used for the name server cache, but doesn't provide a InetSocketAddress (only InetAddress - // so no port), so we only specify the name server in the cache, and then specify both name servers in the - // fallback name server provider. - nsCache.cache("nettyfoo.com.", dnsServer2.localAddress(), 10000, group.next()); - resolver = new DnsNameResolver( - group.next(), new ReflectiveChannelFactory(NioDatagramChannel.class), - NoopDnsCache.INSTANCE, nsCache, NoopDnsQueryLifecycleObserverFactory.INSTANCE, 3000, - ipv4Preferred ? ResolvedAddressTypes.IPV4_ONLY : ResolvedAddressTypes.IPV6_ONLY, true, - 10, 4096, false, HostsFileEntriesResolver.DEFAULT, - new SequentialDnsServerAddressStreamProvider(dnsServer2.localAddress(), dnsServer3.localAddress()), - DnsNameResolver.DEFAULT_SEARCH_DOMAINS, 0, true) { - @Override - InetSocketAddress newRedirectServerAddress(InetAddress server) { - int port = hitServer2.get() ? dnsServer3.localAddress().getPort() : - dnsServer2.localAddress().getPort(); - return new InetSocketAddress(server, port); - } - }; - InetAddress resolvedAddress = resolver.resolve(firstName).syncUninterruptibly().getNow(); - if (ipv4Preferred) { - assertEquals(ipv4Addr, resolvedAddress.getHostAddress()); - } else { - assertEquals(ipv6Addr, NetUtil.toAddressString(resolvedAddress)); - } - assertEquals(firstName, resolvedAddress.getHostName()); - } finally { - dnsServer2.stop(); - dnsServer3.stop(); - if (resolver != null) { - resolver.close(); - } - } - } - - @Test - public void testResolveAllNullIpv4() { - testResolveAll0(ResolvedAddressTypes.IPV4_ONLY, NetUtil.LOCALHOST4, null); - } - - @Test - public void testResolveAllNullIpv6() { - testResolveAll0(ResolvedAddressTypes.IPV6_ONLY, NetUtil.LOCALHOST6, null); - } - - private static void testResolveAll0(ResolvedAddressTypes addressTypes, InetAddress expectedAddr, String name) { - DnsNameResolver resolver = newResolver(addressTypes).build(); - try { - List addresses = resolver.resolveAll(name).syncUninterruptibly().getNow(); - assertEquals(1, addresses.size()); - assertEquals(expectedAddr, addresses.get(0)); - - // We are resolving the local address, so we shouldn't make any queries. - assertNoQueriesMade(resolver); - } finally { - resolver.close(); - } - } - - @Test - public void testResolveAllMx() { - final DnsNameResolver resolver = newResolver().build(); - try { - assertThat(resolver.isRecursionDesired(), is(true)); - - final Map>> futures = new LinkedHashMap<>(); - for (String name : DOMAINS) { - if (EXCLUSIONS_QUERY_MX.contains(name)) { - continue; - } - - futures.put(name, resolver.resolveAll(new DefaultDnsQuestion(name, DnsRecordType.MX))); - } - - for (Entry>> e : futures.entrySet()) { - String hostname = e.getKey(); - Future> f = e.getValue().awaitUninterruptibly(); - - final List mxList = f.getNow(); - assertThat(mxList.size(), is(greaterThan(0))); - StringBuilder buf = new StringBuilder(); - for (DnsRecord r : mxList) { - ByteBuf recordContent = ((ByteBufHolder) r).content(); - - buf.append(StringUtil.NEWLINE); - buf.append('\t'); - buf.append(r.name()); - buf.append(' '); - buf.append(r.type().name()); - buf.append(' '); - buf.append(recordContent.readUnsignedShort()); - buf.append(' '); - buf.append(DnsResolveContext.decodeDomainName(recordContent)); - - ReferenceCountUtil.release(r); - } - - logger.info("{} has the following MX records:{}", hostname, buf); - } - } finally { - resolver.close(); - } - } - - @Test - public void testResolveAllHostsFile() { - final DnsNameResolver resolver = new DnsNameResolverBuilder(group.next()) - .channelType(NioDatagramChannel.class) - .hostsFileEntriesResolver((inetHost, resolvedAddressTypes) -> { - if ("foo.com.".equals(inetHost)) { - try { - return InetAddress.getByAddress("foo.com", new byte[] { 1, 2, 3, 4 }); - } catch (UnknownHostException e) { - throw new Error(e); - } - } - return null; - }).build(); - - final List records = resolver.resolveAll(new DefaultDnsQuestion("foo.com.", A)) - .syncUninterruptibly().getNow(); - assertThat(records, hasSize(1)); - assertThat(records.get(0), instanceOf(DnsRawRecord.class)); - - final DnsRawRecord record = (DnsRawRecord) records.get(0); - final ByteBuf content = record.content(); - assertThat(record.name(), is("foo.com.")); - assertThat(record.dnsClass(), is(DnsRecord.CLASS_IN)); - assertThat(record.type(), is(A)); - assertThat(content.readableBytes(), is(4)); - assertThat(content.readInt(), is(0x01020304)); - record.release(); - } - - @Test - public void testResolveDecodeUnicode() { - testResolveUnicode(true); - } - - @Test - public void testResolveNotDecodeUnicode() { - testResolveUnicode(false); - } - - private static void testResolveUnicode(boolean decode) { - DnsNameResolver resolver = newResolver(decode).build(); - try { - for (Entry entries : DOMAINS_PUNYCODE.entrySet()) { - InetAddress address = resolver.resolve(entries.getKey()).syncUninterruptibly().getNow(); - assertEquals(decode ? entries.getKey() : entries.getValue(), address.getHostName()); - } - - assertQueryObserver(resolver, AAAA); - } finally { - resolver.close(); - } - } - - @Test - @Timeout(value = DEFAULT_TEST_TIMEOUT_MS, unit = TimeUnit.MILLISECONDS) - public void secondDnsServerShouldBeUsedBeforeCNAMEFirstServerNotStarted() throws IOException { - secondDnsServerShouldBeUsedBeforeCNAME(false); - } - - @Test - @Timeout(value = DEFAULT_TEST_TIMEOUT_MS, unit = TimeUnit.MILLISECONDS) - public void secondDnsServerShouldBeUsedBeforeCNAMEFirstServerFailResolve() throws IOException { - secondDnsServerShouldBeUsedBeforeCNAME(true); - } - - private static void secondDnsServerShouldBeUsedBeforeCNAME(boolean startDnsServer1) throws IOException { - final String knownHostName = "netty.io"; - final TestDnsServer dnsServer1 = new TestDnsServer(Collections.singleton("notnetty.com")); - final TestDnsServer dnsServer2 = new TestDnsServer(Collections.singleton(knownHostName)); - DnsNameResolver resolver = null; - try { - final InetSocketAddress dnsServer1Address; - if (startDnsServer1) { - dnsServer1.start(); - dnsServer1Address = dnsServer1.localAddress(); - } else { - // Some address where a DNS server will not be running. - dnsServer1Address = new InetSocketAddress("127.0.0.1", 22); - } - dnsServer2.start(); - - TestRecursiveCacheDnsQueryLifecycleObserverFactory lifecycleObserverFactory = - new TestRecursiveCacheDnsQueryLifecycleObserverFactory(); - - DnsNameResolverBuilder builder = new DnsNameResolverBuilder(group.next()) - .dnsQueryLifecycleObserverFactory(lifecycleObserverFactory) - .resolvedAddressTypes(ResolvedAddressTypes.IPV4_ONLY) - .channelType(NioDatagramChannel.class) - .queryTimeoutMillis(1000) // We expect timeouts if startDnsServer1 is false - .optResourceEnabled(false) - .ndots(1); - - builder.nameServerProvider(new SequentialDnsServerAddressStreamProvider(dnsServer1Address, - dnsServer2.localAddress())); - resolver = builder.build(); - assertNotNull(resolver.resolve(knownHostName).syncUninterruptibly().getNow()); - - TestDnsQueryLifecycleObserver observer = lifecycleObserverFactory.observers.poll(); - assertNotNull(observer); - assertEquals(1, lifecycleObserverFactory.observers.size()); - assertEquals(2, observer.events.size()); - QueryWrittenEvent writtenEvent = (QueryWrittenEvent) observer.events.poll(); - assertEquals(dnsServer1Address, writtenEvent.dnsServerAddress); - QueryFailedEvent failedEvent = (QueryFailedEvent) observer.events.poll(); - - observer = lifecycleObserverFactory.observers.poll(); - assertEquals(2, observer.events.size()); - writtenEvent = (QueryWrittenEvent) observer.events.poll(); - assertEquals(dnsServer2.localAddress(), writtenEvent.dnsServerAddress); - QuerySucceededEvent succeededEvent = (QuerySucceededEvent) observer.events.poll(); - } finally { - if (resolver != null) { - resolver.close(); - } - dnsServer1.stop(); - dnsServer2.stop(); - } - } - - @Test - @Timeout(value = DEFAULT_TEST_TIMEOUT_MS, unit = TimeUnit.MILLISECONDS) - public void aAndAAAAQueryShouldTryFirstDnsServerBeforeSecond() throws IOException { - final String knownHostName = "netty.io"; - final TestDnsServer dnsServer1 = new TestDnsServer(Collections.singleton("notnetty.com")); - final TestDnsServer dnsServer2 = new TestDnsServer(Collections.singleton(knownHostName)); - DnsNameResolver resolver = null; - try { - dnsServer1.start(); - dnsServer2.start(); - - TestRecursiveCacheDnsQueryLifecycleObserverFactory lifecycleObserverFactory = - new TestRecursiveCacheDnsQueryLifecycleObserverFactory(); - - DnsNameResolverBuilder builder = new DnsNameResolverBuilder(group.next()) - .resolvedAddressTypes(ResolvedAddressTypes.IPV4_ONLY) - .dnsQueryLifecycleObserverFactory(lifecycleObserverFactory) - .channelType(NioDatagramChannel.class) - .optResourceEnabled(false) - .ndots(1); - - builder.nameServerProvider(new SequentialDnsServerAddressStreamProvider(dnsServer1.localAddress(), - dnsServer2.localAddress())); - resolver = builder.build(); - assertNotNull(resolver.resolve(knownHostName).syncUninterruptibly().getNow()); - - TestDnsQueryLifecycleObserver observer = lifecycleObserverFactory.observers.poll(); - assertNotNull(observer); - 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(dnsServer2.localAddress(), writtenEvent.dnsServerAddress); - QuerySucceededEvent succeededEvent = (QuerySucceededEvent) observer.events.poll(); - } finally { - if (resolver != null) { - resolver.close(); - } - dnsServer1.stop(); - dnsServer2.stop(); - } - } - - @Test - public void testRecursiveResolveNoCache() throws Exception { - testRecursiveResolveCache(false); - } - - @Test - public void testRecursiveResolveCache() throws Exception { - testRecursiveResolveCache(true); - } - - @Test - public void testIpv4PreferredWhenIpv6First() throws Exception { - testResolvesPreferredWhenNonPreferredFirst0(ResolvedAddressTypes.IPV4_PREFERRED); - } - - @Test - public void testIpv6PreferredWhenIpv4First() throws Exception { - testResolvesPreferredWhenNonPreferredFirst0(ResolvedAddressTypes.IPV6_PREFERRED); - } - - private static void testResolvesPreferredWhenNonPreferredFirst0(ResolvedAddressTypes types) throws Exception { - final String name = "netty.com"; - // This store is non-compliant, returning records of the wrong type for a query. - // It works since we don't verify the type of the result when resolving to deal with - // non-compliant servers in the wild. - List> records = new ArrayList<>(); - final String ipv6Address = "0:0:0:0:0:0:1:1"; - final String ipv4Address = "1.1.1.1"; - if (types == ResolvedAddressTypes.IPV4_PREFERRED) { - records.add(Collections.singleton(TestDnsServer.newAddressRecord(name, RecordType.AAAA, ipv6Address))); - records.add(Collections.singleton(TestDnsServer.newAddressRecord(name, RecordType.A, ipv4Address))); - } else { - records.add(Collections.singleton(TestDnsServer.newAddressRecord(name, RecordType.A, ipv4Address))); - records.add(Collections.singleton(TestDnsServer.newAddressRecord(name, RecordType.AAAA, ipv6Address))); - } - final Iterator> recordsIterator = records.iterator(); - RecordStore arbitrarilyOrderedStore = questionRecord -> recordsIterator.next(); - TestDnsServer nonCompliantDnsServer = new TestDnsServer(arbitrarilyOrderedStore); - nonCompliantDnsServer.start(); - try { - DnsNameResolver resolver = newResolver(types) - .maxQueriesPerResolve(2) - .nameServerProvider(new SingletonDnsServerAddressStreamProvider( - nonCompliantDnsServer.localAddress())) - .build(); - InetAddress resolved = resolver.resolve("netty.com").syncUninterruptibly().getNow(); - if (types == ResolvedAddressTypes.IPV4_PREFERRED) { - assertEquals(ipv4Address, resolved.getHostAddress()); - } else { - assertEquals(ipv6Address, resolved.getHostAddress()); - } - InetAddress ipv4InetAddress = InetAddress.getByAddress("netty.com", - InetAddress.getByName(ipv4Address).getAddress()); - InetAddress ipv6InetAddress = InetAddress.getByAddress("netty.com", - InetAddress.getByName(ipv6Address).getAddress()); - - List resolvedAll = resolver.resolveAll("netty.com").syncUninterruptibly().getNow(); - List expected = types == ResolvedAddressTypes.IPV4_PREFERRED ? - asList(ipv4InetAddress, ipv6InetAddress) : asList(ipv6InetAddress, ipv4InetAddress); - assertEquals(expected, resolvedAll); - } finally { - nonCompliantDnsServer.stop(); - } - } - - private static void testRecursiveResolveCache(boolean cache) - throws Exception { - final String hostname = "some.record.netty.io"; - final String hostname2 = "some2.record.netty.io"; - - final TestDnsServer dnsServerAuthority = new TestDnsServer(new HashSet<>( - asList(hostname, hostname2))); - dnsServerAuthority.start(); - - TestDnsServer dnsServer = new RedirectingTestDnsServer(hostname, - dnsServerAuthority.localAddress().getAddress().getHostAddress()); - dnsServer.start(); - - TestAuthoritativeDnsServerCache nsCache = new TestAuthoritativeDnsServerCache( - cache ? new DefaultAuthoritativeDnsServerCache() : NoopAuthoritativeDnsServerCache.INSTANCE); - TestRecursiveCacheDnsQueryLifecycleObserverFactory lifecycleObserverFactory = - new TestRecursiveCacheDnsQueryLifecycleObserverFactory(); - EventLoopGroup group = new MultithreadEventLoopGroup(1, NioHandler.newFactory()); - final DnsNameResolver resolver = new DnsNameResolver( - group.next(), new ReflectiveChannelFactory(NioDatagramChannel.class), - NoopDnsCache.INSTANCE, nsCache, lifecycleObserverFactory, 3000, ResolvedAddressTypes.IPV4_ONLY, true, - 10, 4096, false, HostsFileEntriesResolver.DEFAULT, - new SingletonDnsServerAddressStreamProvider(dnsServer.localAddress()), - DnsNameResolver.DEFAULT_SEARCH_DOMAINS, 0, true) { - @Override - InetSocketAddress newRedirectServerAddress(InetAddress server) { - if (server.equals(dnsServerAuthority.localAddress().getAddress())) { - return new InetSocketAddress(server, dnsServerAuthority.localAddress().getPort()); - } - return super.newRedirectServerAddress(server); - } - }; - - // Java7 will strip of the "." so we need to adjust the expected dnsname. Both are valid in terms of the RFC - // so its ok. - String expectedDnsName = "dns4.some.record.netty.io."; - - try { - resolver.resolveAll(hostname).syncUninterruptibly(); - - TestDnsQueryLifecycleObserver observer = lifecycleObserverFactory.observers.poll(); - assertNotNull(observer); - assertTrue(lifecycleObserverFactory.observers.isEmpty()); - assertEquals(4, observer.events.size()); - QueryWrittenEvent writtenEvent1 = (QueryWrittenEvent) observer.events.poll(); - assertEquals(dnsServer.localAddress(), writtenEvent1.dnsServerAddress); - QueryRedirectedEvent redirectedEvent = (QueryRedirectedEvent) observer.events.poll(); - - assertEquals(expectedDnsName, redirectedEvent.nameServers.get(0).getHostName()); - assertEquals(dnsServerAuthority.localAddress(), redirectedEvent.nameServers.get(0)); - QueryWrittenEvent writtenEvent2 = (QueryWrittenEvent) observer.events.poll(); - assertEquals(dnsServerAuthority.localAddress(), writtenEvent2.dnsServerAddress); - QuerySucceededEvent succeededEvent = (QuerySucceededEvent) observer.events.poll(); - - if (cache) { - assertNull(nsCache.cache.get("io.")); - assertNull(nsCache.cache.get("netty.io.")); - DnsServerAddressStream entries = nsCache.cache.get("record.netty.io."); - - // First address should be resolved (as we received a matching additional record), second is unresolved. - assertEquals(2, entries.size()); - assertFalse(entries.next().isUnresolved()); - assertTrue(entries.next().isUnresolved()); - - assertNull(nsCache.cache.get(hostname)); - - // Test again via cache. - resolver.resolveAll(hostname).syncUninterruptibly(); - - observer = lifecycleObserverFactory.observers.poll(); - assertNotNull(observer); - assertTrue(lifecycleObserverFactory.observers.isEmpty()); - assertEquals(2, observer.events.size()); - writtenEvent1 = (QueryWrittenEvent) observer.events.poll(); - assertEquals(expectedDnsName, writtenEvent1.dnsServerAddress.getHostName()); - assertEquals(dnsServerAuthority.localAddress(), writtenEvent1.dnsServerAddress); - succeededEvent = (QuerySucceededEvent) observer.events.poll(); - - resolver.resolveAll(hostname2).syncUninterruptibly(); - - observer = lifecycleObserverFactory.observers.poll(); - assertNotNull(observer); - assertTrue(lifecycleObserverFactory.observers.isEmpty()); - assertEquals(2, observer.events.size()); - writtenEvent1 = (QueryWrittenEvent) observer.events.poll(); - assertEquals(expectedDnsName, writtenEvent1.dnsServerAddress.getHostName()); - assertEquals(dnsServerAuthority.localAddress(), writtenEvent1.dnsServerAddress); - succeededEvent = (QuerySucceededEvent) observer.events.poll(); - - // Check that it only queried the cache for record.netty.io. - assertNull(nsCache.cacheHits.get("io.")); - assertNull(nsCache.cacheHits.get("netty.io.")); - assertNotNull(nsCache.cacheHits.get("record.netty.io.")); - assertNull(nsCache.cacheHits.get("some.record.netty.io.")); - } - } finally { - resolver.close(); - group.shutdownGracefully(0, 0, TimeUnit.SECONDS); - dnsServer.stop(); - dnsServerAuthority.stop(); - } - } - - @Test - public void testFollowNsRedirectsNoopCaches() throws Exception { - testFollowNsRedirects(NoopDnsCache.INSTANCE, NoopAuthoritativeDnsServerCache.INSTANCE, false); - } - - @Test - public void testFollowNsRedirectsNoopDnsCache() throws Exception { - testFollowNsRedirects(NoopDnsCache.INSTANCE, new DefaultAuthoritativeDnsServerCache(), false); - } - - @Test - public void testFollowNsRedirectsNoopAuthoritativeDnsServerCache() throws Exception { - testFollowNsRedirects(new DefaultDnsCache(), NoopAuthoritativeDnsServerCache.INSTANCE, false); - } - - @Test - public void testFollowNsRedirectsDefaultCaches() throws Exception { - testFollowNsRedirects(new DefaultDnsCache(), new DefaultAuthoritativeDnsServerCache(), false); - } - - @Test - public void testFollowNsRedirectAndTrySecondNsOnTimeout() throws Exception { - testFollowNsRedirects(NoopDnsCache.INSTANCE, NoopAuthoritativeDnsServerCache.INSTANCE, true); - } - - @Test - public void testFollowNsRedirectAndTrySecondNsOnTimeoutDefaultCaches() throws Exception { - testFollowNsRedirects(new DefaultDnsCache(), new DefaultAuthoritativeDnsServerCache(), true); - } - - private void testFollowNsRedirects(DnsCache cache, AuthoritativeDnsServerCache authoritativeDnsServerCache, - final boolean invalidNsFirst) throws Exception { - final String domain = "netty.io"; - final String ns1Name = "ns1." + domain; - final String ns2Name = "ns2." + domain; - final InetAddress expected = InetAddress.getByAddress("some.record." + domain, new byte[] { 10, 10, 10, 10 }); - - // This is used to simulate a query timeout... - final DatagramSocket socket = new DatagramSocket(new InetSocketAddress(0)); - - final TestDnsServer dnsServerAuthority = new TestDnsServer(new RecordStore() { - @Override - public Set getRecords(QuestionRecord question) { - if (question.getDomainName().equals(expected.getHostName())) { - return Collections.singleton(newARecord( - expected.getHostName(), expected.getHostAddress())); - } - return Collections.emptySet(); - } - }); - dnsServerAuthority.start(); - - TestDnsServer redirectServer = new TestDnsServer(new HashSet<>( - asList(expected.getHostName(), ns1Name, ns2Name))) { - @Override - protected DnsMessage filterMessage(DnsMessage message) { - for (QuestionRecord record: message.getQuestionRecords()) { - if (record.getDomainName().equals(expected.getHostName())) { - message.getAdditionalRecords().clear(); - message.getAnswerRecords().clear(); - if (invalidNsFirst) { - message.getAuthorityRecords().add(TestDnsServer.newNsRecord(domain, ns2Name)); - message.getAuthorityRecords().add(TestDnsServer.newNsRecord(domain, ns1Name)); - } else { - message.getAuthorityRecords().add(TestDnsServer.newNsRecord(domain, ns1Name)); - message.getAuthorityRecords().add(TestDnsServer.newNsRecord(domain, ns2Name)); - } - return message; - } - } - return message; - } - }; - redirectServer.start(); - EventLoopGroup group = new MultithreadEventLoopGroup(1, NioHandler.newFactory()); - final DnsNameResolver resolver = new DnsNameResolver( - group.next(), new ReflectiveChannelFactory(NioDatagramChannel.class), - cache, authoritativeDnsServerCache, NoopDnsQueryLifecycleObserverFactory.INSTANCE, 2000, - ResolvedAddressTypes.IPV4_ONLY, true, 10, 4096, - false, HostsFileEntriesResolver.DEFAULT, - new SingletonDnsServerAddressStreamProvider(redirectServer.localAddress()), - DnsNameResolver.DEFAULT_SEARCH_DOMAINS, 0, true) { - - @Override - InetSocketAddress newRedirectServerAddress(InetAddress server) { - try { - if (server.getHostName().startsWith(ns1Name)) { - return new InetSocketAddress(InetAddress.getByAddress(ns1Name, - dnsServerAuthority.localAddress().getAddress().getAddress()), - dnsServerAuthority.localAddress().getPort()); - } - if (server.getHostName().startsWith(ns2Name)) { - return new InetSocketAddress(InetAddress.getByAddress(ns2Name, - NetUtil.LOCALHOST.getAddress()), socket.getLocalPort()); - } - } catch (UnknownHostException e) { - throw new IllegalStateException(e); - } - return super.newRedirectServerAddress(server); - } - }; - - try { - List resolved = resolver.resolveAll(expected.getHostName()).syncUninterruptibly().getNow(); - assertEquals(1, resolved.size()); - assertEquals(expected, resolved.get(0)); - - List resolved2 = resolver.resolveAll(expected.getHostName()).syncUninterruptibly().getNow(); - assertEquals(1, resolved2.size()); - assertEquals(expected, resolved2.get(0)); - - if (authoritativeDnsServerCache != NoopAuthoritativeDnsServerCache.INSTANCE) { - DnsServerAddressStream cached = authoritativeDnsServerCache.get(domain + '.'); - assertEquals(2, cached.size()); - InetSocketAddress ns1Address = InetSocketAddress.createUnresolved( - ns1Name + '.', DefaultDnsServerAddressStreamProvider.DNS_PORT); - InetSocketAddress ns2Address = InetSocketAddress.createUnresolved( - ns2Name + '.', DefaultDnsServerAddressStreamProvider.DNS_PORT); - - if (invalidNsFirst) { - assertEquals(ns2Address, cached.next()); - assertEquals(ns1Address, cached.next()); - } else { - assertEquals(ns1Address, cached.next()); - assertEquals(ns2Address, cached.next()); - } - } - if (cache != NoopDnsCache.INSTANCE) { - List ns1Cached = cache.get(ns1Name + '.', null); - assertEquals(1, ns1Cached.size()); - DnsCacheEntry nsEntry = ns1Cached.get(0); - assertNotNull(nsEntry.address()); - assertNull(nsEntry.cause()); - - List ns2Cached = cache.get(ns2Name + '.', null); - if (invalidNsFirst) { - assertEquals(1, ns2Cached.size()); - DnsCacheEntry ns2Entry = ns2Cached.get(0); - assertNotNull(ns2Entry.address()); - assertNull(ns2Entry.cause()); - } else { - // We should not even have tried to resolve the DNS name so this should be null. - assertNull(ns2Cached); - } - - List expectedCached = cache.get(expected.getHostName(), null); - assertEquals(1, expectedCached.size()); - DnsCacheEntry expectedEntry = expectedCached.get(0); - assertEquals(expected, expectedEntry.address()); - assertNull(expectedEntry.cause()); - } - } finally { - resolver.close(); - group.shutdownGracefully(0, 0, TimeUnit.SECONDS); - redirectServer.stop(); - dnsServerAuthority.stop(); - socket.close(); - } - } - - @Test - public void testMultipleAdditionalRecordsForSameNSRecord() throws Exception { - testMultipleAdditionalRecordsForSameNSRecord(false); - } - - @Test - public void testMultipleAdditionalRecordsForSameNSRecordReordered() throws Exception { - testMultipleAdditionalRecordsForSameNSRecord(true); - } - - private static void testMultipleAdditionalRecordsForSameNSRecord(final boolean reversed) throws Exception { - final String domain = "netty.io"; - final String hostname = "test.netty.io"; - final String ns1Name = "ns1." + domain; - final InetSocketAddress ns1Address = new InetSocketAddress( - InetAddress.getByAddress(ns1Name, new byte[] { 10, 0, 0, 1 }), - DefaultDnsServerAddressStreamProvider.DNS_PORT); - final InetSocketAddress ns2Address = new InetSocketAddress( - InetAddress.getByAddress(ns1Name, new byte[] { 10, 0, 0, 2 }), - DefaultDnsServerAddressStreamProvider.DNS_PORT); - final InetSocketAddress ns3Address = new InetSocketAddress( - InetAddress.getByAddress(ns1Name, new byte[] { 10, 0, 0, 3 }), - DefaultDnsServerAddressStreamProvider.DNS_PORT); - final InetSocketAddress ns4Address = new InetSocketAddress( - InetAddress.getByAddress(ns1Name, new byte[] { 10, 0, 0, 4 }), - DefaultDnsServerAddressStreamProvider.DNS_PORT); - - TestDnsServer redirectServer = new TestDnsServer(new HashSet<>(asList(hostname, ns1Name))) { - @Override - protected DnsMessage filterMessage(DnsMessage message) { - for (QuestionRecord record: message.getQuestionRecords()) { - if (record.getDomainName().equals(hostname)) { - message.getAdditionalRecords().clear(); - message.getAnswerRecords().clear(); - message.getAuthorityRecords().add(TestDnsServer.newNsRecord(domain, ns1Name)); - message.getAdditionalRecords().add(newARecord(ns1Address)); - message.getAdditionalRecords().add(newARecord(ns2Address)); - message.getAdditionalRecords().add(newARecord(ns3Address)); - message.getAdditionalRecords().add(newARecord(ns4Address)); - return message; - } - } - return message; - } - - private ResourceRecord newARecord(InetSocketAddress address) { - return newARecord(address.getHostName(), address.getAddress().getHostAddress()); - } - }; - redirectServer.start(); - EventLoopGroup group = new MultithreadEventLoopGroup(1, NioHandler.newFactory()); - - final List cached = new CopyOnWriteArrayList<>(); - final AuthoritativeDnsServerCache authoritativeDnsServerCache = new AuthoritativeDnsServerCache() { - @Override - public DnsServerAddressStream get(String hostname) { - return null; - } - - @Override - public void cache(String hostname, InetSocketAddress address, long originalTtl, EventLoop loop) { - cached.add(address); - } - - @Override - public void clear() { - // NOOP - } - - @Override - public boolean clear(String hostname) { - return false; - } - }; - - final AtomicReference redirectedRef = new AtomicReference<>(); - final DnsNameResolver resolver = new DnsNameResolver( - group.next(), new ReflectiveChannelFactory(NioDatagramChannel.class), - NoopDnsCache.INSTANCE, authoritativeDnsServerCache, - NoopDnsQueryLifecycleObserverFactory.INSTANCE, 2000, ResolvedAddressTypes.IPV4_ONLY, - true, 10, 4096, - false, HostsFileEntriesResolver.DEFAULT, - new SingletonDnsServerAddressStreamProvider(redirectServer.localAddress()), - DnsNameResolver.DEFAULT_SEARCH_DOMAINS, 0, true) { - - @Override - protected DnsServerAddressStream newRedirectDnsServerStream( - String hostname, List nameservers) { - if (reversed) { - Collections.reverse(nameservers); - } - DnsServerAddressStream stream = new SequentialDnsServerAddressStream(nameservers, 0); - redirectedRef.set(stream); - return stream; - } - }; - - try { - Throwable cause = resolver.resolveAll(hostname).await().cause(); - assertTrue(cause instanceof UnknownHostException); - DnsServerAddressStream redirected = redirectedRef.get(); - assertNotNull(redirected); - assertEquals(4, redirected.size()); - assertEquals(4, cached.size()); - - if (reversed) { - assertEquals(ns4Address, redirected.next()); - assertEquals(ns3Address, redirected.next()); - assertEquals(ns2Address, redirected.next()); - assertEquals(ns1Address, redirected.next()); - } else { - assertEquals(ns1Address, redirected.next()); - assertEquals(ns2Address, redirected.next()); - assertEquals(ns3Address, redirected.next()); - assertEquals(ns4Address, redirected.next()); - } - - // We should always have the same order in the cache. - assertEquals(ns1Address, cached.get(0)); - assertEquals(ns2Address, cached.get(1)); - assertEquals(ns3Address, cached.get(2)); - assertEquals(ns4Address, cached.get(3)); - } finally { - resolver.close(); - group.shutdownGracefully(0, 0, TimeUnit.SECONDS); - redirectServer.stop(); - } - } - - @Test - public void testNSRecordsFromCache() throws Exception { - final String domain = "netty.io"; - final String hostname = "test.netty.io"; - final String ns0Name = "ns0." + domain + '.'; - final String ns1Name = "ns1." + domain + '.'; - final String ns2Name = "ns2." + domain + '.'; - - final InetSocketAddress ns0Address = new InetSocketAddress( - InetAddress.getByAddress(ns0Name, new byte[] { 10, 1, 0, 1 }), - DefaultDnsServerAddressStreamProvider.DNS_PORT); - final InetSocketAddress ns1Address = new InetSocketAddress( - InetAddress.getByAddress(ns1Name, new byte[] { 10, 0, 0, 1 }), - DefaultDnsServerAddressStreamProvider.DNS_PORT); - final InetSocketAddress ns2Address = new InetSocketAddress( - InetAddress.getByAddress(ns1Name, new byte[] { 10, 0, 0, 2 }), - DefaultDnsServerAddressStreamProvider.DNS_PORT); - final InetSocketAddress ns3Address = new InetSocketAddress( - InetAddress.getByAddress(ns1Name, new byte[] { 10, 0, 0, 3 }), - DefaultDnsServerAddressStreamProvider.DNS_PORT); - final InetSocketAddress ns4Address = new InetSocketAddress( - InetAddress.getByAddress(ns1Name, new byte[] { 10, 0, 0, 4 }), - DefaultDnsServerAddressStreamProvider.DNS_PORT); - final InetSocketAddress ns5Address = new InetSocketAddress( - InetAddress.getByAddress(ns2Name, new byte[] { 10, 0, 0, 5 }), - DefaultDnsServerAddressStreamProvider.DNS_PORT); - - TestDnsServer redirectServer = new TestDnsServer(new HashSet<>(asList(hostname, ns1Name))) { - @Override - protected DnsMessage filterMessage(DnsMessage message) { - for (QuestionRecord record: message.getQuestionRecords()) { - if (record.getDomainName().equals(hostname)) { - message.getAdditionalRecords().clear(); - message.getAnswerRecords().clear(); - message.getAuthorityRecords().add(TestDnsServer.newNsRecord(domain, ns0Name)); - message.getAuthorityRecords().add(TestDnsServer.newNsRecord(domain, ns1Name)); - message.getAuthorityRecords().add(TestDnsServer.newNsRecord(domain, ns2Name)); - - message.getAdditionalRecords().add(newARecord(ns0Address)); - message.getAdditionalRecords().add(newARecord(ns5Address)); - - return message; - } - } - return message; - } - - private ResourceRecord newARecord(InetSocketAddress address) { - return newARecord(address.getHostName(), address.getAddress().getHostAddress()); - } - }; - redirectServer.start(); - EventLoopGroup group = new MultithreadEventLoopGroup(1, NioHandler.newFactory()); - - final List cached = new CopyOnWriteArrayList<>(); - final AuthoritativeDnsServerCache authoritativeDnsServerCache = new AuthoritativeDnsServerCache() { - @Override - public DnsServerAddressStream get(String hostname) { - return null; - } - - @Override - public void cache(String hostname, InetSocketAddress address, long originalTtl, EventLoop loop) { - cached.add(address); - } - - @Override - public void clear() { - // NOOP - } - - @Override - public boolean clear(String hostname) { - return false; - } - }; - - EventLoop loop = group.next(); - DefaultDnsCache cache = new DefaultDnsCache(); - cache.cache(ns1Name, null, ns1Address.getAddress(), 10000, loop); - cache.cache(ns1Name, null, ns2Address.getAddress(), 10000, loop); - cache.cache(ns1Name, null, ns3Address.getAddress(), 10000, loop); - cache.cache(ns1Name, null, ns4Address.getAddress(), 10000, loop); - - final AtomicReference redirectedRef = new AtomicReference<>(); - final DnsNameResolver resolver = new DnsNameResolver( - loop, new ReflectiveChannelFactory(NioDatagramChannel.class), - cache, authoritativeDnsServerCache, - NoopDnsQueryLifecycleObserverFactory.INSTANCE, 2000, ResolvedAddressTypes.IPV4_ONLY, - true, 10, 4096, - false, HostsFileEntriesResolver.DEFAULT, - new SingletonDnsServerAddressStreamProvider(redirectServer.localAddress()), - DnsNameResolver.DEFAULT_SEARCH_DOMAINS, 0, true) { - - @Override - protected DnsServerAddressStream newRedirectDnsServerStream( - String hostname, List nameservers) { - DnsServerAddressStream stream = new SequentialDnsServerAddressStream(nameservers, 0); - redirectedRef.set(stream); - return stream; - } - }; - - try { - Throwable cause = resolver.resolveAll(hostname).await().cause(); - assertTrue(cause instanceof UnknownHostException); - DnsServerAddressStream redirected = redirectedRef.get(); - assertNotNull(redirected); - assertEquals(6, redirected.size()); - assertEquals(3, cached.size()); - - // The redirected addresses should have been retrieven from the DnsCache if not resolved, so these are - // fully resolved. - assertEquals(ns0Address, redirected.next()); - assertEquals(ns1Address, redirected.next()); - assertEquals(ns2Address, redirected.next()); - assertEquals(ns3Address, redirected.next()); - assertEquals(ns4Address, redirected.next()); - assertEquals(ns5Address, redirected.next()); - - // As this address was supplied as ADDITIONAL we should put it resolved into the cache. - assertEquals(ns0Address, cached.get(0)); - assertEquals(ns5Address, cached.get(1)); - - // We should have put the unresolved address in the AuthoritativeDnsServerCache (but only 1 time) - assertEquals(unresolved(ns1Address), cached.get(2)); - } finally { - resolver.close(); - group.shutdownGracefully(0, 0, TimeUnit.SECONDS); - redirectServer.stop(); - } - } - - @Test - public void testNsLoopFailsResolveWithAuthoritativeDnsServerCache() throws Exception { - testNsLoopFailsResolve(new DefaultAuthoritativeDnsServerCache()); - } - - @Test - public void testNsLoopFailsResolveWithoutAuthoritativeDnsServerCache() throws Exception { - testNsLoopFailsResolve(NoopAuthoritativeDnsServerCache.INSTANCE); - } - - @Test - public void testRRNameContainsDifferentSearchDomainNoDomains() { - CompletionException e = assertThrows(CompletionException.class, new Executable() { - @Override - public void execute() throws Throwable { - testRRNameContainsDifferentSearchDomain(Collections.emptyList(), "netty"); - } - }); - assertThat(e.getCause(), instanceOf(UnknownHostException.class)); - } - - @Test - public void testRRNameContainsDifferentSearchDomainEmptyExtraDomain() throws Exception { - testRRNameContainsDifferentSearchDomain(asList("io", ""), "netty"); - } - - @Test - public void testRRNameContainsDifferentSearchDomainSingleExtraDomain() throws Exception { - testRRNameContainsDifferentSearchDomain(asList("io", "foo.dom"), "netty"); - } - - @Test - public void testRRNameContainsDifferentSearchDomainMultiExtraDomains() throws Exception { - testRRNameContainsDifferentSearchDomain(asList("com", "foo.dom", "bar.dom"), "google"); - } - - private static void testRRNameContainsDifferentSearchDomain(final List searchDomains, String unresolved) - throws Exception { - final String ipAddrPrefix = "1.2.3."; - TestDnsServer searchDomainServer = new TestDnsServer(new RecordStore() { - @Override - public Set getRecords(QuestionRecord questionRecord) { - Set records = new HashSet(searchDomains.size()); - final String qName = questionRecord.getDomainName(); - for (String searchDomain : searchDomains) { - if (qName.endsWith(searchDomain)) { - continue; - } - final ResourceRecord rr = newARecord(qName + '.' + searchDomain, - ipAddrPrefix + ThreadLocalRandom.current().nextInt(1, 10)); - logger.info("Adding A record: " + rr); - records.add(rr); - } - return records; - } - }); - searchDomainServer.start(); - - final DnsNameResolver resolver = newResolver(false, null, searchDomainServer) - .searchDomains(searchDomains) - .build(); - - try { - final List addresses = resolver.resolveAll(unresolved).sync().get(); - assertThat(addresses, hasSize(greaterThan(0))); - for (InetAddress address : addresses) { - assertThat(address.getHostName(), startsWith(unresolved)); - assertThat(address.getHostAddress(), startsWith(ipAddrPrefix)); - } - } finally { - resolver.close(); - searchDomainServer.stop(); - } - } - - private static void testNsLoopFailsResolve(AuthoritativeDnsServerCache authoritativeDnsServerCache) - throws Exception { - final String domain = "netty.io"; - final String ns1Name = "ns1." + domain; - final String ns2Name = "ns2." + domain; - - TestDnsServer testDnsServer = new TestDnsServer(new HashSet( - singletonList(domain))) { - - @Override - protected DnsMessage filterMessage(DnsMessage message) { - // Just always return NS records only without any additional records (glue records). - // Because of this the resolver will never be able to resolve and so fail eventually at some - // point. - for (QuestionRecord record: message.getQuestionRecords()) { - if (record.getDomainName().equals(domain)) { - message.getAdditionalRecords().clear(); - message.getAnswerRecords().clear(); - message.getAuthorityRecords().add(TestDnsServer.newNsRecord(domain, ns1Name)); - message.getAuthorityRecords().add(TestDnsServer.newNsRecord(domain, ns2Name)); - } - } - return message; - } - }; - testDnsServer.start(); - DnsNameResolverBuilder builder = newResolver(); - - final DnsNameResolver resolver = builder.resolveCache(NoopDnsCache.INSTANCE) - .authoritativeDnsServerCache(authoritativeDnsServerCache) - .nameServerProvider(new SingletonDnsServerAddressStreamProvider(testDnsServer.localAddress())).build(); - - try { - assertThat(resolver.resolve(domain).await().cause(), - instanceOf(UnknownHostException.class)); - assertThat(resolver.resolveAll(domain).await().cause(), - instanceOf(UnknownHostException.class)); - } finally { - resolver.close(); - testDnsServer.stop(); - } - } - - private static InetSocketAddress unresolved(InetSocketAddress address) { - return InetSocketAddress.createUnresolved(address.getHostString(), address.getPort()); - } - - private static void resolve(DnsNameResolver resolver, Map> futures, String hostname) { - futures.put(hostname, resolver.resolve(hostname)); - } - - private static void queryMx( - DnsNameResolver resolver, - Map>> futures, - String hostname) { - futures.put(hostname, resolver.query(new DefaultDnsQuestion(hostname, DnsRecordType.MX))); - } - - private static void assertNoQueriesMade(DnsNameResolver resolver) { - TestRecursiveCacheDnsQueryLifecycleObserverFactory lifecycleObserverFactory = - (TestRecursiveCacheDnsQueryLifecycleObserverFactory) resolver.dnsQueryLifecycleObserverFactory(); - assertTrue(lifecycleObserverFactory.observers.isEmpty()); - } - - private static void assertQueryObserver(DnsNameResolver resolver, DnsRecordType cancelledType) { - TestRecursiveCacheDnsQueryLifecycleObserverFactory lifecycleObserverFactory = - (TestRecursiveCacheDnsQueryLifecycleObserverFactory) resolver.dnsQueryLifecycleObserverFactory(); - TestDnsQueryLifecycleObserver observer; - while ((observer = lifecycleObserverFactory.observers.poll()) != null) { - Object o = observer.events.poll(); - if (o instanceof QueryCancelledEvent) { - assertEquals(cancelledType, observer.question.type()); - } else if (o instanceof QueryWrittenEvent) { - QuerySucceededEvent succeededEvent = (QuerySucceededEvent) observer.events.poll(); - } else { - fail("unexpected event type: " + o); - } - assertTrue(observer.events.isEmpty()); - } - } - - private static final class TestRecursiveCacheDnsQueryLifecycleObserverFactory - implements DnsQueryLifecycleObserverFactory { - final Queue observers = - new ConcurrentLinkedQueue<>(); - @Override - public DnsQueryLifecycleObserver newDnsQueryLifecycleObserver(DnsQuestion question) { - TestDnsQueryLifecycleObserver observer = new TestDnsQueryLifecycleObserver(question); - observers.add(observer); - return observer; - } - } - - private static final class QueryWrittenEvent { - final InetSocketAddress dnsServerAddress; - - QueryWrittenEvent(InetSocketAddress dnsServerAddress) { - this.dnsServerAddress = dnsServerAddress; - } - } - - private static final class QueryCancelledEvent { - final int queriesRemaining; - - QueryCancelledEvent(int queriesRemaining) { - this.queriesRemaining = queriesRemaining; - } - } - - private static final class QueryRedirectedEvent { - final List nameServers; - - QueryRedirectedEvent(List nameServers) { - this.nameServers = nameServers; - } - } - - private static final class QueryCnamedEvent { - final DnsQuestion question; - - QueryCnamedEvent(DnsQuestion question) { - this.question = question; - } - } - - private static final class QueryNoAnswerEvent { - final DnsResponseCode code; - - QueryNoAnswerEvent(DnsResponseCode code) { - this.code = code; - } - } - - private static final class QueryFailedEvent { - final Throwable cause; - - QueryFailedEvent(Throwable cause) { - this.cause = cause; - } - } - - private static final class QuerySucceededEvent { - } - - private static final class TestDnsQueryLifecycleObserver implements DnsQueryLifecycleObserver { - final Queue events = new ArrayDeque<>(); - final DnsQuestion question; - - TestDnsQueryLifecycleObserver(DnsQuestion question) { - this.question = question; - } - - @Override - public void queryWritten(InetSocketAddress dnsServerAddress, Future future) { - events.add(new QueryWrittenEvent(dnsServerAddress)); - } - - @Override - public void queryCancelled(int queriesRemaining) { - events.add(new QueryCancelledEvent(queriesRemaining)); - } - - @Override - public DnsQueryLifecycleObserver queryRedirected(List nameServers) { - events.add(new QueryRedirectedEvent(nameServers)); - return this; - } - - @Override - public DnsQueryLifecycleObserver queryCNAMEd(DnsQuestion cnameQuestion) { - events.add(new QueryCnamedEvent(cnameQuestion)); - return this; - } - - @Override - public DnsQueryLifecycleObserver queryNoAnswer(DnsResponseCode code) { - events.add(new QueryNoAnswerEvent(code)); - return this; - } - - @Override - public void queryFailed(Throwable cause) { - events.add(new QueryFailedEvent(cause)); - } - - @Override - public void querySucceed() { - events.add(new QuerySucceededEvent()); - } - } - - private static final class TestAuthoritativeDnsServerCache implements AuthoritativeDnsServerCache { - final AuthoritativeDnsServerCache cache; - final Map cacheHits = new HashMap<>(); - - TestAuthoritativeDnsServerCache(AuthoritativeDnsServerCache cache) { - this.cache = cache; - } - - @Override - public void clear() { - cache.clear(); - } - - @Override - public boolean clear(String hostname) { - return cache.clear(hostname); - } - - @Override - public DnsServerAddressStream get(String hostname) { - DnsServerAddressStream cached = cache.get(hostname); - if (cached != null) { - cacheHits.put(hostname, cached.duplicate()); - } - return cached; - } - - @Override - public void cache(String hostname, InetSocketAddress address, long originalTtl, EventLoop loop) { - cache.cache(hostname, address, originalTtl, loop); - } - } - - private static final class TestDnsCache implements DnsCache { - final DnsCache cache; - final Map> cacheHits = - new HashMap<>(); - - TestDnsCache(DnsCache cache) { - this.cache = cache; - } - - @Override - public void clear() { - cache.clear(); - } - - @Override - public boolean clear(String hostname) { - return cache.clear(hostname); - } - - @Override - public List get(String hostname, DnsRecord[] additionals) { - List cached = cache.get(hostname, additionals); - cacheHits.put(hostname, cached); - return cached; - } - - @Override - public DnsCacheEntry cache(String hostname, DnsRecord[] additionals, InetAddress address, - long originalTtl, EventLoop loop) { - return cache.cache(hostname, additionals, address, originalTtl, loop); - } - - @Override - public DnsCacheEntry cache(String hostname, DnsRecord[] additionals, Throwable cause, EventLoop loop) { - return cache.cache(hostname, additionals, cause, loop); - } - } - - private static class RedirectingTestDnsServer extends TestDnsServer { - - private final String dnsAddress; - private final String domain; - - RedirectingTestDnsServer(String domain, String dnsAddress) { - super(Collections.singleton(domain)); - this.domain = domain; - this.dnsAddress = dnsAddress; - } - - @Override - protected DnsMessage filterMessage(DnsMessage message) { - // Clear the answers as we want to add our own stuff to test dns redirects. - message.getAnswerRecords().clear(); - message.getAuthorityRecords().clear(); - message.getAdditionalRecords().clear(); - - String name = domain; - for (int i = 0 ;; i++) { - int idx = name.indexOf('.'); - if (idx <= 0) { - break; - } - name = name.substring(idx + 1); // skip the '.' as well. - String dnsName = "dns" + idx + '.' + domain; - message.getAuthorityRecords().add(newNsRecord(name, dnsName)); - message.getAdditionalRecords().add(newARecord(dnsName, i == 0 ? dnsAddress : "1.2.3." + idx)); - - // Add an unresolved NS record (with no additionals as well) - message.getAuthorityRecords().add(newNsRecord(name, "unresolved." + dnsName)); - } - - return message; - } - } - - @Test - @Timeout(value = 3000, unit = TimeUnit.MILLISECONDS) - public void testTimeoutNotCached() { - DnsCache cache = new DnsCache() { - @Override - public void clear() { - // NOOP - } - - @Override - public boolean clear(String hostname) { - return false; - } - - @Override - public List get(String hostname, DnsRecord[] additionals) { - return Collections.emptyList(); - } - - @Override - public DnsCacheEntry cache(String hostname, DnsRecord[] additionals, InetAddress address, - long originalTtl, EventLoop loop) { - fail("Should not be cached"); - return null; - } - - @Override - public DnsCacheEntry cache(String hostname, DnsRecord[] additionals, Throwable cause, EventLoop loop) { - fail("Should not be cached"); - return null; - } - }; - DnsNameResolverBuilder builder = newResolver(); - builder.queryTimeoutMillis(100) - .authoritativeDnsServerCache(cache) - .resolveCache(cache) - .nameServerProvider(new SingletonDnsServerAddressStreamProvider( - new InetSocketAddress(NetUtil.LOCALHOST, 12345))); - DnsNameResolver resolver = builder.build(); - Future result = resolver.resolve("doesnotexist.netty.io").awaitUninterruptibly(); - Throwable cause = result.cause(); - assertTrue(cause instanceof UnknownHostException); - assertTrue(cause.getCause() instanceof DnsNameResolverTimeoutException); - assertTrue(DnsNameResolver.isTimeoutError(cause)); - assertTrue(DnsNameResolver.isTransportOrTimeoutError(cause)); - resolver.close(); - } - - @Test - public void testDnsNameResolverBuilderCopy() { - ChannelFactory channelFactory = - new ReflectiveChannelFactory<>(NioDatagramChannel.class); - DnsNameResolverBuilder builder = new DnsNameResolverBuilder(group.next()) - .channelFactory(channelFactory); - DnsNameResolverBuilder copiedBuilder = builder.copy(); - - // change channel factory does not propagate to previously made copy - ChannelFactory newChannelFactory = - new ReflectiveChannelFactory<>(NioDatagramChannel.class); - builder.channelFactory(newChannelFactory); - assertEquals(channelFactory, copiedBuilder.channelFactory()); - assertEquals(newChannelFactory, builder.channelFactory()); - } - - @Test - public void testFollowCNAMEEvenIfARecordIsPresent() throws IOException { - TestDnsServer dnsServer2 = new TestDnsServer(question -> { - if (question.getDomainName().equals("cname.netty.io")) { - Map map1 = new HashMap<>(); - map1.put(DnsAttribute.IP_ADDRESS.toLowerCase(), "10.0.0.99"); - return Collections.singleton( - new TestDnsServer.TestResourceRecord(question.getDomainName(), RecordType.A, map1)); - } else { - Set records = new LinkedHashSet<>(2); - Map map = new HashMap<>(); - map.put(DnsAttribute.DOMAIN_NAME.toLowerCase(), "cname.netty.io"); - records.add(new TestDnsServer.TestResourceRecord( - question.getDomainName(), RecordType.CNAME, map)); - - Map map1 = new HashMap<>(); - map1.put(DnsAttribute.IP_ADDRESS.toLowerCase(), "10.0.0.2"); - records.add(new TestDnsServer.TestResourceRecord( - question.getDomainName(), RecordType.A, map1)); - return records; - } - }); - dnsServer2.start(); - DnsNameResolver resolver = null; - try { - DnsNameResolverBuilder builder = newResolver() - .recursionDesired(true) - .resolvedAddressTypes(ResolvedAddressTypes.IPV4_ONLY) - .maxQueriesPerResolve(16) - .nameServerProvider(new SingletonDnsServerAddressStreamProvider(dnsServer2.localAddress())); - - resolver = builder.build(); - List resolvedAddresses = - resolver.resolveAll("somehost.netty.io").syncUninterruptibly().getNow(); - assertEquals(2, resolvedAddresses.size()); - assertTrue(resolvedAddresses.contains(InetAddress.getByAddress(new byte[] { 10, 0, 0, 99 }))); - assertTrue(resolvedAddresses.contains(InetAddress.getByAddress(new byte[] { 10, 0, 0, 2 }))); - } finally { - dnsServer2.stop(); - if (resolver != null) { - resolver.close(); - } - } - } - - @Test - public void testFollowCNAMELoop() throws Throwable { - TestDnsServer dnsServer2 = new TestDnsServer(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 = new DnsNameResolver[1]; - try { - DnsNameResolverBuilder builder = newResolver() - .recursionDesired(false) - .resolvedAddressTypes(ResolvedAddressTypes.IPV4_ONLY) - .maxQueriesPerResolve(16) - .nameServerProvider(new SingletonDnsServerAddressStreamProvider(dnsServer2.localAddress())); - - resolver[0] = builder.build(); - final CompletionException completion = assertThrows(CompletionException.class, - () -> resolver[0].resolveAll("somehost.netty.io").syncUninterruptibly().getNow()); - assertTrue(completion.getCause() instanceof UnknownHostException); - } catch (CompletionException e) { - throw e.getCause(); - } finally { - dnsServer2.stop(); - if (resolver[0] != null) { - resolver[0].close(); - } - } - } - - @Test - public void testCNAMELoopInCache() throws Throwable { - DnsNameResolver[] resolver = new DnsNameResolver[1]; - try { - DnsNameResolverBuilder builder = newResolver() - .recursionDesired(false) - .resolvedAddressTypes(ResolvedAddressTypes.IPV4_ONLY) - .maxQueriesPerResolve(16) - .nameServerProvider(new SingletonDnsServerAddressStreamProvider(dnsServer.localAddress())); - - resolver[0] = builder.build(); - // Add a CNAME loop into the cache - String name = "somehost.netty.io."; - String name2 = "cname.netty.io."; - - resolver[0].cnameCache().cache(name, name2, Long.MAX_VALUE, resolver[0].executor()); - resolver[0].cnameCache().cache(name2, name, Long.MAX_VALUE, resolver[0].executor()); - final CompletionException completion = assertThrows(CompletionException.class, - () -> resolver[0].resolve(name).syncUninterruptibly().getNow()); - assertTrue(completion.getCause() instanceof UnknownHostException); - } finally { - if (resolver[0] != null) { - resolver[0].close(); - } - } - } - - @Test - public void testSearchDomainQueryFailureForSingleAddressTypeCompletes() throws Throwable { - assertThrows(UnknownHostException.class, - () -> testSearchDomainQueryFailureCompletes(ResolvedAddressTypes.IPV4_ONLY)); - } - - @Test - public void testSearchDomainQueryFailureForMultipleAddressTypeCompletes() throws Throwable { - assertThrows(UnknownHostException.class, - () -> testSearchDomainQueryFailureCompletes(ResolvedAddressTypes.IPV4_PREFERRED)); - } - - private static void testSearchDomainQueryFailureCompletes(ResolvedAddressTypes types) throws Throwable { - DnsNameResolver resolver = newResolver() - .resolvedAddressTypes(types) - .ndots(1) - .searchDomains(singletonList(".")).build(); - try { - resolver.resolve("invalid.com").syncUninterruptibly(); - } catch (CompletionException cause) { - throw cause.getCause(); - } finally { - resolver.close(); - } - } - - @Test - @Timeout(value = 2000, unit = TimeUnit.MILLISECONDS) - 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(); - } - - @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(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(); - } - } - - @Test - public void testChannelFactoryException() { - final IllegalStateException exception = new IllegalStateException(); - try { - newResolver().channelFactory(eventLoop -> { - throw exception; - }).build(); - fail(); - } catch (Exception e) { - 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(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.endsWith("."), hostname); - return cache.get(hostname); - } - - @Override - public void cache(String hostname, String cname, long originalTtl, EventLoop loop) { - assertTrue(hostname.endsWith("."), hostname); - 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(); - } - } - } - - @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 - 4096, // maxPayloadSize - true, // optResourceEnabled - HostsFileEntriesResolver.DEFAULT, // hostsFileEntriesResolver - DnsServerAddressStreamProviders.platformDefault(), // dnsServerAddressStreamProvider - null, // searchDomains - 1, // ndots - true // decodeIdn - ).close(); - } - - @Test - public void testQueryTxt() throws Exception { - final String hostname = "txt.netty.io"; - final String txt1 = "some text"; - final String txt2 = "some more text"; - - TestDnsServer server = new TestDnsServer(question -> { - if (question.getDomainName().equals(hostname)) { - Map map1 = new HashMap(); - map1.put(DnsAttribute.CHARACTER_STRING.toLowerCase(), txt1); - - Map map2 = new HashMap(); - map2.put(DnsAttribute.CHARACTER_STRING.toLowerCase(), txt2); - - Set records = new HashSet(); - records.add(new TestDnsServer.TestResourceRecord(question.getDomainName(), RecordType.TXT, map1)); - records.add(new TestDnsServer.TestResourceRecord(question.getDomainName(), RecordType.TXT, map2)); - return records; - } - return Collections.emptySet(); - }); - server.start(); - DnsNameResolver resolver = newResolver(ResolvedAddressTypes.IPV4_ONLY) - .nameServerProvider(new SingletonDnsServerAddressStreamProvider(server.localAddress())) - .build(); - try { - AddressedEnvelope envelope = resolver.query( - new DefaultDnsQuestion(hostname, DnsRecordType.TXT)).syncUninterruptibly().getNow(); - assertNotNull(envelope.sender()); - - DnsResponse response = envelope.content(); - assertNotNull(response); - - assertEquals(DnsResponseCode.NOERROR, response.code()); - int count = response.count(DnsSection.ANSWER); - - assertEquals(2, count); - List txts = new ArrayList(); - - for (int i = 0; i < 2; i++) { - txts.addAll(decodeTxt(response.recordAt(DnsSection.ANSWER, i))); - } - assertTrue(txts.contains(txt1)); - assertTrue(txts.contains(txt2)); - envelope.release(); - } finally { - resolver.close(); - server.stop(); - } - } - - private static List decodeTxt(DnsRecord record) { - if (!(record instanceof DnsRawRecord)) { - return Collections.emptyList(); - } - List list = new ArrayList(); - ByteBuf data = ((DnsRawRecord) record).content(); - int idx = data.readerIndex(); - int wIdx = data.writerIndex(); - while (idx < wIdx) { - int len = data.getUnsignedByte(idx++); - list.add(data.toString(idx, len, CharsetUtil.UTF_8)); - idx += len; - } - return list; - } - - @Test - public void testNotIncludeDuplicates() throws IOException { - final String name = "netty.io"; - final String ipv4Addr = "1.2.3.4"; - TestDnsServer dnsServer2 = new TestDnsServer(question -> { - Set records = new LinkedHashSet(4); - String qName = question.getDomainName().toLowerCase(); - if (qName.equals(name)) { - records.add(new TestDnsServer.TestResourceRecord( - qName, RecordType.CNAME, - Collections.singletonMap( - DnsAttribute.DOMAIN_NAME.toLowerCase(), "cname.netty.io"))); - } - records.add(new TestDnsServer.TestResourceRecord(qName, - RecordType.A, Collections.singletonMap( - DnsAttribute.IP_ADDRESS.toLowerCase(), ipv4Addr))); - return records; - }); - dnsServer2.start(); - DnsNameResolver resolver = null; - try { - DnsNameResolverBuilder builder = newResolver() - .recursionDesired(true) - .maxQueriesPerResolve(16) - .nameServerProvider(new SingletonDnsServerAddressStreamProvider(dnsServer2.localAddress())); - builder.resolvedAddressTypes(ResolvedAddressTypes.IPV4_ONLY); - - resolver = builder.build(); - List resolvedAddresses = resolver.resolveAll(name).syncUninterruptibly().getNow(); - assertEquals(singletonList(InetAddress.getByAddress(name, new byte[] { 1, 2, 3, 4 })), - resolvedAddresses); - } finally { - dnsServer2.stop(); - if (resolver != null) { - resolver.close(); - } - } - } - - @Test - public void testIncludeDuplicates() throws IOException { - final String name = "netty.io"; - final String ipv4Addr = "1.2.3.4"; - TestDnsServer dnsServer2 = new TestDnsServer(question -> { - Set records = new LinkedHashSet(2); - String qName = question.getDomainName().toLowerCase(); - records.add(new TestDnsServer.TestResourceRecord(qName, - RecordType.A, Collections.singletonMap( - DnsAttribute.IP_ADDRESS.toLowerCase(), ipv4Addr))); - records.add(new TestDnsServer.TestResourceRecord(qName, - RecordType.A, Collections.singletonMap( - DnsAttribute.IP_ADDRESS.toLowerCase(), ipv4Addr))); - return records; - }); - dnsServer2.start(); - DnsNameResolver resolver = null; - try { - DnsNameResolverBuilder builder = newResolver() - .recursionDesired(true) - .maxQueriesPerResolve(16) - .nameServerProvider(new SingletonDnsServerAddressStreamProvider(dnsServer2.localAddress())); - builder.resolvedAddressTypes(ResolvedAddressTypes.IPV4_ONLY); - - resolver = builder.build(); - List resolvedAddresses = resolver.resolveAll(new DefaultDnsQuestion(name, A)) - .syncUninterruptibly().getNow(); - assertEquals(2, resolvedAddresses.size()); - for (DnsRecord record: resolvedAddresses) { - ReferenceCountUtil.release(record); - } - } finally { - dnsServer2.stop(); - if (resolver != null) { - resolver.close(); - } - } - } - - @Test - public void testDropAAAA() throws IOException { - String host = "somehost.netty.io"; - TestDnsServer dnsServer2 = new TestDnsServer(Collections.singleton(host)); - dnsServer2.start(true); - DnsNameResolver resolver = null; - try { - DnsNameResolverBuilder builder = newResolver() - .recursionDesired(false) - .queryTimeoutMillis(500) - .resolvedAddressTypes(ResolvedAddressTypes.IPV4_PREFERRED) - .maxQueriesPerResolve(16) - .nameServerProvider(new SingletonDnsServerAddressStreamProvider(dnsServer2.localAddress())); - - resolver = builder.build(); - List addressList = resolver.resolveAll(host).syncUninterruptibly().getNow(); - assertEquals(1, addressList.size()); - assertEquals(host, addressList.get(0).getHostName()); - } finally { - dnsServer2.stop(); - if (resolver != null) { - resolver.close(); - } - } - } - - @Test - @Timeout(value = 2000, unit = TimeUnit.MILLISECONDS) - public void testDropAAAAResolveFast() throws IOException { - String host = "somehost.netty.io"; - TestDnsServer dnsServer2 = new TestDnsServer(Collections.singleton(host)); - dnsServer2.start(true); - DnsNameResolver resolver = null; - try { - DnsNameResolverBuilder builder = newResolver() - .recursionDesired(false) - .queryTimeoutMillis(10000) - .resolvedAddressTypes(ResolvedAddressTypes.IPV4_PREFERRED) - .maxQueriesPerResolve(16) - .nameServerProvider(new SingletonDnsServerAddressStreamProvider(dnsServer2.localAddress())); - - resolver = builder.build(); - InetAddress address = resolver.resolve(host).syncUninterruptibly().getNow(); - assertEquals(host, address.getHostName()); - } finally { - dnsServer2.stop(); - if (resolver != null) { - resolver.close(); - } - } - } - - @Test - @Timeout(value = 2000, unit = TimeUnit.MILLISECONDS) - public void testDropAAAAResolveAllFast() throws IOException { - final String host = "somehost.netty.io"; - TestDnsServer dnsServer2 = new TestDnsServer(question -> { - String name = question.getDomainName(); - if (name.equals(host)) { - Set records = new HashSet(2); - records.add(new TestDnsServer.TestResourceRecord(name, RecordType.A, - Collections.singletonMap(DnsAttribute.IP_ADDRESS.toLowerCase(), - "10.0.0.1"))); - records.add(new TestDnsServer.TestResourceRecord(name, RecordType.A, - Collections.singletonMap(DnsAttribute.IP_ADDRESS.toLowerCase(), - "10.0.0.2"))); - return records; - } - return null; - }); - dnsServer2.start(true); - DnsNameResolver resolver = null; - try { - DnsNameResolverBuilder builder = newResolver() - .recursionDesired(false) - .queryTimeoutMillis(10000) - .resolvedAddressTypes(ResolvedAddressTypes.IPV4_PREFERRED) - .completeOncePreferredResolved(true) - .maxQueriesPerResolve(16) - .nameServerProvider(new SingletonDnsServerAddressStreamProvider(dnsServer2.localAddress())); - - resolver = builder.build(); - List addresses = resolver.resolveAll(host).syncUninterruptibly().getNow(); - assertEquals(2, addresses.size()); - for (InetAddress address: addresses) { - assertThat(address, instanceOf(Inet4Address.class)); - assertEquals(host, address.getHostName()); - } - } finally { - dnsServer2.stop(); - if (resolver != null) { - resolver.close(); - } - } - } - - @Test - @Timeout(value = 5000, unit = TimeUnit.MILLISECONDS) - public void testTruncatedWithoutTcpFallback() throws IOException { - testTruncated0(false, false); - } - - @Test - @Timeout(value = 5000, unit = TimeUnit.MILLISECONDS) - public void testTruncatedWithTcpFallback() throws IOException { - testTruncated0(true, false); - } - - @Test - @Timeout(value = 5000, unit = TimeUnit.MILLISECONDS) - public void testTruncatedWithTcpFallbackBecauseOfMtu() throws IOException { - testTruncated0(true, true); - } - - private static DnsMessageModifier modifierFrom(DnsMessage message) { - DnsMessageModifier modifier = new DnsMessageModifier(); - modifier.setAcceptNonAuthenticatedData(message.isAcceptNonAuthenticatedData()); - modifier.setAdditionalRecords(message.getAdditionalRecords()); - modifier.setAnswerRecords(message.getAnswerRecords()); - modifier.setAuthoritativeAnswer(message.isAuthoritativeAnswer()); - modifier.setAuthorityRecords(message.getAuthorityRecords()); - modifier.setMessageType(message.getMessageType()); - modifier.setOpCode(message.getOpCode()); - modifier.setQuestionRecords(message.getQuestionRecords()); - modifier.setRecursionAvailable(message.isRecursionAvailable()); - modifier.setRecursionDesired(message.isRecursionDesired()); - modifier.setReserved(message.isReserved()); - modifier.setResponseCode(message.getResponseCode()); - modifier.setTransactionId(message.getTransactionId()); - modifier.setTruncated(message.isTruncated()); - return modifier; - } - - private static void testTruncated0(boolean tcpFallback, final boolean truncatedBecauseOfMtu) throws IOException { - final String host = "somehost.netty.io"; - final String txt = "this is a txt record"; - final AtomicReference messageRef = new AtomicReference(); - - TestDnsServer dnsServer2 = new TestDnsServer(question -> { - String name = question.getDomainName(); - if (name.equals(host)) { - return Collections.singleton( - new TestDnsServer.TestResourceRecord(name, RecordType.TXT, - Collections.singletonMap( - DnsAttribute.CHARACTER_STRING.toLowerCase(), txt))); - } - return null; - }) { - @Override - protected DnsMessage filterMessage(DnsMessage message) { - // Store a original message so we can replay it later on. - messageRef.set(message); - - if (!truncatedBecauseOfMtu) { - // Create a copy of the message but set the truncated flag. - DnsMessageModifier modifier = modifierFrom(message); - modifier.setTruncated(true); - return modifier.getDnsMessage(); - } - return message; - } - }; - dnsServer2.start(); - DnsNameResolver resolver = null; - ServerSocket serverSocket = null; - try { - DnsNameResolverBuilder builder = newResolver() - .queryTimeoutMillis(10000) - .resolvedAddressTypes(ResolvedAddressTypes.IPV4_PREFERRED) - .maxQueriesPerResolve(16) - .nameServerProvider(new SingletonDnsServerAddressStreamProvider(dnsServer2.localAddress())); - - if (tcpFallback) { - // If we are configured to use TCP as a fallback also bind a TCP socket - serverSocket = new ServerSocket(dnsServer2.localAddress().getPort()); - serverSocket.setReuseAddress(true); - - builder.socketChannelType(NioSocketChannel.class); - } - resolver = builder.build(); - if (truncatedBecauseOfMtu) { - resolver.ch.pipeline().addFirst(new ChannelHandler() { - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - if (msg instanceof DatagramPacket) { - // Truncate the packet by 1 byte. - DatagramPacket packet = (DatagramPacket) msg; - packet.content().writerIndex(packet.content().writerIndex() - 1); - } - ctx.fireChannelRead(msg); - } - }); - } - Future> envelopeFuture = resolver.query( - new DefaultDnsQuestion(host, DnsRecordType.TXT)); - - if (tcpFallback) { - // If we are configured to use TCP as a fallback lets replay the dns message over TCP - Socket socket = serverSocket.accept(); - - InputStream in = socket.getInputStream(); - assertTrue((in.read() << 8 | in.read() & 0xff) > 2); // skip length field - int txnId = in.read() << 8 | in.read() & 0xff; - - IoBuffer ioBuffer = IoBuffer.allocate(1024); - // Must replace the transactionId with the one from the TCP request - DnsMessageModifier modifier = modifierFrom(messageRef.get()); - modifier.setTransactionId(txnId); - new DnsMessageEncoder().encode(ioBuffer, modifier.getDnsMessage()); - ioBuffer.flip(); - - ByteBuffer lenBuffer = ByteBuffer.allocate(2); - lenBuffer.putShort((short) ioBuffer.remaining()); - lenBuffer.flip(); - - while (lenBuffer.hasRemaining()) { - socket.getOutputStream().write(lenBuffer.get()); - } - - while (ioBuffer.hasRemaining()) { - socket.getOutputStream().write(ioBuffer.get()); - } - socket.getOutputStream().flush(); - // Let's wait until we received the envelope before closing the socket. - envelopeFuture.syncUninterruptibly(); - - socket.close(); - serverSocket.close(); - } - - AddressedEnvelope envelope = envelopeFuture.syncUninterruptibly().getNow(); - assertNotNull(envelope.sender()); - - DnsResponse response = envelope.content(); - assertNotNull(response); - - assertEquals(DnsResponseCode.NOERROR, response.code()); - int count = response.count(DnsSection.ANSWER); - - assertEquals(1, count); - List texts = decodeTxt(response.recordAt(DnsSection.ANSWER, 0)); - assertEquals(1, texts.size()); - assertEquals(txt, texts.get(0)); - - if (tcpFallback) { - assertFalse(envelope.content().isTruncated()); - } else { - assertTrue(envelope.content().isTruncated()); - } - assertTrue(envelope.release()); - } finally { - dnsServer2.stop(); - if (resolver != null) { - resolver.close(); - } - } - } - - @Test - public void testCancelPromise() throws Exception { - final EventLoop eventLoop = group.next(); - final Promise promise = eventLoop.newPromise(); - final TestDnsServer dnsServer1 = new TestDnsServer(Collections.emptySet()) { - @Override - protected DnsMessage filterMessage(DnsMessage message) { - promise.cancel(); - return message; - } - }; - dnsServer1.start(); - final AtomicBoolean isQuerySentToSecondServer = new AtomicBoolean(); - final TestDnsServer dnsServer2 = new TestDnsServer(Collections.emptySet()) { - @Override - protected DnsMessage filterMessage(DnsMessage message) { - isQuerySentToSecondServer.set(true); - return message; - } - }; - dnsServer2.start(); - DnsServerAddressStreamProvider nameServerProvider = - new SequentialDnsServerAddressStreamProvider(dnsServer1.localAddress(), - dnsServer2.localAddress()); - final DnsNameResolver resolver = new DnsNameResolverBuilder(group.next()) - .dnsQueryLifecycleObserverFactory(new TestRecursiveCacheDnsQueryLifecycleObserverFactory()) - .channelType(NioDatagramChannel.class) - .optResourceEnabled(false) - .nameServerProvider(nameServerProvider) - .build(); - - try { - resolver.resolve("non-existent.netty.io", promise).sync(); - fail(); - } catch (Exception e) { - assertThat(e, is(instanceOf(CancellationException.class))); - } - assertThat(isQuerySentToSecondServer.get(), is(false)); - } - - @Test - public void testCNAMERecursiveResolveDifferentNameServersForDomains() throws IOException { - final String firstName = "firstname.com"; - final String secondName = "secondname.com"; - final String lastName = "lastname.com"; - final String ipv4Addr = "1.2.3.4"; - final TestDnsServer dnsServer2 = new TestDnsServer(new RecordStore() { - @Override - public Set getRecords(QuestionRecord question) { - ResourceRecordModifier rm = new ResourceRecordModifier(); - rm.setDnsClass(RecordClass.IN); - rm.setDnsName(question.getDomainName()); - rm.setDnsTtl(100); - - if (question.getDomainName().equals(firstName)) { - rm.setDnsType(RecordType.CNAME); - rm.put(DnsAttribute.DOMAIN_NAME, secondName); - } else if (question.getDomainName().equals(lastName)) { - rm.setDnsType(question.getRecordType()); - rm.put(DnsAttribute.IP_ADDRESS, ipv4Addr); - } else { - return null; - } - return Collections.singleton(rm.getEntry()); - } - }); - dnsServer2.start(); - final TestDnsServer dnsServer3 = new TestDnsServer(new RecordStore() { - @Override - public Set getRecords(QuestionRecord question) { - if (question.getDomainName().equals(secondName)) { - ResourceRecordModifier rm = new ResourceRecordModifier(); - rm.setDnsClass(RecordClass.IN); - rm.setDnsName(question.getDomainName()); - rm.setDnsTtl(100); - rm.setDnsType(RecordType.CNAME); - rm.put(DnsAttribute.DOMAIN_NAME, lastName); - return Collections.singleton(rm.getEntry()); - } - return null; - } - }); - dnsServer3.start(); - DnsNameResolver resolver = null; - try { - resolver = newResolver() - .resolveCache(NoopDnsCache.INSTANCE) - .cnameCache(NoopDnsCnameCache.INSTANCE) - .recursionDesired(true) - .maxQueriesPerResolve(16) - .nameServerProvider(new DnsServerAddressStreamProvider() { - @Override - public DnsServerAddressStream nameServerAddressStream(String hostname) { - if (hostname.equals(secondName + '.')) { - return DnsServerAddresses.singleton(dnsServer3.localAddress()).stream(); - } - return DnsServerAddresses.singleton(dnsServer2.localAddress()).stream(); - } - }) - .resolvedAddressTypes(ResolvedAddressTypes.IPV4_PREFERRED).build(); - - assertResolvedAddress(resolver.resolve(firstName).syncUninterruptibly().getNow(), ipv4Addr, firstName); - } finally { - dnsServer2.stop(); - dnsServer3.stop(); - if (resolver != null) { - resolver.close(); - } - } - } - - private static void assertResolvedAddress(InetAddress resolvedAddress, String ipAddr, String hostname) { - assertEquals(ipAddr, resolvedAddress.getHostAddress()); - assertEquals(hostname, resolvedAddress.getHostName()); - } - - @Test - public void testAllNameServers() throws IOException { - final String domain = "netty.io"; - final String ipv4Addr = "1.2.3.4"; - final AtomicInteger server2Counter = new AtomicInteger(); - final TestDnsServer dnsServer2 = new TestDnsServer(new RecordStore() { - @Override - public Set getRecords(QuestionRecord question) { - server2Counter.incrementAndGet(); - ResourceRecordModifier rm = new ResourceRecordModifier(); - rm.setDnsClass(RecordClass.IN); - rm.setDnsName(question.getDomainName()); - rm.setDnsTtl(100); - - rm.setDnsType(question.getRecordType()); - rm.put(DnsAttribute.IP_ADDRESS, ipv4Addr); - return Collections.singleton(rm.getEntry()); - } - }); - dnsServer2.start(); - - final AtomicInteger server3Counter = new AtomicInteger(); - final TestDnsServer dnsServer3 = new TestDnsServer(new RecordStore() { - @Override - public Set getRecords(QuestionRecord question) { - server3Counter.incrementAndGet(); - ResourceRecordModifier rm = new ResourceRecordModifier(); - rm.setDnsClass(RecordClass.IN); - rm.setDnsName(question.getDomainName()); - rm.setDnsTtl(100); - - rm.setDnsType(question.getRecordType()); - rm.put(DnsAttribute.IP_ADDRESS, ipv4Addr); - return Collections.singleton(rm.getEntry()); - } - }); - dnsServer3.start(); - DnsNameResolver resolver = null; - try { - resolver = newResolver() - .resolveCache(NoopDnsCache.INSTANCE) - .cnameCache(NoopDnsCnameCache.INSTANCE) - .recursionDesired(true) - .maxQueriesPerResolve(16) - .nameServerProvider(new DnsServerAddressStreamProvider() { - private final DnsServerAddresses addresses = - DnsServerAddresses.rotational(dnsServer2.localAddress(), dnsServer3.localAddress()); - @Override - public DnsServerAddressStream nameServerAddressStream(String hostname) { - return addresses.stream(); - } - }) - .resolvedAddressTypes(ResolvedAddressTypes.IPV4_ONLY).build(); - - assertResolvedAddress(resolver.resolve(domain).syncUninterruptibly().getNow(), ipv4Addr, domain); - assertEquals(1, server2Counter.get()); - assertEquals(0, server3Counter.get()); - assertResolvedAddress(resolver.resolve(domain).syncUninterruptibly().getNow(), ipv4Addr, domain); - assertEquals(1, server2Counter.get()); - assertEquals(1, server3Counter.get()); - assertResolvedAddress(resolver.resolve(domain).syncUninterruptibly().getNow(), ipv4Addr, domain); - assertEquals(2, server2Counter.get()); - assertEquals(1, server3Counter.get()); - assertResolvedAddress(resolver.resolve(domain).syncUninterruptibly().getNow(), ipv4Addr, domain); - assertEquals(2, server2Counter.get()); - assertEquals(2, server3Counter.get()); - } finally { - dnsServer2.stop(); - dnsServer3.stop(); - if (resolver != null) { - resolver.close(); - } - } - } - - @Test - @Timeout(value = 2000, unit = TimeUnit.MILLISECONDS) - public void testSrvWithCnameNotCached() throws Exception { - final AtomicBoolean alias = new AtomicBoolean(); - TestDnsServer dnsServer2 = new TestDnsServer(new RecordStore() { - @Override - public Set getRecords(QuestionRecord question) { - String name = question.getDomainName(); - if (name.equals("service.netty.io")) { - Set records = new HashSet(2); - - ResourceRecordModifier rm = new ResourceRecordModifier(); - rm.setDnsClass(RecordClass.IN); - rm.setDnsName(name); - rm.setDnsTtl(10); - rm.setDnsType(RecordType.CNAME); - rm.put(DnsAttribute.DOMAIN_NAME, "alias.service.netty.io"); - records.add(rm.getEntry()); - - rm = new ResourceRecordModifier(); - rm.setDnsClass(RecordClass.IN); - rm.setDnsName(name); - rm.setDnsTtl(10); - rm.setDnsType(RecordType.SRV); - rm.put(DnsAttribute.DOMAIN_NAME, "foo.service.netty.io"); - rm.put(DnsAttribute.SERVICE_PORT, "8080"); - rm.put(DnsAttribute.SERVICE_PRIORITY, "10"); - rm.put(DnsAttribute.SERVICE_WEIGHT, "1"); - records.add(rm.getEntry()); - return records; - } - if (name.equals("foo.service.netty.io")) { - ResourceRecordModifier rm = new ResourceRecordModifier(); - rm.setDnsClass(RecordClass.IN); - rm.setDnsName(name); - rm.setDnsTtl(10); - rm.setDnsType(RecordType.A); - rm.put(DnsAttribute.IP_ADDRESS, "10.0.0.1"); - return Collections.singleton(rm.getEntry()); - } - if (alias.get()) { - ResourceRecordModifier rm = new ResourceRecordModifier(); - rm.setDnsClass(RecordClass.IN); - rm.setDnsName(name); - rm.setDnsTtl(10); - rm.setDnsType(RecordType.SRV); - rm.put(DnsAttribute.DOMAIN_NAME, "foo.service.netty.io"); - rm.put(DnsAttribute.SERVICE_PORT, "8080"); - rm.put(DnsAttribute.SERVICE_PRIORITY, "10"); - rm.put(DnsAttribute.SERVICE_WEIGHT, "1"); - return Collections.singleton(rm.getEntry()); - } - return null; - } - }); - dnsServer2.start(); - DnsNameResolver resolver = null; - try { - DnsNameResolverBuilder builder = newResolver() - .recursionDesired(false) - .queryTimeoutMillis(10000) - .resolvedAddressTypes(ResolvedAddressTypes.IPV4_PREFERRED) - .completeOncePreferredResolved(true) - .maxQueriesPerResolve(16) - .nameServerProvider(new SingletonDnsServerAddressStreamProvider(dnsServer2.localAddress())); - - resolver = builder.build(); - assertNotEmptyAndRelease(resolver.resolveAll(new DefaultDnsQuestion("service.netty.io", SRV))); - alias.set(true); - assertNotEmptyAndRelease(resolver.resolveAll(new DefaultDnsQuestion("service.netty.io", SRV))); - alias.set(false); - assertNotEmptyAndRelease(resolver.resolveAll(new DefaultDnsQuestion("service.netty.io", SRV))); - } finally { - dnsServer2.stop(); - if (resolver != null) { - resolver.close(); - } - } - } - - @Test - public void testCNAMEOnlyTriedOnAddressLookups() throws Exception { - - final AtomicInteger cnameQueries = new AtomicInteger(); - - TestDnsServer dnsServer2 = new TestDnsServer(new RecordStore() { - @Override - public Set getRecords(QuestionRecord questionRecord) { - if (questionRecord.getRecordType() == RecordType.CNAME) { - cnameQueries.incrementAndGet(); - } - - return Collections.emptySet(); - } - }); - - dnsServer2.start(); - - DnsNameResolver resolver = null; - try { - resolver = newNonCachedResolver(ResolvedAddressTypes.IPV4_PREFERRED) - .maxQueriesPerResolve(4) - .searchDomains(Collections.emptyList()) - .nameServerProvider(new SingletonDnsServerAddressStreamProvider(dnsServer2.localAddress())) - .build(); - - // We expect these resolves to fail with UnknownHostException, - // and then check that no unexpected CNAME queries were performed. - assertThat(resolver.resolveAll(new DefaultDnsQuestion("lookup-srv.netty.io", SRV)).await().cause(), - instanceOf(UnknownHostException.class)); - assertEquals(0, cnameQueries.get()); - - assertThat(resolver.resolveAll(new DefaultDnsQuestion("lookup-naptr.netty.io", NAPTR)).await().cause(), - instanceOf(UnknownHostException.class)); - assertEquals(0, cnameQueries.get()); - - assertThat(resolver.resolveAll(new DefaultDnsQuestion("lookup-cname.netty.io", CNAME)).await().cause(), - instanceOf(UnknownHostException.class)); - assertEquals(1, cnameQueries.getAndSet(0)); - - assertThat(resolver.resolveAll(new DefaultDnsQuestion("lookup-a.netty.io", A)).await().cause(), - instanceOf(UnknownHostException.class)); - assertEquals(1, cnameQueries.getAndSet(0)); - - assertThat(resolver.resolveAll(new DefaultDnsQuestion("lookup-aaaa.netty.io", AAAA)).await().cause(), - instanceOf(UnknownHostException.class)); - assertEquals(1, cnameQueries.getAndSet(0)); - - assertThat(resolver.resolveAll("lookup-address.netty.io").await().cause(), - instanceOf(UnknownHostException.class)); - assertEquals(1, cnameQueries.getAndSet(0)); - } finally { - dnsServer2.stop(); - if (resolver != null) { - resolver.close(); - } - } - } - - private static void assertNotEmptyAndRelease(Future> recordsFuture) throws Exception { - List records = recordsFuture.get(); - assertFalse(records.isEmpty()); - for (DnsRecord record : records) { - ReferenceCountUtil.release(record); - } - } -} diff --git a/resolver-dns/src/test/java/io/netty/resolver/dns/DnsResolveContextTest.java b/resolver-dns/src/test/java/io/netty/resolver/dns/DnsResolveContextTest.java deleted file mode 100644 index cb85ea82c5..0000000000 --- a/resolver-dns/src/test/java/io/netty/resolver/dns/DnsResolveContextTest.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.channel.embedded.EmbeddedChannel; -import org.junit.jupiter.api.Test; - -import java.net.UnknownHostException; - -import static org.junit.jupiter.api.Assertions.fail; - -public class DnsResolveContextTest { - - private static final String HOSTNAME = "netty.io."; - - @Test - public void testCnameLoop() { - for (int i = 1; i < 128; i++) { - try { - DnsResolveContext.cnameResolveFromCache(buildCache(i), HOSTNAME); - fail(); - } catch (UnknownHostException expected) { - // expected - } - } - } - - private static DnsCnameCache buildCache(int chainLength) { - EmbeddedChannel channel = new EmbeddedChannel(); - DnsCnameCache cache = new DefaultDnsCnameCache(); - if (chainLength == 1) { - cache.cache(HOSTNAME, HOSTNAME, Long.MAX_VALUE, channel.executor()); - } else { - String lastName = HOSTNAME; - for (int i = 1; i < chainLength; i++) { - String nextName = i + "." + lastName; - cache.cache(lastName, nextName, Long.MAX_VALUE, channel.executor()); - lastName = nextName; - } - cache.cache(lastName, HOSTNAME, Long.MAX_VALUE, channel.executor()); - } - return cache; - } -} diff --git a/resolver-dns/src/test/java/io/netty/resolver/dns/DnsServerAddressStreamProvidersTest.java b/resolver-dns/src/test/java/io/netty/resolver/dns/DnsServerAddressStreamProvidersTest.java deleted file mode 100644 index f2bfcc104f..0000000000 --- a/resolver-dns/src/test/java/io/netty/resolver/dns/DnsServerAddressStreamProvidersTest.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertSame; - -public class DnsServerAddressStreamProvidersTest { - - @Test - public void testUseCorrectProvider() { - assertSame(DnsServerAddressStreamProviders.unixDefault(), - DnsServerAddressStreamProviders.platformDefault()); - } -} diff --git a/resolver-dns/src/test/java/io/netty/resolver/dns/DnsServerAddressesTest.java b/resolver-dns/src/test/java/io/netty/resolver/dns/DnsServerAddressesTest.java deleted file mode 100644 index 5a15cdbffa..0000000000 --- a/resolver-dns/src/test/java/io/netty/resolver/dns/DnsServerAddressesTest.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.resolver.dns; - -import io.netty.util.NetUtil; -import org.junit.jupiter.api.Test; - -import java.net.InetSocketAddress; -import java.util.Collections; -import java.util.IdentityHashMap; -import java.util.Set; - -import static io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider.defaultAddressList; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; - -public class DnsServerAddressesTest { - - private static final InetSocketAddress ADDR1 = new InetSocketAddress(NetUtil.LOCALHOST, 1); - private static final InetSocketAddress ADDR2 = new InetSocketAddress(NetUtil.LOCALHOST, 2); - private static final InetSocketAddress ADDR3 = new InetSocketAddress(NetUtil.LOCALHOST, 3); - - @Test - public void testDefaultAddresses() { - assertThat(defaultAddressList().size(), is(greaterThan(0))); - } - - @Test - public void testSequential() { - DnsServerAddresses seq = DnsServerAddresses.sequential(ADDR1, ADDR2, ADDR3); - assertThat(seq.stream(), is(not(sameInstance(seq.stream())))); - - for (int j = 0; j < 2; j ++) { - DnsServerAddressStream i = seq.stream(); - assertNext(i, ADDR1); - assertNext(i, ADDR2); - assertNext(i, ADDR3); - assertNext(i, ADDR1); - assertNext(i, ADDR2); - assertNext(i, ADDR3); - } - } - - @Test - public void testRotational() { - DnsServerAddresses seq = DnsServerAddresses.rotational(ADDR1, ADDR2, ADDR3); - - DnsServerAddressStream i = seq.stream(); - assertNext(i, ADDR1); - assertNext(i, ADDR2); - assertNext(i, ADDR3); - assertNext(i, ADDR1); - assertNext(i, ADDR2); - assertNext(i, ADDR3); - - i = seq.stream(); - assertNext(i, ADDR2); - assertNext(i, ADDR3); - assertNext(i, ADDR1); - assertNext(i, ADDR2); - assertNext(i, ADDR3); - assertNext(i, ADDR1); - - i = seq.stream(); - assertNext(i, ADDR3); - assertNext(i, ADDR1); - assertNext(i, ADDR2); - assertNext(i, ADDR3); - assertNext(i, ADDR1); - assertNext(i, ADDR2); - - i = seq.stream(); - assertNext(i, ADDR1); - assertNext(i, ADDR2); - assertNext(i, ADDR3); - assertNext(i, ADDR1); - assertNext(i, ADDR2); - assertNext(i, ADDR3); - } - - @Test - public void testShuffled() { - DnsServerAddresses seq = DnsServerAddresses.shuffled(ADDR1, ADDR2, ADDR3); - - // Ensure that all three addresses are returned by the iterator. - // In theory, this test can fail at extremely low chance, but we don't really care. - Set set = Collections.newSetFromMap(new IdentityHashMap<>()); - DnsServerAddressStream i = seq.stream(); - for (int j = 0; j < 1048576; j ++) { - set.add(i.next()); - } - - assertThat(set.size(), is(3)); - assertThat(seq.stream(), is(not(sameInstance(seq.stream())))); - } - - @Test - public void testSingleton() { - DnsServerAddresses seq = DnsServerAddresses.singleton(ADDR1); - - // Should return the same iterator instance for least possible footprint. - assertThat(seq.stream(), is(sameInstance(seq.stream()))); - - DnsServerAddressStream i = seq.stream(); - assertNext(i, ADDR1); - assertNext(i, ADDR1); - assertNext(i, ADDR1); - } - - private static void assertNext(DnsServerAddressStream i, InetSocketAddress addr) { - assertThat(i.next(), is(sameInstance(addr))); - } -} diff --git a/resolver-dns/src/test/java/io/netty/resolver/dns/NameServerComparatorTest.java b/resolver-dns/src/test/java/io/netty/resolver/dns/NameServerComparatorTest.java deleted file mode 100644 index f986ad9fd9..0000000000 --- a/resolver-dns/src/test/java/io/netty/resolver/dns/NameServerComparatorTest.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class NameServerComparatorTest { - - private static InetSocketAddress IPV4ADDRESS1; - private static InetSocketAddress IPV4ADDRESS2; - private static InetSocketAddress IPV4ADDRESS3; - - private static InetSocketAddress IPV6ADDRESS1; - private static InetSocketAddress IPV6ADDRESS2; - - private static InetSocketAddress UNRESOLVED1; - private static InetSocketAddress UNRESOLVED2; - private static InetSocketAddress UNRESOLVED3; - - @BeforeAll - public static void before() throws UnknownHostException { - IPV4ADDRESS1 = new InetSocketAddress(InetAddress.getByAddress("ns1", new byte[] { 10, 0, 0, 1 }), 53); - IPV4ADDRESS2 = new InetSocketAddress(InetAddress.getByAddress("ns2", new byte[] { 10, 0, 0, 2 }), 53); - IPV4ADDRESS3 = new InetSocketAddress(InetAddress.getByAddress("ns3", new byte[] { 10, 0, 0, 3 }), 53); - - IPV6ADDRESS1 = new InetSocketAddress(InetAddress.getByAddress( - "ns1", new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }), 53); - IPV6ADDRESS2 = new InetSocketAddress(InetAddress.getByAddress( - "ns2", new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 }), 53); - - UNRESOLVED1 = InetSocketAddress.createUnresolved("ns3", 53); - UNRESOLVED2 = InetSocketAddress.createUnresolved("ns4", 53); - UNRESOLVED3 = InetSocketAddress.createUnresolved("ns5", 53); - } - - @Test - public void testCompareResolvedOnly() { - NameServerComparator comparator = new NameServerComparator(Inet4Address.class); - int x = comparator.compare(IPV4ADDRESS1, IPV6ADDRESS1); - int y = comparator.compare(IPV6ADDRESS1, IPV4ADDRESS1); - - assertEquals(-1, x); - assertEquals(x, -y); - - assertEquals(0, comparator.compare(IPV4ADDRESS1, IPV4ADDRESS1)); - assertEquals(0, comparator.compare(IPV6ADDRESS1, IPV6ADDRESS1)); - } - - @Test - public void testCompareUnresolvedSimple() { - NameServerComparator comparator = new NameServerComparator(Inet4Address.class); - int x = comparator.compare(IPV4ADDRESS1, UNRESOLVED1); - int y = comparator.compare(UNRESOLVED1, IPV4ADDRESS1); - - assertEquals(-1, x); - assertEquals(x, -y); - assertEquals(0, comparator.compare(IPV4ADDRESS1, IPV4ADDRESS1)); - assertEquals(0, comparator.compare(UNRESOLVED1, UNRESOLVED1)); - } - - @Test - public void testCompareUnresolvedOnly() { - NameServerComparator comparator = new NameServerComparator(Inet4Address.class); - int x = comparator.compare(UNRESOLVED1, UNRESOLVED2); - int y = comparator.compare(UNRESOLVED2, UNRESOLVED1); - - assertEquals(0, x); - assertEquals(x, -y); - - assertEquals(0, comparator.compare(UNRESOLVED1, UNRESOLVED1)); - assertEquals(0, comparator.compare(UNRESOLVED2, UNRESOLVED2)); - } - - @Test - public void testSortAlreadySortedPreferred() { - List expected = Arrays.asList(IPV4ADDRESS1, IPV4ADDRESS2, IPV4ADDRESS3); - List addresses = new ArrayList<>(expected); - NameServerComparator comparator = new NameServerComparator(Inet4Address.class); - - Collections.sort(addresses, comparator); - - assertEquals(expected, addresses); - } - - @Test - public void testSortAlreadySortedNotPreferred() { - List expected = Arrays.asList(IPV4ADDRESS1, IPV4ADDRESS2, IPV4ADDRESS3); - List addresses = new ArrayList<>(expected); - NameServerComparator comparator = new NameServerComparator(Inet6Address.class); - - Collections.sort(addresses, comparator); - - assertEquals(expected, addresses); - } - - @Test - public void testSortAlreadySortedUnresolved() { - List expected = Arrays.asList(UNRESOLVED1, UNRESOLVED2, UNRESOLVED3); - List addresses = new ArrayList<>(expected); - NameServerComparator comparator = new NameServerComparator(Inet6Address.class); - - Collections.sort(addresses, comparator); - - assertEquals(expected, addresses); - } - - @Test - public void testSortAlreadySortedMixed() { - List expected = Arrays.asList( - IPV4ADDRESS1, IPV4ADDRESS2, IPV6ADDRESS1, IPV6ADDRESS2, UNRESOLVED1, UNRESOLVED2); - - List addresses = new ArrayList<>(expected); - NameServerComparator comparator = new NameServerComparator(Inet4Address.class); - - Collections.sort(addresses, comparator); - - assertEquals(expected, addresses); - } - - @Test - public void testSort1() { - List expected = Arrays.asList( - IPV4ADDRESS1, IPV4ADDRESS2, IPV6ADDRESS1, IPV6ADDRESS2, UNRESOLVED1, UNRESOLVED2); - List addresses = new ArrayList<>( - Arrays.asList(IPV6ADDRESS1, IPV4ADDRESS1, IPV6ADDRESS2, UNRESOLVED1, UNRESOLVED2, IPV4ADDRESS2)); - NameServerComparator comparator = new NameServerComparator(Inet4Address.class); - - Collections.sort(addresses, comparator); - - assertEquals(expected, addresses); - } - - @Test - public void testSort2() { - List expected = Arrays.asList( - IPV4ADDRESS1, IPV4ADDRESS2, IPV6ADDRESS1, IPV6ADDRESS2, UNRESOLVED1, UNRESOLVED2); - List addresses = new ArrayList<>( - Arrays.asList(IPV4ADDRESS1, IPV6ADDRESS1, IPV6ADDRESS2, UNRESOLVED1, IPV4ADDRESS2, UNRESOLVED2)); - NameServerComparator comparator = new NameServerComparator(Inet4Address.class); - - Collections.sort(addresses, comparator); - - assertEquals(expected, addresses); - } -} diff --git a/resolver-dns/src/test/java/io/netty/resolver/dns/PreferredAddressTypeComparatorTest.java b/resolver-dns/src/test/java/io/netty/resolver/dns/PreferredAddressTypeComparatorTest.java deleted file mode 100644 index 427855306d..0000000000 --- a/resolver-dns/src/test/java/io/netty/resolver/dns/PreferredAddressTypeComparatorTest.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.channel.socket.InternetProtocolFamily; -import org.junit.jupiter.api.Test; - -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class PreferredAddressTypeComparatorTest { - - @Test - public void testIpv4() throws UnknownHostException { - InetAddress ipv4Address1 = InetAddress.getByName("10.0.0.1"); - InetAddress ipv4Address2 = InetAddress.getByName("10.0.0.2"); - InetAddress ipv4Address3 = InetAddress.getByName("10.0.0.3"); - InetAddress ipv6Address1 = InetAddress.getByName("::1"); - InetAddress ipv6Address2 = InetAddress.getByName("::2"); - InetAddress ipv6Address3 = InetAddress.getByName("::3"); - - PreferredAddressTypeComparator ipv4 = PreferredAddressTypeComparator.comparator(InternetProtocolFamily.IPv4); - - List addressList = new ArrayList(); - Collections.addAll(addressList, ipv4Address1, ipv4Address2, ipv6Address1, - ipv6Address2, ipv4Address3, ipv6Address3); - Collections.sort(addressList, ipv4); - - assertEquals(Arrays.asList(ipv4Address1, ipv4Address2, ipv4Address3, ipv6Address1, - ipv6Address2, ipv6Address3), addressList); - } - - @Test - public void testIpv6() throws UnknownHostException { - InetAddress ipv4Address1 = InetAddress.getByName("10.0.0.1"); - InetAddress ipv4Address2 = InetAddress.getByName("10.0.0.2"); - InetAddress ipv4Address3 = InetAddress.getByName("10.0.0.3"); - InetAddress ipv6Address1 = InetAddress.getByName("::1"); - InetAddress ipv6Address2 = InetAddress.getByName("::2"); - InetAddress ipv6Address3 = InetAddress.getByName("::3"); - - PreferredAddressTypeComparator ipv4 = PreferredAddressTypeComparator.comparator(InternetProtocolFamily.IPv6); - - List addressList = new ArrayList(); - Collections.addAll(addressList, ipv4Address1, ipv4Address2, ipv6Address1, - ipv6Address2, ipv4Address3, ipv6Address3); - Collections.sort(addressList, ipv4); - - assertEquals(Arrays.asList(ipv6Address1, - ipv6Address2, ipv6Address3, ipv4Address1, ipv4Address2, ipv4Address3), addressList); - } -} diff --git a/resolver-dns/src/test/java/io/netty/resolver/dns/SearchDomainTest.java b/resolver-dns/src/test/java/io/netty/resolver/dns/SearchDomainTest.java deleted file mode 100644 index d14ada954c..0000000000 --- a/resolver-dns/src/test/java/io/netty/resolver/dns/SearchDomainTest.java +++ /dev/null @@ -1,316 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioDatagramChannel; -import io.netty.util.concurrent.Future; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.not; -import static org.hamcrest.core.StringContains.containsString; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class SearchDomainTest { - - private DnsNameResolverBuilder newResolver() { - return new DnsNameResolverBuilder(group.next()) - .channelType(NioDatagramChannel.class) - .nameServerProvider(new SingletonDnsServerAddressStreamProvider(dnsServer.localAddress())) - .maxQueriesPerResolve(1) - .optResourceEnabled(false) - .ndots(1); - } - - private TestDnsServer dnsServer; - private EventLoopGroup group; - private DnsNameResolver resolver; - - @BeforeEach - public void before() { - group = new MultithreadEventLoopGroup(1, NioHandler.newFactory()); - } - - @AfterEach - public void destroy() { - if (dnsServer != null) { - dnsServer.stop(); - dnsServer = null; - } - if (resolver != null) { - resolver.close(); - } - group.shutdownGracefully(); - } - - @Test - public void testResolve() throws Exception { - Set domains = new HashSet<>(); - domains.add("host1.foo.com"); - domains.add("host1"); - domains.add("host3"); - domains.add("host4.sub.foo.com"); - domains.add("host5.sub.foo.com"); - domains.add("host5.sub"); - - TestDnsServer.MapRecordStoreA store = new TestDnsServer.MapRecordStoreA(domains); - dnsServer = new TestDnsServer(store); - dnsServer.start(); - - resolver = newResolver().searchDomains(Collections.singletonList("foo.com")).ndots(2).build(); - - String a = "host1.foo.com"; - String resolved = assertResolve(resolver, a); - assertEquals(store.getAddress("host1.foo.com"), resolved); - - // host1 resolves host1.foo.com with foo.com search domain - resolved = assertResolve(resolver, "host1"); - assertEquals(store.getAddress("host1.foo.com"), resolved); - - // "host1." absolute query - resolved = assertResolve(resolver, "host1."); - assertEquals(store.getAddress("host1"), resolved); - - // "host2" not resolved - assertNotResolve(resolver, "host2"); - - // "host3" does not contain a dot nor it's absolute but it should still be resolved after search list have - // been checked - resolved = assertResolve(resolver, "host3"); - assertEquals(store.getAddress("host3"), resolved); - - // "host3." does not contain a dot but is absolute - resolved = assertResolve(resolver, "host3."); - assertEquals(store.getAddress("host3"), resolved); - - // "host4.sub" contains a dot but not resolved then resolved to "host4.sub.foo.com" with "foo.com" search domain - resolved = assertResolve(resolver, "host4.sub"); - assertEquals(store.getAddress("host4.sub.foo.com"), resolved); - - // "host5.sub" would have been directly resolved but since it has less than ndots the "foo.com" search domain - // is used. - resolved = assertResolve(resolver, "host5.sub"); - assertEquals(store.getAddress("host5.sub.foo.com"), resolved); - } - - @Test - public void testResolveAll() throws Exception { - Set domains = new HashSet<>(); - domains.add("host1.foo.com"); - domains.add("host1"); - domains.add("host3"); - domains.add("host4.sub.foo.com"); - domains.add("host5.sub.foo.com"); - domains.add("host5.sub"); - - TestDnsServer.MapRecordStoreA store = new TestDnsServer.MapRecordStoreA(domains, 2); - dnsServer = new TestDnsServer(store); - dnsServer.start(); - - resolver = newResolver().searchDomains(Collections.singletonList("foo.com")).ndots(2).build(); - - String a = "host1.foo.com"; - List resolved = assertResolveAll(resolver, a); - assertEquals(store.getAddresses("host1.foo.com"), resolved); - - // host1 resolves host1.foo.com with foo.com search domain - resolved = assertResolveAll(resolver, "host1"); - assertEquals(store.getAddresses("host1.foo.com"), resolved); - - // "host1." absolute query - resolved = assertResolveAll(resolver, "host1."); - assertEquals(store.getAddresses("host1"), resolved); - - // "host2" not resolved - assertNotResolveAll(resolver, "host2"); - - // "host3" does not contain a dot nor it's absolute but it should still be resolved after search list have - // been checked - resolved = assertResolveAll(resolver, "host3"); - assertEquals(store.getAddresses("host3"), resolved); - - // "host3." does not contain a dot but is absolute - resolved = assertResolveAll(resolver, "host3."); - assertEquals(store.getAddresses("host3"), resolved); - - // "host4.sub" contains a dot but not resolved then resolved to "host4.sub.foo.com" with "foo.com" search domain - resolved = assertResolveAll(resolver, "host4.sub"); - assertEquals(store.getAddresses("host4.sub.foo.com"), resolved); - - // "host5.sub" would have been directly resolved but since it has less than ndots the "foo.com" search domain - // is used. - resolved = assertResolveAll(resolver, "host5.sub"); - assertEquals(store.getAddresses("host5.sub.foo.com"), resolved); - } - - @Test - public void testMultipleSearchDomain() throws Exception { - Set domains = new HashSet<>(); - domains.add("host1.foo.com"); - domains.add("host2.bar.com"); - domains.add("host3.bar.com"); - domains.add("host3.foo.com"); - - TestDnsServer.MapRecordStoreA store = new TestDnsServer.MapRecordStoreA(domains); - dnsServer = new TestDnsServer(store); - dnsServer.start(); - - resolver = newResolver().searchDomains(Arrays.asList("foo.com", "bar.com")).build(); - - // "host1" resolves via the "foo.com" search path - String resolved = assertResolve(resolver, "host1"); - assertEquals(store.getAddress("host1.foo.com"), resolved); - - // "host2" resolves via the "bar.com" search path - resolved = assertResolve(resolver, "host2"); - assertEquals(store.getAddress("host2.bar.com"), resolved); - - // "host3" resolves via the "foo.com" search path as it is the first one - resolved = assertResolve(resolver, "host3"); - assertEquals(store.getAddress("host3.foo.com"), resolved); - - // "host4" does not resolve - assertNotResolve(resolver, "host4"); - } - - @Test - public void testSearchDomainWithNdots2() throws Exception { - Set domains = new HashSet<>(); - domains.add("host1.sub.foo.com"); - domains.add("host2.sub.foo.com"); - domains.add("host2.sub"); - - TestDnsServer.MapRecordStoreA store = new TestDnsServer.MapRecordStoreA(domains); - dnsServer = new TestDnsServer(store); - dnsServer.start(); - - resolver = newResolver().searchDomains(Collections.singleton("foo.com")).ndots(2).build(); - - String resolved = assertResolve(resolver, "host1.sub"); - assertEquals(store.getAddress("host1.sub.foo.com"), resolved); - - // "host2.sub" is resolved with the foo.com search domain as ndots = 2 - resolved = assertResolve(resolver, "host2.sub"); - assertEquals(store.getAddress("host2.sub.foo.com"), resolved); - } - - @Test - public void testSearchDomainWithNdots0() throws Exception { - Set domains = new HashSet<>(); - domains.add("host1"); - domains.add("host1.foo.com"); - domains.add("host2.foo.com"); - - TestDnsServer.MapRecordStoreA store = new TestDnsServer.MapRecordStoreA(domains); - dnsServer = new TestDnsServer(store); - dnsServer.start(); - - resolver = newResolver().searchDomains(Collections.singleton("foo.com")).ndots(0).build(); - - // "host1" resolves directly as ndots = 0 - String resolved = assertResolve(resolver, "host1"); - assertEquals(store.getAddress("host1"), resolved); - - // "host1.foo.com" resolves to host1.foo - resolved = assertResolve(resolver, "host1.foo.com"); - assertEquals(store.getAddress("host1.foo.com"), resolved); - - // "host2" shouldn't resolve because it is not in the known domain names, and "host2" has 0 dots which is not - // less ndots (which is also 0). - assertNotResolve(resolver, "host2"); - } - - private static void assertNotResolve(DnsNameResolver resolver, String inetHost) throws InterruptedException { - Future fut = resolver.resolve(inetHost); - assertTrue(fut.await(10, TimeUnit.SECONDS)); - assertFalse(fut.isSuccess()); - } - - private static void assertNotResolveAll(DnsNameResolver resolver, String inetHost) throws InterruptedException { - Future> fut = resolver.resolveAll(inetHost); - assertTrue(fut.await(10, TimeUnit.SECONDS)); - assertFalse(fut.isSuccess()); - } - - private static String assertResolve(DnsNameResolver resolver, String inetHost) throws InterruptedException { - Future fut = resolver.resolve(inetHost); - assertTrue(fut.await(10, TimeUnit.SECONDS)); - return fut.getNow().getHostAddress(); - } - - private static List assertResolveAll(DnsNameResolver resolver, - String inetHost) throws InterruptedException { - Future> fut = resolver.resolveAll(inetHost); - assertTrue(fut.await(10, TimeUnit.SECONDS)); - List list = new ArrayList<>(); - for (InetAddress addr : fut.getNow()) { - list.add(addr.getHostAddress()); - } - return list; - } - - @Test - public void testExceptionMsgContainsSearchDomain() throws Exception { - TestDnsServer.MapRecordStoreA store = new TestDnsServer.MapRecordStoreA(Collections.emptySet()); - dnsServer = new TestDnsServer(store); - dnsServer.start(); - - resolver = newResolver().searchDomains(Collections.singletonList("foo.com")).ndots(1).build(); - - Future fut = resolver.resolve("unknown.hostname"); - assertTrue(fut.await(10, TimeUnit.SECONDS)); - assertFalse(fut.isSuccess()); - final Throwable cause = fut.cause(); - assertThat(cause, instanceOf(UnknownHostException.class)); - assertThat("search domain is included in UnknownHostException", cause.getMessage(), - containsString("foo.com")); - } - - @Test - public void testExceptionMsgDoesNotContainSearchDomainIfNdotsIsNotReached() throws Exception { - TestDnsServer.MapRecordStoreA store = new TestDnsServer.MapRecordStoreA(Collections.emptySet()); - dnsServer = new TestDnsServer(store); - dnsServer.start(); - - resolver = newResolver().searchDomains(Collections.singletonList("foo.com")).ndots(2).build(); - - Future fut = resolver.resolve("unknown.hostname"); - assertTrue(fut.await(10, TimeUnit.SECONDS)); - assertFalse(fut.isSuccess()); - final Throwable cause = fut.cause(); - assertThat(cause, instanceOf(UnknownHostException.class)); - assertThat("search domain is included in UnknownHostException", cause.getMessage(), - not(containsString("foo.com"))); - } -} diff --git a/resolver-dns/src/test/java/io/netty/resolver/dns/TestDnsServer.java b/resolver-dns/src/test/java/io/netty/resolver/dns/TestDnsServer.java deleted file mode 100644 index 43a7c42616..0000000000 --- a/resolver-dns/src/test/java/io/netty/resolver/dns/TestDnsServer.java +++ /dev/null @@ -1,358 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.util.NetUtil; -import org.apache.directory.server.dns.DnsServer; -import org.apache.directory.server.dns.io.decoder.DnsMessageDecoder; -import org.apache.directory.server.dns.io.encoder.DnsMessageEncoder; -import org.apache.directory.server.dns.io.encoder.ResourceRecordEncoder; -import org.apache.directory.server.dns.messages.DnsMessage; -import org.apache.directory.server.dns.messages.QuestionRecord; -import org.apache.directory.server.dns.messages.RecordClass; -import org.apache.directory.server.dns.messages.RecordType; -import org.apache.directory.server.dns.messages.ResourceRecord; -import org.apache.directory.server.dns.messages.ResourceRecordImpl; -import org.apache.directory.server.dns.messages.ResourceRecordModifier; -import org.apache.directory.server.dns.protocol.DnsProtocolHandler; -import org.apache.directory.server.dns.protocol.DnsUdpEncoder; -import org.apache.directory.server.dns.store.DnsAttribute; -import org.apache.directory.server.dns.store.RecordStore; -import org.apache.directory.server.protocol.shared.transport.UdpTransport; -import org.apache.mina.core.buffer.IoBuffer; -import org.apache.mina.core.session.IoSession; -import org.apache.mina.filter.codec.ProtocolCodecFactory; -import org.apache.mina.filter.codec.ProtocolCodecFilter; -import org.apache.mina.filter.codec.ProtocolDecoder; -import org.apache.mina.filter.codec.ProtocolDecoderAdapter; -import org.apache.mina.filter.codec.ProtocolDecoderOutput; -import org.apache.mina.filter.codec.ProtocolEncoder; -import org.apache.mina.filter.codec.ProtocolEncoderOutput; -import org.apache.mina.transport.socket.DatagramAcceptor; -import org.apache.mina.transport.socket.DatagramSessionConfig; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ThreadLocalRandom; - -class TestDnsServer extends DnsServer { - private static final Map BYTES = new HashMap<>(); - private static final String[] IPV6_ADDRESSES; - - static { - BYTES.put("::1", new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}); - BYTES.put("0:0:0:0:0:0:1:1", new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1}); - BYTES.put("0:0:0:0:0:1:1:1", new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1}); - BYTES.put("0:0:0:0:1:1:1:1", new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1}); - BYTES.put("0:0:0:1:1:1:1:1", new byte[]{0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}); - BYTES.put("0:0:1:1:1:1:1:1", new byte[]{0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}); - BYTES.put("0:1:1:1:1:1:1:1", new byte[]{0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}); - BYTES.put("1:1:1:1:1:1:1:1", new byte[]{0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}); - - IPV6_ADDRESSES = BYTES.keySet().toArray(new String[0]); - } - - private final RecordStore store; - - TestDnsServer(Set domains) { - this(new TestRecordStore(domains)); - } - - TestDnsServer(RecordStore store) { - this.store = store; - } - - @Override - public void start() throws IOException { - start(false); - } - - /** - * Start the {@link TestDnsServer} but drop all {@code AAAA} queries and not send any response to these at all. - */ - public void start(final boolean dropAAAAQueries) throws IOException { - InetSocketAddress address = new InetSocketAddress(NetUtil.LOCALHOST4, 0); - UdpTransport transport = new UdpTransport(address.getHostName(), address.getPort()); - setTransports(transport); - - DatagramAcceptor acceptor = transport.getAcceptor(); - - acceptor.setHandler(new DnsProtocolHandler(this, store) { - @Override - public void sessionCreated(IoSession session) { - // USe our own codec to support AAAA testing - session.getFilterChain() - .addFirst("codec", new ProtocolCodecFilter( - new TestDnsProtocolUdpCodecFactory(dropAAAAQueries))); - } - }); - - ((DatagramSessionConfig) acceptor.getSessionConfig()).setReuseAddress(true); - - // Start the listener - acceptor.bind(); - } - - public InetSocketAddress localAddress() { - return (InetSocketAddress) getTransports()[0].getAcceptor().getLocalAddress(); - } - - protected DnsMessage filterMessage(DnsMessage message) { - return message; - } - - protected static ResourceRecord newARecord(String name, String ipAddress) { - return newAddressRecord(name, RecordType.A, ipAddress); - } - - protected static ResourceRecord newNsRecord(String dnsname, String domainName) { - ResourceRecordModifier rm = new ResourceRecordModifier(); - rm.setDnsClass(RecordClass.IN); - rm.setDnsName(dnsname); - rm.setDnsTtl(100); - rm.setDnsType(RecordType.NS); - rm.put(DnsAttribute.DOMAIN_NAME, domainName); - return rm.getEntry(); - } - - protected static ResourceRecord newAddressRecord(String name, RecordType type, String address) { - ResourceRecordModifier rm = new ResourceRecordModifier(); - rm.setDnsClass(RecordClass.IN); - rm.setDnsName(name); - rm.setDnsTtl(100); - rm.setDnsType(type); - rm.put(DnsAttribute.IP_ADDRESS, address); - return rm.getEntry(); - } - - /** - * {@link ProtocolCodecFactory} which allows to test AAAA resolution. - */ - private final class TestDnsProtocolUdpCodecFactory implements ProtocolCodecFactory { - private final DnsMessageEncoder encoder = new DnsMessageEncoder(); - private final TestAAAARecordEncoder recordEncoder = new TestAAAARecordEncoder(); - private final boolean dropAAAArecords; - - TestDnsProtocolUdpCodecFactory(boolean dropAAAArecords) { - this.dropAAAArecords = dropAAAArecords; - } - - @Override - public ProtocolEncoder getEncoder(IoSession session) { - return new DnsUdpEncoder() { - - @Override - public void encode(IoSession session, Object message, ProtocolEncoderOutput out) { - IoBuffer buf = IoBuffer.allocate(1024); - DnsMessage dnsMessage = filterMessage((DnsMessage) message); - encoder.encode(buf, dnsMessage); - for (ResourceRecord record : dnsMessage.getAnswerRecords()) { - // This is a hack to allow to also test for AAAA resolution as DnsMessageEncoder - // does not support it and it is hard to extend, because the interesting methods - // are private... - // In case of RecordType.AAAA we need to encode the RecordType by ourselves. - if (record.getRecordType() == RecordType.AAAA) { - try { - recordEncoder.put(buf, record); - } catch (IOException e) { - // Should never happen - throw new IllegalStateException(e); - } - } - } - buf.flip(); - - out.write(buf); - } - }; - } - - @Override - public ProtocolDecoder getDecoder(IoSession session) { - return new ProtocolDecoderAdapter() { - private final DnsMessageDecoder decoder = new DnsMessageDecoder(); - - @Override - public void decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws IOException { - DnsMessage message = decoder.decode(in); - if (dropAAAArecords) { - for (QuestionRecord record: message.getQuestionRecords()) { - if (record.getRecordType() == RecordType.AAAA) { - return; - } - } - } - out.write(message); - } - }; - } - - private final class TestAAAARecordEncoder extends ResourceRecordEncoder { - - @Override - protected void putResourceRecordData(IoBuffer ioBuffer, ResourceRecord resourceRecord) { - byte[] bytes = BYTES.get(resourceRecord.get(DnsAttribute.IP_ADDRESS)); - if (bytes == null) { - throw new IllegalStateException(resourceRecord.get(DnsAttribute.IP_ADDRESS)); - } - // encode the ::1 - ioBuffer.put(bytes); - } - } - } - - static final class MapRecordStoreA implements RecordStore { - - private final Map> domainMap; - - MapRecordStoreA(Set domains, int length) { - domainMap = new HashMap<>(domains.size()); - for (String domain : domains) { - List addresses = new ArrayList<>(length); - for (int i = 0; i < length; i++) { - addresses.add(TestRecordStore.nextIp()); - } - domainMap.put(domain, addresses); - } - } - - MapRecordStoreA(Set domains) { - this(domains, 1); - } - - public String getAddress(String domain) { - return domainMap.get(domain).get(0); - } - - public List getAddresses(String domain) { - return domainMap.get(domain); - } - - @Override - public Set getRecords(QuestionRecord questionRecord) { - String name = questionRecord.getDomainName(); - List addresses = domainMap.get(name); - if (addresses != null && questionRecord.getRecordType() == RecordType.A) { - Set records = new LinkedHashSet<>(); - for (String address : addresses) { - Map attributes = new HashMap<>(); - attributes.put(DnsAttribute.IP_ADDRESS.toLowerCase(), address); - records.add(new TestResourceRecord(name, questionRecord.getRecordType(), attributes)); - } - return records; - } - return null; - } - } - - private static final class TestRecordStore implements RecordStore { - private static final int[] NUMBERS = new int[254]; - private static final char[] CHARS = new char[26]; - - static { - for (int i = 0; i < NUMBERS.length; i++) { - NUMBERS[i] = i + 1; - } - - for (int i = 0; i < CHARS.length; i++) { - CHARS[i] = (char) ('a' + i); - } - } - - private static int index(int arrayLength) { - return Math.abs(ThreadLocalRandom.current().nextInt()) % arrayLength; - } - - private static String nextDomain() { - return CHARS[index(CHARS.length)] + ".netty.io"; - } - - private static String nextIp() { - return ipPart() + "." + ipPart() + '.' + ipPart() + '.' + ipPart(); - } - - private static int ipPart() { - return NUMBERS[index(NUMBERS.length)]; - } - - private static String nextIp6() { - return IPV6_ADDRESSES[index(IPV6_ADDRESSES.length)]; - } - - private final Set domains; - - private TestRecordStore(Set domains) { - this.domains = domains; - } - - @Override - public Set getRecords(QuestionRecord questionRecord) { - String name = questionRecord.getDomainName(); - if (domains.contains(name)) { - Map attr = new HashMap<>(); - switch (questionRecord.getRecordType()) { - case A: - do { - attr.put(DnsAttribute.IP_ADDRESS.toLowerCase(Locale.US), nextIp()); - } while (ThreadLocalRandom.current().nextBoolean()); - break; - case AAAA: - do { - attr.put(DnsAttribute.IP_ADDRESS.toLowerCase(Locale.US), nextIp6()); - } while (ThreadLocalRandom.current().nextBoolean()); - break; - case MX: - int priority = 0; - do { - attr.put(DnsAttribute.DOMAIN_NAME.toLowerCase(Locale.US), nextDomain()); - attr.put(DnsAttribute.MX_PREFERENCE.toLowerCase(Locale.US), String.valueOf(++priority)); - } while (ThreadLocalRandom.current().nextBoolean()); - break; - default: - return null; - } - return Collections.singleton( - new TestResourceRecord(name, questionRecord.getRecordType(), attr)); - } - return null; - } - } - - static final class TestResourceRecord extends ResourceRecordImpl { - - TestResourceRecord(String domainName, RecordType recordType, Map attributes) { - super(domainName, recordType, RecordClass.IN, 100, attributes); - } - - @Override - public int hashCode() { - return System.identityHashCode(this); - } - - @Override - public boolean equals(Object o) { - return o == this; - } - } -} 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 deleted file mode 100644 index 41411baf75..0000000000 --- a/resolver-dns/src/test/java/io/netty/resolver/dns/UnixResolverDnsServerAddressStreamProviderTest.java +++ /dev/null @@ -1,294 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver.dns; - -import io.netty.util.CharsetUtil; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -import java.io.File; -import java.io.IOException; -import java.net.InetSocketAddress; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.UUID; - -import static io.netty.resolver.dns.UnixResolverDnsServerAddressStreamProvider.parseEtcResolverOptions; -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class UnixResolverDnsServerAddressStreamProviderTest { - @Test - public void defaultLookupShouldReturnResultsIfOnlySingleFileSpecified(@TempDir Path tempDir) throws Exception { - File f = buildFile(tempDir, "domain linecorp.local\n" + - "nameserver 127.0.0.2\n" + - "nameserver 127.0.0.3\n"); - UnixResolverDnsServerAddressStreamProvider p = - new UnixResolverDnsServerAddressStreamProvider(f, null); - - DnsServerAddressStream stream = p.nameServerAddressStream("somehost"); - assertHostNameEquals("127.0.0.2", stream.next()); - assertHostNameEquals("127.0.0.3", stream.next()); - } - - @Test - public void nameServerAddressStreamShouldBeRotationalWhenRotationOptionsIsPresent( - @TempDir Path tempDir) throws Exception { - File f = buildFile(tempDir, "options rotate\n" + - "domain linecorp.local\n" + - "nameserver 127.0.0.2\n" + - "nameserver 127.0.0.3\n" + - "nameserver 127.0.0.4\n"); - UnixResolverDnsServerAddressStreamProvider p = - new UnixResolverDnsServerAddressStreamProvider(f, null); - - DnsServerAddressStream stream = p.nameServerAddressStream(""); - assertHostNameEquals("127.0.0.2", stream.next()); - assertHostNameEquals("127.0.0.3", stream.next()); - assertHostNameEquals("127.0.0.4", stream.next()); - - stream = p.nameServerAddressStream(""); - assertHostNameEquals("127.0.0.3", stream.next()); - assertHostNameEquals("127.0.0.4", stream.next()); - assertHostNameEquals("127.0.0.2", stream.next()); - - stream = p.nameServerAddressStream(""); - assertHostNameEquals("127.0.0.4", stream.next()); - assertHostNameEquals("127.0.0.2", stream.next()); - assertHostNameEquals("127.0.0.3", stream.next()); - - stream = p.nameServerAddressStream(""); - assertHostNameEquals("127.0.0.2", stream.next()); - assertHostNameEquals("127.0.0.3", stream.next()); - assertHostNameEquals("127.0.0.4", stream.next()); - } - - @Test - public void nameServerAddressStreamShouldAlwaysStartFromTheTopWhenRotationOptionsIsAbsent( - @TempDir Path tempDir) throws Exception { - File f = buildFile(tempDir, "domain linecorp.local\n" + - "nameserver 127.0.0.2\n" + - "nameserver 127.0.0.3\n" + - "nameserver 127.0.0.4\n"); - UnixResolverDnsServerAddressStreamProvider p = - new UnixResolverDnsServerAddressStreamProvider(f, null); - - DnsServerAddressStream stream = p.nameServerAddressStream(""); - assertHostNameEquals("127.0.0.2", stream.next()); - assertHostNameEquals("127.0.0.3", stream.next()); - assertHostNameEquals("127.0.0.4", stream.next()); - - stream = p.nameServerAddressStream(""); - assertHostNameEquals("127.0.0.2", stream.next()); - assertHostNameEquals("127.0.0.3", stream.next()); - assertHostNameEquals("127.0.0.4", stream.next()); - - stream = p.nameServerAddressStream(""); - assertHostNameEquals("127.0.0.2", stream.next()); - assertHostNameEquals("127.0.0.3", stream.next()); - assertHostNameEquals("127.0.0.4", stream.next()); - } - - @Test - public void defaultReturnedWhenNoBetterMatch(@TempDir Path tempDir) throws Exception { - File f = buildFile(tempDir, "domain linecorp.local\n" + - "nameserver 127.0.0.2\n" + - "nameserver 127.0.0.3\n"); - File f2 = buildFile(tempDir, "domain squarecorp.local\n" + - "nameserver 127.0.0.4\n" + - "nameserver 127.0.0.5\n"); - UnixResolverDnsServerAddressStreamProvider p = - new UnixResolverDnsServerAddressStreamProvider(f, f2); - - DnsServerAddressStream stream = p.nameServerAddressStream("somehost"); - assertHostNameEquals("127.0.0.2", stream.next()); - assertHostNameEquals("127.0.0.3", stream.next()); - } - - @Test - public void moreRefinedSelectionReturnedWhenMatch(@TempDir Path tempDir) throws Exception { - File f = buildFile(tempDir, "domain linecorp.local\n" + - "nameserver 127.0.0.2\n" + - "nameserver 127.0.0.3\n"); - File f2 = buildFile(tempDir, "domain dc1.linecorp.local\n" + - "nameserver 127.0.0.4\n" + - "nameserver 127.0.0.5\n"); - UnixResolverDnsServerAddressStreamProvider p = - new UnixResolverDnsServerAddressStreamProvider(f, f2); - - DnsServerAddressStream stream = p.nameServerAddressStream("myhost.dc1.linecorp.local"); - assertHostNameEquals("127.0.0.4", stream.next()); - assertHostNameEquals("127.0.0.5", stream.next()); - } - - @Test - public void ndotsOptionIsParsedIfPresent(@TempDir Path tempDir) throws IOException { - File f = buildFile(tempDir, "search localdomain\n" + - "nameserver 127.0.0.11\n" + - "options ndots:0\n"); - assertEquals(0, parseEtcResolverOptions(f).ndots()); - - f = buildFile(tempDir, "search localdomain\n" + - "nameserver 127.0.0.11\n" + - "options ndots:123 foo:goo\n"); - assertEquals(123, parseEtcResolverOptions(f).ndots()); - } - - @Test - public void defaultValueReturnedIfNdotsOptionsNotPresent(@TempDir Path tempDir) throws IOException { - File f = buildFile(tempDir, "search localdomain\n" + - "nameserver 127.0.0.11\n"); - assertEquals(1, parseEtcResolverOptions(f).ndots()); - } - - @Test - public void timeoutOptionIsParsedIfPresent(@TempDir Path tempDir) throws IOException { - File f = buildFile(tempDir, "search localdomain\n" + - "nameserver 127.0.0.11\n" + - "options timeout:0\n"); - assertEquals(0, parseEtcResolverOptions(f).timeout()); - - f = buildFile(tempDir, "search localdomain\n" + - "nameserver 127.0.0.11\n" + - "options foo:bar timeout:124\n"); - assertEquals(124, parseEtcResolverOptions(f).timeout()); - } - - @Test - public void defaultValueReturnedIfTimeoutOptionsIsNotPresent(@TempDir Path tempDir) throws IOException { - File f = buildFile(tempDir, "search localdomain\n" + - "nameserver 127.0.0.11\n"); - assertEquals(5, parseEtcResolverOptions(f).timeout()); - } - - @Test - public void attemptsOptionIsParsedIfPresent(@TempDir Path tempDir) throws IOException { - File f = buildFile(tempDir, "search localdomain\n" + - "nameserver 127.0.0.11\n" + - "options attempts:0\n"); - assertEquals(0, parseEtcResolverOptions(f).attempts()); - - f = buildFile(tempDir, "search localdomain\n" + - "nameserver 127.0.0.11\n" + - "options foo:bar attempts:12\n"); - assertEquals(12, parseEtcResolverOptions(f).attempts()); - } - - @Test - public void defaultValueReturnedIfAttemptsOptionsIsNotPresent(@TempDir Path tempDir) throws IOException { - File f = buildFile(tempDir, "search localdomain\n" + - "nameserver 127.0.0.11\n"); - assertEquals(16, parseEtcResolverOptions(f).attempts()); - } - - @Test - public void emptyEtcResolverDirectoryDoesNotThrow(@TempDir Path tempDir) throws IOException { - File f = buildFile(tempDir, "domain linecorp.local\n" + - "nameserver 127.0.0.2\n" + - "nameserver 127.0.0.3\n"); - UnixResolverDnsServerAddressStreamProvider p = - new UnixResolverDnsServerAddressStreamProvider(f, tempDir.resolve("netty-empty").toFile().listFiles()); - - DnsServerAddressStream stream = p.nameServerAddressStream("somehost"); - assertHostNameEquals("127.0.0.2", stream.next()); - } - - @Test - public void searchDomainsWithOnlyDomain(@TempDir Path tempDir) throws IOException { - File f = buildFile(tempDir, "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(@TempDir Path tempDir) throws IOException { - File f = buildFile(tempDir, "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(@TempDir Path tempDir) throws IOException { - File f = buildFile(tempDir, "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 searchDomainsWithMultipleSearchSeperatedByWhitespace(@TempDir Path tempDir) throws IOException { - File f = buildFile(tempDir, "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(@TempDir Path tempDir) throws IOException { - File f = buildFile(tempDir, "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(@TempDir Path tempDir) throws IOException { - File f = buildFile(tempDir, "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); - } - - @Test - public void ignoreInvalidEntries(@TempDir Path tempDir) throws Exception { - File f = buildFile(tempDir, "domain netty.local\n" + - "nameserver nil\n" + - "nameserver 127.0.0.3\n"); - UnixResolverDnsServerAddressStreamProvider p = - new UnixResolverDnsServerAddressStreamProvider(f, null); - - DnsServerAddressStream stream = p.nameServerAddressStream("somehost"); - assertEquals(1, stream.size()); - assertHostNameEquals("127.0.0.3", stream.next()); - } - - private File buildFile(Path tempDir, String contents) throws IOException { - Path path = tempDir.resolve("netty-dns-" + UUID.randomUUID().toString().substring(24) + ".txt"); - Files.write(path, contents.getBytes(CharsetUtil.UTF_8)); - return path.toFile(); - } - - @Test - public void ignoreComments(@TempDir Path tempDir) throws Exception { - File f = buildFile(tempDir, "domain linecorp.local\n" + - "nameserver 127.0.0.2 #somecomment\n"); - UnixResolverDnsServerAddressStreamProvider p = - new UnixResolverDnsServerAddressStreamProvider(f, null); - - DnsServerAddressStream stream = p.nameServerAddressStream("somehost"); - assertHostNameEquals("127.0.0.2", stream.next()); - } - - private static void assertHostNameEquals(String expectedHostname, InetSocketAddress next) { - assertEquals(expectedHostname, next.getHostString(), "unexpected hostname: " + next); - } -} diff --git a/resolver-dns/src/test/resources/logback-test.xml b/resolver-dns/src/test/resources/logback-test.xml deleted file mode 100644 index 4da155151c..0000000000 --- a/resolver-dns/src/test/resources/logback-test.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - - - - - - - - // Disable logging for apacheds to reduce noise. - - diff --git a/resolver/pom.xml b/resolver/pom.xml deleted file mode 100644 index eb24507877..0000000000 --- a/resolver/pom.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - 4.0.0 - - io.netty - netty-parent - 5.0.0.Final-SNAPSHOT - - - netty-resolver - jar - - Netty/Resolver - - - io.netty.resolver - - - - - ${project.groupId} - netty-common - ${project.version} - - - org.mockito - mockito-core - - - - diff --git a/resolver/src/main/java/io/netty/resolver/AbstractAddressResolver.java b/resolver/src/main/java/io/netty/resolver/AbstractAddressResolver.java deleted file mode 100644 index a078b7fee3..0000000000 --- a/resolver/src/main/java/io/netty/resolver/AbstractAddressResolver.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.resolver; - -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; -import io.netty.util.internal.TypeParameterMatcher; - -import java.net.SocketAddress; -import java.nio.channels.UnsupportedAddressTypeException; -import java.util.Collections; -import java.util.List; - -import static java.util.Objects.requireNonNull; - -/** - * A skeletal {@link AddressResolver} implementation. - */ -public abstract class AbstractAddressResolver implements AddressResolver { - - private final EventExecutor executor; - private final TypeParameterMatcher matcher; - - /** - * @param executor the {@link EventExecutor} which is used to notify the listeners of the {@link Future} returned - * by {@link #resolve(SocketAddress)} - */ - protected AbstractAddressResolver(EventExecutor executor) { - this.executor = requireNonNull(executor, "executor"); - matcher = TypeParameterMatcher.find(this, AbstractAddressResolver.class, "T"); - } - - /** - * @param executor the {@link EventExecutor} which is used to notify the listeners of the {@link Future} returned - * by {@link #resolve(SocketAddress)} - * @param addressType the type of the {@link SocketAddress} supported by this resolver - */ - protected AbstractAddressResolver(EventExecutor executor, Class addressType) { - this.executor = requireNonNull(executor, "executor"); - matcher = TypeParameterMatcher.get(addressType); - } - - /** - * Returns the {@link EventExecutor} which is used to notify the listeners of the {@link Future} returned - * by {@link #resolve(SocketAddress)}. - */ - protected EventExecutor executor() { - return executor; - } - - @Override - public boolean isSupported(SocketAddress address) { - return matcher.match(address); - } - - @Override - public final boolean isResolved(SocketAddress address) { - if (!isSupported(address)) { - throw new UnsupportedAddressTypeException(); - } - - @SuppressWarnings("unchecked") - final T castAddress = (T) address; - return doIsResolved(castAddress); - } - - /** - * Invoked by {@link #isResolved(SocketAddress)} to check if the specified {@code address} has been resolved - * already. - */ - protected abstract boolean doIsResolved(T address); - - @Override - public final Future resolve(SocketAddress address) { - if (!isSupported(requireNonNull(address, "address"))) { - // Address type not supported by the resolver - return executor().newFailedFuture(new UnsupportedAddressTypeException()); - } - - if (isResolved(address)) { - // Resolved already; no need to perform a lookup - @SuppressWarnings("unchecked") - final T cast = (T) address; - return executor.newSucceededFuture(cast); - } - - try { - @SuppressWarnings("unchecked") - final T cast = (T) address; - final Promise promise = executor().newPromise(); - doResolve(cast, promise); - return promise.asFuture(); - } catch (Exception e) { - return executor().newFailedFuture(e); - } - } - - @Override - public final Future resolve(SocketAddress address, Promise promise) { - requireNonNull(address, "address"); - requireNonNull(promise, "promise"); - - if (!isSupported(address)) { - // Address type not supported by the resolver - promise.setFailure(new UnsupportedAddressTypeException()); - return promise.asFuture(); - } - - if (isResolved(address)) { - // Resolved already; no need to perform a lookup - @SuppressWarnings("unchecked") - final T cast = (T) address; - promise.setSuccess(cast); - return promise.asFuture(); - } - - try { - @SuppressWarnings("unchecked") - final T cast = (T) address; - doResolve(cast, promise); - } catch (Exception e) { - promise.setFailure(e); - } - return promise.asFuture(); - } - - @Override - public final Future> resolveAll(SocketAddress address) { - if (!isSupported(requireNonNull(address, "address"))) { - // Address type not supported by the resolver - return executor().newFailedFuture(new UnsupportedAddressTypeException()); - } - - if (isResolved(address)) { - // Resolved already; no need to perform a lookup - @SuppressWarnings("unchecked") - final T cast = (T) address; - return executor.newSucceededFuture(Collections.singletonList(cast)); - } - - try { - @SuppressWarnings("unchecked") - final T cast = (T) address; - final Promise> promise = executor().newPromise(); - doResolveAll(cast, promise); - return promise.asFuture(); - } catch (Exception e) { - return executor().newFailedFuture(e); - } - } - - @Override - public final Future> resolveAll(SocketAddress address, Promise> promise) { - requireNonNull(address, "address"); - requireNonNull(promise, "promise"); - - if (!isSupported(address)) { - // Address type not supported by the resolver - promise.setFailure(new UnsupportedAddressTypeException()); - return promise.asFuture(); - } - - if (isResolved(address)) { - // Resolved already; no need to perform a lookup - @SuppressWarnings("unchecked") - final T cast = (T) address; - promise.setSuccess(Collections.singletonList(cast)); - return promise.asFuture(); - } - - try { - @SuppressWarnings("unchecked") - final T cast = (T) address; - doResolveAll(cast, promise); - } catch (Exception e) { - promise.setFailure(e); - } - return promise.asFuture(); - } - - /** - * Invoked by {@link #resolve(SocketAddress)} to perform the actual name - * resolution. - */ - protected abstract void doResolve(T unresolvedAddress, Promise promise) throws Exception; - - /** - * Invoked by {@link #resolveAll(SocketAddress)} to perform the actual name - * resolution. - */ - protected abstract void doResolveAll(T unresolvedAddress, Promise> promise) throws Exception; - - @Override - public void close() { } -} diff --git a/resolver/src/main/java/io/netty/resolver/AddressResolver.java b/resolver/src/main/java/io/netty/resolver/AddressResolver.java deleted file mode 100644 index 6012f08a43..0000000000 --- a/resolver/src/main/java/io/netty/resolver/AddressResolver.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver; - -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; - -import java.io.Closeable; -import java.net.SocketAddress; -import java.nio.channels.UnsupportedAddressTypeException; -import java.util.List; - -/** - * Resolves a possibility unresolved {@link SocketAddress}. - */ -public interface AddressResolver extends Closeable { - - /** - * Returns {@code true} if and only if the specified address is supported by this resolved. - */ - boolean isSupported(SocketAddress address); - - /** - * Returns {@code true} if and only if the specified address has been resolved. - * - * @throws UnsupportedAddressTypeException if the specified address is not supported by this resolver - */ - boolean isResolved(SocketAddress address); - - /** - * Resolves the specified address. If the specified address is resolved already, this method does nothing - * but returning the original address. - * - * @param address the address to resolve - * - * @return the {@link SocketAddress} as the result of the resolution - */ - Future resolve(SocketAddress address); - - /** - * Resolves the specified address. If the specified address is resolved already, this method does nothing - * but returning the original address. - * - * @param address the address to resolve - * @param promise the {@link Promise} which will be fulfilled when the name resolution is finished - * - * @return the {@link SocketAddress} as the result of the resolution - */ - Future resolve(SocketAddress address, Promise promise); - - /** - * Resolves the specified address. If the specified address is resolved already, this method does nothing - * but returning the original address. - * - * @param address the address to resolve - * - * @return the list of the {@link SocketAddress}es as the result of the resolution - */ - Future> resolveAll(SocketAddress address); - - /** - * Resolves the specified address. If the specified address is resolved already, this method does nothing - * but returning the original address. - * - * @param address the address to resolve - * @param promise the {@link Promise} which will be fulfilled when the name resolution is finished - * - * @return the list of the {@link SocketAddress}es as the result of the resolution - */ - Future> resolveAll(SocketAddress address, Promise> promise); - - /** - * Closes all the resources allocated and used by this resolver. - */ - @Override - void close(); -} diff --git a/resolver/src/main/java/io/netty/resolver/AddressResolverGroup.java b/resolver/src/main/java/io/netty/resolver/AddressResolverGroup.java deleted file mode 100644 index c185d8af88..0000000000 --- a/resolver/src/main/java/io/netty/resolver/AddressResolverGroup.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.resolver; - -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.FutureListener; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.io.Closeable; -import java.net.SocketAddress; -import java.util.IdentityHashMap; -import java.util.Map; -import java.util.concurrent.ConcurrentMap; - -import static java.util.Objects.requireNonNull; - -/** - * Creates and manages {@link NameResolver}s so that each {@link EventExecutor} has its own resolver instance. - */ -public abstract class AddressResolverGroup implements Closeable { - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(AddressResolverGroup.class); - - /** - * Note that we do not use a {@link ConcurrentMap} here because it is usually expensive to instantiate a resolver. - */ - private final Map> resolvers = new IdentityHashMap<>(); - - private final Map> executorTerminationListeners = - new IdentityHashMap<>(); - - protected AddressResolverGroup() { } - - /** - * Returns the {@link AddressResolver} associated with the specified {@link EventExecutor}. If there's no associated - * resolver found, this method creates and returns a new resolver instance created by - * {@link #newResolver(EventExecutor)} so that the new resolver is reused on another - * {@code #getResolver(EventExecutor)} call with the same {@link EventExecutor}. - */ - public AddressResolver getResolver(final EventExecutor executor) { - requireNonNull(executor, "executor"); - - if (executor.isShuttingDown()) { - throw new IllegalStateException("executor not accepting a task"); - } - - AddressResolver r; - synchronized (resolvers) { - r = resolvers.get(executor); - if (r == null) { - final AddressResolver newResolver; - try { - newResolver = newResolver(executor); - } catch (Exception e) { - throw new IllegalStateException("failed to create a new resolver", e); - } - - resolvers.put(executor, newResolver); - FutureListener terminationListener = future -> { - synchronized (resolvers) { - resolvers.remove(executor); - executorTerminationListeners.remove(executor); - } - newResolver.close(); - }; - executorTerminationListeners.put(executor, terminationListener); - executor.terminationFuture().addListener(terminationListener); - - r = newResolver; - } - } - - return r; - } - - /** - * Invoked by {@link #getResolver(EventExecutor)} to create a new {@link AddressResolver}. - */ - protected abstract AddressResolver newResolver(EventExecutor executor) throws Exception; - - /** - * Closes all {@link NameResolver}s created by this group. - */ - @Override - @SuppressWarnings({ "unchecked", "ZeroLengthArrayAllocation" }) - public void close() { - final AddressResolver[] rArray; - - synchronized (resolvers) { - rArray = (AddressResolver[]) resolvers.values().toArray(new AddressResolver[0]); - resolvers.clear(); - executorTerminationListeners.clear(); - } - - for (final AddressResolver r: rArray) { - try { - r.close(); - } catch (Throwable t) { - logger.warn("Failed to close a resolver:", t); - } - } - } -} diff --git a/resolver/src/main/java/io/netty/resolver/CompositeNameResolver.java b/resolver/src/main/java/io/netty/resolver/CompositeNameResolver.java deleted file mode 100644 index ac4890aaa5..0000000000 --- a/resolver/src/main/java/io/netty/resolver/CompositeNameResolver.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver; - -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.FutureListener; -import io.netty.util.concurrent.Promise; - -import java.util.Arrays; -import java.util.List; - -import static java.util.Objects.requireNonNull; - -/** - * A composite {@link SimpleNameResolver} that resolves a host name against a sequence of {@link NameResolver}s. - * - * In case of a failure, only the last one will be reported. - */ -public final class CompositeNameResolver extends SimpleNameResolver { - - private final NameResolver[] resolvers; - - /** - * @param executor the {@link EventExecutor} which is used to notify the listeners of the {@link Future} returned - * by {@link #resolve(String)} - * @param resolvers the {@link NameResolver}s to be tried sequentially - */ - public CompositeNameResolver(EventExecutor executor, NameResolver... resolvers) { - super(executor); - requireNonNull(resolvers, "resolvers"); - for (int i = 0; i < resolvers.length; i++) { - requireNonNull(resolvers[i], "resolvers[" + i + "]"); - } - if (resolvers.length < 2) { - throw new IllegalArgumentException("resolvers: " + Arrays.asList(resolvers) + - " (expected: at least 2 resolvers)"); - } - this.resolvers = resolvers.clone(); - } - - @Override - protected void doResolve(String inetHost, Promise promise) throws Exception { - doResolveRec(inetHost, promise, 0, null); - } - - private void doResolveRec(final String inetHost, - final Promise promise, - final int resolverIndex, - Throwable lastFailure) throws Exception { - if (resolverIndex >= resolvers.length) { - promise.setFailure(lastFailure); - } else { - NameResolver resolver = resolvers[resolverIndex]; - resolver.resolve(inetHost).addListener(future -> { - if (future.isSuccess()) { - promise.setSuccess(future.getNow()); - } else { - doResolveRec(inetHost, promise, resolverIndex + 1, future.cause()); - } - }); - } - } - - @Override - protected void doResolveAll(String inetHost, Promise> promise) throws Exception { - doResolveAllRec(inetHost, promise, 0, null); - } - - private void doResolveAllRec(final String inetHost, - final Promise> promise, - final int resolverIndex, - Throwable lastFailure) throws Exception { - if (resolverIndex >= resolvers.length) { - promise.setFailure(lastFailure); - } else { - NameResolver resolver = resolvers[resolverIndex]; - resolver.resolveAll(inetHost).addListener(future -> { - if (future.isSuccess()) { - promise.setSuccess(future.getNow()); - } else { - doResolveAllRec(inetHost, promise, resolverIndex + 1, future.cause()); - } - }); - } - } -} diff --git a/resolver/src/main/java/io/netty/resolver/DefaultAddressResolverGroup.java b/resolver/src/main/java/io/netty/resolver/DefaultAddressResolverGroup.java deleted file mode 100644 index 15980fc3f2..0000000000 --- a/resolver/src/main/java/io/netty/resolver/DefaultAddressResolverGroup.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.resolver; - -import io.netty.util.concurrent.EventExecutor; - -import java.net.InetSocketAddress; - -/** - * A {@link AddressResolverGroup} of {@link DefaultNameResolver}s. - */ -public final class DefaultAddressResolverGroup extends AddressResolverGroup { - - public static final DefaultAddressResolverGroup INSTANCE = new DefaultAddressResolverGroup(); - - private DefaultAddressResolverGroup() { } - - @Override - protected AddressResolver newResolver(EventExecutor executor) throws Exception { - return new DefaultNameResolver(executor).asAddressResolver(); - } -} diff --git a/resolver/src/main/java/io/netty/resolver/DefaultHostsFileEntriesResolver.java b/resolver/src/main/java/io/netty/resolver/DefaultHostsFileEntriesResolver.java deleted file mode 100644 index 378fd98d87..0000000000 --- a/resolver/src/main/java/io/netty/resolver/DefaultHostsFileEntriesResolver.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver; - -import io.netty.util.CharsetUtil; -import io.netty.util.internal.PlatformDependent; - -import java.net.InetAddress; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.Map; - -/** - * Default {@link HostsFileEntriesResolver} that resolves hosts file entries only once. - */ -public final class DefaultHostsFileEntriesResolver implements HostsFileEntriesResolver { - - private final Map> inet4Entries; - private final Map> inet6Entries; - - public DefaultHostsFileEntriesResolver() { - this(parseEntries()); - } - - // for testing purpose only - DefaultHostsFileEntriesResolver(HostsFileEntriesProvider entries) { - inet4Entries = entries.ipv4Entries(); - inet6Entries = entries.ipv6Entries(); - } - - @Override - public InetAddress address(String inetHost, ResolvedAddressTypes resolvedAddressTypes) { - String normalized = normalize(inetHost); - switch (resolvedAddressTypes) { - case IPV4_ONLY: - return firstAddress(inet4Entries.get(normalized)); - case IPV6_ONLY: - return firstAddress(inet6Entries.get(normalized)); - case IPV4_PREFERRED: - InetAddress inet4Address = firstAddress(inet4Entries.get(normalized)); - return inet4Address != null ? inet4Address : firstAddress(inet6Entries.get(normalized)); - case IPV6_PREFERRED: - InetAddress inet6Address = firstAddress(inet6Entries.get(normalized)); - return inet6Address != null ? inet6Address : firstAddress(inet4Entries.get(normalized)); - default: - throw new IllegalArgumentException("Unknown ResolvedAddressTypes " + resolvedAddressTypes); - } - } - - /** - * Resolves all addresses of a hostname against the entries in a hosts file, depending on the specified - * {@link ResolvedAddressTypes}. - * - * @param inetHost the hostname to resolve - * @param resolvedAddressTypes the address types to resolve - * @return all matching addresses or {@code null} in case the hostname cannot be resolved - */ - public List addresses(String inetHost, ResolvedAddressTypes resolvedAddressTypes) { - String normalized = normalize(inetHost); - switch (resolvedAddressTypes) { - case IPV4_ONLY: - return inet4Entries.get(normalized); - case IPV6_ONLY: - return inet6Entries.get(normalized); - case IPV4_PREFERRED: - List allInet4Addresses = inet4Entries.get(normalized); - return allInet4Addresses != null ? allAddresses(allInet4Addresses, inet6Entries.get(normalized)) : - inet6Entries.get(normalized); - case IPV6_PREFERRED: - List allInet6Addresses = inet6Entries.get(normalized); - return allInet6Addresses != null ? allAddresses(allInet6Addresses, inet4Entries.get(normalized)) : - inet4Entries.get(normalized); - default: - throw new IllegalArgumentException("Unknown ResolvedAddressTypes " + resolvedAddressTypes); - } - } - - // package-private for testing purposes - String normalize(String inetHost) { - return inetHost.toLowerCase(Locale.ENGLISH); - } - - private static List allAddresses(List a, List b) { - List result = new ArrayList(a.size() + (b == null ? 0 : b.size())); - result.addAll(a); - if (b != null) { - result.addAll(b); - } - return result; - } - - private static InetAddress firstAddress(List addresses) { - return addresses != null && !addresses.isEmpty() ? addresses.get(0) : null; - } - - private static HostsFileEntriesProvider 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 instance. - return HostsFileEntriesProvider.parser() - .parseSilently(Charset.defaultCharset(), CharsetUtil.UTF_16, CharsetUtil.UTF_8); - } - return HostsFileEntriesProvider.parser().parseSilently(); - } -} diff --git a/resolver/src/main/java/io/netty/resolver/DefaultNameResolver.java b/resolver/src/main/java/io/netty/resolver/DefaultNameResolver.java deleted file mode 100644 index b399d41f06..0000000000 --- a/resolver/src/main/java/io/netty/resolver/DefaultNameResolver.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.resolver; - -import io.netty.util.internal.SocketUtils; -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.Promise; - -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.Arrays; -import java.util.List; - -/** - * A {@link InetNameResolver} that resolves using JDK's built-in domain name lookup mechanism. - * Note that this resolver performs a blocking name lookup from the caller thread. - */ -public class DefaultNameResolver extends InetNameResolver { - - public DefaultNameResolver(EventExecutor executor) { - super(executor); - } - - @Override - protected void doResolve(String inetHost, Promise promise) throws Exception { - try { - promise.setSuccess(SocketUtils.addressByName(inetHost)); - } catch (UnknownHostException e) { - promise.setFailure(e); - } - } - - @Override - protected void doResolveAll(String inetHost, Promise> promise) throws Exception { - try { - promise.setSuccess(Arrays.asList(SocketUtils.allAddressesByName(inetHost))); - } catch (UnknownHostException e) { - promise.setFailure(e); - } - } -} diff --git a/resolver/src/main/java/io/netty/resolver/HostsFileEntries.java b/resolver/src/main/java/io/netty/resolver/HostsFileEntries.java deleted file mode 100644 index c11464023e..0000000000 --- a/resolver/src/main/java/io/netty/resolver/HostsFileEntries.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver; - -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -/** - * A container of hosts file entries. - * The mappings contain only the first entry per hostname. - * Consider using {@link HostsFileEntriesProvider} when mappings with all entries per hostname are needed. - */ -public final class HostsFileEntries { - - /** - * Empty entries - */ - static final HostsFileEntries EMPTY = new HostsFileEntries(Collections.emptyMap(), Collections.emptyMap()); - - private final Map inet4Entries; - private final Map inet6Entries; - - public HostsFileEntries(Map inet4Entries, Map inet6Entries) { - this.inet4Entries = Collections.unmodifiableMap(new HashMap<>(inet4Entries)); - this.inet6Entries = Collections.unmodifiableMap(new HashMap<>(inet6Entries)); - } - - /** - * The IPv4 entries - * @return the IPv4 entries - */ - public Map inet4Entries() { - return inet4Entries; - } - - /** - * The IPv6 entries - * @return the IPv6 entries - */ - public Map inet6Entries() { - return inet6Entries; - } -} diff --git a/resolver/src/main/java/io/netty/resolver/HostsFileEntriesProvider.java b/resolver/src/main/java/io/netty/resolver/HostsFileEntriesProvider.java deleted file mode 100644 index aaf29208e0..0000000000 --- a/resolver/src/main/java/io/netty/resolver/HostsFileEntriesProvider.java +++ /dev/null @@ -1,317 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver; - -import io.netty.util.NetUtil; -import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.Reader; -import java.net.Inet4Address; -import java.net.InetAddress; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.regex.Pattern; - -import static io.netty.util.internal.ObjectUtil.checkNotNull; - -/** - * A container of hosts file entries - */ -public final class HostsFileEntriesProvider { - - public interface Parser { - - /** - * Parses the hosts file at standard OS location using the system default {@link Charset} for decoding. - * - * @return a new {@link HostsFileEntriesProvider} - * @throws IOException file could not be read - */ - HostsFileEntriesProvider parse() throws IOException; - - /** - * Parses the hosts file at standard OS location using the given {@link Charset}s one after another until - * parse something or none is left. - * - * @param charsets the {@link Charset}s to try as file encodings when parsing - * @return a new {@link HostsFileEntriesProvider} - * @throws IOException file could not be read - */ - HostsFileEntriesProvider parse(Charset... charsets) throws IOException; - - /** - * Parses the provided hosts file using the given {@link Charset}s one after another until - * parse something or none is left. In case {@link Charset}s are not provided, - * the system default {@link Charset} is used for decoding. - * - * @param file the file to be parsed - * @param charsets the {@link Charset}s to try as file encodings when parsing, in case {@link Charset}s - * are not provided, the system default {@link Charset} is used for decoding - * @return a new {@link HostsFileEntriesProvider} - * @throws IOException file could not be read - */ - HostsFileEntriesProvider parse(File file, Charset... charsets) throws IOException; - - /** - * Performs the parsing operation using the provided reader of hosts file format. - * - * @param reader the reader of hosts file format - * @return a new {@link HostsFileEntriesProvider} - */ - HostsFileEntriesProvider parse(Reader reader) throws IOException; - - /** - * Parses the hosts file at standard OS location using the system default {@link Charset} for decoding. - * - * @return a new {@link HostsFileEntriesProvider} - */ - HostsFileEntriesProvider parseSilently(); - - /** - * Parses the hosts file at standard OS location using the given {@link Charset}s one after another until - * parse something or none is left. - * - * @param charsets the {@link Charset}s to try as file encodings when parsing - * @return a new {@link HostsFileEntriesProvider} - */ - HostsFileEntriesProvider parseSilently(Charset... charsets); - - /** - * Parses the provided hosts file using the given {@link Charset}s one after another until - * parse something or none is left. In case {@link Charset}s are not provided, - * the system default {@link Charset} is used for decoding. - * - * @param file the file to be parsed - * @param charsets the {@link Charset}s to try as file encodings when parsing, in case {@link Charset}s - * are not provided, the system default {@link Charset} is used for decoding - * @return a new {@link HostsFileEntriesProvider} - */ - HostsFileEntriesProvider parseSilently(File file, Charset... charsets); - } - - /** - * Creates a parser for {@link HostsFileEntriesProvider}. - * - * @return a new {@link HostsFileEntriesProvider.Parser} - */ - public static Parser parser() { - return ParserImpl.INSTANCE; - } - - static final HostsFileEntriesProvider EMPTY = - new HostsFileEntriesProvider( - Collections.>emptyMap(), - Collections.>emptyMap()); - - private final Map> ipv4Entries; - private final Map> ipv6Entries; - - HostsFileEntriesProvider(Map> ipv4Entries, Map> ipv6Entries) { - this.ipv4Entries = Collections.unmodifiableMap(new HashMap>(ipv4Entries)); - this.ipv6Entries = Collections.unmodifiableMap(new HashMap>(ipv6Entries)); - } - - /** - * The IPv4 entries. - * - * @return the IPv4 entries - */ - public Map> ipv4Entries() { - return ipv4Entries; - } - - /** - * The IPv6 entries. - * - * @return the IPv6 entries - */ - public Map> ipv6Entries() { - return ipv6Entries; - } - - private static final class ParserImpl implements Parser { - - private static final String WINDOWS_DEFAULT_SYSTEM_ROOT = "C:\\Windows"; - private static final String WINDOWS_HOSTS_FILE_RELATIVE_PATH = "\\system32\\drivers\\etc\\hosts"; - private static final String X_PLATFORMS_HOSTS_FILE_PATH = "/etc/hosts"; - - private static final Pattern WHITESPACES = Pattern.compile("[ \t]+"); - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(Parser.class); - - static final ParserImpl INSTANCE = new ParserImpl(); - - private ParserImpl() { - // singleton - } - - @Override - public HostsFileEntriesProvider parse() throws IOException { - return parse(locateHostsFile(), Charset.defaultCharset()); - } - - @Override - public HostsFileEntriesProvider parse(Charset... charsets) throws IOException { - return parse(locateHostsFile(), charsets); - } - - @Override - public HostsFileEntriesProvider parse(File file, Charset... charsets) throws IOException { - checkNotNull(file, "file"); - checkNotNull(charsets, "charsets"); - if (charsets.length == 0) { - charsets = new Charset[]{Charset.defaultCharset()}; - } - if (file.exists() && file.isFile()) { - for (Charset charset : charsets) { - BufferedReader reader = new BufferedReader( - new InputStreamReader(new FileInputStream(file), charset)); - try { - HostsFileEntriesProvider entries = parse(reader); - if (entries != HostsFileEntriesProvider.EMPTY) { - return entries; - } - } finally { - reader.close(); - } - } - } - return HostsFileEntriesProvider.EMPTY; - } - - @Override - public HostsFileEntriesProvider parse(Reader reader) throws IOException { - checkNotNull(reader, "reader"); - BufferedReader buff = new BufferedReader(reader); - try { - Map> ipv4Entries = new HashMap>(); - Map> ipv6Entries = new HashMap>(); - String line; - while ((line = buff.readLine()) != null) { - // remove comment - int commentPosition = line.indexOf('#'); - if (commentPosition != -1) { - line = line.substring(0, commentPosition); - } - // skip empty lines - line = line.trim(); - if (line.isEmpty()) { - continue; - } - - // split - List lineParts = new ArrayList(); - for (String s : WHITESPACES.split(line)) { - if (!s.isEmpty()) { - lineParts.add(s); - } - } - - // a valid line should be [IP, hostname, alias*] - if (lineParts.size() < 2) { - // skip invalid line - continue; - } - - byte[] ipBytes = NetUtil.createByteArrayFromIpAddressString(lineParts.get(0)); - - if (ipBytes == null) { - // skip invalid IP - continue; - } - - // loop over hostname and aliases - for (int i = 1; i < lineParts.size(); i++) { - String hostname = lineParts.get(i); - String hostnameLower = hostname.toLowerCase(Locale.ENGLISH); - InetAddress address = InetAddress.getByAddress(hostname, ipBytes); - List addresses; - if (address instanceof Inet4Address) { - addresses = ipv4Entries.get(hostnameLower); - if (addresses == null) { - addresses = new ArrayList(); - ipv4Entries.put(hostnameLower, addresses); - } - } else { - addresses = ipv6Entries.get(hostnameLower); - if (addresses == null) { - addresses = new ArrayList(); - ipv6Entries.put(hostnameLower, addresses); - } - } - addresses.add(address); - } - } - return ipv4Entries.isEmpty() && ipv6Entries.isEmpty() ? - HostsFileEntriesProvider.EMPTY : - new HostsFileEntriesProvider(ipv4Entries, ipv6Entries); - } finally { - try { - buff.close(); - } catch (IOException e) { - logger.warn("Failed to close a reader", e); - } - } - } - - @Override - public HostsFileEntriesProvider parseSilently() { - return parseSilently(locateHostsFile(), Charset.defaultCharset()); - } - - @Override - public HostsFileEntriesProvider parseSilently(Charset... charsets) { - return parseSilently(locateHostsFile(), charsets); - } - - @Override - public HostsFileEntriesProvider parseSilently(File file, Charset... charsets) { - try { - return parse(file, charsets); - } catch (IOException e) { - if (logger.isWarnEnabled()) { - logger.warn("Failed to load and parse hosts file at " + file.getPath(), e); - } - return HostsFileEntriesProvider.EMPTY; - } - } - - private static File locateHostsFile() { - File hostsFile; - if (PlatformDependent.isWindows()) { - hostsFile = new File(System.getenv("SystemRoot") + WINDOWS_HOSTS_FILE_RELATIVE_PATH); - if (!hostsFile.exists()) { - hostsFile = new File(WINDOWS_DEFAULT_SYSTEM_ROOT + WINDOWS_HOSTS_FILE_RELATIVE_PATH); - } - } else { - hostsFile = new File(X_PLATFORMS_HOSTS_FILE_PATH); - } - return hostsFile; - } - } -} diff --git a/resolver/src/main/java/io/netty/resolver/HostsFileEntriesResolver.java b/resolver/src/main/java/io/netty/resolver/HostsFileEntriesResolver.java deleted file mode 100644 index 06bd140988..0000000000 --- a/resolver/src/main/java/io/netty/resolver/HostsFileEntriesResolver.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver; - -import java.net.InetAddress; - -/** - * Resolves a hostname against the hosts file entries. - */ -public interface HostsFileEntriesResolver { - - /** - * Default instance: a {@link DefaultHostsFileEntriesResolver}. - */ - HostsFileEntriesResolver DEFAULT = new DefaultHostsFileEntriesResolver(); - - /** - * Resolve the address of a hostname against the entries in a hosts file, depending on some address types. - * @param inetHost the hostname to resolve - * @param resolvedAddressTypes the address types to resolve - * @return the first matching address - */ - InetAddress address(String inetHost, ResolvedAddressTypes resolvedAddressTypes); -} diff --git a/resolver/src/main/java/io/netty/resolver/HostsFileParser.java b/resolver/src/main/java/io/netty/resolver/HostsFileParser.java deleted file mode 100644 index f10e5f0ca3..0000000000 --- a/resolver/src/main/java/io/netty/resolver/HostsFileParser.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver; - -import java.io.File; -import java.io.IOException; -import java.io.Reader; -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.nio.charset.Charset; -import java.util.List; -import java.util.HashMap; -import java.util.Map; - -/** - * A parser for hosts files. - * The produced mappings contain only the first entry per hostname. - * Consider using {@link HostsFileEntriesProvider} when mappings with all entries per hostname are needed. - */ -public final class HostsFileParser { - - /** - * Parse hosts file at standard OS location using the systems default {@link Charset} for decoding. - * - * @return a {@link HostsFileEntries} - */ - public static HostsFileEntries parseSilently() { - return hostsFileEntries(HostsFileEntriesProvider.parser().parseSilently()); - } - - /** - * 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) { - return hostsFileEntries(HostsFileEntriesProvider.parser().parseSilently(charsets)); - } - - /** - * 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 - */ - public static HostsFileEntries parse() throws IOException { - return hostsFileEntries(HostsFileEntriesProvider.parser().parse()); - } - - /** - * 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 hostsFileEntries(HostsFileEntriesProvider.parser().parse(file)); - } - - /** - * 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 { - return hostsFileEntries(HostsFileEntriesProvider.parser().parse(file, charsets)); - } - - /** - * Parse a reader of hosts file format. - * - * @param reader the file to be parsed - * @return a {@link HostsFileEntries} - * @throws IOException file could not be read - */ - public static HostsFileEntries parse(Reader reader) throws IOException { - return hostsFileEntries(HostsFileEntriesProvider.parser().parse(reader)); - } - - /** - * Can't be instantiated. - */ - private HostsFileParser() { - } - - @SuppressWarnings("unchecked") - private static HostsFileEntries hostsFileEntries(HostsFileEntriesProvider provider) { - return provider == HostsFileEntriesProvider.EMPTY ? HostsFileEntries.EMPTY : - new HostsFileEntries((Map) toMapWithSingleValue(provider.ipv4Entries()), - (Map) toMapWithSingleValue(provider.ipv6Entries())); - } - - private static Map toMapWithSingleValue(Map> fromMapWithListValue) { - Map result = new HashMap<>(fromMapWithListValue.size()); - for (Map.Entry> entry : fromMapWithListValue.entrySet()) { - List value = entry.getValue(); - if (!value.isEmpty()) { - result.put(entry.getKey(), value.get(0)); - } - } - return result; - } -} diff --git a/resolver/src/main/java/io/netty/resolver/InetNameResolver.java b/resolver/src/main/java/io/netty/resolver/InetNameResolver.java deleted file mode 100644 index 55e53a7222..0000000000 --- a/resolver/src/main/java/io/netty/resolver/InetNameResolver.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver; - -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.Future; - -import java.net.InetAddress; -import java.net.InetSocketAddress; - -/** - * A skeletal {@link NameResolver} implementation that resolves {@link InetAddress}. - */ -public abstract class InetNameResolver extends SimpleNameResolver { - private volatile AddressResolver addressResolver; - - /** - * @param executor the {@link EventExecutor} which is used to notify the listeners of the {@link Future} returned - * by {@link #resolve(String)} - */ - protected InetNameResolver(EventExecutor executor) { - super(executor); - } - - /** - * Return a {@link AddressResolver} that will use this name resolver underneath. - * It's cached internally, so the same instance is always returned. - */ - public AddressResolver asAddressResolver() { - AddressResolver result = addressResolver; - if (result == null) { - synchronized (this) { - result = addressResolver; - if (result == null) { - addressResolver = result = new InetSocketAddressResolver(executor(), this); - } - } - } - return result; - } -} diff --git a/resolver/src/main/java/io/netty/resolver/InetSocketAddressResolver.java b/resolver/src/main/java/io/netty/resolver/InetSocketAddressResolver.java deleted file mode 100644 index d1304a9c09..0000000000 --- a/resolver/src/main/java/io/netty/resolver/InetSocketAddressResolver.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver; - -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.FutureListener; -import io.netty.util.concurrent.Promise; - -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.util.ArrayList; -import java.util.List; - -/** - * A {@link AbstractAddressResolver} that resolves {@link InetSocketAddress}. - */ -public class InetSocketAddressResolver extends AbstractAddressResolver { - - final NameResolver nameResolver; - - /** - * @param executor the {@link EventExecutor} which is used to notify the listeners of the {@link Future} returned - * by {@link #resolve(java.net.SocketAddress)} - * @param nameResolver the {@link NameResolver} used for name resolution - */ - public InetSocketAddressResolver(EventExecutor executor, NameResolver nameResolver) { - super(executor, InetSocketAddress.class); - this.nameResolver = nameResolver; - } - - @Override - protected boolean doIsResolved(InetSocketAddress address) { - return !address.isUnresolved(); - } - - @Override - protected void doResolve(final InetSocketAddress unresolvedAddress, final Promise promise) - throws Exception { - // Note that InetSocketAddress.getHostName() will never incur a reverse lookup here, - // because an unresolved address always has a host name. - nameResolver.resolve(unresolvedAddress.getHostName()) - .addListener(future -> { - if (future.isSuccess()) { - promise.setSuccess(new InetSocketAddress(future.getNow(), unresolvedAddress.getPort())); - } else { - promise.setFailure(future.cause()); - } - }); - } - - @Override - protected void doResolveAll(final InetSocketAddress unresolvedAddress, - final Promise> promise) throws Exception { - // Note that InetSocketAddress.getHostName() will never incur a reverse lookup here, - // because an unresolved address always has a host name. - nameResolver.resolveAll(unresolvedAddress.getHostName()) - .addListener(future -> { - if (future.isSuccess()) { - List inetAddresses = future.getNow(); - List socketAddresses = - new ArrayList<>(inetAddresses.size()); - for (InetAddress inetAddress : inetAddresses) { - socketAddresses.add(new InetSocketAddress(inetAddress, unresolvedAddress.getPort())); - } - promise.setSuccess(socketAddresses); - } else { - promise.setFailure(future.cause()); - } - }); - } - - @Override - public void close() { - nameResolver.close(); - } -} diff --git a/resolver/src/main/java/io/netty/resolver/NameResolver.java b/resolver/src/main/java/io/netty/resolver/NameResolver.java deleted file mode 100644 index 844cdb98d7..0000000000 --- a/resolver/src/main/java/io/netty/resolver/NameResolver.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.resolver; - -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; - -import java.io.Closeable; -import java.util.List; - -/** - * Resolves an arbitrary string that represents the name of an endpoint into an address. - */ -public interface NameResolver extends Closeable { - - /** - * Resolves the specified name into an address. - * - * @param inetHost the name to resolve - * - * @return the address as the result of the resolution - */ - Future resolve(String inetHost); - - /** - * Resolves the specified name into an address. - * - * @param inetHost the name to resolve - * @param promise the {@link Promise} which will be fulfilled when the name resolution is finished - * - * @return the address as the result of the resolution - */ - Future resolve(String inetHost, Promise promise); - - /** - * Resolves the specified host name and port into a list of address. - * - * @param inetHost the name to resolve - * - * @return the list of the address as the result of the resolution - */ - Future> resolveAll(String inetHost); - - /** - * Resolves the specified host name and port into a list of address. - * - * @param inetHost the name to resolve - * @param promise the {@link Promise} which will be fulfilled when the name resolution is finished - * - * @return the list of the address as the result of the resolution - */ - Future> resolveAll(String inetHost, Promise> promise); - - /** - * Closes all the resources allocated and used by this resolver. - */ - @Override - void close(); -} diff --git a/resolver/src/main/java/io/netty/resolver/NoopAddressResolver.java b/resolver/src/main/java/io/netty/resolver/NoopAddressResolver.java deleted file mode 100644 index d4e645c4e4..0000000000 --- a/resolver/src/main/java/io/netty/resolver/NoopAddressResolver.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.resolver; - -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.Promise; - -import java.net.SocketAddress; -import java.util.Collections; -import java.util.List; - -/** - * A {@link AddressResolver} that does not perform any resolution but always reports successful resolution. - * This resolver is useful when name resolution is performed by a handler in a pipeline, such as a proxy handler. - */ -public class NoopAddressResolver extends AbstractAddressResolver { - - public NoopAddressResolver(EventExecutor executor) { - super(executor); - } - - @Override - protected boolean doIsResolved(SocketAddress address) { - return true; - } - - @Override - protected void doResolve(SocketAddress unresolvedAddress, Promise promise) throws Exception { - promise.setSuccess(unresolvedAddress); - } - - @Override - protected void doResolveAll( - SocketAddress unresolvedAddress, Promise> promise) throws Exception { - promise.setSuccess(Collections.singletonList(unresolvedAddress)); - } -} diff --git a/resolver/src/main/java/io/netty/resolver/NoopAddressResolverGroup.java b/resolver/src/main/java/io/netty/resolver/NoopAddressResolverGroup.java deleted file mode 100644 index e2135fb92e..0000000000 --- a/resolver/src/main/java/io/netty/resolver/NoopAddressResolverGroup.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.resolver; - -import io.netty.util.concurrent.EventExecutor; - -import java.net.SocketAddress; - -/** - * A {@link AddressResolverGroup} of {@link NoopAddressResolver}s. - */ -public final class NoopAddressResolverGroup extends AddressResolverGroup { - - public static final NoopAddressResolverGroup INSTANCE = new NoopAddressResolverGroup(); - - private NoopAddressResolverGroup() { } - - @Override - protected AddressResolver newResolver(EventExecutor executor) throws Exception { - return new NoopAddressResolver(executor); - } -} diff --git a/resolver/src/main/java/io/netty/resolver/ResolvedAddressTypes.java b/resolver/src/main/java/io/netty/resolver/ResolvedAddressTypes.java deleted file mode 100644 index cb2d384ac5..0000000000 --- a/resolver/src/main/java/io/netty/resolver/ResolvedAddressTypes.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver; - -/** - * Defined resolved address types. - */ -public enum ResolvedAddressTypes { - /** - * Only resolve IPv4 addresses - */ - IPV4_ONLY, - /** - * Only resolve IPv6 addresses - */ - IPV6_ONLY, - /** - * Prefer IPv4 addresses over IPv6 ones - */ - IPV4_PREFERRED, - /** - * Prefer IPv6 addresses over IPv4 ones - */ - IPV6_PREFERRED -} diff --git a/resolver/src/main/java/io/netty/resolver/RoundRobinInetAddressResolver.java b/resolver/src/main/java/io/netty/resolver/RoundRobinInetAddressResolver.java deleted file mode 100644 index fda92477ee..0000000000 --- a/resolver/src/main/java/io/netty/resolver/RoundRobinInetAddressResolver.java +++ /dev/null @@ -1,100 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver; - -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.FutureListener; -import io.netty.util.concurrent.Promise; - -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.ThreadLocalRandom; - -/** - * A {@link NameResolver} that resolves {@link InetAddress} and force Round Robin by choosing a single address - * randomly in {@link #resolve(String)} and {@link #resolve(String, Promise)} - * if multiple are returned by the {@link NameResolver}. - * Use {@link #asAddressResolver()} to create a {@link InetSocketAddress} resolver - */ -public class RoundRobinInetAddressResolver extends InetNameResolver { - private final NameResolver nameResolver; - - /** - * @param executor the {@link EventExecutor} which is used to notify the listeners of the {@link Future} returned by - * {@link #resolve(String)} - * @param nameResolver the {@link NameResolver} used for name resolution - */ - public RoundRobinInetAddressResolver(EventExecutor executor, NameResolver nameResolver) { - super(executor); - this.nameResolver = nameResolver; - } - - @Override - protected void doResolve(final String inetHost, final Promise promise) throws Exception { - // hijack the doResolve request, but do a doResolveAll request under the hood. - // Note that InetSocketAddress.getHostName() will never incur a reverse lookup here, - // because an unresolved address always has a host name. - nameResolver.resolveAll(inetHost).addListener(future -> { - if (future.isSuccess()) { - List inetAddresses = future.getNow(); - int numAddresses = inetAddresses.size(); - if (numAddresses > 0) { - // if there are multiple addresses: we shall pick one by one - // to support the round robin distribution - promise.setSuccess(inetAddresses.get(randomIndex(numAddresses))); - } else { - promise.setFailure(new UnknownHostException(inetHost)); - } - } else { - promise.setFailure(future.cause()); - } - }); - } - - @Override - protected void doResolveAll(String inetHost, final Promise> promise) throws Exception { - nameResolver.resolveAll(inetHost).addListener(future -> { - if (future.isSuccess()) { - List inetAddresses = future.getNow(); - if (!inetAddresses.isEmpty()) { - // create a copy to make sure that it's modifiable random access collection - List result = new ArrayList<>(inetAddresses); - // rotate by different distance each time to force round robin distribution - Collections.rotate(result, randomIndex(inetAddresses.size())); - promise.setSuccess(result); - } else { - promise.setSuccess(inetAddresses); - } - } else { - promise.setFailure(future.cause()); - } - }); - } - - private static int randomIndex(int numAddresses) { - return numAddresses == 1 ? 0 : ThreadLocalRandom.current().nextInt(numAddresses); - } - - @Override - public void close() { - nameResolver.close(); - } -} diff --git a/resolver/src/main/java/io/netty/resolver/SimpleNameResolver.java b/resolver/src/main/java/io/netty/resolver/SimpleNameResolver.java deleted file mode 100644 index 8bf0c27619..0000000000 --- a/resolver/src/main/java/io/netty/resolver/SimpleNameResolver.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.resolver; - -import static java.util.Objects.requireNonNull; - -import io.netty.util.concurrent.EventExecutor; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; - -import java.util.List; - -/** - * A skeletal {@link NameResolver} implementation. - */ -public abstract class SimpleNameResolver implements NameResolver { - - private final EventExecutor executor; - - /** - * @param executor the {@link EventExecutor} which is used to notify the listeners of the {@link Future} returned - * by {@link #resolve(String)} - */ - protected SimpleNameResolver(EventExecutor executor) { - this.executor = requireNonNull(executor, "executor"); - } - - /** - * Returns the {@link EventExecutor} which is used to notify the listeners of the {@link Future} returned - * by {@link #resolve(String)}. - */ - protected EventExecutor executor() { - return executor; - } - - @Override - public final Future resolve(String inetHost) { - final Promise promise = executor().newPromise(); - return resolve(inetHost, promise); - } - - @Override - public Future resolve(String inetHost, Promise promise) { - requireNonNull(promise, "promise"); - - try { - doResolve(inetHost, promise); - } catch (Exception e) { - promise.setFailure(e); - } - return promise.asFuture(); - } - - @Override - public final Future> resolveAll(String inetHost) { - final Promise> promise = executor().newPromise(); - return resolveAll(inetHost, promise); - } - - @Override - public Future> resolveAll(String inetHost, Promise> promise) { - requireNonNull(promise, "promise"); - - try { - doResolveAll(inetHost, promise); - } catch (Exception e) { - promise.setFailure(e); - } - return promise.asFuture(); - } - - /** - * Invoked by {@link #resolve(String)} to perform the actual name resolution. - */ - protected abstract void doResolve(String inetHost, Promise promise) throws Exception; - - /** - * Invoked by {@link #resolveAll(String)} to perform the actual name resolution. - */ - protected abstract void doResolveAll(String inetHost, Promise> promise) throws Exception; - - @Override - public void close() { } -} diff --git a/resolver/src/main/java/io/netty/resolver/package-info.java b/resolver/src/main/java/io/netty/resolver/package-info.java deleted file mode 100644 index 3695a59762..0000000000 --- a/resolver/src/main/java/io/netty/resolver/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * Resolves an arbitrary string that represents the name of an endpoint into an address. - */ -package io.netty.resolver; diff --git a/resolver/src/test/java/io/netty/resolver/DefaultHostsFileEntriesResolverTest.java b/resolver/src/test/java/io/netty/resolver/DefaultHostsFileEntriesResolverTest.java deleted file mode 100644 index 8c2a1ed223..0000000000 --- a/resolver/src/test/java/io/netty/resolver/DefaultHostsFileEntriesResolverTest.java +++ /dev/null @@ -1,142 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver; - -import io.netty.util.NetUtil; - -import org.junit.jupiter.api.Test; - -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.instanceOf; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; - -public class DefaultHostsFileEntriesResolverTest { - - /** - * show issue https://github.com/netty/netty/issues/5182 - * HostsFileParser tries to resolve hostnames as case-sensitive - */ - @Test - public void testCaseInsensitivity() { - DefaultHostsFileEntriesResolver resolver = new DefaultHostsFileEntriesResolver(); - //normalized somehow - assertEquals(resolver.normalize("localhost"), resolver.normalize("LOCALHOST")); - } - - @Test - public void shouldntFindWhenAddressTypeDoesntMatch() { - Map> inet4Entries = new HashMap<>(); - Map> inet6Entries = new HashMap<>(); - - inet4Entries.put("localhost", Collections.singletonList(NetUtil.LOCALHOST4)); - - DefaultHostsFileEntriesResolver resolver = - new DefaultHostsFileEntriesResolver(new HostsFileEntriesProvider(inet4Entries, inet6Entries)); - - InetAddress address = resolver.address("localhost", ResolvedAddressTypes.IPV6_ONLY); - assertNull(address, "Should pick an IPv6 address"); - } - - @Test - public void shouldPickIpv4WhenBothAreDefinedButIpv4IsPreferred() { - Map> inet4Entries = new HashMap<>(); - Map> inet6Entries = new HashMap<>(); - - inet4Entries.put("localhost", Collections.singletonList(NetUtil.LOCALHOST4)); - inet6Entries.put("localhost", Collections.singletonList(NetUtil.LOCALHOST6)); - - DefaultHostsFileEntriesResolver resolver = - new DefaultHostsFileEntriesResolver(new HostsFileEntriesProvider(inet4Entries, inet6Entries)); - - InetAddress address = resolver.address("localhost", ResolvedAddressTypes.IPV4_PREFERRED); - assertThat("Should pick an IPv4 address", address, instanceOf(Inet4Address.class)); - } - - @Test - public void shouldPickIpv6WhenBothAreDefinedButIpv6IsPreferred() { - Map> inet4Entries = new HashMap<>(); - Map> inet6Entries = new HashMap<>(); - - inet4Entries.put("localhost", Collections.singletonList(NetUtil.LOCALHOST4)); - inet6Entries.put("localhost", Collections.singletonList(NetUtil.LOCALHOST6)); - - DefaultHostsFileEntriesResolver resolver = - new DefaultHostsFileEntriesResolver(new HostsFileEntriesProvider(inet4Entries, inet6Entries)); - - InetAddress address = resolver.address("localhost", ResolvedAddressTypes.IPV6_PREFERRED); - assertThat("Should pick an IPv6 address", address, instanceOf(Inet6Address.class)); - } - - @Test - public void shouldntFindWhenAddressesTypeDoesntMatch() { - Map> inet4Entries = new HashMap<>(); - Map> inet6Entries = new HashMap<>(); - - inet4Entries.put("localhost", Collections.singletonList(NetUtil.LOCALHOST4)); - - DefaultHostsFileEntriesResolver resolver = - new DefaultHostsFileEntriesResolver(new HostsFileEntriesProvider(inet4Entries, inet6Entries)); - - List addresses = resolver.addresses("localhost", ResolvedAddressTypes.IPV6_ONLY); - assertNull(addresses, "Should pick an IPv6 address"); - } - - @Test - public void shouldPickIpv4FirstWhenBothAreDefinedButIpv4IsPreferred() { - Map> inet4Entries = new HashMap<>(); - Map> inet6Entries = new HashMap<>(); - - inet4Entries.put("localhost", Collections.singletonList(NetUtil.LOCALHOST4)); - inet6Entries.put("localhost", Collections.singletonList(NetUtil.LOCALHOST6)); - - DefaultHostsFileEntriesResolver resolver = - new DefaultHostsFileEntriesResolver(new HostsFileEntriesProvider(inet4Entries, inet6Entries)); - - List addresses = resolver.addresses("localhost", ResolvedAddressTypes.IPV4_PREFERRED); - assertNotNull(addresses); - assertEquals(2, addresses.size()); - assertThat("Should pick an IPv4 address", addresses.get(0), instanceOf(Inet4Address.class)); - assertThat("Should pick an IPv6 address", addresses.get(1), instanceOf(Inet6Address.class)); - } - - @Test - public void shouldPickIpv6FirstWhenBothAreDefinedButIpv6IsPreferred() { - Map> inet4Entries = new HashMap<>(); - Map> inet6Entries = new HashMap<>(); - - inet4Entries.put("localhost", Collections.singletonList(NetUtil.LOCALHOST4)); - inet6Entries.put("localhost", Collections.singletonList(NetUtil.LOCALHOST6)); - - DefaultHostsFileEntriesResolver resolver = - new DefaultHostsFileEntriesResolver(new HostsFileEntriesProvider(inet4Entries, inet6Entries)); - - List addresses = resolver.addresses("localhost", ResolvedAddressTypes.IPV6_PREFERRED); - assertNotNull(addresses); - assertEquals(2, addresses.size()); - assertThat("Should pick an IPv6 address", addresses.get(0), instanceOf(Inet6Address.class)); - assertThat("Should pick an IPv4 address", addresses.get(1), instanceOf(Inet4Address.class)); - } -} diff --git a/resolver/src/test/java/io/netty/resolver/HostsFileEntriesProviderTest.java b/resolver/src/test/java/io/netty/resolver/HostsFileEntriesProviderTest.java deleted file mode 100644 index 2b5c5414f7..0000000000 --- a/resolver/src/test/java/io/netty/resolver/HostsFileEntriesProviderTest.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright 2021 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; - -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.Reader; -import java.io.StringReader; -import java.net.InetAddress; -import java.nio.charset.Charset; -import java.util.List; -import java.util.Map; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; - -class HostsFileEntriesProviderTest { - - @Test - void testParse() throws IOException { - String hostsString = new StringBuilder() - .append("127.0.0.1 host1").append("\n") // single hostname, separated with blanks - .append("::1 host1").append("\n") // same as above, but IPv6 - .append("\n") // empty line - .append("192.168.0.1\thost2").append("\n") // single hostname, separated with tabs - .append("#comment").append("\n") // comment at the beginning of the line - .append(" #comment ").append("\n") // comment in the middle of the line - .append("192.168.0.2 host3 #comment").append("\n") // comment after hostname - .append("192.168.0.3 host4 host5 host6").append("\n") // multiple aliases - .append("192.168.0.4 host4").append("\n") // host mapped to a second address, must be considered - .append("192.168.0.5 HOST7").append("\n") // uppercase host, should match lowercase host - .append("192.168.0.6 host7").append("\n") // must be considered - .toString(); - - HostsFileEntriesProvider entries = HostsFileEntriesProvider.parser() - .parse(new BufferedReader(new StringReader(hostsString))); - Map> inet4Entries = entries.ipv4Entries(); - Map> inet6Entries = entries.ipv6Entries(); - - assertEquals(7, inet4Entries.size(), "Expected 7 IPv4 entries"); - assertEquals(1, inet6Entries.size(), "Expected 1 IPv6 entries"); - - assertEquals(1, inet4Entries.get("host1").size()); - assertEquals("127.0.0.1", inet4Entries.get("host1").get(0).getHostAddress()); - - assertEquals(1, inet4Entries.get("host2").size()); - assertEquals("192.168.0.1", inet4Entries.get("host2").get(0).getHostAddress()); - - assertEquals(1, inet4Entries.get("host3").size()); - assertEquals("192.168.0.2", inet4Entries.get("host3").get(0).getHostAddress()); - - assertEquals(2, inet4Entries.get("host4").size()); - assertEquals("192.168.0.3", inet4Entries.get("host4").get(0).getHostAddress()); - assertEquals("192.168.0.4", inet4Entries.get("host4").get(1).getHostAddress()); - - assertEquals(1, inet4Entries.get("host5").size()); - assertEquals("192.168.0.3", inet4Entries.get("host5").get(0).getHostAddress()); - - assertEquals(1, inet4Entries.get("host6").size()); - assertEquals("192.168.0.3", inet4Entries.get("host6").get(0).getHostAddress()); - - assertNotNull(inet4Entries.get("host7"), "Uppercase host doesn't resolve"); - assertEquals(2, inet4Entries.get("host7").size()); - assertEquals("192.168.0.5", inet4Entries.get("host7").get(0).getHostAddress()); - assertEquals("192.168.0.6", inet4Entries.get("host7").get(1).getHostAddress()); - - assertEquals(1, inet6Entries.get("host1").size()); - assertEquals("0:0:0:0:0:0:0:1", inet6Entries.get("host1").get(0).getHostAddress()); - } - - @Test - void testCharsetInputValidation() { - assertThrows(NullPointerException.class, new Executable() { - @Override - public void execute() throws IOException { - HostsFileEntriesProvider.parser().parse((Charset[]) null); - } - }); - - assertThrows(NullPointerException.class, new Executable() { - @Override - public void execute() throws IOException { - HostsFileEntriesProvider.parser().parse(new File(""), (Charset[]) null); - } - }); - - assertThrows(NullPointerException.class, new Executable() { - @Override - public void execute() { - HostsFileEntriesProvider.parser().parseSilently((Charset[]) null); - } - }); - - assertThrows(NullPointerException.class, new Executable() { - @Override - public void execute() { - HostsFileEntriesProvider.parser().parseSilently(new File(""), (Charset[]) null); - } - }); - } - - @Test - void testFileInputValidation() { - assertThrows(NullPointerException.class, new Executable() { - @Override - public void execute() throws IOException { - HostsFileEntriesProvider.parser().parse((File) null); - } - }); - - assertThrows(NullPointerException.class, new Executable() { - @Override - public void execute() { - HostsFileEntriesProvider.parser().parseSilently((File) null); - } - }); - } - - @Test - void testReaderInputValidation() { - assertThrows(NullPointerException.class, new Executable() { - @Override - public void execute() throws IOException { - HostsFileEntriesProvider.parser().parse((Reader) null); - } - }); - } -} diff --git a/resolver/src/test/java/io/netty/resolver/HostsFileParserTest.java b/resolver/src/test/java/io/netty/resolver/HostsFileParserTest.java deleted file mode 100644 index 1e5cff8ae4..0000000000 --- a/resolver/src/test/java/io/netty/resolver/HostsFileParserTest.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver; - -import io.netty.util.CharsetUtil; -import io.netty.util.internal.ResourcesUtil; -import org.junit.jupiter.api.Test; - -import java.io.BufferedReader; -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.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -public class HostsFileParserTest { - - @Test - public void testParse() throws IOException { - String hostsString = new StringBuilder() - .append("127.0.0.1 host1").append("\n") // single hostname, separated with blanks - .append("::1 host1").append("\n") // same as above, but IPv6 - .append("\n") // empty line - .append("192.168.0.1\thost2").append("\n") // single hostname, separated with tabs - .append("#comment").append("\n") // comment at the beginning of the line - .append(" #comment ").append("\n") // comment in the middle of the line - .append("192.168.0.2 host3 #comment").append("\n") // comment after hostname - .append("192.168.0.3 host4 host5 host6").append("\n") // multiple aliases - .append("192.168.0.4 host4").append("\n") // host mapped to a second address, must be ignored - .append("192.168.0.5 HOST7").append("\n") // uppercase host, should match lowercase host - .append("192.168.0.6 host7").append("\n") // should be ignored since we have the uppercase host already - .toString(); - - HostsFileEntries entries = HostsFileParser.parse(new BufferedReader(new StringReader(hostsString))); - Map inet4Entries = entries.inet4Entries(); - Map inet6Entries = entries.inet6Entries(); - - assertEquals(7, inet4Entries.size(), "Expected 7 IPv4 entries"); - assertEquals(1, inet6Entries.size(), "Expected 1 IPv6 entries"); - assertEquals("127.0.0.1", inet4Entries.get("host1").getHostAddress()); - assertEquals("192.168.0.1", inet4Entries.get("host2").getHostAddress()); - assertEquals("192.168.0.2", inet4Entries.get("host3").getHostAddress()); - assertEquals("192.168.0.3", inet4Entries.get("host4").getHostAddress()); - assertEquals("192.168.0.3", inet4Entries.get("host5").getHostAddress()); - assertEquals("192.168.0.3", inet4Entries.get("host6").getHostAddress()); - assertNotNull(inet4Entries.get("host7"), "uppercase host doesn't resolve"); - 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) { - return; - } - testParseFile(HostsFileParser.parse( - ResourcesUtil.getFile(getClass(), "hosts-unicode"), unicodeCharset)); - } - - @Test - public void testParseMultipleCharsets() throws IOException { - final Charset unicodeCharset; - try { - unicodeCharset = Charset.forName("unicode"); - } catch (UnsupportedCharsetException e) { - return; - } - testParseFile(HostsFileParser.parse(ResourcesUtil.getFile(getClass(), "hosts-unicode"), - 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(2, inet4Entries.size(), "Expected 2 IPv4 entries"); - assertEquals(1, inet6Entries.size(), "Expected 1 IPv6 entries"); - 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/java/io/netty/resolver/InetSocketAddressResolverTest.java b/resolver/src/test/java/io/netty/resolver/InetSocketAddressResolverTest.java deleted file mode 100644 index 39825a254c..0000000000 --- a/resolver/src/test/java/io/netty/resolver/InetSocketAddressResolverTest.java +++ /dev/null @@ -1,38 +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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.resolver; - -import io.netty.util.concurrent.ImmediateEventExecutor; -import org.junit.jupiter.api.Test; - -import java.net.InetAddress; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -public class InetSocketAddressResolverTest { - - @Test - public void testCloseDelegates() { - @SuppressWarnings("unchecked") - NameResolver nameResolver = mock(NameResolver.class); - InetSocketAddressResolver resolver = new InetSocketAddressResolver( - ImmediateEventExecutor.INSTANCE, nameResolver); - resolver.close(); - verify(nameResolver, times(1)).close(); - } -} diff --git a/resolver/src/test/resources/io/netty/resolver/hosts-unicode b/resolver/src/test/resources/io/netty/resolver/hosts-unicode deleted file mode 100644 index 68750bfd94..0000000000 Binary files a/resolver/src/test/resources/io/netty/resolver/hosts-unicode and /dev/null differ diff --git a/run-example.sh b/run-example.sh index dd078f2f4a..d038e9d18b 100755 --- a/run-example.sh +++ b/run-example.sh @@ -15,49 +15,49 @@ # under the License. # ---------------------------------------------------------------------------- EXAMPLE_MAP=( - 'discard-client:io.netty.example.discard.DiscardClient' - 'discard-server:io.netty.example.discard.DiscardServer' - 'echo-client:io.netty.example.echo.EchoClient' - 'echo-server:io.netty.example.echo.EchoServer' - 'factorial-client:io.netty.example.factorial.FactorialClient' - 'factorial-server:io.netty.example.factorial.FactorialServer' - 'file-server:io.netty.example.file.FileServer' - 'http-cors-server:io.netty.example.http.cors.HttpCorsServer' - 'http-file-server:io.netty.example.http.file.HttpStaticFileServer' - 'http-helloworld-server:io.netty.example.http.helloworld.HttpHelloWorldServer' - 'http-snoop-client:io.netty.example.http.snoop.HttpSnoopClient' - 'http-snoop-server:io.netty.example.http.snoop.HttpSnoopServer' - 'http-upload-client:io.netty.example.http.upload.HttpUploadClient' - 'http-upload-server:io.netty.example.http.upload.HttpUploadServer' - 'websocket-client:io.netty.example.http.websocketx.client.WebSocketClient' - 'websocket-server:io.netty.example.http.websocketx.server.WebSocketServer' - 'http2-client:io.netty.example.http2.helloworld.client.Http2Client' - 'http2-server:io.netty.example.http2.helloworld.server.Http2Server' - 'http2-tiles:io.netty.example.http2.tiles.Launcher' - 'http2-multiplex-server:io.netty.example.http2.helloworld.multiplex.server.Http2Server' - 'worldclock-client:io.netty.example.worldclock.WorldClockClient' - 'worldclock-server:io.netty.example.worldclock.WorldClockServer' - 'objectecho-client:io.netty.example.objectecho.ObjectEchoClient' - 'objectecho-server:io.netty.example.objectecho.ObjectEchoServer' + 'discard-client:io.net5.example.discard.DiscardClient' + 'discard-server:io.net5.example.discard.DiscardServer' + 'echo-client:io.net5.example.echo.EchoClient' + 'echo-server:io.net5.example.echo.EchoServer' + 'factorial-client:io.net5.example.factorial.FactorialClient' + 'factorial-server:io.net5.example.factorial.FactorialServer' + 'file-server:io.net5.example.file.FileServer' + 'http-cors-server:io.net5.example.http.cors.HttpCorsServer' + 'http-file-server:io.net5.example.http.file.HttpStaticFileServer' + 'http-helloworld-server:io.net5.example.http.helloworld.HttpHelloWorldServer' + 'http-snoop-client:io.net5.example.http.snoop.HttpSnoopClient' + 'http-snoop-server:io.net5.example.http.snoop.HttpSnoopServer' + 'http-upload-client:io.net5.example.http.upload.HttpUploadClient' + 'http-upload-server:io.net5.example.http.upload.HttpUploadServer' + 'websocket-client:io.net5.example.http.websocketx.client.WebSocketClient' + 'websocket-server:io.net5.example.http.websocketx.server.WebSocketServer' + 'http2-client:io.net5.example.http2.helloworld.client.Http2Client' + 'http2-server:io.net5.example.http2.helloworld.server.Http2Server' + 'http2-tiles:io.net5.example.http2.tiles.Launcher' + 'http2-multiplex-server:io.net5.example.http2.helloworld.multiplex.server.Http2Server' + 'worldclock-client:io.net5.example.worldclock.WorldClockClient' + 'worldclock-server:io.net5.example.worldclock.WorldClockServer' + 'objectecho-client:io.net5.example.objectecho.ObjectEchoClient' + 'objectecho-server:io.net5.example.objectecho.ObjectEchoServer' 'quote-client:org.jboss.netty.example.qotm.QuoteOfTheMomentClient' 'quote-server:org.jboss.netty.example.qotm.QuoteOfTheMomentServer' - 'redis-client:io.netty.example.redis.RedisClient' - 'securechat-client:io.netty.example.securechat.SecureChatClient' - 'securechat-server:io.netty.example.securechat.SecureChatServer' - 'telnet-client:io.netty.example.telnet.TelnetClient' - 'telnet-server:io.netty.example.telnet.TelnetServer' - 'proxy-server:io.netty.example.proxy.HexDumpProxy' - 'socksproxy-server:io.netty.example.socksproxy.SocksServer' - 'memcache-binary-client:io.netty.example.memcache.binary.MemcacheClient' - 'stomp-client:io.netty.example.stomp.StompClient' - 'uptime-client:io.netty.example.uptime.UptimeClient' - 'uptime-server:io.netty.example.uptime.UptimeServer' - 'sctpecho-client:io.netty.example.sctp.SctpEchoClient' - 'sctpecho-server:io.netty.example.sctp.SctpEchoServer' - 'localecho:io.netty.example.localecho.LocalEcho' - 'udp-dns-client:io.netty.example.dns.udp.DnsClient' - 'tcp-dns-client:io.netty.example.dns.tcp.TcpDnsClient' - 'dot-dns-client:io.netty.example.dns.dot.DoTClient' + 'redis-client:io.net5.example.redis.RedisClient' + 'securechat-client:io.net5.example.securechat.SecureChatClient' + 'securechat-server:io.net5.example.securechat.SecureChatServer' + 'telnet-client:io.net5.example.telnet.TelnetClient' + 'telnet-server:io.net5.example.telnet.TelnetServer' + 'proxy-server:io.net5.example.proxy.HexDumpProxy' + 'socksproxy-server:io.net5.example.socksproxy.SocksServer' + 'memcache-binary-client:io.net5.example.memcache.binary.MemcacheClient' + 'stomp-client:io.net5.example.stomp.StompClient' + 'uptime-client:io.net5.example.uptime.UptimeClient' + 'uptime-server:io.net5.example.uptime.UptimeServer' + 'sctpecho-client:io.net5.example.sctp.SctpEchoClient' + 'sctpecho-server:io.net5.example.sctp.SctpEchoServer' + 'localecho:io.net5.example.localecho.LocalEcho' + 'udp-dns-client:io.net5.example.dns.udp.DnsClient' + 'tcp-dns-client:io.net5.example.dns.tcp.TcpDnsClient' + 'dot-dns-client:io.net5.example.dns.dot.DoTClient' ) EXAMPLE='' diff --git a/scripts/finish_release.sh b/scripts/finish_release.sh deleted file mode 100755 index 193904cae2..0000000000 --- a/scripts/finish_release.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash -# ---------------------------------------------------------------------------- -# Copyright 2021 The Netty Project -# -# The Netty Project licenses this file to you under the Apache License, -# version 2.0 (the "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at: -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- -set -e - -if [ "$#" -ne 2 ]; then - echo "Expected staging profile id and branch name, login into oss.sonatype.org to retrieve it" - exit 1 -fi - -OS=$(uname) - -if [ "$OS" != "Darwin" ]; then - echo "Needs to be executed on macOS" - exit 1 -fi - -BRANCH=$(git branch --show-current) - -if git tag | grep -q "$2" ; then - echo "Tag $2 already existed locally, deleting it" - git tag -d "$2" -fi - -git fetch -git checkout "$2" - -export JAVA_HOME="$JAVA8_HOME" - -./mvnw -Psonatype-oss-release -am -pl resolver-dns-native-macos,transport-native-unix-common,transport-native-kqueue clean package gpg:sign org.sonatype.plugins:nexus-staging-maven-plugin:deploy -DstagingRepositoryId="$1" -DnexusUrl=https://oss.sonatype.org -DserverId=sonatype-nexus-staging -DskipTests=true -./mvnw -Psonatype-oss-release,mac-m1-cross-compile -am -pl resolver-dns-native-macos,transport-native-unix-common,transport-native-kqueue clean package gpg:sign org.sonatype.plugins:nexus-staging-maven-plugin:deploy -DstagingRepositoryId="$1" -DnexusUrl=https://oss.sonatype.org -DserverId=sonatype-nexus-staging -DskipTests=true - -./mvnw -Psonatype-oss-release,full,uber-staging -pl all clean package gpg:sign org.sonatype.plugins:nexus-staging-maven-plugin:deploy -DstagingRepositoryId="$1" -DnexusUrl=https://oss.sonatype.org -DserverId=sonatype-nexus-staging -DskipTests=true - -./mvnw org.sonatype.plugins:nexus-staging-maven-plugin:rc-close org.sonatype.plugins:nexus-staging-maven-plugin:rc-release -DstagingRepositoryId="$1" -DnexusUrl=https://oss.sonatype.org -DserverId=sonatype-nexus-staging -DskipTests=true -DstagingProgressTimeoutMinutes=10 - -git checkout "$BRANCH" diff --git a/scripts/generate_docs.sh b/scripts/generate_docs.sh deleted file mode 100755 index af24277e0d..0000000000 --- a/scripts/generate_docs.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/bin/bash -# ---------------------------------------------------------------------------- -# Copyright 2021 The Netty Project -# -# The Netty Project licenses this file to you under the Apache License, -# version 2.0 (the "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at: -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- -set -e -# Adjust for different branch if needed -VERSION=5.0 - -if [ "$#" -ne 2 ]; then - echo "Expected netty-website directory and tag" - exit 1 -fi - -if [ ! -d "$1" ]; then - echo "$1 is not a directory" - exit 1 -fi - - -BRANCH=$(git branch --show-current) -TAG="$2" -WEBSITE_API_DIR="$1"/"$VERSION"/api/ -WEBSITE_XREF_DIR="$1"/"$VERSION"/xref/ -API_DIR=target/site/apidocs/ -XREF_DIR=target/site/xref/ - -git checkout "$TAG" -JAVA_HOME=$JAVA8_HOME ./mvnw -Paggregate clean package javadoc:aggregate jxr:aggregate -DskipTests=true - -echo "Delete old javadocs and xref files" -rm -rf "$WEBSITE_API_DIR"/* -rm -rf "$WEBSITE_XREF_DIR"/* - -echo "Copy javadocs and xref files" -cp -r "$API_DIR"/* "$WEBSITE_API_DIR" -cp -r "$XREF_DIR"/* "$WEBSITE_XREF_DIR" diff --git a/scripts/list_staged_release.sh b/scripts/list_staged_release.sh deleted file mode 100755 index 2ae3b5adc8..0000000000 --- a/scripts/list_staged_release.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash -# ---------------------------------------------------------------------------- -# Copyright 2021 The Netty Project -# -# The Netty Project licenses this file to you under the Apache License, -# version 2.0 (the "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at: -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- -set -e - -RC_LIST=$(mvn org.sonatype.plugins:nexus-staging-maven-plugin:rc-list -DserverId=sonatype-nexus-staging -DnexusUrl=https://oss.sonatype.org | grep -A 2 "\[INFO\] ID State Description") -STAGED=$(echo "$RC_LIST" | grep 'OPEN' | cut -f 2 -d ' ') -echo "$STAGED" diff --git a/testsuite-autobahn/pom.xml b/testsuite-autobahn/pom.xml deleted file mode 100644 index 46c3cfaaf8..0000000000 --- a/testsuite-autobahn/pom.xml +++ /dev/null @@ -1,107 +0,0 @@ - - - - - 4.0.0 - - io.netty - netty-parent - 5.0.0.Final-SNAPSHOT - - - netty-testsuite-autobahn - jar - - Netty/Testsuite/Autobahn - - - true - - true - - - - - ${project.groupId} - netty-common - ${project.version} - - - ${project.groupId} - netty-buffer - ${project.version} - - - ${project.groupId} - netty-transport - ${project.version} - - - ${project.groupId} - netty-codec-http - ${project.version} - - - - - - skipTests - - - skipTests - - - - true - - - - - - - - me.normanmaurer.maven.autobahntestsuite - autobahntestsuite-maven-plugin - 0.1.5 - - io.netty.testsuite.autobahn.AutobahnServer - - * - - - false - ${skipAutobahnTestsuite} - - - - test - - fuzzingclient - - - - - - org.python - jython-standalone - 2.7.1 - - - - - - diff --git a/testsuite-autobahn/src/main/java/io/netty/testsuite/autobahn/AutobahnServer.java b/testsuite-autobahn/src/main/java/io/netty/testsuite/autobahn/AutobahnServer.java deleted file mode 100644 index 17aa8bfcd0..0000000000 --- a/testsuite-autobahn/src/main/java/io/netty/testsuite/autobahn/AutobahnServer.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.testsuite.autobahn; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.buffer.PooledByteBufAllocator; -import io.netty.channel.Channel; -import io.netty.channel.ChannelOption; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioServerSocketChannel; - -/** - * A Web Socket echo server for running the - * autobahn test suite - */ -public class AutobahnServer { - - private final int port; - - public AutobahnServer(int port) { - this.port = port; - } - - public void run() throws Exception { - EventLoopGroup bossGroup = new MultithreadEventLoopGroup(1, NioHandler.newFactory()); - EventLoopGroup workerGroup = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - ServerBootstrap b = new ServerBootstrap(); - b.group(bossGroup, workerGroup) - .channel(NioServerSocketChannel.class) - .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT) - .childHandler(new AutobahnServerInitializer()); - - Channel channel = b.bind(port).get(); - System.out.println("Web Socket Server started at port " + port); - channel.closeFuture().sync(); - } finally { - bossGroup.shutdownGracefully(); - workerGroup.shutdownGracefully(); - } - } - - public static void main(String[] args) throws Exception { - int port; - if (args.length > 0) { - port = Integer.parseInt(args[0]); - } else { - port = 9000; - } - new AutobahnServer(port).run(); - } -} 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 deleted file mode 100644 index b05761239d..0000000000 --- a/testsuite-autobahn/src/main/java/io/netty/testsuite/autobahn/AutobahnServerHandler.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.testsuite.autobahn; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelFutureListeners; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http.DefaultFullHttpResponse; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; -import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame; -import io.netty.handler.codec.http.websocketx.ContinuationWebSocketFrame; -import io.netty.handler.codec.http.websocketx.PingWebSocketFrame; -import io.netty.handler.codec.http.websocketx.PongWebSocketFrame; -import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; -import io.netty.handler.codec.http.websocketx.WebSocketFrame; -import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker; -import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory; -import io.netty.util.CharsetUtil; -import io.netty.util.concurrent.Future; -import io.netty.util.internal.StringUtil; - -import java.util.logging.Level; -import java.util.logging.Logger; - -import static io.netty.handler.codec.http.HttpMethod.GET; -import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST; -import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN; -import static io.netty.handler.codec.http.HttpUtil.isKeepAlive; -import static io.netty.handler.codec.http.HttpUtil.setContentLength; -import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; - -/** - * Handles handshakes and messages - */ -public class AutobahnServerHandler implements ChannelHandler { - private static final Logger logger = Logger.getLogger(AutobahnServerHandler.class.getName()); - - private WebSocketServerHandshaker handshaker; - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - if (msg instanceof HttpRequest) { - handleHttpRequest(ctx, (HttpRequest) msg); - } else if (msg instanceof WebSocketFrame) { - handleWebSocketFrame(ctx, (WebSocketFrame) msg); - } else { - throw new IllegalStateException("unknown message: " + msg); - } - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { - ctx.flush(); - } - - private void handleHttpRequest(ChannelHandlerContext ctx, HttpRequest req) - throws Exception { - // Handle a bad request. - if (!req.decoderResult().isSuccess()) { - sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, BAD_REQUEST, ctx.alloc().buffer(0))); - return; - } - - // Allow only GET methods. - if (!GET.equals(req.method())) { - sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN, ctx.alloc().buffer(0))); - return; - } - - // Handshake - WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory( - getWebSocketLocation(req), null, false, Integer.MAX_VALUE); - handshaker = wsFactory.newHandshaker(req); - if (handshaker == null) { - WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel()); - } else { - handshaker.handshake(ctx.channel(), req); - } - } - - private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) { - if (logger.isLoggable(Level.FINE)) { - logger.fine(String.format( - "Channel %s received %s", ctx.channel().hashCode(), StringUtil.simpleClassName(frame))); - } - - if (frame instanceof CloseWebSocketFrame) { - handshaker.close(ctx, (CloseWebSocketFrame) frame); - } else if (frame instanceof PingWebSocketFrame) { - ctx.write(new PongWebSocketFrame(frame.isFinalFragment(), frame.rsv(), frame.content())); - } else if (frame instanceof TextWebSocketFrame || - frame instanceof BinaryWebSocketFrame || - frame instanceof ContinuationWebSocketFrame) { - ctx.write(frame); - } else if (frame instanceof PongWebSocketFrame) { - frame.release(); - // Ignore - } else { - throw new UnsupportedOperationException(String.format("%s frame types not supported", frame.getClass() - .getName())); - } - } - - private static void sendHttpResponse( - ChannelHandlerContext ctx, HttpRequest req, FullHttpResponse res) { - // Generate an error page if response status code is not OK (200). - if (res.status().code() != 200) { - ByteBuf buf = Unpooled.copiedBuffer(res.status().toString(), CharsetUtil.UTF_8); - res.content().writeBytes(buf); - buf.release(); - setContentLength(res, res.content().readableBytes()); - } - - // Send the response and close the connection if necessary. - Future f = ctx.writeAndFlush(res); - if (!isKeepAlive(req) || res.status().code() != 200) { - f.addListener(ctx, ChannelFutureListeners.CLOSE); - } - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - ctx.close(); - } - - private static String getWebSocketLocation(HttpRequest req) { - return "ws://" + req.headers().get(HttpHeaderNames.HOST); - } -} diff --git a/testsuite-autobahn/src/main/java/io/netty/testsuite/autobahn/AutobahnServerInitializer.java b/testsuite-autobahn/src/main/java/io/netty/testsuite/autobahn/AutobahnServerInitializer.java deleted file mode 100644 index 85b62b02c2..0000000000 --- a/testsuite-autobahn/src/main/java/io/netty/testsuite/autobahn/AutobahnServerInitializer.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.testsuite.autobahn; - -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.socket.SocketChannel; -import io.netty.handler.codec.http.HttpRequestDecoder; -import io.netty.handler.codec.http.HttpResponseEncoder; - -public class AutobahnServerInitializer extends ChannelInitializer { - @Override - public void initChannel(SocketChannel ch) throws Exception { - ChannelPipeline pipeline = ch.pipeline(); - pipeline.addLast("encoder", new HttpResponseEncoder()); - pipeline.addLast("decoder", new HttpRequestDecoder()); - pipeline.addLast("handler", new AutobahnServerHandler()); - } -} diff --git a/testsuite-autobahn/src/main/java/io/netty/testsuite/autobahn/package-info.java b/testsuite-autobahn/src/main/java/io/netty/testsuite/autobahn/package-info.java deleted file mode 100644 index 355b2c3a8f..0000000000 --- a/testsuite-autobahn/src/main/java/io/netty/testsuite/autobahn/package-info.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * This package is intended for use with testing against the Python - * AutoBahn test suite. - * - * Autobahn installation documentation can be found here. - * - *

    How to run the tests on Ubuntu.

    - * - *

    01. Install python (if not already installed). - * - *

    02. Install Python Setup Tools if not already - * installed. sudo apt-get install python-setuptools - * - *

    03. Add ppa:twisted-dev/ppa to your system's Software Sources - * - *

    04. Install Twisted: sudo apt-get install python-twisted - * - *

    05. Install AutoBahn: sudo easy_install autobahntestsuite. Test using wstest --help. - * - *

    06. Create a directory for test configuration and results: mkdir autobahn cd autobahn. - * - *

    07. Create fuzzing_clinet_spec.json in the above directory - * {@code - * { - * "options": {"failByDrop": false}, - * "outdir": "./reports/servers", - * - * "servers": [ - * {"agent": "Netty4", - * "url": "ws://localhost:9000", - * "options": {"version": 18}} - * ], - * - * "cases": ["*"], - * "exclude-cases": [], - * "exclude-agent-cases": {} - * } - * } - * - *

    08. Run the AutobahnServer located in this package. If you are in Eclipse IDE, right click on - * AutobahnServer.java and select Run As > Java Application. - * - *

    09. Run the Autobahn test wstest -m fuzzingclient -s fuzzingclient.json. - * - *

    10. See the results in ./reports/servers/index.html - */ -package io.netty.testsuite.autobahn; - diff --git a/testsuite-http2/pom.xml b/testsuite-http2/pom.xml deleted file mode 100644 index 4122a38586..0000000000 --- a/testsuite-http2/pom.xml +++ /dev/null @@ -1,124 +0,0 @@ - - - - - 4.0.0 - - io.netty - netty-parent - 5.0.0.Final-SNAPSHOT - - - netty-testsuite-http2 - jar - - Netty/Testsuite/Http2 - - - true - - true - - - - - ${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 - ${project.version} - - - ${project.groupId} - netty-codec-http2 - ${project.version} - - - - - - - skipTests - - - skipTests - - - - true - - - - - - - - com.github.madgnome - h2spec-maven-plugin - 0.6 - - io.netty.testsuite.http2.Http2Server - - 4.2 - Sends a dynamic table size update at the end of header block - 5.1 - idle: 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 - 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 - 8.1.2.3 - Sends a HEADERS frame that omits ":method" pseudo-header field - 8.1.2.3 - Sends a HEADERS frame that omits ":scheme" pseudo-header field - 8.1.2.3 - Sends a HEADERS frame that omits ":path" pseudo-header field - 8.1.2.3 - Sends a HEADERS frame with duplicated ":method" pseudo-header field - 8.1.2.3 - Sends a HEADERS frame with duplicated ":method" pseudo-header field - 8.1.2.3 - Sends a HEADERS frame with duplicated ":scheme" pseudo-header field - 8.1.2.6 - Sends a HEADERS frame with the "content-length" header field which does not equal the DATA frame payload length - 8.1.2.6 - Sends a HEADERS frame with the "content-length" header field which does not equal the sum of the multiple DATA frames payload length - - ${skipHttp2Testsuite} - - - - test - - h2spec - - - - - - - diff --git a/testsuite-http2/src/main/java/io/netty/testsuite/http2/HelloWorldHttp1Handler.java b/testsuite-http2/src/main/java/io/netty/testsuite/http2/HelloWorldHttp1Handler.java deleted file mode 100644 index cdb0318531..0000000000 --- a/testsuite-http2/src/main/java/io/netty/testsuite/http2/HelloWorldHttp1Handler.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.testsuite.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.channel.ChannelFutureListeners; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.handler.codec.http.DefaultFullHttpResponse; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpUtil; - -import static io.netty.handler.codec.http.HttpHeaderNames.CONNECTION; -import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH; -import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE; -import static io.netty.handler.codec.http.HttpResponseStatus.CONTINUE; -import static io.netty.handler.codec.http.HttpResponseStatus.OK; -import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; -import static java.util.Objects.requireNonNull; - -/** - * HTTP handler that responds with a "Hello World" - */ -public class HelloWorldHttp1Handler extends SimpleChannelInboundHandler { - private final String establishApproach; - - HelloWorldHttp1Handler(String establishApproach) { - this.establishApproach = requireNonNull(establishApproach, "establishApproach"); - } - - @Override - public void messageReceived(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception { - if (HttpUtil.is100ContinueExpected(req)) { - ctx.write(new DefaultFullHttpResponse(HTTP_1_1, CONTINUE, ctx.alloc().buffer(0))); - } - boolean keepAlive = HttpUtil.isKeepAlive(req); - - ByteBuf content = ctx.alloc().buffer(); - content.writeBytes(HelloWorldHttp2Handler.RESPONSE_BYTES.duplicate()); - ByteBufUtil.writeAscii(content, " - via " + req.protocolVersion() + " (" + establishApproach + ')'); - - FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, content); - response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8"); - response.headers().setInt(CONTENT_LENGTH, response.content().readableBytes()); - - if (!keepAlive) { - ctx.write(response).addListener(ctx, ChannelFutureListeners.CLOSE); - } else { - response.headers().set(CONNECTION, HttpHeaderValues.KEEP_ALIVE); - ctx.write(response); - } - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { - ctx.flush(); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - cause.printStackTrace(); - ctx.close(); - } -} diff --git a/testsuite-http2/src/main/java/io/netty/testsuite/http2/HelloWorldHttp2Handler.java b/testsuite-http2/src/main/java/io/netty/testsuite/http2/HelloWorldHttp2Handler.java deleted file mode 100644 index e056142b2c..0000000000 --- a/testsuite-http2/src/main/java/io/netty/testsuite/http2/HelloWorldHttp2Handler.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package io.netty.testsuite.http2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpScheme; -import io.netty.handler.codec.http.HttpServerUpgradeHandler; -import io.netty.handler.codec.http2.DefaultHttp2Headers; -import io.netty.handler.codec.http2.Http2ConnectionDecoder; -import io.netty.handler.codec.http2.Http2ConnectionEncoder; -import io.netty.handler.codec.http2.Http2ConnectionHandler; -import io.netty.handler.codec.http2.Http2Flags; -import io.netty.handler.codec.http2.Http2FrameListener; -import io.netty.handler.codec.http2.Http2Headers; -import io.netty.handler.codec.http2.Http2Settings; -import io.netty.util.CharsetUtil; - -import static io.netty.buffer.Unpooled.copiedBuffer; -import static io.netty.buffer.Unpooled.unreleasableBuffer; -import static io.netty.handler.codec.http.HttpResponseStatus.OK; - -/** - * A simple handler that responds with the message "Hello World!". - */ -public final class HelloWorldHttp2Handler extends Http2ConnectionHandler implements Http2FrameListener { - - static final ByteBuf RESPONSE_BYTES = unreleasableBuffer(copiedBuffer("Hello World", CharsetUtil.UTF_8)); - - HelloWorldHttp2Handler(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder, - Http2Settings initialSettings) { - super(decoder, encoder, initialSettings); - } - - private static Http2Headers http1HeadersToHttp2Headers(FullHttpRequest request) { - CharSequence host = request.headers().get(HttpHeaderNames.HOST); - Http2Headers http2Headers = new DefaultHttp2Headers() - .method(HttpMethod.GET.asciiName()) - .path(request.uri()) - .scheme(HttpScheme.HTTP.name()); - if (host != null) { - http2Headers.authority(host); - } - return http2Headers; - } - - /** - * Handles the cleartext HTTP upgrade event. If an upgrade occurred, sends a simple response via HTTP/2 - * on stream 1 (the stream specifically reserved for cleartext HTTP upgrade). - */ - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (evt instanceof HttpServerUpgradeHandler.UpgradeEvent) { - HttpServerUpgradeHandler.UpgradeEvent upgradeEvent = - (HttpServerUpgradeHandler.UpgradeEvent) evt; - onHeadersRead(ctx, 1, http1HeadersToHttp2Headers(upgradeEvent.upgradeRequest()), 0 , true); - } - super.userEventTriggered(ctx, evt); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - super.exceptionCaught(ctx, cause); - cause.printStackTrace(); - ctx.close(); - } - - /** - * Sends a "Hello World" DATA frame to the client. - */ - private void sendResponse(ChannelHandlerContext ctx, int streamId, ByteBuf payload) { - // Send a frame for the response status - Http2Headers headers = new DefaultHttp2Headers().status(OK.codeAsText()); - encoder().writeHeaders(ctx, streamId, headers, 0, false); - encoder().writeData(ctx, streamId, payload, 0, true); - - // no need to call flush as channelReadComplete(...) will take care of it. - } - - @Override - public int onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding, boolean endOfStream) { - int processed = data.readableBytes() + padding; - if (endOfStream) { - sendResponse(ctx, streamId, data.retain()); - } - return processed; - } - - @Override - public void onHeadersRead(ChannelHandlerContext ctx, int streamId, - Http2Headers headers, int padding, boolean endOfStream) { - if (endOfStream) { - ByteBuf content = ctx.alloc().buffer(); - content.writeBytes(RESPONSE_BYTES.duplicate()); - ByteBufUtil.writeAscii(content, " - via HTTP/2"); - sendResponse(ctx, streamId, content); - } - } - - @Override - public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int streamDependency, - short weight, boolean exclusive, int padding, boolean endOfStream) { - onHeadersRead(ctx, streamId, headers, padding, endOfStream); - } - - @Override - public void onPriorityRead(ChannelHandlerContext ctx, int streamId, int streamDependency, - short weight, boolean exclusive) { - } - - @Override - public void onRstStreamRead(ChannelHandlerContext ctx, int streamId, long errorCode) { - } - - @Override - public void onSettingsAckRead(ChannelHandlerContext ctx) { - } - - @Override - public void onSettingsRead(ChannelHandlerContext ctx, Http2Settings settings) { - } - - @Override - public void onPingRead(ChannelHandlerContext ctx, long data) { - } - - @Override - public void onPingAckRead(ChannelHandlerContext ctx, long data) { - } - - @Override - public void onPushPromiseRead(ChannelHandlerContext ctx, int streamId, int promisedStreamId, - Http2Headers headers, int padding) { - } - - @Override - public void onGoAwayRead(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData) { - } - - @Override - public void onWindowUpdateRead(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement) { - } - - @Override - public void onUnknownFrame(ChannelHandlerContext ctx, byte frameType, int streamId, - Http2Flags flags, ByteBuf payload) { - } -} diff --git a/testsuite-http2/src/main/java/io/netty/testsuite/http2/HelloWorldHttp2HandlerBuilder.java b/testsuite-http2/src/main/java/io/netty/testsuite/http2/HelloWorldHttp2HandlerBuilder.java deleted file mode 100644 index c67d308c83..0000000000 --- a/testsuite-http2/src/main/java/io/netty/testsuite/http2/HelloWorldHttp2HandlerBuilder.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.testsuite.http2; - -import io.netty.handler.codec.http2.AbstractHttp2ConnectionHandlerBuilder; -import io.netty.handler.codec.http2.Http2ConnectionDecoder; -import io.netty.handler.codec.http2.Http2ConnectionEncoder; -import io.netty.handler.codec.http2.Http2FrameLogger; -import io.netty.handler.codec.http2.Http2Settings; - -import static io.netty.handler.logging.LogLevel.INFO; - -public final class HelloWorldHttp2HandlerBuilder - extends AbstractHttp2ConnectionHandlerBuilder { - - private static final Http2FrameLogger logger = new Http2FrameLogger(INFO, HelloWorldHttp2Handler.class); - - HelloWorldHttp2HandlerBuilder() { - frameLogger(logger); - } - - @Override - public HelloWorldHttp2Handler build() { - return super.build(); - } - - @Override - protected HelloWorldHttp2Handler build(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder, - Http2Settings initialSettings) { - HelloWorldHttp2Handler handler = new HelloWorldHttp2Handler(decoder, encoder, initialSettings); - frameListener(handler); - return handler; - } -} diff --git a/testsuite-http2/src/main/java/io/netty/testsuite/http2/Http2Server.java b/testsuite-http2/src/main/java/io/netty/testsuite/http2/Http2Server.java deleted file mode 100644 index fe72ece45c..0000000000 --- a/testsuite-http2/src/main/java/io/netty/testsuite/http2/Http2Server.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.testsuite.http2; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelOption; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; - -/** - * An HTTP/2 Server that responds to requests with a Hello World. Once started, you can test the - * server with the example client. - */ -public final class Http2Server { - - private final int port; - - Http2Server(final int port) { - this.port = port; - } - - void run() throws Exception { - // Configure the server. - EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory()); - try { - ServerBootstrap b = new ServerBootstrap(); - b.option(ChannelOption.SO_BACKLOG, 1024); - b.group(group) - .channel(NioServerSocketChannel.class) - .handler(new LoggingHandler(LogLevel.INFO)) - .childHandler(new Http2ServerInitializer()); - - Channel ch = b.bind(port).get(); - - ch.closeFuture().sync(); - } finally { - group.shutdownGracefully(); - } - } - - public static void main(String[] args) throws Exception { - int port; - if (args.length > 0) { - port = Integer.parseInt(args[0]); - } else { - port = 9000; - } - new Http2Server(port).run(); - } -} 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 deleted file mode 100644 index 5ffc372c8c..0000000000 --- a/testsuite-http2/src/main/java/io/netty/testsuite/http2/Http2ServerInitializer.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.testsuite.http2; - -import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; - -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.channel.socket.SocketChannel; -import io.netty.handler.codec.http.HttpMessage; -import io.netty.handler.codec.http.HttpObjectAggregator; -import io.netty.handler.codec.http.HttpServerCodec; -import io.netty.handler.codec.http.HttpServerUpgradeHandler; -import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeCodecFactory; -import io.netty.handler.codec.http2.CleartextHttp2ServerUpgradeHandler; -import io.netty.handler.codec.http2.Http2CodecUtil; -import io.netty.handler.codec.http2.Http2ServerUpgradeCodec; -import io.netty.util.AsciiString; -import io.netty.util.ReferenceCountUtil; - -/** - * Sets up the Netty pipeline for the example server. Depending on the endpoint config, sets up the - * pipeline for NPN or cleartext HTTP upgrade to HTTP/2. - */ -public class Http2ServerInitializer extends ChannelInitializer { - - private static final UpgradeCodecFactory upgradeCodecFactory = protocol -> { - if (AsciiString.contentEquals(Http2CodecUtil.HTTP_UPGRADE_PROTOCOL_NAME, protocol)) { - return new Http2ServerUpgradeCodec(new HelloWorldHttp2HandlerBuilder().build()); - } else { - return null; - } - }; - - private final int maxHttpContentLength; - - Http2ServerInitializer() { - this(16 * 1024); - } - - Http2ServerInitializer(int maxHttpContentLength) { - checkPositiveOrZero(maxHttpContentLength, "maxHttpContentLength"); - this.maxHttpContentLength = maxHttpContentLength; - } - - @Override - public void initChannel(SocketChannel ch) { - configureClearText(ch); - } - - /** - * Configure the pipeline for a cleartext upgrade from HTTP to HTTP/2.0 - */ - private void configureClearText(SocketChannel ch) { - final ChannelPipeline p = ch.pipeline(); - final HttpServerCodec sourceCodec = new HttpServerCodec(); - final HttpServerUpgradeHandler upgradeHandler = new HttpServerUpgradeHandler(sourceCodec, upgradeCodecFactory); - final CleartextHttp2ServerUpgradeHandler cleartextHttp2ServerUpgradeHandler = - new CleartextHttp2ServerUpgradeHandler(sourceCodec, upgradeHandler, - new HelloWorldHttp2HandlerBuilder().build()); - - p.addLast(cleartextHttp2ServerUpgradeHandler); - p.addLast(new SimpleChannelInboundHandler() { - @Override - protected void messageReceived(ChannelHandlerContext ctx, HttpMessage msg) throws Exception { - // If this handler is hit then no upgrade has been attempted and the client is just talking HTTP. - System.err.println("Directly talking: " + msg.protocolVersion() + " (no upgrade was attempted)"); - ChannelPipeline pipeline = ctx.pipeline(); - ChannelHandlerContext thisCtx = pipeline.context(this); - pipeline.addAfter(thisCtx.name(), null, new HelloWorldHttp1Handler("Direct. No Upgrade Attempted.")); - pipeline.addAfter(thisCtx.name(), null, new HttpObjectAggregator(maxHttpContentLength)); - ctx.fireChannelRead(ReferenceCountUtil.retain(msg)); - pipeline.remove(this); - } - }); - - p.addLast(new UserEventLogger()); - } - - /** - * Class that logs any User Events triggered on this channel. - */ - private static class UserEventLogger implements ChannelHandler { - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { - System.out.println("User Event Triggered: " + evt); - ctx.fireUserEventTriggered(evt); - } - } -} diff --git a/testsuite-http2/src/main/java/io/netty/testsuite/http2/package-info.java b/testsuite-http2/src/main/java/io/netty/testsuite/http2/package-info.java deleted file mode 100644 index d9726f7d7b..0000000000 --- a/testsuite-http2/src/main/java/io/netty/testsuite/http2/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * This package is intended to test the http2 implementation against the specification - * using h2spec - */ -package io.netty.testsuite.http2; diff --git a/testsuite-native-image-client-runtime-init/pom.xml b/testsuite-native-image-client-runtime-init/pom.xml deleted file mode 100644 index 4ef8fec00d..0000000000 --- a/testsuite-native-image-client-runtime-init/pom.xml +++ /dev/null @@ -1,102 +0,0 @@ - - - - - 4.0.0 - - io.netty - netty-parent - 5.0.0.Final-SNAPSHOT - - - netty-testsuite-native-image-client-runtime-init - jar - - Netty/Testsuite/NativeImage/ClientRuntimeInit - - - true - - true - - - - - ${project.groupId} - netty-common - ${project.version} - - - - - - skipTests - - - skipTests - - - - true - - - - - - - - org.graalvm.nativeimage - native-image-maven-plugin - ${graalvm.version} - - - - native-image - - package - - - - ${skipNativeImageTestsuite} - ${project.artifactId} - io.netty.testsuite.svm.client.NativeClientWithNettyInitAtRuntime - --report-unsupported-elements-at-runtime --allow-incomplete-classpath --no-fallback --initialize-at-run-time=io.netty.util.NetUtil - - - - org.codehaus.mojo - exec-maven-plugin - 1.6.0 - - - - - verify-native-image - verify - - exec - - - - - ${skipNativeImageTestsuite} - ${project.build.directory}/${project.artifactId} - - - - - diff --git a/testsuite-native-image-client-runtime-init/src/main/java/io/netty/testsuite/svm/client/NativeClientWithNettyInitAtRuntime.java b/testsuite-native-image-client-runtime-init/src/main/java/io/netty/testsuite/svm/client/NativeClientWithNettyInitAtRuntime.java deleted file mode 100644 index 6c4aa22ba8..0000000000 --- a/testsuite-native-image-client-runtime-init/src/main/java/io/netty/testsuite/svm/client/NativeClientWithNettyInitAtRuntime.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.testsuite.svm.client; - -import io.netty.util.NetUtil; - -/** - * A client that triggers runtime initialization of NetUtil when - * built to a native image. - */ -public final class NativeClientWithNettyInitAtRuntime { - /** - * Main entry point (not instantiable) - */ - private NativeClientWithNettyInitAtRuntime() { - } - - public static void main(String[] args) { - System.out.println(NetUtil.LOCALHOST4); - System.out.println(NetUtil.LOCALHOST6); - System.out.println(NetUtil.LOCALHOST); - } -} diff --git a/testsuite-native-image-client-runtime-init/src/main/java/io/netty/testsuite/svm/client/package-info.java b/testsuite-native-image-client-runtime-init/src/main/java/io/netty/testsuite/svm/client/package-info.java deleted file mode 100644 index c5d8a74b31..0000000000 --- a/testsuite-native-image-client-runtime-init/src/main/java/io/netty/testsuite/svm/client/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * A client that triggers runtime initialization of NetUtil when - * built to a native image. - */ -package io.netty.testsuite.svm.client; diff --git a/testsuite-native-image-client/pom.xml b/testsuite-native-image-client/pom.xml deleted file mode 100644 index 4ba12938ea..0000000000 --- a/testsuite-native-image-client/pom.xml +++ /dev/null @@ -1,108 +0,0 @@ - - - - - 4.0.0 - - io.netty - netty-parent - 5.0.0.Final-SNAPSHOT - - - netty-testsuite-native-image-client - jar - - Netty/Testsuite/NativeImage/Client - - - true - - true - - - - - ${project.groupId} - netty-transport - ${project.version} - - - ${project.groupId} - netty-resolver-dns - ${project.version} - - - - - - - skipTests - - - skipTests - - - - true - - - - - - - - org.graalvm.nativeimage - native-image-maven-plugin - ${graalvm.version} - - - - native-image - - package - - - - ${skipNativeImageTestsuite} - ${project.artifactId} - io.netty.testsuite.svm.client.DnsNativeClient - --report-unsupported-elements-at-runtime --allow-incomplete-classpath --no-fallback --initialize-at-build-time=io.netty -H:ReflectionConfigurationResources=reflection-config.json - - - - org.codehaus.mojo - exec-maven-plugin - 1.6.0 - - - - - verify-native-image - verify - - exec - - - - - ${skipNativeImageTestsuite} - ${project.build.directory}/${project.artifactId} - - - - - diff --git a/testsuite-native-image-client/src/main/java/io/netty/testsuite/svm/client/DnsNativeClient.java b/testsuite-native-image-client/src/main/java/io/netty/testsuite/svm/client/DnsNativeClient.java deleted file mode 100644 index 23d8d2b159..0000000000 --- a/testsuite-native-image-client/src/main/java/io/netty/testsuite/svm/client/DnsNativeClient.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.testsuite.svm.client; - -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioDatagramChannel; -import io.netty.resolver.AddressResolver; -import io.netty.resolver.dns.DnsAddressResolverGroup; -import io.netty.resolver.dns.DnsServerAddressStreamProviders; -import io.netty.util.concurrent.DefaultThreadFactory; - -import java.net.InetSocketAddress; - -/** - * A client that uses netty-dns and gets compiled to a native image. - */ -public final class DnsNativeClient { - /** - * Main entry point (not instantiable) - */ - private DnsNativeClient() { - } - - public static void main(String[] args) throws Exception { - MultithreadEventLoopGroup group = new MultithreadEventLoopGroup( - 1, new DefaultThreadFactory("netty"), NioHandler.newFactory()); - - DnsAddressResolverGroup resolverGroup = new DnsAddressResolverGroup(NioDatagramChannel.class, - DnsServerAddressStreamProviders.platformDefault()); - AddressResolver resolver = resolverGroup.getResolver(group.next()); - System.out.println(resolver); - - resolver.close(); - group.shutdownGracefully().get(); - } -} diff --git a/testsuite-native-image-client/src/main/java/io/netty/testsuite/svm/client/package-info.java b/testsuite-native-image-client/src/main/java/io/netty/testsuite/svm/client/package-info.java deleted file mode 100644 index 7b575e6835..0000000000 --- a/testsuite-native-image-client/src/main/java/io/netty/testsuite/svm/client/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * A client that uses netty-dns and gets compiled to a native image. - */ -package io.netty.testsuite.svm.client; diff --git a/testsuite-native-image-client/src/main/resources/reflection-config.json b/testsuite-native-image-client/src/main/resources/reflection-config.json deleted file mode 100644 index 970565aab8..0000000000 --- a/testsuite-native-image-client/src/main/resources/reflection-config.json +++ /dev/null @@ -1,8 +0,0 @@ -[ - { - "name": "io.netty.channel.socket.nio.NioDatagramChannel", - "methods": [ - { "name": "", "parameterTypes": [ "io.netty.channel.EventLoop"] } - ] - } -] diff --git a/testsuite-native-image/pom.xml b/testsuite-native-image/pom.xml deleted file mode 100644 index 60247aae37..0000000000 --- a/testsuite-native-image/pom.xml +++ /dev/null @@ -1,123 +0,0 @@ - - - - - 4.0.0 - - io.netty - netty-parent - 5.0.0.Final-SNAPSHOT - - - netty-testsuite-native-image - jar - - Netty/Testsuite/NativeImage - - - true - - true - - - - - ${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 - ${project.version} - - - - - - - skipTests - - - skipTests - - - - true - - - - - - - - org.graalvm.nativeimage - native-image-maven-plugin - ${graalvm.version} - - - - native-image - - package - - - - ${skipNativeImageTestsuite} - ${project.artifactId} - io.netty.testsuite.svm.HttpNativeServer - --report-unsupported-elements-at-runtime --allow-incomplete-classpath - - - - org.codehaus.mojo - exec-maven-plugin - 1.6.0 - - - - - verify-native-image - verify - - exec - - - - - ${skipNativeImageTestsuite} - ${project.build.directory}/${project.artifactId} - - - - - diff --git a/testsuite-native-image/src/main/java/io/netty/testsuite/svm/HttpNativeServer.java b/testsuite-native-image/src/main/java/io/netty/testsuite/svm/HttpNativeServer.java deleted file mode 100644 index b03ca0ac83..0000000000 --- a/testsuite-native-image/src/main/java/io/netty/testsuite/svm/HttpNativeServer.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.testsuite.svm; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelOption; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioHandler; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; - -/** - * An HTTP server that sends back the content of the received HTTP request - * in a pretty plaintext form. - */ -public final class HttpNativeServer { - - /** - * Main entry point (not instantiable) - */ - private HttpNativeServer() { - } - - public static void main(String[] args) throws Exception { - // Configure the server. - EventLoopGroup bossGroup = new MultithreadEventLoopGroup(1, NioHandler.newFactory()); - EventLoopGroup workerGroup = new MultithreadEventLoopGroup(NioHandler.newFactory()); - // Control status. - boolean serverStartSucess = false; - try { - ServerBootstrap b = new ServerBootstrap(); - b.option(ChannelOption.SO_BACKLOG, 1024); - b.group(bossGroup, workerGroup) - .channel(NioServerSocketChannel.class) - .handler(new LoggingHandler(LogLevel.INFO)) - .childHandler(new HttpNativeServerInitializer()); - - Channel channel = b.bind(0).get(); - System.err.println("Server started, will shutdown now."); - channel.close().sync(); - serverStartSucess = true; - } finally { - bossGroup.shutdownGracefully(); - workerGroup.shutdownGracefully(); - } - // return the right system exit code to signal success - System.exit(serverStartSucess ? 0 : 1); - } -} diff --git a/testsuite-native-image/src/main/java/io/netty/testsuite/svm/HttpNativeServerHandler.java b/testsuite-native-image/src/main/java/io/netty/testsuite/svm/HttpNativeServerHandler.java deleted file mode 100644 index f397a91a60..0000000000 --- a/testsuite-native-image/src/main/java/io/netty/testsuite/svm/HttpNativeServerHandler.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.testsuite.svm; - -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelFutureListeners; -import io.netty.channel.ChannelHandlerContext; -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.HttpHeaderNames.*; -import static io.netty.handler.codec.http.HttpResponseStatus.*; -import static io.netty.handler.codec.http.HttpVersion.*; - -public class HttpNativeServerHandler extends SimpleChannelInboundHandler { - private static final byte[] CONTENT = { 'H', 'e', 'l', 'l', 'o', ' ', 'N', 'a', 't', 'i', 'v', 'e' }; - - private static final AsciiString KEEP_ALIVE = AsciiString.cached("keep-alive"); - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) { - ctx.flush(); - } - - @Override - public void messageReceived(ChannelHandlerContext ctx, HttpObject msg) { - if (msg instanceof HttpRequest) { - HttpRequest req = (HttpRequest) msg; - - boolean keepAlive = HttpUtil.isKeepAlive(req); - FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, Unpooled.wrappedBuffer(CONTENT)); - response.headers().set(CONTENT_TYPE, "text/plain"); - response.headers().setInt(CONTENT_LENGTH, response.content().readableBytes()); - - if (!keepAlive) { - ctx.write(response).addListener(ctx, ChannelFutureListeners.CLOSE); - } else { - response.headers().set(CONNECTION, KEEP_ALIVE); - ctx.write(response); - } - } - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - cause.printStackTrace(); - ctx.close(); - } -} diff --git a/testsuite-native-image/src/main/java/io/netty/testsuite/svm/HttpNativeServerInitializer.java b/testsuite-native-image/src/main/java/io/netty/testsuite/svm/HttpNativeServerInitializer.java deleted file mode 100644 index 95359908de..0000000000 --- a/testsuite-native-image/src/main/java/io/netty/testsuite/svm/HttpNativeServerInitializer.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.testsuite.svm; - -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.socket.SocketChannel; -import io.netty.handler.codec.http.HttpServerCodec; -import io.netty.handler.codec.http.HttpServerExpectContinueHandler; - -public class HttpNativeServerInitializer extends ChannelInitializer { - - @Override - public void initChannel(SocketChannel ch) { - ChannelPipeline p = ch.pipeline(); - p.addLast(new HttpServerCodec()); - p.addLast(new HttpServerExpectContinueHandler()); - p.addLast(new HttpNativeServerHandler()); - } -} diff --git a/testsuite-native-image/src/main/java/io/netty/testsuite/svm/package-info.java b/testsuite-native-image/src/main/java/io/netty/testsuite/svm/package-info.java deleted file mode 100644 index 3849232de4..0000000000 --- a/testsuite-native-image/src/main/java/io/netty/testsuite/svm/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * 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: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * A hello world server that should be compiled to native. - */ -package io.netty.testsuite.svm; diff --git a/testsuite-native/pom.xml b/testsuite-native/pom.xml deleted file mode 100644 index 6f8b5d8dc0..0000000000 --- a/testsuite-native/pom.xml +++ /dev/null @@ -1,168 +0,0 @@ - - - - - 4.0.0 - - io.netty - netty-parent - 5.0.0.Final-SNAPSHOT - - - netty-testsuite-native - jar - - Netty/Testsuite/Native - - - true - false - - true - - - - - org.junit.jupiter - junit-jupiter-api - - - org.junit.jupiter - junit-jupiter-engine - - - org.junit.vintage - junit-vintage-engine - - - junit - junit - - - - - skipTests - - - skipTests - - - - true - - - - - default - - true - - - - ${project.groupId} - netty-transport-native-epoll - ${project.version} - compile - - - ${project.groupId} - netty-transport-native-kqueue - ${project.version} - compile - - - ${project.groupId} - netty-resolver-dns-native-macos - ${project.version} - compile - - - - - - linux - - - linux - - - - - ${project.groupId} - netty-transport-native-epoll - ${project.version} - ${jni.classifier} - compile - - - ${project.groupId} - netty-transport-native-kqueue - ${project.version} - compile - - - ${project.groupId} - netty-resolver-dns-native-macos - ${project.version} - compile - - - - - - mac - - - mac - - - - - ${project.groupId} - netty-transport-native-kqueue - ${project.version} - ${jni.classifier} - compile - - - ${project.groupId} - netty-resolver-dns-native-macos - ${project.version} - ${jni.classifier} - compile - - - ${project.groupId} - netty-transport-native-epoll - ${project.version} - compile - - - - - - - - - maven-surefire-plugin - - ${skipNativeTestsuite} - - - - - diff --git a/testsuite-native/src/test/java/io/netty/testsuite_native/NativeLoadingTest.java b/testsuite-native/src/test/java/io/netty/testsuite_native/NativeLoadingTest.java deleted file mode 100644 index ae279c6eab..0000000000 --- a/testsuite-native/src/test/java/io/netty/testsuite_native/NativeLoadingTest.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2020 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.testsuite_native; - -import io.netty.channel.epoll.Epoll; -import io.netty.channel.kqueue.KQueue; -import io.netty.resolver.dns.macos.MacOSDnsServerAddressStreamProvider; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.EnabledOnOs; -import org.junit.jupiter.api.condition.OS; - -public class NativeLoadingTest { - - @Test - @EnabledOnOs(OS.MAC) - public void testNativeLoadingKqueue() { - KQueue.ensureAvailability(); - } - - @Test - @EnabledOnOs(OS.MAC) - public void testNativeLoadingDnsServerAddressStreamProvider() { - MacOSDnsServerAddressStreamProvider.ensureAvailability(); - } - - @Test - @EnabledOnOs(OS.LINUX) - public void testNativeLoadingEpoll() { - Epoll.ensureAvailability(); - } -} diff --git a/testsuite-osgi/pom.xml b/testsuite-osgi/pom.xml deleted file mode 100644 index a59e1c318e..0000000000 --- a/testsuite-osgi/pom.xml +++ /dev/null @@ -1,256 +0,0 @@ - - - - - 4.0.0 - - io.netty - netty-parent - 5.0.0.Final-SNAPSHOT - - - netty-testsuite-osgi - jar - - Netty/Testsuite/OSGI - - - 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 - - true - - - - - skipTests - - - skipTests - - - - true - - - - - linux - - - linux - - - - - ${project.groupId} - netty-transport-native-epoll - ${project.version} - test - - - - - - mac - - - mac - - - - - ${project.groupId} - netty-transport-native-kqueue - ${project.version} - test - - - - - - - - - ${project.groupId} - netty-buffer - ${project.version} - test - - - ${project.groupId} - netty-codec - ${project.version} - test - - - ${project.groupId} - netty-codec-dns - ${project.version} - test - - - ${project.groupId} - netty-codec-haproxy - ${project.version} - test - - - ${project.groupId} - netty-codec-http - ${project.version} - test - - - ${project.groupId} - netty-codec-http2 - ${project.version} - test - - - ${project.groupId} - netty-codec-memcache - ${project.version} - test - - - ${project.groupId} - netty-codec-mqtt - ${project.version} - test - - - ${project.groupId} - netty-codec-socks - ${project.version} - test - - - ${project.groupId} - netty-codec-stomp - ${project.version} - test - - - ${project.groupId} - netty-common - ${project.version} - test - - - ${project.groupId} - netty-handler - ${project.version} - test - - - ${project.groupId} - netty-handler-proxy - ${project.version} - test - - - ${project.groupId} - netty-resolver - ${project.version} - test - - - ${project.groupId} - netty-resolver-dns - ${project.version} - test - - - ${project.groupId} - netty-transport - ${project.version} - test - - - ${project.groupId} - netty-transport-sctp - ${project.version} - test - - - - org.apache.felix - org.apache.felix.configadmin - 1.9.14 - - - org.apache.felix - org.apache.felix.framework - 6.0.2 - test - - - org.ops4j.pax.exam - pax-exam-junit4 - ${exam.version} - test - - - org.ops4j.pax.exam - pax-exam-container-native - ${exam.version} - test - - - org.ops4j.pax.exam - pax-exam-link-assembly - ${exam.version} - test - - - - - - - com.github.veithen.alta - alta-maven-plugin - 0.6.2 - - - - generate-test-resources - - - %bundle.symbolicName%.link - %url% - - test - - - - - - - maven-surefire-plugin - - ${skipOsgiTestsuite} - - ${project.build.directory}/generated-test-resources/alta - - - - - - - diff --git a/testsuite-osgi/src/test/java/io/netty/osgitests/OsgiBundleTest.java b/testsuite-osgi/src/test/java/io/netty/osgitests/OsgiBundleTest.java deleted file mode 100644 index 78e73b9a89..0000000000 --- a/testsuite-osgi/src/test/java/io/netty/osgitests/OsgiBundleTest.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2015 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.osgitests; - -import static org.junit.Assert.assertFalse; -import static org.ops4j.pax.exam.CoreOptions.frameworkProperty; -import static org.ops4j.pax.exam.CoreOptions.junitBundles; -import static org.ops4j.pax.exam.CoreOptions.systemProperty; -import static org.ops4j.pax.exam.CoreOptions.url; -import static org.osgi.framework.Constants.FRAMEWORK_BOOTDELEGATION; - -import java.io.File; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.ops4j.pax.exam.Configuration; -import org.ops4j.pax.exam.Option; -import org.ops4j.pax.exam.junit.PaxExam; -import io.netty.util.internal.PlatformDependent; - -@RunWith(PaxExam.class) -public class OsgiBundleTest { - private static final Collection LINKS; - - static { - final Set links = new HashSet(); - - final File directory = new File("target/generated-test-resources/alta/"); - File[] files = directory.listFiles((dir, name) -> - (name.startsWith("io.netty") || name.startsWith("com.barchart.udt")) && name.endsWith(".link")); - if (files == null) { - throw new IllegalStateException(directory + " is not found or is not a directory"); - } - for (File f: files) { - links.add(f.getName()); - } - LINKS = links; - } - - @Configuration - public final Option[] config() { - final Collection